这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 综合技术 » 基础知识 » 【转】ARM汇编基础(Cortex-A7)

共3条 1/1 1 跳转至

【转】ARM汇编基础(Cortex-A7)

助工
2020-05-15 14:20:37     打赏

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

为什么要学习汇编?

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

GNU汇编语法

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

GNU汇编由一系列的语句组成,每条语句包括

label:instrution @comment

label:即标号,表示地址位置

instrution:指令,汇编指令或者伪代码

用户可以使用.section伪操作来定义一个段,汇编系统中预定义了一些段名:

.text 表示代码段。

.data 初始化的数据段。

.bss 未初始化的数据段。

.rodata 只读数据段。

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

image.png

上面代码中.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”指令是返回指令,函数返回语句不是必须的。

Cortex-A7常用汇编指令

1.处理器内部数据传输指令

使用处理器做的最多事情就是在处理器内部来回的传递数据,常见的操作有:

a.将数据从一个寄存器传递到另一个寄存器

b.将数据从一个寄存器传递到特殊寄存器,如CPSR和SPSR寄存器

c.将立即数传递到寄存器

常用的数据传输指令有三个:MOV MRS MSR,三个指令用法如下:

2.存储器访问指令

常用的存储器访问指令有两种:LDR和STR。

(1)LDR指令

LDR R0, =0X0209C004 @将寄存器地址 0X0209C004 加载到 R0 中,即 R0=0X0209C004

LDR R1, [R0] @读取地址 0X0209C004 中的数据到 R1 寄存器中

(2)STR指令

LDR R0, =0X0209C004 @将寄存器地址 0X0209C004 加载到 R0 中,即 R0=0X0209C004

LDR R1, =0X20000002 @R1 保存要写入到寄存器的值,即 R1=0X20000002

STR R1, [R0] @将 R1 中的值写入到 R0 中所保存的地址中

3.压栈和出栈指令

我们通常会在A函数中调用B函数,当B函数执行完以后再回到A函数继续执行。要想在跳回A函数以后代码能够接着正常运行,那就必须在跳到B函数之前将当前处理器状态保存起来(就是保存 R0~R15 这些寄存器值),当B函数执行完成以后再用前面保存的寄存器值恢复

R0~ R15 即可。保存 R0~ R15寄存器的操作就叫做现场保护,恢复R0~R15寄存器的操作就叫做恢复现场。在进行现场保护的时候需要进行压栈入栈操作,恢复现场就要进行出栈操作。压栈的指令为PUSH,出栈的指令为POP,PUSH和POP是一种多存储和多加载指令,即可以一次操作多个寄存器数据,他们利用当前的栈指针SP来生成地址。

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 的指令寄存器列表中编号小的对应低地址,编号高的对应高地址。

4.跳转指令

有多种跳转操作,比如:

直接使用跳转指令,如B、BL、BX等

直接向PC寄存器里面写入数据

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

5.算术运算指令

6.逻辑运算指令


本文为CSDN博主「那可真是太开心了呢」的原创文章,https://blog.csdn.net/qq_44708426/java/article/details/106126535


菜鸟
2020-05-15 22:20:14     打赏
2楼

好东西


助工
2020-05-16 20:45:48     打赏
3楼

总结的很全面


共3条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]