LIB81 DOCUMENTATION

This library exactly replaces Alcyon's libm.  Each entry point
checks to see if the 68881 peripheral is installed, and if it is,
uses it.  If not, it branches to the old Alcyon software routine
(whose name has been changed with a leading X).

The global 'fpexists' holds 0 initially, meaning the 68881 has not been
tested for.  When this is the case, 'fpcheck' is called to see if it
exists.  If it does not, 'fpexists' is set to -1.  If it is, the chip
is initialized and 'fpexists' is set to +1.

Each entry point in this library has the following form:

; example for double-precision multiply, called _dpmult

	.globl	fpcheck, fpexists, _Xdpmult, _dpmult

mustcheck:
	bsr	fpcheck
_dpmult:
	tst.w	fpexists	; 68881 present?
	bmi.w	_Xdpmult	; use software: call old routine
	beq.s	mustcheck	; don't know yet: test & check again
				; else >0: use hardware

	; routine to use hardware goes here

	rts

Note that this is encapsulated in a macro, fpinit, which takes as
its argument the base name of the procedure (in this case, dpmult).

All the routines use a couple of macros: fpidle, which waits for
the hardware chip to be idle (status $0802), and ckcir, which
waits for the chip to present a certain value as its status.
These two macros take care of the chip interface; the only other
thing to do is move commands and data to the chip and back.

Here is the macro header file, in full, and following that the
source file, in full, for the procedure 'dpmult':

*
* FPMACRO.S: macro header for all fp modules.
*
* The macro fpidle waits for the chip to be idle.  If it isn't idle
* within $80 loops, we go to the exception handler.
*
* ckcir just reads the status once.  This is always sufficient when
* you waited for the chip to be idle before beginning the dialog.
*
* fpinit is always the first part of a function.  It begins with
* an entry point which is actually above the entry to the function;
* then the entry to the function, and the fpexists flag is checked.
* If negative, _X\name is used; if zero, the pre-fn entry point
* is jumped to, which will check for the existence of the chip.
* If the flag was positive, this will fall through, and the next
* thing in the function (usually fpidle) is hit.
*

FPCIR	equ	$FFFFFA40		; status register.w
FPCTL	equ	$FFFFFA42		; control register.w
FPCMD	equ	$FFFFFA4A		; command register.w
FPOP	equ	$FFFFFA50		; operand register.l

	.text

	.extern	_fpcexp

	.macro	fpidle			; wait for FP to be idle
	moveq	#$7F,d2			; timeout into d2
.I\~:	move.w	FPCIR,d0		; get response
	and.w	#$BFFF,d0		; mask PC
	cmp.w	#$0802,d0		; compare to expected
	dbeq	d2,.I\~			; pass or loop?
	beq.s	.IX\~			; succeed
	bsr	_fpcexp			; else out because of timeout.
.IX\~:
	.endm

	.macro	ckcir	value
	moveq	#$7F,d2			; timeout into d2
.\~:	move.w	FPCIR,d0		; get response
	andi.w	#$BFFF,d0		; mask PC
	cmpi.w	\value,d0		; compare to expected
	dbeq	d2,.\~			; pass or loop?
	beq.s	.1\~			; why are we out?
	bsr	_fpcexp			; jump to execption handler
.1\~:
	.endm


	.macro	fpinit	name

	.globl	fpcheck, fpexists, _X\name, _\name
mustcheck:
	bsr	fpcheck			; .. and fall through to test flag.

_\name:
	tst.w	fpexists		; test flag
	bmi	_X\name			; minus means use SW,
	beq.s	mustcheck		; zero means must check,
					; else fall through to hw code
	.endm

*****************************************************************************

*** dpmult.s
* 	Double Precision Multiply
*	for Alcyon C
*
*	double dpmult(d1,d2)
*	double d1, d2;
*
*	Copyright Atari Corp. 1987,1988
*
*	MDJohnson       2/15/88
***

	.include fpmacro

; dpmult(x,y)
	fpinit	dpmult
	fpidle				; wait for idle state
	move.w	#$5400,FPCMD		; x -> fp0
	ckcir	#$9608			; ready for double
	move.l	4(sp),FPOP		; write first half
	move.l	8(sp),FPOP		; write second half

	fpidle				; wait for idle state
	move.w	#$5423,FPCMD		; x * y -> fp0
	ckcir	#$9608			; ready for double
	move.l	12(sp),FPOP		; write first half
	move.l	16(sp),FPOP		; write second half

	fpidle				; wait for idle state
	move.w	#$7400,FPCMD		; fp0 -> double
	ckcir	#$B208			; ready for double
	move.l	FPOP,d0			; get double
	move.l	FPOP,d1
	rts

