summaryrefslogblamecommitdiffstats
path: root/src/emu/cpu.c
blob: 5256cb6c503f7ab103e0c6ea3bb46ad2ef5c5601 (plain) (tree)




































































































































































































                                                                   

                                       





































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