目录
前言
1.导学-汇编的本质
汇编
> 每条汇编都会唯一对应一条机器码,且CPU能直接识别和执行
即汇编中所有的指令都是CPU能够识别和执行的
> 汇编中寄存器的使用、栈的分配与使用、程序的调用、参数的传递等
都需要自己维护
C语言
> 每条C语句都要被编译器编译成若干条汇编指令才能被CPU识别和执行
即C语句中的指令CPU不一定能直接识别,需要编译器进行“翻译”
> C中寄存器的使用、栈的分配与使用、程序的调用、参数的传递等
都是编译器来分配和维护
1.1 指令 伪指令 伪操作
.global_ start @声明_ start为全局符号
start : @汇编程序的入口
@ 1.指令:能够编译生成一条32bit机器码, 并且能被CPU识别和执行
@ 1.数据处理指令:进行数学运算、逻辑运算
@ 2.跳转指令:实现程序的跳转,本质就是修改了PC寄存器
@ 3.Load/Srore指令: 访问(读写)内存
@ 4.状态寄存器传送指令:用于访问(读写) CPSR寄存器
@ 5.软中断指令:触发软中断
@ 6.协处理器指令:操作协处理器的指令
@2.伪指令:本身不是指令,编译器可以将其替换成若千条指令
@3.伪操作:不会生成指令,只是在编译阶段告诉编译器怎么编译
MOV R1, #1 @汇编程序
stop: @死循环,防止程序跑飞
B stop
. end @汇编程序的结束
2.数据处理指令
2.1MOV指令
.text
.global _start @声明_ start为全局符号
_start: @汇编程序的入口
MOV R1,#1 @MOV数据搬移指令
MOV R1,#1
MOV R1,#2
MOV R2,#2
MOV R3,#3
@ MOV R3,R2 @ R2的值给R3
@ MOV PC,#0 @PC指向首地址,在这里类似于循环,如果不是4的整数倍,则自动调整为小于该数的最大的4的倍数如 7->4
MVN R1, #0xFF @ R1 =~0XFF MVN按位取反操作
stop: @死循环,防止程序跑飞
B stop
.end @汇编结束
2.2机器码和汇编指令一一对应
MOV R1,#1 @MOV数据搬移指令
MOV R1,#1
MOV R1,#2
MOV R2,#2
2.3立即数
立即数本质是包含在指令里面的数据,是指令的一部分。
优点:取值的时候直接读取到CPU,无需到内存再次读取。速度快,节省空间。
缺点:不能任意用32位的数
MOV R3,#0x12345678 @错误,放不下去
MOV R3,#0x12
2.3.1伪指令
下面的指令 mov指令转成了MVN,所以也能算
MOV R1,#0xFFFFFFFF
2.4加法/减法等常用指令
数据运算格式:
《操作码》《目标寄存器》《第一操作寄存器》《第二操作数》
@操作码:表示执行哪一种操作
@目标寄存器:用于存储运算的结果
@第一操作寄存器:存储第一个参与运算的数据
@第二操作数:第二个参与运算的数据(也可是寄存器上的立即数)
注意:减法不同于加法指令,减法存在减与被减的关系,如果是数 - 寄存器,则不满足运算格式。
所以,这里补充了逆向减法指令。
MOV R2,#1
MOV R3,#2
ADD R1 ,R2, R3 @R1 = R2 + R3
ADD R1 ,R2, #5 @R1 = R2 + 5
@加法
ADD R1 ,R2 ,#5
ADD R1 ,#5 ,R2@错误格式
@减法
SUB R1 ,R2 ,#5 @R1 = R2 - 5
@逆向减法
RSB R1 ,R2 ,#8 @R1 = 8 - R2
@乘法
MUL R1,R2,R3 @R1 = R2 * R3
@按位与
AND R1,R2,R3
@按位或
ORR R1,R2,R3
@按位异或
EOR R1,R2,R3
@左移
LSL R1,R2,R3 @R1 = R2 << R3
@右移
LSR R1,R2,R3
@位清零
MOV R2,#0XFF
BIC R1,R2,#0X0F @第二操作数的哪一位为1就将第一操作寄存器哪一位清零,结果放入目标寄存器
@数据运算指令的格式扩展
MOV R1,R2,LSL #1 @R2左移一位,数据保存到R1
2.4.1数据运算指令对条件位的影响
注意 指令后面+s是为了查看CPSR寄存器
@数据运算指令对条件位的影响
MOV R2,#2
@SUBS R1,R2,#5 @N置1
@SUBS R1,R2,#2 @Z置1 C置1(借位)
@MOV R2,#0X6FFFFFFF
@ADDS R1,R2,#3 @C置1(进位)
MOV R2,#0X7FFFFFFE
ADDS R1,R2,#3 @V置1(符号位进位 置1)
64位数的运算:
加法
@64位数的运算,分成两个32位的数,低位R1,R3高位R2,R4
MOV R1,#0x00000001
MOV R2,#0x00000002
MOV R3,#0x00000005
MOV R4,#0x00000001
ADDS R5,R1,R3
ADC R6,R2,R4 @本质是 R6 = R2 + R4 + 'C' 考虑了进位,低位若进位C=1
减法
@64位数的运算,分成两个32位的数,低位R1,R3高位R2,R4
MOV R1,#0x00000001
MOV R2,#0x00000002
MOV R3,#0x00000005
MOV R4,#0x00000001
SUBS R5,R1,R3
SBS R6,R2,R4 @本质是 R6 = R2 - R4 - '!C' 考虑借位 C取反,原因查看SPSR图
C语言直接编译成汇编,debug时可查看汇编指令。从汇编的角度看C编程/2的操作,如:可以用位移代替,能用整形就用整形
3.跳转与存储器访问指令
3.1跳转指令
MAIN:
MOV R1,#1
MOV R2,#2
MOV R3,#3
BL FUNC @B位不返回跳转指令 BL是返回跳转指令(BL修改了CPSR的LP的值)
MOV R3,#33
FUNC:
MOV R4,#2
MOV R5,#3
MOV PC,LR @LR值给PC 跳转回主函数
3.2ARM指令条件码
类似于条件判断
相关标志位:
V Bit[28]
> 当运算器中进行加法运算且产生符号位进位时该位自动置1,否则为0
> 当运算器中进行减法运算且产生符号位借位时该位自动置0,否则为1
CBit[29]
> 当运算器中进行加法运算且产生进位时该位自动置1,否则为0
> 当运算器中进行减法运算且产生借位时该位自动置0,否则为1
Z Bit[30]
当运算器中产生了0的结果该位自动置1,否则为0
NBit[31]
当运算器中产生了负数的结果该位自动置1,否则为0
————————————————
3.2.1 CMP指令
cmp指令相当于R1 -R2,就是做减法然后判断,有以下6个情况:
== z = 1;
!= z = 0;
< C = 0
<= C = 1 或 z = 1;
> C = 1 且 z = 0;
>= C = 1;
.text
.global _start @声明_ start为全局符号
_start: @汇编程序的入口
@ ARM指令条件码
MAIN:
MOV R1,#1
MOV R2,#1
CMP R1,R2
BEQ FUNC @if(R1 == R2){B FUNC}
MOV R3,#3
FUNC:
MOV R4,#4
MOV PC,LR
stop: @死循环,防止程序跑飞
B stop
.end @汇编结束
练习:
.text
.global _start @声明_ start为全局符号
_start: @汇编程序的入口
@ ARM指令条件码
MOV R1,#9
MOV R2,#15
START:
CMP R1,R2
@下面三条指令就是条件判断
BEQ STOP
SUBLT R2,R2,R1
SUBGT R1,R1,R2
B START
STOP: @死循环,防止程序跑飞
B STOP
.end @汇编结束
3.3内存访问指令
3.3.1load/store指令
.text
.global _start @声明_ start为全局符号
_start: @汇编程序的入口
@ ARM指令条件码
MOV R1,#0xFF000000
MOV R2,#0X40000000
@ 写内存
@ 将R1寄存器中的数据存储到R2指向的空间
STR R1,[R2] @保存4个字节到内存word
STRB R1,[R2] @保存一个字节到内存Byte
STRH R1,[R2] @保存2个字节到内存HarfWord
@读内存
@将内存中R2指向的内存空间中的数据读取到R3寄存器
LDR R3,[R2] @后缀同上
@LDR R3, =0X10000000 @这个也可以
STOP: @死循环,防止程序跑飞
@B STOP
.end @汇编结束
3.4ARM寻址方式
.text
.global _start @声明_ start为全局符号
_start: @汇编程序的入口
@ **********寻址方式**********
@1.立即寻址
MOV R1,#1
ADD R2,R1,#2
@2.寄存器寻址
ADD R2,R1,R3
@3.寄存器移位寻址
MOV R1,R2,LSL #1
@4.寄存器间接寻址
STR R1,[R2]
@等......
@基址 + 变址寻址
MOV R1, #0XFFFFFFFF
MOV R2, #0X40000000
MOV R3, #4
STR R1,[R2,R3] @将R1的值写入R2+R3所指向的内存空间
STR R1,[R2,R3,LSL #1] @将R1的值写入R2+R3<<1 所指向的内存空间
@基址 + 变址寻址的索引方式
@前索引
@ MOV R1, #0XFFFFFFFF
@ MOV R2, #0X40000000
@ STR R1,[R2,#8] @将R1的值写入R2+8所指向的内存空间
@后索引
@ MOV R1, #0XFFFFFFFF
@ MOV R2, #0X40000000
@ STR R1,[R2],#8 @将R1的值写入R2所指向的内存空间 然后R2+8
@自动索引
MOV R1, #0XFFFFFFFF
MOV R2, #0X40000000
STR R1,[R2,#8]! @将R1的值写入R2+8所指向的内存空间 然后R2+8
STOP: @死循环,防止程序跑飞
@B STOP
.end @汇编结束
练习:1-100求和
.text
.global _start @声明_ start为全局符号
_start: @汇编程序的入口
@ **********100以内正整数求和**********
MOV R2,#0
MOV R3,#1
MOV R4,#101
START:
CMP R3,R4
BEQ STOP
ADDLT R2,R2,R3
ADD R3,R3,#1
B START
STOP: @死循环,防止程序跑飞
.end @汇编结束
4.栈的种类和应用
4.1多寄存器内存访问指令
.text
.global _start @声明_ start为全局符号
_start: @汇编程序的入口
@ **********多寄存器内存访问**********
MOV R1,#1
MOV R2,#2
MOV R3,#3
MOV R4,#4
MOV R11,#0X40000020
@将R1-R4寄存器的数据保存到内存以R11为起始地址的内存中
@STM R11,{R1-R4}
@将内存中以R11为起始位置的数据读取到R1-R4
LDM R11,{R1-R4}
@非连续的用,
@顺序打乱 内存依然是从小寄存器到大寄存器保存
STM R11,{R4,R2,R3,R1}
@自动索引同样适合多寄存器R11最后是0x40000030,四个寄存器一共16字节
STM R11!,{R4,R2,R3,R1}
STOP: @死循环,防止程序跑飞
B STOP
.end @汇编结束
4.2多寄存器内存访问指令
MOV R1,#1
MOV R2,#2
MOV R3,#3
MOV R4,#4
MOV R11,#0X40000020
@STMIA R11!,{R1-R4} @STM默认是STMIA increase after
@STMIB R11!,{R1-R4}
@STMDA R11!,{R1-R4}
STMDB R11!,{R1-R4}
4.3栈的种类和应用
4.3.1栈的概念
栈的本质就是一段内存,程序运行时用于保存一些临时数据
如局部变量、函数的参数、返回值、以及程序跳转时需要保护的寄存器等
4.3.2栈的分类
栈的分类
增栈:压栈时栈指针越来越大,出栈时栈指针越来越小
减栈:压栈时栈指针越来越大,出栈时栈指针越来越小
满栈:栈指针指向最后一次压入到栈中的数据,压栈时
需要先移动栈指针到相邻位置然后再压栈
空栈:栈指针指向最后一次压入到栈中的数据的相邻位
置,压栈时可直接压栈,之后需要将栈指针移动
到相邻位置栈分为空增(EA)、空减(ED)、满增(FA)、满减(FD)四种
ARM处理器一般使用满减栈
代码案例:用FD后缀代替前面的STMID,LDMIA效果一样
.text
.global _start
_start:
MOV R1,#1
MOV R2,#2
MOV R3,#3
MOV R4,#4
MOV R11,#0X40000020
@STMDB R11!,{R1-R4}
@LDMIA R11!,{R5-R8}
STMFD R11!,{R1-R4}
LDMFD R11!,{R5-R8}
STOP: @死循环,防止程序跑飞
B STOP
.end
4.4栈的应用举例
充分理解C语言里的局部变量!!!
.text
.global _start @声明_ start为全局符号
_start: @汇编程序的入口
@ **********栈的应用**********
@体会C语言中的局部变量
MOV SP ,#0X40000020 @定义栈指针
MAIN:
MOV R1,#1
MOV R2,#3
BL FUNC
ADD R3,R1,R2
B STOP
FUNC: @非叶子函数--这里LR需要压栈,因为会被后面的子函数覆盖
STMFD SP!,{R1,R2,LR} @压栈保护现场
MOV R1,#10
MOV R2,#30
BL FUNC2
SUB R3,R2,R1
@读栈(栈中的数据没有清零,所以局部变量需要定义具体的值,不然会读到之前的数)
LDMFD SP!,{R1,R2,LR} @出栈恢复现场
MOV PC,LR
FUNC2: @叶子函数
STMFD SP!,{R1,R2} @压栈 --叶子函数无需保护链接寄存器LR
MOV R1,#16
MOV R2,#38
ADD R3,R1,R2
LDMFD SP!,{R1,R2} @读栈
MOV PC,LR
STOP: @死循环,防止程序跑飞
B STOP
.end @汇编结束
5.专用指令
5.1状态寄存器传输指令
@ **********状态寄存器传送指令:访问(读写)CPSR寄存器**********
@读CPSR
MRS R1,CPSR
@写CPSR
@MSR CPSR,#0X10 @USER模式
@USER模式无法转成SVC模式,USER模式权限最低
@MSR CPSR,#0XD3
5.2软中断指令
理解程序进入异常和退出异常的过程
.text
.global _start @声明_ start为全局符号
_start: @汇编程序的入口
@ **********软中断指令**********
@异常向量表 空出32字节
B MAIN
B .
B SWI_HANDLER
B .
B .
B .
B .
B .
@应用程序
MAIN:
MOV SP,#0X40000020 @默认是SVC态
MSR CPSR,#0X10
MOV R1,#1
MOV R2,#2
SWI #1 @SWI指令触发软中断,程序进入SVC模式
ADD R3,R1,R2
B STOP
@异常处理程序
SWI_HANDLER:
STMFD SP!,{R1,R2,LR}
MOV R1,#10
MOV R2,#20
ADD R3,R1,R2
LDMFD SP!,{R1,R2,PC}^ @LR复制给PC,^用于SRPC复制给CRPC
STOP: @死循环,防止程序跑飞
B STOP
.end @汇编结束
5.3协处理器指令
@协处理器指令:操控协处理器的指令
@1.协处理器数据运算指令
CDP
@2.协处理器存储器访问指令
STC 协处理器数据存储到存储器
LDC 存储器中的数据存储到协处理器
@3.协处理器寄存器传送指令
MRC 协处理器中寄存器的数据传送到ARM处理器中的寄存器
MCR ARM处理器中的寄存器的数据传送到协处理器中寄存器
5.4伪指令
LDR是很特殊的指令,根据格式不同,可以是指令,也可以是伪指令
@伪指令:本身不是指令,编译器可以将其替换成若干指令
@空指令
NOP
@LDR很特殊,根据格式不同,可以是指令或伪指令
@指令
@LDR R1 ,[R2]
@伪指令
@可以将任意一个32位数放入寄存器
LDR R1, =0X12345678 @ R1 = 0X12345678
@STOP的地址存入R1
LDR R1, =STOP
@STOP地址里的内容(就是机器码)存入R1
LDR R1, STOP
6.伪操作与混合编程
6.1伪操作
@ *********伪操作***********
@伪操作不会生成代码,只是在编译阶段告诉编译器怎么编译
@.GUN伪操作一般都以‘.’ 开头
@symbol声明成全局变量
@.global symbol
@symbol声明成局部变量
@.local symbol
@.equ DATA,0XFF @相当于宏定义
@MOV R1,#DATA
@.macro FUNC
@MOV R1,#1
@MOV R2,#2
@.endm
@FUNC @调用FUNC就是调用两个MOV语句
@if条件语句
@.if 1
@MOV R1,#1
@MOV R2,#2
@.endif
@.rept 3 @重复3遍
@MOV R1,#1
@MOV R2,#2
@.endr
@.weak symbol @即使用到了未定义的symbol,编译器也不报错
@.weak func @即使用到了未定义的FUNC,编译器也不报错
@B func @用未定义的FUNC 执行NOP操作
@word在当前地址申请一个字的空间并将其初始化
@MOV R1,#1
@.word 0XFFFFFFFF
@MOV R2,#2
@byte在当前地址申请一个字的空间并将其初始化
@MOV R1,#1
@.byte 0X1233
@.align 3 @后续指令从2^3的整数倍位置开始存数据
@MOV R2,#2
@space在当前地址申请任意字节的空间并将其初始化
@MOV R1,#1
@.space 12, 0X123356
@.align 5 @后续指令从2^5的整数倍位置开始存数据
@MOV R2,#2
@.arm
@MOV R1,#1
@.thumb
6.2C和汇编的混合编程
c和汇编混合编程的原则:
1.在哪种语言环境下符合哪种语言的语法规则
2.在汇编语言中将C语言中的函数当作标号来处理
3.在C语言中将汇编语言中的标号当作函数来处理
共有三种形式:
1.汇编语言(调用)跳转C语言
2.C语言(调用)跳转汇编语言
3.C语言内联汇编
汇编
.text
.global _start
_start:
@ *********C与汇编混合编程*********
MOV SP ,#40000020
MOV R1,#1
MOV R2,#2
BL func_c @跳到C语言定义的函数
MOV R3,#3
B STOP
.global FUNC_ASM @定义FUNC_ASM为全局标号
FUNC_ASM:
MOV R4,#4
MOV R5,#5
STOP: @死循环,防止程序跑飞
B STOP
.end
C语言
void func_c(){
int a;
a++;
asm
(
//里面写汇编语言
"MOV R6,#6\n"
"MOV R7,#7\n"
);
FUNC_ASM(); //跳转到汇编
a--;
}
6.3ATPCS协议
协议的主要内容:
1.栈的种类,使用满减栈
2.寄存器的使用
1. R15(PC)程序计数器,只能用于存储程序的指针,不能做其他用途
2.R14(LR)链接寄存器,只能用于存储返回的地址,不能做其他用途
3.R13(SP)栈指针,只能用于存储栈指针,不能做其他用途
4.R0-R3 当函数的参数少于4个 使用 R0-R3 传参,多出4个的部分用栈传递
函数的返回值用R0寄存器传递
5.其余寄存器主要用于存储局部变量
16: int f1(int a,int b,int c,int d,int e,int f){
17: return a+b+c+d+e+f+g;
0x00000000 E0801001 ADD R1,R0,R1
0x00000004 E0811002 ADD R1,R1,R2
0x00000008 E0811003 ADD R1,R1,R3
0x0000000C E59D2000 LDR R2,[R13]
0x00000010 E0810002 ADD R0,R1,R2
0x00000014 E59D2004 LDR R2,[R13,#0x0004]
0x00000018 E0800002 ADD R0,R0,R2
0x0000001C E59F3008 LDR R3,[PC,#0x0008]
0x00000020 E5933000 LDR R3,[R3]
18: }
19:
0x00000024 E0800003 ADD R0,R0,R3
0x00000028 E12FFF1E BX R14
0x0000002C 00008154 ANDEQ R8,R0,R4,ASR R1
20: int main(){
0x00000030 E1A0C00D MOV R12,R13
0x00000034 E92DD800 STMDB R13!,{R11-R12,R14-PC}
0x00000038 E24CB004 SUB R11,R12,#0x00000004
0x0000003C E24DD008 SUB R13,R13,#0x00000008
21: g = f1(1,2,3,4,5,6);
22:
0x00000040 E3A03005 MOV R3,#0x00000005
0x00000044 E58D3000 STR R3,[R13]
0x00000048 E3A03006 MOV R3,#0x00000006
0x0000004C E58D3004 STR R3,[R13,#0x0004]
0x00000050 E3A00001 MOV R0,#0x00000001
0x00000054 E3A01002 MOV R1,#0x00000002
0x00000058 E3A02003 MOV R2,#0x00000003
0x0000005C E3A03004 MOV R3,#0x00000004
0x00000060 EBFFFFE6 BL f1(0x00000000)
0x00000064 E59F300C LDR R3,[PC,#0x000C]
0x00000068 E5830000 STR R0,[R3]
23: }