首页 > 其他分享 >ARM 指令 LDR、STR等详解

ARM 指令 LDR、STR等详解

时间:2024-03-29 14:44:07浏览次数:31  
标签:r0 r1 r2 LDR 地址 指令 STR 寄存器 ARM

前言

       本期和大家主要分享的ARM汇编指令集中的内存操作类指令,涉及到内存操作,不得不说CPSR,SP,LR,PC是非常重要的几个寄存器,所以接下来就来具体观察一下其内存窗口的具体变化!

一、跳转指令

1.1 相对跳转

相对跳转: b fun 基于当前pc前后32M范围寻找标号,跳转到标号处执行代码
     bl fun 基于当前pc前后32M范围寻找标号,跳转到标号处执行代码,保存返回地址到LR

1.2 绝对跳转

修改pc的值(两种修改pc的值)

mov pc, #0x100

ldr pc, =fun ;将fun标号的链接地址给pc

 绝对跳转第一种给pc赋值的缺点是:当pc赋值处的汇编代码之前需要加入代码时,那么pc处的指令就不是原来的指令了,发生了变化;但是绝对跳转的第二种方式与相对跳转其实都是把将要执行指令的地址装载给PC,进而实现指令的跳转;

二、内存操作指令

 接下来是非常重要的一部分,内存操作指令能够实现将某一个值写到指定地址的位置处;这样就能够实现函数的跳转(比如用堆栈记录函数的入口地址等);
内存操作指令:实现寄存器与内存之间的数据交互;

2.1 Load和Store

先从字面上来理解一下:
LDR LD=LoaD(加载) R=Register(寄存器) ;
STR ST=STore(存储)R=Register(寄存器)

LDR:加载指定位置32位数据到目标寄存器;(从固定地址处进行取值)
STR:保存指定位置32位数据到目标位置;(给固定位置进行赋值)

这里给出不同指令下进行数据操作的位数:
ldrb/strb 8位操作
ldrh/strh 16位操作
ldr/str 32位操作
ldrd/strd 64位操作

注意:ldr r0, fun === 基于当前pc前后4k,寻找标号,将fun标号地址处的内容给r0

2.1.1 伪指令

 伪指令和指令一样都是会生成机器码的指令,但伪指令与指令的区别是:每条汇编指令都有对应的唯一一条机器码,而伪指令可以有很多一条机器码,也可以理解为伪指令是有很多一条汇编指令组合而成。

ldr伪指令可以在立即数前加上=,以表示把一个地址写到某寄存器中
ldr伪指令和mov是比较相似的。只不过mov指令限制了立即数的长度为8位,也就是不能超过512。ldr伪指令没有这个限制。
如果使用ldr伪指令时,后面跟的立即数没有超过8位,在实际汇编的时候该ldr伪指令是被转换为mov指令的。

2.1.2 伪操作

伪操作:告诉编译器怎么去编译指令,而它本身不生成机器码;

2.2 内存操作指令具体应用

mov r0, #0x40000010
mov r1, #3
str r1, [r0]
ldr r2, [r0]
 以下代码实现的功能是将0x40000010这个地址赋给寄存器,紧接着将3这个立即数给寄存器r1,接下来str指令将立即数3写入到地址为0x40000010的地址位置处;ldr指令实现的操作是将0x40000010处存放的数取出来存入寄存器r2中,接下来看一下寄存器和内存观察窗口;

三、 寄存器的寻址方式

3.1 前索引寻址

ldr r0, [r1, #4] === r0=*(r1+4) r1=r1
str r0, [r1, #4] === *(r1+4)=r0 r1=r1
ldr r0, [r1, r2] === r0=*(r1+r2) r1=r1
str r0, [r1, r2] === *(r1+r2)=r0 r1=r1

3.2 后索引寻址

ldr r0, [r1], #4 === r0=*(r1) r1=r1+4
str r0, [r1], #4 === *(r1)=r0 r1=r1+4
ldr r0, [r1], r2 === r0=*(r1) r1=r1+r2
str r0, [r1], r2 === *(r1)=r0 r1=r1+r2

3.3 基址变址

ldr r0, [r1, #4]! === r0=*(r1+4) r1=r1+4
str r0, [r1, #4]! === *(r1+4)=r0 r1=r1+4
ldr r0, [r1, r2]! === r0=*(r1+4) r1=r1+r2
str r0, [r1, r2]! === *(r1+4)=r0 r1=r1+r2
注意:这里!表示地址一定会发生变化;

四、块拷贝指令(多数据加载)

4.1 块拷贝

快拷贝:完成多个寄存器和连续内存空间数据交互,小编号寄存器对应低地址空间, 大编号寄存器对应高地址空间

ldm:(load much)多数据加载,将地址上的值加载到寄存器上
stm:(store much)多数据存储,将寄存器的值存到地址上
(1)IA:(Increase After) 每次传送后地址加4,其中的寄存器从左到右执行,例如:STMIA R0,{R1,LR} 先存R1,再存LR;
(2)IB:(Increase Before)每次传送前地址加4,同上;
(3)DA:(Decrease After)每次传送后地址减4,其中的寄存器从右到左执行,例如:STMDA R0,{R1,LR} 先存LR,再存R1;
(4)DB:(Decrease Before)每次传送前地址减4,同上;
(5)FD: 满递减堆栈 (每次传送前地址减4);
(6)FA: 满递增堆栈 (每次传送后地址减4);
(7)ED: 空递减堆栈 (每次传送前地址加4);
(8)EA: 空递增堆栈 (每次传送后地址加4);
此处借鉴文章:多数据操作指令

4.2 指令应用

4.2.2 多数据操作指令

ldm r0, {r1,r2,r3} === r1=*r0 r2=*(r0+4) r3=*(r0+8) r0=r0(这里如果不是按照r1,r2,r3进行顺序排布,那么CPU也会自动进行从小到大进行排列)
stm r0, {r1-r3} === *r0=r1 *(r0+4)=r2 *(r0+8)=r3 r0=r0

ldmia/stmia 先内存操作,后地址+4
ldmda/stmda 先内存操作,后地址-4
ldmib/stmib 先地址+4,后内存操作
ldmdb/stmdb 先地址-4,后内存操作

4.2.3 栈操作

指令默认 的栈是满减栈;
满减栈 sp(stack pointer)指向的内存空间不能直接使用,栈的生长方向向下;
满增栈 sp指向的内存空间不能直接使用,栈的生长方向向上;
空减栈 sp指向的内存空间可以直接使用,栈的生长方向向下 ;
空增栈 sp指向的内存空间可以直接使用,栈的生长方向向上;

ldmfd、stmfd 满减
ldmfa、stmfa 满增
ldmed、stmed 空减
ldmea、stmea 空增

入栈: stmfd sp!, {r0-r12, lr} sp为栈指针,把r0-r12, lr中的数据依次入栈;*(sp-4)=r0,*(sp-4*2)=r1,*(sp-4*3)=r2,........,*(sp-4*15)=lr;
出栈: ldmfd sp!, {r0-r12, lr} r0-r12, lr中的数据依次出栈;lr=[sp-1*4],r13=[sp-2*4],r12=[sp-3*4],......,r0=[sp-15*4]
出栈: ldmfd sp!, {r0-r12, lr}^ ^表示模式恢复(因为没对pc赋值,所以^的表示将数据恢复到User模式的[r0-lr]寄存器组中)

五、特殊寄存器操作指令

特殊寄存器 cpsr 的读写访问只能使用 msr 和 mrs 指令
msr:将普通寄存器中的数据写到特殊寄存器中;
mrs:将特殊寄存器中的数据写到普通寄存器中;

msr cpsr, r0 === cpsr=r0
msr cpsr_c, r0 === keil中使用
mrs r0, cpsr === r0=cpsr
mrs 指令: 对状态寄存器CPSR和SPSR进行读操作。通过读CPSR可以获得当前处理器的工作状态。读SPSR寄存器可以获得进入异常前的处理器状态(因为只有异常模式下有SPSR寄存器)。
msr指令: 对状态寄存器CPSR和SPSR进行写操作。与MRS配合使用,可以实现对CPSR或SPSR寄存器的读、修改、写操作,可以切换处理器模式、或者允许/禁止IRQ/FIQ中断等。

MRS 指令允许将 CPSR 或 SPSR_<mode>的内容移动到一个通用寄存器中去。
MSR 指令允许将一个通用寄存器中的内容移动到 CPSR 或 SPSR_<mode>寄存器中去。
以下顺序执行了一个模式的改变:

MRS R0, CPSR ;复制 CPSR 到 R0
BIC R0, R0, #0x1F ;清零模式位
ORR R0, R0, #new_mode ;选择新的模式
MSR CPSR, R0 ;写回到修改了的 CPSR

六、 杂项指令

swi #12 软件中断指令,数值存放在机器码的低24位,可以利用数值进行区分
svc #12 目前将swi更新为svc指令
该指令可以用来触发软件中断;

七、 协处理指令

协处理器操作:辅助主处理器进行电路管理的处理器;(比如计算一些浮点型的数据)
一般处理器有p0—p15的某一些,arm9 只有p15 协处理器;c0-c15 寄存器 32位;

MRC p2, 5, R3, C5, C6, ;请求协处理器 2 用 c5 和 c6 执行操作数 5,将结果(有符号 32 位字)传输回 R3
MRCEQ p3, 9, R3, c5, c6 2 ;条件请求协处理器 3 用 c5 和 c6 执行操作数 9(类型 2),将结果传输回 R3

八、 条件字段  

      在 ARM 状态,所有的指令都可以按照 CPSR 状态码和指令条件字段的状态来有条件地执行。此字段(位[31:28])确定了在什么情况下哪一个指令被执行。如果 C,N,Z 和 V 标志位的状态符合字段的条件码,将执行指令,否则忽略不执行。
  有 16 种可能的条件,每种表示为在指令助记符后附加两个字符后缀。例如,一个分支(汇编语言中的 B)跳
转指令变成 BEQ 为“如果相等则分支跳转”,这意味着只有 Z 标志位被置位了才会执行分支跳转。

总结

       本期和大家主要分享的是ldr、str、ldm、stm、msr、mrs、swi、svc、mrc等ARM指令的具体介绍,对这些基础概念掌握后,接下来就能够读懂基本的2440A的启动代码了,也有助于自己去独立的书写启动代码,依次更好的实现自己想要实现的功能,更好的理解正式启动代码设计流程的巧妙之处,对CPU处理事务的流程以及处理细节会有更深的认识;

标签:r0,r1,r2,LDR,地址,指令,STR,寄存器,ARM
From: https://www.cnblogs.com/FireLife-Cheng/p/18103814

相关文章

  • ARM 寄存器——CPSR/SPSR介绍
    CPSR简介:全称:程序状态寄存器(currentprogramstatusregister)用户级编程时用于存储条件码任何处理器模式下被访问两种运行状态:ARM状态(32位)、Thumb状态(16位),两种状态之间可任意切换七种运行模式:usr(用户)、fiq(快速中断)、irq(外部中断)、svc(管理)、sys(系统)、abt(数据访问中止)、und(......
  • String容器
    String容器1.1string基本概念本质:string是C++风格的字符串,而string本质上是一个类string和char*区别:char*是一个指针string是一个类,类内部封装了char*,管理这个字符串,是一个char*型的容器。特点:string类内部封装了很多成员方法例如:查找find,拷贝copy,删除delete......
  • 【Linux系列-1】-- 必会的 ARM 汇编指令
    原创:lvy嵌入式学习规划学习Linux系统启动流程,必须熟悉几个汇编指令这里不是最全的,只列出一些最常用的汇编指令。一.数据处理指令1.数据传送指令【MOV指令】把一个寄存器的值(立即数)赋给另一个寄存器,或者将一个常量赋给寄存器。MOV指令的格式为:MOV目的寄存器,源操作数M......
  • 前端学习-UI框架学习-Bootstrap5-012-进度条
    菜鸟教程链接创建一个基本的进度条的步骤如下:添加一个带有.progress类的。接着,在上面的内,添加一个带有class.progress-bar的空的。添加一个带有百分比表示的宽度的style属性,例如style="width:70%"表示进度条在70%的位置。注意:我发现如果这个contianer里面不放......
  • 抛出String、StringBuffer与StringBuilder时, 你是否还懵着?
    深入探讨String、StringBuffer与StringBuilder的区别及字符串不可变性引言在Java编程语言中,字符串操作是编写程序中非常常见的事情。Java提供了String、StringBuffer和StringBuilder三种处理字符串的方式,它们之间的区别常常是面试中的热点问题。String的不可变性String......
  • Unity String格式化字符串
    1、格式化货币(跟系统的环境有关,中文系统默认格式化人民币,英文系统格式化美元)string.Format("{0:C}",0.2)结果为:¥0.20(英文操作系统结果:$0.20)默认格式化小数点后面保留两位小数,如果需要保留一位或者更多,可以指定位数string.Format("{0:C1}",23.15)结果为:¥23.2(截取会自动四舍五入)格......
  • C++17: 结构化绑定(Structured Bindings)
    遇到有时候写函数需要返回多个值的情况。C++17之前,只能事先定义一个结构体,然后返回这个结构体类型就可以了。但是如果你的这个结构体类型只在这个这里用一次呢。你还要抠脑壳想一个合适的结构体名字,麻烦!std::tupleC++11引入了一个新的类模板叫std::tuple,是一个容器,可以......
  • mysql 8 报错 ERROR 1872 (HY000): Slave failed to initialize relay log info struc
    在MySQL8中遇到错误1872(HY000):Slavefailedtoinitializerelayloginfostruct的问题通常与主从复制配置有关,可能是由于配置问题或者数据不一致导致的。以下是可能的解决方法:检查主从服务器配置:确保主服务器和从服务器的配置信息正确,包括server_id的设置,主服务......
  • 前端学习-UI框架学习-Bootstrap5-011-徽章(Badges)
    菜鸟教程链接<template><divclass="containermt-3"><h3>徽章<spanclass="badgebg-success">new</span></h3><h3>药丸形状徽章<spanclass="badgebg-dangerrounded-pill">new</span&g......
  • 前端学习-UI框架学习-Bootstrap5-010-按钮组
    菜鸟教程链接btn-group按钮组<template><divclass="containermt-3"><h2>加载按钮组</h2><divclass="btn-group"><buttonclass="btnbtn-primary">......