2024-11-01 20:03:49 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <emscripten/emscripten.h>
|
|
|
|
#include <vb.h>
|
|
|
|
#include <vbu.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////// Constants //////////////////////////////////
|
|
|
|
|
|
|
|
// Break conditions
|
|
|
|
#define BREAK_FRAME 1
|
|
|
|
#define BREAK_POINT 2
|
|
|
|
|
2024-11-02 16:14:24 +00:00
|
|
|
// Anaglyph colors
|
|
|
|
#define STEREO_CYAN 0x00C6F0
|
|
|
|
#define STEREO_GREEN 0x00B400
|
|
|
|
#define STEREO_MAGENTA 0xC800FF
|
|
|
|
#define STEREO_RED 0xFF0000
|
|
|
|
|
|
|
|
// Element counts
|
|
|
|
#define NUM_SAMPLES (41700 / 50 * 2)
|
2024-11-01 20:03:49 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////// Types ////////////////////////////////////
|
|
|
|
|
|
|
|
// Additional monitor state for simulations
|
|
|
|
typedef struct {
|
|
|
|
int32_t breaks;
|
|
|
|
uint32_t left[256];
|
2024-11-02 16:14:24 +00:00
|
|
|
float panning;
|
2024-11-01 20:03:49 +00:00
|
|
|
uint8_t pixels[384 * 224 * 4];
|
|
|
|
uint32_t right[256];
|
2024-11-02 16:14:24 +00:00
|
|
|
float samples[NUM_SAMPLES];
|
|
|
|
float volume;
|
2024-11-01 20:03:49 +00:00
|
|
|
} Ext;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////// Callbacks //////////////////////////////////
|
|
|
|
|
|
|
|
// Frame callback
|
|
|
|
int wasmOnFrame(VB *sim) {
|
|
|
|
((Ext *) vbGetUserData(sim))->breaks |= BREAK_FRAME;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////// Module Exports ////////////////////////////////
|
|
|
|
|
2024-11-02 16:14:24 +00:00
|
|
|
// Specify anaglyph colors
|
|
|
|
EMSCRIPTEN_KEEPALIVE void SetAnaglyph(VB *sim, uint32_t left, uint32_t right) {
|
|
|
|
Ext *ext = (Ext *) vbGetUserData(sim);
|
|
|
|
|
|
|
|
// Erase all RGB values
|
|
|
|
for (int x = 0; x < 256; x++)
|
|
|
|
ext->left[x] = ext->right[x] = 0xFF000000;
|
|
|
|
|
|
|
|
// Process all RGB channels
|
|
|
|
for (int c = 0, shift = 16; c < 3; c++, shift -= 8) {
|
|
|
|
double max; // Magnitude of channel value
|
|
|
|
uint32_t *dest; // Lookup data
|
|
|
|
|
|
|
|
// Select the magnitude and lookup channel
|
|
|
|
dest = ext->left;
|
|
|
|
max = (left >> shift & 0xFF) / 255.0;
|
|
|
|
if (max == 0) {
|
|
|
|
dest = ext->right;
|
|
|
|
max = (right >> shift & 0xFF) / 255.0;
|
|
|
|
if (max == 0)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compute the resulting RGB values
|
|
|
|
for (int x = 0; x < 256; x++)
|
|
|
|
*dest++ |= (uint32_t) (x * max + 0.5) << (16 - shift);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2024-11-01 20:03:49 +00:00
|
|
|
// Instantiate a simulation
|
|
|
|
EMSCRIPTEN_KEEPALIVE void* CreateSim() {
|
|
|
|
size_t sizeOfSim = vbSizeOf();
|
|
|
|
uint8_t *pointer = malloc(sizeOfSim + sizeof (Ext));
|
|
|
|
|
|
|
|
// Configure sim
|
|
|
|
VB *sim = vbInit((VB *) pointer);
|
|
|
|
vbSetFrameCallback(sim, &wasmOnFrame);
|
|
|
|
vbSetOption(sim, VB_PSEUDO_HALT, 1);
|
|
|
|
|
|
|
|
// Configure extra
|
|
|
|
Ext *ext = (Ext *) (pointer + sizeOfSim);
|
2024-11-02 16:14:24 +00:00
|
|
|
ext->breaks = 0;
|
|
|
|
ext->panning = 0.0f;
|
|
|
|
ext->volume = 1.0f;
|
2024-11-01 20:03:49 +00:00
|
|
|
vbSetUserData(sim, ext);
|
2024-11-02 16:14:24 +00:00
|
|
|
SetAnaglyph(sim, STEREO_RED, STEREO_CYAN);
|
2024-11-01 20:03:49 +00:00
|
|
|
|
|
|
|
// Initialize pixels with opaque black
|
|
|
|
for (unsigned x = 0; x < 384 * 224; x++)
|
|
|
|
((uint32_t *) ext->pixels)[x] = 0xFF000000;
|
|
|
|
|
|
|
|
return sim;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Disassemble from a simulation
|
|
|
|
EMSCRIPTEN_KEEPALIVE void* Disassemble(
|
|
|
|
int bcondNotation, int conditionCase, int conditionCL, int conditionEZ,
|
|
|
|
int conditionNotation, int hexCase, int hexNotation, int memoryNotation,
|
|
|
|
int mnemonicCase, int operandOrder, int programCase, int programNotation,
|
|
|
|
int setfNotation, int systemCase, int systemNotation,
|
|
|
|
VB *sim, uint32_t address, unsigned length, int line
|
|
|
|
) {
|
|
|
|
VBU_DasmConfig config;
|
|
|
|
config.bcondNotation = bcondNotation;
|
|
|
|
config.conditionCase = conditionCase;
|
|
|
|
config.conditionCL = conditionCL;
|
|
|
|
config.conditionEZ = conditionEZ;
|
|
|
|
config.conditionNotation = conditionNotation;
|
|
|
|
config.hexCase = hexCase;
|
|
|
|
config.hexNotation = hexNotation;
|
|
|
|
config.memoryNotation = memoryNotation;
|
|
|
|
config.mnemonicCase = mnemonicCase;
|
|
|
|
config.operandOrder = operandOrder;
|
|
|
|
config.programCase = programCase;
|
|
|
|
config.programNotation = programNotation;
|
|
|
|
config.setfNotation = setfNotation;
|
|
|
|
config.systemCase = systemCase;
|
|
|
|
config.systemNotation = systemNotation;
|
|
|
|
return vbuDisassemble(sim, address, &config, length, line);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process simulations
|
|
|
|
EMSCRIPTEN_KEEPALIVE int Emulate(VB **sims, unsigned count, uint32_t *clocks) {
|
|
|
|
for (unsigned x = 0; x < count; x++)
|
|
|
|
((Ext *) vbGetUserData(sims[x]))->breaks = 0;
|
|
|
|
return vbEmulateEx(sims, count, clocks);
|
|
|
|
}
|
|
|
|
|
2024-11-02 16:14:24 +00:00
|
|
|
// Retrieve a sim's pixel pointer
|
|
|
|
EMSCRIPTEN_KEEPALIVE void* GetExtPixels(VB *sim) {
|
|
|
|
return ((Ext *) vbGetUserData(sim))->pixels;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Retrieve a sim's sample pointer
|
|
|
|
EMSCRIPTEN_KEEPALIVE void* GetExtSamples(VB *sim) {
|
|
|
|
return ((Ext *) vbGetUserData(sim))->samples;
|
|
|
|
}
|
|
|
|
|
2024-11-01 20:03:49 +00:00
|
|
|
// Retrieve the break condition flags for a sim
|
|
|
|
EMSCRIPTEN_KEEPALIVE int32_t GetBreaks(VB *sim) {
|
|
|
|
return ((Ext *) vbGetUserData(sim))->breaks;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Serialize disassembled lines into a linear buffer
|
|
|
|
EMSCRIPTEN_KEEPALIVE void GetDasm(uint32_t *buffer, void *dasm, int count) {
|
|
|
|
for (int x = 0; x < count; x++) {
|
|
|
|
VBU_DasmLine *line = &((VBU_DasmLine *) dasm)[x];
|
|
|
|
|
|
|
|
// Numeric data
|
|
|
|
*buffer++ = line->address;
|
|
|
|
*buffer++ = line->codeLength;
|
|
|
|
for (int y = 0; y < 4; y++)
|
|
|
|
*buffer++ = line->code[y];
|
|
|
|
*buffer++ = line->isPC;
|
|
|
|
|
|
|
|
// Text data -- Store offset of string pointer from start of dasm
|
|
|
|
*buffer++ = (uint32_t) ((void *) &line->text.address - dasm);
|
|
|
|
for (int y = 0; y < 4; y++)
|
|
|
|
*buffer++ = (uint32_t) ((void *) &line->text.code[y] - dasm);
|
|
|
|
*buffer++ = (uint32_t) ((void *) &line->text.mnemonic - dasm);
|
|
|
|
*buffer++ = line->text.operandsLength;
|
|
|
|
for (int y = 0; y < 3; y++)
|
|
|
|
*buffer++ = (uint32_t) ((void *) &line->text.operands[y] - dasm);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Retrieve anaglyph-tinted pixels from a sim
|
|
|
|
EMSCRIPTEN_KEEPALIVE void GetPixels(VB *sim) {
|
|
|
|
Ext *ext = (Ext *) vbGetUserData(sim);
|
|
|
|
uint8_t *pixels = ext->pixels;
|
|
|
|
vbGetPixels(sim, pixels, 4, 384 * 4, pixels + 1, 4, 384 * 4);
|
|
|
|
for (unsigned x = 0; x < 384 * 224 * 4; x += 4, pixels += 4)
|
|
|
|
*(uint32_t *) pixels = ext->left[pixels[0]] | ext->right[pixels[1]];
|
|
|
|
}
|
|
|
|
|
2024-11-02 16:14:24 +00:00
|
|
|
// Mix audio samples for output
|
|
|
|
EMSCRIPTEN_KEEPALIVE void Mix(float *buffer, VB **sims, int count) {
|
|
|
|
|
|
|
|
// Process all sims
|
|
|
|
for (int x = 0; x < count; x++) {
|
|
|
|
Ext *ext = (Ext *) vbGetUserData(sims[x]);
|
|
|
|
float *samples = ext->samples;
|
|
|
|
float v = ext->volume;
|
|
|
|
|
|
|
|
// First sim initializes buffer
|
|
|
|
if (x == 0) {
|
|
|
|
if (ext->panning < 0) {
|
|
|
|
float l = -ext->panning * v;
|
|
|
|
float r = (1.0f + ext->panning) * v;
|
|
|
|
for (unsigned y = 0; y < NUM_SAMPLES; y += 2) {
|
|
|
|
buffer[y ] = samples[y] * v + samples[y + 1] * l;
|
|
|
|
buffer[y + 1] = samples[y + 1] * r;
|
|
|
|
}
|
|
|
|
} else if (ext->panning > 0) {
|
|
|
|
float r = ext->panning * v;
|
|
|
|
float l = (1.0f - ext->panning) * v;
|
|
|
|
for (unsigned y = 0; y < NUM_SAMPLES; y += 2) {
|
|
|
|
buffer[y ] = samples[y] * l;
|
|
|
|
buffer[y + 1] = samples[y + 1] * v + samples[y] * r;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (unsigned y = 0; y < NUM_SAMPLES; y++)
|
|
|
|
buffer[y] = samples[y] * v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Subsequent sims add to buffer
|
|
|
|
else {
|
|
|
|
if (ext->panning < 0) {
|
|
|
|
float l = -ext->panning * v;
|
|
|
|
float r = (1.0f + ext->panning) * v;
|
|
|
|
for (unsigned y = 0; y < NUM_SAMPLES; y += 2) {
|
|
|
|
buffer[y ] += samples[y] * v + samples[y + 1] * l;
|
|
|
|
buffer[y + 1] += samples[y + 1] * r;
|
|
|
|
}
|
|
|
|
} else if (ext->panning > 0) {
|
|
|
|
float r = ext->panning * v;
|
|
|
|
float l = (1.0f - ext->panning) * v;
|
|
|
|
for (unsigned y = 0; y < NUM_SAMPLES; y += 2) {
|
|
|
|
buffer[y ] += samples[y] * l;
|
|
|
|
buffer[y + 1] += samples[y + 1] * v + samples[y] * r;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (unsigned y = 0; y < NUM_SAMPLES; y++)
|
|
|
|
buffer[y] += samples[y] * v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clipping
|
|
|
|
for (unsigned y = 0; y < NUM_SAMPLES; y++) {
|
|
|
|
if (buffer[y] < -1.0f)
|
|
|
|
buffer[y] = -1.0f;
|
|
|
|
else if (buffer[y] > +1.0f)
|
|
|
|
buffer[y] = +1.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2024-11-01 20:03:49 +00:00
|
|
|
// Determine the size in bytes of a pointer
|
|
|
|
EMSCRIPTEN_KEEPALIVE int PointerSize() {
|
|
|
|
return sizeof (void *);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Memory management
|
|
|
|
EMSCRIPTEN_KEEPALIVE void* Realloc(void *data, size_t size) {
|
|
|
|
return realloc(data, size);
|
|
|
|
}
|
|
|
|
|
2024-11-02 16:14:24 +00:00
|
|
|
// Specify audio panning
|
|
|
|
EMSCRIPTEN_KEEPALIVE void SetPanning(VB *sim, float panning) {
|
|
|
|
((Ext *) vbGetUserData(sim))->panning = panning;
|
|
|
|
}
|
2024-11-01 20:03:49 +00:00
|
|
|
|
2024-11-02 16:14:24 +00:00
|
|
|
// Specify audio volume
|
|
|
|
EMSCRIPTEN_KEEPALIVE void SetVolume(VB *sim, float volume) {
|
|
|
|
((Ext *) vbGetUserData(sim))->volume = volume;
|
2024-11-01 20:03:49 +00:00
|
|
|
}
|