一条汇编指令对应一条机器码
汇编中的符号:
指令:能够编译生成一条32位的机器码,且能被CPU识别和执行
伪指令: 本身不是指令,编译器可以将其替换成若干条等效指令
伪操作: 不会生成代码,只是在编译之前告诉编译器怎么编译(对应C语言中的#代码)
ARM指令
1.数据处理指令: 数学运算、逻辑运算
2.跳转指令: 实现程序的跳转,本质就是修改了PC寄存器
3.Load/Srore指令: 访问(读写)内存
4.状态寄存器传送指令:访问(读写)CPSR寄存器
5.软中断指令: 触发软中断异常
6.协处理器指令: 操控协处理器的指令
数据指令
MOV @数据搬移指令 通过MOV指令修改寄存器里的值
MOV R1,#1
MOV R2,#3
MOV R2,R1
MOV R1 #0x12 这里#后面的数字是立即数,不能是0x12345678,因为它把整个32位全都占用了,无法编译到机器码。
立即数:本质是包含在指令当中的数,属于指令的一部分。取指的时候可以直接读取到CPU,不能单独取内存存取,速度块。
MVN R0,#0xFF MVN按位取反搬移
PC:程序计数器,各种模式下的 R15
- 每次自增4
LR:连接寄存器
- 用来保存子程序返回地址;
- 各种异常模式下可以根据LR的值返回到异常发生前的相应位置继续执行
SP:堆栈指针(R13)
CPSR:程序状态寄存器
SPSR:程序备份寄存器
数据运算指令的格式
《操作码》 《目标寄存器》 《第一操作寄存器》 《第二操作数》
操作码:用于表示执行那种操作
目标寄存器:用于存储运算的结果
第一操作寄存器:存储第一个参与运算的数据
第二操作数:第二个参与运算的数据(可以是寄存器,可以是立即数)
ADD R1,R2,R3
ADD R1,R2,#2
SUB R1,R2,R3 @减法指令
RSB R1,R2,R3 @逆向减法指令
MUL R1,R2 @乘法指令只能是两个寄存器相乘、
AND R1,R2,R3 @按位与
ORR R1,R2,R3 @按位或
EOR R1,R2,R3 @按位异或
LSL R1,R2,R3 @左移指令
数据运算指令对CPSR条件位(N、Z、C、V)的影响
默认情况下数据运算不会对条件产生影响,当在指令后加后缀”s”后可以影响
eg: ADDS R2,R1,#1
32位处理器进行两个64位数据相加
32位处理器一次操作的最大数据是32位,对于64位数据需要进行两部
第一个数的低32位放在R1
第一个数的高32位放在R2
第二个数的低32位放在R3
第二个数的高32位放在R4
ADC 带进位的加法
跳转指令
实现程序的跳转,本质就是修改PC寄存器
方式一:直接去修改PC寄存器的值
方式二:通过跳转指令B,不带返回的跳转指令
LR连接寄存器:用于保存跳转时的下一条指令地址
方式三:带返回的跳转指令(BL),需要自己额外加返回的代码
ARM指令的条件码
关系运算符
CMP R1,R2 @比较指令
本质就是一天减法指令(SUBS),只是没有将运算的结果存入寄存器
运算结果存储在CPSR中NZCV中
R1和R2不相等,跳转条件EQ不成立,故无法跳转
ARM指令集中大多数指令都可以带条件码后缀
对应C语言就是条件判断,汇编就是CMP,然后通过条件码判断跳转
内存访问指令(存储指令)
Load/Store 访问(读写)内存、
LD :读内存数据
ST:写入到内存
STR R1,[R2] @将R1寄存器中的数据存储到R2指向的地址,寄存器地址用[ ]表示
LDR R3, [R2] @将内存中R2指向的数据读取到R3寄存器
STR与LDR同样支持条件码(指令后缀)
ARM指令的寻址方式(9种)
寻址方式就是CPU寻找一个操作数的方式
立即寻址:参与运算的数来自立即数
ADD R1,R2,#1
寄存器寻址:参与运算的数来自寄存器
ADD R1, R2,R3
寄存器移位寻址:
MOV R1,R2,LSL #1 @R2先移位
寄存器间接寻址:
STR R1 [R2]
基址加变址寻址
MOV R1 ,#0xFFFFFFFF
MOV R2 ,#0x4000000 @地址
MOV R3 ,#4 @偏移地址
STR R1,[R2,R3] @将R1寄存器中的数据写入到R2+R3指向的内存空间
STR R1, [R2,R3,LSL #1] @将R1寄存器中的数据写入到R2+(R3<<1)指向的内存空间
基址加变址寻址的索引方式
1)前索引
MOV R1, #0xFFFFFFFF
MOV R2, #0x40000000
STR R1, [R2,#8] @将R1寄存器中的数据写入到R2+8指向的内存空间
2)后索引
MOV R1, #0xFFFFFFFF
MOV R2, #0x40000000
STR R1, [R2],#8 @将R1寄存器中的数据写入到R2指向的内存空间,然后R2自增8
3)自动索引
MOV R1, #0xFFFFFFFF
MOV R2, #0x40000000
STR R1, [R2,#8]! @将R1寄存器中的数据写入到R2+8指向的内存空间,然后R2自增8
多寄存器访问指令
STM R11, {R1-R4} @将R1-R4寄存器中的数据写入到以R11为起始地址的内存空间中
LDM R11,{R6-R9} @将以R11为起始地址的内存空间中的数据读取到R6-R9寄存器中
STM R11,{R1,R2,R4} @ 当寄存器编号不连续时,使用逗号分隔
不管寄存器列表中的顺序如何,存取时永远是低地址对应小编号的寄存器
STMIA R11!, {R1-R4} @ 先存储数据,后增长地址
STMIB R11!,{R1-R4} @ 先增长地址,后存储数据
STMDA R11!,{R1-R4} @ 先存储数据,后递减地址
STMDB R11!,{R1-R4} @ 先递减地址,后存储数据
栈的应用
栈地址的生长方式(从高地址到低地址):即先分配的变量存高地址、后分配的存低地址
存放:如局部变量、函数的参数、返回值、以及程序跳转时需要保护的寄存器等
栈分类
增栈:压栈时栈指针越来越大,出栈时栈指针越来越小
减栈:压栈时栈指针越来越小,出栈时栈指针越来越大
满栈:栈指针指向最后一次压入到栈中的数据,压栈时需要先移动栈指针到相邻位置然后再压栈
空栈:栈指针指向最后一次压入到栈中的数据的相邻位置,压栈时可直接压栈,之后需要将栈指针移动到相邻位
栈分为空增(EA)、空减(ED)、满增(FA)、满减(FD)四种
LDMFD:出栈 @在汇编的时候还是会转为LDMDB
STMFD:压栈 @在汇编的时候还是会转为STMDB
专用指令
包括状态寄存器传送指令、软中断指令、协处理指令、伪指令
1、状态寄存器传送指令
MRS R1, CPSR @读CPSR,R1 = CPSR
MSR CPSR, #0x10 @写CPSR,CPSR = 0x10 改成了USER模式
在USER模式下不能随意修改CPSR,因为USER模式属于非特权模式,所以在USER模式下,执行MSR CPSR, #0xD3,是没有权限执行的
2、软中断指令
1)异常向量表
B MAIN @跳转到MAIN程序
B . @跳转回自身
B SWI_HANDLER @跳转到软中断执行
B .
B .
B .
B .
B .
2)MAIN: @应用程序
MOV SP, #0x40000020 @初始化SVC模式下的栈指针
MSR CPSR, #0x10 @切换成USER模式,开启FIQ、IRQ
MOV R1, #1
MOV R2, #2
SWI #1 @触发软中断异常
ADD R3, R2, R1
B STOP
3)SWI_HANDLER: @异常处理程序
STMFD SP!,{R1,R2,LR} @ 压栈保护现场 ,因为下面用到R1,R2避免main程序值被改变
MOV R1, #10
MOV R2, #20
SUB R3, R2, R1
LDMFD SP!,{R1,R2,PC}^ @ 出栈恢复现场
将压入到栈中的LR(返回地址)出栈给PC,实现程序的返回
^ 表示出栈的同时将SPSR的值传递给CPSR,实现CPU状态的恢复
3、协处理指令
ARM处理器只支持一些通用的指令,遇到别的指令会交给协处理器指令(比如图像指令)
比如ARM开发成SOC需要用到音频、视频等,需要相应的协处理器
1)协处理器数据运算指令
CDP
2)协处理器存储器访问指令
STC @将协处理器中的数据写入到存储器
LDC @将存储器中的数据读取到协处理器
3)协处理器寄存器传送指令
MRC @将协处理器中寄存器中的数据传送到ARM处理器中的寄存器
MCR @将ARM处理器中寄存器中的数据传送到协处理器中的寄存器
4、伪指令
NOP @空指令 相当于LDR R0,R0
LDR R1, =0x12345678 @LDR伪指令可以将任意一个32位的数据放到一个寄存器
LDR R1, =STOP @将STOP表示的地址写入R1寄存器
LDR R1, STOP @将STOP表示的地址写入R1寄存器
伪操作(GNU):不会生成代码,只是在编译之前告诉编译器怎么编译
1)GNU的伪操作一般都以‘.’开头
2).global symbol @将symbol声明成全局符号
3).local symbol @将symbol声明成局部符号
4).equ DATA, 0xFF @ 告诉编译器 DATA就是 0xFF
5).macro FUNC @封装
MOV R1, #1
MOV R2, #2
.endm
FUNC @调用刚才的封装
6).if 0 @条件编译
MOV R1, #1
MOV R2, #2
.endif
7).rept 3 @重复操作,重复3边
MOV R1, #1
MOV R2, #2
.endr
8).weak symbol @弱化一个符号,即告诉编译器即便没有这个符号也不要报错
.weak func
B func
其他具体伪操作参考相应文档
C和汇编的混合编程
方式一:汇编语言调用(跳转)C语言
MOV R1, #1
MOV R2, #2
BL func_c @汇编中将C中的函数当做标号处理
MOV R3, #3
方式二:C语言调用(跳转)汇编语言
方式三:C内联(内嵌)汇编
不能直接在c代码中写汇编需要插入asm();把汇编内容包含起来
asm("MOV R6, #6\n");
ATPCS协议主要内容
- 栈的种类要求:使用满减栈,防止入栈覆盖原来数据
- 寄存器要求:
R15用作程序计数器,不能作其他用途
R14用作链接寄存器,不能作其他用途
R13用作栈指针,不能作其他用途
当函数的参数不多于4个时使用R0-R3传递,当函数的参数多于4个时,多出的部分用栈传递
函数的返回值使用R0传递
其它寄存器主要用于存储局部变量