栈初始化
相关概念
栈是一种具有后进先出性质的数据组织方式,也就是说后存放的先取出,先存放的后取出。栈底是第一个进栈的数据所处的位置,栈顶是最后一个进栈的数据所处的位置。
根据SP指针指向的位置,栈可以分为满栈和空栈。
1、满栈:当堆栈指针SP总是指向最后压入堆栈的数据。
2、空栈:当堆栈指针SP总是指向下一个将要放入数据的空位置。
ARM采用满栈。
根据SP指针移动的方向,栈可以分为升栈和降栈。
1、升栈:随着数据的入栈,SP指针从低地址->高地址移动。
2、降栈:随着数据的入栈,SP指针从高地址->低地址移动。
ARM采用降栈。
ARM采用满降栈。
简单的讲,栈帧(stack frame)就是一个函数所使用的那部分栈,所有函数的栈帧串起来就组成了一个完整的栈。栈帧的两个边界分别由fp(r11)和sp(r13)来限定。
栈的作用:
1、保存局部变量
2、传递参数
3、保存寄存器值
初始化栈空间:
init_stack:
ldr sp, =0x54000000 @4是任意指定的,指向内存64M的位置
mov pc ,lr
BSS段初始化
BSS段的作用:
存放未初始化的全局变量。
BSS段初始化代码: 清空BSS段
clean_bss: @清空未初始化数据段 BSS段
ldr r1, =bss_start @在lds文件中定义的变量
ldr r2, =bss_end
cmp r1, r2
moveq pc, lr
clean_loop:
mov r0, #0
str r0, [r1], #4
cmp r1, r2
bne clean_loop
mov pc, lr
指令B和BL为相对跳转,直接给pc指针赋值就是绝对跳转,跳转到C语言代码需要使用绝对跳转,使用ldr指令给pc指针复制。
采用C语言编程序:
在完成了栈和bss段的初始化之后,可以使用c语言
bl init_stack
bl clean_bss
ldr pc, =gboot_main
@bl light_LED
C语言如下:
#define GPMCON (volatile unsigned long *)0x7F008820
#define GPMDAT (volatile unsigned long *)0x7F008824
void gboot_main()
{
*(GPMCON) = 0x1111;
*(GPMDAT) = 0b0101;
}
C与汇编混合编程
C语言与汇编语言的特点:
汇编语言:执行效率高;编写繁琐
C语言:可读性强,移植性好,调试方便
在对执行效率要求比较高,或者能够更直接地控制处理器(例如cpsr寄存器)的情况下,可以采用C和汇编混合编程。
汇编调用C函数
在上一部分的gboot_main()函数就是汇编对C函数的调用,直接使用ldr指令,给PC赋值,把函数名赋值给PC指针即可。
C调用汇编函数
直接用标号作为函数名,参数为空,以上面的代码为例,可以在c文件中调用汇编函数,如:
.global light_LED
void gboot_main()
{
light_LED();
}
但是,一定要注意:必须把汇编函数的标号先声明为全局的,否则C文件找不到汇编文件中的汇编函数标号。
C内嵌汇编
格式:
两个下划线+asm+两个下划线
1、汇编语句部分:汇编语句的集合,可以包含多条汇编语句,每条语句之间需要使用换行符 “\n”隔开或使用分号“ ; ”隔开,内容都要用引号套上。
2、输出部分:在汇编中被修改的C变量列表。
3、输入部分: 作为参数输入到汇编中的变量列表。
4、破坏描述部分: 执行汇编指令会破坏的寄存器描述。
在C语言的asm块中,需要使用//来表示注释。
举例1,向cpsr寄存器写入内容:
%0表示第0号参数,凡是要读的参数都要写入输入部分,”r”表示输入的性质是一个通用寄存器的值,具体是那个通用寄存器由系统自己选择,value是输入的变量的值。
举例2,从cpsr寄存器中读取内容
赋值给value。
举例3,使用volatile来阻止优化
使用volatile来告诉编译器,不要对接下来的这部分代码进行优化。
举例4,使用asm来内嵌点亮LED程序
__asm__(
"ldr r1, =0x7F008820\n"
"ldr r0, =0x1111\n"
"str r0, [r1]\n"
"ldr r1, =0x7F008824 \n"
"mov r0, #0b0101\n"
"str r0, [r1]\n"
:
:
: "memory"
);