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!