ARM:从裸机到操作系统07 —— ARM汇编简介
ARM汇编语言程序相关知识
ARM编程基础
本章将简要介绍ARM汇编语言程序相关知识。
ARM汇编简介
由于C语言的简洁高效,嵌入式开发大部分代码采用C语言编写。但是,在需要高度优化代码、编写编译器或需要使用 C 语言无法直接提供的低级功能时,仍需汇编代码来支持。在编写启动代码、设备驱动程序或进行操作系统开发时,可能需要汇编代码。最后,在调试 C 语言程序时,尤其是在理解汇编指令与 C 语言语句之间的映射关系时,阅读反汇编代码是非常有用的。
如需更详细的 ARM 汇编语言说明,可参考
《ARM Compiler Toolchain Assembler Reference》
《ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition》
一、伪操作(directive)
ARM汇编语言程序是由机器指令、伪指令和伪操作组成的。
伪操作不像机器指令那样在计算机运行期间由机器执行,它是在汇编程序对源程序汇编期间由汇编程序处理的。
在ARM的汇编程序中,伪操作主要有:
● 符号定义(Symbol Definition)伪操作。
● 数据定义(Data Definition)伪操作。
● 汇编控制(Assembly Control)伪操作。
● 其他(Miscellaneous)伪操作。
1.1 符号定义伪操作
符号定义(Symbol Definition)伪操作用于定义ARM汇编程序中的变量,对变量进行赋值以及定义寄存器名称。包括以下伪操作:
● GBLA、GBLL及GBLS:声明全局变量。
● LCLA、LCLL及LCLS:声明局部变量。
● SETA、SETL及SETS:给变量赋值。
● RLIST:为通用寄存器列表定义名称。
● CN:为协处理器的寄存器定义名称。
● CP:为协处理器定义名称。
● DN及SN:为VFP的寄存器定义名称。
● FN:为FPA的浮点寄存器定义名称。
1.2 数据定义伪操作
数据定义伪操作用于数据表定义、文字池定义、数据空间分配等。常用的伪操作有:
● LTORG:声明一个数据缓冲池(Literal Pool)的开始。
● MAP:定义一个结构化的内存表(Storage Map)的首地址。
● FIELD:定义结构化的内存表中的一个数据域(Field)。
● SPACE:分配一块内存单元,并用0初始化。
● DCB:分配一段字节的内存单元,并用指定的数据初始化。
● DCD及DCDU:分配一段字的内存单元,并用指定的数据初始化。
● DCDO:分配一段字的内存单元,并将各单元的内容初始化成该单元相对于静态基值寄存器的偏移量。
● DCFD及DCFDU:分配一段双字的内存单元,并用双精度的浮点数据初始化。
● DCFS及DCFSU:分配一段字的内存单元,并用单精度的浮点数据初始化。
● DCI:分配一段字节的内存单元,用指定的数据初始化,指定内存单元中存放的是代码,而不是数据。
● DCQ及DCQU:分配一段双字的内存单元,并用64位的整数数据初始化。
● DCW及DCWU:分配一段半字的内存单元,并用指定的数据初始化。
● DATA:在代码段中使用数据。现已不再使用,仅用于保持向前兼容。
1.3 汇编控制伪操作
汇编控制伪操作用于条件汇编、宏定义、重复汇编控制等,常用的伪操作有:
● IF、ELSE和ENDIF:根据条件把一段源程序代码包括在汇编程序内或排除在程序之外。
● WHILE和WEND:根据条件重复汇编相同的源程序代码。
● MACRO和MEND:MACRO标识宏定义的开始,MEND标识宏定义结束。用MACRO和MEND定义一段代码,称为宏定义体,在程序中可以通过宏指令多次调用该代码段。MACRO、MEND伪操作可以嵌套使用。
● MEXIT:用于从宏定义中跳转出去。
1.4 其他的伪操作
ARM汇编中还有一些其他的伪操作,在汇编程序中经常会被使用,包括段定义伪操作、入口点设置伪操作、包含文件伪操作、标号导出伪操作或引入声明伪操作等。
● AREA:定义一个代码段或数据段。
● ALIGN:使程序当前位置满足一定的对齐方式。
● ENTRY:指定程序入口点。
● END:指示汇编源程序结束。
● CODE16和CODE32:定义16位Thumb和32位ARM指令的起始位置。
● EQU:为数字常量、基于寄存器的值和程序中的标号定义一个字符名称。
● EXPORT和GLOBAL:声明一个全局的符号,该符号可在其他的文件中引用。
● IMPORT和EXTERN:通知编译器要使用的符号名称在其他的源文件中定义,但要在当前源文件中引用。
● GET和INCLUDE:将一个源文件包含到当前的源文件中。
● INCBIN:将一个目标文件或数据文件不作任何修改地包含到当前源文件。
● RN:给特定的寄存器定义名称。
● ROUT:定义局部变量的有效范围。
二、伪指令(pseudo-instruction)
ARM中的伪指令并不是真正的ARM或Thumb指令,这些伪指令在汇编编译器对源程序进行汇编处理时被替换成对应ARM或Thumb指令(或指令序列)。ARM伪指令包括ADR、ADRL、LDR和NOP。
2.1 ADR (小范围的地址读取伪指令)
该指令将基于PC的地址值或基于寄存器的地址值读取到寄存器中。
ADR{cond} register, expr
参数说明:
● cond为可选的指令执行的条件。
● register为目标寄存器。
● expr为基于PC或者基于寄存器的地址表达式,其取值范围如下。
◆ 当地址值不是字对齐时,其取值范围为-255~255。
◆ 当地址值是字对齐时,其取值范围为-1020~1020。
◆ 当地址值是16字节对齐时,其取值范围将更大。
2.2 ADRL(中等范围的地址读取伪指令)
该指令将基于PC或基于寄存器的地址值读取到寄存器中。ADRL伪指令比ADR伪指令可以读取更大范围的地址。
ADRL{cond} register, expr
参数说明:
● cond为可选的指令执行的条件。
● register为目标寄存器。
● expr为基于PC或者基于寄存器的地址表达式,其取值范围如下。
◆ 当地址值不是字对齐时,其取值范围为-64KB~64KB。
◆ 当地址值是字对齐时,其取值范围为-256KB~256KB。
◆ 当地址值是16字节对齐时,其取值范围将更大。
2.3 LDR(大范围的地址读取伪指令)
LDR伪指令将一个32位的常数或者一个地址值读取到寄存器中。
LDR{cond} register, =[expr|label-expr]
参数说明:
● cond为可选的指令执行的条件。
● register为目标寄存器。
● expr为32位的常量。
● label-expr为基于PC的地址表达式或者是外部表达式。
2.4 NOP(空操作伪指令)
NOP伪指令在汇编时,将被替换成ARM中的空操作,比如,可能为MOV R0和R0等。
NOP
三、ARM汇编语句的格式
3.1 语句格式
{symbol} {instruction|directive|pseudo-instruction} {; comment}
其中的符号及参数说明如下:
● instruction:指令。在ARM汇编语言中,指令不能从一行的行头开始。在一行语句中,指令的前面必须有空格或者符号。
● directive:伪操作。
● pseudo-instruction:伪指令。
● symbol:符号。在ARM汇编语言中,符号必须从一行的行头开始,并且符号中不能包含空格。在指令和伪指令中,符号用作地址标号(label);在有些伪操作中,符号用作变量或者常量。
● comment:语句的注释。在ARM汇编语言中,注释以分号(;)开头。注释的结尾即为一行的结尾。注释也可以单独占用一行。
在ARM汇编语言中,各个指令、伪指令及伪操作的助记符必须全部用大写字母,或者全部用小写字母,不能在一个伪操作助记符中既有大写字母又有小写字母。
源程序中,语句之间可以插入空行,让源代码的可读性更好。
如果一条语句很长,为了提高可读性,可以将该长语句分成若干行来写。这时,在一行的末尾用“\”表示下一行将续在本行之后。注意,在“\”之后不能再有其他字符,空格和制表符也不能有。
3.2 符号
在ARM汇编语言中,符号(Symbols)可以代表地址(Addresses)、变量(Variables)和数字常量(Numeric Constants)。
当符号代表地址时,又称为标号(Label)。当标号以数字开头时,其作用范围为当前段(没有使用ROUT伪操作时),这种标号又称为局部标号(Local Label)。符号包括变量、数字常量、标号和局部标号。
符号的命名规则如下:
● 符号由大小写字母、数字以及下划线组成。
● 局部标号以数字开头,其他的符号都不能以数字开头。
● 符号是区分大小写的。
● 符号中的所有字符都是有意义的。
● 符号在其作用范围内必须惟一,即在其作用范围内不可有同名的符号。
● 程序中的符号不能与系统内部变量或者系统预定义的符号同名。
● 程序中的符号通常不要与指令助记符或者伪操作同名。当程序中的符号与指令助记符或者伪操作同名时,可用双竖线将符号括起来,如||require||,这时双竖线并不是符号的组成部分。
- 变量
在ARM汇编语言中,变量有数字变量、逻辑变量和串变量3种类型。
数字变量的取值范围为数字常量和数字表达式所能表示的数值的范围。
逻辑变量的取值范围为{true}及{false}。
串变量的取值范围为串表达式可以表示的范围。
- 数字常量
数字常量是32位的整数。当作为无符号整数时,其取值范围为0到232-1;当作为有符号整数时,其取值范围为-231~231-1。
- 汇编时的变量替换
如果在串变量前面有一个$字符,且这种串变量包含在另一个串中,则汇编时,编译器将用该串变量的数值取代该串变量。
对于数字变量来说,如果该变量前面有一个$字符,在汇编时,编译器将该数字变量的数值转换成十六进制的串,然后用该十六进制的串取代$字符后的数字变量。
对于逻辑变量来说,如果该逻辑变量前面有一个$字符,在汇编时编译器将该逻辑变量替换成它的取值(T或者F)。
如果程序中需要字符$,则用$$来表示。
通常情况下,包含在两个竖线“|”之间的$并不表示进行变量替换。但是如果竖线是在双引号内,则将进行变量替换。使用“.”来表示变量名称的结束。
- 标号
标号是表示程序中的指令或者数据地址的符号。
(1)基于PC的标号
基于PC的标号是位于目标指令前或者程序中数据定义伪操作前的标号。这种标号在汇编时将被处理成PC值加上(或减去)一个数字常量。它常用于表示跳转指令的目标地址,或者代码段中所嵌入的少量数据。
(2)基于寄存器的标号
基于寄存器的标号通常用MAP和FILED伪操作定义,也可以用EQU伪操作定义。这种标号在汇编时将被处理成寄存器的值加上(或减去)一个数字常量。它常用于访问位于数据段中的数据。
(3)绝对地址
绝对地址是一个32位的数字量。它可以寻址的范围为0~232-1,即直接可以寻址整个内存空间。
- 局部标号
局部标号主要用于在局部范围使用。它由两部分组成:开头是一个0~99之间的数字;后面紧接一个通常表示该局部标号作用范围的符号。
局部标号的作用范围通常为当前段,也可以使用伪操作ROUT来定义局部标号的作用范围。
局部标号定义的语法格式如下:
N{routname}
其中:
● N为0~99之间的数值。
● routname为符号,通常为该标号作用范围的名称(用ROUT伪操作定义的)。
局部标号引用的语法格式如下:
%{F|B}{A|T} N{routname}
其中:
● N为局部标号的数字号。
● routname为当前作用范围的名称(是用ROUT伪操作定义的)。
● %表示引用操作。
● F指示编译器只向前搜索。
● B指示编译器只向后搜索。
● A指示编译器搜索宏的所有嵌套层次。
● T指示编译器搜索宏的当前层次。
如果F和B都没有指定,编译器先向前搜索,再向后搜索。
如果A和T都没有指定,编译器搜索所有从当前层次到宏的最高层次,比当前层次低的层次不再搜索。
如果指定了routname,编译器向前搜索最近的ROUT伪操作,若routname与该ROUT伪操作定义的名称不匹配,编译器报告错误,汇编失败。
四、汇编语言的程序结构
段(section)是ARM汇编语言组织源文件的基本单位。段是是独立的、具有特定名称的、不可分割的指令序列或数据序列。段分为代码段和数据段,代码段存放执行代码,数据段存放代码执行时需要的数据。一个ARM汇编程序至少需要一个代码段,大的程序可以包含多个代码段和数据段。
ARM汇编语言源程序经过汇编后生成可执行的映像文件,格式有axf、bin等。可执行的映像文件通常包括三个部分:
● 1个或多个代码段,代码段通常为只读。
● 0个或多个包含初始值的数据段,通常为可读写。
● 0个或多个不包含初始值的数据段,通常为可读写。
链接器根据一定的规则将各个段安排到RAM的相应位置。源程序中段之间的相对位置与可执行的映像文件中段之间的相对位置不一定相同。
更多推荐
所有评论(0)