The simulator

A transistor-level simulation of the MOS 6502, in Rust and WebAssembly, with a WebGL renderer of the actual die. It is a ground-up rebuild of visual6502, whose die trace and simulation approach it is built on.

Nothing here models 6502 behaviour. There is no instruction decoder, no addressing-mode table, no cycle-count lookup. There are 1725 wires and 3510 switches, and the behaviour falls out of simulating them. Every register value you see is read back out of storage nodes on the die; every cycle count is emergent.

What it does

  • Runs the real chip. Switch-level simulation of the revD die, verified bit-exact against the original implementation.
  • Shows the die. 83,227 triangles of real polygon geometry, with live logic state, at any zoom.
  • Traces signals. Click any wire to see what it is connected to at that instant: the connected group changes as transistors switch.
  • Steps backwards. Keyframed rewind over the last 4096 half-cycles.
  • Exposes the microarchitecture. Internal T-states, clock phase, the bus handshake, and the ALU's hold register, including things behavioural emulators paper over.

The result that is in no register

The 6502 does not put an ALU result into the accumulator when the instruction ends. ADC reaches the next opcode fetch with the accumulator still holding the old value; the result sits in the ALU hold register and transfers a cycle later. LDA, which bypasses the ALU, lands a cycle earlier.

Step through LDA #$50 / CLC / ADC #$50 one half-cycle at a time and watch where A actually changes. An emulator that commits results at instruction boundaries cannot show you this, because it is not true of the silicon.

How it is put together

CrateRole
v6502-netlistImmutable topology: nodes, transistors, names. No state.
v6502-simSwitch-level solver, 6502 clock and bus layer, rewind.
v6502-wasmwasm-bindgen surface.
web/WebGL2 renderer and UI. Plain ES modules, no framework, no build step.

A node's logic level is not a property of the node but of the group of nodes currently shorted together through conducting transistors. Settling means rebuilding groups, resolving each to a level, propagating, and repeating to a fixed point.

The renderer turns on one fact: the layout never changes. The triangles go to the GPU once; each frame uploads only a 1725-byte array of node levels as a texture the vertex shader samples by node ID. A frame is six draw calls, regardless of zoom.

Speed

About 28,500 half-cycles per second natively, roughly 94 times the original JavaScript implementation. That figure is the native engine, not the served API: the API page carries the per-instance number, which is lower and measured separately.

Licensing, before redistributing

The code is MIT. The chip data it is built from is not. segdefs.js and transdefs.js in the visual6502 submodule, which are the polygon and transistor geometry, are CC BY-NC-SA 3.0, copyright 2010 Greg James, Brian Silverman and Barry Silverman, with attribution required to Greg James and www.visual6502.org.

That matters because the build derives from that data. The generated netlist.bin and layout.bin, any .wasm embedding them, and any deployed instance all inherit NonCommercial and ShareAlike terms. The repository does not redistribute the data, which is why it is a submodule, but a build does.

In short: fork it, learn from it, host it non-commercially with attribution. A commercial use would need the geometry re-derived from an independent die trace, or separate permission from the rights holders.

halfphi is the exception and is MIT with no such obligation, because it names no chip and embeds no die data.

Credit

This exists because the visual6502 team decapped a 6502, photographed the die, and traced every polygon by hand, then gave it away. Greg James, Brian Silverman, Barry Silverman, Ed Spittles, Segher Boessenkool, Achim Breidenbach, and everyone else who contributed.