Belajar Assembly - Control Flow: Jump & Conditional
Episode 6 of 23

Belajar Assembly - Control Flow: Jump & Conditional

Mengendalikan alur eksekusi CPU: jmp tanpa syarat, perbandingan dengan cmp lalu jump kondisional je/jne/jg/jl untuk integer signed dan ja/jb untuk unsigned, loop dan jrcxz, serta call/ret yang menjadi dasar pemanggilan fungsi di episode 8.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Sejauh ini program kalian selalu berjalan lurus dari atas ke bawah. Padahal kekuatan sebuah program ada pada kemampuannya mengambil keputusan — dan di assembly, keputusan dibuat dengan jump. Episode 6 membedah cara CPU mengubah RIP (ingat episode 2?) lewat instruksi jmp dan conditional jump.

Mengapa ini penting? Karena semua struktur bahasa tingkat tinggi — if, for, while, switch — pada akhirnya diterjemahkan compiler menjadi jump. Saat kalian membaca objdump di episode 16 atau menulis exploit di episode 17, kalian akan terus membaca pola cmp → jXX. Kuasai sekarang, dan sisanya akan terasa familiar.

jmp: Lompatan Tanpa Syarat

Instruksi jmp memaksa RIP menunjuk ke label target, tanpa syarat apa pun:

jmp sederhana
_start:
    jmp skip_data
    ; instruksi di sini tidak akan pernah dieksekusi
skip_data:
    mov rax, 60
    mov rdi, 0
    syscall

Perhatikan bahwa jmp di sini maju (forward). jmp juga bisa mundur (backward) — pola yang dipakai untuk loop:

Loop dengan jmp backward
section .data
    msg db ".", 0x0a
    len equ $-msg
 
section .text
    global _start
_start:
    mov rcx, 5          ; counter
loop_start:
    mov rax, 1
    mov rdi, 1
    mov rsi, msg
    mov rdx, len
    syscall
    dec rcx
    jnz loop_start      ; lompat ke loop_start jika rcx != 0
 
    mov rax, 60
    mov rdi, 0
    syscall

Ini program yang mencetak . sebanyak 5 kali. Bandingkan dengan for (int i=0; i<5; i++) di C — keduanya identik secara semantik.

cmp: Membandingkan Dua Operan

cmp menghitung op1 - op2 secara internal tanpa menyimpan hasil, hanya memperbarui flag di RFLAGS (episode 5). Perbandingan ini kemudian "dibaca" oleh conditional jump:

cmp hanya men-set flag
cmp rax, rbx    ; hitung rax - rbx, set ZF/SF/CF/OF
; flag sudah berubah, nilai rax tidak berubah

Conditional Jump: Membaca Flag

Setelah cmp, kalian memilih jump berdasarkan flag. Penting untuk membedakan dua keluarga conditional jump:

Signed: je, jne, jg, jl, jge, jle

InstruksiKondisiArti
jeZF = 1sama dengan (==)
jneZF = 0tidak sama (!=)
jgZF=0 dan SF=OFlebih besar (>)
jgeSF=OFlebih besar atau sama (>=)
jlSF≠OFlebih kecil (<)
jleZF=1 atau SF≠OFlebih kecil atau sama (<=)

Unsigned: ja, jb, jae, jbe

InstruksiKondisiArti
jaCF=0 dan ZF=0above (>)
jaeCF=0above or equal (>=)
jbCF=1below (<)
jbeCF=1 atau ZF=1below or equal (<=)

Warning

Jangan pernah mencampur signed dan unsigned. cmp rax, 5 lalu ja akan salah jika rax berisi angka negatif: di bit level, 0xFFFFFFFFFFFFFFFF (yaitu -1 signed) lebih besar dari 5 dalam perbandingan unsigned. Pilih keluarga jump sesuai tipe data kalian — ini sumber bug klasik pada program yang menangani ukuran dan panjang.

Contoh: if/else di Assembly

if (rax > rbx) maka...
section .data
    greater db "rax lebih besar", 0x0a
    glen equ $-greater
    smaller db "rax lebih kecil", 0x0a
    slen equ $-smaller
 
section .text
    global _start
_start:
    mov rax, 42
    mov rbx, 7
 
    cmp rax, rbx
    jle else_branch
    ; if (rax > rbx)
    mov rax, 1
    mov rdi, 1
    mov rsi, greater
    mov rdx, glen
    syscall
    jmp done
 
else_branch:
    ; else
    mov rax, 1
    mov rdi, 1
    mov rsi, smaller
    mov rdx, slen
    syscall
 
done:
    mov rax, 60
    mov rdi, 0
    syscall

Perhatikan pola jle else_branch untuk melompati blok if ketika kondisinya salah, lalu jmp done setelah blok if agar eksekusi tidak jatuh ke else. Ini persis seperti yang compiler hasilkan untuk if/else dalam bahasa tingkat tinggi.

loop dan jrcxz

x86-64 menyediakan instruksi loop khusus yang menggabungkan counter dan jump:

  • loop label → kurangi rcx, lompat ke label jika rcx masih bukan nol.
  • jrcxz label → lompat jika rcx adalah nol (biasanya dipakai untuk skip saat panjang = 0).
loop menggunakan rcx
    mov rcx, 5
repeat:
    ; lakukan sesuatu 5 kali
    loop repeat        ; dec rcx; jnz repeat

Tip

Dalam praktik, compiler modern cenderung memakai dec + jnz daripada loop, karena loop memiliki latency yang lebih tinggi di sebagian besar CPU modern. Namun loop tetap penting dipahami — kalian akan menemukannya di banyak kode lama dan di materi reverse engineering.

call dan ret: Fondasi Fungsi

call dan ret adalah jembatan menuju episode 8. Ketika call func dieksekusi:

  1. CPU mendorong alamat instruksi berikutnya (alamat kembali) ke stack.
  2. CPU melompat ke func.

Ketika ret dieksekusi:

  1. CPU mem-pop alamat kembali dari stack.
  2. CPU melompat ke alamat itu — kembali ke titik setelah call.
call dan ret
_start:
    call my_func       ; simpan alamat kembali ke stack, lompat
    mov rax, 60
    mov rdi, 0
    syscall
 
my_func:
    ; do something
    ret                ; kembali ke instruksi setelah call

Inilah mekanisme paling penting untuk struktur program — dan juga titik rapuh bagi keamanan (buffer overflow di episode 17 mengincar alamat kembali ini). Kita bedah stack frame, prologue, dan epilogue secara penuh di episode 8.

Common Pitfalls

  • cmp urutan operan: cmp rax, rbx membedakan "rax dibanding rbx". Membaliknya akan membalik logika jump.
  • Mencampur signed/unsigned jump (lihat Callout di atas).
  • Lupa jmp setelah blok if → eksekusi jatuh ke blok else.
  • loop mengubah rcx → jangan pakai rcx untuk hal lain di dalam loop jika memakai loop.
  • Stack tidak seimbang → call tanpa ret (atau sebaliknya) akan mengacaukan alamat kembali; kita bedah di episode 8.

Penutup

Pada episode 6 ini, kalian telah menguasai cara CPU mengambil keputusan.

Inti yang harus dibawa pulang:

  • jmp = lompat tanpa syarat; bisa maju dan mundur.
  • cmp op1, op2 menghitung op1 - op2 hanya untuk set flag.
  • Signed: je/jne/jg/jl/jge/jle; unsigned: ja/jae/jb/jbe — jangan dicampur.
  • loop = dec rcx; jnz; jrcxz untuk cek nol.
  • call mendorong alamat kembali ke stack, ret mem-pop-nya.

Di episode 7 selanjutnya, kita bedah operasi aritmetika, logika, dan bitwise: add/sub, mul/imul dan div/idiv yang rumit, and/or/xor/not, serta shift shl/shr/sar dan rotate rol/ror — termasuk trik shift untuk perkalian dan pembagian cepat yang dipakai compiler setiap hari. Pastikan flag di episode ini sudah kalian pahami, karena aritmetika akan memainkannya.