Actually, since the description indicates that this is to allow access to memory mapped I/O registers, this is a valid way to handle this(depending on the definition of the __R macro). Presumably, the register are located at addresses 0 through 3 of the memory map. The volatile qualifer is needed to prevent the compiler from optimizing the access to the value to far.
It's being declared volatile because it refers to an I/O port; you don't want the compiler optimizing it out, and/or failing to read/write it.
For example:
P0 = 0x01; /* Turn on thingy attached to bit 0 of Port 0 */ while(P0 & 0x01) { BlinkLights(); BlowHorn(); } // Until thing signals by driving P0.0 LOW. */
..Now, the compiler might optimize that P0 is already true due to the assignment, and never execute what's in the braces. Volatile forces P0 to be read back for the AND operation.
Some of the compilers I've used will complain that you are trying to cast a integer or a long to a char*; the preprocessor in these cases treat all (non floating) literal values as int or long, and the compiler won't perform an implicit cast to a pointer of a different (smaller?) type. I've had to insert explicit casts exactly like this to get the compiler to swallow it.
Basically, it's a kludge. (folks in comp.lang.c will undoubtedly point out that it's not "guaranteed" to work -- esp. P0)
In each case, the constants 0, 1, 2, and 3 are being cast to unsigned char's (probably out of confusion :-/ ). I believe these (rightmost) casts are superfluous.
Moving towards the left, each of these constants is then cast to a pointer to an "unsigned char" -- that being a typical concept of a "generic byte"
The volatile keyword tells the compiler not to optimize away references to this "item" (since you claim it represents a memory mapped I/O device, this is "as expected"...)
No comment as to what the "__R" macro does.
The leftmost '*' tells the compiler to dereference this "pointer". In other words, it evaluates to "the value of the unsigned char located at the 'address' referenced by this pointer that happens to have the value ("address") of "0x0", "0x1", etc."
The leftmost token following the #define keyword allows the programmer to refer to this contortion with a simple mnemonic -- i.e. "P0" (Port0), etc.
--don
N.B. Incoming mail is not accepted at this email address.
On Mon, 01 Dec 2003 02:47:04 GMT, "Adam Braun" wrote in comp.arch.embedded:
The cast of the literal values to unsigned char before casting to a pointer is gratuitous and just plain dumb, unless the compiler is severely non-conforming. The literals 0x0, 0x1, 0x2, and 0x3 have the type int and are unchanged by any transformations other than to type _Bool, which I highly doubt this compiler supports.
It looks to me like a macro written by somebody who doesn't understand how values work in C.
On Sun, 30 Nov 2003 23:07:47 -0500, Gene S. Berkowitz wrote in comp.arch.embedded:
__R is,
There is no such thing as an "implicit cast". Use of the term demonstrates a lack of understanding of the C type system. In C there are conversions. Some are automatic and require no cast. Others are not allowed without a cast. A cast operator performs an explicit conversion which might or might not be automatic in its absence.
That's absurd. Any compiler which will not accept this is horribly broken, so much so that it is lying if it claims to be a C compiler at all.
The type of the literal constants "0x0", "0x1", "0x2", and "0x3" it signed int in every actual C compiler that has ever existed, because otherwise it is not a C compiler.
Any compiler that treats a cast of a signed int to a pointer any differently than it does the cast of an unsigned char to a pointer is absolutely broken.
The actual wording of the ANSI/ISO C Standard:
======== An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation. ========
Now it makes no difference whether you understand that the standard uses the term "an integer" to encompass all of the integer types, or assume it just means type int. A compiler is required to accept a cast of a signed int to a pointer type.
The macro in question was written by either:
a. Somebody ignorant of the C type system.
b. Somebody acutely aware of a non-conforming compiler.
You can call it whatever you like; it doesn't change the fact that tools (I won't call it a C compiler, for your blood pressure), especially those sold into the embedded market, are often non-standard and/or broken. I can insert a cast, and the error goes away, or I can deliver my code late because I'm spending two hours on the phone explaining it to "tech support", then waiting 3 months for the fix.
While the first cast isn't needed in the general case, it doesn't really hurt to have it. On the other hand, it may have a valid usage here. If this is for an 8 bit processor, then this technique may (hypothetically) force the compiler to use single byte addressing to get to register instead of, for instance, using an 16 bit register which would be less efficient. Without getting into the details of the processor and compiler, you can't know this. Admitedly, you would like the compiler to handle this for you, but sometimes they aren't perfect. The end result is that it doesn't hurt anything in this particular case and it may have an actual usage.
On Mon, 01 Dec 2003 06:47:43 GMT, "Adam Braun" wrote in comp.arch.embedded:
???
The final cast is to (volatile unsigned char *), which is a specific type that has a specific size. The fact that the micro involved might have multiple address spaces, as for example does the 8051, is presumably handled by the "__R" macro.
There can't be a compiler out there that would generate different object code for these two lines:
I have sent back alleged C compilers this bad for a refund. It would be better to code in assembly than some language that some unqualified idiot made up. There is no quality control at all, and you would have no idea at all of what the code the compiler did accept would actually do when executed. If it could bungle something as basic as this, who knows what object code it would generate from other basic C source that it misinterprets?
As for compilers for embedded systems, I have been using them for more than 20 years, for 8 through 32 bit controllers and processors, and for DSPs as well, so I think I am aware of the state of the market.
It's worse than that, because __R is explicitly _not_ a user macro.
It's in the namespace reserved for usage by the compiler implementor. This implies it's specifically unfit for being used in normal code that tries to be anywhere near portable. The reason being that portable code cannot know what meaning __R might have on a randomly chosen platform, nor is it allowed to re-#define it to meaning something else if it doesn't like the meaning it happens to have.
--
Hans-Bernhard Broeker (broeker@physik.rwth-aachen.de)
Even if all the snow were burnt, ashes would remain.
Goodness --- get yourself a C textbook and start to learn from it _now_. It may already be too late for this, but it'll definitely be too late if you delay this necessary previous study even further into this new course this homework problem was taken from. You may simply not be fit for this course yet.
--
Hans-Bernhard Broeker (broeker@physik.rwth-aachen.de)
Even if all the snow were burnt, ashes would remain.
Probably not. It's possible, though, that the __R indicates a separate address space that uses only 8 bit addressing, and that without the (unsigned char) cast, the compiler would issue a warning because high-order bits are potentially "thrown away", based on a type size, rather than value, test. We don't know.
It could simply be the programmer's idiom to make port addresses explicitly 8 bits wide to reflect the size of that address space. I use '\0' for a null character, for example, knowing full well that 0 means the same thing to the C compiler.
ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here.
All logos and trade names are the property of their respective owners.