From e9f5437c1f79d87931ccaec3ccc092f171dd56f6 Mon Sep 17 00:00:00 2001 From: Guy Perfect Date: Sat, 19 Oct 2024 20:35:56 -0500 Subject: [PATCH] Implement game pad and timer --- core/bus.c | 75 +++++++++++++++++++++++++--- core/cpu.c | 14 +++++- core/game-pad.c | 117 +++++++++++++++++++++++++++++++++++++++++++ core/timer.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++++ core/vb.c | 57 ++++++++++++++++++++- core/vb.h | 2 + 6 files changed, 386 insertions(+), 9 deletions(-) create mode 100644 core/game-pad.c create mode 100644 core/timer.c diff --git a/core/bus.c b/core/bus.c index c9b85ac..4dd9fd3 100644 --- a/core/bus.c +++ b/core/bus.c @@ -16,6 +16,13 @@ static const uint32_t TYPE_MASKS[] = { +/**************************** Forward references *****************************/ + +static void vipRead (VB *, uint32_t, int, int32_t *); +static void vipWrite(VB *, uint32_t, int, int32_t, int); + + + /*************************** Sub-Module Functions ****************************/ /* Read a typed value from a buffer in host memory */ @@ -76,14 +83,62 @@ static void busWriteBuffer(uint8_t *data, int type, int32_t value) { } +/* Read a value from miscellaneous hardware I/O */ +static int32_t busReadMisc(VB *sim, uint8_t address, int type) { + + /* Unmapped */ + switch (type) { + case VB_S8 : case VB_U8 : if (address & 3) return 0; break; + case VB_S16: case VB_U16: if (address & 1) return 0; + } + + /* Access by register */ + switch (address >> 2 & 15) { + case 0x00>>2: break; /* CCR */ + case 0x04>>2: break; /* CCSR */ + case 0x08>>2: break; /* CDTR */ + case 0x0C>>2: break; /* CDRR */ + case 0x10>>2: return sim->pad.sdlr; /* SDLR */ + case 0x14>>2: return sim->pad.sdhr; /* SDHR */ + case 0x18>>2: return sim->tmr.counter & 0xFF; /* TLR */ + case 0x1C>>2: return sim->tmr.counter >> 8 & 0xFF; /* THR */ + case 0x20>>2: return tmrReadControl(sim); /* TCR */ + case 0x24>>2: return sim->wcr; /* WCR */ + case 0x28>>2: return padReadControl(sim); /* SCR */ + } + + /* Unmapped */ + return 0; +} + +/* Write a value to miscellaneous hardware I/O */ +static void busWriteMisc(VB *sim, uint8_t address, int type, int32_t value) { + + /* Unmapped */ + switch (type) { + case VB_S8 : case VB_U8 : if (address & 3) return; break; + case VB_S16: case VB_U16: if (address & 1) return; + } + + /* Access by register */ + switch (address >> 2 & 15) { + case 0x00>>2: break; /* CCR */ + case 0x04>>2: break; /* CCSR */ + case 0x08>>2: break; /* CDTR */ + case 0x0C>>2: break; /* CDRR */ + case 0x18>>2: tmrWriteLow (sim, value); break; /* TLR */ + case 0x1C>>2: tmrWriteHigh (sim, value); break; /* THR */ + case 0x20>>2: tmrWriteControl(sim, value); break; /* TCR */ + case 0x24>>2: sim->wcr = value & 3; break; /* WCR */ + case 0x28>>2: padWriteControl(sim, value); break; /* SCR */ + } + +} + /***************************** Library Functions *****************************/ -/* Forward references */ -static void vipRead (VB *, uint32_t, int, int32_t *); -static void vipWrite(VB *, uint32_t, int, int32_t, int); - /* Read a typed value from the simulation bus */ static void busRead(VB *sim, uint32_t address, int type, int32_t *value) { @@ -99,7 +154,11 @@ static void busRead(VB *sim, uint32_t address, int type, int32_t *value) { break; case 1: break; /* VSU */ - case 2: break; /* Misc. I/O */ + + case 2: /* Misc. I/O */ + *value = busReadMisc(sim, address, type); + break; + case 3: break; /* Unmapped */ case 4: break; /* Game Pak expansion */ @@ -139,7 +198,11 @@ static void busWrite(VB*sim,uint32_t address,int type,int32_t value,int debug){ break; case 1: break; /* VSU */ - case 2: break; /* Misc. I/O */ + + case 2: /* Misc. I/O */ + busWriteMisc(sim, address, type, value); + break; + case 3: break; /* Unmapped */ case 4: break; /* Game Pak expansion */ diff --git a/core/cpu.c b/core/cpu.c index a5eddc1..b453536 100644 --- a/core/cpu.c +++ b/core/cpu.c @@ -1825,8 +1825,18 @@ static void cpuReset(VB *sim) { /* Determine how many clocks are guaranteed to process */ static uint32_t cpuUntil(VB *sim, uint32_t clocks) { - return sim->cpu.halt || sim->cpu.clocks > clocks ? - clocks : sim->cpu.clocks; + + /* Halting */ + if (sim->cpu.halt) { + return + sim->cpu.operation == CPU_HALTING && + !(sim->cpu.psw.id | sim->cpu.psw.ep | sim->cpu.psw.np) && + IRQ_LEVELS[sim->cpu.irq] < sim->cpu.psw.i + ? 0 : clocks; + } + + /* Not halting */ + return sim->cpu.clocks > clocks ? clocks : sim->cpu.clocks; } #endif /* VBAPI */ diff --git a/core/game-pad.c b/core/game-pad.c new file mode 100644 index 0000000..f0b5067 --- /dev/null +++ b/core/game-pad.c @@ -0,0 +1,117 @@ +/* This file is included into vb.c and cannot be compiled on its own. */ +#ifdef VBAPI + + + +/***************************** Library Functions *****************************/ + +/* Process component */ +static int padEmulate(VB *sim, uint32_t clocks) { + + /* No hardware read in progress */ + if (sim->pad.si_stat == 0) + return 0; + + /* Hardware read completes after time to process */ + if (sim->pad.si_stat > clocks) { + sim->pad.si_stat -= clocks; + return 0; + } + + /* Complete hardware read */ + sim->pad.si_stat = 0; + sim->pad.sdlr = sim->pad.keys; + sim->pad.sdhr = sim->pad.keys >> 8; + + /* Raise game pad interrupt */ + if ( + !sim->pad.k_int_inh && + (sim->pad.keys & 0xFFF0) != 0 && + (sim->pad.keys & 0x000E) == 0 + ) sim->cpu.irq |= 0x0001; + + return 0; +} + +/* Read a value from SCR */ +static int32_t padReadControl(VB *sim) { + return 0x4C | + sim->pad.k_int_inh << 7 | + sim->pad.para_si << 5 | + sim->pad.soft_ck << 4 | + !!sim->pad.si_stat << 1 | + sim->pad.s_abt_dis + ; +} + +/* Simulate a hardware reset */ +static void padReset(VB *sim) { + + /* Normal */ + sim->pad.k_int_inh = 0; + sim->pad.para_si = 0; + sim->pad.s_abt_dis = 0; + sim->pad.sdhr = 0x00; + sim->pad.sdlr = 0x00; + sim->pad.si_stat = 0; + sim->pad.soft_ck = 0; + + /* Other */ + sim->pad.step = 0; +} + +/* Write a value to SCR */ +static void padWriteControl(VB *sim, uint8_t value) { + + /* Acknowledge and disable interrupt */ + sim->pad.k_int_inh = value >> 7 & 1; + if (sim->pad.k_int_inh) + sim->cpu.irq &= ~0x0001; + + /* Enable or disable hardware reads */ + sim->pad.s_abt_dis = value & 1; + if (sim->pad.s_abt_dis) + sim->pad.si_stat = 0; /* Abort hardware read */ + + /* Initiate a hardware read */ + if ( + (value & 0x04) && /* HW-SI */ + sim->pad.si_stat == 0 && /* No hardware read underway */ + !sim->pad.s_abt_dis /* Hardware reads enabled */ + ) { + sim->pad.si_stat = 10240; /* 512us */ + sim->pad.step = 0; /* Abort software read */ + } + + /* Reset the read operation */ + sim->pad.para_si = value >> 5 & 1; + if ( + sim->pad.para_si && + sim->pad.si_stat == 0 /* No hardware read underway */ + ) sim->pad.step = 32; + + /* Signal a bit for a software read */ + sim->pad.soft_ck = value >> 4 & 1; + if ( + sim->pad.step != 0 && /* Software read is underway */ + !sim->pad.para_si && /* Not resetting software read */ + ((sim->pad.soft_ck ^ sim->pad.step) & 1) == 1 /* Advance */ + ) { + sim->pad.step--; + if (sim->pad.step == 0) { + sim->pad.sdlr = sim->pad.keys; + sim->pad.sdhr = sim->pad.keys >> 8; + } + } + +} + +/* Determine how many clocks are guaranteed to process */ +static uint32_t padUntil(VB *sim, uint32_t clocks) { + return sim->pad.si_stat != 0 && sim->pad.si_stat < clocks ? + sim->pad.si_stat : clocks; +} + + + +#endif /* VBAPI */ diff --git a/core/timer.c b/core/timer.c new file mode 100644 index 0000000..a949b6b --- /dev/null +++ b/core/timer.c @@ -0,0 +1,130 @@ +/* This file is included into vb.c and cannot be compiled on its own. */ +#ifdef VBAPI + + + +/***************************** Module Functions ******************************/ + +/* Update the counter to a new value */ +static void tmrUpdate(VB *sim, uint16_t value) { + if (value == 0 && sim->tmr.counter != 0) { + if (sim->tmr.t_enb) + sim->tmr.z_stat = 1; + if (sim->tmr.tim_z_int) + sim->cpu.irq |= 0x0002; + } + sim->tmr.counter = value; +} + + + +/***************************** Library Functions *****************************/ + +/* Process component */ +static int tmrEmulate(VB *sim, uint32_t clocks) { + + /* Timer is disabled */ + if (!sim->tmr.t_enb) + return 0; + + /* Process all clocks */ + for (;;) { + + /* Next tick is after time to process */ + if (sim->tmr.clocks > clocks) { + sim->tmr.clocks -= clocks; + sim->tmr.until -= clocks; + return 0; + } + + /* Advance forward the component's number of clocks */ + clocks -= sim->tmr.clocks; + sim->tmr.until -= sim->tmr.clocks; + sim->tmr.clocks = sim->tmr.t_clk_sel ? 400 : 2000; + + /* Advance to the next counter value */ + tmrUpdate(sim, sim->tmr.counter == 0 ? + sim->tmr.reload : sim->tmr.counter - 1); + if (sim->tmr.counter == 0) + sim->tmr.until = sim->tmr.clocks * ((uint32_t)sim->tmr.reload + 1); + } + +} + +/* Read a value from TCR */ +static int32_t tmrReadControl(VB *sim) { + return 0xE4 | + sim->tmr.t_clk_sel << 4 | + sim->tmr.tim_z_int << 3 | + sim->tmr.z_stat << 1 | + sim->tmr.t_enb + ; +} + +/* Simulate a hardware reset */ +static void tmrReset(VB *sim) { + + /* Normal */ + sim->tmr.t_enb = 0; + sim->tmr.t_clk_sel = 0; + sim->tmr.tim_z_int = 0; + sim->tmr.z_stat = 0; + + /* Other */ + sim->tmr.counter = 0xFFFF; + sim->tmr.reload = 0x0000; +} + +/* Determine how many clocks are guaranteed to process */ +static uint32_t tmrUntil(VB *sim, uint32_t clocks) { + return sim->tmr.t_enb && sim->tmr.until < clocks ? + sim->tmr.until : clocks; +} + +/* Write a value to TCR */ +static void tmrWriteControl(VB *sim, uint8_t value) { + + /* Clear interrupt status */ + if ( + (value & 0x04) && /* Z-Stat-Clr */ + ( + sim->tmr.counter != 0 || + !sim->tmr.t_enb + ) + ) { + sim->tmr.z_stat = sim->tmr.counter != 0; + sim->cpu.irq &= ~0x0002; + } + + /* Parse fields */ + sim->tmr.t_clk_sel = value >> 4 & 1; + sim->tmr.tim_z_int = value >> 3 & 1; + sim->tmr.t_enb = value & 1; + + /* The interrupt is disabled */ + if (!sim->tmr.tim_z_int) + sim->cpu.irq &= ~0x0002; + + /* Configure countdowns */ + sim->tmr.clocks = sim->tmr.t_clk_sel ? 400 : 2000; + sim->tmr.until = sim->tmr.clocks * (sim->tmr.counter == 0 ? + (uint32_t) sim->tmr.reload + 1 : sim->tmr.counter); + + /* TODO: Will Z-Stat raise an interrupt when Tim-Z-Int is set? */ +} + +/* Write to the high data register */ +static void tmrWriteHigh(VB *sim, uint8_t value) { + sim->tmr.reload = (uint16_t) value << 8 | (sim->tmr.reload & 0x00FF); + tmrUpdate(sim, sim->tmr.reload); +} + +/* Write to the low data register */ +static void tmrWriteLow(VB *sim, uint8_t value) { + sim->tmr.reload = (sim->tmr.reload & 0xFF00) | value; + tmrUpdate(sim, sim->tmr.reload); +} + + + +#endif /* VBAPI */ diff --git a/core/vb.c b/core/vb.c index 5d2124a..19c6e9f 100644 --- a/core/vb.c +++ b/core/vb.c @@ -90,6 +90,39 @@ struct VB { int step; /* Operation sub-task ID */ } cpu; + /* Game pad */ + struct { + + /* Hardware state */ + uint8_t k_int_inh; /* Interrupt acknowledge/disable */ + uint8_t para_si; /* Read reset signal */ + uint8_t s_abt_dis; /* Abort hardware read */ + uint8_t sdhr; /* High key bits */ + uint8_t sdlr; /* Low key bits */ + uint32_t si_stat; /* Hardware read in progress */ + uint8_t soft_ck; /* Controller communication signal */ + + /* Simulation state */ + uint8_t keys; /* Next input bits */ + int step; /* Software read processing phase */ + } pad; + + /* Timer */ + struct { + + /* Hardware state */ + uint8_t t_clk_sel; /* Counter tick duration */ + uint8_t t_enb; /* Enable timer */ + uint8_t tim_z_int; /* Enable interrupt */ + uint8_t z_stat; /* Zero status */ + + /* Simulation state */ + uint32_t clocks; /* Master clocks to wait */ + uint16_t counter; /* Current counter value */ + uint16_t reload; /* Reload counter value */ + uint32_t until; /* Clocks until interrupt condition */ + } tmr; + /* VIP */ struct { @@ -168,6 +201,7 @@ struct VB { } vip; /* Other state */ + uint8_t wcr; /* Wait controller state */ uint8_t wram[0x10000]; /* System RAM */ /* Application data */ @@ -199,6 +233,8 @@ static int32_t SignExtend(int32_t value, int32_t bits) { /******************************** Sub-Modules ********************************/ +#include "game-pad.c" +#include "timer.c" #include "bus.c" #include "cpu.c" #include "vip.c" @@ -211,7 +247,9 @@ static int32_t SignExtend(int32_t value, int32_t bits) { static int sysEmulate(VB *sim, uint32_t clocks) { return cpuEmulate(sim, clocks) | - vipEmulate(sim, clocks) + vipEmulate(sim, clocks) | + padEmulate(sim, clocks) | + tmrEmulate(sim, clocks) ; } @@ -219,6 +257,8 @@ static int sysEmulate(VB *sim, uint32_t clocks) { static uint32_t sysUntil(VB *sim, uint32_t clocks) { clocks = cpuUntil(sim, clocks); clocks = vipUntil(sim, clocks); + clocks = padUntil(sim, clocks); + clocks = tmrUntil(sim, clocks); return clocks; } @@ -295,6 +335,11 @@ VBAPI vbOnFrame vbGetFrameCallback(VB *sim) { return sim->onFrame; } +/* Retrieve the current game pad key state */ +VBAPI uint16_t vbGetKeys(VB *sim) { + return sim->pad.keys; +} + /* Retrieve the most recent frame image pixels */ VBAPI void vbGetPixels(VB *sim, void *left, int leftStrideX, int leftStrideY, void *right, int rightStrideX, int rightStrideY) { @@ -391,6 +436,9 @@ VBAPI int32_t vbRead(VB *sim, uint32_t address, int type) { VBAPI VB* vbReset(VB *sim) { int x; /* Iterator */ + /* Wait controller */ + sim->wcr = 0x00; + /* WRAM (the hardware does not do this) */ for (x = 0; x < 0x10000; x++) sim->wram[x] = 0x00; @@ -398,6 +446,8 @@ VBAPI VB* vbReset(VB *sim) { /* Components */ cpuReset(sim); vipReset(sim); + padReset(sim); + tmrReset(sim); return sim; } @@ -451,6 +501,11 @@ VBAPI vbOnFrame vbSetFrameCallback(VB *sim, vbOnFrame callback) { return prev; } +/* Specify new game pad keys */ +VBAPI uint16_t vbSetKeys(VB *sim, uint16_t keys) { + return sim->pad.keys = keys; +} + /* Specify a new value for the program counter */ VBAPI uint32_t vbSetProgramCounter(VB *sim, uint32_t value) { sim->cpu.operation = CPU_FETCH; diff --git a/core/vb.h b/core/vb.h index d1ac6ea..667f4f7 100644 --- a/core/vb.h +++ b/core/vb.h @@ -107,6 +107,7 @@ VBAPI vbOnException vbGetExceptionCallback(VB *sim); VBAPI vbOnExecute vbGetExecuteCallback (VB *sim); VBAPI vbOnFetch vbGetFetchCallback (VB *sim); VBAPI vbOnFrame vbGetFrameCallback (VB *sim); +VBAPI uint16_t vbGetKeys (VB *sim); VBAPI void vbGetPixels (VB *sim, void *left, int leftStrideX, int leftStrideY, void *right, int rightStrideX, int rightStrideY); VBAPI uint32_t vbGetProgramCounter (VB *sim); VBAPI int32_t vbGetProgramRegister (VB *sim, int index); @@ -123,6 +124,7 @@ VBAPI vbOnException vbSetExceptionCallback(VB *sim, vbOnException callback); VBAPI vbOnExecute vbSetExecuteCallback (VB *sim, vbOnExecute callback); VBAPI vbOnFetch vbSetFetchCallback (VB *sim, vbOnFetch callback); VBAPI vbOnFrame vbSetFrameCallback (VB *sim, vbOnFrame callback); +VBAPI uint16_t vbSetKeys (VB *sim, uint16_t keys); VBAPI uint32_t vbSetProgramCounter (VB *sim, uint32_t value); VBAPI int32_t vbSetProgramRegister (VB *sim, int index, int32_t value); VBAPI vbOnRead vbSetReadCallback (VB *sim, vbOnRead callback);