mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 10:19:47 -04:00
340d6e7074
Replaces the old incomplete RAM-only fast_boot snapshot with a COMPLETE hardware save-state captured at the BIOS->game handoff. First launch (and the first launch after any update) boots the real BIOS normally and captures the full machine state at the game's PS-EXE entry_pc; every subsequent same-build launch restores it and presents the game's first frame instantly. This is not HLE: it persists the real hardware state a real BIOS run produced. Format (boot_state.h v2): a sectioned file with an integrity key (bios_checksum + entry_pc + PSX_OVERLAY_CODEGEN_HASH + ABI tag + codegen ver). Any mismatch -> reject -> normal boot + recapture, so a user/app update can never silently load a stale snapshot. Missing/short/unknown sections also reject (no partial restore). Complete capture (one section per subsystem), the fix for the old snapshot's real bug (it left GPU/SPU/CD/DMA/SIO at reset while CPU/RAM assumed configured): - CPU regs, 2 MB RAM, 1 KB scratchpad, IRQ (i_stat/i_mask), root timers, guest cycle counter, dirty-RAM page bitmap (guest-written code pages). - GPU: display/draw-area/offset/mask/texpage + GPUSTAT bits + GP0 command machine + VRAM-transfer state; VRAM (1 MB) via the renderer-agnostic gr_vram_transfer_out/in hooks (so it is correct on the GPU-authoritative GL backend, where the FBO -- not gpu.c vram[] -- is truth, and the FBO re-upload on restore happens for free). - SPU: register file + 24 voice decode/ADSR state + latches + 512 KB SPU RAM. - CD-ROM: full controller FSM (regs/IRQ-latch/FIFOs/seek/read/pending/queued + XA decoder history + disc-speed model). - DMA: channels[7] + dpcr/dicr + async-transfer state. - SIO: registers + pad-config FSM + memcard FSM (incl. per-slot mc_slots[]) + cycle-paced shifter/ACK scheduler. Host-side/recompiler-derived state (overlay/sljit tables, code-provider) is NOT serialized -- it re-initializes fresh on the normal startup path. Per-subsystem accessors use an X-macro field list so bytes/write/read can never drift. Runtime-only; no recompiler/codegen change, no regen. Reuses the [runtime] fast_boot key (semantics upgraded; type unchanged). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
106 lines
5.2 KiB
C
106 lines
5.2 KiB
C
#ifndef PSX_BOOT_STATE_H
|
|
#define PSX_BOOT_STATE_H
|
|
|
|
#include <stdint.h>
|
|
#include "cpu_state.h"
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/*
|
|
* Boot snapshot (a.k.a. fast_boot) — a COMPLETE post-BIOS save-state.
|
|
*
|
|
* Model: first launch (and the first launch after ANY app/recompiler update)
|
|
* runs the real recompiled BIOS normally, logos and all. At the moment the BIOS
|
|
* dispatches into the game's PS-EXE entry_pc, we capture a complete hardware
|
|
* snapshot. Every subsequent launch (same build) restores that snapshot and
|
|
* presents the game's first frame — instant boot, no BIOS, no logos.
|
|
*
|
|
* This is NOT HLE: it persists the REAL hardware state produced by a real BIOS
|
|
* run, then replays it. Nothing about BIOS behaviour is synthesized.
|
|
*
|
|
* Rebuild-proof: the file carries an integrity key (below) that includes the
|
|
* codegen hash + ABI tag + codegen version. A user update changes those, so a
|
|
* stale snapshot can NEVER silently load into a new build — it is rejected and
|
|
* the next boot is a normal boot that recaptures. Graceful, automatic.
|
|
*
|
|
* Completeness is mandatory (v4 no-stub rule): a partial capture that leaves a
|
|
* subsystem at reset while CPU/RAM assume it was configured is a latent stub.
|
|
* Every mutable hardware subsystem gets a section here, and the capture is
|
|
* proven complete by diffing a restored session's frames against a normal-boot
|
|
* session's frames (see the "bootsnap" debug command). Host-side / recompiler-
|
|
* derived state (dirty-RAM bitmap, overlay/sljit tables, debug rings) is NOT
|
|
* serialized — it is re-derived from restored guest RAM on load.
|
|
*/
|
|
|
|
#define BOOT_STATE_MAGIC 0x50535842u /* "PSXB" */
|
|
#define BOOT_STATE_VERSION 2u /* v1 = old incomplete RAM-only snapshot */
|
|
|
|
/*
|
|
* On-disk header. Written verbatim at offset 0, followed by the section stream.
|
|
* ALL key fields must match the running build or the snapshot is rejected.
|
|
*/
|
|
typedef struct {
|
|
uint32_t magic; /* BOOT_STATE_MAGIC */
|
|
uint32_t version; /* BOOT_STATE_VERSION */
|
|
/* ---- integrity key (every field must match to accept) ---- */
|
|
uint32_t bios_checksum; /* sum of all uint32 words in the BIOS ROM */
|
|
uint32_t entry_pc; /* game PS-EXE entry PC */
|
|
uint32_t codegen_hash; /* PSX_OVERLAY_CODEGEN_HASH (auto-gen by cmake) */
|
|
int32_t abi_tag; /* PSX_OVERLAY_ABI_TAG (abi version | flavor<<16) */
|
|
uint32_t codegen_ver; /* PSX_OVERLAY_CODEGEN_VER */
|
|
/* ---- layout ---- */
|
|
uint32_t section_count; /* number of sections that follow */
|
|
uint32_t reserved; /* 0; keeps the header 8-byte aligned */
|
|
} BootStateHeader;
|
|
|
|
/*
|
|
* Section stream: section_count records, each laid out as
|
|
* uint32_t tag; (one of BS_SEC_*)
|
|
* uint32_t pad; (0)
|
|
* uint64_t len; (payload byte count)
|
|
* uint8_t payload[len];
|
|
* An unknown tag, a length mismatch, or a missing required section on load is a
|
|
* hard reject (incomplete restore is never allowed) -> normal boot + recapture.
|
|
*/
|
|
enum {
|
|
BS_SEC_CPU = 0x01, /* CPUState: gpr/pc/hi/lo/cop0/gte_data/gte_ctrl */
|
|
BS_SEC_RAM = 0x02, /* 2 MB main RAM */
|
|
BS_SEC_SPAD = 0x03, /* 1 KB scratchpad */
|
|
BS_SEC_IRQ = 0x04, /* i_stat / i_mask */
|
|
BS_SEC_TIMER = 0x05, /* 3 root counters (counter/mode/target/irq/frac) */
|
|
BS_SEC_CLOCK = 0x06, /* psx_cycle_count */
|
|
BS_SEC_GPU = 0x07, /* GPU regs: display/draw-area/offset/mask/texpage/xfer*/
|
|
BS_SEC_VRAM = 0x08, /* 1 MB VRAM (1024x512x16) */
|
|
BS_SEC_SPU = 0x09, /* SPU regs + 24 voice decode/ADSR state + latches */
|
|
BS_SEC_SPURAM = 0x0A, /* 512 KB SPU RAM */
|
|
BS_SEC_CDROM = 0x0B, /* CD-ROM controller FSM (regs/FIFOs/seek/read/pending)*/
|
|
BS_SEC_DMA = 0x0C, /* DMA channels[7] + dpcr/dicr + async-transfer state */
|
|
BS_SEC_SIO = 0x0D, /* SIO regs + pad-config FSM + memcard FSM */
|
|
BS_SEC_DIRTY = 0x0E, /* dirty-RAM page bitmap (guest-written code pages) */
|
|
};
|
|
|
|
/* Save a COMPLETE snapshot at game handoff. Returns 1 on success. */
|
|
int boot_state_save(const CPUState* cpu, uint32_t bios_checksum,
|
|
uint32_t entry_pc, const char* path);
|
|
|
|
/* Load + validate (integrity key) + restore the full machine. On any mismatch
|
|
* or incompleteness returns 0 (caller then boots normally and recaptures). */
|
|
int boot_state_load(const char* path, uint32_t bios_checksum,
|
|
uint32_t entry_pc, CPUState* cpu);
|
|
|
|
/* Register a deferred capture: when boot_state_trigger_capture() fires (from
|
|
* fntrace at game-start), serialize to path. One-shot. */
|
|
void boot_state_set_capture(const char* path, uint32_t bios_checksum,
|
|
uint32_t entry_pc);
|
|
|
|
/* Called from fntrace when the game entry PC first dispatches. */
|
|
void boot_state_trigger_capture(const CPUState* cpu);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* PSX_BOOT_STATE_H */
|