Skip to content

Add CPU model architecture with NCR 65C02 support - #288

Merged
highbyte merged 16 commits into
masterfrom
feature/cpu-model-architecture
Aug 12, 2026
Merged

Add CPU model architecture with NCR 65C02 support#288
highbyte merged 16 commits into
masterfrom
feature/cpu-model-architecture

Conversation

@highbyte

Copy link
Copy Markdown
Owner

Introduces an internal CPU model architecture and the NCR 65C02 as the first alternative CPU model, enabling 65C02 software (enhanced-Apple-IIe-era ProDOS titles) to run. Milestone M1 of the CPU model architecture feature.

Highlights

  • Internal CPU model concept: immutable CpuModelDefinition (identity, supported profiles, traits, table factories) selected once at CPU construction. Public API unchanged; OpCodeId/InstructionList remain the NMOS facade (documented as such).
  • Per-model descriptor dispatch: the per-instruction addressing switch and interface probes are replaced by a 256-entry per-model table of pre-composed handlers built at CPU construction — one array index + one delegate call on the hot path. Faster across the board: CPU_Run_1000Instructions −21%, representative C64 frame workload −14% (see benchmarks/.../RESULTS.md).
  • NMOS JMP ($xxFF) page-wrap bug implemented (the emulator previously had CMOS behavior while presenting as NMOS) via per-byte model handler overrides.
  • NCR 65C02 model (ncr65c02, the enhanced Apple IIe CPU): all 256 bytes defined (78 defined NOPs with documented sizes/cycles — no byte can jam a 65C02), 27 new opcodes (STZ, BRA, PHX/PHY/PLX/PLY, TRB/TSB, INC/DEC A, new BIT modes, (zp) addressing, JMP (abs,X)), redefined bytes ($9C = STZ abs), CMOS decimal ADC/SBC with valid N/Z flags (+1 cycle), D cleared on interrupt/BRK/reset entry, fixed linear JMP (abs) at 6 cycles, shift/rotate abs,X at 6+p cycles.
  • Verified by Klaus Dormann's test suites on Windows CI: the 65C02 extended-opcodes test (assembled from a pinned revision with Rockwell/WDC bytes as NOPs, per the base/NCR part) and the configurable decimal test compiled for 65C02 flag expectations, plus the existing NMOS functional/BCD tests.
  • Model-aware tooling: monitor and VS Code debug-adapter disassembly read per-model descriptors ($9C disassembles differently per model; new addressing-mode operand formats; listing advance uses per-model sizes).
  • System configuration: Apple II and Generic computer can select the CPU model (default NMOS 6502; validation rejects invalid model/profile combinations). C64/VIC-20 stay fixed NMOS.
  • Snapshots: cpu-6502 module v2 records the CPU model id and hard-rejects restoring onto a different model (before applying any state); v1 payloads restore as nmos6502, proven against a checked-in fixture written by the actual v1 writer. Apple II portable snapshot settings carry the CPU model (machine shape).
  • The CI test workflow can now be run on demand (workflow_dispatch).

Test coverage

  • ~230 new/updated tests: NMOS characterization baselines (decimal flags, interrupt-entry, reset), descriptor-table invariants, all 27 new 65C02 opcodes, CMOS decimal arithmetic, model-aware disassembly, config validation, snapshot model identity incl. historical v1 fixture.
  • Klaus 65C02 extended-opcodes + CMOS decimal tests run on Windows CI (skipped on non-Windows; AS65 assembler is Windows-only).

Performance

Benchmarked per milestone against a recorded baseline (Apple M1, see RESULTS.md): descriptor dispatch improved the hot path ~20%; adding the 65C02 model cost the NMOS path nothing; no new allocations (per-instruction paths remain allocation-free).

Design/history: design log feature document cpu-models-65c02 (M1 scope; M2 ordered bus accesses, M3 6510 port model, M4 port-state migration are follow-ups).

… INC/DEC A, new BIT modes, (zp) addressing, JMP (abs,X)
…t abs,X cycle change, and NMOS-facade doc comments
…ed pinned-revision test compiler; allow CI test workflow to run on demand
…nger timeout, tolerant listing-label parser, symbol addresses from listing
@sonarqubecloud

Copy link
Copy Markdown

@highbyte
highbyte merged commit 75d9678 into master Aug 12, 2026
10 checks passed
@highbyte
highbyte deleted the feature/cpu-model-architecture branch August 12, 2026 20:40
highbyte added a commit that referenced this pull request Aug 13, 2026
…pt entry) (#289)

Makes CPU instructions perform their real per-model bus access sequences
— every read and write in hardware order — so memory-mapped I/O with
access side effects behaves as it does on real machines. Follow-up to
the CPU model architecture PR (#288); milestone M2 of that work.

## Behavioral highlights

- **Read-modify-write sequences per model**: NMOS RMW instructions
(official and undocumented) perform read-write-write — the unmodified
value is written back before the result. The 65C02 performs
read-read-write. Exhaustively guarded by a sequence matrix test over all
66 NMOS and 28 65C02 memory-RMW opcode bytes.
- **Apple II language card is now Sather-correct** (write-enable
requires two consecutive odd READS; a write never completes the sequence
— previously a read+write could unlock). Combined with the RMW sequences
this makes `INC $C083` CPU-model observable: it unlocks the card on a
65C02 (read-read-write) but not on an NMOS 6502 (read-write-write) —
exactly as on real hardware.
- **NMOS indexed dummy reads**: reads that cross a page touch the
un-carried address first; indexed stores and RMW always do. Classic
consequence now emulated: `STA $DC0D,X` with X=0 silently acknowledges
pending C64 CIA interrupts through its dummy read (covered by an
acceptance test).
- **Zero-page pointer wrap**: `(zp,X)`, `(zp),Y`, and 65C02 `(zp)`
pointers at `$FF` take their high byte from `$00` (wrapping within zero
page) instead of `$0100` — a result-level fix on both models, same class
as the `JMP ($xxFF)` fix in #288.
- **Interrupt-entry sequences**: IRQ/NMI entry performs the two hardware
dummy reads of the next opcode byte before the stack pushes and vector
reads; BRK fetches its padding byte as a real bus access (pushed return
address unchanged).

## Design

- Sequences are composed at CPU-construction time per model (a
`PerformsIndexedDummyReads` model trait; single source of truth per
model); the 65C02 keeps its own read-read-write RMW handlers. No model
branches on the hot path, no tracing hooks on the memory path.
- Tests observe bus traffic via a `BusAccessRecorder` built purely on
the production memory-mapped-I/O mechanism
(`Memory.MapReader`/`MapWriter`).
- Language-card rules verified against Sather (Understanding the Apple
IIe, 5-23) via two independent emulator implementations before coding.

## Deliberately deferred (documented in code)

- 65C02-specific dummy-read addresses (the CMOS part re-targeted them;
no observable case yet).
- `zp,X` base-address dummy reads (not page-cross-related) and RTI/pull
stack dummies.
- Interrupt-entry cycle accounting (entry currently consumes 0 cycles,
real hardware 7) — pre-existing gap, discovered here; changing it shifts
C64/VIC-20 frame timing so it needs its own decision.

## Tests & performance

- ~40 new/updated tests: per-model sequence characterization, exhaustive
RMW matrix, pointer-wrap, language-card model divergence, C64 `$DC0D`,
interrupt-entry ordering.
- All suites green incl. Klaus NMOS functional/BCD locally and the Klaus
65C02 extended-opcodes test on Windows CI.
- Benchmarks: representative C64 workload and hot-path benchmarks at or
below the milestone baseline after all changes (per-part measurements
recorded during development; no benchmark moved ≥5%).
highbyte added a commit that referenced this pull request Aug 13, 2026
…ndlers (#291)

## What

Completes the CPU instruction-dispatch modernization started with the
per-model descriptor tables (#288) and ordered bus sequences (#289):
every opcode on both CPU models now executes through small operation
cores composed per addressing mode, or bespoke static handlers where
composition doesn't fit. The ~70 per-instruction classes and the
interface-probing composition path are deleted.

## Changes

- **Operation cores** (`InstructionCores`): one static method per
operation (loads/stores, transfers, logic, compares, shifts/rotates,
INC/DEC, ADC/SBC per model, TSB/TRB, flags, and all NMOS-undocumented
operations), bound to addressing modes by
`ComposeRead`/`ComposeStore`/`ComposeImplied`/`ComposeRmw`/`ComposeBranch`.
- **Bespoke static handlers** for instructions that don't fit
composition: shared (`SharedHandlers`: stack, flow, BRK, NOP),
NMOS-specific (`NmosHandlers`: JMP (addr) page-wrap bug, JAM), and
65C02-specific (`CmosHandlers`: JMP variants, PHX/PHY/PLX/PLY,
defined-NOP family).
- **`InstructionBindings`**: the single table both models compose their
256-entry descriptor tables from, with undocumented NMOS opcodes gated
by the CPU's compatibility profile at binding time.
- **`InstructionList` is now a projection**: its metadata is generated
from the NMOS model's descriptor table instead of the descriptor tables
being composed from instruction objects — the two views can no longer
drift. `InstructionDictionary`/`GetInstruction` and the `Instruction`
class hierarchy are removed (pre-1.0 clean break).
- **Deleted**: all `Instructions/*` classes, the four
`IInstructionUses*` interfaces, `IReadModifyWriteInstruction`,
`AddrModeCalcResult`, `InstructionExtraCyclesCalculator`, and the legacy
table composition. Net **−4.0k lines**.

## Behavior

Zero behavior change by design, verified per migration step against the
previous implementation:

- Byte-for-byte descriptor/metadata parity across all four compatibility
profiles (guarded by test).
- All test suites green on macOS and Windows (incl. Klaus Dormann
functional/BCD/65C02 tests and the exhaustive RMW bus-sequence matrix).
- Hot-path benchmarks at parity with the pre-migration baseline
(same-machine-state pairing); allocations unchanged.
- Live end-to-end check: C64 boots to BASIC and executes correctly in
the Avalonia desktop app.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant