ARM汇编基础-Cortex-A7常用汇编指令
一、 处理器内部数据传输指令
常用的内部数据传输指令有三个:MOVE、MRS、MSR
| 指令 | 目的 | 源 | 描述 |
|---|---|---|---|
| MOVE | R0~R14 通用寄存器(R15也可以) | R0~R14 通用寄存器(R15也可以)、立即数 | 将源寄存器数据移动到目的寄存器 |
| MRS | R0~R14 通用寄存器 | CPSR、SPSR | 将特殊寄存器 CPSR 里面的数据复制到 通用寄存器(R0~R14) 中 |
| MSR | CPSR | R0~R14 通用寄存器 | 将 通用寄存器(R0~R14) 中的数据移动到特殊寄存器 CPSR 中 |
1.1、MOV指令指令
MOV 指令用于将数据从一个寄存器拷贝到另外一个寄存器,或者将一个立即数传递到寄存器里面,使用示例如下:
MOV R0,R1 ;将寄存器 R1 中的数据传递给 R0,即 R0=R1MOV R0, #0X12 ;将立即数 0X12 传递给 R0 寄存器,即 R0=0X121.2、MRS指令
MRS 指令用于将特殊寄存器(如 CPSR 和 SPSR )中的数据传递给通用寄存器,要读取特殊寄存器的数据只能使用 MRS 指令
MRS R0, CPSR ;将特殊寄存器 CPSR 里面的数据传递给 R0,即 R0=CPSR1.3、MSR指令
MSR 指令用于将通用寄存器(==不能是立即数==)中的值传递给特殊寄存器(如 CPSR 和 SPSR ),特殊寄存器只能使用 MSR0 。
MSR CPSR, R0 ;将 R0 中的数据复制到 CPSR 中,即 CPSR=R0二、存储器访问指令
ARM不能够直接访问存储器(如RAM),必须通过存储器访问指令 LDR 、STR 进行访问。
2.1、LDR指令
此命令常用于将存储器的值,保存到通用寄存器,命令格式如下:
LDR Rd, [Rn , #offset]-
Rd:通用寄存器
(R0~R12),(R13~R15也行,但危险),用来保存冲存储器得到的数据。 -
[Rn , #offset]:存储器地址,从通用寄存器
(R0~R12),(R13~R15也行,但危险)读取的值 +offset作为存储器地址,表示要读取的目标存储器地址。
当然,LDR也可以用于加载立即数,即实现MOV指令一样的效果,但是这么使用时,其格式如下:
LDR Rd, =立即数 ;使用LDR将一个立即数的值加载到通用寄存器中用例:
LDR R0, =0X0209C004 ;将寄存器地址 0X0209C004 加载到 R0 中,即 R0=0X0209C004LDR R1, [R0] ;读取地址 0X0209C004 中的数据到 R1 寄存器中命令按字进行数据传输,如果需要按照半字、字节操作,可以在其末尾加上H、B,如:
LDRH Rd, [Rn , #offset] ;按半字传输LDRB Rd, [Rn , #offset] ;按字节传输2.2、STR指令
此命令常用于将数据写入到存储器中,命令格式如下:
STR Rd, [Rn , #offset]使用方法和 LDR 类似,但是不能是一个立即数,只是换了一个传输方向。
-
Rd:用于保存要写入的数据的通用寄存器
-
[Rn , #offset]:要写入的存储器地址:
Rn + offset。
用例:
LDR R0, =0X0209C004 ;将寄存器地址 0X0209C004 加载到 R0 中,即 R0=0X0209C004LDR R1, =0X20000002 ;R1 保存要写入到寄存器的值,即 R1=0X20000002STR R1, [R0] ;将 R1 中的值写入到 R0 中所保存的地址中命令按字进行数据传输,如果需要按照半字、字节操作,可以在其末尾加上H、B,如:
STRH Rd, [Rn , #offset] ;按半字传输STRB Rd, [Rn , #offset] ;按字节传输三、压栈和出栈指令
3.1、压栈和出栈
压栈和出栈即函数调用时的出入栈(stack)操作。调用函数后,用户保护和恢复现场。
压栈的指令为 PUSH,出栈的指令为 POP, PUSH 和 POP 是一种多存储和多加载指令,即可以一次操作多个寄存器数据,他们利用当前的栈指针 SP 来生成地址。
这两条的指令格式如下:
| 指令 | 描述 |
|---|---|
PUSH <reg list> | 将寄存器列表存入栈中 |
POP <reg list> | 从栈中恢复寄存器列表 |
3.2、PUSH指令
指令将通用寄存器 (R0~R15) 中的内容压入栈内存,使用方法如下:
PUSH {R0~R3, R12} ;将 R0~R3 和 R12 压栈假设当前的栈内存的栈顶地址为0x80000000,当执行压栈操作后,其栈结构如图:

栈顶地址变为了 0x80000000 - ( 4 + 1 ) * 4 = 0x7FFFFFEC ,即此时SP指向了 0x7FFFFFEC 。
需要注意的是压栈顺序,是按照寄存器标号,由大到小依次入栈的,即在此用例中,R12先入栈,然后才依次是R3、R2、R1、R0。
因此最终栈顶SP只想的地址是R0的内容。
再次基础上,在对LR寄存器压栈:
PUSH {LR} ;将 LR 进行压栈存储结构又变为:

3.3、POP指令
从栈中恢复寄存器列表,按照3.2的模型,当需要从栈内存恢复现场时:
POP {LR} ;先恢复 LRPOP {R0~R3,R12} ;在恢复 R0~R3,R12POP指令按照入栈顺序,一次将栈内容推出。
3.4、STMFD、LDMFD指令
出入栈除了可以使用PUSH和POP指令外,还可以使用STMFD和LDMFD进行替代。
STMFD和LDMFD分为两个部分,STM + FD 和 LDM + FD,STM、LDM和前面提到的STR、LDR指令类似,都是对存储器进行操作,但是STR、LDR是操作单个寄存器,而STM、LDM是操作多个寄存器,而其中的FD代表着Full Descending,即满递减的意思。
STMFD存储前,指针会先减少要存储的内容大小,为数据腾出空间,然后再写入数据。
LDMFD加载前,指针会先增加要加载的内容大小,为数据腾出空间,然后再加载数据。
对于堆栈而言,刚好地址是向下增长的。因此可以使用STMFD代替PUSH,使用LDMFD代替POP。
入栈:
STMFD SP!,{R0~R3, R12} ;将SP中的存储地址减少腾出空间后,写入R0~R3 和 R12的值,完成后更新SP,即R0~R3 和 R12压栈STMFD SP!,{LR} ;将SP中的存储地址减少腾出空间后,写入LR的值,完成后更新SP,即将LR压栈出栈:
LDMFD SP!,{LR} ;将SP中的存储地址增加后,传输到LR寄存器中,完成后更新SP,即LR出栈LDMFD SP!,{R0~R3,R12} ;将SP中的存储地址增加后,传输到R0~R3 和 R12寄存器中,完成后更新SP,即R0~R3 和 R12出栈里面SP后面跟的!表示更新SP本身的值,即传输完成后,将更新SP中的值,指向新的栈顶地址,它有通过此命令传输的数据大小决定。
当然,STM + FD 和 LDM + FD 代表了STMFD、LDMFD,那也就存在着其他组合方式,如:
- IA (Increment After)
- IB (Increment Before)
- DA (Decrement After)
- DB (Decrement Before)
- FD (Full Descending)
- FA (Full Ascending)
- ED (Empty Descending)
- EA (Empty Ascending)
这里就不过多做介绍了。
四、跳转指令
有多种跳转操作,比如:
- 直接使用跳转指令 B、 BL、 BX 等。
- 直接向 PC 寄存器里面写入数据
但常用的还是使用跳转指令B、BL、BX。
| 指令 | 描述 |
|---|---|
B <label> | 跳转到 label,如果跳转范围超过了+/-2KB,可以指定 B.W <label>使用 32 位版本的跳转指令,这样可以得到较大范围的跳转 |
BX <Rm> | 间接跳转,跳转到存放于 Rm 中的地址处,并且切换指令集 |
BL <label> | 跳转到标号地址,并将返回地址保存在 LR 中 |
BLX <Rm> | 结合 BX 和 BL 的特点,跳转到 Rm 指定的地址,并将返回地 址保存在 LR 中,切换指令集 |
用的最多的还是 B 和 BL 指令
4.1、B指令
这是最简单的跳转指令, B 指令会将 PC 寄存器的值设置为跳转目标地址, 一旦执行 B 指令, ARM 处理器就会立即跳转到指定的目标地址。==如果要调用的函数不会再返回到原来的执行处==,那就可以用 B 指令,如下示例:
_start:
ldr sp,=0X80200000 ;设置栈指针 b main ;跳转到 main 函数这段代码就是典型的跳转到C代码的汇编指令,但这里只初始化了SP指针,还有其他的初始化DDR等操作没有做。
代码中初始化了sp指针,然受使用b跳转到main函数。
4.2、BL
相对于B指令,BL跳转前,会将当前PC寄存器的值保存在LR寄存器,即保存返回地址。因此使用BL跳转后的函数,可以返回。
比如 Cortex-A 处理器的 irq 中断服务函数都是汇编写的,主要用汇编来实现现场的保护和恢复、获取中断号等。
例如:
push {r0, r1} ;保存 r0,r1cps #0x13 ;进入 SVC 模式,允许其他中断再次进去
bl system_irqhandler ;加载 C 语言中断处理函数到 r2 寄存器中
cps #0x12 ;进入 IRQ 模式pop {r0, r1}str r0, [r1, #0X10] ;中断执行完成,写 EOIR上述代码中 bl system_irqhandle 就是执行 C 语言版的中断处理函数,当处理完成以后是需要返回来继续执行下面的程序,所以使用了 BL 指令。
五、算数运算指令
算数运算指令有很多。
常用的运算指令如下图:

在嵌入式开发中最常会用的就是加减指令,乘除基本用不到。
六、逻辑运算指令

文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!













