适用于Cortex-A的常见GNU汇编语法
语句
GNU 汇编语法适用于所有的架构,并不是 ARM 独享的,GNU 汇编由一系列的语句组成,每行一条语句,每条语句有三个可选部分:label: instruction @ comment
label
:标号,表示地址位置instruction
:指令,汇编指令或伪指令@ comment
:注释
ARM中的指令、伪指令、伪操作、寄存器名等可以全部使用大写,也可以全部使用小写,但是不能大小写混用。
伪操作(伪指令)
用户可通过.section
伪操作来定义一个段,汇编系统预定义了一些段名,包括:
.text
:代码段.data
:初始化的数据段.bss
:未初始化的数据段.rodata
:只读数据段
.section
也可用于自定义段,每个段以段名开始,以下一段名或文件结尾结束。如.section .testsection @顶一个testsection段
汇编程序的默认入口标号是_start
,不过我们也可以在链接脚本中使用ENTRY
来指明其它的入口点,下面的代码就是使用_start
作为入口标号:
``
.global _start
_start:
ldr r0, =0x12 @r0=0x12
``
代码中.global
是伪操作,表示_start
是一个全局标号(性质类似于C语言中的全局变量)。
常见的伪指令有:
.global
:声明全局符号,如.global main @声明main为全局符号
.section
:定义段.word
:定义字数据.asciz
:定义以null结尾的字符串,如.asciz "Hello World"
.byte
:定义单字节数据,比如.byte 0x12
.short
:定义双字节数据,比如.short 0x1234
.long
:定义一个4字节数据,比如.long 0x12345678
.equ
:赋值,如.equ num, 0x12
表示num=0x12
.align
:数据字节对齐,.align 4
表示4字节对齐.end
:表示源文件结束
处理器内部数据传输指令
最常见的一类指令,包括:
- 将数据从一个寄存器传递到另外一个寄存器
- 将数据从一个寄存器传递到特殊寄存器,如 CPSR 和 SPSR 寄存器
- 将立即数传递到寄存器
MOV
将数据从一个寄存器拷贝到另一个寄存器,或将一个立即数传递到寄存器中。
1 |
|
MRS
将特殊寄存器(如CPSR和SPSR)中的数据传递给通用寄存器。注意:读取特殊寄存器数据时只能使用MRS
指令。
1 |
|
MSR
将普通寄存器的数据传递至特殊寄存器,即写入特殊寄存器。
1 |
|
存储器访问指令
ARM无法直接访问存储器(如RAM)。以I.MX6UL为例,其寄存器为RAM类型,汇编配置寄存器时需借助存储器访问指令,将待配置的值写入到Rx(x=0~12)寄存器中,然后借助存储器访问指令将Rx中的数据写入之MX6UL的寄存器中,读取亦同。
LDR
用于从存储器加载数据至寄存器Rx中,也可以将一个立即数加载到寄存器Rx中。加载立即数时,使用=
而不是#
,如=0xFFFFFFFF
。
在嵌入式开发中,LDR最常用的就是读取CPU的寄存器值:
1 |
|
STR
与LDR相反,STR用于将数据写入到存储器中。
1 |
|
跳转指令
B指令
B指令将PC寄存器的值设置为目标跳转地址,一旦执行B指令,ARM处理器就会立即跳转至指定的目标地址。如果待调用函数不会再返回到原来的执行处,就可以使用B指令。
1 |
|
BL指令
相比B指令,BL指令在跳转之前会在寄存器 LR(R14) 中保存当前 PC 寄存器值,所以可以通过将 LR 寄存器中的值重新加载到 PC 中来继续从跳转之前的代码处运行,这是子程序调用一个基本但常用的手段,常见于各种中断服务程序。
Cortex-A 处理器的 irq 中断服务函数都是汇编写的,主要用汇编来实现现场的保护和恢复、获取中断号等。但是具体的中断处理过程都是 C 函数,所以就会存在汇编中调用 C 函数的问题。而且当 C 语言版本的中断处理函数执行完成以后是需要返回到 irq 汇编中断服务函数,因为还要处理其他的工作,一般是恢复现场。这个时候就不能直接使用 B 指令了,因为 B 指令一旦跳转就再也不会回来了,这个时候要使用 BL 指令,示例代码如下:
1 |
|
ARM中常见的寄存器
几个ARM中常用的寄存器:
- IP寄存器:内部程序调用暂存寄存器,子程序的连接text段中常使用该规则
- SP寄存器:栈指针寄存器,用于存储当前栈顶地址。程序执行过程中,栈是用来存储临时变量、函数调用返回地址等数据的重要数据结构,SP寄存器的值会随着栈的变化而变化
- LR寄存器:连接寄存器,程序跳转(子程序调用,中断跳转)后,arm自动在该寄存器中存入原程序(未跳转)的下一条指令的地址,也叫函数调用返回地址。当一个函数被调用时,LR寄存器会存储调用该函数的下一条指令的地址,当函数执行完毕后,程序会跳转到LR寄存器中存储的地址继续执行。
- PC寄存器:程序计数器,保存的是当前正在取指的指令的地址(arm采用2级流水线,因此是当前正在执行指令的地址+8)。PC寄存器是ARM中的程序计数器,用于存储下一条将要执行的指令的地址。
此外,还有两个比较重要的状态寄存器:
- CPSR:程序状态寄存器,在任何处理器模式下被访问。它包含了条件标志位、中断禁止位、当前处理器模式标志以及其他的一些控制和状态位。CPSR在用户级编程时用于存储条件码。
- SPSR:程序状态保存寄存器,每一种处理器模式下都有一个状态寄存器SPSR,SPSR用于保存CPSR的状态,以便异常返回后恢复异常发生时的工作状态。当特定的异常中断发生时,这个寄存器用于存放当前程序状态寄存器的内容。在异常中断退出时,可以用SPSR来恢复CPSR。由于用户模式和系统模式不是异常中断模式,所以他没有SPSR。当用户在用户模式或系统模式访问SPSR,将产生不可预知的后果。
二者常用于MRS或MSR指令,用于spsr中的值转移到寄存器或把寄存器的内容加载到spsr中,如:
1 |
|
ARM进入异常模式后,SPSR自动保存进入异常前的CPSR的值,以便异常返回后恢复异常发生时的工作状态。
LED点灯示例
1 |
|