392 lines
11 KiB
C
392 lines
11 KiB
C
|
#ifndef VBAPI
|
||
|
#define VBAPI
|
||
|
#endif
|
||
|
#include <float.h>
|
||
|
#include <vb.h>
|
||
|
|
||
|
|
||
|
|
||
|
/*********************************** Types ***********************************/
|
||
|
|
||
|
/* 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;
|
||
|
|
||
|
/* Other system state */
|
||
|
uint8_t wram[0x10000]; /* System RAM */
|
||
|
|
||
|
/* Application data */
|
||
|
vbOnException onException; /* CPU exception */
|
||
|
vbOnExecute onExecute; /* CPU instruction execute */
|
||
|
vbOnFetch onFetch; /* CPU instruction fetch */
|
||
|
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 "bus.c"
|
||
|
#include "cpu.c"
|
||
|
|
||
|
|
||
|
|
||
|
/***************************** Library Functions *****************************/
|
||
|
|
||
|
/* Process a simulation for a given number of clocks */
|
||
|
static int sysEmulate(VB *sim, uint32_t clocks) {
|
||
|
return
|
||
|
cpuEmulate(sim, clocks)
|
||
|
;
|
||
|
}
|
||
|
|
||
|
/* Determine how many clocks are guaranteed to process */
|
||
|
static uint32_t sysUntil(VB *sim, uint32_t clocks) {
|
||
|
clocks = cpuUntil(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 handle */
|
||
|
VBAPI vbOnException vbGetExceptionCallback(VB *sim) {
|
||
|
return sim->onException;
|
||
|
}
|
||
|
|
||
|
/* Retrieve the execute callback handle */
|
||
|
VBAPI vbOnExecute vbGetExecuteCallback(VB *sim) {
|
||
|
return sim->onExecute;
|
||
|
}
|
||
|
|
||
|
/* Retrieve the fetch callback handle */
|
||
|
VBAPI vbOnFetch vbGetFetchCallback(VB *sim) {
|
||
|
return sim->onFetch;
|
||
|
}
|
||
|
|
||
|
/* 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 handle */
|
||
|
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 handle */
|
||
|
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->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) {
|
||
|
uint32_t x; /* Iterator */
|
||
|
|
||
|
/* WRAM (the hardware does not do this) */
|
||
|
for (x = 0; x < 0x10000; x++)
|
||
|
sim->wram[x] = 0x00;
|
||
|
|
||
|
/* CPU (normal) */
|
||
|
sim->cpu.exception = 0;
|
||
|
sim->cpu.halt = 0;
|
||
|
sim->cpu.irq = 0;
|
||
|
sim->cpu.pc = 0xFFFFFFF0;
|
||
|
cpuSetSystemRegister(sim, VB_ECR, 0x0000FFF0, 1);
|
||
|
cpuSetSystemRegister(sim, VB_PSW, 0x00008000, 1);
|
||
|
|
||
|
/* CPU (extra, hardware does not do this) */
|
||
|
sim->cpu.adtre = 0x00000000;
|
||
|
sim->cpu.eipc = 0x00000000;
|
||
|
sim->cpu.eipsw = 0x00000000;
|
||
|
sim->cpu.fepc = 0x00000000;
|
||
|
sim->cpu.fepsw = 0x00000000;
|
||
|
sim->cpu.sr29 = 0x00000000;
|
||
|
sim->cpu.sr31 = 0x00000000;
|
||
|
cpuSetSystemRegister(sim, VB_CHCW, 0x00000000, 1);
|
||
|
for (x = 0; x < 32; x++)
|
||
|
sim->cpu.program[x] = 0x00000000;
|
||
|
|
||
|
/* CPU (other) */
|
||
|
sim->cpu.clocks = 0;
|
||
|
sim->cpu.nextPC = 0xFFFFFFF0;
|
||
|
sim->cpu.operation = CPU_FETCH;
|
||
|
sim->cpu.step = 0;
|
||
|
|
||
|
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 handle */
|
||
|
VBAPI vbOnException vbSetExceptionCallback(VB *sim, vbOnException callback) {
|
||
|
vbOnException prev = sim->onException;
|
||
|
sim->onException = callback;
|
||
|
return prev;
|
||
|
}
|
||
|
|
||
|
/* Specify a new execute callback handle */
|
||
|
VBAPI vbOnExecute vbSetExecuteCallback(VB *sim, vbOnExecute callback) {
|
||
|
vbOnExecute prev = sim->onExecute;
|
||
|
sim->onExecute = callback;
|
||
|
return prev;
|
||
|
}
|
||
|
|
||
|
/* Specify a new fetch callback handle */
|
||
|
VBAPI vbOnFetch vbSetFetchCallback(VB *sim, vbOnFetch callback) {
|
||
|
vbOnFetch prev = sim->onFetch;
|
||
|
sim->onFetch = callback;
|
||
|
return prev;
|
||
|
}
|
||
|
|
||
|
/* 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 handle */
|
||
|
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 handle */
|
||
|
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);
|
||
|
}
|