ADR 0012 — v1.9.0: accuracy tail — per-cycle 65816 bus trace¶
- Status: Accepted
- Release: v1.9.0 (2026-06-29)
- Theme: The post-v1.8.0 accuracy tail. Opened by the first decision of the cycle; per-release decisions fold in here.
D1 — Per-cycle 65816 bus-trace validation (TestHarte65816BusTrace, #495)¶
-
Context: the WDC 65C816 core (#456, ADR 0010) was validated against the Tom Harte 65816 corpus for final state + cycle count only.
step816computed the cycle count abstractly (cyc := 2; cyc += extras) and issued only the functionalread24/write24accesses — the internal/dummy cycles the real chip drives on the bus (direct-page adds, index adds, RMW modify cycles, stack/branch/jump internal cycles) were counted but not emitted. The 6502 and 65C02 cores already had per-cycle bus-trace tests (TestHarte6502BusTrace#428,TestHarte65C02BusTrace#455/#475); the 65816 was the gap. -
Decision: add
TestHarte65816BusTrace, the 16-bit/24-bit sibling, and reworkstep816to emit every cycle's 24-bit bus access in hardware order. Delivered in four chunks mirroring the original core build: - register/flag/transfer/immediate (the
io816internal-cycle helper); - addressing-mode / ALU-memory — direct-page
+1(dpIO), dp-index and stack-relative adds at PC-1 (ioPC1), the abs/(dp)-indexed cross cycle at the un-carry-corrected address (indexIO/unfixedAddr), and (sr),Y's +Y re-read of the pointer high byte; - RMW, stack, control-flow — the RMW modify cycle is mode-dependent (emulation drives a dummy write of the original value, native a dummy read; 16-bit writes the result high byte then low), stack pushes take one internal cycle / pulls two, taken branches add an internal cycle (plus one on an emulation page-cross), and JSR/JSL/RTS/RTL/RTI/BRK/COP emit their interleaved pushes, signature-byte read, and vector reads;
-
the test iterates all 256 opcodes in both emulation and native, gated by
harteBusSkip816. The harness records eachRead24/Write24as[addr, value, rw](busRecorder816) and compares against the corpus cycle list, parsing the pin string for read/write and treating anullvalue (internal cycle) as a don't-care. -
Consequences: every opcode is per-cycle bus-exact in both modes except four in
harteBusSkip816:WAI/STPhalt with a None-address bus cycle the recorder can't represent, andMVN/MVPuse chippy's whole-block-move debugger model (the corpus caps each block-move case mid-instruction — the same generator artifact that excludes them from the state harness, ADR 0010 D6). The internal cycles are pure dummy reads/writes, soTestHarte65816(state + count) is unchanged and the 8-bit cores are unaffected (the helpers live only on thestep816path). The trace validates the full Harte cycle string — address + value + the 8 pin bits VDA/VPA/VPB/RWB/E/M/X/MLB. Each access tags its type viac.busPins(pinData/pinProg/pinNone/pinVector, plus MLB across RMW): the opcode fetch asserts VDA+VPA, operand fetches VPA, data/stack/pointer accesses VDA, internal cycles none, vector reads VDA+VPB, and RMW accesses MLB (its modify cycle MLB-only). E/M/X are snapshotted at instruction start so they reflect the pre-change flags (SEP/REP/XCE show their old widths). This is stricter than the 6502/65C02 traces (which the corpus gives only as addr+value+rw).