ARM 裸机程序学习 03 发送SOS信号(汇编 + C)

  之前的两个示例程序都是完全由汇编编写的,而这次的示例程序由汇编和C语言写成并编译。

  汇编自有汇编的优点,比如你可以非常清楚地知道CPU在执行每条指令时到底做了什么,怎么做的,数据到底保存在哪儿等等,但是缺点也很明显,写汇编代码太痛苦,而且代码也不易阅读,所以C语言出现了。//其实,还包括C++

  从Code Warrior 的DebugRel Setting 中可以看到,Code Warrior 应该支持汇编,C ,C++ 三种语言。此外,在利用汇编和C语言编程时,要记得两个编译器都要设置成对应的ARM处理器架构。

  程序方面,这次要写的是一个发送SOS信号的程序(莫尔斯码:...---... ,即三短三长三短)。输出方式有很多,比如上两篇文章提到的LED灯或者蜂鸣器。考虑到扰民问题,决定使用LED作为输出。当然,这个示例代码的关注点仅仅是如何从汇编代码跳转到C代码。

汇编代码如下:

init.s

1     AREA |DATA|,CODE,READONLY
2     ENTRY
3     
4     LDR R13, =0x34000000
5     IMPORT ledMain
6     b ledMain
7     
8     END

  这段汇编代码非常简单:

4:LDR伪指令(有等号“=”),将 0x34000000 存入R13 寄存器,为堆栈初始化。

5:IMPORT伪指令,说明了一个跳转符号。比如,上两篇提到过的START,BEEPON 等。可以通过B 跳转指令来控制程序的执行顺序。而当这个符号并不在本文件中时,需要用IMPORT 来说明。从汇编代码跳转到C 代码,就需要这个指令。符号名称即为C 函数的函数名。

6:B 跳转指令,跳转到ledMain 处。即调用C 代码的ledMain 函数处。

有关第4行:

  在ARM 中,R13寄存器常用作堆栈寄存器。之前的纯汇编代码并没有用到R13,但也能运行。说明逻辑程序是并不一定需要一个堆栈。而这里之所以又用到了堆栈寄存器,是因为C 语言所编写的程序必须要一个堆栈。就是说,是C 需要堆栈,而不是ARM 需要。

  那么,如何确定这个初始化的值呢?答案不唯一,甚至可以随便一点。

一、C 程序的布局

  首先来看看C 程序的布局:

  如图,一般而言,C 程序具有固定的布局格式。一般而言,正在运行的程序从内存地址低到高分为如下几个“段”:

  1. 文本段(TEXT),存放代码以及常量,这些数据无论何时,都不会改变。也有人会把常量单独分为一个“常量段”,这也不无道理。简单起见,因为他们不会改变的特性,我把它们一起当做文本段。

注意:C 语言中,用CONST 声明的是一个“只读变量”,它本质上还是变量而非常量,利用指针,是可以改变其值的。

而只有那些在程序中被“写死”的数字符号才是真正意义上的常量。

  2. 数据段(DATA),存放在程序运行之初,已经初始化赋值过的变量。其中有全局变量以及静态变量(static)。

  3. BSS段(BSS),存放程序运行之初,尚未初始化赋值的变量。其中有全局变量以及静态变量。上图中写道会被初始化为0,但实际并不一定都是。比如C 中的野指针就是一个例子,很可能存放着垃圾数据。

  4. 堆(HEAP),动态储存区,只有程序执行时才会出现。常用的MALLOC 所分配的内存就在这个地方。增长方向为从内存地址低到高向上。

  5. 堆栈(STACK),动态存储区,和堆一样,只有程序执行时才会出现。在不考虑编译优化的情况下,只要调用函数,就会压栈。子程序返回,则出栈。增长方向和堆相反,为从内存地址高到低向下。另外,堆就是堆(HEAP),堆栈就是栈(STACK)。两者只是名字比较容易混淆,但是作用完全不一样。

一般而言,局部变量都会被存放在堆栈里。这也说明了为什么局部变量在子程序返回时就被销毁而不可用。

而ARM 在这个地方会有个特别之处:如果函数调用的参数在4个以内,会利用寄存器R0~R3来传递。只有超过部分才会利用栈来传递。

 

相关文章链接: C 程序内存分布ARM函数调用时参数传递规则

二、堆栈寄存器的初始化

  知道了程序在内存中的布局后,如何确定堆栈寄存器也变得有点眉目了。规则我觉得就两条:

  1.地址在内存范围里;2.堆栈不和数据段撞车。

  先讲第1条:首先要明确一点,此时我们的程序并没有启动内存管理单元MMU,真正的内存地址得看S3C2440的储存空间映射图:

  s3c2440有2种启动方式,从NOR FLASH 启动(左)和从NAND FLASH 启动(右)。

  从图中看到,并不是所有的地址都是所谓的“内存地址”。甚至地址也不一定是连续的。ARM 使用统一编址,就是乱七八糟设备的地址都混杂在一起的编址方式(- -)。所以,仔细观察上面这张图,应该立刻发现,我们得把堆栈指针设置到内存地址范围内,而不是随便挑一个自己喜欢的地址。

  具体的例子:64M的SDRAM作为内存,地址空间映射到BANK6,那么内存地址范围就是 0x30000000~0x34000000 (64*1024*1024=67108864,即 0x04000000)

  理论上而言,如果希望CPU运行起来非常简单,通电就可以,内存,硬盘什么都可以不要。但是,此时CPU仅仅是出于“运行”状态,不会去执行任何操作——因为没有指令可以执行。于是,我们给它加上了内存。这样,CPU可以从内存中得到指令,从而执行。并将执行结果放到内存里去。

  此时的计算机可以执行具体操作了,但是仍然不能完成任务。因为内存一掉电数据就丢失——想想一下只能一直开着的电脑。这依然不科学。于是,我们又加上了硬盘,里面存放着掉电也不会丢失的指令和数据。

  这样,计算机一启动,先把硬盘里的指令和数据放到内存,然后CPU从内存里获取指令和数据并执行,把结果返回到内存,再保存到硬盘。这样,关机后,硬盘里也存放着你希望得到的数据。

  s3c2440 拥有一块4K 的片内RAM ,可以看做是一个内存。但并没有任何片内ROM,也就是没有硬盘。在s3c2440 以NOR FLASH 启动时,NOR FLASH 相当于一块速度非常快的硬盘。它好像内存一样,CPU 直接从NOR FLASH 获取指令和数据,然后执行。而NAND FLASH 启动时,由于NAND FLASH 非常慢,相当于硬盘。在这种启动方式下,s3c2440 自动把NAND FLASH 最开始的4K 内容复制到片内RAM,然后再执行。

  所以,按NAND FLASH 启动时,堆栈寄存器可以设为片内RAM 的最大地址:0x1000(4K)或者属于SDRAM的地址,比如本例的 0x34000000。而千万不要设置成其他地址,比如ROM 的地址或者其他奇怪的地址。

  第2条堆栈不和数据段撞车:这就很好理解了。因为堆栈空间是向下增长,也就是向数据段(DATA)方向增长的。如果堆栈里的数据跑到数据段里把数据覆盖,肯定是出问题。当然,这也可以成为破解或者黑客的一种手段。

  本人使用AXD+JLINK来运行程序,实际操作下来 0x1000 和 0x34000000 都可以。

剩下的是C 代码:

ledMain.c

 1 #define GPBCON (*(volatile unsigned *)0x56000010)  //定义IO口地址
 2 #define GPBDAT (*(volatile unsigned *)0x56000014)
 3 #define GPBUP (*(volatile unsigned *)0x56000018)
 4 
 5 //延迟函数,通过让CPU空转实现
 6 void Delay(int x)
 7 {
 8     unsigned a;
 9     
10     while(x)
11     {
12         for(a=0xffff;a>0x0;a--);
13         x--;
14     }
15 }
16 
17 //LED灯亮灭部分,可参照上两篇文章
18 void ledup()
19 {
20     GPBDAT=~(1<<5);
21 }
22 
23 
24 void leddown(void)
25 {
26     GPBDAT=~0;
27 }
28 
29 //控制亮起时间长短和亮灭次数
30 void ledFreq(int t,int count)
31 {    
32     while(count--)
33     {
34         ledup();
35         Delay(t);
36         leddown();
37         Delay(7);
38     }
39 }
40 
41 //主函数
42 void ledMain()
43 {
44     GPBCON=0xddd7fc;  //对IO口的设置可以参考前两篇文章
45     GPBUP=0x0;
46     GPBDAT=~0x0;
47     
48     while(1)
49     { 
50         ledFreq(5,3);
51         Delay(30);
52         
53         ledFreq(15,3);
54         Delay(30);
55         
56         ledFreq(5,3);
57         Delay(30);
58     }
59 }

  本文涉及内容较多,如有谬误或者疏漏,敬请提出!

/*
 * Yiling Zhou
 * Shanghai, China
 * -.-- .. .-.. .. -. --. / --.. .... --- ..-
 * ... .... .- -. --. .... .- .. --..-- / -.-. .... .. -. .-
 */
原文地址:https://www.cnblogs.com/pastgift/p/2476158.html