askesis

x86-64 assembly: 50 cards on registers, instructions and calling conventions

x86-64 assembly in Intel syntax: registers and sub-registers, flags, mov and lea, jumps, the stack frame, the System V calling convention and Linux syscalls.

ProgrammingIntermediate50 cardsFree

Practice now

Study without an account

0 of 50 seen0 graduated
The 16 general-purpose registers of x86-64

Scheduled with the default Askesis timings. Progress is saved in this browser.

All 50 cards

  1. 1
    The 16 general-purpose registers of x86-64
    rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp and r8 to r15
  2. 2
    The smaller registers inside rax
    eax (low 32 bits), ax (low 16 bits), al (low 8 bits) and ah (bits 8 to 15)
  3. 3
    The smaller registers inside r8
    r8d (low 32 bits), r8w (low 16 bits) and r8b (low 8 bits)
  4. 4
    The 8-bit registers of rsi, rdi, rbp and rsp
    sil, dil, bpl and spl
    They need a REX prefix, so an instruction that uses one of them can’t also use ah, bh, ch or dh.
  5. 5
    What happens to the upper half of rax after mov eax, 1?
    It is zeroed: writing a 32-bit register zero-extends the result into the full 64-bit register
    Writing ax or al leaves the other bits unchanged.
  6. 6
    What do rip and rflags hold?
    rip holds the address of the next instruction to run; rflags holds the status flags (ZF, SF, CF, OF and others) and control bits such as the direction flag
  7. 7
    Why do compilers zero a register with xor eax, eax?
    It clears all of rax in 2 bytes instead of the 5 of mov eax, 0, and CPUs recognize it as a zeroing idiom
    Unlike mov, it also changes the flags.
  8. 8
    Sizes of a byte, word, dword and qword
    1, 2, 4 and 8 bytes
    A word is 16 bits for historical reasons: it was the 8086’s register size.
  9. 9
    The four main status flags
    ZF: the result was zero. SF: the result’s sign bit is set. CF: an unsigned carry or borrow. OF: a signed overflow
  10. 10
    What does cmp rax, rbx do?
    Computes rax - rbx, sets the flags as sub would, and throws the result away
  11. 11
    What does test rax, rax do?
    ANDs rax with itself to set the flags without storing anything: ZF is set when rax is zero, and SF when it is negative
    Usually followed by jz or jnz. test al, 1 checks the lowest bit.
  12. 12
    Signed vs unsigned conditional jumps
    Signed comparisons use jl, jle, jg, jge (less, greater); unsigned ones use jb, jbe, ja, jae (below, above)
    Signed jumps read SF, OF and ZF; unsigned jumps read CF and ZF.
  13. 13
    je vs jz
    Two names for the same instruction: jump if ZF is set
    Likewise jne and jnz both jump if ZF is clear.
  14. 14
    Jump to .small if rdi is less than 10, as a signed number
    x86asm
    cmp rdi, 10
    jl .small
    For unsigned values, use jb instead.
  15. 15
    A loop that runs its body 10 times
    x86asm
        mov ecx, 10
    .next:
        ; loop body
        dec ecx
        jnz .next
    dec sets ZF when the counter reaches zero, so no cmp is needed.
  16. 16
    Set rax to the larger of rdi and rsi (signed) without a branch
    x86asm
    mov rax, rdi
    cmp rdi, rsi
    cmovl rax, rsi   ; if rdi < rsi, rax = rsi
  17. 17
    Turn a comparison into 0 or 1
    x86asm
    cmp rdi, rsi
    sete al
    movzx eax, al   ; rax = 1 if rdi == rsi, else 0
    setcc writes only one byte, so zero-extend it before using the full register.
  18. 18
    mov rax, [rbx+8] vs lea rax, [rbx+8]
    mov loads the 8 bytes stored at address rbx+8; lea puts the address rbx+8 itself in rax without touching memory
  19. 19
    Multiply rdi by 5 with a single lea
    x86asm
    lea rax, [rdi+rdi*4]
    lea can do this kind of arithmetic in one instruction, and it doesn’t change the flags.
  20. 20
    The general form of a memory operand
    [base + index*scale + displacement], where scale is 1, 2, 4 or 8 and the displacement is a signed 32-bit constant
    rsp can be a base but never an index.
  21. 21
    Can mov copy directly from memory to memory?
    No. At most one operand can be in memory, so load the value into a register first
    String instructions such as movsb are the exception.
  22. 22
    Why does mov [rax], 1 fail to assemble?
    Nothing says how many bytes to write; add a size: mov qword [rax], 1
    GNU as in Intel mode and MASM write it qword ptr [rax].
  23. 23
    movzx vs movsx
    Both load a smaller value into a larger register: movzx fills the upper bits with zeros, movsx with copies of the sign bit
    x86asm
    movzx eax, byte [rdi]   ; unsigned char
    movsx rax, word [rdi]   ; signed short
    movsxd rax, dword [rdi] ; signed int
  24. 24
    Load the address of msg with RIP-relative addressing
    x86asm
    lea rax, [rel msg]
    GNU as writes lea rax, [rip + msg]. Addresses relative to rip keep code position-independent.
  25. 25
    How big can an immediate operand be?
    Usually 32 bits, sign-extended to 64; only mov into a 64-bit register accepts a full 64-bit immediate
    GNU as calls that form movabs.
  26. 26
    Which byte does mov dword [rdi], 0x12345678 store at the lowest address?
    0x78: x86 is little-endian, so the bytes in memory are 78 56 34 12
  27. 27
    inc rax vs add rax, 1
    Both add one, but inc leaves CF unchanged while add updates it
  28. 28
    Unsigned 64-bit division with div rcx
    Divides the 128-bit value in rdx:rax by rcx: the quotient goes to rax and the remainder to rdx. Zero rdx first
    For signed idiv, sign-extend rax into rdx with cqo. Dividing by zero raises an exception (SIGFPE on Linux).
  29. 29
    shr vs sar
    shr shifts in zeros, dividing an unsigned value by a power of two; sar copies the sign bit, dividing a signed value
    sar rounds toward negative infinity: shifting -7 right by one gives -4, while idiv by 2 gives -3.
  30. 30
    What do push rax and pop rax do?
    push subtracts 8 from rsp, then stores rax at [rsp]; pop loads [rsp] into rax, then adds 8 to rsp
    The stack grows toward lower addresses.
  31. 31
    What does call func do?
    Pushes the return address (the address of the next instruction) and jumps to func
  32. 32
    What does ret do?
    Pops the return address off the stack and jumps to it
  33. 33
    A function prologue and epilogue with a frame pointer
    x86asm
    func:
        push rbp
        mov rbp, rsp
        sub rsp, 16      ; room for locals
        ; ...
        leave            ; mov rsp, rbp, then pop rbp
        ret
  34. 34
    After push rbp and mov rbp, rsp, what are [rbp], [rbp+8] and [rbp-8]?
    The caller’s saved rbp, the return address, and the first 8-byte slot for local variables
    With System V, arguments passed on the stack start at [rbp+16].
  35. 35
    Which registers carry the first six integer or pointer arguments (System V AMD64)?
    rdi, rsi, rdx, rcx, r8, r9, in that order; any further arguments go on the stack
    Windows x64 differs: it uses rcx, rdx, r8, r9 and reserves 32 bytes of shadow space.
  36. 36
    Where does a function return an integer or pointer (System V AMD64)?
    In rax; a 128-bit value uses rdx:rax
  37. 37
    Floating-point arguments and return values (System V AMD64)
    Arguments go in xmm0 to xmm7; a double or float comes back in xmm0
  38. 38
    Callee-saved registers in System V AMD64
    rbx, rbp and r12 to r15 (and rsp): a function that uses them must restore them before returning
  39. 39
    Caller-saved registers in System V AMD64
    rax, rcx, rdx, rsi, rdi, r8 to r11 and all xmm registers: any call may change them, so save them yourself if you need them afterwards
  40. 40
    Stack alignment rule for a call (System V AMD64)
    rsp must be a multiple of 16 just before the call, so on entry to a function rsp is 8 bytes past a 16-byte boundary
    That’s why rsp is 16-byte aligned again once a function has pushed rbp.
  41. 41
    What is the red zone?
    The 128 bytes below rsp that a function may use without moving rsp; signal handlers won’t overwrite them
    Handy for leaf functions. Kernel code is compiled without it.
  42. 42
    What goes in al before calling a variadic function such as printf?
    An upper bound on the number of vector registers used to pass arguments, so xor eax, eax when there are no floating-point arguments
  43. 43
    Implement int sum(int a, int b)
    x86asm
    sum:
        lea eax, [rdi+rsi]
        ret
    a arrives in edi, b in esi, and the result is returned in eax.
  44. 44
    How do you make a Linux system call in x86-64?
    Put the syscall number in rax and arguments in rdi, rsi, rdx, r10, r8, r9, then run syscall; the result comes back in rax, with a negative errno on failure
  45. 45
    Why does the fourth syscall argument go in r10 rather than rcx?
    syscall overwrites rcx with the return address (and r11 with rflags)
  46. 46
    Print hi and exit with status 0 on Linux
    x86asm
    section .data
    msg: db "hi", 10
    len equ $ - msg
    
    section .text
    global _start
    _start:
        mov eax, 1          ; write
        mov edi, 1          ; stdout
        lea rsi, [rel msg]
        mov edx, len
        syscall
        mov eax, 60         ; exit
        xor edi, edi        ; status 0
        syscall
  47. 47
    What goes in the .text, .data, .rodata and .bss sections?
    Code; initialized writable data; read-only data such as string constants; zero-initialized data, which takes no space in the file
  48. 48
    Assemble and link a NASM program on Linux
    bash
    nasm -f elf64 hello.asm -o hello.o
    ld hello.o -o hello
    To call C library functions, define main instead of _start and link with gcc.
  49. 49
    How does AT&T syntax differ from Intel syntax?
    AT&T puts the source first and the destination last, prefixes registers with % and immediates with $, and adds size suffixes: AT&T movq $1, %rax is Intel mov rax, 1
    GNU as and objdump use AT&T by default; objdump -d -M intel shows Intel syntax.
  50. 50
    What is the AT&T operand 8(%rbp,%rcx,4) in Intel syntax?
    [rbp+rcx*4+8]: AT&T writes displacement(base, index, scale)

Frequently asked questions

Intel or AT&T syntax?

Intel syntax, as NASM writes it. GNU as and objdump default to AT&T syntax, so two cards show how to translate it.

Does it cover Windows?

The calling convention cards follow System V AMD64, used by Linux, macOS and the BSDs, and the system call cards are Linux-only. One card notes how the Windows x64 convention differs.

Do I need to know C?

It helps. Several cards relate assembly to C functions and types, but the registers and instructions stand on their own.

Intermediate

JavaScript

Master the JavaScript every developer uses daily: scope, closures, equality, array methods, promises, async/await and the event loop.

50 cards
Intermediate

Advanced JS

Fifty advanced JavaScript cards: event-loop ordering puzzles, generators and async iteration, this binding, Proxy and Reflect, modules, AbortController and coercion edge cases.

50 cards
Intermediate

TypeScript

Learn the TypeScript you use every day: narrowing, generics, keyof, mapped and conditional types, utility types, satisfies and strict mode, with code on every card.

50 cards
Beginner

Python

Learn idiomatic Python fast: comprehensions, f-strings, args and kwargs, generators, decorators, context managers and the standard library.

50 cards
Beginner

Git & terminal

The Git commands and shell skills developers use every day: branching, rebasing, undoing mistakes, pipes, redirects, permissions and processes.

50 cards
Beginner

Java

Core Java with short snippets: equals vs ==, records, sealed types, collections, generics, exceptions, streams, lambdas and JVM basics.

50 cards
Beginner

C#

C# and .NET fundamentals with short snippets: value vs reference types, properties, records, nullable reference types, LINQ, async/await and DI.

50 cards
Beginner

Kotlin

Kotlin fundamentals with short snippets: null safety, val vs var, data and sealed classes, extension functions, scope functions and coroutines.

50 cards
Beginner

Swift

Swift fundamentals with short snippets: optionals, structs vs classes, protocols, enums with associated values, closures, ARC, async/await and SwiftUI.

50 cards
Beginner

Flutter

Flutter and Dart basics: null safety, futures and streams, stateless vs stateful widgets, setState, hot reload, keys, layout and state management.

50 cards
Beginner

PHP

Modern PHP 8 with short snippets: strict types, arrays, match, nullsafe and named arguments, enums, readonly, Composer, PDO and password_hash.

50 cards
Beginner

Ruby

Ruby fundamentals with short snippets: blocks, procs and lambdas, symbols, Enumerable, mixins, truthiness, method_missing, gems and Bundler.

50 cards
Beginner

Go

Go (Golang) fundamentals with short, idiomatic snippets: slices vs arrays, maps, defer, error wrapping, interfaces, goroutines, channels, select and context.

50 cards
Intermediate

Rust

Learn the Rust that the compiler keeps asking about: ownership, borrowing, lifetimes, Option and Result, traits, closures, smart pointers and async.

50 cards
Intermediate

C++

Modern C++ in short snippets: references vs pointers, RAII, smart pointers, move semantics, STL containers and algorithms, const, templates and UB.

50 cards
Intermediate

Haskell

Haskell fundamentals with short code: types and type classes, pattern matching, laziness, Maybe and Either, folds, Functor, Applicative and Monad.

50 cards
Intermediate

Elixir

Elixir fundamentals with short snippets: immutability, pattern matching, the pipe operator, Enum vs Stream, processes, GenServer, supervisors and mix.

50 cards
Beginner

Regex

Learn regular expressions for good: anchors, classes, greedy vs lazy quantifiers, groups, lookarounds, flags, replacements and the backtracking trap.

50 cards