# Category : Assembly Language Source Code

Archive : ASMTUT3.ZIP

Filename : CHAP16.DOC

170

CHAPTER 16 - LONG SIGNED MULTIPLICATION AND DIVISION

Now that you have some subroutines under your belt, it is time to

get back to multiple word arithmetic. This was put on the back

burner because we needed to negate long numbers and it is more

efficient to do that as a subroutine. First, let's negate a long

number and then put the parts together.

To negate a number you complement it, then add 1. It looks like

this:

NUMBER_LENGTH EQU 4

variable1 dq ?

mov si, offset variable1

mov cx, NUMBER_LENGTH

not_loop:

not WORD PTR [si]

add si, 2

loop not_loop

mov si, offset variable1

mov cx, NUMBER_LENGTH

stc ; set carry flag

add_loop:

adc WORD PTR [si], 0

inc si

inc si

loop add_loop

This is straightforward. First negate, then add 1. The first add

will add 1 because the carry flag is set. If there is a carry

out, it will be taken care of in the next word with ADC (we add

nothing but the carry). We can make this more compact and

efficient with:

mov si, offset variable1

mov cx, NUMBER_LENGTH

stc ; set carry flag

negate_loop:

not WORD PTR [si]

adc WORD PTR [si], 0

inc si

inc si

loop negate_loop

Neither NOT nor INC effect CF, the carry flag, so the correct CF

value will be propagated through the whole long number.

When we do negation during our multiplication, the multiplicand

will be a 4 word negation while the result will be a 5 word

______________________

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

Chapter 16 - Multiple Word Arithmetic III 171

_________________________________________

negation, so we will pass the length as a parameter. The call in

C would look like this:

negate_it ( &number, length ) ; {1}

On entry, the stack will look like this:

length bp + 6

address bp + 4

old IP bp + 2

bp -> old BP bp + 0

Here is the entire subroutine:

; - - - - - START SUBROUTINES BELOW THIS LINE

negate_it proc near

NUMBER_LENGTH EQU [bp+6]

NUMBER_ADDRESS EQU [bp+4]

push bp

mov bp, sp

PUSHREGS cx, si

mov si, NUMBER_ADDRESS

mov cx, NUMBER_LENGTH

stc ; set carry flag

negate_loop:

not WORD PTR [si]

adc WORD PTR [si], 0

inc si

inc si

loop negate_loop

POPREGS cx, si

pop bp

ret ; calling routine adjusts stack

negate_it endp

; - - - - - END SUBROUTINES ABOVE THIS LINE

Well, so far we have the negation routine and the unsigned

multiplication and division routines. What else is necessary?

Only the main program, and here it is for multiplication:

; - - - - - START DATA BELOW THIS LINE

multiplicand dq ?

multiplier dw ?

result dt ?

result_sign_flag db ?

; - - - - - END DATA ABOVE THIS LINE

____________________

1. For you non-C people, the '&' stands for the address.

The PC Assembler Tutor 172

______________________

; - - - - - START CODE BELOW THIS LINE

outer_loop:

mov result_sign_flag, 0 ; assume positive

mov ax, offset multiplicand

call get_signed_8byte

test WORD PTR multiplicand + 6, 8000h ; is it negative ?

jz get_next_number

mov ax,4 ; negate 4 word number

push ax

mov ax, offset multiplicand

push ax

call negate_it

add sp, 4 ; clear 2 pushes off stack

not result_sign_flag ; reverse sign of result

get_next_number:

call get_signed ; get signed multiplier

mov multiplier, ax

test ax, 8000h ; is it negative

jz do_the_multiplication

neg multiplier ; negate

not result_sign_flag ; reverse sign of result

do_the_multiplication:

mov ax, offset result

push ax

mov ax, multiplier ; the number, not the address

push ax

mov ax, offset multiplicand

push ax

call multiply_it

add sp, 6 ; clear 3 pushes off stack

; is the result negative?

test result_sign_flag, 0FFh ; 1111 1111 mask

jz print_it

mov ax, 5 ; 5 word result

push ax

mov ax, offset result

push ax

call negate_it

add sp, 4 ; clear 2 pushes off stack

print_it:

mov ax, WORD PTR result + 8 ; top two bytes

call print_hex

mov ax, offset result ; the rest of result

call print_signed_8byte

jmp outer_loop

; - - - - - END CODE ABOVE THIS LINE

The driver routine gets an 8 byte signed number. If the number is

Chapter 16 - Multiple Word Arithmetic III 173

_________________________________________

negative it negates the number (to make it positive) and switches

the sign of the result_sign_flag. The sign flag will either be

00h for positive or FFh for negative. It then gets a two byte

signed number. If it is negative, the routine negates it and

switches the sign flag. At this point both numbers are positive,

so it calls the unsigned multiplication routine. At the end, it

checks the result_sign_flag to see if the result should be

positive or negative. If it should be negative, the routine calls

negate_it one more time. Finally, the routine prints the number.

The hex portion will be 0000 for positive or FFFF for negative

unless the value is larger than an 8 byte signed number can hold,

at which point the value of the 8 byte signed number will be

incorrect.

Here's the unsigned multiplication routine which has been turned

into a subroutine:

; - - - - -

multiply_it proc near

RESULT_ADDRESS EQU [bp+8]

MULTIPLIER_VALUE EQU [bp+6]

MULTIPLICAND_ADDRESS EQU [bp+4]

push bp

mov bp, sp

PUSHREGS ax, bx, cx, dx, si, di

mov si, MULTIPLICAND_ADDRESS ; load pointers

mov bx, RESULT_ADDRESS

mov cx, 4 ; number of words

sub di,di ; clear di

mult_loop:

mov ax, [si] ; multiplicand to ax

mul WORD PTR MULTIPLIER_VALUE

add ax, di ; add high word from last multiplication

jnc store_result

inc dx

store_result:

mov [bx], ax ; store 1 word of result.

mov di, dx ; save high word for next multiplication

add si, 2 ; increment pointers

add bx, 2

loop mult_loop

mov [bx], di ; move last word of result

POPREGS ax, bx, cx, dx, si, di

pop bp

ret ; calling routine adjusts stack

multiply_it endp

; - - - - - - - - - - -

Draw a picture of the stack to verify that the EQU values are

The PC Assembler Tutor 174

______________________

correct. The multiplication and the negation subroutines go in

the subroutine section of SUBTEMP1.ASM. The driver routine is the

main routine. If you don't remember how this multiplication

routine works, go back to the chapter on unsigned multiple word

multiplication since the code is the same.

DIVISION

Division is the same situation. We need a driver routine, but the

division itself will be the unsigned division. In division, the

remainder is the same sign as the dividend, and the sign of the

quotient is (dividend_sign XOR divisor_sign). If both signs are

the same, the quotient is positive; if the signs are different

the quotient is negative. Here's the driver routine:

; - - - - - START DATA BELOW THIS LINE

dividend dq ?

divisor dw ?

quotient dq ?

remainder dw ?

quotient_sign_flag db ?

remainder_sign_flag db ?

; - - - - - END DATA ABOVE THIS LINE

; - - - - - START CODE BELOW THIS LINE

outer_loop:

mov quotient_sign_flag, 0 ; assume positive

mov remainder_sign_flag, 0

mov ax, offset dividend

call get_signed_8byte

test WORD PTR (dividend + 6), 8000h ; is it negative?

jz get_next_number

mov ax,4 ; negate 4 word number

push ax

mov ax, offset dividend

push ax

call negate_it

add sp, 4 ; adjust stack

not quotient_sign_flag ; switch sign of quotient

mov remainder_sign_flag, 0FFh ; remainder is negative

get_next_number:

call get_signed

mov divisor, ax

test ax, 8000h ; is it negative

jz do_the_division

neg divisor

not quotient_sign_flag ; switch sign of quotient

do_the_division:

mov ax, offset remainder

push ax

Chapter 16 - Multiple Word Arithmetic III 175

_________________________________________

mov ax, offset quotient

push ax

mov ax, divisor ; the number, not the address

push ax

mov ax, offset dividend

push ax

call divide_it

add sp, 8 ; clear 4 pushes off stack

; are the remainder and quotient negative?

test remainder_sign_flag, 0FFh

jz test_the_quotient

neg remainder

test_the_quotient:

test quotient_sign_flag, 0FFh ; 1111 1111 mask

jz print_it

mov ax, 4 ; 4 word result

push ax

mov ax, offset quotient

push ax

call negate_it

add sp, 4 ; clear 2 pushes off stack

print_it:

mov ax, offset quotient

call print_signed_8byte

mov ax, remainder

call print_signed

jmp outer_loop

; - - - - - END CODE ABOVE THIS LINE

We get the dividend and check the sign. If it is negative, we (1)

negate the number, (2) switch the sign of the quotient, and (3)

set the remainder sign flag to negative. We get the divisor,

check for negative; if it is negative we negate it and switch the

sign of the quotient. We now have two unsigned numbers and do

unsigned division. After division, both the quotient and

remainder are adjusted for sign.

The division routine is the same as the unsigned routine before

except it is now a subroutine:

; - - - - - - - - ENTER SUBROUTINE BELOW THIS LINE

divide_it proc near

REMAINDER_ADDRESS EQU [bp+10]

QUOTIENT_ADDRESS EQU [bp+8]

DIVISOR_VALUE EQU [bp+6]

DIVIDEND_ADDRESS EQU [bp+4]

push bp

mov bp, sp

PUSHREGS ax, bx, cx, dx, si, di

The PC Assembler Tutor 176

______________________

mov si, DIVIDEND_ADDRESS

mov bx, QUOTIENT_ADDRESS

add si, 6 ; start at the top word

add bx, 6

mov di, WORD PTR DIVISOR_VALUE

mov cx, 4 ; number of words

sub dx, dx ; clear dx for first division

division_loop:

mov ax, [si] ; dividend word to ax

div di

mov [bx], ax ; word of result to quotient

sub si, 2 ; decrement the pointers

sub bx, 2

loop division_loop

mov bx, REMAINDER_ADDRESS ; store remainder

mov [bx], dx

POPREGS ax, bx, cx, dx, si, di

pop bp

ret ; calling routine adjusts the stack

divide_it endp

; - - - - - - - - ENTER SUBROUTINE ABOVE THIS LINE

Draw a picture of the stack to verify that the EQU statements are

correct for a NEAR routine. The division and negation subroutines

go in the SUBROUTINES section of SUBTEMP1.ASM. The driver is the

main program. If you don't remember how this division works, go

back to the division chapter and look it over. Try out a few

numbers to make sure that it is working the way it should.

DATA INTEGRITY

One thing that may have been annoying some of you is that when

the programs sent us numbers for multiplication and division we

sometimes negated them, effectively changing the data in memory,

but never changed them back when we were done. In an operational

subroutine, you would have to do it differently. The logic would

be:

NUMBER NEGATIVE? no everything's o.k.

yes

make copy

negate

reset pointer

If the number is positive we won't change it. If the number is

negative, we make a copy, negate the copy and use the copy for

the operation.

Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/