; FLTOUT.ASM
; ----------
;
; See FALCONER.WS4 for doc.
;
; (Retyped by Emmanuel ROCHE.)
;
;--------------------------------
; External calls required
;
	extrn	derc,dten	   ; in INTARITH
	extrn	fdivt,fload,fmult  ; in FLTARITH
;
;--------------------------------
; External connectors to list and console drivers
;
	extrn	lout,cout	; Undefined in system,
				;   output (C), Set (A) := (C).
;
;--------------------------------
; Entry points allowed
;
	entry	lflt,tflt,oflt,fmat
;
;--------------------------------
; Entry points to utility routines
;
	entry	exdg,otcbk,otccl
	entry	opt,oneg,odzs
;
;--------------------------------
; Macro definitions
;--------------------------------
;
; Load (reg) from TOS and leave on stack (reg)
;
ltos	macro	reg
	pop	reg
	push	reg
	endm
;
; "Return" and check stacl level zero
;
rtn	macro
	if	.lvl
	error	"0"+.lvl
.lvl	set	0
	endif
	ret
	endm
;
; Trade (A) digits, leave LSB in Carry (A,F)
;
tdig	macro
	rlc
	rlc
	rlc
	rlc
	endm
;
;--------------------------------Š; Utility routines
;--------------------------------
;
; Output blank to console/lister
; Use lister if (A) sign bit=1; else console
; A,F,C
;
otcbk:	mvi	c,' '		; 
;
; Output a character
; Use lister if (A) sign bit=1; else console
; A,F,C
;
otccl:	rlc			; 
	jc	lout		; 
	jmp	cout		; On console
;
; Output (A) blanks
; Use lister if (A) sign bit=1; else console
; A,F
;
hblk:	push	b		; 
	mov	b,a		; 
	jmp	hblk2		; Check for zero
hblk1:	mov	a,b		; 
	call	otcbk		; 
	dcr	b		; 
	mov	a,b		; 
hblk2:	ani	7FH		; 
	jnz	hblk1		; 
	pop	b		; 
	rtn			; 
;
; Output a decimal point
; Use lister if (A) sign bit=1; else console
; A,F,C
;
opt:	mvi	c,'.'		; Decimal point
	jmp	otccl		; 
;
; Output "-"
; Use lister if (A) sign bit=1; else console
; A,F,C
;
oneg:	mvi	c,'-'		; Negative sign
	jmp	otccl		; 
;
; Output (HL) in decimal, suppress leading zeros
; Use lister if (A) sign bit=1; else console
;
odzs:	push	b		; 
	mvi	b,5		; 
	push	psw		; Preserve
odzs2:	call	exdg		; Extract a digit
	jnz	odzs4		; Non-zero, end suppress
	dcr	b		; 
	jnz	odzs2		; Continue suppression
	inr	b		; Re-extract final zero and output
odzs3:	call	exdg		; Get next digit
odzs4:	ltos	psw		; Š	call	otccl		; Output to console or lister
	dcr	b		; 
	jnz	odzs3		; 
	pop	psw		; 
	pop	b		; 
	rtn			; 
;
; Extract a decimal digit, 10^((B)-1), from (HL).
; ASCII digit returned in (C) and (A)
;   with Zero flag for digit=zero.
; A,F,C
;
exdg:	push	h		; 
	push	b		; 
exdg1:	call	dten		; 
	dcr	b		; 
	jnz	exdg1		; 
	adi	'0'		; 
	cpi	'0'		; 
	pop	b		; 
	pop	h		; 
	mov	c,a		; 
	rtn			; 
;
;--------------------------------
; End utility routines
;--------------------------------
;
; "Fixed" point representation consist of a 16 bit positive
; integer (in the range 0 to 65535), and a 7 bit offset (by
; 40H) integer exponent, which represents a power of ten
; multiplier. The eighth exponent bit represents the sign
; of the mantissa. This representation is used for input/
; output only.
;
; Convert "real" format to "fixed" format
; A,F,D,E,H
;
fix:	mov	a,h		; 
	ora	a		; 
	jnz	fix1		; Value not zero
	mvi	h,40H		; 00000
	lxi	d,0		; 
	rtn			; 
fix1:	cpi	91H		; 
	push	b		; 
	mvi	b,40H		; Decimal exponent
@01	set	.lvl		; 
	jnc	fix5		; > 65535, integer
	cpi	8EH		; 
	jnc	fix6		; Treat as left shifted integer
fix2:	call	fmult		; < 32768
	dcr	b		; 
	mov	a,h		; 
	sui	90H		; 
	jc	fix2		; Still fractional segment
	jnz	fix5		; Not now integer
fix3:	mov	a,d		; 
	ani	80H		; Extract sign
	ora	b		; Š	mov	h,a		; 
	mov	a,d		; 
	ori	80H		; Set MSbit, range 32768/65535
	mov	d,a		; 
	pop	b		; 
	rtn			; 
.lvl	set	@01		; 
fix5:	call	fdivt		; Integer > 65535
	inr	b		; 
	mov	a,h		; 
fix6:	sui	90H		; 
	jz	fix3		; Now integer representation
	jnc	fix5		; 
	mov	h,a		; Range -1 to -4
	mov	a,d		; 
	ani	80H		; Result sign
	ora	b		; 
	mov	b,a		; 
	mov	a,d		; 
	ori	80H		; 
	mov	d,a		; 
fix7:	ora	a		; Reset Carry
	call	derc		; 
	inr	h		; 
	jnz	fix7		; Shift off fractional segment
	jnc	fix8		; No rounding needed
	inx	d		; 
fix8:	mov	h,b		; 
	pop	b		; 
	ora	a		; Reset Carry
	rtn			; 
;
; ***** Output routines *****
;
; Output (DE.H) in "fixed" form
; Suppress leading zeroes
; If A >= 0, to console. If a < 0, to lister.
; A,F,B,C,D,E,H,L
;
ofix:	push	psw		; 
	mov	a,h		; 
	ani	80H		; 
	jp	ofix1		; Positive
	ltos	psw		; Output "-" sign
	call	oneg		; Send a "-" sign
ofix1:	mov	a,h		; 
	ani	7FH		; Remove sign
	sui	40H-6		; Signed decimal exponent
	mov	h,a		; 
	xchg			; Value in HL, exponent in D
	mvi	b,6		; First digit
ofix2:	dcr	b		; 
@01	set	.lvl		; 
	jz	ofixa		; Done, all digits 0
	dcr	d		; 
	jz	ofix7		; Decimal point here
	jm	ofix7		;  xxxxE-xx
	call	exdg		; 
	jz	ofix2		; Suppress a zero
ofix3:	ltos	psw		; 
	call	otccl		; List a digitŠ	dcr	d		; 
	jnz	ofix4		; 
	ltos	psw		; 
	call	opt		; Decimal point here
ofix4:	dcr	b		; 
	jz	ofix5		; All digits listed
	call	exdg		; 
	jmp	ofix3		; List next digit
ofix5:	xra	a		; 
	sub	d		; 
	jp	ofixb		; Not xxxx.E+xx
	adi	3		; 
	jm	ofix6		; > 9999000
	mvi	c,"0"		; 
	ltos	psw		; 
	call	otccl		; 
	dcr	d		; 
	jmp	ofix5		; 
ofix6:	mvi	c,'E'		; 
	ltos	psw		; 
	call	otccl		; 
	mov	l,d		; 
	mvi	h,0		; 
	pop	psw		; 
	jmp	odzs		; List exponent
.lvl	set	@01		; 
ofix7:	ltos	psw		; 
	call	opt		; .xxxxE-xx
ofix8:	mov	a,d		; 
	ora	a		; 
	jz	ofix9		; Zero exponent
	adi	3		; 
	jm	ofix9		; Range -1 to -3, insert 0's
	mvi	c,"0"		; 
	ltos	psw		; 
	call	otccl		; 
	inr	d		; 
	jmp	ofix8		; Check for more 0's
ofix9:	call	exdg		; 
	ltos	psw		; 
	call	otccl		; 
	dcr	b		; 
	jnz	ofix9		; 
	xra	a		; 
	mov	h,a		; 
	sub	d		; 
	mov	l,a		; 
	jz	ofixb		; Ignore zero exponent
	mvi	c,'E'		; 
	ltos	psw		; 
	call	otccl		; 
	ltos	psw		; 
	call	oneg		; 
	pop	psw		; 
	jmp	odzs		; List exponent and exit
.lvl	set	@01		; 
ofixa:	ltos	psw		; 
	call	otccl		; List a zero
ofixb:	pop	psw		; 
	rtn			; 
;
; Output "real" (DE.H) to listerŠ;
lflt:	push	psw		; 
	mvi	a,-1		; Identify as lister output
	call	oflt		; 
	pop	psw		; 
	rtn			; 
;
; Output "real" (DE.H) to console
;
tflt:	push	psw		; 
	mvi	a,0		; Identify as console output
	call	oflt		; 
	pop	psw		; 
	rtn			; 
;
; Output "real"
; If A < 0, to lister. If A >= 0, to console.
; A
;
oflt:	push	b		; 
	push	d		; 
	push	h		; 
	push	psw		; 
	call	fix		; 
	pop	psw		; Get destination
	call	ofix		; 
	pop	h		; 
	pop	d		; 
	pop	b		; 
	rtn			; 
;
; Output (DE.H) with format specification in (A)
;
; (A) bit field	    Meaning
; -------------     -------
;  0:1 (LH bit)     1=to lister, 0=to console
;  1:3 (3 bits)     Places to left of decimal point
;  4:1 (1 bit)      Use free format, ignore places spec
;  5:3 (3 RH bits)  Places to right of decimal point
;
fmat:	push	psw		; 
	push	b		; 
	push	d		; 
	push	h		; 
	push	psw		; Save places
	call	fix		; 
	ltos	psw		; 
	call	ajfx		; Adjust on right of dec point
	call	tpsn		; Position the field
	pop	psw		; For list/console destination
	call	ofix		; Output the data
	pop	h		;   and restore registers.
	pop	d		; 
	pop	b		; 
	pop	psw		; 
	rtn			; 
;
; Adjust "fixed" format for (A) digits after decimal point
; Max digits=7, 8 bit for floating format
; Round the result
; D,E,HŠ;
ajfx:	push	psw		; 
	push	b		; 
	push	h		; Save BC.L
	ani	15		; 
	cpi	8		; 
	jnc	ajfx4		; Floating format, no adjust
	ani	7		; 7 digits max
	mov	c,a		; Digits required
ajfx1:	mov	a,h		; 
	ani	7FH		; Remove sign
	sui	40H		; 
	add	c		; 
	jp	ajfx4		; No excess fractional segment
	mov	b,a		; 
	xchg			; 
ajfx2:	call	dten		; Remove a digit
	inr	d		; Adjust decimal exponent
	inr	b		; 
	jm	ajfx2		; Remove more
	cpi	5		; 
	jc	ajfx3		; No rounding
	inx	h		; 
ajfx3:	xchg			; 
	jmp	ajfx1		; In case rounding added digit
ajfx4:	pop	b		; Reload BC.L
	mov	l,c		; 
	pop	b		; 
	pop	psw		; 
	rtn			; 
;
; Control leading blanks via bits 1:3 of (A)
; Bit 4:1 specifies free format else output blanks
;   required to place the decimal point at the
;   field position to right of starting point.
;
tpsn:	push	psw		; 
	push	b		; 
	push	psw		; 
	ani	8		; 
	jnz	tpsn9		; Free format
	pop	psw		; 
	ani	0F0H		; 
	mov	b,a		; 
	ani	70H		; 
	tdig			; 
	mov	c,a		; Count of places needed
	push	b		; 
	mov	a,h		; 
	ani	7FH		; 
	sui	40H		; Form dp loc wrt right digit
	jm	tpsn5		; Fract segment
	inr	a		; 
	cmp	b		; 
	jnc	tpsn9		; Too large, use free format
	mvi	a,0		; Use value zero?
tpsn5:	mov	b,a		; Negative value
	call	ndig		; Count of sig digits in value
	add	b		;   to left of decimal point
	jp	tpsn6		; There are someŠ	mvi	a,0		; One + zeroes after dec point
tpsn6:	pop	b		; 
	cma			; 
	inr	a		; 
	add	c		; Spaces required
	jp	tpsn8		; 
	mvi	a,0		; Can't allow negative count
tpsn8:	mov	c,a		; 
	mov	a,h		; 
	ora	a		; 
	jp	tpsn7		; Positive value
	dcr	c		; Allow for - sign
	jp	tpsn7		; Room
	inr	c		; No room, move all right
tpsn7:	mov	a,b		; 
	ani	80H		; File flag
	ora	c		; 
	call	hblk		; Space as required
	push	psw		; 
tpsn9:	pop	psw		; 
	pop	b		; 
	pop	psw		; 
	rtn			; 
;
; Return count of significant digits in (DE)
; Treating (DE) as decimal integer with leading
;   zeroes suppressed. Return 1 for value 00000.
; A,F
;
ndig:	push	b		; 
	push	d		; 
	xchg			; 
	mvi	b,0		; 
ndig1:	inr	b		; 
	call	dten		; 
	mov	a,h		; 
	ora	l		; 
	jnz	ndig1		; More digits left
	mov	a,b		; 
	xchg			; 
	pop	d		; 
	pop	b		; 
	rtn			; 
;
;--------------------------------
;
	end			; of FLTOUT.ASM
