This is the second post in a series on writing assembly language code for a program on the AArch64 architecture. You can find the first here.
Once again the task for this last stretch of our AArch64 exploration is to write a loop that iterates 30 times, which necessitates dealing with 2 digit numbers. This can be done by dividing the index by 10. The result goes into the first digit, with a branch to the secondDigit procedure if the first digit is 0. The full code is as follows:
.text
.globl _start
min = 0 /* starting value for the loop index; note that this is a symbol (constant), not a variable */
max = 31 /* loop exits when the index hits this number (loop condition is i<max) */
_start:
mov x19, min
mov x20, 0x0A
loop:
udiv x21, x19, x20
cmp x21, 0
b.eq secondDigit
add x18, x21, '0'
adr x17, msg+6
strb w18, [x17]
secondDigit:
msub x22, x20, x21, x19
add x18, x22, '0'
adr x17, msg+7
strb w18, [x17]
mov x0, 1 /* file descriptor: 1 is stdout */
adr x1, msg /* message location (memory address) */
mov x2, len /* message length (bytes) */
mov x8, 64 /* write is syscall #64 */
svc 0 /* invoke syscall */
// Proceed with loop
add x19, x19, 1
cmp x19, max
b.ne loop
mov x0, 0 /* status -> 0 */
mov x8, 93 /* exit is syscall #93 */
svc 0 /* invoke syscall */
.data
msg: .ascii "Loop: #\n"
len= . - msg
With the following output:
Loop: #0
Loop: #1
Loop: #2
Loop: #3
Loop: #4
Loop: #5
Loop: #6
Loop: #7
Loop: #8
Loop: #9
Loop: #10
Loop: #11
Loop: #12
Loop: #13
Loop: #14
Loop: #15
Loop: #16
Loop: #17
Loop: #18
Loop: #19
Loop: #20
Loop: #21
Loop: #22
Loop: #23
Loop: #24
Loop: #25
Loop: #26
Loop: #27
Loop: #28
Loop: #29
Loop: #30
This looks like the output we're looking for which is great, let me break down the program in a little more detail and summarize my experiences writing AArch64 assembly.
_start:
mov x19, min
mov x20, 0x0A
We start the program by assigning 0 and 10 to registers 19 and 20, respectively. Both are being used as 64 bit widths as made evident by the x
prefix.
loop:
udiv x21, x19, x20
cmp x21, 0
b.eq secondDigit
This portion divides the values in x19
by x20
and places it in x21
. The syntax for AArch64 assembly is such that you can look at operations as operand = value
or operand = expression
in this case, as the destination register comes first in this syntax.
The second line compares the first digit of the result with 0, branching to the secondDigit label if the expression evaluates true. That would be in the case that it's a single digit result, which it will be for the first 10 iterations.
add x18, x21, '0'
adr x17, msg+6
strb w18, [x17]
The first line adds '0' to the value in x21
and places it in x18
, after which the address of the pound sign in msg
is read into x17. The final line stores a byte from w18
to the address pointed to by x17
, the pound sign pointer we just created.
secondDigit:
msub x22, x20, x21, x19
Finally, the secondDigit label gets the remainder with the msub instruction by setting x22
to the result of x20
-(x21
* x19
), or 10 - (result of the division) * (loop index).
The rest of the code is largely unchanged from the last few iterations of this program so I'll leave it at that.
This was a challenging program to write, although I thought the transition from 6502 assembly to 64 bit assembly would be harder. I'm sure my next step, writing the x86_64 equivalent, will be equally if not not more difficult. There's definitely a more robust feeling for lack of a better word, to writing and building assembly on a machine rather than an emulator, and during my debugging process it seemed like the error messages were more meaningful as well. Although I'm not very familiar with working with Linux and that made some things awkward I like knowing that if I need to dig deeper to find out why something's not working that's an option I have. That's it for this post, stay tuned for the x86_64 equivalent of this program coming soon.
Top comments (0)