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)的关系,是掌握它的关键。

相关推荐
2301_7722042818 小时前
ARM基础知识2
arm开发
我和我导针锋相队19 小时前
C语言“造轮子”大赛:重拾系统编程的工匠精神
arm开发
皮蛋sol周1 天前
嵌入式学习数据结构(三)栈 链式 循环队列
arm开发·数据结构·学习·算法··循环队列·链式队列
小程同学>o<1 天前
嵌入式之ARM体系与架构面试题(四)通信协议篇
arm开发·笔记·架构·嵌入式软件·通信协议·面试题库
乾复道1 天前
巧用终端,每天节省2小时
汇编·经验分享·vim
梁洪飞1 天前
kernel 内存知识
linux·arm开发·嵌入式硬件·arm
代码游侠1 天前
ARM 嵌入式开发学习——从内核到外设
arm开发·笔记·嵌入式硬件·学习
飞凌嵌入式1 天前
全志T153处理器ARM+RISC-V的双核协作实测
arm开发·risc-v
切糕师学AI1 天前
ARM 运算中的MSB(最高有效位)
arm开发
梁洪飞2 天前
clk学习
linux·arm开发·嵌入式硬件·arm