#include "fpsp-namespace.h"
//
//
//	res_func.sa 3.9 7/29/91
//
// Normalizes denormalized numbers if necessary and updates the
// stack frame.  The function is then restored back into the
// machine and the 040 completes the operation.  This routine
// is only used by the unsupported data type/format handler.
// (Exception vector 55).
//
// For packed move out (fmove.p fpm,<ea>) the operation is
// completed here; data is packed and moved to user memory.
// The stack is restored to the 040 only in the case of a
// reportable exception in the conversion.
//
//
//		Copyright (C) Motorola, Inc. 1990
//			All Rights Reserved
//
//	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF MOTOROLA
//	The copyright notice above does not evidence any
//	actual or intended publication of such source code.

RES_FUNC:    //idnt    2,1 | Motorola 040 Floating Point Software Package

	|section	8

#include "fpsp.defs"

sp_bnds:	.short	0x3f81,0x407e
		.short	0x3f6a,0x0000
dp_bnds:	.short	0x3c01,0x43fe
		.short	0x3bcd,0x0000

	|xref	mem_write
	|xref	bindec
	|xref	get_fline
	|xref	round
	|xref	denorm
	|xref	dest_ext
	|xref	dest_dbl
	|xref	dest_sgl
	|xref	unf_sub
	|xref	nrm_set
	|xref	dnrm_lp
	|xref	ovf_res
	|xref	reg_dest
	|xref	t_ovfl
	|xref	t_unfl

	.global	res_func
	.global 	p_move

res_func:
	clrb	DNRM_FLG(%a6)
	clrb	RES_FLG(%a6)
	clrb	CU_ONLY(%a6)
	tstb	DY_MO_FLG(%a6)
	beqs	monadic
dyadic:
	btstb	#7,DTAG(%a6)	//if dop = norm=000, zero=001,
//				;inf=010 or nan=011
	beqs	monadic		//then branch
//				;else denorm
// HANDLE DESTINATION DENORM HERE
//				;set dtag to norm
//				;write the tag & fpte15 to the fstack
	leal	FPTEMP(%a6),%a0

	bclrb	#sign_bit,LOCAL_EX(%a0)
	sne	LOCAL_SGN(%a0)

	bsr	nrm_set		//normalize number (exp will go negative)
	bclrb	#sign_bit,LOCAL_EX(%a0) //get rid of false sign
	bfclr	LOCAL_SGN(%a0){#0:#8}	//change back to IEEE ext format
	beqs	dpos
	bsetb	#sign_bit,LOCAL_EX(%a0)
dpos:
	bfclr	DTAG(%a6){#0:#4}	//set tag to normalized, FPTE15 = 0
	bsetb	#4,DTAG(%a6)	//set FPTE15
	orb	#0x0f,DNRM_FLG(%a6)
monadic:
	leal	ETEMP(%a6),%a0
	btstb	#direction_bit,CMDREG1B(%a6)	//check direction
	bne	opclass3			//it is a mv out
//
// At this point, only opclass 0 and 2 possible
//
	btstb	#7,STAG(%a6)	//if sop = norm=000, zero=001,
//				;inf=010 or nan=011
	bne	mon_dnrm	//else denorm
	tstb	DY_MO_FLG(%a6)	//all cases of dyadic instructions would
	bne	normal		//require normalization of denorm

// At this point:
//	monadic instructions:	fabs  = $18  fneg   = $1a  ftst   = $3a
//				fmove = $00  fsmove = $40  fdmove = $44
//				fsqrt = $05* fssqrt = $41  fdsqrt = $45
//				(*fsqrt reencoded to $05)
//
	movew	CMDREG1B(%a6),%d0	//get command register
	andil	#0x7f,%d0			//strip to only command word
//
// At this point, fabs, fneg, fsmove, fdmove, ftst, fsqrt, fssqrt, and
// fdsqrt are possible.
// For cases fabs, fneg, fsmove, and fdmove goto spos (do not normalize)
// For cases fsqrt, fssqrt, and fdsqrt goto nrm_src (do normalize)
//
	btstl	#0,%d0
	bne	normal			//weed out fsqrt instructions
//
// cu_norm handles fmove in instructions with normalized inputs.
// The routine round is used to correctly round the input for the
// destination precision and mode.
//
cu_norm:
	st	CU_ONLY(%a6)		//set cu-only inst flag
	movew	CMDREG1B(%a6),%d0
	andib	#0x3b,%d0		//isolate bits to select inst
	tstb	%d0
	beql	cu_nmove	//if zero, it is an fmove
	cmpib	#0x18,%d0
	beql	cu_nabs		//if $18, it is fabs
	cmpib	#0x1a,%d0
	beql	cu_nneg		//if $1a, it is fneg
//
// Inst is ftst.  Check the source operand and set the cc's accordingly.
// No write is done, so simply rts.
//
cu_ntst:
	movew	LOCAL_EX(%a0),%d0
	bclrl	#15,%d0
	sne	LOCAL_SGN(%a0)
	beqs	cu_ntpo
	orl	#neg_mask,USER_FPSR(%a6) //set N
cu_ntpo:
	cmpiw	#0x7fff,%d0	//test for inf/nan
	bnes	cu_ntcz
	tstl	LOCAL_HI(%a0)
	bnes	cu_ntn
	tstl	LOCAL_LO(%a0)
	bnes	cu_ntn
	orl	#inf_mask,USER_FPSR(%a6)
	rts
cu_ntn:
	orl	#nan_mask,USER_FPSR(%a6)
	movel	ETEMP_EX(%a6),FPTEMP_EX(%a6)	//set up fptemp sign for
//						;snan handler

	rts
cu_ntcz:
	tstl	LOCAL_HI(%a0)
	bnel	cu_ntsx
	tstl	LOCAL_LO(%a0)
	bnel	cu_ntsx
	orl	#z_mask,USER_FPSR(%a6)
cu_ntsx:
	rts
//
// Inst is fabs.  Execute the absolute value function on the input.
// Branch to the fmove code.  If the operand is NaN, do nothing.
//
cu_nabs:
	moveb	STAG(%a6),%d0
	btstl	#5,%d0			//test for NaN or zero
	bne	wr_etemp		//if either, simply write it
	bclrb	#7,LOCAL_EX(%a0)		//do abs
	bras	cu_nmove		//fmove code will finish
//
// Inst is fneg.  Execute the negate value function on the input.
// Fall though to the fmove code.  If the operand is NaN, do nothing.
//
cu_nneg:
	moveb	STAG(%a6),%d0
	btstl	#5,%d0			//test for NaN or zero
	bne	wr_etemp		//if either, simply write it
	bchgb	#7,LOCAL_EX(%a0)		//do neg
//
// Inst is fmove.  This code also handles all result writes.
// If bit 2 is set, round is forced to double.  If it is clear,
// and bit 6 is set, round is forced to single.  If both are clear,
// the round precision is found in the fpcr.  If the rounding precision
// is double or single, round the result before the write.
//
cu_nmove:
	moveb	STAG(%a6),%d0
	andib	#0xe0,%d0			//isolate stag bits
	bne	wr_etemp		//if not norm, simply write it
	btstb	#2,CMDREG1B+1(%a6)	//check for rd
	bne	cu_nmrd
	btstb	#6,CMDREG1B+1(%a6)	//check for rs
	bne	cu_nmrs
//
// The move or operation is not with forced precision.  Test for
// nan or inf as the input; if so, simply write it to FPn.  Use the
// FPCR_MODE byte to get rounding on norms and zeros.
//
cu_nmnr:
	bfextu	FPCR_MODE(%a6){#0:#2},%d0
	tstb	%d0			//check for extended
	beq	cu_wrexn		//if so, just write result
	cmpib	#1,%d0			//check for single
	beq	cu_nmrs			//fall through to double
//
// The move is fdmove or round precision is double.
//
cu_nmrd:
	movel	#2,%d0			//set up the size for denorm
	movew	LOCAL_EX(%a0),%d1		//compare exponent to double threshold
	andw	#0x7fff,%d1
	cmpw	#0x3c01,%d1
	bls	cu_nunfl
	bfextu	FPCR_MODE(%a6){#2:#2},%d1	//get rmode
	orl	#0x00020000,%d1		//or in rprec (double)
	clrl	%d0			//clear g,r,s for round
	bclrb	#sign_bit,LOCAL_EX(%a0)	//convert to internal format
	sne	LOCAL_SGN(%a0)
	bsrl	round
	bfclr	LOCAL_SGN(%a0){#0:#8}
	beqs	cu_nmrdc
	bsetb	#sign_bit,LOCAL_EX(%a0)
cu_nmrdc:
	movew	LOCAL_EX(%a0),%d1		//check for overflow
	andw	#0x7fff,%d1
	cmpw	#0x43ff,%d1
	bge	cu_novfl		//take care of overflow case
	bra	cu_wrexn
//
// The move is fsmove or round precision is single.
//
cu_nmrs:
	movel	#1,%d0
	movew	LOCAL_EX(%a0),%d1
	andw	#0x7fff,%d1
	cmpw	#0x3f81,%d1
	bls	cu_nunfl
	bfextu	FPCR_MODE(%a6){#2:#2},%d1
	orl	#0x00010000,%d1
	clrl	%d0
	bclrb	#sign_bit,LOCAL_EX(%a0)
	sne	LOCAL_SGN(%a0)
	bsrl	round
	bfclr	LOCAL_SGN(%a0){#0:#8}
	beqs	cu_nmrsc
	bsetb	#sign_bit,LOCAL_EX(%a0)
cu_nmrsc:
	movew	LOCAL_EX(%a0),%d1
	andw	#0x7FFF,%d1
	cmpw	#0x407f,%d1
	blt	cu_wrexn
//
// The operand is above precision boundaries.  Use t_ovfl to
// generate the correct value.
//
cu_novfl:
	bsr	t_ovfl
	bra	cu_wrexn
//
// The operand is below precision boundaries.  Use denorm to
// generate the correct value.
//
cu_nunfl:
	bclrb	#sign_bit,LOCAL_EX(%a0)
	sne	LOCAL_SGN(%a0)
	bsr	denorm
	bfclr	LOCAL_SGN(%a0){#0:#8}	//change back to IEEE ext format
	beqs	cu_nucont
	bsetb	#sign_bit,LOCAL_EX(%a0)
cu_nucont:
	bfextu	FPCR_MODE(%a6){#2:#2},%d1
	btstb	#2,CMDREG1B+1(%a6)	//check for rd
	bne	inst_d
	btstb	#6,CMDREG1B+1(%a6)	//check for rs
	bne	inst_s
	swap	%d1
	moveb	FPCR_MODE(%a6),%d1
	lsrb	#6,%d1
	swap	%d1
	bra	inst_sd
inst_d:
	orl	#0x00020000,%d1
	bra	inst_sd
inst_s:
	orl	#0x00010000,%d1
inst_sd:
	bclrb	#sign_bit,LOCAL_EX(%a0)
	sne	LOCAL_SGN(%a0)
	bsrl	round
	bfclr	LOCAL_SGN(%a0){#0:#8}
	beqs	cu_nuflp
	bsetb	#sign_bit,LOCAL_EX(%a0)
cu_nuflp:
	btstb	#inex2_bit,FPSR_EXCEPT(%a6)
	beqs	cu_nuninx
	orl	#aunfl_mask,USER_FPSR(%a6) //if the round was inex, set AUNFL
cu_nuninx:
	tstl	LOCAL_HI(%a0)		//test for zero
	bnes	cu_nunzro
	tstl	LOCAL_LO(%a0)
	bnes	cu_nunzro
//
// The mantissa is zero from the denorm loop.  Check sign and rmode
// to see if rounding should have occurred which would leave the lsb.
//
	movel	USER_FPCR(%a6),%d0
	andil	#0x30,%d0		//isolate rmode
	cmpil	#0x20,%d0
	blts	cu_nzro
	bnes	cu_nrp
cu_nrm:
	tstw	LOCAL_EX(%a0)	//if positive, set lsb
	bges	cu_nzro
	btstb	#7,FPCR_MODE(%a6) //check for double
	beqs	cu_nincs
	bras	cu_nincd
cu_nrp:
	tstw	LOCAL_EX(%a0)	//if positive, set lsb
	blts	cu_nzro
	btstb	#7,FPCR_MODE(%a6) //check for double
	beqs	cu_nincs
cu_nincd:
	orl	#0x800,LOCAL_LO(%a0) //inc for double
	bra	cu_nunzro
cu_nincs:
	orl	#0x100,LOCAL_HI(%a0) //inc for single
	bra	cu_nunzro
cu_nzro:
	orl	#z_mask,USER_FPSR(%a6)
	moveb	STAG(%a6),%d0
	andib	#0xe0,%d0
	cmpib	#0x40,%d0		//check if input was tagged zero
	beqs	cu_numv
cu_nunzro:
	orl	#unfl_mask,USER_FPSR(%a6) //set unfl
cu_numv:
	movel	(%a0),ETEMP(%a6)
	movel	4(%a0),ETEMP_HI(%a6)
	movel	8(%a0),ETEMP_LO(%a6)
//
// Write the result to memory, setting the fpsr cc bits.  NaN and Inf
// bypass cu_wrexn.
//
cu_wrexn:
	tstw	LOCAL_EX(%a0)		//test for zero
	beqs	cu_wrzero
	cmpw	#0x8000,LOCAL_EX(%a0)	//test for zero
	bnes	cu_wreon
cu_wrzero:
	orl	#z_mask,USER_FPSR(%a6)	//set Z bit
cu_wreon:
	tstw	LOCAL_EX(%a0)
	bpl	wr_etemp
	orl	#neg_mask,USER_FPSR(%a6)
	bra	wr_etemp

//
// HANDLE SOURCE DENORM HERE
//
//				;clear denorm stag to norm
//				;write the new tag & ete15 to the fstack
mon_dnrm:
//
// At this point, check for the cases in which normalizing the
// denorm produces incorrect results.
//
	tstb	DY_MO_FLG(%a6)	//all cases of dyadic instructions would
	bnes	nrm_src		//require normalization of denorm

// At this point:
//	monadic instructions:	fabs  = $18  fneg   = $1a  ftst   = $3a
//				fmove = $00  fsmove = $40  fdmove = $44
//				fsqrt = $05* fssqrt = $41  fdsqrt = $45
//				(*fsqrt reencoded to $05)
//
	movew	CMDREG1B(%a6),%d0	//get command register
	andil	#0x7f,%d0			//strip to only command word
//
// At this point, fabs, fneg, fsmove, fdmove, ftst, fsqrt, fssqrt, and
// fdsqrt are possible.
// For cases fabs, fneg, fsmove, and fdmove goto spos (do not normalize)
// For cases fsqrt, fssqrt, and fdsqrt goto nrm_src (do normalize)
//
	btstl	#0,%d0
	bnes	nrm_src		//weed out fsqrt instructions
	st	CU_ONLY(%a6)	//set cu-only inst flag
	bra	cu_dnrm		//fmove, fabs, fneg, ftst
//				;cases go to cu_dnrm
nrm_src:
	bclrb	#sign_bit,LOCAL_EX(%a0)
	sne	LOCAL_SGN(%a0)
	bsr	nrm_set		//normalize number (exponent will go
//				; negative)
	bclrb	#sign_bit,LOCAL_EX(%a0) //get rid of false sign

	bfclr	LOCAL_SGN(%a0){#0:#8}	//change back to IEEE ext format
	beqs	spos
	bsetb	#sign_bit,LOCAL_EX(%a0)
spos:
	bfclr	STAG(%a6){#0:#4}	//set tag to normalized, FPTE15 = 0
	bsetb	#4,STAG(%a6)	//set ETE15
	orb	#0xf0,DNRM_FLG(%a6)
normal:
	tstb	DNRM_FLG(%a6)	//check if any of the ops were denorms
	bne	ck_wrap		//if so, check if it is a potential
//				;wrap-around case
fix_stk:
	moveb	#0xfe,CU_SAVEPC(%a6)
	bclrb	#E1,E_BYTE(%a6)

	clrw	NMNEXC(%a6)

	st	RES_FLG(%a6)	//indicate that a restore is needed
	rts

//
// cu_dnrm handles all cu-only instructions (fmove, fabs, fneg, and
// ftst) completely in software without an frestore to the 040.
//
cu_dnrm:
	st	CU_ONLY(%a6)
	movew	CMDREG1B(%a6),%d0
	andib	#0x3b,%d0		//isolate bits to select inst
	tstb	%d0
	beql	cu_dmove	//if zero, it is an fmove
	cmpib	#0x18,%d0
	beql	cu_dabs		//if $18, it is fabs
	cmpib	#0x1a,%d0
	beql	cu_dneg		//if $1a, it is fneg
//
// Inst is ftst.  Check the source operand and set the cc's accordingly.
// No write is done, so simply rts.
//
cu_dtst:
	movew	LOCAL_EX(%a0),%d0
	bclrl	#15,%d0
	sne	LOCAL_SGN(%a0)
	beqs	cu_dtpo
	orl	#neg_mask,USER_FPSR(%a6) //set N
cu_dtpo:
	cmpiw	#0x7fff,%d0	//test for inf/nan
	bnes	cu_dtcz
	tstl	LOCAL_HI(%a0)
	bnes	cu_dtn
	tstl	LOCAL_LO(%a0)
	bnes	cu_dtn
	orl	#inf_mask,USER_FPSR(%a6)
	rts
cu_dtn:
	orl	#nan_mask,USER_FPSR(%a6)
	movel	ETEMP_EX(%a6),FPTEMP_EX(%a6)	//set up fptemp sign for
//						;snan handler
	rts
cu_dtcz:
	tstl	LOCAL_HI(%a0)
	bnel	cu_dtsx
	tstl	LOCAL_LO(%a0)
	bnel	cu_dtsx
	orl	#z_mask,USER_FPSR(%a6)
cu_dtsx:
	rts
//
// Inst is fabs.  Execute the absolute value function on the input.
// Branch to the fmove code.
//
cu_dabs:
	bclrb	#7,LOCAL_EX(%a0)		//do abs
	bras	cu_dmove		//fmove code will finish
//
// Inst is fneg.  Execute the negate value function on the input.
// Fall though to the fmove code.
//
cu_dneg:
	bchgb	#7,LOCAL_EX(%a0)		//do neg
//
// Inst is fmove.  This code also handles all result writes.
// If bit 2 is set, round is forced to double.  If it is clear,
// and bit 6 is set, round is forced to single.  If both are clear,
// the round precision is found in the fpcr.  If the rounding precision
// is double or single, the result is zero, and the mode is checked
// to determine if the lsb of the result should be set.
//
cu_dmove:
	btstb	#2,CMDREG1B+1(%a6)	//check for rd
	bne	cu_dmrd
	btstb	#6,CMDREG1B+1(%a6)	//check for rs
	bne	cu_dmrs
//
// The move or operation is not with forced precision.  Use the
// FPCR_MODE byte to get rounding.
//
cu_dmnr:
	bfextu	FPCR_MODE(%a6){#0:#2},%d0
	tstb	%d0			//check for extended
	beq	cu_wrexd		//if so, just write result
	cmpib	#1,%d0			//check for single
	beq	cu_dmrs			//fall through to double
//
// The move is fdmove or round precision is double.  Result is zero.
// Check rmode for rp or rm and set lsb accordingly.
//
cu_dmrd:
	bfextu	FPCR_MODE(%a6){#2:#2},%d1	//get rmode
	tstw	LOCAL_EX(%a0)		//check sign
	blts	cu_dmdn
	cmpib	#3,%d1			//check for rp
	bne	cu_dpd			//load double pos zero
	bra	cu_dpdr			//load double pos zero w/lsb
cu_dmdn:
	cmpib	#2,%d1			//check for rm
	bne	cu_dnd			//load double neg zero
	bra	cu_dndr			//load double neg zero w/lsb
//
// The move is fsmove or round precision is single.  Result is zero.
// Check for rp or rm and set lsb accordingly.
//
cu_dmrs:
	bfextu	FPCR_MODE(%a6){#2:#2},%d1	//get rmode
	tstw	LOCAL_EX(%a0)		//check sign
	blts	cu_dmsn
	cmpib	#3,%d1			//check for rp
	bne	cu_spd			//load single pos zero
	bra	cu_spdr			//load single pos zero w/lsb
cu_dmsn:
	cmpib	#2,%d1			//check for rm
	bne	cu_snd			//load single neg zero
	bra	cu_sndr			//load single neg zero w/lsb
//
// The precision is extended, so the result in etemp is correct.
// Simply set unfl (not inex2 or aunfl) and write the result to
// the correct fp register.
cu_wrexd:
	orl	#unfl_mask,USER_FPSR(%a6)
	tstw	LOCAL_EX(%a0)
	beq	wr_etemp
	orl	#neg_mask,USER_FPSR(%a6)
	bra	wr_etemp
//
// These routines write +/- zero in double format.  The routines
// cu_dpdr and cu_dndr set the double lsb.
//
cu_dpd:
	movel	#0x3c010000,LOCAL_EX(%a0)	//force pos double zero
	clrl	LOCAL_HI(%a0)
	clrl	LOCAL_LO(%a0)
	orl	#z_mask,USER_FPSR(%a6)
	orl	#unfinx_mask,USER_FPSR(%a6)
	bra	wr_etemp
cu_dpdr:
	movel	#0x3c010000,LOCAL_EX(%a0)	//force pos double zero
	clrl	LOCAL_HI(%a0)
	movel	#0x800,LOCAL_LO(%a0)	//with lsb set
	orl	#unfinx_mask,USER_FPSR(%a6)
	bra	wr_etemp
cu_dnd:
	movel	#0xbc010000,LOCAL_EX(%a0)	//force pos double zero
	clrl	LOCAL_HI(%a0)
	clrl	LOCAL_LO(%a0)
	orl	#z_mask,USER_FPSR(%a6)
	orl	#neg_mask,USER_FPSR(%a6)
	orl	#unfinx_mask,USER_FPSR(%a6)
	bra	wr_etemp
cu_dndr:
	movel	#0xbc010000,LOCAL_EX(%a0)	//force pos double zero
	clrl	LOCAL_HI(%a0)
	movel	#0x800,LOCAL_LO(%a0)	//with lsb set
	orl	#neg_mask,USER_FPSR(%a6)
	orl	#unfinx_mask,USER_FPSR(%a6)
	bra	wr_etemp
//
// These routines write +/- zero in single format.  The routines
// cu_dpdr and cu_dndr set the single lsb.
//
cu_spd:
	movel	#0x3f810000,LOCAL_EX(%a0)	//force pos single zero
	clrl	LOCAL_HI(%a0)
	clrl	LOCAL_LO(%a0)
	orl	#z_mask,USER_FPSR(%a6)
	orl	#unfinx_mask,USER_FPSR(%a6)
	bra	wr_etemp
cu_spdr:
	movel	#0x3f810000,LOCAL_EX(%a0)	//force pos single zero
	movel	#0x100,LOCAL_HI(%a0)	//with lsb set
	clrl	LOCAL_LO(%a0)
	orl	#unfinx_mask,USER_FPSR(%a6)
	bra	wr_etemp
cu_snd:
	movel	#0xbf810000,LOCAL_EX(%a0)	//force pos single zero
	clrl	LOCAL_HI(%a0)
	clrl	LOCAL_LO(%a0)
	orl	#z_mask,USER_FPSR(%a6)
	orl	#neg_mask,USER_FPSR(%a6)
	orl	#unfinx_mask,USER_FPSR(%a6)
	bra	wr_etemp
cu_sndr:
	movel	#0xbf810000,LOCAL_EX(%a0)	//force pos single zero
	movel	#0x100,LOCAL_HI(%a0)	//with lsb set
	clrl	LOCAL_LO(%a0)
	orl	#neg_mask,USER_FPSR(%a6)
	orl	#unfinx_mask,USER_FPSR(%a6)
	bra	wr_etemp

//
// This code checks for 16-bit overflow conditions on dyadic
// operations which are not restorable into the floating-point
// unit and must be completed in software.  Basically, this
// condition exists with a very large norm and a denorm.  One
// of the operands must be denormalized to enter this code.
//
// Flags used:
//	DY_MO_FLG contains 0 for monadic op, $ff for dyadic
//	DNRM_FLG contains $00 for neither op denormalized
//	                  $0f for the destination op denormalized
//	                  $f0 for the source op denormalized
//	                  $ff for both ops denormalized
//
// The wrap-around condition occurs for add, sub, div, and cmp
// when
//
//	abs(dest_exp - src_exp) >= $8000
//
// and for mul when
//
//	(dest_exp + src_exp) < $0
//
// we must process the operation here if this case is true.
//
// The rts following the frcfpn routine is the exit from res_func
// for this condition.  The restore flag (RES_FLG) is left clear.
// No frestore is done unless an exception is to be reported.
//
// For fadd:
//	if(sign_of(dest) != sign_of(src))
//		replace exponent of src with $3fff (keep sign)
//		use fpu to perform dest+new_src (user's rmode and X)
//		clr sticky
//	else
//		set sticky
//	call round with user's precision and mode
//	move result to fpn and wbtemp
//
// For fsub:
//	if(sign_of(dest) == sign_of(src))
//		replace exponent of src with $3fff (keep sign)
//		use fpu to perform dest+new_src (user's rmode and X)
//		clr sticky
//	else
//		set sticky
//	call round with user's precision and mode
//	move result to fpn and wbtemp
//
// For fdiv/fsgldiv:
//	if(both operands are denorm)
//		restore_to_fpu;
//	if(dest is norm)
//		force_ovf;
//	else(dest is denorm)
//		force_unf:
//
// For fcmp:
//	if(dest is norm)
//		N = sign_of(dest);
//	else(dest is denorm)
//		N = sign_of(src);
//
// For fmul:
//	if(both operands are denorm)
//		force_unf;
//	if((dest_exp + src_exp) < 0)
//		force_unf:
//	else
//		restore_to_fpu;
//
// local equates:
	.set	addcode,0x22
	.set	subcode,0x28
	.set	mulcode,0x23
	.set	divcode,0x20
	.set	cmpcode,0x38
ck_wrap:
	| tstb	DY_MO_FLG(%a6)	;check for fsqrt
	beq	fix_stk		//if zero, it is fsqrt
	movew	CMDREG1B(%a6),%d0
	andiw	#0x3b,%d0		//strip to command bits
	cmpiw	#addcode,%d0
	beq	wrap_add
	cmpiw	#subcode,%d0
	beq	wrap_sub
	cmpiw	#mulcode,%d0
	beq	wrap_mul
	cmpiw	#cmpcode,%d0
	beq	wrap_cmp
//
// Inst is fdiv.
//
wrap_div:
	cmpb	#0xff,DNRM_FLG(%a6) //if both ops denorm,
	beq	fix_stk		 //restore to fpu
//
// One of the ops is denormalized.  Test for wrap condition
// and force the result.
//
	cmpb	#0x0f,DNRM_FLG(%a6) //check for dest denorm
	bnes	div_srcd
div_destd:
	bsrl	ckinf_ns
	bne	fix_stk
	bfextu	ETEMP_EX(%a6){#1:#15},%d0	//get src exp (always pos)
	bfexts	FPTEMP_EX(%a6){#1:#15},%d1	//get dest exp (always neg)
	subl	%d1,%d0			//subtract dest from src
	cmpl	#0x7fff,%d0
	blt	fix_stk			//if less, not wrap case
	clrb	WBTEMP_SGN(%a6)
	movew	ETEMP_EX(%a6),%d0		//find the sign of the result
	movew	FPTEMP_EX(%a6),%d1
	eorw	%d1,%d0
	andiw	#0x8000,%d0
	beq	force_unf
	st	WBTEMP_SGN(%a6)
	bra	force_unf

ckinf_ns:
	moveb	STAG(%a6),%d0		//check source tag for inf or nan
	bra	ck_in_com
ckinf_nd:
	moveb	DTAG(%a6),%d0		//check destination tag for inf or nan
ck_in_com:
	andib	#0x60,%d0			//isolate tag bits
	cmpb	#0x40,%d0			//is it inf?
	beq	nan_or_inf		//not wrap case
	cmpb	#0x60,%d0			//is it nan?
	beq	nan_or_inf		//yes, not wrap case?
	cmpb	#0x20,%d0			//is it a zero?
	beq	nan_or_inf		//yes
	clrl	%d0
	rts				//then ; it is either a zero of norm,
//					;check wrap case
nan_or_inf:
	moveql	#-1,%d0
	rts



div_srcd:
	bsrl	ckinf_nd
	bne	fix_stk
	bfextu	FPTEMP_EX(%a6){#1:#15},%d0	//get dest exp (always pos)
	bfexts	ETEMP_EX(%a6){#1:#15},%d1	//get src exp (always neg)
	subl	%d1,%d0			//subtract src from dest
	cmpl	#0x8000,%d0
	blt	fix_stk			//if less, not wrap case
	clrb	WBTEMP_SGN(%a6)
	movew	ETEMP_EX(%a6),%d0		//find the sign of the result
	movew	FPTEMP_EX(%a6),%d1
	eorw	%d1,%d0
	andiw	#0x8000,%d0
	beqs	force_ovf
	st	WBTEMP_SGN(%a6)
//
// This code handles the case of the instruction resulting in
// an overflow condition.
//
force_ovf:
	bclrb	#E1,E_BYTE(%a6)
	orl	#ovfl_inx_mask,USER_FPSR(%a6)
	clrw	NMNEXC(%a6)
	leal	WBTEMP(%a6),%a0		//point a0 to memory location
	movew	CMDREG1B(%a6),%d0
	btstl	#6,%d0			//test for forced precision
	beqs	frcovf_fpcr
	btstl	#2,%d0			//check for double
	bnes	frcovf_dbl
	movel	#0x1,%d0			//inst is forced single
	bras	frcovf_rnd
frcovf_dbl:
	movel	#0x2,%d0			//inst is forced double
	bras	frcovf_rnd
frcovf_fpcr:
	bfextu	FPCR_MODE(%a6){#0:#2},%d0	//inst not forced - use fpcr prec
frcovf_rnd:

// The 881/882 does not set inex2 for the following case, so the
// line is commented out to be compatible with 881/882
//	tst.b	%d0
//	beq.b	frcovf_x
//	or.l	#inex2_mask,USER_FPSR(%a6) ;if prec is s or d, set inex2

//frcovf_x:
	bsrl	ovf_res			//get correct result based on
//					;round precision/mode.  This
//					;sets FPSR_CC correctly
//					;returns in external format
	bfclr	WBTEMP_SGN(%a6){#0:#8}
	beq	frcfpn
	bsetb	#sign_bit,WBTEMP_EX(%a6)
	bra	frcfpn
//
// Inst is fadd.
//
wrap_add:
	cmpb	#0xff,DNRM_FLG(%a6) //if both ops denorm,
	beq	fix_stk		 //restore to fpu
//
// One of the ops is denormalized.  Test for wrap condition
// and complete the instruction.
//
	cmpb	#0x0f,DNRM_FLG(%a6) //check for dest denorm
	bnes	add_srcd
add_destd:
	bsrl	ckinf_ns
	bne	fix_stk
	bfextu	ETEMP_EX(%a6){#1:#15},%d0	//get src exp (always pos)
	bfexts	FPTEMP_EX(%a6){#1:#15},%d1	//get dest exp (always neg)
	subl	%d1,%d0			//subtract dest from src
	cmpl	#0x8000,%d0
	blt	fix_stk			//if less, not wrap case
	bra	add_wrap
add_srcd:
	bsrl	ckinf_nd
	bne	fix_stk
	bfextu	FPTEMP_EX(%a6){#1:#15},%d0	//get dest exp (always pos)
	bfexts	ETEMP_EX(%a6){#1:#15},%d1	//get src exp (always neg)
	subl	%d1,%d0			//subtract src from dest
	cmpl	#0x8000,%d0
	blt	fix_stk			//if less, not wrap case
//
// Check the signs of the operands.  If they are unlike, the fpu
// can be used to add the norm and 1.0 with the sign of the
// denorm and it will correctly generate the result in extended
// precision.  We can then call round with no sticky and the result
// will be correct for the user's rounding mode and precision.  If
// the signs are the same, we call round with the sticky bit set
// and the result will be correct for the user's rounding mode and
// precision.
//
add_wrap:
	movew	ETEMP_EX(%a6),%d0
	movew	FPTEMP_EX(%a6),%d1
	eorw	%d1,%d0
	andiw	#0x8000,%d0
	beq	add_same
//
// The signs are unlike.
//
	cmpb	#0x0f,DNRM_FLG(%a6) //is dest the denorm?
	bnes	add_u_srcd
	movew	FPTEMP_EX(%a6),%d0
	andiw	#0x8000,%d0
	orw	#0x3fff,%d0	//force the exponent to +/- 1
	movew	%d0,FPTEMP_EX(%a6) //in the denorm
	movel	USER_FPCR(%a6),%d0
	andil	#0x30,%d0
	fmovel	%d0,%fpcr		//set up users rmode and X
	fmovex	ETEMP(%a6),%fp0
	faddx	FPTEMP(%a6),%fp0
	leal	WBTEMP(%a6),%a0	//point a0 to wbtemp in frame
	fmovel	%fpsr,%d1
	orl	%d1,USER_FPSR(%a6) //capture cc's and inex from fadd
	fmovex	%fp0,WBTEMP(%a6)	//write result to memory
	lsrl	#4,%d0		//put rmode in lower 2 bits
	movel	USER_FPCR(%a6),%d1
	andil	#0xc0,%d1
	lsrl	#6,%d1		//put precision in upper word
	swap	%d1
	orl	%d0,%d1		//set up for round call
	clrl	%d0		//force sticky to zero
	bclrb	#sign_bit,WBTEMP_EX(%a6)
	sne	WBTEMP_SGN(%a6)
	bsrl	round		//round result to users rmode & prec
	bfclr	WBTEMP_SGN(%a6){#0:#8}	//convert back to IEEE ext format
	beq	frcfpnr
	bsetb	#sign_bit,WBTEMP_EX(%a6)
	bra	frcfpnr
add_u_srcd:
	movew	ETEMP_EX(%a6),%d0
	andiw	#0x8000,%d0
	orw	#0x3fff,%d0	//force the exponent to +/- 1
	movew	%d0,ETEMP_EX(%a6) //in the denorm
	movel	USER_FPCR(%a6),%d0
	andil	#0x30,%d0
	fmovel	%d0,%fpcr		//set up users rmode and X
	fmovex	ETEMP(%a6),%fp0
	faddx	FPTEMP(%a6),%fp0
	fmovel	%fpsr,%d1
	orl	%d1,USER_FPSR(%a6) //capture cc's and inex from fadd
	leal	WBTEMP(%a6),%a0	//point a0 to wbtemp in frame
	fmovex	%fp0,WBTEMP(%a6)	//write result to memory
	lsrl	#4,%d0		//put rmode in lower 2 bits
	movel	USER_FPCR(%a6),%d1
	andil	#0xc0,%d1
	lsrl	#6,%d1		//put precision in upper word
	swap	%d1
	orl	%d0,%d1		//set up for round call
	clrl	%d0		//force sticky to zero
	bclrb	#sign_bit,WBTEMP_EX(%a6)
	sne	WBTEMP_SGN(%a6)	//use internal format for round
	bsrl	round		//round result to users rmode & prec
	bfclr	WBTEMP_SGN(%a6){#0:#8}	//convert back to IEEE ext format
	beq	frcfpnr
	bsetb	#sign_bit,WBTEMP_EX(%a6)
	bra	frcfpnr
//
// Signs are alike:
//
add_same:
	cmpb	#0x0f,DNRM_FLG(%a6) //is dest the denorm?
	bnes	add_s_srcd
add_s_destd:
	leal	ETEMP(%a6),%a0
	movel	USER_FPCR(%a6),%d0
	andil	#0x30,%d0
	lsrl	#4,%d0		//put rmode in lower 2 bits
	movel	USER_FPCR(%a6),%d1
	andil	#0xc0,%d1
	lsrl	#6,%d1		//put precision in upper word
	swap	%d1
	orl	%d0,%d1		//set up for round call
	movel	#0x20000000,%d0	//set sticky for round
	bclrb	#sign_bit,ETEMP_EX(%a6)
	sne	ETEMP_SGN(%a6)
	bsrl	round		//round result to users rmode & prec
	bfclr	ETEMP_SGN(%a6){#0:#8}	//convert back to IEEE ext format
	beqs	add_s_dclr
	bsetb	#sign_bit,ETEMP_EX(%a6)
add_s_dclr:
	leal	WBTEMP(%a6),%a0
	movel	ETEMP(%a6),(%a0)	//write result to wbtemp
	movel	ETEMP_HI(%a6),4(%a0)
	movel	ETEMP_LO(%a6),8(%a0)
	tstw	ETEMP_EX(%a6)
	bgt	add_ckovf
	orl	#neg_mask,USER_FPSR(%a6)
	bra	add_ckovf
add_s_srcd:
	leal	FPTEMP(%a6),%a0
	movel	USER_FPCR(%a6),%d0
	andil	#0x30,%d0
	lsrl	#4,%d0		//put rmode in lower 2 bits
	movel	USER_FPCR(%a6),%d1
	andil	#0xc0,%d1
	lsrl	#6,%d1		//put precision in upper word
	swap	%d1
	orl	%d0,%d1		//set up for round call
	movel	#0x20000000,%d0	//set sticky for round
	bclrb	#sign_bit,FPTEMP_EX(%a6)
	sne	FPTEMP_SGN(%a6)
	bsrl	round		//round result to users rmode & prec
	bfclr	FPTEMP_SGN(%a6){#0:#8}	//convert back to IEEE ext format
	beqs	add_s_sclr
	bsetb	#sign_bit,FPTEMP_EX(%a6)
add_s_sclr:
	leal	WBTEMP(%a6),%a0
	movel	FPTEMP(%a6),(%a0)	//write result to wbtemp
	movel	FPTEMP_HI(%a6),4(%a0)
	movel	FPTEMP_LO(%a6),8(%a0)
	tstw	FPTEMP_EX(%a6)
	bgt	add_ckovf
	orl	#neg_mask,USER_FPSR(%a6)
add_ckovf:
	movew	WBTEMP_EX(%a6),%d0
	andiw	#0x7fff,%d0
	cmpiw	#0x7fff,%d0
	bne	frcfpnr
//
// The result has overflowed to $7fff exponent.  Set I, ovfl,
// and aovfl, and clr the mantissa (incorrectly set by the
// round routine.)
//
	orl	#inf_mask+ovfl_inx_mask,USER_FPSR(%a6)
	clrl	4(%a0)
	bra	frcfpnr
//
// Inst is fsub.
//
wrap_sub:
	cmpb	#0xff,DNRM_FLG(%a6) //if both ops denorm,
	beq	fix_stk		 //restore to fpu
//
// One of the ops is denormalized.  Test for wrap condition
// and complete the instruction.
//
	cmpb	#0x0f,DNRM_FLG(%a6) //check for dest denorm
	bnes	sub_srcd
sub_destd:
	bsrl	ckinf_ns
	bne	fix_stk
	bfextu	ETEMP_EX(%a6){#1:#15},%d0	//get src exp (always pos)
	bfexts	FPTEMP_EX(%a6){#1:#15},%d1	//get dest exp (always neg)
	subl	%d1,%d0			//subtract src from dest
	cmpl	#0x8000,%d0
	blt	fix_stk			//if less, not wrap case
	bra	sub_wrap
sub_srcd:
	bsrl	ckinf_nd
	bne	fix_stk
	bfextu	FPTEMP_EX(%a6){#1:#15},%d0	//get dest exp (always pos)
	bfexts	ETEMP_EX(%a6){#1:#15},%d1	//get src exp (always neg)
	subl	%d1,%d0			//subtract dest from src
	cmpl	#0x8000,%d0
	blt	fix_stk			//if less, not wrap case
//
// Check the signs of the operands.  If they are alike, the fpu
// can be used to subtract from the norm 1.0 with the sign of the
// denorm and it will correctly generate the result in extended
// precision.  We can then call round with no sticky and the result
// will be correct for the user's rounding mode and precision.  If
// the signs are unlike, we call round with the sticky bit set
// and the result will be correct for the user's rounding mode and
// precision.
//
sub_wrap:
	movew	ETEMP_EX(%a6),%d0
	movew	FPTEMP_EX(%a6),%d1
	eorw	%d1,%d0
	andiw	#0x8000,%d0
	bne	sub_diff
//
// The signs are alike.
//
	cmpb	#0x0f,DNRM_FLG(%a6) //is dest the denorm?
	bnes	sub_u_srcd
	movew	FPTEMP_EX(%a6),%d0
	andiw	#0x8000,%d0
	orw	#0x3fff,%d0	//force the exponent to +/- 1
	movew	%d0,FPTEMP_EX(%a6) //in the denorm
	movel	USER_FPCR(%a6),%d0
	andil	#0x30,%d0
	fmovel	%d0,%fpcr		//set up users rmode and X
	fmovex	FPTEMP(%a6),%fp0
	fsubx	ETEMP(%a6),%fp0
	fmovel	%fpsr,%d1
	orl	%d1,USER_FPSR(%a6) //capture cc's and inex from fadd
	leal	WBTEMP(%a6),%a0	//point a0 to wbtemp in frame
	fmovex	%fp0,WBTEMP(%a6)	//write result to memory
	lsrl	#4,%d0		//put rmode in lower 2 bits
	movel	USER_FPCR(%a6),%d1
	andil	#0xc0,%d1
	lsrl	#6,%d1		//put precision in upper word
	swap	%d1
	orl	%d0,%d1		//set up for round call
	clrl	%d0		//force sticky to zero
	bclrb	#sign_bit,WBTEMP_EX(%a6)
	sne	WBTEMP_SGN(%a6)
	bsrl	round		//round result to users rmode & prec
	bfclr	WBTEMP_SGN(%a6){#0:#8}	//convert back to IEEE ext format
	beq	frcfpnr
	bsetb	#sign_bit,WBTEMP_EX(%a6)
	bra	frcfpnr
sub_u_srcd:
	movew	ETEMP_EX(%a6),%d0
	andiw	#0x8000,%d0
	orw	#0x3fff,%d0	//force the exponent to +/- 1
	movew	%d0,ETEMP_EX(%a6) //in the denorm
	movel	USER_FPCR(%a6),%d0
	andil	#0x30,%d0
	fmovel	%d0,%fpcr		//set up users rmode and X
	fmovex	FPTEMP(%a6),%fp0
	fsubx	ETEMP(%a6),%fp0
	fmovel	%fpsr,%d1
	orl	%d1,USER_FPSR(%a6) //capture cc's and inex from fadd
	leal	WBTEMP(%a6),%a0	//point a0 to wbtemp in frame
	fmovex	%fp0,WBTEMP(%a6)	//write result to memory
	lsrl	#4,%d0		//put rmode in lower 2 bits
	movel	USER_FPCR(%a6),%d1
	andil	#0xc0,%d1
	lsrl	#6,%d1		//put precision in upper word
	swap	%d1
	orl	%d0,%d1		//set up for round call
	clrl	%d0		//force sticky to zero
	bclrb	#sign_bit,WBTEMP_EX(%a6)
	sne	WBTEMP_SGN(%a6)
	bsrl	round		//round result to users rmode & prec
	bfclr	WBTEMP_SGN(%a6){#0:#8}	//convert back to IEEE ext format
	beq	frcfpnr
	bsetb	#sign_bit,WBTEMP_EX(%a6)
	bra	frcfpnr
//
// Signs are unlike:
//
sub_diff:
	cmpb	#0x0f,DNRM_FLG(%a6) //is dest the denorm?
	bnes	sub_s_srcd
sub_s_destd:
	leal	ETEMP(%a6),%a0
	movel	USER_FPCR(%a6),%d0
	andil	#0x30,%d0
	lsrl	#4,%d0		//put rmode in lower 2 bits
	movel	USER_FPCR(%a6),%d1
	andil	#0xc0,%d1
	lsrl	#6,%d1		//put precision in upper word
	swap	%d1
	orl	%d0,%d1		//set up for round call
	movel	#0x20000000,%d0	//set sticky for round
//
// Since the dest is the denorm, the sign is the opposite of the
// norm sign.
//
	eoriw	#0x8000,ETEMP_EX(%a6)	//flip sign on result
	tstw	ETEMP_EX(%a6)
	bgts	sub_s_dwr
	orl	#neg_mask,USER_FPSR(%a6)
sub_s_dwr:
	bclrb	#sign_bit,ETEMP_EX(%a6)
	sne	ETEMP_SGN(%a6)
	bsrl	round		//round result to users rmode & prec
	bfclr	ETEMP_SGN(%a6){#0:#8}	//convert back to IEEE ext format
	beqs	sub_s_dclr
	bsetb	#sign_bit,ETEMP_EX(%a6)
sub_s_dclr:
	leal	WBTEMP(%a6),%a0
	movel	ETEMP(%a6),(%a0)	//write result to wbtemp
	movel	ETEMP_HI(%a6),4(%a0)
	movel	ETEMP_LO(%a6),8(%a0)
	bra	sub_ckovf
sub_s_srcd:
	leal	FPTEMP(%a6),%a0
	movel	USER_FPCR(%a6),%d0
	andil	#0x30,%d0
	lsrl	#4,%d0		//put rmode in lower 2 bits
	movel	USER_FPCR(%a6),%d1
	andil	#0xc0,%d1
	lsrl	#6,%d1		//put precision in upper word
	swap	%d1
	orl	%d0,%d1		//set up for round call
	movel	#0x20000000,%d0	//set sticky for round
	bclrb	#sign_bit,FPTEMP_EX(%a6)
	sne	FPTEMP_SGN(%a6)
	bsrl	round		//round result to users rmode & prec
	bfclr	FPTEMP_SGN(%a6){#0:#8}	//convert back to IEEE ext format
	beqs	sub_s_sclr
	bsetb	#sign_bit,FPTEMP_EX(%a6)
sub_s_sclr:
	leal	WBTEMP(%a6),%a0
	movel	FPTEMP(%a6),(%a0)	//write result to wbtemp
	movel	FPTEMP_HI(%a6),4(%a0)
	movel	FPTEMP_LO(%a6),8(%a0)
	tstw	FPTEMP_EX(%a6)
	bgt	sub_ckovf
	orl	#neg_mask,USER_FPSR(%a6)
sub_ckovf:
	movew	WBTEMP_EX(%a6),%d0
	andiw	#0x7fff,%d0
	cmpiw	#0x7fff,%d0
	bne	frcfpnr
//
// The result has overflowed to $7fff exponent.  Set I, ovfl,
// and aovfl, and clr the mantissa (incorrectly set by the
// round routine.)
//
	orl	#inf_mask+ovfl_inx_mask,USER_FPSR(%a6)
	clrl	4(%a0)
	bra	frcfpnr
//
// Inst is fcmp.
//
wrap_cmp:
	cmpb	#0xff,DNRM_FLG(%a6) //if both ops denorm,
	beq	fix_stk		 //restore to fpu
//
// One of the ops is denormalized.  Test for wrap condition
// and complete the instruction.
//
	cmpb	#0x0f,DNRM_FLG(%a6) //check for dest denorm
	bnes	cmp_srcd
cmp_destd:
	bsrl	ckinf_ns
	bne	fix_stk
	bfextu	ETEMP_EX(%a6){#1:#15},%d0	//get src exp (always pos)
	bfexts	FPTEMP_EX(%a6){#1:#15},%d1	//get dest exp (always neg)
	subl	%d1,%d0			//subtract dest from src
	cmpl	#0x8000,%d0
	blt	fix_stk			//if less, not wrap case
	tstw	ETEMP_EX(%a6)		//set N to ~sign_of(src)
	bge	cmp_setn
	rts
cmp_srcd:
	bsrl	ckinf_nd
	bne	fix_stk
	bfextu	FPTEMP_EX(%a6){#1:#15},%d0	//get dest exp (always pos)
	bfexts	ETEMP_EX(%a6){#1:#15},%d1	//get src exp (always neg)
	subl	%d1,%d0			//subtract src from dest
	cmpl	#0x8000,%d0
	blt	fix_stk			//if less, not wrap case
	tstw	FPTEMP_EX(%a6)		//set N to sign_of(dest)
	blt	cmp_setn
	rts
cmp_setn:
	orl	#neg_mask,USER_FPSR(%a6)
	rts

//
// Inst is fmul.
//
wrap_mul:
	cmpb	#0xff,DNRM_FLG(%a6) //if both ops denorm,
	beq	force_unf	//force an underflow (really!)
//
// One of the ops is denormalized.  Test for wrap condition
// and complete the instruction.
//
	cmpb	#0x0f,DNRM_FLG(%a6) //check for dest denorm
	bnes	mul_srcd
mul_destd:
	bsrl	ckinf_ns
	bne	fix_stk
	bfextu	ETEMP_EX(%a6){#1:#15},%d0	//get src exp (always pos)
	bfexts	FPTEMP_EX(%a6){#1:#15},%d1	//get dest exp (always neg)
	addl	%d1,%d0			//subtract dest from src
	bgt	fix_stk
	bra	force_unf
mul_srcd:
	bsrl	ckinf_nd
	bne	fix_stk
	bfextu	FPTEMP_EX(%a6){#1:#15},%d0	//get dest exp (always pos)
	bfexts	ETEMP_EX(%a6){#1:#15},%d1	//get src exp (always neg)
	addl	%d1,%d0			//subtract src from dest
	bgt	fix_stk

//
// This code handles the case of the instruction resulting in
// an underflow condition.
//
force_unf:
	bclrb	#E1,E_BYTE(%a6)
	orl	#unfinx_mask,USER_FPSR(%a6)
	clrw	NMNEXC(%a6)
	clrb	WBTEMP_SGN(%a6)
	movew	ETEMP_EX(%a6),%d0		//find the sign of the result
	movew	FPTEMP_EX(%a6),%d1
	eorw	%d1,%d0
	andiw	#0x8000,%d0
	beqs	frcunfcont
	st	WBTEMP_SGN(%a6)
frcunfcont:
	lea	WBTEMP(%a6),%a0		//point a0 to memory location
	movew	CMDREG1B(%a6),%d0
	btstl	#6,%d0			//test for forced precision
	beqs	frcunf_fpcr
	btstl	#2,%d0			//check for double
	bnes	frcunf_dbl
	movel	#0x1,%d0			//inst is forced single
	bras	frcunf_rnd
frcunf_dbl:
	movel	#0x2,%d0			//inst is forced double
	bras	frcunf_rnd
frcunf_fpcr:
	bfextu	FPCR_MODE(%a6){#0:#2},%d0	//inst not forced - use fpcr prec
frcunf_rnd:
	bsrl	unf_sub			//get correct result based on
//					;round precision/mode.  This
//					;sets FPSR_CC correctly
	bfclr	WBTEMP_SGN(%a6){#0:#8}	//convert back to IEEE ext format
	beqs	frcfpn
	bsetb	#sign_bit,WBTEMP_EX(%a6)
	bra	frcfpn

//
// Write the result to the user's fpn.  All results must be HUGE to be
// written; otherwise the results would have overflowed or underflowed.
// If the rounding precision is single or double, the ovf_res routine
// is needed to correctly supply the max value.
//
frcfpnr:
	movew	CMDREG1B(%a6),%d0
	btstl	#6,%d0			//test for forced precision
	beqs	frcfpn_fpcr
	btstl	#2,%d0			//check for double
	bnes	frcfpn_dbl
	movel	#0x1,%d0			//inst is forced single
	bras	frcfpn_rnd
frcfpn_dbl:
	movel	#0x2,%d0			//inst is forced double
	bras	frcfpn_rnd
frcfpn_fpcr:
	bfextu	FPCR_MODE(%a6){#0:#2},%d0	//inst not forced - use fpcr prec
	tstb	%d0
	beqs	frcfpn			//if extended, write what you got
frcfpn_rnd:
	bclrb	#sign_bit,WBTEMP_EX(%a6)
	sne	WBTEMP_SGN(%a6)
	bsrl	ovf_res			//get correct result based on
//					;round precision/mode.  This
//					;sets FPSR_CC correctly
	bfclr	WBTEMP_SGN(%a6){#0:#8}	//convert back to IEEE ext format
	beqs	frcfpn_clr
	bsetb	#sign_bit,WBTEMP_EX(%a6)
frcfpn_clr:
	orl	#ovfinx_mask,USER_FPSR(%a6)
//
// Perform the write.
//
frcfpn:
	bfextu	CMDREG1B(%a6){#6:#3},%d0	//extract fp destination register
	cmpib	#3,%d0
	bles	frc0123			//check if dest is fp0-fp3
	movel	#7,%d1
	subl	%d0,%d1
	clrl	%d0
	bsetl	%d1,%d0
	fmovemx WBTEMP(%a6),%d0
	rts
frc0123:
	cmpib	#0,%d0
	beqs	frc0_dst
	cmpib	#1,%d0
	beqs	frc1_dst
	cmpib	#2,%d0
	beqs	frc2_dst
frc3_dst:
	movel	WBTEMP_EX(%a6),USER_FP3(%a6)
	movel	WBTEMP_HI(%a6),USER_FP3+4(%a6)
	movel	WBTEMP_LO(%a6),USER_FP3+8(%a6)
	rts
frc2_dst:
	movel	WBTEMP_EX(%a6),USER_FP2(%a6)
	movel	WBTEMP_HI(%a6),USER_FP2+4(%a6)
	movel	WBTEMP_LO(%a6),USER_FP2+8(%a6)
	rts
frc1_dst:
	movel	WBTEMP_EX(%a6),USER_FP1(%a6)
	movel	WBTEMP_HI(%a6),USER_FP1+4(%a6)
	movel	WBTEMP_LO(%a6),USER_FP1+8(%a6)
	rts
frc0_dst:
	movel	WBTEMP_EX(%a6),USER_FP0(%a6)
	movel	WBTEMP_HI(%a6),USER_FP0+4(%a6)
	movel	WBTEMP_LO(%a6),USER_FP0+8(%a6)
	rts

//
// Write etemp to fpn.
// A check is made on enabled and signalled snan exceptions,
// and the destination is not overwritten if this condition exists.
// This code is designed to make fmoveins of unsupported data types
// faster.
//
wr_etemp:
	btstb	#snan_bit,FPSR_EXCEPT(%a6)	//if snan is set, and
	beqs	fmoveinc		//enabled, force restore
	btstb	#snan_bit,FPCR_ENABLE(%a6) //and don't overwrite
	beqs	fmoveinc		//the dest
	movel	ETEMP_EX(%a6),FPTEMP_EX(%a6)	//set up fptemp sign for
//						;snan handler
	tstb	ETEMP(%a6)		//check for negative
	blts	snan_neg
	rts
snan_neg:
	orl	#neg_bit,USER_FPSR(%a6)	//snan is negative; set N
	rts
fmoveinc:
	clrw	NMNEXC(%a6)
	bclrb	#E1,E_BYTE(%a6)
	moveb	STAG(%a6),%d0		//check if stag is inf
	andib	#0xe0,%d0
	cmpib	#0x40,%d0
	bnes	fminc_cnan
	orl	#inf_mask,USER_FPSR(%a6) //if inf, nothing yet has set I
	tstw	LOCAL_EX(%a0)		//check sign
	bges	fminc_con
	orl	#neg_mask,USER_FPSR(%a6)
	bra	fminc_con
fminc_cnan:
	cmpib	#0x60,%d0			//check if stag is NaN
	bnes	fminc_czero
	orl	#nan_mask,USER_FPSR(%a6) //if nan, nothing yet has set NaN
	movel	ETEMP_EX(%a6),FPTEMP_EX(%a6)	//set up fptemp sign for
//						;snan handler
	tstw	LOCAL_EX(%a0)		//check sign
	bges	fminc_con
	orl	#neg_mask,USER_FPSR(%a6)
	bra	fminc_con
fminc_czero:
	cmpib	#0x20,%d0			//check if zero
	bnes	fminc_con
	orl	#z_mask,USER_FPSR(%a6)	//if zero, set Z
	tstw	LOCAL_EX(%a0)		//check sign
	bges	fminc_con
	orl	#neg_mask,USER_FPSR(%a6)
fminc_con:
	bfextu	CMDREG1B(%a6){#6:#3},%d0	//extract fp destination register
	cmpib	#3,%d0
	bles	fp0123			//check if dest is fp0-fp3
	movel	#7,%d1
	subl	%d0,%d1
	clrl	%d0
	bsetl	%d1,%d0
	fmovemx ETEMP(%a6),%d0
	rts

fp0123:
	cmpib	#0,%d0
	beqs	fp0_dst
	cmpib	#1,%d0
	beqs	fp1_dst
	cmpib	#2,%d0
	beqs	fp2_dst
fp3_dst:
	movel	ETEMP_EX(%a6),USER_FP3(%a6)
	movel	ETEMP_HI(%a6),USER_FP3+4(%a6)
	movel	ETEMP_LO(%a6),USER_FP3+8(%a6)
	rts
fp2_dst:
	movel	ETEMP_EX(%a6),USER_FP2(%a6)
	movel	ETEMP_HI(%a6),USER_FP2+4(%a6)
	movel	ETEMP_LO(%a6),USER_FP2+8(%a6)
	rts
fp1_dst:
	movel	ETEMP_EX(%a6),USER_FP1(%a6)
	movel	ETEMP_HI(%a6),USER_FP1+4(%a6)
	movel	ETEMP_LO(%a6),USER_FP1+8(%a6)
	rts
fp0_dst:
	movel	ETEMP_EX(%a6),USER_FP0(%a6)
	movel	ETEMP_HI(%a6),USER_FP0+4(%a6)
	movel	ETEMP_LO(%a6),USER_FP0+8(%a6)
	rts

opclass3:
	st	CU_ONLY(%a6)
	movew	CMDREG1B(%a6),%d0	//check if packed moveout
	andiw	#0x0c00,%d0	//isolate last 2 bits of size field
	cmpiw	#0x0c00,%d0	//if size is 011 or 111, it is packed
	beq	pack_out	//else it is norm or denorm
	bra	mv_out


//
//	MOVE OUT
//

mv_tbl:
	.long	li
	.long 	sgp
	.long 	xp
	.long 	mvout_end	//should never be taken
	.long 	wi
	.long 	dp
	.long 	bi
	.long 	mvout_end	//should never be taken
mv_out:
	bfextu	CMDREG1B(%a6){#3:#3},%d1	//put source specifier in d1
	leal	mv_tbl,%a0
	movel	%a0@(%d1:l:4),%a0
	jmp	(%a0)

//
// This exit is for move-out to memory.  The aunfl bit is
// set if the result is inex and unfl is signalled.
//
mvout_end:
	btstb	#inex2_bit,FPSR_EXCEPT(%a6)
	beqs	no_aufl
	btstb	#unfl_bit,FPSR_EXCEPT(%a6)
	beqs	no_aufl
	bsetb	#aunfl_bit,FPSR_AEXCEPT(%a6)
no_aufl:
	clrw	NMNEXC(%a6)
	bclrb	#E1,E_BYTE(%a6)
	fmovel	#0,%FPSR			//clear any cc bits from res_func
//
// Return ETEMP to extended format from internal extended format so
// that gen_except will have a correctly signed value for ovfl/unfl
// handlers.
//
	bfclr	ETEMP_SGN(%a6){#0:#8}
	beqs	mvout_con
	bsetb	#sign_bit,ETEMP_EX(%a6)
mvout_con:
	rts
//
// This exit is for move-out to int register.  The aunfl bit is
// not set in any case for this move.
//
mvouti_end:
	clrw	NMNEXC(%a6)
	bclrb	#E1,E_BYTE(%a6)
	fmovel	#0,%FPSR			//clear any cc bits from res_func
//
// Return ETEMP to extended format from internal extended format so
// that gen_except will have a correctly signed value for ovfl/unfl
// handlers.
//
	bfclr	ETEMP_SGN(%a6){#0:#8}
	beqs	mvouti_con
	bsetb	#sign_bit,ETEMP_EX(%a6)
mvouti_con:
	rts
//
// li is used to handle a long integer source specifier
//

li:
	moveql	#4,%d0		//set byte count

	btstb	#7,STAG(%a6)	//check for extended denorm
	bne	int_dnrm	//if so, branch

	fmovemx ETEMP(%a6),%fp0-%fp0
	fcmpd	#0x41dfffffffc00000,%fp0
// 41dfffffffc00000 in dbl prec = 401d0000fffffffe00000000 in ext prec
	fbge	lo_plrg
	fcmpd	#0xc1e0000000000000,%fp0
// c1e0000000000000 in dbl prec = c01e00008000000000000000 in ext prec
	fble	lo_nlrg
//
// at this point, the answer is between the largest pos and neg values
//
	movel	USER_FPCR(%a6),%d1	//use user's rounding mode
	andil	#0x30,%d1
	fmovel	%d1,%fpcr
	fmovel	%fp0,L_SCR1(%a6)	//let the 040 perform conversion
	fmovel %fpsr,%d1
	orl	%d1,USER_FPSR(%a6)	//capture inex2/ainex if set
	bra	int_wrt


lo_plrg:
	movel	#0x7fffffff,L_SCR1(%a6)	//answer is largest positive int
	fbeq	int_wrt			//exact answer
	fcmpd	#0x41dfffffffe00000,%fp0
// 41dfffffffe00000 in dbl prec = 401d0000ffffffff00000000 in ext prec
	fbge	int_operr		//set operr
	bra	int_inx			//set inexact

lo_nlrg:
	movel	#0x80000000,L_SCR1(%a6)
	fbeq	int_wrt			//exact answer
	fcmpd	#0xc1e0000000100000,%fp0
// c1e0000000100000 in dbl prec = c01e00008000000080000000 in ext prec
	fblt	int_operr		//set operr
	bra	int_inx			//set inexact

//
// wi is used to handle a word integer source specifier
//

wi:
	moveql	#2,%d0		//set byte count

	btstb	#7,STAG(%a6)	//check for extended denorm
	bne	int_dnrm	//branch if so

	fmovemx ETEMP(%a6),%fp0-%fp0
	fcmps	#0x46fffe00,%fp0
// 46fffe00 in sgl prec = 400d0000fffe000000000000 in ext prec
	fbge	wo_plrg
	fcmps	#0xc7000000,%fp0
// c7000000 in sgl prec = c00e00008000000000000000 in ext prec
	fble	wo_nlrg

//
// at this point, the answer is between the largest pos and neg values
//
	movel	USER_FPCR(%a6),%d1	//use user's rounding mode
	andil	#0x30,%d1
	fmovel	%d1,%fpcr
	fmovew	%fp0,L_SCR1(%a6)	//let the 040 perform conversion
	fmovel %fpsr,%d1
	orl	%d1,USER_FPSR(%a6)	//capture inex2/ainex if set
	bra	int_wrt

wo_plrg:
	movew	#0x7fff,L_SCR1(%a6)	//answer is largest positive int
	fbeq	int_wrt			//exact answer
	fcmps	#0x46ffff00,%fp0
// 46ffff00 in sgl prec = 400d0000ffff000000000000 in ext prec
	fbge	int_operr		//set operr
	bra	int_inx			//set inexact

wo_nlrg:
	movew	#0x8000,L_SCR1(%a6)
	fbeq	int_wrt			//exact answer
	fcmps	#0xc7000080,%fp0
// c7000080 in sgl prec = c00e00008000800000000000 in ext prec
	fblt	int_operr		//set operr
	bra	int_inx			//set inexact

//
// bi is used to handle a byte integer source specifier
//

bi:
	moveql	#1,%d0		//set byte count

	btstb	#7,STAG(%a6)	//check for extended denorm
	bne	int_dnrm	//branch if so

	fmovemx ETEMP(%a6),%fp0-%fp0
	fcmps	#0x42fe0000,%fp0
// 42fe0000 in sgl prec = 40050000fe00000000000000 in ext prec
	fbge	by_plrg
	fcmps	#0xc3000000,%fp0
// c3000000 in sgl prec = c00600008000000000000000 in ext prec
	fble	by_nlrg

//
// at this point, the answer is between the largest pos and neg values
//
	movel	USER_FPCR(%a6),%d1	//use user's rounding mode
	andil	#0x30,%d1
	fmovel	%d1,%fpcr
	fmoveb	%fp0,L_SCR1(%a6)	//let the 040 perform conversion
	fmovel %fpsr,%d1
	orl	%d1,USER_FPSR(%a6)	//capture inex2/ainex if set
	bra	int_wrt

by_plrg:
	moveb	#0x7f,L_SCR1(%a6)		//answer is largest positive int
	fbeq	int_wrt			//exact answer
	fcmps	#0x42ff0000,%fp0
// 42ff0000 in sgl prec = 40050000ff00000000000000 in ext prec
	fbge	int_operr		//set operr
	bra	int_inx			//set inexact

by_nlrg:
	moveb	#0x80,L_SCR1(%a6)
	fbeq	int_wrt			//exact answer
	fcmps	#0xc3008000,%fp0
// c3008000 in sgl prec = c00600008080000000000000 in ext prec
	fblt	int_operr		//set operr
	bra	int_inx			//set inexact

//
// Common integer routines
//
// int_drnrm---account for possible nonzero result for round up with positive
// operand and round down for negative answer.  In the first case (result = 1)
// byte-width (store in d0) of result must be honored.  In the second case,
// -1 in L_SCR1(a6) will cover all contingencies (FMOVE.B/W/L out).

int_dnrm:
	movel	#0,L_SCR1(%a6)	// initialize result to 0
	bfextu	FPCR_MODE(%a6){#2:#2},%d1	// d1 is the rounding mode
	cmpb	#2,%d1
	bmis	int_inx		// if RN or RZ, done
	bnes	int_rp		// if RP, continue below
	tstw	ETEMP(%a6)	// RM: store -1 in L_SCR1 if src is negative
	bpls	int_inx		// otherwise result is 0
	movel	#-1,L_SCR1(%a6)
	bras	int_inx
int_rp:
	tstw	ETEMP(%a6)	// RP: store +1 of proper width in L_SCR1 if
//				; source is greater than 0
	bmis	int_inx		// otherwise, result is 0
	lea	L_SCR1(%a6),%a1	// a1 is address of L_SCR1
	addal	%d0,%a1		// offset by destination width -1
	subal	#1,%a1
	bsetb	#0,(%a1)		// set low bit at a1 address
int_inx:
	oril	#inx2a_mask,USER_FPSR(%a6)
	bras	int_wrt
int_operr:
	fmovemx %fp0-%fp0,FPTEMP(%a6)	//FPTEMP must contain the extended
//				;precision source that needs to be
//				;converted to integer this is required
//				;if the operr exception is enabled.
//				;set operr/aiop (no inex2 on int ovfl)

	oril	#opaop_mask,USER_FPSR(%a6)
//				;fall through to perform int_wrt
int_wrt:
	movel	EXC_EA(%a6),%a1	//load destination address
	tstl	%a1		//check to see if it is a dest register
	beqs	wrt_dn		//write data register
	lea	L_SCR1(%a6),%a0	//point to supervisor source address
	bsrl	mem_write
	bra	mvouti_end

wrt_dn:
	movel	%d0,-(%sp)	//d0 currently contains the size to write
	bsrl	get_fline	//get_fline returns Dn in d0
	andiw	#0x7,%d0		//isolate register
	movel	(%sp)+,%d1	//get size
	cmpil	#4,%d1		//most frequent case
	beqs	sz_long
	cmpil	#2,%d1
	bnes	sz_con
	orl	#8,%d0		//add 'word' size to register#
	bras	sz_con
sz_long:
	orl	#0x10,%d0		//add 'long' size to register#
sz_con:
	movel	%d0,%d1		//reg_dest expects size:reg in d1
	bsrl	reg_dest	//load proper data register
	bra	mvouti_end
xp:
	lea	ETEMP(%a6),%a0
	bclrb	#sign_bit,LOCAL_EX(%a0)
	sne	LOCAL_SGN(%a0)
	btstb	#7,STAG(%a6)	//check for extended denorm
	bne	xdnrm
	clrl	%d0
	bras	do_fp		//do normal case
sgp:
	lea	ETEMP(%a6),%a0
	bclrb	#sign_bit,LOCAL_EX(%a0)
	sne	LOCAL_SGN(%a0)
	btstb	#7,STAG(%a6)	//check for extended denorm
	bne	sp_catas	//branch if so
	movew	LOCAL_EX(%a0),%d0
	lea	sp_bnds,%a1
	cmpw	(%a1),%d0
	blt	sp_under
	cmpw	2(%a1),%d0
	bgt	sp_over
	movel	#1,%d0		//set destination format to single
	bras	do_fp		//do normal case
dp:
	lea	ETEMP(%a6),%a0
	bclrb	#sign_bit,LOCAL_EX(%a0)
	sne	LOCAL_SGN(%a0)

	btstb	#7,STAG(%a6)	//check for extended denorm
	bne	dp_catas	//branch if so

	movew	LOCAL_EX(%a0),%d0
	lea	dp_bnds,%a1

	cmpw	(%a1),%d0
	blt	dp_under
	cmpw	2(%a1),%d0
	bgt	dp_over

	movel	#2,%d0		//set destination format to double
//				;fall through to do_fp
//
do_fp:
	bfextu	FPCR_MODE(%a6){#2:#2},%d1	//rnd mode in d1
	swap	%d0			//rnd prec in upper word
	addl	%d0,%d1			//d1 has PREC/MODE info

	clrl	%d0			//clear g,r,s

	bsrl	round			//round

	movel	%a0,%a1
	movel	EXC_EA(%a6),%a0

	bfextu	CMDREG1B(%a6){#3:#3},%d1	//extract destination format
//					;at this point only the dest
//					;formats sgl, dbl, ext are
//					;possible
	cmpb	#2,%d1
	bgts	ddbl			//double=5, extended=2, single=1
	bnes	dsgl
//					;fall through to dext
dext:
	bsrl	dest_ext
	bra	mvout_end
dsgl:
	bsrl	dest_sgl
	bra	mvout_end
ddbl:
	bsrl	dest_dbl
	bra	mvout_end

//
// Handle possible denorm or catastrophic underflow cases here
//
xdnrm:
	bsr	set_xop		//initialize WBTEMP
	bsetb	#wbtemp15_bit,WB_BYTE(%a6) //set wbtemp15

	movel	%a0,%a1
	movel	EXC_EA(%a6),%a0	//a0 has the destination pointer
	bsrl	dest_ext	//store to memory
	bsetb	#unfl_bit,FPSR_EXCEPT(%a6)
	bra	mvout_end

sp_under:
	bsetb	#etemp15_bit,STAG(%a6)

	cmpw	4(%a1),%d0
	blts	sp_catas	//catastrophic underflow case

	movel	#1,%d0		//load in round precision
	movel	#sgl_thresh,%d1	//load in single denorm threshold
	bsrl	dpspdnrm	//expects d1 to have the proper
//				;denorm threshold
	bsrl	dest_sgl	//stores value to destination
	bsetb	#unfl_bit,FPSR_EXCEPT(%a6)
	bra	mvout_end	//exit

dp_under:
	bsetb	#etemp15_bit,STAG(%a6)

	cmpw	4(%a1),%d0
	blts	dp_catas	//catastrophic underflow case

	movel	#dbl_thresh,%d1	//load in double precision threshold
	movel	#2,%d0
	bsrl	dpspdnrm	//expects d1 to have proper
//				;denorm threshold
//				;expects d0 to have round precision
	bsrl	dest_dbl	//store value to destination
	bsetb	#unfl_bit,FPSR_EXCEPT(%a6)
	bra	mvout_end	//exit

//
// Handle catastrophic underflow cases here
//
sp_catas:
// Temp fix for z bit set in unf_sub
	movel	USER_FPSR(%a6),-(%a7)

	movel	#1,%d0		//set round precision to sgl

	bsrl	unf_sub		//a0 points to result

	movel	(%a7)+,USER_FPSR(%a6)

	movel	#1,%d0
	subw	%d0,LOCAL_EX(%a0) //account for difference between
//				;denorm/norm bias

	movel	%a0,%a1		//a1 has the operand input
	movel	EXC_EA(%a6),%a0	//a0 has the destination pointer

	bsrl	dest_sgl	//store the result
	oril	#unfinx_mask,USER_FPSR(%a6)
	bra	mvout_end

dp_catas:
// Temp fix for z bit set in unf_sub
	movel	USER_FPSR(%a6),-(%a7)

	movel	#2,%d0		//set round precision to dbl
	bsrl	unf_sub		//a0 points to result

	movel	(%a7)+,USER_FPSR(%a6)

	movel	#1,%d0
	subw	%d0,LOCAL_EX(%a0) //account for difference between
//				;denorm/norm bias

	movel	%a0,%a1		//a1 has the operand input
	movel	EXC_EA(%a6),%a0	//a0 has the destination pointer

	bsrl	dest_dbl	//store the result
	oril	#unfinx_mask,USER_FPSR(%a6)
	bra	mvout_end

//
// Handle catastrophic overflow cases here
//
sp_over:
// Temp fix for z bit set in unf_sub
	movel	USER_FPSR(%a6),-(%a7)

	movel	#1,%d0
	leal	FP_SCR1(%a6),%a0	//use FP_SCR1 for creating result
	movel	ETEMP_EX(%a6),(%a0)
	movel	ETEMP_HI(%a6),4(%a0)
	movel	ETEMP_LO(%a6),8(%a0)
	bsrl	ovf_res

	movel	(%a7)+,USER_FPSR(%a6)

	movel	%a0,%a1
	movel	EXC_EA(%a6),%a0
	bsrl	dest_sgl
	orl	#ovfinx_mask,USER_FPSR(%a6)
	bra	mvout_end

dp_over:
// Temp fix for z bit set in ovf_res
	movel	USER_FPSR(%a6),-(%a7)

	movel	#2,%d0
	leal	FP_SCR1(%a6),%a0	//use FP_SCR1 for creating result
	movel	ETEMP_EX(%a6),(%a0)
	movel	ETEMP_HI(%a6),4(%a0)
	movel	ETEMP_LO(%a6),8(%a0)
	bsrl	ovf_res

	movel	(%a7)+,USER_FPSR(%a6)

	movel	%a0,%a1
	movel	EXC_EA(%a6),%a0
	bsrl	dest_dbl
	orl	#ovfinx_mask,USER_FPSR(%a6)
	bra	mvout_end

//
// 	DPSPDNRM
//
// This subroutine takes an extended normalized number and denormalizes
// it to the given round precision. This subroutine also decrements
// the input operand's exponent by 1 to account for the fact that
// dest_sgl or dest_dbl expects a normalized number's bias.
//
// Input: a0  points to a normalized number in internal extended format
//	 d0  is the round precision (=1 for sgl; =2 for dbl)
//	 d1  is the the single precision or double precision
//	     denorm threshold
//
// Output: (In the format for dest_sgl or dest_dbl)
//	 a0   points to the destination
//   	 a1   points to the operand
//
// Exceptions: Reports inexact 2 exception by setting USER_FPSR bits
//
dpspdnrm:
	movel	%d0,-(%a7)	//save round precision
	clrl	%d0		//clear initial g,r,s
	bsrl	dnrm_lp		//careful with d0, it's needed by round

	bfextu	FPCR_MODE(%a6){#2:#2},%d1 //get rounding mode
	swap	%d1
	movew	2(%a7),%d1	//set rounding precision
	swap	%d1		//at this point d1 has PREC/MODE info
	bsrl	round		//round result, sets the inex bit in
//				;USER_FPSR if needed

	movew	#1,%d0
	subw	%d0,LOCAL_EX(%a0) //account for difference in denorm
//				;vs norm bias

	movel	%a0,%a1		//a1 has the operand input
	movel	EXC_EA(%a6),%a0	//a0 has the destination pointer
	addw	#4,%a7		//pop stack
	rts
//
// SET_XOP initialized WBTEMP with the value pointed to by a0
// input: a0 points to input operand in the internal extended format
//
set_xop:
	movel	LOCAL_EX(%a0),WBTEMP_EX(%a6)
	movel	LOCAL_HI(%a0),WBTEMP_HI(%a6)
	movel	LOCAL_LO(%a0),WBTEMP_LO(%a6)
	bfclr	WBTEMP_SGN(%a6){#0:#8}
	beqs	sxop
	bsetb	#sign_bit,WBTEMP_EX(%a6)
sxop:
	bfclr	STAG(%a6){#5:#4}	//clear wbtm66,wbtm1,wbtm0,sbit
	rts
//
//	P_MOVE
//
p_movet:
	.long	p_move
	.long	p_movez
	.long	p_movei
	.long	p_moven
	.long	p_move
p_regd:
	.long	p_dyd0
	.long	p_dyd1
	.long	p_dyd2
	.long	p_dyd3
	.long	p_dyd4
	.long	p_dyd5
	.long	p_dyd6
	.long	p_dyd7

pack_out:
 	leal	p_movet,%a0	//load jmp table address
	movew	STAG(%a6),%d0	//get source tag
	bfextu	%d0{#16:#3},%d0	//isolate source bits
	movel	(%a0,%d0.w*4),%a0	//load a0 with routine label for tag
	jmp	(%a0)		//go to the routine

p_write:
	movel	#0x0c,%d0 	//get byte count
	movel	EXC_EA(%a6),%a1	//get the destination address
	bsr 	mem_write	//write the user's destination
	moveb	#0,CU_SAVEPC(%a6) //set the cu save pc to all 0's

//
// Also note that the dtag must be set to norm here - this is because
// the 040 uses the dtag to execute the correct microcode.
//
        bfclr    DTAG(%a6){#0:#3}  //set dtag to norm

	rts

// Notes on handling of special case (zero, inf, and nan) inputs:
//	1. Operr is not signalled if the k-factor is greater than 18.
//	2. Per the manual, status bits are not set.
//

p_move:
	movew	CMDREG1B(%a6),%d0
	btstl	#kfact_bit,%d0	//test for dynamic k-factor
	beqs	statick		//if clear, k-factor is static
dynamick:
	bfextu	%d0{#25:#3},%d0	//isolate register for dynamic k-factor
	lea	p_regd,%a0
	movel	%a0@(%d0:l:4),%a0
	jmp	(%a0)
statick:
	andiw	#0x007f,%d0	//get k-factor
	bfexts	%d0{#25:#7},%d0	//sign extend d0 for bindec
	leal	ETEMP(%a6),%a0	//a0 will point to the packed decimal
	bsrl	bindec		//perform the convert; data at a6
	leal	FP_SCR1(%a6),%a0	//load a0 with result address
	bral	p_write
p_movez:
	leal	ETEMP(%a6),%a0	//a0 will point to the packed decimal
	clrw	2(%a0)		//clear lower word of exp
	clrl	4(%a0)		//load second lword of ZERO
	clrl	8(%a0)		//load third lword of ZERO
	bra	p_write		//go write results
p_movei:
	fmovel	#0,%FPSR		//clear aiop
	leal	ETEMP(%a6),%a0	//a0 will point to the packed decimal
	clrw	2(%a0)		//clear lower word of exp
	bra	p_write		//go write the result
p_moven:
	leal	ETEMP(%a6),%a0	//a0 will point to the packed decimal
	clrw	2(%a0)		//clear lower word of exp
	bra	p_write		//go write the result

//
// Routines to read the dynamic k-factor from Dn.
//
p_dyd0:
	movel	USER_D0(%a6),%d0
	bras	statick
p_dyd1:
	movel	USER_D1(%a6),%d0
	bras	statick
p_dyd2:
	movel	%d2,%d0
	bras	statick
p_dyd3:
	movel	%d3,%d0
	bras	statick
p_dyd4:
	movel	%d4,%d0
	bras	statick
p_dyd5:
	movel	%d5,%d0
	bras	statick
p_dyd6:
	movel	%d6,%d0
	bra	statick
p_dyd7:
	movel	%d7,%d0
	bra	statick

	|end
