ARM汇编伪指令介绍

来源:互联网 发布:java properties读取 编辑:程序博客网 时间:2024/05/16 11:33
参考《ARM体系结构与编程》第三、四章。

1,伪指令介绍

伪指令不属于指令集。汇编在第一次扫描时只扫描了指令,而将伪指令中的东西作为“动态内容”作了标记而已。所以在第一次扫描所得到的清单中是没有看到它占用内存的。 所以不会计算其中的数据的。而第二次扫描才能得到。指令集是属于机器CPU的,一个类型CPU就有这样一个指令集。而伪指令则是由汇编软件提供的,比如ARM汇编器中提供了EQU的伪指令,那么汇编时是由于汇编器进行运       算的。而计算空间时所得 到的清单文件是关于指令的,所以伪指令并没有计算在内。不同类型的CPU会有不同的指令集,不管你使用什么样的汇编软件,同一个类型 CPU指令集是不会变的!而伪指令是由汇编软件提供,不同的汇编软件有不同的伪指令集。
下面是一些常用的伪指令:
— AREA    
— ALIGN    
— CODE16 、 CODE32    
— ENTRY    
— END    
— EQU
— EXPORT (或 GLOBAL )    
— IMPORT    
— EXTERN    
— GET (或 INCLUDE )    
— INCBIN    
— KEEP:告诉编译器将局部符号包含在目标文件的符号表中
— NOFP:禁止源程序中包含浮点运算指令
— REQUIRE:指定段之间的相互依赖关系
— REQUIRE 8及PRESERVE8:指示当前代码中(要求)数据栈8字节对齐
— RN    
— ROUT    

1)EQU

其作用类似于 C 语言中的#define
Expression_name EQU Expression
此后程序中凡需要用到该表达式指出,就可以用表达式名来代替了。可见,EQU的引入提高了程序的可读性,也使其容易修改。
表达式可以是任何有效的操作数格式,可以是任何可求出常数值的表达式,也可以是任何有效的助记符。举例如下:
CONSTANT EQU 256       ;数值赋以符号名
DATA EQU HEIGHT+12    ;地址表达式赋以符号名
ALPAHA EQU 7


2)AREA

AREA    STACK, NOINIT, READWRITE, ALIGN=3

语法格式:    

REA 段名 属性 1 ,属性 2 ,……    

AREA 伪指令用于定义一个代码段或数据段。其中,段名若以数字开头,则该段名需用 “ | ” 括起来,如 |1_test| 。还有一些代码段具有约定的名称,如|.text|表示C语言编译器产生的代码段或者是与C语言库相关的代码段。属性字段表示该代码段或数据段的相关属性,多个属性用逗号分隔。

常用的属性:    

— CODE 属性:用于定义代码段,代码段的默认属性为 READONLY 。    

— DATA  属性:用于定义数据段,数据段的默认属性为 READWRITE 。   

— NOINIT 属性:指定本数据段仅仅保留了内存单元,而没有将各初始值写入内存单元,或者将各内存单元初始化为0.

— READONLY 属性:指定本段为只读,代码段默认为 READONLY 。    

— READWRITE 属性:指定本段为可读可写,数据段的默认属性为 READWRITE 。    

— ALIGN 属性:使用方式为 ALIGN 表达式。在默认时, ELF (可执行连接文件)的代码段和数据段是按字对齐的,表达式的取值范围为 0 ~ 31 ,相应的对齐方式为 2 的 表达式次方。如表达式=3时为8字节对齐。

—ASSOC=section。指定与本段相关的ELF段。任何时候连接section段也必须包括sectionname段。

— COMMON 属性:该属性定义一个通用的段,不包含任何的用户代码和数据。连接器将其初始化为0。各源文件中同名的 COMMON 段共享同一段内存单元,连接器为其分配合适的尺寸。

— COMDEF 属性:该属性定义一个通用的段,该段可以包含代码或数据。在各源文件中,同名的COMDEF段必须相同。

通常可以用AREA伪操作将程序分为多个ELF格式的段。段名称可以相同,这时这些同名的段被放在同一个ELF段中。一个大的程序可以包括多个代码段和数据段,一个汇编语言程序至少要包含一个段。    

使用示例:    

AREA Init , CODE , READONLY  ;该伪操作定义了一个代码段,段名为 Init ,属性为只读


3)SPACE 

语法格式:    

标号 SPACE 表达式    
SPACE 伪指令用于分配一片连续的存储区域并初始化为 0 。其中,表达式为要分配的字节数。    
SPACE 也可用 “ % ” 代替。    

使用示例:    

DataSpace SPACE 100 ;分配连续100 字节的存储单元并初始化为 0 。


4)PRESERVE8

字节对齐关键词,以前用ADS编译器的时候可以不用,但是后来的keil编译器时需要加上(譬如用周立功模板时,将ADS工程转到keil工程时就必须加上)。指定当前文件堆栈8字节对齐 REQUIRE8 指令指定当前文件要求堆栈八字节对齐。它设置 REQ8 编译属性以通知链接器。
PRESERVE8 指令指定当前文件保持堆栈八字节对齐,它设置 PRES8 编译属性以通知链接器。
链接器检查要求堆栈八字节对齐的任何代码是否仅由保持堆栈八字节对齐的代码直接或间接地调用。

语法格式:

REQUIRE8 {bool} PRESERVE8 {bool} 
其中: bool是一个可选布尔常数,取值为 {TRUE} 或 {FALSE}。 

用法:

如果您的代码保持堆栈八字节对齐,在需要时,可使用 PRESERVE8 设置文件的 PRES8 编译属性。 如果您的代码不保持堆栈八字节对齐,则可使用 PRESERVE8 {FALSE} 确保不设置 PRES8 编译属性。

Note:

如果您省略 PRESERVE8 和 PRESERVE8 {FALSE},汇编程序会检查修改 sp 的指令,以决定是否设置 PRES8 编译属性。 ARM 建议明确指定 PRESERVE8。
您可以通过以下形式启用警告:
armasm --diag_warning 1546
您将会收到类似以下警告:
"test.s", line 37: Warning: A1546W: Stack pointer update potentially breaks 8 byte stack alignment

37 00000044         STMFD    sp!,{r2,r3,lr}


5)THUMB

告诉汇编器下面是32位的Thumb指令,如果需要汇编器将插入位以保证对齐
CODE16、CODE32 [THUMB];

语法格式:    

CODE16 (或CODE32 )    
CODE16 伪指令通知编译器,其后的指令序列为 16 位的Thumb 指令。    
CODE32 伪指令通知编译器,其后的指令序列为 32 位的ARM 指令。    
若在汇编源程序中同时包含ARM 指令和 Thumb 指令时,可用CODE16 伪指令通知编译器其后的指令序列为 16 位的Thumb 指令, CODE32 伪指令通知编译器其后的指令序列为 32 位的ARM 指令。因此,在使用 ARM 指令和Thumb 指令混合编程的代码里,可用这两条伪指令进行切换,但注意他们只通知编译器其后指令的类型,并不能对处理器进行状态的切换。    

使用示例:    

AREA Init , CODE, READONLY    
……    
CODE32; 通知编译器其后的指令为32 位的 ARM 指令    
LDR R0, =NEXT + 1 ;将跳转地址放入寄存器 R0    
BX R0 ;程序跳转到新的位置执行,并将处理器切换到 Thumb 工作状态    
……    
CODE16 ;通知编译器其后的指令为16 位的 Thumb 指令    
NEXT LDR R3, =0x3FF    
……    
END ;程序结束 


6)EXPORT(或GLOBAL)    

语法格式:    

EXPORT 标号{[WEAK]}    
EXPORT 伪指令用于在程序中声明一个全局的标号,该标号可在其他的文件中引用。 EXPORT可用GLOBAL 代替。标号在程序中区分大小写, [WEAK] 选项声明其他的同名标号优先于该标号被引用。    

使用示例:    

AREA Init, CODE, READONLY    
EXPORT Stest ;声明一个可全局引用的标号Stest……    
END    

7)DCD(或DCDU)    

语法格式:    

标号DCD (或 DCDU ) 表达式    
DCD (或DCDU )伪指令用于分配一片连续的字存储单元并用伪指令中指定的表达式初始化。其中,表达式可以为程序标号或数字表达式。 DCD 也可用 “ & ” 代替。用DCD 分配的字存储单元是字对齐的,而用 DCDU 分配的字存储单元并不严格字对齐。    

使用示例:    

DataTest DCD 4, 5, 6  ;分配一片连续的字存储单元并初始化。



2,汇编控制( Assembly Control )伪指令    

汇编控制伪指令用于控制汇编程序的执行流程,常用的汇编控制伪指令包括以下几条:    
— IF 、ELSE 、 ENDIF    
— WHILE 、WEND    
— MACRO 、MEND    
— MEXIT    

1)IF、ELSE、ENDIF    

语法格式:    

IF 逻辑表达式    
指令序列1    
ELSE    
指令序列2    
ENDIF    
IF 、ELSE 、 ENDIF 伪指令能根据条件的成立与否决定是否执行某个指令序列。当 IF 后面的逻辑表达式为真,则执行指令序列 1 ,否则执行指令序列 2 。其中, ELSE 及指令序列 2 可以没有,此时,当 IF 后面的逻辑表达式为真,则执行指令序列 1 ,否则继续执行后面的指令。    
IF 、ELSE 、 ENDIF 伪指令可以嵌套使用。    

使用示例:    

GBLL Test ;声明一个全局的逻辑变量,变量名为 Test……    
IF Test = TRUE    
指令序列1    
ELSE    
指令序列2    
ENDIF
IF       NOT: EF:NO_CRP   ;如果宏判断是否定义NO_CRP #ifndef
AREA    |.ARM.__at_0x02FC|, CODE, READONLY   ;自定义只读代码段
CRP_Key         DCD     0xFFFFFFFF   ;加密等级见上注释
ENDIF

注:

X : LAND : Y 表示将X和Y做逻辑与的操作。
X :LOR : Y 表示将X和Y做逻辑或的操作。
: LNOT : Y 表示将Y做逻辑非的操作。
X : LEOR : Y 表示将X和Y做逻辑异或的操作。
NOT: 逻辑与算符
: DEF :

2)PROC 

过程就是子程序。一个过程可以被其它程序所调用(用CALL指令),过程的最后一条指令一般是返回指令(RET)。

过程定义伪指令的格式为:

<过程名>    PROC  [类型]

…  
RET
<过程名>    ENDP
注意:PROC和ENDP必须成对出现

过程的类型有两种:

NEAR——(默认类型)表示段内调用
FAR——表示段间调用

调用一个过程的格式为:

CALL <过程名>
       

3)IMPORT    

语法格式:    

IMPORT 标号 {[WEAK]}    
IMPORT 伪指令用于通知编译器要使用的标号在其他的源文件中定义,但要在当前源文件中引用,而且无论当前源文件是否引用该标号,该标号均会被加入到当前源文件的符号表中。    
标号在程序中区分大小写,[WEAK] 选项表示当所有的源文件都没有定义这样一个标号时,编译器也不给出错误信息,在多数情况下将该标号置为 0 ,若该标号为 B 或 BL 指令引用,则将 或 BL指令置为 NOP 操作。    

使用示例:    

AREA Init , CODE, READONLY    
IMPORT Main ;通知编译器当前文件要引用标号Main,但Main 在其他源文件中定义。
END