ARM Assembly(01)-Registers
无论是高级程序语言还是汇编语言,程序能够访问和操作的只有Register和Memory,本文对ARM汇编中Register的用法进行介绍
ARM中所有寄存器都是32位的(不包括aarch64),有13个GPRs(General Purpose Registers,通用寄存器),和几个特殊用途寄存器,如下图
特殊寄存器主要是堆栈寄存器SP(Stack Pointer)、链接寄存器LR(Link Register)、程序计数器PC(Program Counter),以及两个程序状态寄存器CPSR(Current Program Status Register)和SPSR(Saved Program Status Register)
通用寄存器GPRs
通用寄存器和其他处理器中的寄存器用法类似,都可以用于算数和逻辑指令中,用于暂存数据或地址,在汇编代码中通过”Rn”或者”rn”来标识,n可以是0~15,也就是说SP、LR和PC在汇编代码中可以用SP/sp
、 LR/lr
、PC/pc
表示,也可以用R13/r13
、R14/r14
、 R15/r15
来表示,而R0-R12只能用”R0-R12 r0-r12”表示
这里需要说明一点,R13和R14根据程序上下文,有些时候也可以当作通用寄存器来使用,但是前提是使用者必须清楚的知道SP和LR的作用
MOV
首先介绍第一条ARM汇编指令MOV
1 | MOV Rn,Op2 |
该指令将操作数”Op2”的值拷贝到寄存器Rn中。”Op2”可以是以类似#k
的形式的立即数,也可以是一个寄存器Rm
,Rm
和Rn
可以是R0到R15之间的任何一个寄存器。例如将立即数0x10拷贝到寄存器R0中
1 | MOV R0,#0x10 |
该指令执行后,R0的值为0x10。也可以在寄存器之间进行拷贝,例如
1 | MOV R0,#0x20 |
第一条指令将R0赋值为立即数0x20,第二条指令将R0的值拷贝到R1,因此R1的值也是0x20
ADD
ADD指令格式为
1 | ADD Rd,Rn,Op2 |
该指令将寄存器Rn与操作数Op2相加,结果放入Rd中,Op2可以是立即数或者寄存器。例如
1 | MOV R1,#0x18 |
SUB
SUB指令的格式为
1 | SUB Rd,Rn,Op2 |
该指令将Rn减去Op2的结果放入Rd中,例如
1 | MOV R0,#0x55 |
立即数的表示和范围
注意以上指令中,对立即数的使用,都是以”#”开头,表示操作数是一个立即数。ARM汇编支持多种进制数
十六进制:#0x25
十进制:#10
二进制:#0b0001
ASCII:#’A’
上文中介绍的指令中,MOV/ADD/SUB的立即数操作数都是比较小的数,既然ARM寄存器都是32位的,能否操作较大的数呢,例如以下代码
1 | .global _start |
使用arm-none-eabi-gcc编译该汇编代码会报以下错误
这是为什么呢?这就要介绍一下ARM汇编代码与二进制机器码的关系,ARM使用RISC指令集架构,所有指令的机器码都是32位的, 而在MOV、ADD、SUB等机器码中,32位里只有8bit是留给操作数使用的,因此立即数的范围是0~255,超过255的数据编译器认为非法
编译以下汇编代码
1 | .global _start |
编译后用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
ARM核心通过CPSR对内部操作进行监控,下图展示了CPSR的bit位划分和作用,灰色阴影部分作为保留位为未来扩展使用
CPSR划分为了4个区域,每8bit为一个区域
bit[31:24]:Flags,标志域
bit[23:16]:Status,状态域
bit[15:8]:Extension,扩展域
bit[7:0]:Control,控制域
当前设计中,状态域和扩展域都用做保留位。控制域包括处理器模式、状态和中断掩码。标志域主要是条件标志。处理器模式将在后文中详细介绍
一些ARM处理器可能还有额外的一些bit位,例如在标志域中的”J”bit,这只在Jazelle-enabled处理器中存在。
其中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 | ADD R0,R1,R2 ;不更新CPSR |
堆栈寄存器SP
SP用于暂存数据或寄存器的值到堆栈中,当发生子程序调用或者中断时,常常会用到,这里先不做介绍
链接寄存器LR
LR用于子程序返回时的返回地址记录,主要用于子程序调用过程,先不做介绍