ARM汇编语言
基本特征
RISC,精简指令集。CISC的指令比较复杂,通常可以解码为对应的一系列内部操作序列(microcode),RISC相比之下指令更简单,有更高的能耗比。
无法直接对内存值进行操作,需要通过Load-Store操作进行嫁接。提供了条件按执行和块复制能力。
指令格式方面,第一个操作数一般表示目的地,除了LoadStore作为特例。
ARMv7时32位load/store架构处理器,字长32位。
Thumb
Thumb指令集在ARM7TDMI处理器中首次添加,只支持16位指令,用性能换来了更小的程序大小。
Thumb2扩展了Thumb指令来实现对16位和32位的混合,从而在性能和程序大小之间取得了平衡。
CPSR中T=0表示处于ARM指令集状态,T=1表示处于Thumb状态。
GNU汇编器
|
|
可以进行保留debug信息的汇编。
|
|
可以进行链接。
arm-none-eabi-gdb和arm-none-eabi-insight用来调试
识别源代码目标指令集和目标汇编器
- UAL中汇编指令立即数的#号可以忽略
- UAL中条件语句需要被IT指令立刻处理
- UAL中
.thumb, .arm
用来标记代码是arm还是thumb - 16bitThumb汇编通常有两个操作数,只有branch可以条件执行。
GNU汇编器语法
每行汇编源码的格式是label: instruction @ comment
label:
表示对该行的地址进行标记,从而使改行可以作为跳转目标。instruction
可以是ARM汇编指令,也可以是对于汇编器的指令。@
后的内容均会被忽略,是注释。
当链接时,如果源代码中没有显式指定入口点,则可以在命令行中主动指定。
汇编器指令
所有指令以.
开始
一个程序至少要包含一个.text
代码段,读写的数据可以保存在.data
段中,只读常量保存在.rodata
段,零初始化常量可以保存在.bss
中。
-
.align n
在data段中用0填充,在代码段中用NOP填充,从而让下一个位置和$2^n$字节对齐 -
.ascii "string"
插入ascii字符串,多个字符串之间可以用逗号分隔 -
asciiz
与ascii相同,以0结尾 -
.byte xxx
,.hword xxx
,.word xxx
-
.data
让接下来的生命放置在可执行文件的data段中 -
.end
标记源代码文件的结束,汇编器不对之后的内容进行解析 -
.equ symbol, expression
用expression代替symbol -
.extern symbol
表明symbol在其他源代码文件中定义 -
.global symbol
表明symbol是所有原代码文件中可见的 -
.include "filename"
从filename中添加所有内容到当前文件。 -
.text
将接下来的内容放置在输出文件中的text段。所有汇编指令位于其中。
表达式
- 十进制
- 0x前缀十六进制
- 0b前缀二进制
- 单引号包围的ASCII字符
- 使用标准数学运算和逻辑运算对绝对常量或地址标记进行计算的结果
ARM汇编语言语法
寄存器
- R0-R15
- SP(R13)
- FP(R11)
- LR(R14)
- PC(R15)
- PSR寄存器
切换arm/thumb模式
改变CPSR中T位可以切换模式。
BX/BLX可以清空流水线以避免错误解码,对PC的LDR/POP/LDM也可以达成清空流水线的目的。BX/BLX跳转目标的第零位可以指定目标地址的指令集是ARM还是Thumb。
指令分类
- 数据处理操作
- 内存访问操作
- 控制流操作
- 系统操作
指令基础
ARM指令集中,12位立即数,即-2048到2047的立即数范围,ARM中被划分为8bit的常数和4bit的步长为2的向右移位,编译器需要负责正确转化立即数,汇编器会检查立即数错误。
例如,0x2300_0000可以表示为8位的0x23和4位的0x4,0x4表示向右位移4*2=8个位置。
- MOV32:MOVW将16位常数移动到低16位,MOVT则是高十六位,构成该伪指令。
LDR Rn, =<constant>
:通过使用代码段的字符池或者MOV、MVN来实现加载常量。手动指定.ltorg
可以指定字符池的放置位置。ADR Rn, =label
和ADRL Rn, =label
可以让汇编器通过相对当前PC的加减操作向寄存器中存入label代表的地址。
ARM中几乎所有指令都可以是条件执行的,而非其他指令集中只有跳转可以条件执行,这样可以在较小的范围内避免分支跳转。
如下是一个示例,向R2中存入R1、R0中较小的那个。虽然更短,但是对于较新的处理器这样做会更慢,因为分支预测可以做的更快。
|
|
对于数据处理操作,PSR比较位会在以S为后缀的指令中被设置。
IT指令提供了最多四条指令的条件执行。
运算指令
常见指令
Operation{cond}{S} Rd, Rn, Operand2
cond表示条件执行
s表示是否写入CPSR
指令 | 参数 | 作用 |
---|---|---|
ADD | Rd,Rn,Op2 | |
MOV | Rd,Op2 | |
RSB | Rd,Rn,Op2 | Rd = Op2-Rn,用于被减数是常数 |
SUB | Rd,Rn,Op2 | |
AND | Rd,Rn,Op2 | |
ORR | Rd,Rn,Op2 | |
CMP | Rn,Op2 | |
CMN | Rn,Op2 | |
TEQ | Rn, Op2 | |
BIC | Rd,Rn,Op2 | Rd=Rn&(~Op2) |
第一个操作数必须是寄存器,第二个操作数可以是立即数/寄存器/寄存器的移位
移位实现
因此,ARM不需要任何单独的移位操作。寄存器的移位表示为Rm, shift #x
,其中shift可以是LSL逻辑移位向左,LSR裸机移位向右,ASR算数移位向右,ROR循环移位向右,RRX循环移位扩展。
一个移位操作Rn=Rn»2可以表示为MOV Rn, Rn, LSR #2
乘法操作可以被优化为移位。除法亦可以进行优化。
乘法操作
乘法操作没有办法用立即数作为操作数,只能对两个寄存器操作。
指令 | 参数 | 作用 |
---|---|---|
MLA | Rd, Rn, Rm, Ra | Rd=Ra+(Rn*Rm) |
MLS | Rd, Rn, Rm, Ra | Rd=Ra-(Rn*Rm) |
MUL | Rd, Rn, Rm | Rd = Rn*Rm |
(S/U)(MLA/MUL)L | RdLo, RdHi, Rn, Rm | signed/unsigned RdHiLo saved result |
SIMD操作
本质上是在32位寄存器中对16/8位等运算进行并行化处理。
对于SIMD操作,前缀通常有U、S、Q,分别代表无符号、有符号和饱和运算。其中饱和运算指的是如果大于最大则结果等于最大值。
绝对差相加
USADA8,对Rn、Rm的四个字节分别求绝对差并相加存到Rd
打包、解包
将高低十六位放到不同的寄存器。
UXTH Rd, RN将Rn低十六位存到Rd
PKHBT,Rd,Rn,Rm将Rn低16,Rm高16合并存放到Rd中。
饱和操作
内存操作
LDR、STR执行load、store操作。
可以用B指定字节,H指定半字,D指定双字。
S可以指定有符号,进行符号扩展,没有S则进行0扩展。
寻址方式
- 寄存器寻址
- 寄存器+偏移寻址:
LDR Rd, [Rn, Rm]
,LDR Rd, [Rn, #n]
- 寄存器+偏移寻址+写回:
LDR Rd, [Rn, Op2]!
Load后将Rn+Op2写回Rn - 寄存器+写回:
LDR Rd, [Rn], #n
,Load后及将Rn+n写回Rn
连续读取内存
设定一系列寄存器,保存连续读取到的内存值。通过这种方式可以减少地址加减操作的开销。
LDMIA R10!, {R0-R1, R3}
将R10所指位置开始的三个字节依次存到R0、R1、R3中,并因为!
可选地将R10递增3*4个字节。
LDM(I/D)(A/B)表示在当前地址之前(B)之后(A)递增(I)递减(D)
FD/FA/ED/EA后缀以堆栈角度设定方向。
跳转操作
B、BL与MIPS相同。想要切换指令集,使用BX、BLX。
不建议直接更改PC来实现跳转,但是使用LDR、LDM、POP等操作符从内存中读取到PC确实可以更改程序流。
Thumb下有CMP和B合并为的单指令跳转,分为CBZ和CBNZ表示零或非零时条件跳转。同时,有根据表中保存的相对PC位置跳转的指令TBB和TBH。
其他指令
协处理器指令
最多有16个协处理器可以被实现(CP0-CP15).
他们既可以存在于处理器之内,也可以连接到外部处理器。并非所有CortexA处理器支持这些操作。
CP15控制核心特性,包括缓存和MMU。CP14控制硬件debug功能。CP10、11提供对浮点和NEON硬件的访问。
如果指令执行,但是正确的协处理器不在系统中存在,则会发生指令未定义异常。
指令列表:
指令 | 作用 |
---|---|
CDP | 初始化协处理器数据处理操作 |
MRC | move to register from CP |
MCR | 和MRC相反 |
LDC | Load to CP |
STC | Store from CP |
—— | |
MRRC | 从CP中存入一对寄存器 |
MCCR | 从一对寄存器存入CP |
LDCL | 从多个寄存器读取一个CP寄存器? |
STCL | 向多个寄存器写入一个CP寄存器? |
SVC(supervisor call)
最初被称为SWI(Software Interrupt),产生一个异常,由操作系统处理。
PSR操作指令
指令 | 作用 |
---|---|
MRS | move to register from PSR |
MSR | 将可以更新的位设置为给定寄存器中的值 |
CPS | change processor state |
SETEND | 修改一个cpsr中的Ebit,切换大小端地址 |
bit操作指令
指令 | 作用 |
---|---|
BFI | bit field insert,将一个寄存器的几个连续bit插入到另一个寄存器中 |
BFC | clear,清除寄存器中的连续bit |
SBFX/UBFX | 将相邻的位从一个寄存器拷贝到另一个并符号/无符号扩展到32位 |
RBIT | 将寄存器所有位调换顺序 |
cache预加载
指令 | 作用 |
---|---|
PLD | PreLoad Data |
PLI | PreLoad Instration |
反转字节序
指令 | 作用 |
---|---|
REV | 字内字节序反转 |
REV16 | 两个半字 |
REVSH | 低半字并符号扩展 |
其他指令
指令 | 作用 |
---|---|
BKPT | 使核心进入debug模式。 |
WFI | 释放核心,等待中断唤醒。 |
NOP | do nothing |
WFE | 释放核心,等待事件。 |
SEV | 产生唤醒事件,唤醒同一簇中其他核心。 |