How Computers Work
A computer is a kitchen. Tiny cooks flip switches on and off all day, and a few head chefs combine that work into meals. The trick is that nothing in the kitchen knows what a meal is. Each cook only knows whether the burner under them is on or off. The meal is what comes out when millions of cooks follow a recipe the head chef shouts down the line, one step at a time. Everything in this lesson — the transistors, the gates, the ALU, the memory, the bus — is one rung on the ladder from a single switch up to a working dinner service.

The smallest cook is a transistor. Bell Labs in New Jersey built the first one in December 1947. Three physicists — John Bardeen, Walter Brattain, and William Shockley — were trying to replace vacuum tubes, which were the size of light bulbs, ran hot enough to cook an egg, and burned out after a few hundred hours. They needed a switch that did the same job in less space and on less power. What they came up with was a sliver of crystal with three wires touching it. Push a tiny current into the middle wire and a much bigger current flowed between the outer two. Stop the middle current and the flow stopped. A switch with no moving parts, no heat, no glass to break. They got the Nobel Prize in 1956. The whole modern computer industry exists because a switch you cannot see with the naked eye is faster, cheaper, and cooler than a switch you can.

One transistor on its own does nothing useful. Wire a few of them together in the right pattern and you get a gate — a tiny machine that takes two on/off inputs and produces one on/off output. An AND gate fires only when both inputs are on. An OR gate fires when either is. An XOR fires when exactly one is. These three patterns, built from a handful of transistors each, are the entire alphabet the chip speaks. A man named Claude Shannon proved this in his 1937 master's thesis at MIT. He showed that the rules of Boolean algebra — true and false, AND and OR — map exactly onto switches and wires. Anything you can describe with a truth table you can build out of gates. Anything. Addition, subtraction, comparison, every recipe a chef ever shouted.

Stack a few gates and you get a circuit that adds two binary numbers. Stack a few thousand and you get an ALU, the arithmetic logic unit — the chopping board of the chip. The ALU has wires coming in for the two numbers, wires for the answer, and a control wire that picks the operation. Send it a bit pattern that means "add" and the gates add. Send it the pattern for "AND" and the gates AND. The ALU does not care what the numbers mean. It just shovels bits. Watch the same chopping board do four jobs in a row.
fn show_add(a: u8, b: u8) {
let sum = a.wrapping_add(b);
println!(
" add {:>3} + {:>3} = {:>3} {:08b} + {:08b} = {:08b}",
a, b, sum, a, b, sum
);
}
fn arithmetic_demo() {
println!("ALU: 8-bit unsigned addition (what the adder circuit does)");
println!(" a + b = sum a-bits b-bits sum-bits");
show_add(5, 3);
show_add(15, 1);
show_add(200, 50);
show_add(255, 1);
}fn show_bits(label: &str, a: u8, b: u8, result: u8) {
println!(
" {:<3} {:08b} {} {:08b} = {:08b} ({:>3})",
label,
a,
match label {
"and" => "&",
"or " => "|",
"xor" => "^",
"shl" => "<<",
_ => "?",
},
b,
result,
result
);
}
fn bitwise_demo() {
let a: u8 = 0b1100_1010;
let b: u8 = 0b1010_0110;
println!("ALU: bitwise gates on 8 wires at once");
show_bits("and", a, b, a & b);
show_bits("or ", a, b, a | b);
show_bits("xor", a, b, a ^ b);
let x: u8 = 1;
show_bits("shl", x, 4, x << 4);
}Compile and run it the same way you ran the program in the last lesson.
rustc main.rs
./mainThe first half of the output is the adder doing its job. The second half is the same hardware running the bitwise gates directly. Read the bits beside each line — that is the actual pattern of on-and-off wires the ALU sees.
ALU: 8-bit unsigned addition (what the adder circuit does)
a + b = sum a-bits b-bits sum-bits
add 5 + 3 = 8 00000101 + 00000011 = 00001000
add 15 + 1 = 16 00001111 + 00000001 = 00010000
add 200 + 50 = 250 11001000 + 00110010 = 11111010
add 255 + 1 = 0 11111111 + 00000001 = 00000000
ALU: bitwise gates on 8 wires at once
and 11001010 & 10100110 = 10000010 (130)
or 11001010 | 10100110 = 11101110 (238)
xor 11001010 ^ 10100110 = 01101100 (108)
shl 00000001 << 00000100 = 00010000 ( 16)
CPU: same 8 wires, two ways to read them
bits 00000001 as u8 = 1 as i8 = 1
bits 01111111 as u8 = 127 as i8 = 127
bits 10000000 as u8 = 128 as i8 = -128
bits 11111111 as u8 = 255 as i8 = -1Look at the fourth addition: 255 + 1 = 0. The ALU is not broken. It only has eight wires for the answer, and 256 does not fit. The carry rolls off the top and the bottom eight bits read zero. That overflow is the chip's first honest moment — its arithmetic is the arithmetic of an odometer, not a math textbook. Every CPU on Earth wraps the same way at the same boundary. Rust calls this wrapping_add, and you will spend a whole lesson on overflow later. For now, all you need to see is that the bit pattern 11111111 + 1 truly comes out as 00000000 on the wires.
The ALU shovels bits, but it has to be told which bits. That is what a register is for — a row of flip-flops, each holding one bit until the head chef asks for it. A modern CPU has a couple of dozen registers, each about 64 wires wide, sitting right next to the ALU. The CPU loads two numbers into two registers, points the ALU at them, runs the operation, and stores the answer back into a third register. Loading from a register takes one clock tick, faster than reaching for the salt. Loading from anywhere else takes a hundred to a thousand times longer. The register file is the prep counter inside arm's reach. Everything else is the walk-in fridge across the room.

The same 8 wires can mean different numbers depending on how the CPU reads them. The bit pattern 11111111 is 255 if you call it unsigned and -1 if you call it signed. The hardware does not change. The label does. The signed scheme is called two's complement and it was settled on in the early 1960s because it makes subtraction free — the same adder circuit that handles unsigned addition also handles signed addition, with no extra hardware, as long as the top bit means "this is negative." Look at four bit patterns through both lenses.
fn signed_view(bits: u8) {
let as_unsigned: u8 = bits;
let as_signed: i8 = bits as i8;
println!(
" bits {:08b} as u8 = {:>4} as i8 = {:>4}",
bits, as_unsigned, as_signed
);
}
fn signs_demo() {
println!("CPU: same 8 wires, two ways to read them");
signed_view(0b0000_0001);
signed_view(0b0111_1111);
signed_view(0b1000_0000);
signed_view(0b1111_1111);
}
Once you have an ALU and a stack of registers, you have a CPU — a small box that follows a fixed loop. Fetch the next instruction from memory, decode what it means, execute it on the ALU, store the answer. Fetch, decode, execute, store. That four-beat loop has run, billions of times per second, in every general-purpose computer since John von Neumann wrote down the design in 1945 in a draft report on the EDVAC machine at the University of Pennsylvania. Von Neumann's insight, borrowed from Alan Turing's 1936 paper on computable numbers, was that the program itself can live in the same memory as the data. The CPU does not know the difference. It just fetches the next thing from wherever the program counter says, and trusts that the bits there are an instruction. That is why a virus can take over your machine — bend the program counter to point at attacker-supplied data, and the CPU will happily execute it.

The fridge is RAM. Random access memory is a long row of tiny capacitors, each one charged to hold a single bit. Modern RAM holds tens of billions of bits in a stick the size of a stick of gum. The CPU asks for a byte by sending its 64-bit address down a set of wires called the address bus. The memory finds that byte and sends it back down another set of wires called the data bus. The trip takes about a hundred CPU cycles — fast for you, an eternity for the chip. Between the CPU and the fridge sit several layers of cache, each smaller and faster than the next, holding the bytes the CPU touched most recently. The first IBM mainframe to ship with a cache was the System/360 Model 85 in 1968, and the trick has been on every chip since.

The bus is the conveyor. It is just a bundle of parallel wires, one wire per bit, that any chip in the system can listen to or shout on. The CPU shouts an address, the memory listens, the memory shouts a byte, the CPU listens. The display, the SSD, the keyboard, the network card — every device on the motherboard hangs off some bus and waits to be addressed. The whole machine is a kitchen where the head chef shouts an order, the right station shouts back the ingredient, and the next instruction shouts the next order, ten billion times a second, with nothing in between but copper wires and switches that turn on and off.

A question. Look at the wrapped addition 255 + 1 = 0 in the output. If your chip's ALU is 64 wires wide instead of 8, what is the largest unsigned number you can add 1 to before it rolls back to zero? Two to the 64th power, minus one — about 18 quintillion. That is why your laptop's pointers are 64 bits. The fridge can hold more bytes than there are grains of sand on every beach on Earth, but the CPU still has to be able to count to the address of the last one.
The CPU is fast. The fridge is far. The next lesson is about all the layers between your Rust source code and the on-off pattern the gates actually see, and why every one of those layers exists.