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这样做的原因,不是象有些人说的那样,为了节约内存.事实上,这样做根本节约不了内存.理由如下:
1)如果一个函数func1调用另一个函数func2,那么func1,func2的局部变量根本就不能是同一块内存.C51还是要为他们分配不同的RAM.这跟使用堆栈相比,节约不了内存.
2)如果func1,func2不是在一个调用链上,那么C51可以通过覆盖分析,让它们的局部变量共享相同的内存地址.但这样也不会比使用堆栈节约内存.因为既然它们是在不同的调用链上,那么当其中一个函数运行时,那么另外一个函数必然不在其生命期内,它所占用的堆栈也已释放,归还给系统.
真实的原因(C51使用覆盖段作为局部变量的存放地的原因)是:
51的指令系统没有一个有效的相对寻址(变址寻址)的指令,这使得使用堆栈作为变量的代价太过昂贵.
使用堆栈存放变量的一般做法是:
进入函数时,保留一段堆栈空间,作为变量的存放空间,用一个可作为基址寻址的寄存器指向这个空间,通过加上一个偏移量,就可以访问不同的变量了.
例如: MOV EAX, [EBP + 14];X86指令
LDR R0, [R12, #14];ARM指令
都可以很好的解决这个问题.
但51缺少这样的指令.
*其实,51中还是有2个可变址寻址的指令的,但不适合访问堆栈的局部变量这样的场合.
MOVC A, @A+DPTR
MOVC A, @A+PC所以,C51有个特别的关键字: reentrant用来解决函数重入的问题.