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

Use of RRC (Rotate right through carry) in Keil

The 8051 family of microcontrollers has a very useful assembly instruction RRC. This instruction rotates carry into the MSB of the given register, shifts that register right by 1, and carry becomes equal to what used to be the LSB of the register.

  bit NewCarry;
  unsigned char Data;

  NewCarry = Data & 0x01;
  Data = (Data >> 1);
  if (Carry){
    Data = (Data | 0x80);
  }
  Carry = NewCarry;
Basically it does the above, but all in one instruction. I was wondering what the easiest way to include this in my code was. What I want to do is to read a microcontroller pin into carry, shift it into a data byte, and then use the bit that shifts off as an exit condition. Is there an easy way to include a single assembly instruction in Keil? Perhaps a Intrinsic function?

Thanks

-Tom

Parents
  • Really, the *nicest* solution I think would be the ability to compile .asm functions inline.... if that was possible. Then I could do whatever I want!

    hehe

    I do quite like the DJNZ instruction, and I also like that while(--x){ generates a djnz instruction in the compiled code.

    I found the RRC instruction useful in two situations :
    1) An interrupt that triggers on an external clock signal and shifts in a bit of data.
    2) A tight loop that shifts in a byte of data from a dataflash.

    the reason why I like RRC is that the code that Drew wrote can be expressed as such (Note that this loop is not guaranteed to terminate, in the same way as Drew's)

    do {
      CY = PortPin
      RRC // Shifts the portpin into ACC, ACC flows off into carry
    } while (CY);
    

    Whereas all the binary operators used in Drew's alternative evaluate to a much larger piece of code.

    In the case of clocking data in due to an internal clock source, I have this code in my interrupt

      RxCurrentBit = _DPRX0;
    
      if (!RxLastBit){
        //Code which evaluates incoming byte
        //..
      }
    
      RxLastBit = CurrentData & 0x01;
      CurrentData = (CurrentData >> 1);
      if (RxCurrentBit){
        CurrentData = (CurrentData | 0x80);
      }
    

    The lower portion of code that actually clocks the data in looks like this after the compiler has done its magic (and it is magic, imho :)

      RxLastBit = CurrentData & 0x01;
    
    0044 7800        R     MOV     R0,#LOW CurrentData
    0046 E2                MOVX    A,@R0
    0047 FF                MOV     R7,A
    0048 13                RRC     A
    0049 9200        R     MOV     RxLastBit,C
      CurrentData = (CurrentData >> 1);
    
    004B EF                MOV     A,R7
    004C C3                CLR     C
    004D 13                RRC     A
    004E F2                MOVX    @R0,A
      if (RxCurrentBit){
    004F 300004      R     JNB     RxCurrentBit,?C0028
    
        CurrentData = (CurrentData | 0x80);
    0052 E2                MOVX    A,@R0
    0053 4480              ORL     A,#080H
    0055 F2                MOVX    @R0,A
    

    Now a lot of that overhead is obviously caused by CurrentData being in XDATA - placed there by the compiler because I haven't overridden it. Note these three instructions (to get the LSB of CurrentData)

    0047 FF                MOV     R7,A
    0048 13                RRC     A
    0049 9200        R     MOV     RxLastBit,C
    

    Now if only we had MOV C,RxCurrentBit after line 47, we could avoid a lot of the compiled code above. That would save up to 6 instructions (the if statement and the shift statement)

    This is the code I used in Modula 2 to do exactly the same thing (I just ported this code to the C code you see above)

      CY = RxCurrentBit;
      ACC = CurrentData;
      CODE RRC; (*Rotate accumulator 1 bit right through carry flag (CY -> ACC(7..0) -> CY)*)
      CurrentData = ACC;
      RxLastBit   = CY;
    

    I could go ahead and write a routine in assembly, however this causes the normal routine overhead - pretty much negating any gain of using assembly unless I rewrote the entire function.

    Hopefully this gives you some idea where I am coming from.

    Thanks for your comments

    -Tom

Reply
  • Really, the *nicest* solution I think would be the ability to compile .asm functions inline.... if that was possible. Then I could do whatever I want!

    hehe

    I do quite like the DJNZ instruction, and I also like that while(--x){ generates a djnz instruction in the compiled code.

    I found the RRC instruction useful in two situations :
    1) An interrupt that triggers on an external clock signal and shifts in a bit of data.
    2) A tight loop that shifts in a byte of data from a dataflash.

    the reason why I like RRC is that the code that Drew wrote can be expressed as such (Note that this loop is not guaranteed to terminate, in the same way as Drew's)

    do {
      CY = PortPin
      RRC // Shifts the portpin into ACC, ACC flows off into carry
    } while (CY);
    

    Whereas all the binary operators used in Drew's alternative evaluate to a much larger piece of code.

    In the case of clocking data in due to an internal clock source, I have this code in my interrupt

      RxCurrentBit = _DPRX0;
    
      if (!RxLastBit){
        //Code which evaluates incoming byte
        //..
      }
    
      RxLastBit = CurrentData & 0x01;
      CurrentData = (CurrentData >> 1);
      if (RxCurrentBit){
        CurrentData = (CurrentData | 0x80);
      }
    

    The lower portion of code that actually clocks the data in looks like this after the compiler has done its magic (and it is magic, imho :)

      RxLastBit = CurrentData & 0x01;
    
    0044 7800        R     MOV     R0,#LOW CurrentData
    0046 E2                MOVX    A,@R0
    0047 FF                MOV     R7,A
    0048 13                RRC     A
    0049 9200        R     MOV     RxLastBit,C
      CurrentData = (CurrentData >> 1);
    
    004B EF                MOV     A,R7
    004C C3                CLR     C
    004D 13                RRC     A
    004E F2                MOVX    @R0,A
      if (RxCurrentBit){
    004F 300004      R     JNB     RxCurrentBit,?C0028
    
        CurrentData = (CurrentData | 0x80);
    0052 E2                MOVX    A,@R0
    0053 4480              ORL     A,#080H
    0055 F2                MOVX    @R0,A
    

    Now a lot of that overhead is obviously caused by CurrentData being in XDATA - placed there by the compiler because I haven't overridden it. Note these three instructions (to get the LSB of CurrentData)

    0047 FF                MOV     R7,A
    0048 13                RRC     A
    0049 9200        R     MOV     RxLastBit,C
    

    Now if only we had MOV C,RxCurrentBit after line 47, we could avoid a lot of the compiled code above. That would save up to 6 instructions (the if statement and the shift statement)

    This is the code I used in Modula 2 to do exactly the same thing (I just ported this code to the C code you see above)

      CY = RxCurrentBit;
      ACC = CurrentData;
      CODE RRC; (*Rotate accumulator 1 bit right through carry flag (CY -> ACC(7..0) -> CY)*)
      CurrentData = ACC;
      RxLastBit   = CY;
    

    I could go ahead and write a routine in assembly, however this causes the normal routine overhead - pretty much negating any gain of using assembly unless I rewrote the entire function.

    Hopefully this gives you some idea where I am coming from.

    Thanks for your comments

    -Tom

Children
  • would be the ability to compile .asm functions inline

    Whaddayamean "would be"? This ability exists. Guess what: it's even documented. And supported by the IDE. The fact that you don't appear to know that strongly suggests you complain publically before bothering to actually break open the documentation. That's a bad habit.

    Inline assembly does have some drawbacks, but those are largely inevitable, given the way Keil tools are designed.

  • hi,

    the reason why I like RRC is that the code that Drew wrote can be expressed as such

    do {
      CY = PortPin
      RRC // Shifts the portpin into ACC, ACC flows off into carry
    } while (CY);
    No, it cannot be used such way. You should not access registers and condition flags (including carry) directly from C. Under C, they all are subject of changes by compiler. When you write
      CY = PortPin;
    
    then it does not mean that at next line the carry flag still keeps its value. Compiler may use registers and flags for own purposes at each line of C program and even between lines.

    In short, in C you have got variables and lost registers. If you need to manipulate with registers/flags then you should switch to ASM.

    Regards,
    Oleg

  • would be the ability to compile .asm functions inline

    Whaddayamean "would be"? This ability exists.


    In a way so clumsy that I agree "this function does not exist"

    Erik

  • If I did use CY = Portpin, I would check the compiled assembly for side-affects.

    As for the inline assembly compiling thing, I will look into that more.

    Thanks for your help everyone !

    -Tom