ARM Assembly-Registers

无论是高级程序语言还是汇编语言,程序能够访问和操作的只有Register和Memory,本文对ARM汇编中Register的用法进行介绍

ARM中所有寄存器都是32位的(不包括aarch64),有13个GPRs(General Purpose Registers,通用寄存器),和几个特殊用途寄存器,如下图

特殊寄存器主要是堆栈寄存器SP、链接寄存器LR、程序计数器PC,还有一个程序状态寄存器CPSR未在图中体现

通用寄存器GPRs

通用寄存器和其他处理器中的寄存器用法类似,都可以用于算数和逻辑指令中

MOV

首先介绍第一条ARM汇编指令MOV

1
MOV Rn,Op2

该指令将操作数”Op2”的值拷贝到寄存器Rn中。”Op2”可以是以类似#k的形式的立即数,也可以是一个寄存器RmRmRn可以是R0到R15之间的任何一个寄存器。例如将立即数0x10拷贝到寄存器R0中

1
MOV R0,#0x10

该指令执行后,R0的值为0x10。也可以在寄存器之间进行拷贝,例如

1
2
MOV R0,#0x20
MOV R1,R0

第一条指令将R0赋值为立即数0x20,第二条指令将R0的值拷贝到R1,因此R1的值也是0x20

ADD

ADD指令格式为

1
ADD Rd,Rn,Op2

该指令将寄存器Rn与操作数Op2相加,结果放入Rd中,Op2可以是立即数或者寄存器。例如

1
2
3
4
MOV R1,#0x18
MOV R2,#0x32
ADD R3,R1,#0x20    ;R3 = (R1 + 0x20) = 0x38
ADD R4,R2,R3       ;R4 = (R2 + R3) = 0x58

SUB

SUB指令的格式为

1
SUB Rd,Rn,Op2

该指令将Rn减去Op2的结果放入Rd中,例如

1
2
MOV R0,#0x55
SUN R1,R0,0x20; R1 = (R0 - 0x20) = 0x35

立即数的表示和范围

注意以上指令中,对立即数的使用,都是以”#”开头,表示操作数是一个立即数。ARM汇编支持多种进制数

  • 十六进制:#0x25

  • 十进制:#10

  • 二进制:#0b0001

  • ASCII:#’A’

上文中介绍的指令中,MOV/ADD/SUB的立即数操作数都是比较小的数,既然ARM寄存器都是32位的,能否操作较大的数呢,例如以下代码

1
2
3
.global _start
_start:
MOV R0,#99999

使用arm-none-eabi-gcc编译该汇编代码会报以下错误

这是为什么呢?这就要介绍一下ARM汇编代码与二进制机器码的关系,ARM使用RISC指令集架构,所有指令的机器码都是32位的, 而在MOV/ADD/SUB等机器码中,32位里只有8bit是留给操作数使用的,因此立即数的范围是0~255,超过255的数据编译器认为非法

编译以下汇编代码

1
2
3
4
5
.global _start
_start:
mov r0,#0x99
mov r1,#25
add r2,r0,r1

编译后用hexdump查看二进制文件内容,可以看到总共用了12字节,3个word的大小,可以看到确实是每个指令对应32位

程序计数器PC

PC是程序计数器,类似x86中的ip寄存器,都是用来指向下一条将要被执行的指令的地址。当CPU读取并执行完一条指令后,PC会自动增加以指向下一条指令地址。PC寄存器位宽决定了程序能够访问的地址空间范围,32位地址能够访问的地址范围为0x00000000到0xFFFFFFFF共4GB空间。可以想象,64位CPU的PC寄存器一定是64位的

PC既然指向了下一条将要执行的指令地址,那么当CPU刚启动时,第一条指令指向哪里呢?不同的处理器有不同的启动地址,ARM一般以0x00000000为上电复位后的指令起始地址,但是具体芯片的启动地址可能由生产厂商来确定,因为厂商会在片上ROM中存储一些启动代码,而片上ROM的内存映射地址不是确定的

程序状态寄存器CPSR

程序状态寄存器CPSR是一个非常特殊的寄存器,它不可被汇编程序修改,只有CPU可以维护CPSR的数值。CPSR用于暂存程序运行过程中的各种标志

其中N、Z、C、V是条件标志,用来指示一些条件标志,某些汇编指令可以与这些标志进行关联使用,完成一些条件执行

  • C:进位标志,当bit31发生进位时被置1

  • Z:零标志,算数或者逻辑运算的结果会影响该标志,当结果为0时Z=1。该标志被广泛应用在比较或循环操作中,后续文章会介绍

  • N:负数标志,当有符号数的bit31为负号时,该标志为1

  • V:溢出标志,当有符号数运算结果导致高位数据覆盖了符号位时,该标志为1

  • T:用于指示ARM处于Thumb状态

  • I/F:指示中断使能或者关闭

CPSR另外一个特殊的原因在于很多指令可以增加一个”S”后缀,表示该指令的结果会更新到CPSR中,也就是说,如果指令不带”S”后缀,表示结果不会更新CPSR,例如

1
2
ADD R0,R1,R2    ;不更新CPSR
ADDS R0,R1,R2 ;更新CPSR

堆栈寄存器SP

SP用于暂存数据或寄存器的值到堆栈中,当发生子程序调用或者中断时,常常会用到,这里先不做介绍

链接寄存器LR

LR用于子程序返回时的返回地址记录,主要用于子程序调用过程,先不做介绍