对于ARM Cortex芯片,大部分芯片上电以后,SP指针未初始化,C语言无法运行。有些芯片还需要用汇编来初始化DDR。在进入C语言之前的阶段需要汇编做一些事情。
ARM汇编,编译使用的 GCC 交叉编译器,所以我们的汇编代码要符合 GNU 语法。
GNU 语法
基本格式
label:instruction @ comment
label 即标号,表示地址位置
instruction 即指令,也就是汇编指令或伪指令
comment 就是注释内容。
add:
MOVS R0, #0X12 @设置 R0=0X12
定义段
可以通过用.section 伪操作来定义一个段
.section .testsection @定义一个 testsetcion 段
常见的段有
.text 表示代码段。
.data 初始化的数据段。
.bss 未初始化的数据段。
.rodata 只读数据段。
常见的伪操作有
.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。
汇编函数
函数名:
函数体
返回语句
/* 未定义中断 */
Undefined_Handler:
ldr r0, =Undefined_Handler
bx r0
/* SVC 中断 */
SVC_Handler:
ldr r0, =SVC_Handler
bx r0
/* 预取终止中断 */
PrefAbort_Handler:
ldr r0, =PrefAbort_Handler
bx r0
Cortex-A7常用的汇编指令
内部数据传输指令MOV,MRS,MSR
MOV R0,R1 @将寄存器 R1 中的数据传递给 R0,即 R0=R1
MOV R0, #0X12 @将立即数 0X12 传递给 R0 寄存器,即 R0=0X12
MRS R0, CPSR @将特殊寄存器 CPSR 里面的数据传递给 R0,即 R0=CPSR
MSR CPSR, R0 @将 R0 中的数据复制到 CPSR 中,即 CPSR=R0
存储器访问指令
ARM 不能直接访问存储器,一般先将要配置的值写入到 Rx(x=0~12)寄存器中,然后借助存储器访问指令将 Rx 中的数据写入到 I.MX6UL 寄存器。读取 I.MX6UL 寄存器的值也是类似的逻辑。常用的存储器访问指令有两种:LDR 和STR。
LDR
LDR 最常用的就是读取 CPU 的寄存器值,比如 I.MX6UL 有个寄存器 GPIO1_GDIR,其地址为 0X0209C004,我们现在要读取这个寄存器中的数据。采用下面的汇编指令:
LDR R0, =0X0209C004 @将寄存器地址 0X0209C004 加载到 R0 中,即 R0=0X0209C004
LDR R1, [R0] @读取地址 0X0209C004 中的数据到 R1 寄存器中
STR
LDR是读取,STR就是将数据写入到寄存器
LDR R0, =0X0209C004 @将寄存器地址 0X0209C004 加载到 R0 中,即 R0=0X0209C004
LDR R1, =0X20000002 @R1 保存要写入到寄存器的值,即 R1=0X20000002
STR R1, [R0] @将 R1 中的值写入到 R0 中所保存的地址中
压栈和出栈指令
堆栈(Stack)是一种常见的数据结构,具有“先进后出”(Last In First Out,LIFO)的特性,即最后入栈的元素最先被弹出。堆栈通常用于实现函数调用、表达式求值、递归算法等场景。
堆栈有两个基本操作:
入栈(Push):向堆栈顶部添加元素。
出栈(Pop):从堆栈顶部移除元素。
在进行现场保护的时候需要进行压栈(入栈)操作,恢复现场就要进行出栈操作。压栈的指令为 PUSH,出栈的指令为 POP,PUSH 和 POP 是一种多存储和多加载指令,即可以一次操作多个寄存器数据。
堆区在低地址段,向上增长,可以由程序员自己开辟,栈区在高地址段主要存放函数参数值和局部变量等,由系统自动的分配。
STMFD SP!,{R0~R3, R12} @R0~R3,R12 入栈
STMFD SP!,{LR} @LR 入栈
LDMFD SP!, {LR} @先恢复 LR
LDMFD SP!, {R0~R3, R12} @再恢复 R0~R3, R12
跳转指令
BL 指令相比 B 指令,在跳转之前会在寄存器 LR(R14)中保存当前 PC 寄存器值,所以可以通过将 LR 寄存器中的值重新加载到 PC 中来继续从跳转之前的代码处运行,这是子程序调用一个基本但常用的手段。
算术运算指令
逻辑运算指令
参考:【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.6
声明:本博客部分内容来自网络,旨在分享,若侵犯您的合法权益请及时联系删除。