00. 目录

01. Load/Store指令概述

Load/Store 内存访问指令在 ARM 寄存器和存储器之间传送数据。ARM 指令中有 3 种基本的数据传送指令。

(1)单寄存器 Load/Store 指令(Single Register),这些指令在 ARM 寄存器和存储器之间提供更灵活的单数据项传送方式。数据项可以是字节、16 位半字或 32 位字。

(2)多寄存器 Load/Store 内存访问指令。这些指令的灵活性比单寄存器传送指令差,但可以使大量的数据更有效地传送。它们用于进程的进入和退出、保存和恢复工作寄存器及复制存储器中的一块数据。

(3)单寄存器交换指令(Single Register Swap)。这些指令允许寄存器和存储器中的数值进行交换,在一条指令中有效地完成 Load/Store 操作。它们在用户级编程中很少用到。它的主要用途是在多处理器系统中实现信号量(Semaphores)的操作,以保证不会同时访问公用的数据结构。

(4)单寄存器的 Load/Store 指令,这种指令用于把单一的数据传入或者传出一个寄存器。支持的数据类型有字节(8 位)、半字(16 位)和字(32 位)。

02. 单寄存器的Load/Store指令

如表 3-8 所示列出了所有单寄存器的 Load/Store 指令。
【ARM】Load Store指令_ARM

2.1 LDR 指令

LDR 指令用于从内存中将一个 32 位的字读取到目标寄存器。

(1) 指令的语法格式

LDR{<cond>} <Rd>,<addr_mode>

(2) 应用示例

LDR R1,[R0,#0x12] ;将 R0+12 地址处的数据读出,保存到 R1 中(R0 的值不变)

LDR R1,[R0]  ;将 R0 地址处的数据读出,保存到 R1 中(零偏移)

LDR R1,[R0,R2] ;将 R0+R2 地址的数据读出,保存到 R1 中(R0 的值不变)

LDR R1,[R0,R2,LSL #2]  ;将 R0+R2×4 地址处的数据读出,保存到 R1 中(R0、R2 的值不变)

LDR Rd,label  ;label 为程序标号,label 必须是当前指令的-4~4KB 范围内

LDR Rd,[Rn],#0x04 ;Rn 的值用作传输数据的存储地址。在数据传送后,将偏移量 0x04 与 Rn 相加,
		    #结果写回到 Rn 中。Rn 不允许是 R15

2.2 STR 指令

STR 指令用于将一个 32 位的字数据写入到指令中指定的内存单元。

(1) 指令的语法格式

STR{<cond>} <Rd>,<addr_mode>

(2) 应用示例

LDR/STR 指令用于对内存变量的访问、内存缓冲区数据的访问、查表、外围部件的控制操作等,若使用 LDR 指令加载数据到 PC 寄存器,则实现程序跳转功能,这样也就实现了程序散转。

① 变量访问。

NumCount .equ 0x40003000  ;定义变量 NumCount
LDR R0,=NumCount  ;使用 LDR 伪指令装载 NumCount 的地址到 R0
LDR R1,[R0]  ;取出变量值
ADD R1,R1,#1  ;NumCount=NumCount+1
STR R1,[R0]  ;保存变量

② GPIO 设置。

GPIO—BASE .equ 0xe0028000 ;定义 GPIO 寄存器的基地址
…
LDR R0,=GPIO—BASE
LDR R1,=0x00ffff00  ;将设置值放入寄存器
STR R1,[R0,#0x0C] ;IODIR=0x00ffff00,IOSET 的地址为 0xE0028004

③ 程序散转。

…
MOV R2,R2,LSL #2  ;功能号乘以 4,以便查表
LDR PC,[PC,R2] ;查表取得对应功能子程序地址并跳转
NOP
FUN—TAB .word FUN—SUB0
.word FUN—SUB1
.word FUN—SUB2
…

2.3 LDRB 指令

LDRB 指令根据 addr_mode 所确定的地址模式将一个 8 位字节读取到指令中的目标寄存器 Rd。

指令的语法格式

LDR{<cond>}B <Rd>, <addr_mode>

2.4 STRB 指令

STRB 指令从寄存器中取出指定的 8 位字节放入寄存器的低 8 位,并将寄存器的高位补 0。

指令的语法格式

STR{<cond>}B <Rd>,<addr_mode>

2.5 LDRH 指令

LDRH 指令用于从内存中将一个 16 位的半字读取到目标寄存器。如果指令的内存地址不是半字节对齐的,指令的执行结果不可预知。

指令的语法格式

LDR{<cond>}H <Rd>,<addr_mode>

2.6 STRH 指令

STRH 指令从寄存器中取出指定的 16 位半字放入寄存器的低 16 位,并将寄存器的高位补 0。

指令的语法格式

STR{<cond>}H <Rd>,<addr_mode>

03. 多寄存器的Load/Store内存访问指令

多寄存器的 Load/Store 内存访问指令又称批量加载/存储指令,它可以实现在一组寄存器和一块连续的内存单元之间传送数据。LDM 用于加载多个寄存器,STM 用于存储多个寄存器。多寄存器的 Load/Store 内存访问指令允许一条指令传送 16 个寄存器的任何子集或所有寄存器。多寄存器的 Load/Store 内存访问指令主要用于现场保护、数据复制和参数传递等。

如下表所示列出了多寄存器的 Load/Store 内存访问指令。
【ARM】Load Store指令_ARM_02

3.1 LDM 指令

LDM 指令将数据从连续的内存单元中读取到指令中指定的寄存器列表中的各寄存器中。当 PC 包含在 LDM 指令的寄存器列表中时,指令从内存中读取的字数据将被作为目标地址值,指令执行后程序将从目标地址处开始执行,从而实现了指令的跳转。

指令的语法格式

LDM{<cond>}<addressing_mode> <Rn>{!}, <registers>

寄存器 R0~R15 分别对应于指令编码中 bit[0]~bit[15]位。如果 Ri 存在于寄存器列表中,则相应的位等于 1,否则为 0。LDM 指令将数据从连续的内存单元中读取到指令中指定的寄存器列表中的各寄存器中。

指令的语法格式

LDM{<cond>}<addressing_mode><Rn>,<registers_without_pc>

3.2 STM 指令

STM 指令将指令中寄存器列表中的各寄存器数值写入到连续的内存单元中。主要用于块数据的写入、数据栈操作及进入子程序时保存相关寄存器的操作。

指令的语法格式

STM{<cond>}<addressing_mode> <Rn>{!}, <registers>

STM 指令将指令中寄存器列表中的各寄存器数值写入到连续的内存单元中。主要用于块数据的写入、数据栈操作及进入子程序时保存相关寄存器等操作。

指令的语法格式

STM{<cond>}<addressing_mode> <Rn>, <registers >ˆ

3.3 数据传送指令应用

LDM/STM 批量加载/存储指令可以实现在一组寄存器和一块连续的内存单元之间传输数据。LDM 为加载多个寄存器,STM 为存储多个寄存器。允许一条指令传送 16 个寄存器的任何子集或所有寄存器。

指令格式如下

LDM{cond}<模式> Rn{!},regist{ˆ}
STM{cond}<模式> Rn{!},regist{ˆ}

LDM/STM 的主要用途有现场保护、数据复制和参数传递等。其模式有 8 种,其中前 4种用于数据块的传输,后 4 种是堆栈操作,如下所示。

(1)IA:每次传送后地址加 4。
(2)IB:每次传送前地址加 4。
(3)DA:每次传送后地址减 4。
(4)DB:每次传送前地址减 4。
(5)FD:满递减堆栈。
(6)ED:空递增堆栈。
(7)FA:满递增堆栈。
(8)EA:空递增堆栈。

其中,寄存器 Rn 为基址寄存器,装有传送数据的初始地址,Rn 不允许为 R15;后缀“!”表示最后的地址写回到 Rn 中;寄存器列表 reglist 可包含多于一个寄存器或寄存器范围,使用“,”分开,如{R1,R2,R6~R9},寄存器排列由小到大排列;“ˆ”后缀不允许在用户模式下使用,只能在系统模式下使用。若在 LDM 指令用寄存器列表中包含有 PC 时使用,那么除了正常的多寄存器传送外,将 SPSR 复制到 CPSR 中,这可用于异常处理返回;使用“ˆ”后缀进行数据传送且寄存器列表不包含 PC 时,加载/存储的是用户模式寄存器,而不是当前模式寄存器。

LDMIA R0!,{R3~R9} ;加载 R0 指向的地址上的多字数据,保存到 R3~R9 中,R0 值更新
STMIA R1!,{R3~R9} ;将 R3~R9 的数据存储到 R1 指向的地址上,R1 值更新
STMFD SP!,{R0~R7,LR}  ;现场保存,将 R0~R7、LR 入栈
LDMFD SP!,{R0~R7,PC}ˆ  ;恢复现场,异常处理返回

在进行数据复制时,先设置好源数据指针,然后使用块复制寻址指令 LDMIA/STMIA、LDMIB/STMIB、LDMDA/STMDA、LDMDB/STMDB 进行读取和存储。而进行堆栈操作时,则要先设置堆栈指针,一般使用 SP,然后使用堆栈寻址指令 STMFD/LDMFD、
STMED/LDMED、STMEA/LDMEA 实现堆栈操作。数据是存储在基址寄存器的地址之上还是之下,地址是存储第一个值之前还是之后、增加还是减少,如下表所示。
【ARM】Load Store指令_ldr_03

应用示例一 使用 LDM/STM 进行数据复制

LDR R0,=SrcData  ;设置源数据地址
LDR R1,=DstData  ;设置目标地址
LDMIA R0,{R2~R9} ;加载 8 字数据到寄存器 R2~R9
STMIA R1,{R2~R9} ;存储寄存器 R2~R9 到目标地址

应用示例二 使用 LDM/STM 进行现场寄存器保护,常在子程序或异常处理使用

SENDBYTE:
STMFD SP!,{R0~R7,LR} ;寄存器压栈保护
…
BL DELAY ;调用 DELAY 子程序
…
LDMFD SP!,{R0~R7,PC} ;恢复寄存器,并返回

04. 单数据交换指令

交换指令是 Load/Store 指令的一种特例,它把一个寄存器单元的内容与寄存器内容交换。交换指令是一个原子操作(Atomic Operation),也就是说,在连续的总线操作中读/写一个存储单元,在操作期间阻止其他任何指令对该存储单元的读/写。

交换指令如下表所示。
【ARM】Load Store指令_str_04

4.1 SWP 字交换指令

SWP 指令用于将内存中的一个字单元和一个指定寄存器的值相交换。操作过程如下:假设内存单元地址存放在寄存器中,指令将中的数据读取到目的寄存器 Rd 中,同时将另一个寄存器的内容写入到该内存单元中。当和为同一个寄存器时,指令交换该寄存器和内存单元的内容。

指令的语法格式

SWP{<cond>} <Rd>,<Rm>,[<Rn>]

4.2 SWPB 字节交换指令

SWPB 指令用于将内存中的一个字节单元和一个指定寄存器的低 8 位值相交换,操作过程如下:假设内存单元地址存放在寄存器中,指令将中的数据读取到目的寄存器 Rd 中,寄存器 Rd 的高 24 位设为 0,同时将另一个寄存器的低 8 位内容写入到该内存字节单元中。当和为同一个寄存器时,指令交换该寄存器低 8 位内容和内存字节单元的内容。

指令的语法格式

SWP{<cond>}B <Rd>,<Rm>,[<Rn>]

4.3 交换指令 SWP 应用

SWP 指令用于将一个内存单元(该单元地址放在寄存器 Rn 中)的内容读取到一个寄存器 Rd 中,同时将另一个寄存器 Rm 的内容写到该内存单元中,使用 SWP 可实现信号量操作。

指令的语法格式

SWP{cond}B Rd,Rm,[Rn]

其中,B 为可选后缀,若有 B,则交换字节;否则交换 32 位字。Rd 为目的寄存器,存储从存储器中加载的数据,同时,Rm 中的数据将会被存储到存储器中。若 Rm 与 Rn 相同,则为寄存器与存储器内容进行交换。Rn 为要进行数据交换的存储器地址,Rn 不能与Rd 和 Rm 相同。

SWP 指令举例

SWP R1,R1,[R0] ;将 R1 的内容与 R0 指向的存储单元内容进行交换
SWPB R1,R2,[R0]  ;将 R0 指向的存储单元内容读取一字节数据到 R1 中(高 24 位清零), 并将 R2 的内容
写入到该内存单元中(最低字节有效),使用 SWP 指令可以方便地进行信号量操作
12C_SEM .equ 0x40003000
…
12C_SEM_WAIT:
MOV R0,#0
LDR R0,=12C_SEM
SWP R1,R1,[R0]  ;取出信号量,并将其设为 0
CMP R1,#0  ;判断是否有信号
BEQ 12C_SEM_WAIT ;若没有信号则等待

05. 附录

5.1 ARM Architecture Reference Manual