ARM汇编基础(Cortex-A7)

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

为什么要学习汇编?
因为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,三个指令用法如下:
在这里插入图片描述

存储器访问指令

常用的存储器访问指令有两种: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来生成地址。
在这里插入图片描述
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寄存器中所以还能跳转回来,这是子程序调用的一个常用手段,并且可以用来实现线程的保护和恢复,获取中断号等等。

今天看汇编的时候也遇见了bl指令,想要知道什么时候跳转回来就盯着看代码中的mov pc,LR这条指令

算术运算指令

ARM汇编基础(Cortex-A7)_第1张图片

逻辑运算指令

ARM汇编基础(Cortex-A7)_第2张图片

3.其他

这些是后面看汇编过程中遇到的,慢慢加,当学习笔记

1. .section 命令
是汇编语言汇中最为重要的命令之一,作用是定义内存段

该命令后只跟一个参数,即它声明的段的类型。

例如:

.section .text        #定义文本段(代码段)
.section .data        #定义数据段
.section .bss          #定义 bss 段 

2.cmp与teq
cmp:进行比较两个操作数的大小
teq:用于比较操作数1和操作数2是否相等

你可能感兴趣的:(linux)