#include "common.h"
#include "emulator.h"
#include <emscripten.h>
// hooks we wana implement
#define IS_SPEC_emulator_step_after ~,1
#define IS_SPEC_serial_write ~,1
//fallback to default/dummy "implementations"
#define HOOK_SELECT_INNER(...) HOOK_SELECT_GET_3(__VA_ARGS__)
#define HOOK_SELECT_GET_3(a, b, slot, ...) slot
#define HOOK_VOID_CALL(name, e, f, ...) HOOK_##name(e, f, ##__VA_ARGS__)
#define HOOK_VOID_IGNORE(...) ((void)0)
#define HOOK_BOOL_CALL(name, e, f, ...) HOOK_##name(e, f)
#define HOOK_BOOL_IGNORE(...) (FALSE)
#define HOOK0(name) \
HOOK_SELECT_INNER(IS_SPEC_##name, HOOK_VOID_CALL, HOOK_VOID_IGNORE)(name, e, __func__)
#define HOOK(name, ...) \
HOOK_SELECT_INNER(IS_SPEC_##name, HOOK_VOID_CALL, HOOK_VOID_IGNORE)(name, e, __func__, __VA_ARGS__)
#define HOOK0_FALSE(name) \
HOOK_SELECT_INNER(IS_SPEC_##name, HOOK_BOOL_CALL, HOOK_BOOL_IGNORE)(name, e, __func__)
#ifndef BREAKPOINTS_MAX_BANKS_NUMBER
#define BREAKPOINTS_MAX_BANKS_NUMBER 1
#endif
typedef uint32_t breakpoints_type;
#define MEMORY_SIZE (64 * 1024)
#define BREAKPOINTS_BIT_SIZE (sizeof(breakpoints_type) * 8)
#define BREAKPOINTS_SIZE ((BREAKPOINTS_MAX_BANKS_NUMBER * MEMORY_SIZE) / BREAKPOINTS_BIT_SIZE)
#define BREAKPOINTS_SHIFT (__builtin_ctz(BREAKPOINTS_BIT_SIZE))
#define BREAKPOINTS_MASK (BREAKPOINTS_BIT_SIZE - 1)
#define BREAKPOINTS_BANK_SHIFT (16 - BREAKPOINTS_SHIFT)
#define EMULATOR_FIELDS_HOOK breakpoints_type breakpoint[BREAKPOINTS_SIZE] __attribute__((aligned(8)));
static void HOOK_emulator_step_after(Emulator*, const char* func_name);
static void HOOK_serial_write(Emulator*, const char* func_name, u8 value);
#include "emulator.c"
static inline uint32_t emulator_get_banked_PC_inline(Emulator *e) {
#if BREAKPOINTS_MAX_BANKS_NUMBER > 1
uint16_t pc = REG.PC;
if (pc < 0x4000) {
return (MMAP_STATE.rom_base[0] << (16 - ROM_BANK_SHIFT)) | pc;
} else if (pc < 0x8000) {
return (MMAP_STATE.rom_base[1] << (16 - ROM_BANK_SHIFT)) | pc;
} else if (pc < 0xA000) {
return (e->state.vram.bank << 16) | pc;
} else if (pc < 0xC000) {
return (MMAP_STATE.ext_ram_base << (16 - EXT_RAM_BANK_SHIFT)) | pc;
} else if (pc < 0xE000) {
return (e->state.wram.bank << 16) | pc;
}
return pc;
#else
return REG.PC;
#endif
}
static inline bool is_breakpoint(Emulator* e, uint32_t banked_pc) {
uint32_t idx = banked_pc >> BREAKPOINTS_SHIFT;
return (e->breakpoint[idx] & ((breakpoints_type)1 << (banked_pc & BREAKPOINTS_MASK)));
}
void HOOK_serial_write(Emulator* e, const char* func_name, u8 value) {
EM_ASM({emulator.serialCallback($0);}, value);
}
void HOOK_emulator_step_after(Emulator* e, const char* func_name) {
uint32_t banked_pc = emulator_get_banked_PC_inline(e);
if (is_breakpoint(e, banked_pc)) {
e->state.event |= EMULATOR_EVENT_BREAKPOINT;
}
}
EMSCRIPTEN_KEEPALIVE
void emulator_set_breakpoint(Emulator* e, uint32_t addr) {
uint32_t idx = addr >> BREAKPOINTS_SHIFT;
e->breakpoint[idx] |= ((breakpoints_type)1 << (addr & BREAKPOINTS_MASK));
}
EMSCRIPTEN_KEEPALIVE
void emulator_clear_breakpoints(Emulator* e) {
ZERO_MEMORY(e->breakpoint);
}
EMSCRIPTEN_KEEPALIVE
uint32_t emulator_get_banked_PC(Emulator *e) {
return emulator_get_banked_PC_inline(e);
}
EMSCRIPTEN_KEEPALIVE
void emulator_render_vram(Emulator* e, u32* buffer) {
memset(buffer, 0, sizeof(u32) * 256 * 256);
for (int ty = 0; ty < 24; ty++) {
for (int bank = 0; bank < 2; bank++) {
for (int tx = 0; tx < 16; tx++) {
for (int row = 0; row < 8; row++) {
int n = tx * 16 + ty * 16 * 16 + row * 2 + (bank << 13);
u8 a = VRAM.data[n];
u8 b = VRAM.data[n + 1];
for (int x = 0; x < 8; x++) {
u32 color = 0xFFC2F0C4;
u8 bit = (0x80 >> x);
if ((a & bit) && (b & bit)) {
color = 0xFF001B2D;
} else if (a & bit) {
color = 0xFFA8B95A;
} else if (b & bit) {
color = 0xFF6E601E;
} else if (x == 7 || row == 7) {
color = 0xFFB2E0B4;
}
buffer[(tx * 8 + x + bank * 128) + (ty * 8 + row) * 256] = color;
}
}
}
}
}
if (IS_CGB) {
for (int idx = 0; idx < 8; idx++) {
for (int col = 0; col < PALETTE_COLOR_COUNT; col++) {
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
buffer[x + idx * 8 + (200 + col * 8 + y) * 256] =
PPU.bgcp.palettes[idx].color[col];
buffer[x + idx * 8 + (200 + col * 8 + y) * 256 + 128] =
PPU.obcp.palettes[idx].color[col];
}
}
}
}
} else {
for (int type = 0; type < PALETTE_TYPE_COUNT; type++) {
for (int col = 0; col < PALETTE_COLOR_COUNT; col++) {
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 8; y++) {
buffer[x + type * 8 + (200 + col * 8 + y) * 256] =
e->pal[type].color[col];
}
}
}
}
}
}
EMSCRIPTEN_KEEPALIVE
void emulator_render_background(Emulator* e, u32* buffer, int type) {
memset(buffer, 0, sizeof(u32) * 256 * 256);
int bank = 0;
int tile_map = 0x1800 + ((type & 1) ? 0x400 : 0);
for (int ty = 0; ty < 32; ty++) {
for (int tx = 0; tx < 32; tx++) {
u8 tile = VRAM.data[tile_map + tx + ty * 32];
int offset = 0;
if(tile < 128)
offset = (LCDC.bg_tile_data_select == TILE_DATA_8000_8FFF) ? 0 : 0x1000;
for (int row = 0; row < 8; row++) {
int n = offset + tile * 16 + row * 2;
u8 a = VRAM.data[n];
u8 b = VRAM.data[n + 1];
for (int x = 0; x < 8; x++) {
u32 color = 0xFFC2F0C4;
u8 bit = (0x80 >> x);
if ((a & bit) && (b & bit)) {
color = 0xFF001B2D;
} else if (a & bit) {
color = 0xFFA8B95A;
} else if (b & bit) {
color = 0xFF6E601E;
} else if (x == 7 || row == 7) {
color = 0xFFB2E0B4;
}
buffer[(tx * 8 + x) + (ty * 8 + row) * 256] = color;
}
}
}
}
for (int x = 0; x < SCREEN_WIDTH; x++) {
buffer[((PPU.scx + x) % 256) + (PPU.scy * 256)] &= 0xFF7F7F7F;
buffer[((PPU.scx + x) % 256) +
((PPU.scy + SCREEN_HEIGHT - 1) % 256) * 256] &= 0xFF7F7F7F;
}
for (int y = 0; y < SCREEN_HEIGHT; y++) {
buffer[PPU.scx + ((PPU.scy + y) % 256) * 256] &= 0xFF7F7F7F;
buffer[((PPU.scx + SCREEN_WIDTH) % 256) + ((PPU.scy + y) % 256) * 256] &=
0xFF7F7F7F;
}
}
#ifdef GBSTUDIO
EMSCRIPTEN_KEEPALIVE
Bool set_audio_channel_mute(Emulator *e, int channel, Bool muted) {
EmulatorConfig emu_config = emulator_get_config(e);
emu_config.disable_sound[channel] = muted;
emulator_set_config(e, &emu_config);
return emu_config.disable_sound[channel];
}
#endif
So the current way of adding thrird party code is annoying ... either someone need to do the patch in the project (like gbstudio or rgbds-live)
or borher you with PR (and sometimes they are wrong (sorry again))
I read your
emulator-debug.ccode and got to the conclusion that this should be done like therethe idea is :
src/emscripten/thirdparty-default-hooks.cin similar way asemulator-debug.cdoesEMSCRIPTEN_KEEPALIVEinthirdparty-default-hooks.c)thirdparty-default-hooks.cwith your custom codeEMULATOR_FIELDS_HOOKinsideEmulatorstructure (for extending like breakpoints)only 2 new hooks are needed for existing code
in
and EMSCRIPTEN section of CMakeList.txt may looks like (click to show)
I have code ready to PR (tested) but this will obsolete #69 ... also for sanity purpose i would recomend to accept #71 first as we could see if builds fail or not
here is an thirdparty-default-hooks.c (click to show)
Now if rgbds-live developer wana change something (fx me) they can just do custom hooks based on
thirdparty-default-hooks.cas long as existing hooks allow