第二部分 ARM指令集、寻址方式
1. ARM指令集
ARM指令字长为固定的32位。一条典型的ARM指令编码格式如下: 一条典型的ARM指令语法格式如下所示:ARM指令集大致可分为一下6大类:跳转指令、数据处理指令、程序状态寄存器指令、Load/Store指令、协处理器指令和中断指令等。
1. 数据处理指令
数据处理指令又可细分为移动指令、算术指令、逻辑运算指令、比较运算指令和乘法指令。
1.1 移动指令
最简单的ARM指令,如:
mov r9, sp
mov r1 #0
1.2 移位器
共有5中移位操作:逻辑左移LSL、逻辑右移LSR、算术右移ASR、循环右移ROR和扩展的循环右移RRX。
如:r1 = r1<<9
mov r1, r1, LSL #9
1.3 算术指令
32位有符号数和无符号数的加减法,常用算术指令有:
ADC | 带进位的加法 | Rd = Rn + N + carry |
---|---|---|
ADD | 加法 | Rd = Rn + N |
RSB | 反向减法 | Rd = N - Rn |
RSC | 带借位的反向减法 | Rd = N - Rn - !(carry flag) |
SBC | 带借位的减法 | Rd = Rn - N - !(carry flag) |
SUB | 减法 | Rd = Rn - N |
如:S_FRAME_SIZE是72,r0 = sp + 72
r0 = r1 + (r1<<2)
add r0, sp, #S_FRAME_SIZE
add r0, r1, r1, LSL #2
1.4 逻辑运算指令
AND | 与 | Rd = Rn & N |
---|---|---|
ORR | 或 | Rd = Rn | N |
EOR | 异或 | Rd = Rn ^ N |
BIC | 清位 | Rd = Rn & ~N |
如:
and r1, r0, #0x1f
1.5 比较指令
常见比较指令有:
CMN | 否定比较 | Rn + N |
---|---|---|
CMP | 比较 | Rn - N |
TEQ | 测试两个32位数是否相等 | Rn ^ N |
TST | 测试一个32位数的位 | Rn & N |
如:比较r1是否等于0x1a,并设定cpsr中的标志位,这样后面的指令可以根据标志位条件执行。
teq r1, 0x1a
1.6 乘法指令
常见乘法指令有:
MLA | 乘法和累加 | Rd = Rm * Rs + Rn |
---|---|---|
MUL | 乘法 | Rd = Rm * Rs |
如:r3 = r4 * r1 + r3
mla r3, r4, r1, r3
2. 跳转指令
改变程序执行流程或者用于调用子程序,强制程序计数器PC指向一个新地址。常用的跳转指令有:
B | 跳转 |
---|---|
BL | 带返回的跳转 |
BX | 跳转并切换状态 |
BLX | 带返回的跳转并切换状态 |
如:
bl cpu_init_cp15
3. 软中断指令
常用的软中断指令如下:
SWI | 软中断 |
---|
4. 程序状态寄存器指令
常用程序状态寄存器指令如下:
MRS | 复制程序状态寄存器到通用寄存器中 | Rd = psr |
---|---|---|
MSR | 将通用寄存器的值传到程序状态寄存器中 | psr[field] = Rm |
MSR | 将立即数的值传到程序状态寄存器中 | psr[field] = 立即数 |
5. 协处理器指令
协处理器是指令集的扩展,协处理器既可以提供额外的计算能力,也用于包括Cache和存储管理在内的存储系统。协处理器指令包括数据处理、寄存器传输和内存传输指令。
常用协处理器指令如下:
CDP | 协处理器数据传输——在协处理器中执行一个操作 |
---|---|
MRC|MCR | 协处理器寄存器传输——从协处理器中移出数据或者移入数据 |
LDC STC | 协处理器内存传输——从协处理器中load/store内存 |
5. 伪指令
加载常量的伪指令如下:
LDR | 加载常量的伪指令 | Rd = 32位的常量 |
---|---|---|
ADR | 加载地址的伪指令 | Rd = 32位的相对地址 |
如:
adr lr, here
2. ARM寻址方式
寻址方式是指处理器根据指令中给出的地址信息来寻找物理地址的方式,目前ARM指令系统支持以下几种寻址方式:
2.1 立即寻址
也称为立即数寻址,这种寻址方式指令中就已经给出了操作数。也就是在执行指令的过程中,处理器取得指令的同时也取得了操作数,因此称为立即数寻址。例如:
ADD R0, #1 @R0+1->R0
ADD R0, R0, #0x3F @R0+0x3F->R0
在上面两条指令中,源操作数就是立即数,要求以“#”开始,对于十六进制的立即数,要求在“#”后面加“0x”或“&”。
2.2 寄存器寻址
即将寄存器中的数值作为操作数,是各类微处理器常用的寻址方式,也是效率较高的寻址方式。例如:
ADD R0, R1, R2 @ R1+R2->R0
该指令的执行效果是将R1和R2的值相加,将结果存入R0
2.3 寄存器间接寻址
寄存器间接寻址是以寄存器中的值作为操作数的地址,操作数本身存放在寄存器中。例如:
ADD R0, R1, [R2] @ R1+[R2]->R0
LDR R0, [R1] @ [R1]->R0
第一条指令,以寄存器R2的值作为操作数的地址,在寄存器中取得一个操作数后与R1相加,将结果存入寄存器R0。第二条指令,将寄存器中以R1为地址的值赋给R0。
2.4 基址变址寻址
基址变址寻址是把基址寄存器的内容与指令中给出的地址偏移量相加,从而得到一个操作数的有效地址。该方式常用于访问基地址附近的某些存储单元,一般有以下几种方式:
LDR R0, [R1, #4] @ [R1+4]->R0
第一条指令,将寄存器R1的值加上4作为操作数的有效地址,取得操作数后存入R0中。
LDR R0, [R1, #4]! @ [R1+4]->R0、R1+4->R1
第二条指令,将寄存器R1的值加上4作为操作数的有效地址,取得操作数后存入R0中,然后寄存器R1的值加上4个字节。
LDR R0, [R1], #4 @ [R1]->R0、R1+4->R1
第三条指令,将寄存器R1的值作为操作数的有效地址,取得操作数后存入R0中,然后寄存器R1的值加上4个字节。
LDR R0, [R1, R2] @ [R1+R2]->R0
第四条指令,将寄存器R1和R2的值相加作为操作数的有效地址,取得操作数后存入R0中。
2.5 多寄存器寻址
使用多寄存器寻址,一条指令可以完成多个寄存器值的传送,一条指令最多可以传送16个通用寄存器的值。例如:
LDMIA R0, {R1,R2,R3,R4,} @ [R0]->R1,[R0+4]->R2,[R0+8]->R3,[R0+12]->R4
该指令后缀IA表示每次执行完读取/存储操作后,R0按字长增加,因此,指令可以将连续存储单元的值传送到R1~R4。
2.6 相对寻址
与基址变址寻址类似,相对寻址以程序计数器PC的当前值作为基地址,指令中的地址标号作为偏移量,将两者相加后得到操作数的有效地址。以下程序完成子程序的调用和返回,跳转指令BL采用了相对寻址方式:
BL NEXT @ 跳转到子程序NEXT处执行指令
......
NEXT
......
MV PC, LR @ 从子程序返回
2.7 堆栈寻址
堆栈是一种数据结构,按先进后出的方式工作,使用一个称为堆栈指针的专用寄存器指示当前的操作,堆栈指针总是指向堆栈顶端。当堆栈指针指向最后压入的数据时,称为满堆栈;当堆栈指针指向下一个将要压入的位置时,称为空堆栈。
根据堆栈的生成方式,可分为递增堆栈和递减堆栈。当堆栈由低地址向高地址生成时,称为递增堆栈,反之称为递减堆栈。排列组合后可得到4中类型的堆栈工作方式,ARM微处理器支持全部4种类型的堆栈工作方式。具体如下:
满递增堆栈:堆栈指针指向最后压入的数据,由低地址向高地址生成。
满递减堆栈:堆栈指针指向最后压入的数据,由高地址向低地址生成。
空递增堆栈:堆栈指针指向下一个将要压入数据的空位置,由低地址向高地址生成。
空递减堆栈:堆栈指针指向下一个将要压入数据的空位置,由高地址向低地址生成。