Hello, I'm having trouble understanding the NOAREG, AREG & USING options. I'm also confused with R0-R7 & AR0-AR7. I've read the support files and manual but i'm still confused. May I know a) How is it that using NOAREGs will allow me to disregard register bank currently used when writing function? ( will the registers be automatically saved to stack? ) b) What is the difference with R0 and AR0? How does the USING option affect R0 & AR0? (for example if i put the option "USING 2", have then use R4, does this mean that it is the actually R20 ie in bank 2, whiel AR4 is not affected by the using function) c) How does USING help me to save stack space? Best Regards, AL
"I've read the support files and manual" Which product? Which manual? The following is from the section entitled "Special Assembler Symbols" in the Macro Assembler and Utilities Manual: R0 – R7 The eight 8-bit general purpose 8051 registers in the currently active register bank. A Maximum of four register banks are available. AR0 – AR7 Represent the absolute data addresses of R0 through R7 in the current register bank. The absolute address for these registers will change depending on the register bank that is currently selected. These symbols are only available when the USING directive is given. Refer to the USING directive for more information on selecting the register bank. These representations are suppressed by the NOAREGS directive. Refer to the NOAREGS directive for more information.
The 8051 family of processors have four register banks each of which contain eight 8-bit registers R0 through R7. At any instant, exactly one register bank is selected so that any instruction that references register Rn will access Rn in the currently selected register bank. i.e. the register that is accessed is affected by the currently selected register bank. However, all the registers in all the register banks also have unique direct addresses so that it is also possible to access any register Rn from any register bank – regardless of the currently selected register bank. 8051 instruction have a fairly limited range of addressing modes. For example, it is not possible to move the contents of one register to another directly as in MOV R0,R1. If code is written with knowledge of which register bank is selected when the code is to be executed, then there is an alternative way to access a register – because its absolute address is know. For convenience, the absolute address of a register Rn is simply ARn. So, an instruction such as MOV R0,AR1 is now possible. The Assembler/Compiler knows the currently selected register bank required to work out the absolute address of a register by either using a default (usually register bank 0) or because it has been told otherwise by a C #pragma REGISTERBANK(n) or an Assembler USING control. Note that these controls do not themselves change the register bank selected, they only affect the calculation of the absolute addresses of the ARn registers. Generally, being able to address registers directly is an advantage and results in faster and more compact code. The generation of code that uses absolute register addressing is turned on by the C #pragma AREGS. However, it is possible that a function may be called and it is not known what the currently selected register bank is – the compiler must be told not to attempt to use instructions that use absolute register address because the absolute addresses cannot be reliably computed at compile time. This situation is indicated to the compiler by using #pragma NOAREGS. Sometimes, it is useful to be able to change the currently selected register bank. In Keil C this should be done using the using keyword. This is most usefully applied when the function is question is an interrupt. When an interrupt is generated and the interrupt service function is invoked, that function will need a set of working registers to get anything non-trivial done. Obviously, the interrupt service function must not change the value of any registers in the code that was interrupted. There are two ways of achieving this, the interrupt function can save the current value of all registers that it needs onto the stack and restore them before returning to the interrupted code; or it can switch to a register bank that <u>cannot</u> currently be in use and restore the register bank before returning to the interrupted code. The latter option is generally faster and is achieved by the using keyword. It is common practice to assign a register bank to each 'level' of execution in the code. For example, the base level code (everything called by main) uses register bank 0 (default), low-priority interrupt use register bank 1 and high priority interrupts use register bank 2. Everything should be fairly straightforward unless the interrupt function calls another function – the called function must have the correct register bank indicated by #pragma REGISTERBANK(n); otherwise, things will go very wrong. In general, a function should only be called from code from one 'level' of execution. This can be done, but it is a whole other subject.
Here is an example of the "prologue" to a C51 interrupt function with & without the using(n) option:
; void UartIsr( void ) INTERRUPT(4) ; void UartIsr( void ) INTERRUPT(4) USING(2) RSEG ?PR?UartIsr?VM_OLMFSM RSEG ?PR?UartIsr?VM_OLMFSM USING 0 USING 2 UartIsr: UartIsr: PUSH ACC PUSH ACC PUSH B PUSH B PUSH DPH PUSH DPH PUSH DPL PUSH DPL PUSH PSW PUSH PSW USING 2 MOV PSW,#00H MOV PSW,#010H PUSH AR0 PUSH AR1 PUSH AR2 PUSH AR3 PUSH AR4 PUSH AR5 PUSH AR6 PUSH AR7 USING 0 ; { ; {
Thank You Very Much.
1)How do you set the register bank so that function parameters will be passed using a bank you choose (instead of the default bank 0)? 2)You said that "in general, a function should only be called from code from one level of execution". May I know how this is done? Best Regards AL
Still I am confused with Cole's words because the absolute addresses cannot be reliably computed at compile time.The following word is from Chapter 2,Cx51 Compiler User's Guide:
2 stmt level source 1 extern char func (); 2 char k; 3 4 #pragma NOAREGS 5 noaregfunc () { 6 1 k = func () + func (); 7 1 } 8 9 #pragma AREGS 10 aregfunc () { 11 1 k = func () + func (); 12 1 } ; FUNCTION noaregfunc (BEGIN) ; SOURCE LINE # 6 0000 120000 E LCALL func 0003 EF MOV A,R7 0004 C0E0 PUSH ACC 0006 120000 E LCALL func 0009 D0E0 POP ACC 000B 2F ADD A,R7 000C F500 R MOV k,A ; SOURCE LINE # 7 000E 22 RET ; FUNCTION noaregfunc (END) ; FUNCTION aregfunc (BEGIN) ; SOURCE LINE # 11 0000 120000 E LCALL func 0003 C007 PUSH AR7 0005 120000 E LCALL func 0008 D0E0 POP ACC 000A 2F ADD A,R7 000B F500 R MOV k,A ; SOURCE LINE # 12 000D 22 RET ; FUNCTION aregfunc (END)
push AR7
Do you mean that push AR7 is not reliable because the AR7 isn't reliably computed at compile time? AR7 is the absolute address of register R7 in one particular register bank - could be one of four possibilities depending on of x in the preceeding "USING x". Each possible direct address for R7 is unique. When a function is called, any of the four possible register banks could be selected at that instant and the compiler cannot predict which it will be.
Dear Andrew Neil, May I know where the PC value saved to? and is it possible to have direct access to PC value in your program? Regards, KC
Hello, First of all I wanted to thank Cole for his excellent explanation. It clears a lot of the fog around this issue, and I think that this explanation should be entered into the manual ! I still have several questions regarding interrupt coding and relevant compiler directives: 1) Assuming the following code:
void UartIsr( void ) INTERRUPT(4) USING(2) { f1(); } #pragma NOAREGS void f1(void) { … … f2(); … … } #pragma AREGS void f2(void) { … … … }
And some more questions... Is it deafult compilation state to use AREG if no AREG/NOAREG pragma is written in the code ? Do I need to specify it in the beginning of main? If I specify any option (use AREG or NOAREG) at a specific place in the code, will this direction be relevant in all the following code (including called functions) untill a new direction will be issued ?
Will the direction #pragma REGISTERBANK(x) apply only for the function that follows this statement, or will it apply untill a new REGISTERBANK direction will be stated ? In the following example:
#pragma REGISTERBANK(3) void func1 (void) { ... ... } void func2 (void) { ... ... }
No sorry, there is no way to address the PC (except for using a CALL instruction to a sub-routine that retrieves the return address from stack).
1) May I know where the PC value saved to? 2) and is it possible to have direct access to PC value in your program? 1) by hardware 2) why? Erik
See: http://www.keil.com/support/man/docs/c51/c51_registerbank.htm (The REGISTERBANK directive selects which register bank to use for subsequent functions).
Please start a new thread for such generic questions.