首页 > 代码库 > 嵌入式编程:C与汇编的混用
嵌入式编程:C与汇编的混用
主要参考:《深度探索嵌入式操作系统》 5.4节
??
为什么要嵌入汇编语言?
开关CPU中断、
读取CPU的一些特殊寄存器、
设置CPU模式等功能无法用C语言实现(因为C语言属于高级语言,而高级语言是屏蔽底层硬件的)。
提高速度?好像是有这么一种目的,还不太清楚。
??
【代码模板】
__asm__ __volatile__ (
"代码部分" 冒号是分隔符,如果是有汇编代码部分,则冒号可以省略
:输出部分列表 如果没有输入列表,冒号也是必须加的
:输入部分列表
:损坏部分列表);
asm volatile();
??
C语言中嵌入汇编代码的实例:
__asm__ __volatile__( : : : "memory");
// 告诉GCC内存中的数据可能发生了改变了,要回写寄存器了,这是为了达到防止GCC过度优化带来问题的目的
??
C语言中嵌入汇编代码的加法函数:
1 int add(int a1, int a2)
2 {
3 ????????int sum;
4 ????????__asm__ __volatile__(
5 ????????????????"add %[sv1], %[av1], %[av2] \n\t"
6 ????????????????: [sv1] "=r" [sum]
7 ????????????????: [av1] "r" (a1), [av2] "r" (a2)
8 ????????????????: "r4", "cc", "memory"
9 ????????);
10 ????????return sum;
11 }
输入列表用逗号分隔:相当于管理C语言变量和汇编语言变量
[条目名称] "存储类型"(C语言表达式) 因为C语言里的变量此时要用汇编语言来操作,所以要指定分配的类似,存储类型
"r" 通用寄存器
"m" 表示后面的C表达式是内存地址
"I" 表示后面的C表达式是常数
更多见GCC手册
??
输出列表:
[条目名称] "=限定符"(C语言表达式)
"=r"(sum) 表示让GCC给sum分配一个寄存器,只不过是用于输出计算的
??
损坏列表部分:告诉GCC这些寄存器需要生成代码来保存和恢复,还有内存中的数据可能发生改变
"寄存器"
"cc" CPU标志寄存器
"memory"
??
指令部分:
指令部分由引号包含的,"\n\t"即换行符和制表符,完全是为了让GCC格式化输出汇编代码的。
add %[sv1], %[av1], %[av2],为什么不直接使用寄存器呢?当然是可以的,但这里使用条目名主要是为了让GCC能动态分配寄存器、优化代码,也就是好比在汇编语言里面我们可以定义变量了,而原来我们写汇编语言的时候,是直接使用寄存器的,此处相当于通过GCC使汇编编程更加高级。
比如,代码 add %[sv1], %[av1], %[av2], 一般情况下会被GCC处理成代码 add r0, r0, r1,这是不是比直接写原始的纯汇编代码高级得多了。
??
【例子解读】
读取CPU标志寄存器:也就是在C语言编程过程中,我们可以像调用C函数来使用只有汇编语言才能完成的功能了:
1 cpuflg_t hal_read_cpuflg()
2 {
3 ????cpuflg_t cpuflg;
4 ????__asm__ __volatile__(
5 ????????"mrs %[retcpr], cpsr \n\t"
6 ????????: [retcpr] "=r" (cpuflg) 有一个输出条目[retcpr],关联一个分配一个通用寄存器给他的变量,这个条目本质上其实C语言变量在汇编世界里的代言人,最后C语言直接问cpuflg取值,而[retcpr]就负责在汇编世界管理它
7 ????????:
8 ????????: "cc", "memory"
9 ????);
10 ????return cpuflg
11 }
理想情况下这行代码"mrs %[retcpr], cpsr \n\t"会被GCC处理成"mrs r0, cpsr \n\t",而r0寄存器正是GCC用来存放函数返回值的,
最后嵌入式汇编代码模板的损坏部分高速gcc,CPU的标志寄存器可能损坏,内存中的值可能发生改变(但是这部分的处理方式就不需要C程序员关心了,暂时也不必关系)
??
开关ARM920T CPU中断的函数:读(到某个寄存器中) > 改 > 写(把寄存器回写)
??
CPU中开关中断是通过设置CPU上的CPSR寄存器中的相关位来实现的,代码中的CIRQFIQ是个宏,会被GCC预处理时,替换成常数0xc0.
从上面的代码的输入条目中它前面的限定符"I"可以告诉GCC,CIRQFIQ是一个常数。GCC会根据这个常数的大小决定把它分配在寄存器(寄存器本质上其实和内存差不多,区别在于访问速度极快以及可能有约定的存储作用)中或者直接放在指令编码数据(要满足存放立即数的条件)中。并且会在__asm__ __volatile__()中的指令执行之前,生成相关代码处理好这个问题。
损坏部分:这里C语言其实没有输出变量的,也没有中间变量,所以汇编代码部分之间使用寄存器了r0,因为代码中用到了r0、CPSR寄存器,所以要在代码模板的损坏部分写上r0(让GCC在__asm__ __volatile__()中的指令执行之前,生成保存r0寄存器中的值的相关代码,一遍在其后恢复到r0寄存器中,可见GCC是先整体分析嵌入部分,然后在处理汇编代码部分)。
??
??
嵌入式编程:C与汇编的混用