Reading — step 1 of 5
Learn
~2 min readControl Flow and Memory
Assembly has no if/else or for. Instead: compare two values, then conditional jump based on the result.
cmp and jumps
mov rax, 5
mov rbx, 10
cmp rax, rbx ; sets flags based on rax - rbx
jl is_less ; jump if signed less than
jge is_greater_eq ; jump if signed >=
cmp a, b sets CPU flags as if computing a - b. The conditional jump tests those flags.
Conditional jump instructions
Equality — sign does not matter:
je/jz— equal / zerojne/jnz— not equal / not zero
For signed comparison:
jl/jg— less than / greater thanjle/jge— less or equal / greater or equal
For unsigned:
jb/ja— below / abovejbe/jae— below-or-equal / above-or-equal
For zero/sign:
js/jns— sign / not-signjc/jnc— carry / no carry
Unconditional jump
jmp label ; goto
Loop: count from 0 to 9
section .data
digit db 0, 10
section .text
global _start
_start:
xor rcx, rcx ; rcx = 0 (counter)
loop_start:
cmp rcx, 10
jge loop_end
; convert rcx to ASCII
mov al, cl
add al, '0' ; '0' = 0x30
mov [digit], al
; write to stdout
push rcx ; preserve rcx across syscall
mov rax, 1
mov rdi, 1
mov rsi, digit
mov rdx, 2
syscall
pop rcx
inc rcx
jmp loop_start
loop_end:
mov rax, 60
xor rdi, rdi
syscall
The loop instruction (legacy)
x86 has a built-in loop instruction that decrements rcx and jumps if non-zero:
mov rcx, 10
my_loop:
; ... body ...
loop my_loop ; rcx-- ; if (rcx != 0) jump
It's slower than manual dec/jnz on modern CPUs but more compact in code.
Test bit instruction
test rax, rax ; AND with itself, sets ZF if zero
jz is_zero ; jump if rax was 0
test rax, rax is the canonical way to check if a register is zero.
A pattern: while-not-zero
.loop:
test rax, rax
jz .done
; ... body ...
dec rax
jmp .loop
.done:
The .loop and .done are local labels — visible only within the enclosing global label. Less namespace pollution.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…