Chris Collins

Lab · Exploration

From transistor to CPU

Start with one transistor switching an LED, wire switches into gates, gates into an adder, feedback into memory, and memory plus a clock into a tiny CPU — then watch a real 6502 run a program you can read byte by byte.

Part 1 of 7 in How things work

· 15 min read

UsesCircuit simulatorDigital logic simulator6502 assemblerAtari 8-bit computer: emulator, BASIC, assembler and debugger

ElectronicsRetro computing

A computer is the most complicated thing most of us own, and it is made almost entirely of one very simple part, repeated. A 6502 — the processor in the Apple II, the Commodore 64, the BBC Micro and the Atari 800 — has about 3,500 transistors. A modern laptop chip has tens of billions. In both, each transistor does the same humble job: it is a switch that one wire can turn on and off.

This exploration climbs from that switch to a working processor in six steps, and at every step there is a live circuit on the page for you to poke. Nothing is hidden behind a diagram: each level is built, visibly, from the one below it. By the end you will have run a program on a CPU made of gates you can see, and then on a real 6502, and the two programs will look remarkably alike.

Step 1: a switch that a wire can flip

A transistor has three connections. In the bipolar kind used below, a small current into the base lets a much larger current flow from the collector to the emitter. Push a milliamp into the base and the transistor turns hard on; take it away and it turns off. For analogue work we care about the region in between, where the transistor amplifies. For digital work we deliberately avoid it: we only ever drive the transistor fully on or fully off.

Here is the simplest useful circuit there is. A pulse generator, Vin, swings between 0 V and 5 V every millisecond. It feeds the base of transistor Q1 through a 4.7 kΩ resistor. Q1’s collector pulls current through a red LED and a 220 Ω resistor from the 5 V supply.

What to try: scroll down inside the frame to the scope, then compare the two traces.Open full tool →

Scroll down inside the frame to the Transient (scope) plots. The orange trace is the input, the blue trace is vc, the voltage at the collector — the point between the LED and the transistor.

Try this:

  • Look at the two traces side by side. When vin is low, vc sits high, at about 4.2 V. When vin goes high, vc drops to almost nothing. The Currents plot shows why: about 13.5 mA flows through the LED only while the input is high.

  • Select Rb and change it to 100k, then press Run transient. Less base current means the transistor is no longer driven hard on, and vc no longer falls cleanly to zero. That in-between region is exactly what digital designers keep away from.

  • Put it back to 4k7 and change Rled to 1k. The LED current falls, but the shape of vc does not: on is still on, off is still off.

What to notice: the output is the opposite of the input. High in, low out; low in, high out. Read vc as the output and you have not just built a switch, you have built a logic gate — a NOT gate, or inverter. Everything else in this exploration is made of the same trick, applied over and over.

Real chips use MOSFETs rather than bipolar transistors, because a MOSFET’s gate draws almost no current, and they pair an n-type with a p-type transistor (CMOS) so that one is always off and the circuit draws almost no power when it is not switching. The palette has an NMOS part if you want to try building one. The idea is the same: a voltage on one wire decides whether another wire is connected.

Step 2: from switches to gates

Put two transistors in series, so current only flows when both are on, and the output is pulled low only when both inputs are high: that is a NAND gate. Put them in parallel, so either can pull the output low, and you have NOR. Add an inverter after either and you get AND and OR. From here on we stop drawing transistors and draw gates instead, the way chip designers do. It is the first abstraction on the climb, and it is a safe one: every gate below could be swapped for a handful of transistors without changing a thing.

What to try: flip A and B and watch all seven gates answer at once.Open full tool →

Two switches, A and B, feed seven gates. Bright wires and outputs are 1, dim ones are 0.

Try this:

  • With both switches off, notice that NAND, NOR, XNOR and NOT A are already lit. A gate whose name starts with N is the inverse of its namesake.

  • Flip A on. AND stays dark (it needs both), OR lights (it needs either), XOR lights (it wants them different).

  • Now turn B on too. XOR goes dark again, XNOR lights. XOR is the odd one out of the seven, and it is about to become important: it is the gate that adds.

What to notice: NAND on its own is enough to build every other gate — NOT is a NAND with its inputs tied together, AND is NAND then NOT, and so on. That is why early logic families sold chips full of nothing but NAND gates, and why "everything is NAND" is the usual first surprise in a digital course.

Step 3: gates that add

Add two one-bit numbers and there are only four cases: 0 + 0 = 0, 0 + 1 = 1, 1 + 0 = 1 and 1 + 1 = 10 in binary — that is, a sum bit of 0 and a carry of 1. Look at those sum bits: 0, 1, 1, 0. That is XOR. Look at the carries: 0, 0, 0, 1. That is AND. Two gates make a half adder.

It is only half an adder because it cannot accept a carry coming in from the column to its right. A full adder takes three inputs — A, B and a carry in — and makes a sum and a carry out. You can build one from two half adders and an OR gate.

What to try: read down the S and C columns, then the SUM and COUT columns.Open full tool →

This opens on the Truth table tab, which simulates the circuit for every combination of its inputs. The top part of the circuit is the half adder (inputs A, B, outputs S and C); the bottom part is a full adder packaged as a reusable component (inputs A2, B2, Cin, outputs Sum and Cout).

Try this:

  • Find the rows where A2, B2 and CIN are all 1. Both SUM and COUT are 1: one plus one plus one is three, which is 11 in binary.

  • Scroll down past the table: for each output the tool reads the logic back as an expression and minimises it. The full adder’s sum turns out to be a three-way XOR.

  • Switch to the Circuit tab and double-click the Full adder block to look inside it. A component is just a circuit with pins — the second abstraction on the climb.

Chain four full adders, each one’s carry out feeding the next one’s carry in, and you can add two four-bit numbers.

What to try: set A to 0111 and B to 0001 and watch the carry ripple up.Open full tool →

The switches come in pairs: A0/B0 is the lowest bit, A3/B3 the highest. The four sum bits and the final carry come out on the right, with the sum shown in hex.

Try this:

  • Turn on A0 and B1: 1 + 2. The display reads 3.

  • Now set A to 7 (A0, A1, A2 on) and B to 1 (B0 on). Every column produces a carry that the next column has to absorb, and the display jumps to 8.

  • Set every switch on: 15 + 15. The display shows E and Carry lights: 30 is 1 1110 in binary, one bit too many for four, so the fifth bit falls out as the carry.

  • Turn on Gate delays in the toolbar and repeat 7 + 1. Now each gate takes a moment, and you can watch the carry ripple from bit 0 to bit 3, the sum briefly showing wrong values on the way.

What to notice: this is a ripple-carry adder, and the ripple is its weakness. A 64-bit version has to wait for a carry to crawl through 64 stages. Real processors spend extra gates on carry-lookahead logic that works out all the carries at once. Speed in hardware is often bought with more transistors.

If you would rather write this than wire it, the Adders and hierarchy lesson of the HDL trainer has you build the same full adder and four-bit chain in Verilog, the language chips are actually designed in.

Step 4: memory from feedback

Everything so far has been combinational: the outputs depend only on the inputs right now. Turn the switches back and the answer goes back too. A computer also needs to remember — the number it is adding to, the instruction it is on — and the trick that gives gates a memory is wonderfully small. Feed a gate’s output back into its own input.

What to try: press Set, release it, and notice that Q stays on.Open full tool →

Two NOR gates, each feeding the other. Set and Reset are push buttons. When the page opens, the outputs are red: unknown. Nobody has told the circuit what to remember yet, and the simulator is honest about it.

Try this:

  • Hold Set for a moment and let go. Q goes to 1 and stays there, even though nothing is pressing it any more. Each gate is holding the other in place.

  • Press Reset. Q drops to 0, Q̄ rises, and that state holds too.

  • Press Set again while it is already set. Nothing changes. The latch only cares about which button was pressed last.

What to notice: that is one bit of memory, built from two NOR gates — eight transistors in CMOS. This SR latch is the ancestor of every register in a CPU. Add a little gating so that it only listens when a clock signal says so, and you have a flip-flop: a bit that changes only on the tick of a clock. Line up four flip-flops and you have a four-bit register. Wire their outputs back to their inputs through some logic and you have a circuit that steps through states on its own — the binary counter example does exactly that, counting 0 to 15 from four flip-flops and two AND gates.

The clock is the quiet hero of digital design. Between ticks, the combinational logic is free to glitch and ripple as much as it likes, as you saw in the adder. On the tick, every flip-flop captures the settled answer at once. A designer’s main job is to make sure the slowest path through the logic settles before the next tick; the length of that path sets the clock speed.

Step 5: a CPU you can see inside

We now have every ingredient. A register to hold a number. An adder to change it. A counter to step through a list. Some memory to hold that list. Put them together with one more idea, and you have a processor.

The idea is that the list is instructions. A counter, called the program counter or PC, points at a byte in a read-only memory. That byte is decoded: some of its bits say what to do (the opcode), the rest give a number to do it with (the operand). The decoded bits steer multiplexers — switches made of gates — that route the right values into the adder and the right result into the right register. On the next clock tick, the result is captured and the program counter moves on. Then it all happens again.

What to try: watch the two hex displays, then press Pause and step one clock cycle at a time.Open full tool →

This is a whole four-bit CPU in about two dozen parts. Find the pieces from earlier: the Counter on the left is the program counter, ROM holds the program, DEC decodes the opcode, the ALU is an adder that can also subtract, A is the accumulator register, C is the carry flag (a single flip-flop, with a yellow LED) and OUT is an output register. Hex displays show the PC, A and OUT. It starts running as soon as it loads.

The program in the ROM is four bytes long:

00   LDI 0      ; A ← 0
13   loop: ADD 3 ; A ← A + 3
20   OUT        ; OUT ← A
31   JMP loop   ; PC ← 1

Each byte is one instruction: the high hex digit is the opcode, the low digit the operand. So 13 means "opcode 1 (ADD), operand 3".

Try this:

  • Watch the OUT display, on the far right: 3, 6, 9, C, F, then 2. Four bits can only count to 15, so 15 + 3 wraps round to 2 — and the carry flag notes that it happened.

  • Press Pause, then Cycle to advance one clock period at a time. The display under the counter is the PC; watch it go 1, 2, 3, then jump back to 1.

  • Turn on Gate delays and run slowly. Every instruction becomes a little wave of activity spreading from the ROM through the decoder and ALU, settling before the next tick.

  • Select the ROM to edit the program. Try LDI 5, l: OUT, SUB 2, JC l, HLT. It counts 5, 3, 1 and then stops, because the third subtraction borrows, clears the carry flag, and the conditional jump JC falls through to the halt.

What to notice: the program is data. Nothing in the hardware changed when you edited the ROM, yet the machine did something different. That is the whole point of a processor, and it is why the same chip can run a word processor, a game, or this web page. The second program also shows the other essential ingredient: a conditional jump. A machine that can choose what to do next based on a result is, given enough memory, able to compute anything computable.

Step 6: a real 6502

The 6502 was launched in 1975 at a price of $25, when its rivals cost several times as much, and it ended up in a huge share of the home computers and consoles of the next decade. It is an eight-bit processor rather than a four-bit one, with three registers (A, X and Y), a stack, more addressing modes, and around 150 opcodes instead of eight. But at heart it is our tiny CPU, grown up: a program counter, a decoder, an ALU, an accumulator and a handful of flags.

Here is the "count in threes" program again, written for the 6502.

What to try: read the listing, the address and the bytes of each instruction, then compare it with the tiny CPU's ROM.Open full tool →

The listing on the right shows each instruction’s address and the bytes it assembles to: A9 00 for lda #0, 18 for clc, 69 03 for adc #3, 8D 00 02 for sta $0200 and 4C 02 06 for jmp loop. Eleven bytes in all.

Try this:

  • Compare it with the tiny CPU’s ROM. Load, add, output, jump: the same four ideas. The 6502’s instructions are longer because its addresses are sixteen bits, so jmp loop needs two bytes to say where loop is — 02 06, low byte first.

  • Notice the extra clc (clear carry). The 6502 has no plain add: adc always adds the carry flag too, so that a sixteen-bit sum can be built from two eight-bit ones. Delete the clc and the program may count in fours whenever the previous sum overflowed.

  • Switch to the Opcode table tab and filter for ADC. There are eight versions of it, one for each way of finding the number to add, each with its own cycle count.

Assembling is only half of it. Below is an Atari 800XL, emulated in WebAssembly, with a 6502 that runs the real Atari operating system. The example program is fifteen lines long and does something no amount of reading quite prepares you for.

What to try: press Assemble & run, then read the loop on lines 14 to 19.Open full tool →

Try this:

  • Press Assemble & run. The machine powers on, the program is assembled and loaded at $2000, and the screen fills with scrolling bands of colour.

  • Read the loop. It reads VCOUNT, the number of the scan line the TV beam is drawing; adds the frame counter so the pattern moves; then sta WSYNC, which freezes the CPU until the beam reaches the end of the line; then stores the value in COLBK, the background colour. Every coloured stripe on the screen is one trip round a six-instruction loop.

  • Press Pause and open the Debugger tab. The registers you see there — PC, A, X, Y, S and the flags — are the 6502’s equivalents of the tiny CPU’s counter, accumulator and carry flip-flop. Press Step and watch the PC move through the loop one instruction at a time.

  • Replace adc RTCLOK with adc #0, or add asl a after lda VCOUNT to double the line number, and run it again. Small edits, very different pictures.

What to notice: that loop is racing a television. Each 6502 instruction takes two to six clock cycles, at about 1.8 MHz, and a scan line lasts 114 cycles. Programming these machines meant counting cycles the way we counted gate delays in the adder: the logic has to settle before the next tick, only now the tick is the edge of the picture.

The climb, looking back

Six steps, each a small idea:

  1. A transistor is a switch controlled by a wire, and wired the right way it is a NOT gate.

  2. A few transistors make NAND and NOR, and NAND alone can make every other gate.

  3. XOR and AND make a half adder; two of those make a full adder; a chain of them adds numbers.

  4. Feedback turns gates into memory; a clock makes that memory change in step.

  5. A counter, a ROM, a decoder, an adder and a register make a processor, and the program is just data in the ROM.

  6. Grow every part — wider words, more registers, more opcodes — and you have a 6502. Grow it again, with caches, pipelines and billions of transistors, and you have the chip you are reading this on.

At no point did anything magic happen. Each level is the one below it, wrapped up and given a name. The only genuinely surprising thing is how few kinds of idea it takes.

Where to go next

#transistor#logic gates#boolean logic#adder#flip-flop#latch#counter#cpu#6502#assembly#atari 800#digital logic#computer architecture