summaryrefslogtreecommitdiffstats
path: root/src/emu/cpu.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/cpu.c')
-rw-r--r--src/emu/cpu.c237
1 files changed, 237 insertions, 0 deletions
diff --git a/src/emu/cpu.c b/src/emu/cpu.c
new file mode 100644
index 0000000..b0664cd
--- /dev/null
+++ b/src/emu/cpu.c
@@ -0,0 +1,237 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <signal.h>
+#include <string.h>
+
+#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[b] == (uint32_t) c);
+ N = (GPR[b] < (uint32_t) c);
+ 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);
+ }
+}