APP下载

基于ARM的汇编语言与C语言混合编程的方法研究

2011-11-25杜钦生楚叶峰唐伎玲

长春大学学报 2011年10期
关键词:汇编语言编译器子程序

杜钦生,楚叶峰,唐伎玲

(长春大学 计算机科学技术学院,长春 130022)

基于ARM的汇编语言与C语言混合编程的方法研究

杜钦生,楚叶峰,唐伎玲

(长春大学 计算机科学技术学院,长春 130022)

针对ARM的汇编语言与C语言混合编程的编程问题,具体研究了C语言中内嵌汇编指令、汇编语言和C语言程序变量的相互调用、汇编语言和C语言程序的相互调用和C编译器的特定关键字问题,并给出了实例。

ARM;汇编语言;C语言

0 引言

随着网路与通信技术的发展,正在涌现出大量新的嵌入式系统。在众多嵌入式系统厂家的共同参与、推动下,基于ARM系列处理器的应用技术在多个领域已取得突破性进展。一个基于ARM的嵌入式系统的完整的程序设计,通常情况用C或者C++完成大部分的编程任务,仅有初始化部分用汇编语言完成。本文主要研究汇编语言和C语言混合编程,结合实际情况给出两种处理策略:若汇编代码较短,则可在C/C++源文件中直接内嵌汇编语言实现混合编程;若汇编代码较长,可以单独写成汇编文件,最后以汇编文件的形式加入项目中,通过ATPCS规定与C程序相互调用及访问。

1 C程序中内嵌汇编语言指令

C和C++编译器中内置了内嵌汇编器,可以用其实现C语言不能或不易完成的操作,提高程序执行效率。Armcc编译器的内嵌汇编器支持ARM指令集,tcc编译器的内嵌汇编器支持Thumb指令集。

1.1 内嵌汇编指令的用法

内嵌的汇编指令支持大部分ARM和Thumb指令,其在使用上有以下特点:

(1)操作数。内嵌指令的操作数可以是C或C++表达式,这些表达式的值均按无符号数处理。当指令中同时使用寄存器和C或C++表达式时,表达式不要过于复杂,以免编译器在计算表达式时用到过多的寄存器,以至于指令中用的寄存器冲突。

(2)寄存器。一般不推荐直接使用,因为可能影响编译器的寄存器分配,从而影响程序效率。如果必须使用要注意:不要向PC赋值,只能利用B或BL指令实现跳转;需要注意编译器可能会使用R12和R13存储临时变量,在计算表达式的值时可能会把R0~R3、R12、R14用于函数调用;如果C变量用到了指令中已经用到的物理寄存器,编译器一般会在必要时用栈保存或恢复这些寄存器,但排除sp、sl、fp和sb。

(3)常量。定值表达式的“#”可以省略,如果用“#”,则其后面必须是常量。

(4)标号。可以利用B指令(不能用BL)跳转到C或C++中的标号。

(5)指令展开。除了与协处理器相关的指令,大多数ARM或Thumb指令对常量的操作会被展开成多条指令,各指令的展开对标志位的影响情况:算术指令可以正确的设置NZCV位;逻辑指令可以正确地设置NZ标志位,不影响V位,破坏C位。

(6)内存分配。所有的内存分配在C或C++程序中声明,通过标号在内嵌汇编中引用,不要在内嵌汇编中用伪操作分配内存。

(7)SWI和BL指令的使用。在内嵌的SWI和BL指令中,除了正常的操作数域外,还必须增加如下三个可选的寄存器列表:第1个寄存器列表中的寄存器用于存放输入的参数;第2个寄存器列表中的寄存器用于存放返回的结果。第3个寄存器列表中的寄存器的内容可能被调用的子程序破坏。

1.2 内嵌汇编器与纯粹的ARM ASM汇编器的区别

使用内嵌汇编还应注意以下几点:

(1)不能通过(.)或{PC}获得当前指令地址。

(2)不能用LDR Rn,=expr伪指令,可以用MOV Rn,expr替代(可生成从数据缓冲池中加载数据的汇编指令)。

(3)不支持标号表达式。

(4)不支持ADR和ADRL伪指令。

(5)表示十六进制数只能用0x,不能用&。

(6)编译器可能使用寄存器R0~R3、IP、LR存放中间结果,因此在使用这些寄存器时要注意。

(7)CPSR寄存器中的NZCV条件标志位可能会被编译器在计算C表达式时改变,因此在指令中使用这些标志位时要注意。

(8)指令中使用的C变量不要与ARM物理寄存器同名。

(9)LDM与STM指令的寄存器列表中只能使用物理寄存器,不能使用C表达式。

(10)不能写寄存器PC,不支持BX和BLX指令。

(11)用户不需要维护数据栈,因为编译器会根据需要自动保存或恢复工作寄存器的值。

(12)用户可以改变处理器模式,修改ATPCS寄存器sb、sl、fp,改变协处理器的状态,但这并不为编译器所知。所以,如果用户改变了处理器的模式,不要使用原来的C表达式,直至重新恢复到原来的处理器模式后,方可使用这些C表达式。

(13)内嵌汇编指令常量前面的符号“#”可以省略;

(14)内嵌汇编指令不支持内存分配的伪操作。

1.3 内嵌汇编在C和C++程序中的使用格式

ARM C++程序中可以使用关键词__asm来表示一段内嵌汇编指令,格式如下:asm(“指令”);

其中,asm后面的括号中必须是一条汇编指令语句,并且不能包含注释语句。

在C语言中嵌入的ARM汇编需要注意一些问题。

(1)如果一条指令占用了多行,那么应该使用符号“”续行,如果一行中有多个汇编指令,那么应该使用将多个指令隔开;

(2)汇编中不能再使用“;”作为注释行的开头,而应该使用C语言中的“/**/”或者“//”进行注释;(3)不要企图使用一个物理寄存器去改变一个C变量;

(4)计算汇编代码中的C语言表达式时,会使用这些物理寄存器并会修改CPSR中的NZCV标志位;

(5)在汇编指令中,可以使用表达式,使用逗号“,”作为分隔符。例如__asm{ADD x,y,(f(),z)},其中,(f(),z)为CC++语言表达式。

(6)必须小心使用物理寄存器,如R0-R3、LR和PC。例如:

当计算x/y时,R0会被修改,从而影响R0+x/y的结果。用一个C语言变量代替R0就可以解决这个问题。例如

(7)汇编器检测器检测到隐含的寄存器冲突就会报错。尽管有时寄存器明显对应某个变量,但不能直接使用寄存器代替变量。例如

尽管根据编译器编译规则似乎可确定R0对应x,但这样的代码会使汇编器认为发生了寄存器冲突。用其他寄存器代替R0存放参数x,则使得该函数将x原封不动地返回。正确地写法如下:

(8)对于内嵌的汇编代码用到的寄存器,编译器在编译时会自动加入保存和恢复这些寄存器的代码而不用用户去管理,除了寄存器CPSR和SPSR,其他寄存器都必须先赋值然后再读取,否则编译时将出现错误。

2 汇编语言和C语言程序的变量相互调用

在一个工程中,一般都会由多个汇编文件和多个C/C++程序文件有机组成。在这些汇编文件和C/C++文件之间就存在变量相互访问和函数相互调用的问题。

内嵌汇编不用单独编辑汇编语言文件,比较简洁,但是有诸多限制,当汇编的代码较多时一般放在单独的汇编文件中。这时就需要在汇编和C之间进行一些数据的传递,最简便的办法就是使用全局变量。

2.1 汇编程序中访问C程序变量

在C/C++程序中声明的全局变量可以被汇编程序通过地址间接访问。具体访问方法/步骤如下:

(1)在C/C++程序中声明全局变量;

(2)在汇编程序使用IMPORT/EXTERN伪指令声明引用该全局变量;

(3)使用LDR伪指令读取该变量的内存地址;

(4)根据该数据的类型使用相应的LDR或STR指令读取或设置该变量的值。对于无符号变量,使用LDRB/STRB访问char;使用LDRH/STRH访问short;使用LDR/STR访问integer。对于有符号数,使用LDRSB/LDRSH。

在汇编的源程序中调用C语言风格的字符串需要使用IMPORT伪操作。IMPORT相当于C语言中的extern关键字,告诉编译器引用的符号不是在本文件中定义的,而是在其他的源文件中定义的。

伪操作的格式:

IMPORT symbol[,WEAK]

symbol是声明的符号的名称;[,WEAK]指示编译器如果发现symbol在所有的源文件中都没有找到,那么它也不会产生任何的错误信息。

2.2 C程序中访问汇编程序变量

在汇编程序中声明的数据可以被C/C++程序所访问,具体访问方法/步骤是:在汇编程序中用EXPORT/GLOBAL伪指令声明该符号为全局标号,可以被其他文件应用;C/C++程序中定义相应数据类型的指针变量;对该指针变量赋值为汇编程序中的全局标号,利用该指针访问汇编程序中的数据。

3 汇编语言和C语言程序相互调用

3.1 ATPCS准则

C/C++程序和ARM汇编程序之间相互调用必须遵守ATPCS(ARM/Thumb Procedure Call Standard)。使用ADS的C语言编译器编译的C语言子程序会自动满足用户指定的ATPCS类型。而对于汇编语言来说,完全要依赖用户来保证各个子程序满足选定的ATPCS类型。具体来说,汇编程序必须满足以下3个条件才能实现与C语言的相互调用。在子程序编写时必须遵守相应的ATPCS的规则;堆栈的使用要遵守相应的ATPCS的规则;在汇编编译器中使用-atpcs选项。

独立汇编和编译的子程序间的互访要遵守ATPCS标准。ATPCS中规定了在子程序调用时的一些基本规则,如子程序调用过程中的寄存器的使用规则、堆栈的使用规则、参数的传递规则等,还有一些为特定需要派生的特定ATPCS规则,包括数据栈限制检查、只读段位置无关、可读写段位置无关、ARM和Thumb程序混合使用以及关于浮点运算的一些规则,等。这里仅对ATPCS基本规则进行分析。

3.1.1 寄存器的使用和命名规则

表1列出了ATPCS中寄存器的使用和命名规则。

表1 寄存器的使用和命名规则

3.1.2 堆栈的使用规则

子程序的调用经常需要用到堆栈,特别当使用到较多参数时候。ATPCS规定堆栈为FD类型,即满递减堆栈,对堆栈的操作是8字节对齐。堆栈中为子程序分配的内存区域可用来保存寄存器和局部变量,我们称这块内存区域为堆栈中的数据帧。

使用ADS中的编译器产生的目标代码中包含了DRAFT2格式的数据帧。在调试过程中,调试器可以使用这些数据帧来查看堆栈中的相关信息。对于汇编语言来说,用户必须使用FRAME伪指令来描述堆栈的数据帧。ARM汇编器根据这些伪指令在目标文件中产生相应的DRAFT2格式的数据帧。对于汇编程序来说,如果目标文件中包含了外部函数调用,则必须满足以下条件:外部接口的堆栈必须是8字节对齐的;在汇编程序中使用PRESERVE8伪指令告诉链接器,在本汇编程序数据是8字节对齐的。

3.1.3 参数传递规则

根据参数是否固定可以将子程序分为参数个数固定的子程序和参数个数可变化的子程序。如果系统包含浮点运算的硬件部件,这两种子程序传递参数的规则是不一样的;否则的话,这两种子程序传递参数的规则是一样的。

(1)子程序参数传递规则

对于参数个数可变的子程序,在子程序调用中,当参数个数不超过4个时,可以使用寄存器R0~R3来传递,参数传递时所用参数被看作是存放在连续的内存字单元的字数据,然后依次将各字数据传送到寄存器R0、R1、R2、R3中;当参数个数超过4个时,多余的参数可以使用堆栈来传递,入栈的顺序与参数顺序相反,即最后一个字数据先入栈。这样,对于超过4字节的参数(如双精度浮点数,结构体),可能一部分保存于寄存器,另一部分保存于数据栈,或者全部压栈。

对于包含浮点运算的硬件系统,在浮点参数的传递方面与参数个数不固定的情况不同,规则为:浮点参数按顺序处理,为每个浮点参数分配满足其需要的且编号最小的一组连续的FP寄存器。

(2)子程序中结果返回规则

结果为一个字(32位)的整数时,可以通过寄存器R0返回;结果为一个2~4字(64位)的整数时,可以通过寄存器R0~R1、R0~R2、R0~R3来返回;结果为浮点数,用浮点寄存器f0、d0或s0返回;结果为复合型的浮点数,用f0~fn或d0~dn返回;对于位数更多的结果,需要间接通过内存来返回。

3.2 C程序调用汇编程序

汇编程序的设置要遵循ATPCS规则,保证程序调用时参数的正确传递,在这种情况下,C程序可以调用汇编子函数。C程序调用汇编程序的方法如下:汇编程序中使用EXPORT伪指令声明本子程序可外部使用,使其他程序可调用该子程序;在C语言程序中使用extern关键字声明外部函数(声明要调用的汇编子程序),才可调用此汇编的子程序。

3.3 汇编程序调用C程序

汇编程序的设置要遵循ATPCS规则,保证程序调用时参数的正确传递。汇编程序调用C程序的方法如下:在汇编程序中使用IMPORT伪指令声明将要调用的C程序函数;在调用C程序时,要正确设置入口参数,然后使用BL指令调用。

4 ARM C编译器的特定关键字

ARM C编译器支持一些对ANSI C进行扩展的关键字,用于声明特定的函数或数据类型等。下面列举了一些常用的关键字,_irq、_value_in_regs、_inline、_volatile_pure和_suli等。

5 汇编语言和C语言程序设计实例比较

在程序设计时,对于同一功能既可以使用汇编语言来实现,又可以用C语言来实现。比如对于一个实例:寄存器R1和R2中有两个数,若R0为0则求R1与R2的和,若R0为1则求R1与R2的差,结果存储在R0中。我们用两种语言实现,而且做了对比。

6 结语

本文研究了ARM的汇编语言与C语言混合编程的编程问题,重点对于C语言中内嵌汇编指令、汇编语言和C语言程序变量的相互调用、汇编语言和C语言程序的相互调用和C编译器的特定关键字问题进行了分析,并给出了实例,希望对于嵌入式程序设计与开发人员具有一定的借鉴意义。

[1] 贾智平,张瑞华.嵌入式系统原理与接口技术[M].2版.北京:清华大学出版社,2009.

[2] 张石.ARM嵌入式系统教程[M].北京:机械工业出版社,2008.

[3] 熊茂华,谢建华,熊昕.嵌入式Linux C语言应用程序设计与实践[M].北京:清华大学出版社,2010.

[4] 熊茂华,杨震伦.ARM9嵌入式系统设计与开发应用[M].北京:清华大学出版社,2010.

[5] 俞辉,李永,刘凯,王晓虹.ARM嵌入式Linux系统设计与开发[M].北京:机械工业出版社,2010.

Research on ARM-based Programming Methods of Assemble Language and C Language

DU Qin-sheng, CHU Ye-feng,TANG Ji-ling
(College of Computer Science and Technology,Changchun University,Changchun 130022,China)

In view of ARM-based programming methods of assemble language and C Language,this paper discusses such problems as the assembly instruction embedded in C Language,the transference between assembly language and program variables,the transference between assembly language and program and the keywords of C Compiler,then it gives some examples.

ARM;assembly language;C Language

TP393.17

A

1009-3907(2011)10-0019-05

2011-08-28

吉林省教育厅“十一五”科学技术研究项目(2010449);吉林省科技发展计划项目(201105046);吉林省教育科学“十二五”规划2011年度项目(GH11067)。

杜钦生(1978-),男,吉林磐石人,讲师,博士研究生,主要研究方向为嵌入式系统设计与开发。

责任编辑:吴旭云

猜你喜欢

汇编语言编译器子程序
高等学校计算机专业课程教学改革实践——以汇编语言与接口技术课程为例
基于相异编译器的安全计算机平台交叉编译环境设计
汇编语言与C语言的混合程序设计技术研究
提高《汇编语言程序设计》教学效率的思考与实践
Microchip为MPLAB XC系列专业版编译器推出低成本可续订包月许可证
浅谈子程序在数控车编程中的应用
试论汇编语言与C语言的混合程序设计技术
子程序在数控车加工槽中的应用探索
西门子840D系统JOG模式下PLC调用并执行NC程序
通用NC代码编译器的设计与实现