接下来,总结一些ARM v7-A架构中常用的汇编指令,如下:

1. 处理器内部数据传输

在处理器内部来回传递数据,常见的操作有:

  • 数据从一个寄存器传输到另一个寄存器
  • 数据传输到特殊寄存器,例如CPSR寄存器
  • 将立即数传输到寄存器

常用的数据传输指令有3个,分别是MOV、MRS和MSR,这3个指令的用法如下:

指令 目的 作用
MOV R0 R1 将R1里面的数据赋值到R0中
MRS R0 CPSR 将CPSR里面的数据赋值到R0中
MSR CPSR R1 将R1的数据赋值到CPSR中

MOV指令

MOV指令用于将数据从一个寄存器赋值到另一个寄存器,或将一个立即数赋值到寄存器里面,使用示例如下:

MOV    R0,R1      @将R1中的数据传递到R0,也就是R0=R1
MOV    R0,#0x14    @将立即数0x14传递到R0,也就是R0=0x14

MRS指令

MRS指令用于将特殊寄存器,例如CPSR或SPSR中的数据传递到通用寄存器,使用如下:

MRS    R0,CPSR    @将CPSR中的数据传递到R0,也就是R0=CPSR

MSR指令

MSR指令用来将通用寄存器中的数据传递到特殊寄存器,例如CPSR和CPSR,使用如下:

MSR    CPSR,R0    @将R0的数据传递到CPSR中,也就是CPSR=R0

2. 存储器访问指令

ARM架构不能直接去访问存储器,例如RAM中的数据,I.MX6UL中的寄存器就是RAM类型的,当我们使用汇编指令来配置SoC寄存器的时候就需要借助存储器访问指令,一般的步骤为,先将要配置的寄存器值写入到Rx寄存器中,然后使用存储器访问指令将Rx中的数据写入到SoC的寄存器中,读取SoC寄存器的值类似,常用的存储器访问指令有LDR和STR,用法如下:

指令 作用
LDR Rd,[Rn,#offset] 将存储器Rn+offset位置的数据读取到Rd中
STR Rd,[Rn,#offset] 将Rd中的数据写入到存储器Rn+offset位置中

LDR指令

在嵌入式ARM中,LDR指令用于从存储器中加载数据到通用寄存器Rx中,另外,LDR指令也能将一个立即数加载到寄存器Rx中,但是要使用”=”,而不是”#”,在ARM开发中,LDR最常用的就是读取SoC的寄存器值,例如,I.MX6UL有一个寄存器GPIO1_GDIR,该寄存器地址为0x0209C004,如果想要读取该寄存器中的数值,可以使用下面汇编代码:

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

上述示例代码中,没有使用到offset,也就是offset为0。

STR指令

LDR指令可以用来读取存储器的数据都Rx寄存器中,STR指令可以将数据写入到存储器中,例如,I.MX6UL有一个寄存器GPIO1_GDIR,该寄存器地址为0x0209C004,如果想要往该寄存器中写入数值,可以使用下面汇编代码:

LDR    R0,=0x0209C004    @将寄存器地址0x0209C004加载到R0中
LDR    R1,=0x00000001    @将0x00000001加载到R1中
STR    R1,[R0]    @将R1中的值写入到寄存器地址0x0209C004中

另外,需要注意的是,LDR指令和STR指令都是按照字进行读取和写入的,也就是直接操作32bit数据,如果要按照字节或者半字操作的话,可以在LDR指令和STR指令后面加上”B”或”H”,例如按字节操作的指令为LDRB和STRB。

压栈和出栈指令

在编写代码的时候,通常会在A函数中调用B函数,当B函数执行完以后需要再回到A函数处执行,如果想要跳回到A函数继续正常执行,那就必须在跳到B函数之前将当前处理器的状态保存起来(保存R0~R15寄存器的值),当B函数执行完后,需要将前面保存的寄存器值恢复到R0~R15,保存寄存器的值操作就是现场保护,恢复寄存器的值操作就是恢复现场,在进行现场保护需要使用PUSH指令进行压栈操作,恢复现场就需要使用POP指令进行出栈操作,这些指令一次可以操作多个寄存器数据,利用当前的栈指针SP来生成地址,用法如下:

指令 作用
PUSH 将寄存器列表压入栈中
POP 将寄存器列表出栈

例如,现在需要将R0~R3寄存器和R12这5个寄存器压栈,当前的SP指针指向0x80000000,处理器的堆栈向下增长,ARM汇编代码如下:

PUSH    {R0~R3,R12}    @将R0~R3和R12进行压栈操作

代码执行后,堆栈如下所示:

3_ARM v7-A常用汇编指令 - 图1

此时的堆栈指针SP指向0x7FFFFFEC,假如现在需要将LR寄存器进行压栈,ARM汇编代码如下:

PUSH    {LR}    @将LR寄存器进行压栈操作

代码执行后,堆栈生长如下所示:

3_ARM v7-A常用汇编指令 - 图2

想要将寄存器出栈的话,使用下面的ARM汇编代码:

POP    {LR}    @先将LR寄存器出栈
POP    {R0~R3,R12}    @将R0~R3,R12寄存器出栈

栈是一种先进后出的模型,出栈是从栈顶先开始,地址依次减小来提取堆栈中的数据恢复到寄存器列表中。

跳转指令

在ARM汇编中,有多种跳转操作,例如:

  • 使用B、BL或BX指令直接跳转
  • 直接向PC寄存器里面写入数据

在上面列出的操作中,都可以完成跳转操作,但是一般常用的还是使用跳转指令B、BL或者BX,指令用法如下:

指令 作用
B 跳转到label
BX 间接跳转,跳转到存放在Rm的地址处,并切换指令集
BL 跳转到label,并将返回地址保存到LR中
BLX 跳转到Rm指定的地址,并将返回地址保存到LR中,切换指令集

B指令

B指令会将PC寄存器的值设置为跳转的目标地址,一旦执行B指令后,ARM处理器将会立即跳到指定的目标地址,如果想调用的函数不会再返回到原来的地方执行,就可以使用B指令,使用示例如下:

_start:
    ldr  sp,=0x80200000    @设置堆栈指针SP
    b    main        @跳转到main函数处执行

示例代码就是在汇编中初始化C运行环境,然后跳转到C文件的main函数处执行,main函数调用后,将不会返回到原来的位置处执行。

BL指令

BL指令在跳转之前会将当前PC寄存器的值保存到LR寄存器中,通过将LR寄存器中的值重新加载到PC中,就可以继续从跳转之前的代码处执行,这是子程序调用的一个基本常用手段,例如,ARM处理器的irq中断服务函数使用汇编编写,主要是使用汇编来实现现场保护和现场恢复、获取中断号等,具体的中断处理过程使用C函数,所以存在在汇编中调用C函数的问题,C函数的处理过程完成以后,需要返回到汇编的中断服务函数,一般是恢复现场,这时候就要使用BL指令进行跳转了,典型代码如下:

push    {r0, r1}    @将r0和r1进行保存
cps    #0x13    @处理器进入SVC模式

bl    system_irqhandler    @跳转到C的中断处理函数

cps    #0x12    @处理器进入IRQ模式
pop    {r0, r1}    @恢复r0和r1寄存器
str    r0, {r1, #0x10 }    @中断执行完成,写EOIR

上面代码中,使用BL指令跳转到C版本的中断处理函数system_irqhandler,函数执行完后,需要返回到原来的位置继续执行下面的汇编代码。

算术运算指令

ARM汇编中也可以进行算术运算,例如加减乘除操作,使用对应的运算指令即可,常用的运算指令用法如下:

指令 计算公式 作用
ADD Rd, Rn, Rm Rd = Rn + Rm 加法运算
ADD Rd, Rn, #immed Rd = Rn + #immed
ADC Rd, Rn, Rm Rd = Rn + Rm + 进位 带进位的加法运算
ADC Rd, Rn, #immed Rd = Rn + #immed + 进位
SUB Rd, Rn, Rm Rd = Rn - Rm 减法运算
SUB Rd, Rn, #immed Rd = Rn - #immed
SBC Rd, Rn, Rm Rd = Rn - Rm - 借位 带借位的减法
SBC Rd, Rn, #immed Rd = Rn - #immed -借位
MUL Rd, Rn, Rm Rd = Rn * Rm 乘法运算
UDIV Rd, Rn, Rm Rd = Rn / Rm 无符号除法运算
SDIV Rd, Rn, Rm Rd = Rn / Rm 有符号除法运算

逻辑运算指令

ARM中还有一些常用的逻辑运算指令,用法如下:

指令 计算公式 作用
AND Rd, Rn Rd = Rd & Rn 按位与
AND Rd, Rn, #immed Rd = Rn & #immed 按位与
AND Rd, Rn, Rm Rd = Rn & Rm 按位与
ORR Rd, Rn Rd = Rd | Rn 按位或
ORR Rd, Rn, #immed Rd = Rn | #immed 按位或
ORR Rd, Rn, Rm Rd = Rn | Rm 按位或
BIC Rd, Rn Rd = Rd & (~Rn) 位清除
BIC Rd, Rn, #immed Rd = Rn & (~#immed) 位清除
BIC Rd, Rn, Rm Rd = Rn & (~Rm) 位清除

对于更多更详细的ARM汇编指令,可以参考ARM官网的相关文档。