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

what's the function of "AREGS"and "NOAREGS"

what's the function of "AREGS"and "NOAREGS", can you provide me some sample, so that I could understand them clear. Are them still useful in current updated versions.
In the manual it is said that with "NOAREGS" the function could be called by another functions using different register bank.

#pragma NOAREGS
char add(char i)
{
    return i+10;
}

void main(void) using 1
{
    ch = add(3);
}

whether I enable or disable #pragma NOAREGS
I always get the right result. If with "NOAREGS" the function could be called by another functions using different register bank, then does it also mean that with "AREGS" the function could not be called by another functions using different register bank. I am very confused with "AREGS"and "NOAREGS".

Parents
  • Hi,Thomas
    I tried the following codes,but I could never get right result soever I use NOARGS or not.

    #pragma NOAREGS
    
    char add(char i) using 2
    {
        return(i+10);
    }
    
    void main(void) using 1
    {
    	char ch;
        ch = add(3);
    
    	while (1);
    }
    
    
    If "using 2" for add() is removed,the result will be always right.
    I think the reason may be that the arguments of the functions are transmited with register,but now they are not in the same bank.
    howerver,I can't understant what is,on earth,the function of NOAREGS.
    expecting more discussion.

Reply
  • Hi,Thomas
    I tried the following codes,but I could never get right result soever I use NOARGS or not.

    #pragma NOAREGS
    
    char add(char i) using 2
    {
        return(i+10);
    }
    
    void main(void) using 1
    {
    	char ch;
        ch = add(3);
    
    	while (1);
    }
    
    
    If "using 2" for add() is removed,the result will be always right.
    I think the reason may be that the arguments of the functions are transmited with register,but now they are not in the same bank.
    howerver,I can't understant what is,on earth,the function of NOAREGS.
    expecting more discussion.

Children
  • The point about NOAREGS is that all registers also have an absolute address in memory. So, if the compiler knows that you are using register bank 0, it can access access registers R0, R1, ... R7 not only by name, but also accessing data memory addresses 0, 1, ... 7. Each register of each register bank has a unique address.

    Being able to access registers either way has advantages for speed and compactness of code. The compiler will use a mixture of addressing modes to get at the registers.

    The problems only start when a function is called and the currently selected register bank is unknown. In this case, the compiler cannot know the direct address of a register - in fact you have to tell it not to make any assumptions about the selected register bank by using NOAREGS.

    I find it very strange that the compiler should allow a function that has parameters passed in registers to set the register bank with using. The reason add() does not work with using 2 is parameter i is put into a register by main() and although add() will be looking at the right register, it will have changed the register bank. I cannot think of any good reason for the compiler not to generate a warning here.

  • You (Thomas and Nocky) are mixing two different things.

    The directive using determines what register bank will compiler use
    (if it ever needs use registers) in the particular function. The original RB
    is saved in the beginning (push psw), then switched to the required one (mov psw,#xx)
    and the original RB restored on exit (pop psw).

    The directives AREGS/NOAREGS tell the compiler if it CAN/CANNOT use
    (if it ever needs use registers) absolute register addressing, ie. direct
    addresses AR0 to AR7 for registers R0 to R7 of the Register Bank right being used.
    That's why it is called AREGS = A(bsolute)REG(ister)S.
    It means: if RB=0, AR0 address is 0; if RB=1, AR0 address is 8 and so on...
    or as if you "defined":

    if (RB == 0)
     AR0    data    0     ; assembler
     AR1    data    1
     ...
    endif
    
    if (RB == 1)
     AR0    data    8
     AR1    data    9
     ...
    endif
    
    and so forth...

    it allows simpler and shorter code, for example:
    instead of:
            mov     a,r0       ; this way it will be compiled with NOAREGS
            push    acc
    

    you can use:
                               ; and this way with AREGS
            push    AR0        ; shorter but Register Bank dependent!
    

    Compilation of both posted programs will be the same either you use AREGS or NOAREGS,
    because there is no reason here for absolute register addressing (compiler's decision)!
    for example:
            mov     a,r7       ; 1 byte, 1 cycle instruction code
    
    is more advantageous than:
            mov     a,ar7      ; 2 bytes, 1 cycle instruction code
    

    The behaviour of the posted short programs is absolutely clear
    with the explanation above in mind and the commented compilation below:

    Note: func parameters are passed in register as a default;
    perhaps it is a bit strange that compiler doesn't notice the different
    caller and callee register banks and doesn't generate a warning.

    Thomas' compiled code:
    ; FUNCTION _add (BEGIN)
            MOV     A,R7       ; - Variable 'i' assigned to Register 'R7' -
            ADD     A,#0AH     ; RB without change, ie. RB1 for it was changed in main()
            MOV     R7,A       ; return value in R7, the same RB
            RET
    ; FUNCTION _add (END)
    
    ; FUNCTION main (BEGIN)
            PUSH    PSW
            MOV     PSW,#08H   ; Register bank switched to RB1
            MOV     R7,#03H    ; passing parameter value i=3 in R7-RB1
            LCALL   _add       ; function _add(char i) doesn't touch RBs' setup
            MOV     ch,R7      ; so the return value is in the right register
            POP     PSW
            RET
    ; FUNCTION main (END)
    

    and Nocky's compiled code:

    ; FUNCTION _add (BEGIN)
            PUSH    PSW
            MOV     PSW,#010H  ; Register bank switched to RB2
            MOV     A,R7       ; - Variable 'i' assigned to Register 'R7' - but RB2!!!
            ADD     A,#0AH
            MOV     R7,A       ; function _add returns the result in R7, but RB2!!!
    
    ?C0001:
            POP     PSW
            RET
    ; FUNCTION _add (END)
    
    ; FUNCTION main (BEGIN)
            PUSH    PSW
            MOV     PSW,#08H   ; Register bank switched to RB1
            MOV     R7,#03H    ; passing parameter value i=3 in R7-RB1!!!
            LCALL   _add
            MOV     ch,R7      ; return value is expected to be in R7-RB1 again!!!
    
    ?C0002:
            SJMP    ?C0002
    ; FUNCTION main (END)
    

    Best Regards

  • Thanks for your help!

    But in the manual it is said that with "NOAREGS" one function could be called by another functions using different register bank, why the manual said so, does it mean that with "AREGS" the function could not be called by another functions using different register bank. In the above examples, the two functios add() and main() use different register bank, but whether I used #pragam NOAREGS or #pragma AREGS, I always get the right answer. While in Nocky's sample, whether you used #pragam NOAREGS or #pragma AREGS, you always get the wrong result. How does the "AREGS" and "NOARGES" influence the two functions, only the addressing method?

  • The discussed directives have, as far as I know, the meaning I have explained.
    NOAREGS disables absolute register access, while AREGS enables (but not forces) it.
    When enabled, the compiler decides whether to use it or not in a particular
    situation ("is it advantageous or not?"). It doesn't mean that the addressing method
    unconditionally changes to absolute register accesses in every case.
    See your compiled programs in my previous post. You obtain the same result
    either you use AREGS or NOAREGS, because in both these examples NOAREGS
    option "prevails". There is no reason for absolute register access there.
    That's why it is not used even if AREGS option is selected.


    "But in the manual it is said that with "NOAREGS" one function could be called
    by another functions using different register bank..."


    In my opinion it is confusing (if not even wrong)
    as far as passing parameters or return values in registers are concerned.


    I suppose you mean C51.pdf, page 24 (?):

    "This directive may be used for functions that are called from other
    functions using different register banks."


    or further, on page 124:

    "To make a function insensitive to the current register bank, the function must be
    compiled using the NOAREGS control directive. This would be useful for a
    function that was called from the main program and also from an interrupt
    function that uses a different register bank.
    Note:
    The Cx51 compiler does not and cannot detect a register bank mismatch
    between functions. Therefore, make sure that functions using alternate register
    banks call only other functions that do not assume a default register bank."


    and later, page 128:

    "Functions called from an interrupt procedure must function with the same
    register bank as the interrupt procedure. When the NOAREGS directive is
    not explicitly specified, the compiler may generate absolute register accesses
    using the register bank selected (by the using attribute or by the
    REGISTERBANK control) for that function. Unpredictable results may
    occur when a function assumes a register bank other than the one currently
    selected."


    Once more as to the function register bank in/dependance -
    for instance lets assume 2 functions:

    a) func1() compiled with NOAREGS pragma,
    containing "mov r7,a" instruction in its body,
    the resulting code is 0xFF

    b) func2() compiled with AREGS pragma,
    containing corresponding to the above "mov ar7,a" instruction in its body,
    the resulting codes are 0xF5 0x17 if register bank used at compile time was 2

    if current register bank for both functions at calling time is, say, 1,
    then func1() will always write in R7 of the current register bank
    while func2() will write in absolute address 17H which is probably not the intention

    This is, among others, what they meant "To make a function insensitive
    to the current register bank..."


    In my opinion the RB independence concerns rather the body of the function
    than parameters or return values passed in registers.
    The directive "using" is suitable (my opinion) esp. with ISRoutines to prevent
    them from pushing big load onto the (very limited) stack. It is also useful
    for functions called from ISR. Using "using" :-) for other common functions
    seems to me a bit "violent".

    Best wishes

  • Since the compiler doesn't give a warning ,can I say this is a bug?
    We can send to keil?

  • Having a look at this note in Manual:

    "The Cx51 compiler does not and cannot detect a register bank mismatch between functions. Therefore, make sure that functions using alternate register banks call only other functions that do not assume a default register bank."

    I think it is true, so for the above mentioned warning the adjective "missing" is at least disputable. Realize that compile time and run time are different things and the compiler (compile time) doesn't necessarily know the actual RB of a function at run time in general. Sometimes it is up to you to judge and decide. That's why I suggested: don't use using directive with common functions unless you have a really important reason for it (ISR, etc.)