Registers
Part of Basic Computing
Registers are the processor’s internal high-speed storage — faster than memory by orders of magnitude, directly connected to the ALU, and the workspace where all computation actually happens.
Why This Matters
The processor cannot add two numbers from memory directly. First, it loads them into registers; then the ALU adds the register values; then the result is stored back to memory. This load-compute-store pattern is the fundamental rhythm of all computing, and registers are the workspace where it happens.
Understanding registers means understanding the processor’s internal organization: how many working variables can be held simultaneously, what special-purpose roles specific registers play, and why register allocation is the central challenge of compiler design.
For hardware builders, registers are the key intermediate-complexity circuit between individual flip-flops and the full CPU. A register file (the collection of all CPU registers) is the first truly useful circuit that requires designing multi-bit buses, multiplexers, and write-enable logic together.
Types of Registers
General-purpose registers (GPRs): available for any use by programmer or compiler. Named R0–R7, or with historical names (AX, BX, CX, DX in x86; A, B, C, D, E, H, L in Z80). The more GPRs, the less frequently programs need to spill values to memory — typically 8 to 32 GPRs in modern processors. For hand-built designs, 4–8 GPRs are manageable.
Program Counter (PC): holds the address of the next instruction to fetch. Automatically incremented by the CPU after each fetch. Modified by jump, call, and return instructions. The PC is fundamental — it is what makes the CPU “know where it is” in the program.
Stack Pointer (SP): holds the address of the top of the stack (or the next free stack slot, depending on convention). Automatically decremented by PUSH and incremented by POP. Errors in SP management (stack overflow: SP decrements into code or data; stack underflow: too many POPs) cause crashes.
Status/Flags Register (SR/PSR): holds condition flags set by the most recent ALU operation: Zero (Z), Carry (C), Negative (N), Overflow (V). Tested by conditional branch instructions to control program flow.
Index Registers (X, Y): specialized registers optimized for array indexing and pointer arithmetic. Used as base or offset in indexed addressing modes. Not always separate from GPRs — some architectures designate any GPR as an index register.
Accumulator (A): in accumulator-based architectures (6502, Z80, 8080), most ALU operations read from and write to the accumulator. One operand comes from a GPR or memory; the result always goes to the accumulator. Reduces the number of register selection bits needed in the instruction format.
Register File Architecture
A register file is a small, fast memory array with multiple read and write ports. For a CPU with 8 registers and two-operand instructions:
- Two read ports: simultaneously output two register values to ALU inputs
- One write port: accepts ALU result and writes to destination register
- Register selection: each port has a log2(8) = 3-bit address input selecting which register to access
Hardware implementation of an 8×8-bit register file:
- Eight 8-bit D flip-flop groups (one per register)
- Write enable decoder: 3-bit address → 8 write enable lines (only one active per clock)
- Output multiplexers: two 8-to-1 muxes (one per read port), each selecting among 8 register outputs
With standard ICs:
- Eight 74HC374 (8-bit D flip-flops with output enable) for the storage
- Two 74HC251 (8-to-1 mux, 8-bit wide = 8 chips per 8-bit word) for read ports
- 74HC138 (3-to-8 decoder) for write enable
This is approximately 20 ICs — achievable but space-consuming. Integrating a register file onto a single RAM chip (74HC219 or similar small SRAM) with write enable logic is more compact.
Register Saving and Restoration
When a subroutine is called, it may use the same registers as the calling code. If the subroutine modifies R0 and the caller was using R0 for important data, the caller’s data is destroyed.
Caller-save convention: the caller saves any registers it needs before the call, and restores them after. The callee is free to use any registers.
Callee-save convention: the callee saves any registers it will modify at the start of the subroutine, and restores them before returning. The caller can assume registers are preserved across calls.
Assembly implementation of callee-save:
MYSUB: PUSH R0 ; save registers that will be modified
PUSH R1
; ... subroutine body using R0 and R1 ...
POP R1 ; restore in reverse order
POP R0
RETMost architectures use a mixed convention: some registers are caller-save (the callee may freely modify them; caller saves if needed), others are callee-save (the callee must preserve them).
Register Window Architecture
Some RISC processors (notably SPARC) use register windows: instead of saving/restoring registers on each call, the hardware maintains multiple sets of registers and switches to a new set on each procedure call. The current window overlaps with the caller’s window for parameter passing.
This eliminates most save/restore overhead but requires more physical registers (hundreds of physical registers divided into windows of 8–16 visible registers each). For a hand-built CPU, register windows add significant hardware complexity without proportional benefit — stick with conventional register file plus stack save/restore.