ARM嵌入式开发中经常会涉及到汇编指令的知识,这里就总结一下最常用的几种ARM汇编指令。

零.预备知识

这里我们主要学习ARM的汇编指令,这些预备知识只是作为一个了解。

1.ARM与X86

要了解ARM,最好的是使用对比的方法。

ARM是一款32位的低功耗RSIC(精简指令集)微处理器。我们常了解的CPU可能就是办公中常用到的X86架构的计算机,X86使用的就是CSIC(复杂指令集),比如很有名的Intel处理器,下面就通过分析一下ARM架构与X86架构的区别来认识ARM:

项目 ARM X86
指令集 RSIC精简指令集 CSIC复杂指令集
功耗 低功耗 超高性能
用途 移动端的老大 PC端的老大
解码 并行 并行

ARM的特点是:

  • 体积小、低功耗、高性能
  • 支持Thumb(16位)和ARM(32位)双指令
  • 指令执行效率高
  • 寻址方式简单
  • 指令长度固定

2.ARM中指令的执行

由于ARM采用RSIC架构,所以CPU本身不能直对内存进行操作,而是先将内存中数据加载到CPU中的寄存器,然后对寄存器中的值进行处理。

ARM中指令的执行遵循流水线形式,众所周知,CPU执行指令都是从内存中进行取指令、分析指令、执行指令的。一阶段持从内存中取回的指令,第二阶段开始解码,而第三阶段实际执行它。故此,程序计数器总是超出当前执行的指令两个指令。

ARM处理指令时,比如要连续执行三条指令:a、b、c,那么ARM在执行a指令的同时,已经在解析b指令了,同时有在内存中取c指令。所以pc寄存器中的地址一般是当前指令地址+8(Byte),因为ARM是32位CPU,所以一次处理的指令是4字节,所以第三条指令的地址就是当前指令的地址+8。

在大概说一下RSIC吧,RSIC将ARM要执行的操作以最基本的指令实现。换句话说,如果人们是在RSIC指令集下执行跑步时,大脑(CPU)对人体发出的指令就是:先迈左脚、再迈右脚、再迈左脚、再迈右脚…将跑步这条指令细化为每一个最基本的操作。如果是CSIC(复杂指令集时),大脑只需要对人体发出指令:跑步,就可以了。

3.ARM的九种寻址方式

寻址方式就是CPU根据指令中的地址信息,找出物理地址也就是内存地址的方式,通俗理解就是ARM指出内存地址的方式。

寻址的目的就是找出操作数,比如ARM要做一个除法运算,就需要除数和被除数,除数和被除数都是除法指令的操作数,要找到这些操作数,可以有多种方法,寻找操作数的过程就叫做寻址。(我个人理解)

ARM支持九种寻址方式:

  • 立即数寻址
  • 寄存器寻址
  • 寄存器偏移寻址
  • 寄存器间接寻址
  • 寄存器基址变址寻址
  • 多寄存器寻址
  • 相对寻址
  • 堆栈寻址
  • 块拷贝寻址

立即数寻址

立即数寻址就是直接将内存中的数据发给CPU作为操作数。注意,由于ARM是32位指令集,所以立即数的范围不可以超出0255,也就是说立即数的范围只能是0255。

格式:就是在立即数前面加上 # 来作为操作数

典型的例子就是直接对寄存器进行写值:

ldr r0, #254   ;将254写入r0寄存器
add r1, r2, #3 ;将r2寄存器中的值与3相加后,在写入r1寄存器

寄存器寻址

寄存器寻址就是直接将寄存器中的数值作为操作数:

ldr r1, r0      ;将r0寄存器中的值写到r0
add r3, r2, r1 ;将r1、r2寄存器的值相加,结果写入r3寄存器

寄存器间接寻址

还是利用了寄存器,只不过操作数不是寄存器中的值了,操作数在内存中,那怎么办?没事,操作数的地址就在寄存器中。所以寄存器间接寻址相当于以寄存器中的值作为内存地址,去内存中寻找操作数。

格式:在提供操作数地址的寄存器上加上[],比如[r0]

mov r0, #0X54000032
ldr r1, [r0] ;将地址为0X54000032的数据写入r1寄存器中

寄存器偏移寻址

以寄存器寻址为本,将寄存器中的数移位后作为操作数。

一共有6中移位操作:

LSL:逻辑左移(Logical Shift Left),寄存器中字的低端空出的位补0。

LSR:逻辑右移(Logical Shift Right),寄存器中字的高端空出的位补0。

ASL:算术左移(Arithmetic Shift Left),和逻辑左移LSL相同。

ASR:算术右移(Arithmetic Shift Right),移位过程中符号位不变,即如果源操作数是正数,则字的高端空出的位补0,否则补1。

ROR:循环右移(Rotate Right),由字的低端移出的位填入字的高端空出的位。

RRX:带扩展的循环右移(Rotate Right eXtended),操作数右移一位,高端空出的位用进位标志C的值来填充,低端移出的位填入进位标志位。

格式:rx, 移位命令 移位操作数

ldr r0, r1, lsl #3   ;将r1的值逻辑左移3位后写入r0
ldr r0, r1, ror r2 ;将r1的值循环右移r2中的值对应位后,写入r0

寄存器基址变址寻址

基址变址寻址是基于寄存器间接寻址的,只不过地址不再是寄存器中的值了,而是偏移后的值,这里的偏移值可以理解为地址相加值。

加上感叹号应该有优先执行的意思吧(个人理解)

格式:[rx, n],表示在rx寄存器所指向的地址上,再偏移(相加)n字节

ldr r0, [r1, #3]     ;地址为:r1值+3字节,指令执行完r1不变
ldr r0, [r1, #3]! ;地址为:r1值+3字节,指令执行完r1+3
ldr r0, [r1, #-1] ;地址为:r1值-1字节,指令执行完r1不变
ldr r0, [r1, r2] ;地址为:r1值+r2值
ldr r0, [r1], #4 ;地址为:r1值,但指令执行完后,r1值+4字节

批量寄存器寻址

批量寄存器寻址就是使用一个大括号{}包含多个寄存器

ldmia r0, {r1, r2, r3, r4}     ;将r1,r2,r3,r4中的数据依次放入R0指向的内存地址,r0+4指向的内存地址...
ldmia r0, {r1-r4} ;同上。

注意,作为存储地址时,高编号的寄存器存放在高地址

高编号寄存器存放在高地址

所以,写内存的时候(默认从高地址往下写),优先操作高编号寄存器

相反,读内存的时候(默认从低地址往上读),优先操作低编号寄存器

相对寻址

通过标号进行寻址,经常与跳转指令相配合使用

	bl heihei 

heihei:        ;跳转到heihei执行

堆栈寻址

堆栈即Stack,因为CPU的寄存器总是及其有限的,很多时候我们不得不使用内存来存储数据,比如进行多级跳转的时候,这时候堆栈就是一个很好的工具,每次跳转就将当前函数的返回地址存储到内存,最底层被调用的子函数会最先返回,就先将压入栈的现场返回,以此类推…,ARM使用SP(R13)作为栈指针,ARM设计的内存栈模型有2×2=4种

按照栈在内存增长的方向分为递增栈递减栈

**递增(Increase)**堆栈:向堆栈写入数据时,堆栈由低地址向高地址生长。

**递减(Descend)**堆栈:向堆栈写入数据时,堆栈由高地址向低地址生长。

根据堆栈指针SP指向的位置,又可以把堆栈分为满堆栈空堆栈两种。

满堆栈(Full Stack):SP始终指向栈顶元素,压栈的时候先移动SP,再将数据放入SP指向的地址。

空堆栈(Empty Stack):SP始终指向下一个将要放入元素的位置,压栈时先将数据放入SP指向的地址,再移动SP

最后,可以得到4种基本的堆栈类型:

满增栈(FA):堆栈指针指向最后压入的数据,且由低地址向高地址生长。

满减栈(FD):堆栈指针指向最后压入的数据,且由高地址向低地址生长。常用这种

空增栈(EA):堆栈指针指向下一个将要压入数据的地址,且由低地址向高地址生长。

空减栈(ED):堆栈指针指向下一个将要压入数据的地址,且由高地址向低地址生长。

stmfd sp!, {r1-r7, lr}  ;将r1到r7和lr的数据压入fd栈

块拷贝寻址

块拷贝寻址提供了一块内存和一组寄存器之间的拷贝,按照内存使用方式的不同,可以分为2×2=4种。地址增方向/地址减方向×先偏移/后偏移。堆栈寻址就可以看作是块拷贝寻址的的一个实例。

即:

IB:Increment Before Operating

IA:Increment After Operating

DB:Decrement Before Operating

DA:Decrement After Operating

STMIA  R0!,{R1—R7}  ;将R1-R7的寄存器中的值放入R0指向的地址,R0自动更新,指向操作后的地址

参考文章

一.移位操作

移位操作在ARM中不可以作为一个单独的指令使用,移位操作只是指令格式中的一个字段。

最常用的就是逻辑移位了,遵循左乘右除的法则。

二.寄存器装载和存储指令

寄存器装载指令和寄存器存储指令是控制:寄存器和内存之间的交互的。

一般的汇编指令都是操作寄存器的,然鹅寄存器又要与内存进行数据交互,所以就需要有汇编指令在寄存器与内存之间扮演搬运工的角色了。

当需要装载存储多个数据时,使用LDMxx和STMxx指令如下:

最常用的一组是:LDMIA和STMDB(俩个相对应)

1.LDR:装载单一数据

LDR是寄存器装载指令,可以从内存地址中读取数据,写到指定寄存器中.

格式为:LDR{条件} Rd, <地址>

例如:

LDR R0, [R1]          ;将r1中对应地址的数据写到r0
LDR R0, =0X54000056 ; 将0X54000056写到r0

2.LDMIA:先减少,后装载

IA:先装载,后增加,经常配合栈指针来使用:

LDMIA sp, {fp, sp, pc}

而且对于批量寄存器寻址{},遵循高编号寄存器存放在高内存地址,而fp、ip、sp、lr、pc四个寄存器的编号分别为:11、12、13、14、15,所以LDMIA的操作顺序就是pc、sp、fp

所以指令的指向过程如下:
sp指向的地址-4字节

将pc地址的数据写到sp

sp指向的地址-4字节

将sp地址的数据写到sp

sp指向的地址-4字节

将fp地址的数据写到sp

3.STR:存储单一数据

STR是存储指令,由于将寄存器中的数据存储到指定内存中

格式为:STR{条件} Rd, <内存>

例如:

Rbase表示基地址寄存器,Rindex表示变址寄存器,index表示偏移量

STR Rd, [Rbase]            ;将Rd的值写到Rbase包含的地址中
STR Rd, [Rbase, Rindex] ;将Rd的值写到Rbase+Rindex(偏移后)所包含的地址中
STR Rd, [Rbase, #index] ;将Rd的值写到Rbase包含地址偏移index后的地址中
STR Rd, [Rbase, Rindex]! ;把新地址写回Rbase
STR Rd, [Rbase, #index]! ;把新地址写回Rbase
STR Rd, [Rbase], Rindex ;把Rd的值写到Rbase包含的地址中,再将Rbase+Rindex后的地址写入Rbase中
STR Rd, [Rbase, Rindex, LSL #2]
;将Rd的值写入Rbase+(Rindex*4)后的地址中,LSL为左移,左乘右除,左移2位代表乘以4

4.STMDB:先存储,后增加

STMDB(默认选项)!感叹号代表取最终被修改的结果

高编号寄存器存放在高地址

所以,写内存的时候(默认从高地址往下写),优先操作高编号寄存器

相反,读内存的时候(默认从低地址往上读),优先操作低编号寄存器

STMDB sp!, {fp, ip, lr, pc}

其中!代表sp的值是最终的结果。而且对于批量寄存器寻址{},遵循高编号寄存器存放在高内存地址,而fp、ip、lr、pc四个寄存器的编号分别为:11、12、14、15,所以STMDB的操作顺序就是fp、ip、lr、pc

先将pc写入sp指向的地址

sp-4字节

再将lr写入sp指向的地址

sp-4字节

再将ip写入sp指向的地址

sp-4字节

再将fp写入sp指向的地址

sp最终为sp-12

三.算术和逻辑指令

1.MOV:传送

MOV可以将一个寄存器(也可以是配合移位操作的寄存器)的值传送到另一个寄存器中,相当于复制寄存器的值,当然传送的对象不仅可以是寄存器,也可以是数值,例如:

MOV R0, R1          ;将R1的值传送到R0
MOV R0, =123 ;将123写入R0
MOV R0, R1, LSL #3 ;将R1*8后的值写入R0

2.ADD:加法

ADD就是将俩个操作数相加,将结果写入指定寄存器中,例如:

ADD R0, R1, R2      ;R0=R1+R2
ADD R0, R1, #255 ;R0=R1+255
ADD R0, R1, R2, LSL #2 ;R0=R1+(R2*4)

3.SUB:减法

SUB将俩个操作数做减法,将结果写入指定寄存器中,例如:

SUB R0, R1, R2      ;R0=R1-R2
SUB R0, R1, #255 ;R0=R1-255
SUB R0, R1, R2, LSL #2 ;R0=R1-(R2*4)

4.AND:逻辑与

AND将俩个操作数进行逻辑与操作,将结果写入目的寄存器中,操作数可以是:寄存器、被移位的寄存器、立即数,例如:

AND R0, R0, #2  ;只保留R0中数据的1位

5.ORR:逻辑或

ORR使用方法与AND一样。

6.BIC:位清除

BIC可以定点清除寄存器中数据的某一位,其作用原理与掩码类似,操作数2是一个32位掩码,例如:

BIC R0, R0, #%111011  ;清除2位上的数据

四.比较指令

1.CMP:比较

CMP指令可以用来比较俩个操作数的区别,将结果以更新CPSR寄存器相关的条件标志位,后期通过判断相关位来了解相同还是不相同。

例如:

CMP R0, R1    ;判断R0值与R1值是否相同
CMP R0, #5 ;判断R0值是否为5

五.跳转指令

ARM汇编中的跳转可以有俩种实现方式,第一种就是利用跳转指令。第二种就是直接向程序计数器PC中写入要跳转的地址,这样可以实现任意地址的跳转

1.B:直接跳转

B是最简单的分支,遇到B指令后,ARM就会跳转到B指定的地址进行执行,这个跳转指令没有返回值,一旦跳了就不可回头。

例如:

B Hei   ;跳转到标号Hei处执行

2.BL:跳转且保存当前地址

BL也是跳转指令,与B不同的是,BL指令跳转时,会将当前的地址存储在R14(LR)寄存器中,当执行完调用子程序时,还可以跳回原程序处继续执行。

例如:

BL Hei    ;跳转到标号Hei处执行,同时将当前地址保存在LR寄存器中

嵌入式ARM汇编详解的更多相关文章

  1. 嵌入式linux性能详解_转

    最近简单看了下<嵌入式Linux性能详解>一书,对系统内存分布测试.程序运行.动态库等都很很好的解析. 作者史子旺,loughsky@sina.com. 有时间希望仔细通读,并验证.

  2. ARM指令集详解--汇编

    1.       汇编 1.1.    通用寄存器 通用寄存器 37个寄存器,31个通用寄存器,6个状态寄存器,R13堆栈指针sp,R14返回指针,R15为PC指针, cpsr_c代表的是这32位中的 ...

  3. gcc内嵌汇编详解

    [作者:byeyear    首发:cnblogs    Email:east3@163.com    转载请注明] 有时候我们希望在C/C++代码中使用嵌入式汇编,因为C中没有对应的函数或语法可用. ...

  4. hibernate的@EmbeddedId嵌入式主键详解

    一.前言 在我们的日常开发中,有时候会用到数据库进行设计的时候,采用了复合主键来来保证唯一性,下面介绍一下采用hibernate的@EmbeddedId嵌入式主键. 二.说明 设计一个学生类,包含了三 ...

  5. stdcall、cdecl、fastcall、thiscall 、naked call的汇编详解

    函数调用规范   当高级语言函数被编译成机器码时,有一个问题就必须解决:因为CPU没有办法知道一个函数调用需要多少个.什么样的参数.即计算机不知道怎么给这个函数传递参数,传递参数的工作必须由函数调用者 ...

  6. ARM指令集详解

    一.跳转指令 B: 跳转指令 BL: 带返回的跳转指令 BLX: 带返回和状态切换的跳转指令 BX: 带状态切换的跳转指令 二.数据处理指令 1.MOV:数据传送指令 MOV{条件}{S}    目的 ...

  7. 嵌入式nand flash详解

    一.s3c2440启动后会将nand flash的前4K程序复制到内部的sram中,这个过程是硬件自动完成的,但是如果我们的程序远远大于4K,这个时候就需要将程序从flash拷贝到内存中来运行了. 二 ...

  8. 2.3 ARM寄存器详解

    一共有37个寄存器 1. 31个通用寄存器 2. 6个状态寄存器 R13作为堆栈指针 R14链接寄存器 1.保存函数返回地址 2. 异常返回地址 R15程序计数器(PC指针) 程序状态寄存器 只有在异 ...

  9. 022 ARM寄存器详解

    R13:堆栈指针寄存器 SP R14:链接寄存器 LR R15:程序计数器 PC指针 CPSR:当前程序状态寄存器 SPSR:备份程序状态寄存器

随机推荐

  1. Exception 和Error异常大部分人都犯过的错。

    先看再点赞,给自己一点思考的时间,如果对自己有帮助,微信搜索[程序职场]关注这个执着的职场程序员. 我有什么:职场规划指导,技能提升方法,讲不完的职场故事,个人成长经验. 1,简介 Exception ...

  2. Go nuts

    含义: to behave in a crazy, enthusiastic, or violent way. 发起狂来 详细讲解 go 在这里也不是"去"的意思,而是和 get. ...

  3. 从新建文件夹开始构建UtopiaEngine(2)

    本篇序言 摸了两个月的鱼,又一次拾起了自己引擎的框架,开始完善引擎系统,如果非要用现实中的什么东西比喻的话,那么我们目前实现的框架连个脚手架都不是.把这项目这样晾着显然不符合本人的风格,而且要作为毕业 ...

  4. 深入理解Java多线程——线程池

    目录 为什么需要线程池 定义 ThreadPoolExecutor 工作队列workQueue 不同的线程池 Executor 线程池的工作原理 线程池生命周期 线程池增长策略 线程池大小的设置 线程 ...

  5. 「CF576D」 Flights for Regular Customers

    「CF576D」 Flights for Regular Customers 对不起我又想网络流去了 你看这长得多像啊,走过至少多少条边就是流量下界,然后没上界 但是这个题求的最少走多少条边啊...完 ...

  6. Activiti7 回退与会签

    1.  回退(驳回) 回退的思路就是动态更改节点的流向.先遇水搭桥,最后再过河拆桥. 具体操作如下: 取得当前节点的信息 取得当前节点的上一个节点的信息 保存当前节点的流向 新建流向,由当前节点指向上 ...

  7. WIN7 WIN10修改path不用重启即可生效

    近来安装python scrapy经常忘了添加到path,需要时增加了但需要重启才能起作用,用下面的方法马上能生效: 1修改path:右击"计算机"--高级--环境变量--path ...

  8. excel带格式复制python

    openpyxl 复制cell单元格包括所有样式 target_cell.data_type = source_cell.data_type target_cell.fill = copy(sourc ...

  9. 家庭账本开发day02

    今日完成 今天主要进行了前台界面的构架,利用layUI已有的模板编写新增账单界面 然后进行了后端的Bean和Servlet的初步编写,实现数据库的构建. 遇到问题 获取前端传输数据,并添加数据到数据库 ...

  10. vue2.x移动端ui框架选型

    前言 最近公司准备做移动端spa项目,需要选一个ui框架.优先考虑谷歌Material Design设计风格.针对市面上的框架进行了一次调研,简单总结如下. 选型原则:1. 优先考虑md风格. 2. ...