1.几条汇编指令_bl_add_sub_ldm_stm
⑥ADD/SUB 加法/减法
举例1:
add r0,r1,#4
效果为
r0=r1+4;
举例2:
sub r0,r1,#4
效果为
r0=r1-4;
举例3:
sub r0,r1,r2
效果为
r0=r1-r2;
⑦BL (Brarch and Link)带返回值的跳转 跳转到指定指令,并将返回地址(下一条指令)保存在lr寄存器;
⑧LDM/STM 读内存,写入多个寄存器/把多个寄存器的值写入内存
可搭配的后缀有 过后增加(Increment After)、预先增加(Increment Before)、过后减少(Decrement After)、预先减少(Decrement Before);
举例1:
stmdb sp!, (fp,ip,lr,pc)
假设Sp=4096。 db意思是先减后存,按 高编号寄存器存在高地址 存。
举例2:
ldmia sp, (fp,ip,pc)
2.解析C程序的内部机制
003_led.c内部机制分析:
start.S:
①设置栈;
②调用main,并把返回值地址保存到lr中;
led.c的main()内容:
①定义2个局部变量;
②设置变量;
③return 0;
问题:
①为什么要设置栈?
因为c函数要用。
②怎么使用栈?
a.保存局部变量;
b.保存lr等寄存器;
③调用者如何传参数给被调用者?
④被调用者如何传返回值给调用者?
⑤怎么从栈中恢复那些寄存器?
在arm中有个ATPCS规则,约定r0-r15寄存器的用途。
r0-r3:调用者和被调用者之间传参数;
r4-r11:函数可能被使用,所以在函数的入口保存它们,在函数的出口恢复它们;
下面分析个实例 start.S:
.text .global _start _start: /* 设置内存: sp 栈 */ ldr sp, =4096 /* nand启动 */ // ldr sp, =0x40000000+4096 /* nor启动 */ /* 调用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;
}
将前面的程序反汇编得到led.dis如下:
led.elf: file format elf32-littlearm
Disassembly of section .text:
00000000 <_start>:
0: e3a0da01 mov sp, #4096 ; 0x1000
4: eb000000 bl c <main>
00000008 <halt>:
8: eafffffe b 8 <halt>
0000000c <main>:
c: e1a0c00d mov ip, sp
10: e92dd800 stmdb sp!, {fp, ip, lr, pc}
14: e24cb004 sub fp, ip, #4 ; 0x4
18: e24dd008 sub sp, sp, #8 ; 0x8
1c: e3a03456 mov r3, #1442840576 ; 0x56000000
20: e2833050 add r3, r3, #80 ; 0x50
24: e50b3010 str r3, [fp, #-16]
28: e3a03456 mov r3, #1442840576 ; 0x56000000
2c: e2833054 add r3, r3, #84 ; 0x54
30: e50b3014 str r3, [fp, #-20]
34: e51b2010 ldr r2, [fp, #-16]
38: e3a03c01 mov r3, #256 ; 0x100
3c: e5823000 str r3, [r2]
40: e51b2014 ldr r2, [fp, #-20]
44: e3a03000 mov r3, #0 ; 0x0
48: e5823000 str r3, [r2]
4c: e3a03000 mov r3, #0 ; 0x0
50: e1a00003 mov r0, r3
54: e24bd00c sub sp, fp, #12 ; 0xc
58: e89da800 ldmia sp, {fp, sp, 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.
分析上面的汇编代码:
开发板上电后,将从0地址开始执行,即开始执行
mov sp, #4096:设置栈地址在4k RAM的最高处,sp=4096;
bl c <main>:调到c地址处的main函数,并保存下一行代码地址到lr,即lr=8;
mov ip, sp:给ip赋值sp的值,ip=sp=4096
stmdb sp!, {fp, ip, lr, pc}:按高编号寄存器存在高地址,依次将pc、lr、ip、fp存入sp-4中;
sub fp, ip, #4:fp的值为ip-4=4096-4=4092;
sub sp, sp, #8:sp的值为sp-8=(4096-4x4)-8=4072;
mov r3, #1442840576:r3赋值0x5600 0000;
add r3, r3, #80:r3的值加0x50,即r3=0x5600 0050;
str r3, [fp, #-16]:r3存入[fp-16]所在的地址,即地址4076处存放0x5600 0050;
mov r3, #1442840576:r3赋值0x5600 0000;
add r3, r3, #84:r3的值加0x54,即r3=0x5600 0054;
str r3, [fp, #-20]:r3存入[fp-20]所在的地址,即地址4072处存放0x5600 0054;
ldr r2, [fp, #-16]:r2取[fp-16]地址处的值,即[4076]地址的值,r2=0x5600 0050;
mov r3, #256:r3赋值为0x100;
str r3, [r2]:将r3写到r2内容所对应的地址,即0x5600 0050地址处的值为0x100;;对应c语言*pGPFCON = 0x100;;
ldr r2, [fp, #-20]:r2取[fp-20]地址处的值,即[4072]地址的值,r2=0x5600 0054;
mov r3, #0:r3赋值为0x00;
str r3, [r2]:将r3写到r2内容所对应的地址,即0x5600 0054地址处的值为0x00;对应c语言*pGPFDAT = 0;
mov r3, #0:r3赋值为0x00;
mov r0, r3:r0=r3=0x00;
sub sp, fp, #12:sp=fp-12=4092-12=4080;
ldmia sp, {fp, sp, pc}:从栈中恢复寄存器,fp=4080地址处的值=原来的fp,sp=4084地址处的值=4096,pc=4088地址处的值=8,随后调到0x08地址处继续执行。
过程中的内存数据情况:
前面那个例子,汇编调用main.c并没有传递参数,这里修改下c程序,让其传递参数。
start.S:
.text .global _start _start: /* 设置内存: sp 栈 */ ldr sp, =4096 /* nand启动 */ // ldr sp, =0x40000000+4096 /* nor启动 */ mov r0, #4 bl led_on ldr r0, =100000 bl delay mov r0, #5 bl led_on halt: b halt
led.c:
void delay(volatile int d)
{
while (d--);
}
int led_on(int which)
{
unsigned int *pGPFCON = (unsigned int *)0x56000050;
unsigned int *pGPFDAT = (unsigned int *)0x56000054;
if (which == 4)
{
/* 配置GPF4为输出引脚 */
*pGPFCON = 0x100;
}
else if (which == 5)
{
/* 配置GPF5为输出引脚 */
*pGPFCON = 0x400;
}
/* 设置GPF4/5输出0 */
*pGPFDAT = 0;
return 0;
}
led.elf:
led.elf: file format elf32-littlearm
Disassembly of section .text:
00000000 <_start>:
0: e3a0da01 mov sp, #4096 ; 0x1000
4: e3a00004 mov r0, #4 ; 0x4
8: eb000012 bl 58 <led_on>
c: e59f000c ldr r0, [pc, #12] ; 20 <.text+0x20>
10: eb000003 bl 24 <delay>
14: e3a00005 mov r0, #5 ; 0x5
18: eb00000e bl 58 <led_on>
0000001c <halt>:
1c: eafffffe b 1c <halt>
20: 000186a0 andeq r8, r1, r0, lsr #13
00000024 <delay>:
24: e1a0c00d mov ip, sp
28: e92dd800 stmdb sp!, {fp, ip, lr, pc}
2c: e24cb004 sub fp, ip, #4 ; 0x4
30: e24dd004 sub sp, sp, #4 ; 0x4
34: e50b0010 str r0, [fp, #-16]
38: e51b3010 ldr r3, [fp, #-16]
3c: e2433001 sub r3, r3, #1 ; 0x1
40: e50b3010 str r3, [fp, #-16]
44: e51b3010 ldr r3, [fp, #-16]
48: e3730001 cmn r3, #1 ; 0x1
4c: 0a000000 beq 54 <delay+0x30>
50: eafffff8 b 38 <delay+0x14>
54: e89da808 ldmia sp, {r3, fp, sp, pc}
00000058 <led_on>:
58: e1a0c00d mov ip, sp
5c: e92dd800 stmdb sp!, {fp, ip, lr, pc}
60: e24cb004 sub fp, ip, #4 ; 0x4
64: e24dd00c sub sp, sp, #12 ; 0xc
68: e50b0010 str r0, [fp, #-16]
6c: e3a03456 mov r3, #1442840576 ; 0x56000000
70: e2833050 add r3, r3, #80 ; 0x50
74: e50b3014 str r3, [fp, #-20]
78: e3a03456 mov r3, #1442840576 ; 0x56000000
7c: e2833054 add r3, r3, #84 ; 0x54
80: e50b3018 str r3, [fp, #-24]
84: e51b3010 ldr r3, [fp, #-16]
88: e3530004 cmp r3, #4 ; 0x4
8c: 1a000003 bne a0 <led_on+0x48>
90: e51b2014 ldr r2, [fp, #-20]
94: e3a03c01 mov r3, #256 ; 0x100
98: e5823000 str r3, [r2]
9c: ea000005 b b8 <led_on+0x60>
a0: e51b3010 ldr r3, [fp, #-16]
a4: e3530005 cmp r3, #5 ; 0x5
a8: 1a000002 bne b8 <led_on+0x60>
ac: e51b2014 ldr r2, [fp, #-20]
b0: e3a03b01 mov r3, #1024 ; 0x400
b4: e5823000 str r3, [r2]
b8: e51b3018 ldr r3, [fp, #-24]
bc: e3a02000 mov r2, #0 ; 0x0
c0: e5832000 str r2, [r3]
c4: e3a03000 mov r3, #0 ; 0x0
c8: e1a00003 mov r0, r3
cc: e24bd00c sub sp, fp, #12 ; 0xc
d0: e89da800 ldmia sp, {fp, sp, 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.
简单分析下反汇编:
mov sp, #4096:设置栈地址在4k RAM的最高处,sp=4096; mov r0, #4:r0=4,作为参数; bl 58 <led_on>:调到58地址处的led_on函数,并保存下一行代码地址到lr,即lr=8;在led_on中会使用到r0; ldr r0, [pc, #12]:r0=[pc+12]处的值=[c+12=20]的值=0x186a0=1000000,作为参数; bl 24 <delay>:调用24地址处的delay函数,并保存下一行代码地址到lr,即lr=24;在delay中会使用到r0; mov r0, #5:r0=5,作为参数; bl 58 <led_on>:调到58地址处的led_on函数,并保存下一行代码地址到lr,即lr=58;在led_on中会使用到r0;