TOC PREV NEXT INDEX

Put your logo here!


3 Bit Manipulation (bits.hhf)


The HLA BITS module contains several procedures useful for bit manpulation. Currently, this includes routines like counting bits, reversing bits, and merging bit streams.

3.1 Bit Counting Functions

bits.cnt( b:dword ); @returns( "EAX" );
 

This procedure returns the number of one bits present in the "b" parameter. It returns the count in the EAX register. To count the number of zero bits in the parameter value, invert the value of the parameter before passing it to bits.cnt. If you want to count the number of bits in a 16-bit operand, simply zero extend it to 32 bits prior to calling this function. Here are a couple of examples:


 
// Compute the number of bits in a 16-bit register:
 

 
		pushw( 0 );
 
		push( ax );
 
		call bits.cnt;
 

 
// If you prefer to use a higher-level syntax, try the following:
 

 
		bits.cnt( #{ pushw(0); push(ax); }# );
 

 
// Compute the number of bits in a 16-bit memory location:
 

 
		pushw( 0 );
 
		push( mem16 );
 
		bits.cnt;
 

 

If you want to compute the number of bits in an eight-bit operand it's probably faster to write a simple loop that rotates all the bits in the source operand and adds the carry into the accumulating sum. Of course, if performance isn't an issue, you can zero extend the byte to 32 bits and call the bits.cnt procedure.

Note: to count the number of zero bits in an object, first invert than object and then call bits.cnt.

3.2 Bit Movement, Insertion, and Extraction Functions

bits.reverse32( b:dword );  @returns( "EAX" );
 
bits.reverse16( b:word );  @returns( "AX" );
 
bits.reverse8( b:byte );  @returns( "AL" );
 

These three routines return their parameter with the bits reversed.



Figure 1 Bit Reversal Operation

bits.merge32( even:dword; odd:dword );  @returns( "EDX:EAX" ); 
 
bits.merge16( even:word; odd:word );  @returns( "EAX" ); 
 
bits.merge8( even:byte; odd:byte );  @returns( "AX" );
 

These routines merge two streams of bits to produce a value whose size is the combination of the two parameters. The bits from the even parameter occupy the even bits in the result, the bits from the odd parameter occupy the odd bits in the result. Note that the result is always twice as long as the individual parameters.



Figure 2 Bit Merge Operation

bits.nibbles32( d:dword );  @returns( "EDX:EAX" ); 
 
bits.nibbles16( w:word );  @returns( "EAX" ); 
 
bits.nibbles8( b:byte );  @returns( "AX" ); 
 

These routines extract each nibble from the parameter and place those nibbles into individual bytes. nibbles8 extracts the two nibbles from the b parameter and places the L.O. nibble in AL and the H.O. nibble in AH. nibbles16 extracts the four nibbles from the w parameter and places the four nibbles in the four bytes of the EAX register. The nibbles32 function spreads the eight nibbles in the d parameter across the eight bytes in EDX:EAX.



Figure 3 Nibble Extraction Operation

procedure bits.extract( var d:dword ); 
 
	@returns( "EAX" );  // Really a macro.
 

This function extracts the first set bit in d searching from bit #0 and returns the index of this bit in the EAX register; the function will also return the zero flag clear in this case. This function also clears that bit in the operand. If d contains zero, then this function returns the zero flag set and EAX will contain -1.

Note that HLA actually implements this function as a macro, not a procedure. This means that you can pass any double word operand as a parameter (i.e., a memory or a register operand). However, the results are undefined if you pass EAX as the parameter (since this function returns the bit number in EAX).



Figure 4 Bit Extraction

bits.distribute( source:dword; mask:dword; dest:dword ); 
 
	@returns( "EAX" );
 

This function takes the L.O. n bits of source, where n is the number of "1" bits in mask, and inserts these bits into dest at the bit positions specified by the "1" bits in mask. This function does not change the bits in dest that correspond to the zeros in the mask value. This function does not affect the value of the actual dest parameter, instead, it returns the new value in the EAX register.

Example:

source = $FF00_AA55
 
mask = $F0FF_000F
 
dest = $1234_5678
 

The bits.distribute function grabs the L.O. 16 bits of source and inserts these bits at positions 0, 1, 2, 3, 16, 17, 18, 19, 20, 21, 22, 23, and 28, 29, 30, and 31 into the value $1234_5678 and returns the result in EAX. For this example, bits.distribute begins by grabbing the L.O. four bits of source and inserts them at bit positions 0..3 of $1234_5678 (since mask contains four set bits at positions 0..3). This yields the temporary result $1234_5675. Next, bits.distribute grabs bits 4..11 from source ($A5) and inserts these bits into bit positions 16..23 since mask contains eight consecutive bits between positions 16 and 23. The produces the temporary result $12A5_5675. Finally, bits.distribute grabs bits 12..15 from the source operand ($A) and inserts these into the result at bit positions 28..31 (since the mask value contains set bits at these positions). The final result this function returns in EAX is $A2A5_5675.



Figure 5 Bit Distribution

bits.coalese( source:dword; mask:dword );
 
	@returns( "EAX" );
 

This function is the converse of bits.distribute. It extracts all the bits in source whose corresponding positions in mask contain a one. This function coalesces (right justifies) these bits in the L.O. bit positions of the result and returns the result in EAX.

Example:

source = $afff_ffce
 
mask = $aaaa_5555
 

bits.coalesce grabs bits 0, 2, 4, 6, 8, 10, 12, 14, 17, 19, 21, 23, 25, 27, 29, and 31 from source and packs these into the L.O. 16 bits of EAX (it also sets the H.O. bits of EAX to zero). The final result in EAX is $FFFA.



Figure 6 Bit Coalesce Operation



TOC PREV NEXT INDEX