长时间写java程序,但从来没去研究main函数是如何执行的,今天学习嵌入式终于知道了原理。
之前看过一本30天做一个操作系统这本书,可以通过写个2进制文件来启动系统。然后进化到汇编语言,通过工具把汇编转为二进制一样可以启动系统(虚拟机)。这个系统的执行过程与我们的main函数有什么联系么。做java的同学知道,只要执行java命令自然会执行main函数。做c语言的同学,知道在编译后执行可执行文件(sh a.out)自然也会执行main函数。但为什么呢?看下面这段汇编代码:
.text
.global _start
_start:
ldr r0, =0x53000000 @ WATCHDOG寄存器地址
mov r1, #0x0
str r1, [r0] @ 写入0,禁止WATCHDOG,否则CPU会不断重启
ldr sp, =1024*4 @ 设置堆栈,注意:不能大于4k, 因为现在可用的内存只有4K
@ nand flash中的代码在复位后会移到内部ram中,此ram只有4K
bl main @ 调用C程序中的main函数
halt_loop:
b halt_loop
start段开始后的前3行是关门看门狗程序,防止重启。ldr是设置一个4k的内存,也就是程序的存放地址。bl就是调用main函数了,后面是死循环不用管。
下面我们再写段C语言:
#define GPFCON (*(volatile unsigned long *)0x56000050)
#define GPFDAT (*(volatile unsigned long *)0x56000054)
int main()
{
GPFCON = 0x00000100; // 设置GPF4为输出口, 位[8:7]=0b01
GPFDAT = 0x00000000; // GPF4输出0,LED1点亮
return 0;
}
上面两个宏是定义控制寄存器的地址,为什么是0x56000050,这是从2440板子的数据手册中找到的。如下图:
最后这个c语言程序与汇编程序如何连起来呢?看makefile文件:
arm-linux-gcc -g -c -o crt0.o crt0.S
arm-linux-gcc -g -c -o led_on_c.o led_on_c.c
arm-linux-ld -Ttext 0x0000000 -g crt0.o led_on_c.o -o led_on_c_elf
arm-linux-objcopy -O binary -S led_on_c_elf led_on_c.bin
arm-linux-objdump -D -m arm led_on_c_elf > led_on_c.dis
先编译汇编程序(crto.S),再编译c语言(led_on_c.c),再把这两生成的目标文件链接为led_on_c_elf文件,再把这个转为二进制文件。这个就可以烧进板子执行了。 其实30天制作操作系统也可以通过这个方法也制作,不过工具不是arm-linux_gcc,我们这也相当于在开始制作一个系统了。