mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-28 10:59:16 -04:00
1a8f382085
The watchdog leaves a runaway pass by longjmp from inside guest code, which skips the exits of every frame it leaves: generated functions' bb_defer cleanup, overlay_loader_call_native's unit depth, the native-shard stack and cycle-flush hook, the DMA kick's exec depth, and the dirty-RAM interpreter's active/phase/precise flags, resume latch and pending load. The checkpoint now saves all of it and the restore puts it back (bb_defer with the cycle freeze), as the scheduler and exception landings do for theirs. An overlay shard's unpublished pass cycles are flushed into the frozen clock at the landing so they never reach the live one. PSX_RENDER_PASS_VERIFY also checks that a pass which returned normally left the nesting balanced, since the restore would hide that. render_pass_stats gains nesting_repairs and the fault log names what the landing undid. render_pass_abort_test aborts a pass from five nested frames (real render_pass.c and psx_cycles.c, mock devices and presenter) and checks every value, the clock and RAM; it fails without this change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
50 lines
1.8 KiB
C
50 lines
1.8 KiB
C
#ifndef PSX_CYCLE_FREEZE_H
|
|
#define PSX_CYCLE_FREEZE_H
|
|
|
|
/* Render-pass time freeze (render_pass.c, docs/RENDER_PASSES.md).
|
|
*
|
|
* Runtime-only: generated and overlay code never sees this header. It is kept
|
|
* out of psx_cycles.h on purpose, because psx_cycles.h is part of the
|
|
* overlay codegen hash (runtime/codegen_hash_sources.cmake) and any change to
|
|
* it invalidates every title's overlay cache and savestates. The freeze is
|
|
* enforced in psx_cycles.c's out-of-line service paths, which generated code
|
|
* already reaches through psx_cycles.h.
|
|
*
|
|
* While g_psx_render_pass_active is set, guest cycles are still counted (GTE
|
|
* and mult/div deadlines keep working) but devices are never serviced, no
|
|
* VBlank or device event fires and no interrupt is delivered; end() puts
|
|
* every clock value back exactly, so the pass consumed no guest time. A pass
|
|
* that runs longer than `watchdog_cycles` calls `overrun` (which must not
|
|
* return into the guest; render_pass.c longjmps out of the pass). */
|
|
|
|
#include <stdint.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
extern int g_psx_render_pass_active;
|
|
typedef struct PsxCycleFreeze {
|
|
uint64_t cycle_count;
|
|
uint64_t next_service;
|
|
uint64_t fast_limit;
|
|
uint32_t batch;
|
|
uint32_t batch_limit;
|
|
uint32_t *local_acc;
|
|
uint32_t local_acc_value;
|
|
int in_device_service;
|
|
/* Generated functions bump g_psx_cyc_bb_defer on entry and drop it in a
|
|
* cleanup handler, which a longjmp out of the pass (the watchdog) skips:
|
|
* end() puts the interrupted code's depth back. */
|
|
int bb_defer;
|
|
} PsxCycleFreeze;
|
|
int psx_cycle_freeze_begin(PsxCycleFreeze *save, uint64_t watchdog_cycles,
|
|
void (*overrun)(void));
|
|
void psx_cycle_freeze_end(const PsxCycleFreeze *save);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif
|