mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 18:25:35 -04:00
1a0213ec5e
The kernel's inline RestoreState (at BFC1094C inside the exception handler func_00000C80) restored all registers and did `jr $ra`, but the generated `return;` went back to psx_check_interrupts instead of dispatching to the restored $ra (0xBFC421D8 — the VSync callback loop). Then psx_check_interrupts overwrote all GPRs from the pre-exception save, wiping RestoreState's changes. VSync counter was permanently stuck at 0. Fix has three parts: 1. runtime/src/interrupts.c: Add psx_restore_state_escape() which longjmps with code 2 (vs code 1 for ReturnFromException). psx_check_interrupts now loops: code 2 re-dispatches to cpu->pc (still in exception context), code 1 exits, code 0 is normal. 2. recompiler full_function_emitter: Broaden longjmp-return detection from first-instruction-only to scanning ALL instructions for the exact encoding 0x8C9F0000 (lw $ra, 0($a0)). This catches both standalone RestoreState (func_1FC0227C) and the kernel inline copy (func_00000C80 at BFC10964). 3. Generated jr $ra terminators in matched functions now emit: cpu->pc = cpu->gpr[31]; psx_restore_state_escape(); return; Result: VSync counter increments steadily (~256/vblank), zero crashes, shell renders Memory Card / CD Player screen with no ADEL faults. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
58 lines
1.8 KiB
C
58 lines
1.8 KiB
C
#ifndef PSXRECOMP_V4_INTERRUPTS_H
|
|
#define PSXRECOMP_V4_INTERRUPTS_H
|
|
|
|
#include <stdint.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
struct CPUState;
|
|
|
|
/* IRQ bit positions in I_STAT/I_MASK */
|
|
#define IRQ_VBLANK 0
|
|
#define IRQ_GPU 1
|
|
#define IRQ_CDROM 2
|
|
#define IRQ_DMA 3
|
|
#define IRQ_TIMER0 4
|
|
#define IRQ_TIMER1 5
|
|
#define IRQ_TIMER2 6
|
|
#define IRQ_SIO0 7
|
|
#define IRQ_SIO1 8
|
|
#define IRQ_SPU 9
|
|
#define IRQ_PIO 10
|
|
|
|
void interrupts_init(void);
|
|
|
|
/* Called from the dispatch loop after each function returns.
|
|
* Fires vblank on schedule, checks (i_stat & i_mask), and if
|
|
* pending + COP0 allows, dispatches the exception handler. */
|
|
void psx_check_interrupts(struct CPUState* cpu);
|
|
|
|
/* Query whether we are currently inside an exception handler dispatch. */
|
|
int psx_get_in_exception(void);
|
|
|
|
/* longjmp back to psx_check_interrupts, unwinding the exception handler.
|
|
* Called by ReturnFromException (B0:0x17 or SYSCALL(3)) when inside
|
|
* the exception handler to model real hardware's RFE+JR $k0 unwind. */
|
|
void psx_exception_longjmp(void);
|
|
|
|
/* longjmp back to psx_check_interrupts for RestoreState redirection.
|
|
*
|
|
* On real hardware, RestoreState (A0:0x14) restores all GPRs from a
|
|
* save buffer and does `jr $ra`, which jumps to the restored $ra —
|
|
* abandoning the current call stack. In our model, a plain `return;`
|
|
* would go back to the C caller (the chain walker), not to $ra.
|
|
*
|
|
* This function longjmps with code 2 (vs. 1 for ReturnFromException),
|
|
* so psx_check_interrupts can re-dispatch to cpu->pc while still in
|
|
* exception context. The redirected code (e.g., VSync callback loop)
|
|
* will eventually call ReturnFromException (longjmp code 1) to exit. */
|
|
void psx_restore_state_escape(void);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* PSXRECOMP_V4_INTERRUPTS_H */
|