在C 语言程序中嵌入汇编程序,可以实现一些高级语言没有的功能,并可以提高执行效率。 armcc 和armcpp 内嵌汇编器支持完整的ARM 指令集;tcc 和tcpp 用于Thumb 指集。 但是,内嵌汇编器并不支持诸如直接修改PC 实现跳转的底层功能。
虽然内嵌的汇编指令包括大部分的ARM 指令和Thumb 指令,但是不能直接引用C 语言的变量定义,数据交换必须通过ATPCS 进行。 嵌入式汇编在形式上表现为独立定义的函数体。
(1)内嵌汇编指令的语法格式
__asm(“指令[;指令]”);
ARM C 汇编器使用关键字“__asm”。 如果有多条汇编指令需要嵌入,可以用“{}”将它们归为一条语句。 如:
各指令用“;”分隔。 如果一条指令占据多行,除最后一行外都要使用连字符“\”。 在汇编指令段中,可以使用C 语言的注释语句。 需要特别注意的是__asm 是两条下画线。
(2)内嵌的汇编指令的特点
1)操作数
在内嵌的汇编指令中,操作数可以是寄存器、常量或C 语言表达式。 它们可以是char、short 或者int 类型,而且都是作为无符号数进行操作。 如果需要有符号数,用户需要自己处理与符号有关的操作。 编译器将计算这些表达式的值,并为其分配寄存器。
当汇编指令中同时用到了物理寄存器和C 语言的表达式时,要注意使用的表达式不要过于复杂。
2)物理寄存器
在内嵌的汇编指令中使用物理寄存器有以下限制:
①不能直接向PC 寄存器中赋值,程序的跳转只能通过B 指令和BL 指令实现。
②在使用物理寄存器的内嵌汇编指令中,不要使用过于复杂的C 语言表达式。 因为当表达式过于复杂时,会需要较多的物理寄存器,这些寄存器可能与指令中的物理寄存器的使用冲突。 当编译器发现了寄存器的分配冲突时,会产生相应的错误信息,报告寄存器分配冲突。
③编译器可能会使用R12 寄存器或者R13 寄存器存放编译的中间结果,在计算表达式值时,可能会将寄存器R0 到R3、R12 以及R14 用于子程序调用。 因此,在内嵌的汇编指令中,不要将这些寄存器同时指定为指令中的物理寄存器。
④在内嵌的汇编指令中使用物理寄存器时,如果有C 语言变量使用了该物理寄存器,编译器将在合适时保存并恢复该变量的值。
⑤通常在内嵌的汇编指令中不要指定物理寄存器,因为这可能会影响编译器分配寄存器,进而可能影响代码的效率。
3)常量
在内嵌的汇编指令中,常量前的符号“#”可以省略。 如果在一个表达式中使用符号“#”,该表达式必须是一个常量。
4)标号
C 语言程序中的标号可以被内嵌的汇编指令使用,但是只有指令B 可以使用C 语言程序中的标号,指令BL 不能使用C 语言程序中的标号。 指令B 使用C 语言程序中的标号时,语法格式如下所示:
B{cond}label
5)内存单元的分配
内嵌汇编器不支持汇编语言中用于内存分配的伪操作。 所用的内存单元的分配都是通过C 语言程序完成的,分配的内存单元通过变量供内嵌的汇编器使用。
6)指令展开
内嵌的汇编指令中如果包含常量操作数,该指令可能会被汇编器展开成几条指令。例如,指令“ADD R0,R0,#1023”可能会被展开成下面的指令序列:
ADD R0,R0,#1024
SUB R0,R0,#01
乘法指令MUL 可能会被展开成一系列的加法操作和移位操作。 事实上,除了与协处理器相关的指令外,大部分的ARM 指令和Thumb 指令中包含常量操作数都可能被展开成多条指令。 各展开的指令对CPSR 寄存器中的各条件标志位有如下影响:(www.xing528.com)
①算术指令可以正确地设置CPSR 寄存器中的NZCV 条件标志位。
②逻辑指令可以正确地设置CPSR 寄存器中的NZ 条件标志位,不影响V 条件标志位,破坏C 语言条件标志位(使C 语言标志位变得不准确)。
7)SWI 和BL 指令的使用
在内嵌的SWI 和BL 指令中,除了正常的操作数域外,还必须增加以下三个可选的寄存器列表。
①第一个寄存器列表中的寄存器用于存放输入的参数。
②第二个寄存器列表中的寄存器用于存放返回的结果。
③第三个寄存器列表中的寄存器供被调用的子程序作为工作寄存器,这些寄存器的内容可能被调用的子程序破坏。
8)内嵌汇编器与armasm 汇编器的区别
内嵌汇编器与armasm 汇编器的区别如下:
①内嵌汇编器不支持通过“·”指示符或PC 获取当前指令地址。
②不支持LDR Rn,= expression 伪指令,而使用MOV Rn, expression 指令向寄存器赋值。
③不支持标号表达式。
④不支持ADR 和ADRL 伪指令。
⑤不支持BX 和BLX 指令。
⑥不可以向PC 赋值。
⑦使用0x 前缀替代“&”表示十六进制数。 当使用8 位移位常量导致CPSR 中的ALU 标志位需要更新时,NZCV 标志中的C 不具有真实意义。
(3)内嵌汇编指令的应用举例
下面是在C 语言程序中嵌入汇编程序的例子。 通过以下例子,可以帮助用户更好地理解内嵌汇编的特点及用法。
1)字符串复制
本例主要介绍如何使用指令BL 调用子程序。
注意,由内嵌汇编的特点可知,在内嵌的SWI 和BL 指令中,除了正常的操作数域外,还必须增加下面三个可选的寄存器列表。 在这个程序里就能体现这一点。
示例:使用指令BL 调用子程序。
在这个例子中,主函数main()中的BL my_strcpy,{R0,R1}指令的输入寄存器列表为{R0,R1},没有输出寄存器列表。 子程序使用的工作寄存器为ATPCS 默认工作寄存器R0 ~R3、R12、lr 以及PSR。
2)使能和禁止中断
本例主要介绍如何利用内嵌汇编程序来使能和禁止中断。
使能和禁止中断是通过修改CPSR 寄存器中的[位7]完成的。 这些操作必须在特权模式下进行,因为在用户模式下不能修改CPSR 寄存器中的控制位。
示例:中断的使能和禁止。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。