Course 3: Stage 2 – Core Instructions and Programming
Topic:Function Calls and Stack Operations, ARMv8 Calling Conventions, Bare-Metal Programming Practice
3.1 Basics of Function Calls
Core Concepts:
-
BL Instruction:
-
<span>BL label</span>
:Jump to<span>label</span>
to execute, while saving the return address (the address of the next instruction) to<span>LR</span>
(X30). -
After the function call, return using
<span>RET</span>
(<span>RET</span>
is equivalent to<span>MOV PC, LR</span>
).
Calling Convention:
-
Parameter Passing:The first 8 parameters are passed through
<span>X0-X7</span>
, additional parameters are passed via the stack SP. -
Return Value:Returned through
<span>X0</span>
. -
Caller-Saved Registers:
<span>X19-X28</span>
(must be saved and restored in the function).
3.2 Detailed Stack Operations
Stack Instructions:
-
Stack Pointer (SP):ARMv8 requires SP to be 16-byte aligned, refer to the official manual.
-
Pushing and Popping:
// Push (save X0 and X1 to stack, SP automatically decrements) stp x0, x1, [sp, #-16]! // ! indicates update SP to SP-16 // Pop (restore X0 and X1, SP automatically increments) ldp x0, x1, [sp], #16
Stack Frame:
When entering a function, save<span>FP</span>
(X29) and<span>LR</span>
(X30), build the stack frame:
my_function:
stp x29, x30, [sp, #-16]! // Save FP and LR
mov x29, sp // Set new FP to current SP
// Function body
ldp x29, x30, [sp], #16 // Restore FP and LR
ret
3.3 Practical Project: String Reversal Function
Objective:Write a function<span>reverse_string</span>
that reverses the input string and outputs it via UART.
Code Example(<span>reverse.s</span>
):
.equ UART0_BASE, 0x9000000
.equ UARTFR_TXFF, (1 << 5)
.section .text
.global _start
_start:
ldr x0, =string // Input string address
bl reverse_string // Call reverse function
bl uart_print_string // Call print function
b halt
//--- Reverse Function ---
reverse_string:
stp x29, x30, [sp, #-16]! // Save FP and LR
mov x29, sp
// Calculate string length
mov x1, x0 // x1 = string start address
1: ldrb w2, [x1], #1 // Read character and increment address
cbnz w2, 1b // Loop until 0 encountered
sub x1, x1, x0 // x1 = length + 1
sub x1, x1, #1 // x1 = actual length
// Reverse string (two-pointer method)
add x2, x0, x1 // x2 = end address
mov x3, x0 // x3 = start address
2: cmp x3, x2
b.ge 3f // If start >= end, finish
ldrb w4, [x3] // Swap characters
ldrb w5, [x2]
strb w5, [x3], #1 // Write and increment start address
strb w4, [x2], #-1 // Write and decrement end address
b 2b
3:
ldp x29, x30, [sp], #16 // Restore FP and LR
ret
//--- UART Print Function ---
uart_print_string:
stp x29, x30, [sp, #-16]!
mov x29, sp
mov x1, x0 // x1 = string address
4: ldrb w2, [x1], #1
cbz w2, 5f
bl uart_putc
b 4b
5:
ldp x29, x30, [sp], #16
ret
//--- UART Send Function (same as above) ---
uart_putc:
ldr x3, =UART0_BASE
ldr w4, [x3, #0x18]
tst w4, #(1 << 5)
b.ne uart_putc
str w2, [x3, #0x0]
ret
halt:
b halt
.section .data
string:
.asciz "Hello, ARMv8!"
.align 12
stack_bottom:
.space 1024
stack_top:
Compilation and Execution:
aarch64-linux-gnu-as reverse.s -o reverse.o
aarch64-linux-gnu-ld -nostdlib -o reverse.elf reverse.o -Ttext=0x80000
qemu-system-aarch64 -M virt -cpu cortex-a53 -nographic -kernel reverse.elf
Expected Output:
!8MvRA ,olleH
3.4 Hands-On Experiment
-
Modify String Content:Change
<span>string</span>
to other text (e.g.,<span>ABCDE</span>
), observe if the output is<span>EDCBA</span>
. -
Debug Function Calls:Step through
<span>reverse_string</span>
in GDB, observe stack changes:qemu-system-aarch64 -M virt -cpu cortex-a53 -nographic -kernel reverse.elf -S -s
-
Set breakpoints in GDB:
(gdb) b reverse_string (gdb) c
-
Extend Functionality:Modify the function to support dynamic length calculation (no hardcoding).