看了韦东山的点灯C程序后,为加深印象,现回顾总结如下:
main.c代码如下:
int main()
{
unsigned int *pGPFCON = (unsigned int *)0x56000050;
unsigned int *pGPFDAT = (unsigned int *)0x56000054;
/* 配置GPF4为输出引脚 */
*pGPFCON = 0x100;
/* 设置GPF4输出0 */
*pGPFDAT = 0;
return 0;
}
start.s代码如下:
.text
.global _start
_start:
/* 设置内存: sp 栈 */
ldr sp, =4096 /* nand启动 */
//1为什么要设置堆栈呢?
//答 :因为C语言要用。
//2为什么SP值要设置成4096呢?
//答:也可以不用设置成4096,原则上只要栈生长后不会影响内存中的代码(机器码)就Ok。
// ldr sp, =0x40000000+4096 /* nor启动 /
/ 调用main */
bl main
//bl为brance and link的缩写,即跳转并将下一条指令(b halt)的地址保存在R14(LR)中。
halt:
b halt
重点回归反汇编代码。下面逐条分析。
led.elf: file format elf32-littlearm
Disassembly of section .text:
00000000 <_start>:
0: e3a0da01 mov sp, #4096 ; 0x1000//设置栈,sp指向4096。
4: eb000000 bl c //跳转到地址c处执行,即main处。由此可知,main函数也可以用其他名字代替。这 条语句执行后,程序将跳到c(main)处执行,并将下一条指令的地址值(8)将保存LR中,即LR中的值为8
00000008 :
8: eafffffe b 8
0000000c :
c: e1a0c00d mov ip, sp//此时ip=sp=4096
10: e92dd800 stmdb sp!, {fp, ip, lr, pc}//**stmdb为写多个寄存器命令,db是decrement befor store的缩写,即先减再存。fp,ip,lr,pc的标号分别为R11,R12,R14,R15。stmdb遵循高标号先处理的原则,所以这条命令将先后存储PC,LR,IP,FP的值。下面分析各寄存器的值。
1,首先保存PC。PC值为当前地址+8,即0x10+8=0x18。PC寄存器占据地址为4096~4093共4个字节的内存单元。保存后,sp的值为sp-4=4096-4=4092。
2,然后保存LR。由前所述,LR值为8。同理,LR寄存器占据地址为4092~4089共4个字节的内存单元。保存后,sp的值为sp-4=4092-4=4088。
3.再保存IP。由前所述,IP的值为4096。IP寄存器占据地址为4088~4085共4个字节的内存单元。保存后,sp的值为sp-4=4088-4=4084。
4最后保存FP。FP值未知。FP寄存器占据地址为4084~4081共4个字节的内存单元。保存后,sp的值为sp-4=4084-4=4080。
以上一条指令实现了程序调用时的现场保护操作,即入栈。
**
14: e24cb004 sub fp, ip, #4 ; 0x4//fp=ip-4=4096-4=4092
18: e24dd008 sub sp, sp, #8 ; 0x8//sp=sp-8=4080-8=4072
1c: e3a03456 mov r3, #1442840576 ; 0x56000000//r3=0x56000000
20: e2833050 add r3, r3, #80 ; 0x50//r3=r3++80=0x56000000+0x50=0x56000050,即GPFCON寄存器的地址
24: e50b3010 str r3, [fp, #-16]//将GPFCON存放在FP-16=4092-16=4076地址处。局部变量保存到栈中
28: e3a03456 mov r3, #1442840576 ; 0x56000000//r3=0x56000000
2c: e2833054 add r3, r3, #84 ; 0x54//r3=0x56000000+0x54=0x56000054
30: e50b3014 str r3, [fp, #-20]//将GPFDAT存放在FP-20=4092-20=4072地址处。局部变量保存到栈中。
34: e51b2010 ldr r2, [fp, #-16]//r2=[4092-16=4076]=0x56000050,即GPFCON
38: e3a03c01 mov r3, #256 ; 0x100//r3=0x100
3c: e5823000 str r3, [r2]//将0x100写入0x56000050
40: e51b2014 ldr r2, [fp, #-20]///r2=[4092-20=4072]=0x56000054,即GPFDAT
44: e3a03000 mov r3, #0 ; 0x0//r3=0
48: e5823000 str r3, [r2]//将0写入0x56000054
4c: e3a03000 mov r3, #0 ; 0x0//r3=0
50: e1a00003 mov r0, r3//r0=0,垃圾代码,可以直接用mov r0,#0就可以了
54: e24bd00c sub sp, fp, #12 ; 0xc//sp=fp-12=4092-12=4080,sp指向4080
58: e89da800 ldmia sp, {fp, sp, pc}//此处开始出栈。ldmia为读多个寄存器指令。ia为increment after load,即先读再加,且遵循低标号先处理的原则,所以这条命令将先后读FP,SP,PC的值。下面分析各寄存器的值。
1读FP的值。此时SP指向4080,,FP的值为4081~4084处的值,即原来FP的值。此时SP指向4080+4=4084。
2然后读SP的值。此时SP指向4084,SP的值为4085~4088处的值,由前所述,为4096。所以出栈后,sp又指向了栈顶4096。此时SP指向4084+4=4088。
3读PC的值。此时SP指向4088,即4089~4092处的值,由前所述,为8。所以恢复寄存器后,即函数返回后,程序继续执行halt处程序。
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.