ARM处理器作为RISC架构的典型代表,其指令集设计以精简高效著称。在ARMv4T架构中,指令主要分为数据处理、内存访问、分支和控制四大类。我们今天重点探讨的是前两类——数据处理指令和内存访问指令,它们是构建任何ARM程序的基础模块。
数据处理指令包括:
内存访问指令则包含:
这些指令通过条件执行、灵活的寻址模式和丰富的移位操作组合,能够实现高度优化的底层代码。在嵌入式开发中,理解这些指令的细微差别往往决定着程序的性能和可靠性。
ADD和SUB指令是ARM汇编中最基础的算术运算指令,其语法格式为:
armasm复制ADD{cond}{S} Rd, Rn, Operand2
SUB{cond}{S} Rd, Rn, Operand2
其中Operand2的设计体现了ARM指令集的灵活性,它可以是:
实际案例:
armasm复制ADD R3, R7, #1020 @ 立即数1020(0x3FC)是0xFF循环右移30位
SUBS R8, R6, #240 @ 减法并设置条件标志
RSB R4, R4, #1280 @ 反向减法(1280-R4)
注意事项:当使用S后缀时,指令会更新APSR中的N(负)、Z(零)、C(进位)、V(溢出)标志位。这在实现条件分支和溢出检测时至关重要。
AND、ORR、EOR和BIC指令构成了ARM的逻辑运算基础:
armasm复制AND{cond}{S} Rd, Rn, Operand2 @ 按位与
ORR{cond}{S} Rd, Rn, Operand2 @ 按位或
EOR{cond}{S} Rd, Rn, Operand2 @ 按位异或
BIC{cond}{S} Rd, Rn, Operand2 @ 位清除(Rn AND NOT Operand2)
典型应用场景:
armasm复制AND R0, R5, R2 @ 基本位与操作
BIC R1, R1, #0xFF @ 清除R1的低8位
EOR R3, R3, #0x80 @ 翻转第7位
ARM提供了丰富的移位操作,可作为独立指令或与其他指令结合使用:
| 操作符 | 描述 | 示例 |
|---|---|---|
| LSL | 逻辑左移 | MOV R1, R0, LSL #2 |
| LSR | 逻辑右移 | MOV R1, R0, LSR #3 |
| ASR | 算术右移(保持符号位) | MOV R1, R0, ASR #4 |
| ROR | 循环右移 | MOV R1, R0, ROR #5 |
| RRX | 带扩展的循环右移1位 | MOV R1, R0, RRX |
关键细节:
LDR和STR指令是ARM架构中内存访问的基石,支持多种寻址模式:
armasm复制LDR{cond}{B}{T} Rd, [Rn, #offset] @ 从内存加载
STR{cond}{B}{T} Rd, [Rn, #offset] @ 存储到内存
寻址模式对比:
| 模式 | 语法示例 | 地址计算时机 | 回写 |
|---|---|---|---|
| 零偏移 | LDR R0, [R1] | 使用Rn值 | 无 |
| 前索引 | LDR R0, [R1, #4]! | 先计算Rn+offset | 是 |
| 后索引 | LDR R0, [R1], #4 | 先使用Rn,后更新 | 是 |
| 程序相对 | LDR R0, label | PC相对计算 | 无 |
对齐问题处理:
LDM和STM指令可以高效地传输多个寄存器,特别适用于栈操作和上下文切换:
armasm复制LDM{cond}mode Rn{!}, reglist
STM{cond}mode Rn{!}, reglist
模式说明:
| 模式 | 全称 | 典型应用 |
|---|---|---|
| IA | Increment After | 常规内存访问 |
| IB | Increment Before | ARM中较少使用 |
| DA | Decrement After | 逆向内存访问 |
| DB | Decrement Before | 栈操作(满递减栈) |
| FD | Full Descending | ARM标准栈模式 |
栈操作示例:
armasm复制STMFD SP!, {R0-R3, LR} @ 压栈保存寄存器
LDMFD SP!, {R0-R3, PC} @ 出栈并返回
SWP指令实现原子交换,是构建信号量的基础:
armasm复制SWP{cond}{B} Rd, Rm, [Rn] @ 原子交换Rd←[Rn], [Rn]←Rm
使用场景:
armasm复制MOV R0, #1
SWP R1, R0, [R2] @ 原子测试并设置信号量
CMP R1, #0 @ 检查原值
BNE semaphore_busy @ 如果已被占用则跳转
ARM指令集最强大的特性之一是条件执行,几乎所有指令都可以通过条件码后缀来条件执行:
armasm复制ADDNE R0, R1, R2 @ 仅当Z=0时执行
MOVGT R3, #1 @ 仅当Z=0且N=V时执行
条件码速查表:
| 后缀 | 含义 | 标志位条件 |
|---|---|---|
| EQ | 等于 | Z=1 |
| NE | 不等于 | Z=0 |
| CS/HS | 无符号大于等于 | C=1 |
| CC/LO | 无符号小于 | C=0 |
| MI | 负数 | N=1 |
| PL | 正数或零 | N=0 |
| VS | 溢出 | V=1 |
| VC | 无溢出 | V=0 |
| HI | 无符号大于 | C=1且Z=0 |
| LS | 无符号小于等于 | C=0或Z=1 |
| GE | 有符号大于等于 | N=V |
| LT | 有符号小于 | N!=V |
| GT | 有符号大于 | Z=0且N=V |
| LE | 有符号小于等于 | Z=1或N!=V |
| AL | 无条件执行(默认) | 任何 |
立即数使用技巧:
多寄存器传输优化:
缓存友好代码:
延迟隐藏技巧:
armasm复制LDR R0, [R1] @ 加载内存(有延迟)
ADD R2, R3, R4 @ 不依赖R0的指令
ADD R0, R0, R5 @ 此时加载可能已完成
问题1:非对齐访问异常
问题2:条件标志意外修改
问题3:寄存器冲突
armasm复制@ 高效内存拷贝(R0=目标, R1=源, R2=字节数)
copy_loop:
LDMIA R1!, {R3-R6} @ 一次加载4个字
STMIA R0!, {R3-R6}
SUBS R2, R2, #16 @ 每次迭代处理16字节
BGT copy_loop
armasm复制@ 设置位宏(R0=目标寄存器, R1=位位置)
.macro SET_BIT
MOV R2, #1
ORR R0, R0, R2, LSL R1
.endm
@ 清除位宏
.macro CLR_BIT
MOV R2, #1
BIC R0, R0, R2, LSL R1
.endm
armasm复制@ 条件字符串拷贝(R0=目标, R1=源, R2=最大长度)
MOV R3, #0
copy_str:
LDRB R4, [R1], #1
CMP R4, #0
BEQ copy_done
STRB R4, [R0], #1
ADD R3, R3, #1
CMP R3, R2
BLT copy_str
copy_done:
在嵌入式开发实践中,掌握这些ARM汇编指令的细微差别和优化技巧,能够显著提升代码性能和可靠性。特别是在中断处理、启动代码、性能关键路径等场景中,精心编写的汇编代码往往能带来数量级的性能提升。