Addition & Subtraction
ADD, SUB, ADC, SBB
add rax, rbx ; RAX = RAX + RBX
add rax, 100 ; RAX = RAX + 100
sub rcx, rdx ; RCX = RCX - RDX
; Multi-precision addition (128-bit)
add rax, [low64] ; Add low 64 bits
adc rdx, [high64] ; Add high 64 bits with carry
; Multi-precision subtraction
sub rax, [low64]
sbb rdx, [high64] ; Subtract with borrow
INC, DEC, NEG
inc rax ; RAX = RAX + 1 (doesn't affect CF)
dec rbx ; RBX = RBX - 1 (doesn't affect CF)
neg rcx ; RCX = -RCX (two's complement)
Multiplication
MUL (Unsigned Multiplication)
Important: MUL uses implicit operands. For 64-bit: RAX × operand → RDX:RAX (128-bit result).
; 64-bit unsigned multiplication
mov rax, 1000
mov rbx, 2000
mul rbx ; RDX:RAX = RAX * RBX
; RAX = low 64 bits, RDX = high 64 bits
IMUL (Signed Multiplication)
; Three forms of IMUL
imul rbx ; RDX:RAX = RAX * RBX (one operand)
imul rax, rbx ; RAX = RAX * RBX (two operands)
imul rax, rbx, 10 ; RAX = RBX * 10 (three operands)
Division
DIV (Unsigned Division)
; 64-bit: RDX:RAX / operand → RAX (quotient), RDX (remainder)
xor rdx, rdx ; Clear RDX for 64-bit dividend
mov rax, 100
mov rbx, 7
div rbx ; RAX = 100/7 = 14, RDX = 100%7 = 2
IDIV (Signed Division)
; Must sign-extend RAX into RDX:RAX first
mov rax, -100
cqo ; Sign-extend RAX into RDX:RAX
mov rbx, 7
idiv rbx ; RAX = -14, RDX = -2
Bitwise Operations
AND, OR, XOR, NOT
and rax, rbx ; RAX = RAX & RBX
or rax, rbx ; RAX = RAX | RBX
xor rax, rbx ; RAX = RAX ^ RBX
not rax ; RAX = ~RAX (one's complement)
; Common idioms
xor rax, rax ; Clear register (faster than mov rax, 0)
and rax, 0x0F ; Mask lower nibble
or rax, 0x80 ; Set bit 7
Common Bit Tricks
| Operation | Code | Effect |
| Test if power of 2 | test rax, rax-1 ; jz is_power_of_2 (needs LEA trick) | n & (n-1) == 0 |
| Isolate lowest set bit | blsi rax, rbx or and rax, -rax | e.g., 0b10110 → 0b00010 |
| Clear lowest set bit | blsr rax, rbx or and rax, rax-1 | e.g., 0b10110 → 0b10100 |
| Swap values (no temp) | xor a,b; xor b,a; xor a,b | XOR swap trick |
| Set nth bit | bts rax, n or or rax, (1 << n) | Set specific bit |
| Clear nth bit | btr rax, n | Clear specific bit |
| Toggle nth bit | btc rax, n or xor rax, (1 << n) | Flip specific bit |
| Count set bits | popcnt rax, rbx | Population count (needs CPU support) |
| Find first set bit | bsf rax, rbx | Bit scan forward (from LSB) |
| Find last set bit | bsr rax, rbx | Bit scan reverse (from MSB) |
; Check if number is power of 2
is_power_of_two:
test rdi, rdi ; Check if zero
jz .not_power ; 0 is not a power of 2
lea rax, [rdi - 1] ; rax = n - 1
test rdi, rax ; n & (n-1)
jnz .not_power ; If non-zero, not power of 2
mov eax, 1 ; Is power of 2
ret
.not_power:
xor eax, eax
ret
; Count trailing zeros (position of lowest set bit)
count_trailing_zeros:
bsf rax, rdi ; Bit scan forward
jnz .found
mov eax, 64 ; If input was 0, return 64
.found:
ret
Exercise: Extract Bit Field
Extract bits 4-7 from a register (4-bit field):
; Extract bits 4-7 from RDI into RAX
extract_bits_4_7:
mov rax, rdi
shr rax, 4 ; Shift field to bit 0
and rax, 0x0F ; Mask to keep only 4 bits
ret
Shifts & Rotates
shl rax, 1 ; Shift left (multiply by 2)
shr rax, 1 ; Shift right logical (unsigned divide by 2)
sar rax, 1 ; Shift right arithmetic (signed divide by 2)
rol rax, 1 ; Rotate left
ror rax, 1 ; Rotate right
; Fast multiplication by power of 2
shl rax, 3 ; RAX = RAX * 8
Flags & Overflow Detection
Arithmetic operations set CPU flags that indicate result characteristics:
| Flag | Name | Set When | Use For |
| ZF | Zero Flag | Result is zero | Equality checks, loop termination |
| SF | Sign Flag | Result is negative (MSB=1) | Signed comparisons |
| CF | Carry Flag | Unsigned overflow/borrow | Multi-precision arithmetic, unsigned checks |
| OF | Overflow Flag | Signed overflow | Signed arithmetic error detection |
| PF | Parity Flag | Even number of 1-bits in low byte | Rarely used (legacy) |
| AF | Auxiliary Flag | BCD carry from bit 3 to bit 4 | BCD arithmetic (legacy) |
Overflow vs Carry
Key Distinction:
CF (Carry) - Unsigned overflow:
0xFFFFFFFF + 1 = 0x00000000 with CF=1
(Unsigned: 4294967295 + 1 wraps to 0)
OF (Overflow) - Signed overflow:
0x7FFFFFFF + 1 = 0x80000000 with OF=1
(Signed: 2147483647 + 1 wraps to -2147483648)
; Detect unsigned overflow
add rax, rbx
jc .unsigned_overflow ; CF=1 means overflow
; Detect signed overflow
add rax, rbx
jo .signed_overflow ; OF=1 means overflow
; Safe addition with overflow check
safe_add_unsigned:
add rdi, rsi
jc .overflow_error
mov rax, rdi
ret
.overflow_error:
mov rax, -1 ; Return error code
ret
; Multi-precision 128-bit addition (using CF)
; RDX:RAX += RCX:RBX
add rax, rbx ; Add low 64 bits
adc rdx, rcx ; Add high 64 bits + carry
Which Flag to Check?
- Unsigned arithmetic: Check CF (Carry Flag)
- Signed arithmetic: Check OF (Overflow Flag)
- Subtraction/comparison: CF indicates borrow (A < B unsigned)
Exercise: Saturating Addition
Implement unsigned addition that saturates at MAX instead of wrapping:
; saturate_add(a, b): returns min(a+b, 0xFFFFFFFFFFFFFFFF)
saturate_add:
mov rax, rdi
add rax, rsi ; rax = a + b
sbb rcx, rcx ; rcx = 0 if no carry, -1 if carry
or rax, rcx ; If overflow, rax becomes all 1s (MAX)
ret