0x01 ARM寄存器
1.1 通用寄存器
1.未分组寄存器:R0~R7
2.分组寄存器:R8~812
R13:SP,常用作堆栈指针,始终指向堆栈的顶部,当一个数据(32位)推入堆栈时,SP(R13的值减4)向下浮动指向下一个地址,即新的栈顶,当数据从堆栈中弹出时,SP(R13的值加4)向上浮动指向新的栈顶。
R14:连接寄存器(LR),当执行BL子程序调用指令时,R14中得到R15(程序计数器PC)的备份,其他情况下,R14用作通用寄存器。
1.2 状态寄存器
CPSR(R16):当前程序状态寄存器,用来保存ALU中的当前操作信息,控制允许和禁止中断、设置处理器的工作模式等。
SPSRs:五个备份的程序状态寄存器,用来进行异常处理。当异常发生时,SPSR用于保存CPSR的当前值,从异常退出时可由SPSR来恢复CPSR。
N、Z、C、V均为条件码标志位,他们的内容可被运算的结果所改变。
N:正负标志,N=1表示运算的结果为负(最高位为1),N=0表示运算的结果为正或0(最高位为0)
Z:零标志,Z=1表示运算的结果为0,Z=0表示运算的结果为非0
C:进位标志,产生了进位时则C=1,否则C=0
V:溢出标志,V=1表示有溢出,V=0表示无溢出
1.3 那么如何判断产生进位和溢出呢?
溢出与进位是针对加法而言
溢出
负数用补码表示,只有相加操作,x,y指的是相加操作的两个数
设:
x 为第一个加数的符号位
y 为第二个加数的符号位
r 为结果的符号位
那么:
xy = 00 r=1,溢出,r=0,未溢出
xy = 11 r=0,溢出,r=1,未溢出
xy = 10 不会溢出
XY = 01 不会溢出
进位
负数用补码表示,只有相加操作,x,y指的是相加操作的两个数
设:
x 为第一个加数的符号位(准确点,这时应叫做最高位)
y 为第二个加数的符号位
r 为结果的符号位
那么:
xy = 00 决不会产生进位
xy = 11 一定会进位
xy = 10 r = 0 ,有进位,r = 1,无进位
XY = 01 r = 0 ,有进位,r = 1,无进位
1.4 地址空间
程序正常执行时,每执行一条ARM指令,当前指令计数器增加4个字节。当前的PC执行PC + 2 * 4 = PC + 8的地址。
0x02 ARM汇编语言
2.1 汇编指令格式
格式中<>的内容必不可少,{}中的内容可省略
{
{S}:决定指令的执行结果是否影响CPSR的值,使用该后缀则指令执行的结果影响CPSR的值,否则不影响
例子:ADDEQS R0,R1,#8;其中操作码为ADD,条件域cond为EQ,S表示该指令的执行影响CPSR寄存器的值,目的寄存器Rd为R0,第一个操作数寄存器Rd为R1,第二个操作数OP2为立即数#8。
2.2 指令的条件执行
指令的条件后缀只是影响指令是否执行,不影响指令的内容。
条件码 | 助记符后缀 | 标志 | 含义 |
---|---|---|---|
0000 | EQ | Z置位 | 相等 |
0001 | NE | Z清零 | 不相等 |
0010 | CS | C指令 | 无符号数大于或等于 |
0011 | CC | C清零 | 无符号数小于 |
0100 | MI | N置位 | 负数 |
0101 | PL | N清零 | 正数或零 |
0110 | VS | V置位 | 溢出 |
0111 | VC | V清零 | 未溢出 |
1000 | HI | C置位Z清零 | 无符号数大于 |
1001 | LS | C清零Z置位 | 无符号数小于或等于 |
1010 | GE | N等于V | 带符号数大于或等于 |
1011 | LT | N不等于V | 带符号数小于 |
1100 | GT | Z清零且(N等于V) | 带符号数大于 |
1101 | LE | Z置位或(N不等于V) | 带符号数小于或等于 |
1110 | AL | 忽略 | 无条件执行 |
减法会被转化为加法来进行运算:
x-y => x+(-y) => x+~y+1
5-3 => 5+(-3) => 0000 0101 + 1111 1100 + 1 = 0000 0010
0000 0101:5 的补码
1111 1100:-3的反码,加1后变成 -3 的补码
1111 1101:在计算机里,-3 就是这样表示的
其实1111 1101 即可以表示负数-3,也可以正数253,那么到底是表示哪个数字呢?这就要看比较的时候,当做有符号比较,还是无符号比较。
有符号比较:
char a = -3; char b = 3; if(a > b),这种情况是有符号比较,在上面列表中,可以看到条件,Z清零且(N等于V)。
无符号比较:
unsighed char a = 253; char b = 3; if(a > b),这种情况是无符号比较,在上面的列表中,可以看到条件,C置位Z清零。
我们可以看到,无论是-3,还是253,他们在内存中形式都是1111 1101,只是在比较时,一个是GT(带符号数大于),一个是HI(无符号大于)。
得出一个重要结论,因为同样一个二进制1111 1101,即可以代表有符号数-3,无符号数253,所以C语言的有符号数和无符号数,在汇编层次上的区别比较时是GT还是HI。
那么又出现一个问题,char a = -3; char b = 3; a - b = -6; 而unsighed char a = 253; char b = 3; a - b = 253 - 3 = 250;
在汇编层次上都是1111 1101 + 1111 1101 = 1111 1010,答案是C语言的printf,%d还是%u,%d表示输出有符号,C语言会解析1111 1010为有符号数,得到-3;%u表示输出无符号,C语言会解析1111 1010为无符号数,得到253;
2.3 ARM指令分类
2.3.1 ARM 数据处理指令
1、数据传送指令
MOV,MVN
2、算术逻辑运算指令
ADD,SUB,ADC,SBC,AND,ORR,EOR,BIC
3、比较指令
CMP,CMN,TST,TEQ
4、乘法指令
MUL
2.3.2 跳转指令
B,BX,BL
2.3.3 ARM 存储器访问指令
LDR 和STR,LDM 和STM
2.3.4 ARM伪指令
ADR,LDR
2.4 ARM寻址方式
寻址方式就是根据指令中操作数的信息来寻找操作数实际物理地址的方式
2.4.1 立即数寻址
MOV R0,#15 #15就是立即数
2.4.2 寄存器寻址
ADD R0, R1, R2 将R1和R2的内容相加,其结果存放在寄存器R0中
2.4.3 寄存器间接寻址
LDR R0, [R4] 以寄存器R4的值作为操作数的地址,在存储器中取得一个操作数存入寄存器R0中
2.4.4 寄存器移位寻址
ADD R0,R1,R2,LSL #1 将R2的值左移一位,所得值与R1相加,存放到R0中
MOV R0,R1,LSL R3 将R1的值左移R3位,然后将结果存放到R0中
2.4.5 基址变址寻址
LDR R0,[R1,#4] 将R1的值加4作为操作数的地址,在存储器中取得操作数放入R0中
LDR R0,[R1,#4]! 将R1的值加4作为操作数的地址,在存储器中取得操作数放入R0中,然后R1 = R1+4
LDR R0,[R1],#4 R0 = [R1],R1 = R1 +4
LDR R0,[R1,R2] R0 = [R1+R2]
2.4.6 多寄存器寻址
一条指令可以完成多个寄存器值的传送(最多可传送16个通用寄存器),连续的寄存器用“-”,否则用“,”
LDMIA R0!,{R1 - R4} R1 = [R0],R2=[R0+4],R3=[R0+8],R4=[R0+12]
后缀IA表示在每次执行玩加载/存储操作后,R0按自长度增加。
2.4.7 相对寻址
以程序计数器PC的当前值为基地址,指令中的地址标号作为偏移量,将两者相加之后得到操作数的有效地址,如下图的BL分支跳转
BL proc 跳转到子程序proc处执行
...
proc MOV R0,#1
...
2.4.8 堆栈寻址
按先进先出的方式工作,堆栈指针用R13表示,总是指向栈顶,LDMFD和STMFD分别表示POP出栈和PUSH进栈
STMFD R13!,{R0 - R4};
LDMFD R13!,{R0 - R4};
0x03 ARM指令集
3.1 ARM指令分类
3.1.1 ARM 数据处理指令
1、数据传送指令
MOV
数据传送指令。将8 位图立即数或寄存器(operant2)传送到目标寄存器Rd,可用于移位运算等操作。指令格式如下:
MOV{cond}{S} Rd,operand2
MOV 指令举例如下:
MOV R1#0x10 ;R1=0x10
MOV R0,R1 ;R0=R1
MOVS R3,R1,LSL #2 ;R3=R1<<2,并影响标志位
MOV PC,LR ;PC=LR ,子程序返回
MVN
数据非传送指令。将8 位图立即数或寄存器(operand2)按位取反后传送到目标寄存器(Rd),因为其具有取反功能,所以可以装载范围更广的立即数。指令格式如下:
MVN{cond}{S} Rd,operand2
MVN 指令举例如下:
MVN R1,#0xFF ;R1=0xFFFFFF00
MVN R1,R2 ;将R2 取反,结果存到R1
2、算术逻辑运算指令
ADD
加法运算指令。将operand2 数据与Rn 的值相加,结果保存到Rd 寄存器。指令格式如下:
ADD{cond}{S} Rd,Rn,operand2
ADD 指令举例如下:
ADDS R1,R1,#1 ;R1=R1+1
ADD R1,R1,R2 ;R1=R1+R2
ADDS R3,R1,R2,LSL #2 ;R3=R1+R2<<2
SUB
减法运算指令。用寄存器Rn 减去operand2。结果保存到Rd 中。指令格式如下:
SUB{cond}{S} Rd,Rn,operand2
SUB 指令举例如下:
SUBS R0,R0,#1 ;R0=R0-1
SUBS R2,R1,R2 ;R2=R1-R2
SUB R6,R7,#0x10 ;R6=R7-0x10
ADC
带进位加法指令。将operand2 的数据与Rn 的值相加,再加上CPSR 中的C 条件标志位。结果保存到Rd 寄存器。指令格式如下:
ADC{cond}{S} Rd,Rn,operand2
ADC 指令举例如下:
ADDS R0,R0,R2
ADC R1,R1,R3 ;使用ADC 实现64 位加法,(R1、R0)=(R1、R0)+(R3、R2)
SBC
带进位减法指令。用寄存器Rn 减去operand2,再减去CPSR 中的C 条件标志位的非(即若C 标志清零,则结果减去1),结果保存到Rd 中。指令格式如下:
SBC{cond}{S}Rd,Rn,operand2
SBC 指令举例如下:
SUBS R0,R0,R2
SBC R1,R1,R3 ;使用SBC 实现64 位减法,(R1,R0)-(R3,R2)
AND
逻辑与操作指令。将operand2 值与寄存器Rn 的值按位作逻辑与操作,结果保存到Rd 中。指令格式如下:
AND{cond}{S} Rd,Rn,operand2
AND 指令举例如下:
ANDS R0,R0,#x01 ;R0=R0&0x01,取出最低位数据
AND R2,R1,R3 ;R2=R1&R3
ORR
逻辑或操作指令。将operand2 的值与寄存器Rn 的值按位作逻辑或操作,结果保存到Rd 中。指令格式如下:
ORR{cond}{S} Rd,Rn,operand2
ORR 指令举例如下:
ORR R0,R0,#x0F ;将R0 的低4 位置1
MOV R1,R2,LSR #4
ORR R3,R1,R3,LSL #8 ;使用ORR 指令将近R2 的高8 位数据移入到R3 低8 位中
EOR
逻辑异或操作指令。将operand2 的值与寄存器Rn 的值按位作逻辑异或操作,结果保存到Rd 中。指令格式如下:
EOR{cond}{S}Rd,Rn,operand2
EOR 指令举例如下:
EOR R1,R1,#0x0F ;将R1 的低4 位取反
EOR R2,R1,R0 ;R2=R1^R0
EORS R0,R5,#0x01 ;将R5 和0x01 进行逻辑异或,结果保存到R0,并影响标志位
BIC
位清除指令。将寄存器Rn 的值与operand2 的值的反码按位作逻辑与操作,结果保存到Rd 中。指令格式如下:
BIC{cond}{S}Rd,Rn,operand2
BIC 指令举例如下:
BIC R1,R1,#0x0F ;将R1 的低4 位清零,其它位不变
BIC R1,R2,R3 ;将拭的反码和R2 相逻辑与,结果保存到R1
3、比较指令
CMP
比较指令。指令使用寄存器Rn 的值减去operand2 的值,根据操作的结果更新CPSR中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。指令格式如下:
CMP{cond} Rn,operand2
CMP 指令举例如下:
CMP R1,#10 ;R1 与10 比较,设置相关标志位
CMP R1,R2 ;R1 与R2 比较,设置相关标志位
CMP 指令与SUBS 指令的区别在于CMP 指令不保存运算结果。在进行两个数据大小判断时,常用CMP 指令及相应的条件码来操作。
CMN
负数比较指令。指令使用寄存器Rn 与值加上operand2 的值,根据操作的结果更新CPSR 中的相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行,指令格式如下:
CMN{cond} Rn,operand2
CMN R0,#1 ;R0+1,判断R0 是否为1 的补码,若是Z 置位
CMN 指令与ADDS 指令的区别在于CMN 指令不保存运算结果。CMN 指令可用于负数比较,比如CMNR0,#1 指令则表示R0 与-1 比较,若R0 为-(即1 的补码),则Z 置位,否则Z复位。
TST
位测试指令。指令将寄存器Rn 的值与operand2 的值按位作逻辑与操作,根据操作的结果更新CPSR 中相应的条件标志位,以便后面指令根据相应的条件标志来判断是否执行。指令格式如下:
TST{cond} Rn,operand2
TST 指令举例如下:
TST R0,#0x01 ;判断R0 的最低位是否为0
TST R1,#0x0F ;判断R1 的低4 位是否为0
TST 指令与ANDS 指令的区别在于TST4 指令不保存运算结果。TST 指令通常于EQ、NE条件码配合使用,当所有测试位均为0 时,EQ 有效,而只要有一个测试为不为0,则NE 有效。
TEQ
相等测试指令。指令寄存器Rn 的值与operand2 的值按位作逻辑异或操作,根据操作的结果更新CPSR 中相应条件标志位,以便后面的指令根据相应的条件标志来判断是否执行。指令格式如下:
TEQ{cond} Rn,operand2
TEQ 指令举例如下:
TEQ R0,R1 ;比较R0 与R1 是否相等(不影响V 位和C 位)
TST 指令与EORS 指令的区别在于TST 指令不保存运算结果。使用TEQ 进行相等测试,常与EQNE 条件码配合使用,当两个数据相等时,EQ 有效,否则NE 有效。
4、乘法指令
MUL
32 位乘法指令。指令将Rm 和Rs 中的值相乘,结果的低32 位保存到Rd 中。指令格式如下:
MUL{cond}{S} Rd,Rm,Rs
MUL 指令举例如下:
MUL R1,R2,R3 ;R1=R2×R3
MULS R0,R3,R7 ;R0=R3×R7,同时设置CPSR 中的N 位和Z 位
3.1.2 跳转指令
B
跳转指令。跳转到指定的地址执行程序。指令格式如下:
B{cond} label
跳转指令B 举例如下:
B WAITA ;跳转到WAITA 标号处
B 0x1234 ;跳转到绝对地址0x1234 处
跳转到指令B 限制在当前指令的±32Mb 的范围内。
BL
带链接的跳转指令。指令将下一条指令的地址拷贝到R14(即LR)链接寄存器中,然后跳转到指定地址运行程序。指令格式如下:
BL{cond} label
带链接的跳转指令BL 举例如下:
BL DELAY
跳转指令B 限制在当前指令的±32MB 的范围内。BL 指令用于子程序调用。
BX
带状态切换的跳转指令。跳转到Rm 指定的地址执行程序,若Rm 的位[0]为1,则跳转时自动将CPSR 中的标志T 置位,即把目标地址的代码解释为Thumb 代码;若Rm 的位[0]为0,则跳转时自动将CPSR 中的标志T 复位,即把目标地址的代码解释为ARM 代码。指令格式如下:
BX{cond} Rm
带状态切换的跳转指令BX 举例如下:
ADRL R0,ThumbFun+1
BX R0 ;跳转到R0 指定的地址,并根据R0 的最低位来切换处理器状态
3.1.3 ARM 存储器访问指令
LDR指令
LDR指令的格式为:
LDR{条件} 目的寄存器,<存储器地址>
LDR指令用于从存储器中将一个32位的字数据传送到目的寄存器中。该指令通常用于从存储器中读取32位的字数据到通用寄存器,然后对数据进行处理。当程序计数器PC作为 目的寄存器时,指令从存储器中读取的字数据被当作 目的地址,从而可以实现程序流程的跳转。该指令在程序设计 中比较常用,且寻址方式灵活多样,请读者认真掌握。
指令示例:
LDR R0,[R1] ;将存储器地址为R1的字数据读入寄存器R0。
LDR R0,[R1,R2] ;将存储器地址为R1+R2的字数据读入寄存器R0。
LDR R0,[R1,#8] ;将存储器地址为R1+8的字数据读入寄存器R0。
LDR R0,[R1,R2] ! ;将存储器地址为R1+R2的字数据读入寄存器R0,并将新地址R1+R2写入R1。
LDR R0,[R1,#8] ! ;将存储器地址为R1+8的字数据读入寄存器R0,并将新地址 R1+8写入R1。
LDR R0,[R1],R2 ;将存储器地址为R1的字数据读入寄存器R0,并将新地址 R1+ R2写入R1。
LDR R0,[R1,R2,LSL#2]! ;将存储器地址为R1+R2×4的字数据读入寄存器R0,并将新地址R1+R2×4写入R1。
LDR R0,[R1],R2,LSL#2 ;将存储器地址为R1的字数据读入 寄存器R0,并将新地址R1+R2×4写入R1。
LDRB指令
LDRB指令的格式为:
LDR{条件}B 目的寄存器,<存储器地址>
LDRB指令用于从存储器中将一个8位的字节数据传送到目的寄存器中,同时将寄存器的高24位清零。 该指令通常用于从存储器中读取8位的字节数据到通用寄存器,然后对数据进行处理。当程序计数器PC作为目的寄存器时,指令从存储器中读取的字数据被当作目 的地址,从而可以实现程序流程的跳转。
指令示例:
LDRB R0,[R1] ;将存储器地址为R1的字节数据读入寄存器 R0,并将R0的高24 位清零。
LDRB R0,[R1,#8] ;将存储器地址为R1+8的字节数据读入寄存器R0,并将 R0的高24位清零。
LDRH指令
LDRH指令的格式为:
LDR{条件}H 目的寄存器,<存储器地址>
LDRH指令用于从存储器中将一个16位的半字数据传送到目的寄存器中,同时将寄存器的高16位清零。 该指令通常用于从存储器中读取16位的半字数据到通用寄存器,然后对数据进行处理。当程序计数器PC作为目的寄存器时,指令从存储器中读取的字数据被当作 目的地址,从而可以实现程序流程的跳转。
指令示例:
LDRH R0,[R1] ;将存储器地址为R1的半字数据读入寄存器 R0,并将R0的高16位清零。
LDRH R0,[R1,#8] ;将存储器地址为R1+8的半字数据读入寄存器R0,并将R0 的高16位清零。
LDRH R0,[R1,R2] ;将存储器地址为R1+R2的半字数据读入寄存器R0,并将 R0的高16位清零。
STR指令
STR指令的格式为:
STR{条件} 源寄存器,<存储器地址>
STR指令用于从源寄存器中将一个32位的字数据传送到存储器中。 该指令在程序设计中比较常用,且寻址方式灵活多样,使用方式可参考指令LDR。
指令示例:
STR R0,[R1],#8 ;将R0中的字数据写入以R1为地址的存储器中,并将新地址R1+8写入R1。
STR R0,[R1,#8] ;将R0中的字数据写入以R1+8为地址的存储器中。
STRB指令
STRB指令的格式为:
STR{条件}B 源寄存器,<存储器地址>
STRB指令用于从源寄存器中将一个8位的字节数据传送到存储器中。该字节数据为源寄存器中的低8位。
指令示例:
STRB R0,[R1] ;将寄存器R0中的字节数据写入以R1为地址的存储器中。
STRB R0,[R1,#8] ;将寄存器R0中的字节数据写入以R1+8为地址的存 储器中。
STRH指令
STRH指令的格式为:
STR{条件}H 源寄存器,<存储器地址>
STRH指令用于从源寄存器中将一个16位的半字数据传送到存储器中。该半字数据为源寄存器中的低16位。
指令示例:
STRH R0,[R1] ;将寄存器R0中的半字数据写入以R1为地址的 存储器中。
STRH R0,[R1,#8] ;将寄存器R0中的半字数据写入以R1+8 为地址的存储器中。
LDM 和STM
批量加载/存储指令可以实现在一组寄存器和一块连续的内存单元之间传输数据。LDM 为加载多个寄存器,STM 为存储多个寄存器。允许一条指令传送16 个寄存器的任何子集或所有寄存器。指令格式如下:
LDM{cond}<模式> Rn{!},reglist{^}
STM{cond}<模式> Rn{!},reglist{^}
LDM /STM 的主要用途是现场保护、数据复制、参数传送等。其模式有8 种,如下所列:(前面4 种用于数据块的传输,后面4 种是堆栈操作)。
(1) IA:每次传送后地址加4
(2) IB:每次传送前地址加4
(3) DA:每次传送后地址减4
(4) DB:每次传送前地址减4
(5) FD:满递减堆栈
(6) ED:空递增堆栈
(7) FA:满递增堆栈
(8) EA:空递增堆栈
其中,寄存器Rn 为基址寄存器,装有传送数据的初始地址,Rn 不允许为R15;后缀“!”表示最后的地址写回到Rn 中;寄存器列表reglist 可包含多于一个寄存器或寄存器范围,使用“,”分开,如{R1,R2,R6-R9},寄存器排列由小到大排列;“^”后缀不允许在用户模式呈系统模式下使 用,若在LDM 指令用寄存器列表中包含有PC 时使用,那么除了正常的多寄存器传送外,将SPSR 拷贝到CPSR 中,这可用于异常处理返回;使用“^”后缀进行数据传送且寄存器列表不包含PC 时,加载/存储的是用户模式的寄存器,而不是当前模式的寄存器。
地址对准――这些指令忽略地址的位[1:0]。
批量加载/存储指令举例如下:
LDMIA R0!,{R3-R9} ;加载R0 指向的地址上的多字数据,保存到R3~R9 中,R0 值更新
STMIA R1!,{R3-R9} ;将R3~R9 的数据存储到R1 指向的地址上,R1 值更新
STMFD SP!,{R0-R7,LR} ;现场保存,将R0~R7、LR 入栈
LDMFD SP!,{R0-R7,PC}^;恢复现场,异常处理返回
寄存器入栈及出栈指令.实现低寄存器和可选的 LR 寄存器入栈寄存器和可选的 PC寄存器出栈操作,堆栈地址由 SP 寄存设置,堆栈是满递减堆栈.指令格式如下;
PUSH {reglist[,LR]}
POP {reglist[,PC]}
其中 reglist 入栈/出栈低寄存器列表,即 R0~R7
LR 入栈时的可选寄存器
PC 出栈时的可选寄存器
寄存器入栈及出栈指令举例如下;
PUSH {R0-R7,LR} ;将低寄存器 R0~R7 全部入栈,LR 也入栈
POP {R0-R7,PC} ;将堆栈中的数据弹出到低寄存器 R0~R7 及 PC 中
3.1.4 ARM伪指令
ADR,LDR请参考http://blog.csdn.net/xie0812/article/details/52653170
0x04 Thumb2指令集
thumb2在armv5上不支持 只有在armv7上才支持 thumb2采用thumb16位和thumb32位编程 thumb32 很多指令都会加上.w。现在我们用ida分析的so,一般采用thumb2指令集,因为即支持thumb16位,又支持thumb32位。提高了空间利用率。
thumb16位指令和arm指令,由于arm是32位指令,thumb16在指令位数上较arm少了16位,所有有诸多限制:
1、能够处理的立即数范围减少
2、大多数情况下寄存器只能取R0~R7
3、只有跳转指令可以加上cond
4、不支持寄存器移位寻址
我们在程序中,经常看到下面的代码,就是采用的thumb2编程,混合了thumb16位和thumb32位。
.text:00000D70 CMP R6, #0 ;16位
.text:00000D72 MOV.W R0, #3 ;32位
.text:00000D76 MOV R1, R4 ;16位
我们可以看到,
混合了thumb16位和thumb32位。对应的十六进制如下:
00000D70 00 2E 4F F0 03 00 21 46
4.1 00 2E 为什么对应着CMP R6,#0呢?
我们来查手册,ARM Architecture Reference Manual.pdf,下载地址:http://download.csdn.net/detail/jltxgcy/9602452。
00 2E 对应的二进制为 0000 0000 0010 1110,在A8-368页,我们查到了对应的指令格式:
高位是0010 1110,低位时0000 0000,对号入座,得到Rn为110,imm8为0000 0000。
所以Rn为R7;立即数为0。
CMP R7, #0
4.2 接下来看thumb32位命令:
4F F0 03 00 对应的二进制为 0100 1111 1111 0000 0000 0011 0000 0000,在A8-482页,我们查到了对应的指令格式:
分为前16位和后16位。前16位为4F F0,后16位为03 00。
前16位,高位为F0,低位为4F。下面对照图,列出对应的位。
1111 0000 0100 1111 0000 0000 0000 0011
i:0;S:0;imm3:000;Rd:0000;imm8:0000 0011
所以Rd为R0,立即数为3。
MOV.W R0, #3
4.3 我们接着看一条arm指令:
.text:00000DC8 SUB R8, R1, #1
对应的十六进制为:
00000DC8 01 80 41 E2
对应的二进制为:
0000 0001 1000 0000 0100 0001 1110 0010,在A8-708页,我们查到了对应的指令格式:
此时最高位就是出于末尾的1110 0010,次高位为0100 0001,依次类推,那么对照图,列出对应的位:
1110 0010 0100 0001 1000 0000 0000 0001
S:0;Rn:0001;Rd:1000;imm12:0000 0000 0001
所以Rn为R1;Rd为R8,立即数为1。
SUB R8, R1, #1
最后附一张cond的图表: