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

Is this a C51 / uvision bug?

Hi all
Below is the result of some debugging. I have isolated some code from a bigger project and have put it into a stand-alone project.

I basically don't understand why I can do right bit-shifting and AND'ing on single line, when I can't do left bit-shifting and AND'ing on single line.

If it isn't a bug, then what have I missed?

I have included many comments to describe my problem

#include <ADUC832.H>
#include <string.h>

void main(void) {


char ascii[] = "MC";
unsigned char pdu[3];
int w=0, r=0, len;
char ch1, ch2, rr, rl;


 /*  This is what I want to do:

  while-loop run 1:
     1: Assign to var 'ch1':   ch1 = 'M' (= 0x4D = 0100 1101)
     2: Assign to var 'ch2':   ch2 = 'C' (= 0x43 = 0100 0011)
     3: Assign to var 'w'  :     w = 0
     4: OR together the following:
         ((ch1 >>(w%7))&0x7F) | ((ch2 <<(7-(w%7)))&0xFF);
     <=>     0100 1101        |       1000 0000
     <=>                  1100 1101
     <=>                     0xCD

  while-loop run 2:
     1: Assign to var 'ch1':   ch1 = 'C' (= 0x43 = 0100 0011)
     2: Assign to var 'ch2':   ch2 = 0x00
     3: Assign to var 'w'  :     w = 1
     4: OR together the following:
         ((ch1 >>(w%7))&0x7F) | ((ch2 <<(7-(w%7)))&0xFF);
     <=>     0010 0001        |       0000 0000
     <=>                  0010 0001
     <=>                     0x21

 */

len=strlen(ascii);

while (r<len) {

 // ------ First OR-part  -----------------------
 // -------Both versions below are OK  ----------

      // -- VER 1: OK
      //  ch1 = ascii[r];
      //  rr  = (w%7);
      //  ch1 = (ch1 >> rr) & 0x7F;

      // -- VER 2: OK
        ch1 = (ascii[r] >> (w%7)) & 0x7F;    // Bit-shifting and AND'ing
                                             // may be done in one line

 // ------  Second OR-part  -----------------------------
 //-------  Both versions below are NOT OK ??  ----------

      // -- VER 1: OK
        ch2 = ascii[r+1];
        rl = (7-(w%7));
        ch2 = (ch2 << rl) & ((char)0xFF);    // Bit shift and AND'ing can be
                                             // done in one line, IF type cast
                                             // is used - why?
      //  ch2 = ch2 & 0xFF;                  // If splitting into new line
                                             // type cast is not required?

      // -- VER 2: NOT OK
      //  ch2 = (ascii[r+1] << (7-(w%7))) & 0xFF;  // type cast doesn't help
      //  ch2 = ch2 & 0xFF;  // AND'ing must be on seperate line ?


    //----------------------------------------------------------------
    // IS THIS A BUG ??
    //----------------------------------------------------------------
    // Why can we bit-shift and do the AND'ing in a single line
    // for the first OR-part above, but cannot do it for the second
    // OR-part where bit-shifting and AND'ing must be on two seperate
    // lines ???
    //----------------------------------------------------------------

// ------ Do the actual OR'ing -------
        pdu[w]= (ch1 | ch2) ;

        if ((w%7)==6)
           r++;
        r++;
        w++;
    }
    pdu[w]=0; // terminator

    //----------------------------------------------------------------
    // Run to here in debugger and look at content of
    // local variable 'pdu'.
    // When using 'NOT OK' versions from above
    // pdu will contain {0x4D, 0x21, 0x00}
    // and not {0xCD, 0x21, 0x00} as the 'OK' versions
    // produce.
    //----------------------------------------------------------------

   while(1);

}

Parents
  • The central clues remain the same: char is a signed type, and operations in C programs are defined to take place in a type no smaller than 'int'. This line

    ch1 = (ch1 << 7) & 0xff
    

    is implicitly transformed to the following:

    ch1 = (char)(((int)ch1 << 7) & 0xff);
    

    The result of this operation is clearly 0x80. Now, these implied casts are called "ANSI integer promotions", and in C51 they can be turned off (option NOINTPROMOTE). Technically, the moment you use this option, all bets are off as to what the program may do.

    But for the sake of the argument, let's say this option is in action, so the statement gets turned into:

    ch1 = ((ch1 << 7) & (char)0xff);
    

    You've thus shifted a signed value into overflow: 1 << 7 is 128, which is too large for a char to hold. It's anybody's guess what might happen in this case. Well, don't do that then.

    Lesson learned: don't ever shift values of signed types. And while at it, better don't use bitwise operators on them, either.

Reply
  • The central clues remain the same: char is a signed type, and operations in C programs are defined to take place in a type no smaller than 'int'. This line

    ch1 = (ch1 << 7) & 0xff
    

    is implicitly transformed to the following:

    ch1 = (char)(((int)ch1 << 7) & 0xff);
    

    The result of this operation is clearly 0x80. Now, these implied casts are called "ANSI integer promotions", and in C51 they can be turned off (option NOINTPROMOTE). Technically, the moment you use this option, all bets are off as to what the program may do.

    But for the sake of the argument, let's say this option is in action, so the statement gets turned into:

    ch1 = ((ch1 << 7) & (char)0xff);
    

    You've thus shifted a signed value into overflow: 1 << 7 is 128, which is too large for a char to hold. It's anybody's guess what might happen in this case. Well, don't do that then.

    Lesson learned: don't ever shift values of signed types. And while at it, better don't use bitwise operators on them, either.

Children
No data