I know that longs are stored MSB first in memory. I need to access individual bytes of a long in order to send them out the SPI port. There are a number of ways to do this, I'd like your opinion on which approach might be best (from whatever perspective). Two that come to mind is to simply declare the variable as a long pointer and then access each byte using the pointer. Pretty clean as far as I am concerned, but don't know if there's a better C51 way to handle this task. Another --more convoluted, maybe even ugly-- is to avoid direct memory access and simply rotate/mask the long and cast into an unsigned char four times to grab each byte. Ugly at best. Thanks, -Martin
While Rotate and Mask is portable, It will most likely generate a lot of code. You have Pointers. You could use a union instead. The Best way. Try both and look at the generated code. For both size and speed, depending on which is more important to you.
I should have also mentioned that some of these unsigned longs will be arguments passed to functions. It would seem to me that using pointer addressing might be the easiest way to access the individual bytes in the long.
some of these unsigned longs will be arguments passed to functions That's actually an argument against using pointer fudging to access individual bytes --- if they're function arguments, they'll have arrived in single-byte registers. There is thus not really a contiguous ordinary memory location where a pointer could point at them. [Well, strictly spoken there is, but it's the IDATA equivalent of absolute register access. I wouldn't bet on the compiler figuring that out anywhere near automatically]
someone mentioned "portable" While I believe that "portable" is an illusion in small embedded (did you ever visualize converting a '51 product to PIC or vv?) if you have to "maintain portability" make sure the routines that depend on the byte sequence of longs, ints etc are enclosed in #ifs Erik
> if they're function arguments, they'll have arrived in single-byte registers OK, that makes sense. I need to read-up on how the compiler handles function arguments. So...whould you say that something like: void X(unsigned long Y) { unsigned long *Z; ... (*Z) = Y; ...etc } Would be the way to access the bytes? Or, should I bite the bullet and do a lot of shifting? Not matter what it seems that the mechanism will not be very elegant.
BTW, portability is of little concern here. Besides, it can be easily handled by encapsulating the byte-select operation into it's own little function: unsigned char byte_select(unsigned long value) { // Whatever technique } If the code had to be ported to a different device this routine would simply be redone for best results.
Would be the way to access the bytes? Or, should I bite the bullet and do a lot of shifting? You could shift, the Keil compiler is smart enough to do "byte picking" when shifts are constant multiples of 8, thus no overhead would result. Of course, the "dynamic" way to do it would be with a union, I do that in many places. Erik
the Keil compiler is smart enough to do "byte picking" when shifts are constant multiples of 8 Not always, or so I've found. That is, while the U32 shift function might be smart enough to run faster by skipping multiples of 8, the code generator isn't smart enough to avoid the shift entirely. For example:
U8 nextHighest; MultiByte32 val; nextHighest = val.u8[1];
nextHighest = (U8)(val >> 24);
/// provides access to words/bytes of a U32 typedef union { U32 u32; U16 u16[2]; U8 u8[4]; struct { MultiByte16 msw; MultiByte16 lsw; } words; } MultiByte32;
void X(unsigned long Y) { unsigned long *Z; ... (*Z) = Y; As written, that's completely wrong. You want to take the address of the long, not write its value to where some uninitialized pointer might happen to point to.
void X(unsigned long Y) { unsigned long *Z; ... (*Z) = Y;
Someone just said that function arguments are passed in registers. The code was an attempt to replicate the value elsewhere in memory in order to be able to disect it with pointer access.
I like the "MultiByte32" union approach. Does this work well with function arguments? Can you pass a MultiByte32 as an arg?
Hi Martin, Although you are aware that the MSB is stored 1st, I think that your second idea of shifts and casting is the safer option because programmers who are not as familiar with the 8051 may not easily be able to see the order that you are sending the data out via the SPI (ie. is LSB->MSB or MSB->LSB and yes well commented code should make it self explanatory whatever method you use). Also because you are passing longs to functions, as they are alredy in 4 registers the compiler may actually generate more efficient code because it will effectivly be just loading each reg into the SPI without any need to do the shifts/masking or 'messing' with pointers to retreive each of the the bytes making up the long. Mark.
That's an interesting point. I'm trying to stay as "C-like" as possible with this project. I have twenty+ years of assembler-based embedded work under my belt...but all my C/C++ work has been in application coding on desktop systems. This is my first embedded-C project, which is actually a translation of an existing ASM app to C. It's very different when you have the restrictions of an 8051 to contend with. Thanks for your help.