#include #include #include #include #include ////////////////////////////////// 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 immediateNotation, 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.immediateNotation = immediateNotation; 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; }