美文网首页程序员ARM硬件架构 我爱编程
ARM汇编器的规则和预处理指令

ARM汇编器的规则和预处理指令

作者: 赵国开 | 来源:发表于2018-06-11 19:43 被阅读13次

现在最新的32位的ARM/THUMB汇编语言叫做UAL(Unified Assembler Language,统一的汇编语言),已经取代了早期版本的ARM/THUMB汇编语言。用UAL编写的代码可以被汇编为 ARM, Thumb/Thumb-2指令。最新的一些工具反汇编你的代码之后可能都是用UAL来表示了,但是仍然有大量的代码是用旧格式来编写。


汇编语法规则

汇编文件中源码行的一般格式如下:

{label} {instruction|directive|pseudo-instruction} {;comment}

大括号内的每个字段都是可选的。

  • label其实是对内存中的一个地址的命名,最终会转化为一个数字值,在编码时你只需在代码中用label即可剩下的交给编译器来处理。

注意:label在代码中只能定义一次,并且需要从行首顶格开始(当然也有一些编译器支持在任意位置放置label,这种需要用分隔符进行分隔,比如冒号)

  • {instruction|directive|pseudo-instruction}跟在label之后,和label之间要有一个或者多个空格或者有tab键隔开(就算前面没有label的话也要这样,否则会被认为是label,一个新手ARM汇编程序员常犯的错误就是没有label时把instruction|directive|pseudo-instruction顶格写)

  • instruction是具体的ARM汇编指令,例如ARM7TDMI的ARM指令集如下图所示:


    image.png

对大多数的instruction语句(也有一些是例外)一般的格式如下:

instruction destination, source, source

source有多种格式,可以包括数字,寄存器,寄存器旋转等等

  • directive(英文翻译过来也是指令的意思,有的书把它翻译为保留字),从功能上来说其实有点像编译器的预处理命令,不同编译器的directive并不一样。比如下面的ARM官方的keil(使用的是armasm编译器)和TI 的CSS常用的directive。
image.png
  • pseudo-instruction,直白翻译过来就是伪指令,它可以像instruction出现在汇编源码行中,但编译器在遇到pseudo-instruction会进一步进行转化和解释把它用其他的instruction进行代替,为什么要这么麻烦呢,主要的目的是减轻汇编编写者的负担,通过汇编器自动去处理而不是手动去处理,从而提高编程效率和降低编程犯错空间。

  • comment,注释,以分号';'开始到行结束


常用的directive

这里以armasm编译器中常用的directive进行说明。

  • AREA

功能:定义一个数据块/代码块
语法格式如下:
AREA sectionname{,attr}{,attr}…
sectionname是该段的名称。名字可以随便取,但是如果名字是以数字开头的话需要用一对竖线把名字括起来,比如, |1_DataArea|; 不这样会语法报错。

常用的一些attr(属性)

ALIGN = expr 让该段以2的expr次方的边界对齐。比如, expr = 10,则该段以1K为对齐边界。
CODE 该字段是机器代码(默认是READONLY)
DATA该字段是数据 (默认是READWRITE)
READONLY 该字段放在只读内存 ( CODE段的默认值)
READWRITE 该字段放在可读可写内存 (DATA段的默认值)

例子:

AREA Example,CODE,READONLY ; 一个example代码段.
; code

  • RN

功能:可以理解为为寄存器定义一个符号别名
语法格式如下:
name RN expr
expr取值范围:0~15

例子:

dest RN 0 ;

  • EQU

功能:为常量指定符号名称或者说把符号和常量等同起来(类似C语言的#define预处理命令)
语法格式:
name EQU expr{,type}

其中name是该分配给该值的符号名称,expr可以是一个寄存器相关的地址,程序相关的地址,绝对的地址,32位的整数常量都可以。type是可选的,可以是下面的任意一个:
ARM
THUMB
CODE16
CODE32
DATA

例子:

xyz EQU label+8;把(label+8)这个地址分配给符号xyz
fiq EQU 0x1C, CODE32 ;把0X1C这个绝对地址分配给fiq,并把它标记为code

  • ENTRY

功能:为程序声明一个入口点,你的程序至少含有一个ENTRY点,否则在链接时会发出警告。
语法格式:
ENTRY

例子:

AREA ARMex, CODE, READONLY
ENTRY ; 应用程序的入口点

  • DCB, DCW, DCD

功能:分配内存并指定初始值,DCB以字节为单位进行存储, DCW{U}以半字(2字节)为单位,DCD{U}以字为单位(4字节),DCW{U}/DCD{U}带U表示不考虑内存对齐,用DCW需对齐到半字,DCD对齐到字,其他没有差别。

语法格式:
{label} DCB expr{,expr}…
{label} DCW{U} expr{,expr}…
{label} DCD{U} expr{,expr}...

其中expr是一个数值表达式或带引号的字符串(字符串的字符连续的存储在内存中),如果是数值的话DCB的取值范围是−128~255,DCW是 −32768~65535。上面说的对齐,比如DCD可在第一个定义的字前面最多填充3个字节以实现4字节对齐,如果不需要对齐就使用DCDU。DCB是以字节为对齐的,如果后面有跟汇编指令的话最好在后面用ALIGN来对齐一下,确保汇编指令对齐到正确的内存位置。

例子1:

与C中的字符串不同,ARM汇编器字符串不是NULL终止的。 您可以使用DCB构造一个以NULL结尾的字符串,如下所示:

C_string DCB "C_string",0

如果这个字符串从内存中的地址0x4000开始,它在内存中应该是这样

0x4000 43 C
0x4001 5F _
0x4002 73 s
0x4003 74 t
0x4004 72 r
0x4005 69 i
0x4006 6E n
0x4007 67 g
0x4008 00

例子2

coeff DCW 0xFE37, 0x8ECC ;定义了两个半字
DATA1 DCD 1,2,3;定义了3个字1,2,3
DATA2 DCD DATA1+4;定义了一个字包含DATA1地址+4

AREA mydata,DATA,READWRITE
DCB 255;现在未对齐
DATA3 DCDU 3,4,5;定义了3个字包含3,4,5未对齐到字

  • ALIGN

功能:把当前位置数据/代码通过填充0对齐到特定的内存边界
语法格式:
ALIGN {expr{,offset}}

当前的位置被对齐到下一个地址的方式如下:
offset + n * expr

其中offset可以是任何数值表达式,n是汇编器为了最小化填充而选择的任何整数,expr可以是数值表达式但算出的结果必须是一个2的次幂,最小为2的零次方,最大为2的31次方。
如果未指定expr这些的话,ALIGN是把当前位置设置到下一个字(4字节)边界处。

例子1:

AREA OffsetExample, CODE

;下面两字节是在同一个字内,字的第一个字节和第4个字节,n=0,
;第二个DCB距第一个DCB的偏移offset是3

DCB 1
ALIGN 4,3
DCB 1

例子2:

start LDR r6, = label1

; code
MOV pc,lr
label1 DCB 1 ; pc现在不是对齐的
ALIGN ; 确保subroutine1能正确寻址到下面的指令
subroutine1 MOV r5, #0x5

  • SPACE

功能:预留特定大小的零值内存块。一般用来为子程序预留变量,表,存储数据空间等。

语法格式:
{label} SPACE expr
其中expr表达式的结果为要预留的总字节数。

例子:

AREA MyData, DATA, READWRITE
data1 SPACE 255 ; 定义了255个字节的零值存储空间

  • MACRO和MEND

功能:这两个组合用来一起定义一个宏。
语法格式:
MACRO
{$label} macroname{$cond} {$parameter{,$parameter}…}
; code
MEND

其中label参数一般用来做标签的替换符号,在宏被调用时进行替换。macroname是宏的名字。cond是一个特殊的参数被设计用来包含一个条件码,然而他也允许使用有效条件码以外的值。parameter参数在宏被调用时进行对应的参数替换,可以用$parameter="default value"来给该参数赋值默认值。

你可以把这些参数理解为变量,在宏内部你可以像变量一样的使用$label,$cond,$parameter,但是你必须使用$作为开头来引用这些变量以区分普通符号。当然这些参数都是可选的。

例子:

; 宏的定义
MACRO
$label xmac $p1,$p2
; code
$label.loop1 ; code
; code
BGE $label.loop1
$label.loop2 ; code
BL $p1
BGT $label.loop2
; code
ADR $p2
; code
MEND

; 宏的调用
abc xmac subr1,de

下面是宏展开的样子

; code
abcloop1 ; code
; code
BGE abcloop1
abcloop2 ; code
BL subr1
BGT abcloop2
; code
ADR de
; code

  • LTORG

功能:指示汇编器立即汇编当前的文字池。
语法格式:
LTORG

汇编器会在每个代码段的末尾汇编当前的文字池。何为代码段的末尾,即以AREA(作为下一代码段开始)或者END作为分割点。为什么需要LTORG,因为有时代码太长了,如果有些伪指令(比如LDR)使用默认的文字池(放在代码段末尾)会超出查找范围而出错。使用LTORG就可以确保文字池在有效的范围内被汇编。需要注意的是LTORG必须放在无条件的分支跳转或者子程序还回汇编指令之后。这样处理器就不会把文字池的常量当作汇编指令来执行。汇编器会按字对齐文字池中的数据。

例子:

AREA Example, CODE, READONLY
start BL func1
func1 ; 函数体
; code
LDR r1,=0x55555555 ; 伪指令转化为汇编=> LDR R1, [pc, #相对文字池1的偏移量]
; code
MOV pc,lr ; 函数结束

LTORG ; 文字池1包含文字 &55555555.

data SPACE 4200 ; 预留了4200个零值的内存空间
END ; 默认的文字池,现在是空的.

备注:如果注释掉LTORG,编译出错,提示文字池太远了,因为用SPACE分配了4200的空间导致末尾的默认文字池离LDR超过了4K的访问,所以需要用LTORG来立即生成一个文字池来存放对应的常量。

  • END

功能:告诉汇编器,你已经在文件的末尾了。
语法格式:
END


armasm汇编器常用的运算符

A:MOD:B A对B取模
A:ROL:B 对A循环左移B位
A:ROR:B 对A循环右移B位
A:SHL:B 或者 A << B 对A左移B位
A:SHR:B or A >> B 对A右移B位
A + B 把A加到B
A - B 把B从A中减去
A:AND:B 对A和B执行按位与
A:EOR:B 对A和B执行按位异或
A:OR:B 对A和B执行按位或

例子:

MOV r0,#1:SHL:33
MOV R1,#1:SHR:1
MOV R2,#1:ROL:33
MOV R3,#1:ROR:1
MOV R4,#5:MOD:3
MOV R5,#0XFF:EOR:0XF0
MOV R6,#0XF0:OR:0X0F


参考文献

【1】ARM Assembly Language Fundamentals and Techniques (2nd ed.)
【2】DUI0801I_armasm_user_guide

相关文章

  • ARM汇编器的规则和预处理指令

    现在最新的32位的ARM/THUMB汇编语言叫做UAL(Unified Assembler Language,统一...

  • 汇编指令-收藏方便查阅

    一: 汇编指令格式 ARM 汇编器的基本语法,这与 GCC汇编器 的语法有所不同,整体编译的流程如下所示; 二: ...

  • 汇编指令-收藏方便查阅

    一: 汇编指令格式 ARM 汇编器的基本语法,这与 GCC汇编器 的语法有所不同,整体编译的流程如下所示; 二: ...

  • C# 预处理器指令

    预处理器指令 用于指示编译器如何对待处理源代码。 通用规则 预处理器指令的某些最重要的句法规则如下: 预处理器指令...

  • 预处理指令与typedef

    1.预处理指令 什么是预处理指令:在我们的文件翻译成0和1之前做的操作我们称之为预处理指令一般情况预处理指令都是以...

  • 预处理指令 什么是预处理指令:在我们的文件翻译成0和1之前做的操作我们称之为预处理指令一般情况预处理指令都是以#号...

  • C语言 枚举类型和预处理指令

    枚举的定义 预处理指令 所有的预处理指令都是以#开头 预处理指令分为三种 宏定义 条件编译 文件包含 预处理指令在...

  • 面向对象(三十三)-预处理指令

    什么是预处理指令预处理器指令指导编译器在实际编译开始之前对信息进行预处理。预处理指令注意点所有的预处理器指令都是以...

  • 预处理指令(宏定义)

    目录 [toc] 预处理指令 什么是预处理指令: 在我们的文件翻译成0和1之前做的操作我们称之为预处理指令 一般情...

  • 预编译

    预处理指令 在我们的文件编译成0和1之前做得操作我们称之为预处理指令 一般情况下,预处理指令都是以 # 号开头 预...

网友评论

    本文标题:ARM汇编器的规则和预处理指令

    本文链接:https://www.haomeiwen.com/subject/bdoheftx.html