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
| Crate | Role |
|---|---|
v6502-netlist | Immutable topology: nodes, transistors, names. No state. |
v6502-sim | Switch-level solver, 6502 clock and bus layer, rewind. |
v6502-wasm | wasm-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.