1、再入函数不能传递bit类型参数。
2、与PL/M51兼容的函数不能具有reentrant特点,这样也不能调用再入函数。
3、在编译时:再入函数树立的是模仿仓库区,small形式下模仿仓库区坐落idata区,compact形式下模仿仓库区坐落pdata区,large形式下模仿仓库区坐落xdata区。
4、在同一程序中能够界说和运用不同存储器形式的再入函数,恣意形式的再入函数不能调用不同存储器形式的再入函数,但能够调用一般函数。
5、实践参数能够传递给直接调用的再入函数。无再入特点的直接调用函数不能包括调用参数。
—————————————————————————————————-----
KEIL C51可重入函数及模仿栈浅析
摘要:本文较具体的介绍了keil c51可再入函数和模仿仓库的一些概念和完结原理,经过一个简略的程序来剖析keil c51在大存储形式下可重入函数的调用进程,期望能为keil c51和在51系列单片机上移植嵌入式实时操作体系的初学者供给一些协助。
1、关于可重入函数(可再入函数)和模仿仓库(仿真仓库)
(1)局部变量存储在大局RAM空间(不考虑扩展外部存储器的状况);
(2)在编译链接时,即现已完结局部变量的定位;
(3)假如各函数之间没有直接或直接的调用联系,则其局部变量空间便可掩盖。
依据上述剖析咱们很简略就能够判别出TaskA和TaskB这两个函数是不行重入的(当然,func也不行重入)。那么怎么让函数成为可重入函数呢?C51编译器选用了一个扩展关键字reentrant作为界说函数时的选项,需要将一个函数界说为可重入函数时,只需在函数后边加上关键字reentrant即可。
与非可重入函数的参数传递和局部变量的存储分配办法不同,C51编译器为可重入函数生成一个模仿栈(相对于体系仓库或是硬件仓库来说),经过这个模仿栈来完结参数传递和寄存局部变量。模仿栈以大局变量?C_IBP、?C_PBP和?C_XBP作为栈指针(体系仓库栈顶指针为SP),这些变量界说在DATA地址空间,并且可在文件startup.a51中进行初始化。依据编译时选用的存储器形式,模仿栈区可坐落内部(IDATA)或外部(PDATA或XDATA)存储器中。如表1所示:
表1
留意:51系列单片机的体系仓库(也叫硬件仓库或惯例栈)总是坐落内部数据存储器中(SP为 8位寄存器,只能指向内部),并且是“向上成长”型的(从低地址向高地址),而模仿栈是“向下成长”型的。
2、可重入函数参数传递进程剖析
在进入剖析之前,先简略讲讲c51函数调用时参数是怎么传递的。简略来说,参数主要是经过寄存器R1~R7来传递的,假如在调用时,参数无寄存器可用或是选用了编译操控指令“NOREGPARMS”,则参数的传递将发生在固定的存储器区域,该存储器区域称为参数传递段,其地址空间取决于编译时所挑选的存储器形式。运用51单片机的作业寄存器最多传递3个参数,如表2所示。
表二
举两个比方:
func1(int a):“a”是第一个参数,在R6,R7中传递;
func2(int b,int c, int *d):“b”在R6,R7中传递,“c”在R4,R5中传递,“d”则在R1,R2,R3中传递。
至于函数的返回值经过哪些寄存器或是什么办法传递这儿就不说了,咱们能够看看c51的相关文档或是书本。
好了,接下来咱们开端剖析一个简略的程序,代码如下:
程序很简略,废话少说,下面跟我一同看看c51翻译成的汇编语言是什么姿态的(大存储形式下large XDATA)。
阐明:模仿栈指针开始在startup.a51中初始化为0xFFFF+1;由以上汇编代码能够看出参数是从右往左扫描的。
接下来看看fun的汇编代码:(很长,咱们耐性看吧,有些能够越过的)
阐明:栈结构如下
接下来阐明两个要点子函数C_ADDXBP和C_XBPOFF
总算到结尾了,最终要点阐明啦~~~
是向下成长的,C_XBP开始等于0xffff+1,那么请看下面这句
其实是这样:加0xffff适当与减1,加0xfffe适当与减2,加0xfffd适当于减3。。。。。。为啥,就不用说了吧:)
结束语:
经过了几天的研讨,总算写了个总结报告,算是自己的一点小小成果吧,过错之处在所难免,期望能够同咱们一同评论问题,共同进步。
附录:
在其它环境下(比方PC,比方ARM),函数重入的问题一般不是要特别留意的问题。只需你没有运用static变量,或许指向static变量的指针,一般状况下,函数自然而然地便是可重入的。
但C51不一样,假如你不特别规划你的函数,它便是不行重入的.
引起这个不同的原因在于:一般的C编译器(或许更确切点地说:根据一般的处理器上的C编译器),其函数的局部变量是寄存于仓库中的,而C51是寄存于一个可掩盖的(数据)段中的。
至于C51这样做的原因,不是象有些人说的那样,为了节省内存.事实上,这样做底子节省不了内存.理由如下:
1)
2)
实在的原因(C51运用掩盖段作为局部变量的寄存地的原因)是:
51的指令体系没有一个有用的相对寻址(变址寻址)的指令,这使得运用仓库作为变量的价值过分贵重.
运用仓库寄存变量的一般做法是:
进入函数时,保存一段仓库空间,作为变量的寄存空间,用一个可作为基址寻址的寄存器指向这个空间,经过加上一个偏移量,就能够拜访不同的变量了.
例如:
MOV EAX, [EBP + 14]
LDR R0, [R12, #14]
都能够很好的处理这个问题。但51短少这样的指令.
其实,51中仍是有2个可变址寻址的指令的,但不合适拜访仓库的局部变量这样的场合.
MOVC A, @A+DPTR
MOVC A, @A+PC
所以,C51有个特别的关键字: reentrant