汇编指令命令
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连到一起写,指令名和目标寄存器之间使用空格,寄存器和数据之间使用逗号隔开,指令中的字符不区分大小写
- 数据处理指令
数据搬移指令??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
- ?跳转指令
实现程序的跳转,本质是更改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);
- load/store指令(批量操作)
对内存的读写操作,将运算结果从cpu写到内存
可用地址查找:(我们不用查找,脚本文件中配置了内存空间的分配)?
单寄存器操作指令?ldr?/?str
- 格式: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理器中寄存器的内容读取到协处理器的寄存器中
协处理器指令
- 协处理器数据运算指令
CDP
- 协处理器储存器访问指令
STC?将协处理器中的数据储存到存储器
LDC?将存储器中的数据读取到协处理器中
- 协处理器寄存器传送指令
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的编译器)
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!