/*
* 2018/12/20 11:43 qing
*/
/*
* ARM中的MOV指令格式
*/
31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
| Cond | 0 0 | L| OpCode | S | Rn | Rd | Operand2 |
op2是占了12位,其中 bit 11 - bit 8 是移位数(rotate), bit 7 - 0 是一个 8 位的立即数(imm),
mov Rn, op2 @ 执行之后 Rn = op2 >> (rotate * 2),这里的移位是循环右移,这就决定了mov指令不是所有的立即数都能表示的
1. mov r3, #0x56000000
虽然0x56000000是一个32位的数,但是可以找到这么一个8位立即数,通过右移得到,看下机器码 e3a03456 ,展开成二进制,对照下格式
31 27 23 19 15 11 7 3
1110 0011 1010 0000 0011 0100 0101 0110
Cond[31:28] = 1110
[27:26] = 00
L[25] = 1 ,代表op2是一个立即数
OpCode[24:21] = 1101
S[20] = 0
Rn[19:16]= 0000
Rd[15:12]= 0011 , R3
Op2[11:8]= 0100 ,右移 4 * 2 位
Op2[7:0] = 0101 0110 , 8 位立即数, 0x56
首先要将 0x56 扩展成32位的无符号数, 0x00000056 ,然后循环右移 8 位,就得到了 0x56000000
2. mov r3, #0x56000014
0x56000014是无法通过移位来得到的,这时编译器会报错,C语言编写的程序,编译器会这样来处理:
mov r3, #0x56000000
add r3, r3, #0x14
代替mov的另外一条指令就是ldr,或许会更方便点。
/*
* 在ARM中使用立即数的规律
*/
在ARM中不能像X86那样直接将立即数加载到寄存器中。因为你使用的立即数是受限的。
可以通过LDR绕过这些限制,有以下的的技巧:
每条ARM指令的宽度是32位,所有的指令都是可以条件执行的。
有16中条件可以使用而且每个条件在机器码中的占位都是4位。之后我们需要2位来做为目的寄存器。2位作为第一操作寄存器,
1位用作设置状态的标记位,再加上比如操作码(opcode)这些的占位。最后每条指令留给我们存放立即数的空间只有12位宽。也就是4096个不同的值。
这也就意味着ARM在使用MOV指令时所能操作的立即数值范围是有限的。那如果很大的话,只能拆分成多个部分外加移位操作拼接了。
所以这剩下的12位可以再次划分,8位用作加载0-255中的任意值,4位用作对这个值做0~30位的循环右移。
这也就意味着这个立即数可以通过这个公式得到:v = n ror 2 * r。换句话说,有效的立即数都可以通过循环右移来得到。
这里有一个例子
有效值:
#256 // 1 循环右移 24位 --> 256
#384 // 6 循环右移 26位 --> 384
#484 // 121 循环右移 30位 --> 484
#16384 // 1 循环右移 18位 --> 16384
#2030043136 // 121 循环右移 8位 --> 2030043136
#0x06000000 // 6 循环右移 8位 --> 100663296 (十六进制值0x06000000)
Invalid values:
#370 // 185 循环右移 31位 --> 31不在范围内 (0 – 30)
#511 // 1 1111 1111 --> 比特模型不符合
#0x06010000 // 1 1000 0001.. --> 比特模型不符合