5.14 The HLA Standard Library Bits Module
The HLA Standard Library provides a "bits" module that provides several bit related functions, including builtin functions for many of the algorithms we've studied in this chapter. This section will describe these functions available in the HLA Standard Library.
procedure 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 16bit 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 16bit register: pushw( 0 ); push( ax ); call bits.cnt; // If you prefer to use a higherlevel syntax, try the following: bits.cnt( #{ pushw(0); push(ax); }# ); // Compute the number of bits in a 16bit memory location: pushw( 0 ); push( mem16 ); bits.cnt;If you want to compute the number of bits in an eightbit 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.
procedure 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 (i.e., the same as the distribute algorithm appearing earlier in this chapter). 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.
procedure 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.
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 (see the chapter on Macros for details). 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 computes the bit number in EAX).
procedure bits.reverse32( d:dword ); returns( "EAX" ); procedure bits.reverse16( w:word ); returns( "AX" ); procedure bits.reverse8( b:byte ); returns( "AL" );These three routines return their parameter value with the its bits reversed in the accumulator register (AL/AX/EAX). Call the routine appropriate for your data size.
procedure bits.merge32( even:dword; odd:dword ); returns( "EDX:EAX" ); procedure bits.merge16( even:word; odd:word ); returns( "EAX" ); procedure 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. Notice that these functions return 16, 32, or 64 bits based on byte, word, and double word parameter values.
procedure bits.nibbles32( d:dword ); returns( "EDX:EAX" ); procedure bits.nibbles16( w:word ); returns( "EAX" ); procedure bits.nibbles8( b:byte ); returns( "AX" );These routines extract each nibble from the parameter and place those nibbles into individual bytes.
The bits.nibbles8 function extracts the two nibbles from the b parameter and places the L.O. nibble in AL and the H.O. nibble in AH. The bits.nibbles16 function extracts the four nibbles in w and places them in each of the four bytes of EAX. You can use the BSWAP or ROx instructions to gain access to the nibbles in the H.O. word of EAX. The bits.nibbles32 function extracts the eight nibbles in EAX and distributes them through the eight bytes in EDX:EAX. Nibble zero winds up in AL and nibble seven winds up in the H.O. byte of EDX. Again, you can use BSWAP or the rotate instructions to access the upper bytes of EAX and EDX.5.15 Putting It All Together
Bit manipulation is one area where assembly language really shines. Not only is bit manipulation far more efficient in assembly language than in high level languages, but it's often easier as well. Although the need to manipulate bits is not an everyday requirement, bit manipulation is still a very important problem area. In this chapter we've explored several ways to manipulate data as bits. Although this chapter only begins to cover the possibilities, it should give you some ideas for developing your own bit manipulation algorithms for use in your applications.
