.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 |