在 ARM 处理器的汇编语言中,对指令语法格式中的 <shifter_operand> 的常数表达式有这样的规定: “ 该常数必须对应 8 位位图,即常数是由一个 8 位的常数循环移位偶数位得到的。 ”
首先从 ARM 指令系统的语法格式说起。
一条 ARM 指令语法格式分为如下几个部分:
<opcode>{<cond>}{S} <Rd>,<Rn>{,<shifter_operand>}
其中, <> 内的项是必须的, {} 内的项是可选的,如 <opcode> 是指令助记符,是必须的,而 {<cond>} 为指令执行条件,是可选的,如果不写则使用默认条件 AL( 无条件执行 ) 。
Opcode 指令助记符,如 LDR , STR 等
Cond 执行条件,如 EQ , NE 等
S 是否影响 CPSR 寄存器的值,书写时影响 CPSR ,否则不影响
Rd 目标寄存器
Rn 第一个操作数的寄存器
shifter_operand 第二个操作数
其指令编码格式如下:
31-28 | 27-25 | 24-21 | 20 | 19-16 | 15-12 | 11-0 ( 12 位) |
cond | 001 | opcode | S | Rn | Rd | shifter_operand |
当第 2 个操作数的形式为:# immed_8r 常数表达式时 “ 该常数必须对应 8 位位图,即常数是由一个 8 位的常数循环移位偶数位得到的。 ”
其意思是这样:# immed_8r 在芯片处理时表示一个 32 位数,但是它是由一个 8 位数(比如: 01011010 ,即 0x5A )通过循环移位偶数位得到( 1000 0000 0000 0000 0000 0000 0001 0110 ,就是 0x5A 通过循环右移 2 位(偶数位)的到的)。
而 1010 0000 0000 0000 0000 0000 0001 0110 ,就不符合这样的规定,编译时一定出错。因为你可能通过将 1011 0101 循环右移位得到它,但是不可能通过循环移位偶数位得到。
1011 0000 0000 0000 0000 0000 0001 0110 ,也不符合这样的规定,很明显: 1 0110 1011 有 9 位。
为什么要有这样的规定?
那位大哥的理解是(小呆:这个的确是很有道理):
要从指令编码格式来解释(这就是我为什么一开始讲的是指令编码格式),仔细看表格中的 shifter_operand 所占的位数: 12 位。要用一个 12 位的编码来表示任意的 32 位数是绝对不可能的( 12 位数有 2^12 种可能,而 32 位数有 2^32 种)。
但是又要用 12 位的编码来表示 32 位数,怎么办?
只有在表示数的数量上做限制。通过编码来实现用 12 位的编码来表示 32 位数。
在 12 位的 shifter_operand 中: 8 位存数据, 4 位存移位的次数。
8 位存数据:解释了 “ 该常数必须对应 8 位位图 ” 。
4 位存移位的次数:解释了为什么只能移偶数位。 4 位只有 16 种可能值,而 32 位数可以循环移位 32 次( 32 种可能),那就只好限制:只能移偶数位(两位两位地移,好像一个 16 位数在移位, 16 种移位可能)。这样就解决了能表示的情况是实际情况一半的矛盾。
所以对# immed_8r 常数表达式的限制是解决指令编码的第二个操作数位数不足以表示 32 位操作数的无奈之举,但在我看来:这个可以说是聪明的做法。因为如果直接用 12 位数来表示 32 位操作数,只能表示 0 到( 2^12-1 )。大于 (2^12-1) 的数就没办法表示了。而且细细想来 “8 位存数据, 4 位存移位的次数 ” ,应该是最好的组合了(我并未想过所有的组合,只是顺便试了几个)。
ARM 指令第二操作数 #immed_8r 详解
大多数 ARM 通用数据处理指令有一个灵活的第 2 操作数 (flexible second operand), 这里这解释一下其中的一种格式, #immed_8r 常量的表达式。常量必须对应于 8 位位图 (pattern) 。该位图在 32 位字中,被循环移位偶数位 (0,2,4,...28,30) 。合法常量 0xff,0xff000,0xf000000f 。非法常量: 0x101,0xff04
ARM 在 32 位模式下,一条指令长度为 32 位,在上述数据处理指令中,操作数 2 为 12 位。所以像 0x7f02 这样的数,要两条指令才能完成。
MOV R3, #0x7F00 ; E3 A0 3C 7F 该指令自己完成 0x7f 移位
ORR R1, R3, #2
所以直接是找不到 0x7f02 的
#immed_8r 那个看明白了,但是请问图片里的那个 8~11 位的循环移位数, 0000 是不移位; 0001 是移动 2 位; 0010 是移动 4 位; 0011 是移动 6 位这样子吗?那么 0x7F00 是 7F 左移 8 位指令中变成 4 7F 请问为什么老大你给出的是 C 7F 呢?
这个大哥犯了一个错误,关于循环移位,其实 arm 中只有循环右移( ROR )。 0x7f 到 0x7f00 是通过循环右移 24 次才实现的,这里每次移动 2 位所以是 12 次( 0xc )
原文地址:http://blog.youkuaiyun.com/shansan/archive/2008/10/24/3137187.aspx