summaryrefslogblamecommitdiffstats
path: root/src/emu/cpu.c
blob: fc3d229227755849f9b061a38ddfe2d3d86fb940 (plain) (tree)
1
2
3
4
5
6
7





                    
                     






                    
                            
             
 









                           

          

                               
 
                                                    
             
                                                          
 
                                                      




                                                       




                            





                           

                                                                   
                      



                                                              
                           

                                                            




                             

                                                               



                      
                       


                                
                        

                               
                                                   



                         
                                                                            


                                                           

                                    




                                    

                                         
 
                                      
                                                                       

                                                                
                                                                                   
                                                                   
                        
                                                            
                                                             





                                    

                         
 
                                    

                                         


                                 
                                                                       


                                                                
                                                           


                                                             
                                                           






                                                             





                              
                                           





                                                  























                                                 





                                         
                     

                                       
                      

                           
                      

                            
                      

                           
                      

                                
                      




                                  
                      

                           
                      













                                          
                                                 



                               
                                         

                       

                                          



                           






                                         
 
                             
 
#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 StP;

/* 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);
}