Skip to main content

APU_SNAPSHOT_VERSION

Constant APU_SNAPSHOT_VERSION 

Source
pub const APU_SNAPSHOT_VERSION: u8 = 4;
Expand description

Schema version for the APU snapshot blob.

  • v1 (v0.9.0 .. v1.0.0-rc2): original schema with FrameCounter carrying a pending_irq_clear: bool consumed at the next tick.

  • v2 (Session-25, 2026-05-23): FrameCounter replaces the bool with a irq_flag_clear_cycle: u64 lazy-clear schedule mirroring Mesen2’s _irqFlagClearClock. Old v1 blobs restore by migrating the bool to a synthesized schedule (a pending clear becomes “schedule for cpu_cycle + 1”, a fresh clear).

  • v3 (Session-26 Sprint 2 iter 5, 2026-05-23 onwards): FrameCounter adds irq_line_active: bool as a SEPARATE field from irq_flag. v2 blobs migrate by setting both fields to the v2 irq_flag value (the IRQ-line state coincided with $4015 bit 6 visibility under the v2 conflated model). Per ADR-0003, the v2 -> v3 migration may show a 1-cycle transient where a reloaded inhibited state has the CPU IRQ line deasserted as the FC step re-establishes it — acceptable.

  • v4 (2026-07-22): appends the scheduled warm-reset $4017 re-write (reset_4017_delay + reset_4017_value, 2 bytes). Apu::reset arms the countdown at 2 and tick_with_external decrements it once per CPU cycle, issuing FrameCounter::write when it hits zero (the v2.0.0 beta.3 A4 cycle-accurate reset, calibrated against blargg 4017_timing). Both fields were previously unserialized, so a snapshot taken inside that 2-cycle window restored delay = 0 and dropped the re-write entirely — the restored frame counter then kept the sequencer phase the re-write was supposed to reset. This is the same class as the PPU’s v5 / v6 / v8 tails (ADR 0030 / ADR 0034): live mid-frame state absent from the schema, invisible to any straight-run_frame test and reachable only through a snapshot/restore round trip. Surfaced by the standing schema audit (crates/rustynes-test-harness/tests/snapshot_schema_audit.rs) rather than by a user-visible symptom.

    v1..=3 blobs upconvert with both at 0 — “no re-write pending”, which is the resting value and therefore correct for any pre-v4 state not captured inside the 2-cycle arming window (and for one that was, the bytes simply do not exist to recover).

    Unlike this module’s earlier trailing-optional tails (the v1.x DMC-DMA scheduling bytes and the W3-Stage-4 block, both detected by has_remaining), this one is version-gated. Trailing-optional makes two different blob lengths both valid at one version, which is workable but leaves the format ambiguous; a version gate does not. The bump costs no additional compatibility here because the same change already bumps PPU_SNAPSHOT_VERSION to 8 (ADR 0034), and rustynes_core’s .rns container is version-exact per section — pre-existing save states are already rejected at the PPU section.