6.5. A Simple Function Call

Calling and returning from a function always involves five phases. Control starts at the caller, which has to possibly save information it has in registers r0-r3 and then call the function. The called function needs to gather and save information (the prologue), execute its code, and then clean up and return (the epilogue). Finally, the caller needs to restore any saved values and then work with any returned value. Here is a summary of what happens:

6.5.1. Calling Procedure (Basic)

Function Call (Done by caller)

Any data in r0-r3 that needs to be stored is pushed to the stack.

Parameters are placed in r0, r1, r2, and r3 (in order).

Use BL to branch to the function.

Function Prologue (Done by called function)

If the function will use any registers in r4-r9, push the current values to the stack.

Function Body (Done by called function)

Do the work of the function. Access passed parameters in r0-r3. Only modify registers r0-r3 and any registers that were saved in the prologue.

Function Epilogue (Done by called function)

Place any return value(s) in r0, r1, r2, r3. (C/C++ generally only return one value, but there is no reason in assembly you can’t return more than one.)

Pop any stored registers (r4-r9) from the stack to restore their old values.

Return to the caller with BX lr

Resume Control (Done by caller)

Any returned value(s) are in registers r0-r3.

Pop stored registers (r0-r3) that were preserved before calling.

6.5.2. A Sample Function

The code sample below demonstrates a program that replace r4 and r5 with their absolute values. To do this, it uses an abs function. To call the function, the main part of the program stores r1, which is in use, then places the parameter (value to take absolute value of) in r0. The function preserves, r4 and r5 so it can use them, then does its work. It finishes up by placing the answer in r0, restoring r4 and r5 and branching back. Then the main part of the program moves the returned value out of r0 and restores r1.

Here is how the stack is used to store values along the way:

AddressContents
0xfffffff4
0xfffffff8
0xfffffffc
0x00000000... sp
AddressContents
0xfffffff4
0xfffffff8
0xfffffffcr1 from main sp
0x00000000...

main part of program pushes r1 for safe keeping.

AddressContents
0xfffffff4r4 from abs sp
0xfffffff8r5 from abs
0xfffffffcr1 from main
0x00000000...

abs pushes r4 and r5 for safe keeping.

AddressContents
0xfffffff4r4 from abs
0xfffffff8r5 from abs
0xfffffffcr1 from main sp
0x00000000...

abs restores previous values of r4 and r5

AddressContents
0xfffffff4r4 from abs
0xfffffff8r5 from abs
0xfffffffcr1 from main
0x00000000... sp

main part of program restores r1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
/*
   Main program is using registers 1, 4 and 5
   It is responsible for storing/restoring r1 while calling function
*/
.text
.global _start
_start:
   MOV   r1, #0xAA
   MOV   r4, #30
   MOV   r5, #-12

   PUSH  {r1}        @I want to keep r1 safe, so save it
   MOV   r0, r4      @set up parameter for abs call
   BL    abs
   MOV   r4, r0      @save result from the function back to r4
   POP   {r1}        @restore r1

   PUSH  {r1}        @I want to keep r1 safe, so save it
   MOV   r0, r5      @setup param
   BL    abs
   MOV   r5, r0      @save result
   POP   {r1}        @restore r1

end:
   B     end         @stop here



@----------------------------------------------------------------------
/*AbsoluteValue
Calculates absolute value of a value passed in via r0.

Demonstrates register responsibility:
Uses r0, r1, r4, r5 - must preserve existing values in r4/r5

Equivelent to:
int abs(int x) {
   if (x < 0) x = -x;
   return x;
}

Params:
r0 = number
Return:
r0 = |number|
*/
abs:
   @store any registers 4+ I want to use
   PUSH  {r4, r5}

   @do work, using
   MOV   r1, #0
   CMP   r0, r1         @compare parameter in r0 to 0
   BGE   end_absIf      @if r0 was >= jump ahead
   MVN   r4, r0         @r0 was negative... copy its bitwise negation to r4
   ADD   r5, r4, #1     @add 1 to get 2's complement negation into r5
   MOV   r0, r5         @move negated version into r0

end_absIf:
   @r0 has correct answer at this point
   @restore any registers 4+ I used
   POP   {r4, r5}
   BX    lr             @return
Try sample

Tip

In the simulator, make sure to use Step Into if you want the simulator to follow the branch. If you hit Step Over, the simulator will take the branch, run all the code there and not show any updates until it returns to the next instruction after the branch.

You have attempted of activities on this page
Next Section - 6.6. A Smarter Function Call