Control Flow

Control flow in assembler

Obviously we don’t want to just run our code from top to bottom. Most likely we want the control to jump around depending on what we are trying to compute.

Assembler is different from most other languages in that we don’t have the usual if and while constructs. In Assembler, control flow is done using jumps.

The jmp instruction

The jmp instruction tells the machine to jump to a given location.

How do we specify this location? We can use labels. We used one label in the previous post - _start. We can specify as many labels as we need. For example, here’s the code for an program this will print a message for ever (at least until we kill the program):

; Here we do amazing things, like say hello.
section .data
  greetings db "Greetings earthlings!", 10
  peace db "We come in peace.", 10

section .text
  global _start

_start:
  ; Greet the earthlings.
  mov rax, 1
  mov rdi, 1
  mov rsi, greetings
  mov rdx, 22
  syscall

_loopstart: ; The start of our loop
  ; Say we come in peace
  mov rax, 1
  mov rdi, 1
  mov rsi, peace
  mov rdx, 18
  syscall

  jmp _loopstart ; Jump back to the start of the loop
Greetings earthlings!
We come in peace.
We come in peace.
We come in peace.
We come in peace.
...

That’s all good, but not too useful. Most of the time we want to jump based on a given condition. There’s a bunch of instructions for that. First, we need to know how to set up the condition.

The cmp instruction

The cmp instruction compares the value in a given register to a value.

There is a special register called the FLAGS register. This register contains a number of flags that indicate the state of the CPU, each bit within the flag represents a different state. One of the flags pertinent to our current situation is the Zero flag. This flag is set to 1 when we perform an arithmetic operation that results in 0. Essentially, the cmp instruction performs some arithmetic on it’s argument. However, it doesn’t return any result - it just updates the Zero flag.

The je and jne instructions

The je instruction will look at the Zero flag and will only jump if the Zero flag is set. Conversely, jne jumps if this flag is not set. These instructions can be combined with cmp to perform conditional jumps:

  cmp r15, 0     ; Compare register r15 to 0
  je _somewhere  ; Jump to _somewhere if r15 was 0

The sub instruction

One more useful instruction in our quest is sub. This will subtract a given number from the contents of a particular register.

A loop

Armed with this knowledge we can now perform a loop say, just 3 times:

section .data
  dothis db "Do this stuff!", 10

section .text
  global _start

_start:
  mov r15, 3 ; set a counter in register r15 to 3

_loopstart:
  ;; output our text
  mov rax, 1
  mov rdi, 1
  mov rsi, dothis
  mov rdx, 22
  syscall

  sub r15, 1     ; subtract 1 from our counter.
  cmp r15, 0     ; is the counter now 0?
  jne _loopstart ; if not, then jump to the loop start again

  ;; exit
  mov rax, 60
  mov rdi, 0
  syscall

When we build and run this, we create something beautiful:

╰─$ ./loop
Do this stuff!
Do this stuff!
Do this stuff!