Play free

The machine

Design your own instruction set

An instruction set is a promise: “if you put these bits in memory, the machine will do this.” Every real ISA is a pile of trade-offs that someone made on purpose. Here are the big decisions, and how famous machines made them.

4 min read · Updated

What an ISA is (and isn’t)

The instruction set architecture is everything a programmer needs to know to write machine code: which instructions exist, how each one is encoded as bits, which registers there are, how memory is addressed and what happens on errors or interrupts. It deliberately leaves out how the hardware does it. The same ISA can be implemented by very different circuits. x86 has run on everything from 1978’s 8086 to today’s out-of-order monsters, and RISC-V runs on tiny microcontrollers and on server chips.

Decision 1: how many operands?

Take c = a + b. Machines differ in how many things one instruction can name:

StyleCode for c = a + bExamples
Stack (0 operands)PUSH a · PUSH b · ADD · POP cJVM bytecode, WebAssembly, Forth machines, HP calculators
Accumulator (1)LOAD a · ADD b · STORE cManchester Baby, PDP-8, MARIE, 6502
Two-address (2)MOV r1, a · ADD r1, b · MOV c, r1x86, Motorola 68000
Three-address load/store (3)LW r1, a · LW r2, b · ADD r3, r1, r2 · SW r3, cMIPS, RISC-V, ARM, LC-3

Fewer operands mean shorter instructions and simpler hardware, but more instructions per job. More operands mean longer instructions that do more each.

Decision 2: the encoding

Every instruction has to fit in bits, and the bits are a budget. Suppose your machine has 8-bit instructions. If you spend 3 bits on the opcode, you get 8 operations and 5 bits left for an operand: one of 32 addresses, or a number from 0 to 31. Spend 4 bits on the opcode and you get 16 operations but only 16 addresses. Want two register operands? With four registers, each takes 2 bits.

  7 6 5 4 3 2 1 0
 [ opcode |  address  ]     one format: 3 + 5 bits
 [ opcode |rd |rs |x ]     another:    3 + 2 + 2 + 1 bits

Real ISAs use several formats and let the opcode decide which one applies. Two classic tricks help. First, keep fields in the same position in every format, so the decoder can start reading registers before it even knows the instruction. RISC-V is famous for this. Second, give the most common instructions the shortest or simplest encodings. Fixed-length instructions (all 32 bits in MIPS and RISC-V) make fetching and decoding easy. Variable-length ones (1 to 15 bytes in x86) pack code more tightly at the cost of a harder decoder.

Decision 3: registers

Registers are the fastest storage there is, and naming one costs only a few bits. More registers mean fewer slow memory accesses, but wider register fields and more state to save when switching tasks. The 6502 has three general registers, the LC-3 has 8, and MIPS and RISC-V have 32. RISC-V and MIPS also hard-wire one register to zero, which turns out to be useful everywhere: “move” becomes “add zero”, and “compare with zero” needs no constant.

Decision 4: addressing modes

How does an instruction say where its data is? Immediate (the value is in the instruction), direct (the address is in the instruction), register indirect (a register holds the address), base + offset (good for fields of a struct or stack frames), indexed (good for arrays), PC-relative (good for code that can be loaded anywhere). CISC machines offer many modes. RISC machines offer one or two and let you compute the rest with ordinary instructions.

Decision 5: control flow

A computer needs at least one way to make decisions. The Baby had only “skip the next instruction if negative”. Most ISAs keep flags set by the ALU and branch on them (BEQ, BCC…). MIPS and RISC-V skip flags and compare two registers inside the branch itself. You also need calls: a jump that remembers where to come back to, either by pushing the return address on a stack or by saving it in a link register.

RISC vs CISC, briefly

In the 1970s, when memory was expensive, instruction sets grew rich and dense. A single VAX instruction could evaluate a polynomial. Around 1980, researchers at IBM, Berkeley and Stanford noticed that compilers mostly used the simple instructions, and that a small, regular set could be pipelined to run much faster. That was the RISC idea: fixed-length instructions, load/store architecture, many registers. Today the line is blurry. x86 chips translate their complex instructions into simple internal operations, and RISC ISAs have grown extensions. But the debate shaped every modern design.

Is it Turing complete?

Surprisingly little is needed for a machine to compute anything computable, given enough memory and time. There are even “one instruction set computers”, such as subleq (subtract, and branch if the result is less than or equal to zero). A practical ISA adds instructions for speed and convenience, not for power.

The assembler

An assembler turns text like loop: ADD r1, r2 into machine code. It works in two passes. The first pass walks the program and records the address of every label. The second pass encodes each instruction, replacing label names with addresses (or with offsets, for PC-relative branches). Add constants, data directives (.byte 1, 2, 3) and good error messages, and you have a real tool. Because you choose the syntax, you can make it as friendly as you like.