#ifndef VBAPI #define VBAPI #endif #include #include /*********************************** Types ***********************************/ /* Output image */ typedef uint8_t Pixels[2][384*224]; /* Simulation state */ struct VB { /* Game Pak */ struct { uint8_t *ram; /* Save RAM */ uint8_t *rom; /* Program ROM */ uint32_t ramMask; /* Size of SRAM - 1 */ uint32_t romMask; /* Size of ROM - 1 */ } cart; /* CPU */ struct { /* Cache Control Word */ struct { uint8_t ice; /* Instruction Cache Enable */ } chcw; /* Exception Cause Register */ struct { uint16_t eicc; /* Exception/Interrupt Cause Code */ uint16_t fecc; /* Fatal Error Cause Code */ } ecr; /* Program Status Word */ struct { uint8_t ae; /* Address Trap Enable */ uint8_t cy; /* Carry */ uint8_t ep; /* Exception Pending */ uint8_t fiv; /* Floating Invalid */ uint8_t fov; /* Floating Overflow */ uint8_t fpr; /* Floading Precision */ uint8_t fro; /* Floating Reserved Operand */ uint8_t fud; /* Floading Underflow */ uint8_t fzd; /* Floating Zero Divide */ uint8_t i; /* Interrupt Level */ uint8_t id; /* Interrupt Disable */ uint8_t np; /* NMI Pending */ uint8_t ov; /* Overflow */ uint8_t s; /* Sign */ uint8_t z; /* Zero */ } psw; /* Other registers */ uint32_t adtre; /* Address Trap Register for Execution */ uint32_t eipc; /* Exception/Interrupt PC */ uint32_t eipsw; /* Exception/Interrupt PSW */ uint32_t fepc; /* Fatal Error PC */ uint32_t fepsw; /* Fatal Error PSW */ uint32_t pc; /* Program Counter */ int32_t program[32]; /* Program registers */ uint32_t sr29; /* System register 29 */ uint32_t sr31; /* System register 31 */ /* Working data */ union { struct { uint32_t dest; uint64_t src; } bs; /* Arithmetic bit strings */ struct { uint32_t address; int32_t value; } data; /* Data accesses */ } aux; /* Other state */ uint32_t clocks; /* Master clocks to wait */ uint16_t code[2]; /* Instruction code units */ uint16_t exception; /* Exception cause code */ int halt; /* CPU is halting */ uint16_t irq; /* Interrupt request lines */ int length; /* Instruction code length */ uint32_t nextPC; /* Address of next instruction */ int operation; /* Current operation ID */ 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 */ uint16_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 { /* CTA */ struct { uint8_t cta_l; /* Left column table index */ uint8_t cta_r; /* Right column table index */ } cta; /* Display processor */ struct { /* Hardware state */ uint8_t disp; /* Display enabled */ uint8_t fclk; /* Frame clock signal high */ uint8_t l0bsy; /* Displaying left frame buffer 0 */ uint8_t l1bsy; /* Displaying left frame buffer 1 */ uint8_t lock; /* Lock CTA */ uint8_t r0bsy; /* Displaying right frame buffer 0 */ uint8_t r1bsy; /* Displaying right frame buffer 1*/ uint8_t re; /* Memory refresh enabled */ uint8_t scanrdy; /* Mirrors are stable */ uint8_t synce; /* Servo enabled */ /* Simulation state */ uint8_t brt[4]; /* Precomputed brightness */ int buffer; /* Index of frame buffer to display */ uint32_t clocks; /* Master clocks to wait */ int column; /* Index of column to display */ uint32_t cta; /* Column table pointer in memory */ uint32_t fbDest; /* Output frame pixel address */ uint32_t fbSrc; /* Source frame buffer address */ int32_t repeat; /* Current column table repeat value */ int step; /* Processing phase */ uint32_t until; /* Clocks until interrupt condition */ } dp; /* Pixel processor */ struct { /* Hardware state */ uint8_t f0bsy; /* Drawing into frame buffer 0 */ uint8_t f1bsy; /* Drawing into frame buffer 1 */ uint8_t overtime; /* Drawing extends into display interval */ uint8_t sbcmp; /* Vertical output position compare */ uint8_t sbcount; /* Current vertical output position */ uint32_t sbout; /* Drawing specified vertical output position */ uint8_t xpen; /* Drawing enabled */ /* Simulation state */ uint32_t clocks; /* Master clocks to wait */ int column; /* Current horizontal output position */ int frame; /* FRMCYC counter */ int32_t halfword; /* Current output halfword offset */ int step; /* Processing phase */ uint32_t until; /* Clocks until interrupt condition */ } xp; /* Control state */ uint8_t bkcol; /* Backdrop color */ uint8_t brtRest[4]; /* Brightness and REST */ uint8_t frmcyc; /* Game frame control */ uint8_t gplt[4][4]; /* Background palettes */ uint16_t intenb; /* Interrupts enabled */ uint16_t intpnd; /* Interrupts pending */ uint8_t jplt[4][4]; /* Object palettes */ uint16_t spt[4]; /* Object control */ /* Rendering shadow memory */ uint16_t halfwords[384*28]; /* Output timing by 1x8 halfword */ Pixels output[2]; /* Output images, row-major */ Pixels shadow; /* Drawing shadow image, column-major */ /* Other state */ uint8_t ram[0x40000]; /* Video memory */ } vip; /* Other state */ uint8_t wcr; /* Wait controller state */ uint8_t wram[0x10000]; /* System RAM */ /* Application data */ vbOnException onException; /* CPU exception */ vbOnExecute onExecute; /* CPU instruction execute */ vbOnFetch onFetch; /* CPU instruction fetch */ vbOnFrame onFrame; /* VIP frame ready */ vbOnRead onRead; /* CPU instruction read */ vbOnWrite onWrite; /* CPU instruction write */ void *tag; /* User data */ }; /***************************** Library Functions *****************************/ /* Sign-extend an integer of variable width */ static int32_t SignExtend(int32_t value, int32_t bits) { #ifndef VB_SIGNED_PROPAGATE value &= ~((uint32_t) 0xFFFFFFFF << bits); bits = (int32_t) 1 << (bits - (int32_t) 1); return (value ^ bits) - bits; #else return value << (32 - bits) >> (32 - bits); #endif } /******************************** Sub-Modules ********************************/ #include "game-pad.c" #include "timer.c" #include "bus.c" #include "cpu.c" #include "vip.c" /***************************** Library Functions *****************************/ /* Process a simulation for a given number of clocks */ static int sysEmulate(VB *sim, uint32_t clocks) { return cpuEmulate(sim, clocks) | vipEmulate(sim, clocks) | padEmulate(sim, clocks) | tmrEmulate(sim, clocks) ; } /* Determine how many clocks are guaranteed to process */ 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; } /******************************* API Commands ********************************/ /* Process one simulation */ VBAPI int vbEmulate(VB *sim, uint32_t *clocks) { int brk; /* A callback requested a break */ uint32_t until; /* Clocks guaranteed to process */ while (*clocks != 0) { until = sysUntil(sim, *clocks); brk = sysEmulate(sim, until); *clocks -= until; if (brk) return brk; /* TODO: return 1 */ } return 0; } /* Process multiple simulations */ VBAPI int vbEmulateEx(VB **sims, int count, uint32_t *clocks) { int brk; /* A callback requested a break */ uint32_t until; /* Clocks guaranteed to process */ int x; /* Iterator */ while (*clocks != 0) { until = *clocks; for (x = count - 1; x >= 0; x--) until = sysUntil(sims[x], until); brk = 0; for (x = count - 1; x >= 0; x--) brk |= sysEmulate(sims[x], until); *clocks -= until; if (brk) return brk; /* TODO: return 1 */ } return 0; } /* Retrieve the game pack RAM buffer */ VBAPI void* vbGetCartRAM(VB *sim, uint32_t *size) { if (size != NULL) *size = sim->cart.ram == NULL ? 0 : sim->cart.ramMask + 1; return sim->cart.ram; } /* Retrieve the game pack ROM buffer */ VBAPI void* vbGetCartROM(VB *sim, uint32_t *size) { if (size != NULL) *size = sim->cart.rom == NULL ? 0 : sim->cart.romMask + 1; return sim->cart.rom; } /* Retrieve the exception callback handler */ VBAPI vbOnException vbGetExceptionCallback(VB *sim) { return sim->onException; } /* Retrieve the execute callback handler */ VBAPI vbOnExecute vbGetExecuteCallback(VB *sim) { return sim->onExecute; } /* Retrieve the fetch callback handler */ VBAPI vbOnFetch vbGetFetchCallback(VB *sim) { return sim->onFetch; } /* Retrieve the frame callback handler */ 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) { uint8_t *dest; /* Output data */ int offset; /* Horizontal offset of output pixel */ uint8_t *src; /* Source data */ int xStride; /* Bytes between output pixels */ int yStride; /* Bytes between output lines */ int i, x, y; /* Iterators */ /* Process both eyes */ for (i = 0; i < 2; i++) { /* Working variables for left image */ if (i == 0) { dest = left; xStride = leftStrideX; yStride = leftStrideY; } /* Working variables for right image */ else { dest = right; xStride = rightStrideX; yStride = rightStrideY; } /* Nothing to do */ if (dest == NULL) continue; /* Transfer pixels to the destination */ src = sim->vip.output[sim->vip.dp.buffer ^ 1][i]; for (y = 0; y < 224; y++, dest += yStride) for (x = offset = 0; x < 384; x++, offset += xStride) dest[offset] = *src++; } } /* Retrieve the value of the program counter */ VBAPI uint32_t vbGetProgramCounter(VB *sim) { return sim->cpu.pc; } /* Retrieve the value in a program register */ VBAPI int32_t vbGetProgramRegister(VB *sim, int index) { return index < 1 || index > 31 ? 0 : sim->cpu.program[index]; } /* Retrieve the read callback handler */ VBAPI vbOnRead vbGetReadCallback(VB *sim) { return sim->onRead; } /* Retrieve the value in a system register */ VBAPI uint32_t vbGetSystemRegister(VB *sim, int index) { return index < 0 || index > 31 ? 0 : cpuGetSystemRegister(sim, index); } /* Retrieve a simulation's userdata pointer */ VBAPI void* vbGetUserData(VB *sim) { return sim->tag; } /* Retrieve the write callback handler */ VBAPI vbOnWrite vbGetWriteCallback(VB *sim) { return sim->onWrite; } /* Initialize a simulation instance */ VBAPI VB* vbInit(VB *sim) { sim->cart.ram = NULL; sim->cart.rom = NULL; sim->onExecute = NULL; sim->onFetch = NULL; sim->onFrame = NULL; sim->onRead = NULL; sim->onWrite = NULL; vbReset(sim); return sim; } /* Read a value from the memory bus */ VBAPI int32_t vbRead(VB *sim, uint32_t address, int type) { int32_t value; if (type < 0 || type > 4) return 0; busRead(sim, address, type, &value); return value; } /* Simulate a hardware reset */ 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; /* Components */ cpuReset(sim); vipReset(sim); padReset(sim); tmrReset(sim); return sim; } /* Specify a game pak RAM buffer */ VBAPI int vbSetCartRAM(VB *sim, void *sram, uint32_t size) { if (sram != NULL) { if (size < 16 || size > 0x1000000 || (size & (size - 1)) != 0) return 1; sim->cart.ramMask = size - 1; } sim->cart.ram = sram; return 0; } /* Specify a game pak ROM buffer */ VBAPI int vbSetCartROM(VB *sim, void *rom, uint32_t size) { if (rom != NULL) { if (size < 16 || size > 0x1000000 || (size & (size - 1)) != 0) return 1; sim->cart.romMask = size - 1; } sim->cart.rom = rom; return 0; } /* Specify a new exception callback handler */ VBAPI vbOnException vbSetExceptionCallback(VB *sim, vbOnException callback) { vbOnException prev = sim->onException; sim->onException = callback; return prev; } /* Specify a new execute callback handler */ VBAPI vbOnExecute vbSetExecuteCallback(VB *sim, vbOnExecute callback) { vbOnExecute prev = sim->onExecute; sim->onExecute = callback; return prev; } /* Specify a new fetch callback handler */ VBAPI vbOnFetch vbSetFetchCallback(VB *sim, vbOnFetch callback) { vbOnFetch prev = sim->onFetch; sim->onFetch = callback; return prev; } /* Specify a new frame callback handler */ VBAPI vbOnFrame vbSetFrameCallback(VB *sim, vbOnFrame callback) { vbOnFrame prev = sim->onFrame; sim->onFrame = 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; sim->cpu.pc = sim->cpu.nextPC = value & 0xFFFFFFFE; sim->cpu.step = 0; return sim->cpu.pc; } /* Specify a new value for a program register */ VBAPI int32_t vbSetProgramRegister(VB *sim, int index, int32_t value) { return index < 1 || index > 31 ? 0 : (sim->cpu.program[index] = value); } /* Specify a new read callback handler */ VBAPI vbOnRead vbSetReadCallback(VB *sim, vbOnRead callback) { vbOnRead prev = sim->onRead; sim->onRead = callback; return prev; } /* Specify a new value for a system register */ VBAPI uint32_t vbSetSystemRegister(VB *sim, int index, uint32_t value) { return index < 0 || index > 31 ? 0 : cpuSetSystemRegister(sim, index, value, 1); } /* Specify a new write callback handler */ VBAPI vbOnWrite vbSetWriteCallback(VB *sim, vbOnWrite callback) { vbOnWrite prev = sim->onWrite; sim->onWrite = callback; return prev; } /* Determine the size of a simulation instance */ VBAPI size_t vbSizeOf() { return sizeof (VB); } /* Specify a simulation's userdata pointer */ VBAPI void* vbSetUserData(VB *sim, void *tag) { void *prev = sim->tag; sim->tag = tag; return prev; } /* Write a value to the memory bus */ VBAPI int32_t vbWrite(VB *sim, uint32_t address, int type, int32_t value) { if (type < 0 || type > 4) return 0; busWrite(sim, address, type, value, 1); return vbRead(sim, address, type); }