Files
Alex Vanderveen fe403e6ce1 rollback patches
2026-07-31 17:14:35 -04:00

97 lines
4.2 KiB
C

#ifndef PSX_SELFCHECK_H
#define PSX_SELFCHECK_H
/*
* Solo rollback resim self-check (PSX_RB_SELFCHECK=1, offline only).
*
* Periodically during normal offline play: snapshot the machine at a safe
* BB edge, keep playing N ticks while recording the per-boundary pad rows
* the sampler applied to SIO plus the full netplay digest partitions, then
* rewind to the snapshot and resim the same rows, comparing digests at every
* boundary. Any live-vs-resim fork is exactly the class of determinism bug
* that otherwise only surfaces as a doomed netplay episode in a two-peer
* soak (SIO pace, GTE fold, present-edge PC, v0 restore, ...).
*
* Uses the same machinery as netplay rollback: boot_state raw buffer snaps,
* netplay_state_digest partitions, RB restore recipe (cycle/IRQ resync, SPU
* CD FIFO reset, NO cdrom_accelerate, light frontend hook). No recomp-net
* dependency; fully offline, single process.
*
* Env:
* PSX_RB_SELFCHECK=1 enable
* PSX_RB_SELFCHECK_INTERVAL=N boundaries between windows (default 600)
* PSX_RB_SELFCHECK_SPAN=N window length in boundaries (default 32)
* PSX_RB_SELFCHECK_FAULT=1 corrupt one recorded digest per window to
* prove the comparator/report path
* PSX_RB_SELFCHECK_PRIME=N throwaway resims before counted passes
* (default 1, max 4) — clears live-RECORD ambient
* PSX_RB_SELFCHECK_MASH=1 synthetic fighter-style button spam on live
* boundaries (IDLE/SAVE_WAIT/RECORD). Recorded
* rows replay during resim. Headless-safe.
* PSX_RB_SELFCHECK_MASH_SEED=N xorshift seed (default 0xC0FFEE)
* PSX_RB_SELFCHECK_MASH_RATE=N % chance to start a new press/release each
* boundary when not mid-hold (default 75)
*
* Verdict = warm resim#2 vs #3 (peer episode path). Cold #1 vs #2 is dig only.
*/
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
struct CPUState;
/* Startup bind (after savestate_configure; needs HLE scheduler for the
* rewind longjmp). Reads env; no-op unless PSX_RB_SELFCHECK=1. */
void psx_selfcheck_init(struct CPUState *cpu, uint32_t bios_checksum,
uint32_t entry_pc);
int psx_selfcheck_enabled(void);
/* 1 while a window is open (record/replay/load). Offline GPU present stays
* immediate — full BB-edge present-defer skews clk/tim between warm peers. */
int psx_selfcheck_defer_present(void);
/* 1 while the resim replay is running: skip wall pacer / host audio pump /
* host CD boost exactly like netplay resim. */
int psx_selfcheck_resim_active(void);
/* 1 during replay: main.cpp must skip live pad sampling entirely (the
* self-check republishes the recorded rows itself). */
int psx_selfcheck_replay_input(void);
/* 1 during record or replay: main.cpp must skip the mid-frame low-latency
* input re-sample so pads stay boundary-latched (netplay tick semantics). */
int psx_selfcheck_input_locked(void);
/* Offline pad sampler notes what it applied to SIO this boundary (called
* from apply_pad_slot_to_sio / apply_input_override_to_sio). */
void psx_selfcheck_note_pad(int slot, uint16_t buttons, uint8_t lx, uint8_t ly,
uint8_t rx, uint8_t ry, uint8_t analog);
/* 1 when mash owns this boundary's P1 pad word (active-low). Latched once
* per offline vblank so mid-frame resampling sees a stable word. */
int psx_selfcheck_mash_override(uint16_t *out_buttons);
/* Once per offline vblank boundary, after pad sampling (main.cpp present
* body). defer_window!=0 postpones opening a new window (e.g. multitap
* arming still pending). */
void psx_selfcheck_finish_frame(int defer_window);
/* Safe BB-edge poll (beside savestate_poll in interrupts.c): performs the
* deferred snapshot save. Loads only as a fallback when span-end flush did
* not run (e.g. headless paths that skip present). Near-free when idle. */
void psx_selfcheck_poll(struct CPUState *cpu, uint32_t resume_pc);
/* After present-body C++ RAII: if a window span just ended, rewind+resume
* immediately at that VBlank boundary (no post-span BB tail). Longjmps. */
void psx_selfcheck_flush_load(void);
#ifdef __cplusplus
}
#endif
#endif /* PSX_SELFCHECK_H */