汇编函数封装(pc寄存器)
读:每次读取该寄存器的值,都能获得当前正在执行的指令的实际地址+8
写:将一个地址写入该寄存器,将立即触发跳转,本质就是函数指针,注意此时没有+8的偏移量
汇编函数封装
mov lr , pc pc
b func //调用函数 pc+4
... pc+8 (这就是为什么读取pc时会自动pc+8原因了!)
...
func:
...
mov pc , lr
程序实例:按键控制led灯
//转载请注明原文地址:http://blog.csdn.net/oyhb_1992/article/details/77816900
#define GPM4CON 0X110002E0
#define GPM4DAT 0X110002E4
#define GPX3CON 0X11000C60
#define GPX3DAT 0X11000c64
.global _start
_start:
mov lr , pc //两条指令可以合为一条bl led_init
b led_init
mov lr , pc
b key_init
loop:
mov lr , pc
b key_stat //获取按键状态
mov lr , pc
b led_on //根据按键状态点亮led灯
b loop
led_init:
//GPM4_0~3设为输出
//GPM4CON = (GPM4CON & ~0XFFFF) | 0X1111led_init:
ldr r0 , =GPM4CON
ldr r1 , [r0]
bic r1 , #0xff
bic r1 , #0xff00 //0xffff 0x1111都是非法立即数,两种方法,拆开立即数或用伪指令
ldr r2 , #0x1111
orr r1 , r2
str r1 , [r0]
mov pc , lr
key_init:
//GPX3_2~5设为输入
//GPX3CON &= ~(0XFFFF<<8)
ldr r0 , =GPX3CON
ldr r1 , [r0]
bic r1 , #(0xff<<8)
bic r1 , #(0xff00<<8)
ldr r2 , #0x1111
str r1 , [r0]
mov pc , lr
key_stat:
ldr r0 , =GPM3DAT
ldrb r1 , [r0]
mov r1 , r1 , lsr #2
and r1 , #0xf
mov r0 , r1 //将按键状态stat通过r0传给led_on函数
mov pc , lr
led_on:
//点亮led
//GPM4DAT = (GPM4DAT & ~0Xf) | (stat & 0xf)
ldr r2 , r0 //将函数唯一的参数stat转移到r2里
ldr r0 , =GPM4DAT
ldrb r1 , [r0]
bic r1 , 0xf
orr r1 , r2 //点灯
strb r1 , [r0]
mov pc , lr
汇编中,函数封装的规则
前4个参数通过r0-r3传递,从第4个参数开始通过压栈来传递
函数的返回值通过r0来传递
在函数内,寄存器的使用规则:r0-r3可以随意使用,r4-r12的值必须在退出函数时和调用函数之前一样,所以用r4-r12则要压栈出栈
如果不遵守以上规则,那么汇编在和其他语言编译生成的代码在以函数形式互相调用时就会出错!
符号标签:
mov r0 , #6
loop: //loop是符号标签,可以用#arm-linux-nm(一个门,两个门记忆这个命令) a.out命令来查看可执行文件里的//符号列表,编译器是把这个符号标签当成一个函数名来处理
…
subs r0 , #1
bne loop
数字标签
mov r0 , #6
1:
…
subs r0 , #1
bne 1b
标签后缀为b或f,b==back ,f == forward
1是数字标签,#arm-linux-nm a.out命令来查看可执行文件里的
符号列表,编译器不把这个数字标签当成一个函数名来处理,所以符号列表里没有这个标签,数字标签可以重复出现,符号标签不可以重复出现。跳转是以指定方向最近的一个吻合的数字标签为跳转目标
汇编中标签默认都是局部的,如果c语言想调用汇编函数,那么就要使用.global关键字来使标号成为全局标号,而c语言中函数都是默认全局的只有加了static关键字才是局部的函数