Occasionally you may need to do some computation on a pair of operands that are not the same size. For example, you may need to add a word and a double word together or subtract a byte value from a word value. The solution is simple: just extend the smaller operand to the size of the larger operand and then do the operation on two similarly sized operands. For signed operands, you would sign extend the smaller operand to the same size as the larger operand; for unsigned values, you zero extend the smaller operand. This works for any operation, although the following examples demonstrate this for the addition operation.
static var1: byte; var2: word; . . . // Unsigned addition: movzx( var1, ax ); add( var2, ax ); // Signed addition: movsx( var1, ax ); add( var2, ax );
In both cases, the byte variable was loaded into the AL register, extended to 16 bits, and then added to the word operand. This code works out really well if you can choose the order of the operations (for example, adding the 8-bit value to the 16-bit value). Sometimes, you cannot specify the order of the operations. Perhaps the 16-bit value is already in the AX register and you want to add an 8-bit value to it. For unsigned addition, you could use the following code:
mov( var2, ax ); // Load 16-bit value into ax. . // Do some other operations leaving . // a 16-bit quantity in ax. add( var1, al ); // Add in the 8-bit value. adc( 0, ah ); // Add carry into the H.O. word.
The first add
instruction in this example adds the byte at var1
to the L.O. byte of the value in the accumulator. The adc
instruction above adds the carry from the addition of the L.O. bytes into the H.O. byte of the accumulator. You must take care to ensure that this adc
instruction is present. If you leave it out, you may not get the correct result.
Adding an 8-bit signed operand to a 16-bit signed value is a little more difficult. Unfortunately, you cannot add an immediate value (as above) to the H.O. word of AX. This is because the H.O. extension byte can be either $00 or $FF. If a register is available, the best thing to do is the following:
mov( ax, bx ); // bx is the available register. movsx( var1, ax ); add( bx, ax );
If an extra register is not available, you might try the following code:
push( ax ); // Save word value. movsx( var1, ax ); // Sign extend 8-bit operand to 16 bits. add( [esp], ax ); // Add in previous word value. add( 2, esp ); // Pop junk from stack.
Another alternative is to store the 16-bit value in the accumulator into a memory location and then proceed as before:
mov( ax, temp ); movsx( var1, ax ); add( temp, ax );
All the examples above added a byte value to a word value. By zero or sign extending the smaller operand to the size of the larger operand, you can easily add any two different-size variables together.
As a last example, consider adding an 8-bit signed value to a quadword (64-bit) value:
static QVal:qword; BVal:int8; . . . movsx( BVal, eax ); cdq(); add( (type dword QVal), eax ); adc( (type dword QVal[4]), edx );