随手把今天学的东西记录一下吧,不然老是觉得空空的感觉什么都记不住
为什么要学习汇编?
因为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作为入口标号:
上面代码中.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