寄存器、内存、和栈
- 寄存器:
CPU 自带的变量。数量有限。速度最快。存放的是全局变量。
如果数量较多,就需要放到内存中。
一些寄存器:
CR0, CR2, CR3 (控制寄存器) (如:CR0的作用是切换实模式和保护模式)
D0, D1, D2, D3, D6和D7 (调试寄存器)(作为调试器的硬件支持来设置条件断点)
TR3, TR4, TR5, TR6 和TR? (测试寄存器)(于某些条件测试)
CS 代码段
DS 数据段
ES 附加段
SS 堆栈段
- 内存:
存放的也是全局变量。
- 栈:
存放局部本地变量。
也是一个内存区域,只有拥有栈的特点: 先进后出。ARM的栈是满递减,栈低内存地址最小。
实用寄存器 详解:
SP Stack Pointer 保存栈的栈底地址,称为栈地址。
实际操作中,栈地址会不断变化,但是一块代码里面 栈地址是不变的。
PC Program Counter 存放下一条指令的地址
一般情况下,处理器执行完一条指令后,将PC加1.顺序执行
LR Link Register 连接寄存器,
一是用来保存子程序返回地址;
二是当异常发生时,LR中保存的值等于异常发生时PC的值减4(或者减2)
因此在各种异常模式下可以根据LR的值返回到异常发生前的相应位置继续执行
PSR Program Status Register 程序状态寄存器。
程序的条件判断准则(flag), 如:结果是否为0 | 负数 | 有进位 | 溢出
指令解读
预备知识: { }表示可选项, [ ]表示间接寻址, #立即数直接寻址,@间接寻址
-
数据操作指令:
基本格式: op { cond }{ s } Rd, Rn, Op2
op 指令, cond条件, s设置flag, op2可以是寄存器、被移位的寄存器或立即数。
(如:ADD R0,R1,#5 ;R0=R1+5 #5为立即数)
cond条件:
EQ 结果为0 EQual to 0
NE 结果不为0 Not Equal to 0
CS 有进位或借位 Carry Set
HS 同CS CS unsigned Higher or Same
CC 没有进位或借位 Carry clear
LO 同CC unsigned LOwer
MI 结果小于0 MInus
PL 结果大于0 PLus
VS 溢出 oVerflow Set
VC 无溢出 oVerflow Clear
HI 无符号比较大于 unsigned HIgher
LS 无符号比较小于等于 unsigned Lower or Same
GE 有符号比较大于等于 signed Greater than or Equal
LT 有符号比较小于 signed Less Than
GT 有符号比较大于 signed Greater Than
LE 有符号比较小于等于 signed Less than or Equal
AL 无条件 Always,默认
s的四种flag:
N negative 负数: 结果小于0 则置1,否则置0;
Z zero 零: 结果是0则置1, 否则置0;
C carry 位移: 产生进位则置1,否则0,减操作则产生借位则置0,否则置1...
V overflow 溢出: 操作有导致溢出,则置1, 否则置0。(有符号数运算结果,无符号数溢出用C)
算术操作:
ADD R0, R1, R2; R0=R1+R2
ADC R0, R1, R2; R0=R1+R2 + C(carry)
SUB R0, R1, R2; R0=R1-R2
SBC R0, R1, R2; R0=R1-R2 - !C
RSB R0, R1, R2; R0=R2-R1
RSC R0, R1, R2; R0=R2-R1 - !C
逻辑操作:
AND R0, R1, R2; R0 = R1 & R2
ORR R0, R1, R2; R0 = R1 | R2
EOR R0, R1, R2; R0 = R1 ^ R2
BIC R0, R1, R2; R0 = R1 &~ R2
MOV R0, R1; R0 = R1
MVN R0, R1; R0= ~ R1
位移操作: (如:ADD R0,R1,R2,LSL#5 ;R0=R1+R2左移5位)
LSL 逻辑左移,
LSR 逻辑右移,
ASR 算术右移,
ROR 循环右移
比较操作:
CMP R1, R2 执行 R1 - R2,并依结果设置flag,
CMN R1, R2 执行 R1 + R2,并依结果设置flag,
TST R1, R2 执行 R1 & R2,并依结果设置flag,
TEQ R1, R2 执行 R1 ^ R2,并依结果设置flag,
乘法操作:
MUL R4, R3, R2; R4 = R3 * R2
MLA R4, R3, R2, R1; R4 = R3 * R2 + R1
-
内存操作指令:
基本格式:
op {cond} {type} Rd, [Rn, Op2]
Rn 是基地址寄存器,用于存放基地址。cond 和上面的一样。
type的四种数据类型 (不指定type,则是默认数据类型word)
B (unsigned Byte) 无符号byte,执行时扩展到32位,以0填充
SB (signed Byte) 有符号byte,仅用于LDR指令,执行时扩展到32位,以符号位填充
H (unsigned Halfword) 无符号halfword,执行时扩展到32位,以0填充
SH (signed Halfword) 有符号halfword,仅用于LDR指令,执行时扩展到32位,以符号位填充
ARM内存操作基础指令只有两个:
LDR: (Load Register)将数据从内存中读出来,存到寄存器中。
LDR Rt, [Rn {, #offset}]; Rt = *(Rn {+offset}), { }代表可选
LDR Rt, [Rn, #offset]!; Rt = *(Rn + offset); Rn = Rn + offset
LDR Rt, [Rn], #offset; Rt = *Rn; Rn = Rn + offset
STR: (Store Register)将数据从寄存器中读出来,存到内存中。
STR Rt, [Rn {, #offset}]; *(Rn {+offset}) = Rt
STR Rt, [Rn, #offset]!; *(Rn {+offset}) = Rt; Rn = Rn + offset
STR Rt, [Rn], #offset; *Rn = Rt; Rn = Rn + offset
其变种:LDRD 和 STRD 还可以操作双字(Doubleword),即一次操作两个寄存器
基本格式:
op {cond} Rt, Rt2, [Rn {, #offset}]
用法和上面的类似:
LDRD R4, R5, [R9, #offset]; R4 = *(R9 + offset); R5 = *(R9 + offset +4)
STRD R4, R5, [R9, #offset]; *(R9 + offset) = R4; *(R9 + offset + 4) = R5
除了基础指令,还有两个块传输指令(一次操作多个寄存器):
基本格式:
op {cond}{mode} Rd {!}, reglist
Rd 是基址寄存器, !指定Rd变化后的值是否写会Rd, reglist是一系列寄存器
(如:{R4-R6, R8} 就表示R4,R5,R6, R8寄存器) "{ }" "-" ","
mode 指定Rd的4种变化规律:
IA increment after, 每次传输后增加Rd的值;
IB increment before, 每次传输前增加Rd的值;
DA decrement after, 每次传输后减少Rd的值;
DB decrement before, 每次传输前减少Rd的值。
LDM Load multiple Rd开始连续的内存数据存入reglist中。
STM Store multiple 吧reglist中的值存入 从Rd开始的连续地址中。
LDMIA R0, {R4 - R6}; 表示吧R0开始的内存里面的值 依次存入寄存器中。
可以发现 LDM、STM 和 LDR、STR 的格式是相反的。
LDM 和 STM 是: op 内存地址 寄存器
LDR 和 STR 是: op 寄存器 内存地址
-
分支指令:
分为无条件分支 和 条件分支两种。
无条件分支:
B Label; PC = Label, go to 的意思,直接跳到Label处执行。
BL Label; LR = PC - 4; PC = Label
BLX Label; Label 后面的指令必须为THUMB指令 LR = PC - 4; PC = Label;
BX Rd; PC = Rd 并切换指令集(THUMB 或 ARM)
条件分支:
在该指令前,会有一条数据操作指令来设置flag,分支指令根据flag的值来决定代码走向
CMP R0, 0; (比较指令)如果R0 == 0 则 Z = 1, 否则 Z = 0
BNE Label; (B + NE 如下)Z == 0 则跳到Label处执行
cond flag
EQ Z=1
NE Z=0
CS C=1
HS C=1
CC C=0
LO C=0
MI N=1
PL N=0
VS V=1
VC V=0
HI C=1&Z=0 LS C=0|Z=1 GE N=V
LT N!=V
GT Z=0&N=V LE Z=1|N!=V
-
THUMB指令:
THUMB 指令是ARM指令的子集,均为16位,在16位的数据总线上的传输效率更高,比ARM指令更节省空间。
特点:
1. 除了B指令外,所有的指令均无法条件执行。
2. 桶式位移无法结合其他指令执行。
3. 大多数指令只能使用R0 ~ R7 这八个寄存器。
(例外:ADD、MOV、CMP 可以将 R8~R15 作为操作数使用。
LDR、STR 可以使用 PC 或 SP 寄存器
PUSH 可以使用 LR
POP 可以使用 PC
BX 可以使用所有寄存器)
4. 指令数量减少,如:乘法中只有MUL保留,其他都被精简了。
5. 所有的指令默认附带s,即所有的THUMB指令都会设置flag。
6. 立即数 和 第二操作数使用受限。 除了位移指令、ADD、SUB 其他指令形式都是 "op Rd, Rm"
7. 不支持数据回写,即{!} 不可用了。
后记:
- 传递参数和返回值:
函数的前四个参数 放在 R0 ~ R3 寄存器中,其他参数放在栈中。 返回值放在R0中。 - 特殊用途寄存器:
R0 ~ R3 传递参数与返回值;
R7 帧指针,指向母函数与被调用子函数在栈中的交界;
R9 在iOS 3.0 以前被系统保留;
R12 内部过程调用寄存器,dynamic linker 会用到它;
R13 SP寄存器;
R14 LR寄存器,保存返回值;
R15 PC寄存器