zoukankan      html  css  js  c++  java
  • c语言环境初始化&c语言和汇编混合编程

    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 "//从%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 " //从c1中读出值写到%0中。
    :"=r"(value)@"="表示只写操作数,用于输出部分
    :
    :"memory");//表示内存的值被修改了,value存在于内存。
    return value;
    }
    ------------------------------------------------
    优化:
    unsigned long old;
    unsigned long temp;
    __asm__volatile(
    "mrs %0,cpsr "
    "orr %1,%0,#128 "
    "msr cpsr_c,%1 "
    :"=r"(0ld),"=r"(temp)
    :
    :"memory");
    使用volatile来告诉编译器,不要对接下来的这部分代码进行优化。
    -----------------------------------------------------
    使用内嵌汇编点亮led:
    start.S
    main.c
    #define GPKCON 0x7f008800
    #define GPKDAT 0x7f008808
    int gboot_main(){
    __asm__(
    "ldr r1,=0x11110000 "
    "str r1,[%0] "
    "ldr r1,=0xa0 "
    "str r1,[%1] "
    :
    :"r"(GPKCON),"r"(GPKDAT)
    :"r1"

    );
    return 0;
    }

  • 相关阅读:
    [Linux] expect命令 (自动交互脚本)
    [MAC] 终端bash_profile配置不生效问题
    [IDEA] 开发常用插件
    [MAC] 环境常用工具
    [IDEA] 快捷键输出固定代码模板
    家庭网络-多无线路由器实现无缝漫游
    家庭网络-AP组网方案(POE供电)
    家庭网络-软路由搭建方案
    队列使用
    [多线程] 线程池的使用
  • 原文地址:https://www.cnblogs.com/defen/p/4755809.html
Copyright © 2011-2022 走看看