About reentrant function (A reentrant function) and an analog stack (stack simulation)

Author: xzp21st-mail:  [email protected]  author hard, please indicate the author and source

Keywords: keilc51, simulation stack reentrant function calls, parameter passing, C XBP, C ADDXBP??

Abstract: This paper describes in detail some of the concepts keilc51 may re-entry functions and analog stack and implementation principle, by a simple procedure to analyze the calling process keilc51 in the large memory model reentrant functions, and in hope for keilc51 transplant RTOS beginner provide some help on the 51 series.

 

About reentrant function (A reentrant function) and an analog stack (stack simulation)

"Reentrant functions can be invoked more than one task without fear of data corruption. Reentrant function can be interrupted at any time, after a period of time and can be run, and the corresponding data is not lost." (Excerpt from embedding RTOS uC / OS-II)

Prior to understand these concepts, we must first talk about keilc51 "overlay technology." (The reason for using this technique see Appendix explained a friends)

(1) global variables are stored in local RAM space (without considering the case of extended external memory);

(2) When compiled and linked, i.e. a local variable positioning has been completed;

(3) If there is no direct or indirect relationship between the function call, then the local variable space which can be covered.

It is for these reasons, in the Keil C51 environment, pure function if not addressed (such as adding a simulated stack) is not reentrant. for example:

void TaskA(void* pd)

{

int a;

// define other variables

 

do{

// actual user task handling code

}while(1);

}

 

void TaskB(void* pd)

{

int b;

// define other variables

 

do{

func();

// other practical user task handling code

}while(1);

}

 

void func()

{

int c;

// define other variables

 

// function processing code

}

 

In the above code, there is no TaskA TaskB with direct or indirect calls, such that their local variables a and b is to be overwrite one another, i.e., they may both be positioned a certain same RAM space. Thus, when TaskA running for some time, has changed a, when TaskB obtain control over the CPU and run, it may change b. Since a and b refer to the same RAM space, resulting in TaskA regain control of the CPU, a value has changed, the program causing incorrect operation, turn versa. On the other hand, func () has a direct relationship with the calling TaskB, and therefore its local variables b and c each are not covered, but can not guarantee func local variable c is not formed or local variable TaskA other tasks may cover relationship.

 

According to the above analysis we can be determined easily and TaskA TaskB these two functions are not reentrant (of course, func nor reentrant). So how to become a function reentrant function? C51 compiler option of using a defined function expansion keyword as reentrant, the function needs to be defined as a reentrant function, as long as together after the function key can be reentrant.

 

Storage allocation method and pass parameters and local variables of the non-reentrant different function, the compiler generates a C51 analog for the reentrant stack function (relative to the system or hardware stack for the stack), the simulation parameters to complete the stack transfer and store local variables. Stack to simulate global variables? C_IBP,? C_PBP and? C_XBP as a stack pointer (stack pointer of the SP system stack), these variables are defined in the DATA address space, and can be initialized in startup.a51 file. The memory model used at compile time, the analog may be located within the stack area (the IDATA) or external (or PDATA the XDATA) memory. As shown in Table 1:

 

Storage mode

Stack Pointer

The stack area

Small

? C_IBP (1 byte)

Indirect internal data memory access (IDATA), a maximum of 256 bytes of stack region

Compact

? C_PBP (1 byte)

Paged addressing external data memory (PDATA), the stack area is 256 bytes maximum

Large

? C_XBP (2 bytes)

An external data memory (XDATA), a maximum of 64K stack area

Table 1

Note: the system stack 51 computers (also known as a conventional hardware stack or stack) is always located in the internal data memory (SP 8-bit register, only the internal point), and is "grown up" type (from the lower address high address), and the stack is a simulated "down growth" type.

 

<-! [! If supportLists] -> 2, <-! [Endif] -> reentrant function parameter transfer process Analysis

Before entering the analysis, when c51 briefly talk about how the function call parameters passed. Briefly, mainly through the parameter registers R1 ~ R7 to pass, and if the call parameters or using no register is available compilation control command "NOREGPARMS", the transmission parameters will occur in the fixed memory area, the memory passing a parameter called segment region, which depends on the memory address space of the selected pattern is compiled. MCU 51 using working register transfer up to three parameters, as shown in Table 2.

 

Parameter passing

char, 1 byte pointer

int, 2-byte pointer

long、float

General Pointer

The first argument

R7

R6, R7

R4 ~ R7

R 1, R 2, R 3

The second parameter

R5

R4,R5

R4 ~ R7

R 1, R 2, R 3

The third parameter

R3

R2, R3

no

R 1, R 2, R 3

Table II

Here are two examples:

func1 (int a): "a" is the first argument, passed in R6, R7;

func2 (int b, int c, int * d): "b" passed R6, R7 are, "c" passed R4, R5 are, "* d" is passed in R1, R2, R3 in.

As for the return value of the function which registers passing here or what method did not say, we can see c51 relevant documents or books.

 

Well, then we began to dissect a simple program code is as follows:

int fun (char a, char b, char c, char d) reentrant // To simplify analysis, the type parameters are char;

{

int j1,j2;

 

j1 = a + b + c +d;

j2 = j1 + 10;

return j2;

}

 

main()

{

int i;

i = fun(1,2,3,4);

}

 

The procedure is simple, without further ado, here with me to see c51 translated into assembly language is what it looks like.

main()

{

int i;

i = fun(1,2,3,4);

MOV DPTR, # 0xFFFF; simulated stack pointer C? XBP initially points to 0xFFFF + 1

LCALL C ADDXBP (C: 00A6);? Call C? ADDXBP subroutine, adjust the analog stack pointer C? XBP

; Points to 0xFFFF

MOV A, # 0x04; no register available, the fourth parameter stack pressure directly into the analog

MOVX @DPTR,A ;

MOV R3, # 0x03; R3 transmitted through the 3 parameters, see Table 2

MOV R5, # 0x02; R5 passed through 2 parameters, see Table 2

MOV R7, # 0x01; R7 passing through a parameter, see Table 2

LCALL fun (C: 0003); function call fun

MOV DPTR, # C_STARTUP (0x0000); fun function return value (int type) by R6, R7 is passed back

; And data stored in the external memory at 0x0000 and 0x0001

; (Int type is two bytes)

MOV A,R6

MOVX @DPTR,A

INC DPTR

MOV A,R7

MOVX @DPTR,A

}

RET; main return

 

Description: Analog initial stack pointer is initialized to 0xFFFF + 1 in startup.a51; and assembly code can be seen from the above parameters are scanned from right to left.

 

Then take a look fun :( assembly code is very long, be patient and see, some can be skipped)

 

C:0003

MOV DPTR,#0xFFFF

? LCALL C ADDXBP (C: 00A6); adjust the analog stack pointer C? XBP = C? XBP-1

MOV A,R3

MOVX @ DPTR, A; values ​​of R3 (parameter 3) is pressed into an analog stack

MOV DPTR,#0xFFFF

? LCALL C ADDXBP (C: 00A6); adjust the analog stack pointer C? XBP = C? XBP-1

MOV A,R5

MOVX @ DPTR, A; R5 is the value (parameter 2) is pressed into an analog stack

MOV DPTR,#0xFFFF

? LCALL C ADDXBP (C: 00A6); adjust the analog stack pointer C? XBP = C? XBP-1

MOV A,R7

MOVX @ DPTR, A; values ​​of R7 (Parameter 1) is pressed into an analog stack

MOV DPTR,#0xFFFC

? LCALL C ADDXBP (C: 00A6); continue to adjust the analog stack pointer C? XBP = C? XBP-4, to put two

; Local int variables in preparation

j1 = a + b + c +d;

MOV DPTR,#0x0005

LCALL C XBPOFF (C: 00CA);? Through C? DPTR XBP adjustment value to point to the stack of simulation

; A parameter at this time DPTR = 0xFFFF

; Note: C XBPOFF does not change the C?? The value of XBP

MOVX A,@DPTR

MOV R7, A; 1 Parameter removed

MOV A,R7

。。。。。。

. . . . . . ; Omitted, taken to complete the parameter 2, 3 take parameters, parameters taken and summed 4

。。。。。。

MOV DPH(0x83),?C_XBP(0x08)

MOV DPL (0x82), 0x09;? 0x09 is C_XBP + 1

MOV A, R6

MOVX @DPTR,A

INC DPTR

MOV A,R7

MOVX @ DPTR, A; simulation results j1 pressed stack

 

j2 = j1 + 10;

。。。。。。

。。。。。。

. . . . . . ; Omitted completed j2 = j1 + 10, and the calculated result is pressed into analog stack j1

 

return j2;

MOV DPH(0x83),?C_XBP(0x08)

MOV DPL(0x82),0x09

INC DPTR

INC DPTR

MOVX A,@DPTR

MOV R6,A

INC DPTR

MOVX A,@DPTR

MOV R7, A; j2 taken into the stack from the analog R6, R7

}

MOV DPTR,#?C_XBP(0x0008)

? LCALL C ADDXBP (C: 00A6); fun to return, release simulated stack, so C_XBP point to 0xffff

RIGHT

 

Description: Analog stack structure below

Parameter 4

Parameter 3

Parameter 2

Parameter 1

j1 low byte

j1 high byte

J2 low byte

J2 high byte

 

Next, two key sub-functions C_ADDXBP and C_XBPOFF

C?ADDXBP:

MOV A, 0x09; 0x09 is the C_XBP

ADD A, DPL (0x82); prior to the first complete RET: C_XBP + DPTR

MOV DPL(0x82),A

MOV A,?C_XBP(0x08)

ADDC A,DPH(0x83)

MOV DPH(0x83),A

CJNE A,?C_XBP(0x08),C:00B9

MOV 0x09,DPL(0x82)

RIGHT

 

C:00B9

JBC EA (0xA8.7), C: 00C2; break open? Open put it off (cleared), then jumps C: 00C2

MOV 0x09, DPL (0x82); interruption already closed, security, the following actions will not be interrupted, the new

; Analog stack pointer assigned C_XBP

MOV ?C_XBP(0x08),A

RIGHT

 

C:00C2

MOV 0x09,DPL(0x82)

MOV ?C_XBP(0x08),A

SETB EA (0xA8.7); the opening break

RIGHT

? C XBPOFF:; This function is a self-explanatory, complete DPTR = C_XBP + DPTR

MOV A,0x09

ADD A,DPL(0x82)

MOV DPL(0x82),A

MOV A,?C_XBP(0x08)

ADDC A,DPH(0x83)

MOV DPH(0x83),A

RIGHT

 

Finally come to an end, the last highlights friends ~ ~ ~

Analog stack grows downward, C_XBP initially equal to 0xffff + 1, then look at the following sentence

MOV DPTR,#0xFFFF

LCALL C?ADDXBP(C:00A6)

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

I.e. C_XBP -1;

 

Look

MOV DPTR,#0xFFFE

LCALL C?ADDXBP(C:00A6)

I.e. C_XBP-2

 

Look

MOV DPTR,#0xFFFE

LCALL C?ADDXBP(C:00A6)

That C_XBP-3

。。。

In fact, this is: equivalent to minus 1 plus 0xffff, add and subtract relatively 0xfffe 2, plus minus 4 0xfffd equivalent. . . . . . Why, you would not have said it :)

 

Conclusion:

After a few days of research, and finally wrote a summary report, regarded as a bit of their achievement of it, is wrong with the inevitable, hoping to discuss the problem with you, and common progress.

 

 

references:

1, Xu Aijun, Peng Xiuhua "SCM C51windows high-level language programming environment and application of" Electronic Industry Press 2001

2, Peng red light, a real time operating system 51 configured microcontroller.

 

appendix:

Problems in other environments (such as PC, such as ARM), a function of re-entry problems are generally not pay special attention. As long as you do not use static variables, or pointers to static variables, under normal circumstances, a function naturally is reentrant of.

C51 but not the same, especially if you do not design your function, it is not reentrant.

This difference causes are: ordinary C compiler (or more precisely said points: C compiler based on the general processor), a local variable whose function is located in the stack, and is located on a C51 It may be covered by the segment (data).

As for the reason C51 to do so, not as some people say, in order to save memory, in fact, doing so simply can not conserve memory reasons as follows:

1) If a function calls another function func1 func2, then func1, func2 local variables simply can not be the same piece of memory .C51 or to assign different RAM for them. This is compared with the use of the stack, can not save memory.

2) If func1, func2 not on a call chain, the C51 can be covered by the analysis, so that their local variables share the same memory address. But this will not save memory than using the stack, because since they are in different calls on the chain, then when one of the function is running, then the other is not necessarily a function of its lifetime, it has also been occupied by the stack release, returned to the system.

The real reason (due to the use of C51 as the overlay variables stored locally) is:

Instruction 51 is not a valid instruction relative addressing (Indexed), which makes the cost of using the stack as a variable too expensive.

The general practice is to use the stack to store variables:

Entering function, stack space retained for a period, as the variable storage space, can be used as a base address register points to the address of this space, by adding an offset to access the different variables.

For example: MOV EAX, [EBP + 14]; X86 instruction

LDR R0, [R12, # 14]; ARM instruction

It can be a good solution to this problem.

51, but the absence of such an instruction.

* Local variables fact, there are 2 51 or a variable index addressing instruction, but not for access to the stack such occasions.

MOVC A, @A+DPTR

MOVC A, @A+PC

So, C51 has a special keyword: reentrant functions used to solve the problem of re-entry

 

Forwarded From: https://blog.csdn.net/hjhcs121/article/details/8732180

Guess you like

Origin www.cnblogs.com/WayneKhouTech/p/12045908.html