一、ARM指令的分类
ARM 指令集可以分为跳转指令、数据处理指令、程序状态寄存器(PSR)传输指令、Load/Store指令、协处理器指令和异常中断产生指令6类。
二、ARM指令的一般编码格式
ARM指令字长为固定的32位。
一条典型的ARM指令编码格式如下:
经典ARM指令格式如下:
<opcode> {<cond>} {S} <Rd>,<Rn>,<operand2>
- <> 为必选项,{}为可选项
<opcode>
为操作码(指令助记符),如ADD表示算术加操作指令{<cond>}
表示执行的条件- {S} 决定指令执行是否影响CPSR寄存器的值
<Rd>
表示目标寄存器<Rn>
表示包含的第一个操作数的寄存器<operand2>
为第二个操作数
三、ARM指令的条件码域
大多数ARM 指令都可以条件执行,也就是根据CPSR中的条件标志位决定是否执行该指令。当条件满足时执行该指令,条件不满足时该指令被当作一条NOP指令,这时处理器进行判断中断请求等操作,然后转向下条指令。
在ARM v5之前的版本中,所有的指令都是条件执行的,从ARM v5版本开始引入了一些指令必须无条件执行。
每一条ARM指令包含4位的条件码,如下所示:
条件码共有16个,各条件码的含义和助记符如下表所示。可条件执行的指令可以在助记符的扩展域加上条件码助记符,从而在特定的条件下执行。
四、ARM指令寻址方式
ARM指令寻址方式有以下几种:
- 数据处理指令的操作数的寻址方式。
- 字及无符号字节的Load/Store指令的寻址方式。
- 杂类Load/Store指令的寻址方式。
- 批量Load/Store指令的寻址方式。
- 协处理器Load/Store指令的寻址方式。
1、数据处理指令的操作数的寻址方式
通常数据处理指令的格式如下所示:<opcode>{<cond> } {S} <Rd>,<Rn>,<shifter operand>
其中:<opcode>
是指令助记符,如ADD表示算术加操作指令。
<cond>
表示指令执行的条件。
{S}
决定指令的操作是否影响CPSR的值。
<Rd>
表示目标寄存器。
<Rn>
表示包含第主个操作数的寄存器。
<shifter_operand>
表示第2个操作数。
<shifler_operand>
通常有下面3种格式:
- 立即数:就是一个常数,是指令的一部分,类似于常量,不能作为被赋值的量来使用
- 立即数的优点:取指令的时候,可以将他读取到cpu,不用单独去内存读取,速度比较快
- 立即数的缺点:不能是任意的32位的数
- 有效立即数:
- 在12位的第二个操作数里,[11:8]是4位的循环右移值,[7:0]是8位的常数
- 有效立即数的范围0-0xff
- 确定0x123是不是有效立即数
- 汇编指令:
MOV r0,#0x123
- 第一步:转化成二级制
无法通过循环右移得到8位位图 - 结论:0x123不是立即数
- 汇编指令:
- 确定0x234是不是有效立即数
- 汇编指令:
MOV r0,0x234
- 0000 0000 0000 0000 0000 0010 0011 0100
- 0000 0000 0000 0000 0000 0000 1000 1101—循环右移30位,可以得到rotate_imm的值是15
- 汇编指令:
- 确定0x3F0是不是有效立即数
- 汇编指令:
MOV R0,#0x3f0
- 0000 0000 0000 0000 0000 0011 1111 0000
- 0000 0000 0000 0000 0000 0000 0011 1111
- 汇编指令:
- 0x20000018
- 0010 0000 0000 0000 0000 0000 0001 1000
- 左移4位得到:0000 0000 0000 0000 0000 0001 1000 0010
- 总结
- 判断一个数是否符合8位位图原则,首先看这个数字的二进制,他们中表示1的个数是否不超过8个
- 如果超过了8个,再看看这n个(n<=8)是否能够放到8个二进制位中;
- 如果可以放进去的话,再看下这8个二进制位是否可以循环右移得到原来的二进制书;
- 如果可以的话,就是符合8位位图,是合法的立即数
- 如果不可以,不是合法的立即数;
- 当把数据转换成二级制的时候,如果1不超过8个,但是间隔很多个偶数倍的0,我们可以循环左移
- 然后找到最高位的1,去掉前面偶数个0
- 找到最低位的1,去掉后面偶数个0,得到这个数是否能够放进8位位图,能的话就是一个有效立即数,反之则不是。
立即数方式 每个立即数由一个8位的常数循环右移偶数位得到。其中循环右移的位数由一个4位二进制的两倍表示。如果立即数记作
<immediate>
,8位常数记作immed_8,4位的循环右移值记作rotate_imm,则有:<immediate>= immed_8 循环右移(2*rotate_imm)
立即数 = 8位常数 ROR(2 * 移位数) ROR:循环右移指令
这样并不是每一个32位的常数都是合法的立即数,只有能够通过上面构造方法得到的才是合法的立即数。下面的常数是合法的立即数:
0xff,0x104,0xff0,0xff00
而下面的数不能通过上述构造方法得到,则不是合法的立即数:
0x101,0x102,0xFF1
同时按照上面的构造方法,一个合法的立即数可能有多种编码方法。如0x3F0是一个合法的立即数,它可以采用下面两种编码方法:
immed_8=0x3F,rotate_imm=0xE
或者
immed_8=0xFC, rotate_imm=0Xf
但是,由于这种立即数的构造方法中包含了循环移位操作,而循环移位操作会影响CPSR的条件标志位C。因此,同一个合法的立即数由于采用了不同的编码方式,将使某些指令的执行产生不同的结果,这是不能允许的。ARM 汇编编译器按照下面的规则来生成立即数的编码。- 当立即数数值在0和0xFF范围时,令
immed_8=<immediate>,rotate_imm=0
- 其他情况下,汇编编译器选择使rotate_imm 数值最小的编码方式。
寄存器方式 在寄存器寻址方式下,操作数即为寄存器的数值。如下例所示:
MOV R3,R2 ;将R2的数值放到R3中
ADD R0,R1,R2 ;R0数值等于R1的数值加上R2的数值
寄存器移位方式 寄存器移位方式的操作数为寄存器的数值做相应的移位(或者循环移位)而得到。只体的移位(或者循环移位)的方式有下面几种:
移位(或者循环移位)的位数可以用立即数方式或者寄存器方式表示。
下面是一些寄存器移位方式的操作数示例。
数据处理指令操作数的具体寻址方式有下面11种。ASR 算术右移
LSL 逻辑左移
LSR 逻辑右移
ROR 循环右移
RRX 扩展的循环右移
MOV R0,R1,LSL #3 ; R0=R1*(2**3)
ADD R0,R1,R1,LSL #3 ; R0=R1+R1*(2**3)
SUB R0,R1,R2,LSR #4 ; R0=R1-R2/(2**4)
MOV R0,R1,ROR R2 ; R0-R1循环右移R2位
```
立即数
- 当立即数数值在0和0xFF范围时,令
<a name="b6dc7d63"></a>
#### 1.立即数寻址方式 `#<immediate>`
指令编码格式如下图所示:
![](https://gitee.com/waqwb/my-pic-go/raw/master/202203011051805.png#crop=0&crop=0&crop=1&crop=1&id=uj5Nj&originHeight=67&originWidth=755&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
rotate_imm:4位循环右移值。即之前所提到的公式中的移位数。
immed_8:8位常数。参照下面的立即数公式。
**立即数 = 8位常数 ROR(2 * 移位数) ROR:循环右移指令**
指令操作数<shiter_operand>即为立即数#,立即数#的生成方式之前已经介绍,不在赘述。当rotate_imm = 0时,循环器的进位值(即Carry-out位)为CPSR中的C条件标志;当rotate_imm != 0时,循环器的进位值(即Carry-out位)为操作数<shiter_operand>的最高为bit[31]。
指令中操作数寻址操作的伪代码如下:
shifter_operand = imme_8 Rotate_Right (rotate_imm * 2)
if rotate_imm == 0 then
shifter_carry_out = C flag
else
shifter_carry_out =shifter_operand[31]
立即数的合法性和编码规则,请查看之前文章中介绍。
示例:
MOV R0 , #0xFC0 ;R0=0xFC0
<a name="a081a215"></a>
#### 2.寄存器寻址`<Rm>`
指令编码格式如下:
![](https://gitee.com/waqwb/my-pic-go/raw/master/202203011051509.png#crop=0&crop=0&crop=1&crop=1&id=sFEL9&originHeight=89&originWidth=895&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
指令的操作数<shifter_operand>即为寄存器Rm的数值。另外循环器进位值(即 Carry_Out 位)为CPSR的C条件标志。
指令中操作数语法格式
其中,指定操作数所在的寄存器。
伪代码如下:
shifter_operand = Rm
shifter_carry_out = C Flag
需要注意的是,当R15(PC指针)用作第一个源操作数Rm或者第二个操作数Rm时,操作数即为当前指令地址加常数8。造成上述的原因,是因为ARM指令集采用的是流水线的方式造成的。
示例
MOV R3,R2 ;将R2的数值放到R3中 ADD R0,R1,R2 ;R0数值等于R1的数值加上R2的数值
<a name="0c98f2ea"></a>
#### 3.寄存器逻辑左移:`<Rm>, LSL #<shift_imm>`
指令编码格式:
![](https://gitee.com/waqwb/my-pic-go/raw/master/202203011051516.png#crop=0&crop=0&crop=1&crop=1&id=qnTy1&originHeight=77&originWidth=887&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
指令操作数<shifter_operand>为寄存器Rm的值逻辑左移shift_imm位。shift_imm为5位,范围是0~31位。进行逻辑左移的时候,空出的位补0。
循环器的进位值(Carry_Out位):
- 当shift_imm = 0时,Carry_Out = C Flag(CPSR的C位)。
- 当shift_imm !=0时,Carry_Out = Rm[32 - shift_imm](即shifter_operand 的最高位bit[31])
指令中操作数的语法格式:
, LSL #<shift_imm>
- 为进行逻辑左移操作的寄存器。
- LSL 逻辑左移操作。
- <shift_imm>逻辑左移位数,范围0~31。
指令中操作数寻址操作的伪代码
if shiftimm == 0 then /寄存器操作数/ shifter_operand = Rm shifter_carry_out = Flag else / shift_imn > 0 / shifter_operand = Rm Logical_Shift Left shiftimm shifter_carry_out = Rm [32 - shift imm]
使用说明
当R15用作第1个源操作数Rn或者第⒉个操作数Rm 时,操作数即为当前指令地址加常数8。
示例:
MOV R0,R0,LSL #n ; R0 = R0 (2*n);
<a name="6f3c849b"></a>
#### 4. 寄存器逻辑左移:`<Rm>, LSL <Rs>`
指令编码格式:
![](https://gitee.com/waqwb/my-pic-go/raw/master/202203011051673.png#crop=0&crop=0&crop=1&crop=1&id=YTDmm&originHeight=49&originWidth=721&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
指令操作数的生成方式和之前提到的逻辑左移的方式相同,但移位的位数是由寄存器中的低八位[7:0]决定。
当Rs[7:0] = 0时,指令的操作数<shiter_operand>为寄存器Rm的值,循环器的进位值<Carry_out 位>是CPSR的C位条件标志。
当Rs[7:0] > 0 && Rs[7:0] < 32时,指令操作数<shiter_operand> 是寄存器 Rm的值逻辑左移Rs[7:0]位,循环器的进位值<Carry_out 位>是Rm最后被移出的位Rm[32 - Rs[7:0]]。
当Rs[7:0] = 32 时,指令操作数<shifter_operand> 为0,循环器的进位值是Rm[0]。
Rs[7:0] > 0 时,指令的操作数<shiter_operand> 为0,循环器进位值为0。
指令操作数的语法格式和标题相同:`<Rm>, LSL <Rs>`
其中:
- `<Rm>`为进行逻辑左移操作的寄存器。
- LSL逻辑左移操作。
- `<Rs>`包含逻辑左移位数的寄存器。
指令中操作数寻址操作的伪代码
if Rs [7:0] == then shifter operand = Rm shifter carry_out = C Fiag else if Rs[7:0] < 32 then shifter_operand = Rm Logical_shift_Left Rs[7:0] shifter_carry_out = Rm[32 - Rs[7:0]] else if Rs[7:0] == 32 then shifter_operand = 0 shifter_carry_out = Rm[0] else / Rs[7:0]>32 / shifter_operand = 0 shifter_carry_out = 0 ```
需要注意的是:
当R15(PC指针)用作Rn、Rm、Rd和Rs时,会产生不可预知的后果。