文章目录
一、ARM处理器寻址方式
1、寄存器寻址
寄存器寻址的作数的值在寄存器中,指令中的地址码字段指出的是寄存器编号,指令执行时直接取出寄存器值来操作,例如:
MOV R1,R2 ;表示将R2寄存器的值赋值给R1寄存器中
2、立即寻址
立即寻址指令中的操作码字段后面的地址码部分即是操作数本身(这样的数称为立即数),也就是说,数据就包含在指令当中,例如:
MOV R0,#0xFF000 ;将立即数0xFF000装入R0寄存器中
3、寄存器移位寻址
寄存器移位寻址是ARM指令集特有的寻址方式。当为这种寻址方式时,第2个寄存器中的操作数在与第1个寄存器的操作数结合之前,会先进行移位操作,例如:
MOV R1,R2,LSL #3 ;R2寄存器中的值左移3位,结果放入R1寄存器中
4、寄存器间接寻址
寄存器间接寻址指令中的地址码给出的是一个通用寄存器的编号,所需的操作数保存在寄存器指定地址的存储单元中,即寄存器为操作数的地址指针,例如:
LDR R1,[R2] ;将R2寄存器中存储的地址所指向的存储单元的数据赋值到R1寄存器中
5、基址寻址
基址寻址就是将基址寄存器的内容与指令中给出的偏移量相加,形成操作数的有效地址,例如:
LDR R1,[R2,#0x0C] ;将R2寄存器中的值加上0x0C后,所得到的地址指向存储单元的内容,赋值给R1
6、多寄存器寻址
多寄存器寻址一次可传送几个寄存器值,允许一条指令传送至多16个寄存器。寄存器的顺序是按由小到大的顺序排列,连续的寄存器可用“-”连接,否则用“ , ”分隔书写,例如:
LDMIA R1!,{R2-R7,R12} ;将R1指向的单元中的数据读出到R2~R7、R12中(R1自动加4)
7、堆栈寻址
堆栈寻址是隐含的,它使用一个专门的寄存器(堆栈指针)指向一块存储区域(堆栈),指针所指向的存储单元即是堆栈的栈顶。其中堆栈分为四种类型:
- 满递增:堆栈向高地址方向增长,堆栈指针指向有效数据项增长方向的最后一个数据。
- 空递增:堆栈向高地址方向增长,堆栈指针指向有效数据项增长方向的最后一个数据的下一个位置。
- 满递减:堆栈向低地址方向增长,堆栈指针指向有效数据项增长方向的最后一个数据。
- 空递减:堆栈向低地址方向增长,堆栈指针指向有效数据项增长方向的最后一个数据的下一个位置。
具体指令有LDM、STM指令等。
8、块拷贝寻址
块拷贝寻址方式使用多寄存器传送指令将数据块从存储器的某一位置拷贝到另一位置。
具体指令有LDM、STM指令等。
9、相对寻址
相对寻址是基址寻址的一种变通。由程序计数器PC提供基准地址,指令中的地址码字段作为偏移量,两者相加后得到的地址即为操作数的有效地址。
...
BL SUBRl ;调用到SUBRl子程序
...
SUBR;
...
MOV PC,R14 ;返回
二、ARM指令集介绍
1、指令格式
ARM指令的基本格式如下:
<opcode> { <cond> } {S} <Rd> , <Rn> { , <operand2> }
其中<>号内的项是必须的,{ }号内的项是可选的。各项的说明如下:
-
opcode:指令助记符;
-
cond:执行条件;
-
S:是否影响CPSR寄存器的值;
-
Rd:目标寄存器;
-
Rn:第1个操作数的寄存器;
-
operand2:第2个操作数;
- 形式1:#immed_8r——常数表达式
MOV R0,#1 ;R0 ←1 AND R1,R2,#0x0F ;R2与0x0F,结果保存在R1 LDR R0,[R1],#-4 ;先将R1指向内存中的数存入R0之后, ;传送完成后 R1← R1-4,后变址
- 形式2:Rm——寄存器方式
SUB R1,R1,R2 ; R1-R2→R1 LDR R0,[R1],-R2 ;先将R1指向内存中的数存入R0之后, ; 传送完成后R1 ← R1- R2,后变址
- 形式3:Rm,shift——寄存器移位方式
将寄存器的移位结果作为操作数,但Rm值保持不变,移位方法如下:
操作码 说明 操作码 说明 ASR #n 算术右移n位 ROR #n 循环右移n位 LSL #n 逻辑左移n位 RRX 带扩展的循环右移1位 LSR #n 逻辑右移n位 ADD R1,R1,R1,LSL #3 ;R1=R1+R1×8 SUB R1,R1,R2,LSR #2 ;R1=R1-R2÷4
2、条件码
操作码 | 条件助记符 | 标志 | 含义 |
---|---|---|---|
0000 | EQ | Z=1 | 相等 |
0001 | NE | Z=0 | 不相等 |
0010 | CS/HS | C=1 | 无符号数大于或等于 |
0011 | CC/LO | C=0 | 无符号数小于 |
0100 | MI | N=1 | 负数 |
0101 | PL | N=0 | 正数或零 |
0110 | VS | V=1 | 溢出 |
0111 | VC | V=0 | 没有溢出 |
1000 | HI | C=1,Z=0 | 无符号数大于 |
1001 | LS | C=0,Z=1 | 无符号数小于或等于 |
1010 | GE | N=V | 有符号数大于或等于 |
1011 | LT | N!=V | 有符号数小于 |
1100 | GT | Z=0,N=V | 有符号数大于 |
1101 | LE | Z=1,N!=V | 有符号数小于或等于 |
1110 | AL | 任何 | 无条件执行 (指令默认条件) |
1111 | NV | 任何 | 从不执行(不要使用) |
3、ARM存储器访问指令
(1)单寄存器加载——LDR
助记符 | 说明 | 操作 | 条件码位置 |
---|---|---|---|
LDR Rd,addressing | 加载字数据 | Rd←[addressing],addressing索引 | LDR{cond} |
LDRB Rd,addressing | 加载无符号字节数据 | Rd←[addressing],addressing索引 | LDR{cond}B |
LDRT Rd,addressing | 以用户模式加载字数据 | Rd←[addressing],addressing索引 | LDR{cond}T |
LDRBT Rd, addressing | 以用户模式加载无符号字节数据 | Rd←[addressing],addressing索引 | LDR{cond}BT |
LDRH Rd, addressing | 加载无符号半字数据 | Rd←[addressing],addressing索引 | LDR{cond}H |
LDRSB Rd, addressing | 加载有符号字节数据 | Rd←[addressing],addressing索引 | LDR{cond}SB |
LDRSH Rd, addressing | 加载有符号半字数据 | Rd←[addressing],addressing索引 | LDR{cond}SH |
注意事项:
- LDRSB, LDRSH加载后高位需要填充符号位;
- 有符号位加载是指用符号位加载扩展到32位,无符号半字加载是指用零扩展到32位;
(2)单寄存器存储——STR
助记符 | 说明 | 操作 | 条件码位置 |
---|---|---|---|
STR Rd, addressing | 存储字数据 | [addressing]←Rd, addressing索引 | STR{cond} |
STRB Rd,addressing | 存储字节数据 | [addressing]←Rd, addressing索引 | STR{cond}B |
STRT Rd,addressing | 以用户模式存储字数据 | [addressing]←Rd, addressing索引 | STR{cond}T |
STRBT Rd,addressing | 以用户模式存储字节数据 | [addressing]←Rd, addressing索引 | STR{cond}BT |
STRH Rd,addressing | 存储半字数据 | [addressing] ←Rd, addressing索引 | STR{cond}H |
注意事项:
- 半字读写的指定地址必须为偶数,否则将产生不可靠的结果;
- 若指令有T,那么即使处理器是在特权模式下,存储系统也将访问看成是在用户模式下进行的;
(3)多寄存器存取——LDM和STM
LDM和STM指令可以实现在一组寄存器和一块连续的内存单元之间传输数据。LDM为加载多个寄存器,STM为存储多个寄存器。指令格式如下:
LDM/STM{cond}<模式> Rn{!},reglist{^}
其中后缀“ ! ”表示最后的地址写回到Rn中,后缀“ ^ ”表示进行数据传送且寄存器列表不包含PC时,加载/存储的是用户模式的寄存器(在异常模式中默认操作对象为异常模式的寄存器)。
<模式>表示地址变化规则,具体如下:
模式(数据块) | 说明 | 模式(堆栈) | 说明 |
---|---|---|---|
IA | 每次传送后地址加4 | FD | 满递减堆栈 |
IB | 每次传送前地址加4 | ED | 空递减堆栈 |
DA | 每次传送后地址减4 | FA | 满递增堆栈 |
DB | 每次传送前地址减4 | EA | 空递增堆栈 |
注意事项:
- Rn此处没有[ ],但仍然指向内存;
- 进行数据复制时,先设置好源数据指针和目标指针;
- 进行堆栈操作操作时,要先设置堆栈指针(SP);
- 存器Rn为基址寄存器,装有传送数据的初始地址,Rn不允许为R15;
- 后缀“^”不允许在用户模式或系统模式下使用;
- 若在LDM指令且寄存器列表中包含有PC时使用,那么除了正常的多寄存器传送外,将SPSR也拷贝到CPSR中,这可用于异常处理返回;
(4)寄存器和存储器交换指令
SWP指令用于将一个内存单元(该单元地址放在寄存器Rn中)的内容读取到一个寄存器Rd中,同时将另一个寄存器Rm的内容写入到该内存单元中。指令格式如下:
SWP{cond}{B} Rd,Rm,[Rn]
其中,后缀“ B ”表示交换字节,若没有则表示交换32位字。Rd用于保存从存储器中读入的数据;Rm的数据用于存储到存储器中,若Rm与Rd相同,则为寄存器与存储器内容进行交换。Rn为要进行数据交换的存储器地址。
注意事项:
- Rn不能与Rd和Rm相同
地址偏移类型:
- 零偏移: 如:LDR Rd,[Rn]
- 前索引偏移(前变址):如:LDR Rd,[Rn,#0x04]
- 后索引偏移(后变址):如:LDR Rd,[Rn],#0x04
4、ARM数据处理指令
数据处理指令只能对寄存器的内容进行操作,而不能对内存中的数据进行操作。
(1)数据传送指令
助记符 | 说明 | 操作 | 条件码位置 |
---|---|---|---|
MOV Rd,operand2 | 数据传送 | Rd←operand2 | MOV{cond}{S} |
MVN Rd,operand2 | 数据取非后传送 | Rd←(~operand2) | MVN{cond}{S} |
(2)算术逻辑运算指令
算数指令
助记符 | 说明 | 操作 | 条件码位置 |
---|---|---|---|
ADD Rd, Rn, operand2 | 加法运算指令 | Rd←Rn+operand2 | ADD{cond}{S} |
SUB Rd, Rn, operand2 | 减法运算指令 | Rd←Rn-operand2 | SUB{cond}{S} |
RSB Rd, Rn, operand2 | 逆向减法指令 | Rd←operand2-Rn | RSB{cond}{S} |
ADC Rd, Rn, operand2 | 带进位加法 | Rd←Rn+operand2+Carry | ADC{cond}{S} |
SBC Rd, Rn, operand2 | 带进位减法指令 | Rd←Rn-operand2-(NOT)Carry | SBC{cond}{S} |
RSC Rd, Rn, operand2 | 带进位逆向减法指令 | Rd←operand2-Rn-(NOT)Carry | RSC{cond}{S} |
逻辑指令
助记符 | 说明 | 操作 | 条件码位置 |
---|---|---|---|
AND Rd, Rn, operand2 | 逻辑与操作指令 | Rd←Rn & operand2 | AND{cond}{S} |
ORR Rd, Rn, operand2 | 逻辑或操作指令 | Rd←Rn | operand2 | ORR{cond}{S} |
EOR Rd, Rn, operand2 | 逻辑异或操作指令 | Rd←Rn ^ operand2 | EOR{cond}{S} |
BIC Rd, Rn, operand2 | 位清除指令(赋0) | Rd←Rn & (~operand2) | BIC{cond}{S} |
(3)比较指令
没有目的操作数,只用作更新条件标志位,不保存运算结果,指令后缀无需加S。
助记符 | 说明 | 操作 | 条件码位置 |
---|---|---|---|
CMP Rn, operand2 | 比较指令 | 标志N、Z、C、V ←Rn-operand2 | CMP{cond} |
CMN Rn, operand2 | 负数比较指令 | 标志N、Z、C、V←Rn+operand2 | CMN{cond} |
TST Rn, operand2 | 位测试指令 | 标志N、Z、C、V←Rn & operand2 | TST{cond} |
TEQ Rn, operand2 | 相等测试指令 | 标志N、Z、C、V←Rn ^ operand2 | TEQ{cond} |
5、乘法指令
助记符 | 说明 | 操作 | 条件码位置 |
---|---|---|---|
MUL Rd,Rm,Rs | 32位乘法指令 | Rd←Rm*Rs (Rd≠Rm) | MUL{cond}{S} |
MLA Rd,Rm,Rs,Rn | 32位乘加指令 | Rd←Rm*Rs+Rn (Rd≠Rm) | MLA{cond}{S} |
UMULL RdLo,RdHi,Rm,Rs | 64位无符号乘法指令 | (RdHi,RdLo) ←Rm*Rs | UMULL{cond}{S} |
UMLAL RdLo,RdHi,Rm,Rs | 64位无符号乘加指令 | (RdHi,RdLo) ←Rm*Rs+(RdHi,RdLo) | UMLAL{cond}{S} |
SMULL RdLo,RdHi,Rm,Rs | 64位有符号乘法指令 | (RdHi,RdLo) ←Rm*Rs | SMULL{cond}{S} |
SMLAL RdLo,RdHi,Rm,Rs | 64位有符号乘加指令 | (RdHi,RdLo) ←Rm*Rs+(RdHi,RdLo) | SMLAL{cond}{S} |
注意事项:
- RdHi表示结果的高32位,RdLo表示低32位;
6、ARM分支指令
分支指令跳转范围都为±32M字节,且ARM指令为字对齐,最低2位地址固定为0。
助记符 | 说明 | 操作 | 条件码位置 |
---|---|---|---|
B label | 分支指令 | PC←label | B{cond} |
BL label | 带链接的分支指令 | LR←PC-4,PC←label | BL{cond} |
BX Rm | 带状态切换的分支指令 | PC←label,切换处理器状态 | BX{cond} |
注意事项:
- BX指令,该指令可以根据跳转地址(Rm)的最低位来切换处理器状态,bit[0]=0为转为ARM状态,否则转为Thumb状态。
7、协处理器指令
助记符 | 说明 | 操作 | 条件码位置 |
---|---|---|---|
CDP coproc,opcode1,CRd,CRn, CRm{,opcode2} | 协处理器数据操作指令 | 取决于协处理器 | CDP{cond} |
LDC{L} coproc, CRd,<地址> | 协处理器数据读取指令 | 取决于协处理器 | LDC{cond}{L} |
STC{L} coproc, CRd,<地址> | 协处理器数据写入指令 | 取决于协处理器 | STC{cond}{L} |
MCR coproc,opcode1,Rd,CRn, CRm{,opcode2} | ARM寄存器到协处理器寄存器的数据传送指令 | 取决于协处理器 | MCR{cond} |
MRC coproc,opcode1,Rd,CRn, CRm{,opcode2} | 协处理器寄存器到ARM寄存器到的数据传送指令 | 取决于协处理器 | MRC{cond} |
8、杂项指令
助记符 | 说明 | 操作 | 条件码位置 |
---|---|---|---|
SWI immed_24 | 软中断指令 | 产生软中断,处理器进入管理模式 | SWI{cond} |
MRS Rd,psr | 读状态寄存器指令 | Rd←psr | MRS{cond} |
MSR psr_fields, Rd / #immed_8r | 写状态寄存器指令 | psr_fields←Rd / #immed_8r | MSR{cond} |
SWI指令用于产生软中断,从而实现从用户模式变换到管理模式,并且将CPSR保存到管理模式的SPSR中,然后程序跳转到SWI异常入口。在其它模式下也可使用SWI指令,处理器同样地切换到管理模式。
在ARM处理器中,只有MRS指令可以对状态寄存器CPSR和SPSR进行读操作。通过读CPSR可以了解当前处理器的工作状态。读SPSR寄存器可以了解到进入异常前的处理器状态。该指令不影响条件码。
在ARM处理器中,只有MSR指令可以对状态寄存器CPSR和SPSR进行写操作。与MRS配合使用,可以实现对CPSR或SPSR寄存器的读-修改-写操作,可以切换处理器模式、或者允许/禁止IRQ/FIQ中断等。
注意事项:
- psr为 CPSR或SPSR;
- Rd不允许为R15;
- 只有在特权模式下才能修改状态寄存器;
- 程序中不能通过MSR指令直接修改CPSR中的T控制位来实现ARM状态/Thumb状态的切换,必须使用BX指令完成处理器状态的切换(因为BX指令属分支指令,它会打断流水线状态,实现处理器状态切换)。
9、ARM伪指令
ARM伪指令不属于ARM指令集中的指令,是为了编程方便而定义的。伪指令可以像其它ARM指令一样使用,但在编译时这些指令将被等效的一条或多条ARM指令所代替。
(1)ADR伪指令——小范围的地址读取
ADR伪指令将基于PC相对偏移的地址值或基于寄存器相对偏移的地址值读取到寄存器中,其指令格式如下:
ADR{cond} register , expr
其中地址表达式expr的取指范围为-255~255B(字节对齐),或者-1024~1024B(字对齐)。
示例:
...
ADR R0,Delay
...
Delay
MOV R0,r14
...
注意事项:
- 通常,编译器用一条ADD指令或SUB指令来实现该ADR伪指令的功能,若不能用一条指令实现,则产生错误,编译失败。
(2)ADRL伪指令——中等范围的地址读取
比ADR伪指令可以读取更大范围的地址,其指令格式如下:
ADRL{cond} register , expr
其中地址表达式expr的取指范围为-64K~64KB(字节对齐),或者-256K~256KB(字对齐)。
注意事项:
- 通常,编译器用两条合适的指令来实现该ADRL伪指令的功能。若不能用两条指令实现,则产生错误,编译失败。
(3)LDR伪指令——大范围的地址读取
LDR伪指令用于加载32位的立即数或一个地址值到指定寄存器。若加载的常数未超出MOV或MVN的范围,则使用MOV或MVN指令代替该LDR伪指令,否则汇编器将常量放入文字池,并使用一条程序相对偏移的LDR指令从文字池读出常量,其指令格式如下:
LDR{cond} register , = expr | label_expr
示例:
LDR R2, =0xFF0 ;MOV R2, #0xFF0
LDR R0, =0xFF000000 ;MOV R0, #0xFF000000
LDR R1, =0xFFFFFFFE ;MVN R1, #0x1
注意事项:
- 从指令位置到文字池的偏移量必须小于4KB;
- 与ARM指令的LDR相比,伪指令的LDR的参数有“=”号;
- 文字池其实就是一个存储常量数据的地方,汇编器会使用文字池来在代码段中存储常量数据;
(4)NOP伪指令——空操作
NOP伪指令在汇编时将会被代替成ARM中的空操作,比如可能是“MOV R0,R0”指令等,主要用于延时操作,其指令格式如下:
NOP