Cortex-A相关、汇编基础

Cortex-A 架构

1. Cortex-A架构有9种处理模式
Cortex-A相关、汇编基础_第1张图片
除了 User(USR)用户模式以外,其它 8 种运行模式都是特权模式。
用户模式下是不能访问系统所有资源的,要想访问这些受限的资源就必须进行模式切换。但是用户模式是不能直接进行切换的,用户模式下需要借助异常来完成模式切换,当要切换模式的时候,应用程序可以产生异常,在异常的处理过程中完成处理器模式切换。
2. ARM 架构提供了 16 个 32 位的通用寄存器(R0~R15)供软件使用,此外,ARM 还提供了一个当
前程序状态寄存器 CPSR 和一个备份程序状态寄存器 SPSR

(1)R13 也叫做 SP,用来做为栈指针
(2)R14(LR)来存放当前子程序的返回地址。如果使用 BL 或者 BLX来调用子函数的话,R14(LR)被设置成该子函数的返回地址;若想返回程序原位置,在子函数中,将 R14(LR)中的值赋给R15(PC)即可完成子函数返回。
(3)程序计数器 R15。因为ARM 处理器是 3 级流水线,R15 (PC)值 = 当前执行的程序位置 + 8 个字节。
(4)CPSR和SPSR。当特定的异常中断发生时, SPSR 寄存器用来保存当前程序状态寄存器(CPSR)的值,当异常退出以后可以用 SPSR 中保存的值来恢复 CPSR。

ARM汇编基础

在进行嵌入式 Linux 开发的时候是绝对要掌握基本的 ARM 汇编,因为 Cortex-A 芯片一上电 SP 指针还没初始化(C 语言中的函数调用涉及到出栈入栈,这段内存由SP指针访问), C 环境还没准备好,所以肯定不能运行 C 代码,必须先用汇编语言设置好 C 环境。
1. GNU 汇编语法
我们的汇编代码要符合 GNU 语法;GNU 汇编语法适用于所有的架构,并不是 ARM 独享的
每条语句有三个可选部分 label: instruction @ comment
label 即标号,表示地址位置,有些指令前面可能会有标号,这样就可以通过这个标号得到
指令的地址。注意 label 后面的“:”,任何以“:”结尾的标识符都会被识别为一个标号。
instruction 即指令,也就是汇编指令或伪指令
@符号,表示后面的是注释

比如如下代码:

add:
	MOVS R0, #0X12 @设置 R0=0X12
	
上面代码中“add:”就是标号,“MOVS R0,#0X12”就是指令,最后的“@设置 R0=0X12”就是注释。

注意! ARM 中的指令、伪指令、伪操作、寄存器名等可以全部使用大写,也可以全部使用小写,但是不能大小写混用。
.text 表示代码段。
.data 初始化的数据段。
.bss 未初始化的数据段。
.rodata 只读数据段。
我们当然可以自己使用.section 来定义一个段,每个段以段名开始,以下一段名或者文件结尾结束,

比如:
.section .testsection @定义一个 testsetcion 段

汇编程序的默认入口标号是_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,表示 num=0x12.align 数据字节对齐,比如: .align 4 表示 4 字节对齐。
.end 表示源文件结束。
.global 定义一个全局符号,格式为: .global symbol,比如: .global _start。

2. Cortex-A7 常用汇编指令

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

MOV 指令:将数据从一个寄存器拷贝到另外一个寄存器,或者将一个立即数传到寄存器里面

MOV R0, R1 @将寄存器 R1 中的数据传递给 R0,即 R0=R1
MOV R0, #0X12 @将立即数 0X12 传递给 R0 寄存器,即 R0=0X12

MRS 指令:用于将特殊寄存器(如 CPSR 和 SPSR)中的数据传递给通用寄存器。要读取特殊
寄存器的数据只能使用 MRS 指令!

MRS R0, CPSR @将特殊寄存器 CPSR 里面的数据传递给 R0,即 R0=CPSR

MSR 指令:用来将普通寄存器的数据传递给特殊寄存器

MSR CPSR, R0 @将 R0 中的数据复制到 CPSR 中,即 CPSR=R0

2. 存储器访问指令

ARM 不能直接访问存储器,比如 RAM 中的数据。我们用汇编来配置 芯片修改自身寄存器(例如GPIO、UART、RTC等寄存器)的时候需要借助存储器访问指令,一般先将要配置的值写入到 Rx(x=0~12)寄存器中,然后借助存储器访问指令将 Rx 中的数据写入到 I.MX6UL 寄存器
LDR 指令:将存储器数据读到R0-R15。LDR 也可以将一个立即数加载到寄存器 Rx中, LDR 加载立即数的时候要使用“=”,而不是“#”。在嵌入式开发中, LDR 最常用的就是读取 CPU 的寄存器值

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

STR 指令:将R0-R15数据写入到存储器中

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

LDR 和 STR 都是按照字进行读取和写入的,也就是操作的 32 位数据,如果要按照字节、半字进行操作的话可以在指令“LDR”后面加上 B 或 H

3. 压栈和出栈指令
我们通常会在 A 函数中调用 B 函数,当 B 函数执行完以后再回到 A 函数继续执行。要想在跳回 A 函数以后代码能够接着正常运行,那就必须在跳到 B 函数之前将当前处理器状态保存起来(就是保存 R0~R15 这些寄存器值),当 B 函数执行完成以后再用前面保存的寄存器值恢复R0~R15 即可。在进行现场保护的时候需要进行压栈(入栈)操作,恢复现场就要进行出栈操作。压栈的指令为 PUSH,出栈的指令为 POP。处理器的栈是向下增长的。
PUSH 压栈操作:

PUSH {R0~R3, R12} @将 R0~R3 和 R12 压栈  排最后面的寄存器R12是最先压入的

Cortex-A相关、汇编基础_第2张图片
POP出栈

POP {LR} @先恢复 LR
POP {R0~R3,R12} @在恢复 R0~R3,R12    排在最前面的寄存器先出栈

PUSH 和 POP 的另外一种写法是“STMFD SP!”和“LDMFD SP!”

 STMFD SP!,{R0~R3, R12} @R0~R3,R12 入栈
 STMFD SP!,{LR} @LR 入栈

 LDMFD SP!, {LR} @先恢复 LR
 LDMFD SP!, {R0~R3, R12} @再恢复 R0~R3, R12

LDR 和 STR,这两个是数据加载和存储指令,但是每次只能读写存储器中的一个数据。 STM 和 LDM 就是多存储和多加载,可以连续的读写存储器中的多个连续数据。
FD 是 Full Descending 的缩写,即满递减的意思

4. 跳转指令
①、直接使用跳转指令 B、 BL、 BX 等。(B命令跳转后不会返回,BL跳转后还可以返回)
②、直接向 PC 寄存器里面写入数据。

5. 算数运算符指令
Cortex-A相关、汇编基础_第3张图片
6. 逻辑运算符指令
Cortex-A相关、汇编基础_第4张图片

你可能感兴趣的:(Cortex-A相关、汇编基础)