453 lines
14 KiB
NASM
453 lines
14 KiB
NASM
|
; =====> SCANNUM.ASM
|
|||
|
;****************************************
|
|||
|
;* TIPC Scheme '84 Runtime Support *
|
|||
|
;* Numeric I/O Support *
|
|||
|
;* *
|
|||
|
;* (C) Copyright 1984,1985 by Texas *
|
|||
|
;* Instruments Incorporated. *
|
|||
|
;* All rights reserved. *
|
|||
|
;* *
|
|||
|
;* Date Written: 12 June 1985 *
|
|||
|
;* Last Modification: 22 July 1985 *
|
|||
|
;****************************************
|
|||
|
|
|||
|
DGROUP group data
|
|||
|
data segment word public 'DATA'
|
|||
|
assume DS:DGROUP
|
|||
|
public decpoint
|
|||
|
decpoint db '.'
|
|||
|
data ends
|
|||
|
|
|||
|
PGROUP group prog
|
|||
|
prog segment byte public 'PROG'
|
|||
|
assume CS:PGROUP
|
|||
|
|
|||
|
; Classify numeric string ending with a control character
|
|||
|
; Calling sequence: scannum(s,base)
|
|||
|
; Where ---- s: pointer to start of character string
|
|||
|
; base: default base
|
|||
|
; This function returns 0 if not a number, -1 if a flonum, and n>0
|
|||
|
; if an integer, where n is the number of digits in the integer.
|
|||
|
;
|
|||
|
; NOTE : DS is not guaranteed to point to the local data segment
|
|||
|
;
|
|||
|
scanargs struc
|
|||
|
dw ? ;Caller's BP
|
|||
|
dw ? ;Return address
|
|||
|
sptr dw ? ;Pointer to string
|
|||
|
scanbase dw ? ;Default base
|
|||
|
scanargs ends
|
|||
|
public scannum
|
|||
|
scannum proc near
|
|||
|
push BP
|
|||
|
mov BP,SP
|
|||
|
cld ;Direction forward
|
|||
|
mov SI,[BP].sptr ;Point DS:SI to characters
|
|||
|
mov BX,[BP].scanbase ;Set default base
|
|||
|
xor CX,CX ;Initialize digit count
|
|||
|
baselp: lodsb ;Fetch first char
|
|||
|
cmp AL,'#' ;Skip over the base macros
|
|||
|
jne nomac
|
|||
|
lodsb ;Get base argument
|
|||
|
sub AL,40h
|
|||
|
js nonnum ;If not a base designator, not a number
|
|||
|
and AL,0dfh ;Shift to upper case
|
|||
|
xor BL,BL ;Zero current base
|
|||
|
cmp AL,5 ;Check for #E,#I,#L,#S macros
|
|||
|
je baselp ; (legal, but don't affect base)
|
|||
|
cmp AL,9
|
|||
|
je baselp
|
|||
|
cmp AL,12
|
|||
|
je baselp
|
|||
|
cmp AL,19
|
|||
|
je baselp
|
|||
|
cmp AL,2 ;Jump if binary (#B)
|
|||
|
je bbin
|
|||
|
cmp AL,4 ;Jump if decimal (#D)
|
|||
|
je bdec
|
|||
|
cmp AL,15 ;Jump if octal (#O)
|
|||
|
je boct
|
|||
|
cmp AL,24 ;Jump if hexadecimal (#X)
|
|||
|
je bhex
|
|||
|
cmp AL,8 ;Jump if not #H (the only legal one left)
|
|||
|
jne nonnum
|
|||
|
bhex: mov BL,6
|
|||
|
bdec: add BL,2
|
|||
|
boct: add BL,6
|
|||
|
bbin: add BL,2
|
|||
|
jmp baselp ;Check for another switch
|
|||
|
nomac: cmp AL,'+' ;If +, note its presence
|
|||
|
je wassign
|
|||
|
cmp AL,'-' ;If not -, skip next char fetch
|
|||
|
jne notsign
|
|||
|
wassign: lodsb ;Fetch next char
|
|||
|
notsign: cmp AL,ss:decpoint ;Decimal point already?
|
|||
|
je point1 ;Jump if so... must be a flonum
|
|||
|
call isdg ;Otherwise, there must be a digit
|
|||
|
jnc nonnum ;If not, not a number
|
|||
|
wholelp: lodsb ;Else get next character
|
|||
|
call isdg
|
|||
|
jc wholelp ;Keep reading digits in whole part
|
|||
|
cmp AL,32 ;End of string?
|
|||
|
jb intnum ;Yes, we have an integer
|
|||
|
cmp AL,ss:decpoint ;Jump on decimal point
|
|||
|
je point
|
|||
|
call ismarker ;Jump if exponent marker (E or L valid)
|
|||
|
je expon
|
|||
|
nonnum: xor AX,AX ;Return 0, forget all else
|
|||
|
pop BP
|
|||
|
ret
|
|||
|
intnum: mov AX,CX ;Return digit count
|
|||
|
pop BP
|
|||
|
ret
|
|||
|
point1: lodsb ;We must have digit here
|
|||
|
call isdg
|
|||
|
jnc nonnum
|
|||
|
point: lodsb ;Get characters up to non-digit
|
|||
|
call isdg
|
|||
|
jc point
|
|||
|
cmp AL,32 ;If end of string, we have flonum
|
|||
|
jb flonum
|
|||
|
call ismarker ;Otherwise, check for exponent marker
|
|||
|
je expon
|
|||
|
jne nonnum
|
|||
|
expon: mov BL,10 ;Exponents are in base 10
|
|||
|
lodsb ;Get next char
|
|||
|
cmp AL,'-' ;Valid exponent sign
|
|||
|
jne edig ;Jump if not signed
|
|||
|
lodsb ;Else get next char
|
|||
|
edig: call isdg ;We must end with a nonempty string
|
|||
|
jnc nonnum ; of base 10 digits
|
|||
|
exponlp: lodsb
|
|||
|
call isdg
|
|||
|
jc exponlp
|
|||
|
cmp AL,32 ;If not end of string, nonnum
|
|||
|
jae nonnum
|
|||
|
flonum: mov AX,-1 ;Return -1 (flonum code)
|
|||
|
pop BP
|
|||
|
ret
|
|||
|
;ISDG: CF is set iff the char in AL is a digit in base BX
|
|||
|
; Also, if a digit, the digit count in CX is incremented
|
|||
|
isdg: cmp AL,'0' ;Not if below 0
|
|||
|
jl nodig
|
|||
|
cmp AL,'1' ;0 or 1 anytime
|
|||
|
jbe yesdig
|
|||
|
cmp BL,2 ;Nothing else for base 2
|
|||
|
je nodig
|
|||
|
cmp AL,'7' ;2-7 for base 8,10,16
|
|||
|
jbe yesdig
|
|||
|
cmp BL,8 ;Nothing else for base 8
|
|||
|
je nodig
|
|||
|
cmp AL,'9' ;8 or 9 for bases 10 or 16
|
|||
|
jbe yesdig
|
|||
|
cmp BL,10 ;Nothing else for base 10
|
|||
|
je nodig
|
|||
|
and AL,0dfh ;Convert to upper case
|
|||
|
cmp AL,'A' ;Base 16... Check for A-F
|
|||
|
jb nodig
|
|||
|
cmp AL,'F'
|
|||
|
jbe yesdig
|
|||
|
nodig: clc
|
|||
|
ret
|
|||
|
yesdig: inc CX ;Increment digit count
|
|||
|
stc
|
|||
|
ret
|
|||
|
;ISMARKER: ZF is set iff the character in AL is an exponent marker
|
|||
|
ismarker: cmp AL,'e'
|
|||
|
je mark
|
|||
|
cmp AL,'l'
|
|||
|
je mark
|
|||
|
cmp AL,'E'
|
|||
|
je mark
|
|||
|
cmp AL,'L'
|
|||
|
je mark
|
|||
|
mark: ret
|
|||
|
scannum endp
|
|||
|
|
|||
|
; Check character for digit status in a given base
|
|||
|
; Calling sequence: isdig(c,base)
|
|||
|
; Where ---- c: character to check
|
|||
|
; base: base in which to check
|
|||
|
isdargs struc
|
|||
|
dw ? ;Caller's BP
|
|||
|
dw ? ;Return address
|
|||
|
charg dw ? ;Character
|
|||
|
barg dw ? ;Base
|
|||
|
isdargs ends
|
|||
|
public isdig
|
|||
|
isdig proc near
|
|||
|
push BP
|
|||
|
mov BP,SP
|
|||
|
mov AL,byte ptr[BP].charg ;Fetch character
|
|||
|
mov BX,[BP].barg ;Fetch base
|
|||
|
call isdg ;Determine digitness
|
|||
|
jc wasdg ;Was a digit...don't zero AX
|
|||
|
xor AX,AX ;Otherwise return 0
|
|||
|
wasdg: pop BP
|
|||
|
ret
|
|||
|
isdig endp
|
|||
|
|
|||
|
; Convert digit character to its value
|
|||
|
; Calling sequence: digval(c)
|
|||
|
; Where ---- c: assumed to be a digit character
|
|||
|
digargs struc
|
|||
|
dw ? ;Caller's BP
|
|||
|
dw ? ;Return address
|
|||
|
carg dw ? ;Character
|
|||
|
digargs ends
|
|||
|
public digval
|
|||
|
digval proc near
|
|||
|
push BP
|
|||
|
mov BP,SP
|
|||
|
mov AL,byte ptr[BP].carg ;Fetch character
|
|||
|
xor AH,AH
|
|||
|
and AL,01fh ;Reduce bits
|
|||
|
cmp AL,16 ;Number or letter?
|
|||
|
jb hexdig ;Jump if letter
|
|||
|
and AL,0fh ;Zero the high nibble
|
|||
|
pop BP
|
|||
|
ret
|
|||
|
hexdig: add AL,9 ;Raise the lower nibble
|
|||
|
pop BP
|
|||
|
ret
|
|||
|
digval endp
|
|||
|
|
|||
|
; Convert flonum in interval [1.0e15,1.0e16) to bignum
|
|||
|
; Calling sequence: flo2big(flo,buf)
|
|||
|
; Where ---- flo: flonum in interval [1e15,1e16)
|
|||
|
; buf: bignum math buffer, minimum size 11 bytes
|
|||
|
flo2args struc
|
|||
|
dw ? ;Caller's BP
|
|||
|
dw ? ;Return address
|
|||
|
num dw ?,?,?,? ;Flonum (4 words)
|
|||
|
big dw ? ;Pointer to math buffer
|
|||
|
flo2args ends
|
|||
|
public flo2big
|
|||
|
flo2big proc near
|
|||
|
push BP
|
|||
|
mov BP,SP
|
|||
|
mov DI,[BP].big ;Point DI to math buffer
|
|||
|
cld ;Direction forward
|
|||
|
mov AX,4 ;Store bignum size (words) in buffer
|
|||
|
stosw
|
|||
|
mov AX,[BP+6].num ;Fetch exponent
|
|||
|
mov CX,AX ;Save exponent in CX
|
|||
|
rol AX,1 ;Store sign in buffer
|
|||
|
and AL,1
|
|||
|
stosb
|
|||
|
mov AX,CX ;Restore exponent to AX
|
|||
|
xor CH,CH ;Put (433h-exponent) in CX
|
|||
|
shr CL,1
|
|||
|
shr CL,1
|
|||
|
shr CL,1
|
|||
|
shr CL,1
|
|||
|
sub CL,3
|
|||
|
neg CL
|
|||
|
and AX,0fh ;Remove exponent from word in AX
|
|||
|
or AL,10h
|
|||
|
lea SI,[BP].num ;Point SI to flonum
|
|||
|
movsw
|
|||
|
movsw
|
|||
|
movsw
|
|||
|
stosw ;Word that used to have exponent
|
|||
|
sub DI,8 ;Point DI back to start of bignum
|
|||
|
cmp CL,-1 ;Branch if mantissa to be shifted left
|
|||
|
je manleft
|
|||
|
or CL,CL ;Branch if not to be shifted right
|
|||
|
jz shifted
|
|||
|
manright: shr word ptr[DI+6],1 ;Shift bignum right
|
|||
|
rcr word ptr[DI+4],1
|
|||
|
rcr word ptr[DI+2],1
|
|||
|
rcr word ptr[DI],1
|
|||
|
loop manright ;Loop until done
|
|||
|
jmp short shifted
|
|||
|
manleft: shl word ptr[DI],1 ;Shift bignum left
|
|||
|
rcl word ptr[DI+2],1
|
|||
|
rcl word ptr[DI+4],1
|
|||
|
rcl word ptr[DI+6],1
|
|||
|
shifted: pop BP
|
|||
|
ret
|
|||
|
flo2big endp
|
|||
|
|
|||
|
; Form floating-point ASCII representation from 16 digits and scale
|
|||
|
; Calling sequence: formflo(digs,chars,scale,prec,exp)
|
|||
|
; Where ---- digs: the digit characters of the flonum
|
|||
|
; chars: buffer to store the formed flonum
|
|||
|
; scale: flonum exponent part
|
|||
|
; prec: desired precision
|
|||
|
; exp: whether to use exponential format
|
|||
|
; Returns the length of the formed flonum string
|
|||
|
formargs struc
|
|||
|
dw ? ;Caller's BP
|
|||
|
dw ? ;Return address
|
|||
|
digptr dw ? ;Pointer to digits
|
|||
|
chrptr dw ? ;Pointer to result string
|
|||
|
scale dw ? ;Exponent part
|
|||
|
fprec dw ? ;Precision
|
|||
|
fexp dw ? ;Exponential format specifier
|
|||
|
formargs ends
|
|||
|
public formflo
|
|||
|
formflo proc near
|
|||
|
push BP
|
|||
|
mov BP,SP
|
|||
|
mov SI,[BP].digptr ;Point SI to digit string
|
|||
|
mov DI,[BP].chrptr ;Point DI to destination
|
|||
|
cld ;Direction forward
|
|||
|
mov DX,[BP].fexp ;Fetch form specifier
|
|||
|
mov AL,[SI] ;Fetch first digit
|
|||
|
cmp AL,'0'
|
|||
|
je toosmall ;Jump if zero
|
|||
|
cmp AL,'-' ;Negative sign?
|
|||
|
jne nonsign ;Jump if not signed
|
|||
|
signed: stosb ;Put sign in return buffer
|
|||
|
inc [BP].digptr ;Adjust pointer to first digit
|
|||
|
inc SI
|
|||
|
nonsign: mov BX,14 ;Round off the last digit
|
|||
|
call round
|
|||
|
mov BX,[BP].fprec ;Fetch precision
|
|||
|
or BX,BX
|
|||
|
jz putspace ;Jump if arbitrary precision
|
|||
|
;Determine location at which to begin rounding
|
|||
|
cmp BX,14 ;If precision out of range, replace
|
|||
|
jbe precok ; with highest possible
|
|||
|
mov BX,14
|
|||
|
precok: or DX,DX
|
|||
|
jnz doround ;If exponential, round now
|
|||
|
add BX,[BP].scale ;Add scale to precision
|
|||
|
jns notsmall ;Jump unless number rounds to 0
|
|||
|
cmp BX,-1
|
|||
|
jne toosmall ;Jump if num definitely rounds to 0
|
|||
|
cmp byte ptr[SI],'5' ;Check sigfig
|
|||
|
jb toosmall ;Jump if too small
|
|||
|
mov word ptr[SI],2031h ;Else round up and adjust scale
|
|||
|
inc [BP].scale
|
|||
|
jmp short spaced
|
|||
|
toosmall: mov AL,'0' ;Put (prec+1) 0's at start of input
|
|||
|
mov BX,[BP].fprec ; buffer
|
|||
|
toosmlp: mov [SI],AL
|
|||
|
inc SI
|
|||
|
dec BL
|
|||
|
jns toosmlp
|
|||
|
mov byte ptr[SI],' ' ;Follow by space
|
|||
|
mov DI,[BP].chrptr ;Start output over (wipe out any sign)
|
|||
|
jmp short spaced
|
|||
|
notsmall: cmp BX,16
|
|||
|
jae spaced ;Jump if no sense in rounding
|
|||
|
doround: call round ;Round the digits
|
|||
|
jmp short spaced
|
|||
|
;For arbitrary precision, change all trailing zeros to spaces
|
|||
|
; (there exists at least one nonzero digit)
|
|||
|
putspace: add SI,14 ;Point SI to last digit
|
|||
|
spacelp: cmp byte ptr[SI],'0'
|
|||
|
jne spaced
|
|||
|
and byte ptr[SI],0efh
|
|||
|
dec SI
|
|||
|
jmp spacelp
|
|||
|
;Now the spaces are in - start formatting
|
|||
|
spaced: mov SI,[BP].digptr ;Point SI to digit string
|
|||
|
mov BX,[BP].scale ;Fetch scale
|
|||
|
mov CX,[BP].fprec ;Fetch precision
|
|||
|
or DX,DX ;If exponent form desired
|
|||
|
jnz exform ; supply it
|
|||
|
cmp BX,-14 ;If scale>-15, check precision
|
|||
|
jge midscale
|
|||
|
or CL,CL ;If precision arbitrary, force expo-form
|
|||
|
jz exform
|
|||
|
midscale: cmp BX,0
|
|||
|
jl smallfix ;Branch if explicit form called for
|
|||
|
cmp BX,14
|
|||
|
jle largefix ;Branch if explicit, but >1
|
|||
|
;Form an exponential-format flonum
|
|||
|
exform: movsb ;Transfer first digit
|
|||
|
mov AL,decpoint ;Place decimal point
|
|||
|
placex: stosb ;Store character
|
|||
|
lodsb ;Transfer digits up to first space
|
|||
|
cmp AL,' '
|
|||
|
jne placex
|
|||
|
mov AL,'e' ;Place exponent marker
|
|||
|
stosb
|
|||
|
cmp BH,0 ;If scale negative, negate & store sign
|
|||
|
jge posscale
|
|||
|
neg BX
|
|||
|
mov AL,'-'
|
|||
|
stosb
|
|||
|
posscale: mov AX,BX ;Move scale to AX
|
|||
|
mov BH,10 ;Put divisor in BH
|
|||
|
mov DX,SP ;Save current stack pointer
|
|||
|
divlpf: div BH ;Divide
|
|||
|
mov BL,AH ;Push digit
|
|||
|
add BL,'0'
|
|||
|
push BX
|
|||
|
xor AH,AH ;Remove the remainder
|
|||
|
or AL,AL ;Loop until the quotient is zero
|
|||
|
jnz divlpf
|
|||
|
storelp: pop AX ;Restore exponent digit
|
|||
|
stosb ;Place it
|
|||
|
cmp SP,DX ;Loop until no more digits left
|
|||
|
jne storelp
|
|||
|
jmp short retlen
|
|||
|
;Form a fixed-decimal flonum magnitude greater than 1
|
|||
|
largefix: lodsb ;Fetch digit
|
|||
|
or AL,10h ;Turn ' ' to '0'
|
|||
|
stosb ;Store digit
|
|||
|
dec BL ;Loop until all pre-point digs done
|
|||
|
jns largefix
|
|||
|
mov AL,decpoint ;Place decimal point
|
|||
|
stosb
|
|||
|
digmrg: or CL,CL
|
|||
|
jnz preclp ;Jump if precision set
|
|||
|
arblp: lodsb ;Otherwise, arbitrary; do until space
|
|||
|
cmp AL,' '
|
|||
|
je retlen
|
|||
|
stosb
|
|||
|
jmp arblp
|
|||
|
llp: stosb
|
|||
|
preclp: dec CL ;Last digit done?
|
|||
|
js retlen ;Jump if so
|
|||
|
dodigs: lodsb ;Now do digits until precision reached
|
|||
|
cmp AL,' ' ;Space?
|
|||
|
jne llp ;If not, store it
|
|||
|
dec SI ;Restore SI
|
|||
|
mov AL,'0' ;Prepare to place 0
|
|||
|
jmp llp
|
|||
|
;Form a fixed-decimal flonum magnitude less than 1
|
|||
|
smallfix: mov CH,CL ;Copy precision to CH
|
|||
|
mov AL,'0' ;Place "0."
|
|||
|
stosb
|
|||
|
mov AL,decpoint
|
|||
|
slp: stosb
|
|||
|
inc BX
|
|||
|
jz digmrg ;If 0's done, do significant figures
|
|||
|
or CH,CH ;If precision was zero
|
|||
|
jz skpprec ; don't bother checking it
|
|||
|
dec CL
|
|||
|
js retlen ;If the precision is reached, stop
|
|||
|
skpprec: mov AL,'0' ;Otherwise, place 0's until scale=0
|
|||
|
jmp slp
|
|||
|
;Formation complete
|
|||
|
retlen: mov AX,DI ;Return length of string
|
|||
|
sub AX,[BP].chrptr
|
|||
|
pop BP
|
|||
|
ret
|
|||
|
;ROUND: Round the ASCII digits of a flonum, starting at [BX+SI]
|
|||
|
; SI->start of digits and is unchanged; BX destroyed
|
|||
|
round: mov AL,' ' ;Get digit after least-rounded and
|
|||
|
xchg AL,[BX+SI+1] ; replace it with a space
|
|||
|
cmp AL,'5'
|
|||
|
jb rounded ;Jump if rounded down
|
|||
|
roundlp: mov AL,[BX+SI] ;Otherwise, increment digit
|
|||
|
inc AL
|
|||
|
mov [BX+SI],AL ;Replace incremented digit
|
|||
|
cmp AL,'9'
|
|||
|
jbe rounded ;Jump if no carryover
|
|||
|
mov byte ptr[BX+SI],'0' ;Else replace digit
|
|||
|
dec BX ;Go to next digit
|
|||
|
jns roundlp
|
|||
|
mov byte ptr[BX+SI+1],'1' ;There are no more digits, place
|
|||
|
inc [BP].scale ; leading 1 and adjust scale
|
|||
|
rounded: ret
|
|||
|
formflo endp
|
|||
|
|
|||
|
prog ends
|
|||
|
end
|
|||
|
|