The previous section defines the logical functions for single-bit operands. Because the 80x86 uses groups of 8, 16, or 32 bits, we need to extend the definition of these functions to deal with more than 2 bits. Logical functions on the 80x86 operate on a bit-by-bit (or bitwise) basis. Given two values, these functions operate on bit 0, producing bit 0 of the result. They operate on bit 1 of the input values, producing bit 1 of the result, and so on. For example, if you want to compute the logical and
of the following two 8-bit numbers, you would perform the logical and
operation on each column independently of the others:
%1011_0101 %1110_1110 ---------- %1010_0100
You may apply this bit-by-bit calculation to the other logical functions as well.
Because we've defined logical operations in terms of binary values, you'll find it much easier to perform logical operations on binary values than on other representations. Therefore, if you want to perform a logical operation on two hexadecimal numbers, you should convert them to binary first. This applies to most of the basic logical operations on binary numbers (e.g., and
, or
, xor
, etc.).
The ability to force bits to 0 or 1 using the logical and
/or
operations and the ability to invert bits using the logical xor
operation are very important when working with strings of bits (e.g., binary numbers). These operations let you selectively manipulate certain bits within some bit string while leaving other bits unaffected. For example, if you have an 8-bit binary value X and you want to guarantee that bits 4..7 contain 0s, you could logically and
the value X with the binary value %0000_1111. This bitwise logical and
operation would force the H.O. 4 bits to 0 and pass the L.O. 4 bits of X unchanged. Likewise, you could force the L.O. bit of X to 1 and invert bit 2 of X by logically or
ing X with %0000_0001 and logically exclusive-or
ing X with %0000_0100, respectively. Using the logical and
, or
, and xor
operations to manipulate bit strings in this fashion is known as masking bit strings. We use the term masking because we can use certain values (1 for and
, 0 for or
/xor
) to mask out or mask in certain bits from the operation when forcing bits to 0, 1, or their inverse.
The 80x86 CPUs support four instructions that apply these bitwise logical operations to their operands. The instructions are and
, or
, xor
, and not
. The and
, or
, and xor
instructions use the same syntax as the add
and sub
instructions:
and(source
,dest
); or(source
,dest
); xor(source
,dest
);
These operands have the same limitations as the add
operands. Specifically, the source
operand has to be a constant, memory, or register operand, and the dest
operand must be a memory or register operand. Also, the operands must be the same size and they cannot both be memory operands. These instructions compute the obvious bitwise logical operation via the following equation:
dest
=dest operator source
The 80x86 logical not
instruction, because it has only a single operand, uses a slightly different syntax. This instruction takes the following form:
not( dest
);
This instruction computes the following result:
dest
= not(dest
)
The dest
operand must be a register or memory operand. This instruction inverts all the bits in the specified destination operand.
The program in Example 2-5 inputs two hexadecimal values from the user and calculates their logical and
, or
, xor
, and not
:
Example 2-5. and
, or
, xor
, and not
example
program LogicalOp; #include( "stdlib.hhf" ) begin LogicalOp; stdout.put( "Input left operand: " ); stdin.get( eax ); stdout.put( "Input right operand: " ); stdin.get( ebx ); mov( eax, ecx ); and( ebx, ecx ); stdout.put( "$", eax, " and $", ebx, " = $", ecx, nl ); mov( eax, ecx ); or( ebx, ecx ); stdout.put( "$", eax, " or $", ebx, " = $", ecx, nl ); mov( eax, ecx ); xor( ebx, ecx ); stdout.put( "$", eax, " xor $", ebx, " = $", ecx, nl ); mov( eax, ecx ); not( ecx ); stdout.put( "not $", eax, " = $", ecx, nl ); mov( ebx, ecx ); not( ecx ); stdout.put( "not $", ebx, " = $", ecx, nl ); end LogicalOp;