Skip to content

07 — Basic Instructions

This module covers the core instruction set: data movement, arithmetic, and logic. These form the building blocks of every assembly program.


Data Transfer Instructions

MOV — Move (Copy)

The most common instruction. Copies a value from source to destination.

mov dst, src      ; dst = src
mov rax, 42       ; register  ← immediate
mov rax, rbx      ; register  ← register
mov rax, [rbx]    ; register  ← memory
mov [rbx], rax    ; memory    ← register
mov [rbx], 0      ; memory    ← immediate (size must be clear)
mov dword [rbx], 0

Rules: - Destination and source cannot both be memory references - Source and destination must be the same size

MOVZX — Move with Zero Extension

Copies a smaller value into a larger register, filling upper bits with zeros.

movzx rax, byte  [rsi]   ; rax = zero_extend(*(u8*)rsi)
movzx rax, word  [rsi]   ; rax = zero_extend(*(u16*)rsi)
movzx rax, bl            ; rax = zero_extend(bl)

MOVSX — Move with Sign Extension

Copies a smaller value, filling upper bits with the sign bit (two's complement).

movsx rax, byte  [rsi]   ; rax = sign_extend(*(i8*)rsi)
movsx rax, word  [rsi]   ; rax = sign_extend(*(i16*)rsi)
movsx rax, ebx           ; rax = sign_extend(ebx)  (32→64)
movsxd rax, ebx          ; same as above (explicit 32→64 mnemonic)

XCHG — Exchange

Atomically swap two values (one can be memory). Useful for spin locks.

xchg rax, rbx     ; swap rax and rbx
xchg [mem], rax   ; atomically swap register with memory

CMOVcc — Conditional Move

Move only if condition is true. Avoids branch mispredictions.

cmp  rax, rbx
cmove  rax, rcx    ; if ZF=1 (equal), rax = rcx
cmovne rax, rcx    ; if ZF=0 (not equal)
cmovl  rax, rcx    ; if SF≠OF (less than, signed)
cmovg  rax, rcx    ; if ZF=0 and SF=OF (greater than, signed)

Arithmetic Instructions

ADD — Addition

add dst, src    ; dst = dst + src
add rax, rbx    ; rax += rbx
add rax, 10     ; rax += 10
add [mem], rax  ; memory += rax

Sets: CF (carry/overflow), ZF (zero), SF (sign), OF (signed overflow)

SUB — Subtraction

sub rax, rbx    ; rax -= rbx
sub rax, 5      ; rax -= 5

CF is set if a borrow occurred (unsigned underflow).

INC / DEC — Increment / Decrement

inc rax    ; rax++   (does NOT modify CF)
dec rax    ; rax--   (does NOT modify CF)

Note: INC/DEC don't update CF, which is why some code prefers add rax, 1 in loops.

MUL — Unsigned Multiply

mul rbx    ; RDX:RAX = RAX * RBX  (128-bit result)
mul ecx    ; EDX:EAX = EAX * ECX  (64-bit result)
mul cx     ; DX:AX   = AX  * CX   (32-bit result)

The source is always the implicit RAX (or EAX, AX, AL). Result spans two registers.

IMUL — Signed Multiply

IMUL has three forms:

; 1-operand: RDX:RAX = RAX * src (like MUL but signed)
imul rbx

; 2-operand: dst = dst * src  (truncated to register width)
imul rax, rbx       ; rax *= rbx
imul rax, 7         ; rax *= 7

; 3-operand: dst = src1 * src2  (truncated)
imul rax, rbx, 10   ; rax = rbx * 10

The 2- and 3-operand forms are most commonly used because the result stays in one register.

DIV — Unsigned Divide

; For 64-bit: RDX:RAX ÷ src → quotient=RAX, remainder=RDX
xor rdx, rdx    ; MUST zero RDX before dividing!
mov rax, 100
mov rbx, 7
div rbx         ; rax = 100/7 = 14, rdx = 100%7 = 2

Division by zero triggers a #DE (Divide Error) exception — program crash.

IDIV — Signed Divide

; RDX:RAX ÷ src → quotient=RAX, remainder=RDX
; Sign-extend RAX into RDX first:
cqo             ; sign-extend RAX → RDX:RAX (Convert Quadword to Octaword)
mov rbx, -7
idiv rbx

Sign-extension instructions:

Instruction Effect
cbw Sign-extend AL → AX
cwde Sign-extend AX → EAX
cdqe Sign-extend EAX → RAX
cwd Sign-extend AX → DX:AX
cdq Sign-extend EAX → EDX:EAX
cqo Sign-extend RAX → RDX:RAX

NEG — Negate (Two's Complement)

neg rax    ; rax = -rax  (two's complement negation)

Equivalent to not rax; add rax, 1.


Logic Instructions

AND, OR, XOR

and rax, rbx    ; rax = rax & rbx   (bitwise AND)
or  rax, rbx    ; rax = rax | rbx   (bitwise OR)
xor rax, rbx    ; rax = rax ^ rbx   (bitwise XOR)

Common idioms:

xor rax, rax    ; rax = 0  (faster/smaller than mov rax, 0)
and rax, 0x0F   ; keep only the low nibble
or  rax, 0x20   ; set bit 5 (lowercase a letter)
xor rax, 0xFF   ; toggle the low byte

NOT — Bitwise Complement

not rax    ; rax = ~rax  (flip all bits)

TEST — Bitwise AND Without Storing

TEST performs AND and sets flags but discards the result. Used for comparisons.

test rax, rax    ; is rax zero? (ZF=1 if zero)
test rax, 1      ; is bit 0 set? (ZF=0 if set)
test al,  0x80   ; is the sign bit set?

CMP — Compare (Subtraction Without Storing)

CMP subtracts src from dst and sets flags but discards the result.

cmp rax, rbx    ; sets flags as if: rax - rbx
cmp rax, 0      ; compare with zero
cmp rax, -1     ; compare with -1

After CMP, use a conditional jump:

cmp  rax, 10
je   equal_label    ; jump if rax == 10  (ZF=1)
jne  neq_label      ; jump if rax != 10  (ZF=0)
jl   less_label     ; jump if rax <  10  (signed)
jg   greater_label  ; jump if rax >  10  (signed)
jle  le_label       ; jump if rax <= 10  (signed)
jge  ge_label       ; jump if rax >= 10  (signed)
jb   below_label    ; jump if rax <  10  (unsigned)
ja   above_label    ; jump if rax >  10  (unsigned)

Shift Instructions

SHL / SHR — Shift Left / Right (Logical)

shl rax, 1     ; rax <<= 1  (multiply by 2)
shl rax, 3     ; rax <<= 3  (multiply by 8)
shr rax, 1     ; rax >>= 1  (divide by 2, unsigned)
shr rax, cl    ; shift amount in CL register

SHL fills with 0 bits on the right. SHR fills with 0 bits on the left.

SAR — Shift Arithmetic Right (Signed)

sar rax, 1     ; rax >>= 1  (divide by 2, signed — preserves sign bit)

SAR fills with the sign bit (MSB), preserving the sign for negative numbers.

ROL / ROR — Rotate Left / Right

rol rax, 1     ; rotate left: MSB wraps to LSB
ror rax, 8     ; rotate right by 8 bits

Bits rotate around — nothing is lost, just moved.


NOP — No Operation

nop    ; does nothing, advances RIP by 1 byte

Used for alignment, timing padding, or as placeholder for patching.


Complete Example: Integer Math

; math.asm — demonstrate arithmetic instructions
section .data
    a  dq 100
    b  dq  37

section .text
global _start

_start:
    ; Load values
    mov rax, [a]    ; rax = 100
    mov rbx, [b]    ; rbx = 37

    ; Addition
    mov rcx, rax
    add rcx, rbx    ; rcx = 137

    ; Subtraction
    mov rdx, rax
    sub rdx, rbx    ; rdx = 63

    ; Multiplication (2-operand IMUL)
    imul rax, rbx   ; rax = 100 * 37 = 3700

    ; Division: 100 / 37
    mov  rax, [a]
    xor  rdx, rdx
    mov  rbx, [b]
    div  rbx        ; rax = 2 (quotient), rdx = 26 (remainder)

    ; Bitwise: mask lower byte
    mov  rax, 0xDEADBEEF
    and  eax, 0xFF        ; eax = 0xEF

    ; Fast multiply by 5 using LEA
    mov  rax, 10
    lea  rax, [rax + rax*4]  ; rax = 10 + 10*4 = 50

    ; Exit
    mov rax, 60
    xor rdi, rdi
    syscall

Key Takeaways

Instruction Effect
mov dst, src Copy value
movzx dst, src Copy with zero-extension
movsx dst, src Copy with sign-extension
add/sub dst, src Arithmetic, sets flags
imul dst, src Signed multiply (2-operand)
div/idiv src RDX:RAX ÷ src; always zero/sign-extend RDX first
and/or/xor dst, src Bitwise logic
test dst, src Bitwise AND, sets flags only
cmp dst, src Subtract, sets flags only
shl/shr/sar dst, n Shift (logical/arithmetic)
xor rax, rax Fastest way to zero a register

Next: 08 — Control Flow