FAST FLOATING POINT source code for the ATARI#

Copyright (C) 1981 to 1984 by Newell Industries & Charles W. Marslett



AtariWiki is very, very proud Newell Industries & Charles W. Marslett gave us the source code for the FAST FLOATING POINT routines for PD. Thank you so much Newell Industries & Charles W. Marslett, that is not a little gift, that is a giant leap for the community worldwide and will result in rewrite all Atari OS and burn them on EPROM. We further thank Robert 'Bob' Puff for converting the original AMAC source code into MAC/65 source code. Thank you all so much, the community is in great debt for the work you have done! :-)

Please don't misunderstand this work, it is 'just' fast or even up to 3.5 times faster than the original routines! Indeed, it is way more! The routines give the user reliable results, too! That is very important for calculations. Further, they maintain the jump addresses from the original routines from Atari. Therefore, no trouble, just have fun and enjoy.

Picture#

Ad from Newell Industries from 1981:

ATR image#

HEADER.M65, FASTFP1.M65: MAC/65 Tokenized source. Load the header file, and assemble from it.

FASTFP.OBJ: Object file produced from above files, when assembled at $D800.

FASTFP.ASM: Original AMAC source file (LISTed format).

LDFAST.M65: MAC/65 Tokenized source for a program to load D1:FASTFP.OBJ into the RAM under the OS in XL/XE machines.

LDFAST.ASM: Original AMAC source for above.

FASTFP.DOC: Original document file listing the routines contained in the faschip.

AMAC source code file#

Source Code of the final revision F#

	TITLE	'FAST FLOATING POINT (ATARI)'
	SUBTTL 'SYSTEM EQUATES USED'
;
;	FAST FLOATING POINT ROM FOR THE ATARI 800/400 (TM ATARI, INC)
;	  Written by Charles W. Marslett, August-November, 1981
;	  Copyright 1981, Charles W. Marslett
;	  Copyright 1982 and 1984, Charles W. Marslett
;
;	Permission is granted by the author for any use whatsoever of this
;	  code, so long as this notice remains in the source code, and so
;	  long as the source to this routine, however modified or unmodified,
;	  is made available for a nominal cost.
;
;	SPEED IMPROVED OVER ATARI ROM, IN MOST CASES BY A FACTOR OF 3.
;	ERRORS IN LOG AND DIVIDE ALGORITHMS CORRECTED.
;	ACCURACY IN EXP FUNCTION IMPROVED (I THINK).
;
;	REVISION A, ASCII-->FLOATING CONVERSION OF 9 DIGITS ONLY (LIKE ATARI),
;		19 NOVEMBER 1981.
;	REVISION B, RETURNED TO USING REDARG IN LOG (LIKE ATARI), TO ALLOW
;		INCLUSION OF SIN/COS/SQR CONSTANTS FOR BASIC CARTRIDGE
;		27 NOVEMBER 1981.
;	REVISION C, ELIMINATED USE OF DEGRAD AND FRX IN THE FMUL/FDIV CODE AND
;		FURTHER SPEEDED UP FMUL BY UNROLLING SHIFT LOOP, 30-NOV-81
;	REVISION D, INSERTED COPYRIGHT NOTICE IN THE ROM, 1-DEC-81
;	REVISION E, ADDED ASCII HEX DISPLAY ROUTINE, 4-MAR-82
;	REVISION F, MODIFIED EXP AND LOG FUNCTIONS FOR THE 600XL
;		AND 800XL BASIC ROM POWER ROUTINE BUG, 17-JUNE-84
;
ROMBASE =	$D800		;STARTING ADDRESS OF F.P. ROM
FPREC	=	6		;NUMBER OF BYTES IN A F.P. NUMBER
;
;	BASE PAGE DEFINITIONS
;
	ORG	$D4
FR0	DS	FPREC
FRE	DS	FPREC
FR1	DS	FPREC
FR2	DS	FPREC
FRX	DS	FPREC
EEXP	=	FRX+1
NSIGN	=	FRX+2
ESIGN	=	FRX+3
FCHRFLG =	FRX+4
DIGRT	=	FRX+5
CIX	DS	1
INBUFF	DS	2
Z1	DS	2
Z2	DS	2
Z3	DS	2
DEGRAD	DS	1		;DEGREE OR RADIAN UNITS FOR BASIC
FLPTR	DS	2
FPTR2	DS	2
;
;	POLYNOMIAL WORK AREA
;
	ORG	$57E
LBPR1	DS	1
LBPR2	DS	1
LBUFF	DS	$60
PLYARG	DS	FPREC
FSCR	DS	FPREC
FSCR1	DS	FPREC
	DS	128-$60-FPREC-FPREC-FPREC
LBFEND	=	*-1
	SUBTTL 'FLOATING POINT/ASCII CONVERSION ROUTINES'
;
;	ASCII TO FLOATING POINT CONVERSION ROUTINE (ADDR=D800)
;
	ORG	ROMBASE
AFP	JSR	SKBLK		;SKIP ANY LEADING BLANKS
	JSR	NXTCH		;LOAD THE NEXT CHARACTER
	BCS	EXIT		;IF NOT NUMERIC, EXIT
	LDX	#FRX		;ZERO FRX AND FR0
	JSR	ZF1
	JSR	ZFR0
	DEC	DIGRT		;STORE -1 INTO DIGRT
;
CHRLP	STA	FCHRFLG		;CLEAR THE FIRST CHARACTER INDICATION
	JSR	GETCH		;GET THE NEXT CHARACTER
	BCS	INVLD		;IF NOT NUMERIC
	LDX	FR0
	BNE	NOTZER		;IF FR0 IS NORMALIZED
	JSR	SH0L4		;ELSE, SHIFT CONTENTS LEFT 4 BITS
	ORA	FR0+FPREC-1
	STA	FR0+FPREC-1	;MERGE IN NEW DIGIT
	LDX	DIGRT
	BMI	CHRLP0		;IF STILL AN INTEGER, JUST LOOP BACK
;
DECFND	INC	DIGRT		;ELSE, BUMP NUMBER OF DECIMAL PLACES
;
NOTZER	LDX	DIGRT		;FILLED UP THE 10 DIGITS FOR THE NUMBER
	BPL	CHRLP0		;IF AFTER DECIMAL, IGNORE REMAINING DIGIT
	INC	EEXP		;IF NOT, BUMP THE EXPONENT BY 1
CHRLP0	LDA	#$FF		;RESET FIRST CHARACTER FLAG
	BNE	CHRLP
;
INVLD	CMP	#'.'		;FOUND DECIMAL?
	BNE	NODEC0		;YES, START DECIMAL PLACE COUNTER
	LDX	DIGRT		;BY MAKING NEGATIVE DIGRT POSITIVE
	BMI	DECFND		;IF NOT NEGATIVE, SECOND '.' ENDS FIELD
;
NODEC0	CMP	#'E'
	BNE	NOEXP		;IF NOT 'E', THEN NO EXPONENT SUBFIELD
	LDX	CIX
	JSR	GETCH
	BCC	NOESG		;X CONTAINS 'CIX' BEFORE PARSING EXPONENT
	CMP	#'+'
	BEQ	SKESG
	CMP	#'-'
	BNE	RSTEXP		;IF NO + OR - AFTER 'E' THEN NO EXPONENT
	STA	ESIGN		;MARK NEGATIVE IF '-' FOUND
SKESG	JSR	GETCH		;IN ANY CASE, SEE IF NEXT CHARACTER IS NUMERIC
	BCS	RSTEXP		;IF NOT, NO EXPONENT SUBFIELD
EXPLP	TAY			;SAVE NEXT DIGIT
	LDA	FRX
	ASL	A
	ASL	A		;MULTIPLY PREVIOUS TOTAL BY 10
	ADC	FRX
	ASL	A
	STA	FRX		;THEN ADD TO THIS DIGIT (IN Y)
	TYA
	ADC	FRX
NOESG	STA	FRX		;AND UPDATE FRX
	JSR	GETCH
	BCC	EXPLP		;IF NO OVERFLOW AND NEXT CHARACTER NUMERIC
	BCS	NUMEND		;IF NONNUMERIC, END OF THE NUMBER
;
;	STUFF A BYTE INTO THE TEXT BUFFER (CONVERTING TO ASCII IF REQ.)
;
CVASC	ORA	#'0'		;CONVERT DIGIT TO ASCII CHARACTER
;
STUFF	STA	LBUFF,Y		;STORE BYTE INTO BUFFER
	INY			;BUMP THE INDEX
EXIT	RTS
;
;	CONDITIONALLY PUT A DECIMAL POINT IN A BUFFER
;
CDPNT	DEC	Z2
	BNE	EXIT		;IF NO POINT TO BE STUFFED
	LDA	#'.'		;ELSE, PUT IT IN
	BNE	STUFF
;
NOEXP	LDX	FCHRFLG
	BNE	NUMEND
	CMP	#'+'
	BEQ	CHRLP0		;IF LEADING '+', IGNORE IT
	CMP	#'-'
	BNE	NUMEND		;IF LEADING '-', SET NEGATIVE FLAG
	STA	NSIGN
	BEQ	CHRLP0
;
RSTEXP	STX	CIX		;RESTORE POINTER AT BEGINNING OF PARSE TRY
	LDA	#0
	STA	FRX		;REZERO EXPONENT
NUMEND	LDA	EEXP
	CLC
	ADC	#$88
	LDX	DIGRT
	BMI	NODP		;IF NO DECIMAL POINT ADJUSTMENT
	SEC
	SBC	DIGRT		;ELSE, SUBTRACT NUMBER OF DIGITS TO RIGHT OF DP
NODP	LDX	ESIGN		;CHECK EXPONENT SIGN
	BNE	SUBEXP		;IF NEGATIVE, SUBTRACT VALUE
	CLC
	ADC	FRX		;ELSE, ADD VALUE OF EXPONENT TO BASE ONE
	JMP	EXPOK
;
;	REST OF THE BYTE TO ASCII (HEX) CONVERSION ROUTINE
;
CTUHEX	PLA			;RESTORE ORIGINAL BYTE
	LSR	A
	LSR	A
	LSR	A
	LSR	A		;POSITION UPPER 4 BITS FOR CONVERSION
	JMP	HEXDGT		;RETURN THROUGH THE CONVERSION ROUTINE
;
EXP3DG	TXA
	SBC	#100		;TAKE THE 100 OUT OF THE EXPONENT
	TAX
	LDA	#'1'		;PRINT THE LEADING '1'
	BNE	NNEXP
;
SUBEXP	SEC
	SBC	FRX
EXPOK	LSR	A		;CONVERT TO POWERS OF 100
	BCC	EVEXP		;SKIP AROUND TIMES 10
	JSR	SH0L4
EVEXP	LDY	FR0
	BEQ	NORGT
	ADC	#1
	LDX	#FPREC-2
SH0R	LDY	FR0,X
	STY	FR0+1,X
	DEX
	BPL	SH0R
NORGT	LDX	NSIGN
	BEQ	NOXOR
	EOR	#$80
NOXOR	DEC	CIX		;BACK UP OVER THE FINAL (INVALID) CHARACTER
	JMP	IFPE		;STORE EXPONENT AND NORMALIZE
;
NOEXPF	JSR	PRTDGT		;GO PUT ASCII DIGIT STRING INTO BUFFER
	INY
	ORA	#$80
	BNE	PUTSGN		;GO PUT MINUS SIGN IN IF REQUIRED
;
;	RETURN A VALUE OF ASCII ZERO
;
ASCZER	LDA	#$80+'0'	;ZERO, END OF STRING
	BNE	STZERO
;
;	FLOATING POINT TO ASCII CONVERSION (ADDR=D8E6)
;
	ORG	AFP+$E6
FASC	JSR	INITBF		;PUT ADDRESS OF LBUFF INTO INBUFF
	LDA	FR0		;IS THE NUMBER ZERO?
	BEQ	ASCZER		;IF YES, RETURN ASCII ZERO
;
	ASL	A		;GET RID OF SIGN BIT
	SEC
	SBC	#$7E		;DO RANGE CHECK (>0.01 AND < 1E10)
	CMP	#12		;11 POSSIBLE DECIMAL POINTS (0-10)
	BCC	NOEXPF
	SBC	#2		;CONVERT TO SIGNED EXPONENT OF 10
	STA	EEXP		;AND SAVE IT FOR LATER PRINTING
	LDA	#3		;SET CODE FOR XX.XXXXXXXX, BIT0=1, EXP FORMAT
	JSR	PRTDGT
	INY
	LDA	#'E'		;FOLLOW DIGITS WITH AN 'E'
	JSR	STUFF
;
	LDA	#'+'		;ASSUME SIGN IS POSITIVE
	LDX	EEXP
	BPL	NNEXP		;IF EXPONENT IS POSITIVE
	LDA	#0		;COMPUTE ABSOLUTE VALUE OF EXPONENT
	SEC
	SBC	EEXP
	TAX
	LDA	#'-'		;THEN, DISPLAY A '-'
NNEXP	JSR	STUFF		;DISPLAY SIGN
	CPX	#100		;EXPONENT > 100?
	BCS	EXP3DG		;IF SO, GO PRINT 3 DIGIT EXPONENT (1XX)
	TXA			;RESTORE EXPONENT TO ACC
	INY			;ALLOW FOR 10-S DIGIT OF EXPONENT
	LDX	#'0'-1		;SET UP FOR CONVERT-TO-DECIMAL
	SEC
CVDLP	SBC	#10		;SUBTRACT 10 FROM ACC
	INX
	BCS	CVDLP		;AND CONTINUE DOING SO UNTIL CONVERTED
	ADC	#$80+'0'+10	;RESTORE THE LAST 10 SUBTRACTED
	STA	LBUFF,Y		;STUFF IT INTO THE BUFFER
	TXA			;MOVE 10-S DIGIT TO ACC
PUTSGN	STA	LBUFF-1,Y	;AND PUT IT INTO THE BUFFER
	LDA	FR0		;STRIP OFF LEADING ZERO?
	BPL	MINXIT		;IF NOT, ALL IS OK NOW
	DEC	INBUFF		;ELSE, MAKE ROOM FOR MINUS
	LDA	#'-'
STZERO	LDY	#0
	STA	(INBUFF),Y	;PUT MINUS IN THE BUFFER
MINXIT	RTS			;THEN FINALLY, WE EXIT
;
;	COPYRIGHT NOTICE IN THE ROM TEXT
;	COPYRIGHT 1984, C. W. MARSLETT
;
	DB	'CPYRT.1984,MARSLETT'
	SUBTTL 'FLOATING POINT/INTEGER CONVERSION ROUTINES'
;
;	INTEGER TO FLOATING POINT CONTINUATION
;
IFPSU	STY	FR0+5
	LDY	#16		;SIXTEEN BITS IN NUMBER TO BE CONVERTED
IFPS	ASL	FR0+1
	ROL	A		;FIND FIRST 1 BIT
	DEY
	BCS	IFPEN		;EXIT IF FOUND
	BPL	IFPS		;ELSE, CONTINUE LOOPING
	RTS			;ELSE, RETURN ZERO TO CALLER
;
IFPEN	SED
	STA	Z3		;SAVE UPPER BYTE OF INTEGER
IFPL	LDA	FR0+3
	ADC	FR0+3		;DOUBLE FR0 AND AND IN ONE BIT FROM INTEGER
	STA	FR0+3
	LDA	FR0+2
	ADC	FR0+2
	STA	FR0+2
	ROL	FR0+1		;UPPER TWO DIGITS MAY BE ROTATED (NEVER > 9)
	ROL	Z3
	DEY
	BPL	IFPL		;THEN GO BACK TO HANDLE THE NEXT BIT
;
	LDA	#$42		;GENERATE THE EXPONENT
	JMP	IFPE		;THEN STORE, NORMALIZE AND EXIT
;
;	FLOATING POINT TO INTEGER CONTINUATION
;
FPICTU	LDY	#0
	STY	FR0
	TAX
	BEQ	FFRAC		;IF PROPER FRACTION, GO DO ROUNDING
	ASL	A		;ELSE, MULTIPLY BY 16 (NUMBER OF BITS * 2)
	ASL	A
	ASL	A
	ASL	A
	TAX			;PUT INTO X REGISTER
;
CVTLP	ASL	FR0+4		;SHIFT NUMBER LEFT FOR CONVERSION
	ROL	FR0+3
	ROL	FR0+2
	ROL	FR0+1
	BCC	CVTST		;IF NO BIT, DO NO ADD
	LDA	FR0		;FIRST ADD LOW BITS
	ADC	BITTAB,X
	STA	FR0
	TYA			;THEN THE HIGH BITS
	ADC	BITTAB+1,X
	TAY			;STORE BACK INTO Y-REG
CVTST	DEX
	DEX
	BNE	CVTLP		;IF NOT SHIFTED OUT, LOOK AT NEXT BIT
;
FFRAC	LDA	FR0+1		;ROUND OFF FRACTION
	CMP	#$50
	BCC	FFEXIT
	INC	FR0
	BNE	FFEXIT
	INY			;ADD CARRY TO UPPER BYTE
FFEXIT	STY	FR0+1
	CLC
	RTS			;FINALLY, RETURN
;
;
;	INTEGER --> FLOATING POINT ENTRY POINT (ADDR=D9AA)
;
	ORG	AFP+$1AA
IFP	LDA	FR0+1
	LDX	FR0
	STX	FR0+1
	LDY	#0		;ZERO FR0+2 TO FR0+5
	STY	FR0+2
	STY	FR0+3
	STY	FR0+4
	BEQ	IFPSU		;GO FINISH SETUP (DUE TO LACK OF SPACE HERE)
;
FDIF5	LDA	FR0,X
	SBC	FR1+1,Y
	STA	FR0,X
	DEX
	DEY
	BPL	FDIF5
;
FDIF6	DEX
	BMI	FNME		;IF NO CARRY PROP. REQ.
	LDA	FR0+1,X
	SBC	#0
	STA	FR0+1,X
	BCC	FDIF6		;IF CARRY PROP. NOT COMPLETE
	BCS	FNORM		;IF DONE, GO NORMALIZE
;
;	FLOATING POINT NUMBER TO INTEGER CONVERSION (ADDR=D9D2)
;
	ORG	AFP+$1D2
FPI	LDA	FR0		;USED HEAVILY BY BASIC ! ! ! ! ! ! ! ! ! !
	CMP	#$43		;IS IT TOO BIG?
	BCS	ZFR0		;IF SO, EXIT WITH CARRY SET
	SBC	#$3E		;ELSE, SUBTRACT 3F (NOTE CARRY IS CLEARED)
	BCS	FPICTU		;IF CARRY, THEN AT LEAST ROUNDING NEEDED
	BCC	ZFR0		;IF LESS THAN 0.01, RETURN ZERO, NO CARRY
;
;	CONVERSION ROUTINE FOR A SINGLE HEX DIGIT
;
HEXDGT	SEC
	SED
	SBC	#$9		;CONVERSION USES A VERY FUNNY ALGORITHM
	ADC	#'9'
	CLD
	RTS
;
;	FLOATING POINT ADD/SUBTRACT CODE (SPREAD ABOUT YOU SEE)
;
	ORG	AFP+$1E7	;LINE UP ZFR0, ZF1 AND INITBF
FNME	BCS	FNORM
	SEC
	LDX	#FPREC-1
FNM0	LDA	#0
	SBC	FR0,X
	STA	FR0,X
	DEX
	BNE	FNM0
;
	LDA	FR0
	EOR	#$80
IFPE	STA	FR0
FNORM	CLD
	LDY	#FPREC-1
FNML	LDA	FR0+1
	BEQ	FNM2		;IF UNNORMALIZED, GET RID OF LEADING ZEROS
	CLC			;ELSE, CLEAR CARRY AND RETURN
	RTS
;
FNM3	LDX	#1-FPREC
FNM4	LDA	FR0+FPREC+1,X
	STA	FR0+FPREC,X
	INX
	BNE	FNM4
	DEY
	BNE	FNML		;GO BACK AND CHECK NEXT DIGIT PAIR
	BEQ	RZERO		;IF SHIFTED 5 TIMES, THEN = ZERO!
;
FR0BIG	EOR	#$FF		;IF THE OTHER WAY AROUND, COMPUTE 4-DIF
	ADC	#FPREC-2
	BCC	FNML		;IF SECOND NUMBER IS NON-SIGNIFICANT
	TAY
	SED
	LDA	FR0
	EOR	FR1
	BMI	FDIF5
	CLC			;CLEAR CARRY FOR ADD
FADD5	LDA	FR0,X
	ADC	FR1+1,Y
	STA	FR0,X
	DEX
	DEY
	BPL	FADD5
;
FADD6	DEX
	BMI	FHTST		;IF NO CARRY PROPOGATE REQUIRED
	LDA	FR0+1,X
	ADC	#0
	STA	FR0+1,X
	BCS	FADD6		;IF CARRY OUT, PROPOGATE TO NEXT BYTE
	BCC	FHTST
;
FNM2	STA	FR0+FPREC
FNMUL	LDA	FR0
	DEC	FR0
	EOR	FR0
	BPL	FNM3
RZERO	CLC			;RETURN A ZERO WITH CARRY CLEAR
;
;	ZERO FR0 (ADDR=DA44)
;
ZFR0	LDX	#FR0		;POINT AT FIRST BYTE TO BE ZEROED
;
;	ZERO ANY (6-BYTE) FLOATING REGISTER ON PAGE 0 (ADDR=DA46)
;
ZF1	LDY	#FPREC
;
;	ZERO ANY PART OR ALL OF PAGE ZERO (ADDR=DA48)
;
ZPG0	LDA	#0
ZF1LP	STA	0,X
	INX
	DEY
	BNE	ZF1LP
	RTS			;THEN RETURN
;
;	POINT INBUFF TO LBUFF (ADDR=DA51)
;
INITBF	LDA	#HIGH[LBUFF]	;COPY ADDRESS OF LBUFF INTO INBUFF
	STA	INBUFF+1
	LDA	#LOW[LBUFF]
	STA	INBUFF
	RTS
;
;	SHIFT 2-BYTE POINTER LEFT 1 BIT
;
DBLZ2	ASL	Z2+1		;SHIFT 0 INTO LOW BIT OF SECOND BYTE
	ROL	Z2		;SHIFT HIGH BYTE INTO LOW BIT OF FIRST
	RTS			;THEN RETURN
	SUBTTL 'ADD/SUBTRACT ROUTINES'
	ORG	AFP+$260
;
;	FLOATING SUBTRACT ENTRY POINT (ADDR=DA60)
;
FSUB	LDA	FR1
	EOR	#$80		;FLIP THE SECOND ARGUMENT-S SIGN
	STA	FR1		;BEFORE DOING AN ADD!
;
;	FLOATING ADD ENTRY POINT (ADDR=DA66)
;
FADD	LDX	#FPREC-1	;GET THE NUMBER OF MANTISSA BYTES INTO X
	LDA	FR1		;GET THE EXPONENT OF THE SECOND ARGUMENT
	AND	#$7F		;STRIP OFF THE SIGN BIT
	STA	Z2		;SAVE IT FOR EXPONENT COMPARE
	LDA	FR0		;GET THE EXPONENT OF THE FIRST ARGUMENT
	AND	#$7F		;STRIP OFF ITS SIGN BIT
	SEC
	SBC	Z2		;SUBTRACT SECOND EXPONENT
	BPL	FR0BIG		;IF THE FIRST IS GREATER, GO ON WITH IT
	ADC	#FPREC-1	;SEE IF THE FIRST IS EVEN SIGNIFICANT
	TAY			;STORE NUMBER OF SIGNIFICANT BYTES
	SED			;GO TO DECIMAL MODE FOR ARITHMETIC
	LDA	FR0		;COMPUTE REAL OPERATION (XOR OF SIGNS)
	EOR	FR1
	BMI	FDIF0		;IF DIFFERENT, THIS IS REALLY A SUBTRACT
	CLC			;CLEAR CARRY
	DEY			;Y=0?
	BMI	FADD1		;IF SO, SKIP ADD
FADD0	LDA	FR0+1,Y		;ELSE, ADD THE TWO SELECTED BYTES
	ADC	FR1,X
	STA	FR0,X
	DEX
	DEY
	BPL	FADD0		;AND LOOP UNTIL ALL ARE DONE
;
FADD1	LDA	FR1,X		;THEN PROPOGATE CARRY TO TOP OF LARGER ARGUMENT
	ADC	#0
	STA	FR0,X
	DEX
	BNE	FADD1
;
	LDA	FR1		;COPY EXPONENT FROM SECOND TO FIRST ARG
	STA	FR0
FHTST	BCC	FADDX		;IF NO CARRY, EXIT (ADD COMPLETE!)
	LDX	#FPREC-2	;ELSE, SHIFT MANTISSA RIGHT 1 BYTE
FADD2	LDA	FR0,X
	STA	FR0+1,X
	DEX
	BNE	FADD2		;UNTIL ENTIRE BYTE IS MOVED OVER
	LDA	#1		;PUT A '1' IN THE HIGH MANTISSA BYTE
	STA	FR0+1
	LDA	FR0		;THEN INCREMENT THE EXPONENT
	INC	FR0
	EOR	FR0		;SEE IF OVERFLOW (SIGN CHANGE FROM INCR.!)
	ROL	A
FADDX	CLD			;RETURN TO BINARY MODE
	RTS			;THEN RETURN (OV==>CY SET)
;
FDIF0	SEC			;SET THE CARRY FLAG
	DEY
	BMI	FDIF2		;SKIP SUBTRACT PHASE IF NO SIGNIFICANT BYTES
FDIF1	LDA	FR1,X		;SUBTRACT FR0 FROM FR1*10^N STORING IN FR0
	SBC	FR0+1,Y
	STA	FR0,X
	DEX
	DEY
	BPL	FDIF1
;
FDIF2	LDA	FR1,X
	SBC	#0
	STA	FR0,X		;PROPOGATE CARRIES
	DEX
	BNE	FDIF2
	LDA	FR1		;THEN COPY EXPONENT TO RESULT
	STA	FR0
	JMP	FNME		;AND DO NORMALIZATION
	SUBTTL 'FLOATING POINT (DECIMAL) MULTIPLY'
;
;	FLOATING (DECIMAL) MULTIPLY ROUTINE
;
	ORG	AFP+$2D5	;FORCE ADDRESS OF ROUTINE TO MATCH ATARI-S
NOOVFL	LDX	#FPREC-1	;SET UP MAJOR LOOP COUNTER FOR EITHER MUL/DIV
	STX	Z3
	SED			;DECIMAL MODE FOR ADD LOOPS
	RTS			;NOTE THAT X IS ALSO SET UP AS LOOP COUNTER TOO
;
FMUL	LDA	FR1
	BEQ	JZERO
	LDA	FR0
	BEQ	FDXIT		;IF ZERO FR0, JUST RETURN
;
	TAX			;SAVE FOR LATER ERROR CHECK
	SEC
	SBC	#$3F		;EXCESS 64 ADJUSTMENT
	CLC
	ADC	FR1
	JSR	MDEXP		;GO CHECK FOR OVER/UNDER-FLOW IN EXPONENT CALC
MOVEL	LDA	FR0,X
	STA	FRE,X
	STA	FR2,X
	LDA	#0		;THEN ZERO FR0 TO ACCUMULATE RESULT
	STA	FR0,X
	DEX
	BNE	MOVEL		;SETUP FOR MULTIPLY LOOP
	STA	FRE		;ZERO HIGH ORDER 2 DIGITS OF BOTH FRE AND FR2
	STA	FR2
	STA	Z3+1
	LDX	#FRE
	JSR	SHL4
	BEQ	LOOPE		;GO DO ACTUAL MULTIPLYING LOOP
;
MDEXP	STA	FR0
	TXA			;COMPUTE SIGN OF RESULT WITH XOR
	EOR	FR1
	EOR	FR0		;THEN SEE IF THE SIGNS ARE THE SAME
	BPL	NOOVFL		;IF NOT, THIS IS OVER/UNDERFLOW
;
	PLA			;FLUSH TOP RETURN ADDRESS
	PLA
	LDA	FR0		;RESTORE OVER(UNDER)FLOWED EXPONENT
	ROL	A		;IS IT OVERFLOW OR UNDERFLOW?
	BMI	JZERO		;IF UNDERFLOW, RETURN A ZERO FOR THE RESULT
OVFLDV	SEC			;IF OVERFLOW, RETURN GARBAGE AND CARRY SET!

	RTS
;
FDXIT	CLC			;RETURN ORIGINAL CONTENTS OF FR0 (ZERO)

	RTS			;WITH NO ERROR INDICATION
;
JZERO	JMP	RZERO		;RESULT IS UNDERFLOW (=0)
;
;
;	FLOATING POINT DIVIDE (DECIMAL) ROUTINE ENTRY POINT
;
	ORG	AFP+$320	;FDIV IS ATARI ENTRY POINT, FLDDIV USED BY REDARG
				;RFCONT IS USED BY LOG10
RFCONT	LDA	#3
	JSR	CTUEVL		;CALCULATE NUMERATOR
FLDDIV	JSR	FLD1P		;LOAD THE DIVISOR FROM LOC POINTED TO BY (FLPTR)
;
FDIV	LDA	FR1		;IS THIS DIVIDE BY ZERO?
	BEQ	OVFLDV		;IF SO, IT IS AN OVERFLOW!
	LDA	FR0
	BEQ	FDXIT
	TAX			;ELSE, COMPUTE RESULTANT EXPONENT
	SEC
	SBC	FR1
	CLC
	ADC	#$40		;EXCESS 64 ADJUSTMENT
	JSR	MDEXP		;GO CHECK FOR EXPONENT OVER OR UNDER FLOW
;
DSETUP	LDA	FR1,X		;LOOP CTR (X) ALREADY SET TO FPREC-1
	STA	FR2,X
	LDA	FR0,X
	STA	FR0+1,X		;PROVIDE A LEADING BYTE OF 0 FOR ALGORITHM
	DEX
	BNE	DSETUP
	STX	FR2
	STX	FR0+1		;STUFF ZERO AFTER SHIFTING FR0 RIGHT
	LDX	#FR2
	JSR	SHL4
	SEC
	LDX	#0-FPREC	;POINTER TO CURRENTLY GENERATED QUOTIENT BYTE
	JMP	SUB10E		;SKIP ZEROING 7-TH BYTE OF NUMBER
;
;	MAIN MULTIPLY LOOP
;
LOOP0	LDA	FR0+5
	STA	Z3+1
	LDA	FR0+4
	STA	FR0+5
	LDA	FR0+3		;SHIFT ACCUMULATED PRODUCT RIGHT ONE BYTE
	STA	FR0+4
	LDA	FR0+2
	STA	FR0+3
	LDA	FR0+1
	STA	FR0+2
	STY	FR0+1		;AND SHIFT IN 00 AT HIGH BYTE
;
LOOPE	LDX	Z3		;LOAD DIGIT PAIR BEING USED
	LDA	FR1,X		;GET NEXT DIGIT PAIR TO MULTIPLY BY
	BEQ	NOONE		;IF ZERO, SKIP BOTH MULTIPLY LOOPS
	LSR	A
	LSR	A
	LSR	A
	LSR	A		;EXTRACT UPPER DIGIT
	BEQ	NOTEN		;IF 10-S DIGIT IS ZERO
	LDX	#FRE		;GO ADD IN FRE-S (MULTIPLIER X 10)
	JSR	DOMULT
;
	LDX	Z3
NOTEN	LDA	FR1,X		;GET 1-S DIGIT FROM BYTE
	AND	#$F
	BEQ	NOONE		;IF ONES DIGIT IS ZERO, DONE!
	LDX	#FR2
	JSR	DOMULT
;
NOONE	DEC	Z3		;DONE WITH ALL DIGITS?
	BNE	LOOP0		;IF NOT, CONTINUE
	LDA	Z3+1		;COPY 6-TH QUOTIENT DIGIT TO FR0
	STA	FR0+6
FNDIV	CLD			;BACK OUT OF DECIMAL MODE
	LDA	FR0+1
	BNE	FDXIT		;IF ALREADY NORMALIZED, EXIT TO CALLER
	LDY	#FPREC		;ELSE, NORMALIZE WITH GUARD DIGIT
	JMP	FNMUL
;
;	LOAD 0.5 INTO FR1
;
GETHALF	LDY	#HIGH[HALF]
	LDX	#LOW[HALF]
	JMP	FLD1R
;
;	SKIP OVER A STRING OF BLANKS IN INPUT BUFFER (ADDR=DBA1)
;
	ORG	AFP+$3A1
SKBLK	LDY	CIX		;SKIP BLANKS IN BUFFER
	LDA	#' '
	BNE	FSTTST		;TEST FIRST CHARACTER
LPBLK	INY
FSTTST	CMP	(INBUFF),Y
	BEQ	LPBLK
	STY	CIX		;UPDATE INDEX INTO BUFFER
	RTS			;FOUND A NON-BLANK CHAR. RETURN!
;
;	GET A CHARACTER (AND TRY TO CONVERT IT TO DECIMAL) (ADDR=DBAF)
;
;
	ORG	AFP+$3AF
GCHAR	LDY	CIX		;GET POINTER INTO BUFFER
	LDA	(INBUFF),Y	;GET BYTE FROM BUFFER
	SEC
	SBC	#'0'		;CONVERT FROM ASCII TO BINARY
	CMP	#10		;SEE IF IT FALLS OFF ON THE OTHER SIDE
	RTS			;CARRY SET==>NOT A DIGIT
;
;	GET THE NEXT CHARACTER IF IT IS NOT A VALID NUMERIC CHARACTER
;
NXTCH	JSR	GETCH		;GET THE CURRENT CHARACTER
	BCC	GDCHR		;IF NUMERIC
	CMP	#'.'
	BEQ	DOTVLD		;DECIMAL, GO SEE IF VALID
	CMP	#'+'
	BEQ	SGNCHK		;PLUS OR MINUS, GO SEE IF VALID
	CMP	#'-'
	BNE	BADCH
;
SGNCHK	LDA	(INBUFF),Y
	CMP	#'.'		;IF FOLLOWED BY A DECIMAL, VALID
	BEQ	GDCHR
DOTVLD	LDA	(INBUFF),Y	;MAKE SURE ACC HAS NEXT CHARACTER IN IT!!
	CMP	#'0'
	BCS	MAYBE		;IF DIGIT FOLLOWS, SIGN IS VALID
;
BADCH	SEC			;IF INVALID, SET CARRY AND RETURN
	RTS
;
;	GET A CHARACTER FROM A TEXT BUFFER
;
GETCH	JSR	GCHAR		;GET A CHARACTER FROM BUFFER
	BCC	UPCIX		;IF DIGIT, UPDATE CIX
	LDA	(INBUFF),Y	;ELSE, RESTORE ACTUAL ASCII CODE
UPCIX	INY
	STY	CIX		;BUMP BOTH CIX AND Y-REG
	RTS			;AND RETURN
;
;	SHIFT FR0 LEFT 4 BIT POSITIONS (ADDR=DBE4)
;
	ORG	AFP+$3E4
SH0L4	LDX	#FR0
SHL4	LDY	#4
SHLM	ASL	5,X
	ROL	4,X
	ROL	3,X
	ROL	2,X
	ROL	1,X
	ROL	0,X
	DEY
	BNE	SHLM
	RTS
;
MAYBE	CMP	#'9'+1
	BCS	BADCH
;
GDCHR	CLC
	DEC	CIX		;ADJUST INDEX BACK (NOT INCREMENTED)
	RTS			;AND RETURN
;
	ORG	AFP+$400	;NORMALIZE ENTRY POINT
NORMAL	JMP	FNORM		;MOVED UP INTO THE FADD/FSUB ROUTINES
;
;
;	DIVISION LOOP (GENERATE RESULTANT FRACTION ONLY)
;
;
FDIVLP	STY	FRE+FPREC,X	;ZERO NEW LOW ORDER BYTE
	BMI	SUB10E		;THEN GO TO LOOP NORMAL ENTRY
;
SUB10N	INY			;ADD ANOTHER 10 TO QUOTIENT LOW BYTE
SUB10E	LDA	FRE+6,X
	SBC	FR2+5
	STA	FRE+6,X
	LDA	FRE+5,X
	SBC	FR2+4
	STA	FRE+5,X
	LDA	FRE+4,X
	SBC	FR2+3
	STA	FRE+4,X
	LDA	FRE+3,X
	SBC	FR2+2
	STA	FRE+3,X
	LDA	FRE+2,X
	SBC	FR2+1
	STA	FRE+2,X
	LDA	FRE+1,X
	SBC	FR2
	STA	FRE+1,X
	BCS	SUB10N		;IF NOT NEGATIVE YET, CONTINUE LOOPING
;
	TYA
	ASL	A
	ASL	A		;SHIFT QUOTIENT DIGIT LEFT 4 BITS
	ASL	A
	ASL	A
	ORA	#10		;THEN ADD IN UNITS DIGIT (FIRST GUESS)
	TAY			;RESTORE TO Y-REG
;
ADD01N	LDA	FRE+6,X
	ADC	FR1+5
	STA	FRE+6,X
	LDA	FRE+5,X
	ADC	FR1+4
	STA	FRE+5,X
	LDA	FRE+4,X
	ADC	FR1+3
	STA	FRE+4,X
	LDA	FRE+3,X
	ADC	FR1+2
	STA	FRE+3,X
	LDA	FRE+2,X
	ADC	FR1+1
	STA	FRE+2,X
	LDA	FRE+1,X
	ADC	#0
	STA	FRE+1,X
	DEY			;DECREMENT THE 1-S DIGIT
	BCC	ADD01N		;AND IF NOT POSITIVE, DO ANOTHER ADD OF DIVISOR
	STY	FRE+1,X		;ELSE, STORE RESULTANT 2 DIGITS
	TAY			;REZERO THE Y-ACCUMULATOR
	INX			;THEN SEE IF DIVIDE COMPLETED
	BMI	FDIVLP		;IF NOT, GO GENERATE NEXT 2 DIGITS
	JMP	FNDIV		;IF SO, GO NORMALIZE RESULT AND EXIT
;
DOMULT	TAY			;SAVE NUMBER OF TIMES TO ADD IN Y-REG
	CLC
MULLP	LDA	Z3+1
	ADC	5,X
	STA	Z3+1
	LDA	FR0+5
	ADC	4,X
	STA	FR0+5
	LDA	FR0+4
	ADC	3,X
	STA	FR0+4
	LDA	FR0+3
	ADC	2,X
	STA	FR0+3
	LDA	FR0+2
	ADC	1,X
	STA	FR0+2
	LDA	FR0+1
	ADC	0,X
	STA	FR0+1
	DEY			;ALL ADDITIONS DONE?
	BNE	MULLP		;IF NOT GO DO THE NEXT ONE
	RTS			;ELSE, RETURN
;
;	TABLE TO CONVERT FROM DECIMAL TO BINARY (BITWISE) QUICKLY
;
BITTAB	=	*-2
	DW	0,1,3,7
	DW	9,19,39,79
	DW	99,199,399,799
	DW	999,1999,3999,7999
	DW	9999,19999,-25537,-1
	DW	-1,-1,-1,-1
	SUBTTL 'GENERALLY USEFUL SUBROUTINES'
;
;	CONVERT PACKED DECIMAL MANTISSA (WITH DECIMAL INFORMATION)
;	   TO AN ASCII (NUMERIC) CHARACTER STRING
;
PRTDGT	LSR	A		;GET NUMBER OF BYTES BEFORE DECIMAL POINT
	ROR	CIX		;SAVE LOW BIT AS E-FORMAT FLAG
	STA	Z2		;SAVE DIGITS BEFORE DECIMAL POINT
	LDX	#1-FPREC	;SET UP BYTE COUNTER FOR A F.P. NUMBER
	LDY	#0		;ZERO BOTH INDICES
	INC	Z2		;OFFSET FIRST DECREMENT
DGTLP	JSR	CDPNT		;CONDITIONALLY PRINT A DECIMAL POINT
	LDA	FR0+FPREC,X
	LSR	A
	LSR	A		;POSITION HIGH 4-BIT DIGIT
	LSR	A
	LSR	A
	JSR	CVASC		;PUT THAT CHARACTER IN THE BUFFER
	LDA	FR0+FPREC,X
	AND	#$F		;EXTRACT SECOND DIGIT
	JSR	CVASC		;AND PUT IT INTO THE BUFFER
	INX			;NEXT DIGIT PAIR
	BMI	DGTLP		;IF NOT FINISHED, PROCESS THEM
;
	JSR	CDPNT		;ELSE, SEE IF TRAILING DECIMAL REQ.
	LDA	#'0'		;CHECK FOR LEADING DECIMAL OR ZERO
	CMP	LBUFF
	BEQ	TRNKZE		;IF ZERO, CLIP IT OFF LEAVING 9 DIGIT RESULT
	BCC	DECSFT		;IF SIGNIFICANT DIGIT, MOVE DECIMAL IF IN E-FORMAT
	DEC	INBUFF		;ELSE, IT IS A DECIMAL, BACK UP AND INSERT A
	STA	LBUFF-1		;LEADING ZERO!
	BCS	ZSCAN		;THEN STRIP OFF TRAILING ZEROS
;
TRNKZE	INC	INBUFF		;STEP TO SECOND DIGIT (WHICH CANNOT BE ZERO!)
	BCS	ZSCAN
;
DECSFT	LDA	CIX		;SEE IF IN E-FORMAT TRANSLATION
	BPL	ZSCAN		;IF NOT, BUFFER IS OK, STRIP OFF TRAILING ZEROS
	LDA	LBUFF+1
	STA	LBUFF+2		;ELSE, MOVE DECIMAL POINT LEFT ONE DIGIT
	LDA	#'.'
	STA	LBUFF+1
	INC	EEXP		;AND ADJUST EXPONENT FOR THE MOVEMENT
;
;	SCAN TO ELIMINATE TRAILING ZEROES
;
ZSCAN	LDY	#11		;START WITH DIGIT 10 (AFTER FOLLOWING 'DEC')
ZSCNLP	DEY
	BEQ	LSTCHR		;IF FIRST CHARACTER, THEN USE 1 CHARACTER
	LDA	LBUFF,Y		;GET THE LAST CHARACTER
	CMP	#$30		;IS IT A TRAILING ZERO?
	BEQ	ZSCNLP		;IF SO, CONTINUE SCAN TIL NONZERO OR FIRST CHARACTER
	CMP	#'.'		;IF NOT, IS IT A DECIMAL POINT?
	BNE	TRXIT		;NO, THEN THIS IS THE LAST CHARACTER
	DEY			;YES, THEN DIGIT BEFORE IT IS THE LAST CHARACTER
LSTCHR	LDA	LBUFF,Y		;LOAD IT AND RETURN TO CALLER
TRXIT	RTS
	SUBTTL 'POLYNOMIAL EVALUATION ROUTINE'
	ORG	AFP+$522
RSTARG	LDX	#FPREC-1	;COPY PLYARG TO FR1
RSTALP	LDA	PLYARG,X
	STA	FR1,X
	DEX
	BPL	RSTALP
	RTS
;
LDARG	LDX	#FPREC-1
LDALP	LDA	PLYARG,X
	STA	FR0,X
	DEX
	BPL	LDALP
	RTS
;
PLYONE	JSR	SAVARG		;SAVE ARGUMENT AND POLYNOMIAL ADDRESS
CTUONE	STA	CIX		;SAVE ORDER OF POLYNOMIAL
	BNE	PLYADD		;ENTER LOOP WITH AN ADD
	RTS			;IF A=0, VALUE IS ARGUMENT
;
;	POLYNOMIAL EVALUATION ENTRY (ADDR=DD40)
;
PLYEVL	JSR	SAVARG		;SAVE FR0, X, Y AS ARGUMENTS
CTUEVL	STA	CIX		;SEE IF MORE THAN 1 TERM
	JSR	LDPLY		;LOAD FIRST COEFFICIENT
	DEC	CIX
	BNE	PLYENT		;IF OK, GO DO MULTIPLY AND ADD
	RTS			;ELSE, JUST RETURN
;
PLYLP	DEC	CIX		;END OF POLYNOMIAL?
	BEQ	PLYXIT		;YES, EXIT
	JSR	RSTARG		;RESTORE ARGUMENT
PLYENT	JSR	FMUL		;ARGUMENT TIMES PARTIAL SUM
	BCS	PLYXIT		;EXIT IF OVERFLOW HERE!
PLYADD	JSR	LDPLY		;LOAD NEXT COEFFICIENT IN FR1
	JSR	FADD		;ADD NEXT COEFFICIENT
	BCC	PLYLP
PLYXIT	RTS			;ELSE, RETURN
;
LDPLY	LDY	#FPREC-1	;INDEX BY Y SINCE INDIRECT REFERENCE
LDPLYL	LDA	(FPTR2),Y	;GET ARGUMENT BYTE
	STA	FR1,Y		;STORE IN FR1
	DEY
	BPL	LDPLYL		;DO ALL 6 BYTES
	CLC			;THEN ADVANCE THE POINTER
	LDA	FPTR2
	ADC	#FPREC		;BY THE LENGTH OF 1 FLOATING POINT NUMBER
	STA	FPTR2
	BCC	LDPXIT		;EXIT IF NO CARRY
	INC	FPTR2+1		;ELSE, BUMP UPPER BYTE
LDPXIT	RTS			;AND RETURN
;
SAVARG	STY	FPTR2+1
	STX	FPTR2		;STORE ADDRESS OF POLYNOMIAL
PUTARG	TAY			;SAVE ACC
	LDX	#FPREC-1	;COPY FR0 TO PLYARG
SAVALP	LDA	FR0,X
	STA	PLYARG,X
	DEX
	BPL	SAVALP		;LOOP UNTIL ALL 6 BYTES MOVED
	TYA			;RESTORE ACC
	RTS
	SUBTTL 'MEMORY TO MEMORY MOVE SUBROUTINES'
;
;	LOAD FLOATING REGISTER 0
;
	ORG	AFP+$589
FLD0R	STY	FLPTR+1		;STORE (Y,X) AS ADDRESS
	STX	FLPTR
FLD0P	LDY	#FPREC-1
FLD0L	LDA	(FLPTR),Y
	STA	FR0,Y
	DEY
	BPL	FLD0L		;COPY ALL BYTES OF THE NUMBER
	RTS
;
;	LOAD FLOATING REGISTER 1
;
	ORG	AFP+$598
FLD1R	STY	FLPTR+1		;STORE (Y,X) AS ADDRESS
	STX	FLPTR
FLD1P	LDY	#FPREC-1
FLD1L	LDA	(FLPTR),Y
	STA	FR1,Y
	DEY
	BPL	FLD1L		;COPY ALL BYTES
	RTS
;
;	STORE FLOATING REGISTER 0
;
FST0R	STY	FLPTR+1		;STORE (Y,X) AS ADDRESS OF TARGET
	STX	FLPTR
FST0P	LDY	#FPREC-1
FST0L	LDA	FR0,Y
	STA	(FLPTR),Y
	DEY
	BPL	FST0L		;LOOP UNTIL ENTIRE FIELD IS STORED
	RTS
;
;	MOVE FR0 TO FR1
;
	ORG	AFP+$5B6
FMOVE	LDX	#FPREC-1
FMOVL	LDA	FR0,X
	STA	FR1,X
	DEX
	BPL	FMOVL
OVFEXP	RTS
	SUBTTL 'EXPONENTIAL FUNCTIONS'
;
;	BASE e EXPONENTIAL FUNCTION
;
	ORG	AFP+$5C0
EXP	LDY	#HIGH[INVL10]
	LDX	#LOW[INVL10]
	JSR	FLD1R		;MULTIPLY ARGUMENT TO CONVERT FUNCTION TO EXP10
	JSR	FMUL
	BCS	OVFEXP		;IF OVERFLOW, RETURN CARRY SET
;
;	BASE 10 EXPONENTIAL FUNCTION
;
EXP10	JSR	FMOVE		;SAVE UNMODIFIED ARGUMENT (PRECISION REQ.)
	ASL	FR0
	LSR	FR0		;STRIP OFF SIGN BIT
	JSR	FPI		;CONVERT TO INTEGER
	SEC			;SAVE A BYTE BY SETTING CARRY BEFORE TEST
	LDA	FR1		;SEE WHAT SIGN WAS
	BPL	PLSEXP		;IF EXPONENT IS ALREADY POSITIVE
	LDA	#128
	SBC	FR0		;NEGATE RESULT OF IFP CALL IF NOT
	STA	Z1+1
	LDA	#0
	SBC	FR0+1
	BEQ	EXPRV
	JMP	RZERO
;
PLSEXP	LDA	FR0
	ADC	#127		;SEE IF EXPONENT IS IN RANGE
	STA	Z1+1
	BCS	OVFEXP		;IF EXPONENT BETWEEN 128 AND 255
	SEC			;RETURN ERROR
	LDA	FR0+1		;IS THE EXPONENT BETWEEN -128 AND 127?
	BNE	OVFEXP		;<-128 OR > 255
EXPRV	STA	Z1		;MARK RANGE AS UNSHIFTED
	JSR	IFP		;BACK TO FLOATING POINT
	ASL	FR0		;GET RID OF INTEGER SIGN (ALWAYS +)
	LDA	#$7F		;COPY COMPLIMENT OF SIGN OF FR1 TO CY
	CMP	FR1
	ROR	FR0		;PUT NEW SIGN BACK INTO FR0
	JSR	FADD		;GENERATE FRACTIONAL PART OF EXPONENT
;
	LDA	FR0		;SEE IF FRACTION > 0.25
	AND	#$7F
	CMP	#$3F		;IF EXPONENT NOT 3F THEN ITS NOT
	BNE	EVALG
	LDA	FR0+1		;ELSE, LOOK AT UPPER 2 DIGITS
	CMP	#$25
	BCC	EVALG		;IF < 25 THEN FRACTION IS OK
	STA	Z1		;MARK AS NEEDING ADJUSTMENT
	JSR	GETHALF
	LDA	FR0
	ASL	FR1
	ASL	A
	ROR	FR1		;COPY SIGN TO 0.5 LOADED INTO FR1
	BPL	NODEC
	DEC	Z1+1
NODEC	JSR	FSUB
;
EVALG	LDY	#HIGH[FSCR]	;SAVE V (REDUCED ARGUMENT) IN FSCR
	LDX	#LOW[FSCR]
	JSR	FST0R
	JSR	FMOVE
	JSR	FMUL		;SQUARE V
	LDY	#HIGH[EXPPLY]
	LDX	#LOW[EXPPLY]	;GENERATE P(V^2)
	LDA	#2		;2 TERM POLYNOMIAL
	JSR	PLYEVL
	JSR	FLD1P		;GET ORIGINAL ARGUMENT
	JSR	FMUL		;COMPUTE V*P(V^2)
	JSR	FST0P		;SAVE RESULT
	JSR	LDARG		;COPY ARG TO FR0
	LDA	#2
	JSR	CTUONE		;CONTINUE (ARG AND POLY PTR BOTH RETAINED)
	JSR	FLD1P		;GET NUMERATOR
	JSR	FSUB		;COMPUTE DENOMINATOR (Q-V*P)
	JSR	FMOVE		;PUT IN FR1
	JSR	FLD0P		;GET NUMERATOR
	JSR	FDIV
	JSR	GETHALF
	JSR	FADD
	JSR	FMOVE		;THEN DOUBLE RESULT
	JSR	FADD
	LDA	Z1		;SEE IF ADJUSTMENT REQUIRED
	BEQ	NOSQR
	JSR	LDPMUL		;ELSE, LOAD FR1 WITH SQR(10)
NOSQR	LSR	Z1+1		;THEN SEE IF *10 REQUIRED
	BCC	NOSFT		;IF NOT, SKIP TO EXPONENT BUILD
	LDY	#HIGH[C10]
	LDX	#LOW[C10]
	JSR	FLD1R		;MULTIPLY RESULT BY 10
	JSR	FMUL
NOSFT	CLC
	LDA	FR0
	ADC	Z1+1		;ADD EXPONENT TO ORIGINAL
	SBC	#$3F
	STA	FR0		;GENERATED EXP(X) COMPLETE!
	CLC			;TURN OFF CARRY (RANGE ERROR) FLAG
OVFLOG	RTS
;
;	REDUCE AN ARGUMENT WITH THE FORMULA REDARG=(ARG-K)/(ARG+K)
;	(ADDR=DE95)
;
	ORG	AFP+$68D
LOGRED	BCS	RARG1		;IF SO, SKIP MULTIPLY BY 10
	DEC	Z1
	STX	FR1+1		;CHANGING CONSTANT TO 0.05
	BCC	RARG1
;
REDARG	JSR	FLD1R		;GET K FROM MEMORY INTO FR1
RARG1	JSR	PUTARG		;SAVE UNREDUCED ARGUMENT IN PLYARG
	JSR	FADD		;COMPUTE ARG+K
	LDY	#HIGH[FSCR]	;SAVE DENOMINATOR IN FSCR
	LDX	#LOW[FSCR]
	JSR	FST0R
	JSR	LDARG		;RESTORE UNREDUCED ARGUMENT TO FR0
	JSR	FSUB
	JMP	FLDDIV
	SUBTTL 'LOGRITHMIC FUNCTIONS'
	ORG	AFP+$6AF	;PUT RIGHT BEFORE THE LOG ENTRY POINT
EXPPLY	DB	$40,$08,$70,$38,$83,$65
	DB	$40,$69,$01,$09,$31,$26
;
	DB	$40,$34,$04,$40,$20,$67
	DB	$40,$59,$94,$21,$33,$27
;
SQR10	DB	$40,$03,$16,$22,$77,$66
;
;	LOGRITHM FUNCTIONS (BASE e, ADDR=DECD)
;
	ORG	AFP+$6CD	;ATARI DEFINED ENTRY POINT
LOG	LDA	#$80		;SET FLAG TO INDICATE THIS IS NOT BASE 10 LOG
	BNE	LOGS
;
;	LOGRITHM FUNCTIONS (BASE 10, ADDR=DED1)
;
LOG10	LDA	#0
LOGS	STA	Z1+1		;STORE FLAG TO INDICATE WHICH CLASS OF LOGS
	LDA	FR0
	ASL	A
	BCS	OVFLOG		;IF LOG OVERFLOW (NEGATIVE ARG.)
	SBC	#$7D
	STA	Z1		;SAVE RANGE DATA
	LDA	FR0+1		;IS NUMBER > 0.1?
	CMP	#$10
	BCS	NOLSFT		;IF SO, SHIFT DENOMINATOR RIGHT 1 DIGIT
	DEC	Z1		;UPDATE EXPONENT FOR LEADING ZERO ELIMINATED
	JSR	SH0L4		;ELSE, SHIFT NUMERATOR LEFT 1 DIGIT
NOLSFT	LDA	#$3F		;INITIAL VALUE FOR EXPONENT
	STA	FR0
	JSR	GETHALF		;REDUCTION CONSTANT = 0.50
	JSR	FMUL		;HALVE ARGUMENT
	LDA	FR0+1
	LDX	#$05
	CMP	#$16		;IS THE REDUCED ARGUMENT > 0.32 (SQR(0.1))
;
	JSR	LOGRED		;REDUCE ARGUMENT VIA Z=(X-1)/(X+1)
	LDX	FR0
	BPL	LNORM
	INC	FR0+4-$BF,X		;FORCE NEGATIVE ROUNDING
	INC	FR0+4-$BF,X
LNORM
	JSR	FST0P		;THEN STORE Z IN 'FSCR'
	JSR	FMOVE
	JSR	FMUL		;SQUARE IT
	LDY	#HIGH[LOGPLY]
	LDX	#LOW[LOGPLY]
	LDA	#3
	JSR	PLYONE		;CALCULATE DENOMINATOR OF RAT. FRACTION
	LDX	#LOW[FSCR1]
	JSR	FST0R+2		;NOTE THAT HIGH BYTE OF FLPTR IS UNCHANGED
	JSR	LDARG		;PUT ARGUMENT BACK INTO FR0
	JSR	RFCONT		;CALC. DENOM. & R(Z), THE RAT. FRAC.
;
	JSR	RSTARG		;THEN MULTIPLY BY 'Z'
	JSR	FMUL
	JSR	LDPLY
	JSR	FADD
	LDX	#LOW[FSCR]
	JSR	FLD1R+2		;NOTE THAT HIGH BYTE OF FLPTR IS UNCHANGED
	JSR	FMUL
	JSR	PUTARG
	INX			;CONVERT X (FROM FMOVE) TO ZERO
	STX	FR0+1		;ZERO HIGH BYTE OF INTEGER (EXPONENT)
	LDA	Z1
	BPL	INTPRT		;IF EXPONENT IS NEGATIVE, THEN GET ABSOLUTE VALUE
	SEC
	TXA			;ZERO ACC
	SBC	Z1		;SUBTRACT FROM 0 (NEGATE)
INTPRT	STA	FR0		;STORE Z1 OR -Z1 AS APPROPRIATE
	JSR	IFP
	ASL	FR0		;NEXT, CHANGE SIGN OF RESULT IF NEGATIVE
	ASL	Z1
	ROR	FR0
	JSR	GETHALF
	JSR	FMUL
	JSR	FMOVE
	JSR	LDARG
;
	JSR	FADD		;ADD INTEGER TO FRACTION
	JSR	FADD
	LDA	Z1+1
	BEQ	LOG10E		;IF LOG BASE 10
LDPMUL	JSR	LDPLY		;ELSE, MULTIPLY RESULT BY LN(10)
	JMP	FMUL
;
;	CONSTANTS USED IN THE FLOATING POINT PACKAGE
;
	ORG	AFP+$76C	;REFERENCED BY BASIC SIN/COS ROUTINE
HALF	DB	$3F,$50,$00,$00,$00,$00
;
;	COEFFICIENTS USED IN THE LOG POLYNOMIALS
;
LOGPLY	DB	$C0,$08,$19,$08,$00,$45	; -8.19080045
	DB	$40,$16,$96,$69,$81,$40; 16.96698140
	DB	$C0,$10,$07,$04,$06,$95	;-10.07040695
;
LOG10E	=	*+4	;RETURN INSTRUCTION
	DB	$BF,$67,$35,$81,$60,$15;-0.6735816015
	DB	$40,$03,$16,$30,$34,$92	; 3.16303492
	DB	$C0,2,$91,$56,$81,$44	;-2.91568144
;
	DB	$3F,$86,$85,$88,$96,$38;  0.8685889638
LN10	DB	$40,$2,$30,$25,$85,$9;  2.30258509
;
INVL10	DB	$3F,$43,$42,$94,$48,$19
;
C10	DB	$40,$10,$00,$00,$00,$00
;
;	POLYNOMIAL FOR SIN/COS FUNCTIONS (11 COEFFICIENTS)
;
	ORG	AFP+$7AE
PLYSIN	DB	$3E,$16,$05,$44,$49,$00 ;REF BY BASIC SIN/COS ROUTINES
	DB	$BE,$95,$68,$38,$45,$00
	DB	$3F,$02,$68,$79,$94,$16
	DB	$BF,$04,$92,$78,$90,$80
	DB	$3F,$07,$03,$15,$20,$00
	DB	$BF,$08,$92,$29,$12,$44
	DB	$3F,$11,$08,$40,$09,$11
	DB	$BF,$14,$28,$31,$56,$04
	DB	$3F,$19,$99,$98,$77,$44
	DB	$BF,$33,$33,$33,$31,$13
NONE	DB	$3F,$99,$99,$99,$99,$99 ;ALMOST EQUAL TO 1.0 (USED FOR ROUNDOFF PROBLEM)
;
;	SIN OF 45 DEG.
;
SIN45	DB	$3F,$78,$53,$98,$16,$34
;
;	CONVERT A BYTE TO TWO HEX DIGITSIN (A,X)
;
CVTHEX	PHA			;SAVE UPPER DIGIT
	AND	#$0F
	JSR	HEXDGT		;CONVERT LOWER DIGIT
	TAX
	JMP	CTUHEX		;RESTORE AND CONVERT UPPER DIGIT
	END

FLOATING POINT ROM ENTRY POINTS#

           FLOATING POINT ROM ENTRY POINTS

Entry   Address  Function
Point
AFP       D800   Extract a floating point number from an ASCII
                     string (FR0<--[INBUFF+CIX])
FASC      D8E6   Convert a floating point number to an ASCII
                     string (returns pointer in INBUFF)
IFP       D9AA   Convert a 2-byte integer (FR0) to a floating
                     point number (in FR0)
FPI       D9D2   Convert a floating point number to an integer
ZFR0      DA44   Zero the floating point register (FR0)
ZF1       DA46   Zero 6 zero page bytes pointed to by "X"
ZPG0      DA48   Zero "Y" bytes pointed to by "X"
INITBF    DA51   Store the address of LBUFF in INBUFF
DBLZ2     DA5A   Shift Z2 left one bit (16-bit High/Low format)
FSUB      DA60   Subtract FR1 from FR0
FADD      DA66   Add FR1 to FR0 (result in FR0)
FMUL      DADB   Multiply FR0 by FR1 (result in FR0)
FDIV      DB28   Divide FR0 by FR1 (result in FR0)
SKBLK     DBA1   Skip 0 or more blanks pointed to by [INBUFF,CIX],
                     result modifies CIX
GCHAR     DBAF   Read a byte, convert it as an ASCII decimal
                     number, set CY if it is not
SH0L4     DBE4   Shift the contents of FR0 left 4 bits
NORMAL    DC00   Normalize the contents of FR0
PLYEVL    DD40   Evaluate the polynomial [Y.X](FR0), with A terms
FLD0R     DD89   Load FR0 from [Y.X]
FLD0P     DD8D   Load FR0 from [FLPTR]
FLD1R     DD98   Load FR1 from [Y.X]
FLD1P     DD9C   Load FR1 from [FLPTR]
FST0R     DDA7   Store FR0 into 6 bytes pointed to by [Y.X]
FST0P     DDAB   Store FR0 into [FLPTR]
FMOVE     DDB6   Copy FR0 over into FR1
EXP       DDC0   FR0 <-- exponential function of FR0
EXP10     DDCC   FR0 <-- 10 ^ FR0 (exponent of 10)
REDARG    DE95   FR0 <-- (FR0-[Y.X])/(FR0+[Y.X])
LOG       DECD   FR0 <-- natural log of FR0
LOG10     DED1   FR0 <-- base 10 log of FR0
HALF      DF6C   Constant = 0.5000000000
PLYSIN    DFAE   11 Constant table for SIN polynomial
NONE      DFEA   Constant = 0.9999999999
SIN45     DFF0   Constant = sin(45 deg.)


          Entry points not in the ATARI "D" ROM

RSTARG    DD22   Copy PLYARG over into FR1
LDARG     DD2D   Copy PLYARG over into FR0
PLYONE    DD38   Same as PLYEVL except implicit 1.0 coefficient
LDPLY     DD62   Copy [FPTR2] into FR1, add 6 to FPTR2
SAVARG    DD78   Store X into FPTR2, Y into FPTR2+1, copy FR0
                     over into PLYARG

Remark#

drac030 from AtariAge found the last remaining thing to be changed in the hexdgt routine: SBC $9 should be changed into -> SBC #$9.