In general, I need to transmit (bit by bit) and then accept a 48-bit packet.

If the packet were 32 bits long, the solution could be something like this:

unsigned long data=0x12345678; for(i=0;i<32;i++){ if(data & 0x80000000) setb_MOD; else clrb_MOD; data <<= 1; } 

This code compiles very nicely and compactly. This is exactly what I would do in assembly language:

  code<<=1; ac: 88 0f add r24, r24 ae: 99 1f adc r25, r25 b0: aa 1f adc r26, r26 b2: bb 1f adc r27, r27 b4: 80 93 63 00 sts 0x0063, r24 b8: 90 93 64 00 sts 0x0064, r25 bc: a0 93 65 00 sts 0x0065, r26 c0: b0 93 66 00 sts 0x0066, r27 

But since the packet has a length of 48 bits, you have to declare it as an array and shift it byte-by-byte in a loop:

 unsigned char data[6]={0x12,0x34,0x56,0x78,0xAB,0xCD}; for(i=0;i<48;i++){ if(data[5] & 0x80) setb_MOD; else clrb_MOD; for(j=5;j>0;j--){ data[j]<<=1; if(data[j-1] & 0x80) data[j]+=1; } data[0] <<= 1; } 

The compilation results depend somewhat on the optimizer settings, it is clear that it does exactly what it is supposed to: i. it compiles quite literally and does not use the costation that is obvious to a person (see above). This is what happens:

  for(j=5;j>0;j--){ code[j]<<=1; a8: 82 91 ld r24, -Z aa: 88 0f add r24, r24 ac: 80 83 st Z, r24 if(code[j-1]&0x80) ae: 9e 91 ld r25, -X b0: 97 fd sbrc r25, 7 b2: 13 c0 rjmp .+38 ; 0xda <__vector_2+0x74> clrb_MOD; } else{ setb_MOD; } for(j=5;j>0;j--){ b4: 80 e0 ldi r24, 0x00 ; 0 b6: a3 36 cpi r26, 0x63 ; 99 b8: b8 07 cpc r27, r24 ba: b1 f7 brne .-20 ; 0xa8 <__vector_2+0x42> code[j]<<=1; if(code[j-1]&0x80) code[j]+=1; } 

I would really like to avoid using inline code inserts in assembly language. Firstly, because I don’t really know this technique and absolutely can’t imagine how I can access the variables declared in C from the assembler code. Although, I know for sure that all this is possible.

Are there any other alternatives to get the optimal code?

  • one
    It’s not clear why you don’t use uint64_t instead of an array? - Vlad from Moscow
  • Well, write more clearly for the compiler: shift the N-1th byte, setting its low-order bit to the high-order bit of the Nth. It seems you overestimate compilers. - Vladimir Martyanov
  • @VladfromMoscow This is a bit redundant, but also suitable! I just did not know about the existence of this type. I just checked it - it works fine (although the compiler for some reason transferred the shifts to a separate subroutine, but this can most likely be corrected by optimizer settings). - Vlada Katlinskaya
  • @ Vladimir Martianov. I do not overestimate :) I was sure in advance that the compiler would not guess to turn the cycle into a sequence of shifts. But train yourself before asking the SO question after all to check :) - Vlada Katlinskaya

2 answers 2

You could do the same thing using the type uint64_t or unsigned long long .

For example,

 #include <stdint.h> //... uint64_t data = 0x123456780123; for ( i = 0; i < 48; i++ ) { ( data & 0x8000000000 ) ? setb_MOD : clrb_MOD; data <<= 1; } 

    If you still need to move the array:

     #include <stdint.h> #define ARRAY_LENGTH 6 void rightShift(uint8_t * array){ uint8_t preBuffer = 0; uint8_t postBuffer = 0; for(uint8_t i = 0; i < ARRAY_LENGTH; ++i){ postBuffer = (array[i] & 0b00000001) << 7; array[i] =>> 1; array[i] |= preBuffer; preBuffer = postBuffer; } } void leftShift(uint8_t * array){ uint8_t preBuffer = 0; uint8_t postBuffer = 0; for(uint8_t i = ARRAY_LENGTH - 1; i < ARRAY_LENGTH; --i){ postBuffer = (array[i] & 0b10000000) >> 7; array[i] =<< 1; array[i] |= preBuffer; preBuffer = postBuffer; } }