This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

How does compiler C51 realize the key word reentrant?

In the following example,the chip is Dallas390 and the off-chip xdata starts from x:0x20000.Large model and large reentrant function stack is selected.
The C language source:

unsigned char func1(unsigned char)reentrant;

void main()
{
	unsigned char i = 0;
	i = func1(i);
}

unsigned char func1(unsigned char i)reentrant
{
	return (i);
}
After compiled by C51,the assembly language source of function func1:
     9: unsigned char func1(unsigned char i)reentrant
    10: {
C:0x000064  9000FFFF MOV      DPTR,#0x00FFFF
C:0x000068  110003   ACALL    C?ADDXBP(C:000003)
C:0x00006B  EF       MOV      A,R7
C:0x00006C  F0       MOVX     @DPTR,A
    11:         return (i);
C:0x00006D  850883   MOV      DPH(0x83),0x08
C:0x000070  850982   MOV      DPL(0x82),0x09
C:0x000073  759302   MOV      DPX(0x93),#0x02
C:0x000076  E0       MOVX     A,@DPTR
C:0x000077  FF       MOV      R7,A
    12: }
C:0x000078  90000001 MOV      DPTR,#0x000001
C:0x00007C  010003   AJMP     C?ADDXBP(C:000003)
The library subroutine C?ADDXBP:
                 C?ADDXBP:
C:0x000003  759302   MOV      DPX(0x93),#0x02
C:0x000006  E509     MOV      A,0x09
C:0x000008  2582     ADD      A,DPL(0x82)
C:0x00000A  F582     MOV      DPL(0x82),A
C:0x00000C  E508     MOV      A,0x08
C:0x00000E  3583     ADDC     A,DPH(0x83)
C:0x000010  F583     MOV      DPH(0x83),A
C:0x000012  B50804   CJNE     A,0x08,C:000019
C:0x000015  858209   MOV      0x09,DPL(0x82)
C:0x000018  22       RET
C:0x000019  10AF06   JBC      EA(0xA8.7),C:000022
C:0x00001C  858209   MOV      0x09,DPL(0x82)
C:0x00001F  F508     MOV      0x08,A
C:0x000021  22       RET
C:0x000022  858209   MOV      0x09,DPL(0x82)
C:0x000025  F508     MOV      0x08,A
C:0x000027  D2AF     SETB     EA(0xA8.7)
C:0x000029  22       RET
I am confused with how the simulated stack is realized.What's the machanicsm of re-entered function in C51?
Thanks in advance.


  • A C compiler for a typical architecture will push parameters and local values onto a stack. This implementation makes a function naturally reentrant (as long as it isn't deliberately accessing globals and producing untoward side effects). If you call the routine again, another set of parameters and locals goes onto the stack, so the two calls do not interfere with each other.

    The hardware stack pointer in the 8051 can only address internal RAM. Usually, you have a stack of 128 bytes or less. This is not a lot of room for the usual stack manipulation. So you might look for another answer.

    By default, the Keil compiler does a lot of compile- (and link-) time analysis to determine the usage patterns of those parameters and locals based on the call tree in the program. You can optimize the amount of space that would be the stack in this manner, and generate code that directly references memory instead of addressing indirectly via the stack. The code is smaller and faster -- but routines will no longer be naturally reentrant. Each call to a routine will use the same memory locations for its parameters and locals, so a second call will trash the data from the first, whether that second call is recursive or from another context (task or interrupt).

    Sometimes, of course, the restrictions imposed by this optimization get in the way, and you want a routine that really is reentrant. When you declare a routine reentrant, the Keil compiler generates code pretty much as you'd expect of a typical compiler. It pushes excess parameters and locals onto a stack, just like any other compiler.

    Since the 8051 doesn't have hardware support for a stack to speak of -- no spare 16-bit registers with auto-increment and decrement instructions and that sort of thing -- the compiler uses what Keil likes to call a "simulated" stack. The compiler quite simply declares a 16-bit variable, and uses that as a stack pointer into a region of memory determined by your memory model (xdata for the large model). There's no hardware register designated as a stack pointer, but there's no reason the compiler writers can't make up their "register" and get the same effect. It takes more code than on most processors, but that's the 8051 for you.

    You set up the stack space in your STARTUP.A51 file. See the second section, right after the power-on initialization of memory configuration.

    So, in the first couple of lines in your function (the "prologue"), you see the compiler pushing onto this stack. Load up -1 (0FFFFH) in the DPTR, call ADDXBP, which looks like it adds DPTR to the stack pointer, then move R7 (via A) to the new address. From the code, it looks like the stack pointer happens to be linked at memory locations 8/9; check your map file for the symbol ?C_XBP if you're curious. Those four lines of code implement "push R7".

    You then see the implemention of func1(). It returns its value in R7, so it loads up DPTR with the stack pointer, and moves R7 (via A) from the stack.

    The end of the routine (the "epilogue") reverses the initial behavior. Load up DPTR with the stack pointer, load R7 (via A) from the stack, and add 1 to the stack pointer. That's "pop".