6.4 Logical (Boolean) Expressions


6.4 Logical (Boolean) Expressions

Consider the following expression from a Pascal program:

      B := ((X=Y) and (A <= C)) or ((Z-A) <> 5); 

B is a boolean variable and the remaining variables are all integers.

How do we represent boolean variables in assembly language? Although it takes only a single bit to represent a boolean value, most assembly language programmers allocate a whole byte or word for this purpose (as such, HLA also allocates a whole byte for a boolean variable). With a byte, there are 256 possible values we can use to represent the two values true and false. So which two values (or which two sets of values) do we use to represent these boolean values? Because of the machine's architecture, it's much easier to test for conditions like zero or not zero and positive or negative rather than to test for one of two particular boolean values. Most programmers (and, indeed, some programming languages like "C") choose zero to represent false and anything else to represent true. Some people prefer to represent true and false with one and zero (respectively) and not allow any other values. Others select all one bits ($FFFF_FFFF, $FFFF, or $FF) for true and 0 for false. You could also use a positive value for true and a negative value for false. All these mechanisms have their own advantages and drawbacks.

Using only zero and one to represent false and true offers two very big advantages: (1) The setcc instructions produce these results so this scheme is compatible with those instructions; (2) the 80x86 logical instructions (and, or, xor and, to a lesser extent, not) operate on these values exactly as you would expect. That is, if you have two boolean variables A and B, then the following instructions perform the basic logical operations on these two variables:

 // c = a AND b;      mov( a, al );      and( b, al );      mov( al, c ); // c = a OR b;      mov( a, al );      or( b, al );      mov( al, c ); // c = a XOR b;      mov( a, al );      xor( b, al );      mov( al, c ); // b = not a;      mov( a, al );  // Note that the NOT instruction does not      not( al );     // properly compute al = not al by itself.      and( 1, al );  // I.e., (not 0) does not equal one. The AND      mov( al, b );  // instruction corrects this problem.      mov( a, al );  // Another way to do b = not a;      xor( 1, al );  // Inverts bit zero.      mov( al, b ); 

Note, as pointed out in the preceding code, that the not instruction will not properly compute logical negation. The bitwise NOT of zero is $FF and the bitwise NOT of one is $FE. Neither result is zero or one. However, by ANDing the result with one you get the proper result. Note that you can implement the NOT operation more efficiently using the "xor( 1, ax );" instruction because it only affects the L.O. bit.

As it turns out, using zero for false and anything else for true has a lot of subtle advantages. Specifically, the test for true or false is often implicit in the execution of any logical instruction. However, this mechanism suffers from a very big disadvantage: you cannot use the 80x86 and, or, xor, and not instructions to implement the boolean operations of the same name. Consider the two values $55 and $AA. They're both non-zero so they both represent the value true. However, if you logically AND $55 and $AA together using the 80x86 and instruction, the result is zero. True AND true should produce true, not false. Although you can account for situations like this, it usually requires a few extra instructions and is somewhat less efficient when computing boolean operations.

A system that uses non-zero values to represent true and zero to represent false is an arithmetic logical system. A system that uses the two distinct values like zero and one to represent false and true is called a boolean logical system, or simply a boolean system. You can use either system, as convenient. Consider again the boolean expression

      B := ((X=Y) and (A <= D)) or ((Z-A) <> 5); 

The simple expressions resulting from this expression might be

      mov( x, eax );      cmp( y, eax );      sete( al );       // AL := x = y;      mov( a, ebx );      cmp( ebx, d );      setle( bl );      // BL := a <= d;      and( al, bl );    // BL := (x=y) and (a <= d);      mov( z, eax );      sub( a, eax );      cmp( eax, 5 );      setne( al );      or( bl, al );     // AL := ((X=Y) and (A <= D)) or ((Z-A) <> 5);      mov( al, b ); 

When working with boolean expressions don't forget the that you might be able to optimize your code by simplifying those boolean expressions. You can use algebraic transformations to help reduce the complexity of an expression. In the chapter on control structures you'll also see how to use control flow to calculate a boolean result. This is generally quite a bit more efficient than using complete boolean evaluation, as the examples in this section teach.




The Art of Assembly Language
The Art of Assembly Language
ISBN: 1593272073
EAN: 2147483647
Year: 2005
Pages: 246
Authors: Randall Hyde

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net