#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <signal.h>
#include <string.h>
#include <inttypes.h>
#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
uint64_t __OPEXT(inst_t IR, uint8_t start, uint8_t length)
{
return (IR >> start) & ((1ULL << length) - 1);
}
#define OPEXT(start, length) __OPEXT(IR, start, length)
/* operands */
#define OPCODE OPEXT(58, 6)
#define REGa OPEXT(45, 13)
#define REGb OPEXT(32, 13)
#define REGc OPEXT(19, 13)
#define IMMc OPEXT(0, 32)
/* cpu traps */
void trap(int num)
{
switch (num) {
case TRP_UNALIGNED:
fprintf(stderr, "trap: unaligned memory access\n");
abort();
break;
case TRP_SEGV:
fprintf(stderr, "trap: segmentation fault\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(inst_t IR)
{
/* decode op-code */
uint8_t opcode = OPCODE;
int64_t a, b, c;
if (opcode < OPC_MOV) {
/* arithmetic, logic, comparison */
a = REGa;
b = REGb;
c = REGc;
debug("IP@%#08x: %-4s r%"PRIi64", r%"PRIi64", r%"PRIi64, IP,
opc2mnemonic(IR), a, b, c);
}
else if (opcode < OPC_BEZ) {
/* load/store */
a = REGa;
b = REGb;
c = IMMc;
/* sign extension */
if (c >= 0x80000000)
c -= 0x100000000;
if (opcode < OPC_LW) {
debug("IP@%#08x: %-4s r%"PRIi64", %"PRIi64, IP,
opc2mnemonic(IR), a, c);
} else if (opcode < OPC_PUSH) {
debug("IP@%#08x: %-4s r%"PRIi64", r%"PRIi64", %"PRIi64, IP,
opc2mnemonic(IR), a, b, c);
} else {
debug("IP@%#08x: %-4s r%"PRIi64, IP,
opc2mnemonic(IR), a);
}
}
else if (opcode < OPC_SYS) {
/* jump */
a = REGa;
b = 0;
c = IMMc;
/* sign extension */
if (c >= 0x80000000)
c -= 0x100000000;
switch (opcode) {
case OPC_BEZ:
debug("IP@%#08x: %-4s r%"PRIi64", %"PRIi64, IP,
opc2mnemonic(IR), a, c);
break;
case OPC_JMP:
debug("IP@%#08x: %-4s %"PRIi64, IP,
opc2mnemonic(IR), c);
break;
case OPC_CALL:
debug("IP@%#08x: %-4s %"PRIi64, 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 * sizeof(inst_t);
return;
}
break;
case OPC_JMP:
IP += c * sizeof(inst_t);
return;
case OPC_CALL:
push(IP + sizeof(inst_t));
IP += c * sizeof(inst_t);
return;
case OPC_RET:
IP = pop();
return;
case OPC_SYS:
trap(TRP_SYSCALL);
break;
default:
/* illegal instruction */
trap(TRP_ILL);
}
IP += sizeof(inst_t);
}