Implement game pad and timer
This commit is contained in:
parent
37b9a94338
commit
e9f5437c1f
75
core/bus.c
75
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 */
|
||||
|
||||
|
|
14
core/cpu.c
14
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 */
|
||||
|
|
|
@ -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 */
|
|
@ -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 */
|
57
core/vb.c
57
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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue