Trying to understand the workings of a Memory Mapped I/O

Here is the sample area:

#define P0 ((*(__R volatile unsigned char *)(unsigned char)0x0)) #define P1 ((*(__R volatile unsigned char *)(unsigned char)0x1)) #define P2 ((*(__R volatile unsigned char *)(unsigned char)0x2)) #define P3 ((*(__R volatile unsigned char *)(unsigned char)0x3))

How would you describe what this is or does? If someone could break it down so I could understand what is going on I would be in your debt.

Reply to
Las
Loading thread data ...

Why would your teacher assign you something he had not discussed?

Anyway, when confused about declarations, read from R to L

// #define P0 ((*(__R volatile unsigned char *)(unsigned char)0x0))

// (unsigned char)0x0

You have a character constant of 0

// (__R volatile unsigned char *)

It is cast to a volatile pointer for some bad reason, and who knows what __R is, because it is a user's macro.

// *(..)

Take the contents of the (nonsense) pointer.

// #define P0

Might as well compound the nonsense with bad practice by using a gratuitous macro.

Good luck on your homework.

Reply to
Bryan Hackney

thanks for your help. Would you know of a book or website that describes this type of process.

How does *(..) take the contents?

Las

Reply to
Las

__R is,

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.

Adam

Reply to
Adam Braun

is,

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.

--Gene

Reply to
Gene S. Berkowitz

is,

My problem was not with the volatile, but with the initial superflous cast to unsigned char. What's wrong with :

#define P0 ((*(__R volatile unsigned char *)0x0))

?

Better written as:

#define P0 ( *(__R volatile unsigned char *)0x0 )

Reply to
Bryan Hackney

is,

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.

--Gene

Reply to
Gene S. Berkowitz

down

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.

Reply to
D Yuniskis

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.

--
Jack Klein
Home: http://JK-Technology.Com
 Click to see the full signature
Reply to
Jack Klein

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.

--
Jack Klein
Home: http://JK-Technology.Com
 Click to see the full signature
Reply to
Jack Klein

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.

--Gene

Reply to
Gene S. Berkowitz

There are billions.

When used that way, it is the C idiom for "take the contents of the address". It is followed by an address.

Before assigning this, your teacher should have explained the "address of" and "content of" operators in C. Maybe she jumped way ahead to scare you.

Reply to
Bryan Hackney

to

this(depending on

at

needed to

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.

Reply to
Adam Braun

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:

((*(__R volatile unsigned char *)(unsigned char)0x0))

...and:

((*(__R volatile unsigned char *)0x0))

...at least not that anyone would buy.

--
Jack Klein
Home: http://JK-Technology.Com
 Click to see the full signature
Reply to
Jack Klein

On Mon, 1 Dec 2003 00:17:44 -0500, Gene S. Berkowitz wrote in comp.arch.embedded:

Are you claiming that you know of a compiler that generates a warning or error for this:

unsigned char uc = P0;

...when the P0 macro is defined as:

#define P0 ((*(__R volatile unsigned char *)0x0))

...but does not when the P0 macro is defined as:

#define P0 ((*(__R volatile unsigned char *)(unsigned char)0x0))

???

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.

--
Jack Klein
Home: http://JK-Technology.Com
 Click to see the full signature
Reply to
Jack Klein

So, when using P0 in my code, I will be working with the value 0x0 directly?

Reply to
Las

Bryan Hackney wrote: [...]

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.
Reply to
Hans-Bernhard Broeker

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.
Reply to
Hans-Bernhard Broeker

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.

Thad

Reply to
Thad Smith

You will be using the value "stored in" address "0x0" (assuming your compiler does what it *could* be expected to do...)

--don

N.B. Incoming mail is not accepted at this email address.

Reply to
D Yuniskis

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.