diff --git a/core/bus.c b/core/bus.c index 8804b64..8ffaf0f 100644 --- a/core/bus.c +++ b/core/bus.c @@ -95,17 +95,17 @@ static int32_t busReadMisc(VB *sim, uint8_t address, int type) { /* 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 */ + case 0x00>>2: return extReadCCR(sim); /* CCR */ + case 0x04>>2: return extReadCCSR(sim); /* CCSR */ + case 0x08>>2: return sim->ext.cdtr; /* CDTR */ + case 0x0C>>2: return sim->ext.cdrr; /* 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 0x28>>2: return padReadControl(sim); /* SCR */ + case 0x24>>2: return sim->wcr.exp1w << 1 | sim->wcr.rom1w; /* WCR */ } /* Unmapped */ @@ -123,15 +123,16 @@ static void busWriteMisc(VB *sim, uint8_t address, int type, int32_t value) { /* 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 0x00>>2: extWriteCCR (sim, value); break; /* CCR */ + case 0x04>>2: extWriteCCSR(sim, value); break; /* CCSR */ + case 0x08>>2: sim->ext.cdtr = value; break; /* CDTR */ 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 */ + case 0x24>>2: /* WCR */ + sim->wcr.exp1w = value >> 1 & 1; + sim->wcr.rom1w = value & 1; } } diff --git a/core/ext.c b/core/ext.c new file mode 100644 index 0000000..cc5f45b --- /dev/null +++ b/core/ext.c @@ -0,0 +1,162 @@ +/* This file is included into vb.c and cannot be compiled on its own. */ +#ifdef VBAPI + + + +/***************************** Callback Handlers *****************************/ + +/* Prepare to handle an exception */ +#ifndef VB_DIRECT_LINK + #define VB_ON_LINK sim1->onLink +#else + extern int VB_DIRECT_LINK(VB *, VB *, uint8_t *, uint8_t *); + #define VB_ON_LINK VB_DIRECT_LINK +#endif +static int extOnLink(VB *sim1, VB *sim2, uint8_t *value1, uint8_t *value2) { + return sim1->onLink != NULL && VB_ON_LINK(sim1, sim2, value1, value2); +} +#undef VB_ON_LINK + + + +/***************************** Library Functions *****************************/ + +/* Process component */ +static int extEmulate(VB *sim, uint32_t clocks) { + VB *peer; /* Communication peer */ + VB *sims[2]; /* Communication peers */ + uint8_t values[2]; /* Transmission values */ + int x; /* Iterator */ + + /* Communication will not occur */ + if (!sim->ext.c_stat || sim->ext.c_clk_sel) + return 0; + + /* Communication completes after time to process */ + if (sim->ext.clocks > clocks) { + sim->ext.clocks -= clocks; + return 0; + } + + /* Exchange transmission data */ + sims [0] = sim; + sims [1] = sim->peer; + values[0] = sim->ext.cdtr; + values[1] = sim->peer == NULL ? 0 : sim->peer->ext.cdtr; + if (extOnLink(sim, sim->peer, &values[0], &values[1])) + return 1; + + /* Update state */ + for (x = 0; x < 2; x++) { + sim = sims[x]; + peer = sims[x ^ 1]; + if (sim == NULL) + break; + if (peer == NULL) + peer = sim; + + /* Registers */ + sim->ext.cdrr = values[x ^ 1]; + sim->ext.c_irq |= !sim->ext.c_int_inh; + sim->ext.c_stat = 0; + sim->ext.cc_rd = sim->ext.cc_wr & peer->ext.cc_wr; + sim->ext.cc_smp = sim->ext.cc_sig & peer->ext.cc_sig & sim->ext.cc_rd; + sim->ext.cc_irq |= !sim->ext.cc_int_inh && + sim->ext.cc_smp == sim->ext.cc_int_lev; + + /* Interrupt */ + if (sim->ext.c_irq | sim->ext.cc_irq) + sim->cpu.irq |= 0x0008; + } + return 0; +} + +/* Read a value from CCR */ +static int32_t extReadCCR(VB *sim) { + return 0x6D | + sim->ext.c_int_inh << 7 | + sim->ext.c_clk_sel << 4 | + sim->ext.c_stat << 1 + ; +} + +/* Read a value from CCSR */ +static int32_t extReadCCSR(VB *sim) { + return 0x60 | + sim->ext.cc_int_inh << 7 | + sim->ext.cc_int_lev << 4 | + sim->ext.cc_sig << 3 | + sim->ext.cc_smp << 2 | + sim->ext.cc_wr << 1 | + sim->ext.cc_rd + ; +} + +/* Simulate a hardware reset */ +static void extReset(VB *sim) { + + /* Normal */ + sim->ext.c_clk_sel = 0; + sim->ext.c_int_inh = 0; + sim->ext.c_stat = 0; + sim->ext.cc_int_inh = 1; + sim->ext.cc_int_lev = 1; + sim->ext.cc_sig = 1; + sim->ext.cc_wr = 1; + sim->ext.cdrr = 0x00; + sim->ext.cdtr = 0x00; + + /* Other */ + sim->ext.c_irq = 0; + sim->ext.cc_irq = 0; +} + +/* Determine how many clocks are guaranteed to process */ +static uint32_t extUntil(VB *sim, uint32_t clocks) { + return !sim->ext.c_stat || sim->ext.c_clk_sel || clocks < sim->ext.clocks ? + clocks : sim->ext.clocks; +} + +/* Write a value to CCR */ +static void extWriteCCR(VB *sim, uint8_t value) { + + /* Configure state */ + sim->ext.c_int_inh = value >> 7 & 1; + sim->ext.c_clk_sel = value >> 4 & 1; + + /* Acknowledge interrupt */ + if (sim->ext.c_int_inh) { + sim->ext.c_irq = 0; + if (!sim->ext.cc_irq) + sim->cpu.irq &= ~0x0008; + } + + /* Do not initiate a communication */ + if (sim->ext.c_stat || (value & 0x04) == 0) /* C-Start */ + return; + + /* Initiate a communication */ + sim->ext.c_stat = 1; + sim->ext.clocks = 3200; /* 160us */ +} + +/* Write a value to CCSR */ +static void extWriteCCSR(VB *sim, uint8_t value) { + + /* Configure state */ + sim->ext.cc_int_inh = value >> 7 & 1; + sim->ext.cc_int_lev = value >> 4 & 1; + sim->ext.cc_sig = value >> 3 & 1; + sim->ext.cc_wr = value >> 1 & 1; + + /* Acknowledge interrupt */ + if (!sim->ext.cc_int_inh) + return; + sim->ext.cc_irq = 0; + if (!sim->ext.c_irq) + sim->cpu.irq &= ~0x0008; +} + + + +#endif /* VBAPI */ diff --git a/core/game-pad.c b/core/game-pad.c index 70ad9a8..e973d67 100644 --- a/core/game-pad.c +++ b/core/game-pad.c @@ -9,12 +9,12 @@ static void padEmulate(VB *sim, uint32_t clocks) { /* No hardware read in progress */ - if (sim->pad.si_stat == 0) + if (!sim->pad.si_stat) return; /* Hardware read completes after time to process */ - if (sim->pad.si_stat > clocks) { - sim->pad.si_stat -= clocks; + if (sim->pad.clocks > clocks) { + sim->pad.clocks -= clocks; return; } @@ -38,7 +38,7 @@ static int32_t padReadControl(VB *sim) { sim->pad.k_int_inh << 7 | sim->pad.para_si << 5 | sim->pad.soft_ck << 4 | - !!sim->pad.si_stat << 1 | + sim->pad.si_stat << 1 | sim->pad.s_abt_dis ; } @@ -78,7 +78,8 @@ static void padWriteControl(VB *sim, uint8_t value) { 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.clocks = 10240; /* 512us */ + sim->pad.si_stat = 1; sim->pad.step = 0; /* Abort software read */ } @@ -86,7 +87,7 @@ static void padWriteControl(VB *sim, uint8_t value) { sim->pad.para_si = value >> 5 & 1; if ( sim->pad.para_si && - sim->pad.si_stat == 0 /* No hardware read underway */ + !sim->pad.si_stat /* No hardware read underway */ ) sim->pad.step = 32; /* Signal a bit for a software read */ @@ -107,8 +108,8 @@ static void padWriteControl(VB *sim, uint8_t value) { /* 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; + return sim->pad.si_stat && sim->pad.clocks < clocks ? + sim->pad.clocks : clocks; } diff --git a/core/vb.c b/core/vb.c index 587e419..af8ecfe 100644 --- a/core/vb.c +++ b/core/vb.c @@ -146,6 +146,28 @@ struct VB { int step; /* Operation sub-task ID */ } cpu; + /* Communication port */ + struct { + + /* Register state */ + uint8_t c_clk_sel; /* Transmission clock source */ + uint8_t c_int_inh; /* Interrupt acknowledge/disable */ + uint8_t c_stat; /* Communication is underway */ + uint8_t cc_int_inh; /* Interrupt acknowledge/disable */ + uint8_t cc_int_lev; /* Interrupt condition */ + uint8_t cc_rd; /* Manual read */ + uint8_t cc_sig; /* Automatic write */ + uint8_t cc_smp; /* Automatic read */ + uint8_t cc_wr; /* Manual write */ + uint8_t cdrr; /* Data received */ + uint8_t cdtr; /* Data to transmit */ + + /* Other state */ + int8_t c_irq; /* COM interrupt request */ + int8_t cc_irq; /* COMCNT interrupt request */ + uint32_t clocks; /* Master clocks to wait */ + } ext; + /* Game pad */ struct { @@ -159,8 +181,9 @@ struct VB { uint8_t soft_ck; /* Controller communication signal */ /* Other state */ - uint16_t keys; /* Next input bits */ - int step; /* Software read processing phase */ + uint32_t clocks; /* Master clocks to wait */ + uint16_t keys; /* Next input bits */ + int step; /* Software read processing phase */ } pad; /* Timer */ @@ -310,6 +333,12 @@ struct VB { int sample; /* Output sample index, period 417 */ } vsu; + /* Wait controller */ + struct { + uint8_t exp1w; /* Cartridge expansion 1-wait */ + uint8_t rom1w; /* Cartridge ROM 1-wait */ + } wcr; + /* Pseudo-halt */ struct { uint32_t address; /* Monitor address */ @@ -335,7 +364,6 @@ struct VB { } ph; /* Other state */ - uint8_t wcr; /* Wait controller state */ uint8_t wram[0x10000]; /* System RAM */ /* Application data */ @@ -343,9 +371,11 @@ struct VB { vbOnExecute onExecute; /* CPU instruction execute */ vbOnFetch onFetch; /* CPU instruction fetch */ vbOnFrame onFrame; /* VIP frame ready */ + vbOnLink onLink; /* Communication transfer */ vbOnRead onRead; /* CPU instruction read */ vbOnSamples onSamples; /* VSU samples full */ vbOnWrite onWrite; /* CPU instruction write */ + VB *peer; /* Communication peer */ void *tag; /* User data */ }; @@ -368,6 +398,7 @@ static int32_t SignExtend(int32_t value, int32_t bits) { /******************************** Sub-Modules ********************************/ +#include "ext.c" #include "game-pad.c" #include "timer.c" #include "bus.c" @@ -391,7 +422,8 @@ static int sysEmulate(VB *sim, uint32_t clocks) { ; /* Process all components */ - if (!never) ret = cpuEmulate(sim, clocks); + if (!never){ret = cpuEmulate(sim, clocks);} + ret |= extEmulate(sim, clocks); padEmulate(sim, clocks); tmrEmulate(sim, clocks); vsuEmulate(sim, clocks); @@ -403,6 +435,7 @@ static int sysEmulate(VB *sim, uint32_t clocks) { /* Determine how many clocks are guaranteed to process */ static uint32_t sysUntil(VB *sim, uint32_t clocks) { clocks = cpuUntil(sim, clocks); + clocks = extUntil(sim, clocks); clocks = padUntil(sim, clocks); clocks = tmrUntil(sim, clocks); clocks = vipUntil(sim, clocks); @@ -487,6 +520,11 @@ VBAPI uint16_t vbGetKeys(VB *sim) { return sim->pad.keys; } +/* Retrieve the current link callback handler */ +VBAPI vbOnLink vbGetLinkCallback(VB *sim) { + return sim->onLink; +} + /* Retrieve a core option value */ VBAPI int vbGetOption(VB *sim, int key) { switch (key) { @@ -495,6 +533,11 @@ VBAPI int vbGetOption(VB *sim, int key) { return 0; } +/* Retrieve the communication peer */ +VBAPI VB* vbGetPeer(VB *sim) { + return sim->peer; +} + /* Retrieve the most recent frame image pixels */ VBAPI void vbGetPixels(VB *sim, void *left, int leftStrideX, int leftStrideY, void *right, int rightStrideX, int rightStrideY) { @@ -594,9 +637,11 @@ VBAPI VB* vbInit(VB *sim) { sim->onExecute = NULL; sim->onFetch = NULL; sim->onFrame = NULL; + sim->onLink = NULL; sim->onRead = NULL; sim->onSamples = NULL; sim->onWrite = NULL; + sim->peer = NULL; sim->ph.enabled = 0; vbReset(sim); return sim; @@ -616,7 +661,8 @@ VBAPI VB* vbReset(VB *sim) { int x; /* Iterator */ /* Wait controller */ - sim->wcr = 0x00; + sim->wcr.exp1w = 0; + sim->wcr.rom1w = 0; /* WRAM (the hardware does not do this) */ for (x = 0; x < 0x10000; x++) @@ -624,6 +670,7 @@ VBAPI VB* vbReset(VB *sim) { /* Components */ cpuReset(sim); + extReset(sim); padReset(sim); tmrReset(sim); vipReset(sim); @@ -689,6 +736,13 @@ VBAPI uint16_t vbSetKeys(VB *sim, uint16_t keys) { return sim->pad.keys = keys; } +/* Specify a new link callback handler */ +VBAPI vbOnLink vbSetLinkCallback(VB *sim, vbOnLink callback) { + vbOnLink prev = sim->onLink; + sim->onLink = callback; + return prev; +} + /* Specify a new core option value */ VBAPI int vbSetOption(VB *sim, int key, int value) { switch (key) { @@ -706,6 +760,15 @@ VBAPI int vbSetOption(VB *sim, int key, int value) { return value; } +/* Specify a new communication peer */ +VBAPI VB* vbSetPeer(VB *sim, VB *peer) { + VB *prev = sim->peer; + sim->peer = peer; + if (peer != prev && prev != NULL) + prev->peer = NULL; + return prev; +} + /* 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 6ec8b28..64d92d9 100644 --- a/core/vb.h +++ b/core/vb.h @@ -22,6 +22,7 @@ extern "C" { VB_DIRECT_EXECUTE VB_DIRECT_FETCH VB_DIRECT_FRAME + VB_DIRECT_LINK VB_DIRECT_READ VB_DIRECT_SAMPLES VB_DIRECT_WRITE @@ -94,6 +95,7 @@ typedef int (*vbOnException)(VB *sim, uint16_t *cause); typedef int (*vbOnExecute )(VB *sim, uint32_t address, const uint16_t *code, int length); typedef int (*vbOnFetch )(VB *sim, int fetch, uint32_t address, int32_t *value, uint32_t *cycles); typedef int (*vbOnFrame )(VB *sim); +typedef int (*vbOnLink )(VB *sim1, VB *sim2, uint8_t *value1, uint8_t *value2); typedef int (*vbOnRead )(VB *sim, uint32_t address, int type, int32_t *value, uint32_t *cycles); typedef void (*vbOnSamples )(VB *sim, int16_t *buffer, uint32_t capacity); typedef int (*vbOnWrite )(VB *sim, uint32_t address, int type, int32_t *value, uint32_t *cycles, int *cancel); @@ -113,7 +115,9 @@ VBAPI vbOnExecute vbGetExecuteCallback (VB *sim); VBAPI vbOnFetch vbGetFetchCallback (VB *sim); VBAPI vbOnFrame vbGetFrameCallback (VB *sim); VBAPI uint16_t vbGetKeys (VB *sim); +VBAPI vbOnLink vbGetLinkCallback (VB *sim); VBAPI int vbGetOption (VB *sim, int key); +VBAPI VB* vbGetPeer (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); @@ -132,6 +136,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 vbOnLink vbSetLinkCallback (VB *sim, vbOnLink callback); VBAPI uint16_t vbSetKeys (VB *sim, uint16_t keys); VBAPI int vbSetOption (VB *sim, int key, int value); VBAPI uint32_t vbSetProgramCounter (VB *sim, uint32_t value);