Running Ahead of the Sound Chip
Rusty Nail is my fantasy console: an STM32H753 running a shelf of emulated systems bare-metal, out to a TV over HDMI through an RP2350 co-processor. The from-scratch SNES core is the hardest tenant. Its target is locked NTSC cadence, 60.098814 fps, which at the H7's 480 MHz is a budget of 7,986,846 cycles per frame. In the last SNES post the wire was convicted as the bottleneck and rebuilt. Since then the architecture settled on a split machine: the 65C816 CPU and PPU live on the H7, and the APU (the SPC700 sound CPU plus its DSP) runs remotely on the RP2350 receiver, where it already reached 97% of realtime.
The split has one expensive seam. When game code reads an APU port, the H7 needs the exact byte the SPC700 would have on that port at that exact emulated time. The accepted baseline (Candidate E in the architecture tournament) requests it over the link and waits. Measured on hardware, that wait costs 3.81M to 4.51M cycles per dense frame. Dense Super Mario World frames were landing at 12.81M to 13.62M cycles, which is 34.9 to 36.3 fps against a 7.99M budget. More than half the deficit was the H7 sitting idle with its hands in its lap, waiting for one byte.
The bet
Candidate H asks the obvious follow-up: what if the H7 guesses the byte and keeps going? Predict the port value, continue executing CPU and PPU into private, unpublished state, and when the real byte arrives either commit all that work (prediction right) or throw it away, restore, and replay with the confirmed byte (prediction wrong). Real CPUs have done this for thirty years; this is speculative execution with the branch predictor swapped for a sound chip. The correctness bar stays absolute: the emulator must remain byte-identical to the synchronous machine in every observable way, including the audio, or the whole exercise is disqualified.
Getting there needed a predictor worth betting on, a bounded way to record and undo everything the speculative path touches, and proof on the bench that the bookkeeping costs less than the wait it hides.
Picking the predictor
I scored five implementable policies across four 1,500-frame journeys (Super Mario World and A Link to the Past, each under NTSC and PAL timing), 7,196 scored reads in total. Repeating the previous frame's value for the same port was already right 99.83 to 100% of the time. A direct 32-entry table keyed by read site did marginally better on Zelda. An eight-interaction history scheme did worse than both (97.0 to 99.0%), a useful reminder that cleverness is a cost centre.
The winner was hiding in the protocol. The receiver already returns a 16-byte progress snapshot containing the exact values of all four APU ports at its progress horizon; the product code was validating that snapshot and then discarding those four bytes. Retaining them and predicting "the port reads what the receiver last reported" scored 7,193 of 7,196: perfect on both Super Mario World journeys, three misses total across both Zelda journeys, never more than one miss in a frame. Four bytes of state, no new wire record, no game-specific knowledge.
One embarrassment from this phase is worth recording. The first predictor run
produced numbers from a broken instrument: the host instruction trace
initialised each read's value field to zero and never filled in the byte the
bus actually returned. Addresses and timing were fine, but every
value-dependent percentage was invalid. I rebuilt the trace to complete each
record after the real bus value is known, added a seam test asserting the
trace contains the receiver-returned byte so the defect cannot silently
recur, and reran all four journeys and every rollback trial from the start.
The final machine identities did not change; the predictor percentages above
are the corrected ones.
Making wrong guesses free of consequence
The transaction machinery is a first-write journal. A fixed 521-byte checkpoint covers CPU, scheduler, DMA and joypad state; WRAM, SRAM and VRAM are journalled in 256-byte pages on first write; CGRAM and OAM take one bounded snapshot on first mutation; APU writes are buffered and released in exact timestamp order only after commit, so the receiver never sees a speculative byte. Dense frames dirty 4,185 to 23,685 checkpoint bytes.
The proof harness forces every APU read in six representative dense frames to be wrong (the actual byte XOR 1), lets the wrong world run to the end of the frame, then restores and replays with the confirmed byte. All 18 trials replayed to byte-identical CPU, scheduler, DMA, WRAM, SRAM, PPU state, framebuffer, wire output, ordered APU port events, SPC700/DSP state and PCM. Nothing speculative is ever published; the longest transaction spanned 425,170 master cycles, bounded to one frame.
The bench prices it
Host models propose; the DWT cycle counter disposes. The first hardware placement settled the transaction at every DMA and HDMA edge and was 0.41M to 0.53M cycles slower than Candidate E while remaining exact. That was a placement mistake (the journal can span ordinary DMA safely), and correcting it produced the real number: 3.24M to 3.53M cycles saved per dense frame, repeatable across cold resets.
Two refinements followed. The abort check originally ran after every emulated instruction; moving it to the actual mutation sites (memory writes and APU operations, reusing a scheduler phase boundary that was already tested) recovered another 0.335M to 0.373M cycles. Then four further micro-variants of the first-write journal (a predecessor-page cache, an outlined journal body, journal-owned abort notification, per-page generation counters) were each built, flashed and measured. All four were slower, by 4K to 130K cycles per checkpoint, and all four are rejected and removed. A parallel architecture, Candidate I, which batched the 224 interleaved renderer entries into a post-CPU event replay, also died on the bench: batching saved 0.309M to 0.373M cycles but the replay and PPU rewind cost 0.362M to 0.454M. The tournament docs record all of it so no future session re-runs a closed experiment.
The corruption that passed every checksum
Then the fastest exact build ever measured failed by eye. A band across the top of the HDMI picture showed corruption, while the H7's framebuffer hashes, the committed wire hashes and the complete machine-state hashes all stayed exact, and the receiver reported zero checksum, parser, ordering or row faults. A controlled A/B with a byte-identical receiver pinned it: Candidate E clean, Candidate H corrupted, launcher always clean.
The fault was a buffer-lifetime alias on the H7. The transaction journal and
the priority-P7 link task's frozen wire snapshot both started at the same
static buffer, CART_STORE. After the emulated frame returned, P7 could still
be packing the frozen source rows while the next frame's transaction overwrote
the same prefix. The journal's fixed PPU prefix is 9,911 bytes and its
measured maximum occupancy is 21,351 bytes; at the wire row size that is 39 to
83 native rows, which agrees with the height of the photographed band. The
receiver's telemetry was honestly clean because the bytes were corrupted
before it ever saw them; it checksummed internally consistent garbage.

The repair is explicit, disjoint ownership with a compile-time capacity gate:
unsafe fn transaction_overlay() -> &'static mut [u8] {
// The P7 link task may still be packing the preceding source after this
// emulated frame begins. Its immutable packed image owns the exact prefix;
// Candidate H owns only the disjoint tail. The capacity gate preserves the
// largest measured dense transaction plus the existing worst-case DMA
// reserve, so this correctness fix does not buy safety by settling early.
const START: usize = crate::MD_WIRE_SNAPSHOT_BYTES;
const CAPACITY: usize = core::mem::size_of::<crate::SnesCartFrameOverlay>() - START;
const DENSE_TRANSACTION_CEILING: usize = 22 * 1024;
const _: () =
assert!(CAPACITY >= DENSE_TRANSACTION_CEILING + TransactionJournal::DMA_RESERVE_BYTES);
core::slice::from_raw_parts_mut(
addr_of_mut!(crate::CART_STORE).cast::<u8>().add(START),
CAPACITY,
)
}
P7 keeps an immutable 76,800-byte prefix, the transaction gets a disjoint 94,720-byte tail, and the 16 KiB USB sector scratch moved from AXI SRAM to DMA-reachable D2 to pay for it while keeping the mandatory 32 KiB main-stack floor. Every 300th frame still takes the fully synchronous RGB565 validation path; a validation-only barrier now finishes encoding the old snapshot before that frame borrows the prefix. The receiver image, protocol and clocks are untouched. On the bench the repaired image ran past frame 2,400 with exact checkpoints, three deliberately forced rollbacks restored exactly, HSTX intervals steady at 16,666 to 16,667 us, and zero faults of any kind. The band is gone from the physical screen.
Where it landed
The repaired Candidate H is the new accepted baseline, and the numbers held across independent cold-reset runs:

Dense checkpoints that cost 12.81M, 13.13M and 13.62M cycles under Candidate E now cost 9.15M, 9.26M and 9.72M: savings of 3.66M to 3.90M cycles per frame, 78 to 88% of the deadline deficit, for a cycle-equivalent 49.4 to 52.4 fps. In a steady 1,800-frame run the transaction committed 1,626 times with zero natural mispredictions; checkpoint copying averaged 30.3K cycles per frame with an 88.6K maximum. A forced misprediction costs one bounded frame of restore and replay, about 9.33M cycles, and on the measured Super Mario World predictor it is never paid.
I am deliberately careful with the claim: this is 49 to 52, and 60.098814 means 60.098814. The dense windows still sit 1.17M to 1.73M cycles over budget. The largest named pool is 0.44M to 0.47M cycles per frame of transaction administration that runs even when the frame validates synchronously; the journal micro-variants above establish that clawing it back needs a different storage primitive, since rearranging the current one made everything slower. After that comes the remaining independent non-APU work. The staircase rule that has governed this whole programme still applies: every step must be exact, measured on hardware, and stackable, and Candidate H is now the step everything else stands on.