Assembly Tutorial – Looping

We’re going to write a simple program that demonstrates how to loop in assembly. We won’t be using a direct loop construct like in a higher level language. Instead, we’ll be using the jump and comparison instructions we covered in the a previous post.

We can loop infinitely over a block of code in assembly using a label and an unconditional jump:

loop_start:
### code that get's looped over 
jmp loop_start

Usually we don’t want an infinite loop in our code. So we put a conditional jump inside the loop that jumps to a label after the loop ends. Let’s have a look at an example. We’re going to write some code that uses a loop to print 10 asterisks to the terminal and a new line and then exits.

.section .data
asterick: .byte 0x2A
newline: .byte 0xA

.globl _start
_start:

movq $0, %rbx

loop_start:

movq $1, %rax
movq $1, %rdi
movq $asterick, %rsi
movq $1, %rdx
syscall

incq %rbx

cmpq $10, %rbx
jge exit

jmp loop_start

exit:

movq $1, %rax
movq $1, %rdi
movq $newline, %rsi
movq $1, %rdx
syscall

movq $60, %rax
movq $0, %rbx
syscall

In the data section of this code we declare two separate bytes in memory. The first byte is labelled ‘asterick’ and has hex value 2A (the hex value of an asterick). The second is label ‘newline’ and has hex value A (the hex value for a new line).

Then we have our loop:

movq $0, %rbx

loop_start:

movq $1, %rax
movq $1, %rdi
movq $asterick, %rsi
movq $1, %rdx
syscall

incq %rbx

cmpq $10, %rbx
jge exit

jmp loop_start

In this loop we are using the register rbx as our loop counter, so we begin by setting it to 0. Then we have the usual system call to write to stdout. We give the kernel the memory address of the byte in memory that contains the hex code for an asterisk. There is an important point here. The write system call takes a memory address not a value. If we want to print an asterisk, we cannot just pass it the hex value for an asterisk, we have to pass it the memory address of a byte containing an asterisk.

Once we have performed this system call, we must increment our counter. We do this with the instruction:

incq %rbx

This instruction does a 64 bit increment of the value in the register rbx. incq is one of the special instructions we can use to increment and decrement register values. They come in the usual instruction size variations. The instructions incq, incl, incw and incb increment 8 bytes, 4 bytes, 2 bytes and 1 byte respectively. Similarly the instructions decq, decw, decw and decb decrement 8 bytes, 4 bytes, 2 bytes and 1 bytes respectively.

Once we have incremented our counter, we check if the value is greater or equal to 10. If the value in the register rbx was less than ten we move straight to the next instruction:

jmp loop_start

which jumps back to the start of the loop. Notice that we jump back to the next instruction after we set up our loop counter in rbx. If we had put the loop start label one instruction earlier, our loop would run indefinitely, because the counter would have reset to 0 on every iteration.

If however, our counter in rbx is greater or equal to 10 we jump straight to the labelled exit section:

exit:

movq $1, %rax
movq $1, %rdi
movq $newline, %rsi
movq $1, %rdx
syscall

movq $60, %rax
movq $0, %rbx
syscall

This section prints a new line and then exits with exit code 0 as usual. We now know how to do conditional branching and looping in assembly!

Leave a Reply

Your email address will not be published. Required fields are marked *