ARM LDR/STR, LDM/STM 指令
这里比较下容易混淆的四条指令,已经在这4条指令的混淆上花费了很多精力,现在做个小结,LDR,STR,LDM,STM这四条指令,
关于LDM和STM的说明,见另外一个说明文件,说明了这两个文件用于栈操作时的注意事项。
(1)LDR:L表示LOAD,LOAD的含义应该理解为:Load from memory into register。下面这条语句就说明的很清楚:
LDR R1, [R2] ; R1<——[R2]
就是把R2所指向的存储单元的内容的值(一个memory地址内的值),读取到R1中(一个register)
(2)STR:S表示STORE,STORE的含义应该理解为:Store from a register into memory。下面这条语句表示的很清楚:
STR R1, [R2] ; R1——>[R2]
就是把寄存器R1中的内容“保存”到R2所指向的存储的单元中(一个memory地址)。
显然,这两条语句都有个特点,就是寄存器写在前面(左边)而内存地址写在后面(右边),数据传送的方向则是恰好相反的。
下面对LDM和STM介绍,使用sp来介绍,因为实际使用中,和sp一起使用更多。
(3)LDM:L的含义仍然是LOAD,即是Load from memory into register。
虽然貌似是LDR的升级,但是,千万要注意,这个指令运行的方向和LDR是不一样的,是从左到右运行的。
该指令是将内存中堆栈内的数据,批量的赋值给寄存器,即是出栈操作;
其中堆栈指针一般对应于SP,注意SP是寄存器R13,实际用到的却是R13中的内存地址,只是该指令没有写为[R13],
同时,LDM指令中寄存器和内存地址的位置相对于前面两条指令改变了,下面的例子:
LDMFD SP! , {R0, R1, R2} ; 实际上可以理解为: LDMFD [SP]!, {R0, R1, R2}
意思为:把sp指向的3个连续地址段(应该是3*4=12字节(因为为r0,r1,r2都是32位))中的数据拷贝到r0,r1,r2这3个寄存器中去
(如果这个地方还不懂的话,可以参看我文章开头提到的链接,里面有详细的图解)
(4)STM:S的含义仍然是STORE,与LDM是配对使用的,其指令格式上也相似,即区别于STR,是将堆栈指针写在左边,而把寄存器组写在右边。
STMFD SP!, {R0} ; 同样的,该指令也可理解为: STMFD [SP]!, {R0}
意思是:把R0保存到堆栈(sp指向的地址)中。
显然,这两个堆栈操作指令也有个特点,就是寄存器组写在后面(右边)而堆栈指针写在前面(左边),
而且实际上使用的是堆栈指针中的内存地址,这一点与前面两条指令是有区别的。
(补充:sp后面的!,作用是指命令执行完后,对应的地址值赋给sp,对于例程的SDM,是说最后sp的值应该是sp+3*4=sp+12)
这四条指令中,前面两条和后面两条其实联系不多,反而是差别很大,因此,可以直接把这两组指令区分开来,认为它们之间没有联系,这样避免误解。
STM和LDM的主要用途是现场保护、数据复制、参数传递等,其模式有8种,如下:
注:前面4种用于数据块的传输,后面4种用于堆栈操作
(1)IA 每次传送后地址加4 -- Inc After
(2)IB 每次传送前地址加4 -- Inc Before
(3)DA 每次传送后地址减4 -- Dec After
(4)DB 每次传送前地址减4 -- Dec Before
(5)FD 满递减堆栈
(6)FA 满递增堆栈
(7)ED 空递减堆栈
(8)EA 空递增堆栈
下面的讲述对于空递减堆栈和空递增堆栈同样适用.
在堆栈操作时,经常错误以为使用STMFD满递减将寄存器压入堆栈后,在弹出数据的时候应该使用LDMFA。
但是FD和FA仅用于指示目前操作的堆栈是何种模式(堆栈共有四种模式),FD指明目前的堆栈是满递减堆栈,
则数据入栈时的指令为STMFD,那么数据出栈时的指令对应的为LDMFD,而不是LDMFA。
我们可以这样认为STMFD等价于STMDB,LDMFD等价于STMIA
那么,数据传输的顺序和数据入栈的顺序又是如何呢
先来看STMFD SP!,{R1-R3} 执行的结果图(操作之后SP指向SP')
SP------->
|R3|
|R2|
SP'------>|R1|
那么STMFD SP!,{R3,R2,R1}执行后的堆栈顺序是不是刚好和上面的堆栈顺序相反,实际情况时这两个指令执行后的堆栈数据顺序一样,
因为ARM编译器会自动将STMFD SP!,{R3,R2,R1}转换为STMFD SP!,{R0-R3}指令,也就是说,ARM编译器默认高寄存器优先存入堆栈。
即便你在指令STMFD SP!,{R3,R2,R1}中刻意“安排”了寄存器入栈顺序,而在编译时编译器又重新做了处理,打乱了你期望的数据入栈顺序。
同理STMDB R0!,{R1-R3}和STMDB R0!,{R3,R2,R1}指令执行后数据在堆栈中的顺序完全一致。
STMFD SP!,{R1-R3}指令对应的出栈指令是LDMFD SP!,{R1-R3}(R1,R2,R3的顺序任意)
ARM LDR/STR, LDM/STM 指令的更多相关文章
- arm汇编:ldr,str,ldm,stm,伪指令ldr
ldr,str,ldm,stm的命名规律: 这几个指令命名看起来不易记住,现在找找规律. 指令 样本 效果 归纳名称解释 ldr Rd,addressing ldr r1,[r0] addressin ...
- LDM与STM指令详解
title: LDM与STM指令详解 date: 2019/2/26 17:58:00 toc: true --- LDM与STM指令详解 指令形式如下,这里的存储方向是针对寄存器的 Load Mul ...
- LDM和STM指令
LDM批量加载/STM批量存储指令可以实现一组寄存器和一块连续的内存单元之间传输数据. 允许一条指令传送16个寄存器的任意子集和所有寄存器,指令格式如下: LDM{cond} mode Rn{!} ...
- mov和ldr/str的区别
ARM是RISC结构,数据从内存到CPU之间的移动只能通过L/S指令来完成,也就是ldr/str指令.比如想把数据从内存中某处读取到寄存器中,只能使用ldr比如:ldr r0, 0x12345678就 ...
- [转]ARM平台下独占访问指令LDREX和STREX
参考:ARM平台下独占访问指令LDREX和STREX的原理与使用详解 全文转载如下: 为了实现线程间同步,一般都要在执行关键代码段之前加互斥(Mutex)锁,且在执行完关键代码段之后解锁.为了实现所谓 ...
- ARMv7 ldr/str指令详解
因为ARM的算术运算不支持直接操作内存地址,所以要把内存里的数据先加载进寄存器.ldr指令就是干这事的,称为间接取址模式. 一共有3*3九种模式,先是直接偏移,先偏移,后偏移三大类,指的是如何对源操作 ...
- arm ldr 指令
ldr 指令格式:(读取概念) ldr{条件} 1目的寄存器,2存储器地址 eg: ldr r0,[r1]; 把r1中数据值读取到r0中: ldr r0,[r1,r2];把r1+r2的数值 读取到r0 ...
- ARM LDR伪指令使用方法具体解释
LDR伪指令 10.45 LDR pseudo-instruction 功能:把一个32位马上数或一个32位的内存地址载入到一个寄存器中. 注意:这里描写叙述的是LDR伪指令,而不是LDR指令 ...
- ARM伪指令和协处理器访问指令
伪指令本身没有对应的机器码 .global声明全局符号,点事GUN汇编的特点 .data定义数据段 .equ DA #0x89 定义宏 .align 4 4字节对齐 mov 指令里的立即数只能是8位的 ...
随机推荐
- 五个案例让你明白GCD死锁(转)
转自:http://ios.jobbole.com/82622/ 死锁一直都是在使用多线程时,需要注意的一个问题.以前对同步.异步,串行.并行只有一个模糊的概念,想想也是时候整理一下了.再看看之前的博 ...
- unbind()清除指定元素绑定效果
定义和用法 unbind() 方法移除被选元素的事件处理程序. 该方法能够移除所有的或被选的事件处理程序,或者当事件发生时终止指定函数的运行. ubind() 适用于任何通过 jQuery 附加的事件 ...
- Anaconda+django写出第一个web app(十一)
今天我们来学习给页面添加一个Sidebar,根据Sidebar跳转到相应的tutorial. 打开views.py,编辑single_slug函数: def single_slug(request, ...
- Linux释放内存小脚本
最近发现渣渣ECS内存总是不够用,内存太小一不小心就用完了,用完就用完吧,内存用来做cache是可以快一些,但是内存用完了老是一顿一顿的卡,实在有点受不了,于是就写了释放内存的小脚本,觉得卡了就释放下 ...
- F - A计划
题目链接: https://cn.vjudge.net/contest/254150#problem/F wa代码: #include<iostream> #include<stri ...
- 05 uni-app框架学习:uni-app设置全局变量的方法
原文地址:https://ask.dcloud.net.cn/article/35021
- python格式化输出【转】
今天写代码时,需要统一化输出格式进行,一时想不起具体细节,用了最笨的方法,现在讲常见的方法进行一个总结. 一.格式化输出 1.整数的输出 直接使用'%d'代替可输入十进制数字: >>> ...
- Mycat 配置及优化【转】
前言 Mycat 是一个数据库分库分表中间件 MyCAT 是作为通用代理设计的,后端是以 Mysql协议 和 JDBC 的方式连接数据库,可以支持 Oracle.DB2.SQL Server . mo ...
- ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2) 的解决办法
更换mysql数据目录后出现ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql ...
- Python的CGI编程实现-通过接口运行服务器py脚本
yum 安装apcche [apache]yum 安装Apache(Centos 6.9) https://www.cnblogs.com/lauren1003/p/5993654.html只需一行命 ...