shrooms-vb-core/core/vb.c

491 lines
15 KiB
C
Raw Normal View History

2024-10-14 20:07:00 +00:00
#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;
2024-10-15 19:11:29 +00:00
/* VIP */
struct {
/* CTA */
struct {
uint8_t cta_l; /* Left column table index */
uint8_t cta_r; /* Right column table index */
} cta;
2024-10-16 21:15:39 +00:00
/* Display processor */
2024-10-15 19:11:29 +00:00
struct {
2024-10-16 21:15:39 +00:00
/* 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 */
2024-10-16 23:19:31 +00:00
uint32_t until; /* Clocks until interrupt condition */
2024-10-16 21:15:39 +00:00
} dp;
/* Pixel processor */
2024-10-15 19:11:29 +00:00
struct {
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 */
uint8_t sbout; /* Drawing specified vertical output position */
uint8_t xpen; /* Drawing enabled */
2024-10-16 21:15:39 +00:00
} xp;
2024-10-15 19:11:29 +00:00
/* 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 */
2024-10-16 21:15:39 +00:00
/* Output frame buffers [buffer index][0=left, 1=right][pixel index] */
uint8_t frames[2][2][384 * 224];
2024-10-15 19:11:29 +00:00
/* Other state */
2024-10-16 21:15:39 +00:00
uint8_t ram[0x40000]; /* Video memory */
2024-10-15 19:11:29 +00:00
} vip;
/* Other state */
2024-10-14 20:07:00 +00:00
uint8_t wram[0x10000]; /* System RAM */
/* Application data */
vbOnException onException; /* CPU exception */
vbOnExecute onExecute; /* CPU instruction execute */
vbOnFetch onFetch; /* CPU instruction fetch */
2024-10-16 21:15:39 +00:00
vbOnFrame onFrame; /* VIP frame ready */
2024-10-14 20:07:00 +00:00
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"
2024-10-15 19:11:29 +00:00
#include "vip.c"
2024-10-14 20:07:00 +00:00
/***************************** Library Functions *****************************/
/* Process a simulation for a given number of clocks */
static int sysEmulate(VB *sim, uint32_t clocks) {
return
2024-10-15 19:11:29 +00:00
cpuEmulate(sim, clocks) |
vipEmulate(sim, clocks)
2024-10-14 20:07:00 +00:00
;
}
/* Determine how many clocks are guaranteed to process */
static uint32_t sysUntil(VB *sim, uint32_t clocks) {
clocks = cpuUntil(sim, clocks);
2024-10-15 19:11:29 +00:00
clocks = vipUntil(sim, clocks);
2024-10-14 20:07:00 +00:00
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;
}
2024-10-16 21:15:39 +00:00
/* Retrieve the exception callback handler */
2024-10-14 20:07:00 +00:00
VBAPI vbOnException vbGetExceptionCallback(VB *sim) {
return sim->onException;
}
2024-10-16 21:15:39 +00:00
/* Retrieve the execute callback handler */
2024-10-14 20:07:00 +00:00
VBAPI vbOnExecute vbGetExecuteCallback(VB *sim) {
return sim->onExecute;
}
2024-10-16 21:15:39 +00:00
/* Retrieve the fetch callback handler */
2024-10-14 20:07:00 +00:00
VBAPI vbOnFetch vbGetFetchCallback(VB *sim) {
return sim->onFetch;
}
2024-10-16 21:15:39 +00:00
/* Retrieve the frame callback handler */
VBAPI vbOnFrame vbGetFrameCallback(VB *sim) {
return sim->onFrame;
}
2024-10-16 21:53:33 +00:00
/* 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.frames[sim->vip.dp.buffer][i];
for (y = 0; y < 224; y++, dest += yStride)
for (x = offset = 0; x < 384; x++, offset += xStride)
dest[offset] = *src++;
}
}
2024-10-14 20:07:00 +00:00
/* 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];
}
2024-10-16 21:15:39 +00:00
/* Retrieve the read callback handler */
2024-10-14 20:07:00 +00:00
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;
}
2024-10-16 21:15:39 +00:00
/* Retrieve the write callback handler */
2024-10-14 20:07:00 +00:00
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;
2024-10-16 21:15:39 +00:00
sim->onFrame = NULL;
2024-10-14 20:07:00 +00:00
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) {
2024-10-15 19:11:29 +00:00
int x; /* Iterator */
2024-10-14 20:07:00 +00:00
/* WRAM (the hardware does not do this) */
for (x = 0; x < 0x10000; x++)
sim->wram[x] = 0x00;
2024-10-15 19:11:29 +00:00
/* Components */
cpuReset(sim);
vipReset(sim);
2024-10-14 20:07:00 +00:00
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;
}
2024-10-16 21:15:39 +00:00
/* Specify a new exception callback handler */
2024-10-14 20:07:00 +00:00
VBAPI vbOnException vbSetExceptionCallback(VB *sim, vbOnException callback) {
vbOnException prev = sim->onException;
sim->onException = callback;
return prev;
}
2024-10-16 21:15:39 +00:00
/* Specify a new execute callback handler */
2024-10-14 20:07:00 +00:00
VBAPI vbOnExecute vbSetExecuteCallback(VB *sim, vbOnExecute callback) {
vbOnExecute prev = sim->onExecute;
sim->onExecute = callback;
return prev;
}
2024-10-16 21:15:39 +00:00
/* Specify a new fetch callback handler */
2024-10-14 20:07:00 +00:00
VBAPI vbOnFetch vbSetFetchCallback(VB *sim, vbOnFetch callback) {
vbOnFetch prev = sim->onFetch;
sim->onFetch = callback;
return prev;
}
2024-10-16 21:15:39 +00:00
/* Specify a new frame callback handler */
VBAPI vbOnFrame vbSetFrameCallback(VB *sim, vbOnFrame callback) {
vbOnFrame prev = sim->onFrame;
sim->onFrame = callback;
return prev;
}
2024-10-14 20:07:00 +00:00
/* 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);
}
2024-10-16 21:15:39 +00:00
/* Specify a new read callback handler */
2024-10-14 20:07:00 +00:00
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);
}
2024-10-16 21:15:39 +00:00
/* Specify a new write callback handler */
2024-10-14 20:07:00 +00:00
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);
}