ARM 常用的指令 电脑版发表于:2024/6/19 9:44 ![](https://img.tnblog.net/arcimg/hb/6930d1439f4b43e785a433685b813262.png) >#ARM 常用的指令 [TOC] 搬移指令 ------------ tn2>`mov`、`msr`和`mrs`指令就不讲了,上一篇写得有。 ### and指令 tn2>将第二个参数和第三个参数进行与运算。 我们先给寄存器`r0`赋值5,在对其中进行后五位清空举例: ```bash .text b main nop nop nop nop nop nop nop main: mov r0,#5 and r0,r0,#0xFFFFFFE0 ``` ![](https://img.tnblog.net/arcimg/hb/f5bdf1f97bb341b9b8b8848a805355c3.png) ![](https://img.tnblog.net/arcimg/hb/b647130acb1c4906aeedc14136b9556b.png) tn2>为什么可以清空? 首先我们要了解与运算。 ```bash 1 AND 1 = 1 1 AND 0 = 0 0 AND 1 = 0 0 AND 0 = 0 ``` tn2>由于`0xFFFFFFE0 = 1111 1111 1111 1111 1111 1111 1110 0000`,而5为`101`所以根据与运算会被清0; ### orr指令 tn2>进行后两位数的或运算。 ```bash 1 OR 1 = 1 1 OR 0 = 1 0 OR 1 = 1 0 OR 0 = 0 ``` tn2>我们将16进制2和7进行或运算。 ```bash .text b main nop nop nop nop nop nop nop main: mov r0,#7 @111 orr r0,r0,#2 @10 ``` ![](https://img.tnblog.net/arcimg/hb/fa6a1847a77d4db69d49082eec043a8d.png) tn2>结果仍然是`111`也就是7. ### mov LSL与LSR tn2>LSL表示向左加0,LSR表示向右加0。 ```bash mov r0,#7 @111 orr r1,r0,LSL#1 @1110 orr r1,r1,LSL#2 @111000 ``` ![](https://img.tnblog.net/arcimg/hb/f6ddffe62ed54e14802ca90d67612933.png) ```bash mov r0,#7 @111 orr r1,r0,LSR#2 @1 ``` ![](https://img.tnblog.net/arcimg/hb/f6bf16e51ec6403299067ae9b91e75a1.png) 判断指令 ------------ ### cmp tn2>判断两个数是否相等。 举例:判断r0等于0时,r1=5,r0大于0时r1再加3给自己。 ```bash .text b main nop nop nop nop nop nop nop main: @ if(r0=0) { r1 = 5 } mov r0,#5 cmp r0,#0 moveq R1,#5 @ if r0 > #0 {r1 += 3} addgt r1,r1,#3 ``` ![](https://img.tnblog.net/arcimg/hb/ef1364f828004c02aa9065f4dc2e5c05.png) tn2>我们调试到这里的是偶发现CPSR的C位变了,这是CPU中AUL计算出的结果。 如果第一个操作数大于第二个操作数,C 位会被设置为 1。 如果第一个操作数小于第二个操作数,C 位会被设置为 0。 如果两个操作数相等,C 位也会被设置为 0。<br/> `moveq`表示如果根据上面的判断相等`r1`会进行赋值`5`。 然而条件不成立,所以没有进行赋值。 ![](https://img.tnblog.net/arcimg/hb/40f8683a4ff04016b23942bddf41b691.png) `addgt`表示如果第一个值比第二个值大就进行相加的操作。 ![](https://img.tnblog.net/arcimg/hb/ece81e24453d4022a84a4dac27bbf9e3.png) tn>指令会被解析成机器码,存储在flush里面。 ARM指令机器码 ------------ ![](https://img.tnblog.net/arcimg/hb/5b31d126443c42d7800e10cbdde22658.png) tn2>机器码是以二进制表示的指令,而汇编指令则是其对应的可读性更高的表示形式。 |机器码 (Hex)| 二进制 (Binary)| 汇编指令| | ------------ | ------------ | |`0x01A03001` |`0000 0001 1010 0000 0011 0000 0000 0001`| `moveq r3, r1`| |`0xE1A03001` |`1110 0001 1010 0000 0011 0000 0000 0001`| `mov r3, r1`| |`0xE0832001` |`1110 0000 1000 0011 0010 0000 0000 0001`| `add r2, r3, r1`| ### ARM指令编码的具体结构 tn2>从右往左数。 31到28位表示条件码(condition codes),如0000表示EQ(等于),0001表示NE(不等于)。 24到21位表示操作码(opcode),如1101表示moveq,1101表示mov,0100表示add。 15到12位表示目标寄存器(Rd), 11到0位表示源寄存器(Rs)或立即数(immediate value)。 ### 条件码的解释 | 条件 | 描述 | | ------------ | ------------ | |`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` (none)|不启用条件判断|| tn2>我们在表格中的二进制里面找到32-28位也就是从左往右数的前4位。 `moveeq`表示等于所以对应的是`0000` 其他两行都没有判断所以是`1110`。 ### 常规操作码 tn2>接着的24-21为对应的是相关操作码。 | 操作码 | 汇编指令 | 描述 | | ------------ | ------------ |------------ | |`0000`|AND|按位与| |`0001`|EOR|按位异或| |`0010`|SUB|减法| |`0011`|RSB|反向减法(减法的操作数交换)| |`0100`|ADD|加法| |`0101`|ADC|带进位加法| |`0110`|SBC|带进位减法| |`0111`|RSC|反向带进位减法| |`1000`|TST|测试(与操作结果更新标志)| |`1001`|TEQ|测试(异或操作结果更新标志)| |`1010`|CMP|比较(减操作结果更新标志)| |`1011`|CMN|比较(加操作结果更新标志)| |`1100`|ORR|按位或| |`1101`|MOV|传送| |`1110`|BIC|位清零(与非操作)| |`1111`|MVN|按位取反| ### 拆解指令含义 tn2>`moveq r3, r1` |位| 二进制| 含义| | ------------ | ------------ |------------ | |31-28| `0000`| 条件码 EQ (Z=1)| |27-26| `00`| 指令类型| |25|`0`| | |24-21| `1101`| 操作码 MOV| |20|`0` | S位 (更新条件标志)| |19-16| `0000` | 源寄存器 Rn| |15-12| `0011` | 目标寄存器 Rd (r3)| |11-0| `0000 0000 0001`|源寄存器 Rs (r1)| tn>当S位被设置为1时,执行指令后会更新CPSR中的条件标志位。 当S位被设置为0时,执行指令后不会更新CPSR中的条件标志位。 tn2>`mov r3, r1` |位| 二进制 |含义| | ------------ | ------------ |------------ | |31-28| 1110| 条件码 AL (总是)| |27-26| 00| 指令类型| |25| 0| | |24-21| 1101| 操作码 MOV| |20| 0| S位 (更新条件标志)| |19-16| 0000| 源寄存器 Rn| |15-12| 0011| 目标寄存器 Rd (r3)| |11-0| 0000 0000 0001| 源寄存器 Rs (r1)| tn2>`add r2, r3, r1` |位| 二进制 |含义| | ------------ | ------------ |------------ | |31-28| `1110`| 条件码 AL (总是)| |27-26| `00`| 指令类型| |25| `0`| | |24-21 |`0100`| 操作码 ADD| |20|`0`|S位 (更新条件标志)| |19-16 |`0011`| 源寄存器 Rn (r3)| |15-12 |`0010`| 目标寄存器 Rd (r2)| |11-0 |`0000 0000 0001`| 源寄存器 Rs (r1)| tn2>在这当中`mov r3,#0x1101`是不可以的。 因为它对应的二进制是`0001 0001 0000 0001`,在ARM中最高支持8位而转换成二进制已经是16位了所以是不行的。 ![](https://img.tnblog.net/arcimg/hb/231b56bd93c94bb7a28e92a90e6cc716.png) tn2>但是`mov r3,#0x11000000`这样是可以。 在ARM汇编指令集中,立即数(immediate value)有严格的格式限制,不能任意指定。 具体来说,立即数必须能够通过旋转和8位数的组合表示出来。 `#0x11000000`这个数可以通过8位立即数和右移偶数位的方法表示出来。 ![](https://img.tnblog.net/arcimg/hb/9f9568efaa524656b13adac9112abe96.png) tn2>当然可以通过伪指令解决这个问题。`ldr r3,=0x1101`。 ![](https://img.tnblog.net/arcimg/hb/68fff4610df74655b66ea4cab0ac8f11.png) tn2>原因是在汇编程序中,编译器会将这条指令翻译为两步操作: 计算立即数`0x1101`的地址,假设为`0x1000`。 将地址`0x1000`中的值加载到寄存器 `r3` 中。 跳转指令 ------------ | 指令 | 描述 | | ------------ | ------------ | | `b main` | 跳转到符号为main地代码处 | | `bl func` | 跳转函数func,并保存下一条要执行的指令的位置到lr(LR这里是R14),当跳转代码结束后,用`mor pc,lr`指令跳回来 | | `beq addr` | 相等时,跳转到地址addr处。相等(指CPSR寄存器中的条件码置位时)| | `bne addr` | 不等时,跳转到地址addr | ### 用汇编实现C语言功能 ```c void main(void){ int ret=0 ret=func1(2) while(1){} } int func1(int a){ if(a==2) return func2(1) else return func3(2) } int func2(int a){ return a+3; } int func3(int a){ return a-1; } ``` ```bash .text b main nop nop nop nop nop nop nop main: mov r2,#0 bl func1 main_end: b main_end func1: mov r4,lr cmp r2,#2 mov r5,#1 bleq func2 mov r5,#2 blne func3 func1_end: mov pc,r4 func2: add r2,r5,#3 func2_end: mov pc,lr func3: sub r2,r5,#1 func3_end: mov pc,lr .end ``` ![](https://img.tnblog.net/arcimg/hb/bde2af04c8db4b94aff70f3bad329c59.png) 逻辑指令 ------------ tn2>与,指令`and`。 ```bash mov r1,#0x03 @ 0000 0011 mov r2,#0x0e @ 0000 1110 and r0,r1,r2 @ 0000 0010 ``` ![](https://img.tnblog.net/arcimg/hb/134bb9ebde6b4002b55eebed39923bc4.png) tn2>或,指令`orr`。 ```bash mov r1,#0x03 @ 0000 0011 mov r2,#0x0e @ 0000 1110 orr r0,r1,r2 @ 0000 1111 ``` ![](https://img.tnblog.net/arcimg/hb/e243806d0d2b49a4a1fa951bb8f4149b.png) tn2>BIC(Bit Clear)指令用于将一个寄存器的某些位清零。 它执行`r2`按位与操作,然后将结果取反。 ```bash mov r1,#0x03 @ 0000 0011 mov r2,#0x0e @ 0000 1110 bic r0,r1,r2 @ 0000 0001 ``` ![](https://img.tnblog.net/arcimg/hb/b96c27ef0ad142d69217ab9dab539573.png) tn2>TST(Test)指令用于测试两个操作数的按位与结果,并根据结果更新条件标志(条件码寄存器中的标志)。 它类似于 AND 指令,但不会存储结果,只会更新标志位。 Z(零)标志:如果结果为零,则设置为 1;否则设置为 0。 N(负)标志:如果结果的最高有效位(符号位)为 1,则设置为 1;否则设置为 0。 C(进位)标志:保留原值,不受影响。 V(溢出)标志:保留原值,不受影响。 ![](https://img.tnblog.net/arcimg/hb/9fcf34ffdaeb440e83ef84f2a988adb5.png) tn2>CMP(Compare)指令用于比较两个操作数,并根据比较结果更新条件标志(条件码寄存器中的标志)。 它执行减法操作,但不存储结果,只会更新标志位。 ```bash mov r1,#0x03 @ 0000 0011 mov r2,#0x04 @ 0000 0100 cmp r1,r2 @ 0000 0000 Z=1 ``` ![](https://img.tnblog.net/arcimg/hb/940358a1d3d545eda5ee2f3ac370539f.png) 延时1秒 ------------ ```bash .text b main nop nop nop nop nop nop nop main: mov r0,#1 bl delay1s mov r0,#2 delay1s: ldr r4,=0x1fffff delay1s_loop: cmp r4,#0 beq delay1s_end subne r4,#1 b delay1s_loop delay1s_end: mov pc,lr main_end: b main_end .end ``` ![](https://img.tnblog.net/arcimg/hb/5d11bc73137e4e34bfc6a5fc68c75cf9.png)