uboot-C语言环境初始化

1-栈初始化
栈是一种具有后进先出性质的数据组织方式,也就是说,后存放的先取出,先存放的后取出。
根据sp指针指向的位置,栈可以分为满栈和空栈。
满栈:当堆栈指针sp总是指向最后压入堆栈的数据。
空栈:当堆栈指针sp总是指向下一个将要放入数据的空位置。
ARM采用满栈!
 
根据SP指针移动方向,栈可以分为升栈和降栈。
升栈:随着数据的入栈,SP指针从低地址->高地址移动。
降栈:随着数据的入栈,SP指针从高地址->低地址移动。
ARM采用降栈。
ARM系统采用满降栈!!
栈帧:就是一个函数所使用的那部分栈,所有函数的栈帧串起来就组成了一个完整的栈。栈帧的两个边界分别由fp(r11)和sp(r13)来限定。

栈的作用
1.保存局部变量
2.参数传递
3.保存寄存器值

通过汇编来说明局部变量保存在栈中。
范例解析 stack1.c --- 保存局部变量
#include <stdio.h>

int main()
{
	int a;
	a++;
	return a;	
}
arm-linux-gcc -g stack1.c -o stack1
arm-linux-objdump -D -S stack1 > dump1
打开反汇编文件,然后找到main函数这里,分析汇编。
 
push    {fp}            ; (str fp, [sp, #-4]!)  把fp指针保存到堆栈中,push {fp}翻译过来就是分号后面的汇编代码。ARM采用的是满栈的方式,所以SP指针指向的数据,一定是有效的数据。将fp数据保存在sp-4 的位置。在修改fp之前,先保存。[sp, #-4]! “!”表示要将sp-4 的值再赋值给sp,是需要改变sp值。没有!是不改变sp值。
add     fp, sp, #0      ; 0x0  将sp的数据保存在fp中,也就是上一行代码sp-4之后的值。此时的fp正好是这个程序的栈开始地方。
sub     sp, sp, #12     ; 0xc   sp-12,
ldr     r3, [fp, #-8]     将fp-8的值存进r3寄存器   也就是说a存放在fp-8的这个位置  
add     r3, r3, #1      ; 0x1  执行a++
str     r3, [fp, #-8]     将计算结果存进fp-8位置,也就是放在变量a中。

stack2.c --- 传递参数
#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;
}

反汇编之后来分析汇编代码,
 
函数传递参数时,当参数<=4时,是利用r0-r3寄存器来传递。
当大于4个时候,采用栈来传递。
 
stack3.c --- 保存寄存器的值
#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;
}

 
在函数调用的过程中,栈保存寄存器的值。main中,把func1的两个参数保存在r0、r1中,func1又调用func2,func2的两个参数也是需要保存在r0、r1中的,这样的话就会修改原来r0、r1的值,所以,就要先把r0 r1的值保存在栈中。
代码编写:6410内存的起始地址为50000000,然后我们规定,64MB的地方为栈的起始地址。
init_stack:
	ldr sp, =0x54000000
	mov pc ,lr

2-初始化BSS段

初始化的全局变量:数据段
局部变量:栈

malloc:堆

未初始化的全局变量:bss段

bss.c

#include <stdio.h>
int year;
int main()
{
	year = 2017;
	return year;
}

arm-linux-gcc bss.c -o bss
arm-linux-readelf -a bss > dump
 
year的地址在bss开始和结束之间,所以为初始化的全局变量是放在bss段。
为什么要初始化bss段呢?因为有可能在写程序的时候,全局变量没有初始化就使用了。我们希望bss段中的值开始就是0的。
bss_start bss_end在连接器脚本当中定义了,代码编写如下:
clean_bss:
	ldr r0, =bss_start
	ldr r1, =bss_end
	cmp r0, r1
	moveq pc, lr
clean_loop:
	mov r2, #0
	str r2, [r0], #4
	cmp r0, r1
	bne clean_loop
	mov pc, lr

3-汇编语言跳到c语言运行

跳转有相对跳转和绝对跳转,
相对:b bl指令  利用两个标号之间的差值,将差值加到pc指针中完成跳转。
绝对:ldr pc, =main
在这之前,所有的代码都是运行在SRAM(垫脚石中,6410为4k),所有之前的b bl都是在SRAM中运行的。现在的板子中,垫脚石和内存中都有一份代码,跳转到c语言我们希望pc指针指向内存中的main,所以用ldr pc,=main

4-c与汇编混合编程
汇编语言:执行效率高,但编写繁琐
c语言:可读性强,移植性好,调试方便

为什么需要混合编程?
1.执行效率

2.能够更直接地控制处理器 访问MSR MRS CPSR寄存器等。

一、汇编调用C函数
ldr pc,=main   这就是汇编调用C函数。

二、c调用汇编函数

假设在start.s中用汇编实现了light_led 标号。

int gboot_main()
{
	light_led(); 
	return 0;
}


编译的时候会出错,没有定义light_led。汇编语言中所实现的标号(函数),想让其它文件引用,需要将标号声明为.global属性。

三、c内嵌汇编

格式:
__asm__(
	汇编语句部分
	:输出部分 -- 可能修改了c语言中变量的值修改了,这些变量就放在输出部分
	:输入部分 -- 可能需要从c语言中拿到一些参数作为汇编的操作数,这些参数就是放在输入部分
	:破坏描述部分 -- mov r0,#0 这样把r0原来的值破坏了,所以要放到这个地方来。  
);
C内嵌汇编以关键字”__asm__”或”asm”开始,下辖四个部分,各部分之间使用":"分开, 第一部分是必须写的,
后面三部分是可以省略,但是冒号:不能省略!

1.汇编语句部分:汇编语句的集合,可以包含多条汇编
语句,每条语句之间需要使用换行符 “\n”隔开或
使用分号“ ; ”隔开。
2.输出部分:在汇编中被修改的C变量列表
3.输入部分: 作为参数输入到汇编中的变量列表

4.破坏描述部分: 执行汇编指令会破坏的寄存器描述

void write_p15_c1(unsigned long value)
{
	__asm__(
		"mcr p15, 0, %0, c1,c0, 0\n"
		:
		: "r" (value)  @编译器选择一个rx寄存器。"r"表示是一个通用寄存器。值从value中来。
	);
}

凡是要去读的参数,都是放在输入部分,凡是要是写进去的参数,都是放在输出部分。

unsigned long read_p15_c1(void)
{
	unsigned long value;
	__asm__(
		"mcr p15, 0, %0, c1,c0, 0\n"
		: "=r" (value) @ "=" 表示只写操作数,用于输出部。r0中的值要存放到value变量中。
		:
		:  "memory"); //value变量放在栈中的,也就是内存中的值被修改了,通过“memory”通知系统有语句去修改内存。
	return value;
}

使用volatile来告诉编译器,不要对接下来的这部分代码进行优化

unsigned long old;
unsigned long temp;
__asm__ volatile(
	"mrs %0, cpsr \n"
	"orr %1, %0, #128 \n“
	"msr cpsr_c, %1\n"
	: "=r“ (old), "=r“ (temp)
	:
	:"memory");

使用内嵌汇编点亮LED

#define GPKCON 0x7f008800
#define GPKDAT 0x7f008808
int gboot_main()
{
	__asm__(
		"ldr r1, =0x11110000"
		"str r1, [%0]\n"
		"ldr r1, =0xa0\n"
		"str r1, [%1]\n"
		:
		:"r" (GPKCON), "r" (GPKDAT)
		:"r1"
	);
}


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值