6502 by dbhqGitHub

Status

Where it stands

Every figure here is read from the latest test run, or from a dated measurement.

0 of 51

Machines implemented

0 in progress.

5

Variants of the core

Each proven against its own set of Tom Harte's tests.

1,480

Tests passing

0 failed, 0 skipped.

1,278

Harte opcode tests

Each runs the recorded cases for one opcode, cycle by cycle.

12

Dormann builds

Whole programs that check their own results.

150

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

SuitePassedFailedSkipped
CmosInterruptTests400
CompletenessTests1000
CpuTests700
DormannTests1200
HarteRunnerTests600
NestestTests200
Nmos6502Harte25600
Ricoh2A03Harte25600
Rockwell65C02Harte25600
Synertek65C02Harte25600
TransistorModelTests15000
WaitAndStopTests1100
Wdc65C02Harte25400

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.

BuildBest, MHzTimes a 2 MHz machineRange, MHz
Native108.054.0103.2 to 108.0
Browser, interpreter5.02.51.9 to 5.0
Browser, ahead of time48.924.429.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.

This site uses Google Analytics to count how many people read these pages. No cookie is set unless you accept, and every page works exactly the same either way.