100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

CPU Registers Explained

A practical guide to what CPU registers are, the main categories on x86-64, and how assembly instructions use them to do work.

FoundationsBeginner9 min readJul 10, 2026
Analogies

CPU Registers Explained

A register is a small, extremely fast storage location built directly into the CPU itself, holding a fixed number of bits — 64 on a modern x86-64 processor. Unlike RAM, which sits outside the CPU chip and requires dozens of clock cycles to access, a register can typically be read or written in a single clock cycle, making registers the fastest storage a program can use. Every arithmetic or logic operation an x86-64 CPU performs — addition, comparison, bitwise AND — operates on values sitting in registers, not directly on memory in most cases; data must first be loaded from RAM into a register before the CPU can compute on it. Because there are only a limited number of registers (16 general-purpose 64-bit registers on x86-64), assembly programmers and compilers must constantly decide which values are important enough to keep in a register versus which should be spilled back out to memory, a process called register allocation.

🏏

Cricket analogy: Registers are like the handful of balls a bowler like Jasprit Bumrah actually holds in his hand during an over, versus the full box of balls in the dressing room — only what's in-hand can be bowled immediately, the rest requires a trip back.

General-Purpose Registers

On x86-64, the general-purpose registers are named RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, and R8 through R15, each 64 bits wide, though many can also be accessed as smaller 32-bit (EAX), 16-bit (AX), or 8-bit (AL) sub-portions of the same physical register. While these are called 'general-purpose,' several carry conventional roles by tradition and by the calling convention a platform uses: RAX typically holds a function's return value, RCX, RDX, RSI, and RDI are commonly used to pass the first four integer arguments to a function under the System V AMD64 calling convention used on Linux and macOS, and RSP always points to the top of the current call stack. RBP is conventionally used as a frame pointer to reference local variables and function parameters relative to a fixed point within a stack frame, though optimizing compilers sometimes free it up as a thirteenth general-purpose register when frame-pointer omission is enabled.

🏏

Cricket analogy: General-purpose registers are like a T20 side's flexible batting order — Virat Kohli usually bats at 3, but the same slot can be reassigned depending on match situation, just as RAX conventionally holds a return value but can be repurposed.

Special-Purpose Registers: RIP and RFLAGS

Beyond the general-purpose set, x86-64 has registers with fixed, non-negotiable hardware roles. RIP, the instruction pointer, always holds the memory address of the next instruction the CPU will fetch and execute; a jmp or call instruction works by directly overwriting RIP, which is exactly how loops, function calls, and if/else branching are implemented at the hardware level. RFLAGS is a register where individual bits (flags) are automatically set or cleared as a side effect of arithmetic and comparison instructions — the Zero Flag (ZF) is set when a result equals zero, the Carry Flag (CF) is set on unsigned overflow, and the Sign Flag (SF) reflects the sign of a result. Conditional jump instructions such as je (jump if equal) or jl (jump if less) don't compare values themselves; they simply inspect the flags left behind by a preceding cmp instruction, which is why cmp is almost always immediately followed by a conditional jump in real assembly code.

🏏

Cricket analogy: RIP is like the umpire's mental note of which ball is coming next in the over — it always points to 'what happens next,' and a no-ball call (a jump) redirects the sequence exactly as jmp overwrites RIP.

asm
; x86-64 NASM: registers, flags, and a conditional jump
section .text
    global _start

_start:
    mov     rax, 10        ; RAX = 10
    mov     rbx, 10        ; RBX = 10
    cmp     rax, rbx       ; compare RAX and RBX, sets ZF/SF/CF in RFLAGS
    je      equal_case     ; jump if Zero Flag is set (RAX == RBX)

    mov     rdi, 1         ; not taken in this example
    jmp     done

equal_case:
    mov     rdi, 0         ; exit code 0 means "they were equal"

done:
    mov     rax, 60        ; syscall number for exit
    syscall

Never assume a register's value survives a function call unless the calling convention guarantees it. Under System V AMD64, RAX, RCX, RDX, RSI, RDI, R8-R11 are 'caller-saved' and may be overwritten by any called function; only RBX, RBP, RSP, R12-R15 are 'callee-saved.' Forgetting this is a classic source of subtle bugs in hand-written assembly.

  • Registers are the CPU's fastest storage, built directly into the chip and accessed in a single clock cycle, unlike RAM.
  • x86-64 has 16 general-purpose 64-bit registers (RAX-RDX, RSI, RDI, RBP, RSP, R8-R15), each also addressable as smaller 32/16/8-bit sub-registers.
  • Register roles like 'RAX returns a value' or 'RDI holds the first argument' are calling-convention conventions, not hardware restrictions.
  • RIP (instruction pointer) always holds the address of the next instruction; jmp and call directly overwrite it.
  • RFLAGS holds bits like ZF, CF, and SF that are set automatically by arithmetic/comparison instructions such as cmp.
  • Conditional jumps (je, jl, jg) read RFLAGS rather than recomputing a comparison themselves.
  • The System V AMD64 calling convention divides registers into caller-saved and callee-saved sets, which every assembly function must respect.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#AssemblyLanguageStudyNotes#CPURegistersExplained#CPU#Registers#Explained#General#StudyNotes#SkillVeris#ExamPrep