汇编程序与C程序混合调用
- 在C语言中如何调用汇编语言实现的函数
- 在C语言中如何使用汇编语言定义的变量
- 汇编语言中如何调用C语言的函数
汇编语言中如何调用C语言定义的变量
在C语言中如何调用汇编语言实现的函数
例如用汇编比较两个字符串是否相等的函数的代码片段:
.text
.code32
.global my_strcmp
my_strcmp:
ldrb r2,[r1],#1 //"ledon" 首地址
ldrb r3,[r0],#1 //buf从键盘输入的命令字符
cmp r2,#0
beq cmp_end
.....
cmp_end:
sub r0,r2,r3
bx lr
C语言调用函数的过程的代码片段:
main.c
//要在C代码中得到声明适合于C函数原型
extern void my_strcmp(char *d,const char*s);
int main(void)
{
char buf[256];
uart0_gets(buf);//获取键盘输入的命令
my_strcmp(buf,"ledon");//汇编实现的函数
//传参数默认传递 r0,r1,r2,r3,... 这里有两个参数,因此为r0,r1
//汇编使用寄存器的时候,如果要混调,要按顺序使用
}
遵循的原则:
- 汇编函数的名字声明为全局标号 “.global my_strcmp”
- C程序中如要调用这个汇编函数,需要声明该函数的函数原型
extern void my_strcmp(char *d,const char*s);
- 按照C语言的方式去调用该函数
C语言中如何使用汇编语言定义的变量
.data
.global yy
yy:
.word 0x100 //定义了一个整形变量,变量名称为yy
1,在汇编中将变量声明为全局变量
2,在C语言中声明该变量为外部变量
extern int yy;
3,按照C的方式去使用该变量
汇编语言中如何调用C语言的函数
int add(int a,int b,int c,int d){
return (a+b+c+d);
}
1,在C程序中该函数要定义为全局,即函数前面不能加static
2,汇编程序中将该函数声明为外部标号
.extern add //伪操作,意味着该函数在C中实现
3,按照汇编的方式调用
//准备参数,参数不能随便放,要按照顺序
mov r0,#0
mov r1,#1
mov r2,#2
mov r3,#3
//调用
bl add
cmp r0,#0 //C函数的时候,将返回值放在了r0
关键是:参数的传递规则
int add(int a,int b,int c,int d)
r0, r1 r2 r3
汇编语言中如何调用C语言定义的变量
int a ;
1,C中声明为全局变量
2,汇编程序中声明该变量为外部变量
.extern a;
3,按照汇编的方式使用
ldr r0,=a //a的地址
ldr r1,[r0]
总结:
被调用方循环的原则:
将被调用的函数或变量都要定义为全局
调用方:
声明被调用函数的函数原型或者变量为外部标号
参数传递:
r0,r1,r2,r3
r0作为返回值
案例:
在C程序中调用汇编实现的函数
将用汇编实现的字符串比较函数替换掉用C实现的字符串比较函数
1,汇编函数(字符串比较函数)定义为全局标号
2,在C程序中将该汇编函数声明函数原型
3,替换掉C的字符吕比较函数,用汇编函数
astrcmp.s
.text
.code 32
.global a_strmcmp
a_strmcmp:
@不用获取地址,因为C函数调用时,会通过r0,r1将比较的字符串的地址传递过来
@r0 str1 对应键盘输入的命令
@r1 str2 定义好的命令的名字
@r2 r0,r3 r1
@比较
@返回,通过r0返回;通过r0保存比较的结果
@ 当r0为0,相同
@ 当r0非0,不同
@bx lr 返回
cmp_loop://循环比较
ldrb r2,[r0],#1
ldrb r3,[r1],#1
cmp r2,#0
beq cmp_end
cmp r2,r3
beq cmp_loop
cmp_end: @返回
sub r0,r2,r3
bx lr
.end
.h声明函数原型
//a_strmcmp是汇编实现的字符串比较函数
//返回值为0表示比较成功,非0比较失败
extern int a_strmcmp(const char *str1,const char *str2);
修改makefile
在C中调用
start.s
.text
.code 32
.gloal start
@调用C实现的main
.extern main
.extern __bss_start @ r0 bss的起始地址
.extern __end
start:
stmfd sp!,{lr} @lr入栈
ldr r0,=__bss_start
ldr r1,__end
mov r2,#0
clear_bss: @清除bss段
str,r2,[r0],#4 @字的存储指令,清完一个字,+4表示字对齐
cmp r0,r1 @循环清0
bcc clear_bss
bl main @跳转到C语言中的main中执行
ldmfd sp!,{pc} @出栈
.end
异常处理
1,复位异常,进入管理SVC模式
2,未定义指令异常,不能识别指令,进入未定义模式
3,软中断异常,指行swi指令,进入SVC模式
4,预取指令异常,没有取到指定的指令,进入中止模式
5,数据中止异常,没有取到指定的数据,进入中止模式
6,中断,进入中断模式
7,快速中断,进入快速中断模式
工作模式:
异常模式
管理
快速中断
中断
中止
非异常模式
当发生异常时,如保处理?
CPU做:
1,拷贝CPSR到相应异常模式的CPSR,SPSR_mode = CPSR (备份)
2,设置适当的CPSR的位:
1. 将处理器的状态改为ARM状态,CPSR的bit[5] = 0
2. 改变处理器的工作模式,进入到对应的异常工作模式,改CPSR的bit[4:0]到对应的异常模式
3. 设置中断禁止位,
3,保存返回地址到LR_mode = PC -4(PC -2),异常返回的问题
4,设置PC为相应异常处理程序的入口(异常向量表)
5,处理异常,执行异常处理
6,异常返回,CPSR = SPSR_mode (MOVS PC ,LR)
异常向量表:
软中断
系统执行代码,执行到SWI 0x01指令,在该指令的执行阶段,发生软件中断异常
CPU:
1,备份CSPR,SPSR_SVC = CPSR
2,修改CPSR
1.改状态,ARM状态,CPSR的bit[i]=0
2,改模式,SVC管理模式,CPSR的bit[4:0] = 10011
3. 改中断禁止位
3,保存返加地趣为LR_SVC
4,修改PC为异常向量表中对应的入口地址,PC = 0x8
对应指令:ldr pc,swi_hdl
再次修改PC = swi_hdl函数地址
5,swi_hdl,软中断异常处理程序 ,在此程序中调用C语言缩写的较为复杂的处理程序 (汇编调用C函数的混合调用)
6,当C函数处理异常完毕之后,返回汇编swi_hdl,再做最后的异常返回.
7,movs pc,ls CPSR = SPSR_SVC
8,接下来继续执行swi指令的下一条指令
.text
.code 32
.global vector_start
.extern reset
vector_start: @异常向量表
b reset
ldr pc,_und_hdl
ldr pc,_swi_hdl
ldr pc,_pabt_hdl
ldr pc,dabt_hdl
b .
ldr pc,irq_hdl
ldr pc,fiq_hdl
und_hdl:
.word _und_handler
_swi_hdl:
.word _swi_handler
...
_swi_handler:
stmfd sp!,{r0-r12,lr} @r0到r12入栈
bl c_swi_handler @软中断处理函数,C函数
ldmfd sp!,{r0-r12,pc}^ @出栈
.global swi_test1
swi_test1:
stmfd sp!,{lr}
stmfd sp!,{pc}
reset.s
.text
.global vector_start
.extern reset
reset:
msr cpsr_c,#0xd3
ldr sp,0xd0036000
.....
reset.s
1,将模式改为SVC管理模式,并且屏蔽中断,初始化栈顶指针SP
2,安装MMU的地址转换表,之后CPU给的地址都是虚拟地址
3,初始化IRQ和FIQ模式下栈顶指针
4,清空BSS段
5,跳转到main执行主函数(shell,汇编调用C的混合调用)
提取swi指令中中断号:
swi指令格式:
1,在ARM状态下,执行ARM指令,SWI指令,低24bit为中断号
2,在thumb状态下,执行thumb指令,swi指令,低8bit为中断号
ARM指令格式(32bit):
Thumb指令格式:
1,确定执行swi指令时,CPU所片的状态,ARM? thumb?
由于发生异常时,SPSR_SVC = CPSR
判断SPSR的bit[5] = 0,ARM状态,swi指令是指令(32)
bit[5] = 1,thumb状态,swi指令是thumb指令(16)
1,读SPSR寄存器到R0,MRS指令
2,用位运算bit[5] ANDS EQ(ARM) NE(thumb)
2,获取swi指令的编码
由于发生异常时,保存返回地址 lr = pc -4 ARM
= pc - 2 thumb
ARM 状态: swi指令码的地址,lr - 4
Thumb状态,swi指令码的地址,lr-2
ldreq 加载ARM指令 r0,[lr-4]
ldrneh 加载thumb指令 r0,[lr - 2]
3,使用位运算获取到swi指令码中中断号,将中断号存在R0
ARM指令:提取低24bit
Thumb:提取低8bit
通过位运算,中断号保存在r0
1,biceq r0 24bit , 清除24bit
2,andne r0 8bit,
4,R0中断号做为参数,传递给C函数
5,进入c_swi_handler(unsigned int num),伪代码如下:
c_swi_handler(unsigned int num){
switch(num){
case 1:
...
break;
case 2:
....
break;
...
}
}
取指,解码,执行,在swit指令的第三个阶段,执行阶段,发生异常
1,SPSR= CPSR
2,改CSPR ,ARM状态,SVC模式,中断禁止位
3,保存返回地址 LR= PC -4 add指令地址
= PC -2