We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
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); }
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)
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
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".