在ARM架构中,数据处理操作数是执行算术和逻辑运算的基础构建块。这些操作数通过灵活的移位和旋转机制,实现了高效的寄存器操作和立即数处理。作为一位长期从事嵌入式开发的工程师,我发现理解这些操作数的运作原理对于编写高效ARM汇编代码至关重要。
ARM指令集提供了11种不同的数据处理操作数格式,每种格式都有其特定的应用场景和编码方式。这些操作数格式可以分为三大类:
提示:在ARMv6及更高版本的架构中,所有11种操作数格式都得到了完整支持。早期的ARM架构(如ARMv4)可能不支持某些高级操作数格式。
ARM指令中的立即数采用了一种独特的编码方式:一个8位的常数(immed_8)通过右旋转偶数位(0,2,4,...,30)来形成32位的立即数。这种设计使得指令编码紧凑,同时又能表示广泛的常数值。
具体编码公式为:
code复制立即数 = immed_8 右旋转 (2 × rotate_imm) 位
例如:
并非所有32位值都能作为合法立即数。只有那些可以通过8位常数旋转偶数位得到的数值才是有效的。例如:
在实际编程中,当遇到非法立即数时,通常需要通过多条指令组合或使用加载指令(如LDR)将值存入寄存器。
assembly复制MOV R0, #0xFF ; 将0xFF加载到R0
ADD R3, R3, #0x100 ; R3 += 0x100
CMP R7, #0xFF00 ; 比较R7和0xFF00
最简单的寄存器操作数直接使用寄存器中的值作为操作数。这种形式在指令编码中被视为移位量为0的逻辑左移(LSL #0)。
assembly复制MOV R2, R0 ; R2 = R0
ADD R4, R3, R2 ; R4 = R3 + R2
CMP R7, R8 ; 比较R7和R8
在指令编码中,寄存器操作数使用以下格式:
这种编码方式使得硬件实现可以统一处理所有类型的操作数。
ARM支持两种逻辑移位操作:
assembly复制MOV R2, R0, LSL #2 ; R2 = R0 << 2 (即R0×4)
ADD R9, R5, R5, LSL #3 ; R9 = R5 + (R5 << 3) = R5 × 9
assembly复制MOV R12, R4, LSL R3 ; R12 = R4 << R3
算术右移(ASR)与逻辑右移不同,它会保持符号位不变:
assembly复制MOV R0, R1, ASR #2 ; 算术右移2位,保持符号
ARM支持两种旋转操作:
assembly复制MOV R0, R1, ROR #8 ; 将R1循环右移8位
MOV R0, R1, RRX ; 带进位位的循环右移1位
UXTB(Unsigned ExTend Byte)指令从寄存器中提取8位值并零扩展到32位:
assembly复制UXTB R0, R1 ; 提取R1的最低8位并零扩展到32位存入R0
UXTB R0, R1, ROR #8 ; 先旋转R1,再提取和扩展
UXTH(Unsigned ExTend Halfword)指令从寄存器中提取16位值并零扩展到32位:
assembly复制UXTH R0, R1 ; 提取R1的最低16位并零扩展到32位
UXTB16指令同时提取两个8位值并分别零扩展到16位:
assembly复制UXTB16 R0, R1 ; R0[15:0] = 零扩展(R1[7:0])
; R0[31:16] = 零扩展(R1[23:16])
ARM指令的一个重要特性是条件执行,几乎所有指令都可以根据条件码来决定是否执行:
assembly复制ADDEQ R0, R1, R2 ; 仅在Z标志置位时执行加法
MOVNE R0, #0 ; 仅在Z标志清零时执行移动
条件码位于指令的最高4位,共有16种可能的条件。
许多数据处理指令可以通过设置S后缀来更新条件标志位(N,Z,C,V):
assembly复制ADDS R0, R1, R2 ; 执行加法并更新标志位
SUBS R0, R1, #1 ; 执行减法并更新标志位
标志位的具体含义:
利用移位和加法可以实现高效的常数乘法:
assembly复制; 计算R0 × 15
ADD R0, R0, R0, LSL #1 ; R0 = R0 + (R0 << 1) = R0 × 3
ADD R0, R0, R0, LSL #2 ; R0 = R0 + (R0 << 2) = R0 × 15
assembly复制; 提取R0的第n位(n在R1中)
MOV R2, #1
AND R3, R0, R2, LSL R1 ; R3 = (R0 >> n) & 1
assembly复制; 将两个16位值打包到R0
ORR R0, R1, R2, LSL #16 ; R0 = R1 | (R2 << 16)
问题:尝试使用非法立即数时,汇编器报错。
解决方案:
问题:移位量超过31时行为未定义。
解决方案:
问题:使用R15作为操作数寄存器时结果不可预测。
解决方案:
不同ARM架构版本对数据处理操作数的支持有所差异:
| 指令类型 | ARMv4 | ARMv5 | ARMv6 |
|---|---|---|---|
| 基本操作数 | 是 | 是 | 是 |
| UXTB/UXTH | 否 | 否 | 是 |
| 寄存器移位 | 是 | 是 | 是 |
| 立即数旋转 | 是 | 是 | 是 |
在编写可移植代码时,需要检查目标平台支持的架构版本。