(1)格式
用 .global
来声明子程序全局可见;
前 4
个参数可用R0~R3
传参,从第 5
个参数开始用栈
传递;
返回值用R0
传递;
跳转到子程序时,会自动把返回地址存到LR
寄存器,所以子程序结束时,需要手动跳转回LR
保存的地方。
(2)例子
两个数相加
.global my_add
my_add:
add r0, r0, r1
bx lr // 表示返回; 也可以用 mov pc, lr
打印一个字符到串口,(假设已经初始化过串口了,这里的寄存器地址和偏移量都是随便写的数字):
.global write_char
write_char:
ldr r1, =(0x10000000 + 0x100) // r1=整个寄存器的起始地址+UART偏移量
strb r0, [r1, #0x10] // r1加上某个偏移量,为UART写寄存器 把r0(第一个参数)写入该寄存器
write_char_loop:
ldr r0, [r1, #0x20] // 读取UART写完之后的标志
and r0, r0, #0x30 // r0 = r0 与 某值
cmp r0, #0x30 // 比较与的结果是不是 某值
bne write_char_loop // 不是,表示串口还没写完,就用循环语句等一下
bx lr // 是,表示写完成,就返回;
不过这个子程序没有保存寄存器上下文,如果想让这个函数可以插入到代码的任意两行之间,需要把寄存器保存到栈中,这样返回之后,之前的寄存器还能用,增加如下两行:
.global write_char
write_char:
stmfd sp!, {r0-r1} // 入栈
ldr r1, =(0x10000000 + 0x100)
strb r0, [r1, #0x10]
write_char_loop:
ldr r0, [r1, #0x20]
and r0, r0, #0x30
cmp r0, #0x30
bne write_char_loop
ldmfd sp!, {r0-r1} // 出栈
bx lr
还有个小问题,每次使用都得先加一个mov
指令,不能一行写完,所以增加如下宏,顺便把\r\n
也加上:
.macro PUT_CHAR c:
stmfd sp!, {r0-r1,lr} // 入栈
ldr r0, =\c
bl write_char
mov r0,#0xD
bl write_char
mov r0,#0xA
bl write_char
ldmfd sp!, {r0-r1,pc} // 出栈,返回
.endm
网友评论