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
while constructs. In
Assembler, control flow is done using jumps.
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.
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.
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
One more useful instruction in our quest is
sub. This will subtract a given number from the contents of a particular register.
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!