pcs/sbigmath.asm

707 lines
22 KiB
NASM
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

; =====> SBIGMATH.ASM
;***************************************
;* TIPC Scheme '84 Runtime Support *
;* Bignum Math Utilities *
;* *
;* (C) Copyright 1984,1985 by Texas *
;* Instruments Incorporated. *
;* All rights reserved. *
;* *
;* Date Written: June 1984 *
;* Last Modification: 27 May 1986 *
;***************************************
include scheme.equ
DGROUP group data
data segment word public 'DATA'
assume DS:DGROUP
data ends
XGROUP GROUP PROGX
PROGX segment byte public 'PROGX'
assume CS:XGROUP,DS:DGROUP
; Convert a bignum to a flonum
; Calling sequence: big2flo(bigptr,floptr)
; Where: bigptr ---- pointer to bignum workspace
; floptr ---- pointer to flonum
b2fargs struc
dw ? ;Caller's BP
dd ? ;Return address
dw ? ;Another return address
big dw ? ;Pointer to bignum
flo dw ? ;Pointer to flonum
b2fargs ends
%big2flo proc far
push BP
mov BP,SP
cld ;Direction forward
mov SI,[BP].big ;Point DS:SI to working bignum
mov CX,[SI] ;Get size
cmp CX,3
ja all64 ;Jump if at least 64 bits
add SI,3 ;Point to bignum proper
xor BX,BX
xor DI,DI
lodsw ;Fetch least sig. word
mov DX,AX ;Store in DX
dec CX
jcxz smljust ;Jump if no more bignum words
lodsw ;Else get next least sig. word
mov DI,AX
dec CX
jcxz smljust ;Jump if no more bignum words
lodsw ;Get 3rd least sig. word
mov BX,AX
smljust: xor AX,AX ;Clear most sig. word
jmp short justify ;Left-justify the number
all64: shl CX,1 ;Point SI to 4th most sig. word
add SI,CX
sub SI,5
lodsw ;Load bignum into registers
mov DX,AX
lodsw
mov DI,AX
lodsw
mov BX,AX
lodsw
;JUSTIFY: At this stage, the 64 most significant bignum bits are in
; AX:BX:DI:DX respectively, AX most significant
justify: mov SI,[BP].big ;Fetch pointer to bignum again
mov CX,[SI] ;Get size (words)
cmp CX,40h
ja toobig ;Jump if bignum too big
cmp CX,4 ;Skip if not a small bignum
jae enough
mov CX,4 ;Otherwise, start with constant
enough: shl CX,1 ;Multiply by 16 (size in bits)
shl CX,1
shl CX,1
shl CX,1
justflp: dec CX ;Reduce exponent
shl DX,1 ;Shift bignum left
rcl DI,1
rcl BX,1
rcl AX,1
jnc justflp ;Until most significant 1 vanishes
add CX,3ffh ;Add flonum exponent constant
mov SI,DI ;Now use SI for num, DI for address
shftrt: shr CX,1 ;Shift CX:AX:BX:SI:DX as one
rcr AX,1
rcr BX,1
rcr SI,1
rcr DX,1
cmp CX,80h ;Until most sig. exponent bit is in 2nd
jae shftrt ; most sig. bit of CL
mov DI,[BP].big ;Get pointer to bignum again
test byte ptr[DI]+2,1 ;Negative?
jz posskip ;No, skip
or CL,80h ;Set sign bit
posskip: mov DI,[BP].flo ;Point ES:DI to flonum
push AX ;Save part of new flonum
mov AL,DH ;Write to flonum space
stosb
mov AX,SI
stosw
mov AX,BX
stosw
pop AX
stosw
mov AL,CL
stosb
xor AX,AX ;Return 0 if all well
pop BP ;Restore BP
ret
toobig: mov AX,1 ;Return 1 if conversion impossible
pop BP
ret
%big2flo endp
; Convert fixnum to bignum
; Calling sequence: fix2big(fixnum,bigptr)
; Where: fixnum ---- Integer of small absolute value
; bigptr ---- Pointer to bignum space
f2bargs struc
dw ? ;Caller's BP
dd ? ;Return address
dw ? ;Another return address
fix dw ? ;Fixnum
bigp dw ? ;Pointer to bignum
f2bargs ends
%fix2big proc far
push BP
mov BP,SP
mov DI,[BP].bigp ;Point ES:DI to bignum
mov AX,1 ;Fill size field
stosw
xor AL,AL ;Clear AL
mov BX,[BP].fix ;Fetch fixnum value
shl BX,1 ;Put sign bit in AL
rcl AL,1
stosb ;Fill sign field
sar BX,1 ;Restore fixnum
or AL,AL ;Negative fixnum?
jz posfx ;Skip if positive
neg BX ;Otherwise, find absolute value
posfx: mov [DI],BX ;Store magnitude of fixnum
pop BP ;Restore BP
ret
%fix2big endp
;;;; Decrement bignum
;;;; Calling sequence: sub1big(buf)
;;;; Where: buf ---- pointer to bignum
;;;unibig struc
;;; dw ? ;Return address
;;;bbuf dw ? ;Pointer to working bignum
;;;unibig ends
;;; public sub1big
;;;sub1big proc far
;;; mov BX,SP ;Get bignum pointer
;;; mov SI,SS:[BX].bbuf
;;; test byte ptr[SI]+2,1 ;Is bignum negative?
;;; jnz incbig ;If so, increase magnitude
;;; jmp short decbig ;Else decrease magnitude
;;;sub1big endp
;;;; Increment bignum
;;;; Calling sequence: add1big(buf)
;;; public add1big
;;;add1big proc far
;;; mov BX,SP ;Get bignum pointer
;;; mov SI,SS:[BX].bbuf
;;; test byte ptr[SI]+2,1 ;Is bignum negative?
;;; jnz decbig ;Yes, decrease magnitude
;;;;INCBIG increments magnitude of working bignum at DS:SI
;;;incbig: mov DI,SI ;Save bignum pointer
;;; mov CX,[SI] ;Get length (words)
;;; add SI,3 ;Point to bignum proper
;;;carrylp: inc word ptr[SI] ;Increment bignum
;;; jnz done ;If no carry, finished
;;; inc SI ;Else go to next word
;;; inc SI
;;; loop carrylp ;Loop while not end of bignum
;;; mov word ptr[SI],1 ;Else place final 1
;;; inc word ptr[DI] ;Lengthen bignum
;;;done: ret
;;;;DECBIG decrements magnitude of working bignum at DS:SI
;;;decbig: mov DI,SI ;Save pointer
;;; mov CX,[SI] ;Get length
;;; add SI,3 ;Point to bignum proper
;;; dec CX ;CX = (length - 1)
;;;borrowlp: lodsw ;Load current word
;;; sub AX,1 ;Decrement and store
;;; mov [SI-2],AX
;;; jnc done ;Jump if no borrow
;;; loop borrowlp ;Loop if not on last word
;;; dec word ptr[SI] ;Else decrement last word
;;; jnz done ;Jump if bignum not to be shortened
;;; dec word ptr[DI] ;Else shorten
;;; ret
;;;add1big endp
; set up big1's index for comparison, used with %magcomp
%pbig1 proc near
shl CX,1
dec SI
add SI,CX
shr CX,1
ret
%pbig1 endp
; set up big1's index for comparison, used with %magcomp
%pbig2 proc near
shl CX,1
dec DI
add DI,CX
shr CX,1
ret
%pbig2 endp
; Compare magnitudes of two bignums
; Calling sequence: data = magcomp(big1,big2)
; Where: big1,big2 -- pointers to bignum buffers
; data ------- a positive integer as follows:
; Bit 0 set iff |BIG1| < |BIG2|
; Bit 1 set iff |BIG1| > |BIG2|
; Bit 2 set iff BIG1 < BIG2
; Bit 3 set iff BIG1 > BIG2
; Bit 4 set iff BIG1,BIG2 have same sign
twobigs struc
dd ? ;Return address
dw ? ;Another return address
big1 dw ? ;First bignum
big2 dw ? ;Second bignum
twobigs ends
%magcomp proc far
xor AL,AL ;Clear AL
xor DX,DX ; clear DX
mov BX,SP ;Fetch bignum pointers
mov SI,[BX].big1
mov DI,[BX].big2
mov AH,[SI]+2 ;Get sign bits
mov DH,[DI]+2
xor DH,AH ;Put XOR of signs into carry
shr DH,1
jc sgnskp ;Jump if different signs
or AL,16 ;Else set proper bit in AL
sgnskp: rcl AH,1
mov CX,[SI] ;Get BIG1's length
mov DX,[DI] ; get BIG2's length
cld ;Direction forward
cmpsw ;Compare lengths
jb bigr2 ;Jump if BIG2 longer
ja bigr1 ;Jump if BIG1 longer
same_ln: call %pbig1 ;If same size, point SI,DI to last words
call %pbig2 ; (most significant)
std ;Direction backward
repe cmpsw ;Repeat until unequal
jb rbig2
ja rbig1
test AH,1 ;Signs same?
jz compend ;Yes, exit
difsign: test AH,2 ;Is BIG1 positive?
jnz grtr2 ;No, BIG2 is greater
jz grtr1 ;Else BIG1 is greater
bigr1: call %pbig1
cmp word ptr [SI],0 ; check high word,
jne rbig1 ; big1 is really bigger
mov SI,[BX].big1 ; restore SI
inc SI
inc SI
dec CX ; high order word is empty
cmp CX,DX ; compare length's again
je same_ln ; same length
jmp bigr1 ; repeat until unequal or same lengths
rbig1: or AL,2 ;Set the |BIG1|>|BIG2| bit
test AH,1 ;Signs same?
jnz difsign ;No, different signs
test AH,2 ;Both positive?
jnz grtr2 ;No, so BIG2 is greater
grtr1: or AL,8 ;Set the BIG1>BIG2 bit
cld ; Set direction forward (JCJ-12/6/84)
ret
bigr2: push CX
mov CX,DX ; swap CX and DX
pop DX
call %pbig2 ; Set up big2's pointers
cmp word ptr [DI],0 ; check high word
jne rbig2 ; big2 really is bigger
mov DI,[BX].big2 ; restore DI
inc DI
inc DI
dec CX ; high order word is empty
cmp DX,CX ; compare length's again
je same_ln ;
jmp bigr2 ; repeat until unequal or same lengths
rbig2: or AL,1 ;Set the |BIG1|<|BIG2| bit
test AH,1 ;Signs same?
jnz difsign ;No, different signs
test AH,2 ;Both positive?
jnz grtr1 ;No, BIG1 is greater
grtr2: or AL,4 ;Set the BIG1<BIG2 bit
compend: cld ; Set direction forward (JCJ-12/6/84)
ret
%magcomp endp
; Add magnitudes of bignums
; Calling sequence: bigadd(big1,big2)
; Where: big1 ---- bignum of lesser magnitude
; big2 ---- bignum of greater magnitude
; When done, BIG2 will hold the sum
%bigadd proc far
mov BX,SP ;Fetch bignum pointers
mov SI,[BX].big1
mov DI,[BX].big2
cld ;Direction forward
lodsw ;Get length of smaller bignum
mov CX,AX ;Save length
sub AX,[DI] ;Find and push difference in lengths
neg AX
push AX
inc SI ;Point SI,DI to bignums proper
add DI,3
clc ;Prepare to add
addlp: lodsw ;Fetch source addend
adc [DI],AX ;Add to destination addend
inc DI ;Point DI to next word
inc DI
loop addlp ;Do until smaller bignum exhausted
pop CX ;Fetch length difference (CF unchanged)
jnc doneadd ;Stop if no carry
mov SI,[BX].big2 ;Point SI to destination bignum
jcxz samlen ;Jump if bignums the same length
adclp: inc word ptr[DI] ;Otherwise, add carry
jnz doneadd ;Jump if no resultant carry
add DI,2 ;Point DI to next word
loop adclp ;Do until whole number is done or no carry
samlen: mov word ptr[DI],1 ;Store last carry
inc word ptr[SI] ;Note bignum's size increase
doneadd: ret
%bigadd endp
; Subtract magnitudes of bignums
; Calling sequence: bigsub(big1,big2)
; Where: big1 ---- bignum of lesser magnitude
; big2 ---- bignum of greater magnitude
; When done, BIG2 will hold the difference
; When done, BIG2 will hold the sum
%bigsub proc far
mov BX,SP ;Fetch pointers to bignums
mov SI,[BX].big1
mov DI,[BX].big2
cld ;Direction forward
lodsw ;Get length of smaller bignum
mov CX,AX
inc SI ;Point SI,DI to bignums proper
add DI,3
clc ;Prepare to subtract
sublp: lodsw ;Fetch subtrahend
sbb [DI],AX ;Subtract
inc DI ;Point DI to next word
inc DI
loop sublp ;Do until smaller bignum exhausted
jnc pack ;Jump if no borrow
borlp: mov AX,[DI] ;Fetch word
sub AX,1 ;Decrement and store
stosw
jc borlp ;Jump if further borrowing needed
pack: mov DI,[BX].big2 ;Fetch pointer to 2nd bignum
mov SI,DI ;Save pointer in SI
mov AX,[SI] ;Fetch bignum length
mov CX,AX ;Save (length-1) in CX
dec CX
shl AX,1 ;Point DI to last word of bignum
inc AX
add DI,AX
std ;Direction backward
xor AX,AX ;Find number of leading 0-words
repe scasw ; (not counting least sig. word)
jz smlskp ;Jump if only one non-0 word
inc CX ;Else, at least 2 non-0 words
smlskp: inc CX ;Form (length - # of leading 0-words)
mov [SI],CX ;Save in bignum size field
cld ;Clear the direction flag
ret
%bigsub endp
; Multiply two bignums
; Calling sequence: bigmul(big1,big2,big3)
; Where: big1,big2 -- factors
; big3 ------- destination of product
mulargs struc
carry dw ? ;Multiplication carry
dw ? ;Caller's BP
dd ? ;Return address
dw ? ;Another return address
mbig1 dw ? ;Factor of greater magnitude
mbig2 dw ? ;Factor of lesser magnitude
mbig3 dw ? ;Product destination
mulargs ends
; When done, BIG2 will hold the sum
%bigmul proc far
push BP ;Save BP
dec SP ;Create space for multiplication carry
dec SP
mov BP,SP ;Point BP to args
cld ;Direction forward
mov SI,[BP].mbig1 ;Fetch factor pointers
mov DI,[BP].mbig2
lodsw ;Fetch BIG1's length
mov CX,AX ;Put sum of lengths in CX
add CX,[DI]
scasw ;Which has greater magnitude?
jae xchgskp ;Jump if BIG1 is not smaller
xchg DI,SI
xchgskp: lodsb ;Fetch one factor's sign
xor AL,[DI] ;XOR with the other factor's sign
inc DI ;Point DI to bignum proper
mov BX,DI ;And store in BX
mov DI,[BP].mbig3 ;Store length into product
xchg AX,CX
stosw
push AX ;Save total length of product
xchg AX,CX ;Store sign byte into product
stosb
push DI ;Set product to 0 over whole length
xor AX,AX
rep stosw
pop DI
xchg DI,BX ;Restore BX and DI
mov CX,[DI]-3 ;Fetch length of BIG2
sub BX,SI ;Point [BX+SI-2] to product
dec BX
dec BX
mov [BP].mbig1,SI ;Store pointer to data of BIG1
mullp2: push CX ;Save counter of BIG2 words
;Add (BIG1*part of BIG2) to current product
mov word ptr[BP].carry,0 ;Clear carry in
mov SI,[BP].mbig1 ;Fetch bignum pointer
mov CX,[SI]-3 ;Get number of words in bignum
mullp: lodsw ;Get factor part from BIG1
mul word ptr[DI] ;Multiply by factor part from BIG2
add AX,[BP].carry ;Add carry in
adc DX,0
add [BX+SI],AX ;Add product part into BIG3
adc DX,0 ;Adjust and store carry
mov [BP].carry,DX
loop mullp ;Continue for all BIG1
mov [BX+SI+2],DX ;Store carry remaining
;
pop CX ;Restore BIG2 counter
inc DI ;Point DI to next word in BIG2
inc DI
inc BX ;Point BX to next word in BIG3
inc BX
loop mullp2 ;Continue for all BIG2
mov BX,[BP].mbig3 ;Fetch pointer to BIG3 (beginning)
pop SI ;Point SI to last word of product
shl SI,1
inc SI
add SI,BX
cmp word ptr[SI],0 ;Test last word for zero
jnz muldone ;Done if not zero
dec word ptr[BX] ;Decrement bignum length
muldone: inc SP ;Discard temporary carry variable
inc SP
pop BP ;Restore BP
ret
%bigmul endp
; Divide one bignum by another
; Calling sequence: bigdiv(dvdnd,dvsr,quot)
; Where: dvdnd ----- dividend
; dvsr ------ divisor
; quot ------ quotient
divargs struc
dw ? ;Caller's BP
dvsrsz dw ? ;Size of divisor (words)
bitcount dw ? ;Estimated bits in quotient
align dw ? ;Alignment of dividend to divisor
ldvsr dw ? ;Pointer to last word of divisor
dd ? ;Return address
dw ? ;Another return address
dvdnd dw ? ;Dividend
dvsr dw ? ;Divisor
quot dw ? ;Quotient
divargs ends
; When done, BIG2 will hold the sum
%bigdiv proc far
sub SP,8 ;Room for local variables
push BP
mov BP,SP
mov DI,[BP].quot ;Get pointers to arguments
mov SI,[BP].dvdnd
mov BX,[BP].dvsr
cld ;Direction forward
lodsw ;Get dividend length
mov CX,[BX] ;Fetch divisor length
cmp CX,1 ;Check divisor for 0
jne dvsrok
cmp word ptr[BX]+3,0 ;Check divisor data word
jnz dvsrok
mov AX,CX ;Put nonzero value in AX
pop BP
add SP,8 ;Restore stack
ret ;Exit
dvsrok: inc BX ;Point BX+1 to divisor sign
mov DX,CX ;Find & store pointer to last divisor word
shl DX,1
add DX,BX
mov [BP].ldvsr,DX
sub AX,CX ;Find dividend-divisor length difference
mov DX,AX ;Save in DX for now
inc AX ;Store maximum quotient length (words)
stosw
inc CX ;Save length of working divisor
mov [BP].dvsrsz,CX
dec AX ;Find and store quotient bit count
shl AX,1
shl AX,1
shl AX,1
shl AX,1
inc AX
mov [BP].bitcount,AX
lodsb ;Get dividend sign
xor AL,[BX]+1 ;Find and store quotient sign
stosb
mov [BP].dvdnd,SI ;Save pointer to dividend proper
mov [BP].quot,DI ;Save pointer to quotient proper
xor AX,AX ;Zero first two words of quotient
stosw
std
stosw
dec DX ;Account for extra divisor word
shl DX,1 ;Store divisor-dividend alignment
add DX,SI
mov [BP].align,DX
mov word ptr[BX],0 ;Put 0-word at start of divisor
mov [BP].dvsr,BX ;Save pointer to working divisor
bigdivlp: call divcmp ;Dividend less than aligned divisor?
jb divbit0 ;Yes, perform division
test word ptr[BX],8000h ;Can divisor be shifted left?
jnz divbit1 ;No, perform division
mov SI,[BP].dvsr ;Otherwise, shift entire divisor left
mov CX,[BP].dvsrsz
clc ;Start by shifting in 0
shllp: rcl word ptr[SI],1 ;Shift through divisor word
inc SI ;Point SI to next word
inc SI
loop shllp ;Do for entire divisor
inc [BP].bitcount ;Increase bit count
jmp bigdivlp ;See if divisor is big enough yet
divlp: call divcmp ;Dividend less than aligned divisor?
cld ; (Direction forward)
jb divbit0 ;Yes, rotate 0 into quotient
mov SI,[BP].align ;Otherwise, subtract divisor
mov DI,SI
mov BX,[BP].dvsr
sub BX,SI
dec BX
dec BX
mov CX,[BP].dvsrsz
clc ;No carry in
divsublp: lodsw
sbb AX,[SI+BX]
stosw
loop divsublp
divbit1: clc ;Clear carry (to rotate 1 in)
divbit0: cmc
mov SI,[BP].quot ;Fetch pointer to quotient
mov CX,[SI]-3 ;Fetch quotient length
quotlp: rcl word ptr[SI],1 ;Rotate bit in
inc SI
inc SI
loop quotlp ;Rotate bits through whole quotient
dec [BP].bitcount ;Last quotient bit rotated in?
jz divdone ;Yes, stop
mov SI,[BP].ldvsr ;Otherwise realign divisor (shr)
mov CX,[BP].dvsrsz
std ;Direction backward
cmp word ptr[SI],0 ;Time to shift divisor words?
jnz wshftskp ;No, don't bother
mov BX,SI ;Save last word pointer
mov DX,CX ;Save word count
mov DI,SI ;Destination = source+2
dec SI
dec SI
dec CX ;Shift significant divisor words
rep movsw
xor AX,AX ;Clear least significant word
stosw
mov SI,BX ;Restore last word pointer
mov CX,DX ;Restore count
sub [BP].align,2 ;Reset divisor alignment
wshftskp: clc ;Shift 0 in
shrlp: rcr word ptr[SI],1 ;Shift
dec SI
dec SI
loop shrlp ;Shift entire divisor
jmp divlp ;After all this, loop 'til division done
divdone: mov BX,[BP].dvdnd ;Fetch dividend pointer
mov DI,[BX]-3 ;Fetch former length of dividend
dec DI ;Put length-1 in CX
mov CX,DI
shl DI,1 ;Point DI to last dividend word
add DI,BX
std ;Direction backward
xor AX,AX ;Pack as in BIGSUB
repe scasw
jz smlskp2
inc CX
smlskp2: inc CX
mov [BX]-3,CX ;Save in bignum size field
mov BX,[BP].quot ;Fetch quotient pointer
mov DI,[BX]-3 ;Point BX+DI to last quotient word
dec DI
shl DI,1
cmp word ptr[BX+DI],0 ;If last word is 0, decrease length
jnz divex
dec word ptr[BX]-3
divex: pop BP ;Restore stack
add SP,8
xor AX,AX ;Return 0
cld ;Clear direction flag
ret
%bigdiv endp
;Compare working divisor to dividend
divcmp proc near
mov DI,[BP].ldvsr ;Get pointer to last divisor word
mov CX,[BP].dvsrsz ;Fetch number of compares to do
mov SI,[BP].align ;Get dividend pointer
mov AX,CX ;Save # of wrods for pointer adjust
cmp SI,[BP].dvdnd ;Dividend longer than divisor?
jae adjskp ;Yes, jump
dec CX ;Don't compare first divisor word
adjskp: dec AX ;Adjust pointer into dividend
shl AX,1
add SI,AX
mov BX,DI ;Save pointer to last divisor byte
std ;Direction backward
repz cmpsw ;Compare until unequal
ret
divcmp endp
PROGX ends
PGROUP group prog
prog segment byte public 'PROG'
assume CS:PGROUP
public big2flo
big2flo proc near
call %big2flo
ret
big2flo endp
public fix2big
fix2big proc near
call %fix2big
ret
fix2big endp
public magcomp
magcomp proc near
call %magcomp
ret
magcomp endp
public bigadd
bigadd proc near
call %bigadd
ret
bigadd endp
public bigsub
bigsub proc near
call %bigsub
ret
bigsub endp
public bigmul
bigmul proc near
call %bigmul
ret
bigmul endp
public bigdiv
bigdiv proc near
call %bigdiv
ret
bigdiv endp
prog ends
end