ARM汇编编程基础(三) -- ARM汇编伪操作

来源:互联网 发布:科比和詹姆斯对位数据 编辑:程序博客网 时间:2024/04/30 04:59


本系列文章节选自本人所著《深入浅出嵌入式底层软件开发》

本系列文章,所需代码请从以下地址下载:

http://download.csdn.net/download/scyangzhu/4602585


1.3.1 汇编伪操作在汇编程序中的使用范例

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

1 ;文件名:TEST.S

2 ;功能:实现两个寄存器相加

3         AREA Example,CODE,READONLY ;声明代码段Example

4         ENTRY ;标识程序入口

5         CODE32 ;声明32位ARM指令

6 START  MOV R0,#0 ;设置参数

7         MOV R1,#10

8              BL  ADD_SUB;调用子程序ADD_SUB

9  LOOP        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、9、10行的START、LOOP、ADD_SUB是标号,最经常用于跳转指令B和BL,由于汇编语法要求的缘故,标号必须顶格写(即:不能在行首有空格),否则编译器会报错。与之对应的是,汇编指令一定不能顶格写。

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

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

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

1.3.2 最常见汇编伪操作精解

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

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

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

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

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

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

    GBLAtestval

testval   SETA 9

    IFtestval < 5

      mov r0, #testval

    ELSE

      movr1, #testval

    ENDIF

    IF  :DEF:testval

      movr2, #testval

    ELSE

      INFO 4, "you should definetestval"

    ENDIF

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

mov r1, #9

mov r2, #9

(6) WHILE及WEND :例如

    GBLAtestval   

testval SETA 1

    WHILE testval <= 3

testval SETA testval + 1

    mov r0,#testval

    WEND

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

mov r0, #2

mov r0, #3

mov r0, #4

(7) 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

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

9. EXPORT: 参见“ATPCS与混合编程”

10. IMPORT:参见“ATPCS与混合编程”

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

1.3.3 汇编伪操作列表

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

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

表1 - 3符号定义伪操作

伪操作

语法格式

作用

GBLA

GBLA Variable

 声明一个全局的算术变量,并将其初始化成0

GBLL

GBLL Variable

 声明一个全局的逻辑变量,并将其初始化成{FALSE}

GBLS

GBLS Variable

 声明一个全局的字符串变量,并将其初始化成空串“”

LCLA

LCLA Variable

 声明一个局部的算术变量,并将其初始化成0

LCLL

LCLL Variable

声明一个局部的逻辑变量,并将其初始化成{FALSE}

LCLS

LCLS Variable

声明一个局部的串变量,并将其初始化成空串“”

SETA

Variable SETA expr

给一个全局或局部算术变量赋值

SETL

Variable SETL expr

给一个全局或局部逻辑变量赋值

SETS

Variable SETS expr

给一个全局或局部字符串变量赋值

RLIST

name LIST (list of registers)

 为一个通用寄存器列表定义名称

CN

name CN expr

为一个协处理器的寄存器定义名称

CP

name CP expr

为一个协处理器定义名称

DN/SN

name DN/SN expr

 DN/SN为一个双精度/单精度的VFP寄存器定义名称

FN

name FN expr

 为一个FPA浮点寄存器定义名称

 

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

表1 -  4  数据定义伪操作

伪操作

语法格式

作用

LTORG

LTORG

 声明一个数据缓冲池(也称为文字池)的开始

MAP

MAP expr {,base_register}

 定义一个结构化的内存表(Storage Map)的首地址

FIELD

{label} FIELD expr

 定义一个结构化内存表中的数据域

SPACE

{label} SPACE expr

 分配一块连续内存单元,并用0初始化

DCB

{label} DCB expr {, expr}

分配一段字节内存单元,并用expr初始化

DCD

DCDU

{label} DCD {U} expr {, expr}…

分配一段字(对齐)的内存单元,DCD可能在分配的第1个内存单元前插入填补字节(padding),以保证分配的内存是字对齐的,DCDU不需要对齐

DCFD/

DCFDU

{label} DCFD{U} fpliteral{,fpliteral}...

为双精度的浮点数分配字对齐的内存单元

DCFS/

DCFSU

{label} DCFS{U} fpliteral{,fpliteral}...

为单精度的浮点数分配字对齐的内存单元

DCI

{label} DCI      expr{,expr}...

在ARM代码中分配一段字对齐的内存单元;在Thumb代码中,分配一段半字对齐的半字内存单元

DCQ/DCQU

{label} DCQ{U} {-}literal{,{-}literal}...

分配一段以双字(8个字节)为单位的内存

DCW/ DCWU

{label} DCW   expr{,expr}...

DCW用于分配一段半字对齐的半字内存单元

 

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

表1 -  5  汇编控制伪操作

伪操作

语法格式

作用

IF, ELSE及ENDIF

IF logical expr

{ELSE

…}

ENDIF

能够根据条件把一段源代码包括在汇编语言程序内或者将其排除在程序之外

WHILE及WEND

WHILE logical expr

WEND

能够根据条件重复汇编相同的一段源代码

MACRO, MEND及MEXIT

MACRO

{$label} macroname{$param

{,$param}…}

…;宏代码

MEND

 MACRO标识宏定义的开始,MEND标识宏定义的结束,MEXIT用于从宏中跳转出去,用MACRO和MEND定义的一段代码,称为宏定义体,通过宏名称来调用宏

 

l        信息报告(Reporting)伪操作:

表1 - 6  信息报告伪操作

伪操作

语法格式

作用

ASSERT

ASSERT logical expr

对汇编程序的第二遍扫描中,如果其中ASSERT中条件不成立,ASSERT伪操作将报告该错误信息。

INFO

INFO numberic-expr, string-expr

对汇编程序的第一遍扫描或者第二遍扫描时INFO伪操作报告诊断信息

OPT

OPT n

 通过OPT伪操作可以在源程序中设置列表选项

TTL

TTL title

在列表文件的每一页的开头插入一个标题

 

l        其他(Miscellaneous)伪操作:

表1 - 7  其他伪操作

伪操作

语法格式

作用

CODE16

CODE16

告诉汇编编译器后面的指令序列为16位的Thumb指令

CODE32

CODE32

告诉汇编编译器后面的指令序列为32位的ARM指令

EQU

name EQU expr{, type}

 为数字常量,基于寄存器的值和程序中的标号(基于PC的值)定义一个字符名称,类似于C语言中的#define宏定义

AREA

AREA sectionment {, attr}{, attr}…

定义一个代码段或者数据段

ENTRY

ENTRY

指定程序的入口点

END

END

告诉编译器已经到了源程序结尾

ALIGN

ALIGN {expr{, offset)}

通过添加补丁字节使当前位置满足一定的对齐方式

 

EXPORT/

GLOBAL

EXPORT symbol{[WEAK]}

 

声明一个符号可以被其他文件引用

IMPORT/

EXTERN

IMPORT Symbol{WEAK}

EXTERN symbol{(WEAK)}

告诉编译器当前的符号不是在本源文件中定义的,而是在其他源文件中定义的,在本源文件中可能引用该符号

GET/INCLUDE

GET filename

将一个源文件包含到当前源文件中,并将被包含的文件在其当前位置进行汇编处理

INCBIN

INCBIN filename

将一个文件包含到当前源文件中,被包含的文件不进行汇编处理

KEEP

KEEP{symbol}

告诉编译器将局部符号包含在目标文件的符号表中

NOFP

NOFP

禁止源程序中包含浮点运算指令

REQUIRE

REQUIRE lable

指定段之间的相互依赖关系

原创粉丝点击