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

Write R7 register directly from C

I use KeilC uVision4 for 89C51 MCU.
By "Inline ASM", i can easily direct write value to R7 register, like this :

    #pragma ASM
        MOV R7, #10
    #pragma ENDASM


But now, there are some reason that i have to write to R7 register direct from C (not from inline ASM), eg:

    R7 = 10;


Of course, the C compiler does not understand R7 as MCU Register (not Inline ASM).

-> Is there anyway to solve this problem. Please help me if anyone know, thanks a lot !

Parents
  • Well, I intend to write an Accurate "ASM Delay Routine" by "Inline ASM".

    Let take the simplest delay code:

        #pragma ASM
        MOV R1, #vR1
        // Calculate Time spent From Here
        DJNZ R1, $ //2 Machine cycle (MCs)
        #pragma ENDASM
    


    With 8051MCU, it spent : t1 = vR1 x 2 machine cycles (MCs)

    Now, with 2 registers used:

        #pragma ASM
        MOV R1, #vR1
        MOV R2, #vR2
        // Calculate Time spent From Here
        DJNZ R1, $
        DJNZ R2, $-2
        #pragma ENDASM
    


    time spent : t2 = (t1 + 257vR2 - 256) x 2 MCs

    Finally, with 3 registers used:

        #pragma ASM
        MOV R1, #vR1
        MOV R2, #vR2
        MOV R3, #vR3
        DJNZ R1, $
        DJNZ R2, $-2
        DJNZ R3, $-4
        #pragma ENDASM
    


    time spent : t3 = (t2 + 65793vR3 - 65792) x 2 MCs

    Generraly, with n Register used, we can delay :

    tn = (tn-1 + (Cn-1 + 1)vRn - Cn-1) x 2 MCs
    where :

    Cn = 256^n + 256^(n-1) + ... + 256 + 0

    Now i write a delay 3000MCs routine :

    //8051 MCU
    #define Interval 3000 // Change here to change delay in MCs)
    
    #define C0 0                 // Constant in formula above
    #define C1 256               // Constant
    #define C2 65792             // Constant
    
    #define t3 Interval/2        // DJNZ take 2 MCs, so divide by 2 for easier calculation
    #define vR3 ((t3+C2-2)/(C2+1))   // Calculate R3 value
    
    #define t2  (t3+C2-(C2+1)*vR3)
    #define vR2 ((t2+C1-1)/(C1+1))   // Calculate R2 value
    
    #define t1  (t2+C1-(C2+1)*vR2)
    #define vR1 ((t1+C0-0)/(C0+1))   // Calculate R1 value
    
    void Delay() {
        #pragma ASM
        MOV R1, #vR1
        MOV R2, #vR2
        MOV R3, #vR3
        DJNZ R1, $
        DJNZ R2, $-2
        DJNZ R3, $-4
        #pragma ENDASM
    }
    


    Use in main routine :

    void main() {
        Delay(); // Delay 3000 Machine Cycles
    }
    

    => For easier use, i should write a DelayMCs() Macro, eg :

        #define DelayMCs(Interval) //Code Here???
    


    Then, use this Macro like this :

    void main() {
        DelayMCs(7000);
    }
    


    => That's my idea. But i don't know how to write this Macro. Can anyone help me build "Code Here" above. Thanks !

Reply
  • Well, I intend to write an Accurate "ASM Delay Routine" by "Inline ASM".

    Let take the simplest delay code:

        #pragma ASM
        MOV R1, #vR1
        // Calculate Time spent From Here
        DJNZ R1, $ //2 Machine cycle (MCs)
        #pragma ENDASM
    


    With 8051MCU, it spent : t1 = vR1 x 2 machine cycles (MCs)

    Now, with 2 registers used:

        #pragma ASM
        MOV R1, #vR1
        MOV R2, #vR2
        // Calculate Time spent From Here
        DJNZ R1, $
        DJNZ R2, $-2
        #pragma ENDASM
    


    time spent : t2 = (t1 + 257vR2 - 256) x 2 MCs

    Finally, with 3 registers used:

        #pragma ASM
        MOV R1, #vR1
        MOV R2, #vR2
        MOV R3, #vR3
        DJNZ R1, $
        DJNZ R2, $-2
        DJNZ R3, $-4
        #pragma ENDASM
    


    time spent : t3 = (t2 + 65793vR3 - 65792) x 2 MCs

    Generraly, with n Register used, we can delay :

    tn = (tn-1 + (Cn-1 + 1)vRn - Cn-1) x 2 MCs
    where :

    Cn = 256^n + 256^(n-1) + ... + 256 + 0

    Now i write a delay 3000MCs routine :

    //8051 MCU
    #define Interval 3000 // Change here to change delay in MCs)
    
    #define C0 0                 // Constant in formula above
    #define C1 256               // Constant
    #define C2 65792             // Constant
    
    #define t3 Interval/2        // DJNZ take 2 MCs, so divide by 2 for easier calculation
    #define vR3 ((t3+C2-2)/(C2+1))   // Calculate R3 value
    
    #define t2  (t3+C2-(C2+1)*vR3)
    #define vR2 ((t2+C1-1)/(C1+1))   // Calculate R2 value
    
    #define t1  (t2+C1-(C2+1)*vR2)
    #define vR1 ((t1+C0-0)/(C0+1))   // Calculate R1 value
    
    void Delay() {
        #pragma ASM
        MOV R1, #vR1
        MOV R2, #vR2
        MOV R3, #vR3
        DJNZ R1, $
        DJNZ R2, $-2
        DJNZ R3, $-4
        #pragma ENDASM
    }
    


    Use in main routine :

    void main() {
        Delay(); // Delay 3000 Machine Cycles
    }
    

    => For easier use, i should write a DelayMCs() Macro, eg :

        #define DelayMCs(Interval) //Code Here???
    


    Then, use this Macro like this :

    void main() {
        DelayMCs(7000);
    }
    


    => That's my idea. But i don't know how to write this Macro. Can anyone help me build "Code Here" above. Thanks !

Children
  • See: http://www.keil.com/forum/docs/thread16420.asp

    It is very simple to do this as a separate assembly module - and, in my opinion, much to be preferred. See the above link.

  • By "Inline ASM", i can easily direct write value to R7 register, like this :

    By "having a gun", i can easily shoot myself in the foot, like this

    that something can be done, does not mean it is right to do it.

    Erik

  • Thanks a lot for your helps. In a couple of days, I found the Macro should be as following :

    // Delay Routine
    void RoutineDelayMCs(unsigned int interval) {
        #pragma ASM
            DJNZ R7, $
            DJNZ R6, $-2
        #pragma ENDASM
    }
    
    // Delay Macro -> Calculate interval to pass into Delay Routine
    #define MacroDelayMCs(interval) \ 
        ((!(((interval)/2) - 257*((((interval)/2)+256-1)/257))) ? \ 
        RoutineDelayMCs(257*((((interval)/2)+256-1)/257)) : RoutineDelayMCs(((interval)/2)+256-(((interval)/2)+256-1)/257))
    
    // Offset Macro -> Calculate offset MCs of MOV, LCALL, RET ... command -> for better accuracy
    // Accuracy (if interval even, error = 0; if interval odd, error = -1 MCs
    // interval apply Range : 14 -> 131592
    #define DelayMCs(interval) \ 
        MacroDelayMCs(interval-10)
    
    
    void main() {
        DelayMCs(14);     // This call delay 14 Machine Cycles
        DelayMCs(512);    // This call delay 512 MCs
        DelayMCs(131592); // This call delay 131592 MCs
    }
    

    It works fine !
    Of course, manually hacking the registers is really Bad. I will put the RoutineDelayMCs in an ASM Module later.

    Thanks again, friends !

  • ... to make something simple & safe into something over-complicated and dangerous!

  • Yes.

    For me, it would definitely take less time and effort to write a routine in assembler than to understand those macros!

  • ... that an Engineer is someone who can make for a pound what any fool can make for a tenner.

    I think we have just seen a proof by counter-example...

    ;-)

  • Maybe you understand my idea incorrectly. I've used MikroC for 8051. That compiler has a built-in Delay_ms() routines. When I want to delay 10ms, just code

    Delay_ms(10);
    

    The compiler will auto generate source code (to delay 10ms) :

    _Delay_10ms :
        MOV R5, #8
        MOV R6, #1
        MOV R7, #247
        DJNZ R7, PC-2
        DJNZ R6, PC-4
        DJNZ R5, PC-6
        RET
    


    Then, I wonder if I can write a Macro like that in KeilC. Just code "Delay_ms(x);" with x is any number you want. Anything wrong in my idea ?

  • Yes - trying to mess about with macros in 'C'!

  • if at least 2 experienced people are telling you
    DON'T DO IT! and you refuse and insist on doing it your way, when it is clear even to a non-C51 guy like me that what you are doing is plain wrong, then you are no less than a ticking bomb. have you even considered that your "time gain" will be nullified by the hoops the processor will have to jump through in order to compensate for your macroed assembly...?

  • .. I will repeat

    DO NOT MESS WITH REGISTERS IN INLINE ASM

    you are hellbent on doing it this way and in a while you will come crying here "why does this not work" when you run into a conflict between your inline and the compilers register usage which legally can be any which way and be changed at any time.

    Erik

  • Actually, that's about the one thing that's not at issue!

    We're talking about software delays - so wasting processor cycles is the object of the exercise!

    The problem is that the proposed macro approach is so horribly cumbersome and error-prone - especially when doing it as an assembler module is so clean, simple, and easy!

  • to compensate for his register usage in the middle of it

    Erik

  • When it comes to programming, there is not always a clear right or wrong.

    However, some methods have clear advantages and some clear disadvantages. I can see no advantage in what you're trying to do - And I think others here would concur. It is simply cumbersome and potentially error prone.

    A piece of complicated looking code does not mean that author is a genius. On the contrary, quite often it is the simple elegant pieces of code that are written by the smarter people.

    There's nothing much simpler than a software delay loop - Why try to complicate it?

  • "I can see no advantage in what you're trying to do"

    Absolutely: no advantage whatsoever; but plenty of clear disadvantages!!

    "often it is the simple elegant pieces of code that are written by the smarter people."

    Indeed - hence the "Engineer" comment earlier!

    ;-)

  • I merely trying to convince the OP (without much success, I guess...!) of the cascade negative impact his design will have - include impairing other piece of software that are supposed to have "well-known behavior".