📄 c516.txt
字号:
PUSH DPL
PUSH PSW
MOV PSW,#08H
sys_interp() Entry Code
Still using registerbank 0
; unsigned char sys_interp(unsigned char x_value,
RSEG ?PR?_sys_interp?INTERP
USING 0
_sys_interp:
MOV y_value?10,R5
MOV map_base?10,R2
MOV map_base?10+01H,R3;
--Variable 'x_value?10' assigned to Register 'R1' --
MOV R1,AR7 <----- FAILS!!!!
Absolute register addressing used assuming registerbank 0 is still current a
nd so program fails! (Solutions in 5.3.6-8).
5.3.4 Notes on C51's "Stack Frame"
C51 uses a degree of intelligence when entering interrupt functions. Besides
the obvious step of substituting RETI for RET at the end of the function, i
t automatically stacks only those registers that are actually used in the fu
nction.
There are however, some points to be aware of:
If an interrupt function calls a function, C51 will stack all the Ri registe
rs, regardless of whether they are used or not. The total time to PUSH and P
OP these is 16us at 12MHz, which may be viewed as unacceptable for a time cr
itical interrupt.
Therefore you should either avoid calling functions or use the USING control
. This will do a simple registerbank switch at the entry and exit from the r
outine. As the PUSHING of registers onto the stack uses the same overall num
ber of DATA locations, there is no difference in overall RAM usage.
Any variable declared within an interrupt function will not be overlaid onto
background data or that originating from other interrupts.
Never call an interrupt function from the background. There is sometimes a t
emptation to do this during program initialisation, for example. The linker
will get very confused and will quite likely make dangerous mistakes like ov
erwriting background variables!
Using the USING control will generally consume more RAM than simply PUSHing
registers onto the stack: in the case where the interrupt function employs l
ess than 8 registers, 8 - <number of registers actually used> will be wasted
. Thus there is no virtue in avoiding the USING control!
Interrupts of equal priority can share the same register bank as there is no
chance of them interrupting each other.
5.3.5 When To Use USING
Interrupts which must run as fast as possible, regardless of overall RAM usa
ge.
Interrupts which call other functions.
5.3.6 The NOAREGS pragma
Dealing With C51's Absolute Register Addressing.
As has been pointed out, the 8051 has no MOV Register, Register instruction
so the compiler uses MOV R1,AR7 where AR7 is the absolute address of the cur
rent R7. To do this though, the current registerbank number must be known. I
f a function is called from an interrupt where a using is in force, when com
piling a called function the compiler must be told:
(i) not to use absolute register addressing with #pragma NOAREGS control bef
ore the function, and #pragma RESTORE or #pragmas AREGS control enter the fu
nction.
Or:
(ii) the current registerbank number with #pragma REGISTERBANK(n).
For (i), applying NOAREGS to the sys_interp function removes the MOV R7,AR7,
replacing it with an awkward move of R7 to R1 using XCH A,Ri!
timer0_int Entry Code
; void timer0_int(void) interrupt 1 using 1 {
RSEG ?PR?timer0_int?TIMER0
USING 1
timer0_int:
PUSH ACC
PUSH B
PUSH DPH
PUSH DPL
PUSH PSW
MOV PSW,#08H
sys_interp() Entry Code With NOAREGS
; unsigned char sys_interp(unsigned char x_value,
RSEG ?PR?_sys_interp?INTERP
USING 0
_sys_interp:
MOV y_value?10,R5
MOV map_base?10,R2
MOV map_base?10+01H,R3;
--Variable 'x_value?10' assigned to Register 'R1' --
XCH A,R1 ;
MOV A,R7 ; Slow Reg to Reg move
5.3.7 The REGISTERBANK Control Alternative To NOAREGS
#pragma REGISTERBANK(n) tells C51 the absolute address of the current "using
" registerbank base so that direct register addressing will work.
EXAMPLE:
/* Timer 0 Overflow Interrupt Service Routine */
timer0_int() interrupt 1 USING 1 {
unsigned char temp1 ;
unsigned char temp2 ;
/* executable C statements */
}
Called function:
#pragma SAVE // Rember current registerbank
#pragma REGISTERBANK(1) // Tel C51 base address of current registerbank.
void func(char x) { // Called from interrupt routine
// with "using1"
/* Code */
}
#pragma RESTORE // Put back to original registerbank
Applying #pragma REGISTERBANK(1) to sys_interp() restores absolute register
addressing as C51 now knows the base address of the current register bank.
Note: Always try to use the REGISTERBANK(n) control for any functions called
from an interrupt with a USING!
sys_interp() Entry Code With REGISTERBANK(n)
; unsigned char sys_interp(unsigned char x_value,
RSEG ?PR?_sys_interp?INTERP
USING 1
_sys_interp:
MOV y_value?10,R5
MOV map_base?10,R2
MOV map_base?10+01H,R3;--
Variable 'x_value?10' assigned to Register 'R1' --
MOV R1,AR7
5.3.8 Summary Of USING And REGISTERBANK
Expressed in psuedo-code!
if(interrupt routine = USING 1){
subsequently called function uses #pragma REGISTERBANK(1)
}
Note: subsequently called function must now only be called from functions us
ing register bank 1.
5.3.9 Reentrancy In C51 - The Final Solution
In addition to calling a function from interrupt, it is also sometimes neces
sary to call the same function from the background as well. This leaves the
possibility open that the function may be called from two places simultaneou
sly with disasterous results!
The attribute required to permit a function to be safely called both from ba
ckground and interrupt routines simultaneously is "reentrant". This can also
help in the previous situation of a function being called from an interrupt
. The linker's "MULTIPLE CALL TO SEGMENT" warning is the first sign that you
may be trying to use a function reentrantly.
Due to the way that C51 allocates storage for local variables and parameters
, it is not possible to call a function from both an interrupt and the backg
round loop. To allow only those functions to be used reentrantly that really
need to be, it is possible to specify the reentrant attribute when declarin
g a function.
The ?C_IBP value set up in startup.a51 tells C51 where to locate the artific
ial stacks used for reentrant functions. Each time a reentrant function is c
alled, its incoming parameters are moved from the registers in which they we
re passed into an area of RAM, starting at the address indicated by ?C_IBP.
Likewise, any local variables used by the reentrant function are also alloca
ted a place on this special stack.
When startup.a51 is executed before main(), the line:
IF IBPSTACK <> 0
EXTRN DATA (?C_IBP)
MOV ?C_IBP,#LOW IBPSTACKTOP
ENDIF
initialises ?C_IBP to the value of IBPSTACKTOP that you set up earlier. As e
ach local is "pushed" on to the reentrant stack, ?C_IBP is decremented. Thus
if an interrupt occurs which calls the function again, the new call will st
art its reentrant stack from the current ?C_IBP value. Thereafter, any local
data or parameter is accessed by the code sequence:
Get a local variable at offset 2 from the current base of the re-entrant sta
ck:
MOV R0,?C_IBP ; Get stack base
MOV A,@R0 ; Add offset of local
ADD A,#002 ;
MOV A,@R0 ; Get local via indirect addressing.
MOV R7,A ; Store value whilst other local is ;
accessed.
On leaving the function, ?C_IBP is restored to entry value by adding the tot
al number of locals and parameters that were used. This represents a very la
rge overhead and shows why reentrancy should only be used where absolutely n
ecessary.
EXAMPLE:
The Reentrant Stack When Located In The IDATA Area
0xff sys_interp parameter 0
0xfe sys_interp parameter 1
0xfd sys_interp parameter 2L
0xfc sys_interp parameter 2H - call from background:
?C_IBP= 0xfc
0xfb sys_interp parameter 0
0xfa sys_interp parameter 0
0xf9 sys_interp parameter 1
0xf8 sys_interp parameter 2L
0xf7 sys_interp parameter 2H - call from timer0
interrupt: ?C_IBP = 0xf7
0xf6 sys_interp parameter 0
0xf5 sys_interp parameter 0
0xf4 sys_interp parameter1
0xf3 sys_interp parameter 2L
0xf2 sys_interp parameter 2H - call from background
?C_IBP = 0xf2
0xf1
0xf0
0xef
0xee
?C_IBP acts as a base pointer to the reentrant stack and is used to access a
ll locals in a reentrant function.
Adding the reentrant attribute to sys_interp() still requires the NOAREGS co
ntrol as the registerbank has been changed by USING 1. As a matter of policy
, any reentrant function should also have the NOAREGS control so that it bec
omes totally registerbank-independent.
sys_interp() Entry Code
; unsigned char interp_sub(unsigned char x,
RSEG ?PR?_?interp_sub?INTERP
USING 0
_?interp_sub:
DEC ?C_IBP
DEC ?C_IBP
MOV R0,?C_IBP
XCH A,@R0
MOV A,R2
XCH A,@R0
INC R0
XCH A,@R0
MOV A,R3
XCH A,@R0
DEC ?C_IBP
MOV R0,?C_IBP
XCH A,@R0
MOV A,R5
XCH A,@R0
DEC ?C_IBP
MOV R0,?C_IBP
XCH A,@R0
MOV A,R7
XCH A,@R0
DEC ?C_IBP ;
SOURCE LINE # 22
sys_interp() Exit Code
?C0009:
MOV A,?C_IBP
ADD A,#010H <-- Restore ?C_IBP to original
position
MOV ?C_IBP,A
RET ;
END OF _?sys_interp
END
5.3.10 Summary Of Controls For Interrupt Functions
Provided the following combinations of controls are used, you will avoid lin
ker warnings and potentially dangerous code.
Interrupt Function Attribute | Called Function Attribute:
----------------------------------|-----------------------------------
| "non-reentrant"
No USING | no special attribute required
USING n | USING n
| or
| #pragma REGISTERBANK(n)
| or
| #pragma NOAREGS
Interrupt Function Attribute | Called Function Attribute
----------------------------------------------------------------------
| "reentrant"
no USING | no register attribute
USING n | #pragma NOAREGS
5.3.11 Reentrancy And Library Functions
The majority of C51 library functions are reentrant and can be freely used f
rom interrupts and background. However, some larger library functions such a
s printf(), scanf() etc. are not reentrant. If you have used a non-reentrant
library function reentrantly, you will get a "MULTIPLE CALL TO SEGMENT" war
ning, as would be expected.
"Hidden" library functions used to perform integer divides and multiplies et
c. are all reentrant so you can perform a 16/16 divide in an interrupt witho
ut fear of upsetting the background.
To Summarise:
You can generally use library functions reentrantly but always check the C51
manual section 9 to check whether a function is reentrant or not.
----------------------------------------------------------------------------
----
--
Ours is essentially a tragic age, so we refuse to take it tragically.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -