Skip to content

ADR 0014 — v1.11.0: 65816 interrupts, remote bank reads, WASM 65816

  • Status: Accepted
  • Release: v1.11.0 (2026-07-08)
  • Theme: The post-v1.10.0 tail — finishing the 65816's hardware-interrupt correctness, extending bank-awareness to the remote DAP path, and exposing the 65816 in the hosted WASM playground. Per-release decisions fold in here.

D1 — 65816 hardware interrupt handling (#511/#512)

  • Context: the 65816 core (#456) is Tom Harte-validated for all 256 opcodes (state + full pin bus trace), but the corpus is opcodes-only — it never tests hardware IRQ/NMI. serviceVector (cpu/cpu.go) is 6502-only, so a hardware interrupt on VariantW65816 used the 6502 vectors ($FFFE/$FFFA) even in native mode, pushed via the page-locked 8-bit stack, and never pushed PBR or cleared D.

  • Decision: serviceInterrupt816(vecEmu, vecNat) mirrors the existing brk/cop (cpu/ctrl816.go) minus the signature-byte read, pushing P with the B bit clear (hardware, not a software BRK). Emulation pushes PC16+P at $FFFE/$FFFA (7 cycles); native pushes PBR+PC16+P at $FFEE/$FFEA (8 cycles). Both force PBR=0, set I, clear D, and read the vector through the 24-bit bus (pinVector). serviceNMI/serviceIRQ branch to it for the 65816.

  • Consequences: 6502/CMOS/NES interrupt paths are byte-for-byte untouched. ABORT ($FFF8/$FFE8) is out of scope — no abort source exists (tracked in the epic #517 / #518). Interrupt-entry per-cycle bus-trace validation is deferred (#519) — the entry passes state + cycle-count checks, but Harte gives no hardware-interrupt reference.

D2 — Remote 65816 bank reads over DAP (#510/#516)

  • Context: the bank-aware bus (#505, ADR 0013) made the local source and the DAP server bank-aware, but RemoteSource (internal/tui/source_remote.go) still served all memory from its 64 KiB bank-0 mirror, so a remote 65816's banks 1-255 were invisible over -dap-attach.

  • Decision: RemoteSource.ReadMemory/Disassemble branch on the bank — bank 0 stays on the fast local mirror (reconciled by RefreshMemory + #440 dirtyRanges, no per-frame round-trip); banks 1-255 fetch lazily over the wire via s.client (the remote server is bank-aware since #505/#507).

  • Consequences: no mirror widening and no cross-bank dirtyRanges needed — the earlier ADR-0013 plan (a lazy per-bank page cache) proved unnecessary at this scale. Memory-panel windows never straddle a bank (memWindowFor clamps within 64 KiB), so the per-window bank test is exact. Correctness-on-paper: no remote 65816 host exists in-tree (-dap-attach targets stay 6502/NES), but the path is right and tested (TestRemoteSource_ReadMemory_BankOverWire).

D3 — 65816 in the WASM playground (#514/#515)

  • Context: the hosted playground (cmd/chippy-wasm + web/) could select nmos/65c02/nes but not the fully-built 65816 — jsSetVariant lacked the case and rebuild() never called SetBus24.

  • Decision: add the 65816 variant case + Banked24 wiring in rebuild() (bank 0 through MMIO so the readMem/disasm panes stay accurate), the WDC 65C816 dropdown option, and a native-mode demo. The demo (web/demos/hello816.bin, source example/hello816.s) is hand-assembled and execution-verified against chippy's own 65816 core by example/gen_hello816.go (go run, no cc65 on PATH). loadDemo applies a demo's variant before loading.

  • Consequences: existing bank-0 demos run on the 65816 immediately (emulation mode is 6502-compatible). The browser panes stay bank-0 — a playground bank selector is deferred (#521). The go run generator is the demo's provenance + regression check, kept in-tree under //go:build ignore.