#include iolib.h
#include float.h
#asm
;
;	name...
;		float
;
;	purpose...
;		floating point routines for C programs
;
;	note...
;		This code uses some of the Z-80's UNDOCUMENTED 
;	INSTRUCTIONS. The instructions work because the IX and
;	IY escape bytes are actually a bit more general than
;	Zilog advertises. Note that in the instructions
;		E1	POP HL
;		DD E1	POP IX
;		FD E1	POP IY
;	(which Zilog documents), the opcodes for the IX and IY
;	registers are the same as for the HL register, but are
;	preceded by the escape bytes DD and FD. Similarly,
;		6A	LD L,D
;		DD 6A	LD XL,D		undocumented
;		FD 6A	LD YL,D		undocumented
;	where the new instructions deal with the lower half
;	of the IX and IY registers, respectively. All of the
;	single-byte transfers, and single byte operations such
;	as ADD and INC, involving the H or L registers, can be
;	preceded by escape bytes in this fashion.
;
;	history...
;		The floating point arithmetic routines were
;		written by Neil Colvin and were included in
;		Xitan Disk Basic.  He has consented to release
;		them to the public domain for individual use
;		but not for sale. Disassembly, comments, ifix(),
;		float(), and the interface to c programs were by
;		James R. Van Zandt.
;
;		Aug 84		moved atof() and putf() to
;			PRINT library.
;		17 Jun 84	including float.h & iolib.h, so
;			output can be separately assembled.
;			Calling err() for errors, so walkback
;			trace can work.
;		26 Jun 83	Renamed: abs->fabs, mod->fmod.
;		25 Jun 83	Added 'amax', 'amin',
;			'putf', 'atof'.
;		6 Nov 82	Removed 'sqrt' declaration
;		20 Oct 82	Added 'odd', used it
;			in 'ceil'
;		9 Oct 82	Added 'dswap'.
;		6 Oct 82	Removed my 'floor', renamed
;			existing 'int' to 'floor'.
;		5 Oct 82	"int" -> "qint"
;			declaring int and mod.
;		29 Sept 82	Added 'floor' code.
;		23 Sept 82	Removed transcendental
;			routines.
;		26 Aug 82	Added comparison routines.
;		18 Aug 82	Added 'ifix'
;		15 Aug 82	Added 'float'.
;		8 Aug 82	AS.COM format source code.
;		31 Jul 82	Translated to Zilog mnemonics.
;		30 Jul 82	Disassembled from Xitan Disk
;			Basic.
;
;	double float(),	/* integer to floating point conversion */
;	mod(),		/* mod(x,y) */
;	fabs(),		/* absolute value */
;	floor(),	/* largest integer not greater than */
;	ceil(),		/* smallest integer not less than */
;	rand();		/* random number in range 0...1 */
;	int ifix();	/* floating point to integer
;			(takes floor first) */
;
EXTRA:	DEFS	6
FA:	DEFS	6	;floating point accumulator
FASIGN:	DEFS	1	;msb indicates sign of FA
;			0 => negative, 1 => positive

;
L0F2E:	DEFB	0
SEED:	DEFB	80H,80H,0,0,0,0 ;seed for random number generator
;
DIVZERO: CALL	GRIPE
	DEFB	'can''t /0',0
ILLFCT:	CALL	GRIPE
	DEFB	'Illegal function',0
OFLOW:	CALL	GRIPE
	DEFB	'Arithmetic overflow',0
GRIPE:	CALL	QERR	;top word on stack points to message
	JP	0	;error was fatal
;
;	push the floating point accumulator
;	(preserving return address)
;
DPUSH:	POP	DE
	LD	HL,(FA+4)
	PUSH	HL
	LD	HL,(FA+2)
	PUSH	HL
	LD	HL,(FA)
	PUSH	HL
	EX	DE,HL
	JP	(HL)
;
;	push floating point accumulator
;	(preserve return address and next stacked word)
;
DPUSH2:	POP	DE	;save return address
	POP	BC	;save next word
	LD	HL,(FA+4)
	PUSH	HL
	LD	HL,(FA+2)
	PUSH	HL
	LD	HL,(FA)
	PUSH	HL
	EX	DE,HL
	PUSH	BC	;restore next word
	JP	(HL)	;return
;
;	convert the integer in hl to
;	a floating point number in FA
;
QFLOAT:	LD	A,H	;fetch MSB
	CPL		;reverse sign bit
	LD	(FASIGN),A ;save sign (msb)
	RLA		;move sign into cy
	JR	C,FL4	;c => nonnegative number
	EX	DE,HL
	SBC	HL,HL	;clear hl
	SBC	HL,DE	;get positive number into hl
FL4:	LD	A,L
	DEFB	0DDH
	LD	H,A	;move LSB to hx
	LD	C,H	;move MSB to c
	LD	DE,0	;clear rest of registers
	LD	B,D
	DEFB	0DDH
	LD	L,D	;clear lx
	LD	A,16+128
	LD	(FA+5),A ;preset exponent
	JP	NORM	;go normalize c ix de b
;
;	convert the floating point number in FA
;	to an integer in hl  (rounds toward negative numbers)
;
QIFIX:	CALL	QFLOOR		;take floor first
	LD	HL,0		;initialize the result
	LD	A,(FA+5)	;get the exponent
	LD	B,A		;  and save it
	OR	A
	RET	Z		;z => number was zero
	LD	HL,(FA+3)	;get most significant bits
	LD	C,H		;save sign bit (msb)
	LD	A,B		;get exponent again
	CP	80H+16
	JP	M,IFIX5		;m => fabs(fa) < 32768
	JR	NZ,OFLOW	;nz => fabs(fa) > 32768
;				(overflow)
	LD	A,H
	CP	80H
	JR	NZ,OFLOW	;nz => fa isn't -32768
	LD	A,L
	OR	A
	JR	NZ,OFLOW	;nz => overflow
	RET			;return -32768.
;
IFIX5:	SET	7,H		;restore hidden bit
IFIX6:	SRL	H		;shift right (0 fill)
	RR	L		;shift right (cy fill)
	INC	A
	CP	16+80H
	JR	NZ,IFIX6	;nz => haven't shifted enough
	RL	C
	RET	NC		;nc => positive number
	EX	DE,HL
	LD	HL,1		;compensate for cy bit
	SBC	HL,DE		;negate result
	RET
;
ADDHALF: LD	HL,HALF
HLADD:	CALL	LDBCHL
	JR	FADD
HALF:	DEFB	0,0,0,0,0,80H	;0.5
;
L247E:	CALL	PUSHFA
	CALL	L27EC
	POP	BC
	POP	IX
	POP	DE
	JR	FADD
;
HLSUB:	CALL	LDBCHL
	JR	FSUB
;
;	fmod(z,x) = z-x*floor(z/x)
;		if x>0 then  0 <= fmod(z,x) < x
;		if x<0 then  x < fmod(z,x) <= 0
;
QFMOD:	POP	HL	;return addr
	POP	DE	;discard next number
	POP	DE	; (already in FA)
	POP	DE
	POP	DE	;fetch next number
	POP	IX	; (1st operand, or "z")
	POP	BC
	PUSH	DE	;restore stack
	PUSH	DE
	PUSH	DE
	PUSH	DE
	PUSH	DE
	PUSH	DE
	PUSH	HL	;replace return addr
	PUSH	DE	;save another copy of z
	PUSH	IX
	PUSH	BC
	CALL	PUSHFA	;save a copy of 2nd operand ("x")
	CALL	FDIV	;z/x
	CALL	QFLOOR	;floor(z/x)
	POP	BC
	POP	IX
	POP	DE
	CALL	FMUL	;x*floor(z/x)
	POP	BC
	POP	IX
	POP	DE
;		to find mod(z,x)=z-x*floor(z/x), fall into...
FSUB:	CALL	MINUSFA
	JR	FADD
;
;	subtract the floating point accumulator from the value
;	on the stack (under the return address), leave result
;	in the floating point accumulator.
;
DSUB:	CALL	MINUSFA
;
;	add the value on the stack (under the return address)
;	to the floating point accumulator
;
DADD:	POP	HL	;save return address
	POP	DE
	POP	IX
	POP	BC
	PUSH	HL	;replace return address
;
;	add bc ix de to floating point accumulator
;
FADD:	LD	A,B
	OR	A
	RET	Z	;z => number to be added is zero
	LD	A,(FA+5)
	OR	A
	JP	Z,LDFABC ;z => accumulator is zero,
;				just load number
	SUB	B
	JR	NC,ADD2 ;nc => accumulator has larger number
	NEG		;reverse accumulator & bc ix de...
	EXX
	PUSH	IX
	CALL	LDBCFA
	EXX
	EX	(SP),IX
	CALL	LDFABC
	EXX
	POP	IX	;...end of reversing
ADD2:	CP	29H
	RET	NC	;nc => addition makes no change
	PUSH	AF	;save difference of exponents
	CALL	UNPACK	;restore hidden bit & compare signs
	LD	H,A	;save difference in signs
	POP	AF	;recall difference of exponents
	CALL	RSHIFT	;shift  c ix de b  right by (a)
	OR	H
	LD	HL,FA
	JP	P,ADD4	;p => opposite signs, must subtract
	CALL	FRADD	;c ix de += FA
	JR	NC,PACK	;nc => adding caused no carry
	INC	HL
	INC	(HL)	;increment exponent
	JP	Z,OFLOW
	LD	L,1
	CALL	RSH8	;shift  c ix de b  right by 1
	JR	PACK	;round, hide msb, & load into FA
;
ADD4:	XOR	A	;negate b...
	SUB	B
	LD	B,A
	LD	A,(HL)	;c ix de -= FA...
	SBC	A,E
	LD	E,A
	INC	HL
	LD	A,(HL)
	SBC	A,D
	LD	D,A
	INC	HL
	LD	A,(HL)
	DEFB	0DDH
	SBC	A,L
	DEFB	0DDH
	LD	L,A
	INC	HL
	LD	A,(HL)
	DEFB	0DDH
	SBC	A,H
	DEFB	0DDH
	LD	H,A
	INC	HL
	LD	A,(HL)
	SBC	A,C
	LD	C,A	;...end of subtraction, fall into...
;
;	reverse sign if necessary (cy set) and normalize
;	(sign reversal necessary because we're using
;	sign-magnitude representation rather than
;	twos-complement)
;
NORMA:	CALL	C,MINUSBC
;
;	normalize the 48 bit number in c ix de b
;	current exponent is in FA+5
;
;	result loaded into FA
;
NORM:	LD	L,B
	LD	H,E
	XOR	A
NORM2:	LD	B,A
	LD	A,C
	OR	A
	JR	NZ,NORM12  ;nz => 7 or fewer shifts needed
;			shift c ix d hl  left by one byte
	DEFB	0DDH
	LD	C,H
	DEFB	0DDH
	LD	A,L
	DEFB	0DDH
	LD	H,A
	DEFB	0DDH
	LD	L,D
	XOR	A
	LD	D,H
	LD	H,L
	LD	L,A	;...end of shifting
;
	LD	A,B
	SUB	8	;adjust exponent
	CP	0D0H
	JR	NZ,NORM2
;
NORM4:	XOR	A
NORM6:	LD	(FA+5),A
	RET
;
NORM8:	DEC	B
;			shift  c ix d hl  left one bit...
	ADD	HL,HL
	RL	D
	EX	AF,AF'
	ADD	IX,IX
	EX	AF,AF'
	JR	NC,NORM10
	INC	IX
NORM10:	EX	AF,AF'
	RL	C	;...end of shifting
;
NORM12:	JP	P,NORM8	;p => high order bit still zero
	LD	A,B
;			move number to  c ix de b
	LD	E,H
	LD	B,L
	OR	A
	JR	Z,PACK	;z => exponent unchanged
	LD	HL,FA+5		;update exponent
	ADD	A,(HL)
	LD	(HL),A
	JR	NC,NORM4	;nc => underflow (set to 0)
	RET	Z		;z => underflow (leave as 0)
PACK:	LD	A,B
PACK2:	LD	HL,FA+5	;round c ix de b to 40 bits
	OR	A
	CALL	M,INCR
	LD	B,(HL)	;load exponent
	INC	HL
	LD	A,(HL)	;recover sign
	AND	80H	;mask out all but sign
	XOR	C	;add to high
	LD	C,A	;   order byte
	JP	LDFABC	;place answer in FA
;
INCR:	INC	E	;increment c ix de
	RET	NZ
	INC	D
	RET	NZ
	DEFB	0DDH
	INC	L
	RET	NZ
	DEFB	0DDH
	INC	H
	RET	NZ
	INC	C
	RET	NZ	;z => carry
	LD	C,80H	;set high order bit
	INC	(HL)	;   and increment exponent
	RET	NZ
	JP	OFLOW
;
;	fraction add: c ix de += (hl)
;
FRADD:	LD	A,(HL)
	ADD	A,E
	LD	E,A
	INC	HL
	LD	A,(HL)
	ADC	A,D
	LD	D,A
	INC	HL
	LD	A,(HL)
	DEFB	0DDH
	ADC	A,L
	DEFB	0DDH
	LD	L,A
	INC	HL
	LD	A,(HL)
	DEFB	0DDH
	ADC	A,H
	DEFB	0DDH
	LD	H,A
	INC	HL
	LD	A,(HL)
	ADC	A,C
	LD	C,A
	RET
;
;	complement FASIGN and negate the fraction c ix de b
;
MINUSBC: LD	HL,FASIGN
	LD	A,(HL)
	CPL
	LD	(HL),A
	XOR	A
	LD	L,A
	LD	H,A
	SUB	B
	LD	B,A
	LD	A,L
	SBC	HL,DE
	EX	DE,HL
	LD	L,A
	DEFB	0DDH
	SBC	A,L
	DEFB	0DDH
	LD	L,A
	LD	A,L
	DEFB	0DDH
	SBC	A,H
	DEFB	0DDH
	LD	H,A
	LD	A,L
	SBC	A,C
	LD	C,A
	RET
;
;	shift  c ix de b  right by (a)
;
RSHIFT:	LD	B,0
RSH2:	SUB	8
	JR	C,RSH4	;c => 7 or fewer shifts remain
	LD	B,E	;shift  c ix de b  right by 8...
	LD	E,D
	DEFB	0DDH
	LD	D,L
	EX	AF,AF'
	DEFB	0DDH
	LD	A,H
	DEFB	0DDH
	LD	L,A
	EX	AF,AF'
	DEFB	0DDH
	LD	H,C
	LD	C,0	;...end of shifting
	JR	RSH2
;
RSH4:	ADD	A,9
	LD	L,A
RSH6:	XOR	A
	DEC	L
	RET	Z	;z => requested shift is complete
	LD	A,C
RSH8:	RRA		;shift  c ix de b  right by one...
	LD	C,A
	DEFB	0DDH
	LD	A,H
	RRA
	DEFB	0DDH
	LD	H,A
	DEFB	0DDH
	LD	A,L
	RRA
	DEFB	0DDH
	LD	L,A
	RR	D
	RR	E
	RR	B	;...end of shifting
	JR	RSH6
;
;	multiply the floating point accumulator by the value
;	on the stack (under the return address), leave result
;	in the floating point accumulator.
;
DMUL:	POP	HL	;return addr
	POP	DE	;multiplier...
	POP	IX
	POP	BC
	PUSH	HL	;replace return addr
;
;	multiply floating point accumulator by  bc ix de
;
FMUL:	CALL	SGN
	RET	Z	; z => accumulator has zero
	LD	L,0	;"product" flag
	CALL	DIV14	;find exponent of product
	LD	A,C  ;c' h'l' d'e' (multiplicand) = c ix de...
	PUSH	DE
	EXX
	LD	C,A
	POP	DE
	PUSH	IX
	POP	HL
	EXX		;...end of multiplicand loading
	LD	BC,0	; c ix de b (product) = 0...
	LD	D,B
	LD	E,B
	LD	IX,0
	LD	HL,NORM	; push addr of normalize routine
	PUSH	HL
	LD	HL,MULLOOP	; push addr of top of loop
	PUSH	HL	; (5 iterations wanted,
	PUSH	HL	; once per byte of fraction)
	PUSH	HL
	PUSH	HL
	LD	HL,FA	;point to LSB
MULLOOP: LD	A,(HL)	;get next byte of multiplier
	INC	HL
	OR	A
	JR	NZ,MUL2	; z => next 8 bits of multiplier are 0
	LD	B,E	;shift  c ix de b  right by 8...
	LD	E,D
	DEFB	0DDH
	LD	D,L
	EX	AF,AF'
	DEFB	0DDH
	LD	A,H
	DEFB	0DDH
	LD	L,A
	EX	AF,AF'
	DEFB	0DDH
	LD	H,C
	LD	C,A	;...end of shifting
	RET		;go to top of loop or NORM
;
MUL2:	PUSH	HL	;save multiplier pointer
	EX	DE,HL
	LD	E,8	;8 iterations (once per bit)
MUL4:	RRA		;rotate a multiplier bit into cy
	LD	D,A
	LD	A,C
	JR	NC,MUL6	; nc => no addition needed
	PUSH	HL	; c ix hl (product)  +=
	EXX		;	c' h'l' d'e' (multiplicand)
	EX	(SP),HL
	ADD	HL,DE
	EX	(SP),HL
	EX	DE,HL
	PUSH	IX
	EX	(SP),HL
	ADC	HL,DE
	EX	(SP),HL
	POP	IX
	EX	DE,HL
	ADC	A,C
	EXX
	POP	HL
;
MUL6:	RRA	   ;shift  c ix hl b (product)  right by 1...
	LD	C,A
	DEFB	0DDH
	LD	A,H
	RRA
	DEFB	0DDH
	LD	H,A
	DEFB	0DDH
	LD	A,L
	RRA
	DEFB	0DDH
	LD	L,A
	RR	H
	RR	L
	RR	B		;...end of shifting
	DEC	E
	LD	A,D
	JR	NZ,MUL4		; z => 8 iterations complete
	EX	DE,HL
MUL8:	POP	HL		;recover multiplier pointer
	RET			;go to top of loop or NORM
;
;	divide floating point accumulator by 10
;
DIV10:	CALL	PUSHFA
	LD	BC,8420H	; 10.0
	LD	IX,0
	LD	DE,0
	CALL	LDFABC
DIV1:	POP	BC
	POP	IX
	POP	DE
	JR	FDIV
;
;	divide the value on the stack (under the return
;	address) by the floating point accumulator, leave
;	result in the floating point accumulator.
;
DDIV:	POP	HL	;save return address
	POP	DE
	POP	IX
	POP	BC
	PUSH	HL	;replace return address
;
;	divide  bc ix de  by FA, leave result in FA
;
FDIV:	CALL	SGN
	JP	Z,DIVZERO ; z => attempting to divide by 0
	LD	L,0FFH	;"quotient" flag
	CALL	DIV14	;find quotient exponent
	PUSH	IY
	INC	(HL)
	INC	(HL)
	DEC	HL
	PUSH	HL	; c' h'l' d'e' (divisor) = FA...
	EXX
	POP	HL
	LD	C,(HL)
	DEC	HL
	LD	D,(HL)
	DEC	HL
	LD	E,(HL)
	DEC	HL
	LD	A,(HL)
	DEC	HL
	LD	L,(HL)
	LD	H,A
	EX	DE,HL
	EXX
	LD	B,C	; b iy hl (dividend) = c ix de...
	EX	DE,HL
	PUSH	IX
	POP	IY
	XOR	A	; c ix de (quotient) = 0...
	LD	C,A
	LD	D,A
	LD	E,A
	LD	IX,0
	LD	(EXTRA),A
DIV2:	PUSH	HL	;save b iy hl in case the subtraction
	PUSH	IY	; proves to be unnecessary
	PUSH	BC
	PUSH	HL	; EXTRA b iy hl (dividend)  -=
	LD	A,B	;	c' h'l' d'e' (divisor)...
	EXX
	EX	(SP),HL
	OR	A
	SBC	HL,DE
	EX	(SP),HL
	EX	DE,HL
	PUSH	IY
	EX	(SP),HL
	SBC	HL,DE
	EX	(SP),HL
	POP	IY
	EX	DE,HL
	SBC	A,C
	EXX
	POP	HL
	LD	B,A
	LD	A,(EXTRA)
	SBC	A,0
	CCF
	JR	NC,DIV4	; nc => subtraction caused carry
	LD	(EXTRA),A
	POP	AF	;discard saved value of dividend...
	POP	AF
	POP	AF
	SCF
	JR	DIV6
DIV4:	POP	BC	;restore dividend...
	POP	IY
	POP	HL
;
DIV6:	INC	C
	DEC	C
	RRA
	JP	M,DIV12
	RLA	  ;shift  c ix de a (quotient)  left by 1...
	RL	E
	RL	D
	EX	AF,AF'	;(these 6 lines are  adc ix,ix...)
	ADD	IX,IX
	EX	AF,AF'
	JR	NC,DIV8
	INC	IX
DIV8:	EX	AF,AF'
	RL	C	;...end of  c ix de a  shifting
	ADD	HL,HL	;shift  EXTRA b iy hl  left by 1...
	EX	AF,AF'
	ADD	IY,IY
	EX	AF,AF'
	JR	NC,DIV9
	INC	IY
DIV9:	EX	AF,AF'
	RL	B
	LD	A,(EXTRA)
	RLA
	LD	(EXTRA),A  ;...end of  EXTRA b iy hl  shifting
	LD	A,C	;test  c ix de...
	OR	D
	OR	E
	DEFB	0DDH
	OR	H
	DEFB	0DDH
	OR	L	;...end of  c ix de  testing
	JR	NZ,DIV2	;nz => dividend nonzero
	PUSH	HL
	LD	HL,FA+5
	DEC	(HL)
	POP	HL
	JR	NZ,DIV2
	JR	OFLOW2
;
DIV12:	POP	IY
	JP	PACK2
;
;	find exponent for product (L=0) or quotient (L=ff)
;
DIV14:	LD	A,B
	OR	A
	JR	Z,DIV20
	LD	A,L	;get product/quotient flag
	LD	HL,FA+5
	XOR	(HL)	;get +-FA exponent
	ADD	A,B	;find and...
	LD	B,A	;...load new exponent
	RRA
	XOR	B
	LD	A,B
	JP	P,DIV18
	ADD	A,80H
	LD	(HL),A
	JP	Z,MUL8
	CALL	UNPACK	;restore hidden bits & compare signs
	LD	(HL),A	;save difference of signs
	DEC	HL	;point to MSB of fraction
	RET
;
DIV17:	CALL	SGN
	CPL
	OR	A
	DEFB	21H	;"ignore next 2 bytes"
DIV18:	OR	A
DIV20:	POP	HL
	JP	P,NORM4
OFLOW2:	JP	OFLOW
;
;	multiply FA by 10
;
MUL10:	CALL	LDBCFA
	LD	A,B	;multiply bc ix de by 4...
	OR	A
	RET	Z
	ADD	A,2
	JR	C,OFLOW2
	LD	B,A	;...end of multiplication
	CALL	FADD	;add to FA, yields FA*5
	LD	HL,FA+5
	INC	(HL)	;double again, yielding FA*10
	RET	NZ
	JR	OFLOW2
;
; L27DB:	LD	BC,9980H	; -2**24
	LD	IX,0
	LD	DE,0
	CALL	COMPARE
	RET	Z
	JP	ILLFCT
;
L27EC:	LD	B,88H	; 128.
	LD	DE,0
L27F1:	LD	HL,FA+5
	LD	C,A
	PUSH	DE
	POP	IX
	LD	DE,0
	LD	(HL),B	;store exponent
	LD	B,0
	INC	HL
	LD	(HL),80H	;store minus sign
	RLA
	JP	NORMA
;
	EX	DE,HL
	XOR	A
	LD	B,98H
	JR	L27F1
	LD	B,C
L280C:	LD	D,B
	LD	E,0
	LD	HL,L0F2E
	LD	(HL),E
	LD	B,90H
	JR	L27F1
	LD	B,A
	XOR	A
	JR	L280C
;
L281B:	CALL	SGN
	JP	M,ILLFCT
	LD	A,(FA+5)
	CP	91H
	JP	C,INT2
	LD	BC,9180H	; -2**16
	LD	IX,0
	LD	DE,0
	CALL	COMPARE
	LD	D,C
	RET	Z
L2838:	JP	ILLFCT
	CALL	L281B
	LD	A,D
	OR	A
	JR	NZ,L2838
	LD	A,E
	RET
;
;	set z & s flags per FA
;
SGN:	LD	A,(FA+5)
	OR	A
	RET	Z
	LD	A,(FA+4)
	DEFB	0FEH	;"ignore next byte"
SETFLGS: CPL
	RLA
	SBC	A,A
	RET	NZ
	INC	A
	RET
;
;	Double precision comparisons
;
;	each compares top of stack
;	(under two return addresses) to FA
;
;TOS >= FA?
DGE:	CALL	DCOMPAR
	JR	Z,YES	;z => equal
	JR	DG	;remaining tests are shared
;
;TOS > FA?
DGT:	CALL	DCOMPAR
	JR	Z,NO	;z => equal
DG:	JP	P,NO	;p => not greater than
YES:	LD	HL,1	;load "true"
	RET
;
;TOS <= FA?
DLE:	CALL	DCOMPAR
	JR	Z,YES
	JR	DL
;
;TOS < FA?
DLT:	CALL	DCOMPAR
	JR	Z,NO
DL:	JP	P,YES	;p => less than
NO:	LD	HL,0	;load "false"
	RET
;
;TOS == FA?
DEQ:	CALL	DCOMPAR
	JR	Z,YES
	JR	NO
;
;TOS != FA?
DNE:	CALL	DCOMPAR
	JR	NZ,YES
	JR	NO
;
;common routine to perform double precision comparisons
DCOMPAR: POP	HL	;save 1st return addr
	POP	IY	;save 2nd return addr
	POP	DE	;get number to compare
	POP	IX
	POP	BC
	PUSH	IY	;replace 2nd addr
	PUSH	HL	;replace 1st addr, fall into...
;
;	sets flags per FA - (bc ix de)
;
COMPARE: LD	A,B
	OR	A
	JR	Z,SGN		;bc ix de = 0, so
;				sign of FA is result

	CALL	SGN
	LD	A,C
	JR	Z,SETFLGS	;FA = 0, so sign of
;				-(bc ix de) is result
	LD	HL,FA+4
	XOR	(HL)
	LD	A,C
	JP	M,SETFLGS	;operands have opposite
;				signs, so result is sign
;				of -(bc ix de)

	CALL	CPFRAC
	RRA			;recover cy bit
	XOR	C	;reverse sign if numbers are negative
	JR	SETFLGS
;
CPFRAC:	INC	HL	;compare  bc ix de  to (HL)
	LD	A,B
	CP	(HL)
	RET	NZ
	DEC	HL
	LD	A,C
	CP	(HL)
	RET	NZ
	DEC	HL
	DEFB	0DDH
	LD	A,H
	CP	(HL)
	RET	NZ
	DEC	HL
	DEFB	0DDH
	LD	A,L
	CP	(HL)
	RET	NZ
	DEC	HL
	LD	A,D
	CP	(HL)
	RET	NZ
	DEC	HL
	LD	A,E
	SUB	(HL)
	RET	NZ
	POP	HL	;return zero to program
	RET		;that called "COMPARE"
;
QFABS:	CALL	SGN
	RET	P
MINUSFA: LD	HL,FA+4
	LD	A,(HL)
	XOR	80H
	LD	(HL),A
	RET
;
PUSHFA:	EX	DE,HL
L2895:	LD	HL,(FA)
	EX	(SP),HL
	PUSH	HL
	LD	HL,(FA+2)
	EX	(SP),HL
	PUSH	HL
	LD	HL,(FA+4)
	EX	(SP),HL
	PUSH	HL
	EX	DE,HL
	RET
;			This can be reinstated when the compiler
;			understands "extern". Until then, pi
;			can't be declared without reserving
;			storage again.
; QPI:	DEFW	0A222H	;3.1415926535 = pi
; 	DEFW	0FDAH
; 	DEFW	8249H
;
;	FA = (hl)
;
DLOAD:	LD	DE,FA
	LD	BC,6
	LDIR
	RET
;
;	exchange floating point accumulator with
;	top of stack (under return address)
;
DSWAP:	POP	HL	;return addr
	POP	DE
	POP	IX
	POP	BC
	EXX		;protect the values
	CALL	DPUSH	;push FA
	EXX		;recover the values
	PUSH	HL	;replace return addr, fall into...
;
;	FA = bc ix de
;
LDFABC:	LD	(FA),DE
	LD	(FA+2),IX
	LD	(FA+4),BC
	RET
;
;	bc ix de = FA
;
LDBCFA:	LD	DE,(FA)
	LD	IX,(FA+2)
	LD	BC,(FA+4)
	RET
;
;	bc ix de = (hl)
;
LDBCHL:	LD	E,(HL)
	INC	HL
	LD	D,(HL)
	INC	HL
	LD	C,(HL)
	DEFB	0DDH
	LD	L,C
	INC	HL
	LD	C,(HL)
	DEFB	0DDH
	LD	H,C
	INC	HL
	LD	C,(HL)
	INC	HL
	LD	B,(HL)
	INC	HL
	RET
;
;	(hl) = FA
;
DSTORE:	LD	DE,FA
	LD	BC,6
	EX	DE,HL
	LDIR
	EX	DE,HL
	RET
;
UNPACK:	LD	HL,FA+4
	LD	A,(HL)	;get MSB of fraction
	RLCA		;rotate sign bit into lsb
	SCF		;set carry
	RRA		;rotate sign bit into cy, cy into msb
	LD	(HL),A	;restore MSB (with hidden bit restored)
	CCF		;complement sign bit...
	RRA
	INC	HL
	INC	HL
	LD	(HL),A	;...and save in msb of FASIGN
	LD	A,C	;similarly, get sign bit of bc ix de...
	RLCA
	SCF
	RRA
	LD	C,A	;...restore hidden bit...
	RRA
	XOR	(HL)	;...and compare with sign of FA.
	RET
;
INT2:	LD	B,A	;if a==0, return with  bc ix de = 0...
	LD	C,A
	LD	D,A
	LD	E,A
	DEFB	0DDH
	LD	H,A
	DEFB	0DDH
	LD	L,A
	OR	A
	RET	Z
	PUSH	HL
	CALL	LDBCFA	;copy FA into bc ix de,
	CALL	UNPACK	; restore hidden bits
	XOR	(HL)
	LD	H,A	;put sign in msb of h
	JP	P,INT4 ;p => positive number
	DEC	DE	;decrement c ix de...
	LD	A,D
	AND	E
	INC	A
	JR	NZ,INT4
	DEC	IX
	DEFB	0DDH
	LD	A,H
	DEFB	0DDH
	AND	L
	INC	A
	JR	NZ,INT4
	DEC	C	;...end of c ix de decrementing
;
INT4:	LD	A,0A8H	;shift  c ix de  right so bits to
	SUB	B	; the right of the binary point
	CALL	RSHIFT	; are discarded
	LD	A,H
	RLA
	CALL	C,INCR	;c => negative, increment  c ix de
	LD	B,0
	CALL	C,MINUSBC ;negate the fraction c ix de
	POP	HL
	RET
;
	POP	BC
	POP	IX
	POP	DE
;
;	divide with integer result
;	(truncates toward zero)
;
DIVI:	CALL	FDIV
	CALL	SGN
	JP	P,QFLOOR
	CALL	MINUSFA
	CALL	QFLOOR
	JP	MINUSFA
;
;	return -(floor(-x))
QCEIL:	CALL	ODD
;
;	return largest integer not greater than
QFLOOR:	LD	HL,FA+5
	LD	A,(HL)	;fetch exponent
	CP	0A8H
	LD	A,(FA)
	RET	NC	;nc => binary point is right of lsb
	LD	A,(HL)
	CALL	INT2
	LD	(HL),0A8H  ;place binary pt at end of fraction
	LD	A,E
	PUSH	AF
	LD	A,C
	RLA
	CALL	NORMA
	POP	AF
	RET
;
	LD	HL,FA+5
	LD	(HL),0A8H
	INC	HL
	LD	(HL),80H
	LD	A,C
	RLA
	JP	NORMA
;	amax(a,b)	returns the greater of a and b
QAMAX:	LD	HL,8	;offset for 1st argument
	ADD	HL,SP
	CALL	LDBCHL	;bcixde := 1st argument
	CALL	COMPARE
	JP	M,LDFABC
	RET
;
;	amin(a,b)
QAMIN:	LD	HL,8
	ADD	HL,SP
	CALL	LDBCHL
	CALL	COMPARE
	JP	P,LDFABC
	RET
;
;	negate FA, and push address of MINUSFA
;	called to evaluate functions f(x) when the argument is
;	negative and f() satisfies f(-x)=-f(x)
ODD:	CALL	MINUSFA
L29D1:	LD	HL,MINUSFA
	EX	(SP),HL
	JP	(HL)
;
QRAND:	CALL	SGN
	LD	HL,SEED
	JP	M,DSTORE
	CALL	DLOAD
	RET	Z
	LD	BC,9835H	; 11879545.
	LD	IX,447AH
	LD	DE,0
	CALL	FMUL
	LD	BC,6828H	; 3.92767775e-8
	LD	IX,0B146H
	LD	DE,0
	CALL	FADD
	CALL	LDBCFA
	LD	A,E
	LD	E,C
	LD	C,A
	LD	HL,FASIGN
	LD	(HL),80H
	DEC	HL
	LD	B,(HL)
	LD	(HL),80H
	CALL	NORM
	LD	HL,SEED
	JP	DSTORE
#endasm
                                                        
