1
\$\begingroup\$

Most, if not all, chips targeted by XC8 process one byte at a time. To assign a value from one multi-byte variable to another, it has to load the first 8 bits from the source into WREG and store them into the first 8 bits of the destination and then do that again with the next 8 bits. At the assembly level, this makes it trivial to adjust the endianness at the same time simply by reordering the load instructions or the store instructions (but not both).

But I can't seem to make XC8 do that. I've tried various combinations of shifting by 8, or'ing components, and'ing with masks, etc., and the smallest assembly code I can get comes from a straight assignment followed by an xor swap:

union
{
    unsigned int i;
    struct
    {
        unsigned char lo;
        unsigned char hi;
    };
} address;

    address.i = Packet->dest_addr;
    address.hi ^= address.lo;   //xor swap: valA ^ valB = diff (bitwise difference, not arithmetic)
    address.lo ^= address.hi;   //xor swap: valA ^ diff = valB
    address.hi ^= address.lo;   //xor swap: valB ^ diff = valA

I would think that the pointer source would help or make no difference, depending on whether it took advantage of the pointer inc/decrement instructions, but it insists on recalculating the pointer for each byte except for a direct assignment like this.

Am I missing something?

(I included the hi-tech tag because XC8 is descended from Hi-Tech after Microchip bought it. Maybe there's an old trick from there that still works?)

\$\endgroup\$
2
\$\begingroup\$

First, if you care about the machine instructions, use assembler.

Second, if you want to use a different encoding scheme than what the compiler uses for a multi-byte integer, then you might as well declare it as a byte array in the first place. The compiler won't be able to operate on a byte-flipped integer properly anyway.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ I was wanting to avoid inlining this. I just wanted it to be a little more concise in C without horribly de-optimizing it. The reason to endian-swap in this case is that the incoming Packet already has it backwards and I need to correct it before I use it. But that detail shouldn't matter just to make the tool work. \$\endgroup\$
    – AaronD
    Sep 25 '14 at 18:40
0
\$\begingroup\$

I don't have xc8.

Extending your code:

union Fubar
{
    unsigned int i;
    struct
    {
        unsigned char lo;
        unsigned char hi;
    };
};

    union Fubar address;
    unsigned int da = Packet->dest_addr;
    address.hi = ((union Fubar)da).lo;
    address.lo = ((union Fubar)da).hi;

or

    union Fubar address;
    address.hi = ((union Fubar)Packet->dest_addr).lo;
    address.lo = ((union Fubar)Packet->dest_addr).hi;

Isn't one of those smaller?

I'd expect the second one to be slower, because it reloads stuff, if any part of Packet->dest_addr is volatile.

Edit (Even Uglier Alert):

union Fubar address;
unsigned char* const dp = &((unsigned char)Packet->dest_addr);
address.hi = dp[0];    // may need to swap the two destinations,
address.lo = dp[1];    // hi, lo, to get required byte order

That might help let the compiler do a better job.

\$\endgroup\$
11
  • \$\begingroup\$ Your first one is probably shorter by 2 instructions, but it uses a temp variable, which may not be all bad. (My xor swap takes a total of six plus the initial assignment; your first one would take four.) \$\endgroup\$
    – AaronD
    Sep 25 '14 at 18:43
  • \$\begingroup\$ I would expect your second one to recalculate the pointer simply because it appears a second time. (Stupid, I know, but that's the pattern that I see in the assembly listing.) Good thinking, though. \$\endgroup\$
    – AaronD
    Sep 25 '14 at 18:45
  • 1
    \$\begingroup\$ @AaronD - IMHO it's hard to second guess a compiler, especially for the sort of complex trade-offs small PICs seem to need to squeeze the maximum from them. \$\endgroup\$
    – gbulmer
    Sep 25 '14 at 18:47
  • \$\begingroup\$ Closer look at the original code (I posted a simplified version), Packet is actually a detailed structure itself that includes the same union for dest_addr. Using hi and lo of that does indeed recalculate the pointer. \$\endgroup\$
    – AaronD
    Sep 25 '14 at 18:51
  • 3
    \$\begingroup\$ I would avoid using pointers on a PIC 16 if code efficiency is a primary concern. After all, you're just copying two bytes with the source and destination addresses known at link time. Pointers shouldn't be needed. \$\endgroup\$ Sep 25 '14 at 19:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.