为什么要学习汇编? 因为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!
|