指令格式
1、立即数
一个常数,该常数必须对应8位位图,即一个8位的常数通过循环右移偶数位得到该数,该数位合法立即数
在指令中表示方法:#数字,例如:#100
快速判定是否是合法立即数:
首先将这个数转换为32bit的16进制形式,例如218=0xDA=0x000000DA
①除零外,仅有一位数为合法立即数
②除零外,仅有二位数,并且相邻(包括首尾)的为合法立即数
③除零外,仅有三位数,并且相邻(包括中间有0,例如0x10800000,包括首尾相邻),这三位数中,第一位仅能为1、2、3最低位仅能为4、8、c,中间位0x0~0xf
这种组合的为合法立即数
2、寄存器移位
将寄存器值读取后,进行移位运算后,作为操作数2参与运算。支持的移位方式如下:
LSL(Logical Shift Left) 逻辑左移
LSR(Logical Shift Right)逻辑右移
ASR(Arithmetic Shift Right)算术右移
r0,lsr #4 表示r0>>4
r0,lsr r1 表示r0>>r1
#3,lsl #4 错误,只能是寄存器移位,不能是立即数移位
常用ARM核指令
1、数据传送指令
(1)MOV指令
格式:mov 目标寄存器,操作数
功能:将操作数的值赋值给目标寄存器
mov r0,#12
mov r1,r0
mov r1,r0,lsl #2 //r1=r0<<2
(2)MVN指令
格式:mvn 目标寄存器,操作数2
功能:将操作数2取反的值给目标寄存器
mvn r0,#0 //r0=~0 =>r0=0xffff,ffff
(3)LDR指令
格式:LDR 目标寄存器,数据
功能:完成任意的数据传送到目标寄存器
注意:数据前面不能加#,因为此时数据不按立即数来处理
举例:
LDR r0,=0x12345678
2、数据计算指令
(1)ADD指令
格式:add 目标寄存器,操作数1,操作数2
功能:将操作数1加上操作数2的结果放在目标寄存器中
举例:
add r0,r1,#3 //r0=r1+3
add r0,r1,r2//r0=r1+r2
add r0,r1,r2,lsl #2//r0=r1+( r2<<2 )
(2)SUB指令
格式:sub 目标寄存器,操作数1,操作数2
功能:将操作数1减去操作数2的结果放在目标寄存器中
(3)MUL指令
格式:mul 目标寄存器,操作数1,操作数2
功能:将操作数1乘以操作数2的结果放在目标寄存器中
注意:操作数1和操作数2必须都是寄存器,并且操作数1的寄存器编号不能和目标寄存器一样
举例:mul r0 ,r0 ,#3//error r0=r0*3
3、位运算指令
(1)AND指令
格式:and 目标寄存器,操作数1,操作数2
功能:将操作数1按位与操作数2,结果放在目标寄存器中
(2)ORR指令
格式:orr 目标寄存器,操作数1,操作数2
功能:将操作1按位或操作2的结果存放在目标寄存器中
举例:orr r0,r1,r2 //r0=r1|r2
orr r0,r1,#10 //r0=r1|10
orr r0,r1,r2,lsl #2 //r0=r1|(r2<<2)
(3)EOR指令
格式:eor 目标寄存器,操作1,操作2
功能:将操作数1按位异或操作数2的结果存放在目标寄存器中
举例:
eor r0,r1,r2//r0=r1^r2
eor r0,r1,#10//r0=r1^10
eor r0,r1,r2,lsl #2 //r0=r1^(r2<<2)
(4)BIC指令
格式:bic目标寄存器,操作1,操作2
功能:将操作数1按位与操作数2取反的结果存放在目标寄存器中
目标寄存=操作数1& ~操作数2
将0x55中所有的1位清零
.global _start
_start:
mov r0,#0x55 @ 0101 0101
bic r1,r0,#0x55
stop:
b stop
4、比较指令
格式:cmp 寄存器 ,操作数2
功能:将寄存器的值与操作2比较,比较的结果会自动影像CPSR的NZCV
.global _start
_start:
mov r0,#10
mov r1,#20
cmp r0,r1
addgt r0,r0,#1 //如果r0>r1 则执行这行代码
stop:
b stop
5、跳转指令
格式:B/BL 标签
功能:跳到一个指定的标签,BL跳转之前,将跳转前PC的值保存在LR,跳转范围+/- 32M
.global _start
_start:
mov r0,#10
mov r1,#20
b add_label //跳到add_label标签,然后并不会执行下面的语句
mov r3,#10
stop:
b stop
add_label:
add r0,r0,r1
如果跳转之后,还想跳转回来,有两种办法
①使用bl指令,因为bl跳转之前,将跳转前的pc的值保存在lr,跳转范围+/-32M
.global _start
_start:
mov r0,#10
mov r1,#20
bl add_label
mov r3,#10
stop:
b stop
add_label:
add r0,r0,r1
mov pc,lr
②使用ldr直接赋值
.global _start
_start:
mov r0,#10
mov r1,#20
ldr pc,=add_label //直接给pc赋值
back_label:
mov r3,#10
stop:
b stop
add_label:
add r0,r0,r1
ldr pc,=back_label //给需要跳转回来的标签赋值
练习:实现1-100累加和
.global _start
_start:
mov r0,#0
mov r1,#1
loop:
add r0,r0,r1 //r0=r0+r1
add r1,r1,#1 //r1+1
cmp r1,#100 //r1与100比较
ble loop //r1<=100则跳进循环loop
stop:
b stop
6、内存访问指令
LDR 将内存中的值加载到寄存器中(读内存)
STR 将寄存器中的值写入内存中(写内存)
寄存器间接寻址:寄存器的值是一个地址
LDR r0,[r1] //r0=*r1
STR r0,[r1] //*r1=r0
.global _start
_start:
mov r0,#0x40000000
ldr r1,=0x12345678
str r1,[r0]
ldr r3,[r0]
stop:
b stop
在debug->memory map中设置读写内存
0x40000000,0x4000ffff
基址变址寻址
A.前索引
STR r0,[r1,#4]//*(r1+4)=r0 =>地址先+4在赋值
LDR r0,[r1,#4]//r0=*(r1+4) =>地址先+4在写入寄存器中
r1的值不变
B.后索引
STR r0,[r1],#4 //*r1=r0&&r1=r1+4 =>先写入内存,内存地址再+4
LDR r0,[r1],#4 //r0=*r1 &&r1=r1+4 =>先从内存中读取,内存地址在+4
r1的值改变
C.自动索引
STR r0,[r1,#4]! //*(r1+4)=r0 &&r1=r1+4 =>将r0中的值写入到(r1+4)中并且内存地址+4
LDR r0,[r1,#4]! //r0=*(r1+4)&&r1=r1+4 =>将内存地址+4的值赋值给r0,并且内存地址+4
多个数据访问
LDM 将一块内存的数据,加载到多个寄存器中
STM 将多个寄存器的值,存储到一块内存
格式:
LDM{条件} <MODE> 基址寄存器{!},{Reglist}^
STM{条件} <MODE> 基址寄存器{!},{Reglist}^
参数:
MODE:-IA 后增加地址
-IB 先增加地址
-DA 后减少地址
-DB 先减少地址
基址寄存器:用来存放内存的起始地址
!:最后更新基址寄存器的值
Reglist:多个寄存器,从小到大,中间用','隔开 如{r0,r1,r3} 或{r0-r2}
^:它存在,如果reglist没有pc的时候,这个时候如果操作的寄存器是用户模式下的寄存器,在LDM指令中,有pc的时候,在数据传送的时候,会将SPSR的值拷贝到CPSR,用于异常的返回
栈操作指令:
进栈:stmfd sp!,{寄存器列表}
ldmfd sp!,{寄存器列表}
注意:在对栈操作之前,必须先设置sp的值,进栈和出栈的方式一样,ATPCS标准规定满减栈(从高往低)
.global _start
@0x40000000,0x4000ffff
_start:
ldr sp,=0x4000fff0 //设置高位
mov r0,#0x11
mov r1,#0x22
mov r2,#0x33
stmfd sp!,{r0,r1,r2} //从高位先入栈
mov r0,#0
mov r1,#0
mov r2,#0
ldmfd sp!,{r0,r1,r2}
stop:
b stop
7、CPSR/SPSR操作指令
读操作:MRS rn ,CPSR/SPSR
将状态寄存器的值,读到通用寄存器中
写操作:MSR CPSR/SPSR,rn
将通用寄存器的值,写到状态寄存器中
汇编与C混合编程
汇编内嵌C
.global _start
@0x40000000,0x4000ffff
_start:
mov r0,#2
mov r1,#3
ldr sp,=0x4000fff0 //设置栈sp指针的偏移量
bl add
stop:
b stop
int add(int a ,int b){
int c=a+b;
return c;
}
C内嵌汇编
asm(
"指令1\n"
"指令2\n"
...
:输出列表
:输入列表
:修改列表(通用的寄存器)
);
指令:ARM汇编指令
输出列表:将内嵌汇编中的寄存器值输出到c变量中
输入列表:将C变量输入到内联汇编中使用的寄存器
修改列表:内联汇编中修改的寄存器
修饰符 | 说明 |
---|---|
无 | 被修饰的操作符是只读的 |
= | 被修饰的操作符是只写的 |
+ | 被修饰的操作符具有可读写的属性 |
& | 被修饰的操作符只能作为输出 |
.global _start
@0x40000000,0x4000ffff
_start:
mov r0,#2
mov r1,#3
ldr sp,=0x4000fff0
bl add
stop:
b stop
int add(int a ,int b){
int c;
asm(
"add r0 %1,%2\n",
"mov %0,r0\n"
//从输出列表到输入列表开始编号
:"=r"(c)//%0
:"r"(a),"r"(b) //%1 %2
:"r0"
);
return c;
}