Implement game pad and timer

This commit is contained in:
Guy Perfect 2024-10-19 20:35:56 -05:00
parent 37b9a94338
commit e9f5437c1f
6 changed files with 386 additions and 9 deletions

View File

@ -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 ****************************/ /*************************** Sub-Module Functions ****************************/
/* Read a typed value from a buffer in host memory */ /* 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 *****************************/ /***************************** 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 */ /* Read a typed value from the simulation bus */
static void busRead(VB *sim, uint32_t address, int type, int32_t *value) { 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; break;
case 1: break; /* VSU */ 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 3: break; /* Unmapped */
case 4: break; /* Game Pak expansion */ 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; break;
case 1: break; /* VSU */ 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 3: break; /* Unmapped */
case 4: break; /* Game Pak expansion */ case 4: break; /* Game Pak expansion */

View File

@ -1825,8 +1825,18 @@ static void cpuReset(VB *sim) {
/* Determine how many clocks are guaranteed to process */ /* Determine how many clocks are guaranteed to process */
static uint32_t cpuUntil(VB *sim, uint32_t clocks) { 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 */ #endif /* VBAPI */

117
core/game-pad.c Normal file
View File

@ -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 */

130
core/timer.c Normal file
View File

@ -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 */

View File

@ -90,6 +90,39 @@ struct VB {
int step; /* Operation sub-task ID */ int step; /* Operation sub-task ID */
} cpu; } 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 */ /* VIP */
struct { struct {
@ -168,6 +201,7 @@ struct VB {
} vip; } vip;
/* Other state */ /* Other state */
uint8_t wcr; /* Wait controller state */
uint8_t wram[0x10000]; /* System RAM */ uint8_t wram[0x10000]; /* System RAM */
/* Application data */ /* Application data */
@ -199,6 +233,8 @@ static int32_t SignExtend(int32_t value, int32_t bits) {
/******************************** Sub-Modules ********************************/ /******************************** Sub-Modules ********************************/
#include "game-pad.c"
#include "timer.c"
#include "bus.c" #include "bus.c"
#include "cpu.c" #include "cpu.c"
#include "vip.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) { static int sysEmulate(VB *sim, uint32_t clocks) {
return return
cpuEmulate(sim, clocks) | 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) { static uint32_t sysUntil(VB *sim, uint32_t clocks) {
clocks = cpuUntil(sim, clocks); clocks = cpuUntil(sim, clocks);
clocks = vipUntil(sim, clocks); clocks = vipUntil(sim, clocks);
clocks = padUntil(sim, clocks);
clocks = tmrUntil(sim, clocks);
return clocks; return clocks;
} }
@ -295,6 +335,11 @@ VBAPI vbOnFrame vbGetFrameCallback(VB *sim) {
return sim->onFrame; 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 */ /* Retrieve the most recent frame image pixels */
VBAPI void vbGetPixels(VB *sim, void *left, int leftStrideX, int leftStrideY, VBAPI void vbGetPixels(VB *sim, void *left, int leftStrideX, int leftStrideY,
void *right, int rightStrideX, int rightStrideY) { 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) { VBAPI VB* vbReset(VB *sim) {
int x; /* Iterator */ int x; /* Iterator */
/* Wait controller */
sim->wcr = 0x00;
/* 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++)
sim->wram[x] = 0x00; sim->wram[x] = 0x00;
@ -398,6 +446,8 @@ VBAPI VB* vbReset(VB *sim) {
/* Components */ /* Components */
cpuReset(sim); cpuReset(sim);
vipReset(sim); vipReset(sim);
padReset(sim);
tmrReset(sim);
return sim; return sim;
} }
@ -451,6 +501,11 @@ VBAPI vbOnFrame vbSetFrameCallback(VB *sim, vbOnFrame callback) {
return prev; 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 */ /* Specify a new value for the program counter */
VBAPI uint32_t vbSetProgramCounter(VB *sim, uint32_t value) { VBAPI uint32_t vbSetProgramCounter(VB *sim, uint32_t value) {
sim->cpu.operation = CPU_FETCH; sim->cpu.operation = CPU_FETCH;

View File

@ -107,6 +107,7 @@ VBAPI vbOnException vbGetExceptionCallback(VB *sim);
VBAPI vbOnExecute vbGetExecuteCallback (VB *sim); VBAPI vbOnExecute vbGetExecuteCallback (VB *sim);
VBAPI vbOnFetch vbGetFetchCallback (VB *sim); VBAPI vbOnFetch vbGetFetchCallback (VB *sim);
VBAPI vbOnFrame vbGetFrameCallback (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 void vbGetPixels (VB *sim, void *left, int leftStrideX, int leftStrideY, void *right, int rightStrideX, int rightStrideY);
VBAPI uint32_t vbGetProgramCounter (VB *sim); VBAPI uint32_t vbGetProgramCounter (VB *sim);
VBAPI int32_t vbGetProgramRegister (VB *sim, int index); 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 vbOnExecute vbSetExecuteCallback (VB *sim, vbOnExecute callback);
VBAPI vbOnFetch vbSetFetchCallback (VB *sim, vbOnFetch callback); VBAPI vbOnFetch vbSetFetchCallback (VB *sim, vbOnFetch callback);
VBAPI vbOnFrame vbSetFrameCallback (VB *sim, vbOnFrame 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 uint32_t vbSetProgramCounter (VB *sim, uint32_t value);
VBAPI int32_t vbSetProgramRegister (VB *sim, int index, int32_t value); VBAPI int32_t vbSetProgramRegister (VB *sim, int index, int32_t value);
VBAPI vbOnRead vbSetReadCallback (VB *sim, vbOnRead callback); VBAPI vbOnRead vbSetReadCallback (VB *sim, vbOnRead callback);