Yeah, that's what made it interesting.
Previously this was your format.
Code:
head = 2 bits
data_base[32-30] = 3 bits
pause_bit = 1 bit
data_base[29-15] = 15 bits
pause_bit = 1 bit
data_base[14-0] = 15 bits
pause_bit = 1 bit
data_extent = 9 bits
pause_bit = 1 bit
So the data bits might be represented like this.
Code:
Byte 0 Byte 1 Byte 2 Byte 3 Byte 4 Byte 5
hhdAApBB CCCCCCCC DDDDDpEE FFFFFFFF GGGGGpee eeeeeeep
^^ ^^ ^^^^^^^^ ^^^^^ ^^ ^^^^^^^^ ^^^^^
|| || |||||||| ||||| || |||||||| |||||
0x18|| 0xFF 0xF8 || 0xFF 0xF8
0x03 0x03
Okay, getting those masks was pretty easy. I did the masking of a byte from the incoming array using a byte-sized constant that was an unsigned long. That was for use with the shift later. But this first-stage result will be in the lower 8 bits.
You want to move each of these blocks of bits to the right position in the result.
Code:
Bit 3322222222221111111111
Bit 10987654321098765432109876543210
AABBCCCCCCCCDDDDDEEFFFFFFFFGGGGG
The values of the shift might be more normal-looking if the lsb were always at bit 0. But I skipped any right-shifting to do that, and instead just moved the remaining distance.
Code:
unsigned long foo(unsigned char *addr)
{
return (addr[0] & 0x18UL) << 27 | /* bits 31-30 */
(addr[0] & 0x03UL) << 28 | /* bits 29-28 */
(addr[1] & 0xFFUL) << 20 | /* bits 27-20 */
(addr[2] & 0xF8UL) << 12 | /* bits 19-15 */
(addr[2] & 0x03UL) << 13 | /* bits 14-13 */
(addr[3] & 0xFFUL) << 5 | /* bits 12- 5 */
(addr[4] & 0xF8UL) >> 3; /* bits 4 - 0 */
}
For example, the first shift uses 27 instead of 30 because the bits are already 3 places to the right.