HomeAboutArticles

ARM64 — Functions / subroutines

Start page1 page2 page3 page4 page5 page6 page7 page8 page9 page10

We can, of course, define subroutines in Arm64. A subroutine needs a starting address, mostly some parameters, a way to return a result, and a return address.

To do all this we need a few things.

It is complicated, and there is more to the story, like using the stack. We may look at that later.

First we need to play with the simplest subroutine we can imagine: add one to a register. Place the following code after the code that stops the program:

inc1:   add x0,x0,1
        ret

Just make sure it is not executed in the normal flow of the program. Now call it when you have a known value in x0, and before you print the value:

        bl inc1

Check the value actually has increased by one. Then change your code to call it twice, and check.

Print hex-value in a subroutine

We have one parameter, the number to be printed. In our example it is already in x0. So we move the 20-some lines of code that prints the hex value down near the inc1-subroutine:

phex:   movz x3,16
        adr x2,message

again:  ror x0,x0,60
        and x1,x0,0xF
        cmp x1,10
        bge isAtoF
        add x1,x1,48
        b  done
isAtoF: add x1,x1,55
done:   strb w1,[x2]
        add x2,x2,1

        subs x3,x3,1
        bne again

        mov x0,1
        adr x1,message
        mov x2,17
        mov x8,0x40
        svc 0
        ret

And we also added a ret. Registers x0, x1, x2, x3 and x8 are used, and changed. We may do that by convention. Actually x8 hasn't been mentioned as such - need to investigate.

We call it in the place where we had the code before, like this:

        bl phex

And it prints the value in x0. Just for fun: call it twice:

        bl phex
        bl phex

Now that is a surprise: it prints 0000000000000011 the second time, and not the same value as the first. But I did tell you, didn't I. The value in x0 is not expected to survive a call to a subroutine. The 0x11 is decimal 17, and is the number of bytes printed.

We could save the value in x0 in a register, that by convention survives a call to a subroutine

        mov x19,x0
        bl phex
        mov x0,x19
        bl phex

Make a new subroutine: exit, with the code that stops the program from running. In this case we don't need a ret.

On the next page we will put the subroutines exit and phex in a separate file, and link them to our program in the assembly process.

Start page1 page2 page3 page4 page5 page6 page7 page8 page9 page10

2025-06-16