现在的位置: 首页 > 综合 > 正文

ARM汇编编程基础之四 —— ARM汇编伪操作

2018年04月17日 ⁄ 综合 ⁄ 共 2747字 ⁄ 字号 评论关闭

掌握了基本的ARM汇编指令后,要写出简单的ARM汇编程序,还必须要掌握基本的ARM汇编伪操作(directive)。现在我们来看一个简单的汇编程序,该程序调用子程序完成了加法操作。

1 ;文件名:TEST.S
2 ;功能:实现两个寄存器相加
         AREA Example,CODE,READONLY ;声明代码段Example
         ENTRY ;标识程序入口
         CODE32 ;声明32位ARM指令
START  MOV R0,#0 ;设置参数
         MOV R1,#10
LOOP BL ADD_SUB ;调用子程序ADD_SUB
         B LOOP ;跳转到LOOP
10 ADD_SUB
11         ADD R0,R0,R1 ;R0 = R0 + R1
12         MOV PC,LR ;子程序返回
13         END ;文件结束

第6、7行将传递给子程序的参数存放在r0和r1中,第8行调用子程序。第11、12行是子程序的代码,完成了2个参数相加,并将结果放在r0后返回主程序。第6、8、10行的START、LOOP、ADD_SUB是标号,最经常用于跳转指令B和BL,由于汇编语法要求的缘故,标号必须顶格写(即:不能在行首有空格),否则编译器会报错。与之对应的是,汇编指令一定不能顶格写。

很明显分号(;)在汇编程序中是注释符号,相当于C语言的//号。除此之外,当然大家注意到了第3、4、5、13行是我们没学习过的符号,其实它们就是本文的重点——ARM汇编伪操作。首先我先来解释这几个伪操作,第3行定义了一个代码段,其段名为Example,属性为只读,从而表示第6——12行是程序代码(而不是程序数据)。第4行表示整个程序的入口点(即:程序运行的第一条指令)是第6行的MOV指令(注1)。第5行表示第6——12行的程序代码是ARM指令,而不是thumb指令。第13行表示源代码文件结束,其背后的含义就是:如果程序员在第13行后还写有汇编指令,编译器也根本不会理会这些代码,更不会去编译它们,当然这些代码也就不可能出现在最后的可执行文件中。哈哈,所以请务必记住,在END伪操作的后面再写代码,那是无用功,写了也白写。不要不以为然哟,根据经验,初学者总是会犯这样的错误。

特别说明:第9行的含义是要让程序在运行结束后,在第9行进行死循环,从而让整个程序定格在第9行。这一点也许你很困惑:在写应用程序时,程序结束就结束了,源代码根本不需要再去写个死循环。但你现在要弄清楚:你写应用程序时,有OS为你处理程序结束后的若干事情。可是,你现在已经得不到OS服务。如果你不自己写第9行的代码,那么当你认为程序已经运行结束(第8行执行完成)的时候,CPU不会聪明地停下来,它会继续任劳任怨地去取指第11行,继续运行,这不是你所希望的。其实这还不是最糟糕的,最糟糕的是,如果你的程序没有11-13行,那么CPU任劳任怨取出的指令其实是内存中的随机数,但CPU却会把它当作指令来执行,那么,你认为此时会出现什么情况呢?哈哈,只有天知道。

当然,伪操作远不止这几条,下面我们再来介绍经常使用的若干伪操作。

GBLA:定义全局算术变量(准确说,应该是全局符号),例如:GBLA testval

SETA:对全局算术符号进行赋值,例如:testval    SETA    9;testval SETA testval + 1

DCD:在编译时为整数分配字存储空间,例如:DCD 0x123456ab,这条伪操作将导致编译器在最终的二进制可执行文件中分配一个字的空间,并在该空间中存放整数0x123456ab

DCB:在编译时为整数分配字节存储空间,例如:DCB ‘a’,这条伪操作将导致编译器在最终的二进制可执行文件中分配一个字节的空间,并在该空间中存放字符a的ASCII码

IF,ELSE及ENDIF:相当于C语言的条件编译,例如:

    GBLA testval
testval    SETA 9
    IF testval < 5
      mov r0, #testval
    ELSE
      mov r1, #testval
    ENDIF
    IF :DEF:testval
      mov r2, #testval
    ELSE
      INFO 4, "you should define testval"
    ENDIF

编译器编译该段代码的结果是:

mov r1, #9

mov r2, #9

WHILE及WEND :例如

    GBLA testval   
testval SETA 1
    WHILE testval <= 3
testval SETA testval + 1
    mov r0, #testval
    WEND

编译器编译该段代码的结果是:

mov r0, #2

mov r0, #3

mov r0, #4

MACRO 、MEND及MEXIT:相当于C语言的宏替换,例如:

    MACRO
$label xmac $p1,$p2
; code1
$label.loop1
;code2
    BGE $label.loop1
$label.loop2
;code3
    BL $p1
    BGT $label.loop2
; code4
    ADR r0, $p2
;code5
    MEND
;主程序
abc xmac subr1,de

编译器编译该段代码的结果是:

;code1
abc.loop1
;code2
    BGE abc.loop1
abc.loop2
;code3
    BL subr1
    BGT abc.loop2
;code4
    ADR r0, de
;code5

EQU:相当于C语言的宏定义,例如:testval EQU 4

EXPORT: 见“ATPCS与混合编程”一文

IMPORT:见“ATPCS与混合编程”一文

非常重要的一点是:必须深刻理解汇编伪操作是给编译器提供某些必要的信息,以帮助编译器正确完成程序的编译。当编译完成后,汇编伪操作就完成了它的历史使命,它不可能在最终的可执行程序的二进制代码中留下哪怕是一点点痕迹,当然也就不可能在程序运行时受到CPU的“青睐”。总之记住一句话,汇编伪操作是给编译器看的,而不是给CPU看的。这是汇编伪操作与汇编指令最大的区别。

为了保持内容的完整,下面给出较为完整的汇编伪操作列表。如需完整的列表,请自行查阅ads自带的“Online Books”相关章节。

符号定义(Symbol Definition)伪操作:

image

数据定义(Data Definition)伪操作:

image

汇编控制(Assembly Control)伪操作:

image

信息报告(Reporting)伪操作:

image

其他(Miscellaneous)伪操作:

image

注1:ENTRY的本意并非如此,此处的含义仅是ENTRY的副作用而以。关于其本意,后续文章将予以解释

抱歉!评论已关闭.