ARM指令中的"8位图"立即数格式

最近在学习"ARM9 原理和应用设计"一书. 书中在介绍ARM指令格式的时候, 指出:"ARM 指令中, 第二个操作数 (operand2) 如果是常数表达式的话, 该常数须对应8位位图 (Pattern), 即常数是由一个8位的常数循环移位偶数位得到的. 如 0x3FC, 0, 200 等是合法常量, 0x1FE, 511 等就是非法常量."

一开始我对这句话不是很明白, 书上举出的例子也没有想通. 于是打算从源头——ARM 指令格式开始研究.

一条典型的 ARM 指令的基本格式是 <opcode> {<cond>} {S} <Rd> ,<Rn> {,<operand2>} 共32位. 其中 <operand2> 占12位. 而在使用立即数寻址方式的时候, 数据就包含在指令的32位编码之中, 取出指令就是取出了可以立即使用的操作数. 现在考虑如下情况: 我们要使用立即数寻址的方式进行运算, 如果一个32位的立即数直接用在32位指令编码中, 就有可能完全占据32位编码空间, 使指令的操作码等无法在编码中得到体现. 而如果使用12位的 operand2 直接表示数据, 能够表示的数的范围又略显小了些. 因此, 在 ARM 指令编码中, 32位有效立即数是通过循环移偶数位而间接得到的. 具体的设计方法如下:

在 ARM 数据处理指令中, 当参与操作的第二操作数为立即数时, 每个立即数都是采用一个8位的常数循环右移偶数位而间接得到, 其中循环右移的位数有一个4位二进制的2倍表示. 则有效立即数可表示为: <immediate> := immed_8; 循环右移(2×rotate_imm). 其中: <immediate> 代表立即数, immed_8 代表8位常数,  即所谓的"8位图", rotate_imm 代表4位的循环右移值. 这样一来出现了一个问题: 尽管表示的范围变大了, 但是12位所能表现的数字的个数是一定的. 因此, ARM 规定并不是所有的32位常数都是合法的立即数, 只有通过上面的构造方法得到的才是合法的立即数, 编译的时候才不会报错.

举个例子吧.
0x3FC(0000 0000 0000 0000 0000 0011 1111 1100) 是由 0xff 循环右移 2 位得到的;
200(0000 0000 0000 0000 0000 0000 1100 1000) 是由 0xc8 循环右移 2 位得到的, 它们都是合法的.
而 0x1FE(0000 0000 0000 0000 0000 0001 1111 1110) 和
511(0000 0000 0000 0000 0000 0001 1111 1111) 无法看成是8位的常数循环右移偶数位而得到的, 因此是非法的.

比较下来, 我们可以这样总结:

  1. 判断一个数是否符合8位位图的原则, 首先看这个数的二进制表示中1的个数是否不超过8个. 如果不超过8个, 再看这n个1(n<=8)是否能同时放到8个二进制位中, 如果可以放进去, 再看这八个二进制位是否可以循环右移偶数位得到我们欲使用的数. 如果可以, 则此数符合8位位图原理, 是合法的立即数. 否则, 不符合.
  2. 无法表示的32位数, 只有通过逻辑或算术运算等其它途径获得了. 比如0xffffff00, 可以通过0x000000ff按位取反得到.
因此以后的编程中, 时刻检查用到的第二操作数是否符合8位位图是一件千万不能疏忽的事. 至于为什么要将这12位 operand2 "八四开", 这个问题就要请教大牛了.

你可能感兴趣的:(ARM)