mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-29 03:09:28 -04:00
dirty_ram_interp: poll interrupts at interpreter entry — fixes Kula World wedge (title screen renders)
ROOT CAUSE of the Kula World black-screen wedge: a guest wait loop (libcd's CdSync spin) that lives entirely in dirty RAM re-enters the dirty-RAM interpreter once per short (~7-insn) block. The interpreter's only interrupt poll was gated by the PER-INVOCATION (insns_executed & 0xFFF) counter, which short blocks never reach — and when the loop exits to dispatch each iteration that path isn't reached at all. So the interpreter ran millions of instructions with psx_check_interrupts never called, while SR (IEc+IM2) and Cause (IP2) fully satisfied the MIPS interrupt-take condition and i_stat had CDROM+VBLANK pending. The CD IRQ that sets libcd's wait-flag was never serviced; the loop spun ~14s per cycle until a rare timeout re-dispatched. Fix: poll interrupts at the interpreter's per-invocation entry, gated by a global invocation counter (every 64 entries), cpu->pc set for a coherent resume — the interpreter analogue of a static block-leader poll. RESULT: Kula World now boots to its title screen and menu. Diagnosed with the self-stop trap + gdb sampling of SR/Cause/i_stat + the interrupt gate globals (in_exception, total_checks) + g_dirty_ram_insns_run, which isolated 'CPU spinning in the interpreter without polling interrupts'. General fix: any dirty-RAM spin-loop now yields to interrupts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BdjxoRB1f9VQsNzZayCgGS
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
# Kula World (SCES-01000) — bring-up findings
|
||||
|
||||
Status as of this session: boots on the Linux runtime, past the region
|
||||
wedge, stalls before the demo level renders. Two bugs found; one fixed.
|
||||
Status: SOLVED — Kula World boots to its title screen + menu (Arcade / Time
|
||||
Trial / Load Game). Five bugs found and fixed this session; the final one was
|
||||
the wedge.
|
||||
|
||||
## Setup
|
||||
|
||||
@@ -290,3 +291,41 @@ and ack is being dispatched at all).
|
||||
Tooling built: null-dispatch fntrace freeze, PSX_CD_TRAP_CMD self-stop trap
|
||||
(runtime + oracle), PSX_FNTRACE_ALL boot trace, oracle cdrom_cmd_dump hook,
|
||||
Linux port of runtime+oracle, beetle-linux build docs + patches.
|
||||
|
||||
|
||||
### Round 8 — ROOT CAUSE FOUND AND FIXED: dirty-RAM interpreter interrupt latency
|
||||
|
||||
The wedge: at ~frame 1039 the game froze in bursts of ~14s, then advanced.
|
||||
During each freeze:
|
||||
SR = 0x40000401 (IEc=1, IM2=1) -> interrupts enabled + hw line unmasked
|
||||
Cause = 0x400 (IP2=1) -> hardware interrupt pending
|
||||
i_stat & i_mask = 0x5 -> VBLANK + CDROM pending and enabled
|
||||
in_exception=0, cooldown irrelevant
|
||||
total_checks FROZEN -> psx_check_interrupts is NOT being called
|
||||
g_dirty_ram_insns_run advancing ~15M/s, blocks_run ~2M/s
|
||||
|
||||
So the MIPS interrupt-take condition (IEc && IM2 && IP2) was fully satisfied,
|
||||
yet no interrupt was taken because psx_check_interrupts was never called. The
|
||||
CPU was in the DIRTY-RAM INTERPRETER running libcd's CdSync spin-wait (a guest
|
||||
loop that lives entirely in dirty RAM), re-entering the interpreter once per
|
||||
short (~7-insn) block. The interpreter's only interrupt poll was on the
|
||||
local-dirty-flow path gated by the PER-INVOCATION `(insns_executed & 0xFFF)`
|
||||
counter, which for short blocks never reaches 0x1000 and, when the loop exits
|
||||
to dispatch each iteration, is never reached at all. So the spin ran millions
|
||||
of instructions with no interrupt poll; the CD IRQ that sets libcd's wait-flag
|
||||
was never serviced; the loop only broke when a rare timeout re-dispatched.
|
||||
|
||||
FIX (runtime/src/dirty_ram_interp.c): poll interrupts at the interpreter's
|
||||
per-invocation entry, gated by a GLOBAL invocation counter (every 64 entries),
|
||||
with cpu->pc set so the resume is coherent. This is the interpreter analogue
|
||||
of a static block-leader poll. psx_check_interrupts runs the handler and
|
||||
returns with registers restored, so the loop safely continues and immediately
|
||||
sees its wait-flag set.
|
||||
|
||||
RESULT: Kula World boots to the title screen and menu (Arcade / Time Trial /
|
||||
Load Game / Back). Pixels on screen — bug 3 SOLVED.
|
||||
|
||||
Why this only bit Kula and not Tomba/MMX6: Kula uses the BIOS/libcd CD path
|
||||
whose CdSync spin-waits in a dirty-RAM loop; the working titles use their own
|
||||
CD drivers / loop shapes that hit the interrupt poll differently. This fix is
|
||||
general — any dirty-RAM guest spin-loop now yields to interrupts.
|
||||
|
||||
@@ -1456,6 +1456,23 @@ static int dirty_ram_dispatch_inner(CPUState* cpu, uint32_t addr, uint32_t stop_
|
||||
enum { MAX_INSNS_PER_DISPATCH = 1000000 };
|
||||
uint32_t pc = addr;
|
||||
int insns_executed = 0;
|
||||
/* Async-interrupt latency fix. A guest wait loop that lives ENTIRELY in
|
||||
* dirty RAM (e.g. libcd's CdSync spin, as in Kula World) re-enters this
|
||||
* interpreter once per short (~7-insn) block and exits before the
|
||||
* per-invocation `(insns_executed & 0xFFF)` gate below can fire — so a
|
||||
* pending, IEc+IM2-enabled interrupt is never taken and the loop spins for
|
||||
* seconds while the CD IRQ that would set its wait-flag is never serviced.
|
||||
* Poll on a GLOBAL invocation counter so short-block loops still yield to
|
||||
* interrupts (this is the interpreter analogue of a block-leader poll in
|
||||
* static code). psx_check_interrupts runs the handler and returns with
|
||||
* registers restored, so continuing the loop afterward is safe. */
|
||||
{
|
||||
static uint32_t s_interp_entry_poll = 0;
|
||||
if ((++s_interp_entry_poll & 0x3Fu) == 0) {
|
||||
cpu->pc = pc;
|
||||
psx_check_interrupts(cpu);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < MAX_INSNS_PER_DISPATCH; i++) {
|
||||
uint32_t next_pc = 0;
|
||||
uint32_t insn = fetch_word(pc & 0x1FFFFFFFu);
|
||||
|
||||
Reference in New Issue
Block a user