【说明】
在嵌入式开发中,大部分情况下我们使用C语言来编程,但是在程序的最前面一小段硬件相关的操作,以及有时候对速度要求高的地方我们仍然需要使用汇编进行编程。这就使得我们必须学会如何衔接好C和汇编,具体的有:C语言调用汇编语言的变量或者函数,汇编语言调用C语言的变量或者函数,以及在C语言中嵌入汇编语言。下面我们就分别进行介绍。
【汇编调用C语言函数】
我们后面具体的编程中,首先遇到的是汇编中调用C语言,因为我们说过,程序的最前面是用汇编写的,那么总会有一个分界点,也就是从汇编转向C语言的点,这一点显然需要从汇编语言中调用C语言,怎么做呢?
回忆上一节的内容,ATPCS规则告诉我们传递C函数参数的方法是先放寄存器,还有参数就用栈来传,OK,我们的汇编就是这么调用C函数的。
1】在调用C语言之前需要初始化栈指针SP,所谓初始化,也就是给SP赋一个值,一般是内存中的一个地址,这样,SP就指向这个地址,栈操作就基于这个地址进行,这个地址附近的内容就是栈内容,当然啦,你必须保证这个地址附近的内存空间是可用的,也就是没有什么重要的信息在里边,不然随着进栈出栈的进行,里面的内容也就被覆盖了。
2】如果被调用的C函数没有参数,那就直接用“bl C函数名”就可以了,没错就这么简单。。比如下面的指令
文件a.S中
ldr sp , =8*1024 //初始化栈指针
bl main //跳转到b.c的xxx函数执行
文件b.c
int xxx(void)
{
return 0;
}
注意,也不是每次调用C函数都要初始化栈,第一次初始化就好了
3】如果被调用的C函数有参数,根据ATPCS规则就应该这么玩
文件a.S中
ldr sp, =8*1024 //初始化栈
ldr r0, =9 //传参数!
bl xxx //跳转过去
文件b.c
void xxx(int start)
{
}
4】如果被调用的C函数有超过4个的参数,根据ATPCS规则就应该这么玩
文件a.S
mov r0, #0 /* start */
mov r1, #8 /* end */
mov r2, #0 /* a */
mov r3, #0 /* b */
ldr sp, =6*1024 - 8
mov r4, #0
str r4, [sp] /* c */
mov r4, #1
str r4, [sp, #4] /* d */
bl xxx
文件b.c
void xxx(int start, int end, int a, int b, int c, int d)
{
}
【汇编调用C语言变量】
在汇编中其实可以操作C语言中的变量的,但是实际上用的并不多
在b.c中
int g_val = 5;
在a.S中
IMPORT g_val //下面的指令中就可以用了
ldr r0, =g_val //这么用
是不是很简单!
【C语言中调用汇编函数】
在C语言中调用汇编语言其实也是一眼就懂
文件a.S中
delay:
ldr r0, =1024
在b.c中
void delay(); //首先要声明
int xxx()
{
delay();
} //直接调用,没错不用实现 直接调用
【C语言中嵌入式汇编语言】
GNU C提供了一种在C语言中直接加入汇编语言的方法,
1】最简单形式的内嵌汇编
int xxx()
{
asm(
"mov r0, #0\n\t"
"mov r1, r0\n\t"
);
return 0;
}
2】内嵌汇编的模版应该是这样的,上边的只是简化版本
asm(code : output operand list : input operand list : clobber list);
下面是C语言的一个整型变量传递给汇编,逻辑左移一位后再传递给C语言的另外一个整型变量
asm("mov %[result], %[value], ror #1" : [result] "=r"(y) : [value] "r" (x));
每一个asm语句被冒号分成了4个部分
1)第一个部分是汇编代码
2)第二个部分是输出列表,每一个条目由一对[]和被他包含的符号名组成,它后面跟着限制性字符串,再后面是圆括号括着的C变量。
3)第三个部分是输入列表,语法和输出一模一样
4)第四个部分叫破坏符列表,指明哪些部分会在汇编代码中被改变,本例中没有用到。
5)四个部分如果后面没有其他部分了,那么省略该部分可以连:都不用写,但是如果后面有,:不能省略,也就是如果你要省略该部分,你就保持它空 白, 但是分割的:还是要加上的。下面的例子就有input 没有 output
asm("msr cpsr, %[ps]" : : [ps]"r"(status));
6)其实编译器有时候会把你写的内嵌汇编改得乱七八糟,以便和编译后的C有更好的结合,如果下面这样加入volatile 关键字就可以去掉优化
asm volatile("mov r0, r0");
3】我们知道,C中变量是有大小的,比如int是4字节,char是1字节,那么要用汇编操作C中变量就要注意这些
unsigned char
4】 上边的例子中,"=r"中,"r"是指明了后面数的属性,有以下几种,
1)"i" 表示后面会跟一个立即数
2)"r" 表示后面跟一个变量
3)"m" 表示后面变量的地址
5】上边的"=" ,是一个限制符,有以下几种,
1)"=" 表示只可写,一般用在output 列表中
2)"+" 可读可写
3)"&" 表示输入输出用不同的寄存器
6】现在我们谈谈破坏列表,破坏列表指明了汇编代码中会改变哪些东西的值,让编译器注意。
1)"memory" 指明有些内存的内容被改变了,那么一些保存该内存内容的寄存器就应该被更新
2)"r0" 指明寄存器r0 被改变了,那么编译器就应该在内嵌汇编前边保存该寄存器的值,在内嵌汇编后边恢复它,当然r1, r2都行,r0只是举个例子