This section explains some of the basics of doing math in Assembly language. There are several hardware and software tricks that can be used to perform math calculations as quickly as possible. This section discusses bit level multiplication with shifts and adds.
There are two basic integer add instructions: the standard ADD and the ADC (add with carry). Normally, just the standard add is used for binary integers, but with some code, the add with carry is necessary. Both the ADD and the ADC instructions will alter the contents of the carry flag to indicate an addition overflow out of the high bit position, but the ADC will check the contents of the carry flag at the instruction start and add an extra 1 to the two operands being added if carry condition is true.
Example of a 32 bit binary integer addition:
;add lower half of 32 bit data to AX add ax,datalow ;add upper half of 32 bit data to DX adc dx,datahigh ;branch if carry overflow jc carryover
There are two basic subtract instructions: the standard SUB and the SBB (subtract with borrow). Normally, for small numbers, the SUB is used for standard binary integer subtraction, but in routines that want to handle subtractions of larger numbers, the subtract with borrow is used. The borrow condition is maintained in the carry flag.
Example of a 32 bit subtraction:
;subtract lower half of 32 bit data from AX sub ax,datalow ;subtract upper half of 32 bit data from DX with borrow sbb dx,datahigh
There are two basic multiply instructions: the integer unsigned multiply (MUL) and the integer signed multiply (IMUL). The multiply can be either 8 bit or 16 bit format. For the 8 bit multiply, AL has to hold one data variable. The other data variable can come from a register or memory. The 16 bit result will be placed into AX. For the 16 bit multiply, AX has to hold one data variable. The other can come from a register or memory. The 32 bit result will be placed into DX:AX with DX holding the most significant data and AX holding the least significant data.
;multiply BX * AX = DX:AX MUL BX ;multiply data to accumulator IMUL data_var
Note that if data_var is a byte, the compiler will generate code for an eight bit by eight bit multiply instruction; if data is a word, then it will generate a 16 bit by 16 bit multiply instruction.
There are two basic divide instructions: the standard integer unsigned divide (DIV) and the integer signed divide (IDIV). You can divide a 16 bit number by an 8 bit number or you can divide a 32 bit number by a 16 bit number. For the small divide, the AX register has to hold the 16 bit number. The 8 bit number that is used for the small divide can come from either a register or memory. The result will be in AL with the modulo from the divide in AH. For the larger divide, DX:AX holds the 32 bit data with AX holding the least significant bits. The 16 bit data used for the divide can come from a register or memory. The main result data will be put into AX and DX will hold the modulo data resulting from the divide.
;divide DX:AX by BX = AX modulo DX DIV bx ;divide using signed integer AX by databyte = AL modulo AH IDIV databyte
MULTIPLYING BY LEFT BIT SHIFTING
Shifting a number to the left by one bit position has the effect of multiplying the number by two. Shifting a number to the left by two bit positions has the effect of multiplying the number by four. Shifting a number to the left by three bit positions has the effect of multiplying the number by eight. Each shift doubles the current value of a binary integer variable. In base ten math, shifting a number to the left and putting a zero in the rightmost digit position multiplies the number by ten.
A more elaborate multiplication can be seen with this example. Shift a number left one bit to multiply by two and save the number. Now shift the number left two more times to multiply by eight, then add the saved value of the two multiply and you have the effect of multiplying by ten. Now take this result and shift to the left one more time to double the data value and you have multiplied the starting number by 20.
mov ax,01 ;load 1 into ax shl ax,1 ;ax now 10B or 2 mov dx,ax ;save value shl ax,1 ;ax now 100B or 4 shl ax,1 ;ax now 1000B or 8 add ax,dx ;ax now 1010B or 10 shl ax,1 ;ax now 10100B or 20
INCREMENTING AND DECREMENTING
The INC instruction is a quick way to add one to memory or a register. This is used in many routines that count. The DEC instruction is a quick way to subtract one from memory or a register.
;add to accumulator inc ax ;subtract 1 from memory location data dec data
NOTES ABOUT USING THE 80X87 MATH COPROCESSOR
The 80X87 coprocessor is a very powerful math processing unit. It is an optional math processor that is available for most PC systems. The 80X87 has eight internal data registers for processing math functions that are separate from the standard 80X86 registers. Each of the eight 80X87 data registers is 80 bits wide. The 80X87 has several input and output formats: two byte word integer, four byte short integer, eight byte long integer, ten byte packed decimal, four byte short real, eight byte long real, and a ten byte temporary real. All data inside of the 80X87 is handled in the same 80 bit real number format for math functions. The 80X87 system uses a stack pointer system to index the internal data registers. The 80X87 data registers are addressed as relative to their current position in the stack. The stack works in a circular motion from indexing data register 0 to data register 7 and back to data register 0. The data at the top of the stack is referenced as ST(0). If you pop data off the stack, then ST(1) becomes ST(0). If you push data into the stack, then ST(0) becomes ST(1). The 80X87 is designed to run in parallel with the 80X86 CPU. An interesting note about 80X87 math is the division by zero which results in an answer of infinity except when zero is divided by zero which is indefinite.