×
嵌入式 > 嵌入式开发 > 详情

ARM汇编语言中的伪操作(一)

发布时间:2020-08-27 发布时间:
|
伪操作(derective)是ARM汇编语言程序里的一些特殊的指令助记符,其作用主要是为完成汇编程序做各种准备工作,在源程序运行汇编程序处理,而不是在计算机运行期间有机器执行.也就是说,这些伪操作只是汇编过程中起作用,一旦汇编结束,伪操作的使命也就随之消失.

符号定义( Symbol Definition )伪操作
符号定义伪操作用于定义 ARM 汇编程序中的变量、对变量赋值以及定义寄存器的别名。
包括以下伪操作:
— 用于声明全局变量 GBLA 、 GBLL 和 GBLS 。
— 用于声明局部变量 LCLA 、 LCLL 和 LCLS 。
— 用于对变量赋值 SETA 、 SETL 、 SETS 。
— 为通用寄存器列表定义名称 RLIST 。

—为协处理器的寄存器定义名称 CN
— 为协处理器定义名称 CP
— 为VFP的寄存器定义名称 DN、SN
— 为FPA的浮点寄存器定义名称 FN

1、 GBLA、GBLL 和GBLS
语法格式:
GBLA ( GBLL 或 GBLS ) 全局变量名
GBLA 、 GBLL 和 GBLS 伪操作用于声明一个 ARM 程序中的全局变量,并将其初始化。其中:
GBLA 伪操作用于声明一个全局的算术变量,并初始化为 0 ;
GBLL 伪操作用于声明一个全局的逻辑变量,并初始化为 {FALSE};
GBLS 伪操作用于声明一个全局的字符串变量,并初始化为空串;
全局变量名在其作用范围内必须唯一。 如果用这些伪操作重新声明已经声明过的变量,则变量的值将 被初始化成后一次声明语句中的值。
使用示例:
GBLA Test1 ;声明一个全局的算术变量,变量名为 Test1
Test1 SETA 0xaa ;将该变量赋值为 0xaa
SPACE Test1 ;引用该变量

GBLL Test2 ;声明一个全局的逻辑变量,变量名为 Test2
Test2 SETL {TRUE} ;将该变量赋值为真
GBLS Test3 ;声明一个全局的字符串变量,变量名为 Test3
Test3 SETS “ Testing ” ;将该变量赋值为 “ Testing ”

2、 LCLA、LCLL 和LCLS
语法格式:
LCLA ( LCLL 或 LCLS ) 局部变量名
LCLA 、 LCLL 和 LCLS 伪操作用于声明一个 ARM 程序中的局部变量,并将其初始化。其中:
LCLA 伪操作用于声明一个局部的数字变量,并初始化为 0 ;
LCLL 伪操作用于声明一个局部的逻辑变量,并初始化为 {FALSE};
LCLS 伪操作用于声明一个局部的字符串变量,并初始化为空串;
使用示例:
MACRO ;声明一个宏

$label message $a ;宏的原型

LCLS err ;声明一个局部串变量err

err SETS “error no: “ ;向该变量赋值

$label ;代码

INFO 0, “err”:CC: :STR: $a ;使用该串变量

MEND ;宏定义结束

3、 SETA、SETL 和SETS
语法格式:
变量名 SETA ( SETL 或 SETS ) 表达式
伪操作 SETA 、 SETL 、 SETS 用于给一个ARM程序中的变量赋值。
SETA 伪操作用于给一个算术变量赋值;
SETL 伪操作用于给一个逻辑变量赋值;
SETS 伪操作用于给一个字符串变量赋值;
其中,变量名为已经定义过的全局变量或局部变量,表达式为将要赋给变量的值。 在向变量赋值前,必须先声明该变量。

4 、 RLIST
语法格式:
名称 RLIST { 寄存器列表 }
RLIST 伪操作用于对一个通用寄存器列表定义名称,定义的名称可在LDM/STM指令中使用。在 LDM/STM 指令中,寄存器列表中的寄存器的访问次序总是先访问编号较低的寄存器,再访问编号较高的寄存器,而不管寄存器列表中各寄存器的排列顺序。

使用示例:
RegList RLIST {R0-R5 , R8 , R10} ;将寄存器列表名称定义为 RegList ,可在 ARM 指令 LDM/STM中通过该名称访问寄存器列表。


5 、 CN

语法格式:

name CN expr

其中:name 是该寄存器的名称

expr为协处理器的寄存器的编号,数值范围为0~15。

CN伪操作用于给一个协处理器的寄存器定义名称。方便程序员记忆该寄存器的功能

示例

Power CN 6 ;将协处理器的寄存器6名称定义为Power

注:以下的伪操作类似(可参考CN)

数据定义( Data Definition )伪操作
数据定义伪操作包括以下伪操作:

LTORG 声明一个数据缓冲池的开始;

MAP 定义一个结构化的内存表首地址;

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

SPACE 分配一块内存单元,并用0初始化;

DCB 分配一段字节的内存单元,并用指定的数据初始化;

DCD及DCDU 分配一段字的内存单元,并用指定的数据初始化;

DCDO 分配一段字的内存单元,并将每个单元的内容初始化成该单元相对于基态值的寄存器的偏移量;

DCFD及DCFDU 分配一段双字的内存单元,并用双精度的浮点数据初始化;

DCFS及DCFSU 分配一段字的内存单元, 并用单精度的浮点数据初始化;

DCI 分配一段字节的内存单元,用指定的数据初始化,指定内存单元中存放的是代码,而不是数据;

DCQ及DCQU 分配一段双字的内存单元,并用64位的整数数据初始化;

DCW及DCWU 分配一段半字的内存单元,并用指定的数据初始化;

DATA 在代码段中使用数据(现在已不再使用,仅用于保持向前兼容)。

以下主要介绍几种常用的:
1、 DCB (“ = ”)
语法格式:
标号 DCB 表达式
用于分配一段字节内存单元并用伪操作中指定的表达式初始化。其中,表达式可以为 -128~ 255 的数值或字符串。
使用示例:
Str DCB “ This is a test ! ” ;分配一片连续的字节存储单元并初始化。

2、DCD “ &”(或DCDU)
语法格式:
标号 DCD (或 DCDU ) 表达式
用于分配一段字内存单元并用伪操作中指定的表达式初始化。其中,表达式可以为程序中的标号或数字表达式。

用DCD 分配的字存储单元是字对齐的,而用 DCDU 分配的字存储单元并不严格字对齐。
使用示例:
DataTest DCD 4,5,6 ;其值分别为4,5和6。
data2 DCD memaddr+4 ;分配一个字单元,其值为程序中标号memaddr加4个字节
3、DCQ(或DCQU)
语法格式:
标号 DCQ (或 DCQU ) 表达式
用于分配一段以8个字节为单位的内存并用伪操作中指定的表达式初始化。
用 DCQ 分配的存储单元是字对齐的,而用 DCQU 分配的存储单元并不严格字对齐。
使用示例:
DataTest DCQ -255,2_101 ;2_101指的是二进制的101

4、 SPACE (“ % ”)
语法格式:
标号 SPACE 表达式
用于分配一块内存单元,并初始化为 0 。其中,表达式为要分配的字节数。
使用示例:
DataSpace SPACE 100 ;分配连续 100 字节的存储单元并初始化为 0 。

5、 MAP (“ ^ ”)
语法格式:
MAP 表达式 { ,基址寄存器 }
用于定义一个结构化的内存表的首地址。
表达式可以为程序中的标号或数字表达式,基址寄存器为可选项,当基址寄存器选项不存在时,表达式的值即为内存表的首地址,当该选项存在时,内存表的首地址为表达式的值与基址寄存器的和。
MAP 伪操作通常与 FIELD 伪操作配合使用来定义结构化的内存表。
使用示例:
MAP 0x100 , R9;定义结构化内存表首地址的值为 0x100 + R9 。

6、 FILED (“ # ” )
语法格式:
标号 FIELD 表达式
用于定义一个结构化内存表中的数据域。
表达式的值为当前数据域在内存表中所占的字节数。
FIELD 伪操作常与 MAP 伪操作配合使用来定义结构化的内存表结构。 MAP 伪操作定义内存表的首地址, FIELD 伪操作定义内存表中的各数据域的字节长度,并可以为每个数据域指定一个标号供其他的指令引用。
注意 MAP 和 FIELD 伪操作仅用于定义数据结构,并不实际分配存储单元。
示例1:
下面的伪操作序列定义一个内存表,其首地址为固定地址4096,该内存表中包括5个数据域:consta长度为4个字节;constb长度为4个字节;x长度为8个字节;y长度为8个字节;string长度为256个字节。这种内存表称为基于绝对地址的内存表。

MAP 4096 ;内存表的首地址为4096(0x1000)

consta FIELD 4 ;consta长度为4个字节,相对位置为0

constb FIELD 4 ;constb长度为4个字节,相对位置为5000

x FIELD 8 ;x长度为4个字节,相对位置为5004

y FIELD 8 ;y长度为4个字节,相对位置为5012

string FIELD 256 ;string长度为256字节,相对位置为5020

;在指令中可以这样引用内存表中的数据域:

LDR R6,consta

上面的指令仅仅可以访问LDR指令前面(或后面)4KB地址范围的数据域

示例2:

下面的伪操作序列定义一个内存表,其首地址为0,该内存表中包括5个数据域:consta长度为4个字节;constb长度为4个字节;x长度为8个字节;y长度为8个字节;string长度为256个字节。这种内存表称为基于相对地址的内存表。

MAP 4096 ;内存表的首地址为0

consta FIELD 4 ;consta长度为4个字节,相对位置为0

constb FIELD 4 ;constb长度为4个字节,相对位置为4

x FIELD 8 ;x长度为4个字节,相对位置为8

y FIELD 8 ;y长度为4个字节,相对位置为16

string FIELD 256 ;string长度为256字节,相对位置为24

;可以通过下面的指令方便地访问地址范围超过4KB的数据

MOV R9,#4096

LDR R5,[R9,constb] ;将内存表中数据域constb读取到R5中

在这里,内存表中各数据域的实际内存地址不是基于一个固定地址,而是基于LDR指令执行时R9寄存器中的内容。这样通过上面方法定义的内存表结构可以在程序中有多个实例(通过在LDR指令中指定不同的基址寄存器值来实现)。通常用R9作为静态基址寄存器。

7、LTORG

用于声明一个数据缓冲池(literal pool)的开始

通常ARM汇编编译器把数据缓冲池放在代码段的最后面,即下一个代码段开始之前,或者END伪操作之前。

当程序中使用LDFD之类的指令时,数据缓冲池的使用可能越界。这时可以使用LTORG伪操作定义数据缓冲池,已越界发生。通常大的代码段可以使用多个数据缓冲池。

LTORG伪操作通常放在无条件跳转指令之后,或者子程序返回指令之后,这样处理器就不会错误地将数据缓冲池中的数据当作指令来执行。

汇编控制( Assembly Control )伪操作
汇编控制伪操作用于控制汇编程序的执行流程,常用的汇编控制伪操作包括以下几条:
— IF 、 ELSE 、 ENDIF
— WHILE 、 WEND
— MACRO 、 MEND
— MEXIT
1、 IF、ELSE、ENDIF
语法格式:
IF 逻辑表达式
指令序列 1
ELSE
指令序列 2
ENDIF
IF 、 ELSE 、 ENDIF 伪操作能根据条件把一段源代码包括在汇编程序内或者将其排除在程序之外。[是IF伪操作的同义词,|是ELSE伪操作的同义词,]是ENDIF伪操作的同义词。
IF 、 ELSE 、 ENDIF 伪指令可以嵌套使用。
使用示例:
MACRO
MOV_PC_LR
[ THUMBCODE
bx lr
|
mov pc,lr
]
MEND
2、 WHILE、WEND
语法格式:
WHILE 逻辑表达式
指令序列
WEND
WHILE 、 WEND 伪操作能根据条件重复汇编相同的或者几乎相同的一段源代码。 WHILE 、 WEND 伪操作可以嵌套使用。
使用示例:
GBLA Counter ;声明一个全局的算术变量,变量名为 counter
counter SETA 3 ;设置循环计数变量counter初始值为3
WHILE counter <=10 ;由counter控制循环执行的次数
counter SETA counter+1 ;将循环计数变量加1
;代码 ;代码
WEND

3、 MACRO、MEND
语法格式:
MACRO
[$ label] macroname{ $ parameter1, $ parameter,…… }
指令序列
MEND
MACRO伪操作标识宏定义的开始,MEND标识宏定义的结束。用MACRO及MEND定义一段代码,称为宏定义体,这样在程序中就可以通过宏指令多次调用该代码段。
其中, $ label在宏指令被展开时,label会被替换成相应的符号,通常是一个标号。在一个符号前使用$表示程序被汇编时将使用相应的值来替代$后的符号。
macroname为所定义的宏的名称。
$parameter为宏指令的参数。当宏指令被展开时将被替换成相应的值,类似于函数中的形式参数,可以在宏定义时为参数指定相应的默认值。
宏指令的使用方式和功能与子程序有些相似,子程序可以提供模块化的程序设计、节省存储空间并提高运行速度。但在使用子程序结构时需要保护现场,从而增加了系统的开销,因此,在代码较短且需要传递的参数较多时,可以使用宏汇编技术。
首先使用MACRO和MEND等伪操作定义宏。包含在 MACRO 和 MEND 之间的代码段称为宏定义体,在MACRO伪操作之后的一行声明宏的原型(包含宏名、所需的参数),然后就可以在汇编程序中通过宏名来调用它。在源程序被汇编时,汇编器将宏调用展开,用宏定义体代替源程序中的宏定义的名称,并用实际参数值代替宏定义时的形式参数。
宏定义中的$label是一个可选参数。当宏定义体中用到多个标号时,可以使用类似$label.$internallabel的标号命名规则使程序易读。
MACRO 、 MEND 伪操作可以嵌套使用。
使用示例:
MACRO
$HandlerLabel HANDLER $HandleLabel ;宏的名称为HANDLER,有1个参数$HandleLabel

$HandlerLabel
sub sp,sp,#4 ;decrement sp(to store jump address)
stmfd sp!,{r0} ;PUSH the work register to stack(lr does not push because it return to original address)
ldr r0,=$HandleLabel;load the address of HandleXXX to r0
ldr r0,[r0] ;load the contents(service routine start address) of HandleXXX
str r0,[sp,#4] ;store the contents(ISR) of HandleXXX to stack
ldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR)
MEND
;在程序中调用该宏
HandlerFIQ HANDLER HandleFIQ ;通过宏的名称HANDLER调用宏,其中宏的标号为HandlerFIQ,参数为HandleFIQ
HandlerIRQ HANDLER HandleIRQ
HandlerUndef HANDLER HandleUndef
HandlerSWI HANDLER HandleSWI
HandlerDabort HANDLER HandleDabort
HandlerPabort HANDLER HandlePabort
;程序被汇编后,宏展开的结果
HandlerFIQ
sub sp,sp,#4 ;decrement sp(to store jump address)
stmfd sp!,{r0} ;PUSH the work register to stack(lr does not push because it return to original address)
ldr r0,=HandleFIQ;load the address of HandleXXX to r0
ldr r0,[r0] ;load the contents(service routine start address) of HandleXXX
str r0,[sp,#4] ;store the contents(ISR) of HandleXXX to stack
ldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR)

b HandlerUndef ;handler for Undefined mode
b HandlerSWI ;handler for SWI interrupt
b HandlerPabort ;handler for PAbort
b HandlerDabort ;handler for DAbort
b . ;reserved
b HandlerIRQ ;handler for IRQ interrupt
b HandlerFIQ ;handler for FIQ interrupt

4、 MEXIT
语法格式:
MEXIT
MEXIT 用于从宏定义中跳转出去。
信息报告伪操作
ASSERT 断言错误伪操作;
INFO 汇编诊断信息显示伪操作;
OPT 设置列表选项伪操作;
TTL及SUBT 用于在列表文件每一项的开头插入一个标题和子标题



『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
基于CAN总线与以太网的嵌入式网关电路设计