几条简单的ARM汇编指令
什么是程序
我认为程序就是
- 一段被烧写在flash里面的机器码(指令)(bin文件)
- 运行过程中的数据(属于程序里的一部分)
cpu运行期间会在flash取指令然后执行各种操作。
程序运行时CPU如何读写内存
cpu内部有寄存器,R0~R15寄存器
读数据是将数据从内存RAM读取到寄存器,写数据是将数据从寄存器写入内存RAM。
其中读写操作涉及几条汇编指令:LDR、STR、SUB、ADD、CMP、B、BL、POP、PUSH、LDIMA。
下面我们就来介绍一下这些指令。
ARM汇编简介
了解汇编指令之前我们了解一下什么是汇编语言:
1985年,ARM公司推出了第一款ARM(Acorn RISC Machine)处理器,采用精简指令集(RISC)设计理念。他们发布了两类指令集:ARM指令集和Thumb指令集。ARM指令集是32位指令,Thumb指令集是16位指令。所以基于ARM架构的CPU可以同时运行ARM指令集和Thumb指令集。
那怎么区分当前指令是ARM指令集还是Thumb指令集呢?在程序状态寄存器(CPSR)中,有一个标志位T。当T位为0时,当前指令是ARM指令集;当T位为1时,当前指令是Thumb指令集。
但是指令集只是一串二进制代码,我们怎么区分?所以ARM公司推出了Unified Assembly Language(UAL)即统一汇编语言。所以我们只用记住一些简单的指令,在程序前声明THUMB/ARM指令集之后就可以编写代码了。
LDR读内存
格式为:LDR 目的寄存器,<内存地址>
LDR R0,[R1] ;将内存地址为R1的字数据读入寄存器R0。 |
立即数
MOV R0, #VAL // 将立即数VAL加载到寄存器R0中 |
什么是立即数,val可以是任何数吗?
- 立即数是指在指令中直接给出的数值。ARM汇编中用#前缀表示立即数。
- 立即数并不是任意 32 位整数,由于 ARM 指令的固定长度(32 位),其中一部分位必须用于表示操作码、目标寄存器等。例如“MOV R0, #VAL”这条指令本身就是16位或者32位指令,哪来的空间保存任意数值的VAL?我们暂且认为立即数满足某种规定。
LDR伪指令
但是我就是想将任意数加载到寄存器中,怎么办?
这就引出了LDR伪指令:LDR R0, =VAL。
什么是伪指令?伪指令指的就是不存在的指令,只是编译器在编译时将其转换为实际的指令。例如:
LDR R0, =0X12 |
写内存STR
格式为:STR 源寄存器,<内存地址>
STR R0,[R1],#8 ;将R0中的字数据写入以R1为地址的存储器中,并将新地址R1+8写入R1。 |
加减SUB、ADD
ADD{S} 目的寄存器,操作数1,操作数2:把两个操作数相加,并将结果存放到目的寄存器中。
操作数1应是一个寄存器,操作数2可以是一个寄存器、被移位的寄存器,或者一个立即数。
ADD R0,R1,R2 ; R0 = R1 + R2 |
比较CMP
CMP{条件} 操作数1,操作数2:将一个寄存器的内容和另一个寄存器的内容或立即数进行比较,比较结果放在CPSR中条件标志位的值
CMP R1,R0 ;将寄存器R1的值与寄存器R0的值相减,并根据结果设置CPSR的标志位 |
跳转指令
| 指令 | 功能描述 | 应用场景 | 对寄存器的影响 | 示例 |
|---|---|---|---|---|
| B | 实现无条件跳转,跳转值为相对当前PC的偏移量(24位有符号数扩展为32位) | 短距离跳转、循环或条件跳转 | 仅改变程序计数器(PC) | B Label;CMP R1, #0;BEQ Label; |
| BL | 跳转前将下一条指令地址存入链接寄存器LR(R14),便于子程序返回 | 子程序调用 | 改变PC,将返回地址存入LR | BL func |
| BX | 跳转到指定地址,根据目标地址寄存器最低位/两位切换处理器状态(1为Thumb,0为ARM) | ARM与Thumb指令集间切换跳转 | 改变PC,切换处理器状态 | BX R0 |
| BLX | 结合BL和BX功能,跳转前存返回地址到LR,并切换处理器状态 | 调用Thumb指令集子程序(调用者为ARM指令集) | 改变PC,将返回地址存入LR,切换处理器状态 | BLX R0 |
不了解栈的建议先看这篇博客:内存堆栈管理
POP出栈
内存: |
从上面例子可以看出两点
- POP指令会将栈顶的数据弹出,并且SP会指向栈顶的下一个地址
- {R0-R3} 这些高标号的寄存器对应高地址
- 出栈的时候SP指针从低地址向高地址移动
PUSH压栈
一开始SP指向 0x2000 0284 |
从上面例子可以看出两点
- PUSH指令会将寄存器中的数据压栈,并且SP会指向栈顶的下一个地址
- {R0-R3} 这些高标号的寄存器对应高地址
- 入栈的时候SP指针从高地址向低地址移动
