微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > 关于可重入函数(可再入函数)和模拟堆栈(仿真堆栈)

关于可重入函数(可再入函数)和模拟堆栈(仿真堆栈)

时间:11-27 来源:互联网 点击:

,2,3,4);

MOVDPTR,#0xFFFF;模拟栈指针C?XBP最初指向0xFFFF+1

LCALLC?ADDXBP(C:00A6);调用C?ADDXBP子程序,调整模拟栈指针C?XBP

;指向0xFFFF

MOVA,#0x04;无寄存器可用,第四个参数直接压入模拟栈

MOVX@DPTR,A;

MOVR3,#0x03;参数3通过R3传递,见表2

MOVR5,#0x02;参数2过R5传递,见表2

MOVR7,#0x01;参数1通过R7传递,见表2

LCALLfun(C:0003);调用fun函数

MOVDPTR,#C_STARTUP(0x0000) ; fun函数返回值(int型)通过R6,R7传递回来

;并存储在外部数据存储器0x0000和0x0001处

;(int型为两个字节)

MOVA,R6

MOVX@DPTR,A

INCDPTR

MOVA,R7

MOVX@DPTR,A

}

RET;main返回

说明:模拟栈指针最初在startup.a51中初始化为0xFFFF+1;由以上汇编代码可以看出参数是从右往左扫描的。

接下来看看fun的汇编代码:(很长,大家耐心看吧,有些可以跳过的)

C:0003

MOVDPTR,#0xFFFF

LCALLC?ADDXBP(C:00A6);调整模拟栈指针C?XBP=C?XBP-1

MOVA,R3

MOVX@DPTR,A;R3中的值(参数3)压入模拟栈

MOVDPTR,#0xFFFF

LCALLC?ADDXBP(C:00A6);调整模拟栈指针C?XBP=C?XBP-1

MOVA,R5

MOVX@DPTR,A;R5中的值(参数2)压入模拟栈

MOVDPTR,#0xFFFF

LCALLC?ADDXBP(C:00A6);调整模拟栈指针C?XBP=C?XBP-1

MOVA,R7

MOVX@DPTR,A;R7中的值(参数1)压入模拟栈

MOVDPTR,#0xFFFC

LCALLC?ADDXBP(C:00A6);继续调整模拟栈指针C?XBP=C?XBP-4,为放两个

;局部int变量做准备

j1 = a + b + c +d;

MOVDPTR,#0x0005

LCALLC?XBPOFF(C:00CA);通过C?XBP的值调整DPTR使其指向模拟栈中第

;一个参数,此时DPTR=0xFFFF

;注意:C?XBPOFF不改变C?XBP的值

MOVXA,@DPTR

MOVR7,A;取出参数1

MOVA,R7

。。。。。。

。。。。。。;省略,完成取参数2,取参数3,取参数4并相加

。。。。。。

MOVDPH(0x83),?C_XBP(0x08)

MOVDPL(0x82),0x09;0x09就是?C_XBP+1

MOVA, R6

MOVX@DPTR,A

INCDPTR

MOVA,R7

MOVX@DPTR,A;计算结果j1压入模拟栈

j2 = j1 + 10;

。。。。。。

。。。。。。

。。。。。。;省略,完成j2=j1+10,并把计算结果j1压入模拟栈

return j2;

MOVDPH(0x83),?C_XBP(0x08)

MOVDPL(0x82),0x09

INCDPTR

INCDPTR

MOVXA,@DPTR

MOVR6,A

INCDPTR

MOVXA,@DPTR

MOVR7,A;从模拟栈取出j2送入R6,R7

}

MOVDPTR,#?C_XBP(0x0008)

LCALLC?ADDXBP(C:00A6);fun要返回,释放模拟栈,使C_XBP指向0xffff

RET

说明:模拟栈结构如下

参数4

参数3

参数2

参数1

j1低字节

j1高字节

J2低字节

J2高字节

接下来说明两个重点子函数C_ADDXBP和C_XBPOFF

C?ADDXBP:

MOVA,0x09;0x09即为C_XBP

ADDA,DPL(0x82);以下到第一个RET之前即完成:C_XBP+DPTR

MOVDPL(0x82),A

MOVA,?C_XBP(0x08)

ADDCA,DPH(0x83)

MOVDPH(0x83),A

CJNEA,?C_XBP(0x08),C:00B9

MOV0x09,DPL(0x82)

RET

C:00B9

JBCEA(0xA8.7),C:00C2;中断开着吗?开着就把它关了(清0),然后跳到C:00C2

MOV0x09,DPL(0x82);中断本来就关着,安全,下面的行动不会被打断,把新

;的模拟栈指针赋给C_XBP

MOV?C_XBP(0x08),A

RET

C:00C2

MOV0x09,DPL(0x82)

MOV?C_XBP(0x08),A

SETBEA(0xA8.7);开中断

RET

C?XBPOFF:;此函数的功能一看就明白,即完成DPTR=C_XBP+DPTR

MOVA,0x09

ADDA,DPL(0x82)

MOVDPL(0x82),A

MOVA,?C_XBP(0x08)

ADDCA,DPH(0x83)

MOVDPH(0x83),A

RET

终于到尾声了,最后重点说明啦~~~

模拟堆栈是向下生长的,C_XBP最初等于0xffff+1,那么请看下面这句

MOVDPTR,#0xFFFF

LCALLC?ADDXBP(C:00A6)

(0xffff+1)+0xffff = 0xffff

即C_XBP -1;

再看

MOVDPTR,#0xFFFE

LCALLC?ADDXBP(C:00A6)

即C_XBP-2

再看

MOVDPTR,#0xFFFE

LCALLC?ADDXBP(C:00A6)

即C_XBP-3

。。。

其实是这样:加0xffff相当与减1,加0xfffe相当与减2,加0xfffd相当于减4。。。。。。为啥,就不用说了吧:)

结束语:

经过了几天的研究,终于写了个总结报告,算是自己的一点小小成就吧,错误之处在所难免,希望能够同大家一起讨论问题,共同进步。

参考文献:

1、徐爱钧,彭秀华 《单片机高级语言C51windows环境编程与应用》电子工业出版社2001

2、彭光红,构造一个51单片机的实时操作系统。

附录:

在其它环境下(比如PC,比如ARM),函数重入的问题一般不是要特别注意的问题.只要你没有使用static变量,或者指向static变量的指针,一般情况下,函数自然而然地就是可重入的.

但C51不一样,如果你不特别设计你的函数,它就是不可重入的.

引起这个差别的原因在于:一般的C编译器(或者更确切点地说:基于一般的处理器上的C编译器),其函数的局部变量是存放于堆栈中的,而C51是存放于一个可覆盖的(数据)段中的.

至于C51这样做的原因,不是象有些人说的那样,为了节约内存.事实上,这样

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top