Ports in mode on change audio samples (CPU cycles of the original are not spent) #11

Closed
opened 2026-10-29 08:14:32 +00:00 by Gatchan · 2 comments
Owner

Found while validating battle/Rand (first port, branch registry).

Route tests/inputs/newgame_first_battle.txt, 12 000 frames, US v1.0:

  • shadow: 13 calls, 13 match.
  • mode on vs interpreter only: picture hash identical (BC2A27B4AA629945), audio hash differs (DDA5EA5278AFFED9 vs 86CF3C9222C5400D).

Cause (to confirm): the bridge runs the C instantly, without the cycles the original routine spends. The SPC700 runs in parallel on its own clock synchronised to CPU cycles, so later APU port writes reach it a few cycles earlier and the resampled output shifts. This is the Chrono Trigger lesson in lessons.md (APU handshakes depend on timing the per-call shadow cannot see) and consistent with decision D1 (native timing differs from the original).

Plan:

  1. Measure: first differing frame, max sample delta, and whether WRAM/VRAM/CGRAM/OAM stay identical every frame (test rom.ports.* now checks picture + all memories each frame and reports audio).
  2. Define a timing-independent audio check for ports: compare the sequence of values written to the APU ports per frame (sound commands), and the SPC700 RAM at frame ends, instead of PCM samples.
  3. Keep PCM comparison for the interpreter-only per-frame oracle.
Found while validating `battle/Rand` (first port, branch `registry`). Route `tests/inputs/newgame_first_battle.txt`, 12 000 frames, US v1.0: - shadow: 13 calls, 13 match. - mode on vs interpreter only: **picture hash identical** (`BC2A27B4AA629945`), **audio hash differs** (`DDA5EA5278AFFED9` vs `86CF3C9222C5400D`). Cause (to confirm): the bridge runs the C instantly, without the cycles the original routine spends. The SPC700 runs in parallel on its own clock synchronised to CPU cycles, so later APU port writes reach it a few cycles earlier and the resampled output shifts. This is the Chrono Trigger lesson in `lessons.md` (APU handshakes depend on timing the per-call shadow cannot see) and consistent with decision D1 (native timing differs from the original). Plan: 1. Measure: first differing frame, max sample delta, and whether WRAM/VRAM/CGRAM/OAM stay identical every frame (test `rom.ports.*` now checks picture + all memories each frame and reports audio). 2. Define a timing-independent audio check for ports: compare the sequence of values written to the APU ports per frame (sound commands), and the SPC700 RAM at frame ends, instead of PCM samples. 3. Keep PCM comparison for the interpreter-only per-frame oracle.
Author
Owner

Measured with the test rom.ports.US_V10 (PR #12), port battle/Rand on vs interpreter, same seed and inputs:

frame
first Rand call 8776
first audio difference 8784 (686 frames differ by 9500, max sample delta 7426)
first memory difference at a frame where both CPUs have the same PC and S 8792: $7E:131E 4D vs 4E
pictures identical through 9500

$7E:131E is the CPU-side sound driver's transfer state (sound code runs with D=$1300). So it is not only PCM resampling: game state that depends on the CPU/SPC700 handshake timing takes another path when a port does not spend the original's cycles (timing census case E8).

Decision taken (documented in docs/validation.md): the per-call shadow is the exact criterion for a port; the on-mode divergence is reported by the test, not required. Whole-game behaviour is validated by routes and semantic checkpoints (decision D1).

Left open: whether scaffold builds should keep frame-exactness by charging the original's cycles in bridges (rejected by the brief as "per-port cycle cost estimation"; would only matter for scaffold-phase frame comparisons). Keeping this issue open until routes/checkpoints exist to confirm game behaviour does not diverge (e.g. battle outcome identical).

Measured with the test `rom.ports.US_V10` (PR #12), port `battle/Rand` on vs interpreter, same seed and inputs: | | frame | |---|---| | first `Rand` call | 8776 | | first audio difference | 8784 (686 frames differ by 9500, max sample delta 7426) | | first memory difference at a frame where both CPUs have the same PC and S | 8792: `$7E:131E` 4D vs 4E | | pictures | identical through 9500 | `$7E:131E` is the CPU-side sound driver's transfer state (sound code runs with D=`$1300`). So it is not only PCM resampling: **game state that depends on the CPU/SPC700 handshake timing takes another path** when a port does not spend the original's cycles (timing census case E8). Decision taken (documented in `docs/validation.md`): the per-call shadow is the exact criterion for a port; the on-mode divergence is reported by the test, not required. Whole-game behaviour is validated by routes and semantic checkpoints (decision D1). Left open: whether scaffold builds should keep frame-exactness by charging the original's cycles in bridges (rejected by the brief as "per-port cycle cost estimation"; would only matter for scaffold-phase frame comparisons). Keeping this issue open until routes/checkpoints exist to confirm game behaviour does not diverge (e.g. battle outcome identical).
Author
Owner

Closed by PR #21. With battle/Rand on, the behaviour-driven route title_to_first_battle_win reaches its goal at the same frame as the interpreter-only movie, with identical semantic checkpoints and even identical end picture/WRAM/SRAM hashes (US 10253, JP 9556). The divergence of the blind input script (sound driver $7E:131E, audio) stays reported by rom.ports.*; whole-game behaviour is guarded by the new route_ports_on.* tests.

Closed by PR #21. With `battle/Rand` on, the behaviour-driven route `title_to_first_battle_win` reaches its goal at the same frame as the interpreter-only movie, with identical semantic checkpoints and even identical end picture/WRAM/SRAM hashes (US 10253, JP 9556). The divergence of the blind input script (sound driver `$7E:131E`, audio) stays reported by `rom.ports.*`; whole-game behaviour is guarded by the new `route_ports_on.*` tests.
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Gatchan/FF6Portage#11
No description provided.