ARM指令集概述
ARM 指令集简介
ARM 微处理器的 ARM 指令集 ,所有的指令长度都是 32 位 ,并且大多数指令都在一个单独指令周期内执行。
主要特点包括:
- 指令是条件执行的;
- ARM 微处理器的指令集是加载/存储型的;
- 在多寄存器操作指令中一次最多可以完成 16 个寄存器的数据传送。
ARM Instruction Format
助记符表示
用助记符表示的 ARM 指令格式如下:<opcode> {<cond>} {S} {<Rd>} { ,<Rn>} {,<OP2>}
格式中 < >
的内容必不可少,{ }
中的内容可省略。
<opcode>
表示操作码,如 ADD 表示算术加法。{<cond>}
表示指令执行的条件域,如 EQ、NE 等。{S}
决定指令的执行结果是否影响 CPSR 的值,使用该后缀则指令执行的结将果影响 CPSR 的值,否则不影响。<Rd>
表示目标或源寄存器。<Rn>
表示第一个操作数,为寄存器。<op2>
表示第二个操作数,可以是立即数、寄存器和寄存器移位操作数。
除了 <opcode>
其余域都可以选择使用
例如: 指令
ADDEQS R0,R1,#8;
- 操作码为 ADD,
- 条件域 cond 为 EQ,
- S 表示该指令的执行影响 CPSR 寄存器的值,
- 目的寄存器 Rd 为 R0,
- 第一个操作数寄存器 Rn 为 R1,
- 第二个操作数 OP2 为立即数#8。
执行结果:R0 = R1+ 8
机器码表示
ARM 指令代码一般可以分为 5 个域:
第 1 个域是 4 位[31:28]的条件码域,4 位条件码共有 16 种组合;
第 2 个域是指令代码域[27:20],除了指令编码外,还包含几个很重要的指令特征和可选后缀的编码;
第 3 个域是第 1 个操作数寄存器 Rn,是 4 位[19:16],为 R0 ~ R15 共 16 个寄存器编码;
第 4 个域是目标或源寄存器 Rd,是 4 位[15:12],为 R0 ~ R15 共 16 个寄存器编码;
第 5 个域是第二个操作数[11:0]。
可选后缀
- S 后缀
指令中使用 S 后缀时,指令执行后程序状态寄存器的条件标志位将被刷新,不使用 S 后缀时,指令执行后程序状态寄存器 CPSR 的条件标志将不会发生变化。
假设 R0=0x1,R3=0x3,指令执行之前 CPSR 部分标志位为 nZcv,分别执行如下指令 CPSR 的值有何变化?
SUB R1,R0,R3; R0的值减去R3的值,结果存入R1
>SUBS R1,R0,R3; R0的值减去R3的值,结果存入R1影响标志位。
分析:执行第 1 条指令对于标志寄存器的值没有任何影响,因此 CPSR 的值不变。执行第 2 条指令后 CPSR=Nzcv。
- !后缀
如果指令地址表达式中不含!后缀,则基址寄存器中的地址值不会发生变化。
指令中的地址表达式中含有!后缀时,指令执行后,基址寄存器中的地址值将发生变化,变化的结果如下:
基址寄存器中的值(指令执行后)=指令执行前的值+地址偏移量
使用!后缀需要注意如下事项:
- !后缀必须紧跟在地址表达式后面,而地址表达式要有明确的地址偏移量;
- ! 后缀不能用于 R15(PC)的后面;
- 当用在单个地址寄存器后面时,必须确信这个寄存器有隐性的偏移量,例如“STMIA R7!, {R0 – R3}”此时地址基址寄存器 R7 的隐性偏移量是 16 字节。如果 R7 的初始值为 0X40000000,则该语句结束后为 0X40000010
例 分别执行下面两条指令有何区别?
LDR R3,[R0,#4]
>LDR R3,[R0,#4]!
分析:在上述指令中,第 1 条指令没有后缀!,指令的结果是把 R0 加 4 作为地址指针,把这个指针所指向的地址单元所存储的数据读入 R3,R0 的值不变。第 2 条指令除了实现以上操作外,还把 R0 + 4 的结果送到 R0 中。
- B 后缀
B 后缀的含义是:指令所涉及的数据是一个字节,不是一个字或半字。LDR R4,[R0]
R4=[R0],指令传送一个字LDRB R4,[R0]
R4=[R0],指令传送一个字节LDREQB R4,[R0]
如果相等则执行,R4=[R0],指令传送一个字节
ARM 指令的条件码
条件码 | 条件码助记符 | CPSR 中条件标志位值 | 含义 |
---|---|---|---|
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 | ARMV3 之前 | 从不执行不要使用 |
ARM 指令分类
ARM 指令可以分为:分支指令、数据处理指令、存储访问指令、协处理器指令和杂项指令五类。
1. 分支指令
分支指令用于控制程序的执行流程、实现 ARM 代码与 Thumb 代码之间进行切换。
2. 数据处理指令
数据处理指令在通用寄存器上执行计算,主要分为 3 种:算术/逻辑指令、比较指令和乘法指令。
3. 存储访问指令
用于加载/存储存放于 MCU 片外存储系统中的数据。加载指令用于从内存中读取数据放入寄存器中,存储指令用于将寄存器中的数据保存到内存中。
4. ARM 协处理器指令
ARM 协处理器指令用于控制外部的协处理器。包括
- 数据处理指令:启动一个协处理器专用的内部操作。
- 数据转移指令:使数据在协处理器和存储器之间进行转移。
- 寄存器转移指令:协处理器值转移到 ARM 寄存器或 ARM 寄存器的值转移到协处理器。
5. 杂项指令
包括状态寄存器转移指令和异常中断产生指令。
状态寄存器转移指令将 CPSR 或 SPSR 的内容转移到一个通用寄存器,或者反过来将通用寄存器的内容写入 CPSR 或 SPSR 寄存器
ARM 有 两条 异常中断产生指令,分别为 软中断指令 SWI 和 断点中断指令 BKPT。
ARM 指令的寻址方式
立即数寻址 Immediate Addressing
立即数寻址也叫立即寻址,操作数本身就在指令中给出,取出指令也就取到了操作数。这个操作数被称为立即数,对应的寻址方式也就叫做立即数寻址。
立即数要求以“#”为前缀,
对于以十六进制表示的立即数,还要求在“#”后加上“0x”或“&”;
对于以二进制表示的立即数,要求在“#”后加上“0b”;
对于以十进制表示的立即数,要求在“#”后加上“0d”或缺省。
在指令格式中,第二个操作数有 12 位:
因此有效立即数 immediate 可以表示成: <immediate> = immed_8 循环右移(2×rot)
[8,11]这4 bit
移位因子 (十进制表示范围 0-15)乘 2(内存对齐,ARM 内存地址为 4 的倍数),得到一个范围在 0-30,步长为 2 的移位值。
因此,将 ARM 中的立即数称为 8 位位图。“最后 8 位移动偶数位”得到立即数
如何判断一个数是合法立即数还是非法立即数
判断一个数是否符合 8 位位图的原则, 首先看这个数的二进制表示中1 的个数是否不超过 8 个. 如果不超过 8 个, 再看这 n 个 1(n<=8)是否能同时放到 8 个二进制位中, 如果可以放进去, 再看这八个二进制位是否可以循环右移偶数位得到
我们欲使用的数. 如果可以, 则此数符合 8 位位图原理, 是合法的立即数. 否则, 不符合.
寄存器寻址 Register Addressing
寄存器寻址就是利用寄存器中的数值作为操作数,这种寻址方式是各类微处理器经常采用的一种方式,也是一种执行效率较高的寻址方式。
寄存器移位寻址 Register Shift Addressing
当第二操作数为寄存器型时,在执行寄存器寻址操作时,也可以对第二操作数寄存器进行移位,此时第二操作数形式为:ADD Rd, Rn, Rm,{<shift>}
Rm
: 第二操作数寄存器<shift>
:用来指定移位类型和移位位数,有两种形式:5 位立即数,寄存器(用 Rs 表示)
在指令执行时,将寄存器移位后的内容,作为第二操作数参与运算。
例如指令:
第二操作数的移位方式:
移位位数可以用立即数方式或者寄存器方式给出,其值均小于 32,应为[0,31]
LSL,LSR
逻辑左/右移: 空出的最低/高有效位用 0 填充ASL,ASR
算术左/右移: 算术移位的对象为带符号数;故 ASL 空出的最低有效位用 0 填充,而 ASR 移位时,如果为负数,最高有效位用 1 填充;若为正数,则用 0 填充ROR
Rotate Right;循环右移 移出的字的最低有效位依次填入空出的最高有效位。RRX
Rotate Right Extended 带扩展的循环右移;将寄存器的内容循环右移 1 位,空位用原来 C 标志位填充
寄存器间接寻址 Register Indirect Addressing
寄存器间接寻址就是以寄存器中的值作为操作数的地址,而操作数本身存放在存储器中。LDR R0,[R4];AR0←[R4]
基址变址寻址 Base-index Addressing
变址寻址,也叫基址变址寻址,是指将基址寄存器的内容与指令中给出的地址偏移量相加,得到操作数所在的存储器的有效地址
变址寻址方式常用于访问某基地址附近的地址单元(4K 范围的偏移)LDR R0,[R1,#4]; R0←mem32[R1+4]
偏移地址方式
有三种加偏址的方式:前变址、自动变址和后变址寻址方式
前变址模式(不修改基址寄存器):先基址+偏址,生成操作数地址,再做指令指定的操作。也叫前索引偏移。例如 STR r0,[r1,#12]
(将 r0 的内容存入 r1+12 的地址单元中)
自动变址模式(修改基址寄存器): 先基址+偏移,生成操作数地址,做指令指定的操作。然后自动修改基址寄存器。例如 LDR R0,[R1,#4]!;
(从 R1 指向的地址加上 4 的位置加载数据到 R0,并将计算得出的地址写回到 R1 中”。)
后变址模式(修改基址寄存器): 基址寄存器不加偏移作为操作数地址。完成指令操作后,用(基址+偏移)的值修改基址寄存器。例如 STR r0,[r1],#12
将 r0 中的值存储到 r1 指向的地址,然后将 r1 的值加上 12 并把结果写回 r1”。
偏移地址形式:可以是一个立即数,也可以是另一个寄存器,并且还可以是寄存器移位操作;常用的是立即数偏移的形式
多寄存器寻址 Multiple Register Addressing
采用多寄存器寻址方式,一条指令可以完成多个寄存器值的传送。
这种寻址方式是多寄存器传送指令 LDM/STM 的寻址方式,这种寻址方式中用一条指令最多可传送 16 个通用寄存器的值。连续的寄存器间用 -
连接,否则用 ,
分隔
LDMIA “Load Multiple Increment After”
从 R0 指向的地址开始,连续加载寄存器 R1, R2, R3, R4 的值,并将最后的地址写回到 R0 中”
堆栈寻址 Stack Addressing
堆栈是一种数据结构,按后进先出(Last In First Out, LIFO)的方式工作,使用一个称作堆栈指针的专用寄存器指示当前的操作位置,堆栈指针总是指向栈顶。
堆栈可分为两种增长方式:
- 向上生长:向高地址方向生长,称为递增堆栈。
- 向下生长:向低地址方向生长,称为递减堆栈。
根据堆栈指针指向的数据位置的不同,可分为:
- 满堆栈:堆栈指针指向最后压入堆栈的有效数据项,称为满堆栈;
- 空堆栈:堆栈指针指向下一个待压入数据的空位置,称为空堆栈。
四种类型的堆栈工作方式
- 满递增堆栈 FA(Full Ascending):堆栈指针指向最后压入的数据,且由低地址向高地址生长。
- 空递增堆栈 EA(Empty Ascending):堆栈指针指向下一个将要放入数据的空位置,且由低地址向高地址生长。
- 满递减堆栈 FD(Full Descending) :堆栈指针指向最后压入的数据,且由高地址向低地址生长。
- 空递减堆栈 ED(Empty Descending):堆栈指针指向下一个将要放入数据的空位置,且由高地址向低地址生长。
STMXX 是存入到主存,是入栈操作;LDMXX 是从主存读出到寄存器,是出栈操作
相对寻址 Relative Addressing
与基址变址寻址方式相类似,相对寻址以程序计数器 PC 的当前值为基地址,指令中的地址标号作为偏移量,将两者相加之后得到操作数的有效地址