Obsess over every detail. Ask why it works. Ask why it isn’t built another way.

Assembly Registers

We already talked about registers earlier when explaining the memory hierarchy, but as a refresher:
processor registers are small, volatile storage areas built directly into the CPU.

They are the fastest storage in the system and are where the CPU actually performs operations.

On x86-64, Intel CPUs have 16 general-purpose registers, plus the instruction pointer (RIP), which points to the next instruction to execute. Some of these registers are architecturally general-purpose but have conventional roles.

On x86-32 systems, registers are 32 bits wide, and there are only 8 general-purpose registers, plus the instruction pointer.

Intel Register Evolution

Registers were not replaced over time, they were extended. Older registers still exist as sub-registers.

┌──────────┬────────┬────────────────────────────────────────┐
│ Register │ Width  │ Processor / Notes                      │
├──────────┼────────┼────────────────────────────────────────┤
│ A        │  8-bit │ Intel 8008                             │
│ AX       │ 16-bit │ Intel 8086 ("A-extended")              │
│ EAX      │ 32-bit │ Intel 80386                            │
│ RAX      │ 64-bit │ x86-64 (AMD Opteron / Intel P4+)       │
└──────────┴────────┴────────────────────────────────────────┘
RAX  (64-bit)
└── EAX (32-bit)
    └── AX  (16-bit)
        ├── AL (low 8 bits)
        └── AH (high 8 bits, legacy)

Older registers were never removed, they became smaller views into newer, wider registers.

Sub-register access remains available:

General-Purpose Registers (x86-64)

64-bit   32-bit   16-bit   8-bit

RAX      EAX      AX       AL / AH
RBX      EBX      BX       BL / BH
RCX      ECX      CX       CL / CH
RDX      EDX      DX       DL / DH

Disassemblers almost always use the historical names (RAX, RBX, etc.) rather than numeric identifiers, because they are easier to read and match older documentation.

General Registers with Conventional roles

64-bit   32-bit   16-bit   8-bit     Convention

RSP      ESP      SP       SPL       Stack pointer
RBP      EBP      BP       BPL       Stack base frame
RSI      ESI      SI       SIL       Source index
RDI      EDI      DI       DIL       Destination index

Extra Registers Added in x86-64

x86-64 introduced eight additional registers to help compilers keep more values in registers:

R8  R9  R10 R11
R12 R13 R14 R15

Each of these supports full-width access:

R8   (64)
R8D  (32)
R8W  (16)
R8B  (8)

Instruction Pointer

RIP → points to the next instruction to execute

For a quick reference, see this x86-64 register cheat sheet.

Why should we care about accessing smaller parts of registers?

Not all operations use 64-bit values.

Many data types are smaller than 64 bits, and CPUs need a way to operate on those sizes correctly and efficiently. On x86-64, accessing the lower portions of registers (such as 32-bit, 16-bit, or 8-bit parts) is still very important.

Registers can hold 64 bits, but many data types are smaller:

Because of this, the CPU must be able to:

That’s why accessing parts of a register still matters.

Example: 32-bit arithmetic behavior

Consider this C code:

1
2
3
4
unsigned int x = 1; // On most systems unsigned int is 32 bits 
x += UINT_MAX;      // UINT_MAX is 2^32 - 1
if (x)
    printf("Nope");

Mathematically:

1 + (2^32 - 1) = 2^32 → wraps to 0

If the CPU did this using a full 64-bit register without truncating, the result would be 2^32, not 0, and the program would behave incorrectly. By doing the operation in a 32-bit register (EAX), the CPU automatically enforces the correct wraparound behavior.

Why this still matters on x86-64

Is this only for backward compatibility?

Partially.

You will encounter these forms frequently when reading assembly, so understanding them avoids constantly checking manuals.

Intel Register Conventions

Intel originally suggested certain usage conventions for registers in their manuals. These conventions are mostly based on register names and intended roles. However, these are only recommendations, not strict rules. Compilers are free to use registers however they want, and in simple assembly examples, many of these conventions won’t always appear.

RAX — Accumulator / Return value

RBX — Base register

RCX — Counter register

RDX — Data / I/O register

RSI — Source index

RDI — Destination index

Note: Even though registers like RSI and RDI have traditional roles, they are still general-purpose registers. The compiler can use them however it wants.

RSP — Stack pointer

RBP — Base pointer

RIP — Instruction pointer