指令格式

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;
}

ARM的异常源

在这里插入图片描述

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部