ARM 汇编程序的由机器指令,伪指令和宏指令组成。伪指令不像机器指令那样在处理器运行期间由机器执行,而是汇编程序对源程序汇编期间由汇编程序处理。在前面的指令集章节中,我们已经接触了几条常用到的伪指令,如ADR 、ADRL、LDR、NOP 等,把它们和指令集一起介绍是因为它们在汇编时会被合适的机器指令代替,实现真正机器指令操作。宏是一段独立的程序代码,它是通过伪指令定义的,在程序中使用宏指令即可调用宏。
当程序被汇编时,汇编程序将对每个调用进行展开,用宏定义取代源程序中的宏指令。
A.4.1 符号定义伪指令
符号定义伪指令用于定义ARM 汇编程序的变量,对变量进行赋值以及定义寄存器名称,该类伪指令如下:
全局变量声明:GBLA、GBLL 和GBLS。
局部变量声明:LCLA、LCLL 和LCLS。
变量赋值: SETA、SETL 和SETS。
为一个通用寄存器列表定义名称:RLIST。
为一个协处理器的寄存器定义名称:CN。
为一个协处理定义名称: CP。
为一个VFP 寄存器定义名称:DN 和SN。
为一个FPA 浮点寄存器定义名称:FN。
GBLA、GBLL、GBLS
全局变量声明伪指令。
GBLA 伪指令用于声明一个全局的算术变量,并将其初始化为0。
GBLL 伪指令用于声明一个全局的逻辑变量,并将其初始化为{FALSE}。
GBLS 伪指令用于声明一个全局的字符串变量,并将其初始化为空字符串“”。
伪指令格式:
GBLA variable
GBLL variable
GBLS variable
其中:variable 定义的全局变量名,在其作用范围内必须惟一。全局变量的作用范围为包含该变量的源程序。
伪指令应用举例如下:
GBLL codedbg ;声明一个全局逻辑变量
codebg SETL {TRUE} ;设置变量为{TRUE}
…
LCLA、LCLL、LCLS
局部变量声明伪指令,用于宏定义的体中。
LCLA 伪指令用于声明一个局部的算术变量,并将其初始化为0。
LCLL 伪指令用于声明一个局部的逻辑变量,并将其初始化为{FALSE}。
LCLS 伪指令用于声明一个局部的字符串变量,并将其初始化为空字符串“”。
伪指令格式:
LCLA variable
LCLL variable
LCLS variable
其中:variable 定义的局部变量名。在其作用范围内必须惟一。局部变量的作用范围为包含该局部变量只能在宏中进行声明及使用。
伪指令应用举例如下:
MACRO ;声明一个宏
SENDDAT $dat ;宏的原型
LCLA bitno ;声明一个局部算术变量
…
bitno SETA 8 ;设置变量值为8
…
MEND
SETA、SETL、SETS
变量赋值伪指令。用于对已定义的全局变量,局部变量赋值。
SETA 伪指令用于给一个全局/局部的算术变量赋值。
SETL 伪指令用于给一个全局/局部的逻辑变量赋值。
SETS 伪指令用于给一个全局/局部的字符串变量赋值。
伪指令格式:
variable_a SETA expr_a
variable_l SETL expr_l
variable_s SETS expr_s
其中:variable_a 算术变量。用GBLA、LCLA 伪指令定义的变量。
expr_a 赋值的常数。
variable_l 逻辑变量。用GBLL、LCLL 伪指令定义的变量。
expr_l 逻辑值,即{TRUE}或{FALSE}。
variable_s 字符串变量。用GBLS、LCLS 伪指令定义的变量。
expr_s 赋值的字符串。
伪指令应用举例如下:
GBLS ErrStr
…
ErrStr SETS “No,semaphone”
…
RLIST
RLIST 为一个通用寄存器列表定义名称。伪指令格式如下:
name RLIST {reglist}
其中:name 要定义的寄存器列表的名称。
reglist 通用寄存器列表。
伪指令应用举例如下:
LoReg RLIST {R0-R7} ;定义寄存器列表LoReg
…
STMFD SP!,LoReg ;保存寄存器列表LoReg
…
CN
CN 为一个协处理器的寄存器定义名称。
伪指令格式:
name CN expr
其中:name 要定义的协处理器的寄存器名称。
expr 协处理器的寄存器编号,数值范围为0~15。
伪指令应用举例如下:
MemSet CN l ;将协处理的寄存器l 名称定义为MemSet
CP
CP 为一个协处理器定义的名称。
伪指令格式:
name CP expr
其中:name 要定义的协处理器名称。
expr 协处理器的编号,数值范围为0~15。
伪指令应用举例如下:
DivRun CN 5 ;将协处理器5 名称定义为DivRun
DN、SN
DN 和SN 为VFP 的寄存器的名称定义的伪指令。
DN 为一个双精度原VFP 寄存器定义名称。
SN 为一个单精度的VFP 寄存器定义名称。
伪指令格式:
name DN expr
name SN expr
其中:name 要定义的VFP 寄存器名称。
expr 双精度的VFP 寄存器编号为0~15,单精度的VFP 寄存器编号为0~31。
伪指令应用举例如下:
cdn DN 1 ;将VFP 双精度寄存器1 名称定义为cdn
rex SN 3 ;将VFP 单精度寄存器3 名称定义为rex
FN
FN 为一个FPA 浮点寄存器定义名称
伪指令格式:
name FN expr
其中:name 要定义的浮点寄存器名称。
expr 浮点寄存器的编号,值为0~7。
伪指令应用举例如下:
ibq FN l ;将浮点寄存器l 名称定义为ibq
A.4.2 数据定义伪指令
数据定义伪指令用于数据表定义,文字池定义,数据空间分配等。该类伪指令如下:
声明一个文字池:LTORG。
定义一个结构化的内存表的首地址:MAP。
定义结构化内存表中的一个数据域:FIELD。
分配一块内存空间,并用0 初始化:SPACE。
分配一段字节的内存单元,并用指定的数据初始化:DCB。
分配一段字的内存单元,并用指令的数据初始化:DCD 和DCDU。
分配一段字的内存单元,将每个单元的内容初始化为该单元相对于静态基址寄存器的偏移量:DCDO。
分配一段双字的内存单元,并用双精度的浮点数据初始化:DCFD 和DCFDU。
分配一段字的内存单元,并用单精度的浮点数据初始化:DCFS 和DCFSU。
分配一段字的内存单元,并用单精度的浮点数据初始化,指定内存单元存放的是代码,而不是数据:DCI。
分配一段双字的内存单元,并用64 位整数数据初始化:DCQ 和DCQU。
分配一段半字的内存单元,并用指定的数据初始化:DCW 和DCWU。
LTORG
LTORG 用于声明一个文字池,在使用LDR 伪指令时,要在适当的地址加入LTORG 声明文字池,这样就会把要加载的数据保存在文字池内,再用ARM 的加载指令读出数据。(若没有使用LTORG 声明文字池,则汇编器会在程序末尾自动声明。)
伪指令格式:
LTORG
伪指令应用举例如下:
…
LDR R0,=0x12345678
ADD R1,R1,R0
MOV PC,LR
LTORG ;声明文字池,此地址存储0x12345678
… ;其它代码
LTORG 伪指令常放在无条件跳转指令之后,或者子程序返回指令之后,这样处理器就不会错误地将文字池中的数据当作指令来执行。
MAP
MAP 用于定义一个结构化的内存表的首地址。此时,内存表的位置计数器{VAR}设置为该地址值{VAR}为汇编器的内置变量。^与MAP 同义。
伪指令格式:
MAP expr,{base_register}
其中:expr 数字表达式或程序中的标号。当指令中没有
base_register 时,expr 即为结构化内存表的首地址。
base_register 一个寄存器。当指令中包含这一项时,结构化内存表的首地址为expr 与base_register 寄存器值的和。
伪指令应用举例如下:
MAP 0x00,R9 ;定义内存表的首地址为R9
Timer FIELD 4 ;定义数据域Timer,长度为4 字节
Attrib FIELD 4 ;定义数据域Attrib,长度为4 字节
String FIELD 100 ;定义数据域String,长度为100 字节
…
ADR R9,DataStart ;设置R9 的值,即设置结构化的内存表地址
LDR R0,Atrrib ;相当于LDR,R0,[R9,#4]
…
MAP 伪指令和FIELD 伪指令配合使用,用于定义结构化的内存表结构。MAP 伪指令中的base-register 寄存器的值对于其后所有的FIELD 伪指令定义的数据域是默认使用的,直到遇到新的包含base-register 项的MAP 伪指令。
FIELD
FIELD 用于定义一个结构化内存表中的数据域。#与FIELD 同义。
伪指令格式:
{tabel} FIELD expr
其中:label 当指令中包含这一项时,label 的值为当前内存表的位置计数器{VAR}的值,汇编编译器处理了这条FIELD 伪指令后,内存表计数器的值将加上expr。
expr 表示本数据域在内存表中所占用的字节数。
伪指令应用举例如下:
MAP 0x40003000 ;内存表的首地址为0x40003000
count1 FIELD 4 ;定义数据域count1,长度为4 字节
count2 FIELD 4 ;定义数据域count2,长度为4 字节
count3 FIELD 4 ;定义数据域count3,长度为4 字节
LDR R1,count1 ;R1=[0x40003000+0x00]
STR R1,count2 ;[0x40003000+0x00]=R1
MAP、FIELD 伪指令仅仅是定义数据结构,它们并不实际分配内存单元。
SPACE
SPACE 用于分配一块内存单元,并用0 初始化。%与SPACE 同义。
伪指令格式:
{label} SPACE expr
其中:label 内存块起始地址标号。
expr 所要分配的内存字节数。
伪指令应用举例如下:
AREA DataRA,DATA,READWROTE ;声明一数据段,名为DataRAM
DataBuf SPACE 1000 ;分配1000 字节空间
DCB
DCB 用于分配一段字节内存单元,并用伪指令中的expr 初始化。一般可用来定义数据表格,或文字符串。=与DCB 同义。
伪指令格式:
{label} DCB expr{,expr}{,expr}…
其中:label 内存块起始地址标号。
expr 可以为-128~255 的数值或字符串。内存分配的字节数由expr 个数决定。
伪指令应用举例如下
DISPTAB DCB 0x33,0x43,0x76,0x12
DCB -120,20,36,55
ERRSTR DCB “Send,data is error!”,0
DCD 和DCDU
DCD 用于分配一段字内存单元,并用伪指令中的expr 初始化。DCD 伪指令分配的内存需要字对齐,一般可用来定义数据表格或其它常数。&与DCD 同义。
DCDU 用于分配一段字内存单元,并用伪指令中的expr 初始化。DCD 伪指令分配的内存不需要字对齐,一般可用来定义数据表格或其它常数。
伪指令格式:
{label} DCD expr{,expr}{,expr}…
{label} DCDU expr{,expr}{,expr}…
其中:label 内存块起始地址标号。
expr 常数表达式或程序中的标号。内存分配字节数由expr 个数决定。
伪指令应用举例如下:
Vectors
LDR PC,ReserAddr
LDR PC,UndefinedAddr
…
ResetAddr DCD Reset
UndefinedAddr DCD Undefined
…
Reset
…
Undefined
…
DCDO
DCDO 用于分配一段字内存单元。并将每个单元的内容初始化为该单元相对于静态基址寄存器的偏移量。DCDO 伪指令作为基于静态基址寄存器R9 的偏移量分配内存单元。DCDO 伪指令分配的内存需要字对齐。
伪指令格式:
{label} DCDO expr{,expr}{,expr}…
其中:label 内存块起始地址标号。
expr 地址偏移表达式或程序中的标号。内存分配的字数由expr 个数决定。
伪指令应用举例如下:
IMPORT externsym
DCDO externsym ;分配32 位的字单元,其值为标号externsym 基于R9 的偏移
DCFD 和DCFDU
DCFD 用于分配一段双字的内存单元,并用双精度的浮点数据fpliteral 初始化。每个双精度的浮点数占据两个字单元。DCFD 伪指令分配的内存需要字对齐。
DCFDU 具有DCFD 同样的功能,但分配的内存不需要字对齐。
伪指令格式:
{label} DCFD fpliteral{,fpliteral}{,fpliteral}…
{label} DCFDU fpliteral{,fpliteral}{,fpliteral}…
其中:label 内存块起始地址标号。
fpliteral 双精度的浮点数。
伪指令应用举例如下:
DCFD 2E30,-3E-20
DCFDU -.1,1000,2.1E18
DCFS 和DCFSU
DCFS 用于分配一段字的内存单元,并用单精度的浮点数据fpliteral 初始化。每个单精度的浮点数占据一个字单元。DCFD 伪指令分配的内存需要字对齐。
DCFSU 具有DCFS 同样的功能,但分配的内存不需要字对齐。
伪指令格式:
{label} DCFS fpliteral{,fpliteral}{,fpliteral}…
{label} DCFSU fpliteral{,fpliteral}{,fpliteral}…
其中:label 内存块起始地址标号
fpliteral 单精度的浮点数。
伪指令应用举例如下:
DCFS 1.1E2,-1.3E10,0.0999
DCI
在ARM 代码中,DCI 用于分配一段字节的内存单元,用指定的数据expr 初始化。指定内存单元存放的是代码,而不是数据。
在Thumb 代码中,DCI 用于分配一段半字节的内存单元,用指定的数据expr 初始化。指定内存单元存放的是代码,而不是数据。
伪指令格式:
{label} DCI expr
其中:label 内存块起始地址标号。
expr 可为数字表达式。
DCI 伪指令和DCD 伪指令非常类似,不同之处在于DCI 分配的内存中的数据被标识为指令。可用于通过宏指令业定义处理器不支持的指令。
伪指令应用举例如下:
MACRO ;宏定义(定义NEWCMN Rd,Rn 指令)
NEWCMN $Rd,$Rm ;宏名为NEWCMN,参数为Rd 和Rm
DCI 0xe16a0e20:OR:($Rd:SHL:12):OR:$Rm
MEND
DCQ 和DCQU
DCQ 用于分配一段双字的内存单元,并用64 位的整数数据literal 初始化。DCQ 伪指令分配的内存需要字对齐。
DCQU 具有DCQ 同样的功能,但分配的内存不需要字对齐。
伪指令格式:
{label} DCQ {-}literal{,{-}{literal}}…
{label} DCQU {-}literal{,{-}{literal}}…
其中:label 内存块起始地址标号。
literal 64 位的数字表达式。取值范围为0~264-1 当literal前有“.”号时,取值范围为-263~-1 之间。
伪指令应用举例如下:
DCQU 1234,-76568798776
DCW 和DCWU
DCW 用于分配一段字的内存单元,并用指定的数据expr 初始化。DCW 伪指令分配的内存需要字对齐。
DCWU 具有DCW 同样的功能,但分配的内存不需要字对齐。
伪指令格式:
{label} DCW expr{,expr}{,expr}…
{label} DCWU expr{,expr}{,expr}…
其中:label 内存块起始地址标号。
expr 数字表达式,取值范围为-32768~65535。
伪指令应用举例如下:
DCW -592,123,6756
报告伪指令
报告伪指令用于汇编报告指示。该类伪指令如下:
断言错误:ASSERT。
汇编诊断信息显示:INFO。
设置列表选项:OPT。
插入标题:TTL 和SUBT。
ASSERT
ASSERT 为断言错误伪指令。在汇编编译器对汇编程序的第二遍扫描中,如果其中
ASSERT 条件不成立,ASSERT 伪指令将报告该错误信息。
伪指令格式:
ASSERT Logical_expr
其中:Logical_expr 用于断言的逻辑表达式
伪指令应用举例如下:
ASSERT Top<>Temp ;断言Top 不等于Temp
INFO
汇编诊断信息显示伪指令,在汇编器处理过程中的第一遍扫描或第一遍扫描时报告诊断信息。
伪指令格式:
INFO numeric_expr,string_expr
其中:numeric_expr 数据表达式。若值为0,则在第一遍扫描时报告诊断信息。否则在第一遍扫描时报告诊断信息。
strint_expr 要显示的字串
伪指令应用举例如下:
INFO 0,”Version 0。1” ;在第二遍扫描时,报告版本信息
if cont1 > cont2 ;如果cont1 > cont2
INFO 1,”cont1 > cont2” ;则在第一遍扫描时报告”cont1 > cont2”
OPT
设置列表选项伪指令。通过OPT 伪指令可以在源程序中设置列表选项。
伪指令格式:
OPI n
其中n 所设置的选项的编码如下:
1 设置常规列表选项
2 关闭常规列表选项
4 设置分页符,在新的一页开始显示
8 将行号重新设置为0
16 设置选项,显示SET、GBL、LCL 伪指令
32 设置选项,不显示SET、GBL、LCL 伪指令
64 设置选项,显示宏展开
128 设置选项,不显示宏展开
256 设置选顶,显示宏调用
512 设置先项,不显示宏调用
1024 设置选顶,显示第一遍扫描列表
2048 设置选项,不显示第一遍扫描列表
4096 设置选项目,显示条件汇编伪指令
8192 设置选项,不显示条件汇编伪指令
16384 设置选项,显示MEND 伪指令
32768 设置选项,不显示MEND 伪
默认情况下,-list 选项生成常规的列表文件,包括变量声明,宏展开,条件汇编伪指令及MEND 伪指令,而且列表文件只是在第二遍扫描时给出,通过OPT 伪指令,可以在源程序中改变默认的选项。
伪指令应用举例如下:
… ;代码
OPT 512 ;不显示宏调用
… ;代码
TTL 和SUBT
TTL 和SUBT 为插入标题伪指令。
TTL 伪指令在列表文件的每一页的开头插入一个标题。该TTL 伪指令的作用在其后的每一页,直到遇到新的TTL 伪指令。
SUBT 伪指令在列表文件的每页的开头第一个子标题。该SUBT 伪指令的作用在其后的每一页,直到遇到新的SUBT 伪指令。
伪指令格式:
TTL title
SUBT subtitle
其中:title 标题名。
subtitle 子标题名。
伪指令应用举例如下:
…
TTL mainc
…
SUBT subc con
…
A.4.3 汇编控制伪指令
汇编控制伪指令用于条件汇编、宏定义、重复汇编控制等。该类伪指令如下:
条件汇编控制: IF、ELSE 和ENDIF
宏定义: MACRO 和MEND
重复汇编: WHILE 及WEND
IF、ELSE 和ENDIF
IF 、ELSE 和ENDIF 伪指令能够根据条件把一段代码包括在汇编程序内或将其排除在程序之外。
[与IF 同义,|与ELSE 同义,]与ENDIF 同义。
伪指令格式:
IF logical_expr
;指令或伪指令代码段1
ELSE
;指令或伪指令代码段2
ENDIF
其中:logical_expr 用于控制的逻辑表达式。若条件成立,则代码段落在汇编源程序中有效。若条件不成立,代码段1 无效,同时若使用ELSE 伪指令,代码段有效。
伪指令应用举例如下:
…
IF {CONFIG}=16
BNE __rt_udiv_1
LDR R0,=__rt_div0
BX R0
ELSE
BEQ __rt_div0
ENDIF
IF、ELSE 和ENDIF 伪指令是可以嵌套使用的。
MACRO 和MEND
MACRO 和MEND 伪指令用于宏定义。MACRO 标识宏定义的开始,MEND 标识宏定义久的结束。用MACRO 及MEND 定义的一段代码,称为宏定义体。这样在程序中就可以通过宏指令多次调用该代码段。
伪指令格式:
MACRO
{$label} macroname {$parameter} {$parameter}…
;宏定义体。
MEND
其中:$label 宏指令被展开时,label 可被替换成相应的符号,通常为一个标号在一个符号前使用$表示被汇编时将使用相应的值替代$后的符号。
macroname 所定义的宏的名称。
$parameter 宏指令的参数。当宏指令被展开时将被替换成相应的值,类似于函数中的形式参数。
对于子程序代码比较短,而需要传递的参数比较多的情况下可以使用汇编技术。首先要用MACR 和MEND 伪指令定义宏,包括宏定义体代码。在MACRO 伪指令之后的第一行声明宏的原型,其中包含该宏定义的名称,及需要的参数。在汇编程序中可以通过该宏定义的名称来调用它。当源程序被汇编时,汇编编译器将展开每个宏调用,用宏定义体代替源程序中的宏定义的名称,并用实际的参数值代替宏定义时的形式参数。
伪指令应用举例如下:
MACRO
CSI_SETB ;宏名为CSI_SETB,无参数
LDR R0,=rPDATG ;读取GPG0 口的值
LDR R1,[R0]
ORR R1,R1#0x01 ;CSI 置位操作
STR R1,[R0] ;输出控制
MEND
带参数的宏定义如程序清单:
MACRO
$IRQ_Label HANDLER $IRQ_Exception
EXPORT $IRQ_Label
IMPORT $IRQ_Exception
$IRQ_Label
SUB LR,LR,#4
STMFD SP!,{R0-R3,R12,LR}
MRS R3,STSR
STMFD SP!,{R3}
…
MEND
WHIL 和WEND
WHILE 和WEND 伪指令用于根据条件重复汇编相同的或几乎相同的一段源程序。
伪指令格式:
WHILE logical_expr
;指令或伪指令代码段
WEND
其中:logical_expr 用于控制的逻辑表达式。若条件成立,则代码段在汇编源程序中有效,并不断重复这段代码直到条件不成立。
伪指令应用举例如下:
WHILE no<5
no SETA no+1
…
WEND
WHILE 和WEND 伪指令是可以嵌套使用的。
A.4.5 杂项伪指令
杂项伪指令在汇编编程设计较为常用,如段定义伪指令,入口点设置伪指令,包含文件伪指令,标号导出或引入声明等,该类伪指令如下:
边界对齐: ALIGN。
段定义: AREA。
指令集定义: CODE16 和CODE32。
汇编结束: END。
程序入口: ENTRY。
常量定义: EQU。
声明一个符号可以被其它文件引用:EXPORT 和GLORBAL。
声明一个外部符号:IMPORT 和EXTERN。
包含文件:GET 和INCLUDE。
包含不被汇编的文件:INCBIN。
保留符号表中的局部符号:KEEP。
禁止浮点指令:NOFP。
指示两段之间的依赖关系:REQUIRE。
堆栈8 字节对准:PEQUIRE8 和PRESERVE8。
给特定的寄存器命名:RN。
标记局部标号使用范围的界限:ROUT。
ALIGN
ALIGN 伪指令通过添加补丁字节使当前位置满足一定的对齐方式。
伪指令格式:
ALIGN {expr{,offset}}
其中:expr 数字表达式,用于指定对齐的方式。取值为2 的n 次幂,如1、2、4、8等,不能为0 其没有expr,则默认为字对齐方式。
offset 数字表达式,当前位置对齐到下面形式的地址处:offset+n*expr
在下面的情况中,需要特定的地址对齐方式:
(A)Thumb 伪指令ADR 要求地址是字对齐的。而Thumb 代码中地址标号可能不是字对齐的。这时就要使用伪指令ALIGN4 使Thumb 代码中地址标号为字对齐。
(B)由于有些ARM 处理器的Cache 采用了其他对齐方式。如16 字节对齐方式,这时使用ALIGN 伪指令指定合适的对齐方式可以充分发挥Cache 的性能优势。
(C)LDRD 和STRD 指令要求存储单元为8 字节对齐。这样在为LDRD/STRD 指令分配的存储单元前要使用伪指令ALIGN8 实现8 字节对齐方式。
(D)地址标号通常自身没有对齐要求,而在ARM 代码中要求地起标号对齐是字对齐的,Thumb 代码中要求半字对齐。这样可以使用ALIGN4 和ALIGN2 伪指令来调整对齐方式。
伪指令应用举例如下。
通过ALIGN 伪指令使程序中的地址标号字对齐:
AREA Example,CODE,READONLY ;声明代码段Example
START LDR R0,=Sdfjk
…
MOV PC,LR
Sdfjk DCB 0x58 ;定义一字节存储空间,字对齐方式被破坏
ALIGN ;声明字对齐
SUBI MOV R1,R3 ;其它代码
…
MOV PC,LR
在段定义AREA 中,也可使用ALIGN 伪指令对齐,但表达式的数字含义是同的
AREA MyStack,DATA,NOINIT,ALIGN=2 ;声明数据段
;MyStack,并重新字对齐
IrqStackSpace SPACE IRQ_STACK_LEGTH*4 ;中断模式堆栈空间
FiqStackSpace SPACE FIQ_STACK_LEGTH*4 ;快速中断模式堆栈空间
AbtStackSpace SPACE ABT_STACK_LEGTH*4 ;中止义模式堆栈空间
UndtStackSpace SPACE UND_STACK_LEGTH*4 ;未定义模式堆栈
…
将两个字节的数据放在同一个字的第一个字节和第四个字节中,带offset 的ALIGN对齐:
AREA offsetFxample, CODE
DCB 0x31 ;第一个字节保存0x31
ALIGN 4,3 ;字对齐
DCB 0x32 ;第四个字节保存0x32
…
AREA
AREA 伪指令用于定义一个代码段或数据段。ARM 汇编程序设计采用分段式设计,一个ARM 源程序至少需要一个代码段,大的程序可以包含多少个代码段及数据段。
伪指令格式:
AREA sectionname{,attr}{,attr}…
其中:sectionname 所定义的代码段或数据段的名称。如果该名称是以数据开头的,则该名称必须用“|”括起来,如|1_datasec|。还有一些代码段具有的约定的名称。如|text|表示C 语言编译器产生的代码段或者与C 语言库相关的代码段。
attr 该代码段或数据段的属性。
在AREA 伪指令中,各属性之间用逗号隔开。以下为段属性及相关说明:
ALIGN = expr。默认的情况下,ELF 的代码段和数据段是4 字节对齐的,expr 可以取0~31 的数值,相应的对齐方为2expr 字节对齐。如expr=3 时为字节对齐。对于代码段,expr 不能为0 或1。
ASSOC = section。指定与本段相关的ELF 段。任何时候连接section 段也必须包括sectionname 段。
DODE 为定义代码段。默认属性为READONLY。
COMDEF 定义一个通用的段。该段可以包含代码或者数据。在其它源文件中,同名的COMDEF 段必须相同。
COMMON 定义一个通用的段。该段不包含任何用户代码和数据,连接器将其初始化为此。各源文件中同名的COMMON 段共用同样的内存单元,连接器为其分配合适的尺寸。
DATA 为定义段。默认属性为READWRITE。
NOINIT 指定本数据段仅仅保留了内存单元,而没有将各初始写入内存单元,或者将内存单元值初始化为0。
READONLY 指定本段为只读,代码段的默认属性为READONLY。
READWRITE 指定本段为可读可写。数据段的默认属性为READWRITE。
使用AREA 伪指令将程序分为多个ELF 格式的段,段名称可以相同, 这时同名的段被放在同一个ELF 段中。
伪指令应用举例如下:
AREA Example ,CODE,READNOLY ;声明一个代码,名为Example
CODE16 和CODE32
CODE16 伪指令指示汇编编译器后面的指令为16 位的Thumb 指令。
CODE32 伪指令指示汇编编译器后面的指令为32 位的ARM 指令。
伪指令格式:
CODE16
CODE32
CODE16 和CODE32 伪指令只是指示汇编编译器后面的指令的类型,伪指令本身并不进行程序状态的切换。要进行状态切换,可以使用BX 指令操作。
伪指令应用举例如下:
AREA Example CODE,READONLY
CODE32
…
使用CODE16 和CODE32 定义Thumb 指令及ARM 指令并用BX 指令进行切换。
CODE16 和CODE32 的使用:
AREA ArmThumC,CODE,READONLY
CODE32
ADR R0,ThumbStart+1
BX R0
CODE16
ThumbStart
MOV R0,#10
…
END
END
END 伪指令用于指示汇编编译器源文件已结束。每一个汇编源文件均要使用一个END 伪指令,指示本源程序结束。
伪指令格式:
END
ENTRY
ENTRY 伪指令用于指定程序的入口点。
伪指令格式:
ENTRY
一个程序(可以包含多个源文件)中至少要有一个ENTRY,可以有多个ENTRY。但一个源文件中最多只有一个ENTRY。
伪指令应用举例如下。
AREA, Example, CODE,READNOLY
ENTRY
CODE32
START MOV R1,#0x5F
…
EQU
EQU 伪指令为数字常量,基于寄存器的值和程序中的标号定义一个名称。*与EQU同义。
指令格式:
name EQU expr{,type}
其中:name 要定义的常量的名称。
expr 基于寄存器的地址值,程序中的标号,32 位地址常量或32 位常量。
type 当expr 为32 位常量时,可用type 指示expr 表示的数据类型。如下示例:
CODE16
CODE32
DATA
EQU 伪指令的作用类似于C 语言中的#define。用于为一个常量定义名称。
伪指令应用举例如下:
T_bit EQU 0x20 ;定义常量T_bit,其值为0x20
PLLCON EQU 0xE01FC080 ;定义寄存器PLLCON,地址为0Xe01F080
ABCD EQU label+8 ;定义ABCD 为label+8
EXPORT 和GLOBAL
EXPORT 声明一个符号可以被其它文件引用。相当于声明了一个全局变量。
GLOBAL 与EXPORT 相同
指令格式:
EXPORT symbol{[WEAK]}
GLOBAL symbol{[WEAK]}
其中:symbol 要声明的符号名称
[WEAK] 声明其它的同名符优先于本符号被引用。
伪指令应用举例如下:
EXPORT InitStack
GLOBAL Vectors
IMPORT 和EXTERN
IMJPORT 伪指令指示编译器当前的符号不是在本源文件中定义的,而是在其他源文件中定义的,在本源文件中可能引用该符号。
EXTERN 与IMPORT 相同
指令格式:
IMPORT symbol{[WEAK]}
EXTERN symbol{[WEAK]}
其中:symbol 要声明的符号名称。
[WEAK] 指定该选项后,如果symbol 在所有的源程序中都没有被定义,编译器也不会生任何错误信息,同时编译器也不会到当前没有被INCLUDE 进来库中去查找该标号。
使用IMPORT 或EXTERN 声明外部标号时,若连接器在连接处理时不能解释该符号,而伪指令中没有[WEAK]选项,则连接器会报告错误,若伪指令中有[WEAK]选项,则连接器不会报告错误,而是进行下面的操作:
(A)如果该符号被B 或者BL 指令引用,则该符号被设置成下一条指令的地址,该B 或者BL 指令相当于一条NOP 指令。
(B)其它情况下该符号被设置0。
伪指令应用举例如下:
IMPORT InitStack
EXTERN Vectors
GET 和INCLUDE
GET 伪指令将一个源文件包含到当前源文件中,并将被包含的文件在当前位置进行汇编处理。INCLUDE 与GFT 同义。
指令格式:
GET filename
INCLUDE filename
其中:filename 要包含的源文件名,可以使用路径信息。
GET 伪指令通常用于包含一些宏定义或常量定义的源文件。如用EQU 定义的常量,用MAP 和FIELD 定义的结构化的数据类型,这样的源文件类似于C 语言中的头文件,GET、INCLUDE 伪指令不能用来包含目标文件,而INCBIN 伪指令可以包含目标文件。
伪指令应用举例如下:
INCLUDE LPC2106.inc
INCBIN
INCBIN 伪指令将一个文件包含到当前源文件中,而被包含的文件不进行汇编处理。
指令格式:
INCBIN filename
其中:filename 要包含的源文件名,可以使用路径信息。
通常可以使用INCBIN 将一个执行文件或者任意数据包含到当前文件中,被包含的执行文件或数据将被原封不动地放下当前文件中,编译器从INCBIN 伪指令后面开始继续处理。
伪指令应用举例如下:
NCBIN charlib。bin
KEEP
KEEP 伪指令指示编译器保留符号表中的局部符号。
伪指令格式:
KEEP {symbol}
其中:symbol 要保留的局部标号。若没有此项,则除了基于寄存器处的所有符号将包含在目标文件的符号表中。
NOFP
NOFP 伪指令用于禁止源程序中包含浮点运算指令。
伪指令格式:
NOFP
REQUIRE
REQUIRE 伪指令指定段之间的依赖关系。
伪指令格式:
REQUIRE label
其中:label 所需要的标号的名称。
当进行链接处理时,包含了REQUIRE label 伪指令的源文件,则定义label 的源文件也被包含。
PEQUIRE8 和PRESERVE8
PEQUIRE8 伪指令指示当前文件请求堆栈为8 字节对齐。
PRESERVE8 伪指令指示当前文件保持堆栈为8 字节对齐。
伪指令格式:
PEQUIRE8
PRESERVE8
链接器保证要求8 字节对齐的堆栈只能被堆栈为8 字的对齐的代码调用