From 4d594da0b93d1266c1693aca4e75690369dbb57f Mon Sep 17 00:00:00 2001 From: Matthew Stanley <1379tech@gmail.com> Date: Mon, 27 Apr 2026 08:58:04 -0700 Subject: [PATCH] RSPRecomp: pre-task hook + richer watchdog state dump MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two related changes for diagnosing hung RSP ucodes: 1. Per-ucode pre-task hook. The legacy wrapper now calls recomp::rsp::run_pre_task_hook(rdram, &persistent_ctx, ucode_name, ucode_addr) before invoking impl. Lets games register hook callbacks keyed by ucode name to replicate parts of rspboot's setup that the static recompilation can't infer (initial GPRs, DMA-engine residue, pre-loaded command data in DMEM). No-op when no hook is registered (one branch). Used this session to unstick Stadium's aspMain dispatch by pre-loading audio commands at DMEM[0x2B0] and seeding $29 = 0x2B0, $30 = chunk_size. Confirmed via watchdog trail: the dispatch now lands on real handlers (r26 = first audio cmd word 0x020004E0, r28/r27/r30 advance through one command) instead of looping on dispatch-table residue. 2. Watchdog trip dump now includes r1, r2, r3, r25, r26, r27, r28, r29, r30, r31, jump_target, dma_mem_address, dma_dram_address. Earlier dump (r1/r28/r29/r31 only) was too sparse to localize the next blocker — Stadium's hang now occurs at L_11B4 ↔ L_10EC because r3 is uninit going into the dispatched handler (handler does `r3 -= 1` then DMAs, but no upstream path set r3 for the first command). Richer state lets future hangs in any consumer game be diagnosed without recompiling the recompiler each time. Co-Authored-By: Claude Opus 4.7 (1M context) --- RSPRecomp/src/rsp_recomp.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/RSPRecomp/src/rsp_recomp.cpp b/RSPRecomp/src/rsp_recomp.cpp index 3bb3e11..fe169db 100644 --- a/RSPRecomp/src/rsp_recomp.cpp +++ b/RSPRecomp/src/rsp_recomp.cpp @@ -257,7 +257,8 @@ bool process_instruction(size_t instr_index, const std::vectorpc_trail_idx + i) & 31;\n" " fprintf(stderr, \" [%2u] PC=0x%04X\\n\", i, ctx->pc_trail[pos]);\n" " }}\n" - " fprintf(stderr, \"[rsp watchdog] regs: r1=%08X r28=%08X r29=%08X r31=%08X data_ptr-related: r28=%08X data_size: r27=%08X\\n\", ctx->r1, ctx->r28, ctx->r29, ctx->r31, ctx->r28, ctx->r27);\n" + " fprintf(stderr, \"[rsp watchdog] gprs: r1=%08X r2=%08X r3=%08X r25=%08X r26=%08X r27=%08X r28=%08X r29=%08X r30=%08X r31=%08X jt=%08X dma_mem=%08X dma_dram=%08X\\n\",\n" + " ctx->r1, ctx->r2, ctx->r3, ctx->r25, ctx->r26, ctx->r27, ctx->r28, ctx->r29, ctx->r30, ctx->r31, ctx->jump_target, ctx->dma_mem_address, ctx->dma_dram_address);\n" " return RspExitReason::Watchdog;\n" " }}\n", instr_vram); @@ -1173,6 +1174,14 @@ void create_function(const std::string& function_name, std::ofstream& output_fil "\n" "RspExitReason {0}(uint8_t* rdram, [[maybe_unused]] uint32_t ucode_addr) {{\n" " static thread_local RspContext persistent_ctx{{}};\n" + " // Pre-task hook: if a runtime registered a hook keyed by\n" + " // this ucode's name, call it here. Lets game-specific code\n" + " // replicate parts of rspboot's setup that the static\n" + " // recompilation can't infer (initial GPRs, DMA-engine\n" + " // residue, pre-loaded command data in DMEM). Inline\n" + " // null-check by the std::unordered_map lookup — typical\n" + " // cost is one branch when no hook is registered.\n" + " recomp::rsp::run_pre_task_hook(rdram, &persistent_ctx, \"{0}\", ucode_addr);\n" " return {0}_impl(rdram, &persistent_ctx);\n" "}}\n", function_name);