ARM汇编指令的速查表、特点和格式
扫描二维码
随时随地手机看文章
理解汇编指令的特点,对于汇编指令助记符记忆、理解以及使用帮助很大,请重视!
0.1 指令与伪指令
汇编指令-CPU机器指令的助记符,经过编译后会得到一串1和0组成的机器码,可以由CPU读取执行。
汇编伪指令-编译器环境提供的,用来指导编译过程的“指令”,本质上不是指令,仅是与汇编指令写在一起,经过编译后伪指令不会生成机器码。
0.2 两种不同风格的ARM指令
ARM官方风格ARM汇编:指令一般用大写字母、Windows中的IDE开发环境(如ADS、MDK等)常用。如: LDR R0, [R1]
GNU风格ARM汇编:指令一般用小写字母、linux中常用。如:ldr r0, [r1]
0.3 ARM汇编指令的特点
0.3.1 LDR/STR架构
ARM采用RISC架构,CPU不能直接读取内存,需要先将内存内容加载到通用寄存器中才能被CPU处理。
ldr(load register)指令--将内存内容加载到通用寄存器。
str(store register)指令--将通用寄存器内容存入到内存中。
ldr/str指令组合可以实现 ARM CPU和内存数据的**
0.3.2 九种寻址方式
具体可见ARM的寻址方式http://www.eefocus.com/hfxin2001/blog/16-03/377628_27b21.html
立即寻址 mov r0, #0xFF00
寄存器寻址 mov r1, r2
寄存器移位寻址 mov r0, r1, lsl #3
寄存器间接寻址 ldr r1, [r2]
基址变址寻址 ldr r1, [r2, #4]
多寄存器寻址 ldmia r1!, {r2-r7, r12}
堆栈寻址 stmfd sp!, {r0-r7, lr}
块拷贝寻址 stmia r0!,{r1-r7}
相对寻址 beq flag
flag:
0.3.3 指令后缀
同一指令经常附带不同后缀,变成不同的指令。常用后缀有
B(byte)---功能不变,操作长度变为8位;
H(half word)---功能不变,长度变为16位;
S(signed)---功能不变,操作数变为有符号,如 ldr ldrb ldrh ldrsb ldrsh;
S(S标志)---功能不变,影响CPSR标志位,如 mov和movs
mov r0, #0 //将立即数0存入到寄存器R0中,CPSR的Z标志位不置1
movs r0, #0 //将立即数0存入到寄存器R0中,CPSR的Z标志位置1
任何一个ARM数据处理指令可以选择是否根据操作的结果来更新CPSR寄存器中的ALU状态标志位。在数据处理指令中使用S后缀来实现该功能。不要在CMP,CMN,TST或TEQ指令中使用S后缀,这些比较指令总会更新标志位。
0.3.4 指令条件码
在ARM模式下,几乎所有的ARM指令都可以根据CPSR中的ALU状态标志位来条件执行,通过指令条件码(见1.5中表),你可以:
A)根据数据操作的结果更新CPSR中的ALU状态标志;
B)执行其他几种操作,但不更新状态标志;
C)根据当前状态标志,决定是否执行接下来的指令。
0.3.5 多级指令流水线
为增加处理器指令流的速度,ARM使用多级流水线,如S5PV210使用13级流水线,ARM11为8级
1 ARM指令的格式
1.1 基本格式
基本格式为:
<>是必选项,{}可选项,如
opcode 指令助记符,如 LDR,STR 等
cond 执行条件码,如 EQ,NE 等
S 是否影响 CPSR 寄存器的值,有S时影响 CPSR,否则不影响
Rd 目标寄存器
Rn 第一个操作数的寄存器
shifter_operand 第二个操作数
例:
LDR R0,[R1] ;将地址=R1中数据的内存单元内容存入到R0中,执行条件 AL
BEQ DATAEVEN ;跳转指令,执行条件 EQ,即若相等,则跳转到 DATAEVEN
ADDS R1,R1,#1 ;加法指令,(R1+1)->R1,带有 S ,影响 CPSR 寄存器
SUBNES R1,R1,#0xD;条件执行减法运算(NE),(R1-0xD)->R1,影响 CPSR 寄存器
ARM指令的编码格式见下表
31-28
27-25
24-21
20
19-16
15-12
11-0
cond
001
opcode
S
Rn
Rd
shifter_operand
1.2 第二个操作数
灵活使用第二个操作数可提高ARM指令的代码效率,当第二个操作数为常数时(#immed_8r),该常数必须对应8位位图,即由一个8位的常数循环移位偶数位得到的。
深层含义为:ARM在处理#immed_8r时将其视为32位数,它必须由一个8位数通过循环移位偶数位得到,如
合法常数0x80000016(1000 0000 0000 0000 0000 0000 0001 0110)可由0x5A(0101 1010)通过循环右移2位得到,这是合法的。
非法常数0xA0000016(1010 0000 0000 0000 0000 0000 0001 0110),虽可由0x(1011 0101)循环右移3位得到,但循环移位不是偶数,故是非法的。
非法常数0xB0000016(1011 0000 0000 0000 0000 0000 0001 0110),虽可由1 0110 1011 右移4位得到,但有1 0110 1011 是9位数,故也是非法的。
这样规定的原因要从指令编码格式中shifter_operand所占的位数--12位来分析,用一个12位的编码来表示任意的32位数是绝对不可能的(12位数有2^12种可能,而32位数有2^32种)。
但又要用12位编码来表示32位数,怎么办?只有在表示数的数量上做限制。通过编码来实现用12位编码来表示32位数。在12位的shifter_operand中,8位存数据,4位存移位的次数。
8位存数据:解释了“该常数必须对应8位位图”。
4位存移位的次数:解释了为什么只能移偶数位。4位只有16种可能值,而32位数可以循环移位32次(32种可能),那就只好限制:只能移偶数位(两位两位地移,好像一个16位数在移位,16种移位可能)。这样就解决了能表示的情况是实际情况一半的矛盾。
所以对#immed_8r常数表达式的限制是解决指令编码的第二个操作数位数不足以表示32位操作数的无奈之举。
但在我看来,这个是聪明的做法。因为如果直接用12位数来表示32位操作数,只能表示0 到(2^12-1),大于(2^12-1)的数就没办法表示了。而细细想来“8位存数据,4位存移位的次数”,应该是最好的组合了。
1.3 寄存器Rm (Rd、Rn)
在寄存器寻址方式下,Rm表示寄存器的值,如:
SUB R1,R1,R2 ;R1-R2->R1
MOV PC,R0 ;PC=R0,程序跳转到指定地址
LDR R0,[R1],-R2 ;读取 R1 地址上的存储器单元内容并存入 R0,且 R1=R1-R2
1.4 寄存器移位方式Rm,shift
将寄存器的移位结果作为操作数,但 Rm 值保存不变,如:
ASR #n 算术右移 n 位(1≤n≤32)
LSL #n 逻辑左移 n 位(1≤n≤31)
LSR #n 逻辑左移 n 位(1≤n≤32)
ROR #n 循环右移 n 位(1≤n≤31)
RRX 带扩展的循环右移 1 位
type Rs 其中,type 为 ASR,LSL,和 ROR 中的一种;Rs 偏移量寄存器,低 8 位有效,若其值大于或等于 32,则第 2 个操作数的结果为 0(ASR、ROR 例外)。
例:
ADD R1,R1,R1,LSL #3 ;R1=R1+R1*8
SUB R1,R1,R2,LSR #2 ;R1=R1-R2*4
R15 为处理器的程序计数器 PC,一般不要对其进行操作,而且有些指令是不允许使用 R15,如 UMULL 指令。
1.5 指令条件码
使用指令条件码,可实现高效的逻辑操作,提高代码效率。
序号
条件码助记符
标志
含义
1
EQ
Z=1
相等
2
NE
Z=0
不相等
3
CS/HS
C=1
无符号数大于或等于
4
CC/LO
C=0
无符号数小于
5
MI
N=1
负数
6
PL
N=0
正数或零
7
VS
V=1
溢出
8
VC
V=0
没有溢出
9
HI
C=1,Z=0
无符号数大于
10
LS
C=0,Z=1
无符号数小于或等于
11
GE
N=V
带符号数大于或等于
12
LT
N!=V
带符号数小于
13
GT
Z=0,N=V
带符号数大于
14
LE
Z=1,N!=V
带符号数小于或等于
15
AL
任何
无条件执行(指令默认条件)
例1:
比较两个值大小,并进行相应加 1 处理,C 代码为
if(a>b)a++;
else b++;
对应的 ARM 指令为:其 R0 为 a,R1 为 b。
CMP R0,R1 ;R0 与 R1 比较
ADDHI R0,R0,#1 ;若 R0>R1,则 R0=R0+1
ADDLS R1,R1,#1 ;若 R0<=R1,则 R1=R1+1
例2:
若两个条件均成立,则将这两个数值相加,C 代码为
If((a!=10)&&(b!=20)) a=a+b;
对应的 ARM 指令如下.其中 R0 为 a,R1 为 b.
CMP R0,#10 ;比较 R0 是否为 10
CMPNE R1,#20 ;若 R0 不为 10,则比较 R1 是否 20
ADDNE R0,R0,R1 ;若 R0 不为 10 且 R1 不为 20,指令执行,R0=R0+R1