欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ARM汇编基础(Cortex-A7)

程序员文章站 2022-06-09 08:25:53
...

随手把今天学的东西记录一下吧,不然老是觉得空空的感觉什么都记不住

为什么要学习汇编?
因为Cortex-A芯片一上电的时候SP指针还没有初始化,C语言环境还没有准备好,所以不能运行C代码,所以必须先用汇编语言设置好C环境,比如初始化DDR、设置SP指针等。所谓C语言环境就是能够保证C语言能够正常运行。C语言中的函数调用涉及到出栈和入栈,出栈入栈就是对栈顶进行操作,堆栈其实就是一段内存,这段内存比较特殊,由SP指针访问,SP指针指向栈顶。芯片一上电SP指针还没有初始化,所以C语言没法运行。对于有些芯片需要初始化DDR,用户代码需要在DDR中运行,所以一开始要用汇编来初始化DDR控制器。

1. GNU汇编语法

GNU 汇编语法适用于所有的架构,并不是 ARM 独享的

GNU汇编由一系列的语句组成,每条语句包括
label:instrution @comment
label:即标号,表示地址位置
instrution:指令,汇编指令或者伪代码

用户可以使用.section伪操作来定义一个段,汇编系统中预定义了一些段名:
.text 表示代码段。
.data 初始化的数据段。
.bss 未初始化的数据段。
.rodata 只读数据段。

汇编程序的默认入口标号是_start,不过我们也可以在链接脚本中使用ENTRY来指明其它的入口点,下面的代码就是使用_start作为入口标号:

.global _start

_start:
	ldr r0, =0x12 @r0=0x12

上面代码中.global是伪操作,表示_start是一个全局标号,类似C语言里面的全局变量一样,常见的伪操作有:
.byte 定义单字节数据,比如.byte 0x12。
.short 定义双字节数据,比如.short 0x1234。
.long 定义一个 4 字节数据,比如.long 0x12345678。
.equ 赋值语句,格式为: .equ 变量名,表达式,比如.equ num, 0x12,表示
.align 数据字节对齐,比如: .align 4 表示 4 字节对齐。
.end 表示源文件结束。
global 定义一个全局符号,格式为: .global symbol,比如: .global _start

GNU汇编也支持函数,比如:
Undefined_Handler:
ldr r0, =Undefined_Handler
bx r0

“Undefined_Handler”就是函数名,“ldr r0, =Undefined_Handler”是函数体,“bx r0”是函数
返回语句,“bx”指令是返回指令,函数返回语句不是必须的。

2. Cortex-A7常用汇编指令

处理器内部数据传输指令

使用处理器做的最多事情就是在处理器内部来回的传递数据,常见的操作有:
a.将数据从一个寄存器传递到另一个寄存器
b.将数据从一个寄存器传递到特殊寄存器,如CPSR和SPSR寄存器
c.将立即数传递到寄存器

常用的数据传输指令有三个:MOV MRS MSR,三个指令用法如下:
ARM汇编基础(Cortex-A7)

存储器访问指令

常用的存储器访问指令有两种:LDR和STR。
LDR指令
LDR R0, =0X0209C004 @将寄存器地址 0X0209C004 加载到 R0 中,即 R0=0X0209C004
LDR R1, [R0] @读取地址 0X0209C004 中的数据到 R1 寄存器中

STR指令
LDR R0, =0X0209C004 @将寄存器地址 0X0209C004 加载到 R0 中,即 R0=0X0209C004
LDR R1, =0X20000002 @R1 保存要写入到寄存器的值,即 R1=0X20000002
STR R1, [R0] @将 R1 中的值写入到 R0 中所保存的地址中

压栈和出栈指令

我们通常会在A函数中调用B函数,当B函数执行完以后再回到A函数继续执行。要想在跳回A函数以后代码能够接着正常运行,那就必须在跳到B函数之前将当前处理器状态保存起来(就是保存 R0~R15 这些寄存器值),当B函数执行完成以后再用前面保存的寄存器值恢复
R0~ R15 即可。保存 R0~ R15寄存器的操作就叫做现场保护,恢复R0~R15寄存器的操作就叫做恢复现场。在进行现场保护的时候需要进行压栈入栈操作,恢复现场就要进行出栈操作。压栈的指令为PUSH,出栈的指令为POP,PUSH和POP是一种多存储和多加载指令,即可以一次操作多个寄存器数据,他们利用当前的栈指针SP来生成地址。
ARM汇编基础(Cortex-A7)
PUSH和POP的另一种写法是STMFD SP!和LDMFD SP!
因此
PUSH {R0~ R3,R12} @将R0~R3和R12压栈
PUSH {LR} @将 LR 进行压栈
等价于
STMFD SP!,{R0~R3,R12}
STMFD SP!,{LR} @LR 入栈

POP {LR} @先恢复LR
POP {R0~ R3,R12} @在恢复 R0~R3,R12
等价于
LDMFD SP!,{LR}
LDMFD SP!,{R0~R3,R12}

STM 和 LDM 就是多存储和多加载,可以连续的读写存储器中的多个连续数据。FD 是 Full Descending 的缩写,即满递减的意思。根据 ATPCS 规则,ARM 使用的 FD 类型的堆栈,SP指向最后一个入栈的数值,堆栈是由高地址向下增长的,也就是前面说的向下增长
的堆栈,因此最常用的指令就是 STMFD 和 LDMFD。 STM 和 LDM 的指令寄存器列表中编号小的对应低地址,编号高的对应高地址。

跳转指令

有多种跳转操作,比如:
直接使用跳转指令,如B、BL、BX等
直接向PC寄存器里面写入数据

主要使用B、BL,区别就是B跳转了回不来了BL跳转由于将PC寄存器中的值保存到了LR寄存器中所以还能跳转回来,这是子程序调用的一个常用手段,并且可以用来实现线程的保护和恢复,获取中断号等等。

算术运算指令

ARM汇编基础(Cortex-A7)

逻辑运算指令

ARM汇编基础(Cortex-A7)