【ARM汇编-LDM】如何向寄存器批量加载数据?

举报
IM汤姆凯特 发表于 2022/04/19 17:26:07 2022/04/19
【摘要】 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 main
main:
    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 main
main:
    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 main
main:
    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只能从一片连续的存储器中读取数据,不连续还是需要单独读取。


学会了吗?学会了送自己一朵小花花呀!

在这里插入图片描述

还没有关注汤姆的朋友,点个关注每天学一点汇编
下期预告:暂定(*^▽^*)
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。