1. ARM伪指令概述
在ARM汇编开发中,伪指令(Pseudo-instruction)是汇编器提供的一种特殊指令形式,它们并非真实的机器指令,而是会被汇编器转换为一条或多条等效的实际指令。伪指令的存在极大简化了汇编代码的编写,让程序员可以用更直观、更接近高级语言的语法来表达底层操作。
注意:不同版本的ARM架构和不同厂商的汇编器支持的伪指令可能存在差异,使用时需查阅对应文档。
2. 常见ARM伪指令功能对照表
2.1 数据加载与存储类伪指令
| 伪指令格式 | 等效指令序列 | 功能说明 | 适用架构 |
|---|---|---|---|
| LDR Rd, =const | MOVW/MOVT或PC相对加载 | 加载32位立即数到寄存器 | ARMv7+ |
| ADR Rd, label | ADD/SUB Rd, PC, #offset | 加载标签地址到寄存器 | 全系列 |
| ADRL Rd, label | 两条ADD/SUB指令组合 | 加载较远标签地址 | 全系列 |
| MOV32 Rd, const | MOVW+MOVT组合 | 加载32位立即数 | ARMv7+ |
典型应用场景:
LDR Rd, =0x12345678:当需要加载一个32位立即数时,直接使用MOV指令可能无法表示(ARM指令中立即数有范围限制),此时伪指令会自动选择最优实现方式ADR R0, data_area:在位置无关代码中获取数据区域地址
实操技巧:在Thumb-2模式下,
LDR Rd, =const会根据立即数值大小自动选择MOV/MOVW+MOVT或文字池加载方式。
2.2 流程控制类伪指令
| 伪指令格式 | 等效指令序列 | 功能说明 | 条件标志 |
|---|---|---|---|
| CBZ Rn, label | CMP+B.EQ组合 | 寄存器为零则跳转 | Z=1 |
| CBNZ Rn, label | CMP+B.NE组合 | 寄存器非零则跳转 | Z=0 |
| IT{x{y{z}}} | 无(Thumb-2特性) | 条件执行块前缀 | 多种 |
实现原理:
- CBZ/CBNZ在ARMv7+的Thumb-2指令集中有对应的真实指令,但在传统ARM模式下会被展开为比较+分支的组合
- IT指令(If-Then)是Thumb-2特有的条件执行机制,最多支持4条指令的条件执行
代码示例:
armasm复制loop:
CBZ R1, exit_loop ; R1为0则退出循环
SUB R1, R1, #1
B loop
exit_loop:
2.3 数据操作类伪指令
| 伪指令格式 | 等效指令序列 | 功能说明 | 位数支持 |
|---|---|---|---|
| NEG Rd, Rn | RSB Rd, Rn, #0 | 取负值 | 32位 |
| MVN Rd, Rn | 同真实指令 | 按位取反 | 32位 |
| ASR Rd, Rn, #imm | 同真实指令 | 算术右移 | 32位 |
| RRX Rd, Rn | 真实指令 | 带扩展循环右移1位 | 32位 |
移位操作详解:
- ASR/LSL/LSR/ROR等移位操作在ARMv6+中都有对应的真实指令
- 移位量可以是立即数(0-31)或寄存器值
- RRX是唯一带C标志位移位的单指令操作
2.4 特殊功能伪指令
| 伪指令格式 | 功能说明 | 典型应用场景 |
|---|---|---|
| .ltorg | 声明文字池位置 | 保证LDR伪指令的访问范围 |
| .pool | 同.ltorg | 代码段末尾放置常量 |
| .unreq alias | 取消寄存器别名 | 宏定义结束后的清理 |
文字池使用示例:
armasm复制 LDR R0, =0x12345678 ; 可能生成到文字池的引用
; ... 大量代码 ...
.ltorg ; 在此处放置文字池
3. 伪指令使用注意事项
3.1 立即数范围限制
MOV指令只能加载特定格式的立即数(8位数值+4位旋转)- 当使用
LDR Rd, =const时,汇编器会:- 尝试用MOV/MOVW+MOVT实现
- 若无法表示,则放入文字池并生成PC相对加载
- 在Thumb模式下可能使用更复杂的转换序列
3.2 地址加载的精度问题
ADR的有效范围约±4095字节ADRL可处理约±1MB范围内的地址,但需要两条指令- 对于更远的地址必须使用
LDR Rd, =label
3.3 条件执行的限制
- Thumb-2的IT块最多包含4条指令
- IT块内的指令必须使用统一的条件码后缀
- 某些指令(如CPSID)不能出现在IT块内
4. 不同工具链的差异
4.1 GNU汇编器(GAS)特性
- 支持
.syntax unified指令统一ARM/Thumb语法 - 提供
.thumb_func声明Thumb函数 - 宏系统比Keil等商业工具更强大
4.2 ARMCC/Keil特性
- 支持
DCD等数据定义伪指令 - 提供
IMPORT/EXPORT管理符号可见性 - 有更丰富的调试信息伪指令
4.3 兼容性处理技巧
- 对于跨工具链的代码:
armasm复制#if defined(__GNUC__)
.syntax unified
#endif
- 关键代码段使用最基础的伪指令
- 避免使用工具链特有的宏扩展
5. 性能优化建议
5.1 伪指令展开分析
- 使用汇编器的列表文件功能查看伪指令展开结果
- ARMCC:
--asm --list=output.lst - GAS:
-a -ahl=output.lst
5.2 关键路径优化
- 将频繁使用的
LDR =const替换为寄存器加载 - 把
ADR可访问的常用数据放在代码附近 - 对性能敏感循环避免使用会展开为多条指令的伪指令
5.3 代码密度优化
- 在Thumb模式下优先使用CBZ/CBNZ
- 合理利用IT块减少分支指令
- 对多个连续常量使用单一文字池
6. 调试技巧与常见问题
6.1 调试器中的表现
- 大多数调试器会显示原始伪指令
- 单步执行时会进入展开后的实际指令
- 可能需要在反汇编窗口查看最终机器码
6.2 常见错误排查
-
"invalid constant"错误:
- 原因:立即数超出MOV可表示范围且文字池不可达
- 解决:添加
.ltorg或拆分操作为多条指令
-
"out of range"地址错误:
- 原因:ADR/ADRL的目标地址超出范围
- 解决:改用LDR=label或调整代码布局
-
IT块使用错误:
- 原因:指令数量/条件码不匹配
- 解决:检查IT指令中的条件码数量与后续指令是否一致
7. 实际工程应用案例
7.1 启动代码中的伪指令应用
armasm复制_start:
LDR R0, =_stack_top ; 加载栈顶地址
MOV SP, R0 ; 设置栈指针
LDR R1, =main ; 获取main函数地址
BX R1 ; 跳转到C入口
.pool ; 放置文字池
7.2 中断处理中的优化
armasm复制isr_handler:
PUSH {R0-R3, LR} ; 保存上下文
ADR R0, isr_data ; 获取专用数据区地址
CBZ R1, quick_exit ; 快速路径判断
; ... 完整处理流程 ...
quick_exit:
POP {R0-R3, PC} ; 快速恢复
7.3 宏定义中的灵活应用
armasm复制.macro COND_SUB cond, dst, src, val
ITT \cond
SUB\cond \dst, \src, #\val
.endm
COND_SUB gt, R0, R1, 4 ; 仅在GT条件下执行减法
掌握这些伪指令的正确使用方式,可以显著提升ARM汇编代码的开发效率和可维护性。在实际项目中,建议结合具体芯片的参考手册和工具链文档,了解特定环境下伪指令的实现细节。
