汇编写启动代码之设置栈和调用C语言

来源:互联网 发布:淘宝延长发货时间 编辑:程序博客网 时间:2024/05/21 17:41

汇编写启动代码之设置栈和调用C语言

                                                                                         --参考朱老师ARM裸机编程


1、为什么汇编写启动代码之设置栈和调用C语言

(1) C语言运行时:(环境runtime)需要一定的环境和条件,这些环境由汇编来提供。
有些硬件已经默认内部提供了C语言的环境来运行。
我们自己来提供一个C语言运行时环境,主要是需要栈。
C语言中的局部变量都是存放在栈里面的。
如果我们汇编部分没有给C部分预先设置合理的合法的栈的地址的话,

那么C代码中定义的局部变量就会落空。整个程序就会死掉。



(2) 我们平时在编写单片机程序(比如51单片机)或者
编写应用程序时并没有去设置栈,但是C程序还是可以运行的
原因是:在单片机中由硬件初始化时提供了一个默认可以使用的栈。


在应用程序中我们编写的C程序其实并不是全部,编译器(gcc)
在链接的时候会帮我们添加一个头,这个头就是一段引导我们的C程序。
执行的汇编实现的代码,这个代码中就帮我们设置了栈以及其他的运行时需要。


(3)CPU模式和各种模式下面的栈

栈在什么地方设置的?

其实就是指SP指针指向的内容。

ARM的37个寄存器中,每种模式下面都有自己独立的SP寄存器(r13)


2、为什么要这么设置?

这样设置的话,不会耗费很多有用的内存吗?

但是如果所有的模式都使用同一个SP,那么就意味着整个程序(操作系统内核程序

用户自己编写的应用程序)都是使用一个栈的,那么整个程序都是一个栈。

你的应用程序一旦出错,比如栈溢出。


在程序里面如果使用递归的话,你的应用程序一旦出错的话,

就会连累操作系统的栈也会损坏,整个程序都是用一个栈,

你的应用程序一旦出错,就会连累操作系统的栈也会损坏,

整个操作系统的程序就会奔溃。

这样的操作系统设计是非常脆弱的,不合理的。


解决方案就是各种模式下面使用不同的栈。

这样我的操作系统内核使用自己的栈,每个应用程序也是用自己独立的栈。

这样各是各的,一个损坏不会连累到其他的地方。


虽然我们现在要设置栈,也没有必要设置所有的栈。

我们先要找到自己的模式,然后设置自己的模式下的栈道合理合法。


我们当前处于SVC模式下面,系统在复位后默认都是进入SVC模式的,

我们怎么去访问SVC模式下面的栈呢?很简单,先把模式设置为SVC。

再直接设置为SVC模式的。


3、查阅文档并设置栈指针至合法位置

栈必须是当前一段可用的内存(可用的意思就是这个地方必须由CPU

初始化可以访问的内存,而且这个内存只会被我们用作栈,不会被其他程序征用)


当前CPU刚复位(刚启动)外部的DRAM尚未初始化,目前可用的内存只有内部的SRAM。

在SRAM里面找一段内存来作为SVC的栈。


他要求你的BL1只有16K

这96K怎么分配别人已经分配好了,主要是0xd0037780还有0xd0037D80这两个地址。

在ARM中的ATPCS要求使用满减栈,ARM关于程序应该程序怎么实现的规范。

所以我们一般是使用满减栈。


4、使用C语言来访问寄存器的语法

寄存器的地址类似于内存地址(IO与内存统一编址)

所以这里的问题使用C语言读写寄存器。

就是使用C语言来读写内存地址,用C语言来访问内存,就是要用指针


volatile unsigned int *p = (unsigned int *)0xE0200240;
因为你是寄存器访问的寻址,所以寄存器的数是经常的变化的,

所以需要volatile来变化。


5、编译报错(实际上是连接的阶段报错)
undefinded

直接去搜索到的内容一个一个看一个一个尝试。


遇到这个问题的时候,加上-nostdlib选项即可。
就是不使用标准的函数库。
标准函数库就是我们编译器中的自带的函数库。


可以让编译器和链接器优先选择我程序。
我们就要用我们自己的。


汇编和C语言的联合调式方法。


这根编译器是有关的,好几年前做过这个东西。
volatile的作用是:程序在编译时,编译器不对程序做优化。
优化有时候是OK的,但是有时候是自作聪明的,造成程序的不对。


如果你的一个变量是易变的,不希望编译器帮我们做优化,
就在这个变量定义时加volatile。
加不加有没有差别,取决于编译器,如果编译器做了优化则有差异。
如果编译器本身没有做优化,那么就没有差别。
在我们这里,arm-2009q3,实际测试加不加效果是一样的。


volatile加上是肯定是对的,但是效率会有下降。

一般人家写裸机程序都是这么写寄存器的,用来在寄存器写东西

#define rGPJ0CON  *((volatile unsigned int *)GPJ0CON)
#define rGPJ0DAT  *((volatile unsigned int *)GPJ0DAT)


#define WTCON  0xE2700000#define SVC_STACK 0xd0037d80//汇编语言的初始化的语言.global _start      //把_start链接属性改为外部,这样其他文件就可以看到了_start://第1步:关闭看门狗(向WTCON的bit5写入0即可)ldr r0, =WTCONldr r1, =0x0str r1, [r0]//第2步:设置SVC栈,这是看用户手册里面查看得到的ldr sp, =SVC_STACK     //从这里开始就可以开始调用C程序了bl led_blink       //这是C 语言实现的一个函数//最后汇编的这个死循环不能丢掉b .//led.c#define GPJ0CON  0xE0200240#define GPJ0DAT  0xE0200244void delay(void);void led_blink(void){    //led初始化,也就是把GPJ0CON中设置为输出模式unsigned int *p = (unsigned int *)GPJ0CON;unsigned int *p1 = (unsigned int *)GPJ0DAT;*p = 0x11111111;while(1){   //led亮   *p1 = ((0<<3) | (0<<4) | (0<<5));   //延时   delay();   //LED灭   *p1 = ((1<<3) | (1<<4) | (1<<5));   //   delay();}}void delay(void){   volatile unsigned int i = 900000;   while(i--);}