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); }
// ch2 = (ascii[r+1] << (7-(w%7))) & 0xFF; // type cast doesn't help
How would you have type-cast here ?
The way it is right now, it looks to the compiler like you're ANDing a char variable with 0xFF, which doesn't really make sense and can be optimized out.
AND'ing a char with 0xFF should leave the char unmodified, right? Then why isn't that the case? Try run the code in the keil debugger.
Your right, a type cast shouldn't really be included. But I just experience that this produce correct results
ch2 = ascii[r+1]; // ch2 is a char rl = (7-(w%7)); // rl is a char ch2 = (ch2 << rl) & ((char)0xFF); // Use type cast
and this doesn't (i.e. without type cast)
ch2 = ascii[r+1]; // ch2 is a char rl = (7-(w%7)); // rl is a char ch2 = (ch2 << rl) & (0xFF); // don't use type cast
I think I have narrowed it bit more down.
This code: ('uchar' and AND'ing )
void main(void) { unsigned char ascii = 'C'; char ch1, ch2; ch1 = (ascii <
and this ('char' and no AND'ing )
void main(void) { char ascii = 'C'; char ch1, ch2; ch1 = (ascii <
produce the same assembly:
line level source 1 #include 2 #include 3 4 void main(void) { 5 1 char ascii = 'C'; 6 1 char ch1, ch2; 7 1 8 1 ch1 = (ascii <
However, if defining 'ascii' above as 'char' and include AND'ing, I get
Can someone please translate the difference between these assemblies into english
Note This message was edited to reduce width.
Still, my query is not what should it be, but why does it work 'split' but not 'combined', the requirements should be the same.
Not necessarily. What you're overlooking is that by splitting up the computation, you've introduced a couple of implicit casts (if you run a MISRA rules checker on the example, it'll warn you about them not being made explicit). The actual equivalent of the computation statements in this sample
u8 a, b, c; u16 result; result = a * b; result = result / c; result = result / 2;
combined into a single line is not, as your example suggested
result = ((a * b) / c / 2);
but rather something like
result = ((u16)((u16)(a * b) / (u16)c) / (u16)2);
By assigning the intermediate results to the u16 variable "result", you effectively introduce a cast of these values to type u16. Which in turn requires implicit casts of the latter two operands, c and 2, to the same type. All these casts are missing from your one-liner rendition of the computation, and that's what rightfully breaks the equivalence you expected.
Oh, well, I just wondered, after all it works.
Erik
Having played a bit with this funny left bit-shifting issue the conclusion seems to be: When left bit-shifting a char variable and AND'ing with 0xFF the MSB (sign bit) is not allowed to change.
This seems to be a specific char issue. Signed int variables left-shiftet and AND'ed with 0xFFFF are allowed to change MSB.
Below are some code examples to illustrate. In all examples ch1 is a signed char.
With MSB initially cleared we cannot set it:
ch1 = 0x01; // MSB initially NOT set ch1 = (ch1 << 7) & 0xFF; // This produces 0x00
No AND'ing
ch1 = 0x01; ch1 = (ch1 << 7); // This produces 0x80
When not using a variable, but a casted constant
ch1 = ((char)0x01 << 7) & 0xFF; // This produces 0x80
MSB can be set when AND'ing with something other than 0xFF
ch1 = 0x01; ch1 = (ch1 << 7) & 0xF0; // This produce 0x80
Now try the opposite. With MSB initially set, left bit-shift will not be able to clear it
ch1 = 0x80; ch1 = (ch1 << 7) & 0xFF; // This produce 0x80
Is this really intentionally? If so, why only for chars? And why only when AND'ing with 0xFF?
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.
I did not go for an extended study of the C standard to see what was right and what was wrong
Still refusing to read the manual, then?
but I do believe that whether the typecast is required or not should be the same in both cases
Facts, man, facts. This is engineering, not religion.
I sent an 'inconsistency report' to support
Oh dear.
Why would I, as you see I solved the problem.
I have succesfully coded C for more than 15 years and really do not have any interest, whatsoever, in why things are as they are. That I know enough to program in C and figure out what is wrong when it does not work is more than enough for me.
Of course - and fortunately, I do not have the ability to code C in such a way that no one can figure out what it is doing. That part I leave to those that "study every detail of the manual".
I have had Kochans book since day one and that is my manual. Yes, you probably need a severe study of e.g. K&R to find out if a union will have an odd or even length when it is a long and 5 chars, but I REALLY do not care about such.
Is that so terrible, I did not send a 'bug report', I did not request a fix, I stated "no reply required". I just pointed out the (if you insist - percieved) inconsistency. If the Keil people study decide there is no inconsistency, I am sure they have a trashcan.
No. You changed the code around until it gave the result you expected. There was no problem to solve other than your false expectations.
I have succesfully coded C for more than 15 years and really do not have any interest, whatsoever, in why things are as they are.
This is exactly the reason there is so much sloppy, buggy code out there. When using a language like 'C' you need to have a good understanding of what is going on behind the scenes. I'm afraid that the best way of doing that is... reading the manual.
That I know enough to program in C and figure out what is wrong when it does not work is more than enough for me.
Thing is, in the example you gave you didn't figure out what was wrong. You did not understand why the code behaved the way it did.
You seem to have a very negative attitude to those who read the manual. This is strange considering the frequency with which you encourage others to do so.
I have had Kochans book since day one and that is my manual.
Well, either the book is inadequate or you haven't read it. Let's see: I wonder if I can guess which one is the case?
I just pointed out the (if you insist - percieved) inconsistency.
There is no inconsistency. If you had understood the code you had written you would not have been surprised by the result.
I did not send a 'bug report'
Let's think about this: You had two pieces of code that you thought should give the same result. That means that either you were wrong or the compiler was wrong. I don't imagine you emailed Keil to tell them you were wrong, did you?
Is that so terrible
Yes. Read the manual instead of wasting peoples time.
You seem to have a very negative attitude to those who read the manual. This is strange considering the frequency with which you encourage others to do so. absolutely NOT! I just have " very negative attitude" about your hammering that "your" manual is mandatory. My 'manual' has sufficed me for 15 years. You may, very well know more about the intricacies of special cases in C, I have no probloem with that, I do not need them.
I have had Kochans book since day one and that is my manual. Well, either the book is inadequate or you haven't read it. Let's see: I wonder if I can guess which one is the case? The book in not "inadequate", and I have read it (and still refer to it on occasion); that it might not cover every nook and cranny of the finer, more obscure, details of the language (which, if you use them, will result in code that only the few chosen can understand) is not a problem for me.
I am more interested in studying the finer details of what to code than the finer details of how to code. I have seen lots of s**t from 'professional fully implemeted understanding of C' and none from 'professional fully implemeted understanding of the processor'
Read the manual instead of wasting peoples time.
1) did I ask YOU to waste time posting here.
2) if you took the time to read the thread (instead of wasting it as you state you do by responding here) you would see that I did not start this thread, just posted that what I had seen might be an answer to the OPs problem.
3) you are welcome to 'hate' me, my skin is thick.
Ps could your attitude against me be that you are offended that I often post "this is not a processor to just willy nilly code C for. You actually need to know the hardware and THINK, not just code 'good C'". I know you will not agree but '51 C is not "just C"
absolutely NOT! I just have " very negative attitude" about your hammering that "your" manual is mandatory.
The 'C' standard defines the language and is therefore the best reference. I would suggest that if you dispute this you are plain wrong.
that it might not cover every nook and cranny of the finer, more obscure, details of the language (which, if you use them, will result in code that only the few chosen can understand) is not a problem for me.
The problem you encountered was with arithmetic using simple integer data types. If your book doesn't cover that it is seriously flawed. I suspect it probably does and the real problem is that you haven't read the appropriate section.
I am more interested in studying the finer details of what to code than the finer details of how to code.
A programming language is a tool. If you aren't prepared to learn how the tool works you won't be able to use it properly. In the context of a programming language this will result in buggy, inefficient code.
did I ask YOU to waste time posting here.
No. I was thinking of the support people who have to read the same old questions over and over again from those who haven't read the manual.
if you took the time to read the thread (instead of wasting it as you state you do by responding here) you would see that I did not start this thread, just posted that what I had seen might be an answer to the OPs problem
I did read the thread and I did notice that you didn't start it. I also noticed that you had again posted code showing a fundamental lack of understanding of the 'C' language and thought that I'd take the opportunity to offer you more encouragement to read the manual and improve your programming skills.
you are welcome to 'hate' me, my skin is thick
Please don't confuse a little constructive criticism with hatred. This is a professional forum not a playground.
You actually need to know the hardware and THINK, not just code 'good C'".
Of course. But you do need to be able to code good 'C'.
I know you will not agree but '51 C is not "just C"
'C' is 'C'. Just because you are using it on an 8051 is no excuse to write poor 'C'.
Read the manual!
I have seem that from ever so many that are totally fluent in C never from someone that is "fluent in the '51".
just an example A Cidiot will have switches all over the place when an if, else if construct is much more efficient in Keil C. yes, I have studied the tool.
View all questions in Keil forum