change byte order for 32-bit number (change endianness)

Go To Last Post
14 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I need to store 4-byte value in big-endian format on a little-endian MCU (atmega128)

Is there a neat way to do that?

struct Foo {
  uint32_t data :32;
}
...
Foo foo = { .data = 0x12345678 };
// I want following assert to pass.
assert(((uint8_t*) &foo.data)[0] == 0x12);

I didn't find an answer, I apologize if this is a gross duplicate.

I want an elegant solution.

GCC compiler. MCU: atmega128rfa1

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
  uint32_t data :32;

I don't see the purpose of making a 32 bit value a 32 bit bit field.

static inline void flipEndianess(uint32_t *inVal)
{
    uint8_t *bytes = (uint8_t*)inVal;
    uint8_t temp = bytes[0];
    bytes[0] = bytes[3];
    bytes[3] = temp;
    temp = bytes[1];
    bytes[1] = bytes[2];
    bytes[2] = temp;
}

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Steve, the OP said he wanted an elegant solution. Can you jazz it up a little? :)

"I may make you feel but I can't make you think" - Jethro Tull - Thick As A Brick

"void transmigratus(void) {transmigratus();} // recursio infinitus" - larryvc

"It's much more practical to rely on the processing powers of the real debugger, i.e. the one between the keyboard and chair." - JW wek3

"When you arise in the morning think of what a privilege it is to be alive: to breathe, to think, to enjoy, to love." -  Marcus Aurelius

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How about

#define HTONL(n) (((((unsigned long)(n) & 0xFF)) << 24) | \
               ((((unsigned long)(n) & 0xFF00)) << 8) | \
               ((((unsigned long)(n) & 0xFF0000)) >> 8) | \
               ((((unsigned long)(n) & 0xFF000000)) >> 24))

Edit: This generates ~50 instructions, while Steve's function only needs ~10.

Only about 10 instructions, Steve wins ;) Not much to be gained from writing it in asm either.

"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it"

Last Edited: Sat. Aug 13, 2011 - 03:15 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A structure with 7 bytes, a couple of unions that make the first 4 bytes one long and the last 4 bytes a different long, stick in the number, do 3 copies, and read out the swapped number.

Chuck Baird

"I wish I were dumber so I could be more certain about my opinions. It looks fun." -- Scott Adams

http://www.cbaird.org

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
unsigned long  huge = 0x12345678, swapped;

uint8_t   *ptr;		

int main(void){

uint8_t   yea[4];

   ptr = (uint8_t * )&huge;    
   for ( uint8_t i = 4; i > 0; i-- ){
       yea[i-1] = *ptr++ ; 
   }
.
.

now you can do

assert( yea[0] == 0x12 );

If you need a swapped 32 bit var then after for loop continue with:

   swapped = *( uint32_t * )yea;

Optimizer's TOO BITCHIN' !
*.LSS

	for ( uint8_t i = 4; i > 0; i-- ){

		yea[i-1] = *ptr++ ;
  da:	80 91 00 01 	lds	r24, 0x0100
  de:	8c 83       	std	Y+4, r24	; 0x04
  e0:	80 91 01 01 	lds	r24, 0x0101
  e4:	8b 83       	std	Y+3, r24	; 0x03
  e6:	80 91 02 01 	lds	r24, 0x0102
  ea:	8a 83       	std	Y+2, r24	; 0x02
  ec:	80 91 03 01 	lds	r24, 0x0103
  f0:	89 83       	std	Y+1, r24	; 0x01
	}

	swapped = *( uint32_t * )yea;
  f2:	89 81       	ldd	r24, Y+1	; 0x01
  f4:	9a 81       	ldd	r25, Y+2	; 0x02
  f6:	ab 81       	ldd	r26, Y+3	; 0x03
  f8:	bc 81       	ldd	r27, Y+4	; 0x04
  fa:	80 93 04 01 	sts	0x0104, r24
  fe:	90 93 05 01 	sts	0x0105, r25
 102:	a0 93 06 01 	sts	0x0106, r26
 106:	b0 93 07 01 	sts	0x0107, r27

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

Last Edited: Sat. Aug 13, 2011 - 06:55 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

larryvc wrote:
Steve, the OP said he wanted an elegant solution. Can you jazz it up a little? :)

Challenge accepted:

static inline void flipEndianess(uint32_t *inVal)
{
    uint8_t *bytes = (uint8_t*)inVal;
    bytes[0] ^= bytes[3];
    bytes[3] ^= bytes[0];
    bytes[0] ^= bytes[3];
    bytes[1] ^= bytes[2];
    bytes[2] ^= bytes[1];
    bytes[1] ^= bytes[2];
}

Is that suitably inscrutable^H^H^H^H^H^H^H elegant?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You can do it in 6 asm lines according to the inline asm cookbook. So you'd either use inline, or a *.S file that you link into a C pjt. as another source file.

For my code above, the local var yea[] should be global, or else prologue_saves() gets called and blows it up by 60 bytes !! All that call does is push w/ no pops, a grip a registers, mostly. :shock:

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I was thinking more in terms of tweaking the compiler, rather than doing it run-time.

I mean, when I say:

.data = 0x12345678

compiler will put the value in memory (flash) in the "wrong" order right off the start.
Maybe there is a way to tell it to do it "right" the first time?

Something like:

 .data = __bigendian__(0x12345678)
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Koshchi wrote:

  uint32_t data :32;

I don't see the purpose of making a 32 bit value a 32 bit bit field.

The Foo structure defines a structure of a packet, so it has more fields of different sizes. Though the original problem is valid for a simple uint32_t value as well.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For byteswapping constants, thygate's define should produce no code at all. So it is quite suitable for initializers.

For runtime swaps, something seemingly as dumb as

static inline unsigned long BEL(unsigned long i)
{

  union {unsigned long i; unsigned char b[4];} a,b;
  a.i=i;
  b.b[0]=a.b[3];
  b.b[1]=a.b[2];
  b.b[2]=a.b[1];
  b.b[3]=a.b[0];
  return b.i;
}

is very effective. What happens, at least with the compiler I am using, is that it merely changes the code generator's view of what is in which 8 bit register, and no actual code is emitted. And yet it still 'swaps'.

/Kasper

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You can use a union for a compile time swap but you have to set the bytes in order yourself. That is:

#include 
#include 
#include 

typedef union {
  uint32_t ulong;
  uint8_t byte[4];
} un_type;

typedef struct {
  un_type data;
} Foo_type;

Foo_type foo = { 
  .data.byte[0] = 0x12,
  .data.byte[1] = 0x34,
  .data.byte[2] = 0x56,
  .data.byte[3] = 0x78 };

int main(void) {
// I want following assert to pass.
	assert(((uint8_t*) &foo.data.ulong)[0] == 0x12); 
}

In the simulator this skips the call to abort() within the assert() macro.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
.data = __bigendian__(0x12345678)

Intel finally added bi-endian support to their x86 C compiler. It's wonderful! For some reason, most compiler authors seem adverse to implementing features like this. Too concrete and not abstracted enough.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here's LUFA's endianness header file:

https://code.google.com/p/lufa-l...

That defines upper-case versions (e.g. CPU_TO_LE16) of the swapping macros for compile time constants, and lower case (e.g. cpu_to_le16) for run-time constants, modeled after the names used in Linux. The macros are defined for 8, 16 and 32 bit types, in both directions (native to little/big, or little/big to native).

You could also use the GCC-specific __builtin_constant_p() pseudo-macro to automatically select between then for runtime and compile time constants using a single set of macros.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!