汇编指令命令

2023-12-14 14:04:56

ARM汇编指令学习

  • 工程搭建

汇编编程环境搭建

HN0AE-522LW-NNRAF-63PUS-7IGFH-YF58D

配置编译工具链

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

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

创建汇编文件

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

  • 汇编指令学习

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>,?<Rn>,?<Operand2>

opcode:指令的名字

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

S:状态标志

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

不加s,无影响

Rd:目标寄存器

Rn:第一个操作寄存器

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

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

  1. 数据处理指令

数据搬移指令??mov

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

什么是立即数?

立即数

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

寻址方式:

寻址方式_百度百科

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

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

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

立即数的缺点:数量有限

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

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

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

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

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

PC寄存器讲解

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

算术运算指令

数据运算指令格式

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

ADD?R3,R1,R2;

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

目标寄存器 存放运算结果

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

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

算术运算指令

add加法 adc?带进位的加法

sub?减法 sbc?带借位的减法

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

add?普通加法

adc?带进位加法

假设有两个64位的数相加

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

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

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

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

sub?普通减法

subs?减法(刷新CPSR)

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

mul?乘法

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

与?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<<R1-->8
	lsr?r7,r2,r1	@右移?R7?=?R2>>R1-->0

  1. ?跳转指令

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

修改PC

不建议使用,因为需要查询地址

b???bl?:指令跳转

格式:b/bl?Label?

Label:?指令

相当C语言的函数调用

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

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

一路向前,不返回

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

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

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

补充了解:

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

练习

实现以下逻辑
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写到内存

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

单寄存器操作指令?ldr?/?str

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

ldr:

str:写

Rm:?存储是数据

Rn:存储的数据,地址

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

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

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

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

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

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

stm?r0,?{r1-r4}

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

ldm?r0,?{r5-r8}

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

stm?r0,?{r1-r3,r5}

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

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

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

4、栈的操作指令?stmfd?/?ldmfd

栈的种类

空栈(Empty)

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

满栈(Full)

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

增栈(Ascending)

栈指针向高地址方向移动

减栈(Descending)

栈指针向低地址方向移动

操作栈的方式

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

FA:Full?Ascending 满增

FD:Full?Descending 满减

EA:Empty?Ascending 空增

ED:Empty?Descending 空减

ARM默认采用的是满减栈

stmfd/ldmfd<code>?sp!,?{寄存器列表}

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

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

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

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

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

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

程序中运用

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:
	b?loop
	func:
	ldr?r1,=0x1
	ldr?r2,=0x2
	ldr?r3,=0x3
	ldr?r4,=0x4
	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}
	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?

写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

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

异常源及处理过程

7、协处理器指令

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

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

协处理器指令

  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的编译器)

文章来源:https://blog.csdn.net/m0_74937538/article/details/134991625
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。