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:
Address | Contents | |
---|---|---|
0xfffffff4 | ||
0xfffffff8 | ||
0xfffffffc | ||
0x00000000 | ... | sp |
Address | Contents | |
---|---|---|
0xfffffff4 | ||
0xfffffff8 | ||
0xfffffffc | r1 from main | sp |
0x00000000 | ... |
main part of program pushes r1 for safe keeping.
Address | Contents | |
---|---|---|
0xfffffff4 | r4 from abs | sp |
0xfffffff8 | r5 from abs | |
0xfffffffc | r1 from main | |
0x00000000 | ... |
abs pushes r4 and r5 for safe keeping.
Address | Contents | |
---|---|---|
0xfffffff4 | r4 from abs | |
0xfffffff8 | r5 from abs | |
0xfffffffc | r1 from main | sp |
0x00000000 | ... |
abs restores previous values of r4 and r5
Address | Contents | |
---|---|---|
0xfffffff4 | r4 from abs | |
0xfffffff8 | r5 from abs | |
0xfffffffc | r1 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
|
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.