#include #include #include #include #include #include #include "cpu.h" #include "log.h" #include "mem.h" #include "opc.h" #include "syscall.h" /* stack pointer register*/ uint32_t SP; /* base pointer register */ uint32_t BP; /* return value register */ uint32_t RV; /* instruction pointer */ uint32_t IP; /* status bits */ bool N, Z; /* general purpose registers */ uint32_t *GPR; /* extract operands from the instruction register */ static inline uint32_t __OPEXT(uint32_t IR, uint8_t start, uint8_t length) { return (IR >> start) & ((1 << length) - 1); } #define OPEXT(start, length) __OPEXT(IR, start, length) /* operands */ #define OPCODE OPEXT(26, 6) #define REGa OPEXT(21, 5) #define REGb OPEXT(16, 5) #define REGc OPEXT(11, 5) #define IMMc OPEXT(0, 16) /* cpu traps */ void trap(int num) { switch (num) { case TRP_UNALIGNED: fprintf(stderr, "trap: unaligned memory access\n"); abort(); break; case TRP_DIVBYZERO: fprintf(stderr, "trap: division by zero\n"); abort(); break; case TRP_SYSCALL: do_syscall(); break; case TRP_ILL: fprintf(stderr, "trap: illegal instruction\n"); abort(); break; } } void execute(uint32_t IR) { /* decode op-code */ uint8_t opcode = OPCODE; int32_t a, b, c; if (opcode < OPC_MOV) { /* arithmetic, logic, comparison */ a = REGa; b = REGb; c = REGc; debug("IP@%#08x: %-4s r%i, r%i, r%i", IP, opc2mnemonic(IR), a, b, c); } else if (opcode < OPC_BEZ) { /* load/store */ a = REGa; b = REGb; c = IMMc; /* sign extension */ if (c >= 0x8000) c -= 0x10000; if (opcode < OPC_LW) { debug("IP@%#08x: %-4s r%i, %i", IP, opc2mnemonic(IR), a, c); } else if (opcode < OPC_PUSH) { debug("IP@%#08x: %-4s r%i, r%i, %i", IP, opc2mnemonic(IR), a, b, c); } else { debug("IP@%#08x: %-4s r%i", IP, opc2mnemonic(IR), a); } } else if (opcode < OPC_SYS) { /* jump */ a = REGa; b = 0; c = IMMc; /* sign extension */ if (c >= 0x8000) c -= 0x10000; switch (opcode) { case OPC_BEZ: debug("IP@%#08x: %-4s r%i, %i", IP, opc2mnemonic(IR), a, c); break; case OPC_JMP: debug("IP@%#08x: %-4s %i", IP, opc2mnemonic(IR), c); break; case OPC_CALL: debug("IP@%#08x: %-4s %i", IP, opc2mnemonic(IR), c); break; case OPC_RET: debug("IP@%#08x: %-4s", IP, opc2mnemonic(IR)); break; } } else { /* misc */ a = b = c = 0; debug("IP@%#08x: %-3s", IP, opc2mnemonic(IR)); } // make sure r0 is zero GPR[0] = 0; switch (opcode) { case OPC_ADD: // XXX: signed/unsigned? GPR[a] = GPR[b] + GPR[c]; break; case OPC_SUB: // XXX: signed/unsigned? GPR[a] = GPR[b] - GPR[c]; break; case OPC_MUL: // XXX: signed/unsigned? GPR[a] = GPR[b] * GPR[c]; break; case OPC_DIV: // XXX: signed/unsigned? if (GPR[c] == 0) trap(TRP_DIVBYZERO); else GPR[a] = GPR[b] / GPR[c]; break; case OPC_MOD: // XXX: signed/unsigned? GPR[a] = GPR[b] % GPR[c]; break; case OPC_AND: GPR[a] = GPR[b] & GPR[c]; break; case OPC_OR: GPR[a] = GPR[b] | GPR[c]; break; case OPC_CMP: Z = (GPR[a] == GPR[b]); N = (GPR[a] < GPR[b]); break; case OPC_EQ: GPR[a] = Z; break; case OPC_NE: GPR[a] = !Z; break; case OPC_LT: GPR[a] = N; break; case OPC_LE: GPR[a] = Z || N; break; case OPC_GE: GPR[a] = !N; break; case OPC_GT: GPR[a] = !Z && !N; break; case OPC_MOV: GPR[a] = c; break; case OPC_LW: GPR[b] = load(GPR[a] + c); break; case OPC_SW: store(GPR[a] + c, GPR[b]); break; case OPC_PUSH: push(GPR[a]); break; case OPC_POP: GPR[a] = pop(); break; case OPC_BEZ: if (GPR[a] == 0) { IP += c * 4; return; } break; case OPC_JMP: IP += c * 4; return; case OPC_CALL: push(IP + 4); IP += c * 4; return; case OPC_RET: IP = pop(); return; case OPC_SYS: trap(TRP_SYSCALL); break; default: /* illegal instruction */ trap(TRP_ILL); } IP += 4; }