1. 指令系统
1.1 简介
- ARM基于精简指令集RISC
- 有32位ARM指令集和16位Thumb指令集,ARM指令集效率高代码密度低,Thumb密度较高,是ARM的子集
- 所有ARM指令可有条件执行,而Thumb指令仅有一条指令(跳转指令)有条件执行功能
- 两种指令集下的程序可相互调用
1.2 寻址方式分类
根据指令中的地址字段实现寻找操作数地址的方式
寄存器寻址
- 操作数存在寄存器中,地址码字段是寄存器编号
MOV R1,R2
将R2的值存入R1SUB R0,R1,R2
R0 = R1 - R2
- 立即寻址
- 操作码字段后面的地址吗部分就是操作数本身,操作数是指令的一部分
MOV R0,#0xFF00
- 寄存器移位寻址
- 第二个操作数是寄存器移位方式
MOV R0,R2,LSL #3
R2的值左移3位,放入R0ANDS R1,R1,R2,LSL R3
R2的值左移R3里的数的位,并于R1相与存在R1中
- 寄存器间接寻址
- 所需的操作数保存在寄存器指定地址的存储单元中
LDR R1,[R2]
将R2指向的存储单元的数据读出,保存在R1中SWP R1,R1,[R2]
将R1的值和R2指定的寄存器中的值交换
- 基址寻址
- 将基址寄存器的内容与指令中给出的偏移量相加
LDR R2,[R3,#0x0C]
读取R3+0x0C地址上的存储单元的内容存入R2,但不改变R3中的值STR R1,[R0,#-4]!
将R1保存在R0-4指向的内存单元中,并更新R0中的值
- 多寄存器寻址
- 一次传送几个寄存器值
LDMIA R1!,{R2-R7,R12}
将R1指向的单元中的数据读出到R2~R7,R12中,每读出一个R1自动加4- 寄存器列表中的寄存器数不能多于16个,排列必须由大到小
- 堆栈寻址
- 后进先出
- 堆栈寻址是隐含的,使用一个专门的寄存器(堆栈指针),指针指向栈顶
- 生长方式分两种
- 向上生长:向高地址生长
- 向下生长:向低地址生长
- 堆栈形式
- 满堆栈:指针指向的是最后压入堆栈的有效数据项
- 空堆栈:指针指向的是下一个待压入数据的空位置
- 组合有四种堆栈的工作方式
- 满递增
LDMFA STMFA
- 空递增
LDMEA STMEA
- 满递减
LDMFD STMFD
- 空递减
LDMED STMED
- 满递增
- 块拷贝寻址(多地址寻址)
- 用于将一块数据从存储器的某一位置拷贝到另一位置
STMIA R0!,{R1-R7}
将R1-R7的数据存储到R0指向的地址空间中,每次R0,递增
- 相对寻址
- 基址寻址的变通,由程序计数器PC提供基准地址,指令中的地址码字段作为偏移量
1.3 指令集介绍
1.3.1指令格式
<opcode> {<cond>} {S} <Rd>,<Rn>{,<operand2>}
< >项为必须项,{ }为可选项
opcode |
指令助记符 |
---|---|
cond |
执行条件 |
S |
是否影响CPSR寄存器的值 |
Rd |
目标寄存器 |
Rn |
第一个操作数的寄存器 |
operand2 |
第二个操作数 |
1.3.2 第二个操作数
<opcode> {<cond>} {S} <Rd>,<Rn>{,<operand2>}
operand2:
#immed_8r
常数表达式- 该常数必须对应8位位图,即一个8位的常数通过循环右移偶数位得到
- 判断一个常数是否合法:该常数是否小于8位,左移偶数位是否能放入低8位
Rm
寄存器方式- 操作数即为寄存器数值
Rm,shift
寄存器移位方式- 将寄存器的移位结果作为操作数,但Rm值保持不变(如果需要改变,使用!刷新)
移位操作码 | 说明 |
---|---|
ASR #n |
算术右移n位,首位复制空位补充 |
LSL #n |
逻辑左移n位,0位补充 |
LSR #n |
逻辑右移n位,0位补充 |
ROR #n |
循环右移n位,溢出位空位补充 |
RRX |
带扩展位的循环右移1位 |
Type Rs |
Type为移位的类型,Rs为偏移量寄存器,低8位有效 |
1.3.3 条件码
<opcode>{<cond>}{S} <Rd>,<Rn>{,<operand2>}
cond:
- 使用条件码可以实现高效的逻辑操作,提高代码效率
- 所有的ARM指令都可条件执行,而Thumb指令只有跳转指令
B
具有条件执行功能 - 如果指令不标明条件代码,默认为无条件执行(
AL
)
CPSR
寄存器
示例:
if(a > b)
a++;
else
b++;
对应ASM
CMP R0,R1 ;R0与R1比较
ADDHI R0,R0,#1 ;若R0>R1,则R0=R0+1
ADDLS R1,R1,#1 ;若R0<=R1,则R1=R1+1
1.3.4 存储器访问指令
- RISC处理器,对存储器的访问只能使用加载和存储指令
- 分单寄存器操作指令和多寄存器操作指令
1.3.4.1 单寄存器存取
- 单寄存器加载
| 助记符 | 说明 | 条件码位置 |
| —- | —- | —- |
|
LDR
| 加载字数据 |LDR{cond}
| |LDRB
| 加载无符号字节数据 |LDR{cond}B
| |LDRT
| 以用户模式加载字数据 |LDR{cond}T
| |LDRBT
| 以用户模式加载无符号字节数据 |LDR{cond}BT
| |LDRH
| 加载无符号半字数据 |LDR{cond}H
| |LDRSB
| 加载有符号字节数据 |LDR{cond}SB
| |LDRSH
| 加载有符号半字数据 |LDR{cond}SH
|
- 单寄存器存储
| 助记符 | 说明 | 条件码位置 |
| —- | —- | —- |
|
STR
| 存储字数据 |STR{cond}
| |STRB
| 存储字节数据 |STR{cond}B
| |STRT
| 以用户模式存储字数据 |STR{cond}T
| |STRBT
| 以用户模式存储字节数据 |STR{cond}BT
| |STRH
| 存储半字数据 |STR{cond}H
|
- LDR/STR指令用于对内存变量的访问,内存缓冲区数据的访问等
- 使用LDR指令加载数据到PC寄存器,则实现程序的跳转
- 可分为
字和无符号字节加载存储指令
和半字和有符号字节加载存储指令
字和无符号字节加载存储指令:
- T为可选后缀,如果使用了T,即使处理器在特权模式下,存储系统也将访问看成是在用户模式下进行
- T在用户模式下无效,不能与前索引偏移一起使用。
LDRT Rd,[Rn,#0x04]!
指令错误❌
寻址方式灵活:基址寄存器(任一通用寄存器)+ 地址偏移量
⭕地址偏移量格式:
- 立即数
LDR R1,[R0,#0x12]
- 寄存器
LDR R1,[R0,R2]
- 寄存器及移位常数
LDR R1,[R0,R2,LSL #2]
⭕寻址方式的地址计算方法:
- 零偏移
LDR Rd,[Rn]
- 前索引偏移
LDR Rd,[Rn,#0x04]
- 程序相对偏移
LDR Rd,label
- 后索引偏移
LDR Rd,[Rn],#0x04
半字和有符号字节加载存储指令:
- 偏移量格式、寻址方式与前者相同
- 有符号位半字/字节加载是指用符号位加载扩展到32位
- 无符号半字加载是指用零扩展到32位
- 半字读写的指定地址必须为偶数,否则产生不可靠的结果
1.3.4.2多寄存器存取
LDM{cond}< 模式 > Rn{!},reglist{^}
STM{cond}< 模式 > Rn{!},reglist{^}
符号 | 说明 |
---|---|
cond | 执行条件 |
模式 | 地址增长模式 |
! | 操作结束后,地址回写 |
reglist | 寄存器列表 |
^ | 加载/存储用户模式下的寄存器 |
IA与EA等效,IB和FA等效,DA和ED等效,DB和FD等效
使用多寄存器块取和块存时,
如果使用数据块式的操作,需要考虑对应关系(I和A均需要反转)
如果使用堆栈操作,不需要考虑对应关系,存取后缀一样即可
1.3.4.3 寄存器和存储器交换指令
SWP{cond}{B} Rd,Rm,[Rn]
符号 | 说明 |
---|---|
B | 可选,交换字节,否则交换字 |
Rd,Rm,Rn | Rn指向地址的数存到Rd,Rm的数存入Rn指向的地址 |
若Rm=Rd | 将Rd中的数与Rn指向的地址的数交换 |
1.3.5 数据处理指令
- 数据处理指令只能对寄存器的内容操作,而不能对内存中的数据操作
- 所有数据处理指令都可选择使用
S
后缀,影响状态标志
1.3.5.1 算术运算
MOV{cond}{S} Rd,operand2 数据传送
MVN{cond}{S} Rd,operand2 数据非传送
ADD{cond}{S} Rd,Rn,operand2 加法运算指令
SUB{cond}{S} Rd,Rn,operand2 减法运算指令
RSB{cond}{S} Rd,Rn,operand2 逆向减法指令
ADC{cond}{S} Rd,Rn,operand2 带进位加法指令
SBC{cond}{S} Rd,Rn,operand2 带进位减法指令
RSC{cond}{S} Rd,Rn,operand2 逆向带进位减法指令
- 带进位指令一般用于32位加减法向64位扩展
1.3.5.2 逻辑运算
AND{cond}{S} Rd,Rn,operand2 逻辑与操作指令
ORR{cond}{S} Rd,Rn,operand2 逻辑或操作指令
EOR{cond}{S} Rd,Rn,operand2 逻辑异或操作指令
BIC{cond}{S} Rd,Rn,operand2 位清除指令
1.3.5.3 比较指令
CMP{cond} Rd,operand2 比较指令指令
CMN{cond} Rd,operand2 负数比较指令
TST{cond} Rd,operand2 位测试指令
TEQ{cond} Rd,operand2 相等测试指令
- 实质上是减法,逻辑与,异或等运算
- 与原运算相比的区别是,比较指令不保存计算结果,而将比较结果自动保存在CPSR中
1.3.5.4 乘法指令
MUL{cond}{S} Rd,Rm,Rs 32位乘法指令
MLA{cond}{S} Rd,Rm,Rs,Rn 32位乘加指令
UMULL{cond}{S} RdLo,RdHi,Rm,Rs 64位无符号乘法指令
UMLAL{cond}{S} RdLo,RdHi,Rm,Rs 64位无符号乘加指令
SMULL{cond}{S} RdLo,RdHi,Rm,Rs 64位有符号乘法指令
SMLAL{cond}{S} RdLo,RdHi,Rm,Rs 64位有符号乘加指令
1.3.5.5 ARM分支指令
B{cond} Label 分支指令,跳转到指定地址±32MB范围
BL{cond} Label 带链接的分支指令(调用子程序)
跳转前先将当前地址存至LR,然后再跳转至BL所指地址
BX{cond} Label 带状态切换的分支指令(地址最低位存放要切换的状态标志)
跳转到Rm指定的地址执行
Rm的最低位为1,跳转时自动将CPSR中的T置位,即把目标地址的代码解释为Thumb代码
Rm的最低位为0,跳转时自动将CPSR中的T复位,即把目标地址的代码解释为ARM代码
1.3.6 杂项指令
1.3.6.1 软中断指令
SWI{cond} immed_24 产生软中断,从其他模式切换到管理模式,实现用户程序调用操作系统的系统服务
- SWI
- 根据SWI指令传递的参数SWI异常处理程序可以做出相应的处理
- 传递参数的方法有两种
- 1.指令中的24位立即数指定了用户请求的服务类型
- 2.指令中的24位立即数被忽略,用户请求的服务类型由寄存器R0的值决定,参数通过其它通用寄存器传递
1.3.6.2 状态寄存器指令
MRS{cond} Rd,psr
MSR{cond} psr_fields,#immed_8r
MSR{cond} psr_fields,Rm
- MRS
- 仅有MRS指令可以对CPSR和SPSR进行读操作,了解当前处理器的工作状态
- MSR
- 仅有MSR指令可以对CPSR和SPSR进行写操作
- 用于切换处理器,允许禁止中断等
field | 含义 |
---|---|
c | 控制域屏蔽字节(psr[7-0]) |
x | 扩展域屏蔽字节(psr[15-8]) |
s | 状态域屏蔽字节(psr[23-16]) |
f | 标志域屏蔽字节(psr[31-24]) |
1.3.7 伪指令
- 伪指令不属于ARM指令,为了方便编程而定义
-
1.3.7.1 小范围地址读取
ADR{cond} Rm,label
将程序标号label的地址存入寄存器Rm
- 编译时等效为pc的偏移地址存入Rm
只能用一条ADD或SUB指令实现该伪指令功能,如果不能实现则产生错误,编译失败
1.3.7.2 中等范围地址读取
ADRL{cond} Rm,label
比ADR伪指令更大的范围的地址
- 编译时被替换为两条合适的指令
1.3.7.3 大范围地址读取
LDR{cond} Rm,=label
LDR{cond} Rm,=#immed_8r
NOP
- 用于延时