在ARM汇编的数据处理指令中经常会使用到常数,而ARM汇编中规定使用的常数必 须是立即数;为什么会有立即数这样的规定呢?
答:这是由于所有的ARM指令是精简指令集,指令长度固定都是32位,对于ARM数据处理指令自然也是一样。数据处理指令大致可包含3类,数据传送指令、数据算术逻辑运算指令和数据比较指令。在一条ARM数据处理指令中,除了要包含处理的数据值外,还要标识ARM命令名称,控制位,寄存器等其他信息。这样在一条ARM数据处理指令中,能用于表示要处理的数据值的位数只能小于32位;
一条ARM指令语法格式分为如下几个部分:
<opcode>{<cond>}{S} <Rd>,<Rn>{,<shifter_operand>}
其中,<>内的项是必须的,{}内的项是可选的,如<opcode>是指令助记符,是必须的,而{<cond>}为指令执行条件,是可选的,如果不写则使用默认条件AL(无条件执行)。
Opcode 指令助记符,如LDR,STR 等
Cond 执行条件,如EQ,NE 等
S 是否影响CPSR 寄存器的值,书写时影响CPSR,否则不影响
Rd 目标寄存器
Rn 第一个操作数的寄存器
shifter_operand 第二个操作数(第二操作数有三种形式: 1-- 立即数 add r1,r2,#10 ; 2-- 寄存器 addeqs r1,r2,r3 @ r1=r2+r3 ; 3-- 寄存器移位 add r1,r2,r3,LSL #2 @ r1 = r2 +r3*4)
ARM在指令格式中设定,只能用指令机器码32位中的低12位来表示要操作的常数。ARM处理器是按32位来处理数据的,ARM处理器处理的数据是32位,如果简单的用这12位来表示,显然范围太小了,为了扩展到32位,因此使用了构造的方法,在12位中用8位表示基本数据值,用4位表示位移值,通过用8位基本数据值往右循环移动4位位移值*2次,来表示要操作的常数。这里要强调最终的循环次数是4位位移值乘以2得到的,所以得到的最终循环次数肯定是一个偶数,为什么要乘以2呢,实质还是因为范围不够,4位表示位移次数,最大才15次(移位值==0:等于没有循环,不算),加上8位数据还是不够32位,这样只能通过ALU的内部结构设计将4位位移次数乘以2,这样就能用12位表示32位常数了。
立即数表示方法:立即数(#immed_8r) = X循环右移Y*2; //为何要*2 ? 答:红色。为何要移偶数位? 因为要*2 构造成32位
[31 11] | [11 8] Y:移位值最大 1111b = 15 | [7 0] X:8bit常数 |
所以对#immed_8r常数表达式的限制是解决指令编码的第二个操作数位数不足以表示32位操作数的无奈之举,但总比只有12位所表达的数大。
所以:一个8bit常数循环右移(Y*2 = {0,2,4,6,8, ...,26, 28, 30})就得到一个立即数了;
转自:https://blog.csdn.net/linkedin_35878439/article/details/79252808
二、如何判断一个数是否是合法立即数?
- 首先将这个数转换为32bit16进制形式,例如218=0xDA=0x000000DA
- 除零外,仅有一位数为合法立即数。
- 除零外,仅有二位数,并且相邻(包括首尾,如0x1000000A)的为合法立即数。
- 除零外,仅有三位数,并且相邻(包括中间有0相间,例如0x10800000,包括首尾相邻,如:0x14000003),这三位数中,最高位取值仅能为1、2、3,最低位取值仅能为4、8、C,中间位0x0~0xF。这种组合的为合法立即数。
- 除了以上三种,其他基本是非法立即数。
三、非法立即数如何输入?
利用LDR伪指令可将任意32bit的立即数赋给寄存器。
格式:LDR RD,=#Imm32
编译时,编译器会优先使用MOV或者MVN指令来加载立即数,以便提高代码运行效率,如不行,则一般编译成如下形式:
LDR RD,[PC,#offset]
....
PC+OFFSET:.word Imm32