;
; GRDP
;
; Copyright(c) LADsoft
;
; David Lindauer, camille@bluegrass.net
;
;
; FP.ASM
;
; Function: Handle FP status commands
;
	;MASM MODE
	.MODEL SMALL
	.386

	public	floatcheck, fpcommand

include  eprints.inc 
include  einput.inc 
include  emtrap.inc 
include  ebreaks.inc 
include  eloader.inc
include  edos.inc
include eexec.inc

;
; hack to help MASM versions that die on pop-fmath
;
	.data
ten		dw	10
pointone	dt	0.1
nan		dd	7fffffffh	; quiet nan
inf		dd	7f800000h	; plus infinity
curdig		dd	?		;current digit for input
work		dw	?		;used in detecting FP hardware
fpflag		db	?		;flag if we can use FP commands
floatstat 	dw	47 dup (0)	;FNSAVE/FNSTOR buffer
enames		db	"IDZOUP"	;exception names


	.code
precmsg	dw	offset _text:prsing,offset _text:reserved
	dw	offset _text:prdbl,offset _text:prxt
roundmsg dw	offset _text:rdnear,offset _text:rdminus
	dw	offset _text:rdplus,offset _text:rdzer
prsing	db	"Single",0
prdbl	db	"Double",0
prxt	db	"Extended",0
reserved db	"Reserved",0
rdnear	db	"Nearest",0
rdzer	db	"Zero",0
rdplus	db	"Plus Infinity",0
rdminus db	"Minus Infinity",0

;
; check for floating point unit
;
floatcheck	proc
		mov	[fpflag],1
		mov	[work],4567h
		fninit
		fstsw 	[work]
		cmp	[work],0
		jz	hasfp
		mov	[fpflag],0
hasfp:
		ret
floatcheck	endp
;
; fp commands
;
fpcommand	proc
	test	[fpflag],1
	jnz	hasfloat
	PRINT_MESSAGE	<13,10,"No FP unit">
	clc
	ret
hasfloat:
	fnsave	[floatstat]
	fninit
	fwait
	call	WadeSpace
	jz	dumpstack
	cmp	al,'s'
	jnz	flread
;
; status command
;
	inc	si
	call	WadeSpace
	jnz	flerr
	frstor [floatstat]
	PRINT_MESSAGE	<13,10,"Masked exceptions: ">
	mov	dx,word ptr [floatstat]
	call	except
	PRINT_MESSAGE	<13,10,"Active exceptions: ">
	mov	dx,word ptr [floatstat+2]
	call	except
	PRINT_MESSAGE	<13,10,"Precision:         ">
	movzx	bx,byte ptr [floatstat+1]
	and	bl,3
	shl	bl,1
	mov	bx,[bx+precmsg]
	call	olMessage
	PRINT_MESSAGE	<13,10,"Rounding:          ">
	movzx	bx,byte ptr [floatstat+1]
	and	bl,0ch
	shr	bl,1
	mov	bx,[bx+roundmsg]
	call	olMessage
	PRINT_MESSAGE	<13,10,"FPU Status flags:  ">
	test	word ptr [floatstat+2],100h
	jz	notc0
	PRINT_MESSAGE	"C0 "
notc0:
	test	word ptr [floatstat+2],200h
	jz	notc1
	PRINT_MESSAGE	"C1 "
notc1:
	test	word ptr [floatstat+2],400h
	jz	notc2
	PRINT_MESSAGE	"C2 "
notc2:
	test	word ptr [floatstat+2],4000h
	jz	notc3
	PRINT_MESSAGE	"C3 "
notc3:
	clc
	ret
;
; dump the FP stack
;
dumpstack:
	mov	ax,word ptr [floatstat+2]
	shr	ax,11
	and	ax,7
	sub	ax,7
	neg	ax
	mul	[ten]
	mov	bx,offset floatstat+14
	add	bx,ax
	mov	cx,8
dsl:
	push	bx
	push	cx
	call	dumpval
	pop	cx
	pop	bx
	sub	bx,10
	loop	dsl
	frstor [floatstat]
	clc
	ret
;
; read an FP number into a reg
;
flread:
	sub	al,'0'
	jc	flerr
	cmp	al,8
	jae	flerr
	push	ax
	inc	si
	call	wadespace
	jz	flerr2
	call	ReadFpNumber
	jc	flerr2
	pop	ax
	mov	cl,al
	call	testtag
	jz	flerr3
	mov	bx,word ptr [floatstat+2]
	shr	bx,11
	and	bx,7
	movzx	ax,al
	sub	ax,bx
	mul	[ten]
	mov	bx,ax
	fstp	tbyte ptr [bx + floatstat + 14]
	frstor [floatstat]
	clc
	ret
	
testtag	PROC
	and	cl,7
	add	cl,cl
	mov	bx,[floatstat+4]
	shr	bx,cl
	and	bl,3
	cmp	bl,3
	ret
testtag	ENDP

flerr3:
	frstor	[floatstat]
	PRINT_MESSAGE	<13,10,"err - empty reg">
	clc
	ret
flerr2:
	add	sp,2
flerr:
	frstor [floatstat]
	stc
	ret
fpcommand	endp
except	PROC
	mov	si,offset enames
	mov	cx,6
exlp:
	shr	dx,1
	lodsb
	jnc	nhr
	push	dx
   	mov	dl,al
	call	putchar
	mov	dl,'e'
	call	putchar
	call	printspace
	pop	dx
nhr:
	loop	exlp
	ret
except	ENDP
;
; dump the value of a stack register
;
dumpval	PROC
	call	crlf
	dec	cl
	mov	dl,cl
	add	dl,'0'
	call	putchar
	mov	ax,word ptr [floatstat+2]
	shr	ax,11
	and	al,7
	cmp	al,cl
	jnz	nottop
       	mov	dl,'*'
	call	putchar
	jmp	join
nottop:
	call	printspace
join:
	mov	dl,')'
	call	putchar
	call	printspace
	call	printspace
	call	printspace
	mov	ax,word ptr [floatstat+4]
	shr	ax,cl
	shr	ax,cl
	and	al,3
	cmp	al,3
	jnz	notempty
	PRINT_MESSAGE	"<Empty>"
	clc
	ret

notempty:
	cmp	al,2
	jnz	notnan
	test	byte ptr [bx + 9],80h
	jz	nsinf
      	mov	dl,'-'
	call	putchar
nsinf:
	fld	tbyte ptr [bx]
	fxam
	fnstsw	ax
	fstp	st(0)
	sahf
	jpe	isinf
	PRINT_MESSAGE	"<Nan>"
	clc
	ret
isinf:
	PRINT_MESSAGE	"<Inf>"
	clc
	ret
notnan:
	cmp	al,1
	jnz	notzero
	jnz	notnan
	test	byte ptr [bx + 9],80h
	jz	nszer
      	mov	dl,'-'
	call	putchar
nszer:
	mov	dl,'0'
	call	putchar
	clc
	ret
notzero:
	fld	tbyte ptr [bx]
	call	PrintFloating
	fcomp
	clc
	ret
dumpval	ENDP
;
; actual FP print routines
;
PrintFloating	PROC
	call	fextract
	or	ax,ax
	jz	notdenorm
	PRINT_MESSAGE	"<Denorm>"
	ret
notdenorm:
	or	dx,dx
	jns	noneg
	call	putneg
noneg:
	cmp	bx,8		; getter be less than 16!!!!
	jg	highexp
	cmp	bx,-4
	jle	lowexp
	or	bx,bx
	jge	gte0
;
; print for less than one but not exponential
;
	push	bx
	call	putzer
	call	putper
	pop	cx
	not	cx
	jcxz	nozr
	inc	cx
zrl:
	call	putzer
	loop	zrl
nozr:
	mov	cx,16
nl2:
	call	fnd
	call	putdig
	loop	nl2
	ret
;
; print for greater than or equal to one but not exponential
;
gte0:
	push	bx
	mov	cx,bx
	call	fnd
	call	putdig
	jcxz	gte0nl
gtel1:
	call	fnd
	call	putdig
	loop	gtel1
gte0nl:
	call	putper
	pop	cx
	sub	cx,16
	neg	cx
gtel2:
	call	fnd
	call	putdig
	loop	gtel2
	ret
;
; hack to justify negative exponents
;
lowexp:
	dec	bx
	call	fnd
;
; exponential print
;
highexp:
	push	bx
	call	fnd
	call	putdig
	call	putper
	mov	cx,16
hel1:
	call	fnd
	call	putdig
	loop	hel1
	mov	dl,'e'
	call	putchar
	pop	ax
	or	ax,ax
	jns	nonegx
	push	ax
	call	putneg
	pop	ax
	neg	ax
nonegx:
	sub	cx,cx
elp1:
	sub	dx,dx
	div	[ten]
	push	dx
	inc	cx
	or	ax,ax
	jnz	elp1
elp2:
	pop	ax
	call	putdig
	loop	elp2
	ret
putdig:
	mov	dl,al
	add	dl,'0'
	call	putchar
       	ret
putper:
	mov	dl,'.'
	call	putchar
	ret
putneg:
	mov	dl,'-'
	call	putchar
	ret
putzer:
	mov	dl,'0'
	call	putchar
	ret
PrintFloating	ENDP
;
; get exponent and mantissa and sign
;
; enter:
;   st(0) = value
;
; returns:
;   ax = 0	; ok val
;   
;     st(0) = mantissa
;     bx = exp
;     dx = sign   ( +-1)
;
;   ax = -1	; infinity or dnormal
;
fextract 	PROC
	enter	6,0
	fstcw	[bp-2]
	mov	ax,[bp-2]
	or	ah,0ch		; round toward zero
	mov	[bp-4],ax
	fldcw	[bp-4]

	ftst
	fnstsw	ax
	fnclex			; just in case ... 
	sahf
	mov	eax,-1   	; first check for out of range
	jp	fxx
	jnz	dosign		; now check for zero
	sub	bx,bx
	sub	dx,dx
	jmp	fxx

dosign:
	mov	dx,1		; nonzero, finally get sign
	jnc	pos
	fabs       		; we will work with positive nums hereafter
	mov	dx,-1
pos:
	fldlg2       		; log to base 10
	fxch
	fyl2x
	
	fld	st(0)		; get int part
	frndint
	fist	word ptr [bp-6]
	pop	bx

	fsubp	st(1),st(0)			; fraction
	fldl2t			; convert back to base 2
	fmulp	st(1),st(0)

	fld	st(0)		; lovely exponentiation
	frndint
	fxch
	fld	st(1)
	fsubp	st(1),st(0)
	f2xm1			;
	fld1
	faddp	st(1),st(0)
	fscale
	fxch
	fcomp
	sub	eax,eax
fxx:
	fnclex
	fldcw	[bp-2]
	fwait
	leave
	ret
fextract	ENDP
;
; get next digit from mantissa
;
; enter:
;   mantissa from fextract on stack
;
; exit:
;   eax = next digit (base 10)
;   stack = new mantissa
;
fnd	PROC
	ENTER	6,0
	fstcw	[bp-2]
	mov	ax,[bp-2]
	or	ah,0ch		; round toward zero
	mov	[bp-4],ax
	fldcw	[bp-4]

	fld	st(0)		; next digit
	frndint
	fist	word ptr [bp-6]

	fsubp	st(1),st(0)			; new mantissa
	fimul	word ptr [ten]

	fnclex
	fldcw	[bp-2]
	fwait
	pop	ax		; cute trick to get result :)
	leave
	ret
fnd	ENDP
;
; read floating point number
readfpnumber PROC
	sub	dx,dx		; sign = +
	cmp	al,'+'		; get sign bit
	jz	rfn_gotsign
	cmp	al,'-'
	jnz	rfn_nosign
	inc	dx		; minus sign, sign = '-'
rfn_gotsign:
	inc	si		; step past sign
rfn_nosign:
	call	wadespace	; skip space
	jz	rfn_err
	mov	eax,[si]	; check for nan
	and	eax,0ffffffh
	cmp	eax,"nan"
	jnz	rfn_notnan
	fld	[nan]		; nan, load it
	jmp	rfn_ni
rfn_notnan:
	cmp	eax,"fni"	; check for inf
	jnz	rfn_notinf
	fld	[inf]		; inf load it
rfn_ni:
	add	si,3		; skip past inf/nan
	call	wadespace	; err if more
	jnz	rfn_err2
	jmp	rfn_done		; else tag in sign and exit
;
rfn_notinf:
	fldz            	; return val
	call	getdig		; see if leading digits
	jc	rfn_err2		
	jz	rfn_nostart     ; go check for '.' if not digit
rfn_prelp:
	fimul	[ten]   	; else shift digit in
	fild	[curdig]
	faddp	st(1),st(0)
	call	getdig     	; loop till all digits got
	jc	rfn_err2
	jnz	rfn_prelp
	cmp	al,'.'		; now check for '.'
	jz	rfn_dot		; yes, do it
	jmp	rfn_exp
;
rfn_nostart:
	cmp	al,'.'		; no leading digit, must have dot
	jnz	rfn_err
rfn_dot:
	call	getdig		; if no digits get exponent
	jc	rfn_err2
	jz	rfn_exp
	fld	[pointone]	; else we have digits, load up 0.1
rfn_ptlp:
	fild	[curdig]	; load digit
	fmul	st(0),st(1)	; mul by placeholder and add it in
	faddp	st(2),st(0)
	fld	[pointone] 	; adjust placeholder
	fmulp	st(1),st(0)
	call	getdig
	jc	rfn_err3
	jnz	rfn_ptlp
	fstp	st(0)   	; get rid of placeholder
rfn_exp:
	cmp	al,'e'  	; check for exponent
	jnz	rfn_done
	sub	bx,bx
	sub	cx,cx
rfn_exl:
	call	getdig		; first digit of exponent or sign
	jc	rfn_err2
	jnz	rfn_nexs
	inc	cx
	call	getdig
	jz	rfn_err2
	jc	rfn_err2
rfn_nexs:
	xchg	ax,bx
	push	dx
	mul	[ten]
	pop	dx
	xchg	ax,bx
	movzx	ax,al
	add	bx,ax
	call	getdig
	jc	rfn_err2
	jnz	rfn_nexs
	or	cx,cx
	jz	rfn_nexs2
      	neg	bx
rfn_nexs2:
	cmp	bx,1024		; verify range
	jg	rfn_err2
	cmp	bx,-1024
	jl	rfn_err2
	enter	4,0		; need temp space now
	mov	[bp-2],bx	; get exponent to fp stack
	fild	word ptr [bp-2]
	fldl2t                 	; convert exponent to logarithmic base 2
	fmulp	st(1),st(0)
	fnstcw	[bp-4]		; set rounding mode to zero
	mov	ax,[bp-4]
	and	ah,0f3h
	or	ah,0ch
	mov	[bp-2],ax
	fldcw	[bp-2]
	fld	st(0)   	; extract int and fraction of exponent
	frndint
	fxch	st(1)
	fsub	st(0),st(1)
	f2xm1              	; exponentiate the fraction
	fld1
	faddp	st(1),st(0)
	fscale			; scale in the integer part of exponent
	fxch	st(1)  		; get rid of int part
	fcomp	st(1)
	fldcw	[bp-4]		; restore round mode
	leave			; clear stack
	fmulp	st(1),st(0)	; multiply base * exponent (may result in 
				; an infinity)
rfn_done:
	or	dx,dx		; get the sign bit
	jz	rfn_x
	fchs			; to the FPU
rfn_x:
	clc
	ret

rfn_err3:
	fstp	st(0)
rfn_err2:
	fstp	st(0)
rfn_err:
	stc
	ret
;
; routine to get a digit, returns ZR for a '.' or 'e' or CR
; and Carry for an error, else NZ means a digit
;
getdig	PROC
	lodsb			; get val
	cmp	al,'0'		; check low end of digits
	jc	gds		; no, check high end
	cmp	al,'9'
	ja	gds
	sub	al,'0'
	mov	byte ptr [curdig],al ; digit, store it for later
	or	si,si		; set nz
	ret
gds:
	cmp	al,'-'
	jz	gdsk
	cmp	al,'e'		; check for 'e'
	jz	gdsk
	cmp	al,'.'		; '.'
	jz	gdsk
	cmp	al,13		; CR
	jnz	gdsk
	pushf			; if is CR bump char ptr back so
	dec	si		; we don't overrun
	popf
gdsk:
	clc
	ret

getdig	ENDP
readfpnumber ENDP
end