bootloader通常会分为两个阶段:第一阶段采用汇编语言来编写,主要是一些核心的初始化工作(内存,时钟的初始化),第二阶段使用C语言来编写,主要是它会完成一些板载硬件的初始化(串口,网口)然后其启动我们的操作系统。所以我们需要先搭建好C语言环境。
-----------------------------------------------------
栈的初始化(只有一行,但信息量巨大,而且非常重要,对以后嵌入式的学习起了至关重要的作用):
栈:一种后进先出性质的数据组织方式。
满栈:当堆栈指针sp总是指向最后压入堆栈的数据
空栈:当堆栈指针sp总是指向下一个将要放入数据的空位置。
ARM采用满栈。
--------------------------------------------------
升栈和降栈:
升栈:sp指针从地地址到高地址
降栈:sp指针从高地址到地地址
ARM采用降栈。
-------------------------------------------
栈帧:就是一个函数所使用的那部分栈,所以函数的栈帧串起来就组成了一个完整的栈,栈帧的两个边界分别由fp(r11)和sp(r13)来限定。main与main函数中所有调用的函数,共同形成一个栈,main也是一个栈帧。
栈帧与栈帧之间的界定:上边界为fp指针,下边界为sp指针。
而main函数的边界保存在所调用的函数中,通过指针映射,将整个main函数包含进来。
---------------------------------------------------------
三个实例阐述栈的作用:
1.保存局部变量(局部变量保存在栈中):
#include<stdio.h>
int main()
{
int a;
a++;
return a;
}
arm-linux-gcc -g -o stack1 stack1.c
arm-linux-objdump -D -S stack1 >dump
vim dump
:/main
(str fp,[sp,#-4]!),将fp放到sp减去4个字节的位置,"!"表示sp减去4个字节后的值还要赋值给sp.
2.传递参数(当参数个数大于4是,使用栈来传递参数):
#include<stdio.h>
void func1(int a,int b,int c,int d,int e,int f)
{
int k;
k=e+f;
}
int main()
{
func1(1,2,3,4,5,6);
return 0;
}
arm-linux-gcc -g -o stack1 stack2.c
arm-linux-objdump -D -S stack2 >dump2
vim dump2
:/main
push {1,2}先压入的是2。
3.保存寄存器的值:
#include<stdio.h>
void func2(int a,int b)
{
int k;
k=a+b;
}
void func1(int a,int b){
int c;
func2(3,4);
c=a+b;
}
int main()
{
func1(1,2);
return 0;
}
arm-linux-gcc -g -o stack1 stack2.c
arm-linux-objdump -D -S stack2 >dump3
vim dump3
:/main
-------------------------------------------------------
栈初始化编程:
bl init_stack
bl light_led
init_stack:
ldr sp,=0x54000000 //210:0x24000000 //2440:0x34000000
mov cp,lr
-------------------------------------------------------
初始化bss段(就是对bss段进行整体性清零,防止为初始化的全局变量产生错误):
bss段的作用:
初始化的全局变量存放在数据段,局部变量存放在栈段,未初始化的全局变量存放在bss段,malloc内存分配在堆中。面试易考。
如:#include<stdio.h>
int year;
int main()
{
year=2014;
return year;
}
arm-linux-gcc bss.c -o bss
arm-linux-readelf -a bss >dump
vim dump
查看year是否存放在bss段的起止位置之间。
-------------------------------------------------
首先我们要找到bss段的起始位置,然后在bss段中全部填0.
起始位置可以在链接器脚本中找到。
bl clean_bss
clean_bss:
ldr r0,=bss_start
ldr r1,=bss_end
cmp r0,r1 //比较bss段的起始位置是否相等
moveq pc,lr //相等则返回pc
clean_loop:
mov r2,#0 //每次清零4个字节
str r2,[r0],#4 //将r2的值放到r0中
cmp r0,r1
bne clean_loop
mov pc,lr
--------------------------------------------------
一跃进入c大门:采用什么方式跃,检验是否跃成功。
相对跳转:b,bl:跳转到内存当中的main
绝对跳转:pc=....:跳转到SRAM中的main
vi main.c
int gboot_main()
{
return 0;
}
reset:
ldr pc,=gboot_main
vim makefile
加上++main.o
-------------------------------------------
接下来是验证C程序已经被执行到了。这次我们使用c语言来点亮led.打开之前编写的start.S文件,然后将里面用汇编语言写的bl light_led的代码都注释掉,将它的实现部分放到main.c中。
实际代码:
#define GPKCON (volatile unsigned long*) 0x7f008800
#define GPKDAT (volatile unsigned long*) 0x7f008800

int gboot_main()
{
*(GPKCON) = 0x11110000
*(GPKDAT) = 0xa0
return 0;
}
210的开发板需要起点处向后移16字节的头。
-----------------------------------------------
c与汇编混合编程:
C语言:可读性强,移植性好,调试方便
汇编语言:执行效率高,编写繁琐,能直接控制处理器(关键)。
1.汇编调用c函数:
ldr pc,gboot_main:调用C函数。
2.C语言中调用汇编函数:
直接将汇编函数名放到c语言的main中:light_led;还有一点,就是要将其申明为全局:
.global light_led
light_led:
3.c内嵌汇编:
格式:
__asm__(
汇编语句部分
:输出部分
:输入部分
:破坏描述部分
);
void write_p15_c1(unsigned long value){
__asm__(
"mcr p15,0,,%0,c1,c0,0\n"//从%0中读取参数放到c1中
:
:"r"(value)将value赋值给r,然后将r中的值放到c1中。
);
}
-----------------------------------------------------
实例2:
unsigned long read_p15_c1(void){
unsigned long value;
__asm__(
"mrc p15,0,%0,c1,c0,0\n" //从c1中读出值写到%0中。
:"=r"(value)@"="表示只写操作数,用于输出部分
:
:"memory");//表示内存的值被修改了,value存在于内存。
return value;
}
------------------------------------------------
优化:
unsigned long old;
unsigned long temp;
__asm__volatile(
"mrs %0,cpsr \n"
"orr %1,%0,#128 \n"
"msr cpsr_c,%1\n"
:"=r"(0ld),"=r"(temp)
:
:"memory");
使用volatile来告诉编译器,不要对接下来的这部分代码进行优化。
-----------------------------------------------------
使用内嵌汇编点亮led:
start.S
main.c
#define GPKCON 0x7f008800
#define GPKDAT 0x7f008808
int gboot_main(){
__asm__(
"ldr r1,=0x11110000\n"
"str r1,[%0]\n"
"ldr r1,=0xa0\n"
"str r1,[%1]\n"
:
:"r"(GPKCON),"r"(GPKDAT)
:"r1"

);
return 0;
}

c语言环境初始化&c语言和汇编混合编程的更多相关文章

  1. 【转】VxWorks中高精度实时时钟的实现及C语言汇编混合编程

    最近一个项目中需要在VxWorks下使用一个高精度实时时钟,要求精度为1ms,溢 出时间大于5小时.VxWorks提供系统时钟,该时钟在操作系统启动后开始计数,精度为1个tick,可以通过tickGe ...

  2. ARM中C和汇编混合编程及示例(转)

    在嵌入式系统开发中,目前使用的主要编程语言是C和汇编,C++已经有相应的编译器,但是现在使用还是比较少的.在稍大规模的嵌入式软件中,例如含有OS,大部分的代码都是用C编写的,主要是因为C语言的结构比较 ...

  3. C51与汇编混合编程详解

    C51和汇编混合编程(1)-C语言中嵌入汇编 1.在 C文件中要嵌入汇编代码片以如下方式加入汇编代码: #pragma ASM ;Assembler Code Here #pragma ENDASM ...

  4. Part10-C语言环境初始化-C与汇编混合编程lesson4

    1.为什么要混合编程 汇编语言:执行效率高:编写繁琐: 执行效率高:能够更直接地控制处理器. c语言:可读性强,移植性好,调试方便. 1.汇编调用c函数 2.c调用汇编函数 汇编语言定义的函数(标号) ...

  5. [Swift通天遁地]五、高级扩展-(13)图片资源本地化设置:根据不同的语言环境显示不同语言版本图片

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

  6. Part10-C语言环境初始化-一跃进入C大门lesson3

    1.跳转到c代码 因为内存中的代码来自于垫脚石SRAM,他们是相同的. 采用绝对跳转方式来完成. 因为我们是从汇编代码跳转到c语言的程序,所以我们要提前准备一个main.c文件. 修改makefile ...

  7. Part10-C语言环境初始化-栈初始化lesson1

    1.概念解析 ARM系统使用的是满栈! ARM采用降栈!!! 栈帧 每一个进程会有一个栈,该进程中的每一个函数会分割栈的一部分,那么每一个函数使用的那部分栈就叫做栈帧.那么所有栈帧组成了整个栈. 子函 ...

  8. Part10-C语言环境初始化-Bss段初始化lesson2

    1.BSS段的作用 初始化的全局变量存放在数据段: 局部变量存放在栈中: malloc的存放在堆: 未初始化的全局变量存放在BSS段: 找到bss段的起始与结束地址,往里面添加0,便初始化好了. 打开 ...

  9. arm:c语言和汇编混合编程

    仅作演示. 1.C和汇编可相互调用,汇编子函数格式参考 汇编:普通的函数调用的汇编代码解析 http://www.cnblogs.com/mylinux/p/4139972.html 本文演示了 : ...

随机推荐

  1. 学习Lucene、solr之前应当了解的一些术语

    一些简单易理解术语,例如:词条搜索.语义信息.搜索引擎 搜索引擎分类:全文搜索(百度.谷歌).目录搜索.元搜索.垂直搜索 元搜索例子:360综合搜索.搜魅网(someta 集合了百度.google.搜 ...

  2. 关于php存储cookie时path存放路径放的详细介绍

    以前在存储cookie时,没有用到path这个参数,最近开发中在存储cookie时,要用到path参数来决定存储的路径,结果一开始的时候一直取不到值,到最后详细看了一下path参数的介绍后,才顿悟.分 ...

  3. 一步步部署基于Windows系统的Jenkins持续集成环境

    如题:本文将介绍如何在Windows环境下运用Jenkins部署持续集成环境.之所以写本文,是因为在最近工作当中,学习使用Jenkins时,确实遇到了一些问题,而大多数教程文档都是基于Mac或是Lin ...

  4. Python3 的元组

    元组(tuple):戴上了枷锁的列表 元组与列表非常相似但是元组内元素的类型相同,且元组内的元素不能修改 1.创建元组的方法 与列表不同创建元组大部分情况下是用小括号,例如 tuple1=(1,2,3 ...

  5. Codeforces 895C - Square Subsets 状压DP

    题意: 给了n个数,要求有几个子集使子集中元素的和为一个数的平方. 题解: 因为每个数都可以分解为质数的乘积,所有的数都小于70,所以在小于70的数中一共只有19个质数.可以使用状压DP,每一位上0表 ...

  6. CCF系列之模板生成系统( 201509-3 )

    试题名称: 模板生成系统 试题编号: 201509-3 时间限制: 1.0s 内存限制: 256.0MB 问题描述 成成最近在搭建一个网站,其中一些页面的部分内容来自数据库中不同的数据记录,但是页面的 ...

  7. [知了堂学习笔记]_集合接口list与集合接口set的区别

    在Java中 除了 Map以外的集合的根接口都是Collection接口,而在Collection接口的子接口中,最重要的莫过于List和Set集合接口. 今天我们就来谈谈List集合接口与Set集合 ...

  8. Log4j源码解析--LoggerRepository和Configurator解析

    本文转自上善若水的博客,原文出处:http://www.blogjava.net/DLevin/archive/2012/07/10/382678.html.感谢作者的无私分享. LoggerRepo ...

  9. Android studio修改包名

    最后一步,需要手动修改工程 build.gradle文件中的applicationId改为跟你的包名一致即可,

  10. struts2 添加请求后缀的3种方式

    第一种方式在struts.xml文件中添加 <constant name="struts.action.extension" value="">&l ...