Registers#
Assembly’s “variables” are CPU registers and memory addresses. Each architecture exposes a fixed set of general-purpose registers; memorise the names, the sizes, the calling convention, and which are caller-saved vs callee-saved.
For data widths and addressing modes, see Types. For calling conventions, see Functions.
x86-64 registers#
Sixteen general-purpose 64-bit registers. Each has 32-, 16-, and 8-bit aliases.
64-bit |
32-bit |
16-bit |
8-bit / role |
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Special: rip (instruction pointer; used in
RIP-relative addressing), rflags (status bits, see
Control flow).
SIMD: xmm0..15 (128 bit, SSE), ymm0..15 (256 bit, AVX),
zmm0..31 (512 bit, AVX-512). Float / double arguments live
in xmm0..7 under SysV.
ARM64 (AArch64) registers#
31 general-purpose 64-bit registers plus a stack pointer.
Register |
Role |
|---|---|
|
argument and return registers |
|
indirect result location / syscall number on Linux |
|
temporaries; caller-saved |
|
intra-procedure-call scratch |
|
platform; not free to use |
|
callee-saved |
|
frame pointer |
|
link register (return address) |
|
stack pointer |
|
32-bit alias for |
SIMD / float: v0..v31 (128-bit NEON registers);
s (32-bit float), d (64-bit double), q (128-bit
quad) views into the same vector register.
Reading and writing#
x86-64.
mov rax, 10 ; rax = 10
mov ebx, eax ; ebx = eax (upper 32 bits of rbx zeroed)
mov ax, 0xFFFF ; only low 16 bits modified
mov al, 0xFF ; only low 8 bits modified
The 32-bit write to eax zero-extends into the upper 32
bits of rax. The 8- and 16-bit writes do not. This is the
single biggest x86-64 gotcha.
ARM64.
mov x0, #10 // x0 = 10
mov w0, w1 // w0 = w1; upper 32 bits of x0 zeroed
Stack#
The stack grows downward on x86-64 and ARM64; rsp /
sp points at the top (lowest address) of the live stack.
; x86-64: push / pop work in 8-byte units
push rax ; rsp -= 8; [rsp] = rax
pop rbx ; rbx = [rsp]; rsp += 8
sub rsp, 32 ; reserve 32 bytes (4 qwords)
mov [rsp + 0], rdi
mov [rsp + 8], rsi
; … use the buffer …
add rsp, 32 ; release
// ARM64: push / pop done with pre-/post-indexed ld/st
stp x0, x1, [sp, #-16]! // pre-decrement: sp -= 16; store x0/x1
ldp x0, x1, [sp], #16 // post-increment: load; sp += 16
The stack must stay 16-byte aligned at function-call boundaries on both architectures. Misalignment crashes on some SSE instructions and on most ARM64 SIMD.
Frame pointer#
A traditional frame pointer (rbp / x29) saves the
caller’s frame pointer at the top of the prologue so debuggers
can walk the stack.
; x86-64 standard prologue
push rbp
mov rbp, rsp
sub rsp, 32 ; local space
; epilogue
mov rsp, rbp
pop rbp
ret
Modern optimised builds often omit the frame pointer (the
operator gets unwind data from .eh_frame instead); for
hand-written assembly the operator keeps the standard frame
pointer so gdb can backtrace.
Caller-saved vs callee-saved#
Caller-saved (volatile): the caller must save before a
callif it needs the value preserved. x86-64 SysV:rax rcx rdx rsi rdi r8 r9 r10 r11. ARM64:x0..x18.Callee-saved (non-volatile): the callee must preserve these across its execution. x86-64 SysV:
rbx rbp r12 r13 r14 r15. ARM64:x19..x28 x29 x30.
The operator who writes assembly that other code calls must save and restore callee-saved registers in the prologue / epilogue.
Local variables#
There are no named locals; the operator reserves stack space and
indexes through rbp (or rsp directly when no frame
pointer is used).
; reserve 32 bytes of locals
push rbp
mov rbp, rsp
sub rsp, 32
; local A at rbp-8, local B at rbp-16
mov qword [rbp - 8], rdi
mov qword [rbp - 16], rsi
mov rax, qword [rbp - 8]
add rax, qword [rbp - 16]
; epilogue
leave ; mov rsp, rbp; pop rbp
ret
Globals and static data#
The .data, .rodata, and .bss sections hold
program-lifetime data. The operator labels them and refers by
name; the assembler / linker resolves the address.
section .rodata
msg: db "hello", 0xa
len equ $ - msg
section .data
counter: dq 0
section .bss
buf: resb 4096 ; 4 KB uninitialised
section .text
global _start
_start:
lea rsi, [rel msg]
mov rdx, len
; ...
mov rax, qword [rel counter]
inc rax
mov qword [rel counter], rax
[rel name] (RIP-relative addressing) is required for
position-independent code (PIE binaries, shared libraries).