汇编指令:机器码的助记符
数据处理指令
特殊寄存器操作指令
内存操作指令
栈操作指令
协处理器操作指令(协助主处理器操作)
汇编语言中中操作数有三种:寄存器操作数、存储器操作数和立即数。
其中立即数相当于高级语言中的常量(常数),它是直接出现在指令中的数,不用存储在寄存器或存储器中的数,如指令ADD AL,06H中的06H即为立即数。
改寄存器里面的值只能是汇编语言。
Linux系统将c程序转换为汇编语言:arm-linux-gcc -S add.c -o add.s
vim add.s
数据传输指令:
mov 传输指令
mov{S}<c> <Rd>, #<const> 传递立即数const到目标寄存器
{S} 表示movs,有s的时候才可以更改NZCV标志位
<c> 指令可以条件执行
<Rd> 目标寄存器
#<const> 操作数,(必须是立即数)#1
mov r0, #1
mvn 取反传输
mvn r0, #12 ==== r0 = ~12
mvn r0, r1 ==== r0 = ~r1
算数逻辑运算指令:
目标寄存器和操作数是同一个寄存器时,可以省略
add
ADD{S}<c> <Rd>, <Rn>, #<const>
add r0, r1, #12 ==== r0=r1+12
add r0, r1, r2 ==== r0=r1+r2
add r0, #12 ==== r0 = r0+12
add r0, r1 ==== r0 = r0+r1
sub 减法
SUB{S}<c> <Rd>, <Rn>, #<const>
sub r0, r1, #12 ==== r0 = r1-12
sub r0, r1, r2 ==== r0 = r1-r2
sub r0, #12 ==== r0 = r0-12
sub r0, r1 ==== r0 = r0-r1
mul 乘法
mul{S}<c> <Rd>, <Rn>, <Rm>
mul r0, r1, r2 ==== r0 = r1*r2
rsb 反向减
RSB{S}<c> <Rd>, <Rn>, #<const>
rsb r0, r1, #12 ==== r0 = 12-r1
rsb r0, r1, r2 ==== r0 = r2-r1
rsb r0, #12 ==== r0 = 12-r0
rsb r0, r1 ==== r0 = r1-r0
adc 带进位的加法
ADC{S}<c> <Rd>, <Rn>, #<const>
adc r0, r1, #12 ==== r0 = r1+12+C(NZCV标志) 完成64位加法的高位运算
sbc 带借位的减法
SBC{S}<c> <Rd>, <Rn>, #<const>
sbc r0, r1, #12 ==== r0 = r1-12-!C(NZCV标志) 完成64位减法的高位运算
int i;
i = i|1<<9; 置位
i = i&~(1<<9) 清除
位操作指令:
and 按位与
AND{S}<c> <Rd>, <Rn>, #<const>
and r0, r1, #12 ==== r0=r1
orr 按位或
ORR{S}<c> <Rd>, <Rn>, #<const>
orr r0, r1, #12 ==== r0=r1|12
eor 按位异或
EOR{S}<c> <Rd>, <Rn>, #<const>
eor r0, r1, #12 ==== r0=r1^12
bic 位清除, 清除第一操作数,第二操作数为1的那些位(取反再与)
BIC{S}<c> <Rd>, <Rn>, #<const>
bic r0, r1, #1 ==== r0 = r1 & ~(1)
bic r0, #(1<<15)
移位操作指令:
lsl 逻辑左移 ,高位丢失,低位补0
LSL{S}<c> <Rd>, <Rn>, <Rm>
lsl r0, r1, r2 ==== r0 = r1<<r2
lsl r0, r1, #1 ==== r0 = r1<<1
lsr 逻辑右移 ,低位丢失,高位补0
LSR{S}<c> <Rd>, <Rn>, <Rm>
lsr r0, r1, r2 ==== r0 = r1>>r2
lsr r0, r1, #1 ==== r0 = r1>>1
ror 循环右移
asr 算数右移, 符号位不变,移位操作按照逻辑右移,空位补符号
ASR{S}<c> <Rd>, <Rn>, <Rm>
条件判断执行:
eq =
ne !=
lt <
le <=
gt >
ge >=
moveq r0, #12 条件执行,当Z==1 时,指令执行
MVN{S}{<c>}{<q>} <Rd>, <Rm>, <type> <Rs>
<type>表示移位
表示指令支持移位操作
先将<Rm> 移位操作后执行mvn
ADC{S}<c> <Rd>, <Rn>, <Rm>{, <shift>} 表示指令可以增加移位
cmp 比较, 适合所有条件
cmp r1, #3 ==== 根据r1-3的结果修改NZCV
cmp r1, r2 ==== 根据r1-r2的结果修改NZCV
cmn cmp的扩展,快速判断两个数是否相反数
cmn r1, #3 ==== 根据r1+3的结果修改NZCV
cmn r1, r2 ==== 根据r1+r2的结果修改NZCV
teq 判断相等,只能和NE或EQ使用
TEQ<c> <Rn>, #<const>
teq r0, #12==== 根据r1^12的结果修改NZCV
teq r1, r0 ==== 根据r1^r2的结果修改NZCV
tst 位测试, 判断第一操作数,第二操作数为1的那些位是0还是1
可以使用的条件 ne和eq
TST<c> <Rn>, #<const>
tst r0, #1 ==== 根据r1&1的结果修改NZCV,判断r0的最后一位是否为0
tst r0, #0xf ====判断r0最后四位是否都为0
伪指令:
ldr 可以传递任意32位数据到指定寄存器
ldr r0, =0x12345678 ==== r0 = 0x12345678
跳转指令:
相对跳转:基于当前PC前后32M范围寻找标号
标号:地址助记符(只表示地址,不占空间)
b label 跳转到标号处执行代码,不关心返回不返回
bl label 跳转到标号处执行代码,保存返回地址到lr
绝对跳转:对pc进行写操作
mov pc, #12
ldr pc, =0x12345678
ldr pc, =label;将标号表示的地址给目标寄存器
用汇编写出for(i = 0;i < 5,i++)
功能寄存器操作指令:
msr cpsr,r0 cpsr = r0
mrs r0,cpsr r0 = cpsr
内存操作指令:
load/store指令
标号寻址:
ldr r0, fun 将标号地址处的数值加载到r0
ldr r0, =fun 将标号表示的地址传递给r0
单寄存器操作:
ldr 从指定内存地址加载一个内容到寄存器
str 保存一个寄存器4字节数据到指定内存
ldr r0, [r1] ==== r0 = *r1
str r0, [r1] ==== *r1 = r0
偏移量可以是数值或寄存器。
STR<c> <Rt>, [<Rn>{, #+/-<imm12>}] 前索引寻址,不改变r1的数值(r1存的是地址,即地址不偏移)
str r0, [r1, #4] *(r1+4) = r0 r1=r1
STR<c> <Rt>, [<Rn>], #+/-<imm12> 后索引寻址,
str r0, [r1], #4 *r1 = r0 r1=r1+4
STR<c> <Rt>, [<Rn>, #+/-<imm12>]! 基址变址寻址
str r0, [r1, #4]! *(r1+4) = r0 r1=r1+4
ldr r0, [r1, #4] ==== r0 = *(r1+4) r1=r1
ldr r0, [r1], #4 ==== r0 = *r1 r1=r1+4
ldr r0, [r1, #4]! ==== r0 = *(r1+4) r1=r1+4
“!” 表示更新基地址
ldrb/strb 8位,使用寄存器的低8位
ldrh/strh 16位操作,使用寄存器的低16位
ldr/str 32位操作
ldrd/strd 64位操作
用法和32位的ldr/str一样。
保存:将寄存器的数据保存到内存
加载:将内存里的数据传输到寄存器中
多寄存器操作(块拷贝):
遵守的规则:小编号寄存器对应低地址空间
ldm 加载连续内存空间数据到多个寄存器
LDM<c> <Rn>{!}, <registers>
ldm r0, {r1,r2,r3} ==== r1 = *r0 r2 = *(r0+4) r3=*(r0+8) r0=r0//注意r0的地址不变
stm 将多个寄存器的内容保存到内存中
stm r0, {r1,r2,r3} ==== *r0=r1 *(r0+4)=r2 *(r0+8)=r3 r0=r0
ldm r0, {r1-r4, r8}
ldm r0!, {r1,r2,r3} ==== r1 = *r0 r2 = *(r0+4) r3=*(r0+8) r0=r0+8
stm r0!, {r1,r2,r3} ==== *r0=r1 *(r0+4)=r2 *(r0+8)=r3 r0=r0+8
ldmia/stmia 先内存操作,后地址-4
ldmda/stmda 先内存操作,后地址+4
ldmdb/stmdb 先地址-4,后内存操作
ldmib/stmib 先地址+4,后内存操作
(找规律记忆)
栈的使用:
满减栈(默认使用此栈):栈的生长方向减小,sp指向的空间不能直接使用。
满增栈:栈的生长方向增大,sp指向的空间不能直接使用。
空减栈:栈的生长方向减小,sp指向的空间可以直接使用。
空增栈:栈的生长方向增大,sp指向的空间可以直接使用。
入栈指令:stmdb sp!,{r0-r12,lr} 先减再存
出站指令:ldmia sp!,{r0-r12,pc} 先出再加
栈的操作指令:
ldmfd/stmfd 满减栈 stmfd sp!,{r0-r12,lr} ldmfd sp!,{r0-r12,pc}
ldmfa/stmfa 满增栈
ldmea/stmea 满增栈
ldmed/stmed 满增栈
stmfd sp!,{r0-r12,lr} ldmfd sp!,{r0-r12,pc}^ ^表示模式恢复,程序异常跳转时候最好加^,不加^只是数据恢复,但是模式不会恢复。
push {r0-r12,lr}
pop {r0-r12,pc}
协处理器操作指令:
Arm有16个协处理器 ,cp15协处理器可以设置,其他的不可以设置
CP15协处理器有16个32位的寄存器 c0-c15
mrc指令将协处理器寄存器中的数据传送到 ARM 处理器的寄存器中.若协处理器不能完成该操作, 产生未定义的异常中断。
mrc p15, 0, r0, c0, c0, 0 //将cp15协处理器的c0寄存器数值以c0,0的格式读取到r0
mcr指令将ARM处理器的寄存器中的数据传送到协处理器的寄存器中.若协处理器不能成功地执行该操作,将产生未定义指令异常中断.。
ARM的几种寻址方式:
寄存器寻址
寄存器间接寻址
立即数寻址
寄存器偏移寻址
多寄存器寻址
栈寻址
相对寻址
基址寻址
相对寻址
BSS(Block Started by Symbol)通常是指用来存放程序中未初始化的全局变量和静态变量的一块内存区域。特点是:可读写的,在程序执行之前BSS段会自动清0。所以,未初始的全局变量在程序执行之前已经成0了。
static分为三种:修饰局部变量,程序执行之后才会回收变量空间
修饰全局变量,该全局变量只在本.c使用
修饰函数,只在本.c中使用
复制代码
转载于:https://juejin.im/post/5b570e265188251af86bf119