汇编指令命令

ARM汇编指令学习

  • 工程搭建

汇编编程环境搭建

HN0AE-522LW-NNRAF-63PUS-7IGFH-YF58D汇编指令命令_第1张图片

配置编译工具链

汇编指令命令_第2张图片

汇编指令命令_第3张图片

汇编指令命令_第4张图片

汇编指令命令_第5张图片

汇编指令命令_第6张图片

汇编指令命令_第7张图片

为工程配置链接脚本(map.lds)

将map.lds 复制到工程文件夹(在桌面创建的ARM-ASM文件)

汇编指令命令_第8张图片

汇编指令命令_第9张图片

汇编指令命令_第10张图片

汇编指令命令_第11张图片

创建汇编文件

接下来我们需要建立一个start.s汇编文件添加到我们的工程中去

汇编指令命令_第12张图片

汇编指令命令_第13张图片

汇编指令命令_第14张图片

汇编指令命令_第15张图片

汇编指令命令_第16张图片

  • 汇编指令学习

c语言中哪些代码可以生成汇编指令?

1》带#号预处理,辅助编译器怎么编译,编译什么内容

预处理器是C语言编译器的一个组成部分,它在编译代码之前对代码进行处理。预处理器指令以#号开头,告诉编译器在编译代码之前执行一些操作。其中,#include指令用于将头文件包含到源代码中,#define指令用于定义宏。预处理器的主要作用是辅助编译器编译代码。 例如在编译时将头文件中的函数声明插入到源代码中,或者将宏替换为实际的值。预处理器处理完代码后,编译器将生成目标代码,最终生成可执行文件。

2》带;号的语句,可以编译生成指令

在编译器中,分号是语句结束的标志,编译器会将分号之前的语句编译成指令并添加到指令序列中。

汇编整体分类

1》指令:编译完生成一条机器码存储在内存单元当中,CPU执行时能完成对应的操作(类似于C中的语句)

2》伪操作(相当于c中的#的内容,告诉编译器怎么编译):不会生成机器码也不会占用内存,其作用是告诉编译器怎样编译(类似于C中的预处理指令)

3》伪指令:不是指令,编译器在编译时将其替换成等效的指令 

 (如:cpu中没有乘法器,对应没有乘法指令,3*3 ---》用加法器实现3+3+3,替换实现)

 汇编中注释代码用@或;注释一行 ,/* */注释一段代码 

指令分类

1.数据处理指令: 对数据进行逻辑、算术运算

2.跳转指令: 实现程序的跳转,本质是修改PC

3.Load/Store指令: 对内存的读写操作

4.状态寄存器传送指令: 对CPSR进行读写操作

5.异常中断产生指令: 触发软中断,常用于内核的系统调用  //SWI:软中断

6.协处理器指令: 操作协处理器的指令

//如3*3 ---》用加法器实现3+3+3,比较慢。我们可以外接一个协处理器(乘法器)(每个协处理器的功能比较单一),协处理器指令就是操作这个协处理器的,用的比较多的cp15协处理器。

汇编指令代码框架

.text            @声明一段代码
.global _start    @将_start 声明为一个全局的符号,其他.s文件也可以引用
                    @如调用函数  func  :  .global func
_start:            @汇编的入口
                    @汇编代码段
     
.end                @汇编的结束
(最后空一行,否则有警告)

指令的语法格式(不用记)

<opcode{<cond>}{S}> <Rd>, <Rn>, <Operand2>
<操作码> <目标寄存器Rd> <第一操作寄存器Rn> <第二操作数Operand2>
;第一个位置必须是寄存器,第二操作数可以是寄存器,也可以是立即数

<opcode{<cond>}{S}> <Rd>, 

opcode:指令的名字

cond:条件码(if else),可以省略不写,默认指令是无条件执行

S:状态标志

加s,指令的执行结果影响CPSR的NZCV位,

不加s,无影响

Rd:目标寄存器

Rn:第一个操作寄存器

oprand2:第二个操作数,可以是普通寄存器,可以是立即数

注:指令的名字,条件码,s连到一起写,指令名和目标寄存器之间使用空格,寄存器和数据之间使用逗号隔开,指令中的字符不区分大小写

  1. 数据处理指令

数据搬移指令  mov

汇编指令命令_第17张图片

如果是立即数,前面必须加#号

什么是立即数?

立即数

立即数通常是指在立即寻址方式指令中给出的数。可以是8位、16位或32位,该数值紧跟在操作码之后。

寻址方式:

寻址方式_百度百科

立即数是保存在指令中的数,取指令的同时将值取过去,和普通变量的区别是,变量保存在内存中的数据,需要单独取值运算。

立即数的本质:立即数是包含在指令当中的数据(即属于指令的一部分)

立即数的优点:读取指令的同时也将立即数读取到了内存中,速度快

立即数的缺点:数量有限

汇编指令命令_第18张图片

怎么判断一个数是否立即数?

给定一个数,将这个数中的所有的1,可以组合成一个0-255之间的数,将0-255之间的这个数,循环右移偶数个位数,如果可以得到给定的这个数,说明是立即数。

问:只要挨着的就是立即数?对吗?(对)

注:使用mov 给寄存器里面存放值的时候,#号后面需是有效数(1:立即数,2:取反之后是立即数),如果不是立即数需要用ldr指令进行存放。

如果不是立即数,用伪指令ldr  赋值

汇编指令命令_第19张图片

PC寄存器讲解

汇编指令命令_第20张图片

指令的执行三步:取址,译码,执行(PC永远指向当前正在取指指令的地址)

算术运算指令

数据运算指令格式

<操作码><目标寄存器><第一操作寄存器><第二操作数>

ADD R3,R1,R2;

操作码 指定当前指令是哪种运算

目标寄存器 存放运算结果

第一操作寄存器 存放参与运算的一个数据(只能是寄存器)

第二操作数 存放参与运算的另一个数据(可以是寄存器/立即数)

算术运算指令

add加法 adc 带进位的加法

sub 减法 sbc 带借位的减法

mul乘法 (乘法运算的R2(第二操作数)不能为立即数) 

add 普通加法

汇编指令命令_第21张图片

adc 带进位加法

假设有两个64位的数相加

第一个64位数,R0放低32位数,R1放高32位数;

第一个64位数,R2放低32位数,R3放高32位数;

结果放在R4放低32位数,R5放高32位数;

汇编指令命令_第22张图片

汇编指令命令_第23张图片

减法运算,产生借位时c=1,否则 c=0;

汇编指令命令_第24张图片

sub 普通减法

汇编指令命令_第25张图片

subs 减法(刷新CPSR)

减法指令执行时,有借位时 CPSR 'C' 位置 1

汇编指令命令_第26张图片

mul 乘法

注意:mul r4, r3, #0x4  @ 错误------>乘法指令的第二个操作数只能是一个寄存器

汇编指令命令_第27张图片

与 and 、或orr、 异或eor、 左移lsl、 右移lsr

	mov r0,#1
	mov r1,#2
	mul r2,r1,r0
	and r3,r1,#1	@与 R3 = R1&1 -->0
	orr r4,r2,r1	@或 R4 = R2|R1-->2
	eor r5,r2,r1	@异或 R5 = R2^R1-->0
	lsl r6,r2,r1	@左移 R6 = R2<8
	lsr r7,r2,r1	@右移 R7 = R2>>R1-->0

汇编指令命令_第28张图片

  1.  跳转指令

实现程序的跳转,本质是更改PC

修改PC

不建议使用,因为需要查询地址汇编指令命令_第29张图片

b   bl :指令跳转

格式:b/bl Label 

Label: 指令

相当C语言的函数调用

b指令(不带返回的跳转)       

      不保存返回地址的跳转(返回地址不保存到lr中)

一路向前,不返回

汇编指令命令_第30张图片

bl指令(带返回的跳转指令)

将LR的值修改成跳转指令下一条指令的地址

再将PC的值修改成跳转标识符下指令的地址

汇编指令命令_第31张图片

补充了解:

指令条件码表:可跟的判断条件成立跳转(NZCV在用于判断两者之间关系使用比较多)

汇编指令命令_第32张图片

练习

实现以下逻辑
unsigned int r1 = 9;
unsigned int r2 = 15;
while(1)
{
    if(r1 == r2)   //cmp
    goto stop;
    if(r1 > r2)
    r1 = r1 - r2;
    if(r1 < r2)
    r2 = r2 - r1;    //subcc r2,r2,r1
}
stop:
    while(1);

  1. load/store指令(批量操作)

对内存的读写操作,将运算结果从cpu写到内存

汇编指令命令_第33张图片

可用地址查找:(我们不用查找,脚本文件中配置了内存空间的分配) 

单寄存器操作指令 ldr / str

  1. 格式:ldr/str  Rm, [Rn]

ldr:

str:写

Rm: 存储是数据

Rn:存储的数据,地址

汇编指令命令_第34张图片

1>前索引

	mov r1,#0xffffffff
	mov r2,#0x40000000
	str r1,[r2,#8]		@基址加变址寻址把R1存在0x40000000+8的内存里

2>后索引

mov r1,#0xffffffff
mov r2,#0x40000000
str r1,[r2],#4		@将R1寄存器的内容存到[R2]地址,然后R2=R2+4

汇编指令命令_第35张图片

目的:可以做连续存储,压栈时用的比较多  存完一个数,他就把地址自动指向下一个了

3>自动索引(前后索引)

	mov r1,#0xffffffff
	mov r2,#0x40000000
	str r1,[r2,#4]!    @将R1寄存器的内容存到[R2+4]地址,然后R2=R2+4

汇编指令命令_第36张图片

批量寄存器操作指令ldm/stm 

1、将r1到r4中的值存储到r0指向地址空间中,连续16个字节的地址空间

stm r0, {r1-r4}

汇编指令命令_第37张图片

2、将r0指向的地址空间中,连续的16个字节的数据,读到r5-r8寄存器中

ldm r0, {r5-r8}

汇编指令命令_第38张图片

3、如果寄存器列表中的寄存器编号既有连续又有不连续,连续的使用 - 隔开 不连续的使用 ,

stm r0, {r1-r3,r5}

汇编指令命令_第39张图片

4、不管寄存器列表中的寄存器编号顺序如何变化,都是小地址对应小编号的寄存器高地址对应大编号的寄存器

stm r0,{r4,r2,r1,r3}

ldm r0,{r8,r6,r5,r7}

汇编指令命令_第40张图片

4、栈的操作指令 stmfd / ldmfd

栈的种类

空栈(Empty)

栈指针指向的地址是空的,在栈中存储数据时,可以直接存储,存储完成之后需要将栈指针再次指向空的位置。

汇编指令命令_第41张图片

满栈(Full)

栈指针指向的地址有数据,在栈中存储数据时,需要先将栈指针,指向一个空的位置,然后在存储数据。

汇编指令命令_第42张图片

增栈(Ascending)

栈指针向高地址方向移动

减栈(Descending)

栈指针向低地址方向移动

操作栈的方式

满增栈,满减栈,空增栈,空减栈

FA:Full Ascending 满增

FD:Full Descending 满减

EA:Empty Ascending 空增

ED:Empty Descending 空减

汇编指令命令_第43张图片

ARM默认采用的是满减栈

汇编指令命令_第44张图片

stmfd/ldmfd sp!, {寄存器列表}

stmfd sp!, {r1-r5}(写) (压栈)

汇编指令命令_第45张图片

更新栈指针指向的地址空间

ldmfd sp!, {r6-r10}(读) (出栈)

汇编指令命令_第46张图片

汇编指令命令_第47张图片

特殊:(CPU寄存器不连续)

汇编指令命令_第48张图片

汇编指令命令_第49张图片

stmfd sp!, {r1-r5,lr}(写) (压栈

汇编指令命令_第50张图片

ldmfd sp!, {r6-r10,pc}(读) (出栈)    //r1-r5出栈给r6-r10, 将lr的值出栈给pc

汇编指令命令_第51张图片

程序中运用

汇编指令命令_第52张图片

ldr r1,=0x11111111
	ldr r2,=0x22222222
	ldr r3,=0x33333333
	ldr r4,=0x44444444
	ldr r5,=0x55555555
	ldr sp,=0x40001020
	
	stmfd sp!,{r1-r5}
	bl func
	ldmfd sp!,{r1-r5}
	ldr r6,=0x66666666
	
	loop:
loop
	func:
	ldr r1,=0x1
	ldr r2,=0x2
	ldr r3,=0x3
	ldr r4,=0x4
	mov pc,lr

汇编指令命令_第53张图片

栈的应用-》叶子函数的调用过程

叶子函数是指一个函数内部没有调用其他函数的函数,也就是说,它是程序调用树的末端节点,不依赖于其他函数。

	MOV SP,#0X40000020
MAIN:
	MOV R1,#3
	MOV R2,#2
	BL F
	ADD R3,R1,R2
T:
	B T
F:
	STMFD SP!,{R1,R2}
	MOV R1,#5
	MOV R2,#4
	ADD R3,R1,R2
	LDMFD SP!,{R1,R2}
	MOV PC,LR

栈的应用-》非叶子函数的调用过程

非叶子函数是指一个函数内部调用了其他函数的函数,也就是说,它不是程序调用树的末端节点,可以被其他函数调用。

	MOV SP,#0X40000020
MAIN:
	MOV R1,#3
	MOV R2,#2
	BL F
	ADD R3,R1,R2
T:
	B T
F:
	STMFD SP!,{R1,R2,LR}
	MOV R1,#5
	MOV R2,#4
	BL D
	ADD R3,R1,R2
	LDMFD SP!,{R1,R2,LR}
	MOV PC,LR
D:
    STMFD SP!,{R1,R2,LR}
	MOV R1,#6
	MOV R2,#7
	ADD R3,R1,R2
	LDMFD SP!,{R1,R2,LR}
	MOV PC,LR

5、状态寄存器操作指令

注:刚上电是在SVC模式下

对CPSR进行读写操作

//其他都不能动CPSR   (SWI 指令是linux内核有,所以arm为了匹配才有的指令)

(CPSR保存cpu的状态、模式、中断中断开关、运算状态,非常重要,不能任意更改,只有一类指令能操作这个寄存器)

读cpsr指令 mrs 

汇编指令命令_第54张图片

汇编指令命令_第55张图片

写cpsr指令 msr  

	@状态寄存器操作指令		mrs / msr
	ldr r1,=0x12345678
	mrs r0,cpsr			@读cpsr的值到r0
	msr cpsr_c,#0x1f	@修改cpsr的值,cpsr_c 指的是后八位控制位
	msr cpsr_c,#0x13	@切换到svc
	msr cpsr_c,#0x10	@切换到user,非特权模式
	ldr r2,=0x87654321
	msr cpsr_c,#0x13	@切换到svc

注:修改CPSR的控制域(bit[7:0]),修改CPSR时必须指定修改哪个区域;USER模式下不能修改CPSR的值,防止应用程序修改CPU状态,保护操作系统;CPSR_C修改的是CPSR的低八位ctrl(控制)域,一般都只修改C域

6、异常中断指令

触发软中断,常用于内核的系统调用   //SWI:软中断

@异常中断指令		软中断指令swi
	ldr r1,=0x12345678
	ldr r2,=0x22345678
	ldr r4,=0x42345678
	msr cpsr_c,#0x10	@切换到user模式
	ldr r3,=0x32345678
	swi #1		@软中断
	@执行软中断指令:spsr、lr、cpsr、pc 均发生了改变
	
	ldr r6,=0x52345678

汇编指令命令_第56张图片

为什么会出现以上情况?做完的请先自行阅读下方文档

异常源及处理过程

7、协处理器指令

操作协处理器的指令(一般用不到-----协助cpu处理数据)

1.数据运算
2.内存访问
3.与主处理器通信
MRC 将协处理器中寄存器的内容读取到ARM处理器的寄存器中
MCR 将ARM理器中寄存器的内容读取到协处理器的寄存器中

汇编指令命令_第57张图片

协处理器指令

  1. 协处理器数据运算指令

CDP

  1. 协处理器储存器访问指令

STC 将协处理器中的数据储存到存储器

LDC 将存储器中的数据读取到协处理器中

  1. 协处理器寄存器传送指令

MRC 将协处理器中寄存器的数据传送到ARM处理器中的寄存器

MCR 将ARM处理器寄存器中的数据读取到协处理器寄存器中

伪指令

本质:本身不是指令,但是cpu替换成等效的操作。

举例1
延时一个指令周期(耗时一条指令的时间)  (cpu没有这个指令)
	NOP    ;执行NOP和MOV R0,R0一个效果,执行NOP,cpu替换成MOV R0,R0
	MOV R0,R0  
LDR的两种形式
;->指令
        LDR R1,[R2]     
;->伪指令
	    LDR R1,=0x12345678     ;R1 = 0x12345678
;可以将任何一个32bit的数据放入寄存器

伪操作

指令是arm公司规定的,而伪操作是编译器规定的,不同的编译器伪操作指令不同。

(我们学的linux,用linux的编译器)

你可能感兴趣的:(ARM开发,汇编,嵌入式硬件,arm开发)