shrooms-vb-core/web/wasm.c

273 lines
8.5 KiB
C

#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
// 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)
//////////////////////////////////// Types ////////////////////////////////////
// Additional monitor state for simulations
typedef struct {
int32_t breaks;
uint32_t left[256];
float panning;
uint8_t pixels[384 * 224 * 4];
uint32_t right[256];
float samples[NUM_SAMPLES];
float volume;
} Ext;
////////////////////////////////// Callbacks //////////////////////////////////
// Frame callback
int wasmOnFrame(VB *sim) {
((Ext *) vbGetUserData(sim))->breaks |= BREAK_FRAME;
return 1;
}
/////////////////////////////// Module Exports ////////////////////////////////
// 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);
}
}
// 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);
ext->breaks = 0;
ext->panning = 0.0f;
ext->volume = 1.0f;
vbSetUserData(sim, ext);
SetAnaglyph(sim, STEREO_RED, STEREO_CYAN);
// 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);
}
// 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;
}
// 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]];
}
// 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;
}
}
// 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);
}
// Specify audio panning
EMSCRIPTEN_KEEPALIVE void SetPanning(VB *sim, float panning) {
((Ext *) vbGetUserData(sim))->panning = panning;
}
// Specify audio volume
EMSCRIPTEN_KEEPALIVE void SetVolume(VB *sim, float volume) {
((Ext *) vbGetUserData(sim))->volume = volume;
}