1> 汇编指令: 编译器可以将其编译生成机器码,执行汇编指令可以完成一定的功能,占用内存中代码段空间。
2> 伪指令:伪指令本身不是一条指令,但是编译器可以将其编译生成多条指令,共同完成一条伪指令的功能,伪指令占用内存中代码段的空间。
3> 伪操作:伪操作不占用代码段的内存空间,只是告诉编译器如何对代码进行编译的。
比如:.text .end .global .glabl .data .if .else .endif .byte .short .word
编译器不同,伪操作也是不同的。
4> 注释:编译器的不同,代码的注释方式也是不同的。
当行注释:@
多行注释:/**/ .if 0/1 .else .endif
格式:
{cond}{s} Rd, Rn, oprand_shifter 解释:
:指令码,汇编指令对应的指令名字,比如mov add sub {cond}:条件码,实现汇编指令的有条件执行
{s}:状态位,指令后边加s,指令的执行结果影响CPSR的NZCV位,
指令后边不加是,指令的执行结果不影响CPSR的NZCV位。
Rd:目标寄存器,存放指令的执行结果,只能是普通的寄存器R0-R15
Rn:第一个操作寄存器,只能是普通的寄存器R0-R15
Oprand_shifter:第二个操作数
1> 可以是一个普通的寄存器
2> 可以是一个立即数
3> 可以是一个经过移位操作的寄存器
注意:
1》1条汇编指令占用1行
2》汇编指令不区分大小写
3》
{cond}{s} 连到一起写 4》Rd,Rn,Oprand_shifter 使用英文逗号隔开
5》
{cond}{s}和Rd之间使用空格隔开
1> 数据操作指令
数据搬移指令
算数运算指令
移位操作指令
位运算指令
比较指令
2> 跳转指令
3> load/store内存读写指令
单寄存器操作指令
多寄存器操作指令
栈操作指令
4> 软中断指令
5> 特殊功能寄存器读写指令
mov ---> 将第二个操作数赋值给目标寄存器
mvn ---> 将第二个操作数按位取反赋值给目标寄存器
{cond}{s} Rd,Oprand_shifter
mov r0, #0xFF ==>编译生成32位的机器码 ==>包含mov, r0, 立即数的信息
如何判断一个数是否为立即数?
首先根据需要判断的那个数,找到一个0-255之间的数,然后将这个0-255之间的数循环右移偶数位,如果循环右移偶数位之后的结果和要判断的那个数相等,则那个数就是一个立即数。
格式:
ldr Rd, = number @将number数赋值给Rd寄存器
add ----> 普通的加法指令,不需要考虑进位标志位(CPSR的C位)
adc ----> 带进位的加法指令,需要考虑进位标志位(CPSR的C位)
sub ----> 普通的减法指令,不需要考虑借位标志位(CPSR的C位)
sbc ----> 带借位的减法指令,需要考虑借位标志位(CPSR的C位)
mul ----> 乘法指令
div ----> 除法指令,arm-v8架构中引入了除法指令。
{cond}{s} Rd, Rn, Oprand_shifter
lsl ---> 逻辑左移/无符号数左移
lsr ---> 逻辑右移/无符号数右移
asr---> 算数右移/有符号数右移
ror ---> 循环右移
{cond}{s} Rd, Rn, Oprand_shifter
and : 按位与运算 &
orr :按位或运算 |
eor: 按位异或运算^
bic:按位清除
口诀:
与0清0,与1不变
或1置1,或0不变
异或1取反,异或0不变
{cond}{s} Rd, Rn, Oprand_shifter
cmp ---> 比较两个数的大小
cmp{cond} Rn,oprand_shifter
注:
1> 比较指令没有目标寄存器,指令的执行结果影响的是cpsr的NZCV位,并且不需要加s。
2> 比较指令的本质做减法运算。
3> 比较指令和条件码配合使用。
b --> 不保存返回地址的跳转指令
执行跳转指令时,不保存跳转指令的下一条指令的地址到LR寄存器中
bl --> 保存返回地址的跳转指令
执行跳转指令时,保存跳转指令的下一条指令的地址到LR寄存器中
b{cond}label(标签/汇编函数名)
--> 跳转到标签下的第一条指令执行。
bl {cond} label(标签/汇编函数名)
---> 跳转到标签下的第一条指令执行,并保存返回地址到LR中。
label(标签):
汇编指令
跳转指令的本质就是修改PC寄存器的值。
有去无回就用b跳转指令,比如while(1){ }死循环;
有去有回就用bl跳转指令,比如函数的调用。
mov pc,lr ---> 可以实现跳转,不保存返回地址
ldr pc, =lable
msr ---> 将普通寄存器中的数据写到特殊功能寄存器中
mrs ---> 将特殊功能寄存器中的数据写到普通寄存器中
对于特殊工程寄存器cpsr的读写访问只能使用 msr 和 mrs指令。
msr cpsr, 普通寄存器/立即数 @spsr = 普通寄存器/立即数
mrs Rd, cpsr @Rd = cpsr
ldr --> 将内存中的数据读到普通的寄存器中,读4个字节的大小。
str --> 将普通寄存器中的数据写到内存中,写4个字节的大小
ldrb --> 将内存中的数据读到普通的寄存器中,读1个字节的大小
strb --> 将普通寄存器中的数据写到内存中,写1个字节的大小
ldrh --> 将内存中的数据读到普通寄存器中,读2个字节的大小
strh --> 将普通寄存器中的数据写到内存中,写2个字节的大小
ld: Load
st:Store
r :Register
b : Byte
h : Half
ldr / ldrh / ldrb Rd,[Rm]
Rd是一个普通的寄存器,
[Rm]:Rm寄存器中的数据被当成内存的地址
功能:将[Rm]指向地址空间中的数据读到Rd普通寄存器中。
str / strh /strb Rn,[Rm]
Rn是一个普通的寄存器,
[Rm]:Rm寄存器中的数据将被当成内存的地址。
功能:将Rn寄存器中的数据,写到[Rm]指向的地址空间中。
其他用法:
ldr /ldrh /ldrb Rd, [Rm, #offset]
将[Rm + offset]地址中的内容读到Rd寄存器中,Rm中的值不变
ldr/ ldrh /ldrb Rd, [Rm], #offset
将[Rm]地址中的内容读到Rd寄存器中,同时更新Rm中的地址:Rm = Rm+offset
ldr/ ldrh / ldrb Rd, [Rm, #offset]!
将[Rm + offset]地址中的内容读到Rd寄存器中,同时更新Rm中的地址:Rm=Rm+offset
!:更新地址
str/ strh/ strb Rn,[Rm, #offset]
将Rn寄存器中的数据写到[Rm + offset]地址中,Rm中的值不变
str/ strh/ strb Rn, [Rm], #offset
将Rn寄存器中的数据写到[Rm]地址中,同时更新Rm中的地址:Rm = Rm+offset
str/ strh/ strb Rn, [Rm, #offset]
将Rn寄存器中的数据写到[Rm + offset]地址中,同时更新Rm中的地址:Rm=Rm+offset
offset:偏移地址,偏移地址的大小是Load/Store指令可访问空间大小的整数倍。
ldm
stm
ld:Load
st:Store
m:mutil
ldm Rm, {寄存器列表}
将Rm指向的连续的地址空间中的数据读到寄存器列表的每个寄存器中。
stm Rm, {寄存器lieb}
将寄存器列表中的每个寄存器中的数据写到Rm指向的连续的地址空间中
注意:
1. Rm寄存器中的数据被当成一个地址看待
2. 寄存器列表中的寄存器如果是连续的,则使用"-" 隔开
3. 寄存器列表中的寄存器如果不连续则使用“ , ”隔开
4. 寄存器列表中的寄存器要求从小到大依次书写,如果从大到小书写,要求依次用逗号隔开书写,编译器会报警告。
1> 增栈:压栈之后,栈指针向高地址方向移动。
2> 减栈:压栈之后,栈指针向低地址方向移动。
3> 空栈:当前栈指针指向的空间没有有效的数据,因此可以先压栈,压栈之后栈指向的空间就为有效的数据,因此需要移动栈指针,让栈指针指向一个没有有效数据的空间。
4> 满栈:栈指针指向的空间有有效的数据,需要先移动栈指针,让栈指针指向一个空的位置,再进行压栈的操作,压栈之后,此时栈指针指向的空间又为有效的数据。
满增栈:Full Ascending ---> stmfa/ ldmfs
满减栈:Full Descending ---> stmfd/ ldmfd
空增栈:Empty Ascending---> stmea/ldmea
空减栈:Empty Descending-->stmed/ ldmed
ARM处理器默认采用的是满减栈,ARM指令集本身是支持以上4种栈的操作方式的所有的指令
ldmfd
stmfd
ldmfd sp!, {寄存器列表}
将sp指向的栈空间中的数据读到寄存器列表的每个寄存器中。
stm Rm, {寄存器列表}
将寄存器列表中的每个寄存器中的数据写到sp指向的栈空间中
注意:
0. !:压栈和出栈之后都需要更新栈指针
1. sp寄存器中的数据被当成一个栈空间的地址看待
2. 寄存器列表中的寄存器如果是连续的,则使用"-" 隔开
3. 寄存器列表中的寄存器如果不连续则使用“,”隔开
4. 寄存器列表中的寄存器要求从小到大依次书写,如果从大到小书写,要求依次用逗号隔开书写,编译器会报警告。
C和汇编的混合编程需要遵循ATPCS规范,ATPCS规范是ARM公司指定的。
ARPCS:主要有以下规范:
1> 处理器默认使用满减栈
2> 参数的传递通过R0-R3进行传递,如果参数的个数少于4个通过r0-r3传递,
如果参数的个数超过4个,则超过的部分使用压栈的方式传递。
3> 函数的返回值通过R0进行返回。如果超过4个字节通过r0-r1返回。
C代码中嵌套一段汇编代码,这种方式叫做内联汇编或者叫做内嵌汇编。
asm volatile(
指令列表
"汇编指令\n\t"
.........
:输出列表
:输入列表
:破坏列表
);