The CPU Explained

September 4, 2026 • 10 min read

The CPU Explained
Table of contents

Part of the series:Computers Explained

The computer overview gave the CPU1 one paragraph: it fetches instructions and executes them, billions of times per second. This post opens that black box. We will look at the contract a CPU offers to software, the parts inside it, the exact loop it runs, and how lofty ideas like “function calls” and “the heap” reduce to bytes and registers.

The ISA: the contract between software and silicon

A processor does not understand Python, JavaScript, or even C. It only understands its own machine language. The instruction set architecture (ISA)2 is the contract that defines that language: which instructions exist, what registers are available, and how programs talk to the machine.

Each CPU family speaks one ISA. The most common in daily life are:

  • x863: very common in laptops and desktops.
  • ARM4: present in most phones.
  • RISC-V5: an open ISA growing in embedded hardware.

A program compiled for one ISA cannot run on another. That is why your phone cannot run laptop binaries, and why Apple spent years translating x86 apps to ARM. Same code, different contract, no execution.

Everything below uses x86 with ARM notes where they differ, since the concepts transfer.

Registers: the CPU’s workbench

The CPU cannot work directly on RAM for most instructions. Memory is too slow and too far. So it keeps a few slots on the chip itself called processor registers6. They are the fastest storage the CPU has, its hands and its desk.

Think of registers as named boxes, each holding one number. x86 has 16 general boxes (like rax, rbx, rcx) and a few with special jobs:

  • rip: holds the address of the next instruction to run.
  • rsp: points to the top of the stack.
  • rflags: stores small yes or no notes about the last calculation.

You do not need to memorize these names because what matters here is understanding what they are and what they are for.

Real example: when you write int a = 5, the value 5 waits in RAM. RAM can hold millions of values, but the CPU cannot calculate directly on RAM. So to use it, the CPU copies 5 into a register like eax, does the work there, then copies the result back to RAM.

Think of RAM as the pantry where all ingredients are stored, and the register as the cutting board holding only the one ingredient you are using right now.

Caches: data close by

Waiting for RAM costs hundreds of clock cycles. So between RAM and registers the CPU keeps two middle layers, called L1 and L2 caches7.

They have different jobs. Registers hold the few values the CPU is using right now, with names like rax. Caches hold copies of RAM data the CPU will probably need soon, with no names, managed automatically. On a miss it waits for RAM and saves a copy for next time.

RAM (big, slow)
  ↓
L2 cache (smaller, closer)
  ↓
L1 cache (even smaller, even closer)
  ↓
registers (the values in use now)

The ALU: where the math happens

The ALU8 does the actual calculation. It takes two numbers plus an operation (add, subtract, AND, OR) and returns a result.

The ALU is built from the circuits of the previous post:

  • Adder does addition, and subtraction reuses the same adder by flipping the number to its two’s complement form.
  • Logic gates do AND, OR, and XOR.
  • A multiplexer at the end picks which result to keep.

Many instructions also leave small notes in rflags as a side effect:

  • ZF: result was zero.
  • SF: result was negative.
  • CF / OF: number overflowed.

Real example: read add rax, rbx as a short order with three parts: what to do (add), where to save the answer (rax), and which ingredient to use (rbx).

rax holds 10, rbx holds 7.
Command: add rax, rbx
Result: rax now holds 17, and rbx still holds 7.

The cmp rax, rbx instruction saves no new number. Internally it does rax - rbx and throws the result away, keeping only the notes in the flags. The next instruction doesn’t look at rax or rbx again; it reads only those notes to decide whether to jump or keep going straight.

rax and rbx both hold 10 -> note says "equal", so je jumps
rax holds 10, rbx holds 7 -> note says "not equal", so je stays

The control unit: the conductor

It turns a passive byte pattern in memory into coordinated action. Registers are the workbench and the ALU is the tool, but someone must decide what to do each moment.

The control unit9 reads the current instruction, decodes its bits (with a decoder, from the previous post), and sends control wires to the rest of the chip: send these two registers into the ALU, select addition, write the result back there, move to the next instruction.

Simple RISC CPUs do this with direct hardwired logic. Big x86 chips first break complex instructions into smaller RISC-like steps called micro-operations, then schedule those. Either way the job is the same.

Real example: for add rax, rbx (with rax = 10 and rbx = 7), the control unit says: connect box rax and box rbx to the ALU inputs, select ADD, write the ALU output (17) back into box rax, advance rip to the next instruction.

State machines: how the CPU remembers what it is doing

The control unit has a problem that combinational logic alone cannot solve. A decoder or a mux forgets everything instantly. But running an instruction takes several steps, so the CPU must remember which step it is on.

It solves this with a finite state machine10. That is a fancy name for a simple idea: a current state stored in flip-flops, plus combinational logic that decides the next state. The CPU lives in one state at a time and moves on each clock tick.

Real example: a traffic light is a state machine with three states: red, green, yellow. Red always goes to green, green always goes to yellow, yellow always goes to red. The CPU control unit works the same way. That stored state is what lets a pile of forgetful gates behave like a cook following a recipe step by step.

The instruction cycle: fetch, decode, execute

It runs one instruction after another, forever. This loop is the instruction cycle11. It is the state machine above in action:

FETCH -> DECODE -> EXECUTE -> FETCH -> DECODE -> ...
  1. Fetch. Copy the bytes at the address in rip into the CPU, and move rip past them.
  2. Decode. Look at those bytes: which operation, which registers, which memory address.
  3. Execute. Move data through the ALU or memory, write results back, update flags, and either keep going forward or jump rip somewhere new. After EXECUTE the machine returns to FETCH, unless a jump changed rip.

A 3 GHz clock ticks three billion times per second, but that does not mean three billion instructions per second. Modern CPUs finish several instructions per tick or take several ticks per instruction. The fetch, decode, execute contract stays the same.

Instruction anatomy

All instructions share the same basic structure:

  • Opcode: what to do.
  • Operands: what to do it with.

x86 instructions range from 1 to 15 bytes to keep common cases tiny. ARM64 instructions are typically a fixed 4 bytes, which makes decoding simpler. Operands come in three flavors:

  • Immediate: the value is written right in the instruction. add rax, 5 means rax = rax + 5.
  • Register: the value is in another box. add rax, rbx means rax = rax + rbx.
  • Memory: the value is in RAM. Square brackets mean “go to RAM”. mov rax, [rbx+16] takes the number in rbx, adds 16 to find an address, reads the 8 bytes stored there, and copies them into rax. This is how arrays (array[i]) and structs (object.field) work underneath.

Calling conventions and the stack

Caller and callee must agree on where arguments go. That agreement is the calling convention12.

On Linux and macOS, integer arguments go in specific registers (rdi, rsi, rdx and so on), and the answer always comes back in rax.

So a function knows where to return after it finishes, we use the stack13. It is just space in RAM controlled by the rsp register (remember it?).

Think of it like a pile of plates: push puts a plate on top, pop takes one off. The call instruction pushes the return address and jumps. The ret instruction pops it and goes back to where it was.

The heap: memory on request

The heap gets memory while the program runs, when you do not know the size in advance.

The heap is a region of memory managed by library code like malloc, which asks the operating system for large chunks (on Linux with brk and mmap) and then splits them into pieces. malloc and free are not CPU features. They are bookkeeping lists of free blocks.

At machine level there are no variables, no objects, no heap. There are only addresses, values, and conventions. malloc(64) just finds 64 free bytes somewhere and returns the address.

Doing more at once: pipeline and cores

So far we described one executor running one instruction at a time. Modern CPUs go faster with two simple tricks.

Pipeline14: Instead of finishing fetch, decode, execute for one instruction before starting the next, the CPU overlaps them like an assembly line. While one instruction executes, the next decodes and the one after fetches. That overlap is why a CPU can finish more than one instruction per clock tick.

Cores15: Almost every processor today has several independent executors, called cores, each running its own fetch, decode, execute loop. One core is still the lonely executor from this post, but four or eight cores run four or eight loops in parallel.

The big picture

A CPU is a contract (the ISA), a workbench (registers), a calculator (the ALU), a conductor (the control unit), and a state machine running fetch, decode, execute on loop:

ISA (contract)
  ↓
registers (workbench)
  ↓
ALU (calculator)
  ↓
control unit (conductor)
  ↓
FETCH -> DECODE -> EXECUTE (state machine loop)

Stack, heap, and function calls are conventions built on addresses, values, and control flow. How an operating system turns this lonely executor into a machine running a hundred programs at once is another post.

Footnotes

  1. Central processing unit - Wikipedia

  2. Instruction set architecture - Wikipedia

  3. x86 - Wikipedia

  4. ARM architecture - Wikipedia

  5. RISC-V - Wikipedia

  6. Processor register - Wikipedia

  7. CPU cache - Wikipedia

  8. Arithmetic logic unit - Wikipedia

  9. Control unit - Wikipedia

  10. Finite-state machine - Wikipedia

  11. Instruction cycle - Wikipedia

  12. x86 calling conventions - Wikipedia

  13. Call stack - Wikipedia

  14. Instruction pipelining - Wikipedia

  15. Multi-core processor - Wikipedia