Binary Register

How CPU Registers Store Binary Data at the Circuit Level

Registers are the fastest memory in any computer — they live right inside the CPU and hold the data the processor is actively working on. When I was learning about computer architecture, the register file was one of those components I took for granted until I realized just how clever the circuit design is. Every register is built from a basic memory cell called a flip-flop, and understanding how flip-flops store a single bit is the key to understanding how the entire register file works.

In this article I'll start with the simplest storage circuit — the D flip-flop — and build up to multi-bit registers, register files, and the architectural role registers play in a CPU.

The D Flip-Flop: One Bit of Storage

The fundamental building block of any register is the D flip-flop (Data flip-flop). A D flip-flop stores a single bit and updates its stored value only on the rising edge of a clock signal. This edge-triggered behavior is what makes synchronous digital design possible.

How it works:

  • D (Data) input: The value to be stored.
  • CLK (Clock) input: A square-wave signal that synchronizes updates across the entire CPU.
  • Q output: The currently stored value.
  • Q' output: The inverted stored value.

On each rising clock edge, Q takes the value of D. Between clock edges, Q holds its value regardless of changes on D. This is what makes flip-flops suitable for synchronous sequential logic — everything updates at precisely the same moment.

Inside the flip-flop: A D flip-flop is typically built from two SR latches in a master-slave configuration, using around 6 NAND gates (or about 24 transistors). The master latch captures the input while the clock is low, and the slave latch transfers it to the output on the rising edge.

From Flip-Flop to Register

An N-bit register is simply N D flip-flops sharing a common clock signal. Each flip-flop stores one bit, and together they store an N-bit binary value. All N bits are written simultaneously on the same clock edge.

An 8-bit register uses 8 D flip-flops. If you load the binary value 11001010 into the register, flip-flop 7 stores 1, flip-flop 6 stores 1, flip-flop 5 stores 0, and so on down to flip-flop 0 storing 0. The register holds that value until the next clock edge loads a new value.

In a real CPU, registers also have additional control signals:

  • Write Enable (WE): When asserted, the register captures the input on the next clock edge. When deasserted, the register ignores the input and holds its current value.
  • Output Enable (OE): Controls whether the register drives its value onto the data bus. Multiple registers can share a bus as long as only one has its output enabled at a time.

Register Width Evolution: 8-Bit to 64-Bit

As CPUs evolved, register widths grew to handle larger data and address spaces:

  • 8-bit (1970s): 6502, Z80 — could address 64KB of memory. Registers like A (accumulator), X, and Y were 8 bits each.
  • 16-bit (1980s): 8086, 68000 — 1MB address space. The 8086 had 8 general-purpose registers (AX, BX, CX, DX, SI, DI, BP, SP), each 16 bits.
  • 32-bit (1990s-2000s): 80386 onward — 4GB address space. Register names gained an E prefix (EAX, EBX, etc.) and expanded to 32 bits.
  • 64-bit (2000s-present): x86-64, ARMv8 — 16 exabytes of address space. Registers gained an R prefix (RAX, RBX, etc.) and expanded to 64 bits. x86-64 also added 8 new general-purpose registers (R8 through R15).

A modern 64-bit CPU might have 16 to 32 general-purpose registers, each containing 64 D flip-flops. That's 1,024 to 2,048 flip-flops for the register file alone, before counting special-purpose registers.

General-Purpose vs. Special-Purpose Registers

General-purpose registers (GPRs) can store any data the programmer or compiler chooses — intermediate results, loop counters, function arguments. In x86-64, RAX is typically used for return values, RCX for loop counts, and RSP for the stack pointer, but these are conventions, not hard requirements.

Special-purpose registers have dedicated hardware roles:

  • Program Counter (PC): Holds the address of the next instruction to execute. The PC increments after each instruction fetch (or jumps to a new address on branches).
  • Stack Pointer (SP): Points to the top of the call stack in memory.
  • Instruction Register (IR): Holds the currently executing instruction after it's fetched from memory.
  • Status Register / Flags: Individual bits (carry flag, zero flag, overflow flag, negative flag) that record outcomes of the last arithmetic operation.
  • Memory Address Register (MAR): Holds the address being accessed in memory.
  • Memory Data Register (MDR): Holds data being read from or written to memory.

The Register File: A Bank of Registers

CPUs don't just have one register — they have a register file, which is an array of registers with multi-port access. A typical register file has:

  • Two read ports: Two independent addresses, each reading out a 32-bit or 64-bit value simultaneously. This allows the ALU to receive both operands in one clock cycle.
  • One write port: One write address and write data line, allowing the ALU result to be written back to the register file.

The register file is implemented as a grid of SRAM cells (each using 6 transistors) arranged in rows (registers) and columns (bit positions). Decoders select which row (register) to read from or write to. The entire register file of 32 registers x 64 bits = 2,048 flip-flops can be read (both operands) and written in a single clock cycle, which is why register access is the fastest memory operation in any computer.

Registers vs. Cache vs. RAM: The Speed Hierarchy

Understanding the speed gap between registers and other storage explains why register allocation matters for performance:

  • Register: ~0.25-0.5 ns access time. Inside the CPU core, directly connected to the ALU. No address decoding overhead for individual registers.
  • L1 Cache: ~1-2 ns access time. On the same die, but requires tag comparison and data select.
  • L2 Cache: ~5-10 ns. Slightly further from the core, larger but slower.
  • L3 Cache: ~15-30 ns. Shared across cores.
  • Main Memory (RAM): ~50-100 ns. Off-chip, through the memory bus.

The ratio from register to main memory is roughly 200x to 400x. That's why compilers work so hard to keep frequently accessed values in registers rather than spilling them to the stack in memory.

Try It Yourself

Open the Binary Code Decoder in a new tab and enter some binary patterns to see the results instantly. All conversions happen in your browser — no data is sent to any server.