#include #include #include #include #include #include #include "cpu.h" #include "log.h" #include "mem.h" #include "opc.h" #include "syscall.h" // program counter uint32_t PC; // status bits bool N, Z; // 32 general purpose registers uint32_t GPR[32]; /* extract operands from the instruction regiter */ 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: raise(SIGSEGV); break; case TRP_DIVBYZERO: raise(SIGFPE); break; case TRP_SYSCALL: do_syscall(); break; case TRP_ILL: raise(SIGILL); break; } } void execute(uint32_t IR) { /* decode op-code */ uint8_t opcode = OPCODE; int32_t a, b, c; if (opcode < OPC_MOV) { /* arithmetic & logic */ a = REGa; b = REGb; c = REGc; debug("PC@%#08x: %-3s r%i, r%i, r%i", PC, opc2mnemonic(IR), a, b, c); } else if (opcode < OPC_J) { /* load/store & branch */ a = REGa; b = REGb; c = IMMc; /* sign extension */ if (c >= 0x8000) c -= 0x10000; if (opcode < OPC_LB) { debug("PC@%#08x: %-3s r%i, %i", PC, opc2mnemonic(IR), b, c); } else { debug("PC@%#08x: %-3s r%i, r%i, %i", PC, opc2mnemonic(IR), a, b, c); } } else if (opcode < OPC_SYS) { /* jump */ a = REGa; b = c = 0; debug("PC@%#08x: %-3s r%i", PC, opc2mnemonic(IR), a); } else { /* misc */ a = b = c = 0; debug("PC@%#08x: %-3s", PC, opc2mnemonic(IR)); } // make sure r0 is zero GPR[0] = 0; // buffer for load/store instructions uint8_t tmp8; uint16_t tmp16; uint32_t tmp32; 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_SHL: // XXX: signed/unsigned? GPR[a] = GPR[b] << GPR[c]; break; case OPC_SHR: // 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_XOR: GPR[a] = GPR[b] ^ GPR[c]; break; case OPC_NOR: GPR[a] = ~(GPR[b] & GPR[c]); break; case OPC_MOV: GPR[b] = c; break; case OPC_LB: memcpy(&tmp8, &MEM[GPR[a] + c], sizeof(uint8_t)); GPR[b] = tmp8; break; case OPC_LH: if ((GPR[a] + c) & 0x1) trap(TRP_UNALIGNED); memcpy(&tmp16, &MEM[GPR[a] + c], sizeof(uint16_t)); GPR[b] = tmp16; break; case OPC_LW: if ((GPR[a] + c) & 0x2) trap(TRP_UNALIGNED); memcpy(&tmp32, &MEM[GPR[a] + c], sizeof(uint32_t)); GPR[b] = tmp32; break; case OPC_SB: tmp8 = GPR[b]; memcpy(&MEM[GPR[a] + c], &tmp8, sizeof(uint8_t)); break; case OPC_SH: if ((GPR[a] + c) & 0x1) trap(TRP_UNALIGNED); tmp16 = GPR[b]; memcpy(&MEM[GPR[a] + c], &tmp16, sizeof(uint16_t)); break; case OPC_SW: if ((GPR[a] + c) & 0x2) trap(TRP_UNALIGNED); tmp32 = GPR[b]; memcpy(&MEM[GPR[a] + c], &tmp32, sizeof(uint32_t)); break; case OPC_CMP: Z = (GPR[a] == GPR[b]); N = (GPR[a] < GPR[b]); break; case OPC_BEQ: if (Z) PC += c * sizeof(uint32_t); break; case OPC_BNE: if (!Z) PC += c * sizeof(uint32_t); break; case OPC_BLT: if (N) PC += c * sizeof(uint32_t); break; case OPC_BGE: if (!N) PC += c * sizeof(uint32_t); break; case OPC_BLE: if (Z || N) PC += c * sizeof(uint32_t); break; case OPC_BGT: if (!Z && !N) PC += c * sizeof(uint32_t); case OPC_J: PC = GPR[a]; break; case OPC_JAL: GPR[31] = PC + sizeof(uint32_t); PC = GPR[a]; case OPC_SYS: trap(TRP_SYSCALL); break; default: /* illegal instruction */ trap(TRP_ILL); } }