Category : Assembly Language Source Code
Archive   : ASMTUT2.ZIP
Filename : CHAP6.DOC

 
Output of file : CHAP6.DOC contained in archive : ASMTUT2.ZIP




41

CHAPTER 6 - MULTIPLICATION AND DIVISION



Unlike addition and subtraction, where the result can be in
either memory or in one of the registers, the multiplication and
division instructions have a rigid format.


MULTIPLICATION

You can multiply a one byte number by a one byte number and get a
two byte result, or you can multiply a one word number by a one
word number and get a two word result. The first number MUST be
in AL for the byte operation or in AX for the word operation. The
second number may be a register or a memory location (but not a
constant). The result is in AH:AL for the byte operation and
DX:AX for the word operation. Our possibilities are:

AL X (one byte register or memory) -> AH:AL
AX X (one word register or memory) -> DX:AX

Is there a difference between signed and unsigned numbers? Yes, a
very big difference. For the byte operation FFh = -1 signed but
FFh = 255 unsigned. -1 X -1 = 1 = 0001h. 255 X 255 = 65025 =
FE01h. These are two completely different answers. You need to
tell the 8086 whether you want signed multiplication or unsigned
multiplication. The 8086 does the rest. Let's look at both signed
and unsigned multiplication. We'll do byte multiplication for
unsigned numbers and word multiplication for signed numbers. The
instruction for unsigned multiplication is MUL. The instruction
for signed multiplication is IMUL. AX or AL is understood to be
the register, so it is not in the code. The instructions are:

variable1 db ?
variable2 dw ?

mul bx ; unsigned word from a register
mul variable1 ; unsigned byte from memory
imul ch ; signed byte from a register
imul variable2 ; signed word from memory

No AX or AL. It's understood. Here's our program:

template.asm
; + + + + + + + + + + + + + + + START DATA BELOW THIS LINE
answer1 dw ?
answer2 dw ?
; + + + + + + + + + + + + + + + END DATA ABOVE THIS LINE

; - - - - - START CODE BELOW THIS LINE

mov cx, 0 ; clear cx for visual effect

______________________

The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson




The PC Assembler Tutor 42
______________________


outer_loop:
; unsigned byte multiplication
mov ax_byte, 0A2h ; half regs, unsigned
mov bx_byte, 0A2h ; half regs, unsigned
lea ax, ax_byte
call set_reg_style

mov ax, 0 ; clear regs
mov bx, 0
mov dx, 0
call show_regs

call get_unsigned_byte ; get two unsigned bytes
call show_regs
push ax ; save the first number
call get_unsigned_byte
mov bl, al
pop ax ; restore the first number
call show_regs_and_wait
mul bl ; unsigned multiplication
call print_unsigned ; display the result (ax)
call show_regs_and_wait


; signed word multiplication
mov ax_byte, 01h ; full reg, signed
mov bx_byte, 01h
mov dx_byte, 01h
lea ax, ax_byte
call set_reg_style

mov ax, 0 ; clear regs
mov bx, 0
call show_regs

call get_signed ; get two numbers
call show_regs
push ax ; save the first number
call get_signed
mov bx, ax
pop ax ; restore the first number
call show_regs_and_wait
imul bx ; signed multiplication
push ax ; save result
mov answer1, ax ; display 4 byte result
mov answer2, dx
lea ax, answer1
call print_signed_4byte
pop ax ; restore result
call show_regs_and_wait

jmp outer_loop
; - - - - - END CODE ABOVE THIS LINE

If the answer for the unsigned byte multiplication is greater
than 255, it will be difficult to read the answer from the half




Chapter 6 - Multiplication and Division 43
_______________________________________

registers, so we print out the whole AX register.

If the answer for the signed word multiplication is greater than
+32767 or is less than -32768, the answer will be unreadable in
the DX:AX registers. We move the answer to memory, and then call
print_signed_4byte. As with set_reg_style, the data is too long
to be put in AX, so we pass the address of the first byte of
data with:

lea ax, answer1

and then call print_signed_4byte. Everything from PUSH AX to POP
AX is designed to do that.

Do MUL and IMUL set any flags? Yes. For byte multiplication, if
AL contains the total answer, the 8086 clears the OF and CF
flags. If part of the answer is in AH, then the 8086 sets both
the OF and CF flags. For word multiplication, if AX contains the
total answer, the 8086 clears the OF and CF flags. If part of the
answer is in DX, then the 8086 sets both the OF and CF flags.

What do we mean by the total answer? This is simple for unsigned
multiplication. If AH (or DX for word) is 0, then the total
answer is in AL (or AX for word). It is more complicated for
signed multiplication. Consider word multiplication. +30000 X +2
= +60000. But that's less than 65536, so it is completely
contained in AX, right? WRONG. The leftmost bit of AX contains
the sign. If the signed result is out of the range -32768 to
+32767, information about the absolute value of the number is
corrupting information about the sign of the number. AX will
have the wrong number and the wrong sign. Only by combining AX
with DX will you get the correct answer. Similarly for byte
multiplication with AL, if the result is not in the range -128 to
+127, The leftmost (sign) bit will be corrupted, and only by
looking at AH:AL will you be able to get the correct result.

If CF and OF are set, you need to look at both registers to
evaluate the number. You might want to do error handling, so once
again, you can have:

mul bx
jnc go_on
call error_handler
go_on:

using the same reverse logic as before (if nothing is wrong, skip
the error handler). We can also use:

mul bx
into

if there is an INTO error handler.


DIVISION

Division operates in the same way as multiplication. Word




The PC Assembler Tutor 44
______________________

division operates on the DX:AX pair and byte division operates on
the AH:AL pair. There are two instructions, DIV for unsigned
division and IDIV for signed division. After the division:

byte AL = quotient, AH = remainder
word AX = quotient, DX = remainder

Both DIV and IDIV operate on BOTH registers. For bytes, they
consider AH:AL a single number. This means that AH must be set
correctly before the division or you will get an incorrect
answer. For words, they consider DX:AX a single number. This
means that DX must be set correctly before the division, or the
result will be incorrect. Why did Intel include AH and DX in the
division? Wouldn't it have been easier to use just AH (or AX for
word division) and put the quotient and remainder in the same
place? These instructions are actually designed for dividing a
long number (4 or 8 bytes). How it works is pretty slick; you'll
find out about it later in the book.

How do you set AH and DX correctly? For unsigned numbers, that's
easy. Make them 0:

mov al, variable
mov ah, 0
div cl ; unsigned byte division

For signed division, set AH or DX to 0 (0000h) if it is a
positive number and set them to -1 (FFFFh) if the number is
negative. This is just standard sign extension that was covered
in the chapter on numbers. Fortunately for us, Intel has provided
instructions which do the sign extension for us. CBW (convert
byte to word) correctly extends the signed number in AL through
AH:AL. CWD (convert word to double) correctly extends the signed
number in AX through DX:AX. The code is

mov ax, variable5
cwd
idiv bx ; signed word division

Of course with these two instructions you can convert a byte to a
double word.

mov al, variable6
cbw
cwd
idiv bx ; signed word division

first converting to a word, then to a double word.

For the division program, we are going to use the multiplication
program and make some small changes. Make a copy of your
multiplication program:

>copy mult.asm div.asm

and then make the following changes:





Chapter 6 - Multiplication and Division 45
_______________________________________

MULTIPLICATION DIVISION

; unsigned byte ; unsigned byte
pop ax
pop ax mov ah, 0
call show_regs_and_wait call show_regs_and_wait
mul bl div bl


; signed word ; signed word
pop ax
pop ax cwd
call show_regs_and_wait call show_regs_and_wait
imul bx idiv bx

The calls to print_unsigned and print_signed_4byte are
irrelevant, so you may either delete them or ignore the output.
All we did was change the multiplication instruction to division
and prepare the upper register correctly (AH for byte, DX for
word). That's all.

Assemble, link, and run it. Try out both positive and negative
numbers and see what the remainder looks like. Also notice the
sign extension just before the division. Remember, for division,
the results are in the following places:

byte AL = quotient, AH = remainder
word AX = quotient, DX = remainder

Now divide by 0. Ka-pow! You should have exited the program and
gotten an error message. Unlike the other arithmetical errors

where you have the option of ignoring them or making an error
handler for them, the 8086 considers division by 0 a major no-no.
When the 8086 detects division by zero,{1} it interrupts the
program and goes to the zero-divide handler (which is external to
the program). Normally, this just exits the program since the
data is now worthless.






____________________

1 What it actually detects is that the quotient is too large to
fit in the lower register (AL for byte or AX for word). As long
as the upper register is correctly sign extended, the only time
this can happen is when you divide by 0. If the upper register is
NOT sign extended correctly, you can have zero divide errors all
over the place, even though you aren't dividing by 0. As an
example, if AH:AL contain 3275 and bl contains 10, then:

div bl

will give a quotient of 327 ( > 255) and will generate a zero
divide error.




The PC Assembler Tutor 46
______________________

SUMMARY

MUL and IMUL
MUL performs unsigned multiplication and IMUL performs
signed multiplication. For bytes, the multiplicand is in AL
and the result is in the AH:AL pair. For words, the
multiplicand is in AX and the result is in the DX:AX pair.
If the total result is contained in the lower register, CF
and OF are cleared (0). If part of the result is in the
upper register, CF and OF are set (1). The multiplier may be
either a register or a variable in memory.

variable1 db ?
variable2 dw ?

mul variable1 ; unsigned byte
mul cx ; unsigned word
imul bl ; signed byte
imul variable2 ; signed word


DIV and IDIV
DIV performs unsigned division. IDIV performs signed
division. For bytes, the dividend is the AH:AL pair. For
words, the dividend is the DX:AX pair. In byte division, AH
must be correctly prepared before the division. For word
division, DX must be correctly prepared before the division.
The divisor may be either a register or a variable in
memory.

variable1 db ?
variable2 dw ?

div variable1 ; unsigned byte
div cx ; unsigned word
idiv bl ; signed byte
idiv variable2 ; signed word

The quotient and remainder are as follows:

byte AL = quotient, AH = remainder
word AX = quotient, DX = remainder

No flags are affected. If the quotient is too large for the
lower register, or if you divide by zero, a zero divide
program interrupt occurs.

CORRECT SIGN EXTENSION
To prepare for division, you must correctly sign extend the
lower register into the upper register. For unsigned
division, zero the upper register (AH = 0 or DX = 0). For
signed division, use CBW and CWD. CBW (convert byte to word)
extends a signed number in AL through AH:AL. CWD (convert
word to double) extends a signed number in AX through DX:AX