汇编语言与C语言混合编程技术
汇编语言与C语言混合编程技术
ARM体系结构支持ARM的汇编语言与C与C++的混合编程。
在一个完整的程序设计的中,除了初始化部分用汇编语言完成外,其大部分的编程任务一般都用C或C++完成。
汇编程序中访问C程序变量1
在汇编的源程序中调用C语言风格的字符串需要使用IMPORT伪操作。
IMPORT相当于C语言中的extern关键字,告诉编译器引用的符号不是在本文件中定义的,而是在其他的源文件中定义的。
伪操作的格式 :
IMPORT symbol [,WEAK]
symbol是声明的符号的名称;
[,WEAK]指示编译器如果发现symbol在所有的源文件中都没有找到,那么它也不会产生任何的错误信息。
示例见下页。
汇编程序中访问C程序变量2
C语言代码文件str.c里面只有一个简单的字符串的定义:
char *strhello="Hello world!\n\0";
汇编代码文件hello.s
1 AREA ||.text||, CODE, READONLY
2 main PROC
3 STMFD sp!,{lr}
4 LDR r0,=strtemp
5 LDR r0,[r0]
6 BL _printf
7 LDMFD sp!,{pc}
8 strtemp
9 DCD strhello
10 ENDP
11 EXPORT main
12 IMPORT strhello
13 IMPORT _main
14 IMPORT _main
15 IMPORT _printf
16 IMPORT ||Lib$$Request$$armlib||, WEAK
17 END
C程序中内嵌汇编指令1
在ARM的C语言程序中可以使用关键字__asm来加入一段汇编语言的程序。
格式:
__asm
{
instruction/*comment*/
…
}
C程序中内嵌汇编指令2
在C语言中嵌入的ARM汇编需要注意一些问题:
在汇编指令中,可以使用表达式,使用逗号“,”作为分隔符 ;
如果一条指令占用了多行,那么应该使用符号“\”续行,如果一行中有多个汇编指令,那么应该使用“;”将多个指令隔开。
汇编中不能再使用“;”作为注释行的开头,而应该使用C语言中的“/**/”或者“//”进行注释;
不要企图使用一个物理寄存器去改变一个C变量;
对于内嵌的汇编代码用到的寄存器,编译器在编译时会自动加入保存和恢复这些寄存器的代码而不用用户去管理,除了寄存器CPSR和SPSR,其他寄存器都必须先赋值然后再读取,否则编译时将出现错误。
C程序中内嵌汇编指令3
内嵌的ARM汇编与纯粹的ARM汇编的区别有:
内嵌的ARM汇编不支持ADR、ADRL伪指令;
十六进制数使用0x作为前缀,而后者使用&;
编译器在编译函数时使用R0、R1、R2、R3、IP和LR存放中间结果,因此使用这些寄存器时要特别小心;
注意C语言中的C语言变量不要和物理寄存器同名,否则可能出现混乱;
内嵌的汇编代码不能使用PC寄存器返回当前指令的地址;
STM和LDM指令中不能使用C语言表达式;
不支持BX和BLX;
用户可以改变处理器的模式,但仍然需要自己把处理器模式恢复过来,编译器不会自动做处理;
内嵌汇编指令常量前面的符号“#”可以省略;
内嵌汇编指令不支持内存分配的伪操作。
C程序调用汇编程序1
为了满足ARM汇编、C与C++之间的互相调用,必须保证编写的代码遵循APCS(ARM过程调用标准)。
APCS规定了子程序调用的基本规则,这些规则包括子程序调用过程中寄存器、数据栈的使用规则以及参数的传递规则。
注意:如果函数有4个参数,则将分别用r0 、r1、r2和r3来传递,如果参数多于4个,则多余的参数将被压入堆栈。
程序实例见下页。
C程序调用汇编程序2
文件strtest.c
1 #include
2 extern void strcopy(char *d, const char *s);
3 int main()
4 { const char *srcstr = "First string - source";
5 char dststr[] = "Second string - destination";
6 /* dststr is an array since we're going to change it */
7
8 printf("Before copying:\n");
9 printf(" '%s'\n '%s'\n",srcstr,dststr);
10 strcopy(dststr,srcstr);
11 printf("After copying:\n");
12 printf(" '%s'\n '%s'\n",srcstr,dststr);
13 return 0;
14 }
C程序调用汇编程序3
1 AREA SCopy, CODE, READONLY
2 EXPORT strcopy
3 strcopy
4 ; r0 points to destination string
5 ; r1 points to source string
6 LDRB r2, [r1],#1 ; load byte and update address
7 STRB r2, [r0],#1 ; store byte and update address;
8 CMP r2, #0 ; check for zero terminator
9 BNE strcopy ; keep going if not
10 MOV pc,lr ; Return
文件scopy.s
11 END
在汇编中使用C定义的全局变量
内嵌汇编不用单独编辑汇编语言文件,比较简洁,但是有诸多限制,当汇编的代码较多时一般放在单独的汇编文件中。这时就需要在汇编和C 之间进行一些数据的传递,最简便的办法就是使用全局变量。
在汇编中使用C定义的全局变量
* cfile.c * 定义全局变量,并作为主调程序 */ #include int gVar_1 = 12; extern asmDouble(void); int main() { printf("original value of gVar_1 is: %d", gVar_1); asmDouble(); printf(" modified value of gVar_1 is: %d", gVar_1); return 0; }
在汇编中使用C定义的全局变量
对应的汇编语言文件: ;called by main(in C),to double an integer, a
;global var defined in C is used. AREA asmfile, CODE, READONLY EXPORT asmDouble IMPORT gVar_1 asmDouble ldr r0, =gVar_1 ldr r1, [r0] mov r2, #2 mul r3, r1, r2 str r3, [r0] mov pc, lr END
在汇编中使用C定义的全局变量
对应的汇编语言文件: ;called by main(in C),to double an integer, a
;global var defined in C is used. AREA asmfile, CODE, READONLY EXPORT asmDouble IMPORT gVar_1 asmDouble ldr r0, =gVar_1 ldr r1, [r0] mov r2, #2 mul r3, r1, r2 str r3, [r0] mov pc, lr END
内嵌汇编的注意事项
在C语言中嵌入的ARM汇编需要注意一些问题:
在汇编指令中,可以使用表达式,使用逗号“,”作为分隔符 ;
如果一条指令占用了多行,那么应该使用符号“\”续行,如果一行中有多个汇编指令,那么应该使用“;”将多个指令隔开。
汇编中不能再使用“;”作为注释行的开头,而应该使用C语言中的“/**/”或者“//”进行注释;
内嵌汇编的注意事项
必须小心使用物理寄存器,如R0-R3, LR和PC
计算汇编代码中的C语言表达式时,会使用这些物理寄存器并会修改CPSR中的NZCV标志位。例如:
不要使用寄存器寻址变量
尽管有时寄存器明显对应某个变量,但不能直接使用寄存器代替变量
内嵌汇编的注意事项
使用内嵌汇编时,编译器自己会保存和恢复它可能用到的寄存器,用户无需保存和恢复寄存器。事实上,除了CPSR和SPSR寄存器,对物理寄存器没写就读都会引起汇编器报错。
内嵌汇编的注意事项
LDM和STM指令的寄存器列表只允许物理寄存器。
内嵌汇编可以修改处理器模式、协处理器状态和FP、SL及SB等ATPCS寄存器。但是编译器在编译时并不了解这些变化,所以必须保证在执行C语言代码前恢复相应被修改的处理器模式。
汇编语言用“,”作为操作数分隔符。
例如
__asm {ADD x, y, (f(), z)}
其中,(f() ,z)为C\C++语言表达式。
复习题
1.什么是伪指令和伪操作?在ARM的汇编程序中有哪几种伪指令?
2.如何定义寄存器列表,试举一个使用寄存器列表的例子,要求实现4个字的内存复制。
3.如何定义一个宏,宏与子程序的区别是什么?
4.ARM汇编中如何定义一个段,段有哪几种属性?
5.在一个汇编源文件中如何包含另一个文件中的内容?
6.分别编写一个函数和一个宏,实现字符串的复制。
7.用汇编语言编写一个函数实现数据块复制的功能,以字为单位。