# Extending an instruction set

*Adding an opcode, and paying for it in decode and datapath*

Extending an instruction set means assigning a new opcode in a CPU's fixed instruction table and wiring the decode and datapath logic that opcode needs, often by reusing hardware already present, as when a subtract reuses the existing adder with a controllable inverter.

Group: Processor
URL: https://digiwleea.wleeaf.dev/learn/extending-an-isa/

The [CPU](https://digiwleea.wleeaf.dev/learn/cpu/) you built runs four instructions: `LOAD`, `ADD`, `STORE`, `HALT`. That set is its **instruction set architecture**, the fixed contract between the programs and the hardware. A program may only ask for what the opcode table names, so making the machine more capable means **extending the ISA**: adding an opcode and the logic behind it.

In this machine an instruction is one byte: the high nibble is the [opcode](https://digiwleea.wleeaf.dev/learn/machine-code/) (what to do) and the low nibble is the operand (usually an address). Four bits of opcode name up to **sixteen** instructions, and only four are used, so there is plenty of room. Adding one is three questions: which unused opcode does it get, what does the decoder do when it sees that opcode, and what datapath does it drive?

## The cheapest extension: subtract

Subtraction looks like new arithmetic, but the [ALU](https://digiwleea.wleeaf.dev/learn/alu/) already has an [adder](https://digiwleea.wleeaf.dev/learn/adder8/), and `A - B` is just `A + (NOT B) + 1` in [two's complement](https://digiwleea.wleeaf.dev/learn/twos-complement/). So `SUB` reuses the adder with a **controllable inverter**: put an `XOR` on each operand bit driven by a shared `SUB` line (`X XOR 0 = X`, `X XOR 1 = NOT X`) and feed that same `SUB` into the adder's carry-in for the plus-one. When `SUB` is `0` the machine adds; when it is `1` it subtracts. One row of XORs and a wire to the carry-in, and the ISA has a new instruction.

```asm
opcode  name    what it does                 hardware it reuses
 0x1    LOAD    ACC <- mem[a]               the data bus + register load
 0x2    ADD     ACC <- ACC + mem[a]         the adder
 0x3    STORE   mem[a] <- ACC               the data bus + memory write
 0xF    HALT    stop the clock              a latch that gates the writes
 ---- extensions ----
 0x4    SUB     ACC <- ACC - mem[a]         the adder + a controllable inverter
 0x5    JMP     PC  <- a                    the program counter's load path
 0x6    JZ      PC  <- a if ACC == 0        the load path + the zero flag
 0x7    OUT     show ACC on a display       an output register
```

_The accumulator ISA before and after extension. The four new opcodes reuse the datapath already present: SUB reuses the adder, JMP/JZ reuse the program counter's load path, OUT reuses a register and a display._

Every extension in that table follows the same rule: **spend as little new hardware as possible.** `JMP` and `JZ` do not need a new counter, they reuse the [program counter](https://digiwleea.wleeaf.dev/learn/program-counter/)'s existing load path. `OUT` does not need a new bus, it latches the accumulator into a spare register. The decoder grows by a few gates per opcode; the datapath barely grows at all. That reuse is why a real ALU gets subtract almost for free once it has an adder.

> **WARN:** **Common mistakes.** An opcode is a fixed-width field: four bits give exactly sixteen slots, so an ISA cannot grow without bound, real machines guard the encoding space carefully. Keep old opcodes at their old values so existing programs still assemble to the same bytes (backward compatibility); reusing an already-taken opcode silently breaks every program that used it. And every new opcode needs BOTH halves: decode logic to recognize it and datapath to carry it out. Naming an instruction the assembler accepts, with no hardware behind it, just produces a byte the CPU ignores.

> **KEY:** An instruction set is a promise, and extending it is how a processor family grows over decades while old software keeps running. The trick that makes it affordable is reuse: most "new" instructions are a new way to steer datapath the chip already has. Subtract is the clearest example, one XOR row turns the adder into an adder-subtractor.

**Q (Try it):** You want to add an `AND a` instruction (`ACC <- ACC AND mem[a]`) to this machine. What unused opcode could it take, and what new datapath does it need?

**A:** It can take any unused high nibble, `0x8` for example (0x0, 0x8-0xE are all free). The datapath it needs is a bank of `AND` gates, one per bit, computing `ACC AND mem[a]`, plus a way for the decoder to route that result into the accumulator instead of the adder's sum (a multiplexer selected by the opcode). Unlike `SUB`, it cannot reuse the adder, so it is a slightly more expensive extension, a real bit of new logic, but still just a few gates and one mux input.

### FAQ

**Q:** What does it mean to extend a CPU's instruction set?

**A:** It means adding a new instruction the CPU can execute: assigning it an unused opcode, adding decode logic so the control unit recognizes that opcode, and adding or reusing datapath so the operation actually happens. Old opcodes keep their meaning, so existing programs still run.

**Q:** Why does a CPU get subtraction almost for free once it has an adder?

**A:** Because `A - B` equals `A + (NOT B) + 1` in two's complement. Putting a controllable inverter (an XOR per bit) on one operand and driving the adder's carry-in with the same control turns the adder into an adder-subtractor, so `SUB` reuses the existing adder instead of needing a separate subtract circuit.

**Q:** How many instructions can an instruction set have?

**A:** It is limited by the opcode field's width. A 4-bit opcode names at most 16 instructions; an 8-bit opcode, 256. Widening the field allows more instructions but makes every instruction byte larger, so ISA designers balance how many opcodes they need against how compact the encoding should be.
