summaryrefslogblamecommitdiffstats
path: root/src/emu/cpu.c
blob: f32bb7f597e501143eb68bf1d294424b19b3230c (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13












                    

                           
 









                           

          

                               
 
                                                    



















                                                            

                                                                   

                           

                                                            




                             

                                                               










                                
                                                   



                         
                                                         


                                                           

                                    







                                     




                                                                
                                                                   


                                                             





                                    

                         
 





















                                                                





                              
                                           





                                                  























                                                 





                                         
                     

                                       
                      

                           
                      

                            
                      

                           
                      

                                
                      




                                  

                           
                      



























                                          






                                         

                
 
#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"

/* 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
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:
		fprintf(stderr, "trap: unaligned memory access\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(uint32_t IR)
{
	/* decode op-code */
	uint8_t opcode = OPCODE;
	int32_t a, b, c;

	if (opcode < OPC_MOV) {
		/* arithmetic, logic, comparison */
		a = REGa;
		b = REGb;
		c = REGc;

		debug("IP@%#08x: %-4s r%i, r%i, r%i", IP,
				opc2mnemonic(IR), a, b, c);
	}

	else if (opcode < OPC_BEZ) {
		/* load/store */
		a = REGa;
		b = REGb;
		c = IMMc;

		/* sign extension */
		if (c >= 0x8000)
			c -= 0x10000;

		if (opcode < OPC_LW) {
			debug("IP@%#08x: %-4s r%i, %i", IP,
					opc2mnemonic(IR), a, c);
		} else if (opcode < OPC_PUSH) {
			debug("IP@%#08x: %-4s r%i, r%i, %i", IP,
					opc2mnemonic(IR), a, b, c);
		} else {
			debug("IP@%#08x: %-4s r%i", IP,
					opc2mnemonic(IR), a);
		}
	}

	else if (opcode < OPC_SYS) {
		/* jump */
		a = REGa;
		b = 0;
		c = IMMc;

		/* sign extension */
		if (c >= 0x8000)
			c -= 0x10000;

		switch (opcode) {
		case OPC_BEZ:
			debug("IP@%#08x: %-4s r%i, %i", IP,
					opc2mnemonic(IR), a, c);
			break;
		case OPC_JMP:
			debug("IP@%#08x: %-4s %i", IP,
					opc2mnemonic(IR), c);
			break;
		case OPC_CALL:
			debug("IP@%#08x: %-4s %i", 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;
	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 * 4;
			return;
		}
		break;
	case OPC_JMP:
		IP += c * 4;
		return;
	case OPC_CALL:
		push(IP + 4);
		IP += c * 4;
		return;
	case OPC_RET:
		IP = pop();
		return;
	case OPC_SYS:
		trap(TRP_SYSCALL);
		break;
	default:
		/* illegal instruction */
		trap(TRP_ILL);
	}

	IP += 4;
}