ARM 汇编指令:LDM

ARM 汇编指令:LDM

本文来自于我关于 ARM 汇编指令系列文章。欢迎阅读、点评与交流~
1、汇编指令在不同架构中的联系与区别
2、ARM 汇编指令:MOV
3、ARM 汇编指令:LDR
4、ARM 汇编指令:STR
5、ARM 汇编指令:MRS 和 MSR
6、ARM 汇编指令:ORRS
7、ARM 汇编指令:BEQ
8、ARM 汇编指令:TST
9、ARM 汇编指令:B
10、ARM 汇编指令:BX
11、ARM 汇编指令:ERET
12、ARM 汇编指令:STP\LDP
13、ARM 汇编指令:UBFX
14、ARM 汇编指令:STM
15、ARM 汇编指令:LDM

1. 核心定义

LDMLoad Multiple 的缩写,意为"加载多个寄存器"。它是 ARM 汇编中用于从连续的内存地址中一次性加载数据到多个寄存器的指令。

它通常与 STM 指令配对使用,后者用于将多个寄存器的值存储到连续的内存中。

2. 基本语法

复制代码
LDM{addr_mode}{cond} Rn{!}, reglist{^}
  • LDM:指令本身。
  • {addr_mode}地址模式后缀 。这是关键部分,决定了地址的增长方式和数据的加载顺序。常见的有:
    • IA : Increment After (默认,可省略)。每次加载后地址增加。
    • IB : Increment Before。每次加载前地址增加(仅用于 ARM 特权模式)。
    • DA : Decrement After。每次加载后地址减少。
    • DB : Decrement Before。每次加载前地址减少。
  • {cond} :可选的条件码,如 EQ, NE, GT 等。
  • Rn基址寄存器,其中保存着内存起始地址。
  • {!} :可选的回写后缀。如果加上 !,则指令执行后,会将计算得到的最终地址写回 Rn 基址寄存器。这在堆栈操作和数据块移动中非常有用。
  • reglist寄存器列表 。用大括号 {} 括起来,包含要加载数据的寄存器。例如 {R0, R4-R7, R10}。寄存器在列表中的顺序不重要加载总是按照寄存器编号从小到大的顺序进行 ,而地址的递增/递减方向由 addr_mode 决定。
  • {^} :可选的特权后缀。有两个含义:
    • 如果 reglist包含 PC 寄存器^ 表示除了正常加载数据外,还会将 SPSR 的内容复制到 CPSR(用于从异常处理返回)。
    • 如果 reglist不包含 PC 寄存器^ 表示加载的是用户模式下的寄存器,而不是当前特权模式的寄存器。

3. 工作原理(以最常见的 LDMIA 为例)

假设执行指令:LDMIA R0!, {R1, R3, R5}

  • 内存起始地址是 R0 中的值。
  • 虽然列表写的是 {R1, R3, R5},但 ARM 硬件会按编号排序为 R1, R3, R5
  • **IA(后增)**模式:
    1. [R0] 处的 4 字节数据加载到 R1
    2. [R0+4] 处的 4 字节数据加载到 R3
    3. [R0+8] 处的 4 字节数据加载到 R5
  • 因为使用了 !,指令执行后,R0 = R0 + 12(3个寄存器 * 4字节)。

4. 与堆栈操作的关系

ARM 为堆栈操作(后进先出 LIFO)定义了更直观的别名。堆栈由**堆栈指针 SP(R13)**管理,可以向下增长(满递减)或向上增长(空递增)。

标准指令 堆栈别名 含义 常见用途
LDMDB LDMFD Full Descending 堆栈的弹出操作 ARM 默认堆栈类型(向下增长,满栈)
LDMIA LDMEA Empty Ascending 堆栈的弹出操作 较少使用
STMDB STMFD Full Descending 堆栈的压入操作 ARM 默认堆栈的压栈
STMIA STMEA Empty Ascending 堆栈的压入操作 较少使用

记住这个口诀:PUSH = STMFDPOP = LDMFD

5. 经典用例

a) 块数据复制

armasm 复制代码
; 将 R1 指向的源地址处的 4 个字,复制到 R2 指向的目标地址
LDMIA R1!, {R4-R7} ; 从源地址加载4个寄存器
STMIA R2!, {R4-R7} ; 存储到目标地址

b) 子程序进入/退出(保护与恢复寄存器)

armasm 复制代码
; 进入子程序时,将工作寄存器压栈保护
STMFD SP!, {R4-R12, LR} ; 压栈,LR (R14) 是返回地址

; ... 子程序主体 ...

; 退出子程序时,从堆栈恢复寄存器并返回
LDMFD SP!, {R4-R12, PC} ; 弹出,直接将返回地址加载到 PC (R15),实现跳转
; 等同于 POP {R4-R12, PC}

c) 异常返回

armasm 复制代码
; 从 IRQ 异常处理程序返回
SUBS PC, LR, #4          ; 简单方法

; 或使用 LDM 从堆栈恢复所有上下文(包括 PC 和 CPSR)
LDMFD SP!, {R0-R12, LR}  ; 恢复通用寄存器和链接寄存器
RFEFD SP!                ; 使用 RFE 指令返回(现代方式)
; 或者使用带 ^ 的 LDM(传统方式)
; LDMFD SP!, {R0-R12, PC}^ ; ^ 表示同时将 SPSR 复制到 CPSR

6. 重要注意事项

  1. 加载顺序固定 :无论 reglist 如何书写,总是 R0(如果存在)从最低地址加载,R1 从下一个地址加载,以此类推。
  2. 基址寄存器对齐:地址通常是字对齐的(4字节边界)。
  3. PC 的特殊性 :如果 reglist 包含 PC(R15),它总是最后被加载。加载到 PC 的值将导致程序跳转。
  4. 效率 :一条 LDM 指令可以加载多个寄存器,这比用多条 LDR 指令更高效,因为它减少了指令取指和解码的开销。

总结

LDM 是 ARM 架构中一个强大且高效的批量数据加载指令,尤其在与 STM 配对用于堆栈操作内存块复制 时,是 ARM 汇编编程的基石之一。理解其地址模式(特别是 IA/DB)和与堆栈别名(FD/EA)的关系,是掌握它的关键。

相关推荐
浩浩测试一下1 天前
抬栈 恢复上下文 (逆向分析)
汇编·逆向·堆栈·windows核心编程
zhouwy1131 天前
ARM汇编指令集详解
汇编·arm开发
iCxhust1 天前
微机原理实践教程(汇编篇)---A002流水灯
汇编·单片机·嵌入式硬件·51单片机·微机原理
浩浩测试一下1 天前
栈帧 抬栈与平栈 (逆向分析)
汇编·windows api·堆栈·windows编程·windows 开发
陈eaten1 天前
win11下nasm编写汇编及链接方案
汇编·链接·nasm·gcc·golink
iCxhust1 天前
【无标题】8086/8088裸机对于学习微机原理的重要意义
汇编·单片机·嵌入式硬件·嵌入式·微机原理
山后太阳2 天前
Keil5(MDK-ARM)完整下载安装教程+入门教程:从零搭建STM32开发环境
arm开发·stm32·嵌入式硬件
zz_lzh2 天前
arm版AI牛马:armbian(rk3588)设备部署openclaw
arm开发·人工智能·arm
lanxiao88883 天前
F1C100S 内核
arm开发
杰杰桀桀桀3 天前
基于stm32ARM库函数的IIR二阶巴特沃斯低通滤波器--附完整代码
arm开发·stm32·嵌入式硬件·数字滤波器·巴特沃斯低通滤波