| Program
in 8086 assembly language to multiply
two 4 digit packed BCD nos.and
print the result as ascii(decimal)
value
contributed by Kailas Jagtap
.model small
.data
;Messages for user
s0 db 0dh,0ah,'MULTIPLICATION
OF TWO 4 DIGIT PACKED BCD NUMBERS
..By Kailas Jagtap$'
s1 db 0dh,0ah,'Enter first 4-digit
Decimal no (XXXX): $'
s2 db 0dh,0ah,'Enter second 4-digit
Decimal no (XXXX): $'
s3 db 0dh,0ah,'Above nos. first
converted to packed BCD form $'
s33 db 'and then to hex. form',0dh,0ah,0ah,'Packed
BCD output : $'
s333 db 0dh,0ah,0ah,'Hexadecimal
output : $'
s4 db 0dh,0ah,'Multiplication
of above two nos.(printed in Hex)
: $'
s44 db 0dh,0ah,'Multiplication
of above two nos.(printed in ASCII)
: $'
s5 db 0dh,0ah,' $'
s6 db 0dh,0ah,'*****************
ERROR!!! - INVALID INPUT ******************
$'
n1 db 4 dup(0) ;storage for 1st
no. from Keyboard (4 digits in
unpacked ¼D form)
n2 db 4 dup(0) ;storage for 2nd
no. from Keyboard (4 digits in
unpacked ¼D form)
pnum1 dw 1 dup(0) ;2-byte storage
for 1st no.(4 digits in packed
BCD form)
pnum2 dw 1 dup(0) ;2-byte storage
for 2nd no.(4 digits in packed
BCD form)
unum1 db 4 dup(0) ;4-byte storage
for 1st no.(4 digits in unpacked
¼D form)
unum2 db 4 dup(0) ;4-byte storage
for 2nd no.(4 digits in unpacked
BCD form)
;(unum1 & unum2(8 bytes)also
used for storing upk remainders
;as they will become free after
initial use)
hnum1 dw 1 dup(0) ;2-byte storage
for 1st hex no.
hnum2 dw 1 dup(0) ;2-byte storage
for 2nd hex no.
result dw 2 dup(0) ;4-byte storage
for multiplication result(packed
Hex)
uresult db 8 dup(0) ;8-byte storage
for multiplication result(unpacked
Hex)
quot db 8 dup(0) ;8-byte storage
for quotient(unpacked Hex)
dividend dw 1 dup(0) ;2-byte
storage for addr.of varying dividend
quotient dw 1 dup(0) ;2-byte
storage for addr.of varying quotient
rmcnt db 1 dup(0) ;remainder
counter
err_flg db 1 dup(0) ;error flag
for indicating invalid i/p
.code
start:
mov ax,@data ;initialise ds with
data seg. addr.
mov ds,ax
call disp_nl ;move cursor to
new line
mov ah,9 ;display initial msg.
lea dx,s0
int 21h
call disp_nl ;move cursor to
new line
call disp_nl ;move cursor to
new line
mov err_flg,0 ;clear error flag
mov ah,9 ;display enter 1st no.
msg.
lea dx,s1
int 21h
lea bx,n1 ;get 1st 4-dig.no.
and store in
call input ;unpacked BCD form
cmp err_flg,0 ;is input invalid?
jz noerr ;if valid(z) go to get
2nd no.
;otherwise comeout flashing err
msg
iperr: call disp_nl ;go to new
line
call disp_nl ;go to new line
mov ah,9 ;display err msg.
lea dx,s6
int 21h
mov ah,4ch ;hand over control
to DOS
int 21h
noerr: mov ah,9 ;display enter
2nd no. msg.
lea dx,s2
int 21h
lea bx,n2 ;get 2nd 4-dig.no.and
store in
call input ;unpacked BCD form
cmp err_flg,0 ;is input invalid?
jnz iperr ;if yes(nz) comeout
flashing err msg
;convert nos. n1 & n2 from
unpacked to packed BCD
lea bx,n1 ;get 1st no.
call u_to_p ;convert
mov pnum1,ax ;store converted
no.
lea bx,n2 ;get 2nd no.
call u_to_p ;convert
mov pnum2,ax ;store converted
no.
;convert first no. from packed
BCD to unpacked
lea si,pnum1 ;get addr. of 1st
packed no.
lea di,unum1 ;get addr. for storing
no. converted to unpacked form
call p_to_u ;convert from packed
to unpacked
lea bx,unum1 ;get start addr.
of 1st unpacked no.
call u_to_h ;convert 4 bytes
of unpacked no. to equivalent
2 byte hex
mov hnum1,dx ;store converted
hex no.
;convert second no. from packed
BCD to Hex
lea si,pnum2 ;get addr. of 2nd
packed no.
lea di,unum2 ;get addr. for storing
no. converted to unpacked form
call p_to_u ;convert from packed
to unpacked
lea bx,unum2 ;get start addr.
of 2nd unpacked no.
call u_to_h ;convert 4 bytes
of unpacked no. to equivalent
2 byte hex
mov hnum2,dx ;store converted
hex no.
;display pk BCD and hex nos.
call disp_nl ;move cursor to
new line
mov ah,9 ;display pk BCD output
msg.
lea dx,s3
int 21h
lea dx,s33
int 21h
;display packed BCD nos.(pnum1
& pnum2)
lea si,pnum1 ;get addr.of 1st
pk BCD no.(no.in 2 byte word)
call display ;display 1st pk
BCD no.
call disp_nl ;move cursor to
new line
lea si,pnum2 ;get addr.of 2nd
pk BCD no.(no.in 2 byte word)
call display ;display 2nd pk
BCD no.
mov ah,9 ;display hex output
msg.
lea dx,s333
int 21h
;display hex nos.(hnum1 &
hnum2)
lea si,hnum1 ;get addr.of 1st
hex no.
call display ;display 1st hex
no.
call disp_nl ;move cursor to
new line
lea si,hnum2 ;get addr.of 2nd
hex no.
call display ;display 2nd hex
no.
;multiply nos.(in hex form) i.e.
hnum1 X hnum2
mov ax,hnum1 ;get 1st no.
mov cx,hnum2 ;get 2nd no.
mul cx ;word to word mul.- result
in dx(msb) & ax(lsb)
mov result,dx ;store msb part
first
mov result+2,ax ;store lsb part
;display mul.result in hex form
call disp_nl ;move cursor to
new line
call disp_nl ;move cursor to
new line
mov ah,9 ;display mul.in hex
msg.
lea dx,s4
int 21h
lea si,result ;get addr.of result
(msb part)
call display ;display msb part
lea si,result+2 ;get addr.of
result (lsb part)
call display ;display lsb part
;------------------
;convert result from packed(4
byte Hex) to unpacked(8 byte Hex)
lea si,result ;get addr. of result(msb)
lea di,uresult ;get addr. for
storing no. converted to unpacked
form
call p_to_u ;convert from packed
to unpacked
lea si,result+2 ;get addr. of
result(lsb)
lea di,uresult+4;get addr. for
storing no. converted to unpacked
form
call p_to_u ;convert from packed
to unpacked
;display msg 'mul.result in ascii'
call disp_nl ;move cursor to
new line
call disp_nl ;move cursor to
new line
mov ah,9 ;display mul.in hex
msg.
lea dx,s44
int 21h
;divide result(dividend) by 10
successively until quotient is
<10
;collect remainders in each /10
and display them in reverse order
;which will be mul.result displayed
in decimal
lea si,uresult ;get start add.
of unpacked result
lea di,quot ;get start add. of
unpacked quotient
;every quot has one digit less
than dividend
nxt_div: mov [dividend],si ;save
current pointers(addr.)
mov [quotient],di
mov dh,0ah ;get divisor 10
mov ah,[si] ;first get two msb
digits(upk) for division(1st now
& next in loop)
mov ch,8-1 ;counter(0-7) for
dividing 8 upk digits
mov cl,4 ;counter for nibble
shifting
div_agn: inc si ;point to next
dig of current dividend
inc di ;point to next dig of
current quotient
mov al,[si] ;get next dig and
combine it with earlier
shl ah,cl ;remainder to carry
out next division
or al,ah ;make 1 byte packed
binary from 2 upk bytes
mov ah,0 ;(e.g. 02 0E converted
to 00 2E bin )
div dh ;ax/dh, ah=rem (00 to
09 always) al=quo(00 to 0f always)
mov [di],al ;store current quot.
dig in quotient
dec ch ;all 8 dig of dividend
are divided?
jnz div_agn ;if not,get next
dig for division
;otherwise, save current remainder(ah)
on stack
mov al,0 ;only ah can not be
pushed on stack
push ax ;save remainder on stack(which
is upk BCD digit)
inc rmcnt ;incr.rem counter by
1
;check whether after successive
div. quot. is reduced to <10
;i.e. first 7 bytes are 0 and
last byte is <10
mov cl,0 ;counter for counting
zeros in quotient
mov di,[quotient];get quotient
start addr.
chk_z: mov ah,[di] ;get next
dig from quotient
cmp ah,0 ;is dig 0 ?
jnz div_cont ;if not, continue
dividing the quotient again
inc di ;otherwise point to next
dig for checking
inc cl ;update zero counter
cmp cl,7 ;is it crossing 7 ?
jl chk_z ;if not(0-6), get next
dig from quotient for checking
mov ah,[di] ;otherwise get last(8th)dig
cmp ah,9 ;is it <10 ?
jle disp_bcd ;if yes(0-9), go
to display all remainders
;otherwise, continue dividing
the quotient again
div_cont: mov si,[dividend];get
start addr.of dividend
mov cl,8 ;counter for filling
zeros
mov al,0 ;load zero
fz: mov [si],al ;fill a byte
of dividend with 0
inc si ;point to next location
loop fz ;loop until all 8 bytes
filled with 0
mov si,[quotient] ;now si will
point to old quot.(treating it
as new dividend)
mov di,[dividend] ;and di will
point to old dividend (which will
store new quot.)
jmp nxt_div ;go to divide quotient
again
;
disp_bcd: call disp_ln ;ah=last
rem, display it first
dbcd: pop ax ;ah=next rem in
reverse order
call disp_ln ;display all rem
one by one
dec rmcnt ;all rem over?
jnz dbcd ;if not,get next one
mov ah,4ch ;otherwise, hand over
control to DOS
int 21h
;-----------------------------------------------------------------
;get 4-dig.no.from Keyboard and
store in 4 byte buffer(in unpacked
BCD form)
input PROC NEAR
mov cl,4 ;counter for 4 bcd dig
i/p
again:
mov ah,1 ;dos int call for geting
keyboard i/p
int 21h ;input is in al
cmp al,30h ;check for validity(below
0?)
jb inval ;if <30h, set err
flg
cmp al,39h ;check for validity(beyond
9?)
jg inval ;if >39h, set err
flg
sub al,30h ;make from ascii to
bin
mov [bx],al ;store it in buffer
inc bx ;point to next location
in buffer
dec cl ;all 4 i/p are over?
jnz again ;if not(nz), loop for
next input
ret ;otherwise return, as 4 i/ps
collected
inval: mov err_flg,0ffh ;set
err flag
ret ;return immediately breaking
i/p loop
input ENDP
;convert 4-digit BCD no. from
unpacked(4 bytes) to packed(2
bytes) form
u_to_p PROC NEAR
mov ah,[bx] ;get 1st upk no.
mov cl,4 ;counter for nibble
shifting
shl ah,cl ;shift right nibble
to left e.g.change 03 to 30
inc bx ;point to next no.
add ah,[bx] ;add next no. to
1st shifted no.
inc bx ;point to next no.
mov al,[bx] ;get next upk no.
shl al,cl ;shift right nibble
to left
inc bx ;point to next no.
add al,[bx] ;add next no. to
no.shifted earlier
ret ;return with ax=packed no.
u_to_p ENDP
;Convert 4-digit BCD no. from
packed(2 bytes) to unpacked(4
bytes)form
p_to_u PROC NEAR
mov ax,[si] ;get packed no.
call conv_pu ;first convert no.in
ah to unpacked & store
mov ah,al ;then get no.in al
for conversion
inc di ;point to next storage
location
call conv_pu ;convert no.in al
to unpacked & store
ret
p_to_u ENDP
conv_pu PROC NEAR
push ax ;save no.in ah for future
use
and ah,0f0h ;mask lower nibble
to extract upper nibble
mov cl,4 ;load shift count
shr ah,cl ;shift right 4 times
to shift upper nibble to right
mov [di],ah ;store unpacked no.
e.g. in packed 24h, 02 extracted
inc di ;point to next storage
location
pop ax ;restore old no. in ah
and ah,0fh ;mask upper nibble
to extract lower nibble
mov [di],ah ;store unpacked no.
e.g. in packed 24h, 04 extracted
ret
conv_pu ENDP
;display 4-dig. hex no. available
in a word(2 bytes)
;e.g bx=1234h i.e. bh=12h bl=34h
display PROC NEAR
mov bx,[si] ;get hex no. in bx(display
bh & bl one by one)
call dis_byt ;first display upper
byte(in bh)
mov bh,bl ;move byte in bl to
bh for display
call dis_byt ;display lower byte
ret
display ENDP
dis_byt PROC NEAR
mov ah,bh ;get byte in ah
and ah,0f0h ;mask lower nibble
call conv_un ;conv. upper nibble
of byte to ascii(result in ah)
call disp ;display upper nibble
of byte in ah
mov ah,bh ;get byte in ah again
and ah,0fh ;mask upper nibble
disp_ln: call conv_ln ;conv.
lower nibble of byte to ascii(result
in ah)
call disp ;display lower nibble
of byte in ah
ret
dis_byt ENDP
;convert upper nibble in byte
from BCD to ascii
;input ah=BCD no. ouput ah=ascii
code
conv_un PROC NEAR
shr ah,1 ;shift right 4 times
e.g. 10h to 01h, add 30h
shr ah,1 ;to it to make it 31h(ascii
for 1)
shr ah,1
shr ah,1
conv_ln:add ah,30h ;convert to
ascii code(also used for conv.of
lower nibble)
cmp ah,39h ;if no.>9 i.e.A
to F add 7 to make
jle conv ;it corresponding displayable
ascii code
add ah,7 ;e.g if 0C, 0C+7=13h,
13h+30h=43h(ascii for C)
conv: ret
conv_un ENDP
disp PROC NEAR
mov dl,ah ;int 21 needs byte(to
be displayed) in dl
disp1: mov ah,2 ;select function
no. of int 21
int 21h ;dos call for displaying
byte in dl
ret
disp ENDP
disp_nl PROC NEAR ;move cursor
to new line on screen
mov ah,9
lea dx,s5
int 21h ;display cr & lf
disp_nl ENDP
;convert 4-digit BCD no. from
unpacked to equivalent hex form
;e.g. upk no. in 4 bytes - 01
02 03 04, hex no. in 2 bytes -
04D2 h
;bx=start addr.of upk BCD no.
dx=actual hex no.
u_to_h PROC NEAR ;e.g 01 02 03
04 = 1x1000 + 2x100 + 3x10 + 4
mov ax,1000 ;First do calculation
for 1000's place e.g. 1x1000
mov ch,0 ;word to word mul. so
ch should be 0
mov cl,[bx] ;get upk BCD byte
for mul.
mul cx ;ax X cx Result in dx(MSB)
& ax(LSB)
;we can not use just 'mul cl'
here, as 1000 D = 03E8 H
;which will require word mul.
& not byte mul.
;after mul.dx=0 always, as max.is
1000x9=9000D=2328H
;which definately fit in 2 bytes(in
ax), so we can
;spoil dx and use for other work
mov dx,ax ;save mul.result of
1000's place
push dx ;save dx as used in further
mul.
mov ax,100 ;do calculation for
100's place e.g. 2x100
inc bx ;point to next byte of
upk BCD
mov cl,[bx] ;again word mul.required
as max is 9x100=900D=0384H
mul cx ;word to word mul.
pop dx ;get previously saved
calculation of 1000's place
add dx,ax ;add calculations of
100's place and 1000's place
;dx not necessary to save as
no word to word mul. further
mov ax,10 ;do calculation for
10's place e.g. 3x10
inc bx ;point to next byte of
upk BCD
mov cl,[bx] ;byte mul.ok(word
mul.not required as max.is 9x10=90D
mul cl ;which can fit in one
byte only
add dx,ax ;add result to above
addition of two places
inc bx ;point to next byte of
upk BCD
mov cl,[bx] ;get no.at one's
place
mov ch,0 ;word to word addition
so ch should be 0
add dx,cx ;add last byte to above
addition of three places(e.g add
4)
ret
u_to_h ENDP
end start
end
|