aboutsummaryrefslogtreecommitdiff
path: root/comp1521/smips/smips.c
diff options
context:
space:
mode:
Diffstat (limited to 'comp1521/smips/smips.c')
-rw-r--r--comp1521/smips/smips.c243
1 files changed, 243 insertions, 0 deletions
diff --git a/comp1521/smips/smips.c b/comp1521/smips/smips.c
new file mode 100644
index 0000000..19acd73
--- /dev/null
+++ b/comp1521/smips/smips.c
@@ -0,0 +1,243 @@
+// July 2020.
+// Simple mips emulator which only reads hex instructions.
+
+#include "smips.h"
+
+// Prints an instruction and its variables based off a supplied type.
+static void print_wrapper(const uint32_t instr, const char *name, const int type) {
+ int i = (int16_t)get_i(instr);
+ int d = (int16_t)get_d(instr);
+ int t = (int16_t)get_t(instr);
+ int s = (int16_t)get_s(instr);
+ switch (type) {
+ case DST:
+ printf("%-4s $%d, $%d, $%d", name, d, s, t);
+ break;
+ case STI:
+ printf("%-4s $%d, $%d, %d", name, s, t, i);
+ break;
+ case TSI:
+ printf("%-4s $%d, $%d, %d", name, t, s, i);
+ break;
+ case TI:
+ printf("%-4s $%d, %d", name, t, i);
+ break;
+ }
+}
+
+// Returns the instruction type as defined in the instr enum.
+// Returns >= 0 if the instruction was found.
+static int query_instruction(const uint32_t instr) {
+ uint32_t pattern = get_p(instr);
+ uint32_t immediate = get_i(instr);
+
+ // Syscall, mul and lui are special cases which should be dealt with before
+ // we move into the switch statatement.
+ if (pattern == pm_mul && (immediate & SHORT_IMMEDIATE_BITS) == im_mul) {
+ return mul;
+ } else if (instr == im_syscall) {
+ return syscall;
+ } else if (get_p(instr) == pm_lui && get_s(instr) == 0) {
+ return lui;
+ }
+
+ // If the pattern bits are empty, we are dealing with instructions between
+ // add and slt inclusive. This is only true after the special cases of mul,
+ // syscall and lui are dealt with. The others can be encapsulated in an
+ // else.
+ if (!pattern) {
+ switch (immediate & SHORT_IMMEDIATE_BITS) {
+ case im_add:
+ return add;
+ case im_sub:
+ return sub;
+ case im_and:
+ return and;
+ case im_or:
+ return or ;
+ case im_slt:
+ return slt;
+ default:
+ return -1;
+ }
+ } else {
+ switch (pattern) {
+ case pm_beq:
+ return beq;
+ case pm_bne:
+ return bne;
+ case pm_addi:
+ return addi;
+ case pm_slti:
+ return slti;
+ case pm_andi:
+ return andi;
+ case pm_ori:
+ return ori;
+ default:
+ return -1;
+ }
+ }
+ return 0;
+}
+
+// Calls print wrapper with the correct name char *.
+static void print_instruction(const uint32_t instr) {
+ switch (query_instruction(instr)) {
+ case mul:
+ print_wrapper(instr, "mul", DST);
+ break;
+ case lui:
+ print_wrapper(instr, "lui", TI);
+ break;
+ case syscall:
+ printf("syscall");
+ break;
+ case add:
+ print_wrapper(instr, "add", DST);
+ break;
+ case sub:
+ print_wrapper(instr, "sub", DST);
+ break;
+ case and:
+ print_wrapper(instr, "and", DST);
+ break;
+ case or:
+ print_wrapper(instr, "or", DST);
+ break;
+ case slt:
+ print_wrapper(instr, "slt", DST);
+ break;
+ case beq:
+ print_wrapper(instr, "beq", STI);
+ break;
+ case bne:
+ print_wrapper(instr, "bne", STI);
+ break;
+ case addi:
+ print_wrapper(instr, "addi", STI);
+ break;
+ case slti:
+ print_wrapper(instr, "slti", TSI);
+ break;
+ case andi:
+ print_wrapper(instr, "andi", TSI);
+ break;
+ case ori:
+ print_wrapper(instr, "ori", TSI);
+ break;
+ }
+}
+
+// Executes a mips_? function based off the enum returned from the
+// query_instruction function. Non-zero values indicates execution should stop.
+static int execute_instruction(FILE *const iptr, const uint32_t instr,
+ uint32_t registers[32]) {
+ // At this point, guaranteed to be known instructions.
+ switch (query_instruction(instr)) {
+ case add:
+ mips_add(instr, registers);
+ break;
+ case sub:
+ mips_sub(instr, registers);
+ break;
+ case and:
+ mips_and(instr, registers);
+ break;
+ case or:
+ mips_or(instr, registers);
+ break;
+ case slt:
+ mips_slt(instr, registers);
+ break;
+ case mul:
+ mips_mul(instr, registers);
+ break;
+ case beq:
+ mips_beq(iptr, instr, registers);
+ break;
+ case bne:
+ mips_bne(iptr, instr, registers);
+ break;
+ case addi:
+ mips_addi(instr, registers);
+ break;
+ case slti:
+ mips_slti(instr, registers);
+ break;
+ case andi:
+ mips_andi(instr, registers);
+ break;
+ case ori:
+ mips_ori(instr, registers);
+ break;
+ case lui:
+ mips_lui(instr, registers);
+ break;
+ case syscall:
+ return mips_syscall(registers);
+ break;
+ }
+ // We need to restore $0, which should always be zero. So after each
+ // instruction, simply set it back to 0. Syscalls do not touch $0!
+ registers[0] = 0;
+ return 0;
+}
+
+int main(const int argc, const char *argv[]) {
+ if (argc < 2) {
+ printf("Not enough arguments!\n");
+ return EXIT_FAILURE;
+ }
+ FILE *iptr = fopen(argv[1], "r");
+ if (iptr == NULL) {
+ printf("File does not exist!\n");
+ return EXIT_FAILURE;
+ }
+ // We will read through program first, then perform a loop which
+ // reads out the instructions, loop which executes the instructions, then
+ // finally a loop which prints changed registers.
+ int i = 1;
+ uint32_t instr = 0;
+ while ((instr = get_next_instruction(iptr))) { // Proofread loop.
+ if (query_instruction(instr) == -1) {
+ printf("%s:%d: invalid instruction code: %08x\n", argv[1], i,
+ instr);
+ return EXIT_FAILURE;
+ }
+ ++i;
+ }
+ rewind(iptr);
+
+ i = 0;
+ printf("Program\n");
+ while ((instr = get_next_instruction(iptr))) { // Print loop.
+ printf("%3d: ", i);
+ print_instruction(instr);
+ printf("\n");
+ ++i;
+ }
+ rewind(iptr);
+
+ uint32_t registers[32] = {0};
+ printf("Output\n");
+ while ((instr = get_next_instruction(iptr))) { // Execution loop.
+ // The iptr may change after execute_instruction runs due to branch
+ // instructions.
+ // In addition, execute instruction returns a non-zero if execution
+ // should stop due to syscalls, etc.
+ if (execute_instruction(iptr, instr, registers)) {
+ break;
+ }
+ }
+
+ printf("Registers After Execution\n"); // Print registers loop.
+ for (size_t j = 0; j < sizeof(registers) / sizeof(uint32_t); ++j) {
+ if (registers[j]) {
+ printf("$%-2d = %u\n", (int)j, registers[j]);
+ }
+ }
+
+ fclose(iptr);
+ return EXIT_SUCCESS;
+}