Compare commits

...
43 Commits
Author SHA1 Message Date
GuyPerfect 31b7403252 Correction for negative affine parallax 2026-05-21 16:23:23 -05:00
GuyPerfect 29ade46a0a Fix for affine parameter address resolution 2026-03-28 16:46:32 -05:00
GuyPerfect 534ce852f4 Adjust brightness values 2025-11-23 12:08:36 -06:00
GuyPerfect 4ed3b72995 Fix nullary function declarations 2025-08-27 09:53:01 -05:00
GuyPerfect 8598eab087 Fix double CPU clocks in reads/writes, assuage unused argument in vsuWrite() 2025-08-24 11:03:51 -05:00
GuyPerfect fe9c5c4781 Adjustment for VSU hardware behavior on erroneous address writes 2025-08-18 20:16:41 -05:00
GuyPerfect a9a02e2d15 Merging pull request 2025-08-17 13:32:01 -05:00
GuyPerfect ab29f47004 Merge pull request 'Fix issues with H-Bias rendering' (#1) from hbias-fix into main
Reviewed-on: #1
2025-08-17 18:22:53 +00:00
GuyPerfect 531e4a8c3e Fix handler address for FIV exception 2025-08-17 13:21:53 -05:00
GuyPerfect f295e02aaf Style adjustments for previous 2025-08-17 13:11:29 -05:00
GuyPerfect 67928da1e6 Merge pull request 'Add Floogle's latest audio fixes' (#3) from floogle-audio-fixes into main
Reviewed-on: #3
2025-08-17 18:02:04 +00:00
SonicSwordcane 4ebdad0c2d Add Floogle's latest audio fixes 2025-08-15 00:24:07 -04:00
SonicSwordcane 5d4a422e61 Fix handler address for FIV exception 2025-08-15 00:07:04 -04:00
SonicSwordcane 8b020ad808 Fix issues with HBias rendering 2025-08-14 23:46:39 -04:00
GuyPerfect 483714c0d6 Pushing current web edits 2025-03-23 07:26:43 -05:00
GuyPerfect ecbd103917 Manage IRQ on writes to INTENB 2025-03-04 10:19:12 -06:00
GuyPerfect b2412d9487 Fix shameful syntax error 2025-03-01 15:51:49 -06:00
GuyPerfect dd1045553b Even more VSU edge cases 2025-03-01 15:42:16 -06:00
GuyPerfect 459a16076a Expand BRTx to 0..132, fix affine parallax 2025-02-25 09:02:22 -06:00
GuyPerfect 185362e6cd Fix BKCOL debug writes, more VSU edge cases 2025-02-24 13:44:26 -06:00
GuyPerfect c62dd833a3 Include missing ISX decover 2025-02-18 15:13:18 -06:00
GuyPerfect 4f0b889b74 Adjustments for use in the web frontend 2025-02-18 15:10:46 -06:00
GuyPerfect 8ae8980fc8 Implement ISX decoding 2025-02-18 15:09:38 -06:00
GuyPerfect 10505e9e98 Supporting additional VSU edge cases 2025-02-18 15:09:15 -06:00
GuyPerfect 155a3aa678 Adjust GAMESTART logic, consolidate VIP sign-extensions 2025-01-17 09:43:58 -06:00
GuyPerfect 04e79c9151 Fix sign extensions from vipRead() and busReadMisc() 2025-01-15 19:47:44 -06:00
GuyPerfect 57dcd8370a Frame buffer management fixes 2025-01-05 20:54:37 -06:00
GuyPerfect 06849b54ba Fix TRAP restore PC, but better 2024-12-29 09:52:07 -06:00
GuyPerfect eef255b507 Fix TRAP restore PC 2024-12-29 09:22:14 -06:00
GuyPerfect db966c8cb8 Fix memory growth handling 2024-12-26 14:13:13 -06:00
GuyPerfect b4feff41f0 Fix broken timer fix bug fixes 2024-12-26 13:23:56 -06:00
GuyPerfect 4449acada0 Fix timer clock computation 2024-12-26 12:57:44 -06:00
GuyPerfect 799ac9f51a Fix style check error 2024-12-26 12:28:10 -06:00
GuyPerfect b83b49221d Timer accuracy adjustments 2024-12-26 12:23:00 -06:00
GuyPerfect 27e926bd58 Fix byte writes to VIP cells, objects and worlds 2024-12-25 13:48:39 -06:00
GuyPerfect d802d39d8d Prevent non-debug writes to CTA 2024-12-23 09:56:54 -06:00
GuyPerfect 18b2c589e6 Add vbuCodeSize() 2024-11-21 18:50:31 -06:00
GuyPerfect f45636a491 VIP bugfix 2024-11-19 22:22:34 -06:00
GuyPerfect 9ce8c9c778 VIP bugfix 2024-11-19 22:21:06 -06:00
GuyPerfect c90b8137de VIP optimizations 2024-11-19 21:56:44 -06:00
GuyPerfect bc864644f7 Aid VSU performance 2024-11-17 14:57:56 -06:00
GuyPerfect e97b52e944 Cleanup adjustments 2024-11-17 12:31:37 -06:00
GuyPerfect 7e31fbd582 VSU performance tweak, add immediate notation setting 2024-11-11 20:05:14 -06:00
16 changed files with 1437 additions and 542 deletions
+2
View File
@@ -159,6 +159,8 @@ static void busRead(VB *sim, uint32_t address, int type, int32_t *value) {
case 2: /* Misc. I/O */ case 2: /* Misc. I/O */
*value = busReadMisc(sim, address, type); *value = busReadMisc(sim, address, type);
if (type == VB_S8)
*value = (int8_t) *value;
break; break;
case 3: break; /* Unmapped */ case 3: break; /* Unmapped */
+4 -8
View File
@@ -91,11 +91,6 @@
#define CPU_XORBSU 82 #define CPU_XORBSU 82
#define CPU_XORNBSU 83 #define CPU_XORNBSU 83
/* Functional operand types */
#define CPU_LITERAL 0
#define CPU_MEMORY 1
#define CPU_REGISTER 2
/* Bit string operations */ /* Bit string operations */
#define CPU_AND_BS 0 #define CPU_AND_BS 0
#define CPU_ANDN_BS 1 #define CPU_ANDN_BS 1
@@ -239,7 +234,7 @@ static int cpuOnExecute(VB *sim) {
#define VB_ON_READ VB_DIRECT_READ #define VB_ON_READ VB_DIRECT_READ
#endif #endif
static int cpuRead(VB *sim, uint32_t address, int type, int32_t *value) { static int cpuRead(VB *sim, uint32_t address, int type, int32_t *value) {
uint32_t cycles = 4; /* TODO: Research this */ uint32_t cycles = 0; /* TODO: Research this */
/* Retrieve the value from the simulation state directly */ /* Retrieve the value from the simulation state directly */
busRead(sim, address, type, value); busRead(sim, address, type, value);
@@ -294,7 +289,7 @@ static int cpuReadFetch(VB *sim, int fetch, uint32_t address, int32_t *value) {
#endif #endif
static int cpuWrite(VB *sim, uint32_t address, int type, int32_t value) { static int cpuWrite(VB *sim, uint32_t address, int type, int32_t value) {
int cancel = 0; int cancel = 0;
uint32_t cycles = 3; /* TODO: Research this */ uint32_t cycles = 0; /* TODO: Research this */
/* Reset pseudo-halt */ /* Reset pseudo-halt */
if (sim->ph.enabled) if (sim->ph.enabled)
@@ -500,7 +495,7 @@ static int cpuException(VB *sim) {
sim->cpu.eipc = sim->cpu.pc; sim->cpu.eipc = sim->cpu.pc;
sim->cpu.psw.ep = 1; sim->cpu.psw.ep = 1;
sim->cpu.nextPC = 0xFFFF0000 | sim->cpu.nextPC = 0xFFFF0000 |
((cause & 0xFFF0) == 0xFF60 ? 0xFF60 : cause & 0xFFF0); ((cause & 0xFFF0) == 0xFF70 ? 0xFF60 : cause & 0xFFF0);
} }
/* All exceptions */ /* All exceptions */
@@ -1642,6 +1637,7 @@ static void cpuSUBF_S(VB *sim) {
/* TRAP */ /* TRAP */
static void cpuTRAP(VB *sim) { static void cpuTRAP(VB *sim) {
sim->cpu.clocks += cpuClocks(15); sim->cpu.clocks += cpuClocks(15);
sim->cpu.pc += 2;
cpuThrow(sim, 0xFFA0 + cpuGetImm5U(sim)); cpuThrow(sim, 0xFFA0 + cpuGetImm5U(sim));
} }
+41 -14
View File
@@ -5,6 +5,15 @@
/***************************** Module Functions ******************************/ /***************************** Module Functions ******************************/
/* Compute clocks until the next decrement to zero */
static uint32_t tmrGetUntil(VB *sim) {
uint32_t fullTick = sim->tmr.t_clk_sel ? 400 : 2000;
uint32_t thisTick = sim->tmr.clocks +
(sim->tmr.t_clk_sel ? 0 : 400 * (4 - sim->tmr.tick20));
return thisTick + fullTick *
(sim->tmr.counter == 0 ? sim->tmr.reload : sim->tmr.counter - 1);
}
/* Update the counter to a new value */ /* Update the counter to a new value */
static void tmrUpdate(VB *sim, uint16_t value) { static void tmrUpdate(VB *sim, uint16_t value) {
if (value == 0 && sim->tmr.counter != 0) { if (value == 0 && sim->tmr.counter != 0) {
@@ -23,10 +32,6 @@ static void tmrUpdate(VB *sim, uint16_t value) {
/* Process component */ /* Process component */
static void tmrEmulate(VB *sim, uint32_t clocks) { static void tmrEmulate(VB *sim, uint32_t clocks) {
/* Timer is disabled */
if (!sim->tmr.t_enb)
return;
/* Process all clocks */ /* Process all clocks */
for (;;) { for (;;) {
@@ -38,15 +43,22 @@ static void tmrEmulate(VB *sim, uint32_t clocks) {
} }
/* Advance forward the component's number of clocks */ /* Advance forward the component's number of clocks */
clocks -= sim->tmr.clocks; clocks -= sim->tmr.clocks;
sim->tmr.until -= sim->tmr.clocks; sim->tmr.until -= sim->tmr.clocks;
sim->tmr.clocks = sim->tmr.t_clk_sel ? 400 : 2000; sim->tmr.clocks = 400;
sim->tmr.tick20 += sim->tmr.tick20 == 4 ? -4 : 1;
/* Do not decrement counter */
if (
!sim->tmr.t_enb ||
(!sim->tmr.t_clk_sel && sim->tmr.tick20 != 0)
) continue;
/* Advance to the next counter value */ /* Advance to the next counter value */
tmrUpdate(sim, sim->tmr.counter == 0 ? tmrUpdate(sim, sim->tmr.counter == 0 ?
sim->tmr.reload : sim->tmr.counter - 1); sim->tmr.reload : sim->tmr.counter - 1);
if (sim->tmr.counter == 0) if (sim->tmr.counter == 0)
sim->tmr.until = sim->tmr.clocks * ((uint32_t)sim->tmr.reload + 1); sim->tmr.until = tmrGetUntil(sim);
} }
} }
@@ -71,8 +83,10 @@ static void tmrReset(VB *sim) {
sim->tmr.z_stat = 0; sim->tmr.z_stat = 0;
/* Other */ /* Other */
sim->tmr.clocks = 400;
sim->tmr.counter = 0xFFFF; sim->tmr.counter = 0xFFFF;
sim->tmr.reload = 0x0000; sim->tmr.reload = 0x0000;
sim->tmr.tick20 = 0;
} }
/* Determine how many clocks are guaranteed to process */ /* Determine how many clocks are guaranteed to process */
@@ -88,14 +102,27 @@ static void tmrWriteControl(VB *sim, uint8_t value) {
if ( if (
(value & 0x04) && /* Z-Stat-Clr */ (value & 0x04) && /* Z-Stat-Clr */
( (
sim->tmr.counter != 0 || !sim->tmr.t_enb ||
!sim->tmr.t_enb (
sim->tmr.counter != 0 &&
!(value & 0x01) /* T-Enb */
)
) )
) { ) {
sim->tmr.z_stat = sim->tmr.counter != 0; sim->tmr.z_stat = sim->tmr.counter != 0;
sim->cpu.irq &= ~0x0002; sim->cpu.irq &= ~0x0002;
} }
/* Hardware bug: decrement on switch to 20us mode */
if (
!sim->tmr.t_clk_sel &&
(value & 0x10) && /* T-Clk-Sel */
sim->tmr.tick20 != 0
) {
tmrUpdate(sim, sim->tmr.counter == 0 ?
sim->tmr.reload : sim->tmr.counter - 1);
}
/* Parse fields */ /* Parse fields */
sim->tmr.t_clk_sel = value >> 4 & 1; sim->tmr.t_clk_sel = value >> 4 & 1;
sim->tmr.tim_z_int = value >> 3 & 1; sim->tmr.tim_z_int = value >> 3 & 1;
@@ -105,10 +132,8 @@ static void tmrWriteControl(VB *sim, uint8_t value) {
if (!sim->tmr.tim_z_int) if (!sim->tmr.tim_z_int)
sim->cpu.irq &= ~0x0002; sim->cpu.irq &= ~0x0002;
/* Configure countdowns */ /* Configure state */
sim->tmr.clocks = sim->tmr.t_clk_sel ? 400 : 2000; sim->tmr.until = tmrGetUntil(sim);
sim->tmr.until = sim->tmr.clocks * (sim->tmr.counter == 0 ?
(uint32_t) sim->tmr.reload + 1 : sim->tmr.counter);
/* TODO: Will Z-Stat raise an interrupt when Tim-Z-Int is set? */ /* TODO: Will Z-Stat raise an interrupt when Tim-Z-Int is set? */
} }
@@ -117,12 +142,14 @@ static void tmrWriteControl(VB *sim, uint8_t value) {
static void tmrWriteHigh(VB *sim, uint8_t value) { static void tmrWriteHigh(VB *sim, uint8_t value) {
sim->tmr.reload = (uint16_t) value << 8 | (sim->tmr.reload & 0x00FF); sim->tmr.reload = (uint16_t) value << 8 | (sim->tmr.reload & 0x00FF);
tmrUpdate(sim, sim->tmr.reload); tmrUpdate(sim, sim->tmr.reload);
sim->tmr.until = tmrGetUntil(sim);
} }
/* Write to the low data register */ /* Write to the low data register */
static void tmrWriteLow(VB *sim, uint8_t value) { static void tmrWriteLow(VB *sim, uint8_t value) {
sim->tmr.reload = (sim->tmr.reload & 0xFF00) | value; sim->tmr.reload = (sim->tmr.reload & 0xFF00) | value;
tmrUpdate(sim, sim->tmr.reload); tmrUpdate(sim, sim->tmr.reload);
sim->tmr.until = tmrGetUntil(sim);
} }
+86 -20
View File
@@ -8,8 +8,11 @@
/*********************************** Types ***********************************/ /*********************************** Types ***********************************/
/* Output image */ /* VIP BG map cell */
typedef uint8_t Pixels[2][384*224]; typedef struct {
uint8_t *palette; /* Points to a VB.vip.gplt */
uint8_t *pixels; /* Points to a VB.vip.characters */
} Cell;
/* VSU channel */ /* VSU channel */
typedef struct { typedef struct {
@@ -25,8 +28,9 @@ typedef struct {
uint8_t value; /* Master output level */ uint8_t value; /* Master output level */
/* Other state */ /* Other state */
uint32_t clocks; /* Clocks until modification */ uint32_t clocks; /* Clocks until modification */
uint8_t reload; /* Automatic reload value */ uint8_t modmask; /* Modifications masked */
uint8_t reload; /* Automatic reload value */
} env; } env;
/* Frequency */ /* Frequency */
@@ -64,9 +68,58 @@ typedef struct {
} wave; } wave;
/* Other state */ /* Other state */
uint32_t clocks; /* Clocks until next sample */ uint32_t clocks; /* Clocks until next wave or noise sample */
uint8_t freqmod; /* Frequency modifications are active */
uint32_t until; /* Clocks until channel component update */
} Channel; } Channel;
/* VIP character (hflp << 1 | vflp) */
typedef uint8_t Character[4][64];
/* VIP object */
typedef struct {
Cell cell; /* BG map-like attributes */
int16_t jp; /* Parallax */
int16_t jx; /* Screen base X */
int16_t jy; /* Screen Y */
uint8_t lron; /* Visible in left and right images */
} Object;
/* Output image */
typedef uint8_t Pixels[2][384*224];
/* VIP world */
typedef struct {
/* Attributes */
uint8_t bgm; /* Mode */
uint8_t end; /* Control world */
int16_t gp; /* World parallax */
int16_t gx; /* World base X */
int16_t gy; /* World Y */
int16_t mx; /* Scroll base X */
int16_t mp; /* Scroll parallax */
int16_t my; /* Scroll Y */
uint8_t over; /* Use overplane */
Cell *overplane; /* Overplane cell */
/* Non-attributes */
Cell *bg[64]; /* Background arrangement */
int32_t bgHeight; /* Height of background in pixels */
uint32_t bgMaskX; /* Horizontal background mask */
uint32_t bgMaskY; /* Vertical background mask */
uint32_t bgShift; /* Bits to shift when selecting a BG map */
int32_t bgWidth; /* Width of background in pixels */
uint32_t hAffine; /* H for affine mode */
uint32_t hNaffine; /* H for not affine mode */
uint32_t height; /* Height of world in pixels */
uint8_t lron; /* Visible in left and right images */
uint32_t paramBase; /* H-bias and affine parameter address */
uint32_t wAffine; /* W for affine mode */
uint32_t wNaffine; /* W for not affine mode */
uint32_t width; /* Width of world in pixels */
} World;
/* Simulation state */ /* Simulation state */
struct VB { struct VB {
@@ -199,6 +252,7 @@ struct VB {
uint32_t clocks; /* Master clocks to wait */ uint32_t clocks; /* Master clocks to wait */
uint16_t counter; /* Current counter value */ uint16_t counter; /* Current counter value */
uint16_t reload; /* Reload counter value */ uint16_t reload; /* Reload counter value */
uint8_t tick20; /* Current 20-microsecond tick */
uint32_t until; /* Clocks until interrupt condition */ uint32_t until; /* Clocks until interrupt condition */
} tmr; } tmr;
@@ -259,6 +313,7 @@ struct VB {
int frame; /* FRMCYC counter */ int frame; /* FRMCYC counter */
int32_t halfword; /* Current output halfword offset */ int32_t halfword; /* Current output halfword offset */
int step; /* Processing phase */ int step; /* Processing phase */
uint8_t toggle; /* Switch frame buffers next frame */
uint32_t until; /* Clocks until interrupt condition */ uint32_t until; /* Clocks until interrupt condition */
} xp; } xp;
@@ -273,12 +328,17 @@ struct VB {
uint16_t spt[4]; /* Object control */ uint16_t spt[4]; /* Object control */
/* Rendering shadow memory */ /* Rendering shadow memory */
uint8_t buffer; /* Output buffer */
uint16_t halfwords[384*28]; /* Output timing by 1x8 halfword */ uint16_t halfwords[384*28]; /* Output timing by 1x8 halfword */
Pixels output[2]; /* Output images, row-major */ Pixels output[2]; /* Output images, row-major */
Pixels shadow; /* Drawing shadow image, column-major */ Pixels shadow; /* Drawing shadow image, column-major */
/* Other state */ /* Other state */
uint8_t ram[0x40000]; /* Video memory */ Cell cells[0x10000]; /* Pre-computed BG map attributes */
Character characters[2048]; /* Pre-computed character pixels */
Object objects[1024]; /* Pre-computed object attributes */
uint8_t ram[0x40000]; /* Video memory */
World worlds[32]; /* Pre-computed world attributes */
} vip; } vip;
/* VSU */ /* VSU */
@@ -300,9 +360,11 @@ struct VB {
uint8_t shift; /* Sweep shift amount */ uint8_t shift; /* Sweep shift amount */
/* Other state */ /* Other state */
uint32_t clocks; /* Clocks until modification */ uint32_t clocks; /* Clocks until modification */
uint16_t next; /* Next frequency value */ uint8_t freqmask; /* Frequency mask */
int sample; /* Current sample index */ uint8_t modmask; /* Modifications masked */
uint16_t next; /* Next frequency value */
int sample; /* Current sample index */
} freqmod; } freqmod;
/* Channel 6 noise generator */ /* Channel 6 noise generator */
@@ -575,7 +637,7 @@ VBAPI void vbGetPixels(VB *sim, void *left, int leftStrideX, int leftStrideY,
continue; continue;
/* Transfer pixels to the destination */ /* Transfer pixels to the destination */
src = sim->vip.output[sim->vip.dp.buffer ^ 1][i]; src = sim->vip.output[sim->vip.buffer][i];
for (y = 0; y < 224; y++, dest += yStride) for (y = 0; y < 224; y++, dest += yStride)
for (x = offset = 0; x < 384; x++, offset += xStride) for (x = offset = 0; x < 384; x++, offset += xStride)
dest[offset] = *src++; dest[offset] = *src++;
@@ -643,6 +705,7 @@ VBAPI vbOnWrite vbGetWriteCallback(VB *sim) {
VBAPI VB* vbInit(VB *sim) { VBAPI VB* vbInit(VB *sim) {
sim->cart.ram = NULL; sim->cart.ram = NULL;
sim->cart.rom = NULL; sim->cart.rom = NULL;
sim->ph.enabled = 0;
sim->vsu.out.samples = NULL; sim->vsu.out.samples = NULL;
sim->onExecute = NULL; sim->onExecute = NULL;
sim->onFetch = NULL; sim->onFetch = NULL;
@@ -652,7 +715,7 @@ VBAPI VB* vbInit(VB *sim) {
sim->onSamples = NULL; sim->onSamples = NULL;
sim->onWrite = NULL; sim->onWrite = NULL;
sim->peer = NULL; sim->peer = NULL;
sim->ph.enabled = 0; sim->tag = NULL;
vbReset(sim); vbReset(sim);
return sim; return sim;
} }
@@ -716,28 +779,28 @@ VBAPI int vbSetCartROM(VB *sim, void *rom, uint32_t size) {
/* Specify a new exception callback handler */ /* Specify a new exception callback handler */
VBAPI vbOnException vbSetExceptionCallback(VB *sim, vbOnException callback) { VBAPI vbOnException vbSetExceptionCallback(VB *sim, vbOnException callback) {
vbOnException prev = sim->onException; vbOnException prev = sim->onException;
sim->onException = callback; sim->onException = callback;
return prev; return prev;
} }
/* Specify a new execute callback handler */ /* Specify a new execute callback handler */
VBAPI vbOnExecute vbSetExecuteCallback(VB *sim, vbOnExecute callback) { VBAPI vbOnExecute vbSetExecuteCallback(VB *sim, vbOnExecute callback) {
vbOnExecute prev = sim->onExecute; vbOnExecute prev = sim->onExecute;
sim->onExecute = callback; sim->onExecute = callback;
return prev; return prev;
} }
/* Specify a new fetch callback handler */ /* Specify a new fetch callback handler */
VBAPI vbOnFetch vbSetFetchCallback(VB *sim, vbOnFetch callback) { VBAPI vbOnFetch vbSetFetchCallback(VB *sim, vbOnFetch callback) {
vbOnFetch prev = sim->onFetch; vbOnFetch prev = sim->onFetch;
sim->onFetch = callback; sim->onFetch = callback;
return prev; return prev;
} }
/* Specify a new frame callback handler */ /* Specify a new frame callback handler */
VBAPI vbOnFrame vbSetFrameCallback(VB *sim, vbOnFrame callback) { VBAPI vbOnFrame vbSetFrameCallback(VB *sim, vbOnFrame callback) {
vbOnFrame prev = sim->onFrame; vbOnFrame prev = sim->onFrame;
sim->onFrame = callback; sim->onFrame = callback;
return prev; return prev;
} }
@@ -749,7 +812,7 @@ VBAPI uint16_t vbSetKeys(VB *sim, uint16_t keys) {
/* Specify a new link callback handler */ /* Specify a new link callback handler */
VBAPI vbOnLink vbSetLinkCallback(VB *sim, vbOnLink callback) { VBAPI vbOnLink vbSetLinkCallback(VB *sim, vbOnLink callback) {
vbOnLink prev = sim->onLink; vbOnLink prev = sim->onLink;
sim->onLink = callback; sim->onLink = callback;
return prev; return prev;
} }
@@ -774,6 +837,8 @@ VBAPI int vbSetOption(VB *sim, int key, int value) {
VBAPI void vbSetPeer(VB *sim, VB *peer) { VBAPI void vbSetPeer(VB *sim, VB *peer) {
if (sim->peer == peer) if (sim->peer == peer)
return; return;
if (sim->peer != NULL)
sim->peer->peer = NULL;
sim->peer = peer; sim->peer = peer;
if (peer == NULL) if (peer == NULL)
return; return;
@@ -784,6 +849,7 @@ VBAPI void vbSetPeer(VB *sim, VB *peer) {
/* Specify a new value for the program counter */ /* Specify a new value for the program counter */
VBAPI uint32_t vbSetProgramCounter(VB *sim, uint32_t value) { VBAPI uint32_t vbSetProgramCounter(VB *sim, uint32_t value) {
sim->cpu.clocks = 0;
sim->cpu.operation = CPU_FETCH; sim->cpu.operation = CPU_FETCH;
sim->cpu.pc = sim->cpu.nextPC = value & 0xFFFFFFFE; sim->cpu.pc = sim->cpu.nextPC = value & 0xFFFFFFFE;
sim->cpu.step = 0; sim->cpu.step = 0;
@@ -798,7 +864,7 @@ VBAPI int32_t vbSetProgramRegister(VB *sim, unsigned index, int32_t value) {
/* Specify a new read callback handler */ /* Specify a new read callback handler */
VBAPI vbOnRead vbSetReadCallback(VB *sim, vbOnRead callback) { VBAPI vbOnRead vbSetReadCallback(VB *sim, vbOnRead callback) {
vbOnRead prev = sim->onRead; vbOnRead prev = sim->onRead;
sim->onRead = callback; sim->onRead = callback;
return prev; return prev;
} }
@@ -829,19 +895,19 @@ VBAPI uint32_t vbSetSystemRegister(VB *sim, unsigned index, uint32_t value) {
/* Specify a new write callback handler */ /* Specify a new write callback handler */
VBAPI vbOnWrite vbSetWriteCallback(VB *sim, vbOnWrite callback) { VBAPI vbOnWrite vbSetWriteCallback(VB *sim, vbOnWrite callback) {
vbOnWrite prev = sim->onWrite; vbOnWrite prev = sim->onWrite;
sim->onWrite = callback; sim->onWrite = callback;
return prev; return prev;
} }
/* Determine the size of a simulation instance */ /* Determine the size of a simulation instance */
VBAPI size_t vbSizeOf() { VBAPI size_t vbSizeOf(void) {
return sizeof (VB); return sizeof (VB);
} }
/* Specify a simulation's userdata pointer */ /* Specify a simulation's userdata pointer */
VBAPI void* vbSetUserData(VB *sim, void *tag) { VBAPI void* vbSetUserData(VB *sim, void *tag) {
void *prev = sim->tag; void *prev = sim->tag;
sim->tag = tag; sim->tag = tag;
return prev; return prev;
} }
+1 -2
View File
@@ -126,7 +126,6 @@ typedef int (*vbOnWrite )(VB *sim, uint32_t address, int type, int32_t *valu
VBAPI int vbEmulate (VB *sim, uint32_t *clocks); VBAPI int vbEmulate (VB *sim, uint32_t *clocks);
VBAPI int vbEmulateEx (VB **sims, unsigned count, uint32_t *clocks); VBAPI int vbEmulateEx (VB **sims, unsigned count, uint32_t *clocks);
VBAPI void* vbGetCallback (VB *sim, int id);
VBAPI void* vbGetCartRAM (VB *sim, uint32_t *size); VBAPI void* vbGetCartRAM (VB *sim, uint32_t *size);
VBAPI void* vbGetCartROM (VB *sim, uint32_t *size); VBAPI void* vbGetCartROM (VB *sim, uint32_t *size);
VBAPI vbOnException vbGetExceptionCallback(VB *sim); VBAPI vbOnException vbGetExceptionCallback(VB *sim);
@@ -167,7 +166,7 @@ VBAPI vbOnSamples vbSetSamplesCallback (VB *sim, vbOnSamples callback);
VBAPI uint32_t vbSetSystemRegister (VB *sim, unsigned index, uint32_t value); VBAPI uint32_t vbSetSystemRegister (VB *sim, unsigned index, uint32_t value);
VBAPI void* vbSetUserData (VB *sim, void *tag); VBAPI void* vbSetUserData (VB *sim, void *tag);
VBAPI vbOnWrite vbSetWriteCallback (VB *sim, vbOnWrite callback); VBAPI vbOnWrite vbSetWriteCallback (VB *sim, vbOnWrite callback);
VBAPI size_t vbSizeOf (); VBAPI size_t vbSizeOf (void);
VBAPI int32_t vbWrite (VB *sim, uint32_t address, int type, int32_t value); VBAPI int32_t vbWrite (VB *sim, uint32_t address, int type, int32_t value);
+636 -343
View File
File diff suppressed because it is too large Load Diff
+195 -109
View File
@@ -45,9 +45,28 @@ static void vsuOnSamples(VB *sim) {
/***************************** Module Functions ******************************/ /***************************** Module Functions ******************************/
/* Determine while frequency modifications should be processed */
static uint8_t vsuCheckFreqMod(VB *sim) {
return
sim->vsu.freqmod.enb &&
sim->vsu.freqmod.interval != 0 && (
sim->vsu.freqmod.modmask != 2 || (
sim->vsu.freqmod.func == 1 &&
sim->vsu.freqmod.rep
)
)
;
}
/* Stage the next frequency modification value */ /* Stage the next frequency modification value */
static void vsuNextFreqMod(VB *sim, Channel *chan) { static void vsuNextFreqMod(VB *sim, Channel *chan) {
uint16_t next; int32_t next;
/* Modulation mask control */
if (sim->vsu.freqmod.modmask == 1 && sim->vsu.freqmod.func == 1) {
sim->vsu.freqmod.modmask = 2;
chan->freqmod = vsuCheckFreqMod(sim);
}
/* Sweep */ /* Sweep */
if (sim->vsu.freqmod.func == 0) { if (sim->vsu.freqmod.func == 0) {
@@ -57,117 +76,111 @@ static void vsuNextFreqMod(VB *sim, Channel *chan) {
else next = chan->freq.current + next; else next = chan->freq.current + next;
if (next > 2047) if (next > 2047)
chan->int_.enb = 0; chan->int_.enb = 0;
if (next < 0)
next = 0;
} }
/* Modulation */ /* Modulation */
else { else {
next = (chan->freq.written + next = ((int32_t) chan->freq.written +
sim->vsu.modulation[sim->vsu.freqmod.sample]) & 2047; sim->vsu.modulation[sim->vsu.freqmod.sample]) & 0x7FF;
if (sim->vsu.freqmod.sample != 31) if (sim->vsu.freqmod.freqmask == 1)
sim->vsu.freqmod.sample++; next = (next & 0x700) | (chan->freq.written & 0x0FF);
else if (sim->vsu.freqmod.rep) else if (sim->vsu.freqmod.freqmask == 2)
sim->vsu.freqmod.sample = 0; next = (next & 0x0FF) | (chan->freq.written & 0x700);
} }
/* Configure state */ /* Configure state */
sim->vsu.freqmod.next = next; if (chan->freqmod)
sim->vsu.freqmod.next = next;
}
/* Advance the noise channel's shift register */
static void vsuNextNoise(VB *sim) {
uint32_t bit = ((
sim->vsu.noise.register_ >> NOISE_TAPS[sim->vsu.noise.tap] ^
sim->vsu.noise.register_ >> 7
) & 1) ^ 1;
sim->vsu.noise.register_ = bit |
(sim->vsu.noise.register_ << 1 & 0x7FFE);
} }
/* Process one channel */ /* Process one channel */
static void vsuEmulateChannel(VB *sim, int index, uint32_t clocks) { static void vsuEmulateChannel(VB *sim, Channel *chan) {
uint32_t bit; /* Pseudorandom bit */
Channel *chan; /* Channel handle */
int freqmod; /* Frequency modifications enabled */
uint32_t until; /* Clocks to process sub-channel components */
/* Select channel */ /* Automatic shutoff */
chan = &sim->vsu.channels[index]; if (chan->int_.auto_ && chan->int_.clocks == 0) {
chan->int_.enb = 0;
/* Channel is disabled */
if (!chan->int_.enb)
return; return;
}
/* Process all clocks */ /* Next input sample */
do { if (chan->clocks == 0) {
/* Frequency modifications are active */ /* Wave */
freqmod = if (chan != &sim->vsu.channels[5]) {
index == 4 && /* Channel 5 */ chan->clocks = 4 * (2048 - (uint32_t) chan->freq.current);
sim->vsu.freqmod.enb && /* Modifications enabled */ chan->wave.sample = (chan->wave.sample + 1) & 31;
sim->vsu.freqmod.interval != 0 /* Modifications valid */
;
/* Clocks until next state change */
until = clocks;
if (chan->clocks < until)
until = chan->clocks;
if (chan->env.enb && chan->env.clocks < until)
until = chan->env.clocks;
if (chan->int_.auto_ && chan->int_.clocks < until)
until = chan->int_.clocks;
if (freqmod && sim->vsu.freqmod.clocks < until)
until = sim->vsu.freqmod.clocks;
/* Manage clocks */
clocks -= until;
chan->clocks -= until;
if (chan->env.enb)
chan->env.clocks -= until;
if (chan->int_.auto_)
chan->int_.clocks -= until;
if (freqmod)
sim->vsu.freqmod.clocks -= until;
/* Automatic shutoff */
if (chan->int_.auto_ && chan->int_.clocks == 0) {
chan->int_.enb = 0;
return;
} }
/* Next sample */ /* Noise */
if (chan->clocks == 0) { else {
chan->clocks = 40 * (2048 - (uint32_t) chan->freq.current);
/* Wave */ vsuNextNoise(sim);
if (index != 5) {
chan->clocks = 4 * (2048 - (uint32_t) chan->freq.current);
chan->wave.sample = (chan->wave.sample + 1) & 31;
}
/* Noise */
else {
chan->clocks = 40 * (2048 - (uint32_t) chan->freq.current);
bit = ((
sim->vsu.noise.register_ >> NOISE_TAPS[sim->vsu.noise.tap]^
sim->vsu.noise.register_ >> 7
) & 1) ^ 1;
sim->vsu.noise.register_ = bit |
(sim->vsu.noise.register_ << 1 & 0x7FFE);
}
} }
/* Envelope modification */ }
if (chan->env.enb && chan->env.clocks == 0) {
if (chan->env.dir == 0 && chan->env.value != 0)
chan->env.value--;
else if (chan->env.dir == 1 && chan->env.value != 15)
chan->env.value++;
else if (chan->env.rep)
chan->env.value = chan->env.reload;
chan->env.clocks = ((uint32_t) chan->env.interval + 1) * 307220;
}
/* Frequency modification */ /* Envelope modification */
if (freqmod && sim->vsu.freqmod.clocks == 0) { if (chan->env.clocks == 0) {
uint8_t new_envelope = chan->env.value;
if (chan->env.dir == 0 && chan->env.value != 0)
new_envelope--;
else if (chan->env.dir == 1 && chan->env.value != 15)
new_envelope++;
else if (chan->env.rep && chan->env.modmask != 2) {
new_envelope = chan->env.reload;
chan->env.modmask = 0;
} else if (!chan->env.modmask)
chan->env.modmask = 1;
if (chan->env.enb && !chan->env.modmask)
chan->env.value = new_envelope;
chan->env.clocks = ((uint32_t) chan->env.interval + 1) * 307220;
}
/* Frequency modification */
if (chan == &sim->vsu.channels[4] && sim->vsu.freqmod.clocks == 0) {
/* Apply previously computed frequency modification */
if (chan->freqmod)
chan->freq.current = sim->vsu.freqmod.next; chan->freq.current = sim->vsu.freqmod.next;
vsuNextFreqMod(sim, chan);
if (!chan->int_.enb) /* Channel itself is disabled */
return; if (!chan->int_.enb)
return;
/* Frequency modifications are enabled */
if (chan->freqmod) {
/* Not the final modulation sample */
if (sim->vsu.freqmod.sample != 31)
sim->vsu.freqmod.sample++;
/* Last modulation sample processed */
else {
sim->vsu.freqmod.modmask = 1;
if (sim->vsu.freqmod.rep)
sim->vsu.freqmod.sample = 0;
}
/* Schedule next modification */
sim->vsu.freqmod.clocks = (uint32_t) sim->vsu.freqmod.interval * sim->vsu.freqmod.clocks = (uint32_t) sim->vsu.freqmod.interval *
(sim->vsu.freqmod.clk == 0 ? 19200 : 153600); (sim->vsu.freqmod.clk == 0 ? 19200 : 153600);
} }
} while (clocks != 0); /* Always process and validate next frequency value */
vsuNextFreqMod(sim, chan);
}
} }
@@ -207,8 +220,11 @@ static void vsuWriteEV0(VB *sim, int index, uint8_t value) {
chan->env.interval = value & 7; chan->env.interval = value & 7;
chan->env.value = value >> 4 & 15; chan->env.value = value >> 4 & 15;
chan->env.reload = chan->env.value; chan->env.reload = chan->env.value;
if (index == 4) if (chan->env.modmask == 1) chan->env.modmask = 2;
if (index == 4) {
chan->freqmod = vsuCheckFreqMod(sim);
vsuNextFreqMod(sim, chan); vsuNextFreqMod(sim, chan);
}
/* Configure state */ /* Configure state */
chan->env.clocks = 307220 * ((uint32_t) chan->env.interval + 1); chan->env.clocks = 307220 * ((uint32_t) chan->env.interval + 1);
@@ -221,6 +237,12 @@ static void vsuWriteEV1(VB *sim, int index, uint8_t value) {
/* Parse fields */ /* Parse fields */
chan->env.enb = value & 1; chan->env.enb = value & 1;
chan->env.rep = value >> 1 & 1; chan->env.rep = value >> 1 & 1;
if (
!chan->env.rep && (
(chan->env.reload == 0 && chan->env.dir == 0) ||
(chan->env.reload == 15 && chan->env.dir == 1)
)
) chan->env.modmask = 1;
/* Processing by channel */ /* Processing by channel */
switch (index) { switch (index) {
@@ -229,12 +251,14 @@ static void vsuWriteEV1(VB *sim, int index, uint8_t value) {
sim->vsu.freqmod.enb = value >> 6 & 1; sim->vsu.freqmod.enb = value >> 6 & 1;
sim->vsu.freqmod.func = value >> 4 & 1; sim->vsu.freqmod.func = value >> 4 & 1;
sim->vsu.freqmod.rep = value >> 5 & 1; sim->vsu.freqmod.rep = value >> 5 & 1;
chan->freqmod = vsuCheckFreqMod(sim);
vsuNextFreqMod(sim, chan); vsuNextFreqMod(sim, chan);
break; break;
case 5: /* Channel 6 */ case 5: /* Channel 6 */
sim->vsu.noise.tap = value >> 4 & 7; sim->vsu.noise.tap = value >> 4 & 7;
sim->vsu.noise.register_ = 0x0000; sim->vsu.noise.register_ = 0x0000;
vsuNextNoise(sim);
} }
} }
@@ -245,8 +269,10 @@ static void vsuWriteFQH(VB *sim, int index, uint16_t value) {
value = value << 8 & 0x0700; value = value << 8 & 0x0700;
chan->freq.current = (chan->freq.current & 0x00FF) | value; chan->freq.current = (chan->freq.current & 0x00FF) | value;
chan->freq.written = (chan->freq.written & 0x00FF) | value; chan->freq.written = (chan->freq.written & 0x00FF) | value;
if (index == 4) if (index != 4)
vsuNextFreqMod(sim, chan); return;
sim->vsu.freqmod.freqmask = 2;
vsuNextFreqMod(sim, chan);
} }
/* Write a value to S*FQL */ /* Write a value to S*FQL */
@@ -254,8 +280,10 @@ static void vsuWriteFQL(VB *sim, int index, uint8_t value) {
Channel *chan = &sim->vsu.channels[index]; Channel *chan = &sim->vsu.channels[index];
chan->freq.current = (chan->freq.current & 0x0700) | value; chan->freq.current = (chan->freq.current & 0x0700) | value;
chan->freq.written = (chan->freq.written & 0x0700) | value; chan->freq.written = (chan->freq.written & 0x0700) | value;
if (index == 4) if (index != 4)
vsuNextFreqMod(sim, chan); return;
sim->vsu.freqmod.freqmask = 1;
vsuNextFreqMod(sim, chan);
} }
/* Write a value to S*INT */ /* Write a value to S*INT */
@@ -269,19 +297,29 @@ static void vsuWriteINT(VB *sim, int index, uint8_t value) {
/* Update state */ /* Update state */
chan->int_.clocks = 76805 * ((uint32_t) chan->int_.interval + 1); chan->int_.clocks = 76805 * ((uint32_t) chan->int_.interval + 1);
if (chan->env.enb) chan->env.modmask = 0;
chan->env.clocks = 307220 * ((uint32_t) chan->env.interval + 1); if (
!chan->env.rep && (
(chan->env.reload == 0 && chan->env.dir == 0) ||
(chan->env.reload == 15 && chan->env.dir == 1)
)
) chan->env.modmask = 1;
chan->env.clocks = 307220 * ((uint32_t) chan->env.interval + 1);
if (index != 5) { if (index != 5) {
chan->wave.sample = 0; chan->wave.sample = 0;
chan->clocks = 4 * (2048 - (uint32_t) chan->freq.current); chan->clocks = 4 * (2048 - (uint32_t) chan->freq.current);
} else { } else {
sim->vsu.noise.register_ = 0x0000; sim->vsu.noise.register_ = 0x0000;
vsuNextNoise(sim);
chan->clocks = 40 * (2048 - (uint32_t) chan->freq.current); chan->clocks = 40 * (2048 - (uint32_t) chan->freq.current);
} }
if (index == 4) { if (index == 4) {
sim->vsu.freqmod.clocks = (uint32_t) sim->vsu.freqmod.interval * sim->vsu.freqmod.clocks = (uint32_t) sim->vsu.freqmod.interval *
(sim->vsu.freqmod.clk == 0 ? 19200 : 153600); (sim->vsu.freqmod.clk == 0 ? 19200 : 153600);
sim->vsu.freqmod.sample = 0; sim->vsu.freqmod.modmask = 0;
sim->vsu.freqmod.sample = 0;
chan->freqmod = vsuCheckFreqMod(sim);
vsuNextFreqMod(sim, chan);
} }
} }
@@ -313,6 +351,8 @@ static void vsuWriteSWP(VB *sim, uint8_t value) {
(sim->vsu.freqmod.clk == 0 ? 19200 : 153600); (sim->vsu.freqmod.clk == 0 ? 19200 : 153600);
if (clocks < sim->vsu.freqmod.clocks) if (clocks < sim->vsu.freqmod.clocks)
sim->vsu.freqmod.clocks = clocks; sim->vsu.freqmod.clocks = clocks;
sim->vsu.channels[4].freqmod = vsuCheckFreqMod(sim);
vsuNextFreqMod(sim, &sim->vsu.channels[4]);
} }
@@ -321,6 +361,8 @@ static void vsuWriteSWP(VB *sim, uint8_t value) {
/* Process component */ /* Process component */
static void vsuEmulate(VB *sim, uint32_t clocks) { static void vsuEmulate(VB *sim, uint32_t clocks) {
Channel *chan; /* Input channel */
uint32_t chantil; /* Clocks until next channel state update */
float i0; /* Current analog input sample */ float i0; /* Current analog input sample */
float o0; /* Current analog output sample */ float o0; /* Current analog output sample */
uint16_t output[2]; /* Digital output samples */ uint16_t output[2]; /* Digital output samples */
@@ -340,12 +382,55 @@ static void vsuEmulate(VB *sim, uint32_t clocks) {
sim->vsu.clocks -= until; sim->vsu.clocks -= until;
/* Process all channels */ /* Process all channels */
for (x = 0; x < 6; x++) for (x = 0; x < 6; x++) {
vsuEmulateChannel(sim, x, until); chan = &sim->vsu.channels[x];
chantil = until;
/* Process all clocks */
while (chan->int_.enb && chantil != 0) {
/* Determine when the next state change will occur */
if (chan->until == 0) {
/* Clocks until next state change */
chan->until = chan->clocks;
if (chan->env.enb && chan->env.modmask != 2 &&
chan->env.clocks < chan->until)
chan->until = chan->env.clocks;
if (chan->int_.auto_ && chan->int_.clocks < chan->until)
chan->until = chan->int_.clocks;
if (chan->freqmod && sim->vsu.freqmod.clocks < chan->until)
chan->until = sim->vsu.freqmod.clocks;
/* Manage clocks */
chan->clocks -= chan->until;
if (chan->env.enb && chan->env.modmask != 2)
chan->env.clocks -= chan->until;
if (chan->int_.auto_)
chan->int_.clocks -= chan->until;
if (chan->freqmod)
sim->vsu.freqmod.clocks -= chan->until;
}
/* Manage clocks */
if (chan->until > chantil) {
chan->until -= chantil;
chantil = 0;
} else {
chantil -= chan->until;
chan->until = 0;
}
/* Update channel state */
if (chan->until == 0)
vsuEmulateChannel(sim, chan);
}
}
/* Wait for the current sample to finish */ /* Wait for the current sample to finish */
if (sim->vsu.clocks != 0) if (sim->vsu.clocks != 0)
continue; return;
/* Compute the output sample */ /* Compute the output sample */
output[0] = output[1] = 0; output[0] = output[1] = 0;
@@ -416,10 +501,13 @@ static void vsuReset(VB *sim) {
for (x = 0; x < 6; x++) { for (x = 0; x < 6; x++) {
chan = &sim->vsu.channels[x]; chan = &sim->vsu.channels[x];
chan->clocks = 0; chan->clocks = 0;
chan->freqmod = 0;
chan->until = 0;
chan->env.clocks = 0; chan->env.clocks = 0;
chan->env.enb = 0; chan->env.enb = 0;
chan->env.dir = 0; chan->env.dir = 0;
chan->env.interval = 0; chan->env.interval = 0;
chan->env.modmask = 0;
chan->env.reload = 0; chan->env.reload = 0;
chan->env.rep = 0; chan->env.rep = 0;
chan->env.value = 0; chan->env.value = 0;
@@ -440,8 +528,10 @@ static void vsuReset(VB *sim) {
sim->vsu.freqmod.clocks = 0; sim->vsu.freqmod.clocks = 0;
sim->vsu.freqmod.dir = 0; sim->vsu.freqmod.dir = 0;
sim->vsu.freqmod.enb = 0; sim->vsu.freqmod.enb = 0;
sim->vsu.freqmod.freqmask = 0;
sim->vsu.freqmod.func = 0; sim->vsu.freqmod.func = 0;
sim->vsu.freqmod.interval = 0; sim->vsu.freqmod.interval = 0;
sim->vsu.freqmod.modmask = 0;
sim->vsu.freqmod.next = 0; sim->vsu.freqmod.next = 0;
sim->vsu.freqmod.rep = 0; sim->vsu.freqmod.rep = 0;
sim->vsu.freqmod.sample = 0; sim->vsu.freqmod.sample = 0;
@@ -458,21 +548,17 @@ static void vsuReset(VB *sim) {
/* Write a typed value to the VSU bus */ /* Write a typed value to the VSU bus */
static void vsuWrite(VB*sim,uint32_t address,int type,int32_t value,int debug){ static void vsuWrite(VB*sim,uint32_t address,int type,int32_t value,int debug){
(void) type;
/* Only byte writes are allowed */
switch (type) {
case VB_S16:
case VB_U16:
case VB_S32:
return;
}
/* Unmapped */ /* Unmapped */
if (address & 3) if (address & 1)
return; return;
/* Working variables */ /* Working variables */
address &= 0x7FF; address &= 0x7FC;
/* Clear frequency mask */
sim->vsu.freqmod.freqmask = 0;
/* Wave memory */ /* Wave memory */
if (address < 0x280) { if (address < 0x280) {
+22 -16
View File
@@ -1,4 +1,4 @@
/* This file is included into vb.c and cannot be compiled on its own. */ /* This file is included into vbu.c and cannot be compiled on its own. */
#ifdef VBUAPI #ifdef VBUAPI
@@ -253,18 +253,24 @@ static void dasmOpDisp26(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) {
} }
/* Format a 5-bit sign-extended immediate operand */ /* Format a 5-bit sign-extended immediate operand */
static void dasmOpImm5S(char *dest, VBU_DasmLine *line) { static void dasmOpImm5S(char *dest, VBU_DasmConfig*config, VBU_DasmLine *line){
if (config->immediateNotation == VBU_NUMBER)
*dest++ = '#';
sprintf(dest, "%d", SignExtend(line->code[0], 5)); sprintf(dest, "%d", SignExtend(line->code[0], 5));
} }
/* Format a 5-bit zero-filled immediate operand */ /* Format a 5-bit zero-filled immediate operand */
static void dasmOpImm5U(char *dest, VBU_DasmLine *line) { static void dasmOpImm5U(char *dest, VBU_DasmConfig*config, VBU_DasmLine *line){
if (config->immediateNotation == VBU_NUMBER)
*dest++ = '#';
sprintf(dest, "%d", line->code[0] & 31); sprintf(dest, "%d", line->code[0] & 31);
} }
/* Format a 16-bit sign-extended immediate operand */ /* Format a 16-bit sign-extended immediate operand */
static void dasmOpImm16S(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) { static void dasmOpImm16S(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) {
int32_t imm = (int16_t) ((int16_t) line->code[3] << 8 | line->code[2]); int32_t imm = (int16_t) ((int16_t) line->code[3] << 8 | line->code[2]);
if (config->immediateNotation == VBU_NUMBER)
*dest++ = '#';
if (imm >= -256 && imm <= 256) { if (imm >= -256 && imm <= 256) {
sprintf(dest, "%d", imm); sprintf(dest, "%d", imm);
return; return;
@@ -279,6 +285,8 @@ static void dasmOpImm16S(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) {
/* Format a 16-bit zero-filled immediate operand */ /* Format a 16-bit zero-filled immediate operand */
static void dasmOpImm16U(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) { static void dasmOpImm16U(char*dest, VBU_DasmConfig*config, VBU_DasmLine*line) {
uint16_t imm = (uint16_t) line->code[3] << 8 | line->code[2]; uint16_t imm = (uint16_t) line->code[3] << 8 | line->code[2];
if (config->immediateNotation == VBU_NUMBER)
*dest++ = '#';
dasmToHex(dest, config, 4, imm); dasmToHex(dest, config, 4, imm);
} }
@@ -441,12 +449,6 @@ static VBU_DasmLine* dasmGrow(
return *lines == NULL ? NULL : &(*lines)[index]; return *lines == NULL ? NULL : &(*lines)[index];
} }
/* Determine the size of an instruction */
static uint32_t dasmInstSize(VB *sim, uint32_t address) {
unsigned opcode = vbRead(sim, address, VB_U16) >> 10 & 63;
return opcode < 0x20 || opcode == 0x32 || opcode == 0x36 ? 2 : 4;
}
/* Format an operand */ /* Format an operand */
static void dasmOperand(char *dest, VBU_DasmConfig *config, static void dasmOperand(char *dest, VBU_DasmConfig *config,
VBU_DasmLine *line, uint8_t type) { VBU_DasmLine *line, uint8_t type) {
@@ -454,8 +456,8 @@ static void dasmOperand(char *dest, VBU_DasmConfig *config,
case DASM_BCOND : dasmOpBCOND (dest, config, line); break; case DASM_BCOND : dasmOpBCOND (dest, config, line); break;
case DASM_DISP9 : dasmOpDisp9 (dest, config, line); break; case DASM_DISP9 : dasmOpDisp9 (dest, config, line); break;
case DASM_DISP26: dasmOpDisp26(dest, config, line); break; case DASM_DISP26: dasmOpDisp26(dest, config, line); break;
case DASM_IMM5S : dasmOpImm5S (dest, line); break; case DASM_IMM5S : dasmOpImm5S (dest, config, line); break;
case DASM_IMM5U : dasmOpImm5U (dest, line); break; case DASM_IMM5U : dasmOpImm5U (dest, config, line); break;
case DASM_IMM16S: dasmOpImm16S(dest, config, line); break; case DASM_IMM16S: dasmOpImm16S(dest, config, line); break;
case DASM_IMM16U: dasmOpImm16U(dest, config, line); break; case DASM_IMM16U: dasmOpImm16U(dest, config, line); break;
case DASM_JMP : dasmOpJMP (dest, config, line); break; case DASM_JMP : dasmOpJMP (dest, config, line); break;
@@ -507,10 +509,12 @@ static int dasmLine(VB *sim, uint32_t *address, uint32_t pc,
/* Process non-text members */ /* Process non-text members */
line = &(*lines)[index]; line = &(*lines)[index];
line->address = *address; line->address = *address;
line->codeLength = dasmInstSize(sim, *address); line->codeLength = vbuCodeSize(sim, *address);
line->isPC = *address == pc; line->isPC = *address == pc;
for (x = 0; x < line->codeLength; x++) for (x = 0; x < line->codeLength; x++)
line->code[x] = vbRead(sim, *address + x, VB_U8); line->code[x] = vbRead(sim, *address + x, VB_U8);
/* Advance to the next instruction or PC, whichever is sooner */
*address += pc != *address && pc - *address < line->codeLength ? *address += pc != *address && pc - *address < line->codeLength ?
pc - *address : line->codeLength; pc - *address : line->codeLength;
@@ -652,9 +656,11 @@ static VBU_DasmLine* dasmDisassemble(VB *sim, uint32_t address,
} }
/* Check if the instruction contains the reference address */ /* Check if the instruction contains the reference address */
size = dasmInstSize(sim, addr); size = vbuCodeSize(sim, addr);
if (address - addr < size) if (address - addr < size)
break; break;
/* Advance to the next instruction or PC, whichever is sooner */
addr += addr != pc && pc - addr < size ? pc - addr : size; addr += addr != pc && pc - addr < size ? pc - addr : size;
} }
@@ -666,7 +672,7 @@ static VBU_DasmLine* dasmDisassemble(VB *sim, uint32_t address,
/* Keep decoding until the first line of output */ /* Keep decoding until the first line of output */
else for (; line < 0; line++) else for (; line < 0; line++)
addr += dasmInstSize(sim, addr); addr += vbuCodeSize(sim, addr);
/* Working variables */ /* Working variables */
size = length * sizeof (VBU_DasmLine); size = length * sizeof (VBU_DasmLine);
@@ -680,7 +686,7 @@ static VBU_DasmLine* dasmDisassemble(VB *sim, uint32_t address,
if (dasmLine(sim, &addr, pc, config, &lines, &size, &offset, x)) if (dasmLine(sim, &addr, pc, config, &lines, &size, &offset, x))
goto catch; goto catch;
} }
return lines; return VBU_REALLOC(lines, offset);
/* Exception handler */ /* Exception handler */
catch: catch:
+297
View File
@@ -0,0 +1,297 @@
/* This file is included into vbu.c and cannot be compiled on its own. */
#ifdef VBUAPI
/********************************* Constants *********************************/
/* Range types */
/*
0x41 ('A') 8-bit integer (.db), string
0x42 ('B') 8-bit integer (.db), numeric
0x43 ('C') Compiled C code
0x44 ('D') 16-bit integer (.dd, high byte first)
0x45 ('E') 65C816 code (8-bit accumulator, 8-bit index)
0x46 ('F') 65C816 code (8-bit accumulator, 16-bit index)
0x47 ('G') 65C816 code (16-bit accumulator, 8-bit index)
0x48 ('H') 65C816 code (16-bit accumulator, 16-bit index)
0x4C ('L') 32-bit integer (.dl SNES, .dw Virtual Boy, low byte first)
0x53 ('S') Size reservation, as in .bss allocation (.ds)
0x57 ('W') 16-bit integer (.dw SNES, .dh Virtual Boy, low byte first)
*/
/***************************** Module Functions ******************************/
/* Skip ISX record type 0x01 */
static size_t isx01(uint8_t *bytes, size_t length, size_t src) {
/*
u8 Type =0x01
u8 Bank Cartridge bank number
u8 BankHigh Only if Bank >= 0x80
u16 Address CPU bus address
u16 DataLength Number of bytes in Data
u8[] Data Code bytes
*/
/* Bank field is not present */
if (src + 2 > length)
return (size_t) -1;
/* Advance to DataLength field */
src += bytes[src + 1] >= 0x80 ? 5 : 4;
/* DataLength field is not present */
if (src + 2 > length)
return (size_t) -1;
/* Advance past Data field */
return src + 2 + ((uint16_t) bytes[src + 1] << 8 | bytes[src]);
}
/* Decode ISX record type 0x11 */
static size_t isx11(uint8_t *bytes, size_t length, size_t src,
uint32_t *address, uint32_t *size) {
/*
u8 Type =0x11
u32 Address CPU bus address
u32 DataLength Number of bytes in Data
u8[] Data Code bytes
*/
/* DataLength field is not present */
if (src + 9 > length)
return (size_t) -1;
/* Parse Address and DataLength fields */
*address = (uint32_t) bytes[src+4] << 24 | (uint32_t) bytes[src+3] << 16 |
(uint32_t) bytes[src+2] << 8 | bytes[src+1];
*size = (uint32_t) bytes[src+8] << 24 | (uint32_t) bytes[src+7] << 16 |
(uint32_t) bytes[src+6] << 8 | bytes[src+5];
/* Advance past Data field */
return src + 9 + *size;
}
/* Skip ISX range records */
static size_t isxRange(uint8_t *bytes,size_t length,size_t src,size_t size) {
/*
u8 Type =0x03 (SNES) or 0x13 (Virtual Boy)
u16 Count Number of ranges
Range[] Ranges
=== SNES fields ===
u8 Bank Cartridge bank number
u16 StartAddress Lowest CPU bus address
u16 EndAddress Highest CPU bus address
u8 RangeType See range types
=== Virtual Boy fields ===
u32 StartAddress Lowest CPU bus address
u32 EndAddress Highest CPU bus address
u8 RangeType See range types
*/
/* Count field is not present */
if (src + 3 > length)
return (size_t) -1;
/* Advance past Ranges field */
return src + 3 + size * ((uint16_t) bytes[src + 2] << 8 | bytes[src + 1]);
}
/* Skip ISX symbol record */
static size_t isxSymbol(uint8_t *bytes,size_t length,size_t src,size_t size) {
size_t count; /* Number of symbols */
size_t x; /* Iterator */
/*
u8 Type =0x04 (SNES) or 0x14 (Virtual Boy)
u16 Count Number of symbols
Symbol[] Symbols
u8 NameLength Number of bytes in Name
u8[] Name Symbol name
=== SNES fields ===
u8 Flags Undocumented
u8 Bank Cartridge bank number
u16 Address CPU bus address
=== Virtual Boy fields ===
u16 Flags Undocumented
u32 Address CPU bus address
*/
/* Count field is not present */
if (src + 3 > length)
return (size_t) -1;
/* Working variables */
count = (uint16_t) bytes[src + 2] << 8 | bytes[src + 1];
src += 3;
/* Process all symbols */
for (x = 0; x < count; x++) {
/* NameLength field is not present */
if (src > length)
return (size_t) -1;
/* Advance past remainder of symbol */
src += size + bytes[src];
/* Remainder of symbol is not present */
if (src > length)
return (size_t) -1;
}
return src;
}
/* Skip ISX system record */
static size_t isxSystem(uint8_t *bytes, size_t length, size_t src) {
/*
u8 Type =0x20, 0x21 or 0x22
u32 DataLength Number of bytes in Data
u8[] Data Record data
*/
/* DataLength field is not present */
if (src + 5 > length)
return (size_t) -1;
/* Advance past Data field */
return src + 5 + (
(uint32_t) bytes[src + 4] << 24 | (uint32_t) bytes[src + 3] << 16 |
(uint32_t) bytes[src + 2] << 8 | bytes[src + 1]
);
}
/***************************** Library Functions *****************************/
/* Decode an ISX debugger file to a Virtual Boy ROM */
static void* isxFrom(uint8_t *bytes, size_t length, size_t *romLength) {
uint32_t addr; /* Code record base address */
uint32_t highMin; /* Lowest address in upper half of ROM */
uint32_t lowMax; /* Highest address in lower half of ROM */
uint8_t *rom; /* Output ROM */
uint32_t size; /* Code record size in bytes */
size_t src; /* Input position */
size_t start; /* Offset of first record */
/* Error checking */
if (length < 3)
return NULL;
/* Working variables */
highMin = 0xFFFFFF;
lowMax = 0x000000;
size = 0;
start = strncmp((char *) bytes, "ISX", 3) ? 0 : 32;
/* First pass: determine the size of the ROM */
for (src = start; src < length;) {
switch (bytes[src]) {
case 0x11: /* Code (Virtual Boy) */
/* Decode record fields */
src = isx11(bytes, length, src, &addr, &size);
if (src > length)
return NULL;
/* Validate address and data size */
addr &= 0x07FFFFFF;
if (
addr < 0x07000000 ||
0x08000000 - addr < size
) return NULL;
addr &= 0x00FFFFFF;
/* Requires 16 MiB */
if (addr < 0x800000 && addr + size > 0x800000) {
highMin = 0x800000;
lowMax = 0x7FFFFF;
}
/* Map from top of ROM space */
else if (addr & 0x800000) {
if (addr < highMin)
highMin = addr;
}
/* Map from bottom of ROM space */
else {
addr += size - 1;
if (addr > lowMax)
lowMax = addr;
}
break;
case 0x01: /* Code (SNES) */
src = isx01 (bytes, length, src ); break;
case 0x03: /* Range (SNES) */
src = isxRange (bytes, length, src, 6); break;
case 0x04: /* Symbol (SNES) */
src = isxSymbol(bytes, length, src, 5); break;
case 0x13: /* Range (Virtual Boy) */
src = isxRange (bytes, length, src, 9); break;
case 0x14: /* Symbol (Virtual Boy) */
src = isxSymbol(bytes, length, src, 7); break;
case 0x20: /* System (undocumented) */
case 0x21: /* System (undocumented) */
case 0x22: /* System (undocumented) */
src = isxSystem(bytes, length, src ); break;
default: /* Invalid record type */
return NULL;
}
/* Record contains invalid data */
if (src > length)
return NULL;
}
/* Produce a ROM buffer of minimal size to fit all the code records */
for (
src = 1;
src < lowMax + 0x1000001 - highMin;
src <<= 1
);
rom = VBU_REALLOC(NULL, src);
if (rom == NULL)
return NULL;
*romLength = src;
memset(rom, 0x00, src);
/* Second pass: fill the ROM buffer with the code records */
for (src = start; src < length;) {
switch (bytes[src]) {
case 0x11: /* Code (Virtual Boy) */
src = isx11(bytes, length, src, &addr, &size);
memcpy(&rom[addr & (*romLength-1)], &bytes[src - size], size);
break;
case 0x01: /* Code (SNES) */
src = isx01 (bytes, length, src ); break;
case 0x03: /* Range (SNES) */
src = isxRange (bytes, length, src, 9); break;
case 0x04: /* Symbol (SNES) */
src = isxSymbol(bytes, length, src, 5); break;
case 0x13: /* Range (Virtual Boy) */
src = isxRange (bytes, length, src, 12); break;
case 0x14: /* Symbol (Virtual Boy) */
src = isxSymbol(bytes, length, src, 7); break;
case 0x20: /* System (undocumented) */
case 0x21: /* System (undocumented) */
case 0x22: /* System (undocumented) */
src = isxSystem(bytes, length, src ); break;
}
}
return rom;
}
#endif /* VBUAPI */
+13
View File
@@ -33,11 +33,18 @@ static int32_t SignExtend(int32_t value, int32_t bits) {
/******************************** Sub-Modules ********************************/ /******************************** Sub-Modules ********************************/
#include "disassembler.c" #include "disassembler.c"
#include "isx.c"
/******************************* API Commands ********************************/ /******************************* API Commands ********************************/
/* Determine the size in bytes of an instruction */
VBUAPI int vbuCodeSize(VB *sim, uint32_t address) {
int opcode = vbRead(sim, address, VB_U16) >> 10 & 63;
return opcode < 0x20 || opcode == 0x32 || opcode == 0x36 ? 2 : 4;
}
/* Initialize disassembler options with default settings */ /* Initialize disassembler options with default settings */
VBUAPI VBU_DasmConfig* vbuDasmInit(VBU_DasmConfig *config) { VBUAPI VBU_DasmConfig* vbuDasmInit(VBU_DasmConfig *config) {
config->bcondNotation = VBU_JOINED; config->bcondNotation = VBU_JOINED;
@@ -47,6 +54,7 @@ VBUAPI VBU_DasmConfig* vbuDasmInit(VBU_DasmConfig *config) {
config->conditionNotation = VBU_NAMES; config->conditionNotation = VBU_NAMES;
config->hexCase = VBU_UPPER; config->hexCase = VBU_UPPER;
config->hexNotation = VBU_0X; config->hexNotation = VBU_0X;
config->immediateNotation = VBU_NONE;
config->memoryNotation = VBU_OUTSIDE; config->memoryNotation = VBU_OUTSIDE;
config->mnemonicCase = VBU_UPPER; config->mnemonicCase = VBU_UPPER;
config->operandOrder = VBU_DEST_LAST; config->operandOrder = VBU_DEST_LAST;
@@ -63,3 +71,8 @@ VBUAPI VBU_DasmLine* vbuDisassemble(VB *sim, uint32_t address,
VBU_DasmConfig *config, unsigned length, int line) { VBU_DasmConfig *config, unsigned length, int line) {
return dasmDisassemble(sim, address, config, length, line); return dasmDisassemble(sim, address, config, length, line);
} }
/* Decode an ISX debugger file to a Virtual Boy ROM */
VBUAPI void* vbuFromISX(void *bytes, size_t length, size_t *romLength) {
return isxFrom(bytes, length, romLength);
}
+5
View File
@@ -29,6 +29,8 @@ extern "C" {
#define VBU_L 0 #define VBU_L 0
#define VBU_LOWER 1 #define VBU_LOWER 1
#define VBU_NAMES 1 #define VBU_NAMES 1
#define VBU_NONE 0
#define VBU_NUMBER 1
#define VBU_NUMBERS 0 #define VBU_NUMBERS 0
#define VBU_OUTSIDE 0 #define VBU_OUTSIDE 0
#define VBU_SPLIT 1 #define VBU_SPLIT 1
@@ -48,6 +50,7 @@ typedef struct { /* Defaults listed first */
uint8_t conditionNotation; /* NAMES, NUMBERS */ uint8_t conditionNotation; /* NAMES, NUMBERS */
uint8_t hexCase; /* UPPER, LOWER */ uint8_t hexCase; /* UPPER, LOWER */
uint8_t hexNotation; /* 0X, H, DOLLAR */ uint8_t hexNotation; /* 0X, H, DOLLAR */
uint8_t immediateNotation; /* NONE, NUMBER */
uint8_t memoryNotation; /* OUTSIDE, INSIDE */ uint8_t memoryNotation; /* OUTSIDE, INSIDE */
uint8_t mnemonicCase; /* UPPER, LOWER */ uint8_t mnemonicCase; /* UPPER, LOWER */
uint8_t operandOrder; /* DEST_LAST, DEST_FIRST */ uint8_t operandOrder; /* DEST_LAST, DEST_FIRST */
@@ -82,8 +85,10 @@ typedef struct {
/******************************* API Commands ********************************/ /******************************* API Commands ********************************/
VBUAPI int vbuCodeSize (VB *sim, uint32_t address);
VBUAPI VBU_DasmConfig* vbuDasmInit (VBU_DasmConfig *config); VBUAPI VBU_DasmConfig* vbuDasmInit (VBU_DasmConfig *config);
VBUAPI VBU_DasmLine* vbuDisassemble(VB *sim, uint32_t address, VBU_DasmConfig *config, unsigned length, int line); VBUAPI VBU_DasmLine* vbuDisassemble(VB *sim, uint32_t address, VBU_DasmConfig *config, unsigned length, int line);
VBUAPI void* vbuFromISX (void *bytes, size_t length, size_t *romLength);
-2
View File
@@ -1,7 +1,5 @@
"use strict"; "use strict";
//////////////////////////////////// Audio ////////////////////////////////////
// Dedicated audio output processor // Dedicated audio output processor
class Audio extends AudioWorkletProcessor { class Audio extends AudioWorkletProcessor {
+4 -3
View File
@@ -1,4 +1,5 @@
let Constants = { "use strict";
export default {
// Core // Core
VB: { VB: {
@@ -61,6 +62,8 @@ let Constants = {
L : 0, L : 0,
LOWER : 1, LOWER : 1,
NAMES : 1, NAMES : 1,
NONE : 0,
NUMBER : 1,
NUMBERS : 0, NUMBERS : 0,
OUTSIDE : 0, OUTSIDE : 0,
SPLIT : 1, SPLIT : 1,
@@ -83,5 +86,3 @@ let Constants = {
} }
}; };
export { Constants };
+57 -8
View File
@@ -1,5 +1,5 @@
"use strict"; "use strict";
import { Constants } from "./Constants.js"; import Constants from /***/"./Constants.js";
@@ -87,11 +87,8 @@ new class Core {
this.sims.set(sim.pointer, sim); this.sims.set(sim.pointer, sim);
// Video // Video
sim.pixels = this.#noalloc( sim.pixels = this.#getPixels(sim);
this.GetExtPixels(sim.pointer), sim.image = new ImageData(sim.pixels, 384, 224);
384*224*4, sim, "pixels", Uint8ClampedArray
);
sim.image = new ImageData(sim.pixels, 384, 224);
// Audio // Audio
sim.samples = this.#noalloc( sim.samples = this.#noalloc(
@@ -128,6 +125,7 @@ new class Core {
message.config.conditionNotation, message.config.conditionNotation,
message.config.hexCase, message.config.hexCase,
message.config.hexNotation, message.config.hexNotation,
message.config.immediateNotation,
message.config.memoryNotation, message.config.memoryNotation,
message.config.mnemonicCase, message.config.mnemonicCase,
message.config.operandOrder, message.config.operandOrder,
@@ -260,6 +258,47 @@ new class Core {
}); });
} }
// Attempt to produce a ROM from an ISX debugger file
fromISX(message) {
// Transfer the input data into WebAssembly memory
let input = new Uint8Array(message.data);
let inPtr = this.Realloc(0, input.length);
let inMem = new Uint8Array(this.memory.buffer, inPtr, input.length);
for (let x = 0; x < input.length; x++)
inMem[x] = input[x];
// Attempt to decode the ISX file as a ROM
let outPtr = this.FromISX(inPtr, input.length);
this.Realloc(inPtr, 0);
// The data is not an ISX file
if (outPtr == 0) {
this.dom.postMessage({ promised: true, data: null });
return;
}
// Transfer the decoded ROM from WebAssembly memory
let output = new Uint8Array(this.GetISXLength(outPtr));
let outMem = new Uint8Array(this.memory.buffer,
this.GetISXROM(outPtr), output.length);
for (let x = 0; x < output.length; x++)
output[x] = outMem[x];
this.Realloc(outPtr, 0);
// Notify DOM thread
this.dom.postMessage({
promised: true,
data : output.buffer
}, [ output.buffer ]);
}
// Reset simulation state
reset(message) {
this.vbReset(message.sim);
this.dom.postMessage({ promised: true });
}
// Specify anaglyph colors // Specify anaglyph colors
setAnaglyph(message) { setAnaglyph(message) {
this.SetAnaglyph(message.sim, message.left, message.right); this.SetAnaglyph(message.sim, message.left, message.right);
@@ -363,8 +402,10 @@ new class Core {
this.mallocs.set(buffer.pointer, buffer); this.mallocs.set(buffer.pointer, buffer);
this.#updateTarget(buffer); this.#updateTarget(buffer);
} }
for (let sim of this.sims) for (let sim of this.sims.values()) {
sim.image = new ImageData(sim.pixels, 384, 224); sim.pixels = this.#getPixels(sim);
sim.image = new ImageData(sim.pixels, 384, 224);
}
} }
@@ -435,6 +476,14 @@ new class Core {
this.Realloc(buffer.pointer, 0); this.Realloc(buffer.pointer, 0);
} }
// Register a sim's pixel buffer
#getPixels(sim) {
return this.#noalloc(
this.GetExtPixels(sim.pointer),
384*224*4, sim, "pixels", Uint8ClampedArray
);
}
// Allocate memory in WebAssembly and register the buffer // Allocate memory in WebAssembly and register the buffer
#malloc(count, target = null, assign = null, type = Uint8ClampedArray) { #malloc(count, target = null, assign = null, type = Uint8ClampedArray) {
return this.#noalloc( return this.#noalloc(
+70 -14
View File
@@ -1,5 +1,5 @@
"use strict"; "use strict";
import { Constants } from "./Constants.js"; import Constants from /**/"./Constants.js";
// Instantiation guard // Instantiation guard
const GUARD = Symbol(); const GUARD = Symbol();
@@ -19,6 +19,7 @@ class DasmConfig {
#conditionNotation; #conditionNotation;
#hexCase; #hexCase;
#hexNotation; #hexNotation;
#immediateNotation;
#memoryNotation; #memoryNotation;
#mnemonicCase; #mnemonicCase;
#operandOrder; #operandOrder;
@@ -40,6 +41,7 @@ class DasmConfig {
this.#conditionNotation = Constants.VBU.NAMES; this.#conditionNotation = Constants.VBU.NAMES;
this.#hexCase = Constants.VBU.UPPER; this.#hexCase = Constants.VBU.UPPER;
this.#hexNotation = Constants.VBU["0X"]; this.#hexNotation = Constants.VBU["0X"];
this.#immediateNotation = Constants.VBU.NONE;
this.#memoryNotation = Constants.VBU.OUTSIDE; this.#memoryNotation = Constants.VBU.OUTSIDE;
this.#mnemonicCase = Constants.VBU.UPPER; this.#mnemonicCase = Constants.VBU.UPPER;
this.#operandOrder = Constants.VBU.DEST_LAST; this.#operandOrder = Constants.VBU.DEST_LAST;
@@ -125,6 +127,16 @@ class DasmConfig {
this.#hexNotation = value; this.#hexNotation = value;
} }
get immediateNotation() { return this.#immediateNotation; }
set immediateNotation(value) {
switch (value) {
case Constants.VBU.NONE :
case Constants.VBU.NUMBER: break;
default: return;
}
this.#immediateNotation = value;
}
get memoryNotation() { return this.#memoryNotation; } get memoryNotation() { return this.#memoryNotation; }
set memoryNotation(value) { set memoryNotation(value) {
switch (value) { switch (value) {
@@ -407,6 +419,7 @@ class Sim extends HTMLElement {
conditionNotation: config.conditionNotation, conditionNotation: config.conditionNotation,
hexCase : config.hexCase, hexCase : config.hexCase,
hexNotation : config.hexNotation, hexNotation : config.hexNotation,
immediateNotation: config.immediateNotation,
memoryNotation : config.memoryNotation, memoryNotation : config.memoryNotation,
mnemonicCase : config.mnemonicCase, mnemonicCase : config.mnemonicCase,
operandOrder : config.operandOrder, operandOrder : config.operandOrder,
@@ -423,8 +436,17 @@ class Sim extends HTMLElement {
response.lines.map(l=>new DasmLine(GUARD, l)); response.lines.map(l=>new DasmLine(GUARD, l));
} }
// Reset simulation state
reset() {
return this.#core.toCore({
command : "reset",
promised: true,
sim : this.#pointer
});
}
// Specify anaglyph colors // Specify anaglyph colors
async setAnaglyph(left, right) { setAnaglyph(left, right) {
// Error checking // Error checking
if (!Number.isSafeInteger(left ) || left < 0 || left > 0xFFFFFF) if (!Number.isSafeInteger(left ) || left < 0 || left > 0xFFFFFF)
@@ -442,7 +464,7 @@ class Sim extends HTMLElement {
this.#anaglyph[1] = right; this.#anaglyph[1] = right;
// Send the colors to the core // Send the colors to the core
await this.#core.toCore({ return this.#core.toCore({
command : "setAnaglyph", command : "setAnaglyph",
promised: true, promised: true,
sim : this.#pointer, sim : this.#pointer,
@@ -462,7 +484,7 @@ class Sim extends HTMLElement {
} }
// Specify new game pad keys // Specify new game pad keys
async setKeys(keys) { setKeys(keys) {
// Error checking // Error checking
if (!Number.isSafeInteger(keys) || keys < 0 || keys > 0xFFFF) if (!Number.isSafeInteger(keys) || keys < 0 || keys > 0xFFFF)
@@ -474,7 +496,7 @@ class Sim extends HTMLElement {
this.#keys = keys; this.#keys = keys;
// Send the keys to the core // Send the keys to the core
await this.#core.toCore({ return this.#core.toCore({
command : "setKeys", command : "setKeys",
promised: true, promised: true,
sim : this.#pointer, sim : this.#pointer,
@@ -483,7 +505,7 @@ class Sim extends HTMLElement {
} }
// Specify audio panning // Specify audio panning
async setPanning(panning) { setPanning(panning) {
// Error checking // Error checking
if (!Number.isFinite(panning) ||panning < -1 || panning > +1) { if (!Number.isFinite(panning) ||panning < -1 || panning > +1) {
@@ -495,7 +517,7 @@ class Sim extends HTMLElement {
this.#panning = panning; this.#panning = panning;
// Send the panning to the core // Send the panning to the core
await this.#core.toCore({ return this.#core.toCore({
command : "setPanning", command : "setPanning",
promised: true, promised: true,
sim : this.#pointer, sim : this.#pointer,
@@ -516,7 +538,7 @@ class Sim extends HTMLElement {
} }
// Specify audio volume // Specify audio volume
async setVolume(volume) { setVolume(volume) {
// Error checking // Error checking
if (!Number.isFinite(volume) ||volume < 0 || volume > 10) { if (!Number.isFinite(volume) ||volume < 0 || volume > 10) {
@@ -528,7 +550,7 @@ class Sim extends HTMLElement {
this.#volume = volume; this.#volume = volume;
// Send the volume to the core // Send the volume to the core
await this.#core.toCore({ return this.#core.toCore({
command : "setVolume", command : "setVolume",
promised: true, promised: true,
sim : this.#pointer, sim : this.#pointer,
@@ -583,7 +605,7 @@ class VB {
#commands; // Computed method table #commands; // Computed method table
#core; // Core worker #core; // Core worker
#proxy; // Self proxy for sim access #proxy; // Self proxy for sim access
#sims; // All sims #sims; // Mapping of Sim|pointer -> proxy
#state; // Operations state #state; // Operations state
@@ -650,6 +672,8 @@ class VB {
static get L () { return Constants.VBU.L ; } static get L () { return Constants.VBU.L ; }
static get LOWER () { return Constants.VBU.LOWER ; } static get LOWER () { return Constants.VBU.LOWER ; }
static get NAMES () { return Constants.VBU.NAMES ; } static get NAMES () { return Constants.VBU.NAMES ; }
static get NONE () { return Constants.VBU.NONE ; }
static get NUMBER () { return Constants.VBU.NUMBER ; }
static get NUMBERS () { return Constants.VBU.NUMBERS ; } static get NUMBERS () { return Constants.VBU.NUMBERS ; }
static get OUTSIDE () { return Constants.VBU.OUTSIDE ; } static get OUTSIDE () { return Constants.VBU.OUTSIDE ; }
static get RELATIVE () { return Constants.VBU.RELATIVE ; } static get RELATIVE () { return Constants.VBU.RELATIVE ; }
@@ -668,8 +692,8 @@ class VB {
///////////////////////////// Static Methods ////////////////////////////// ///////////////////////////// Static Methods //////////////////////////////
// Create a core instance // Create a core instance
static async create(options) { static create(options) {
return await new VB(GUARD).#construct(options); return new VB(GUARD).#construct(options);
} }
@@ -754,6 +778,16 @@ class VB {
/////////////////////////////// Properties ////////////////////////////////
// Number of managed simulations
get size() { return this.#sims.size / 2; }
// Managed simulations
get sims() { return [... this.#sims.keys()].filter(s=>s instanceof Sim); }
///////////////////////////// Public Methods ////////////////////////////// ///////////////////////////// Public Methods //////////////////////////////
// Create one or more sims // Create one or more sims
@@ -794,7 +828,7 @@ class VB {
if ( if (
!Array.isArray(sims) || !Array.isArray(sims) ||
sims.length == 0 || sims.length == 0 ||
sims.find(s=>!this.#sims.has(s)) sims.some(s=>!this.#sims.has(s))
) { ) {
throw new TypeError("Must specify a Sim or array of Sims " + throw new TypeError("Must specify a Sim or array of Sims " +
"that belong to this core."); "that belong to this core.");
@@ -849,6 +883,28 @@ class VB {
return true; return true;
} }
// Decode an ISX debugger file to a Virtual Boy ROM
async fromISX(data) {
// Validation
if (data instanceof ArrayBuffer)
data = new Uint8Array(data);
if (
!(data instanceof Uint8Array) &&
!(data instanceof Uint8ClampedArray)
) data = Uint8Array.from(data);
// Send the memory to the core
data = data.slice();
let response = await this.#toCore({
command : "fromISX",
promised : true,
data : data.buffer,
transfers: [ data.buffer ]
});
return response.data == null ? null : new Uint8Array(response.data);
}
// Suspend automatic emulation // Suspend automatic emulation
async suspend() { async suspend() {
@@ -917,4 +973,4 @@ class VB {
} }
export { VB }; export default VB;
+4 -3
View File
@@ -108,9 +108,9 @@ EMSCRIPTEN_KEEPALIVE void* CreateSim() {
// Disassemble from a simulation // Disassemble from a simulation
EMSCRIPTEN_KEEPALIVE void* Disassemble( EMSCRIPTEN_KEEPALIVE void* Disassemble(
int bcondNotation, int conditionCase, int conditionCL, int conditionEZ, int bcondNotation, int conditionCase, int conditionCL, int conditionEZ,
int conditionNotation, int hexCase, int hexNotation, int memoryNotation, int conditionNotation, int hexCase, int hexNotation, int immediateNotation,
int mnemonicCase, int operandOrder, int programCase, int programNotation, int memoryNotation, int mnemonicCase, int operandOrder, int programCase,
int setfNotation, int systemCase, int systemNotation, int programNotation, int setfNotation, int systemCase, int systemNotation,
VB *sim, uint32_t address, unsigned length, int line VB *sim, uint32_t address, unsigned length, int line
) { ) {
VBU_DasmConfig config; VBU_DasmConfig config;
@@ -121,6 +121,7 @@ EMSCRIPTEN_KEEPALIVE void* Disassemble(
config.conditionNotation = conditionNotation; config.conditionNotation = conditionNotation;
config.hexCase = hexCase; config.hexCase = hexCase;
config.hexNotation = hexNotation; config.hexNotation = hexNotation;
config.immediateNotation = immediateNotation;
config.memoryNotation = memoryNotation; config.memoryNotation = memoryNotation;
config.mnemonicCase = mnemonicCase; config.mnemonicCase = mnemonicCase;
config.operandOrder = operandOrder; config.operandOrder = operandOrder;