【ARM汇编-LDM】如何向寄存器批量加载数据?
大家好,我是汤姆凯特。
文章目录
- 如何从存储器中批量加载/存储数据?
每篇前言
☀️作者简介:大家好我是汤姆凯特,大家可以叫我汤姆
🐋个人主页:IM汤姆凯特的CSDN博客
🎁系列专栏:【ARM嵌入式基础】
🌱每日一句:
是的,我们总是在惯性中生活,在教导下学习,在成规中思考,在劝解中决定,并在无助的结果中自责。我们着实需要一种智识和能力,去观察、反思自己被局限的生活,去发现和实践更多成长的路径。——《精进》
如何从存储器中批量加载/存储数据?
想要批量读取数据你首先要有三个基础,前面我们都有介绍到
传送门:
如何调用C语言中的printf 用于显示结果?
如何用LDR从存储器中读取单个数值?
堆栈在数据传送中有什么作用?
这些问题的答案,之前的文章都给出了答案,可以点击上面的链接查看,这里就不再赘述。
如果你以上都没有问题,那我们看一下今天的问题,怎么从寄存器中一次性加载多个数据?
给大家先详细介绍一下LDM、STM这两个批量加载、存储指令:
ARM微处理器所支持批量数据加载、存储指令,可以一次性在一片连续的存储器单用和多个寄存器之间传递数据,批量加载指令用于将一片连续的存储器中的数据传送到多个寄存器,批量数据存储指令则完成相反的动作。 |
格式:
LDM(或STM){类型} 基址寄存器{!} ,寄存器列表{……}
功能:
LDM(或STM)指令用于从基址寄存器所指示的一片连续存储器到寄存器列表所指示的多个寄存器之间传送数据。
有两个常用的用法
1.压栈(图中下面4个指令) |
2.多寄存器寻址(图中上面4个指令) |
下面分别给大家用例子来讲解
一、如何用STM指令进行压栈?
提到压栈,看过上一篇文章的小伙伴肯定都很熟悉,用push就可以。
这里给大家介绍另外一个压栈的指令`LDM R13!`,细心的铁子肯定会发现,这不就是第一天给大家ARM汇编模板`main框架`中的语句吗? 没错是的,之前说过R13的别名可以叫`sp`(堆栈指针,并且指在最上面元素),STM刚刚讲到是用来传送数据的存储指令,如果把寄存器中的内容存储到R13(堆栈指针),那不就代表把内容放到堆栈中了嘛! |
举个例子
用输出超过三个数的例子来试一下(因为超出三个数必须要压栈)
源代码:
.data str:.asciz " %5d\n %5d\n %5d\n %5d\n %5d\n %5d\n" a:.word 1 b:.word 2 c:.word 3 d:.word 4 e:.word 5 f:.word 6.text.globl mainmain: stmfd sp!,{lr} ldr r0,=a ldr r1,[r0] ldr r0,=b ldr r2,[r0] ldr r0,=c ldr r3,[r0] ldr r0,=d ldr r4,[r0] ldr r0,=e ldr r5,[r0] ldr r0,=f ldr r6,[r0] stmfd r13!,{r4,r5,r6} ldr r0,=str bl printf mov r0, #0 ldmfd sp!,{lr}mov pc,lr.end
这里我先把6个数从内存中读了出来,然后用
stmfd r13!,{r4,r5,r6}
进行了压栈操作(批量存储,寄存器之间用“,”隔开即可)
运行结果:
这样,我们就学会了除push的另外一个压栈方式stmfd R13
,我这里用的是ARM默认的满递减堆栈(FD)的压栈方式,不同的堆栈类型可以用图片中不同的类型指令。
二、如何让寄存器一次性取到多个存储器中的地址?
上面的例子可以看到我们用LDR一次只能取一个地址,写的非常的麻烦
那今天学了LDM就能让你方便很多,先上原理,再上例子
多寄存器寻址
多寄存器寻址是指一次可以传送多个寄存器的值,允许一条指令可以传送16个寄存器的任何子集。多寄存器寻址对应后缀的含义如下: |
- I:Increment 地址递增
- D:Decrement 地址递减
- A:After 传递后地址才开始变化
- B:Before 地址先变化后才开始传送
例如:
LDMIA R0,{R1,R2,R3,R4}
这其实是执行了四次
R1←[R0]R2←[R0+4]R3←[R0+8]R4←[R0+12]
因为IA后缀表示每次执行完加载/存储后,R0按字长度增加。因此,指令可将连续存储单元的值传送到R1~R4
运用LDM简化后的代码(还是用上面的例子)
源代码:
.data str:.asciz " %5d\n %5d\n %5d\n %5d\n %5d\n %5d\n" a:.word 7 b:.word 6 c:.word 5 d:.word 4 e:.word 3 f:.word 2.text.globl mainmain: stmfd sp!,{lr} ldr r2,=a ldmia r2,{r4-r9} mov r1,r4 mov r2,r5 mov r3,r6 push {r7,r8,r9} ldr r0,=str bl printf mov r0, #0 ldmfd sp!,{lr}mov pc,lr.end
用多寄存器寻址指令
ldmia r2,{r4-r9}
把{r4,r5,r6,r7,r8,r9}寄存器以此存上a,b,c,d,e,f
的地址。我们这里用的是IA每次传输后地址加1,根据需求也可以用上面图片中其他类型的后缀。
运行结果:
学会了多寄存器寻址就大大降低了代码行数
再次简化后的代码(两个用法融合)
运用LDM取址简化了取址的操作——当数量超过3个数据后——必须用到堆栈——又学到了新的压栈方法——那么我们可以把上面的两个用法融合到一起。 |
源代码:
.data str:.asciz " %6d\n %6d\n %6d\n %6d\n %6d\n %6d\n" a:.word 666666 b:.word 66666 c:.word 6666 d:.word 666 e:.word 66 f:.word 6.text.globl mainmain: stmfd sp!,{lr} ldr r0,=a ldmia r0,{r1,r2,r3,r4,r5,r6} stmfd R13!,{r4,r5,r6} ldr r0,=str bl printf mov r0, #0 ldmfd sp!,{lr}mov pc,lr.end
运行结果:
由于printf是从R1开始输出,所以把前面的R4直接改成R1,就不用在进行MOV了。
对比看一下两个代码:
小提示:
多个连续的寄存器可以用
“ - ”
符号链接;不连接的寄存器用“,”
分隔书写,如:
LDMIA R0,{R1-R6}LDMIA R0,{R1,R3-R7,R9}
总结:
今天讲到了两个很重要的指令LDM、STM(批量加载,批量存储)
1.可运用STM和LDM加上后缀进行压栈和出栈,例如:
STMFD R13!
和LDMFD R13!
。2.运用LDM可进行多寄存器寻址,例如:
LDMIA
不同的寻址需求加上不同的类型后缀。3.指令LDM和STM只能从一片连续的存储器中读取数据,不连续还是需要单独读取。
学会了吗?学会了送自己一朵小花花呀!
还没有关注汤姆的朋友,点个关注每天学一点汇编 下期预告: 暂定(*^▽^*)