ARM汇编指令⽴即数规范和判断
在ARM汇编的数据处理指令中经常会使⽤到常数,⽽ARM汇编中规定使⽤的常数必须是⽴即数。
在讨论什么是⽴即数,为什么有⽴即数,如何快速判断⽴即数之前,我们先来弄明⽩⼀个问题:什么不是⽴即数。
汇编指令是对数据(指令、数据)对象的操作,很关键的⼀个问题我们如何寻我们的操作对象?汇编指令是⼀门关于寻址的艺术。ARM 有九种寻址⽅式:1.寄存器寻址2.⽴即寻址3.寄存器移位寻址4.寄存器间接寻址5.基址寻址6.多寄存器寻址7.堆栈寻址8.块拷贝寻址9.相对寻址。 九中寻址⽅式都是再告诉我们如何寻我们的操作对象,其中⽴即数寻址相对于其他寻址⽅式有什么不同?
为了理解ARM指令是如何实现对数据对象操作,我们需要知道ARM指令的机器码格式。(熟悉的可以直接跳到如何快速判断合法⽴即数)(ARM指令的典型编码格式如下:)
ARM指令语法格式如下:
opcode{} {s} rd, rn,
各个部分解释如下:
cond(bit[31:28]):指令的条件码助记符,默认是al(⽆条件执⾏)
*type(bit[27:32]):指令码类型,根据其编码的不同,所代表的类型:00:数据处理指令及杂类Load/Store指令;01:Load/Store指令;10:批量Load/Store指令及分⽀指令;11:协处理指令与软中断指令
*X(bit[25]) :第⼆操作数类型标志码。(X=0表⽰第⼆操作数是移位寄存器,X=1表⽰第⼆操作数是⽴即数)opcode(bit[24:21]) :指令助记符,如mov
S(bit[20]): 指令的执⾏是否影响CPSR(当前程序状态寄存器)的值(S:state)
Rn(bit[19:16]): 包含第⼀个操作数的寄存器编码(Register Num)
Rd(bit[15:12]): ⽬标寄存器编码(Register Destination)
opcode2(bit[11:0]): 第⼆操作数
ARM指令的第⼆操作数⽤法⽐较灵活,有如下⼏种情况。 * ⽴即数 * 寄存器
寄存器移位
ARM指令⾥有两个操作数Rn,opcode2和⼀个⽬的寄存器Rd(⽤于存放操作结果),Rn为包含第⼀个操作数的寄存器地址,opcode2有三种形式:⽴即数⽅式、寄存器⽅式、寄存器移位⽅式,其中寄存器和寄存器位移⽅式也都是存储的寄存器地址所以要通过寄存器间接获得数据对象,也就是⾮⽴即,⽽⽴即数形式不同,指令中opcode2数据域不是地址⽽是数据本⾝,所以叫⽴即数。也就是说其它寻值⽅式最终操作的数据对象是放在Rn或opcode2所指向的寄存器地址中的数据,显然⽴即数操作对象也需要根据地址寻,但它所在的地址⽐较特殊,或者说它存储的位置⽐较特殊,因为它直接存储在指令opcode2中。
⽽opcode2只有12位,也就是说opcode2所表⽰的⽴即数有⼀定限制0-4095,为了进⼀步扩⼤12bit数据所能表⽰数的范围,ARM规定了数据的格式:也即
⽴即数是由⼀个8位的常数循环右移偶数位得到的,其中循环右移 的位数由⼀个4位2进制的两倍表⽰,公式如下:
immediate=immed_8<<(2*rotate_imm4) “<<”表⽰循环右移 简单的说⼀个常数如果可以由⼀个8位的常数循环移位偶数位得到,那么就是⽴即数。
为什么会有⽴即数这样的规定呢?这是由于所有的ARM指令是精简指令集,指令长度固定都是32位,对于ARM数据处理指令⾃然也是⼀样。数据处理指令⼤致可包含3类,数据传送指令、数据算术逻辑
运算指令和数据⽐较指令。在⼀条ARM数据处理指令中,除了要包含处理的数据值外,还要标识ARM命令名称,控制位,寄存器等其他信息。这样在⼀条ARM数据处理指令中,能⽤于表⽰要处理的数据值的位数只能⼩于32位。
ARM在指令格式中设定,只能⽤指令机器码32位中的低12位来表⽰要操作的常数。ARM处理器是按32位来处理数据的,ARM处理器处理的数据是32位,如果简单的⽤这12位来表⽰,显然范围太⼩了,为了扩展到32位,因此使⽤了构造的⽅法,在12位中⽤8位表⽰基本数据值,⽤4位表⽰位移值,通过⽤8位基本数据值往右循环移动4位位移值*2次,来表⽰要操作的常数。这⾥要强调最终的循环次数是4位位移值乘以2得到的,所以得到的最终循环次数肯定是⼀个偶数,为什么要乘以2呢,实质还是因为范围不够,4位表⽰位移次数,最⼤才15次,加上8位数据还是不够32位,这样只能通过ALU的内部结构设计将4位位移次数乘以2,这样就能⽤12位表⽰32位常数了。
通过循环偶数位得的到操作数,扩⼤了操作数的范围,但也带来了问题,并不是每个数据都能通过8位基本数据循环移动偶数为得到,如果你在ARM数据处理指令中使⽤的操作数,不是⽴即数,⽐如MOV R1,#0x12345678,编译器就会报错,所以我们在使⽤前必须进⾏判断,这也是很多ARM相关求职笔试中常考的⼀道题⽬。
那怎样怎么快速判断⼀个数是否是⽴即数?对于简单的数字我们可以直接判断,⽐如⼩于255的数字肯
定是⽴即数。对相对复杂的数字进⾏判断就需要先把它转换为2进制形式,然后根据定义进⾏判断了。我这⾥总结了个⽐较快速的⽅法:(简⽽⾔之,就是利⽤⽴即数的⽣成⽅法,迅速逆推到合法的8位常熟)
1、把数据转换成⼆进制形式,从低位到⾼位写成4位1组的形式,最⾼位⼀组不够四位的,在最⾼位前⾯补0。
2、数1的个数,如果⼤于8个肯定不是⽴即数,如果⼩于等于8进⾏下⾯步骤。
3、如果数据中间有连续的⼤于等于24个0,循环左移4的倍数,使⾼位全为0。
4、到最⾼位的1,去掉前⾯最⼤偶数个0。
5、到最低位的1,去掉后⾯最⼤偶数个0。
6、数剩下的位数,如果⼩于等于8位,那么这个数就是⽴即数,反之就不是⽴即数。
针对可能现的情况,举5个典型例⼦:
(1)0x4FF (2)0x122 (3)0x234 (4)0xF000000F (5)0x8000007F
例1: 0x4FF
第⼀步:0100 1111 1111
第⼆步:其中1的个数是9个,⼤于8个,判定不是⽴即数
例2: 0x122
第⼀步: 0001 0010 0010
第⼆步: 其中1的个数4个,⼩于8,继续
第三步: 其中没有连续⼤于等于24个0,继续
第四部: xx01 0010 0010 (最⾼位前⾯有3个0,最⼤偶数2,去掉2个0)
第五步: xx10 0011 0010 (最低位后⾯只有1个0,最⼤偶数0)
第六部: 剩下10 0011 0010 共10位,⼤于8,判定0x122不是⽴即数
例3: 0x234
第⼀步: 0010 0011 0100
第⼆步: 其中1的个数4个,⼩于8,继续
第三步: 其中没有连续⼤于等于24个0,继续
第四部: xx10 0011 0100
第五步: xx10 0011 01xx
第六部: 剩下10 0011 01 共8位,等于8,判定0x234是⽴即数
例4: 0xF000000F
第⼀步: 1111 0000 0000 0000 0000 0000 0000 1111
第⼆步: 其中1的个数8个,没有⼤于8,继续
第三步: 其中有连续24个0,循环左移4位,使⾼位全为0
0000 0000 0000 0000 0000 0000 0000 1111 1111
第四部: xxxx xxxx xxxx xxxx xxxx xxxx xxxx 1111 1111
第五步: xxxx xxxx xxxx xxxx xxxx xxxx xxxx 1111 1111
第六部: 剩下1111 1111共8位,等于8,判定0xF000000F是⽴即数
例5: 0x8000007F
第⼀步: 1000 0000 0000 0000 0000 0000 0111 1111
第⼆步: 其中1的个数8个,没有⼤于8,继续
第三步: 其中有连续24个0,循环左移4位,使⾼位全为0
0000 0000 0000 0000 0000 0000 0111 1111 1000
第四部: xxxx xxxx xxxx xxxx xxxx xxxx 0111 1111 10xx
第五步: xxxx xxxx xxxx xxxx xxxx xxxx 0111 1111 10xx
第六部: 剩下0111 1111 10共10位,等于8,判定0x7000008F是⽴即数
问题还没有结束,我们在ARM汇编中如何规避⽴即数这个问题呢,其实可以使⽤ARM汇编LDR伪指令,例如直接把MOV指令变为, LDR R1,=0x12345678这样编译器就不会报错了。但这种⽅法也有弊端会增加开销和影响执⾏效率。同时ARM汇编中还有有效数的概念,⽐如 MOV R1,#0xFFFFFFFF 指令中 0xFFFFFFFF 不是⽴即数,但是是有效数,编译器最⾃动把原指令变换为 MVN R1,#0,也不会报错。有效数判定:原数是⽴即数或者原数反码是⽴即数。
汇编判断指令希望对⼤家有帮助。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论