Introduction

x86-64 assembly is the programming language for the 64-bit version of the x86 instruction set. It is based on the original 8086 instruction set from 1978.

This post is intended as a sort of cheat-sheet, assembled while working through the x86-64 Assembly track on Exercism.

Registers

There are 16 general purpose registers, each of which can be addressed in full, or by the lower 32, 16, and 8 bits:

64-bit32-bit16-bit8-bitSpecial Purpose for functionsWhen calling a functionWhen writing a function
raxeaxaxah, alReturn ValueMight be changedUse freely
rbxebxbxbh, blWill not be changedSave before using!
rcxecxcxch, cl4th integer argumentMight be changedUse freely
rdxedxdxdh, dl3rd integer argumentMight be changedUse freely
rsiesisisil2nd integer argumentMight be changedUse freely
rdiedidisil1st integer argumentMight be changedUse freely
rbpebpbpbplFrame PointerMaybe Be CarefulMaybe Be Careful
rspespspsplStack PointerBe Very Careful!Be Very Careful!
r8r8dr8wr8b5th integer argumentMight be changedUse freely
r9r9dr9wr9b6th integer argumentMight be changedUse freely
r10r10dr10wr10bMight be changedUse freely
r11r11dr11wr11bMight be changedUse freely
r12r12dr12wr12bWill not be changedSave before using!
r13r13dr13wr13bWill not be changedSave before using!
r14r14dr14wr14bWill not be changedSave before using!
r15r15dr15wr15bWill not be changedSave before using!
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ rax ━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃                               ┏━━━━━━━━━━━━━ eax ━━━━━━━━━━━━┫
┃                               ┃               ┏━━━━━ ax ━━━━━┫
┃                               ┃               ┣━━ ah ━┳━ al ━┫
0000000000000000000000000000000000000000000000000000000000000000

Usage during syscall/function call:

Instructions

Data Transfer

mov  rax, 0x000F     ; Store the value 15 into the rax register
mov  rdi, rsi        ; Copy the value in the rsi register into the rdi register

lea  rdi, [message]  ; Load the address of [message] into the rdi register

push rcx             ; Push the value in the rcx register onto the top of the stack

pop  rcx             ; Pop the value off the top of the stack and into the rcx register

Arithmetic

add  rdi, rsi        ; Add the value in rsi to rdi, and store the result in rdi
                     ; (i.e. rdi = rdi + rsi)

sub  rdi, rsi        ; Subtract the value in rsi from rdi, and store the result in rdi
                     ; (i.e. rdi = rdi - rsi)

inc  rax             ; Increment the value in rax by 1
                     ; (i.e. rax = rax + 1)

dec  rax             ; Decrement the value in rax by 1
                     ; (i.e. rax = rax - 1)

Binary

and  rdi, rsi        ; Bitwise AND, store result in rdi
                     ; (i.e. rdi = rdi & rsi)

or   rdi, rsi        ; Bitwise OR, store result in rdi
                     ; (i.e. rdi = rdi | rsi)

xor  rdi, rsi        ; Bitwise XOR, store result in rdi
                     ; (i.e. rdi = rdi ^ rsi)

not  rax             ; Bitwise Logical Not

shl  rax, 1          ; Shift left the bits in rax, padding the resulting empty bit positions with zeros

shr  rax, 1          ; Shift right the bits in rax, padding the resulting empty bit positions with zeros

Comparison

cmp  rdi, rsi       ; Compare rdi and rsi (rdi - rsi) and set flags.
                    ; Similar to sub, but result is discarded.

test rdi, rsi       ; Compare rdi and rsi (rdi & rsi) and set flags.
                    ; Similar to and, but result is discarded.

Jump

InstructionSynonymDescription
jmpUnconditional
jejzEqual / zero
jnejnzNot equal / not zero
jsNegative
jnsNon-negative
jgjnleGreater than (signed >)
jgejnlGreater than or equal to (signed >=)
jljngeLess then (signed <)
jlejngLess then or equal to (signed <=)
jajnbeAbove (unsigned >)
jaejnbAbove or equal (unsigned >=)
jbjnaeBelow (unsigned <)
jbejnaBelow or equal (unsigned <=)

For all jump instructions other than jmp (which is unconditional), some previous instruction (cmp, test, etc.) is needed to set the condition codes to be examined by the jump.

cmp  rdi, rsi        ; Compare rdi and rsi
je   .label          ; Jump to .label if equal

Procedure calls

call  label            ; Push return address and jump to label

ret                    ; Pop return address from stack and jump there

System Calls

Assembly programs can interact with the operating system using system calls:

  1. Put the system call number into rax
  2. Load any arguments into the corresponding registers (rdi, rsi, rdx, rcx, r8, r9)
  3. Call syscall

System call numbers differ for Linux and macOS:

; ----------------------------------------------------------------------------------------
; Writes "Hello, World" to the console using only system calls. Runs on 64-bit macOS only.
; To assemble and run:
;
;     nasm -fmacho64 hello.asm
;     ld hello.o -static -o hello
;     ./hello
; ----------------------------------------------------------------------------------------

          global    start

          section   .text
start:    mov       rax, 0x02000004         ; system call for write
          mov       rdi, 1                  ; file handle 1 is stdout
          mov       rsi, message            ; address of string to output
          mov       rdx, 13                 ; number of bytes
          syscall                           ; invoke operating system to do the write

          mov       rax, 0x02000001         ; system call for exit
          mov       rdi, 0                  ; exit code 0
          syscall                           ; invoke operating system to exit


          section   .data
message:  db        "Hello, World", 10      ; note the newline at the end

Using C libraries

; ----------------------------------------------------------------------------------------
; Prints the numbers 1-10 using the C library printf function. Runs on 64-bit macOS only.
; To assemble and run:
;
;     nasm -fmacho64 numbers.asm
;     ld -L /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib -lc -o numbers numbers.o
;     ./numbers
; ----------------------------------------------------------------------------------------

          global    _main
          extern    _printf
          default   rel


          section   .text
_main:
          mov       rcx, 1                  ; initialise the counter to 1

print_loop:
          ; Print the counter
          push      rcx                     ; caller - save register
          lea       rdi, [format]           ; set 1st parameter (format)
          mov       rsi, rcx                ; set 2nd parameter (current number)
          call      _printf                 ; call the C library printf function
          pop       rcx                     ; restore caller-saved register

          ; Increment the counter and loop while <= 10
          inc       rcx                     ; increment the counter
          cmp       rcx, 10                 ; compare the counter value to 10
          jle       print_loop              ; jump to print_loop if less then or equal to

          ; Exit the program
          mov       rax, 0                  ; set the exit code
          ret


          section   .data
format:   db        "%d", 10, 0             ; note the newline and NULL characters at the end

The above is equivalent to this in C:

#include <stdio.h>

int main() {
  int i;
  for (i = 1; i <= 10; i++) {
    printf("%d\n", i);
  }
  return 0;
}

or this in Ruby:

(1..10).each {|n| puts n }

Resources