diff --git a/runtime/include/overlay_api.h b/runtime/include/overlay_api.h index 92d8b60a..d779c423 100644 --- a/runtime/include/overlay_api.h +++ b/runtime/include/overlay_api.h @@ -46,7 +46,14 @@ /* v11 batches per-instruction cycle charges inside overlay DLLs and flushes at * block/device/store boundaries, removing a cross-DLL callback per instruction * without changing the shared guest-cycle timeline. */ -#define PSX_OVERLAY_ABI_VERSION 11 +/* v12: rfe_mark_escape callback — the emitter emits psx_rfe_mark_escape() at + * every `rfe` (code_generator.cpp COP0 case), including in overlay-resident + * exception-context code (Tomba kernel-window overlays hit this). It mutates + * the runtime's shared RFE-pending escape state (interrupts.c), so the DLL + * MUST forward — a local no-op would swallow exception returns. Without the + * shim any rfe-containing overlay fails to LINK (undefined psx_rfe_mark_escape) + * and that variant runs interpreted forever — the gte_read_ctrl class again. */ +#define PSX_OVERLAY_ABI_VERSION 12 /* Codegen flavor of the recompiled output the overlays + runtime were built * against. Overlays are keyed in the cache by guest-bytes CRC, which is @@ -196,6 +203,10 @@ typedef struct { uint32_t (*gte_read_ctrl)(CPUState *cpu, uint8_t reg); void (*gte_write_data)(CPUState *cpu, uint8_t reg, uint32_t value); void (*gte_write_ctrl)(CPUState *cpu, uint8_t reg, uint32_t value); + /* RFE escape marker (ABI v12; see the version-history note above). + * Appended last; forwards unconditionally — v12 hosts always supply it + * and the ABI gate rejects DLL/host mixes that don't. */ + void (*rfe_mark_escape)(void); } OverlayCallbacks; #ifdef __cplusplus diff --git a/runtime/src/overlay_loader.c b/runtime/src/overlay_loader.c index b0efd562..24d9eec8 100644 --- a/runtime/src/overlay_loader.c +++ b/runtime/src/overlay_loader.c @@ -1746,6 +1746,13 @@ static void init_callbacks(void) { s_callbacks.gte_write_data = gte_write_data; s_callbacks.gte_write_ctrl = gte_write_ctrl; } + /* ABI v12: rfe escape marker — emitted at every `rfe`, including in + * overlay-resident exception-context code; mutates the runtime's + * shared RFE-pending state so it must forward. */ + { + extern void psx_rfe_mark_escape(void); + s_callbacks.rfe_mark_escape = psx_rfe_mark_escape; + } } } diff --git a/tools/compile_overlays.py b/tools/compile_overlays.py index 30ed9c30..cc8b2659 100644 --- a/tools/compile_overlays.py +++ b/tools/compile_overlays.py @@ -953,6 +953,15 @@ void psx_restore_state_escape(void) { overlay_flush_cycles(); if (g_cbs.psx_restore_state_escape) g_cbs.psx_restore_state_escape(); } +/* RFE escape marker (ABI v12): emitted at every `rfe`, including in + * overlay-resident exception-context code. Mutates the runtime's shared + * RFE-pending state, so it MUST forward (a local no-op would swallow + * exception returns). Unconditional — v12 hosts always supply it; the + * ABI gate rejects mixes that don't. */ +void psx_rfe_mark_escape(void) { + overlay_flush_cycles(); + g_cbs.rfe_mark_escape(); +} /* Faithful-timing functions (ABI v9): overlay code built with PSX_ENABLE_BLOCK_CYCLES * emits these; forward to the runtime's real impls so native overlays charge cycles on * the SAME timeline as the interp/BIOS (a local copy would diverge). NULL-guarded so a