|  | 
 
| 5.4  多寄存器Load/Store内存访问指令 
 多寄存器Load/Store内存访问指令也叫批量加载/存储指令,它可以实现在一组寄存器和一块连续的内存单元之间传送数据。LDM用于加载多个寄存器,STM用于存储多个寄存器。多寄存器Load/Store内存访问指令允许一条指令传送16个寄存器的任何子集或所有寄存器。
 
 多寄存器Load/Store内存访问指令主要用于现场保护、数据复制和参数传递等。
 
 
 |   
 | 注意 
 | 多寄存器Load/Store内存访问指令会增加中断延时,因为ARM通常不会打断正在执行的指令去响应中断,而必须等到指令执行完。也就是说,如果一个中断在多寄存器Load/Store内存访问指令执行期间产生,那么处理器在多寄存器Load/Store内存访问指令执行完后才对中断响应。 
 | 
 表5.2总结了多寄存器Load/Store内存访问指令
 表5.2     多寄存器Load/Store内存访问指令
 
 | 指    令 
 | 作    用 
 | 操    作 
 |  | LDM 
 | 装载多个寄存器 
 | {Rd}*N←mem32[start address+4*N] 
 |  | STM 
 | 保存多个寄存器 
 | {Rd}*N→mem32[start address+4*N] 
 | 
 5.4.1  多寄存器内存字数据传送指令
 
 1.LDM(1)指令
 
 (1)指令编码格式
 LDM(1)指令将数据从连续的内存单元中读取到指令中指定的寄存器列表中的各寄存器中。
 
 当PC包含在LDM指令的寄存器列表中时,指令从内存中读取的字数据将被作为目标地址值,指令执行后程序将从目标地址处开始执行,从而实现了指令的跳转。
 
 指令的编码格式如图5.15所示。
 
 
         图5.15  LDM(1)指令编码格式 
 (2)指令的语法格式
 
 LDM{<cond>}<addressing_mode>  <Rn>{!},  <registers>
 
 ① <cond>
 为指令编码中的条件域。它指示LDM(1)指令在什么条件下执行。当<cond>忽略时,指令为无条件执行(cond=AL(Alway))。
 
 ② <address_mode>
 指令的寻址方式。确定编码格式中的P、U和W位。
 
 ③ <Rn>
 确定寻址模式所使用的基址寄存器。
 如果r15作为指令的基址寄存器,指令的执行结果不可预知。
 
 ④ !
 设置指令编码格式中的W位。它使指令执行后将操作数的内存地址写入基址寄存器<Rn>中;如果!被忽略,W位为0,指令执行完后,不修改基址寄存器的值。
 
 
 | ![]() 
 | 注意 
 | 如果基址寄存器包含在指令列表中,当指令执行完后,基址寄存器的值是新加载进的特定内存地址的值。也就是说,即使指令没有出现在指令列表中,基址寄存器的值也可能被修改。 
 | 
 ⑤ <registers>
 被加载的寄存器列表。不同的寄存器之间用“,”隔开。完整的寄存器列表包含在“{}”中。编号低的寄存器对应于内存中低地址单元,编号高的寄存器对应于内存中高地址单元。
 
 
 | ![]() 
 | 注意 
 | 无论寄存器在寄存器列表“{}”中如何排列,都将遵循上述规则。 
 | 
 寄存器r0~r15分别对应于指令编码中bit[0]~bit[15]位。如果Ri存在于寄存器列表中,则相应的位等于1,否则为0。
 
 (3)指令操作的伪代码
 指令操作伪代码如下面程序段所示。
 
 If  ConditionPass{cond}  then
 Address=start_address
 
 For  i=0  to 14
 If  register_list==1  then
 Ri=Memory[address,4]
 Address=address+4
 
 If  register_list[15]==1  then
 Value = Memory[address,4]
 If(architecture version 5 or above)  then
 Pc= value  AND  0xfffffffe
 T bit=value[0]
 Else
 Pc= value  AND  0xfffffffc
 Address=address+4
 Assert end_address=address+4
 2.STM(1)指令
 
 (1)指令编码格式
 STM(1)指令将指令中寄存器列表中的各寄存器数值写入到连续的内存单元中。主要用于块数据的写入、数据栈操作以及进入子程序时保存相关寄存器的操作。
 
 指令编码格式如图5.16所示。
 
 
         图5.16  STM(1)指令编码格式 
 (2)指令的语法格式
 
 STM{<cond>}<addressing_mode>  <Rn>{!},  <registers>
 ① <cond>
 为指令编码中的条件域。它指示STM(1)指令在什么条件下执行。当<cond>忽略时,指令为无条件执行(cond=AL(Alway))。
 
 ② <address_mode>
 指令的寻址方式。确定编码格式中的P、U和W位。
 
 ③ <Rn>
 确定寻址模式所使用的基址寄存器。
 如果r15作为指令的基址寄存器,指令的执行结果不可预知。
 
 ④ !
 设置指令编码格式中的W位。它使指令执行后将操作数的内存地址写入基址寄存器<Rn>中;如果!被忽略,W位为0,指令执行完后,不修改基址寄存器的值。
 
 ⑤ <registers>
 被加载的寄存器列表。不同的寄存器之间用“,”隔开。完整的寄存器列表包含在“{}”中。编号低的寄存器对应于内存中低地址单元,编号高的寄存器对应于内存中高地址单元。
 寄存器r0~r15分别对应于指令编码中bit[0]~bit[15]位。如果Ri存在于寄存器列表中,则相应的位等于1,否则为0。
 
 (3)指令操作的伪代码
 指令操作伪代码如下面程序段所示。
 
 If  ConditionPassed{cond}  then
 Address=Start_address
 For  i=0  to  15
 If  register_list==1
 Memory[address,4]=Ri
 Address=address+4
 Assert  end_address==address-4
 
 5.4.2  用户模式多寄存器内存字数据传送指令
 
 1.LDM(2)指令
 
 (1)指令编码格式
 LDM(2)指令将数据从连续的内存单元中读取到指令中指定的寄存器列表中的各寄存器中。
 
 
 | ![]() 
 | 注意 
 | 与LDM(1)指令不同,PC不能包含在寄存器列表中。 
 | 
 指令的编码格式如图5.17所示。
 
 
         图5.17  LDM(2)指令编码格式 
 (2)指令的语法格式
 
 LDM{<cond>}<addressing_mode>  <Rn>,  <registers_without_pc>ˆˆ
 
 ① <cond>
 为指令编码中的条件域。它指示LDM(2)指令在什么条件下执行。当<cond>忽略时,指令为无条件执行(cond=AL(Alway))。
 
 ② <address_mode>
 指令的寻址方式。确定编码格式中的P位和U位。此指令中W位指定为0。
 
 ③ <Rn>
 确定寻址模式所使用的基址寄存器。
 如果r15作为指令的基址寄存器,指令的执行结果不可预知。
 
 ④ <registers_without_pc>ˆ
 被加载的寄存器列表。不同的寄存器之间用“,”隔开。完整的寄存器列表包含在“{}”中。此寄存器列表中不能包含PC寄存器。
 
 如果PC包含在寄存器列表中,指令的执行结果不可预知。
 其他细节可参考LDM(1)指令。
 
 (3)指令操作的伪代码
 指令操作伪代码如下面程序段所示。
 
 If  ConditionPassed{cond}  then
 Address=start_address
 For  i=0  to  14
 If  register_list==1
 Ri_usr=Memory[address,4]
 Address=address+4
 Assert  end_address == address-4
 
 2.STM(2)指令
 
 (1)指令编码格式
 STM(2)指令将指令中寄存器列表中的各寄存器数值写入到连续的内存单元中。主要用于块数据的写入、数据栈操作以及进入子程序时保存相关寄存器等操作。
 
 指令编码格式如图5.18所示。
 
 
         图5.18  STM(2)指令编码格式 
 (2)指令的语法格式
 
 STM{<cond>}<addressing_mode>  <Rn>,  <registers >ˆ
 ① <cond>
 为指令编码中的条件域。它指示LDM(2)指令在什么条件下执行。当<cond>忽略时,指令为无条件执行(cond=AL(Alway))。
 
 ② <address_mode>
 指令的寻址方式。确定编码格式中的P位和U位。此指令中W位指定为0。
 
 ③ <Rn>
 确定寻址模式所使用的基址寄存器。
 
 如果r15作为指令的基址寄存器,指令的执行结果不可预知。
 ④ <registers >ˆ
 寄存器列表。只能使用用户模式下的寄存器。
 
 (3)指令操作的伪代码
 指令操作伪代码如下面程序段所示。
 
 If  ConditionPassed{cond}  then
 Address=start_address
 For  i=0  to  15
 If  register_list == 1
 Memory[address,4]=Ri_usr
 Address = address +4
 Assert  end_address == address-4
 
 5.4.3  带状态寄存器的多寄存器内存字数据装载指令(LDM(3))
 
 (1)指令编码格式
 LDM(3)指令将数据从连续的内存单元中读取数据到寄存器列表中的各寄存器中。它同时将当前处理器模式对应的SPSR寄存器的内容复制到CPSR寄存器中。
 
 当PC包含在LDM指令的寄存器列表中时,指令从内存中读取的数据将被作为目标地址值,指令执行后程序将从目标地址处开始执行,从而实现了指令的跳转。
 
 在ARM v5及以上的版本和T系列的ARM v4版本中,SPSR寄存器的T位将复制到CPSR寄存器的T位,该位决定目标地址处的程序状态。在以前的版本中程序继续执行在ARM状态。
 
 指令的编码格式如图5.19所示。
 
 
         图5.19  LDM(3)指令编码格式 
 (2)指令的语法格式
 
 LDM{<cond>}<addressing_mode>  <Rn>{!},  <registers_and_pc>ˆ
 
 ① <cond>
 为指令编码中的条件域。它指示LDM(1)指令在什么条件下执行。当<cond>忽略时,指令为无条件执行(cond=AL(Alway))。
 
 ② <address_mode>
 指令的寻址方式。确定编码格式中的P、U和W位。
 
 ③ <Rn>
 确定寻址模式所使用的基址寄存器。
 如果r15作为指令的基址寄存器,指令的执行结果不可预知。
 
 ④ !
 设置指令编码格式中的W位。它使指令执行后将操作数的内存地址写入基址寄存器<Rn>中;如果!被忽略,W位为0,指令执行完后,不修改基址寄存器的值。
 
 
 | ![]() 
 | 注意 
 | 如果基址寄存器包含在指令列表中,当指令执行完后,基址寄存器的值是新加载进的特定内存地址的值。也就是说,即使指令没有出现在指令列表中,基址寄存器的值也可能被修改。 
 | 
 ⑤ <registers_and_pc>ˆ
 寄存器列表。
 
 
 | ![]() 
 | 注意 
 | 在本格式的指令中寄存器列表中必须包含PC寄存器。 
 | 
 被加载的寄存器列表。不同的寄存器之间用“,”隔开。完整的寄存器列表包含在“{}”中。编号低的寄存器对应于内存中的低地址单元,编号高的寄存器对应于内存中的高地址单元。
 
 寄存器r0~r15分别对应于指令编码中bit[0]~bit[15]位。如果Ri存在于寄存器列表中,则相应的位等于1,否则为0。
 
 该指令执行时将当前处理器模式下的SPSR值复制到CPSR中。指令的其他参数可参见LDM(1)指令格式。
 
 (3)指令操作的伪代码
 指令操作伪代码如下面程序段所示。
 
 If  ConditionPass{<cond>}  then
 Address=start_address
 For i=0  to  14
 If  register_list==1 then
 Ri=Memory[address,4]
 Address=address+4
 CPSR=SPSR
 Value=memory[address,4]
 If {architecture version 4T, 5 or above} and {T bit ==1} then
 Else
 Pc=value AND oxfffffffc
 Address=address + 4
 Assert end_address=address-4
 
 5.4.4  数据传送指令应用
 
 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时,加载/存储的是用户模式寄存器,而不是当前模式寄存器。
 
 
 
 | ![]() 
 | 注意 
 | 地址对齐问题,在这些指令中,忽略地址位[1:0]。 批量加载/存储指令举例如下。
 
 | 
 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实现堆栈操作。
 
 多寄存器传送指令如例5.3所示。其中r1为指令执行前的基址寄存器,r1’则为指令执行后的基址寄存器。
 
 【例5.3】多寄存器传送指令示意。
 
 
         (1)STMIA  r1,{r5~r7} 
 
         (2)STMIB  r1!,{r5~r7} 
 
         (3)STMDA  r1!,{r5~r7} 
 
         (4)STMDB  r1!,{r5~r7} 
 
 数据是存储在基址寄存器的地址之上还是之下,地址是存储第一个值之前还是之后、增加还是减少,如表5.3所示。
 表5.3 多寄存器Load/Store内存访问指令映射
 
 | 
 | 向 上 生 长 
 | 向 下 生 长 
 |  | 满 
 | 空 
 | 满 
 | 空 
 |  | 增加 
 | 之前 
 | STMIB 
 | 
 | 
 | LDMIB 
 |  | STMFA 
 | 
 | 
 | LDMED 
 |  | 之后 
 | 
 | STMIA 
 | LDMIA 
 | 
 |  | 
 | STMEA 
 | LDMFD 
 | 
 |  | 增加 
 | 之前 
 | 
 | LDMDB 
 | STMDB 
 | 
 |  | 
 | LDMEA 
 | STMFD 
 | 
 |  | 之后 
 | LDMDA 
 | 
 | 
 | STMDA 
 |  | LDMFA 
 | 
 | 
 | STMED 
 | 
 【例5.4】使用LDM/STM进行数据复制。
 
 LDR  r0,=SrcData           ;设置源数据地址
 LDR  r1,=DstData           ;设置目标地址
 LDMIA   r0,{r2~r9}        ;加载8字数据到寄存器r2~r9
 STMIA   r1,{r2~r9}        ;存储寄存器r2~r9到目标地址
 
 【例5.5】使用LDM/STM进行现场寄存器保护,常在子程序或异常处理使用。
 
 SENDBYTE
 STMFD     SP!,{r0~r7,LR}        ;寄存器压栈保护
 …….
 BL        DELAY                  ;调用DELAY子程序
 …….
 LDMFD     SP!,{r0~r7,PC}        ;恢复寄存器,并返回
 | 
 |