基于S3C6410的ARM11学习(十三) C语言环境初始化
0赞
如今,汇编的部分,就剩下最后一步了。C语言环境初始化。因为后面的代码要用C语言来写了。毕竟C语言编写比汇编要容易对了,而且c程序具有易读性。
C程序要运行,一个最重要的东西就是栈了。因为有栈,c程序才能在程序跳转的时候,保存上文。然后在程序返回的时候,将保存的上文恢复。这样,才保证了调用函数之前和之后的上下文是不变的。
使用汇编代码写的话,是不用设置栈的,因为保护上下文是要用汇编在代码中自行写出的,而C代码是靠编译器编译自动加上的。
栈,有4种。
从图中可以看出四种栈的区别。栈都是放在内存空间的,因为要随时读写。栈的起始地址放在内存的高地址出,那么增长方向就是向下增长,这种就是递减栈。如果栈的起始地址放在内存的低地址处,那么增长方向就是向上增长,这种就是递增栈。
栈顶指针一直指向栈顶数据,就是满栈,如果一直指向栈顶的下一个数据,就是空栈。
ARM采用的是满递减栈。使用汇编对栈的操作的指令是ldmfd,stmfd。
ARM的栈顶指针使用的是r13寄存器。但是,之前说过,在不同的模式下,有各自的r13寄存器。所以说,当程序运行到其他模式的时候,如果是用C语言的话,就要设置对应模式的r13寄存器,以设定栈顶指针。目前程序都是在SVC模式下运行的,就只设置这个模式下的r13寄存器就可以了。
栈的位置是要设置在内存中的,之前初始化的ddr的地址空间是0x50000000~0x57ffffff。ARM又是满递减栈,这里就可以把栈的位置设置在0x54000000。当然这个位置可以随意设置,但是要注意栈的空间不能和程序空间重合。因为在内存中也保存的程序的代码。栈又是一直递减的,如果栈的设置不合理,就会递减到程序的空间,从而把程序给覆盖了,那么就可能程序就会运行出错了。
其实,设置栈的程序很简单,就只是给r13寄存器赋个值就行了。
Sp是r13寄存器的别名,表示栈指针。注意,这里设置的栈指针是给SVC模式设置的。当切换到其他模式下,是要重新设置sp的。
下面一步就是对BSS段清零了。BSS段,保存的C代码中定义的未初始化的全局变量。在连接器脚本中,有设置BSS段的起始地址,也有设置BSS段的结束地址。
BSS段有什么作用了,保存未初始化的全局变量。假设在程序中,定义了一个int a[10000]这么大的数组。但是没有对这个数组进行初始化。如果把这个变量给写到代码区的话,那么代码就会增加40000个字节大小。这无疑是不科学的。所以将这个数据放在BSS段,给出首地址和结束地址,那么就只需要8个字节就够了,节约了很大的空间。
就利用这起始地址和结束地址来对BSS段进行清零。
代码也是比较简单的。
首先判断BSS段的起始地址和结束地址是否一样,一样的话,说明目前没有BSS段数据,那么就没有必要清零,直接返回。程序中用的就是movep,带条件判断的赋值指令。不一样的话,说明BSS段有数据,那么就对这一片空间进行清零就可以了。
这样,C语言的环境就初始化完毕了。也就是设置栈指针,以及对BSS段清零。
对比STM32,
在启动代码的最后,有如下代码:
这段代码对堆栈进行初始化。栈是用来程序跳转的时候使用,堆是动态分配内存的时候用的。
目前,我还没有找到这段代码是在什么地方调用的。查阅网上资料,说是在_main函数中有进行调用。但是在调用_main函数之前,就已经调用了一个c函数进行系统的初始化。而c函数在执行之前,是需要栈的啊。
这个问题的话,就要说起STM32的复位启动了。以下是cortex-M3权威手册中说的。
MSP,是主堆栈的意思。在STM32中有两个堆栈,这里就先不管这个,就认为这个MSP是堆栈就好了。
0地址处的值就是堆栈的地址。所以CM3先从0地址处取出堆栈的地址,赋值给sp。这样,就有了堆栈指针,剩下调用c函数就没有问题了。然后再取出0x4的值,这个是复位的跳转地址,然后程序跳转到这个地址去,执行复位代码。
上图是软件仿真看的反汇编代码及执行到reset的第一条代码的各个寄存器值。0地址处的值是0x0418。STM32的内存空间是从0x20000000开始的,这个就是偏移。所以sp的值就是0x20000418。寄存器列表中的sp也是这个值。这个418是怎么来的,目前我还没有搞懂,但是程序大小不一样,这个值就会不一样。
这样的话,栈就设置好了,就可以进行c函数的调用了。后面在_main函数中,又调用了启动代码最后的堆栈初始化的代码。