fntrace: capture dispatch-level bail source (runtime-only, no BIOS regen)

The dominant Tomba 2 splash-spin bails are emitted by the generated dispatch
trampoline (compiled functions C-returning wild), which the direct-call
psx_call_contract ledger can't see. fntrace_record() runs at the top of every
psx_dispatch_impl iteration; on a g_psx_bail_flattened delta it feeds
(g_debug_current_func_addr = the just-bailed compiled fn, target = wild dest)
into the traps.c bail ledger -> surfaced in the heartbeat as bail_top_site_ra /
bail_top_wild_pc. Survives the bail-storm degradation (read from the file).

Pinned the root cause: dominant bailing fn = 0xB0 (B0 syscall vector); a nested
B0 syscall returns non-locally to its outer frame's return (0x8008AE50, the
jal+8 of a `jal 0x80085900` in high RAM), the wild-return contract bails, the
outer frame's $sp is not restored, and the guest degenerates to $ra=$sp=1 in an
infinite bail loop -- the wild-call-contract family (Bug A/C/D) on the B0 path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Matthew Stanley
2026-06-22 14:09:44 -07:00
parent 732e093ab2
commit b2cfdac236
+22
View File
@@ -46,6 +46,28 @@ static inline int armed_match(uint32_t target) {
}
void fntrace_record(CPUState* cpu, uint32_t target) {
/* Dispatch-level bail-source capture (Tomba 2 wild-return spin, runtime-only,
* no BIOS regen). The generated psx_dispatch_impl calls us at the top of each
* dispatch iteration. When a previous iteration bail-FLATTENED a compiled
* function's wild return, g_psx_bail_flattened has incremented and
* g_debug_current_func_addr still holds that bailing function, while `target`
* is the wild destination it returned to. Feed (bailing_fn, wild_target) into
* the traps.c bail ledger (surfaced in the heartbeat as bail_top_site_ra /
* bail_top_wild_pc) so the dominant wild-returning function is readable even
* when the bail storm makes the window "Not Responding". */
{
extern uint64_t g_psx_bail_flattened;
extern uint32_t g_debug_current_func_addr;
extern void psx_bail_record(uint32_t site_ra, uint32_t site_sp,
uint32_t wild_pc, uint32_t guest_sp);
static uint64_t s_last_flat = 0;
uint64_t f = g_psx_bail_flattened;
if (f != s_last_flat) {
s_last_flat = f;
psx_bail_record(g_debug_current_func_addr, cpu->gpr[29], target, cpu->gpr[31]);
}
}
if (!s_game_started && s_game_entry_phys != 0) {
if ((target & 0x1FFFFFFFu) == s_game_entry_phys) {
s_game_started = 1;