"); //-->
实验环境:TQ2440
实验例程:韦东山 嵌入式linux应用开发完全手册
3.1 根据数据手册设置sdram的寄存器
3.2 ATPCS规则分析
ATPCS是ARM-Thumb Procedure Call Standard 的缩写, 也就是ARM,Thumb的程序调用标准.
根据这张表显示, r0~r3一般用来传递函数的参数,r4~r7则用来放置局部变量. 而r12~r15则可以有特别的用途.
基本的ATPCS支持两种浮点数体系结构. FPA和AFP. FPA体系有八个可以放置单精度和双精度数的浮点寄存器. AFP体系有16个双精度寄存器.
参数调用分两种情况,参数可变的调用和参数个数固定的调用. 有以下的规则
* 小于32位的参数值会被自动扩展为32位.
* 64位的参数被当成两个32位数.
* 对于浮点数.如果芯片本身硬件上支持浮点运算,则浮点参数会放在浮点寄存器里传递. 如果硬件上不支持浮点数运算, 则转化为整型放通用寄存器传递.
* 其它类型通通转为32位整型数传递
* 对于参数可变的程序调用, 前四个参数放在r0~r3中传递,如果多于四个参则按相反的顺序进栈保存,所谓相反的顺序是指靠前参数后进栈.
* 对于固定参数的调用, 如果有可以做浮点运算的硬件部件, 各个浮点参数按顺序处理;为每个浮点参数分配FP寄存器;分配的方法是,满足该浮点参数需要的且编号最小的一组连续的FP寄存器.第一个整数参数通过寄存器R0~R3来传递,其他参数通过数据栈传递.
这是c文件:
#define GPBCON (*(volatile
unsigned long *)0x56000010)
#define GPBDAT (*(volatile
unsigned long *)0x56000014)
#define GPB5_out (1<<(5*2))
#define GPB6_out (1<<(6*2))
#define GPB7_out (1<<(7*2))
#define GPB8_out (1<<(8*2))
void wait(volatile unsigned long dly)
{
for(; dly
> 0; dly--);
}
int main(void)
{
unsigned
long i = 0;
//
LED1,LED2,LED3,LED4对应的4根引脚设为输出
GPBCON =
GPB5_out | GPB6_out | GPB7_out | GPB8_out;
while(1){
wait(30000);
GPBDAT =
(~(i<<5)); // 根据i的值,点亮LED1,2,3,4
if(++i ==
16)
i = 0;
}
return
0;
}
这是反汇编文件(一部分):
ARM架构中使用R12作为子程序间的scratch寄存器 (ATPCS中规定)。可以将R12 用于保存SP,在函数返回时使用该寄存器出栈,记作ip。可简单的认为暂存SP.
R11:stack frame
stack我们都知道,每一个进程都有自己的栈。考虑进程执行时发生函数调用的场景,母函数和子函数使用的是同一个栈,在通常的情况下,我们并不需 要区分母函数和子函数分别使用了栈的哪个部分。但是,当我们需要在执行过程中对函数调用进行backtrace的时候,这一信息就很重要了。
简单的说,stack frame就是一个函数所使用的stack的一部分,所有函数的stack frame串起来就组成了一个完整的栈。stack frame的两个边界分别由FP和SP来限定。
backtrace
在程序执行过程中(通常是发生了某种意外情况而需要进行调试),通过SP和FP所限定的stack frame,就可以得到母函数的SP和FP,从而得到母函数的stack frame(PC,LR,SP,FP会在函数调用的第一时间压栈),以此追溯,即可得到所有函数的调用顺序。
此图只是示例,不表示文中程序调用情况
注:好像fp的指向要往下一一格,直接指向pc的指针
300000b0 :
链接地址 机器码 汇编 注释
300000b0: e1a0c00d mov ip,
sp;ip指r12,将当前调用函数的堆栈保存
300000b4: e92dd800 stmdb sp!,
{fp, ip, lr, pc};sp=sp-4*4
;将fp寄存器和sp共用得到母函数的SP和FP,这里保存的fp指向main函数的pc
;lr保存调用函数的下一条指令的地址
;pc保存当前函数的入口地址,在回溯调用函数时很有用
300000b8: e24cb004 sub fp, ip, #4 ; 0x4 ;fp指r11,减4后fp指向wait函数的pc
显然通过回溯可以得到wait被main调用,main被启动函数调用
300000bc: e24dd004 sub sp, sp,
#4 ;
0x4,sp减1,准备局部变量i入栈
300000c0: e50b0010 str r0, [fp,
#-16];将实参值入栈
300000c4: e51b3010 ldr r3, [fp,
#-16]
300000c8: e3530000 cmp r3,
#0 ;
0x0
300000cc: 0a000003 beq 300000e0
300000d0: e51b3010 ldr r3, [fp,
#-16]
300000d4: e2433001 sub r3, r3,
#1 ;
0x1
300000d8: e50b3010 str r3, [fp,
#-16]
300000dc: eafffff8 b 300000c4
300000e0: e89da808 ldmia sp,
{r3, fp, sp, pc};局部变量到r3,fp到fp,ip到sp,lr到pc
;将R12用于保存SP,在函数返回时使用该寄存器出栈,记作ip,
300000e4 :
300000e4: e1a0c00d mov ip,
sp;ip指r12
300000e8: e92dd800 stmdb sp!,
{fp, ip, lr, pc} ;fp一般指r11,保存调用者的寄存器
300000ec: e24cb004 sub fp, ip,
#4 ;
0x4
300000f0: e24dd004 sub sp, sp,
#4 ; 0x4; sp减1,准备局部变量i入栈
;根据ATPCS,数据为满递减类型
300000f4: e3a03000 mov r3,
#0 ;
0x0 保存的是i的值
300000f8: e50b3010 str r3, [fp,
#-16] ;将i的值放入堆栈(局部变量)
300000fc: e3a03456 mov r3,
#1442840576 ;
0x56000000
30000100: e2833010 add r3, r3,
#16 ;
0x10
30000104: e3a02b55 mov r2,
#87040 ;
0x15400
30000108: e5832000 str r2,
[r3] ;设置GPBCON
3000010c: e3a00c75 mov r0,
#29952 ;
0x7500
30000110: e2800030 add r0, r0,
#48 ;
0x30 通过r0传递实参r0=30000
30000114: ebffffe5 bl 300000b0
;跳转到wait,下条指令地址保存在lr中
30000118: e3a02456 mov r2,
#1442840576 ;
0x56000000
3000011c: e2822014 add r2, r2,
#20 ;
0x14
30000120: e51b3010 ldr r3, [fp,
#-16]
30000124: e1a03283 mov r3, r3,
lsl #5
30000128: e1e03003 mvn r3,
r3
3000012c: e5823000 str r3,
[r2]
30000130: e51b3010 ldr r3, [fp,
#-16]
30000134: e2833001 add r3, r3,
#1 ;
0x1
30000138: e50b3010 str r3, [fp,
#-16]
3000013c: e3530010 cmp r3,
#16 ;
0x10
30000140: 1afffff1 bne 3000010c
30000144: e3a03000 mov r3,
#0 ;
0x0
30000148: e50b3010 str r3, [fp,
#-16]
3000014c: eaffffee b 3000010c
*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。