1 寄存器 内存 栈
在高级语言里面,操作对象是变量,在ARM汇编里面,操作对象是寄存器(register),内存和栈(stack)
2 数据操作指令
1 | op {cond}{s} Rd, Rn, Op2 |
其中,cond和s是两个可选后缀,cond用来指定指令op在什么条件下执行,共有下面17种条件。
指令 | 说明 |
---|---|
EQ | 结果为0, Equal to 0 |
NE | 结果不为0,Not Equal to 0 |
CS | 有进位或者借位 Carry Set |
HS | 同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 equal |
AL | 无条件(Always ,默认) |
例如:
比较 R0,R1
移动 GE R2, R1
移动 LT R2, R0
比较R0和R1的值,如果R0大于R1,那么R2等于R1,如果R0小于R1,那么R2等于R0。
s的作用是指定指令op是否设置flag,共有下面四种flag。
符号 | 说明 |
---|---|
N(Negative) | 如果结果小于0则置1,否则置0 |
Z (Zero) | 如果结果是0则置1,否则置0 |
C (Carry) | 对于加操作来说,如果产生进位则置1,否则置0,对于减操作来说,如果产生借位则置0,否则置1,对于有位移的非加/减操作来说,C置移出值的最后一位,对于其他的非加/减操作来说,C的值一般不变 |
V(Overflos) | 如果操作溢出则置1,否则置0 |
需要注意的是,Cflag表示的是无符号位运算结果是否溢出,Vflag表示的是有符号位数运算结果是否溢出。
算术操作
ADD R0, R1, R2 ; R0 = R1 + R2
ADC R0, R1, R2 ; R0 = R1 + R2 + C(arry)
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, R2 ; R0 = R2 //赋值
MVN R0, R2 ; R0 = ~R2 //按位取反
比较操作
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
乘法操作的操作数必须来自寄存器
###3 内存操作指令
内存操作指令的基本格式是:1
op{cond}{type} Rn, [Rn,?Op2]
其中Rn是基址寄存器,用户存放基地址
cond的作用于数据操作指令相同,都是用来指定op是在什么条件下执行
type指定指令op操作的数据类型,共有四种
指令 | 说明 |
---|---|
B(unsigned Byte) | 无符号byte,执行时扩展到32bit,以0填充 |
SB(Signed Byte) | 有符号byte,仅用于LDR指令,执行时扩展到32bit,以符号位填充 |
H (signed Halfword) | 无符号halfword,执行时扩展到32bitj,以0填充 |
SH (signed halfword) | 有符号halfword 仅用于LDR指令,执行时扩展到32bit,以符号位填充 |
如果不指定byte,则默认的数据类型是word
ARM内存操作基础指令只有两个: LDR (LoaD Register)将数据从内存中读出来,存到寄存器中。以及STR(STore Register)将寄存器的数据读出来,存到内存中。两个指令的使用情况如下:
LDR
-
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
-
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
此外, LDR和STR的变种LDRD和STRD还可以操
作双字( Doubleword),即一次性操作2个寄存器,
其基本格式如下:
op{cond} Rt, Rt2, [Rn {, #offset}]
用法及原型类似,如下:
STRD R4, R5, [R9,#offset] ; *(R9+offset) = R4 , *(R9+offset+R4) = R5
LDRD R4, R5, [R9,#offset] ; R4 = *(R9 + offset);
R5 = *(R9 + offset + 4)
除了LDR和STR外,还可以通过LDM( LoaD
Multiple)和STM( STore Multiple)进行块传输,一
次性操作多个寄存器。块传输指令的基本格式是:
1 | op{cond}{mode} Rd{!}, reglist |
其中Rd是基址寄存器,可选的“!”指定Rd变化后
的值是否写回Rd; reglist是一系列寄存器,用大括号
括起来,它们之间可以用“,”分隔,也可以用“-”表示
一个范围,比如, {R4–R6,R8}表示寄存器R4、 R5、
R6、 R8;这些寄存器的顺序是按照自身的编号由小
到大排列的,与大括号内的排列顺序无关。
需要特别注意的是, LDM和STM的操作方向与
LDR和STR完全相反: LDM是把从Rd开始,地址连续
的内存数据存入reglist中, STM是把reglist中的值存入
从Rd开始,地址连续的内存中。
“cond”的作用与数据操作指令相同。 “mode”指定
Rd值的4种变化规律,如下所示:
IA( Increment After)
每次传输后增加Rd的值;
IB( Increment Before)
每次传输前增加Rd的值;
DA( Decrement After)
每次传输后减少Rd的值;
DB( Decrement Before)
每次传输前减少Rd的值
示例: 假设R0的值为5
执行以下命令后, R4、 R5、 R6的值分别变成:
foo():
LDMIA R0, {R4 – R6} ; R4 = 5, R5 = 6,
R6 = 7
LDMIB R0, {R4 – R6} ; R4 = 6, R5 = 7,
R6 = 8
LDMDA R0, {R4 – R6} ; R4 = 5, R5 = 4,
R6 = 3
LDMDB R0, {R4 – R6} ; R4 = 4, R5 = 3,
R6 = 2
4 分支指令
分支指令可以分为无条件分支和条件分支两种。
. 无条件分支
foo():
B Label ; 跳转到Label处往下执行
…… ; 得不到执行
Label:
……
. 条件分支
条件分支的cond是依照6.2.1节提到的4种flag来判
断的,它们的对应关系如下:
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
在条件分支指令前会有一条数据操作指令来设置
flag,分支指令根据flag的值来决定代码走向,举例如
下:
Label:
LDR R0, [R1], #4
CMP R0, 0 ; 如果R0 == 0, Z = 1;否则Z = 0
BNE Label ; Z == 0则跳转