Preliminary CPU implementation

This commit is contained in:
Guy Perfect 2021-09-19 01:31:40 +00:00
parent 70f0f2a318
commit 94486ecf02
7 changed files with 1792 additions and 85 deletions

View File

@ -1,6 +1,10 @@
/* This file is included into vb.c and cannot be compiled on its own. */ /* This file is included into vb.c and cannot be compiled on its own. */
#ifdef VBAPI #ifdef VBAPI
/***************************** Utility Functions *****************************/
/* Read a data unit from a memory buffer */ /* Read a data unit from a memory buffer */
static int32_t busReadMemory(uint8_t *mem, int type) { static int32_t busReadMemory(uint8_t *mem, int type) {
@ -28,31 +32,6 @@ static int32_t busReadMemory(uint8_t *mem, int type) {
} }
/* Read a data unit from the bus */
static int32_t busRead(VB *emu, uint32_t address, int type, int debug) {
/* Force address alignment */
address &= ~((uint32_t) TYPE_SIZES[type] - 1);
/* Process by address range */
switch (address >> 24 & 7) {
case 0 : return 0; /* VIP */
case 1 : debug = debug; return 0; /* VSU */
case 2 : return 0; /* Miscellaneous hardware */
case 3 : return 0; /* Unmapped */
case 4 : return 0; /* Game pak expansion */
case 5 : return /* WRAM */
busReadMemory(&emu->wram[address & 0xFFFF], type);
case 6 : return emu->cart.sram == NULL ? 0 : /* Game pak RAM */
busReadMemory(&emu->cart.sram
[address & (emu->cart.sramSize - 1)], type);
default: return emu->cart.rom == NULL ? 0 : /* Game pak ROM */
busReadMemory(&emu->cart.rom
[address & (emu->cart.romSize - 1)], type);
}
}
/* Write a data unit to a memory buffer */ /* Write a data unit to a memory buffer */
static void busWriteMemory(uint8_t *mem, int type, int32_t value) { static void busWriteMemory(uint8_t *mem, int type, int32_t value) {
@ -80,6 +59,35 @@ static void busWriteMemory(uint8_t *mem, int type, int32_t value) {
} }
/***************************** Module Functions ******************************/
/* Read a data unit from the bus */
static int32_t busRead(VB *emu, uint32_t address, int type, int debug) {
/* Force address alignment */
address &= ~((uint32_t) TYPE_SIZES[type] - 1);
/* Process by address range */
switch (address >> 24 & 7) {
case 0 : return 0; /* VIP */
case 1 : debug = debug; return 0; /* VSU */
case 2 : return 0; /* Miscellaneous hardware */
case 3 : return 0; /* Unmapped */
case 4 : return 0; /* Game pak expansion */
case 5 : return /* WRAM */
busReadMemory(&emu->wram[address & 0xFFFF], type);
case 6 : return emu->cart.sram == NULL ? 0 : /* Game pak RAM */
busReadMemory(&emu->cart.sram
[address & (emu->cart.sramSize - 1)], type);
default: return emu->cart.rom == NULL ? 0 : /* Game pak ROM */
busReadMemory(&emu->cart.rom
[address & (emu->cart.romSize - 1)], type);
}
}
/* Write a data unit to the bus */ /* Write a data unit to the bus */
static void busWrite( static void busWrite(
VB *emu, uint32_t address, int type, uint32_t value, int debug) { VB *emu, uint32_t address, int type, uint32_t value, int debug) {
@ -110,4 +118,6 @@ static void busWrite(
} }
#endif /* VBAPI */ #endif /* VBAPI */

1589
core/cpu.c

File diff suppressed because it is too large Load Diff

136
core/vb.c
View File

@ -1,6 +1,7 @@
#define VBAPI #define VBAPI
/* Header includes */ /* Header includes */
#include <float.h>
#include <vb.h> #include <vb.h>
@ -15,7 +16,7 @@ static const uint8_t TYPE_SIZES[] = { 1, 1, 2, 2, 4 };
/********************************** Macros ***********************************/ /********************************** Macros ***********************************/
/* Sign-extend a value of some number of bits to 32 bits */ /* Sign-extend a value of some number of bits to 32 bits */
#define SignExtend(v,b) ( (v) | (((v) & 1 << b - 1) ? ~(int32_t)0 << b : 0) ) #define SignExtend(v,b) ((v) | (((v) & ((1<<(b)) - 1)) ? ~(int32_t)0<<(b) : 0))
@ -23,11 +24,78 @@ static const uint8_t TYPE_SIZES[] = { 1, 1, 2, 2, 4 };
/* Component includes */ /* Component includes */
#include "bus.c" #include "bus.c"
#include "cpu.c"
/***************************** Module Functions ******************************/
/* Process a simulation for some number of clocks */
static int sysEmulate(VB *emu, uint32_t clocks) {
int broke;
broke = cpuEmulate(emu, clocks);
return broke;
}
/* Determine the number of clocks before a break condititon could occur */
static uint32_t sysUntil(VB *emu, uint32_t clocks) {
clocks = cpuUntil(emu, clocks);
return clocks;
}
/******************************* API Functions *******************************/ /******************************* API Functions *******************************/
/* Associate two simulations as peers */
void vbConnect(VB *emu1, VB *emu2) {
/* Disconnect any existing link associations */
if (emu1->peer != NULL && emu1->peer != emu2)
emu1->peer->peer = NULL;
if (emu2->peer != NULL && emu2->peer != emu1)
emu2->peer->peer = NULL;
/* Link the two simulations */
emu1->peer = emu2;
emu2->peer = emu1;
}
/* Disassociate linked peers */
void vbDisconnect(VB *emu) {
if (emu->peer != NULL)
emu->peer->peer = NULL;
emu->peer = NULL;
}
/* Process one or two simulations */
int vbEmulate(VB *emu1, VB *emu2, uint32_t *clocks) {
int broke; /* The simulation requested an application break */
uint32_t until; /* Maximum clocks before a break could happen */
/* Processing one simulaiton */
if (emu2 == NULL) {
do {
until = sysUntil (emu1, *clocks);
broke = sysEmulate(emu1, until );
*clocks -= until;
} while (!broke && *clocks > 0);
}
/* Processing two simulations */
else {
do {
until = sysUntil (emu1, *clocks);
until = sysUntil (emu2, until );
broke = sysEmulate(emu1, until );
broke |= sysEmulate(emu2, until );
*clocks -= until;
} while (!broke && *clocks > 0);
}
return broke;
}
/* Retrieve the value of PC */ /* Retrieve the value of PC */
uint32_t vbGetProgramCounter(VB *emu, int type) { uint32_t vbGetProgramCounter(VB *emu, int type) {
switch (type) { switch (type) {
@ -97,13 +165,23 @@ uint32_t vbGetSystemRegister(VB *emu, int id) {
/* Prepare a simulation state instance for use */ /* Prepare a simulation state instance for use */
void vbInit(VB *emu) { void vbInit(VB *emu) {
/* Breakpoint callbacks */
emu->onException = NULL;
emu->onExecute = NULL;
emu->onFetch = NULL;
emu->onRead = NULL;
emu->onWrite = NULL;
/* System */
emu->peer = NULL;
/* Cartridge */ /* Cartridge */
emu->cart.rom = NULL; emu->cart.rom = NULL;
emu->cart.romSize = 0; emu->cart.romSize = 0;
emu->cart.sram = NULL; emu->cart.sram = NULL;
emu->cart.sramSize = 0; emu->cart.sramSize = 0;
/* All others */ /* Everything else */
vbReset(emu); vbReset(emu);
} }
@ -118,7 +196,7 @@ void vbReset(VB *emu) {
uint32_t x; /* Iterator */ uint32_t x; /* Iterator */
/* CPU registers */ /* CPU registers */
emu->cpu.pc = 0xFFFFFFF0; vbSetProgramCounter(emu, 0xFFFFFFF0);
vbSetSystemRegister(emu, VB_ECR, 0x0000FFF0); vbSetSystemRegister(emu, VB_ECR, 0x0000FFF0);
vbSetSystemRegister(emu, VB_PSW, 0x00008000); vbSetSystemRegister(emu, VB_PSW, 0x00008000);
@ -132,9 +210,20 @@ void vbReset(VB *emu) {
emu->cpu.fepsw = 0x00000000; emu->cpu.fepsw = 0x00000000;
emu->cpu.sr29 = 0x00000000; emu->cpu.sr29 = 0x00000000;
emu->cpu.sr31 = 0x00000000; emu->cpu.sr31 = 0x00000000;
/* History tracking */
emu->cpu.eipcFrom = 0xFFFFFFF0;
emu->cpu.eipcTo = 0xFFFFFFF0;
emu->cpu.fepcFrom = 0xFFFFFFF0;
emu->cpu.fepcTo = 0xFFFFFFF0;
emu->cpu.pcFrom = 0xFFFFFFF0; emu->cpu.pcFrom = 0xFFFFFFF0;
emu->cpu.pcTo = 0xFFFFFFF0; emu->cpu.pcTo = 0xFFFFFFF0;
/* Other CPU state */
emu->cpu.state = CPU_FETCH;
for (x = 0; x < 5; x++)
emu->cpu.irq[x] = 0;
/* WRAM (the hardware does not do this) */ /* WRAM (the hardware does not do this) */
for (x = 0; x < 0x10000; x++) for (x = 0; x < 0x10000; x++)
emu->wram[x] = 0x00; emu->wram[x] = 0x00;
@ -143,8 +232,10 @@ void vbReset(VB *emu) {
/* Specify a new value for PC */ /* Specify a new value for PC */
uint32_t vbSetProgramCounter(VB *emu, uint32_t value) { uint32_t vbSetProgramCounter(VB *emu, uint32_t value) {
value &= 0xFFFFFFFE; value &= 0xFFFFFFFE;
emu->cpu.fetch = 0;
emu->cpu.pc = value; emu->cpu.pc = value;
/* Set stage to fecth=0 */ emu->cpu.state = CPU_FETCH;
emu->cpu.substring = 0;
return value; return value;
} }
@ -181,42 +272,7 @@ int vbSetSRAM(VB *emu, void *sram, uint32_t size) {
/* Specify a new value for a system register */ /* Specify a new value for a system register */
uint32_t vbSetSystemRegister(VB *emu, int id, uint32_t value) { uint32_t vbSetSystemRegister(VB *emu, int id, uint32_t value) {
switch (id) { return cpuSetSystemRegister(emu, id, value, 1);
case VB_ADTRE: return emu->cpu.adtre = value & 0xFFFFFFFE;
case VB_EIPC : return emu->cpu.eipc = value & 0xFFFFFFFE;
case VB_EIPSW: return emu->cpu.eipsw = value & 0x000FF3FF;
case VB_FEPC : return emu->cpu.fepc = value & 0xFFFFFFFE;
case VB_FEPSW: return emu->cpu.fepsw = value & 0x000FF3FF;
case VB_PIR : return 0x00005346;
case VB_TKCW : return 0x000000E0;
case 29 : return emu->cpu.sr29 = value & 0x00000001;
case 31 : return emu->cpu.sr31 = value;
case VB_CHCW :
emu->cpu.chcw.ice = value >> 1 & 1;
return value & 0x00000002;
case VB_ECR :
emu->cpu.ecr.fecc = value >> 16;
emu->cpu.ecr.eicc = value;
return value;
case VB_PSW :
emu->cpu.psw.i = value >> 16 & 15;
emu->cpu.psw.np = value >> 15 & 1;
emu->cpu.psw.ep = value >> 14 & 1;
emu->cpu.psw.ae = value >> 13 & 1;
emu->cpu.psw.id = value >> 12 & 1;
emu->cpu.psw.fro = value >> 9 & 1;
emu->cpu.psw.fiv = value >> 8 & 1;
emu->cpu.psw.fzd = value >> 7 & 1;
emu->cpu.psw.fov = value >> 6 & 1;
emu->cpu.psw.fud = value >> 5 & 1;
emu->cpu.psw.fpr = value >> 4 & 1;
emu->cpu.psw.cy = value >> 3 & 1;
emu->cpu.psw.ov = value >> 2 & 1;
emu->cpu.psw.s = value >> 1 & 1;
emu->cpu.psw.z = value & 1;
return value & 0x000FF3FF;
}
return 0;
} }
/* Write a data unit to the bus */ /* Write a data unit to the bus */

View File

@ -18,6 +18,7 @@ extern "C" {
/********************************* Constants *********************************/ /********************************* Constants *********************************/
/* Memory access types */ /* Memory access types */
#define VB_CANCEL -1
#define VB_S8 0 #define VB_S8 0
#define VB_U8 1 #define VB_U8 1
#define VB_S16 2 #define VB_S16 2
@ -45,8 +46,38 @@ extern "C" {
/*********************************** Types ***********************************/ /*********************************** Types ***********************************/
/* Simulation state */ /* Forward references */
typedef struct VB VB; typedef struct VB VB;
/* Memory access */
typedef struct {
uint32_t address; /* Bus address being accessed */
uint32_t clocks; /* Number of clocks required to complete */
int32_t value; /* Value read (callback's responsibility) or to write */
int8_t type; /* Data type of value */
} VB_ACCESS;
/* CPU instruction */
typedef struct {
/* Public fields */
uint32_t address; /* Bus address */
uint16_t bits[2]; /* Binary instruction code */
uint8_t size; /* Size in bytes of the instruction */
/* Implementation fields */
int32_t aux[2]; /* Auxiliary storage for CAXI and bit strings */
uint8_t id; /* Internal operation ID */
} VB_INSTRUCTION;
/* Breakpoint callbacks */
typedef int (*VB_EXCEPTIONPROC)(VB *, uint16_t);
typedef int (*VB_EXECUTEPROC )(VB *, VB_INSTRUCTION *);
typedef int (*VB_FETCHPROC )(VB *, int, VB_ACCESS *);
typedef int (*VB_READPROC )(VB *, VB_ACCESS *);
typedef int (*VB_WRITEPROC )(VB *, VB_ACCESS *);
/* Simulation state */
struct VB { struct VB {
/* Game pak */ /* Game pak */
@ -101,17 +132,37 @@ struct VB {
/* Other registers */ /* Other registers */
uint32_t pc; /* Program counter */ uint32_t pc; /* Program counter */
uint32_t pcFrom; /* Source of most recent jump */
uint32_t pcTo; /* Destination of most recent jump */
int32_t program[32]; /* program registers */ int32_t program[32]; /* program registers */
/* Other fields */ /* History tracking */
uint32_t clocks; /* Clocks until next action */ uint32_t eipcFrom; /* Source of most recent jump */
uint8_t fetch; /* Index of fetch unit */ uint32_t eipcTo; /* Destination of most recent jump */
uint8_t state; /* Operations state */ uint32_t fepcFrom; /* Source of most recent jump */
} cpu; uint32_t fepcTo; /* Destination of most recent jump */
uint32_t pcFrom; /* Source of most recent jump */
uint32_t pcTo; /* Destination of most recent jump */
/* Other fields */ /* Other fields */
VB_ACCESS access; /* Memory access descriptor */
VB_INSTRUCTION inst; /* Instruction descriptor */
uint8_t irq[5]; /* Interrupt request lines */
uint8_t busWait; /* Waiting on a memory access */
uint16_t causeCode; /* Exception cause code */
uint32_t clocks; /* Clocks until next action */
int16_t fetch; /* Index of fetch unit */
uint8_t state; /* Operations state */
uint8_t substring; /* A bit string operation is in progress */
} cpu;
/* Breakpoint callbacks */
VB_EXCEPTIONPROC onException; /* CPU exception */
VB_EXECUTEPROC onExecute; /* Instruction execute */
VB_FETCHPROC onFetch; /* Instruction fetch */
VB_READPROC onRead; /* Memory read */
VB_WRITEPROC onWrite; /* Memory write */
/* Other fields */
VB *peer; /* Communications peer */
uint8_t wram[0x10000]; /* Main memory */ uint8_t wram[0x10000]; /* Main memory */
}; };
@ -119,6 +170,9 @@ struct VB {
/**************************** Function Prototypes ****************************/ /**************************** Function Prototypes ****************************/
VBAPI void vbConnect (VB *emu1, VB *emu2);
VBAPI void vbDisconnect (VB *emu);
VBAPI int vbEmulate (VB *emu1, VB *emu2, uint32_t *clocks);
VBAPI uint32_t vbGetProgramCounter (VB *emu, int type); VBAPI uint32_t vbGetProgramCounter (VB *emu, int type);
VBAPI int32_t vbGetProgramRegister (VB *emu, int id); VBAPI int32_t vbGetProgramRegister (VB *emu, int id);
VBAPI void* vbGetROM (VB *emu, uint32_t *size); VBAPI void* vbGetROM (VB *emu, uint32_t *size);

View File

@ -1,4 +1,4 @@
Copyright (C) 2021 Planet Virtual Boy Copyright (C) 2021 Guy Perfect
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@ -1,7 +1,7 @@
.PHONY: help .PHONY: help
help: help:
@echo @echo
@echo "Virtual Boy Emulator - September 10, 2021" @echo "Virtual Boy Emulator - September 18, 2021"
@echo @echo
@echo "Target build environment is any Debian with the following packages:" @echo "Target build environment is any Debian with the following packages:"
@echo " emscripten" @echo " emscripten"