32-bit var from four 8-bit var's

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

This should be a very common "problem" with GCC. I want to assign a 32-bit variable a value constructed from four 8-bit variables.
This could be done as

var32 = var8_1 + var8_2*256 + var8_3*256*256 + var8_4*256*256*256

but this seems to generate a large amount of (redundant) code. I simply want to assign each byte of var32 the value of var8_1, var8_2, etc. Can I do this in a better (more efficient) way?

Edit: actually there's not a lot of redundant code, only a little...

/Jakob Selbing

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

Try shifts instead of multiplications to see if that is more efficient:

var32 = var8_1 + ((unsigned int)var8_2 << 8) + ((unsigned long)var8_3 << 16) + ((unsigned long)var8_4 << 24)

Certainly, shifts are more efficient than multiplications, at least in the older AVRs...

- Dean :twisted:

EDIT: Also, a much better way would be to use a union of four bytes and a signle unsigned long:

union CBytesToLong
{
	unsigned long ULongVar;
	unsigned char Byte1;
	unsigned char Byte2;
	unsigned char Byte3;
	unsigned char Byte4;
};

CBytesToLong.Byte1 = var8_4;
CBytesToLong.Byte2 = var8_3;
CBytesToLong.Byte3 = var8_2;
CBytesToLong.Byte4 = var8_1;

var32 = CBytesToLong.ULongVar

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

Last Edited: Fri. Sep 16, 2005 - 02:59 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hallo to ABC-Australia,

does this realy work?

(var8_4 << 24)

I think you have to cast the byte variable to a long variable, because all shift operations are done with 16bit by default. But I'm not realy sure...
It's only because I remember that I had sometimes trouble when the shift value is too big...

Greetings from Germany
Michael

In the beginning was the Word, and the Word was with God, and the Word was God.

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

Skotti, you replied just as I was compiling the code to see if what I wrote would work - I did indeed run into that (message modified). Also, I added the union conversion method, which should result in MUCH simpler code - it's just shoving all the bytes next to each other in RAM and then treating it as an unsigned long.

- Dean :twisted:

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

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

abcminiuser wrote:
Try shifts instead of multiplications to see if that is more efficient:

var32 = var8_1 + ((unsigned int)var8_2 << 8) + ((unsigned long)var8_3 << 16) + ((unsigned long)var8_4 << 24)

Certainly, shifts are more efficient than multiplications, at least in the older AVRs...

- Dean :twisted:

EDIT: Also, a much better way would be to use a union of four bytes and a signle unsigned long:

union CBytesToLong
{
	unsigned long ULongVar;
	unsigned char Byte1;
	unsigned char Byte2;
	unsigned char Byte3;
	unsigned char Byte4;
};

CBytesToLong.Byte1 = var8_4;
CBytesToLong.Byte2 = var8_3;
CBytesToLong.Byte3 = var8_2;
CBytesToLong.Byte4 = var8_1;

var32 = CBytesToLong.ULongVar


Hm, are you really sure that works as it should? Is it guaranteed that the Byte's are not overwriting each other?

BTW I forgot to mention that GCC realizes that multiplication by 256 does not need a full multiplication, but only a full byte shift. So that is not the problem...

/Jakob Selbing

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

Ok, gimme a break, it's 1:10AM here. You need to whack the bytes into a struct:

union CBytesToLong
{
   unsigned long ULongVar;

   struct Bytes
   {
      unsigned char Byte1;
      unsigned char Byte2;
      unsigned char Byte3;
      unsigned char Byte4;
   };
};

CBytesToLong.Bytes.Byte1 = var8_4;
CBytesToLong.Bytes.Byte2 = var8_3;
CBytesToLong.Bytes.Byte3 = var8_2;
CBytesToLong.Bytes.Byte4 = var8_1;

var32 = CBytesToLong.ULongVar

Somthing like that - I'm too tired to fix the syntax. I'm off to bed, g'night!

- Dean :twisted:

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

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

abcminiuser wrote:
Ok, gimme a break, it's 1:10AM here. You need to whack the bytes into a struct:

union CBytesToLong
{
   unsigned long ULongVar;

   struct Bytes
   {
      unsigned char Byte1;
      unsigned char Byte2;
      unsigned char Byte3;
      unsigned char Byte4;
   };
};

CBytesToLong.Bytes.Byte1 = var8_4;
CBytesToLong.Bytes.Byte2 = var8_3;
CBytesToLong.Bytes.Byte3 = var8_2;
CBytesToLong.Bytes.Byte4 = var8_1;

var32 = CBytesToLong.ULongVar

Somthing like that - I'm too tired to fix the syntax. I'm off to bed, g'night!

- Dean :twisted:


Haha, ok thanks. I tried the first version, without struct, and it does NOT work, so now we cleared that out. Seems like GCC won't do assignments for the first 3 bytes, only the fourth.

/Jakob Selbing

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

abcminiuser wrote:
Ok, gimme a break, it's 1:10AM here. You need to whack the bytes into a struct:

Yep, that actually works. Very efficient code. However, is this the _correct_ way of doing it? :twisted:

/Jakob Selbing

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

jaksel,

The union of four bytes and a long to acces the individual bytes in that long are a pretty standard way of doing it.

Another idea would be to use pointers:

unsigned char *p = (unsigned char *)&var_32;

*p++ = var8_1;
*p++ = var8_2;
*p++ = var8_3;
*p++ = var8_4;

Cliff

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

jaksel wrote:
abcminiuser wrote:
Ok, gimme a break, it's 1:10AM here. You need to whack the bytes into a struct:

Yep, that actually works. Very efficient code. However, is this the _correct_ way of doing it? :twisted:

I do it the same way, so it must be the right, correct and best way ;-)

/Jesper
http://www.yampp.com
The quick black AVR jumped over the lazy PIC.
What boots up, must come down.

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

Hello,

I see, the dark side of the force is generally the shortest way... unless you are a pure jedi...

#include 

#ifndef __ATTR_CONST__
#define __ATTR_CONST__ __attribute__((__const__))
#endif

#ifndef __ATTR_ALWAYSINLINE__
#define __ATTR_ALWAYSINLINE__ __attribute__((__always_inline__))
#endif


static __inline__ int16_t CompWord (int8_t lo, int8_t hi) __ATTR_CONST__ __ATTR_ALWAYSINLINE__;
static __inline__ int32_t CompDWord (int16_t lo, int16_t hi) __ATTR_CONST__ __ATTR_ALWAYSINLINE__;
static __inline__ int32_t CompDWord2 (int8_t b0, int8_t b1, int8_t b2, int8_t b3) __ATTR_CONST__ __ATTR_ALWAYSINLINE__;


int16_t CompWord (int8_t lo, int8_t hi)
{
    int16_t tmp;

    __asm__ __volatile__(

            "mov    %A[dst], %[in0]"        "\n\t"
            "mov    %B[dst], %[in1]"        "\n\t"
        :
            [dst] "=&r" (tmp)
        :
            [in0] "r"   (lo),
            [in1] "r"   (hi)
    );

    return tmp;
}


int32_t CompDWord (int16_t lo, int16_t hi)
{
    int32_t tmp;

    __asm__ __volatile__(

            "movw   %A[dst], %[in0]"        "\n\t"
            "movw   %C[dst], %[in1]"        "\n\t"
        :
            [dst] "=&r" (tmp)
        :
            [in0] "r"   (lo),
            [in1] "r"   (hi)
    );

    return tmp;
}


int32_t CompDWord2 (int8_t b0, int8_t b1, int8_t b2, int8_t b3)
{
    int32_t tmp;

    __asm__ __volatile__(

            "mov    %A[dst], %[in0]"        "\n\t"
            "mov    %B[dst], %[in1]"        "\n\t"
            "mov    %C[dst], %[in2]"        "\n\t"
            "mov    %D[dst], %[in3]"        "\n\t"
        :
            [dst] "=&r" (tmp)
        :
            [in0] "r"   (b0),
            [in1] "r"   (b1),
            [in2] "r"   (b2),
            [in3] "r"   (b3)
    );

    return tmp;
}

PS. @Jörg Wunsch: I know, I shoudn't start names with underscores..l. Someday, I'll commit the RFD to add attributes.h header to avr-libc distribution.

Regards,

Carlos.

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

The drawback of the union is portability: one has to take care about
endianess issues then. Not a big deal as long as you want to stay at
the AVR (because it's mostly little endian, at least at the
compiler/library level), but it makes the code less portable.

The `ugly' shifts probably produce the same tight code as the union
but in that case, portability is ensured. That's why this
construction can usually be found in portable operating system
implementations (e.g. SCSI handling in Unix, where the byte order is
defined by the SCSI protocol and must be independent of the hosts'
byte order).

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

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

Hi

I use this code and it work OK. Maybe it will help. I tryed use "shift" (var1 << 8) + (var2 << 16) etc. but it sometimes does not work.

u32 GetU32(u08 hi, u08 mid1, u08 mid2, u08 lo)  //used in FatInit
{
    //u32 ret;
    union
        {
        u08 c[3];
        u32 temp;
        } unie;

    unie.c[0] = lo;
    unie.c[1] = mid2;
    unie.c[2] = mid1;
    unie.c[3] = hi;

    return unie.temp;
}
Last Edited: Tue. Sep 20, 2005 - 03:45 PM