#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[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);
}
}