1 内存访问指令
1.1 LDR:字数据读取指令
指令语法格式: LDR Rd, <地址>
LDR指令用于从地址中将一个32位的字读取到指令中的目标寄存器(Rd)中
LDR R0,[R1,#4] ; 将内存单元R1+4中的字读取到R0寄存器中
LDR R0,[R1,#-4] ; 将内存单元R1-4中的字读取到R0寄存器中
LDR R0,[R1,R2] ; 将内存单元R1+R2中的字读取到R0寄存器中
LDR R0,[R1,-R2] ; 将内存单元R1-R2中的字读取到R0寄存器中
LDR R0,[R1,R2,LSL #2] ; 将内存单元(R1+R2*4)中的数据读取到R0中
LDR R0,[R1,#4]! ; 将内存单元R1+4中的数据读取到R0中,同时R1=R1+4
LDR R0,[R1,R2]! ; 将内存单元R1+R2中的数据读取到R0中,同时R1=R1+R2
LDR R0,[R1,R2,LSL #2]! ; 将内存单元(R1+R2*4)中的数据读取到R0中,同时R1=R1+R2*4
LDR R0,[R1],#4 ; 将内存单元R1中的数据读取到R0中,然后R1=R1+4
LDR R0,[R1],R2 ; /将内存单元R1中的数据读取到R0中,然后R1=R1+R2
LDR R0,[R1],R2,LSL #2 ; 将内存单元R1中的数据读取到R0中,然后R1=R1+R2*4
伪指令形式: LDR Rd, =expr
装载一个32bit常数或一个地址到寄存器Rd中,当expr表示的地址值没有超过MOV或MVN指令中地址的取值范围时,编译器用合适的MOV或者MVN指令代替该LDR伪指令。
LDR R0,=0x12345678 ; R0=0x12345678伪指令,它会被拆分为几条真正的RAM指令,因为ARM指令是
;32bit,有几个bit表示指令本身,剩下的位数不足以保存任意数,只能表示简单值(被
;称为立即数)
1.2 STR:字数据写入指令
指令语法格式: STR Rd, <地址>
STR指令用于将一个32位的字数据写入到指令中指定的内存单元.
STR R0,[R1,#0x100] ; 将R0中的字数据保存到内存单元(R1+0x100)中
STR R0,[R1],#8 ; 将R0中的字数据保存到内存单元R1中,然后R1=R1+8
1.3 LDM/STM:批量内存字数据读取/写入指令
指令语法格式: LDM/STM{类型} Rn{!} , {寄存器列表}
主要包含四种类型的指令:
栈 | 其他 | 说明 |
---|---|---|
LDMED | LDMIB | 预先增加装载 |
LDMFD | LDMIA | 过后增加装载 |
LDMEA | LDMDB | 预先减少装载 |
LDMFA | LDMDA | 过后减少装载 |
STMFA | STMIB | 预先增加存储 |
STMEA | STMIA | 过后增加存储 |
STMFD | STMDB | 预先减少存储 |
STMED | STMDA | 过后减少存储 |
- FD、ED、FA、和 EA 指定是满栈还是空栈,是升序栈还是降序栈。一个满栈的栈指针指向上次写的最后一个数据单元,而空栈的栈指针指向第一个空闲单元。一个降序栈是在内存中反向增长(就是说,从应用程序空间结束处开始反向增长)而升序栈在内存中正向增长。
- 其他形式简单的描述指令的行为,意思分别是过后增加(Increment After)、预先增加(Increment Before)、过后减少(Decrement After)、预先减少(Decrement Before)。
- 寄存器按从最低到最高的编号次序与从低端到高端的内存之间传送数据。
LDR R0,=0x8000 ;R0=0x8000
MOV R1,#0x12 ;R1=0x12
MOV R2,#0x34 ;R2=0x34
MOV R3,#0x56 ;R3=0x56
(1)STMIA R0!,{R1-R3} ;[8000]=0x12,[8004]=0x34,[8008]=0x56,R0=0x8008
LDMDA R0!,{R4-R6} ;R4=0x12,R5=0x34,R6=0x56,R0=0x8000
(2)STMIB R0!,{R1-R3} ;[8004]=0x12,[8008]=0x34,[800c]=0x56,R0=0x800c
LDMDB R0!,{R4-R6} ;R4=[8000]内的数值,R5=0x12,R6=0x34,R0=0x8000
(3)STMDA R0!,{R1-R3} ;[7ff8]=0x12,[7ffc]=0x34,[8000]=0x56,R0=0x7ff8
LDMIA R0!,{R4-R6} ;R4=0x12,R5=0x34,R6=0x56,R0=0x8000
(4)STMDB R0!,{R1-R3} ;[7ff4]=0x12,[7ff8]=0x34,[7ffc]=0x56,R0=0x7ff4
LDMIB R0!,{R4-R6} ;R4=0x34,R5=0x56,R6=[8000]内的数值,R0=0x8000
LDR SP,=0x8000 ;SP=0x8000
MOV R1,#0x12 ;R1=0x12
MOV R2,#0x34 ;R2=0x34
MOV R3,#0x56 ;R3=0x56
(1)LDMEA SP!,{R1-R3} ;[7ff4]=0x12,[7ff8]=0x34,[7ffc]=0x56,SP=0x7ff4,对于向下生长栈,第一个有效栈地址为0x8000(栈顶)-4=0x7ffc
STMEA SP!,{R4-R6} ;R4=0x12,R5=0x34,R6=0x56,R0=0x7ffc
(2)LDMFD SP!,{R1-R3} ;[8000]=0x12,[8004]=0x34,[8008]=0x56,SP=0x8008,对于向上生长栈,第一个有效栈地址为0x8000
STMED SP!,{R4-R6} ;R4=0x12,R5=0x34,R6=0x56,R0=0x8000
2 跳转指令
2.1 B:跳转指令及BL(带返回的跳转指令)
B指令和BL指令均可以跳转到指令中的目标地址,这两个指令和目标出的指令独属于ARM指令集。不同之处在于B指令仅仅执行跳转操作;BL指令同时还将PC寄存器的值减4保存到LR寄存器中.
ARM汇编器通过以下步骤完成跳转:
- 将PC寄存器的值作为本跳转指令的基地址值
- 从跳转的目标地址中减去上面所说的跳转的基地址值,生成字节偏移值。由于ARM指令是字节对齐的,该
字节偏移量为4的倍数 - 当上面生成的字节偏移量超过范围33554432~33554430,程序需要作相应的处理
- 否则,设置偏移量为上述字节偏移量的bits[25:2]
B Lable ; 程序跳转到标号Lable处执行
BL func ; 程序跳转到子程序func处执行,同时将当前PC值减4保存到LR中(即下一条指令的地址)
3 数据处理指令
3.1 MOV:传送指令
指令语法格式: MOV Rd,
MOV从另一个寄存器、或被移位的寄存器、或一个立即数装载到目的寄存器。可以指定相同的寄存器来实现NOP指令的效果.
MOV R0,R0 ; R0=R0...NOP指令效果
MOV R0,R0,LSL#3 ; R0=R0*8
MOV R0,#0x100 ; R0=0x100
3.2 MVN:传送指令
指令语法格式: MOV Rd,
MVN 从另一个寄存器、被移位的寄存器、或一个立即值装载一个值到目的寄存器。不同之处是在传送之前位被反转了,所以把一被取反的值传送到一个寄存器中。这是逻辑非操作而不是算术操作,这个取反的值加 1 才是它的取负的值
MVN R0,#4 ; R0=-5 0x04取反为0xfb.该0xfb以补码形式存放,原码为:0xfb除符号位全部取反,然后加1,即为0x85,最高位为符号位即-5.
MVN R0,#0 ; R0=-1 0x00取反为0xff.该0xff以补码形式存放,原码为:0xff除符号位全部取反,然后加1,即为0x81,最高位为符号位即-1.
- 正数的反码和补码都与原码相同。
- 负数的反码为对该数的原码除符号位外各位取反。
- 负数的补码为对该数的原码除符号位外各位取反,然后在最后一位加1。
3.3 ADD:加法指令
指令语法格式: ADD Rd,
ADD 将把两个操作数加起来,把结果放置到目的寄存器中。操作数 1 是一个寄存器,操作数 2 可以是一个寄存器,被移位的寄存器,或一个立即值.
ADD R0,R1,R2 ; R0 = R1 + R2
ADD R0,R1,#256 ; R0 = R1 + 256
ADD R0,R2,R3,LSL#1 ; R0 = R2 + (R3 << 1)
3.4 SUB:减法指令
指令语法格式: SUB Rd,
SUB 用操作数 one 减去操作数 two,把结果放置到目的寄存器中。操作数 1 是一个寄存器,操作数 2 可以是一个寄存器,被移位的寄存器,或一个立即值
SUB R0,R1,R2 ; R0 = R1 - R2
SUB R0,R1,#256 ; R0 = R1 - 256
SUB R0,R2,R3,LSL#1 ; R0 = R2 - (R3 << 1)
3.5 CMP:比较指令
指令语法格式:CMP
CMP指令表示寄存器
CMP指令和SUBS指令的区别在于CMP指令不保存操作结果。
标志 | 说明 |
---|---|
N 标 志 | 本位设置成当前指令运算结果的bit[31]的值,当两个补码表示的有符号整数运算时,N=1表示运算的结果为负数;N=0表示结果为正数。 |
Z表示 | Z=1表示运算的结果为0;Z=0表示运算的结果不为0.对于CMP指令,Z=1表示进行比较的两个数大小相等. |
C标志 | 1. 在加法指令中(包括比较指令CMN),当结果产生了进位,则C=1,表示无符号数运算发生上溢出;其它情况下C=0 , 2. 在减法指令中(包括比较指令CMP),当运算中发生了借位,则C=0,表示无符号数运算发生下溢出;其它情况下C=1. 3.对于包含移位操作的非加/减法运算指令,C中包含最后一次被溢出的位的数值. 4对于其它非加/减法运算指令,C位的值通常不受影响 |
V标志 | 对于加/减法运算指令,当操作数和运算结果为二进制的补码表示的带符号数时,V=1表示符号位溢出.通常其它的指令不影响V位,具体可参考各指令的说明 |
4 实际反汇编分析
4.1 示例1-纯汇编代码的反汇编分析
/*********************start.S文件汇编代码***********************/
.text
.global _start
_start:
/* 配置GPF4为输出引脚
* 把0x100写到地址0x56000050
*/
ldr r1, =0x56000050
ldr r0, =0x100 /* mov r0, #0x100 */
str r0, [r1]
/* 设置GPF4输出高电平
* 把0写到地址0x56000054
*/
ldr r1, =0x56000054
ldr r0, =0
str r0, [r1]
/* 死循环 */
halt:
b halt
/********************反汇编代码**********************/
led_on.elf: file format elf32-littlearm
Disassembly of section .text:
00000000 <_start>:
0: e59f1014 ldr r1, [pc, #20] ; R1=[PC+20]=[8+20]=[0x1c] 读取地址0x1c里面的值然后赋值给R1,即R1=0x56000050.PC值为当前指令地址加8
4: e3a00c01 mov r0, #256 ; R0=0x100
8: e5810000 str r0, [r1] ; R0的值(0x100)存入R1寄存器中的地址内(0x56000050)
c: e59f100c ldr r1, [pc, #12] ; R1=[PC+12]=[0x0c+8+12]=[0x20] 读取0x20地址里面的值然后赋值给R1,即R1=0x56000054
10: e3a00000 mov r0, #0 ; R0=0x00
14: e5810000 str r0, [r1] ; R0的值(0x00)存入R1寄存器中的地址内(0x56000054)
00000018 :
18: eafffffe b 18
1c: 56000050 undefined
20: 56000054 undefined
4.1 示例2-包含C程序的反汇编分析
/*******************start.S文件汇编代码*******************/
.text
.global _start
_start:
/* 设置内存: sp 栈 */
ldr sp, =4096 /* nand启动 */
/* 调用main */
bl main
halt:
b halt
/*********************led.c文件代码***********************/
int main()
{
unsigned int *pGPFCON = (unsigned int *)0x56000050;
unsigned int *pGPFDAT = (unsigned int *)0x56000054;
/* 配置GPF4为输出引脚 */
*pGPFCON = 0x100;
/* 设置GPF4输出0 */
*pGPFDAT = 0;
return 0;
}
/********************C语言程序反汇编代码******************/
00000000 <_start>:
0: e3a0da01 mov sp, #4096 ; 0x1000
4: eb000000 bl c
00000008 :
8: eafffffe b 8
0000000c :
c: e1a0c00d mov ip, sp ; ip(R12)=sp=4096
10: e92dd800 stmdb sp!, {fp, ip, lr, pc} ; 感叹号表示 sp=最终的,被修改的sp值,如果不加感叹号,sp=4096,加了感叹号sp=4096-16=4080,高编号寄存器存放高地址.[4092]=PC的内容,[4088]=lr的内容,[4084]=ip的内容,[4080]=fp的内容
14: e24cb004 sub fp, ip, #4 ; fp(R11)=4096-4=4092
18: e24dd008 sub sp, sp, #8 ; sp=4080-8=4072
1c: e3a03456 mov r3, #1442840576 ; r3=0x56000000
20: e2833050 add r3, r3, #80 ; r3=0x56000000+0x50=0x56000050
24: e50b3010 str r3, [fp, #-16] ; r3(0x56000050)存入[fp-16]的地方即4092-16=4076 即局部变量存放在栈中
28: e3a03456 mov r3, #1442840576 ; r3=0x56000000
2c: e2833054 add r3, r3, #84 ; r3=0x56000000+0x54=0x56000054
30: e50b3014 str r3, [fp, #-20] ; r3(0x56000054)存入[fp-20]的地方即4092-20=4072 即局部变量存放在栈中
34: e51b2010 ldr r2, [fp, #-16] ; 读取[fp-16]=[4076]地址上的内容(0x56000050)存入R2中,
38: e3a03c01 mov r3, #256 ; r3=0x100
3c: e5823000 str r3, [r2] ; 把R3里面的内容(0x100)存入r2中的地址内(0x56000050)
40: e51b2014 ldr r2, [fp, #-20] ; 读取[fp-20]=[4072]地址上的内容(0x56000054)存入R2中,
44: e3a03000 mov r3, #0 ; r3=0x00
48: e5823000 str r3, [r2] ; 把R3里面的内容(0x00)存入r2中的地址内(0x56000054)
4c: e3a03000 mov r3, #0 ; r3=0x00
50: e1a00003 mov r0, r3 ; r0=0x00,main函数返回值赋值给r0
54: e24bd00c sub sp, fp, #12 ; sp=fp-12=4092-12=4080 恢复栈
58: e89da800 ldmia sp, {fp, sp, pc} ; [4080]内容赋值给fp,[4084]内容赋值给sp,[4088]内容赋值给pc
Disassembly of section .comment:
00000000 <.comment>:
0: 43434700 cmpmi r3, #0 ; 0x0
4: 4728203a undefined
8: 2029554e eorcs r5, r9, lr, asr #10
c: 2e342e33 mrccs 14, 1, r2, cr4, cr3, {1}
10: Address 0x10 is out of bounds.