diff --git a/runtime/src/dirty_ram_interp.c b/runtime/src/dirty_ram_interp.c index b52072bf..bfebac42 100644 --- a/runtime/src/dirty_ram_interp.c +++ b/runtime/src/dirty_ram_interp.c @@ -279,6 +279,7 @@ extern void psx_dispatch_call(CPUState* cpu, uint32_t addr, uint32_t return_addr /* Forward decls from memory.c — used to read instruction bytes. */ extern uint8_t *memory_get_ram_ptr(void); +extern void dirty_ram_mark_executable_range(uint32_t phys, uint32_t len); /* MIPS instruction field decoders. */ static inline uint32_t op_field (uint32_t i) { return (i >> 26) & 0x3Fu; } @@ -2210,7 +2211,19 @@ static int dirty_ram_dispatch_inner(CPUState* cpu, uint32_t addr, uint32_t stop_ } #define OV_FPLOG_RET1() do { if (_ovfp) overlay_fp_log(addr, _in_regs, cpu, 0); return 1; } while (0) - if (!dirty_ram_is_dirty(phys) && !clean_game_text_miss) return 0; + if (!dirty_ram_is_dirty(phys) && !clean_game_text_miss) { + /* Some games populate post-EXE overlays through bulk host transfers that + * do not pass through the normal RAM write hooks. A real control transfer + * to decodable code above the configured boot-EXE text end is sufficient + * evidence that the page is executable; mark it so local overlay flow can + * remain in the interpreter. Invalid/data targets still fail dispatch. */ + if (phys < (2u * 1024u * 1024u) && phys >= g_overlay_region_floor && + dirty_ram_word_looks_decodable(fetch_word(phys))) { + dirty_ram_mark_executable_range(phys, 4u); + } else { + return 0; + } + } /* Interp-pressure signal for variant-capture automation (step 2.8): * counts dispatches the interpreter actually handles inside a capture diff --git a/runtime/src/overlay_loader.c b/runtime/src/overlay_loader.c index 4c674bae..485049c6 100644 --- a/runtime/src/overlay_loader.c +++ b/runtime/src/overlay_loader.c @@ -2298,6 +2298,7 @@ static int overlay_find_by_range(uint32_t phys) { int overlay_loader_dispatch(CPUState *cpu, uint32_t addr) { uint32_t phys = addr & 0x1FFFFFFFu; + if (!s_active) return 0; int lazy_loaded = 0; retry_candidates: int head = idx_head(phys); @@ -2791,6 +2792,7 @@ static FpEnt s_fp[FP_CAP]; static uint64_t s_fp_seq = 0; int overlay_loader_is_candidate(uint32_t phys) { + if (!s_active) return 0; phys &= 0x1FFFFFFFu; return idx_head(phys) >= 0 || lazy_has_exact_entry(phys); } @@ -3137,7 +3139,8 @@ void overlay_fp_log(uint32_t addr, const uint32_t *in_regs, * leaks (root cause of the dwarf->overworld native blue screen). * Returns 1 iff a native candidate ran. */ int overlay_loader_call_native(CPUState *cpu, uint32_t addr) { - if (!s_native_exec) return 0; /* interp mode: keep the legacy inline path */ + if (!s_active || !s_native_exec) + return 0; /* inactive/interp mode: keep the legacy inline path */ uint32_t phys = addr & 0x1FFFFFFFu; if (idx_head(phys) < 0 && !lazy_has_exact_entry(phys)) return 0; /* neither a registered nor an exact cached entry */