ADR 0015 — v1.12.0: 65816 tail — bank navigation + interrupt-timing validation¶
- Status: Accepted
- Release: v1.12.0 (2026-07-10)
- Theme: Draining the 65816 follow-up epic (#517) — bank-navigation UX in the playground and the TUI, plus per-cycle validation of hardware-interrupt timing. Per-release decisions fold in here.
D1 — Playground bank selector (#521/#523)¶
-
Context: the WASM playground exposed the 65816 (ADR 0014 D3) but its memory/disassembly panes were bank-0 only — no way to inspect banks 1-255 of a running 65816 program in the browser (the TUI already had
:bank). -
Decision: widen
jsReadMem/jsDisasm(cmd/chippy-wasm) to 24-bit addresses via areadByte24helper (banks 1-255 throughBanked24, bank 0 through RAM) and abankBus(a 16-bitcpu.BusoverreadByte24) forcpu.DisasmCPUAt;jsStateexposespbr. The web UI (web/) gains abankinput, shown only for the 65816, feeding the memory pane; disassembly anchors atPBR:PCfrom state; both panes render$BB:XXXX. -
Consequences: the browser panes reach the same bank-aware inspection the TUI has. The Go plumbing reuses the DAP-tested
DisasmCPUAt+ bank-view path, so there's no new disassembly logic — only the JS/wasm glue.
D2 — Pinned cross-bank disassembly (#520/#524)¶
-
Context: cross-bank disassembly (ADR 0013 D2) followed the live program bank PBR, so a pinned view (disasm-follow off) still used the live PBR — you couldn't hold or scroll the disassembly in a bank ≠ the one executing.
-
Decision: add
DisasmAnchorBank(persisted) and adisasmBank()helper — live PBR when following, the pinned bank otherwise — threaded throughsyncDisasm/disasmView/disasmScroll(the first pin captures the live PBR). A new:dacommand ($XXXX/$BB:XXXX/ symbol, viaparseDisasmTarget) pins the view to any bank:offset. -
Consequences: the pinned disassembly is a full 24-bit view; the header and row addresses show the pinned bank. 8-bit cores are unaffected (
disasmBank()returns 0). Cross-bank scrolling within the snapshot still clamps at bank edges (the DAP window is per-bank) —:dais the cross-bank jump.
D3 — Hardware-interrupt per-cycle bus trace (#519/#525)¶
- Context: the 65816 core is Tom Harte-validated for all 256 opcodes (state
-
full pin bus trace), but the corpus is opcodes-only — it never exercises hardware IRQ/NMI.
serviceInterrupt816(ADR 0014 D1) was cycle-count correct but emitted no bus cycles for the two leading internal cycles of interrupt entry, so nothing validated the per-cycle sequence. -
Decision: emit the two
io816()internal cycles (dummy reads at PBR:PC,pinNone) before the stack writes, per the WDC sequence. The 7/8-cycle count is added abstractly, so this surfaces only on the bus trace. A new non-Hartecpu/interrupt816_test.go(irqTraceBusrecorder) pins the full sequence against a hand-authored WDC reference: 2 internal → stack writes (PCH/PCL/P, native +PBR,pinData) → 2 vector reads (pinVector= VDA+VPB), for emulation IRQ and native NMI. -
Consequences: interrupt entry is now bus-exact, not just count-exact.
serviceInterrupt816is unreachable frombrk/cop, so the Harte state/count/bus-trace harnesses are unaffected. The remaining epic item, #518 ABORT, stays parked — no abort source exists in-tree to drive it.