ARM指令学习笔记

一条汇编指令对应一条机器码

汇编中的符号:

指令:能够编译生成一条32位的机器码,且能被CPU识别和执行

伪指令: 本身不是指令,编译器可以将其替换成若干条等效指令

伪操作: 不会生成代码,只是在编译之前告诉编译器怎么编译(对应C语言中的#代码)

ARM指令

       1.数据处理指令:       数学运算、逻辑运算

       2.跳转指令:              实现程序的跳转,本质就是修改了PC寄存器

       3.Load/Srore指令:    访问(读写)内存

       4.状态寄存器传送指令:访问(读写)CPSR寄存器

       5.软中断指令:           触发软中断异常

       6.协处理器指令:       操控协处理器的指令

数据指令

MOV  @数据搬移指令  通过MOV指令修改寄存器里的值

MOV R1,#1   

MOV R2,#3

MOV R2,R1

MOV R1  #0x12  这里#后面的数字是立即数,不能是0x12345678,因为它把整个32位全都占用了,无法编译到机器码。

立即数:本质是包含在指令当中的数,属于指令的一部分。取指的时候可以直接读取到CPU,不能单独取内存存取,速度块。

MVN R0,#0xFF  MVN按位取反搬移

PC:程序计数器,各种模式下的 R15

  1. 每次自增4

LR:连接寄存器

  1. 用来保存子程序返回地址;
  2. 各种异常模式下可以根据LR的值返回到异常发生前的相应位置继续执行

SP:堆栈指针(R13)

CPSR:程序状态寄存器

SPSR:程序备份寄存器

 数据运算指令的格式

《操作码》 《目标寄存器》 《第一操作寄存器》 《第二操作数》

操作码:用于表示执行那种操作

目标寄存器:用于存储运算的结果

第一操作寄存器:存储第一个参与运算的数据

第二操作数:第二个参与运算的数据(可以是寄存器,可以是立即数)

ADD R1,R2,R3

ADD R1,R2,#2

SUB R1,R2,R3   @减法指令

RSB R1,R2,R3   @逆向减法指令

MUL R1,R2  @乘法指令只能是两个寄存器相乘、

AND R1,R2,R3   @按位与

ORR R1,R2,R3   @按位或

EOR R1,R2,R3   @按位异或

LSL R1,R2,R3    @左移指令

数据运算指令对CPSR条件位(N、Z、C、V)的影响

默认情况下数据运算不会对条件产生影响,当在指令后加后缀”s”后可以影响

eg: ADDS R2,R1,#1

32位处理器进行两个64位数据相加

32位处理器一次操作的最大数据是32位,对于64位数据需要进行两部

第一个数的低32位放在R1

第一个数的高32位放在R2

第二个数的低32位放在R3

第二个数的高32位放在R4

ADC 带进位的加法

跳转指令

实现程序的跳转,本质就是修改PC寄存器

方式一:直接去修改PC寄存器的值

方式二:通过跳转指令B,不带返回的跳转指令

LR连接寄存器:用于保存跳转时的下一条指令地址

方式三:带返回的跳转指令(BL),需要自己额外加返回的代码

ARM指令的条件码

关系运算符

CMP R1,R2 @比较指令

本质就是一天减法指令(SUBS),只是没有将运算的结果存入寄存器

运算结果存储在CPSR中NZCV中

R1和R2不相等,跳转条件EQ不成立,故无法跳转

ARM指令集中大多数指令都可以带条件码后缀

对应C语言就是条件判断,汇编就是CMP,然后通过条件码判断跳转

内存访问指令(存储指令)

Load/Store 访问(读写)内存、

LD :读内存数据

ST:写入到内存

STR  R1,[R2] @将R1寄存器中的数据存储到R2指向的地址,寄存器地址用[ ]表示

LDR R3, [R2]  @将内存中R2指向的数据读取到R3寄存器

STR与LDR同样支持条件码(指令后缀)

ARM指令的寻址方式(9种)

寻址方式就是CPU寻找一个操作数的方式

立即寻址:参与运算的数来自立即数

ADD R1,R2,#1

寄存器寻址:参与运算的数来自寄存器

ADD R1, R2,R3

寄存器移位寻址:

MOV R1,R2,LSL #1  @R2先移位

寄存器间接寻址:

STR R1 [R2]

基址加变址寻址

MOV R1 ,#0xFFFFFFFF

MOV R2 ,#0x4000000  @地址

MOV R3 ,#4                    @偏移地址

STR R1,[R2,R3]   @将R1寄存器中的数据写入到R2+R3指向的内存空间

STR R1, [R2,R3,LSL #1] @将R1寄存器中的数据写入到R2+(R3<<1)指向的内存空间

基址加变址寻址的索引方式

1)前索引

MOV R1, #0xFFFFFFFF

MOV R2, #0x40000000

STR R1, [R2,#8]  @将R1寄存器中的数据写入到R2+8指向的内存空间

2)后索引

MOV R1, #0xFFFFFFFF

MOV R2, #0x40000000

STR R1, [R2],#8  @将R1寄存器中的数据写入到R2指向的内存空间,然后R2自增8

3)自动索引

MOV R1, #0xFFFFFFFF

MOV R2, #0x40000000

STR R1, [R2,#8]! @将R1寄存器中的数据写入到R2+8指向的内存空间,然后R2自增8

多寄存器访问指令

STM R11, {R1-R4}   @将R1-R4寄存器中的数据写入到以R11为起始地址的内存空间中

LDM R11,{R6-R9}   @将以R11为起始地址的内存空间中的数据读取到R6-R9寄存器中

STM R11,{R1,R2,R4}  @ 当寄存器编号不连续时,使用逗号分隔

不管寄存器列表中的顺序如何,存取时永远是低地址对应小编号的寄存器

STMIA R11!, {R1-R4}   @ 先存储数据,后增长地址

STMIB R11!,{R1-R4}    @ 先增长地址,后存储数据

STMDA R11!,{R1-R4}   @ 先存储数据,后递减地址

STMDB R11!,{R1-R4}   @ 先递减地址,后存储数据

栈的应用

栈地址的生长方式(从高地址到低地址):即先分配的变量存高地址、后分配的存低地址

存放:如局部变量、函数的参数、返回值、以及程序跳转时需要保护的寄存器等

栈分类

增栈:压栈时栈指针越来越大,出栈时栈指针越来越小

减栈:压栈时栈指针越来越小,出栈时栈指针越来越大

满栈:栈指针指向最后一次压入到栈中的数据,压栈时需要先移动栈指针到相邻位置然后再压栈

空栈:栈指针指向最后一次压入到栈中的数据的相邻位置,压栈时可直接压栈,之后需要将栈指针移动到相邻位

栈分为空增(EA)、空减(ED)、满增(FA)、满减(FD)四种

LDMFD:出栈  @在汇编的时候还是会转为LDMDB

STMFD:压栈  @在汇编的时候还是会转为STMDB

专用指令

包括状态寄存器传送指令、软中断指令、协处理指令、伪指令

1、状态寄存器传送指令

MRS R1, CPSR     @读CPSR,R1 = CPSR

MSR CPSR, #0x10  @写CPSR,CPSR = 0x10  改成了USER模式

在USER模式下不能随意修改CPSR,因为USER模式属于非特权模式,所以在USER模式下,执行MSR CPSR, #0xD3,是没有权限执行的

2、软中断指令

1)异常向量表

B MAIN  @跳转到MAIN程序

B .       @跳转回自身

B SWI_HANDLER  @跳转到软中断执行

B .

B .

B .

B .

B .

2)MAIN:   @应用程序

MOV SP, #0x40000020   @初始化SVC模式下的栈指针

MSR CPSR, #0x10        @切换成USER模式,开启FIQ、IRQ

MOV R1, #1

MOV R2, #2

SWI #1                 @触发软中断异常

ADD R3, R2, R1

B STOP

3)SWI_HANDLER:   @异常处理程序

STMFD SP!,{R1,R2,LR}    @ 压栈保护现场 ,因为下面用到R1,R2避免main程序值被改变

MOV R1, #10

MOV R2, #20

SUB R3, R2, R1

LDMFD SP!,{R1,R2,PC}^   @ 出栈恢复现场

将压入到栈中的LR(返回地址)出栈给PC,实现程序的返回

^ 表示出栈的同时将SPSR的值传递给CPSR,实现CPU状态的恢复

3、协处理指令

ARM处理器只支持一些通用的指令,遇到别的指令会交给协处理器指令(比如图像指令)

比如ARM开发成SOC需要用到音频、视频等,需要相应的协处理器

1)协处理器数据运算指令

       CDP

2)协处理器存储器访问指令

       STC @将协处理器中的数据写入到存储器

       LDC @将存储器中的数据读取到协处理器

3)协处理器寄存器传送指令

       MRC  @将协处理器中寄存器中的数据传送到ARM处理器中的寄存器

       MCR  @将ARM处理器中寄存器中的数据传送到协处理器中的寄存器

4、伪指令

       NOP  @空指令  相当于LDR R0,R0

       LDR R1, =0x12345678   @LDR伪指令可以将任意一个32位的数据放到一个寄存器

       LDR R1, =STOP  @将STOP表示的地址写入R1寄存器

LDR R1, STOP   @将STOP表示的地址写入R1寄存器

伪操作(GNU):不会生成代码,只是在编译之前告诉编译器怎么编译

1)GNU的伪操作一般都以‘.’开头

2).global symbol  @将symbol声明成全局符号

3).local symbol   @将symbol声明成局部符号

4).equ DATA, 0xFF  @ 告诉编译器 DATA就是 0xFF

5).macro FUNC    @封装

       MOV R1, #1

       MOV R2, #2

       .endm

       FUNC     @调用刚才的封装

6).if 0       @条件编译

       MOV R1, #1

       MOV R2, #2

       .endif

7).rept 3     @重复操作,重复3边

       MOV R1, #1

       MOV R2, #2

       .endr

8).weak symbol  @弱化一个符号,即告诉编译器即便没有这个符号也不要报错

.weak func

       B func

其他具体伪操作参考相应文档

C和汇编的混合编程

方式一:汇编语言调用(跳转)C语言

MOV R1, #1

       MOV R2, #2

       BL  func_c  @汇编中将C中的函数当做标号处理

       MOV R3, #3

方式二:C语言调用(跳转)汇编语言

方式三:C内联(内嵌)汇编

不能直接在c代码中写汇编需要插入asm();把汇编内容包含起来

asm("MOV R6, #6\n");

ATPCS协议主要内容

  1. 栈的种类要求:使用满减栈,防止入栈覆盖原来数据
  2. 寄存器要求:

R15用作程序计数器,不能作其他用途   

R14用作链接寄存器,不能作其他用途

R13用作栈指针,不能作其他用途

当函数的参数不多于4个时使用R0-R3传递,当函数的参数多于4个时,多出的部分用栈传递

函数的返回值使用R0传递

   其它寄存器主要用于存储局部变量

  • 15
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值