Post

8. Branch Instructions

8. Branch Instructions

Branch instructions are what make programs “intelligent” - they allow your code to make decisions, repeat actions, and respond to different conditions. This is where we implement if statements, loops, and all conditional logic.

What are Branch Instructions?

Branch instructions change the flow of program execution based on conditions. They work by:

  1. Checking flags set by previous operations
  2. Deciding whether to jump to a different location
  3. Modifying RIP (Instruction Pointer) to change what executes next

Types of Jumps

1. Unconditional Jump (JMP)

Always jumps to the specified label.

1
2
3
4
jmp my_label      ; Always jump to my_label
; Code here won't execute
my_label:
    ; Execution continues here

2. Conditional Jumps

Jump only if specific flag conditions are met. These are the workhorses of decision-making.

Conditional Jump Categories

A. Equality Comparisons (based on ZF)
InstructionConditionDescription
JE / JZZF = 1Jump if Equal / Jump if Zero
JNE / JNZZF = 0Jump if Not Equal / Jump if Not Zero
1
2
3
4
5
6
7
8
9
mov rax, 10
mov rbx, 10
cmp rax, rbx        ; Sets ZF=1 (they are equal)
je they_are_equal   ; This jump WILL be taken

mov rax, 10
mov rbx, 20
cmp rax, rbx        ; Sets ZF=0 (they are not equal)
je they_are_equal   ; This jump will NOT be taken
B. Unsigned Comparisons (based on CF)

Used when comparing unsigned numbers.

InstructionConditionDescription
JB / JNAECF = 1Jump if Below / Jump if Not Above or Equal
JAE / JNBCF = 0Jump if Above or Equal / Jump if Not Below
JA / JNBECF=0 and ZF=0Jump if Above / Jump if Not Below or Equal
JBE / JNACF=1 or ZF=1Jump if Below or Equal / Jump if Not Above
1
2
3
4
5
6
7
8
9
mov al, 200    ; Unsigned: 200
mov bl, 100    ; Unsigned: 100
cmp al, bl
ja above       ; Jump because 200 > 100 (unsigned)

mov al, 50     ; Unsigned: 50  
mov bl, 100    ; Unsigned: 100
cmp al, bl
jb below       ; Jump because 50 < 100 (unsigned)
C. Signed Comparisons (based on SF and OF)

Used when comparing signed numbers.

InstructionConditionDescription
JL / JNGESF ≠ OFJump if Less / Jump if Not Greater or Equal
JGE / JNLSF = OFJump if Greater or Equal / Jump if Not Less
JG / JNLEZF=0 and SF=OFJump if Greater / Jump if Not Less or Equal
JLE / JNGZF=1 or SF≠OFJump if Less or Equal / Jump if Not Greater
1
2
3
4
5
6
7
8
9
mov al, -10    ; Signed: -10
mov bl, 5      ; Signed: +5
cmp al, bl
jl less        ; Jump because -10 < 5 (signed)

mov al, -5     ; Signed: -5
mov bl, -10    ; Signed: -10  
cmp al, bl
jg greater     ; Jump because -5 > -10 (signed)

Let’s see some practical examples of Branch Instructions.

Example 1: Simple If-Else

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
section .text
global _start

_start:
    mov rax, 15
    mov rbx, 20
    
    cmp rax, rbx
    jg greater
    jl less
    ; If we get here, they're equal
    
equal:
    ; rax == rbx
    jmp end
    
greater:
    ; rax > rbx
    jmp end
    
less:
    ; rax < rbx
    
end:
    mov rax, 60
    mov rdi, 0
    syscall

Example 2: Loop with Counter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
section .text
global _start

_start:
    mov rcx, 5          ; Loop counter
    mov rax, 0          ; Sum
    
loop_start:
    add rax, rcx        ; Add counter to sum
    dec rcx             ; Decrement counter
    jnz loop_start      ; Jump if RCX != 0
    
    ; Now RAX = 5+4+3+2+1 = 15
    
    mov rax, 60
    mov rdi, 0
    syscall
Common Patterns and Idioms

Infinite Loop:

1
2
3
infinite_loop:
    ; Do something
    jmp infinite_loop

Skip Code Block:

1
2
3
4
    cmp rax, 0
    jz skip_block
    ; Code to skip if RAX == 0
skip_block:

Keep this in mind.

JG is used for signed comparisons. For unsigned comparisons, you would use JA (Jump if Above).

Example the following code has a problem

1
2
3
4
5
mov rax, -1         ; Signed: -1
mov rbx, 255        ; Unsigned: 255
cmp rax, rbx
jg signed_compare   ; Uses signed comparison!
; -1 > 255? No! But 255 > -1? Yes for unsigned!
  • Signed comparison treats values as if they’re part of a signed number system, which means it accounts for positive and negative numbers.
  • Unsigned comparison treats all values as positive, and doesn’t account for sign at all.

rax contains -1, which, in two’s complement representation (the most common way to represent negative numbers in a computer), is represented as 0xFFFFFFFFFFFFFFFF in 64-bit. And rbx contains 255, which is simply 0xFF in 64-bit.

The cmp instruction performs a signed comparison. So, -1 (in signed comparison) is considered “larger” than 255 in this case, because -1 is represented as the largest 64-bit signed value (0xFFFFFFFFFFFFFFFF), and 255 is much smaller in signed terms. However, if you were to perform unsigned comparison, the situation would be reversed, and 255 would be considered “larger” than -1, since unsigned comparison treats -1 as a very large positive number (specifically, 0xFFFFFFFFFFFFFFFF).

To perform an unsigned comparison, you need to use the ja (jump if above) or jae (jump if above or equal) instructions instead of jg. The jg instruction is for signed comparisons.

1
2
; jg signed_compare
ja unsigned_compare  ; Use unsigned comparison!

To clear this again lemme clarify again. When you compare -1 (which is 0xFFFFFFFFFFFFFFFF in two’s complement) and 255 (0xFF), we need to distinguish between signed and unsigned comparisons:

  • In signed comparison:

    • rax contains -1, which is a negative number.
    • rbx contains 255, which is positive.
    • So, in signed comparison, -1 is less than 255 because negative numbers are always smaller than positive numbers.
  • In unsigned comparison:

    • rax contains 0xFFFFFFFFFFFFFFFF (which is very large in unsigned terms).
    • rbx contains 255 (0xFF), which is much smaller in unsigned terms.
    • So, in unsigned comparison, -1 (which is treated as the maximum unsigned value) is greater than 255.

jg (jump if greater) is a signed comparison that checks if the value in rax is greater than rbx assuming signed numbers.

Since we are dealing with a signed number (rax = -1) and an unsigned number (rbx = 255), and you want to check if -1 is greater than 255 in unsigned comparison, we should use ja (jump if above) correctly — because in unsigned comparison, -1 is treated as a very large number (i.e., 0xFFFFFFFFFFFFFFFF), which is indeed greater than 255.

Use the following code in SASM to get better picture while debugging -

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
%include "io64.inc"

section .text
global CMAIN
CMAIN:
    mov rbp, rsp        ; for correct debugging
    mov rax, -1         ; Signed: -1
    mov rbx, 255        ; Unsigned: 255
    cmp rax, rbx
;    jg signed_compare   ; Uses signed comparison!  <-- This is incorrect for unsigned comparison.
    ja unsigned_compare ; Correct: Use unsigned comparison (rax > rbx in unsigned terms)

    mov rax, 60
    mov rdi, 0x1
    syscall
    
unsigned_compare:        ; New label for unsigned comparison
    jmp end

end:
    mov rax, 60
    syscall

You can checkout this page

This post is licensed under CC BY 4.0 by the author.