嵌入式ARM汇编详解 您所在的位置:网站首页 汇编中sub 嵌入式ARM汇编详解

嵌入式ARM汇编详解

2022-05-26 05:11| 来源: 网络整理| 查看: 265

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

零.预备知识

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

1.ARM与X86

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

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

image.png

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寄存器中



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有