diff options
| author | Nicolas James <Eele1Ephe7uZahRie@tutanota.com> | 2025-02-13 18:00:17 +1100 |
|---|---|---|
| committer | Nicolas James <Eele1Ephe7uZahRie@tutanota.com> | 2025-02-13 18:00:17 +1100 |
| commit | 98cef5e9a772602d42acfcf233838c760424db9a (patch) | |
| tree | 5277fa1d7cc0a69a0f166fcbf10fd320f345f049 /comp1521/smips/smips.c | |
initial commit
Diffstat (limited to 'comp1521/smips/smips.c')
| -rw-r--r-- | comp1521/smips/smips.c | 243 |
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; +} |
