Status
Where it stands
Every figure here is read from the latest test run, or from a dated measurement.
Machines implemented
0 in progress.
Variants of the core
Each proven against its own set of Tom Harte's tests.
Tests passing
0 failed, 0 skipped.
Harte opcode tests
Each runs the recorded cases for one opcode, cycle by cycle.
Dormann builds
Whole programs that check their own results.
NMOS interrupt runs
Each checked cycle by cycle against a model of the 6502's transistors.
Test run on commit 1393d10, 1 October 2026.
Test suites
| Suite | Passed | Failed | Skipped |
|---|---|---|---|
| CmosInterruptTests | 4 | 0 | 0 |
| CompletenessTests | 10 | 0 | 0 |
| CpuTests | 7 | 0 | 0 |
| DormannTests | 12 | 0 | 0 |
| HarteRunnerTests | 6 | 0 | 0 |
| NestestTests | 2 | 0 | 0 |
| Nmos6502Harte | 256 | 0 | 0 |
| Ricoh2A03Harte | 256 | 0 | 0 |
| Rockwell65C02Harte | 256 | 0 | 0 |
| Synertek65C02Harte | 256 | 0 | 0 |
| TransistorModelTests | 150 | 0 | 0 |
| WaitAndStopTests | 11 | 0 | 0 |
| Wdc65C02Harte | 254 | 0 | 0 |
Speed
How many cycles a second the core runs, as a multiple of a 2 MHz machine. The design's target is at least 25 times real speed, for the core alone, and it does not say which build that means. In this collection of 5 runs, against this site's reference (a 2 MHz machine, the BBC Micro's clock), the best native run is 54.0 times: met. In the same collection the best browser run, compiled ahead of time, is 24.4 times: not yet met. A different collection of runs gives a different best, so these are one collection's figures. The reference is this site's choice, not the design's, and the verdicts assume it: the KIM-1 runs at 1 MHz, so against that machine every multiple would be 2 times as large. Measured on 30 September 2026 on a KVM virtual machine, DO-Premium-AMD, 8 cores. The browser rows of the table ran in Chrome 153.0.8010.47; the Native row ran directly on the machine, in no browser. A synthetic 6502 program of our own, run on a plain 64 KB array with no logging: 100 million cycles per run after a 5 million cycle warm-up. One machine, one day: the figures are a record, not a promise.
| Build | Best, MHz | Times a 2 MHz machine | Range, MHz |
|---|---|---|---|
| Native | 108.0 | 54.0 | 103.2 to 108.0 |
| Browser, interpreter | 5.0 | 2.5 | 1.9 to 5.0 |
| Browser, ahead of time | 48.9 | 24.4 | 29.1 to 48.9 |
Where the core knowingly differs
Where the core knowingly differs from a reference it is tested against, or where no reference we trust exists. Each entry says what, why, and how the tests treat it. It was written with the plan, after the plan’s code had been run against every reference, and is kept true as the code lands.
The 65C02’s extra decimal cycle, in immediate mode
What. On the three 65C02 variants, ADC #imm ($69) and SBC #imm
($E9) take one extra cycle when the decimal flag is set. Tom Harte’s data
records that cycle as a read of a fixed address: $007F, $0059 or $0056
for ADC on WDC, Rockwell and Synertek, and $0000 for SBC on all three.
Why we differ. A fixed address that changes between chips and between two sibling instructions looks like a property of the program that generated the data, not of the chip. In every other addressing mode the same extra cycle re-reads the operand’s address, so the core does that here too: it re-reads the immediate byte.
How the tests treat it. For those two opcodes, on those three variants, with the decimal flag set, the comparison checks that the third cycle exists and is a read, and does not compare its address or value. Everything else in those cases is compared as normal.
Synertek’s bit-instruction opcodes, where two references disagree
What. On the Synertek 65C02, which has no RMB, SMB, BBR or BBS,
the opcodes in columns 7 and F are no-ops. Harte’s data says the column 7
opcodes are two bytes long and read zero page, and the column F opcodes three
bytes, with an extra cycle in odd rows. Klaus Dormann’s extended test, set up
to check them as no-ops, expects $07 to be one byte long.
Why we differ from Dormann. Harte’s data is the per-instruction authority in this project, and the core matches all of it. Which of the two is right about real Synertek silicon is not known here.
How the tests treat it. The Synertek build of Dormann’s extended test is
assembled with rkwl_wdc_op = 2, which is the test’s own setting for leaving
those opcodes out. Everything else in that test still runs.
WAI and STP
What. WDC’s WAI ($CB) and STP ($DB) have no data in Harte’s WDC
set, because neither can be tested one instruction at a time.
How the tests treat it. Tests of our own check what they do: WAI waits
for an interrupt and then either takes it or carries on, depending on the
interrupt-disable flag, and STP stops until reset. The cycle counts follow
WDC’s datasheet and are not checked against the chip. How many cycles WAI
takes to wake is not asserted, because no reference we trust gives it.
65C02 interrupt timing
What. The NMOS interrupt tests are checked against the Visual6502 transistor-level model, and the 65C02 uses the same timing rules in the core. There is no public transistor-level model of the 65C02 to check that against.
How the tests treat it. The 65C02’s own interrupt behaviour follows WDC’s
datasheet for the decimal-flag clear, the WAI and STP cycle counts and the
wake rules. It is not checked against the chip. That the 65C02 times
interrupts like the NMOS chip is assumed. The tests check the decimal flag
and the pushed P, and the WAI and STP cycle counts, wake outcome and
stop-until-reset; they hold no bus logs of a 65C02 taking an interrupt.
JAM
What. The NMOS JAM opcodes (for example $02) lock the chip up. Harte
records one step of one: the opcode fetch plus ten reads.
How the tests treat it. The core makes those eleven bus accesses and
matches Harte’s data. What happens after that is not checked against any
reference: the core reads $FFFF on every Step, ignores IRQ and NMI, and
is cleared by Reset.
Unstable NMOS opcodes
What. ANE, LXA, SHA, SHX, SHY and TAS give results on real
chips that vary between individual parts, and for ANE and LXA with
temperature.
How the tests treat it. The core matches Harte’s data, which fixes the
ANE and LXA constant at $EE. That is one answer, not every
chip’s.