C\C++以及汇编语言的混合编程
在应用系统设计中,如果所有程序都用汇编语言实现,虽然提高了执行效率,但工作量大而且不易于移植和升级;如果全部用C语言来编写,一些对硬件的操作,例如允许和禁止中断就无法实现。因此,通常在实际系统设计中采用汇编和C语言混合编程的方式。ARM体系结构支持汇编和C语言的混合编程。一般在ARM实际编程应用中,程序的初始化部分用汇编语言来实现,然后用C/C++语言完成其他任务。
²
C和汇编之间的函数调用
n
(温故知新)C语言程序中的调用
n
C程序调用汇编程序函数
n
汇编程序调用c程序函数
n
汇编程序访问c程序变量
n
ATPCS标准(ARM-Thumb Procedure Call Standard)
²
C程序嵌入汇编指令
n
内联汇编
n
嵌入式汇编
l
C和汇编之间的函数调用
ARM工程中,C程序调用汇编函数和汇编程序调用C函数时经常的事情,遵守ARM-Thumb过程调用标准ATPCS(ARM-Thumb Procedure Call
Standard)。
ATPCS标准——ARM编译器使用的函数调用规则(详见下面)
Ø
(温故知新)C语言程序中的调用
int main()
{
printf("1234+5678=%d\n",sum(1234,5678));
return 0;
}
请编写sum函数,实现两个数的相加。
int
sum(int x,int y)
{
int
s;
s=x+y;
return
s
}
Ø
C程序调用汇编程序函数
要点:
1.
在汇编语言中,用该函数名作为汇编代码段的标识,定义函数代码,最后用MOV
PC,LR返回
2.
在汇编语言中用EXPORT导出函数名,使得该函数可以被其他的程序调用。
3.
在C语言中用extern关键词声明函数原型。
4.
函数调用时参数传递规则:寄存器组中的{R0-R3}作为参数传递和结果返回寄存器,如果参数数目超过4个,则使用堆栈进行传递。
;工程exp10_1_2,实现字符串的复制
;汇编函数文件strcopy.s
;把R1指向的数据块复制到R0指向的存储块。
AREA StrCopy,CODE,READONLY
EXPORT strcopy
strcopy
LDRB R2,[R1],#1
STRB R2,[R0],#1
CMP R2,#0
BNE strcopy
MOV PC,LR
END
;C文件main.c
;调用strcopy函数
extern void StrCopy(char *d,const char
*s); //声明strcopy为外部引用符号
int main(void)
{
const char *src=”Source”;
char dest[12]=”Destination”;
strcopy(dest,src); //调用汇编函数strcopy
printf(“After copying:\n”);
printf(“%s\n%s\n”,src,dest);
return(0);
}
根据ATPCS的C语言程序调用汇编函数规则,参数由左向右依次传递给寄存器R0~R3,可知汇编函数strcopy在C程序中的原型应该为:
void strcopy(char *d,const char *s);
其中,参数d对应R0,参数s对应R1。函数没有返回值,类型为void。函数名称与汇编函数标号名称一致。
Ø
汇编程序调用c程序函数
在汇编程序中调用C程序函数的步骤如下:
在C程序中,无需任何关键字导出或者声明被调用的C语言函数。
在汇编程序中,在调用其他文件的C语言函数前,先用IMPORT伪操作声明该函数。
在汇编程序中,通常使用BL指令来调用其他程序文件中的C语言函数,并保证调用时参数正确传递。参数的传递规则与C程序调用汇编语言函数的规则相同。
;工程Exp10_1_3,累加
C函数
int g(int a,int b,int c,int d,int e)
{
return a+b+c+d+e;
}
汇编函数f中调用C函数g(),以实现下面的功能:
int f(int i)
{
return g(i, 2*i, 3*i, 4*i, 5*i );
}
整个汇编函数f的代码如下:
EXPORT f
AREA f,CODE,READONLY
IMPORT g
STR LR,[SP,#-4]
ADD R1,R0,R0
ADD R2,R1,R0
ADD R3,R1,R2
STR R3,[SP,#-4]
ADD R3,R1,R1
BL g
ADD SP,SP,#4
RSB R0,R0,#0
LDR PC,[SP],#4
END
;由于函数g()有5个参数,所以该函数用寄存器R0~R3来传递a~d,而用堆栈来传递第5个参数e。
Ø
汇编程序访问c程序变量
变量定义是混合编程的基本问题。汇编程序访问C程序中的变量的步骤类似于汇编程序调用C程序中的函数的步骤,具体操作如下:
²
在C程序中,无需任何关键字导出或声明被调用的C程序变量。
²
在汇编程序中,在访问其他文件的C程序变量前,先用IMPORT伪操作声明该变量。
在汇编程序中,通常根据该数据的数据类型使用相应的LDR指令读取这个全局变量的地址和值,然后在此基础上,可以使用其他数据处理指令(如ADD、SUB或STR指令等)对这个全局变量进行进一步操作。
实际上是引用不同文件定义的变量:
IMPORT:声明符号为外部引用符号
EXPORT:声明符号为导出符号
例子:
AREA
globals,CODE,READONLY
EXPORT
asmsubroutine ;声明可导出符号,以供外程序调用
IMPORT
globvar ;声明引用外部符号
asmsubroutine
LDR R1,= globvar
LDR R0,[R1]
ADD R0,R0,#2
STR R0,[R1]
MOV PC,LR
END
Ø
ATPCS标准(ARM-Thumb Procedure Call Standard)
ARM编译器使用的函数调用规则。
1.
寄存器的使用规则
R0 ~R3用来传递参数;
R4~R11用来保存局部变量;
R12用做子程序内部调用的scratch寄存器
R13堆栈指针,保存当前处理器模式的堆栈的栈顶
R14链接寄存器
R15程序计数器
进入函数时,必须保存R4~R11中被函数破坏的寄存器。
2.
堆栈的使用规则
FD类型。
3.
参数的传递规则
<4个:R0~R3
>4个:堆栈
4.
函数返回值
R0,R1
l
C程序嵌入汇编指令
内联汇编inline assemble
嵌入式汇编embedded assemble
有时候,必须在C/C++语言程序中嵌入汇编指令:
C/C++语言程序中需要对协处理器进行操作。
C/C++语言程序中完成对程序状态寄存器CPSR的操作。
C/C++语言程序中内嵌汇编指令由两种方法,分别是内联汇编和嵌入式汇编,他们都是包含在C/C++编译器中的汇编器,可以实现一些高级语言没有的功能。不仅如此,在C/C++语言程序中灵活使用内联汇编和嵌入式汇编,还有助于提高程序的执行效率。
Ø
内联汇编
在c函数定义中插入汇编语句。
使用关键字__asm来定义一个内联汇编程序段,当ARM编译器遇到这个关键字时,就会启动内联汇编器对关键字下面的程序进行汇编工作。语法格式与编译器相关,不同版本的ARM编译器对内联汇编的语法要求不一样,例如armcc和gcc的汇编格式就不一样。
用法特点:
u
如果同一行包含多条指令,则用分号隔开。
u
如果一条指令不能在同一行中完成,则用反斜杠连接。
u
内联汇编中出现的寄存器使用前必须先声明,且不一定与同名物理寄存器相对应。
u
内联汇编中的寄存器(除CPSR和SPSR外),在读取前必须先赋值。
由于内联汇编嵌入在C/C++语言程序中,所以可以在内联汇编代码中执行的操作有某些限制:
u
不支持Thumb指令集;
u
不支持带转移的跳转指令
u
不支持对寄存器执行装载/存储操作,而使用MOV指令对寄存器赋值
u
不支持标号变量
u
不能直接向PC赋值
u
十六进制数前使用0x,不能使用前缀&
语法格式
__asm
{
汇编语句段
}
内嵌内联汇编的两个整型数相加的C语言程序。(工程exp10_1,文件exp10_exa1.c)
#include
int sum(int i,int j)
{
int total;
__asm
{
ADD total,i,j
}
return total;
}
int main()
{
printf("1234+5678=%d\n",sum(1234,5678));
return 0;
}
Ø
嵌入式汇编
在形式上表现为独立定义的函数。嵌入式汇编具有真实汇编的所有特性,同时支持ARM和Thumb指令集。
嵌入式汇编程序时一个编写在C程序外的单独汇编程序段,可以像函数那样被C程序调用。可以有参数和返回值。定义嵌入式汇编函数的语法格式为:
__asm return-type
function-name(parameter-list)
{
汇编程序段
}
使用嵌入式汇编有以下限制:
²
嵌入式汇编中不能直接引用C/C++的变量定义。
²
编译程序不为_asm函数生成返回指令
例子:
__asm int add(int i, int j)
{
ADD R0,R0,R1
MOV PC,LR
}
注意参数名只允许在参数列表中,不能用在嵌入式汇编函数体内。
在C中调用嵌入式汇编程序的方法与调用C函数的方法相同。
void main()
{
printf(”%d”,add(123,456));
}
BDHN
内嵌嵌入式汇编的两个整型数相加的C语言程序。(工程exp10_1,文件exp10_exa2.c)
#include
_asm int add(int i,int j)
{
ADD R0,R0,R1
MOV PC,LR
}
void
main( )
{
printf("1234+5678=%d\n",add(1234,5678));
return
0;
}
内联汇编和嵌入式汇编的主要区别
功能
内联汇编
嵌入式汇编
指令集
仅支持ARM
支持ARM和Thumb
ARM汇编伪操作
不支持
支持
C/C++表达式
完全支持
只支持常量表达式
汇编代码优化
完全优化
无优化
能否被内联
有可能
不可能
寄存器访问
使用虚拟寄存器不能使用PC、LR和SP
使用指定的物理寄存器可以使用PC、LR和SP
是否自动产生返回指令
指定产生
手工添加返回指令
课堂补充实验:
实验目的
Ø
掌握C程序中内嵌汇编指令的使用方法。
Ø
掌握C程序调用汇编程序函数的方法。
Ø
掌握汇编程序调用C程序函数和变量的方法。
实验一 允许和禁止中断程序(ARM7)
本实验使用内联汇编完成允许和禁止中断程序设计,采用ARMulator方式调试,选用ARM7作为目标处理器。
;工程exp10_1,文件exp10_1_1.c
#include
__inline
void enable_IRQ(void)
{
int tmp;
__asm
{
MRS
tmp,CPSR //read
CPSR
BIC
tmp,tmp,#0x80 //bit[7]=0,IRQ alowed
MSR
CPSR_c,tmp //write CPSR
}
}
__inline void
disable_IRQ(void)
{
int tmp;
__asm
{
MRS
tmp,CPSR //read CPSR
ORR
tmp,tmp,#0x80 //bit[7]=1,IRQ forbitten
MSR
CPSR_c,tmp //write CPSR
}
}
int
main(void)
{
enable_IRQ();
disable_IRQ();
return 0;
}
单步调试程序,观察C程序的执行过程,将寄存器的变化情况记录在下表中。
序号
执行指令
指令执行后的变化情况
寄存器
R0
R1
R2
R3
R4
R5
R12
SP
LR
PC
CPSR
0
---------------
1
MRS tmp,CPSR
2
BIC tmp,tmp,#0x80
3
MSR CPSR_c,tmp
4
MRS tmp,CPSR
5
ORR tmp,tmp,#0x80
6
MSR CPSR_c,tmp
实验二 伪随机数程序(ARM7)
本实验使用C程序调用汇编函数的方法设计伪随机数程序。本实验中的伪随机数并不是真正的随机数,而只是足够分散的一系列数据,因为它足够分散,所以看起来是随机的。真正的随机数,需要由物理方式来产生。本实验通过移位操作和“异或”运算来产生伪随机数,并采用ARMulator方式调试,选用ARM7作为目标处理器。
(工程exp10_2.mcp)
实验三 验证汇编程序调用C程序函数和访问C程序变量的执行过程(ARM9)
本实验在汇编程序中调用C程序函数和访问C程序变量,采用ARMualtor方式调试,选用ARM9作为目标处理器。(工程Exp10_3.mcp)
序号
执行指令
指令执行后的变化情况
寄存器
存储空间
变量
R0
R1
R2
R3
R4
SP
LR
PC
0x9FFC
0x9FF8
sum
0
-----------
1
2
3
4
5
思考:
1.
分别概述C程序调用汇编函数和汇编程序调用C函数的步骤。
2.
在C程序中内嵌汇编指令有哪几种方法?适用于哪些情况。