# Category : Assembly Language Source Code

Archive : ASMTUT2.ZIP

Filename : CHAP6.DOC

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