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

@ -291,7 +291,7 @@ let run = async function() {
if (!file.name.endsWith(".woff2"))
continue;
let family = "/" + file.name;
family = family.substring(family.lastIndexOf("/") + 1, family.length - 6);
family = family.substring(family.lastIndexOf("/")+1, family.length-6);
let font = new FontFace(family, file.data);
await font.load();
document.fonts.add(font);

View File

@ -1,6 +1,10 @@
/* This file is included into vb.c and cannot be compiled on its own. */
#ifdef VBAPI
/***************************** Utility Functions *****************************/
/* Read a data unit from a memory buffer */
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 */
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 */
static void busWrite(
VB *emu, uint32_t address, int type, uint32_t value, int debug) {
@ -110,4 +118,6 @@ static void busWrite(
}
#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
/* Header includes */
#include <float.h>
#include <vb.h>
@ -15,7 +16,7 @@ static const uint8_t TYPE_SIZES[] = { 1, 1, 2, 2, 4 };
/********************************** Macros ***********************************/
/* 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 */
#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 *******************************/
/* 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 */
uint32_t vbGetProgramCounter(VB *emu, int type) {
switch (type) {
@ -97,13 +165,23 @@ uint32_t vbGetSystemRegister(VB *emu, int id) {
/* Prepare a simulation state instance for use */
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 */
emu->cart.rom = NULL;
emu->cart.romSize = 0;
emu->cart.sram = NULL;
emu->cart.sramSize = 0;
/* All others */
/* Everything else */
vbReset(emu);
}
@ -118,7 +196,7 @@ void vbReset(VB *emu) {
uint32_t x; /* Iterator */
/* CPU registers */
emu->cpu.pc = 0xFFFFFFF0;
vbSetProgramCounter(emu, 0xFFFFFFF0);
vbSetSystemRegister(emu, VB_ECR, 0x0000FFF0);
vbSetSystemRegister(emu, VB_PSW, 0x00008000);
@ -132,9 +210,20 @@ void vbReset(VB *emu) {
emu->cpu.fepsw = 0x00000000;
emu->cpu.sr29 = 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.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) */
for (x = 0; x < 0x10000; x++)
emu->wram[x] = 0x00;
@ -143,8 +232,10 @@ void vbReset(VB *emu) {
/* Specify a new value for PC */
uint32_t vbSetProgramCounter(VB *emu, uint32_t value) {
value &= 0xFFFFFFFE;
emu->cpu.fetch = 0;
emu->cpu.pc = value;
/* Set stage to fecth=0 */
emu->cpu.state = CPU_FETCH;
emu->cpu.substring = 0;
return value;
}
@ -181,42 +272,7 @@ int vbSetSRAM(VB *emu, void *sram, uint32_t size) {
/* Specify a new value for a system register */
uint32_t vbSetSystemRegister(VB *emu, int id, uint32_t value) {
switch (id) {
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;
return cpuSetSystemRegister(emu, id, value, 1);
}
/* Write a data unit to the bus */

View File

@ -18,6 +18,7 @@ extern "C" {
/********************************* Constants *********************************/
/* Memory access types */
#define VB_CANCEL -1
#define VB_S8 0
#define VB_U8 1
#define VB_S16 2
@ -45,8 +46,38 @@ extern "C" {
/*********************************** Types ***********************************/
/* Simulation state */
/* Forward references */
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 {
/* Game pak */
@ -101,17 +132,37 @@ struct VB {
/* Other registers */
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 */
/* Other fields */
uint32_t clocks; /* Clocks until next action */
uint8_t fetch; /* Index of fetch unit */
uint8_t state; /* Operations state */
} cpu;
/* History tracking */
uint32_t eipcFrom; /* Source of most recent jump */
uint32_t eipcTo; /* Destination of most recent jump */
uint32_t fepcFrom; /* Source of most recent jump */
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 */
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 */
};
@ -119,6 +170,9 @@ struct VB {
/**************************** 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 int32_t vbGetProgramRegister (VB *emu, int id);
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
warranty. In no event will the authors be held liable for any damages

View File

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