Split core into its own repository

This commit is contained in:
Guy Perfect 2024-10-14 15:07:00 -05:00
commit 767675eb42
6 changed files with 2552 additions and 0 deletions

12
.gitattributes vendored Normal file
View File

@ -0,0 +1,12 @@
*.c text eol=lf diff=c
*.css text eol=lf diff=css
*.h text eol=lf diff=c
*.html text eol=lf diff=html
*.java text eol=lf diff=java
*.js text eol=lf diff=js
*.txt text eol=lf
*.class binary
*.dll binary
*.wasm binary
*.woff2 binary

156
core/bus.c Normal file
View File

@ -0,0 +1,156 @@
/* This file is included into vb.c and cannot be compiled on its own. */
#ifdef VBAPI
/********************************* Constants *********************************/
/* Memory access address masks by data type */
static const uint32_t TYPE_MASKS[] = {
0x07FFFFFF, /* S8 */
0x07FFFFFF, /* U8 */
0x07FFFFFE, /* S16 */
0x07FFFFFE, /* U16 */
0x07FFFFFC /* S32 */
};
/*************************** Sub-Module Functions ****************************/
/* Read a typed value from a buffer in host memory */
static int32_t busReadBuffer(uint8_t *data, int type) {
/* Processing by data type */
switch (type) {
/* Generic implementation */
#ifndef VB_LITTLE_ENDIAN
case VB_S8 : return ((int8_t *)data)[0];
case VB_U8 : return data [0];
case VB_S16: return (int32_t) ((int8_t *)data)[1] << 8 | data[0];
case VB_U16: return (int32_t) data [1] << 8 | data[0];
case VB_S32: return
(int32_t) data[3] << 24 | (int32_t) data[2] << 16 |
(int32_t) data[1] << 8 | data[0];
/* Little-endian host */
#else
case VB_S8 : return *(int8_t *) data;
case VB_U8 : return * data;
case VB_S16: return *(int16_t *) data;
case VB_U16: return *(uint16_t *) data;
case VB_S32: return *(int32_t *) data;
#endif
}
return 0; /* Unreachable */
}
/* Write a typed value to a buffer in host memory */
static void busWriteBuffer(uint8_t *data, int type, int32_t value) {
/* Processing by data type */
switch (type) {
/* Generic implementation */
#ifndef VB_LITTLE_ENDIAN
case VB_S32: data[3] = value >> 24;
data[2] = value >> 16; /* Fallthrough */
case VB_S16: /* Fallthrough */
case VB_U16: data[1] = value >> 8; /* Fallthrough */
case VB_S8 : /* Fallthrough */
case VB_U8 : data[0] = value;
/* Little-endian host */
#else
case VB_S8 : /* Fallthrough */
case VB_U8 : * data = value; return;
case VB_S16: /* Fallthrough */
case VB_U16: *(uint16_t *) data = value; return;
case VB_S32: *(int32_t *) data = value; return;
#endif
}
}
/***************************** Library Functions *****************************/
/* Read a typed value from the simulation bus */
static void busRead(VB *sim, uint32_t address, int type, int32_t *value) {
/* Working variables */
address &= TYPE_MASKS[type];
*value = 0;
/* Process by address range */
switch (address >> 24) {
case 0: break; /* VIP */
case 1: break; /* VSU */
case 2: break; /* Misc. I/O */
case 3: break; /* Unmapped */
case 4: break; /* Game Pak expansion */
case 5: /* WRAM */
*value = busReadBuffer(&sim->wram[address & 0x0000FFFF], type);
break;
case 6: /* Game Pak RAM */
if (sim->cart.ram != NULL) {
*value = busReadBuffer(
&sim->cart.ram[address & sim->cart.ramMask], type);
}
break;
case 7: /* Game Pak ROM */
if (sim->cart.rom != NULL) {
*value = busReadBuffer(
&sim->cart.rom[address & sim->cart.romMask], type);
}
break;
}
}
/* Write a typed value to the simulation bus */
static void busWrite(VB*sim,uint32_t address,int type,int32_t value,int debug){
/* Working variables */
address &= TYPE_MASKS[type];
/* Process by address range */
switch (address >> 24) {
case 0: break; /* VIP */
case 1: break; /* VSU */
case 2: break; /* Misc. I/O */
case 3: break; /* Unmapped */
case 4: break; /* Game Pak expansion */
case 5: /* WRAM */
busWriteBuffer(&sim->wram[address & 0x0000FFFF], type, value);
break;
case 6: /* Game Pak RAM */
if (sim->cart.ram != NULL) {
busWriteBuffer(
&sim->cart.ram[address & sim->cart.ramMask], type, value);
}
break;
case 7: /* Game Pak ROM */
if (debug && sim->cart.rom != NULL) {
busWriteBuffer(
&sim->cart.rom[address & sim->cart.romMask], type, value);
}
break;
}
}
#endif /* VBAPI */

1801
core/cpu.c Normal file

File diff suppressed because it is too large Load Diff

391
core/vb.c Normal file
View File

@ -0,0 +1,391 @@
#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);
}

143
core/vb.h Normal file
View File

@ -0,0 +1,143 @@
#ifndef VB_H_
#define VB_H_
#ifdef __cplusplus
extern "C" {
#endif
#ifndef VBAPI
#define VBAPI extern
#endif
/* Header includes */
#include <stddef.h>
#include <stdint.h>
/**************************** Compilation Control ****************************/
/*
VB_DIRECT_EXCEPTION
VB_DIRECT_EXECUTE
VB_DIRECT_FETCH
VB_DIRECT_READ
VB_DIRECT_WRITE
Implements callbacks as direct function calls with names in the form of
vbxOn<callback>() with the same prototypes as the regular function pointer
callbacks. If left undefined, the function pointers are used.
*/
/*
VB_DIV_GENERIC
Provides a generic implementation for the DIV instruction that guarantees the
correct results for quotient and remainder. If left undefined, an accelerated
implementation is used that depends on the compiler's behavior.
*/
/*
VB_LITTLE_ENDIAN
Accelerates simulated memory reads and writes on hosts with little-endian
byte ordering. If left undefined, a generic implementation is used instead.
*/
/*
VB_SIGNED_PROPAGATE
Accelerates sign extension by assuming the >> operator propagates the sign
bit for signed types and does not for unsigned types. If left undefined, a
generic implementation is used instead.
*/
/********************************* Constants *********************************/
/* Callback IDs */
#define VB_EXCEPTION 0
#define VB_EXECUTE 1
#define VB_FETCH 2
#define VB_FRAME 3
#define VB_READ 4
#define VB_WRITE 5
/* System registers */
#define VB_ADTRE 25
#define VB_CHCW 24
#define VB_ECR 4
#define VB_EIPC 0
#define VB_EIPSW 1
#define VB_FEPC 2
#define VB_FEPSW 3
#define VB_PIR 6
#define VB_PSW 5
#define VB_TKCW 7
/* Memory access data types */
#define VB_S8 0
#define VB_U8 1
#define VB_S16 2
#define VB_U16 3
#define VB_S32 4
/*********************************** Types ***********************************/
/* Simulation state */
typedef struct VB VB;
/* Callbacks */
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 (*vbOnRead )(VB *sim, uint32_t address, int type, int32_t *value, uint32_t *cycles);
typedef int (*vbOnWrite )(VB *sim, uint32_t address, int type, int32_t *value, uint32_t *cycles, int *cancel);
/******************************* API Commands ********************************/
VBAPI int vbEmulate (VB *sim, uint32_t *clocks);
VBAPI int vbEmulateEx (VB **sims, int count, uint32_t *clocks);
VBAPI void* vbGetCallback (VB *sim, int id);
VBAPI void* vbGetCartRAM (VB *sim, uint32_t *size);
VBAPI void* vbGetCartROM (VB *sim, uint32_t *size);
VBAPI vbOnException vbGetExceptionCallback(VB *sim);
VBAPI vbOnExecute vbGetExecuteCallback (VB *sim);
VBAPI vbOnFetch vbGetFetchCallback (VB *sim);
VBAPI uint32_t vbGetProgramCounter (VB *sim);
VBAPI int32_t vbGetProgramRegister (VB *sim, int index);
VBAPI vbOnRead vbGetReadCallback (VB *sim);
VBAPI uint32_t vbGetSystemRegister (VB *sim, int index);
VBAPI void* vbGetUserData (VB *sim);
VBAPI vbOnWrite vbGetWriteCallback (VB *sim);
VBAPI VB* vbInit (VB *sim);
VBAPI int32_t vbRead (VB *sim, uint32_t address, int type);
VBAPI VB* vbReset (VB *sim);
VBAPI int vbSetCartRAM (VB *sim, void *sram, uint32_t size);
VBAPI int vbSetCartROM (VB *sim, void *rom, uint32_t size);
VBAPI vbOnException vbSetExceptionCallback(VB *sim, vbOnException callback);
VBAPI vbOnExecute vbSetExecuteCallback (VB *sim, vbOnExecute callback);
VBAPI vbOnFetch vbSetFetchCallback (VB *sim, vbOnFetch callback);
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);
VBAPI uint32_t vbSetSystemRegister (VB *sim, int index, uint32_t value);
VBAPI void* vbSetUserData (VB *sim, void *tag);
VBAPI vbOnWrite vbSetWriteCallback (VB *sim, vbOnWrite callback);
VBAPI size_t vbSizeOf ();
VBAPI int32_t vbWrite (VB *sim, uint32_t address, int type, int32_t value);
#ifdef __cplusplus
}
#endif
#endif /* VB_H_ */

49
makefile Normal file
View File

@ -0,0 +1,49 @@
.PHONY: help
help:
@echo
@echo "Shrooms Virtual Boy core module - October 14, 2024"
@echo
@echo "Target build environment is any Debian with the following packages:"
@echo " emscripten"
@echo " gcc"
@echo
@echo "Available make targets:"
@echo " clean Remove output files"
@echo " core Check the C core source for compiler warnings"
@echo " wasm Build the WebAssembly module"
@echo
.PHONY: clean
clean:
@rm -f web/core.wasm
.PHONY: core
core:
# GCC generic
@gcc core/vb.c -I core -c -o /dev/null \
-Werror -std=c90 -Wall -Wextra -Wpedantic
# GCC compilation control
@gcc core/vb.c -I core -c -o /dev/null \
-Werror -std=c90 -Wall -Wextra -Wpedantic \
-D VB_LITTLE_ENDIAN -D VB_SIGNED_PROPAGATE -D VB_DIV_GENERIC \
-D VB_DIRECT_EXCEPTION -D VB_DIRECT_EXECUTE -D VB_DIRECT_FETCH \
-D VB_DIRECT_READ -D VB_DIRECT_WRITE -D VB_DIV_GENERIC
# Clang generic
@emcc core/vb.c -I core -c -o /dev/null \
-Werror -std=c90 -Wall -Wextra -Wpedantic
# Clang compilation control
@emcc core/vb.c -I core -c -o /dev/null \
-Werror -std=c90 -Wall -Wextra -Wpedantic \
-D VB_LITTLE_ENDIAN -D VB_SIGNED_PROPAGATE -D VB_DIV_GENERIC \
-D VB_DIRECT_EXCEPTION -D VB_DIRECT_EXECUTE -D VB_DIRECT_FETCH \
-D VB_DIRECT_READ -D VB_DIRECT_WRITE -D VB_DIV_GENERIC
.PHONY: wasm
wasm:
@emcc -o web/core.wasm web/wasm.c core/vb.c -I core \
-D VB_LITTLE_ENDIAN -D VB_SIGNED_PROPAGATE \
-D "VBAPI=__attribute__((used))" \
--no-entry -O2 -flto -s WASM=1 \
-s EXPORTED_RUNTIME_METHODS=[] -s ALLOW_MEMORY_GROWTH \
-s MAXIMUM_MEMORY=4GB -fno-strict-aliasing
@rm -f web/*.wasm.tmp*