Perl - Forming a value from bits lying between two (16 bit) fields (data across word boundaries)

250 views Asked by At

I have am reading some data into 16 bit data words, and extracting VALUES from parts of the 16 bit words. Some of the values I need straddles the word boundaries.

I need to take the bits from the first word and some from the second word and join them to form a value.

I am thinking of the best way to do this. I could bit shift stuff all over the place and compose the data that way, but I am thinking there must be perhaps an easier/better way because I have many cases like this and the values are in some case different sizes (which I know since I have a data map).

For instance:

[TTTTTDDDDPPPPYYY] - 16 bit field

[YYYYYWWWWWQQQQQQ] - 16 bit field

TTTTT  = 5 bit value, easily extracted
DDDD   = 4 bit value, easily extracted
WWWWW  = 5 bit value, easily extracted
QQQQQQ = 6 bit value, easily extracted

YYYYYYYY = 8 bit value, which straddles the word boundaries. What is the best way to extract this? In my case I have a LOT of data like this, so elegance/simplicity in a solution is what I seek.

Aside - In Perl what are the limits of left shifting? I am on a 32 bit computer, am I right to guess that my (duck) types are 32 bit variables and that I can shift that far, even though I unpacked the data as 16 bits (unpack with type n) into a variable? This situation came up in the case of trying to extract a 31 bit variable that lies between two 16 bit fields.

Lastly (someone may ask), reading/unpacking the data into 32 bit words does not help me as I still face the same issue - Data is not aligned on word boundaries but crosses it.


1

There are 1 answers

2
ikegami On BEST ANSWER

The size of your integers are given (in bytes) by perl -V:ivsize or programatically using use Config qw( %Config ); $Config{ivsize}. They'll have 32 bit in a 32-bit build (since they are guaranteed to be large enough to hold a pointer). That means you can use

my $i = ($hi << 16 | $lo);      # TTTTTDDDDPPPPYYYYYYYYWWWWWQQQQQQ
my $q = ($i >>  0) & (2**6-1);
my $w = ($i >>  6) & (2**5-1);
my $y = ($i >> 11) & (2**8-1);
my $p = ($i >> 19) & (2**4-1);
my $d = ($i >> 23) & (2**4-1);
my $t = ($i >> 27) & (2**5-1);

If you wanted to stick to 16 bits, you could use the following:

my $y = ($hi & 0x7) << 5 | ($lo >> 11);

00000[00000000YYY     ]
     [           YYYYY]WWWWWQQQQQQ
     ------------------
     [00000000YYYYYYYY]