ifpsp060/src/fpsp.S [11521:15834]:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#########################################################################
# XDEF ****************************************************************	#
#	fmul(): emulates the fmul instruction				#
#	fsmul(): emulates the fsmul instruction				#
#	fdmul(): emulates the fdmul instruction				#
#									#
# XREF ****************************************************************	#
#	scale_to_zero_src() - scale src exponent to zero		#
#	scale_to_zero_dst() - scale dst exponent to zero		#
#	unf_res() - return default underflow result			#
#	ovf_res() - return default overflow result			#
#	res_qnan() - return QNAN result					#
#	res_snan() - return SNAN result					#
#									#
# INPUT ***************************************************************	#
#	a0 = pointer to extended precision source operand		#
#	a1 = pointer to extended precision destination operand		#
#	d0  rnd prec,mode						#
#									#
# OUTPUT **************************************************************	#
#	fp0 = result							#
#	fp1 = EXOP (if exception occurred)				#
#									#
# ALGORITHM ***********************************************************	#
#	Handle NANs, infinities, and zeroes as special cases. Divide	#
# norms/denorms into ext/sgl/dbl precision.				#
#	For norms/denorms, scale the exponents such that a multiply	#
# instruction won't cause an exception. Use the regular fmul to		#
# compute a result. Check if the regular operands would have taken	#
# an exception. If so, return the default overflow/underflow result	#
# and return the EXOP if exceptions are enabled. Else, scale the	#
# result operand to the proper exponent.				#
#									#
#########################################################################

	align		0x10
tbl_fmul_ovfl:
	long		0x3fff - 0x7ffe		# ext_max
	long		0x3fff - 0x407e		# sgl_max
	long		0x3fff - 0x43fe		# dbl_max
tbl_fmul_unfl:
	long		0x3fff + 0x0001		# ext_unfl
	long		0x3fff - 0x3f80		# sgl_unfl
	long		0x3fff - 0x3c00		# dbl_unfl

	global		fsmul
fsmul:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&s_mode*0x10,%d0	# insert sgl prec
	bra.b		fmul

	global		fdmul
fdmul:
	andi.b		&0x30,%d0
	ori.b		&d_mode*0x10,%d0	# insert dbl prec

	global		fmul
fmul:
	mov.l		%d0,L_SCR3(%a6)		# store rnd info

	clr.w		%d1
	mov.b		DTAG(%a6),%d1
	lsl.b		&0x3,%d1
	or.b		STAG(%a6),%d1		# combine src tags
	bne.w		fmul_not_norm		# optimize on non-norm input

fmul_norm:
	mov.w		DST_EX(%a1),FP_SCR1_EX(%a6)
	mov.l		DST_HI(%a1),FP_SCR1_HI(%a6)
	mov.l		DST_LO(%a1),FP_SCR1_LO(%a6)

	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)

	bsr.l		scale_to_zero_src	# scale src exponent
	mov.l		%d0,-(%sp)		# save scale factor 1

	bsr.l		scale_to_zero_dst	# scale dst exponent

	add.l		%d0,(%sp)		# SCALE_FACTOR = scale1 + scale2

	mov.w		2+L_SCR3(%a6),%d1	# fetch precision
	lsr.b		&0x6,%d1		# shift to lo bits
	mov.l		(%sp)+,%d0		# load S.F.
	cmp.l		%d0,(tbl_fmul_ovfl.w,%pc,%d1.w*4) # would result ovfl?
	beq.w		fmul_may_ovfl		# result may rnd to overflow
	blt.w		fmul_ovfl		# result will overflow

	cmp.l		%d0,(tbl_fmul_unfl.w,%pc,%d1.w*4) # would result unfl?
	beq.w		fmul_may_unfl		# result may rnd to no unfl
	bgt.w		fmul_unfl		# result will underflow

#
# NORMAL:
# - the result of the multiply operation will neither overflow nor underflow.
# - do the multiply to the proper precision and rounding mode.
# - scale the result exponent using the scale factor. if both operands were
# normalized then we really don't need to go through this scaling. but for now,
# this will do.
#
fmul_normal:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst operand

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fmul.x		FP_SCR0(%a6),%fp0	# execute multiply

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

fmul_normal_exit:
	fmovm.x		&0x80,FP_SCR0(%a6)	# store out result
	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# load {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	or.w		%d2,%d1			# concat old sign,new exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x80	# return default result in fp0
	rts

#
# OVERFLOW:
# - the result of the multiply operation is an overflow.
# - do the multiply to the proper precision and rounding mode in order to
# set the inexact bits.
# - calculate the default result and return it in fp0.
# - if overflow or inexact is enabled, we need a multiply result rounded to
# extended precision. if the original operation was extended, then we have this
# result. if the original operation was single or double, we have to do another
# multiply using extended precision and the correct rounding mode. the result
# of this operation then has its exponent scaled by -0x6000 to create the
# exceptional operand.
#
fmul_ovfl:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst operand

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fmul.x		FP_SCR0(%a6),%fp0	# execute multiply

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

# save setting this until now because this is where fmul_may_ovfl may jump in
fmul_ovfl_tst:
	or.l		&ovfl_inx_mask,USER_FPSR(%a6) # set ovfl/aovfl/ainex

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x13,%d1		# is OVFL or INEX enabled?
	bne.b		fmul_ovfl_ena		# yes

# calculate the default result
fmul_ovfl_dis:
	btst		&neg_bit,FPSR_CC(%a6)	# is result negative?
	sne		%d1			# set sign param accordingly
	mov.l		L_SCR3(%a6),%d0		# pass rnd prec,mode
	bsr.l		ovf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# set INF,N if applicable
	fmovm.x		(%a0),&0x80		# return default result in fp0
	rts

#
# OVFL is enabled; Create EXOP:
# - if precision is extended, then we have the EXOP. simply bias the exponent
# with an extra -0x6000. if the precision is single or double, we need to
# calculate a result rounded to extended precision.
#
fmul_ovfl_ena:
	mov.l		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# test the rnd prec
	bne.b		fmul_ovfl_ena_sd	# it's sgl or dbl

fmul_ovfl_ena_cont:
	fmovm.x		&0x80,FP_SCR0(%a6)	# move result to stack

	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.w		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	sub.l		%d0,%d1			# add scale factor
	subi.l		&0x6000,%d1		# subtract bias
	andi.w		&0x7fff,%d1		# clear sign bit
	andi.w		&0x8000,%d2		# keep old sign
	or.w		%d2,%d1			# concat old sign,new exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	bra.b		fmul_ovfl_dis

fmul_ovfl_ena_sd:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst operand

	mov.l		L_SCR3(%a6),%d1
	andi.b		&0x30,%d1		# keep rnd mode only
	fmov.l		%d1,%fpcr		# set FPCR

	fmul.x		FP_SCR0(%a6),%fp0	# execute multiply

	fmov.l		&0x0,%fpcr		# clear FPCR
	bra.b		fmul_ovfl_ena_cont

#
# may OVERFLOW:
# - the result of the multiply operation MAY overflow.
# - do the multiply to the proper precision and rounding mode in order to
# set the inexact bits.
# - calculate the default result and return it in fp0.
#
fmul_may_ovfl:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fmul.x		FP_SCR0(%a6),%fp0	# execute multiply

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	fabs.x		%fp0,%fp1		# make a copy of result
	fcmp.b		%fp1,&0x2		# is |result| >= 2.b?
	fbge.w		fmul_ovfl_tst		# yes; overflow has occurred

# no, it didn't overflow; we have correct result
	bra.w		fmul_normal_exit

#
# UNDERFLOW:
# - the result of the multiply operation is an underflow.
# - do the multiply to the proper precision and rounding mode in order to
# set the inexact bits.
# - calculate the default result and return it in fp0.
# - if overflow or inexact is enabled, we need a multiply result rounded to
# extended precision. if the original operation was extended, then we have this
# result. if the original operation was single or double, we have to do another
# multiply using extended precision and the correct rounding mode. the result
# of this operation then has its exponent scaled by -0x6000 to create the
# exceptional operand.
#
fmul_unfl:
	bset		&unfl_bit,FPSR_EXCEPT(%a6) # set unfl exc bit

# for fun, let's use only extended precision, round to zero. then, let
# the unf_res() routine figure out all the rest.
# will we get the correct answer.
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst operand

	fmov.l		&rz_mode*0x10,%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fmul.x		FP_SCR0(%a6),%fp0	# execute multiply

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x0b,%d1		# is UNFL or INEX enabled?
	bne.b		fmul_unfl_ena		# yes

fmul_unfl_dis:
	fmovm.x		&0x80,FP_SCR0(%a6)	# store out result

	lea		FP_SCR0(%a6),%a0	# pass: result addr
	mov.l		L_SCR3(%a6),%d1		# pass: rnd prec,mode
	bsr.l		unf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# unf_res2 may have set 'Z'
	fmovm.x		FP_SCR0(%a6),&0x80	# return default result in fp0
	rts

#
# UNFL is enabled.
#
fmul_unfl_ena:
	fmovm.x		FP_SCR1(%a6),&0x40	# load dst op

	mov.l		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# is precision extended?
	bne.b		fmul_unfl_ena_sd	# no, sgl or dbl

# if the rnd mode is anything but RZ, then we have to re-do the above
# multiplication because we used RZ for all.
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

fmul_unfl_ena_cont:
	fmov.l		&0x0,%fpsr		# clear FPSR

	fmul.x		FP_SCR0(%a6),%fp1	# execute multiply

	fmov.l		&0x0,%fpcr		# clear FPCR

	fmovm.x		&0x40,FP_SCR0(%a6)	# save result to stack
	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	addi.l		&0x6000,%d1		# add bias
	andi.w		&0x7fff,%d1
	or.w		%d2,%d1			# concat old sign,new exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	bra.w		fmul_unfl_dis

fmul_unfl_ena_sd:
	mov.l		L_SCR3(%a6),%d1
	andi.b		&0x30,%d1		# use only rnd mode
	fmov.l		%d1,%fpcr		# set FPCR

	bra.b		fmul_unfl_ena_cont

# MAY UNDERFLOW:
# -use the correct rounding mode and precision. this code favors operations
# that do not underflow.
fmul_may_unfl:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst operand

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fmul.x		FP_SCR0(%a6),%fp0	# execute multiply

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	fabs.x		%fp0,%fp1		# make a copy of result
	fcmp.b		%fp1,&0x2		# is |result| > 2.b?
	fbgt.w		fmul_normal_exit	# no; no underflow occurred
	fblt.w		fmul_unfl		# yes; underflow occurred

#
# we still don't know if underflow occurred. result is ~ equal to 2. but,
# we don't know if the result was an underflow that rounded up to a 2 or
# a normalized number that rounded down to a 2. so, redo the entire operation
# using RZ as the rounding mode to see what the pre-rounded result is.
# this case should be relatively rare.
#
	fmovm.x		FP_SCR1(%a6),&0x40	# load dst operand

	mov.l		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# keep rnd prec
	ori.b		&rz_mode*0x10,%d1	# insert RZ

	fmov.l		%d1,%fpcr		# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fmul.x		FP_SCR0(%a6),%fp1	# execute multiply

	fmov.l		&0x0,%fpcr		# clear FPCR
	fabs.x		%fp1			# make absolute value
	fcmp.b		%fp1,&0x2		# is |result| < 2.b?
	fbge.w		fmul_normal_exit	# no; no underflow occurred
	bra.w		fmul_unfl		# yes, underflow occurred

################################################################################

#
# Multiply: inputs are not both normalized; what are they?
#
fmul_not_norm:
	mov.w		(tbl_fmul_op.b,%pc,%d1.w*2),%d1
	jmp		(tbl_fmul_op.b,%pc,%d1.w)

	swbeg		&48
tbl_fmul_op:
	short		fmul_norm	- tbl_fmul_op # NORM x NORM
	short		fmul_zero	- tbl_fmul_op # NORM x ZERO
	short		fmul_inf_src	- tbl_fmul_op # NORM x INF
	short		fmul_res_qnan	- tbl_fmul_op # NORM x QNAN
	short		fmul_norm	- tbl_fmul_op # NORM x DENORM
	short		fmul_res_snan	- tbl_fmul_op # NORM x SNAN
	short		tbl_fmul_op	- tbl_fmul_op #
	short		tbl_fmul_op	- tbl_fmul_op #

	short		fmul_zero	- tbl_fmul_op # ZERO x NORM
	short		fmul_zero	- tbl_fmul_op # ZERO x ZERO
	short		fmul_res_operr	- tbl_fmul_op # ZERO x INF
	short		fmul_res_qnan	- tbl_fmul_op # ZERO x QNAN
	short		fmul_zero	- tbl_fmul_op # ZERO x DENORM
	short		fmul_res_snan	- tbl_fmul_op # ZERO x SNAN
	short		tbl_fmul_op	- tbl_fmul_op #
	short		tbl_fmul_op	- tbl_fmul_op #

	short		fmul_inf_dst	- tbl_fmul_op # INF x NORM
	short		fmul_res_operr	- tbl_fmul_op # INF x ZERO
	short		fmul_inf_dst	- tbl_fmul_op # INF x INF
	short		fmul_res_qnan	- tbl_fmul_op # INF x QNAN
	short		fmul_inf_dst	- tbl_fmul_op # INF x DENORM
	short		fmul_res_snan	- tbl_fmul_op # INF x SNAN
	short		tbl_fmul_op	- tbl_fmul_op #
	short		tbl_fmul_op	- tbl_fmul_op #

	short		fmul_res_qnan	- tbl_fmul_op # QNAN x NORM
	short		fmul_res_qnan	- tbl_fmul_op # QNAN x ZERO
	short		fmul_res_qnan	- tbl_fmul_op # QNAN x INF
	short		fmul_res_qnan	- tbl_fmul_op # QNAN x QNAN
	short		fmul_res_qnan	- tbl_fmul_op # QNAN x DENORM
	short		fmul_res_snan	- tbl_fmul_op # QNAN x SNAN
	short		tbl_fmul_op	- tbl_fmul_op #
	short		tbl_fmul_op	- tbl_fmul_op #

	short		fmul_norm	- tbl_fmul_op # NORM x NORM
	short		fmul_zero	- tbl_fmul_op # NORM x ZERO
	short		fmul_inf_src	- tbl_fmul_op # NORM x INF
	short		fmul_res_qnan	- tbl_fmul_op # NORM x QNAN
	short		fmul_norm	- tbl_fmul_op # NORM x DENORM
	short		fmul_res_snan	- tbl_fmul_op # NORM x SNAN
	short		tbl_fmul_op	- tbl_fmul_op #
	short		tbl_fmul_op	- tbl_fmul_op #

	short		fmul_res_snan	- tbl_fmul_op # SNAN x NORM
	short		fmul_res_snan	- tbl_fmul_op # SNAN x ZERO
	short		fmul_res_snan	- tbl_fmul_op # SNAN x INF
	short		fmul_res_snan	- tbl_fmul_op # SNAN x QNAN
	short		fmul_res_snan	- tbl_fmul_op # SNAN x DENORM
	short		fmul_res_snan	- tbl_fmul_op # SNAN x SNAN
	short		tbl_fmul_op	- tbl_fmul_op #
	short		tbl_fmul_op	- tbl_fmul_op #

fmul_res_operr:
	bra.l		res_operr
fmul_res_snan:
	bra.l		res_snan
fmul_res_qnan:
	bra.l		res_qnan

#
# Multiply: (Zero x Zero) || (Zero x norm) || (Zero x denorm)
#
	global		fmul_zero		# global for fsglmul
fmul_zero:
	mov.b		SRC_EX(%a0),%d0		# exclusive or the signs
	mov.b		DST_EX(%a1),%d1
	eor.b		%d0,%d1
	bpl.b		fmul_zero_p		# result ZERO is pos.
fmul_zero_n:
	fmov.s		&0x80000000,%fp0	# load -ZERO
	mov.b		&z_bmask+neg_bmask,FPSR_CC(%a6) # set Z/N
	rts
fmul_zero_p:
	fmov.s		&0x00000000,%fp0	# load +ZERO
	mov.b		&z_bmask,FPSR_CC(%a6)	# set Z
	rts

#
# Multiply: (inf x inf) || (inf x norm) || (inf x denorm)
#
# Note: The j-bit for an infinity is a don't-care. However, to be
# strictly compatible w/ the 68881/882, we make sure to return an
# INF w/ the j-bit set if the input INF j-bit was set. Destination
# INFs take priority.
#
	global		fmul_inf_dst		# global for fsglmul
fmul_inf_dst:
	fmovm.x		DST(%a1),&0x80		# return INF result in fp0
	mov.b		SRC_EX(%a0),%d0		# exclusive or the signs
	mov.b		DST_EX(%a1),%d1
	eor.b		%d0,%d1
	bpl.b		fmul_inf_dst_p		# result INF is pos.
fmul_inf_dst_n:
	fabs.x		%fp0			# clear result sign
	fneg.x		%fp0			# set result sign
	mov.b		&inf_bmask+neg_bmask,FPSR_CC(%a6) # set INF/N
	rts
fmul_inf_dst_p:
	fabs.x		%fp0			# clear result sign
	mov.b		&inf_bmask,FPSR_CC(%a6)	# set INF
	rts

	global		fmul_inf_src		# global for fsglmul
fmul_inf_src:
	fmovm.x		SRC(%a0),&0x80		# return INF result in fp0
	mov.b		SRC_EX(%a0),%d0		# exclusive or the signs
	mov.b		DST_EX(%a1),%d1
	eor.b		%d0,%d1
	bpl.b		fmul_inf_dst_p		# result INF is pos.
	bra.b		fmul_inf_dst_n

#########################################################################
# XDEF ****************************************************************	#
#	fin(): emulates the fmove instruction				#
#	fsin(): emulates the fsmove instruction				#
#	fdin(): emulates the fdmove instruction				#
#									#
# XREF ****************************************************************	#
#	norm() - normalize mantissa for EXOP on denorm			#
#	scale_to_zero_src() - scale src exponent to zero		#
#	ovf_res() - return default overflow result			#
#	unf_res() - return default underflow result			#
#	res_qnan_1op() - return QNAN result				#
#	res_snan_1op() - return SNAN result				#
#									#
# INPUT ***************************************************************	#
#	a0 = pointer to extended precision source operand		#
#	d0 = round prec/mode						#
#									#
# OUTPUT **************************************************************	#
#	fp0 = result							#
#	fp1 = EXOP (if exception occurred)				#
#									#
# ALGORITHM ***********************************************************	#
#	Handle NANs, infinities, and zeroes as special cases. Divide	#
# norms into extended, single, and double precision.			#
#	Norms can be emulated w/ a regular fmove instruction. For	#
# sgl/dbl, must scale exponent and perform an "fmove". Check to see	#
# if the result would have overflowed/underflowed. If so, use unf_res()	#
# or ovf_res() to return the default result. Also return EXOP if	#
# exception is enabled. If no exception, return the default result.	#
#	Unnorms don't pass through here.				#
#									#
#########################################################################

	global		fsin
fsin:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&s_mode*0x10,%d0	# insert sgl precision
	bra.b		fin

	global		fdin
fdin:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&d_mode*0x10,%d0	# insert dbl precision

	global		fin
fin:
	mov.l		%d0,L_SCR3(%a6)		# store rnd info

	mov.b		STAG(%a6),%d1		# fetch src optype tag
	bne.w		fin_not_norm		# optimize on non-norm input

#
# FP MOVE IN: NORMs and DENORMs ONLY!
#
fin_norm:
	andi.b		&0xc0,%d0		# is precision extended?
	bne.w		fin_not_ext		# no, so go handle dbl or sgl

#
# precision selected is extended. so...we cannot get an underflow
# or overflow because of rounding to the correct precision. so...
# skip the scaling and unscaling...
#
	tst.b		SRC_EX(%a0)		# is the operand negative?
	bpl.b		fin_norm_done		# no
	bset		&neg_bit,FPSR_CC(%a6)	# yes, so set 'N' ccode bit
fin_norm_done:
	fmovm.x		SRC(%a0),&0x80		# return result in fp0
	rts

#
# for an extended precision DENORM, the UNFL exception bit is set
# the accrued bit is NOT set in this instance(no inexactness!)
#
fin_denorm:
	andi.b		&0xc0,%d0		# is precision extended?
	bne.w		fin_not_ext		# no, so go handle dbl or sgl

	bset		&unfl_bit,FPSR_EXCEPT(%a6) # set unfl exc bit
	tst.b		SRC_EX(%a0)		# is the operand negative?
	bpl.b		fin_denorm_done		# no
	bset		&neg_bit,FPSR_CC(%a6)	# yes, so set 'N' ccode bit
fin_denorm_done:
	fmovm.x		SRC(%a0),&0x80		# return result in fp0
	btst		&unfl_bit,FPCR_ENABLE(%a6) # is UNFL enabled?
	bne.b		fin_denorm_unfl_ena	# yes
	rts

#
# the input is an extended DENORM and underflow is enabled in the FPCR.
# normalize the mantissa and add the bias of 0x6000 to the resulting negative
# exponent and insert back into the operand.
#
fin_denorm_unfl_ena:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	lea		FP_SCR0(%a6),%a0	# pass: ptr to operand
	bsr.l		norm			# normalize result
	neg.w		%d0			# new exponent = -(shft val)
	addi.w		&0x6000,%d0		# add new bias to exponent
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch old sign,exp
	andi.w		&0x8000,%d1		# keep old sign
	andi.w		&0x7fff,%d0		# clear sign position
	or.w		%d1,%d0			# concat new exo,old sign
	mov.w		%d0,FP_SCR0_EX(%a6)	# insert new exponent
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	rts

#
# operand is to be rounded to single or double precision
#
fin_not_ext:
	cmpi.b		%d0,&s_mode*0x10	# separate sgl/dbl prec
	bne.b		fin_dbl

#
# operand is to be rounded to single precision
#
fin_sgl:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	bsr.l		scale_to_zero_src	# calculate scale factor

	cmpi.l		%d0,&0x3fff-0x3f80	# will move in underflow?
	bge.w		fin_sd_unfl		# yes; go handle underflow
	cmpi.l		%d0,&0x3fff-0x407e	# will move in overflow?
	beq.w		fin_sd_may_ovfl		# maybe; go check
	blt.w		fin_sd_ovfl		# yes; go handle overflow

#
# operand will NOT overflow or underflow when moved into the fp reg file
#
fin_sd_normal:
	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fmov.x		FP_SCR0(%a6),%fp0	# perform move

	fmov.l		%fpsr,%d1		# save FPSR
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

fin_sd_normal_exit:
	mov.l		%d2,-(%sp)		# save d2
	fmovm.x		&0x80,FP_SCR0(%a6)	# store out result
	mov.w		FP_SCR0_EX(%a6),%d1	# load {sgn,exp}
	mov.w		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	sub.l		%d0,%d1			# add scale factor
	andi.w		&0x8000,%d2		# keep old sign
	or.w		%d1,%d2			# concat old sign,new exponent
	mov.w		%d2,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x80	# return result in fp0
	rts

#
# operand is to be rounded to double precision
#
fin_dbl:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	bsr.l		scale_to_zero_src	# calculate scale factor

	cmpi.l		%d0,&0x3fff-0x3c00	# will move in underflow?
	bge.w		fin_sd_unfl		# yes; go handle underflow
	cmpi.l		%d0,&0x3fff-0x43fe	# will move in overflow?
	beq.w		fin_sd_may_ovfl		# maybe; go check
	blt.w		fin_sd_ovfl		# yes; go handle overflow
	bra.w		fin_sd_normal		# no; ho handle normalized op

#
# operand WILL underflow when moved in to the fp register file
#
fin_sd_unfl:
	bset		&unfl_bit,FPSR_EXCEPT(%a6) # set unfl exc bit

	tst.b		FP_SCR0_EX(%a6)		# is operand negative?
	bpl.b		fin_sd_unfl_tst
	bset		&neg_bit,FPSR_CC(%a6)	# set 'N' ccode bit

# if underflow or inexact is enabled, then go calculate the EXOP first.
fin_sd_unfl_tst:
	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x0b,%d1		# is UNFL or INEX enabled?
	bne.b		fin_sd_unfl_ena		# yes

fin_sd_unfl_dis:
	lea		FP_SCR0(%a6),%a0	# pass: result addr
	mov.l		L_SCR3(%a6),%d1		# pass: rnd prec,mode
	bsr.l		unf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# unf_res may have set 'Z'
	fmovm.x		FP_SCR0(%a6),&0x80	# return default result in fp0
	rts

#
# operand will underflow AND underflow or inexact is enabled.
# Therefore, we must return the result rounded to extended precision.
#
fin_sd_unfl_ena:
	mov.l		FP_SCR0_HI(%a6),FP_SCR1_HI(%a6)
	mov.l		FP_SCR0_LO(%a6),FP_SCR1_LO(%a6)
	mov.w		FP_SCR0_EX(%a6),%d1	# load current exponent

	mov.l		%d2,-(%sp)		# save d2
	mov.w		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	sub.l		%d0,%d1			# subtract scale factor
	andi.w		&0x8000,%d2		# extract old sign
	addi.l		&0x6000,%d1		# add new bias
	andi.w		&0x7fff,%d1
	or.w		%d1,%d2			# concat old sign,new exp
	mov.w		%d2,FP_SCR1_EX(%a6)	# insert new exponent
	fmovm.x		FP_SCR1(%a6),&0x40	# return EXOP in fp1
	mov.l		(%sp)+,%d2		# restore d2
	bra.b		fin_sd_unfl_dis

#
# operand WILL overflow.
#
fin_sd_ovfl:
	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fmov.x		FP_SCR0(%a6),%fp0	# perform move

	fmov.l		&0x0,%fpcr		# clear FPCR
	fmov.l		%fpsr,%d1		# save FPSR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

fin_sd_ovfl_tst:
	or.l		&ovfl_inx_mask,USER_FPSR(%a6) # set ovfl/aovfl/ainex

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x13,%d1		# is OVFL or INEX enabled?
	bne.b		fin_sd_ovfl_ena		# yes

#
# OVFL is not enabled; therefore, we must create the default result by
# calling ovf_res().
#
fin_sd_ovfl_dis:
	btst		&neg_bit,FPSR_CC(%a6)	# is result negative?
	sne		%d1			# set sign param accordingly
	mov.l		L_SCR3(%a6),%d0		# pass: prec,mode
	bsr.l		ovf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# set INF,N if applicable
	fmovm.x		(%a0),&0x80		# return default result in fp0
	rts

#
# OVFL is enabled.
# the INEX2 bit has already been updated by the round to the correct precision.
# now, round to extended(and don't alter the FPSR).
#
fin_sd_ovfl_ena:
	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	sub.l		&0x6000,%d1		# subtract bias
	andi.w		&0x7fff,%d1
	or.w		%d2,%d1
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	bra.b		fin_sd_ovfl_dis

#
# the move in MAY overflow. so...
#
fin_sd_may_ovfl:
	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fmov.x		FP_SCR0(%a6),%fp0	# perform the move

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	fabs.x		%fp0,%fp1		# make a copy of result
	fcmp.b		%fp1,&0x2		# is |result| >= 2.b?
	fbge.w		fin_sd_ovfl_tst		# yes; overflow has occurred

# no, it didn't overflow; we have correct result
	bra.w		fin_sd_normal_exit

##########################################################################

#
# operand is not a NORM: check its optype and branch accordingly
#
fin_not_norm:
	cmpi.b		%d1,&DENORM		# weed out DENORM
	beq.w		fin_denorm
	cmpi.b		%d1,&SNAN		# weed out SNANs
	beq.l		res_snan_1op
	cmpi.b		%d1,&QNAN		# weed out QNANs
	beq.l		res_qnan_1op

#
# do the fmove in; at this point, only possible ops are ZERO and INF.
# use fmov to determine ccodes.
# prec:mode should be zero at this point but it won't affect answer anyways.
#
	fmov.x		SRC(%a0),%fp0		# do fmove in
	fmov.l		%fpsr,%d0		# no exceptions possible
	rol.l		&0x8,%d0		# put ccodes in lo byte
	mov.b		%d0,FPSR_CC(%a6)	# insert correct ccodes
	rts

#########################################################################
# XDEF ****************************************************************	#
#	fdiv(): emulates the fdiv instruction				#
#	fsdiv(): emulates the fsdiv instruction				#
#	fddiv(): emulates the fddiv instruction				#
#									#
# XREF ****************************************************************	#
#	scale_to_zero_src() - scale src exponent to zero		#
#	scale_to_zero_dst() - scale dst exponent to zero		#
#	unf_res() - return default underflow result			#
#	ovf_res() - return default overflow result			#
#	res_qnan() - return QNAN result					#
#	res_snan() - return SNAN result					#
#									#
# INPUT ***************************************************************	#
#	a0 = pointer to extended precision source operand		#
#	a1 = pointer to extended precision destination operand		#
#	d0  rnd prec,mode						#
#									#
# OUTPUT **************************************************************	#
#	fp0 = result							#
#	fp1 = EXOP (if exception occurred)				#
#									#
# ALGORITHM ***********************************************************	#
#	Handle NANs, infinities, and zeroes as special cases. Divide	#
# norms/denorms into ext/sgl/dbl precision.				#
#	For norms/denorms, scale the exponents such that a divide	#
# instruction won't cause an exception. Use the regular fdiv to		#
# compute a result. Check if the regular operands would have taken	#
# an exception. If so, return the default overflow/underflow result	#
# and return the EXOP if exceptions are enabled. Else, scale the	#
# result operand to the proper exponent.				#
#									#
#########################################################################

	align		0x10
tbl_fdiv_unfl:
	long		0x3fff - 0x0000		# ext_unfl
	long		0x3fff - 0x3f81		# sgl_unfl
	long		0x3fff - 0x3c01		# dbl_unfl

tbl_fdiv_ovfl:
	long		0x3fff - 0x7ffe		# ext overflow exponent
	long		0x3fff - 0x407e		# sgl overflow exponent
	long		0x3fff - 0x43fe		# dbl overflow exponent

	global		fsdiv
fsdiv:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&s_mode*0x10,%d0	# insert sgl prec
	bra.b		fdiv

	global		fddiv
fddiv:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&d_mode*0x10,%d0	# insert dbl prec

	global		fdiv
fdiv:
	mov.l		%d0,L_SCR3(%a6)		# store rnd info

	clr.w		%d1
	mov.b		DTAG(%a6),%d1
	lsl.b		&0x3,%d1
	or.b		STAG(%a6),%d1		# combine src tags

	bne.w		fdiv_not_norm		# optimize on non-norm input

#
# DIVIDE: NORMs and DENORMs ONLY!
#
fdiv_norm:
	mov.w		DST_EX(%a1),FP_SCR1_EX(%a6)
	mov.l		DST_HI(%a1),FP_SCR1_HI(%a6)
	mov.l		DST_LO(%a1),FP_SCR1_LO(%a6)

	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)

	bsr.l		scale_to_zero_src	# scale src exponent
	mov.l		%d0,-(%sp)		# save scale factor 1

	bsr.l		scale_to_zero_dst	# scale dst exponent

	neg.l		(%sp)			# SCALE FACTOR = scale1 - scale2
	add.l		%d0,(%sp)

	mov.w		2+L_SCR3(%a6),%d1	# fetch precision
	lsr.b		&0x6,%d1		# shift to lo bits
	mov.l		(%sp)+,%d0		# load S.F.
	cmp.l		%d0,(tbl_fdiv_ovfl.b,%pc,%d1.w*4) # will result overflow?
	ble.w		fdiv_may_ovfl		# result will overflow

	cmp.l		%d0,(tbl_fdiv_unfl.w,%pc,%d1.w*4) # will result underflow?
	beq.w		fdiv_may_unfl		# maybe
	bgt.w		fdiv_unfl		# yes; go handle underflow

fdiv_normal:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		L_SCR3(%a6),%fpcr	# save FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fdiv.x		FP_SCR0(%a6),%fp0	# perform divide

	fmov.l		%fpsr,%d1		# save FPSR
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

fdiv_normal_exit:
	fmovm.x		&0x80,FP_SCR0(%a6)	# store result on stack
	mov.l		%d2,-(%sp)		# store d2
	mov.w		FP_SCR0_EX(%a6),%d1	# load {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	or.w		%d2,%d1			# concat old sign,new exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x80	# return result in fp0
	rts

tbl_fdiv_ovfl2:
	long		0x7fff
	long		0x407f
	long		0x43ff

fdiv_no_ovfl:
	mov.l		(%sp)+,%d0		# restore scale factor
	bra.b		fdiv_normal_exit

fdiv_may_ovfl:
	mov.l		%d0,-(%sp)		# save scale factor

	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# set FPSR

	fdiv.x		FP_SCR0(%a6),%fp0	# execute divide

	fmov.l		%fpsr,%d0
	fmov.l		&0x0,%fpcr

	or.l		%d0,USER_FPSR(%a6)	# save INEX,N

	fmovm.x		&0x01,-(%sp)		# save result to stack
	mov.w		(%sp),%d0		# fetch new exponent
	add.l		&0xc,%sp		# clear result from stack
	andi.l		&0x7fff,%d0		# strip sign
	sub.l		(%sp),%d0		# add scale factor
	cmp.l		%d0,(tbl_fdiv_ovfl2.b,%pc,%d1.w*4)
	blt.b		fdiv_no_ovfl
	mov.l		(%sp)+,%d0

fdiv_ovfl_tst:
	or.l		&ovfl_inx_mask,USER_FPSR(%a6) # set ovfl/aovfl/ainex

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x13,%d1		# is OVFL or INEX enabled?
	bne.b		fdiv_ovfl_ena		# yes

fdiv_ovfl_dis:
	btst		&neg_bit,FPSR_CC(%a6)	# is result negative?
	sne		%d1			# set sign param accordingly
	mov.l		L_SCR3(%a6),%d0		# pass prec:rnd
	bsr.l		ovf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# set INF if applicable
	fmovm.x		(%a0),&0x80		# return default result in fp0
	rts

fdiv_ovfl_ena:
	mov.l		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# is precision extended?
	bne.b		fdiv_ovfl_ena_sd	# no, do sgl or dbl

fdiv_ovfl_ena_cont:
	fmovm.x		&0x80,FP_SCR0(%a6)	# move result to stack

	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.w		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	sub.l		%d0,%d1			# add scale factor
	subi.l		&0x6000,%d1		# subtract bias
	andi.w		&0x7fff,%d1		# clear sign bit
	andi.w		&0x8000,%d2		# keep old sign
	or.w		%d2,%d1			# concat old sign,new exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	bra.b		fdiv_ovfl_dis

fdiv_ovfl_ena_sd:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst operand

	mov.l		L_SCR3(%a6),%d1
	andi.b		&0x30,%d1		# keep rnd mode
	fmov.l		%d1,%fpcr		# set FPCR

	fdiv.x		FP_SCR0(%a6),%fp0	# execute divide

	fmov.l		&0x0,%fpcr		# clear FPCR
	bra.b		fdiv_ovfl_ena_cont

fdiv_unfl:
	bset		&unfl_bit,FPSR_EXCEPT(%a6) # set unfl exc bit

	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		&rz_mode*0x10,%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fdiv.x		FP_SCR0(%a6),%fp0	# execute divide

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x0b,%d1		# is UNFL or INEX enabled?
	bne.b		fdiv_unfl_ena		# yes

fdiv_unfl_dis:
	fmovm.x		&0x80,FP_SCR0(%a6)	# store out result

	lea		FP_SCR0(%a6),%a0	# pass: result addr
	mov.l		L_SCR3(%a6),%d1		# pass: rnd prec,mode
	bsr.l		unf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# 'Z' may have been set
	fmovm.x		FP_SCR0(%a6),&0x80	# return default result in fp0
	rts

#
# UNFL is enabled.
#
fdiv_unfl_ena:
	fmovm.x		FP_SCR1(%a6),&0x40	# load dst op

	mov.l		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# is precision extended?
	bne.b		fdiv_unfl_ena_sd	# no, sgl or dbl

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

fdiv_unfl_ena_cont:
	fmov.l		&0x0,%fpsr		# clear FPSR

	fdiv.x		FP_SCR0(%a6),%fp1	# execute divide

	fmov.l		&0x0,%fpcr		# clear FPCR

	fmovm.x		&0x40,FP_SCR0(%a6)	# save result to stack
	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factoer
	addi.l		&0x6000,%d1		# add bias
	andi.w		&0x7fff,%d1
	or.w		%d2,%d1			# concat old sign,new exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exp
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	bra.w		fdiv_unfl_dis

fdiv_unfl_ena_sd:
	mov.l		L_SCR3(%a6),%d1
	andi.b		&0x30,%d1		# use only rnd mode
	fmov.l		%d1,%fpcr		# set FPCR

	bra.b		fdiv_unfl_ena_cont

#
# the divide operation MAY underflow:
#
fdiv_may_unfl:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fdiv.x		FP_SCR0(%a6),%fp0	# execute divide

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	fabs.x		%fp0,%fp1		# make a copy of result
	fcmp.b		%fp1,&0x1		# is |result| > 1.b?
	fbgt.w		fdiv_normal_exit	# no; no underflow occurred
	fblt.w		fdiv_unfl		# yes; underflow occurred

#
# we still don't know if underflow occurred. result is ~ equal to 1. but,
# we don't know if the result was an underflow that rounded up to a 1
# or a normalized number that rounded down to a 1. so, redo the entire
# operation using RZ as the rounding mode to see what the pre-rounded
# result is. this case should be relatively rare.
#
	fmovm.x		FP_SCR1(%a6),&0x40	# load dst op into fp1

	mov.l		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# keep rnd prec
	ori.b		&rz_mode*0x10,%d1	# insert RZ

	fmov.l		%d1,%fpcr		# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fdiv.x		FP_SCR0(%a6),%fp1	# execute divide

	fmov.l		&0x0,%fpcr		# clear FPCR
	fabs.x		%fp1			# make absolute value
	fcmp.b		%fp1,&0x1		# is |result| < 1.b?
	fbge.w		fdiv_normal_exit	# no; no underflow occurred
	bra.w		fdiv_unfl		# yes; underflow occurred

############################################################################

#
# Divide: inputs are not both normalized; what are they?
#
fdiv_not_norm:
	mov.w		(tbl_fdiv_op.b,%pc,%d1.w*2),%d1
	jmp		(tbl_fdiv_op.b,%pc,%d1.w*1)

	swbeg		&48
tbl_fdiv_op:
	short		fdiv_norm	- tbl_fdiv_op # NORM / NORM
	short		fdiv_inf_load	- tbl_fdiv_op # NORM / ZERO
	short		fdiv_zero_load	- tbl_fdiv_op # NORM / INF
	short		fdiv_res_qnan	- tbl_fdiv_op # NORM / QNAN
	short		fdiv_norm	- tbl_fdiv_op # NORM / DENORM
	short		fdiv_res_snan	- tbl_fdiv_op # NORM / SNAN
	short		tbl_fdiv_op	- tbl_fdiv_op #
	short		tbl_fdiv_op	- tbl_fdiv_op #

	short		fdiv_zero_load	- tbl_fdiv_op # ZERO / NORM
	short		fdiv_res_operr	- tbl_fdiv_op # ZERO / ZERO
	short		fdiv_zero_load	- tbl_fdiv_op # ZERO / INF
	short		fdiv_res_qnan	- tbl_fdiv_op # ZERO / QNAN
	short		fdiv_zero_load	- tbl_fdiv_op # ZERO / DENORM
	short		fdiv_res_snan	- tbl_fdiv_op # ZERO / SNAN
	short		tbl_fdiv_op	- tbl_fdiv_op #
	short		tbl_fdiv_op	- tbl_fdiv_op #

	short		fdiv_inf_dst	- tbl_fdiv_op # INF / NORM
	short		fdiv_inf_dst	- tbl_fdiv_op # INF / ZERO
	short		fdiv_res_operr	- tbl_fdiv_op # INF / INF
	short		fdiv_res_qnan	- tbl_fdiv_op # INF / QNAN
	short		fdiv_inf_dst	- tbl_fdiv_op # INF / DENORM
	short		fdiv_res_snan	- tbl_fdiv_op # INF / SNAN
	short		tbl_fdiv_op	- tbl_fdiv_op #
	short		tbl_fdiv_op	- tbl_fdiv_op #

	short		fdiv_res_qnan	- tbl_fdiv_op # QNAN / NORM
	short		fdiv_res_qnan	- tbl_fdiv_op # QNAN / ZERO
	short		fdiv_res_qnan	- tbl_fdiv_op # QNAN / INF
	short		fdiv_res_qnan	- tbl_fdiv_op # QNAN / QNAN
	short		fdiv_res_qnan	- tbl_fdiv_op # QNAN / DENORM
	short		fdiv_res_snan	- tbl_fdiv_op # QNAN / SNAN
	short		tbl_fdiv_op	- tbl_fdiv_op #
	short		tbl_fdiv_op	- tbl_fdiv_op #

	short		fdiv_norm	- tbl_fdiv_op # DENORM / NORM
	short		fdiv_inf_load	- tbl_fdiv_op # DENORM / ZERO
	short		fdiv_zero_load	- tbl_fdiv_op # DENORM / INF
	short		fdiv_res_qnan	- tbl_fdiv_op # DENORM / QNAN
	short		fdiv_norm	- tbl_fdiv_op # DENORM / DENORM
	short		fdiv_res_snan	- tbl_fdiv_op # DENORM / SNAN
	short		tbl_fdiv_op	- tbl_fdiv_op #
	short		tbl_fdiv_op	- tbl_fdiv_op #

	short		fdiv_res_snan	- tbl_fdiv_op # SNAN / NORM
	short		fdiv_res_snan	- tbl_fdiv_op # SNAN / ZERO
	short		fdiv_res_snan	- tbl_fdiv_op # SNAN / INF
	short		fdiv_res_snan	- tbl_fdiv_op # SNAN / QNAN
	short		fdiv_res_snan	- tbl_fdiv_op # SNAN / DENORM
	short		fdiv_res_snan	- tbl_fdiv_op # SNAN / SNAN
	short		tbl_fdiv_op	- tbl_fdiv_op #
	short		tbl_fdiv_op	- tbl_fdiv_op #

fdiv_res_qnan:
	bra.l		res_qnan
fdiv_res_snan:
	bra.l		res_snan
fdiv_res_operr:
	bra.l		res_operr

	global		fdiv_zero_load		# global for fsgldiv
fdiv_zero_load:
	mov.b		SRC_EX(%a0),%d0		# result sign is exclusive
	mov.b		DST_EX(%a1),%d1		# or of input signs.
	eor.b		%d0,%d1
	bpl.b		fdiv_zero_load_p	# result is positive
	fmov.s		&0x80000000,%fp0	# load a -ZERO
	mov.b		&z_bmask+neg_bmask,FPSR_CC(%a6)	# set Z/N
	rts
fdiv_zero_load_p:
	fmov.s		&0x00000000,%fp0	# load a +ZERO
	mov.b		&z_bmask,FPSR_CC(%a6)	# set Z
	rts

#
# The destination was In Range and the source was a ZERO. The result,
# Therefore, is an INF w/ the proper sign.
# So, determine the sign and return a new INF (w/ the j-bit cleared).
#
	global		fdiv_inf_load		# global for fsgldiv
fdiv_inf_load:
	ori.w		&dz_mask+adz_mask,2+USER_FPSR(%a6) # no; set DZ/ADZ
	mov.b		SRC_EX(%a0),%d0		# load both signs
	mov.b		DST_EX(%a1),%d1
	eor.b		%d0,%d1
	bpl.b		fdiv_inf_load_p		# result is positive
	fmov.s		&0xff800000,%fp0	# make result -INF
	mov.b		&inf_bmask+neg_bmask,FPSR_CC(%a6) # set INF/N
	rts
fdiv_inf_load_p:
	fmov.s		&0x7f800000,%fp0	# make result +INF
	mov.b		&inf_bmask,FPSR_CC(%a6)	# set INF
	rts

#
# The destination was an INF w/ an In Range or ZERO source, the result is
# an INF w/ the proper sign.
# The 68881/882 returns the destination INF w/ the new sign(if the j-bit of the
# dst INF is set, then then j-bit of the result INF is also set).
#
	global		fdiv_inf_dst		# global for fsgldiv
fdiv_inf_dst:
	mov.b		DST_EX(%a1),%d0		# load both signs
	mov.b		SRC_EX(%a0),%d1
	eor.b		%d0,%d1
	bpl.b		fdiv_inf_dst_p		# result is positive

	fmovm.x		DST(%a1),&0x80		# return result in fp0
	fabs.x		%fp0			# clear sign bit
	fneg.x		%fp0			# set sign bit
	mov.b		&inf_bmask+neg_bmask,FPSR_CC(%a6) # set INF/NEG
	rts

fdiv_inf_dst_p:
	fmovm.x		DST(%a1),&0x80		# return result in fp0
	fabs.x		%fp0			# return positive INF
	mov.b		&inf_bmask,FPSR_CC(%a6) # set INF
	rts

#########################################################################
# XDEF ****************************************************************	#
#	fneg(): emulates the fneg instruction				#
#	fsneg(): emulates the fsneg instruction				#
#	fdneg(): emulates the fdneg instruction				#
#									#
# XREF ****************************************************************	#
#	norm() - normalize a denorm to provide EXOP			#
#	scale_to_zero_src() - scale sgl/dbl source exponent		#
#	ovf_res() - return default overflow result			#
#	unf_res() - return default underflow result			#
#	res_qnan_1op() - return QNAN result				#
#	res_snan_1op() - return SNAN result				#
#									#
# INPUT ***************************************************************	#
#	a0 = pointer to extended precision source operand		#
#	d0 = rnd prec,mode						#
#									#
# OUTPUT **************************************************************	#
#	fp0 = result							#
#	fp1 = EXOP (if exception occurred)				#
#									#
# ALGORITHM ***********************************************************	#
#	Handle NANs, zeroes, and infinities as special cases. Separate	#
# norms/denorms into ext/sgl/dbl precisions. Extended precision can be	#
# emulated by simply setting sign bit. Sgl/dbl operands must be scaled	#
# and an actual fneg performed to see if overflow/underflow would have	#
# occurred. If so, return default underflow/overflow result. Else,	#
# scale the result exponent and return result. FPSR gets set based on	#
# the result value.							#
#									#
#########################################################################

	global		fsneg
fsneg:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&s_mode*0x10,%d0	# insert sgl precision
	bra.b		fneg

	global		fdneg
fdneg:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&d_mode*0x10,%d0	# insert dbl prec

	global		fneg
fneg:
	mov.l		%d0,L_SCR3(%a6)		# store rnd info
	mov.b		STAG(%a6),%d1
	bne.w		fneg_not_norm		# optimize on non-norm input

#
# NEGATE SIGN : norms and denorms ONLY!
#
fneg_norm:
	andi.b		&0xc0,%d0		# is precision extended?
	bne.w		fneg_not_ext		# no; go handle sgl or dbl

#
# precision selected is extended. so...we can not get an underflow
# or overflow because of rounding to the correct precision. so...
# skip the scaling and unscaling...
#
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	mov.w		SRC_EX(%a0),%d0
	eori.w		&0x8000,%d0		# negate sign
	bpl.b		fneg_norm_load		# sign is positive
	mov.b		&neg_bmask,FPSR_CC(%a6)	# set 'N' ccode bit
fneg_norm_load:
	mov.w		%d0,FP_SCR0_EX(%a6)
	fmovm.x		FP_SCR0(%a6),&0x80	# return result in fp0
	rts

#
# for an extended precision DENORM, the UNFL exception bit is set
# the accrued bit is NOT set in this instance(no inexactness!)
#
fneg_denorm:
	andi.b		&0xc0,%d0		# is precision extended?
	bne.b		fneg_not_ext		# no; go handle sgl or dbl

	bset		&unfl_bit,FPSR_EXCEPT(%a6) # set unfl exc bit

	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	mov.w		SRC_EX(%a0),%d0
	eori.w		&0x8000,%d0		# negate sign
	bpl.b		fneg_denorm_done	# no
	mov.b		&neg_bmask,FPSR_CC(%a6)	# yes, set 'N' ccode bit
fneg_denorm_done:
	mov.w		%d0,FP_SCR0_EX(%a6)
	fmovm.x		FP_SCR0(%a6),&0x80	# return default result in fp0

	btst		&unfl_bit,FPCR_ENABLE(%a6) # is UNFL enabled?
	bne.b		fneg_ext_unfl_ena	# yes
	rts

#
# the input is an extended DENORM and underflow is enabled in the FPCR.
# normalize the mantissa and add the bias of 0x6000 to the resulting negative
# exponent and insert back into the operand.
#
fneg_ext_unfl_ena:
	lea		FP_SCR0(%a6),%a0	# pass: ptr to operand
	bsr.l		norm			# normalize result
	neg.w		%d0			# new exponent = -(shft val)
	addi.w		&0x6000,%d0		# add new bias to exponent
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch old sign,exp
	andi.w		&0x8000,%d1		# keep old sign
	andi.w		&0x7fff,%d0		# clear sign position
	or.w		%d1,%d0			# concat old sign, new exponent
	mov.w		%d0,FP_SCR0_EX(%a6)	# insert new exponent
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	rts

#
# operand is either single or double
#
fneg_not_ext:
	cmpi.b		%d0,&s_mode*0x10	# separate sgl/dbl prec
	bne.b		fneg_dbl

#
# operand is to be rounded to single precision
#
fneg_sgl:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	bsr.l		scale_to_zero_src	# calculate scale factor

	cmpi.l		%d0,&0x3fff-0x3f80	# will move in underflow?
	bge.w		fneg_sd_unfl		# yes; go handle underflow
	cmpi.l		%d0,&0x3fff-0x407e	# will move in overflow?
	beq.w		fneg_sd_may_ovfl	# maybe; go check
	blt.w		fneg_sd_ovfl		# yes; go handle overflow

#
# operand will NOT overflow or underflow when moved in to the fp reg file
#
fneg_sd_normal:
	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fneg.x		FP_SCR0(%a6),%fp0	# perform negation

	fmov.l		%fpsr,%d1		# save FPSR
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

fneg_sd_normal_exit:
	mov.l		%d2,-(%sp)		# save d2
	fmovm.x		&0x80,FP_SCR0(%a6)	# store out result
	mov.w		FP_SCR0_EX(%a6),%d1	# load sgn,exp
	mov.w		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	sub.l		%d0,%d1			# add scale factor
	andi.w		&0x8000,%d2		# keep old sign
	or.w		%d1,%d2			# concat old sign,new exp
	mov.w		%d2,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x80	# return result in fp0
	rts

#
# operand is to be rounded to double precision
#
fneg_dbl:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	bsr.l		scale_to_zero_src	# calculate scale factor

	cmpi.l		%d0,&0x3fff-0x3c00	# will move in underflow?
	bge.b		fneg_sd_unfl		# yes; go handle underflow
	cmpi.l		%d0,&0x3fff-0x43fe	# will move in overflow?
	beq.w		fneg_sd_may_ovfl	# maybe; go check
	blt.w		fneg_sd_ovfl		# yes; go handle overflow
	bra.w		fneg_sd_normal		# no; ho handle normalized op

#
# operand WILL underflow when moved in to the fp register file
#
fneg_sd_unfl:
	bset		&unfl_bit,FPSR_EXCEPT(%a6) # set unfl exc bit

	eori.b		&0x80,FP_SCR0_EX(%a6)	# negate sign
	bpl.b		fneg_sd_unfl_tst
	bset		&neg_bit,FPSR_CC(%a6)	# set 'N' ccode bit

# if underflow or inexact is enabled, go calculate EXOP first.
fneg_sd_unfl_tst:
	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x0b,%d1		# is UNFL or INEX enabled?
	bne.b		fneg_sd_unfl_ena	# yes

fneg_sd_unfl_dis:
	lea		FP_SCR0(%a6),%a0	# pass: result addr
	mov.l		L_SCR3(%a6),%d1		# pass: rnd prec,mode
	bsr.l		unf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# unf_res may have set 'Z'
	fmovm.x		FP_SCR0(%a6),&0x80	# return default result in fp0
	rts

#
# operand will underflow AND underflow is enabled.
# Therefore, we must return the result rounded to extended precision.
#
fneg_sd_unfl_ena:
	mov.l		FP_SCR0_HI(%a6),FP_SCR1_HI(%a6)
	mov.l		FP_SCR0_LO(%a6),FP_SCR1_LO(%a6)
	mov.w		FP_SCR0_EX(%a6),%d1	# load current exponent

	mov.l		%d2,-(%sp)		# save d2
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# subtract scale factor
	addi.l		&0x6000,%d1		# add new bias
	andi.w		&0x7fff,%d1
	or.w		%d2,%d1			# concat new sign,new exp
	mov.w		%d1,FP_SCR1_EX(%a6)	# insert new exp
	fmovm.x		FP_SCR1(%a6),&0x40	# return EXOP in fp1
	mov.l		(%sp)+,%d2		# restore d2
	bra.b		fneg_sd_unfl_dis

#
# operand WILL overflow.
#
fneg_sd_ovfl:
	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fneg.x		FP_SCR0(%a6),%fp0	# perform negation

	fmov.l		&0x0,%fpcr		# clear FPCR
	fmov.l		%fpsr,%d1		# save FPSR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

fneg_sd_ovfl_tst:
	or.l		&ovfl_inx_mask,USER_FPSR(%a6) # set ovfl/aovfl/ainex

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x13,%d1		# is OVFL or INEX enabled?
	bne.b		fneg_sd_ovfl_ena	# yes

#
# OVFL is not enabled; therefore, we must create the default result by
# calling ovf_res().
#
fneg_sd_ovfl_dis:
	btst		&neg_bit,FPSR_CC(%a6)	# is result negative?
	sne		%d1			# set sign param accordingly
	mov.l		L_SCR3(%a6),%d0		# pass: prec,mode
	bsr.l		ovf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# set INF,N if applicable
	fmovm.x		(%a0),&0x80		# return default result in fp0
	rts

#
# OVFL is enabled.
# the INEX2 bit has already been updated by the round to the correct precision.
# now, round to extended(and don't alter the FPSR).
#
fneg_sd_ovfl_ena:
	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	subi.l		&0x6000,%d1		# subtract bias
	andi.w		&0x7fff,%d1
	or.w		%d2,%d1			# concat sign,exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	mov.l		(%sp)+,%d2		# restore d2
	bra.b		fneg_sd_ovfl_dis

#
# the move in MAY underflow. so...
#
fneg_sd_may_ovfl:
	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fneg.x		FP_SCR0(%a6),%fp0	# perform negation

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	fabs.x		%fp0,%fp1		# make a copy of result
	fcmp.b		%fp1,&0x2		# is |result| >= 2.b?
	fbge.w		fneg_sd_ovfl_tst	# yes; overflow has occurred

# no, it didn't overflow; we have correct result
	bra.w		fneg_sd_normal_exit

##########################################################################

#
# input is not normalized; what is it?
#
fneg_not_norm:
	cmpi.b		%d1,&DENORM		# weed out DENORM
	beq.w		fneg_denorm
	cmpi.b		%d1,&SNAN		# weed out SNAN
	beq.l		res_snan_1op
	cmpi.b		%d1,&QNAN		# weed out QNAN
	beq.l		res_qnan_1op

#
# do the fneg; at this point, only possible ops are ZERO and INF.
# use fneg to determine ccodes.
# prec:mode should be zero at this point but it won't affect answer anyways.
#
	fneg.x		SRC_EX(%a0),%fp0	# do fneg
	fmov.l		%fpsr,%d0
	rol.l		&0x8,%d0		# put ccodes in lo byte
	mov.b		%d0,FPSR_CC(%a6)	# insert correct ccodes
	rts

#########################################################################
# XDEF ****************************************************************	#
#	ftst(): emulates the ftest instruction				#
#									#
# XREF ****************************************************************	#
#	res{s,q}nan_1op() - set NAN result for monadic instruction	#
#									#
# INPUT ***************************************************************	#
#	a0 = pointer to extended precision source operand		#
#									#
# OUTPUT **************************************************************	#
#	none								#
#									#
# ALGORITHM ***********************************************************	#
#	Check the source operand tag (STAG) and set the FPCR according	#
# to the operand type and sign.						#
#									#
#########################################################################

	global		ftst
ftst:
	mov.b		STAG(%a6),%d1
	bne.b		ftst_not_norm		# optimize on non-norm input

#
# Norm:
#
ftst_norm:
	tst.b		SRC_EX(%a0)		# is operand negative?
	bmi.b		ftst_norm_m		# yes
	rts
ftst_norm_m:
	mov.b		&neg_bmask,FPSR_CC(%a6)	# set 'N' ccode bit
	rts

#
# input is not normalized; what is it?
#
ftst_not_norm:
	cmpi.b		%d1,&ZERO		# weed out ZERO
	beq.b		ftst_zero
	cmpi.b		%d1,&INF		# weed out INF
	beq.b		ftst_inf
	cmpi.b		%d1,&SNAN		# weed out SNAN
	beq.l		res_snan_1op
	cmpi.b		%d1,&QNAN		# weed out QNAN
	beq.l		res_qnan_1op

#
# Denorm:
#
ftst_denorm:
	tst.b		SRC_EX(%a0)		# is operand negative?
	bmi.b		ftst_denorm_m		# yes
	rts
ftst_denorm_m:
	mov.b		&neg_bmask,FPSR_CC(%a6)	# set 'N' ccode bit
	rts

#
# Infinity:
#
ftst_inf:
	tst.b		SRC_EX(%a0)		# is operand negative?
	bmi.b		ftst_inf_m		# yes
ftst_inf_p:
	mov.b		&inf_bmask,FPSR_CC(%a6)	# set 'I' ccode bit
	rts
ftst_inf_m:
	mov.b		&inf_bmask+neg_bmask,FPSR_CC(%a6) # set 'I','N' ccode bits
	rts

#
# Zero:
#
ftst_zero:
	tst.b		SRC_EX(%a0)		# is operand negative?
	bmi.b		ftst_zero_m		# yes
ftst_zero_p:
	mov.b		&z_bmask,FPSR_CC(%a6)	# set 'N' ccode bit
	rts
ftst_zero_m:
	mov.b		&z_bmask+neg_bmask,FPSR_CC(%a6)	# set 'Z','N' ccode bits
	rts

#########################################################################
# XDEF ****************************************************************	#
#	fint(): emulates the fint instruction				#
#									#
# XREF ****************************************************************	#
#	res_{s,q}nan_1op() - set NAN result for monadic operation	#
#									#
# INPUT ***************************************************************	#
#	a0 = pointer to extended precision source operand		#
#	d0 = round precision/mode					#
#									#
# OUTPUT **************************************************************	#
#	fp0 = result							#
#									#
# ALGORITHM ***********************************************************	#
#	Separate according to operand type. Unnorms don't pass through	#
# here. For norms, load the rounding mode/prec, execute a "fint", then	#
# store the resulting FPSR bits.					#
#	For denorms, force the j-bit to a one and do the same as for	#
# norms. Denorms are so low that the answer will either be a zero or a	#
# one.									#
#	For zeroes/infs/NANs, return the same while setting the FPSR	#
# as appropriate.							#
#									#
#########################################################################

	global		fint
fint:
	mov.b		STAG(%a6),%d1
	bne.b		fint_not_norm		# optimize on non-norm input

#
# Norm:
#
fint_norm:
	andi.b		&0x30,%d0		# set prec = ext

	fmov.l		%d0,%fpcr		# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fint.x		SRC(%a0),%fp0		# execute fint

	fmov.l		&0x0,%fpcr		# clear FPCR
	fmov.l		%fpsr,%d0		# save FPSR
	or.l		%d0,USER_FPSR(%a6)	# set exception bits

	rts

#
# input is not normalized; what is it?
#
fint_not_norm:
	cmpi.b		%d1,&ZERO		# weed out ZERO
	beq.b		fint_zero
	cmpi.b		%d1,&INF		# weed out INF
	beq.b		fint_inf
	cmpi.b		%d1,&DENORM		# weed out DENORM
	beq.b		fint_denorm
	cmpi.b		%d1,&SNAN		# weed out SNAN
	beq.l		res_snan_1op
	bra.l		res_qnan_1op		# weed out QNAN

#
# Denorm:
#
# for DENORMs, the result will be either (+/-)ZERO or (+/-)1.
# also, the INEX2 and AINEX exception bits will be set.
# so, we could either set these manually or force the DENORM
# to a very small NORM and ship it to the NORM routine.
# I do the latter.
#
fint_denorm:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6) # copy sign, zero exp
	mov.b		&0x80,FP_SCR0_HI(%a6)	# force DENORM ==> small NORM
	lea		FP_SCR0(%a6),%a0
	bra.b		fint_norm

#
# Zero:
#
fint_zero:
	tst.b		SRC_EX(%a0)		# is ZERO negative?
	bmi.b		fint_zero_m		# yes
fint_zero_p:
	fmov.s		&0x00000000,%fp0	# return +ZERO in fp0
	mov.b		&z_bmask,FPSR_CC(%a6)	# set 'Z' ccode bit
	rts
fint_zero_m:
	fmov.s		&0x80000000,%fp0	# return -ZERO in fp0
	mov.b		&z_bmask+neg_bmask,FPSR_CC(%a6) # set 'Z','N' ccode bits
	rts

#
# Infinity:
#
fint_inf:
	fmovm.x		SRC(%a0),&0x80		# return result in fp0
	tst.b		SRC_EX(%a0)		# is INF negative?
	bmi.b		fint_inf_m		# yes
fint_inf_p:
	mov.b		&inf_bmask,FPSR_CC(%a6)	# set 'I' ccode bit
	rts
fint_inf_m:
	mov.b		&inf_bmask+neg_bmask,FPSR_CC(%a6) # set 'N','I' ccode bits
	rts

#########################################################################
# XDEF ****************************************************************	#
#	fintrz(): emulates the fintrz instruction			#
#									#
# XREF ****************************************************************	#
#	res_{s,q}nan_1op() - set NAN result for monadic operation	#
#									#
# INPUT ***************************************************************	#
#	a0 = pointer to extended precision source operand		#
#	d0 = round precision/mode					#
#									#
# OUTPUT **************************************************************	#
#	fp0 = result							#
#									#
# ALGORITHM ***********************************************************	#
#	Separate according to operand type. Unnorms don't pass through	#
# here. For norms, load the rounding mode/prec, execute a "fintrz",	#
# then store the resulting FPSR bits.					#
#	For denorms, force the j-bit to a one and do the same as for	#
# norms. Denorms are so low that the answer will either be a zero or a	#
# one.									#
#	For zeroes/infs/NANs, return the same while setting the FPSR	#
# as appropriate.							#
#									#
#########################################################################

	global		fintrz
fintrz:
	mov.b		STAG(%a6),%d1
	bne.b		fintrz_not_norm		# optimize on non-norm input

#
# Norm:
#
fintrz_norm:
	fmov.l		&0x0,%fpsr		# clear FPSR

	fintrz.x	SRC(%a0),%fp0		# execute fintrz

	fmov.l		%fpsr,%d0		# save FPSR
	or.l		%d0,USER_FPSR(%a6)	# set exception bits

	rts

#
# input is not normalized; what is it?
#
fintrz_not_norm:
	cmpi.b		%d1,&ZERO		# weed out ZERO
	beq.b		fintrz_zero
	cmpi.b		%d1,&INF		# weed out INF
	beq.b		fintrz_inf
	cmpi.b		%d1,&DENORM		# weed out DENORM
	beq.b		fintrz_denorm
	cmpi.b		%d1,&SNAN		# weed out SNAN
	beq.l		res_snan_1op
	bra.l		res_qnan_1op		# weed out QNAN

#
# Denorm:
#
# for DENORMs, the result will be (+/-)ZERO.
# also, the INEX2 and AINEX exception bits will be set.
# so, we could either set these manually or force the DENORM
# to a very small NORM and ship it to the NORM routine.
# I do the latter.
#
fintrz_denorm:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6) # copy sign, zero exp
	mov.b		&0x80,FP_SCR0_HI(%a6)	# force DENORM ==> small NORM
	lea		FP_SCR0(%a6),%a0
	bra.b		fintrz_norm

#
# Zero:
#
fintrz_zero:
	tst.b		SRC_EX(%a0)		# is ZERO negative?
	bmi.b		fintrz_zero_m		# yes
fintrz_zero_p:
	fmov.s		&0x00000000,%fp0	# return +ZERO in fp0
	mov.b		&z_bmask,FPSR_CC(%a6)	# set 'Z' ccode bit
	rts
fintrz_zero_m:
	fmov.s		&0x80000000,%fp0	# return -ZERO in fp0
	mov.b		&z_bmask+neg_bmask,FPSR_CC(%a6) # set 'Z','N' ccode bits
	rts

#
# Infinity:
#
fintrz_inf:
	fmovm.x		SRC(%a0),&0x80		# return result in fp0
	tst.b		SRC_EX(%a0)		# is INF negative?
	bmi.b		fintrz_inf_m		# yes
fintrz_inf_p:
	mov.b		&inf_bmask,FPSR_CC(%a6)	# set 'I' ccode bit
	rts
fintrz_inf_m:
	mov.b		&inf_bmask+neg_bmask,FPSR_CC(%a6) # set 'N','I' ccode bits
	rts

#########################################################################
# XDEF ****************************************************************	#
#	fabs():  emulates the fabs instruction				#
#	fsabs(): emulates the fsabs instruction				#
#	fdabs(): emulates the fdabs instruction				#
#									#
# XREF **************************************************************** #
#	norm() - normalize denorm mantissa to provide EXOP		#
#	scale_to_zero_src() - make exponent. = 0; get scale factor	#
#	unf_res() - calculate underflow result				#
#	ovf_res() - calculate overflow result				#
#	res_{s,q}nan_1op() - set NAN result for monadic operation	#
#									#
# INPUT *************************************************************** #
#	a0 = pointer to extended precision source operand		#
#	d0 = rnd precision/mode						#
#									#
# OUTPUT ************************************************************** #
#	fp0 = result							#
#	fp1 = EXOP (if exception occurred)				#
#									#
# ALGORITHM ***********************************************************	#
#	Handle NANs, infinities, and zeroes as special cases. Divide	#
# norms into extended, single, and double precision.			#
#	Simply clear sign for extended precision norm. Ext prec denorm	#
# gets an EXOP created for it since it's an underflow.			#
#	Double and single precision can overflow and underflow. First,	#
# scale the operand such that the exponent is zero. Perform an "fabs"	#
# using the correct rnd mode/prec. Check to see if the original		#
# exponent would take an exception. If so, use unf_res() or ovf_res()	#
# to calculate the default result. Also, create the EXOP for the	#
# exceptional case. If no exception should occur, insert the correct	#
# result exponent and return.						#
#	Unnorms don't pass through here.				#
#									#
#########################################################################

	global		fsabs
fsabs:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&s_mode*0x10,%d0	# insert sgl precision
	bra.b		fabs

	global		fdabs
fdabs:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&d_mode*0x10,%d0	# insert dbl precision

	global		fabs
fabs:
	mov.l		%d0,L_SCR3(%a6)		# store rnd info
	mov.b		STAG(%a6),%d1
	bne.w		fabs_not_norm		# optimize on non-norm input

#
# ABSOLUTE VALUE: norms and denorms ONLY!
#
fabs_norm:
	andi.b		&0xc0,%d0		# is precision extended?
	bne.b		fabs_not_ext		# no; go handle sgl or dbl

#
# precision selected is extended. so...we can not get an underflow
# or overflow because of rounding to the correct precision. so...
# skip the scaling and unscaling...
#
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	mov.w		SRC_EX(%a0),%d1
	bclr		&15,%d1			# force absolute value
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert exponent
	fmovm.x		FP_SCR0(%a6),&0x80	# return result in fp0
	rts

#
# for an extended precision DENORM, the UNFL exception bit is set
# the accrued bit is NOT set in this instance(no inexactness!)
#
fabs_denorm:
	andi.b		&0xc0,%d0		# is precision extended?
	bne.b		fabs_not_ext		# no

	bset		&unfl_bit,FPSR_EXCEPT(%a6) # set unfl exc bit

	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	mov.w		SRC_EX(%a0),%d0
	bclr		&15,%d0			# clear sign
	mov.w		%d0,FP_SCR0_EX(%a6)	# insert exponent

	fmovm.x		FP_SCR0(%a6),&0x80	# return default result in fp0

	btst		&unfl_bit,FPCR_ENABLE(%a6) # is UNFL enabled?
	bne.b		fabs_ext_unfl_ena
	rts

#
# the input is an extended DENORM and underflow is enabled in the FPCR.
# normalize the mantissa and add the bias of 0x6000 to the resulting negative
# exponent and insert back into the operand.
#
fabs_ext_unfl_ena:
	lea		FP_SCR0(%a6),%a0	# pass: ptr to operand
	bsr.l		norm			# normalize result
	neg.w		%d0			# new exponent = -(shft val)
	addi.w		&0x6000,%d0		# add new bias to exponent
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch old sign,exp
	andi.w		&0x8000,%d1		# keep old sign
	andi.w		&0x7fff,%d0		# clear sign position
	or.w		%d1,%d0			# concat old sign, new exponent
	mov.w		%d0,FP_SCR0_EX(%a6)	# insert new exponent
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	rts

#
# operand is either single or double
#
fabs_not_ext:
	cmpi.b		%d0,&s_mode*0x10	# separate sgl/dbl prec
	bne.b		fabs_dbl

#
# operand is to be rounded to single precision
#
fabs_sgl:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	bsr.l		scale_to_zero_src	# calculate scale factor

	cmpi.l		%d0,&0x3fff-0x3f80	# will move in underflow?
	bge.w		fabs_sd_unfl		# yes; go handle underflow
	cmpi.l		%d0,&0x3fff-0x407e	# will move in overflow?
	beq.w		fabs_sd_may_ovfl	# maybe; go check
	blt.w		fabs_sd_ovfl		# yes; go handle overflow

#
# operand will NOT overflow or underflow when moved in to the fp reg file
#
fabs_sd_normal:
	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fabs.x		FP_SCR0(%a6),%fp0	# perform absolute

	fmov.l		%fpsr,%d1		# save FPSR
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

fabs_sd_normal_exit:
	mov.l		%d2,-(%sp)		# save d2
	fmovm.x		&0x80,FP_SCR0(%a6)	# store out result
	mov.w		FP_SCR0_EX(%a6),%d1	# load sgn,exp
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	sub.l		%d0,%d1			# add scale factor
	andi.w		&0x8000,%d2		# keep old sign
	or.w		%d1,%d2			# concat old sign,new exp
	mov.w		%d2,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x80	# return result in fp0
	rts

#
# operand is to be rounded to double precision
#
fabs_dbl:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	bsr.l		scale_to_zero_src	# calculate scale factor

	cmpi.l		%d0,&0x3fff-0x3c00	# will move in underflow?
	bge.b		fabs_sd_unfl		# yes; go handle underflow
	cmpi.l		%d0,&0x3fff-0x43fe	# will move in overflow?
	beq.w		fabs_sd_may_ovfl	# maybe; go check
	blt.w		fabs_sd_ovfl		# yes; go handle overflow
	bra.w		fabs_sd_normal		# no; ho handle normalized op

#
# operand WILL underflow when moved in to the fp register file
#
fabs_sd_unfl:
	bset		&unfl_bit,FPSR_EXCEPT(%a6) # set unfl exc bit

	bclr		&0x7,FP_SCR0_EX(%a6)	# force absolute value

# if underflow or inexact is enabled, go calculate EXOP first.
	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x0b,%d1		# is UNFL or INEX enabled?
	bne.b		fabs_sd_unfl_ena	# yes

fabs_sd_unfl_dis:
	lea		FP_SCR0(%a6),%a0	# pass: result addr
	mov.l		L_SCR3(%a6),%d1		# pass: rnd prec,mode
	bsr.l		unf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# set possible 'Z' ccode
	fmovm.x		FP_SCR0(%a6),&0x80	# return default result in fp0
	rts

#
# operand will underflow AND underflow is enabled.
# Therefore, we must return the result rounded to extended precision.
#
fabs_sd_unfl_ena:
	mov.l		FP_SCR0_HI(%a6),FP_SCR1_HI(%a6)
	mov.l		FP_SCR0_LO(%a6),FP_SCR1_LO(%a6)
	mov.w		FP_SCR0_EX(%a6),%d1	# load current exponent

	mov.l		%d2,-(%sp)		# save d2
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# subtract scale factor
	addi.l		&0x6000,%d1		# add new bias
	andi.w		&0x7fff,%d1
	or.w		%d2,%d1			# concat new sign,new exp
	mov.w		%d1,FP_SCR1_EX(%a6)	# insert new exp
	fmovm.x		FP_SCR1(%a6),&0x40	# return EXOP in fp1
	mov.l		(%sp)+,%d2		# restore d2
	bra.b		fabs_sd_unfl_dis

#
# operand WILL overflow.
#
fabs_sd_ovfl:
	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fabs.x		FP_SCR0(%a6),%fp0	# perform absolute

	fmov.l		&0x0,%fpcr		# clear FPCR
	fmov.l		%fpsr,%d1		# save FPSR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

fabs_sd_ovfl_tst:
	or.l		&ovfl_inx_mask,USER_FPSR(%a6) # set ovfl/aovfl/ainex

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x13,%d1		# is OVFL or INEX enabled?
	bne.b		fabs_sd_ovfl_ena	# yes

#
# OVFL is not enabled; therefore, we must create the default result by
# calling ovf_res().
#
fabs_sd_ovfl_dis:
	btst		&neg_bit,FPSR_CC(%a6)	# is result negative?
	sne		%d1			# set sign param accordingly
	mov.l		L_SCR3(%a6),%d0		# pass: prec,mode
	bsr.l		ovf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# set INF,N if applicable
	fmovm.x		(%a0),&0x80		# return default result in fp0
	rts

#
# OVFL is enabled.
# the INEX2 bit has already been updated by the round to the correct precision.
# now, round to extended(and don't alter the FPSR).
#
fabs_sd_ovfl_ena:
	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	subi.l		&0x6000,%d1		# subtract bias
	andi.w		&0x7fff,%d1
	or.w		%d2,%d1			# concat sign,exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	mov.l		(%sp)+,%d2		# restore d2
	bra.b		fabs_sd_ovfl_dis

#
# the move in MAY underflow. so...
#
fabs_sd_may_ovfl:
	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fabs.x		FP_SCR0(%a6),%fp0	# perform absolute

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	fabs.x		%fp0,%fp1		# make a copy of result
	fcmp.b		%fp1,&0x2		# is |result| >= 2.b?
	fbge.w		fabs_sd_ovfl_tst	# yes; overflow has occurred

# no, it didn't overflow; we have correct result
	bra.w		fabs_sd_normal_exit

##########################################################################

#
# input is not normalized; what is it?
#
fabs_not_norm:
	cmpi.b		%d1,&DENORM		# weed out DENORM
	beq.w		fabs_denorm
	cmpi.b		%d1,&SNAN		# weed out SNAN
	beq.l		res_snan_1op
	cmpi.b		%d1,&QNAN		# weed out QNAN
	beq.l		res_qnan_1op

	fabs.x		SRC(%a0),%fp0		# force absolute value

	cmpi.b		%d1,&INF		# weed out INF
	beq.b		fabs_inf
fabs_zero:
	mov.b		&z_bmask,FPSR_CC(%a6)	# set 'Z' ccode bit
	rts
fabs_inf:
	mov.b		&inf_bmask,FPSR_CC(%a6)	# set 'I' ccode bit
	rts

#########################################################################
# XDEF ****************************************************************	#
#	fcmp(): fp compare op routine					#
#									#
# XREF ****************************************************************	#
#	res_qnan() - return QNAN result					#
#	res_snan() - return SNAN result					#
#									#
# INPUT ***************************************************************	#
#	a0 = pointer to extended precision source operand		#
#	a1 = pointer to extended precision destination operand		#
#	d0 = round prec/mode						#
#									#
# OUTPUT ************************************************************** #
#	None								#
#									#
# ALGORITHM ***********************************************************	#
#	Handle NANs and denorms as special cases. For everything else,	#
# just use the actual fcmp instruction to produce the correct condition	#
# codes.								#
#									#
#########################################################################

	global		fcmp
fcmp:
	clr.w		%d1
	mov.b		DTAG(%a6),%d1
	lsl.b		&0x3,%d1
	or.b		STAG(%a6),%d1
	bne.b		fcmp_not_norm		# optimize on non-norm input

#
# COMPARE FP OPs : NORMs, ZEROs, INFs, and "corrected" DENORMs
#
fcmp_norm:
	fmovm.x		DST(%a1),&0x80		# load dst op

	fcmp.x		%fp0,SRC(%a0)		# do compare

	fmov.l		%fpsr,%d0		# save FPSR
	rol.l		&0x8,%d0		# extract ccode bits
	mov.b		%d0,FPSR_CC(%a6)	# set ccode bits(no exc bits are set)

	rts

#
# fcmp: inputs are not both normalized; what are they?
#
fcmp_not_norm:
	mov.w		(tbl_fcmp_op.b,%pc,%d1.w*2),%d1
	jmp		(tbl_fcmp_op.b,%pc,%d1.w*1)

	swbeg		&48
tbl_fcmp_op:
	short		fcmp_norm	- tbl_fcmp_op # NORM - NORM
	short		fcmp_norm	- tbl_fcmp_op # NORM - ZERO
	short		fcmp_norm	- tbl_fcmp_op # NORM - INF
	short		fcmp_res_qnan	- tbl_fcmp_op # NORM - QNAN
	short		fcmp_nrm_dnrm	- tbl_fcmp_op # NORM - DENORM
	short		fcmp_res_snan	- tbl_fcmp_op # NORM - SNAN
	short		tbl_fcmp_op	- tbl_fcmp_op #
	short		tbl_fcmp_op	- tbl_fcmp_op #

	short		fcmp_norm	- tbl_fcmp_op # ZERO - NORM
	short		fcmp_norm	- tbl_fcmp_op # ZERO - ZERO
	short		fcmp_norm	- tbl_fcmp_op # ZERO - INF
	short		fcmp_res_qnan	- tbl_fcmp_op # ZERO - QNAN
	short		fcmp_dnrm_s	- tbl_fcmp_op # ZERO - DENORM
	short		fcmp_res_snan	- tbl_fcmp_op # ZERO - SNAN
	short		tbl_fcmp_op	- tbl_fcmp_op #
	short		tbl_fcmp_op	- tbl_fcmp_op #

	short		fcmp_norm	- tbl_fcmp_op # INF - NORM
	short		fcmp_norm	- tbl_fcmp_op # INF - ZERO
	short		fcmp_norm	- tbl_fcmp_op # INF - INF
	short		fcmp_res_qnan	- tbl_fcmp_op # INF - QNAN
	short		fcmp_dnrm_s	- tbl_fcmp_op # INF - DENORM
	short		fcmp_res_snan	- tbl_fcmp_op # INF - SNAN
	short		tbl_fcmp_op	- tbl_fcmp_op #
	short		tbl_fcmp_op	- tbl_fcmp_op #

	short		fcmp_res_qnan	- tbl_fcmp_op # QNAN - NORM
	short		fcmp_res_qnan	- tbl_fcmp_op # QNAN - ZERO
	short		fcmp_res_qnan	- tbl_fcmp_op # QNAN - INF
	short		fcmp_res_qnan	- tbl_fcmp_op # QNAN - QNAN
	short		fcmp_res_qnan	- tbl_fcmp_op # QNAN - DENORM
	short		fcmp_res_snan	- tbl_fcmp_op # QNAN - SNAN
	short		tbl_fcmp_op	- tbl_fcmp_op #
	short		tbl_fcmp_op	- tbl_fcmp_op #

	short		fcmp_dnrm_nrm	- tbl_fcmp_op # DENORM - NORM
	short		fcmp_dnrm_d	- tbl_fcmp_op # DENORM - ZERO
	short		fcmp_dnrm_d	- tbl_fcmp_op # DENORM - INF
	short		fcmp_res_qnan	- tbl_fcmp_op # DENORM - QNAN
	short		fcmp_dnrm_sd	- tbl_fcmp_op # DENORM - DENORM
	short		fcmp_res_snan	- tbl_fcmp_op # DENORM - SNAN
	short		tbl_fcmp_op	- tbl_fcmp_op #
	short		tbl_fcmp_op	- tbl_fcmp_op #

	short		fcmp_res_snan	- tbl_fcmp_op # SNAN - NORM
	short		fcmp_res_snan	- tbl_fcmp_op # SNAN - ZERO
	short		fcmp_res_snan	- tbl_fcmp_op # SNAN - INF
	short		fcmp_res_snan	- tbl_fcmp_op # SNAN - QNAN
	short		fcmp_res_snan	- tbl_fcmp_op # SNAN - DENORM
	short		fcmp_res_snan	- tbl_fcmp_op # SNAN - SNAN
	short		tbl_fcmp_op	- tbl_fcmp_op #
	short		tbl_fcmp_op	- tbl_fcmp_op #

# unlike all other functions for QNAN and SNAN, fcmp does NOT set the
# 'N' bit for a negative QNAN or SNAN input so we must squelch it here.
fcmp_res_qnan:
	bsr.l		res_qnan
	andi.b		&0xf7,FPSR_CC(%a6)
	rts
fcmp_res_snan:
	bsr.l		res_snan
	andi.b		&0xf7,FPSR_CC(%a6)
	rts

#
# DENORMs are a little more difficult.
# If you have a 2 DENORMs, then you can just force the j-bit to a one
# and use the fcmp_norm routine.
# If you have a DENORM and an INF or ZERO, just force the DENORM's j-bit to a one
# and use the fcmp_norm routine.
# If you have a DENORM and a NORM with opposite signs, then use fcmp_norm, also.
# But with a DENORM and a NORM of the same sign, the neg bit is set if the
# (1) signs are (+) and the DENORM is the dst or
# (2) signs are (-) and the DENORM is the src
#

fcmp_dnrm_s:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),%d0
	bset		&31,%d0			# DENORM src; make into small norm
	mov.l		%d0,FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	lea		FP_SCR0(%a6),%a0
	bra.w		fcmp_norm

fcmp_dnrm_d:
	mov.l		DST_EX(%a1),FP_SCR0_EX(%a6)
	mov.l		DST_HI(%a1),%d0
	bset		&31,%d0			# DENORM src; make into small norm
	mov.l		%d0,FP_SCR0_HI(%a6)
	mov.l		DST_LO(%a1),FP_SCR0_LO(%a6)
	lea		FP_SCR0(%a6),%a1
	bra.w		fcmp_norm

fcmp_dnrm_sd:
	mov.w		DST_EX(%a1),FP_SCR1_EX(%a6)
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		DST_HI(%a1),%d0
	bset		&31,%d0			# DENORM dst; make into small norm
	mov.l		%d0,FP_SCR1_HI(%a6)
	mov.l		SRC_HI(%a0),%d0
	bset		&31,%d0			# DENORM dst; make into small norm
	mov.l		%d0,FP_SCR0_HI(%a6)
	mov.l		DST_LO(%a1),FP_SCR1_LO(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	lea		FP_SCR1(%a6),%a1
	lea		FP_SCR0(%a6),%a0
	bra.w		fcmp_norm

fcmp_nrm_dnrm:
	mov.b		SRC_EX(%a0),%d0		# determine if like signs
	mov.b		DST_EX(%a1),%d1
	eor.b		%d0,%d1
	bmi.w		fcmp_dnrm_s

# signs are the same, so must determine the answer ourselves.
	tst.b		%d0			# is src op negative?
	bmi.b		fcmp_nrm_dnrm_m		# yes
	rts
fcmp_nrm_dnrm_m:
	mov.b		&neg_bmask,FPSR_CC(%a6)	# set 'Z' ccode bit
	rts

fcmp_dnrm_nrm:
	mov.b		SRC_EX(%a0),%d0		# determine if like signs
	mov.b		DST_EX(%a1),%d1
	eor.b		%d0,%d1
	bmi.w		fcmp_dnrm_d

# signs are the same, so must determine the answer ourselves.
	tst.b		%d0			# is src op negative?
	bpl.b		fcmp_dnrm_nrm_m		# no
	rts
fcmp_dnrm_nrm_m:
	mov.b		&neg_bmask,FPSR_CC(%a6)	# set 'Z' ccode bit
	rts

#########################################################################
# XDEF ****************************************************************	#
#	fsglmul(): emulates the fsglmul instruction			#
#									#
# XREF ****************************************************************	#
#	scale_to_zero_src() - scale src exponent to zero		#
#	scale_to_zero_dst() - scale dst exponent to zero		#
#	unf_res4() - return default underflow result for sglop		#
#	ovf_res() - return default overflow result			#
#	res_qnan() - return QNAN result					#
#	res_snan() - return SNAN result					#
#									#
# INPUT ***************************************************************	#
#	a0 = pointer to extended precision source operand		#
#	a1 = pointer to extended precision destination operand		#
#	d0  rnd prec,mode						#
#									#
# OUTPUT **************************************************************	#
#	fp0 = result							#
#	fp1 = EXOP (if exception occurred)				#
#									#
# ALGORITHM ***********************************************************	#
#	Handle NANs, infinities, and zeroes as special cases. Divide	#
# norms/denorms into ext/sgl/dbl precision.				#
#	For norms/denorms, scale the exponents such that a multiply	#
# instruction won't cause an exception. Use the regular fsglmul to	#
# compute a result. Check if the regular operands would have taken	#
# an exception. If so, return the default overflow/underflow result	#
# and return the EXOP if exceptions are enabled. Else, scale the	#
# result operand to the proper exponent.				#
#									#
#########################################################################

	global		fsglmul
fsglmul:
	mov.l		%d0,L_SCR3(%a6)		# store rnd info

	clr.w		%d1
	mov.b		DTAG(%a6),%d1
	lsl.b		&0x3,%d1
	or.b		STAG(%a6),%d1

	bne.w		fsglmul_not_norm	# optimize on non-norm input

fsglmul_norm:
	mov.w		DST_EX(%a1),FP_SCR1_EX(%a6)
	mov.l		DST_HI(%a1),FP_SCR1_HI(%a6)
	mov.l		DST_LO(%a1),FP_SCR1_LO(%a6)

	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)

	bsr.l		scale_to_zero_src	# scale exponent
	mov.l		%d0,-(%sp)		# save scale factor 1

	bsr.l		scale_to_zero_dst	# scale dst exponent

	add.l		(%sp)+,%d0		# SCALE_FACTOR = scale1 + scale2

	cmpi.l		%d0,&0x3fff-0x7ffe	# would result ovfl?
	beq.w		fsglmul_may_ovfl	# result may rnd to overflow
	blt.w		fsglmul_ovfl		# result will overflow

	cmpi.l		%d0,&0x3fff+0x0001	# would result unfl?
	beq.w		fsglmul_may_unfl	# result may rnd to no unfl
	bgt.w		fsglmul_unfl		# result will underflow

fsglmul_normal:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsglmul.x	FP_SCR0(%a6),%fp0	# execute sgl multiply

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

fsglmul_normal_exit:
	fmovm.x		&0x80,FP_SCR0(%a6)	# store out result
	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# load {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	or.w		%d2,%d1			# concat old sign,new exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x80	# return result in fp0
	rts

fsglmul_ovfl:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsglmul.x	FP_SCR0(%a6),%fp0	# execute sgl multiply

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

fsglmul_ovfl_tst:

# save setting this until now because this is where fsglmul_may_ovfl may jump in
	or.l		&ovfl_inx_mask, USER_FPSR(%a6) # set ovfl/aovfl/ainex

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x13,%d1		# is OVFL or INEX enabled?
	bne.b		fsglmul_ovfl_ena	# yes

fsglmul_ovfl_dis:
	btst		&neg_bit,FPSR_CC(%a6)	# is result negative?
	sne		%d1			# set sign param accordingly
	mov.l		L_SCR3(%a6),%d0		# pass prec:rnd
	andi.b		&0x30,%d0		# force prec = ext
	bsr.l		ovf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# set INF,N if applicable
	fmovm.x		(%a0),&0x80		# return default result in fp0
	rts

fsglmul_ovfl_ena:
	fmovm.x		&0x80,FP_SCR0(%a6)	# move result to stack

	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	sub.l		%d0,%d1			# add scale factor
	subi.l		&0x6000,%d1		# subtract bias
	andi.w		&0x7fff,%d1
	andi.w		&0x8000,%d2		# keep old sign
	or.w		%d2,%d1			# concat old sign,new exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	bra.b		fsglmul_ovfl_dis

fsglmul_may_ovfl:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsglmul.x	FP_SCR0(%a6),%fp0	# execute sgl multiply

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	fabs.x		%fp0,%fp1		# make a copy of result
	fcmp.b		%fp1,&0x2		# is |result| >= 2.b?
	fbge.w		fsglmul_ovfl_tst	# yes; overflow has occurred

# no, it didn't overflow; we have correct result
	bra.w		fsglmul_normal_exit

fsglmul_unfl:
	bset		&unfl_bit,FPSR_EXCEPT(%a6) # set unfl exc bit

	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		&rz_mode*0x10,%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsglmul.x	FP_SCR0(%a6),%fp0	# execute sgl multiply

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x0b,%d1		# is UNFL or INEX enabled?
	bne.b		fsglmul_unfl_ena	# yes

fsglmul_unfl_dis:
	fmovm.x		&0x80,FP_SCR0(%a6)	# store out result

	lea		FP_SCR0(%a6),%a0	# pass: result addr
	mov.l		L_SCR3(%a6),%d1		# pass: rnd prec,mode
	bsr.l		unf_res4		# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# 'Z' bit may have been set
	fmovm.x		FP_SCR0(%a6),&0x80	# return default result in fp0
	rts

#
# UNFL is enabled.
#
fsglmul_unfl_ena:
	fmovm.x		FP_SCR1(%a6),&0x40	# load dst op

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsglmul.x	FP_SCR0(%a6),%fp1	# execute sgl multiply

	fmov.l		&0x0,%fpcr		# clear FPCR

	fmovm.x		&0x40,FP_SCR0(%a6)	# save result to stack
	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	addi.l		&0x6000,%d1		# add bias
	andi.w		&0x7fff,%d1
	or.w		%d2,%d1			# concat old sign,new exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	bra.w		fsglmul_unfl_dis

fsglmul_may_unfl:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsglmul.x	FP_SCR0(%a6),%fp0	# execute sgl multiply

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	fabs.x		%fp0,%fp1		# make a copy of result
	fcmp.b		%fp1,&0x2		# is |result| > 2.b?
	fbgt.w		fsglmul_normal_exit	# no; no underflow occurred
	fblt.w		fsglmul_unfl		# yes; underflow occurred

#
# we still don't know if underflow occurred. result is ~ equal to 2. but,
# we don't know if the result was an underflow that rounded up to a 2 or
# a normalized number that rounded down to a 2. so, redo the entire operation
# using RZ as the rounding mode to see what the pre-rounded result is.
# this case should be relatively rare.
#
	fmovm.x		FP_SCR1(%a6),&0x40	# load dst op into fp1

	mov.l		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# keep rnd prec
	ori.b		&rz_mode*0x10,%d1	# insert RZ

	fmov.l		%d1,%fpcr		# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsglmul.x	FP_SCR0(%a6),%fp1	# execute sgl multiply

	fmov.l		&0x0,%fpcr		# clear FPCR
	fabs.x		%fp1			# make absolute value
	fcmp.b		%fp1,&0x2		# is |result| < 2.b?
	fbge.w		fsglmul_normal_exit	# no; no underflow occurred
	bra.w		fsglmul_unfl		# yes, underflow occurred

##############################################################################

#
# Single Precision Multiply: inputs are not both normalized; what are they?
#
fsglmul_not_norm:
	mov.w		(tbl_fsglmul_op.b,%pc,%d1.w*2),%d1
	jmp		(tbl_fsglmul_op.b,%pc,%d1.w*1)

	swbeg		&48
tbl_fsglmul_op:
	short		fsglmul_norm		- tbl_fsglmul_op # NORM x NORM
	short		fsglmul_zero		- tbl_fsglmul_op # NORM x ZERO
	short		fsglmul_inf_src		- tbl_fsglmul_op # NORM x INF
	short		fsglmul_res_qnan	- tbl_fsglmul_op # NORM x QNAN
	short		fsglmul_norm		- tbl_fsglmul_op # NORM x DENORM
	short		fsglmul_res_snan	- tbl_fsglmul_op # NORM x SNAN
	short		tbl_fsglmul_op		- tbl_fsglmul_op #
	short		tbl_fsglmul_op		- tbl_fsglmul_op #

	short		fsglmul_zero		- tbl_fsglmul_op # ZERO x NORM
	short		fsglmul_zero		- tbl_fsglmul_op # ZERO x ZERO
	short		fsglmul_res_operr	- tbl_fsglmul_op # ZERO x INF
	short		fsglmul_res_qnan	- tbl_fsglmul_op # ZERO x QNAN
	short		fsglmul_zero		- tbl_fsglmul_op # ZERO x DENORM
	short		fsglmul_res_snan	- tbl_fsglmul_op # ZERO x SNAN
	short		tbl_fsglmul_op		- tbl_fsglmul_op #
	short		tbl_fsglmul_op		- tbl_fsglmul_op #

	short		fsglmul_inf_dst		- tbl_fsglmul_op # INF x NORM
	short		fsglmul_res_operr	- tbl_fsglmul_op # INF x ZERO
	short		fsglmul_inf_dst		- tbl_fsglmul_op # INF x INF
	short		fsglmul_res_qnan	- tbl_fsglmul_op # INF x QNAN
	short		fsglmul_inf_dst		- tbl_fsglmul_op # INF x DENORM
	short		fsglmul_res_snan	- tbl_fsglmul_op # INF x SNAN
	short		tbl_fsglmul_op		- tbl_fsglmul_op #
	short		tbl_fsglmul_op		- tbl_fsglmul_op #

	short		fsglmul_res_qnan	- tbl_fsglmul_op # QNAN x NORM
	short		fsglmul_res_qnan	- tbl_fsglmul_op # QNAN x ZERO
	short		fsglmul_res_qnan	- tbl_fsglmul_op # QNAN x INF
	short		fsglmul_res_qnan	- tbl_fsglmul_op # QNAN x QNAN
	short		fsglmul_res_qnan	- tbl_fsglmul_op # QNAN x DENORM
	short		fsglmul_res_snan	- tbl_fsglmul_op # QNAN x SNAN
	short		tbl_fsglmul_op		- tbl_fsglmul_op #
	short		tbl_fsglmul_op		- tbl_fsglmul_op #

	short		fsglmul_norm		- tbl_fsglmul_op # NORM x NORM
	short		fsglmul_zero		- tbl_fsglmul_op # NORM x ZERO
	short		fsglmul_inf_src		- tbl_fsglmul_op # NORM x INF
	short		fsglmul_res_qnan	- tbl_fsglmul_op # NORM x QNAN
	short		fsglmul_norm		- tbl_fsglmul_op # NORM x DENORM
	short		fsglmul_res_snan	- tbl_fsglmul_op # NORM x SNAN
	short		tbl_fsglmul_op		- tbl_fsglmul_op #
	short		tbl_fsglmul_op		- tbl_fsglmul_op #

	short		fsglmul_res_snan	- tbl_fsglmul_op # SNAN x NORM
	short		fsglmul_res_snan	- tbl_fsglmul_op # SNAN x ZERO
	short		fsglmul_res_snan	- tbl_fsglmul_op # SNAN x INF
	short		fsglmul_res_snan	- tbl_fsglmul_op # SNAN x QNAN
	short		fsglmul_res_snan	- tbl_fsglmul_op # SNAN x DENORM
	short		fsglmul_res_snan	- tbl_fsglmul_op # SNAN x SNAN
	short		tbl_fsglmul_op		- tbl_fsglmul_op #
	short		tbl_fsglmul_op		- tbl_fsglmul_op #

fsglmul_res_operr:
	bra.l		res_operr
fsglmul_res_snan:
	bra.l		res_snan
fsglmul_res_qnan:
	bra.l		res_qnan
fsglmul_zero:
	bra.l		fmul_zero
fsglmul_inf_src:
	bra.l		fmul_inf_src
fsglmul_inf_dst:
	bra.l		fmul_inf_dst

#########################################################################
# XDEF ****************************************************************	#
#	fsgldiv(): emulates the fsgldiv instruction			#
#									#
# XREF ****************************************************************	#
#	scale_to_zero_src() - scale src exponent to zero		#
#	scale_to_zero_dst() - scale dst exponent to zero		#
#	unf_res4() - return default underflow result for sglop		#
#	ovf_res() - return default overflow result			#
#	res_qnan() - return QNAN result					#
#	res_snan() - return SNAN result					#
#									#
# INPUT ***************************************************************	#
#	a0 = pointer to extended precision source operand		#
#	a1 = pointer to extended precision destination operand		#
#	d0  rnd prec,mode						#
#									#
# OUTPUT **************************************************************	#
#	fp0 = result							#
#	fp1 = EXOP (if exception occurred)				#
#									#
# ALGORITHM ***********************************************************	#
#	Handle NANs, infinities, and zeroes as special cases. Divide	#
# norms/denorms into ext/sgl/dbl precision.				#
#	For norms/denorms, scale the exponents such that a divide	#
# instruction won't cause an exception. Use the regular fsgldiv to	#
# compute a result. Check if the regular operands would have taken	#
# an exception. If so, return the default overflow/underflow result	#
# and return the EXOP if exceptions are enabled. Else, scale the	#
# result operand to the proper exponent.				#
#									#
#########################################################################

	global		fsgldiv
fsgldiv:
	mov.l		%d0,L_SCR3(%a6)		# store rnd info

	clr.w		%d1
	mov.b		DTAG(%a6),%d1
	lsl.b		&0x3,%d1
	or.b		STAG(%a6),%d1		# combine src tags

	bne.w		fsgldiv_not_norm	# optimize on non-norm input

#
# DIVIDE: NORMs and DENORMs ONLY!
#
fsgldiv_norm:
	mov.w		DST_EX(%a1),FP_SCR1_EX(%a6)
	mov.l		DST_HI(%a1),FP_SCR1_HI(%a6)
	mov.l		DST_LO(%a1),FP_SCR1_LO(%a6)

	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)

	bsr.l		scale_to_zero_src	# calculate scale factor 1
	mov.l		%d0,-(%sp)		# save scale factor 1

	bsr.l		scale_to_zero_dst	# calculate scale factor 2

	neg.l		(%sp)			# S.F. = scale1 - scale2
	add.l		%d0,(%sp)

	mov.w		2+L_SCR3(%a6),%d1	# fetch precision,mode
	lsr.b		&0x6,%d1
	mov.l		(%sp)+,%d0
	cmpi.l		%d0,&0x3fff-0x7ffe
	ble.w		fsgldiv_may_ovfl

	cmpi.l		%d0,&0x3fff-0x0000	# will result underflow?
	beq.w		fsgldiv_may_unfl	# maybe
	bgt.w		fsgldiv_unfl		# yes; go handle underflow

fsgldiv_normal:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		L_SCR3(%a6),%fpcr	# save FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsgldiv.x	FP_SCR0(%a6),%fp0	# perform sgl divide

	fmov.l		%fpsr,%d1		# save FPSR
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

fsgldiv_normal_exit:
	fmovm.x		&0x80,FP_SCR0(%a6)	# store result on stack
	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# load {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	or.w		%d2,%d1			# concat old sign,new exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x80	# return result in fp0
	rts

fsgldiv_may_ovfl:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# set FPSR

	fsgldiv.x	FP_SCR0(%a6),%fp0	# execute divide

	fmov.l		%fpsr,%d1
	fmov.l		&0x0,%fpcr

	or.l		%d1,USER_FPSR(%a6)	# save INEX,N

	fmovm.x		&0x01,-(%sp)		# save result to stack
	mov.w		(%sp),%d1		# fetch new exponent
	add.l		&0xc,%sp		# clear result
	andi.l		&0x7fff,%d1		# strip sign
	sub.l		%d0,%d1			# add scale factor
	cmp.l		%d1,&0x7fff		# did divide overflow?
	blt.b		fsgldiv_normal_exit

fsgldiv_ovfl_tst:
	or.w		&ovfl_inx_mask,2+USER_FPSR(%a6) # set ovfl/aovfl/ainex

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x13,%d1		# is OVFL or INEX enabled?
	bne.b		fsgldiv_ovfl_ena	# yes

fsgldiv_ovfl_dis:
	btst		&neg_bit,FPSR_CC(%a6)	# is result negative
	sne		%d1			# set sign param accordingly
	mov.l		L_SCR3(%a6),%d0		# pass prec:rnd
	andi.b		&0x30,%d0		# kill precision
	bsr.l		ovf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# set INF if applicable
	fmovm.x		(%a0),&0x80		# return default result in fp0
	rts

fsgldiv_ovfl_ena:
	fmovm.x		&0x80,FP_SCR0(%a6)	# move result to stack

	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	subi.l		&0x6000,%d1		# subtract new bias
	andi.w		&0x7fff,%d1		# clear ms bit
	or.w		%d2,%d1			# concat old sign,new exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	bra.b		fsgldiv_ovfl_dis

fsgldiv_unfl:
	bset		&unfl_bit,FPSR_EXCEPT(%a6) # set unfl exc bit

	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		&rz_mode*0x10,%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsgldiv.x	FP_SCR0(%a6),%fp0	# execute sgl divide

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x0b,%d1		# is UNFL or INEX enabled?
	bne.b		fsgldiv_unfl_ena	# yes

fsgldiv_unfl_dis:
	fmovm.x		&0x80,FP_SCR0(%a6)	# store out result

	lea		FP_SCR0(%a6),%a0	# pass: result addr
	mov.l		L_SCR3(%a6),%d1		# pass: rnd prec,mode
	bsr.l		unf_res4		# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# 'Z' bit may have been set
	fmovm.x		FP_SCR0(%a6),&0x80	# return default result in fp0
	rts

#
# UNFL is enabled.
#
fsgldiv_unfl_ena:
	fmovm.x		FP_SCR1(%a6),&0x40	# load dst op

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsgldiv.x	FP_SCR0(%a6),%fp1	# execute sgl divide

	fmov.l		&0x0,%fpcr		# clear FPCR

	fmovm.x		&0x40,FP_SCR0(%a6)	# save result to stack
	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	addi.l		&0x6000,%d1		# add bias
	andi.w		&0x7fff,%d1		# clear top bit
	or.w		%d2,%d1			# concat old sign, new exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	bra.b		fsgldiv_unfl_dis

#
# the divide operation MAY underflow:
#
fsgldiv_may_unfl:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsgldiv.x	FP_SCR0(%a6),%fp0	# execute sgl divide

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	fabs.x		%fp0,%fp1		# make a copy of result
	fcmp.b		%fp1,&0x1		# is |result| > 1.b?
	fbgt.w		fsgldiv_normal_exit	# no; no underflow occurred
	fblt.w		fsgldiv_unfl		# yes; underflow occurred

#
# we still don't know if underflow occurred. result is ~ equal to 1. but,
# we don't know if the result was an underflow that rounded up to a 1
# or a normalized number that rounded down to a 1. so, redo the entire
# operation using RZ as the rounding mode to see what the pre-rounded
# result is. this case should be relatively rare.
#
	fmovm.x		FP_SCR1(%a6),&0x40	# load dst op into %fp1

	clr.l		%d1			# clear scratch register
	ori.b		&rz_mode*0x10,%d1	# force RZ rnd mode

	fmov.l		%d1,%fpcr		# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsgldiv.x	FP_SCR0(%a6),%fp1	# execute sgl divide

	fmov.l		&0x0,%fpcr		# clear FPCR
	fabs.x		%fp1			# make absolute value
	fcmp.b		%fp1,&0x1		# is |result| < 1.b?
	fbge.w		fsgldiv_normal_exit	# no; no underflow occurred
	bra.w		fsgldiv_unfl		# yes; underflow occurred

############################################################################

#
# Divide: inputs are not both normalized; what are they?
#
fsgldiv_not_norm:
	mov.w		(tbl_fsgldiv_op.b,%pc,%d1.w*2),%d1
	jmp		(tbl_fsgldiv_op.b,%pc,%d1.w*1)

	swbeg		&48
tbl_fsgldiv_op:
	short		fsgldiv_norm		- tbl_fsgldiv_op # NORM / NORM
	short		fsgldiv_inf_load	- tbl_fsgldiv_op # NORM / ZERO
	short		fsgldiv_zero_load	- tbl_fsgldiv_op # NORM / INF
	short		fsgldiv_res_qnan	- tbl_fsgldiv_op # NORM / QNAN
	short		fsgldiv_norm		- tbl_fsgldiv_op # NORM / DENORM
	short		fsgldiv_res_snan	- tbl_fsgldiv_op # NORM / SNAN
	short		tbl_fsgldiv_op		- tbl_fsgldiv_op #
	short		tbl_fsgldiv_op		- tbl_fsgldiv_op #

	short		fsgldiv_zero_load	- tbl_fsgldiv_op # ZERO / NORM
	short		fsgldiv_res_operr	- tbl_fsgldiv_op # ZERO / ZERO
	short		fsgldiv_zero_load	- tbl_fsgldiv_op # ZERO / INF
	short		fsgldiv_res_qnan	- tbl_fsgldiv_op # ZERO / QNAN
	short		fsgldiv_zero_load	- tbl_fsgldiv_op # ZERO / DENORM
	short		fsgldiv_res_snan	- tbl_fsgldiv_op # ZERO / SNAN
	short		tbl_fsgldiv_op		- tbl_fsgldiv_op #
	short		tbl_fsgldiv_op		- tbl_fsgldiv_op #

	short		fsgldiv_inf_dst		- tbl_fsgldiv_op # INF / NORM
	short		fsgldiv_inf_dst		- tbl_fsgldiv_op # INF / ZERO
	short		fsgldiv_res_operr	- tbl_fsgldiv_op # INF / INF
	short		fsgldiv_res_qnan	- tbl_fsgldiv_op # INF / QNAN
	short		fsgldiv_inf_dst		- tbl_fsgldiv_op # INF / DENORM
	short		fsgldiv_res_snan	- tbl_fsgldiv_op # INF / SNAN
	short		tbl_fsgldiv_op		- tbl_fsgldiv_op #
	short		tbl_fsgldiv_op		- tbl_fsgldiv_op #

	short		fsgldiv_res_qnan	- tbl_fsgldiv_op # QNAN / NORM
	short		fsgldiv_res_qnan	- tbl_fsgldiv_op # QNAN / ZERO
	short		fsgldiv_res_qnan	- tbl_fsgldiv_op # QNAN / INF
	short		fsgldiv_res_qnan	- tbl_fsgldiv_op # QNAN / QNAN
	short		fsgldiv_res_qnan	- tbl_fsgldiv_op # QNAN / DENORM
	short		fsgldiv_res_snan	- tbl_fsgldiv_op # QNAN / SNAN
	short		tbl_fsgldiv_op		- tbl_fsgldiv_op #
	short		tbl_fsgldiv_op		- tbl_fsgldiv_op #

	short		fsgldiv_norm		- tbl_fsgldiv_op # DENORM / NORM
	short		fsgldiv_inf_load	- tbl_fsgldiv_op # DENORM / ZERO
	short		fsgldiv_zero_load	- tbl_fsgldiv_op # DENORM / INF
	short		fsgldiv_res_qnan	- tbl_fsgldiv_op # DENORM / QNAN
	short		fsgldiv_norm		- tbl_fsgldiv_op # DENORM / DENORM
	short		fsgldiv_res_snan	- tbl_fsgldiv_op # DENORM / SNAN
	short		tbl_fsgldiv_op		- tbl_fsgldiv_op #
	short		tbl_fsgldiv_op		- tbl_fsgldiv_op #

	short		fsgldiv_res_snan	- tbl_fsgldiv_op # SNAN / NORM
	short		fsgldiv_res_snan	- tbl_fsgldiv_op # SNAN / ZERO
	short		fsgldiv_res_snan	- tbl_fsgldiv_op # SNAN / INF
	short		fsgldiv_res_snan	- tbl_fsgldiv_op # SNAN / QNAN
	short		fsgldiv_res_snan	- tbl_fsgldiv_op # SNAN / DENORM
	short		fsgldiv_res_snan	- tbl_fsgldiv_op # SNAN / SNAN
	short		tbl_fsgldiv_op		- tbl_fsgldiv_op #
	short		tbl_fsgldiv_op		- tbl_fsgldiv_op #

fsgldiv_res_qnan:
	bra.l		res_qnan
fsgldiv_res_snan:
	bra.l		res_snan
fsgldiv_res_operr:
	bra.l		res_operr
fsgldiv_inf_load:
	bra.l		fdiv_inf_load
fsgldiv_zero_load:
	bra.l		fdiv_zero_load
fsgldiv_inf_dst:
	bra.l		fdiv_inf_dst

#########################################################################
# XDEF ****************************************************************	#
#	fadd(): emulates the fadd instruction				#
#	fsadd(): emulates the fadd instruction				#
#	fdadd(): emulates the fdadd instruction				#
#									#
# XREF ****************************************************************	#
#	addsub_scaler2() - scale the operands so they won't take exc	#
#	ovf_res() - return default overflow result			#
#	unf_res() - return default underflow result			#
#	res_qnan() - set QNAN result					#
#	res_snan() - set SNAN result					#
#	res_operr() - set OPERR result					#
#	scale_to_zero_src() - set src operand exponent equal to zero	#
#	scale_to_zero_dst() - set dst operand exponent equal to zero	#
#									#
# INPUT ***************************************************************	#
#	a0 = pointer to extended precision source operand		#
#	a1 = pointer to extended precision destination operand		#
#									#
# OUTPUT **************************************************************	#
#	fp0 = result							#
#	fp1 = EXOP (if exception occurred)				#
#									#
# ALGORITHM ***********************************************************	#
#	Handle NANs, infinities, and zeroes as special cases. Divide	#
# norms into extended, single, and double precision.			#
#	Do addition after scaling exponents such that exception won't	#
# occur. Then, check result exponent to see if exception would have	#
# occurred. If so, return default result and maybe EXOP. Else, insert	#
# the correct result exponent and return. Set FPSR bits as appropriate.	#
#									#
#########################################################################

	global		fsadd
fsadd:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&s_mode*0x10,%d0	# insert sgl prec
	bra.b		fadd

	global		fdadd
fdadd:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&d_mode*0x10,%d0	# insert dbl prec

	global		fadd
fadd:
	mov.l		%d0,L_SCR3(%a6)		# store rnd info

	clr.w		%d1
	mov.b		DTAG(%a6),%d1
	lsl.b		&0x3,%d1
	or.b		STAG(%a6),%d1		# combine src tags

	bne.w		fadd_not_norm		# optimize on non-norm input

#
# ADD: norms and denorms
#
fadd_norm:
	bsr.l		addsub_scaler2		# scale exponents

fadd_zero_entry:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fadd.x		FP_SCR0(%a6),%fp0	# execute add

	fmov.l		&0x0,%fpcr		# clear FPCR
	fmov.l		%fpsr,%d1		# fetch INEX2,N,Z

	or.l		%d1,USER_FPSR(%a6)	# save exc and ccode bits

	fbeq.w		fadd_zero_exit		# if result is zero, end now

	mov.l		%d2,-(%sp)		# save d2

	fmovm.x		&0x01,-(%sp)		# save result to stack

	mov.w		2+L_SCR3(%a6),%d1
	lsr.b		&0x6,%d1

	mov.w		(%sp),%d2		# fetch new sign, exp
	andi.l		&0x7fff,%d2		# strip sign
	sub.l		%d0,%d2			# add scale factor

	cmp.l		%d2,(tbl_fadd_ovfl.b,%pc,%d1.w*4) # is it an overflow?
	bge.b		fadd_ovfl		# yes

	cmp.l		%d2,(tbl_fadd_unfl.b,%pc,%d1.w*4) # is it an underflow?
	blt.w		fadd_unfl		# yes
	beq.w		fadd_may_unfl		# maybe; go find out

fadd_normal:
	mov.w		(%sp),%d1
	andi.w		&0x8000,%d1		# keep sign
	or.w		%d2,%d1			# concat sign,new exp
	mov.w		%d1,(%sp)		# insert new exponent

	fmovm.x		(%sp)+,&0x80		# return result in fp0

	mov.l		(%sp)+,%d2		# restore d2
	rts

fadd_zero_exit:
#	fmov.s		&0x00000000,%fp0	# return zero in fp0
	rts

tbl_fadd_ovfl:
	long		0x7fff			# ext ovfl
	long		0x407f			# sgl ovfl
	long		0x43ff			# dbl ovfl

tbl_fadd_unfl:
	long	        0x0000			# ext unfl
	long		0x3f81			# sgl unfl
	long		0x3c01			# dbl unfl

fadd_ovfl:
	or.l		&ovfl_inx_mask,USER_FPSR(%a6) # set ovfl/aovfl/ainex

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x13,%d1		# is OVFL or INEX enabled?
	bne.b		fadd_ovfl_ena		# yes

	add.l		&0xc,%sp
fadd_ovfl_dis:
	btst		&neg_bit,FPSR_CC(%a6)	# is result negative?
	sne		%d1			# set sign param accordingly
	mov.l		L_SCR3(%a6),%d0		# pass prec:rnd
	bsr.l		ovf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# set INF,N if applicable
	fmovm.x		(%a0),&0x80		# return default result in fp0
	mov.l		(%sp)+,%d2		# restore d2
	rts

fadd_ovfl_ena:
	mov.b		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# is precision extended?
	bne.b		fadd_ovfl_ena_sd	# no; prec = sgl or dbl

fadd_ovfl_ena_cont:
	mov.w		(%sp),%d1
	andi.w		&0x8000,%d1		# keep sign
	subi.l		&0x6000,%d2		# add extra bias
	andi.w		&0x7fff,%d2
	or.w		%d2,%d1			# concat sign,new exp
	mov.w		%d1,(%sp)		# insert new exponent

	fmovm.x		(%sp)+,&0x40		# return EXOP in fp1
	bra.b		fadd_ovfl_dis

fadd_ovfl_ena_sd:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	mov.l		L_SCR3(%a6),%d1
	andi.b		&0x30,%d1		# keep rnd mode
	fmov.l		%d1,%fpcr		# set FPCR

	fadd.x		FP_SCR0(%a6),%fp0	# execute add

	fmov.l		&0x0,%fpcr		# clear FPCR

	add.l		&0xc,%sp
	fmovm.x		&0x01,-(%sp)
	bra.b		fadd_ovfl_ena_cont

fadd_unfl:
	bset		&unfl_bit,FPSR_EXCEPT(%a6) # set unfl exc bit

	add.l		&0xc,%sp

	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		&rz_mode*0x10,%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fadd.x		FP_SCR0(%a6),%fp0	# execute add

	fmov.l		&0x0,%fpcr		# clear FPCR
	fmov.l		%fpsr,%d1		# save status

	or.l		%d1,USER_FPSR(%a6)	# save INEX,N

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x0b,%d1		# is UNFL or INEX enabled?
	bne.b		fadd_unfl_ena		# yes

fadd_unfl_dis:
	fmovm.x		&0x80,FP_SCR0(%a6)	# store out result

	lea		FP_SCR0(%a6),%a0	# pass: result addr
	mov.l		L_SCR3(%a6),%d1		# pass: rnd prec,mode
	bsr.l		unf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# 'Z' bit may have been set
	fmovm.x		FP_SCR0(%a6),&0x80	# return default result in fp0
	mov.l		(%sp)+,%d2		# restore d2
	rts

fadd_unfl_ena:
	fmovm.x		FP_SCR1(%a6),&0x40	# load dst op

	mov.l		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# is precision extended?
	bne.b		fadd_unfl_ena_sd	# no; sgl or dbl

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

fadd_unfl_ena_cont:
	fmov.l		&0x0,%fpsr		# clear FPSR

	fadd.x		FP_SCR0(%a6),%fp1	# execute multiply

	fmov.l		&0x0,%fpcr		# clear FPCR

	fmovm.x		&0x40,FP_SCR0(%a6)	# save result to stack
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	addi.l		&0x6000,%d1		# add new bias
	andi.w		&0x7fff,%d1		# clear top bit
	or.w		%d2,%d1			# concat sign,new exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	bra.w		fadd_unfl_dis

fadd_unfl_ena_sd:
	mov.l		L_SCR3(%a6),%d1
	andi.b		&0x30,%d1		# use only rnd mode
	fmov.l		%d1,%fpcr		# set FPCR

	bra.b		fadd_unfl_ena_cont

#
# result is equal to the smallest normalized number in the selected precision
# if the precision is extended, this result could not have come from an
# underflow that rounded up.
#
fadd_may_unfl:
	mov.l		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1
	beq.w		fadd_normal		# yes; no underflow occurred

	mov.l		0x4(%sp),%d1		# extract hi(man)
	cmpi.l		%d1,&0x80000000		# is hi(man) = 0x80000000?
	bne.w		fadd_normal		# no; no underflow occurred

	tst.l		0x8(%sp)		# is lo(man) = 0x0?
	bne.w		fadd_normal		# no; no underflow occurred

	btst		&inex2_bit,FPSR_EXCEPT(%a6) # is INEX2 set?
	beq.w		fadd_normal		# no; no underflow occurred

#
# ok, so now the result has a exponent equal to the smallest normalized
# exponent for the selected precision. also, the mantissa is equal to
# 0x8000000000000000 and this mantissa is the result of rounding non-zero
# g,r,s.
# now, we must determine whether the pre-rounded result was an underflow
# rounded "up" or a normalized number rounded "down".
# so, we do this be re-executing the add using RZ as the rounding mode and
# seeing if the new result is smaller or equal to the current result.
#
	fmovm.x		FP_SCR1(%a6),&0x40	# load dst op into fp1

	mov.l		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# keep rnd prec
	ori.b		&rz_mode*0x10,%d1	# insert rnd mode
	fmov.l		%d1,%fpcr		# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fadd.x		FP_SCR0(%a6),%fp1	# execute add

	fmov.l		&0x0,%fpcr		# clear FPCR

	fabs.x		%fp0			# compare absolute values
	fabs.x		%fp1
	fcmp.x		%fp0,%fp1		# is first result > second?

	fbgt.w		fadd_unfl		# yes; it's an underflow
	bra.w		fadd_normal		# no; it's not an underflow

##########################################################################

#
# Add: inputs are not both normalized; what are they?
#
fadd_not_norm:
	mov.w		(tbl_fadd_op.b,%pc,%d1.w*2),%d1
	jmp		(tbl_fadd_op.b,%pc,%d1.w*1)

	swbeg		&48
tbl_fadd_op:
	short		fadd_norm	- tbl_fadd_op # NORM + NORM
	short		fadd_zero_src	- tbl_fadd_op # NORM + ZERO
	short		fadd_inf_src	- tbl_fadd_op # NORM + INF
	short		fadd_res_qnan	- tbl_fadd_op # NORM + QNAN
	short		fadd_norm	- tbl_fadd_op # NORM + DENORM
	short		fadd_res_snan	- tbl_fadd_op # NORM + SNAN
	short		tbl_fadd_op	- tbl_fadd_op #
	short		tbl_fadd_op	- tbl_fadd_op #

	short		fadd_zero_dst	- tbl_fadd_op # ZERO + NORM
	short		fadd_zero_2	- tbl_fadd_op # ZERO + ZERO
	short		fadd_inf_src	- tbl_fadd_op # ZERO + INF
	short		fadd_res_qnan	- tbl_fadd_op # NORM + QNAN
	short		fadd_zero_dst	- tbl_fadd_op # ZERO + DENORM
	short		fadd_res_snan	- tbl_fadd_op # NORM + SNAN
	short		tbl_fadd_op	- tbl_fadd_op #
	short		tbl_fadd_op	- tbl_fadd_op #

	short		fadd_inf_dst	- tbl_fadd_op # INF + NORM
	short		fadd_inf_dst	- tbl_fadd_op # INF + ZERO
	short		fadd_inf_2	- tbl_fadd_op # INF + INF
	short		fadd_res_qnan	- tbl_fadd_op # NORM + QNAN
	short		fadd_inf_dst	- tbl_fadd_op # INF + DENORM
	short		fadd_res_snan	- tbl_fadd_op # NORM + SNAN
	short		tbl_fadd_op	- tbl_fadd_op #
	short		tbl_fadd_op	- tbl_fadd_op #

	short		fadd_res_qnan	- tbl_fadd_op # QNAN + NORM
	short		fadd_res_qnan	- tbl_fadd_op # QNAN + ZERO
	short		fadd_res_qnan	- tbl_fadd_op # QNAN + INF
	short		fadd_res_qnan	- tbl_fadd_op # QNAN + QNAN
	short		fadd_res_qnan	- tbl_fadd_op # QNAN + DENORM
	short		fadd_res_snan	- tbl_fadd_op # QNAN + SNAN
	short		tbl_fadd_op	- tbl_fadd_op #
	short		tbl_fadd_op	- tbl_fadd_op #

	short		fadd_norm	- tbl_fadd_op # DENORM + NORM
	short		fadd_zero_src	- tbl_fadd_op # DENORM + ZERO
	short		fadd_inf_src	- tbl_fadd_op # DENORM + INF
	short		fadd_res_qnan	- tbl_fadd_op # NORM + QNAN
	short		fadd_norm	- tbl_fadd_op # DENORM + DENORM
	short		fadd_res_snan	- tbl_fadd_op # NORM + SNAN
	short		tbl_fadd_op	- tbl_fadd_op #
	short		tbl_fadd_op	- tbl_fadd_op #

	short		fadd_res_snan	- tbl_fadd_op # SNAN + NORM
	short		fadd_res_snan	- tbl_fadd_op # SNAN + ZERO
	short		fadd_res_snan	- tbl_fadd_op # SNAN + INF
	short		fadd_res_snan	- tbl_fadd_op # SNAN + QNAN
	short		fadd_res_snan	- tbl_fadd_op # SNAN + DENORM
	short		fadd_res_snan	- tbl_fadd_op # SNAN + SNAN
	short		tbl_fadd_op	- tbl_fadd_op #
	short		tbl_fadd_op	- tbl_fadd_op #

fadd_res_qnan:
	bra.l		res_qnan
fadd_res_snan:
	bra.l		res_snan

#
# both operands are ZEROes
#
fadd_zero_2:
	mov.b		SRC_EX(%a0),%d0		# are the signs opposite
	mov.b		DST_EX(%a1),%d1
	eor.b		%d0,%d1
	bmi.w		fadd_zero_2_chk_rm	# weed out (-ZERO)+(+ZERO)

# the signs are the same. so determine whether they are positive or negative
# and return the appropriately signed zero.
	tst.b		%d0			# are ZEROes positive or negative?
	bmi.b		fadd_zero_rm		# negative
	fmov.s		&0x00000000,%fp0	# return +ZERO
	mov.b		&z_bmask,FPSR_CC(%a6)	# set Z
	rts

#
# the ZEROes have opposite signs:
# - Therefore, we return +ZERO if the rounding modes are RN,RZ, or RP.
# - -ZERO is returned in the case of RM.
#
fadd_zero_2_chk_rm:
	mov.b		3+L_SCR3(%a6),%d1
	andi.b		&0x30,%d1		# extract rnd mode
	cmpi.b		%d1,&rm_mode*0x10	# is rnd mode == RM?
	beq.b		fadd_zero_rm		# yes
	fmov.s		&0x00000000,%fp0	# return +ZERO
	mov.b		&z_bmask,FPSR_CC(%a6)	# set Z
	rts

fadd_zero_rm:
	fmov.s		&0x80000000,%fp0	# return -ZERO
	mov.b		&neg_bmask+z_bmask,FPSR_CC(%a6) # set NEG/Z
	rts

#
# one operand is a ZERO and the other is a DENORM or NORM. scale
# the DENORM or NORM and jump to the regular fadd routine.
#
fadd_zero_dst:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	bsr.l		scale_to_zero_src	# scale the operand
	clr.w		FP_SCR1_EX(%a6)
	clr.l		FP_SCR1_HI(%a6)
	clr.l		FP_SCR1_LO(%a6)
	bra.w		fadd_zero_entry		# go execute fadd

fadd_zero_src:
	mov.w		DST_EX(%a1),FP_SCR1_EX(%a6)
	mov.l		DST_HI(%a1),FP_SCR1_HI(%a6)
	mov.l		DST_LO(%a1),FP_SCR1_LO(%a6)
	bsr.l		scale_to_zero_dst	# scale the operand
	clr.w		FP_SCR0_EX(%a6)
	clr.l		FP_SCR0_HI(%a6)
	clr.l		FP_SCR0_LO(%a6)
	bra.w		fadd_zero_entry		# go execute fadd

#
# both operands are INFs. an OPERR will result if the INFs have
# different signs. else, an INF of the same sign is returned
#
fadd_inf_2:
	mov.b		SRC_EX(%a0),%d0		# exclusive or the signs
	mov.b		DST_EX(%a1),%d1
	eor.b		%d1,%d0
	bmi.l		res_operr		# weed out (-INF)+(+INF)

# ok, so it's not an OPERR. but, we do have to remember to return the
# src INF since that's where the 881/882 gets the j-bit from...

#
# operands are INF and one of {ZERO, INF, DENORM, NORM}
#
fadd_inf_src:
	fmovm.x		SRC(%a0),&0x80		# return src INF
	tst.b		SRC_EX(%a0)		# is INF positive?
	bpl.b		fadd_inf_done		# yes; we're done
	mov.b		&neg_bmask+inf_bmask,FPSR_CC(%a6) # set INF/NEG
	rts

#
# operands are INF and one of {ZERO, INF, DENORM, NORM}
#
fadd_inf_dst:
	fmovm.x		DST(%a1),&0x80		# return dst INF
	tst.b		DST_EX(%a1)		# is INF positive?
	bpl.b		fadd_inf_done		# yes; we're done
	mov.b		&neg_bmask+inf_bmask,FPSR_CC(%a6) # set INF/NEG
	rts

fadd_inf_done:
	mov.b		&inf_bmask,FPSR_CC(%a6) # set INF
	rts

#########################################################################
# XDEF ****************************************************************	#
#	fsub(): emulates the fsub instruction				#
#	fssub(): emulates the fssub instruction				#
#	fdsub(): emulates the fdsub instruction				#
#									#
# XREF ****************************************************************	#
#	addsub_scaler2() - scale the operands so they won't take exc	#
#	ovf_res() - return default overflow result			#
#	unf_res() - return default underflow result			#
#	res_qnan() - set QNAN result					#
#	res_snan() - set SNAN result					#
#	res_operr() - set OPERR result					#
#	scale_to_zero_src() - set src operand exponent equal to zero	#
#	scale_to_zero_dst() - set dst operand exponent equal to zero	#
#									#
# INPUT ***************************************************************	#
#	a0 = pointer to extended precision source operand		#
#	a1 = pointer to extended precision destination operand		#
#									#
# OUTPUT **************************************************************	#
#	fp0 = result							#
#	fp1 = EXOP (if exception occurred)				#
#									#
# ALGORITHM ***********************************************************	#
#	Handle NANs, infinities, and zeroes as special cases. Divide	#
# norms into extended, single, and double precision.			#
#	Do subtraction after scaling exponents such that exception won't#
# occur. Then, check result exponent to see if exception would have	#
# occurred. If so, return default result and maybe EXOP. Else, insert	#
# the correct result exponent and return. Set FPSR bits as appropriate.	#
#									#
#########################################################################

	global		fssub
fssub:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&s_mode*0x10,%d0	# insert sgl prec
	bra.b		fsub

	global		fdsub
fdsub:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&d_mode*0x10,%d0	# insert dbl prec

	global		fsub
fsub:
	mov.l		%d0,L_SCR3(%a6)		# store rnd info

	clr.w		%d1
	mov.b		DTAG(%a6),%d1
	lsl.b		&0x3,%d1
	or.b		STAG(%a6),%d1		# combine src tags

	bne.w		fsub_not_norm		# optimize on non-norm input

#
# SUB: norms and denorms
#
fsub_norm:
	bsr.l		addsub_scaler2		# scale exponents

fsub_zero_entry:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fsub.x		FP_SCR0(%a6),%fp0	# execute subtract

	fmov.l		&0x0,%fpcr		# clear FPCR
	fmov.l		%fpsr,%d1		# fetch INEX2, N, Z

	or.l		%d1,USER_FPSR(%a6)	# save exc and ccode bits

	fbeq.w		fsub_zero_exit		# if result zero, end now

	mov.l		%d2,-(%sp)		# save d2

	fmovm.x		&0x01,-(%sp)		# save result to stack

	mov.w		2+L_SCR3(%a6),%d1
	lsr.b		&0x6,%d1

	mov.w		(%sp),%d2		# fetch new exponent
	andi.l		&0x7fff,%d2		# strip sign
	sub.l		%d0,%d2			# add scale factor

	cmp.l		%d2,(tbl_fsub_ovfl.b,%pc,%d1.w*4) # is it an overflow?
	bge.b		fsub_ovfl		# yes

	cmp.l		%d2,(tbl_fsub_unfl.b,%pc,%d1.w*4) # is it an underflow?
	blt.w		fsub_unfl		# yes
	beq.w		fsub_may_unfl		# maybe; go find out

fsub_normal:
	mov.w		(%sp),%d1
	andi.w		&0x8000,%d1		# keep sign
	or.w		%d2,%d1			# insert new exponent
	mov.w		%d1,(%sp)		# insert new exponent

	fmovm.x		(%sp)+,&0x80		# return result in fp0

	mov.l		(%sp)+,%d2		# restore d2
	rts

fsub_zero_exit:
#	fmov.s		&0x00000000,%fp0	# return zero in fp0
	rts

tbl_fsub_ovfl:
	long		0x7fff			# ext ovfl
	long		0x407f			# sgl ovfl
	long		0x43ff			# dbl ovfl

tbl_fsub_unfl:
	long	        0x0000			# ext unfl
	long		0x3f81			# sgl unfl
	long		0x3c01			# dbl unfl

fsub_ovfl:
	or.l		&ovfl_inx_mask,USER_FPSR(%a6) # set ovfl/aovfl/ainex

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x13,%d1		# is OVFL or INEX enabled?
	bne.b		fsub_ovfl_ena		# yes

	add.l		&0xc,%sp
fsub_ovfl_dis:
	btst		&neg_bit,FPSR_CC(%a6)	# is result negative?
	sne		%d1			# set sign param accordingly
	mov.l		L_SCR3(%a6),%d0		# pass prec:rnd
	bsr.l		ovf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# set INF,N if applicable
	fmovm.x		(%a0),&0x80		# return default result in fp0
	mov.l		(%sp)+,%d2		# restore d2
	rts

fsub_ovfl_ena:
	mov.b		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# is precision extended?
	bne.b		fsub_ovfl_ena_sd	# no

fsub_ovfl_ena_cont:
	mov.w		(%sp),%d1		# fetch {sgn,exp}
	andi.w		&0x8000,%d1		# keep sign
	subi.l		&0x6000,%d2		# subtract new bias
	andi.w		&0x7fff,%d2		# clear top bit
	or.w		%d2,%d1			# concat sign,exp
	mov.w		%d1,(%sp)		# insert new exponent

	fmovm.x		(%sp)+,&0x40		# return EXOP in fp1
	bra.b		fsub_ovfl_dis

fsub_ovfl_ena_sd:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	mov.l		L_SCR3(%a6),%d1
	andi.b		&0x30,%d1		# clear rnd prec
	fmov.l		%d1,%fpcr		# set FPCR

	fsub.x		FP_SCR0(%a6),%fp0	# execute subtract

	fmov.l		&0x0,%fpcr		# clear FPCR

	add.l		&0xc,%sp
	fmovm.x		&0x01,-(%sp)
	bra.b		fsub_ovfl_ena_cont

fsub_unfl:
	bset		&unfl_bit,FPSR_EXCEPT(%a6) # set unfl exc bit

	add.l		&0xc,%sp

	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		&rz_mode*0x10,%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsub.x		FP_SCR0(%a6),%fp0	# execute subtract

	fmov.l		&0x0,%fpcr		# clear FPCR
	fmov.l		%fpsr,%d1		# save status

	or.l		%d1,USER_FPSR(%a6)

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x0b,%d1		# is UNFL or INEX enabled?
	bne.b		fsub_unfl_ena		# yes

fsub_unfl_dis:
	fmovm.x		&0x80,FP_SCR0(%a6)	# store out result

	lea		FP_SCR0(%a6),%a0	# pass: result addr
	mov.l		L_SCR3(%a6),%d1		# pass: rnd prec,mode
	bsr.l		unf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# 'Z' may have been set
	fmovm.x		FP_SCR0(%a6),&0x80	# return default result in fp0
	mov.l		(%sp)+,%d2		# restore d2
	rts

fsub_unfl_ena:
	fmovm.x		FP_SCR1(%a6),&0x40

	mov.l		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# is precision extended?
	bne.b		fsub_unfl_ena_sd	# no

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

fsub_unfl_ena_cont:
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsub.x		FP_SCR0(%a6),%fp1	# execute subtract

	fmov.l		&0x0,%fpcr		# clear FPCR

	fmovm.x		&0x40,FP_SCR0(%a6)	# store result to stack
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	addi.l		&0x6000,%d1		# subtract new bias
	andi.w		&0x7fff,%d1		# clear top bit
	or.w		%d2,%d1			# concat sgn,exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	bra.w		fsub_unfl_dis

fsub_unfl_ena_sd:
	mov.l		L_SCR3(%a6),%d1
	andi.b		&0x30,%d1		# clear rnd prec
	fmov.l		%d1,%fpcr		# set FPCR

	bra.b		fsub_unfl_ena_cont

#
# result is equal to the smallest normalized number in the selected precision
# if the precision is extended, this result could not have come from an
# underflow that rounded up.
#
fsub_may_unfl:
	mov.l		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# fetch rnd prec
	beq.w		fsub_normal		# yes; no underflow occurred

	mov.l		0x4(%sp),%d1
	cmpi.l		%d1,&0x80000000		# is hi(man) = 0x80000000?
	bne.w		fsub_normal		# no; no underflow occurred

	tst.l		0x8(%sp)		# is lo(man) = 0x0?
	bne.w		fsub_normal		# no; no underflow occurred

	btst		&inex2_bit,FPSR_EXCEPT(%a6) # is INEX2 set?
	beq.w		fsub_normal		# no; no underflow occurred

#
# ok, so now the result has a exponent equal to the smallest normalized
# exponent for the selected precision. also, the mantissa is equal to
# 0x8000000000000000 and this mantissa is the result of rounding non-zero
# g,r,s.
# now, we must determine whether the pre-rounded result was an underflow
# rounded "up" or a normalized number rounded "down".
# so, we do this be re-executing the add using RZ as the rounding mode and
# seeing if the new result is smaller or equal to the current result.
#
	fmovm.x		FP_SCR1(%a6),&0x40	# load dst op into fp1

	mov.l		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# keep rnd prec
	ori.b		&rz_mode*0x10,%d1	# insert rnd mode
	fmov.l		%d1,%fpcr		# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsub.x		FP_SCR0(%a6),%fp1	# execute subtract

	fmov.l		&0x0,%fpcr		# clear FPCR

	fabs.x		%fp0			# compare absolute values
	fabs.x		%fp1
	fcmp.x		%fp0,%fp1		# is first result > second?

	fbgt.w		fsub_unfl		# yes; it's an underflow
	bra.w		fsub_normal		# no; it's not an underflow

##########################################################################

#
# Sub: inputs are not both normalized; what are they?
#
fsub_not_norm:
	mov.w		(tbl_fsub_op.b,%pc,%d1.w*2),%d1
	jmp		(tbl_fsub_op.b,%pc,%d1.w*1)

	swbeg		&48
tbl_fsub_op:
	short		fsub_norm	- tbl_fsub_op # NORM - NORM
	short		fsub_zero_src	- tbl_fsub_op # NORM - ZERO
	short		fsub_inf_src	- tbl_fsub_op # NORM - INF
	short		fsub_res_qnan	- tbl_fsub_op # NORM - QNAN
	short		fsub_norm	- tbl_fsub_op # NORM - DENORM
	short		fsub_res_snan	- tbl_fsub_op # NORM - SNAN
	short		tbl_fsub_op	- tbl_fsub_op #
	short		tbl_fsub_op	- tbl_fsub_op #

	short		fsub_zero_dst	- tbl_fsub_op # ZERO - NORM
	short		fsub_zero_2	- tbl_fsub_op # ZERO - ZERO
	short		fsub_inf_src	- tbl_fsub_op # ZERO - INF
	short		fsub_res_qnan	- tbl_fsub_op # NORM - QNAN
	short		fsub_zero_dst	- tbl_fsub_op # ZERO - DENORM
	short		fsub_res_snan	- tbl_fsub_op # NORM - SNAN
	short		tbl_fsub_op	- tbl_fsub_op #
	short		tbl_fsub_op	- tbl_fsub_op #

	short		fsub_inf_dst	- tbl_fsub_op # INF - NORM
	short		fsub_inf_dst	- tbl_fsub_op # INF - ZERO
	short		fsub_inf_2	- tbl_fsub_op # INF - INF
	short		fsub_res_qnan	- tbl_fsub_op # NORM - QNAN
	short		fsub_inf_dst	- tbl_fsub_op # INF - DENORM
	short		fsub_res_snan	- tbl_fsub_op # NORM - SNAN
	short		tbl_fsub_op	- tbl_fsub_op #
	short		tbl_fsub_op	- tbl_fsub_op #

	short		fsub_res_qnan	- tbl_fsub_op # QNAN - NORM
	short		fsub_res_qnan	- tbl_fsub_op # QNAN - ZERO
	short		fsub_res_qnan	- tbl_fsub_op # QNAN - INF
	short		fsub_res_qnan	- tbl_fsub_op # QNAN - QNAN
	short		fsub_res_qnan	- tbl_fsub_op # QNAN - DENORM
	short		fsub_res_snan	- tbl_fsub_op # QNAN - SNAN
	short		tbl_fsub_op	- tbl_fsub_op #
	short		tbl_fsub_op	- tbl_fsub_op #

	short		fsub_norm	- tbl_fsub_op # DENORM - NORM
	short		fsub_zero_src	- tbl_fsub_op # DENORM - ZERO
	short		fsub_inf_src	- tbl_fsub_op # DENORM - INF
	short		fsub_res_qnan	- tbl_fsub_op # NORM - QNAN
	short		fsub_norm	- tbl_fsub_op # DENORM - DENORM
	short		fsub_res_snan	- tbl_fsub_op # NORM - SNAN
	short		tbl_fsub_op	- tbl_fsub_op #
	short		tbl_fsub_op	- tbl_fsub_op #

	short		fsub_res_snan	- tbl_fsub_op # SNAN - NORM
	short		fsub_res_snan	- tbl_fsub_op # SNAN - ZERO
	short		fsub_res_snan	- tbl_fsub_op # SNAN - INF
	short		fsub_res_snan	- tbl_fsub_op # SNAN - QNAN
	short		fsub_res_snan	- tbl_fsub_op # SNAN - DENORM
	short		fsub_res_snan	- tbl_fsub_op # SNAN - SNAN
	short		tbl_fsub_op	- tbl_fsub_op #
	short		tbl_fsub_op	- tbl_fsub_op #

fsub_res_qnan:
	bra.l		res_qnan
fsub_res_snan:
	bra.l		res_snan

#
# both operands are ZEROes
#
fsub_zero_2:
	mov.b		SRC_EX(%a0),%d0
	mov.b		DST_EX(%a1),%d1
	eor.b		%d1,%d0
	bpl.b		fsub_zero_2_chk_rm

# the signs are opposite, so, return a ZERO w/ the sign of the dst ZERO
	tst.b		%d0			# is dst negative?
	bmi.b		fsub_zero_2_rm		# yes
	fmov.s		&0x00000000,%fp0	# no; return +ZERO
	mov.b		&z_bmask,FPSR_CC(%a6)	# set Z
	rts

#
# the ZEROes have the same signs:
# - Therefore, we return +ZERO if the rounding mode is RN,RZ, or RP
# - -ZERO is returned in the case of RM.
#
fsub_zero_2_chk_rm:
	mov.b		3+L_SCR3(%a6),%d1
	andi.b		&0x30,%d1		# extract rnd mode
	cmpi.b		%d1,&rm_mode*0x10	# is rnd mode = RM?
	beq.b		fsub_zero_2_rm		# yes
	fmov.s		&0x00000000,%fp0	# no; return +ZERO
	mov.b		&z_bmask,FPSR_CC(%a6)	# set Z
	rts

fsub_zero_2_rm:
	fmov.s		&0x80000000,%fp0	# return -ZERO
	mov.b		&z_bmask+neg_bmask,FPSR_CC(%a6)	# set Z/NEG
	rts

#
# one operand is a ZERO and the other is a DENORM or a NORM.
# scale the DENORM or NORM and jump to the regular fsub routine.
#
fsub_zero_dst:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	bsr.l		scale_to_zero_src	# scale the operand
	clr.w		FP_SCR1_EX(%a6)
	clr.l		FP_SCR1_HI(%a6)
	clr.l		FP_SCR1_LO(%a6)
	bra.w		fsub_zero_entry		# go execute fsub

fsub_zero_src:
	mov.w		DST_EX(%a1),FP_SCR1_EX(%a6)
	mov.l		DST_HI(%a1),FP_SCR1_HI(%a6)
	mov.l		DST_LO(%a1),FP_SCR1_LO(%a6)
	bsr.l		scale_to_zero_dst	# scale the operand
	clr.w		FP_SCR0_EX(%a6)
	clr.l		FP_SCR0_HI(%a6)
	clr.l		FP_SCR0_LO(%a6)
	bra.w		fsub_zero_entry		# go execute fsub

#
# both operands are INFs. an OPERR will result if the INFs have the
# same signs. else,
#
fsub_inf_2:
	mov.b		SRC_EX(%a0),%d0		# exclusive or the signs
	mov.b		DST_EX(%a1),%d1
	eor.b		%d1,%d0
	bpl.l		res_operr		# weed out (-INF)+(+INF)

# ok, so it's not an OPERR. but we do have to remember to return
# the src INF since that's where the 881/882 gets the j-bit.

fsub_inf_src:
	fmovm.x		SRC(%a0),&0x80		# return src INF
	fneg.x		%fp0			# invert sign
	fbge.w		fsub_inf_done		# sign is now positive
	mov.b		&neg_bmask+inf_bmask,FPSR_CC(%a6) # set INF/NEG
	rts

fsub_inf_dst:
	fmovm.x		DST(%a1),&0x80		# return dst INF
	tst.b		DST_EX(%a1)		# is INF negative?
	bpl.b		fsub_inf_done		# no
	mov.b		&neg_bmask+inf_bmask,FPSR_CC(%a6) # set INF/NEG
	rts

fsub_inf_done:
	mov.b		&inf_bmask,FPSR_CC(%a6)	# set INF
	rts

#########################################################################
# XDEF ****************************************************************	#
#	fsqrt(): emulates the fsqrt instruction				#
#	fssqrt(): emulates the fssqrt instruction			#
#	fdsqrt(): emulates the fdsqrt instruction			#
#									#
# XREF ****************************************************************	#
#	scale_sqrt() - scale the source operand				#
#	unf_res() - return default underflow result			#
#	ovf_res() - return default overflow result			#
#	res_qnan_1op() - return QNAN result				#
#	res_snan_1op() - return SNAN result				#
#									#
# INPUT ***************************************************************	#
#	a0 = pointer to extended precision source operand		#
#	d0  rnd prec,mode						#
#									#
# OUTPUT **************************************************************	#
#	fp0 = result							#
#	fp1 = EXOP (if exception occurred)				#
#									#
# ALGORITHM ***********************************************************	#
#	Handle NANs, infinities, and zeroes as special cases. Divide	#
# norms/denorms into ext/sgl/dbl precision.				#
#	For norms/denorms, scale the exponents such that a sqrt		#
# instruction won't cause an exception. Use the regular fsqrt to	#
# compute a result. Check if the regular operands would have taken	#
# an exception. If so, return the default overflow/underflow result	#
# and return the EXOP if exceptions are enabled. Else, scale the	#
# result operand to the proper exponent.				#
#									#
#########################################################################

	global		fssqrt
fssqrt:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&s_mode*0x10,%d0	# insert sgl precision
	bra.b		fsqrt

	global		fdsqrt
fdsqrt:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&d_mode*0x10,%d0	# insert dbl precision

	global		fsqrt
fsqrt:
	mov.l		%d0,L_SCR3(%a6)		# store rnd info
	clr.w		%d1
	mov.b		STAG(%a6),%d1
	bne.w		fsqrt_not_norm		# optimize on non-norm input

#
# SQUARE ROOT: norms and denorms ONLY!
#
fsqrt_norm:
	tst.b		SRC_EX(%a0)		# is operand negative?
	bmi.l		res_operr		# yes

	andi.b		&0xc0,%d0		# is precision extended?
	bne.b		fsqrt_not_ext		# no; go handle sgl or dbl

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsqrt.x		(%a0),%fp0		# execute square root

	fmov.l		%fpsr,%d1
	or.l		%d1,USER_FPSR(%a6)	# set N,INEX

	rts

fsqrt_denorm:
	tst.b		SRC_EX(%a0)		# is operand negative?
	bmi.l		res_operr		# yes

	andi.b		&0xc0,%d0		# is precision extended?
	bne.b		fsqrt_not_ext		# no; go handle sgl or dbl

	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)

	bsr.l		scale_sqrt		# calculate scale factor

	bra.w		fsqrt_sd_normal

#
# operand is either single or double
#
fsqrt_not_ext:
	cmpi.b		%d0,&s_mode*0x10	# separate sgl/dbl prec
	bne.w		fsqrt_dbl

#
# operand is to be rounded to single precision
#
fsqrt_sgl:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)

	bsr.l		scale_sqrt		# calculate scale factor

	cmpi.l		%d0,&0x3fff-0x3f81	# will move in underflow?
	beq.w		fsqrt_sd_may_unfl
	bgt.w		fsqrt_sd_unfl		# yes; go handle underflow
	cmpi.l		%d0,&0x3fff-0x407f	# will move in overflow?
	beq.w		fsqrt_sd_may_ovfl	# maybe; go check
	blt.w		fsqrt_sd_ovfl		# yes; go handle overflow

#
# operand will NOT overflow or underflow when moved in to the fp reg file
#
fsqrt_sd_normal:
	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fsqrt.x		FP_SCR0(%a6),%fp0	# perform absolute

	fmov.l		%fpsr,%d1		# save FPSR
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

fsqrt_sd_normal_exit:
	mov.l		%d2,-(%sp)		# save d2
	fmovm.x		&0x80,FP_SCR0(%a6)	# store out result
	mov.w		FP_SCR0_EX(%a6),%d1	# load sgn,exp
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	sub.l		%d0,%d1			# add scale factor
	andi.w		&0x8000,%d2		# keep old sign
	or.w		%d1,%d2			# concat old sign,new exp
	mov.w		%d2,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x80	# return result in fp0
	rts

#
# operand is to be rounded to double precision
#
fsqrt_dbl:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)

	bsr.l		scale_sqrt		# calculate scale factor

	cmpi.l		%d0,&0x3fff-0x3c01	# will move in underflow?
	beq.w		fsqrt_sd_may_unfl
	bgt.b		fsqrt_sd_unfl		# yes; go handle underflow
	cmpi.l		%d0,&0x3fff-0x43ff	# will move in overflow?
	beq.w		fsqrt_sd_may_ovfl	# maybe; go check
	blt.w		fsqrt_sd_ovfl		# yes; go handle overflow
	bra.w		fsqrt_sd_normal		# no; ho handle normalized op

# we're on the line here and the distinguising characteristic is whether
# the exponent is 3fff or 3ffe. if it's 3ffe, then it's a safe number
# elsewise fall through to underflow.
fsqrt_sd_may_unfl:
	btst		&0x0,1+FP_SCR0_EX(%a6)	# is exponent 0x3fff?
	bne.w		fsqrt_sd_normal		# yes, so no underflow

#
# operand WILL underflow when moved in to the fp register file
#
fsqrt_sd_unfl:
	bset		&unfl_bit,FPSR_EXCEPT(%a6) # set unfl exc bit

	fmov.l		&rz_mode*0x10,%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsqrt.x		FP_SCR0(%a6),%fp0	# execute square root

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

# if underflow or inexact is enabled, go calculate EXOP first.
	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x0b,%d1		# is UNFL or INEX enabled?
	bne.b		fsqrt_sd_unfl_ena	# yes

fsqrt_sd_unfl_dis:
	fmovm.x		&0x80,FP_SCR0(%a6)	# store out result

	lea		FP_SCR0(%a6),%a0	# pass: result addr
	mov.l		L_SCR3(%a6),%d1		# pass: rnd prec,mode
	bsr.l		unf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# set possible 'Z' ccode
	fmovm.x		FP_SCR0(%a6),&0x80	# return default result in fp0
	rts

#
# operand will underflow AND underflow is enabled.
# Therefore, we must return the result rounded to extended precision.
#
fsqrt_sd_unfl_ena:
	mov.l		FP_SCR0_HI(%a6),FP_SCR1_HI(%a6)
	mov.l		FP_SCR0_LO(%a6),FP_SCR1_LO(%a6)
	mov.w		FP_SCR0_EX(%a6),%d1	# load current exponent

	mov.l		%d2,-(%sp)		# save d2
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# subtract scale factor
	addi.l		&0x6000,%d1		# add new bias
	andi.w		&0x7fff,%d1
	or.w		%d2,%d1			# concat new sign,new exp
	mov.w		%d1,FP_SCR1_EX(%a6)	# insert new exp
	fmovm.x		FP_SCR1(%a6),&0x40	# return EXOP in fp1
	mov.l		(%sp)+,%d2		# restore d2
	bra.b		fsqrt_sd_unfl_dis

#
# operand WILL overflow.
#
fsqrt_sd_ovfl:
	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fsqrt.x		FP_SCR0(%a6),%fp0	# perform square root

	fmov.l		&0x0,%fpcr		# clear FPCR
	fmov.l		%fpsr,%d1		# save FPSR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

fsqrt_sd_ovfl_tst:
	or.l		&ovfl_inx_mask,USER_FPSR(%a6) # set ovfl/aovfl/ainex

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x13,%d1		# is OVFL or INEX enabled?
	bne.b		fsqrt_sd_ovfl_ena	# yes

#
# OVFL is not enabled; therefore, we must create the default result by
# calling ovf_res().
#
fsqrt_sd_ovfl_dis:
	btst		&neg_bit,FPSR_CC(%a6)	# is result negative?
	sne		%d1			# set sign param accordingly
	mov.l		L_SCR3(%a6),%d0		# pass: prec,mode
	bsr.l		ovf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# set INF,N if applicable
	fmovm.x		(%a0),&0x80		# return default result in fp0
	rts

#
# OVFL is enabled.
# the INEX2 bit has already been updated by the round to the correct precision.
# now, round to extended(and don't alter the FPSR).
#
fsqrt_sd_ovfl_ena:
	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	subi.l		&0x6000,%d1		# subtract bias
	andi.w		&0x7fff,%d1
	or.w		%d2,%d1			# concat sign,exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	mov.l		(%sp)+,%d2		# restore d2
	bra.b		fsqrt_sd_ovfl_dis

#
# the move in MAY underflow. so...
#
fsqrt_sd_may_ovfl:
	btst		&0x0,1+FP_SCR0_EX(%a6)	# is exponent 0x3fff?
	bne.w		fsqrt_sd_ovfl		# yes, so overflow

	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fsqrt.x		FP_SCR0(%a6),%fp0	# perform absolute

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	fmov.x		%fp0,%fp1		# make a copy of result
	fcmp.b		%fp1,&0x1		# is |result| >= 1.b?
	fbge.w		fsqrt_sd_ovfl_tst	# yes; overflow has occurred

# no, it didn't overflow; we have correct result
	bra.w		fsqrt_sd_normal_exit

##########################################################################

#
# input is not normalized; what is it?
#
fsqrt_not_norm:
	cmpi.b		%d1,&DENORM		# weed out DENORM
	beq.w		fsqrt_denorm
	cmpi.b		%d1,&ZERO		# weed out ZERO
	beq.b		fsqrt_zero
	cmpi.b		%d1,&INF		# weed out INF
	beq.b		fsqrt_inf
	cmpi.b		%d1,&SNAN		# weed out SNAN
	beq.l		res_snan_1op
	bra.l		res_qnan_1op

#
#	fsqrt(+0) = +0
#	fsqrt(-0) = -0
#	fsqrt(+INF) = +INF
#	fsqrt(-INF) = OPERR
#
fsqrt_zero:
	tst.b		SRC_EX(%a0)		# is ZERO positive or negative?
	bmi.b		fsqrt_zero_m		# negative
fsqrt_zero_p:
	fmov.s		&0x00000000,%fp0	# return +ZERO
	mov.b		&z_bmask,FPSR_CC(%a6)	# set 'Z' ccode bit
	rts
fsqrt_zero_m:
	fmov.s		&0x80000000,%fp0	# return -ZERO
	mov.b		&z_bmask+neg_bmask,FPSR_CC(%a6)	# set 'Z','N' ccode bits
	rts

fsqrt_inf:
	tst.b		SRC_EX(%a0)		# is INF positive or negative?
	bmi.l		res_operr		# negative
fsqrt_inf_p:
	fmovm.x		SRC(%a0),&0x80		# return +INF in fp0
	mov.b		&inf_bmask,FPSR_CC(%a6)	# set 'I' ccode bit
	rts
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -



ifpsp060/src/pfpsp.S [7952:12265]:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#########################################################################
# XDEF ****************************************************************	#
#	fmul(): emulates the fmul instruction				#
#	fsmul(): emulates the fsmul instruction				#
#	fdmul(): emulates the fdmul instruction				#
#									#
# XREF ****************************************************************	#
#	scale_to_zero_src() - scale src exponent to zero		#
#	scale_to_zero_dst() - scale dst exponent to zero		#
#	unf_res() - return default underflow result			#
#	ovf_res() - return default overflow result			#
#	res_qnan() - return QNAN result					#
#	res_snan() - return SNAN result					#
#									#
# INPUT ***************************************************************	#
#	a0 = pointer to extended precision source operand		#
#	a1 = pointer to extended precision destination operand		#
#	d0  rnd prec,mode						#
#									#
# OUTPUT **************************************************************	#
#	fp0 = result							#
#	fp1 = EXOP (if exception occurred)				#
#									#
# ALGORITHM ***********************************************************	#
#	Handle NANs, infinities, and zeroes as special cases. Divide	#
# norms/denorms into ext/sgl/dbl precision.				#
#	For norms/denorms, scale the exponents such that a multiply	#
# instruction won't cause an exception. Use the regular fmul to		#
# compute a result. Check if the regular operands would have taken	#
# an exception. If so, return the default overflow/underflow result	#
# and return the EXOP if exceptions are enabled. Else, scale the	#
# result operand to the proper exponent.				#
#									#
#########################################################################

	align		0x10
tbl_fmul_ovfl:
	long		0x3fff - 0x7ffe		# ext_max
	long		0x3fff - 0x407e		# sgl_max
	long		0x3fff - 0x43fe		# dbl_max
tbl_fmul_unfl:
	long		0x3fff + 0x0001		# ext_unfl
	long		0x3fff - 0x3f80		# sgl_unfl
	long		0x3fff - 0x3c00		# dbl_unfl

	global		fsmul
fsmul:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&s_mode*0x10,%d0	# insert sgl prec
	bra.b		fmul

	global		fdmul
fdmul:
	andi.b		&0x30,%d0
	ori.b		&d_mode*0x10,%d0	# insert dbl prec

	global		fmul
fmul:
	mov.l		%d0,L_SCR3(%a6)		# store rnd info

	clr.w		%d1
	mov.b		DTAG(%a6),%d1
	lsl.b		&0x3,%d1
	or.b		STAG(%a6),%d1		# combine src tags
	bne.w		fmul_not_norm		# optimize on non-norm input

fmul_norm:
	mov.w		DST_EX(%a1),FP_SCR1_EX(%a6)
	mov.l		DST_HI(%a1),FP_SCR1_HI(%a6)
	mov.l		DST_LO(%a1),FP_SCR1_LO(%a6)

	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)

	bsr.l		scale_to_zero_src	# scale src exponent
	mov.l		%d0,-(%sp)		# save scale factor 1

	bsr.l		scale_to_zero_dst	# scale dst exponent

	add.l		%d0,(%sp)		# SCALE_FACTOR = scale1 + scale2

	mov.w		2+L_SCR3(%a6),%d1	# fetch precision
	lsr.b		&0x6,%d1		# shift to lo bits
	mov.l		(%sp)+,%d0		# load S.F.
	cmp.l		%d0,(tbl_fmul_ovfl.w,%pc,%d1.w*4) # would result ovfl?
	beq.w		fmul_may_ovfl		# result may rnd to overflow
	blt.w		fmul_ovfl		# result will overflow

	cmp.l		%d0,(tbl_fmul_unfl.w,%pc,%d1.w*4) # would result unfl?
	beq.w		fmul_may_unfl		# result may rnd to no unfl
	bgt.w		fmul_unfl		# result will underflow

#
# NORMAL:
# - the result of the multiply operation will neither overflow nor underflow.
# - do the multiply to the proper precision and rounding mode.
# - scale the result exponent using the scale factor. if both operands were
# normalized then we really don't need to go through this scaling. but for now,
# this will do.
#
fmul_normal:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst operand

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fmul.x		FP_SCR0(%a6),%fp0	# execute multiply

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

fmul_normal_exit:
	fmovm.x		&0x80,FP_SCR0(%a6)	# store out result
	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# load {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	or.w		%d2,%d1			# concat old sign,new exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x80	# return default result in fp0
	rts

#
# OVERFLOW:
# - the result of the multiply operation is an overflow.
# - do the multiply to the proper precision and rounding mode in order to
# set the inexact bits.
# - calculate the default result and return it in fp0.
# - if overflow or inexact is enabled, we need a multiply result rounded to
# extended precision. if the original operation was extended, then we have this
# result. if the original operation was single or double, we have to do another
# multiply using extended precision and the correct rounding mode. the result
# of this operation then has its exponent scaled by -0x6000 to create the
# exceptional operand.
#
fmul_ovfl:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst operand

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fmul.x		FP_SCR0(%a6),%fp0	# execute multiply

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

# save setting this until now because this is where fmul_may_ovfl may jump in
fmul_ovfl_tst:
	or.l		&ovfl_inx_mask,USER_FPSR(%a6) # set ovfl/aovfl/ainex

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x13,%d1		# is OVFL or INEX enabled?
	bne.b		fmul_ovfl_ena		# yes

# calculate the default result
fmul_ovfl_dis:
	btst		&neg_bit,FPSR_CC(%a6)	# is result negative?
	sne		%d1			# set sign param accordingly
	mov.l		L_SCR3(%a6),%d0		# pass rnd prec,mode
	bsr.l		ovf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# set INF,N if applicable
	fmovm.x		(%a0),&0x80		# return default result in fp0
	rts

#
# OVFL is enabled; Create EXOP:
# - if precision is extended, then we have the EXOP. simply bias the exponent
# with an extra -0x6000. if the precision is single or double, we need to
# calculate a result rounded to extended precision.
#
fmul_ovfl_ena:
	mov.l		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# test the rnd prec
	bne.b		fmul_ovfl_ena_sd	# it's sgl or dbl

fmul_ovfl_ena_cont:
	fmovm.x		&0x80,FP_SCR0(%a6)	# move result to stack

	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.w		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	sub.l		%d0,%d1			# add scale factor
	subi.l		&0x6000,%d1		# subtract bias
	andi.w		&0x7fff,%d1		# clear sign bit
	andi.w		&0x8000,%d2		# keep old sign
	or.w		%d2,%d1			# concat old sign,new exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	bra.b		fmul_ovfl_dis

fmul_ovfl_ena_sd:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst operand

	mov.l		L_SCR3(%a6),%d1
	andi.b		&0x30,%d1		# keep rnd mode only
	fmov.l		%d1,%fpcr		# set FPCR

	fmul.x		FP_SCR0(%a6),%fp0	# execute multiply

	fmov.l		&0x0,%fpcr		# clear FPCR
	bra.b		fmul_ovfl_ena_cont

#
# may OVERFLOW:
# - the result of the multiply operation MAY overflow.
# - do the multiply to the proper precision and rounding mode in order to
# set the inexact bits.
# - calculate the default result and return it in fp0.
#
fmul_may_ovfl:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fmul.x		FP_SCR0(%a6),%fp0	# execute multiply

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	fabs.x		%fp0,%fp1		# make a copy of result
	fcmp.b		%fp1,&0x2		# is |result| >= 2.b?
	fbge.w		fmul_ovfl_tst		# yes; overflow has occurred

# no, it didn't overflow; we have correct result
	bra.w		fmul_normal_exit

#
# UNDERFLOW:
# - the result of the multiply operation is an underflow.
# - do the multiply to the proper precision and rounding mode in order to
# set the inexact bits.
# - calculate the default result and return it in fp0.
# - if overflow or inexact is enabled, we need a multiply result rounded to
# extended precision. if the original operation was extended, then we have this
# result. if the original operation was single or double, we have to do another
# multiply using extended precision and the correct rounding mode. the result
# of this operation then has its exponent scaled by -0x6000 to create the
# exceptional operand.
#
fmul_unfl:
	bset		&unfl_bit,FPSR_EXCEPT(%a6) # set unfl exc bit

# for fun, let's use only extended precision, round to zero. then, let
# the unf_res() routine figure out all the rest.
# will we get the correct answer.
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst operand

	fmov.l		&rz_mode*0x10,%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fmul.x		FP_SCR0(%a6),%fp0	# execute multiply

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x0b,%d1		# is UNFL or INEX enabled?
	bne.b		fmul_unfl_ena		# yes

fmul_unfl_dis:
	fmovm.x		&0x80,FP_SCR0(%a6)	# store out result

	lea		FP_SCR0(%a6),%a0	# pass: result addr
	mov.l		L_SCR3(%a6),%d1		# pass: rnd prec,mode
	bsr.l		unf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# unf_res2 may have set 'Z'
	fmovm.x		FP_SCR0(%a6),&0x80	# return default result in fp0
	rts

#
# UNFL is enabled.
#
fmul_unfl_ena:
	fmovm.x		FP_SCR1(%a6),&0x40	# load dst op

	mov.l		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# is precision extended?
	bne.b		fmul_unfl_ena_sd	# no, sgl or dbl

# if the rnd mode is anything but RZ, then we have to re-do the above
# multiplication because we used RZ for all.
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

fmul_unfl_ena_cont:
	fmov.l		&0x0,%fpsr		# clear FPSR

	fmul.x		FP_SCR0(%a6),%fp1	# execute multiply

	fmov.l		&0x0,%fpcr		# clear FPCR

	fmovm.x		&0x40,FP_SCR0(%a6)	# save result to stack
	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	addi.l		&0x6000,%d1		# add bias
	andi.w		&0x7fff,%d1
	or.w		%d2,%d1			# concat old sign,new exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	bra.w		fmul_unfl_dis

fmul_unfl_ena_sd:
	mov.l		L_SCR3(%a6),%d1
	andi.b		&0x30,%d1		# use only rnd mode
	fmov.l		%d1,%fpcr		# set FPCR

	bra.b		fmul_unfl_ena_cont

# MAY UNDERFLOW:
# -use the correct rounding mode and precision. this code favors operations
# that do not underflow.
fmul_may_unfl:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst operand

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fmul.x		FP_SCR0(%a6),%fp0	# execute multiply

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	fabs.x		%fp0,%fp1		# make a copy of result
	fcmp.b		%fp1,&0x2		# is |result| > 2.b?
	fbgt.w		fmul_normal_exit	# no; no underflow occurred
	fblt.w		fmul_unfl		# yes; underflow occurred

#
# we still don't know if underflow occurred. result is ~ equal to 2. but,
# we don't know if the result was an underflow that rounded up to a 2 or
# a normalized number that rounded down to a 2. so, redo the entire operation
# using RZ as the rounding mode to see what the pre-rounded result is.
# this case should be relatively rare.
#
	fmovm.x		FP_SCR1(%a6),&0x40	# load dst operand

	mov.l		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# keep rnd prec
	ori.b		&rz_mode*0x10,%d1	# insert RZ

	fmov.l		%d1,%fpcr		# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fmul.x		FP_SCR0(%a6),%fp1	# execute multiply

	fmov.l		&0x0,%fpcr		# clear FPCR
	fabs.x		%fp1			# make absolute value
	fcmp.b		%fp1,&0x2		# is |result| < 2.b?
	fbge.w		fmul_normal_exit	# no; no underflow occurred
	bra.w		fmul_unfl		# yes, underflow occurred

################################################################################

#
# Multiply: inputs are not both normalized; what are they?
#
fmul_not_norm:
	mov.w		(tbl_fmul_op.b,%pc,%d1.w*2),%d1
	jmp		(tbl_fmul_op.b,%pc,%d1.w)

	swbeg		&48
tbl_fmul_op:
	short		fmul_norm	- tbl_fmul_op # NORM x NORM
	short		fmul_zero	- tbl_fmul_op # NORM x ZERO
	short		fmul_inf_src	- tbl_fmul_op # NORM x INF
	short		fmul_res_qnan	- tbl_fmul_op # NORM x QNAN
	short		fmul_norm	- tbl_fmul_op # NORM x DENORM
	short		fmul_res_snan	- tbl_fmul_op # NORM x SNAN
	short		tbl_fmul_op	- tbl_fmul_op #
	short		tbl_fmul_op	- tbl_fmul_op #

	short		fmul_zero	- tbl_fmul_op # ZERO x NORM
	short		fmul_zero	- tbl_fmul_op # ZERO x ZERO
	short		fmul_res_operr	- tbl_fmul_op # ZERO x INF
	short		fmul_res_qnan	- tbl_fmul_op # ZERO x QNAN
	short		fmul_zero	- tbl_fmul_op # ZERO x DENORM
	short		fmul_res_snan	- tbl_fmul_op # ZERO x SNAN
	short		tbl_fmul_op	- tbl_fmul_op #
	short		tbl_fmul_op	- tbl_fmul_op #

	short		fmul_inf_dst	- tbl_fmul_op # INF x NORM
	short		fmul_res_operr	- tbl_fmul_op # INF x ZERO
	short		fmul_inf_dst	- tbl_fmul_op # INF x INF
	short		fmul_res_qnan	- tbl_fmul_op # INF x QNAN
	short		fmul_inf_dst	- tbl_fmul_op # INF x DENORM
	short		fmul_res_snan	- tbl_fmul_op # INF x SNAN
	short		tbl_fmul_op	- tbl_fmul_op #
	short		tbl_fmul_op	- tbl_fmul_op #

	short		fmul_res_qnan	- tbl_fmul_op # QNAN x NORM
	short		fmul_res_qnan	- tbl_fmul_op # QNAN x ZERO
	short		fmul_res_qnan	- tbl_fmul_op # QNAN x INF
	short		fmul_res_qnan	- tbl_fmul_op # QNAN x QNAN
	short		fmul_res_qnan	- tbl_fmul_op # QNAN x DENORM
	short		fmul_res_snan	- tbl_fmul_op # QNAN x SNAN
	short		tbl_fmul_op	- tbl_fmul_op #
	short		tbl_fmul_op	- tbl_fmul_op #

	short		fmul_norm	- tbl_fmul_op # NORM x NORM
	short		fmul_zero	- tbl_fmul_op # NORM x ZERO
	short		fmul_inf_src	- tbl_fmul_op # NORM x INF
	short		fmul_res_qnan	- tbl_fmul_op # NORM x QNAN
	short		fmul_norm	- tbl_fmul_op # NORM x DENORM
	short		fmul_res_snan	- tbl_fmul_op # NORM x SNAN
	short		tbl_fmul_op	- tbl_fmul_op #
	short		tbl_fmul_op	- tbl_fmul_op #

	short		fmul_res_snan	- tbl_fmul_op # SNAN x NORM
	short		fmul_res_snan	- tbl_fmul_op # SNAN x ZERO
	short		fmul_res_snan	- tbl_fmul_op # SNAN x INF
	short		fmul_res_snan	- tbl_fmul_op # SNAN x QNAN
	short		fmul_res_snan	- tbl_fmul_op # SNAN x DENORM
	short		fmul_res_snan	- tbl_fmul_op # SNAN x SNAN
	short		tbl_fmul_op	- tbl_fmul_op #
	short		tbl_fmul_op	- tbl_fmul_op #

fmul_res_operr:
	bra.l		res_operr
fmul_res_snan:
	bra.l		res_snan
fmul_res_qnan:
	bra.l		res_qnan

#
# Multiply: (Zero x Zero) || (Zero x norm) || (Zero x denorm)
#
	global		fmul_zero		# global for fsglmul
fmul_zero:
	mov.b		SRC_EX(%a0),%d0		# exclusive or the signs
	mov.b		DST_EX(%a1),%d1
	eor.b		%d0,%d1
	bpl.b		fmul_zero_p		# result ZERO is pos.
fmul_zero_n:
	fmov.s		&0x80000000,%fp0	# load -ZERO
	mov.b		&z_bmask+neg_bmask,FPSR_CC(%a6) # set Z/N
	rts
fmul_zero_p:
	fmov.s		&0x00000000,%fp0	# load +ZERO
	mov.b		&z_bmask,FPSR_CC(%a6)	# set Z
	rts

#
# Multiply: (inf x inf) || (inf x norm) || (inf x denorm)
#
# Note: The j-bit for an infinity is a don't-care. However, to be
# strictly compatible w/ the 68881/882, we make sure to return an
# INF w/ the j-bit set if the input INF j-bit was set. Destination
# INFs take priority.
#
	global		fmul_inf_dst		# global for fsglmul
fmul_inf_dst:
	fmovm.x		DST(%a1),&0x80		# return INF result in fp0
	mov.b		SRC_EX(%a0),%d0		# exclusive or the signs
	mov.b		DST_EX(%a1),%d1
	eor.b		%d0,%d1
	bpl.b		fmul_inf_dst_p		# result INF is pos.
fmul_inf_dst_n:
	fabs.x		%fp0			# clear result sign
	fneg.x		%fp0			# set result sign
	mov.b		&inf_bmask+neg_bmask,FPSR_CC(%a6) # set INF/N
	rts
fmul_inf_dst_p:
	fabs.x		%fp0			# clear result sign
	mov.b		&inf_bmask,FPSR_CC(%a6)	# set INF
	rts

	global		fmul_inf_src		# global for fsglmul
fmul_inf_src:
	fmovm.x		SRC(%a0),&0x80		# return INF result in fp0
	mov.b		SRC_EX(%a0),%d0		# exclusive or the signs
	mov.b		DST_EX(%a1),%d1
	eor.b		%d0,%d1
	bpl.b		fmul_inf_dst_p		# result INF is pos.
	bra.b		fmul_inf_dst_n

#########################################################################
# XDEF ****************************************************************	#
#	fin(): emulates the fmove instruction				#
#	fsin(): emulates the fsmove instruction				#
#	fdin(): emulates the fdmove instruction				#
#									#
# XREF ****************************************************************	#
#	norm() - normalize mantissa for EXOP on denorm			#
#	scale_to_zero_src() - scale src exponent to zero		#
#	ovf_res() - return default overflow result			#
#	unf_res() - return default underflow result			#
#	res_qnan_1op() - return QNAN result				#
#	res_snan_1op() - return SNAN result				#
#									#
# INPUT ***************************************************************	#
#	a0 = pointer to extended precision source operand		#
#	d0 = round prec/mode						#
#									#
# OUTPUT **************************************************************	#
#	fp0 = result							#
#	fp1 = EXOP (if exception occurred)				#
#									#
# ALGORITHM ***********************************************************	#
#	Handle NANs, infinities, and zeroes as special cases. Divide	#
# norms into extended, single, and double precision.			#
#	Norms can be emulated w/ a regular fmove instruction. For	#
# sgl/dbl, must scale exponent and perform an "fmove". Check to see	#
# if the result would have overflowed/underflowed. If so, use unf_res()	#
# or ovf_res() to return the default result. Also return EXOP if	#
# exception is enabled. If no exception, return the default result.	#
#	Unnorms don't pass through here.				#
#									#
#########################################################################

	global		fsin
fsin:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&s_mode*0x10,%d0	# insert sgl precision
	bra.b		fin

	global		fdin
fdin:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&d_mode*0x10,%d0	# insert dbl precision

	global		fin
fin:
	mov.l		%d0,L_SCR3(%a6)		# store rnd info

	mov.b		STAG(%a6),%d1		# fetch src optype tag
	bne.w		fin_not_norm		# optimize on non-norm input

#
# FP MOVE IN: NORMs and DENORMs ONLY!
#
fin_norm:
	andi.b		&0xc0,%d0		# is precision extended?
	bne.w		fin_not_ext		# no, so go handle dbl or sgl

#
# precision selected is extended. so...we cannot get an underflow
# or overflow because of rounding to the correct precision. so...
# skip the scaling and unscaling...
#
	tst.b		SRC_EX(%a0)		# is the operand negative?
	bpl.b		fin_norm_done		# no
	bset		&neg_bit,FPSR_CC(%a6)	# yes, so set 'N' ccode bit
fin_norm_done:
	fmovm.x		SRC(%a0),&0x80		# return result in fp0
	rts

#
# for an extended precision DENORM, the UNFL exception bit is set
# the accrued bit is NOT set in this instance(no inexactness!)
#
fin_denorm:
	andi.b		&0xc0,%d0		# is precision extended?
	bne.w		fin_not_ext		# no, so go handle dbl or sgl

	bset		&unfl_bit,FPSR_EXCEPT(%a6) # set unfl exc bit
	tst.b		SRC_EX(%a0)		# is the operand negative?
	bpl.b		fin_denorm_done		# no
	bset		&neg_bit,FPSR_CC(%a6)	# yes, so set 'N' ccode bit
fin_denorm_done:
	fmovm.x		SRC(%a0),&0x80		# return result in fp0
	btst		&unfl_bit,FPCR_ENABLE(%a6) # is UNFL enabled?
	bne.b		fin_denorm_unfl_ena	# yes
	rts

#
# the input is an extended DENORM and underflow is enabled in the FPCR.
# normalize the mantissa and add the bias of 0x6000 to the resulting negative
# exponent and insert back into the operand.
#
fin_denorm_unfl_ena:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	lea		FP_SCR0(%a6),%a0	# pass: ptr to operand
	bsr.l		norm			# normalize result
	neg.w		%d0			# new exponent = -(shft val)
	addi.w		&0x6000,%d0		# add new bias to exponent
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch old sign,exp
	andi.w		&0x8000,%d1		# keep old sign
	andi.w		&0x7fff,%d0		# clear sign position
	or.w		%d1,%d0			# concat new exo,old sign
	mov.w		%d0,FP_SCR0_EX(%a6)	# insert new exponent
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	rts

#
# operand is to be rounded to single or double precision
#
fin_not_ext:
	cmpi.b		%d0,&s_mode*0x10	# separate sgl/dbl prec
	bne.b		fin_dbl

#
# operand is to be rounded to single precision
#
fin_sgl:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	bsr.l		scale_to_zero_src	# calculate scale factor

	cmpi.l		%d0,&0x3fff-0x3f80	# will move in underflow?
	bge.w		fin_sd_unfl		# yes; go handle underflow
	cmpi.l		%d0,&0x3fff-0x407e	# will move in overflow?
	beq.w		fin_sd_may_ovfl		# maybe; go check
	blt.w		fin_sd_ovfl		# yes; go handle overflow

#
# operand will NOT overflow or underflow when moved into the fp reg file
#
fin_sd_normal:
	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fmov.x		FP_SCR0(%a6),%fp0	# perform move

	fmov.l		%fpsr,%d1		# save FPSR
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

fin_sd_normal_exit:
	mov.l		%d2,-(%sp)		# save d2
	fmovm.x		&0x80,FP_SCR0(%a6)	# store out result
	mov.w		FP_SCR0_EX(%a6),%d1	# load {sgn,exp}
	mov.w		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	sub.l		%d0,%d1			# add scale factor
	andi.w		&0x8000,%d2		# keep old sign
	or.w		%d1,%d2			# concat old sign,new exponent
	mov.w		%d2,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x80	# return result in fp0
	rts

#
# operand is to be rounded to double precision
#
fin_dbl:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	bsr.l		scale_to_zero_src	# calculate scale factor

	cmpi.l		%d0,&0x3fff-0x3c00	# will move in underflow?
	bge.w		fin_sd_unfl		# yes; go handle underflow
	cmpi.l		%d0,&0x3fff-0x43fe	# will move in overflow?
	beq.w		fin_sd_may_ovfl		# maybe; go check
	blt.w		fin_sd_ovfl		# yes; go handle overflow
	bra.w		fin_sd_normal		# no; ho handle normalized op

#
# operand WILL underflow when moved in to the fp register file
#
fin_sd_unfl:
	bset		&unfl_bit,FPSR_EXCEPT(%a6) # set unfl exc bit

	tst.b		FP_SCR0_EX(%a6)		# is operand negative?
	bpl.b		fin_sd_unfl_tst
	bset		&neg_bit,FPSR_CC(%a6)	# set 'N' ccode bit

# if underflow or inexact is enabled, then go calculate the EXOP first.
fin_sd_unfl_tst:
	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x0b,%d1		# is UNFL or INEX enabled?
	bne.b		fin_sd_unfl_ena		# yes

fin_sd_unfl_dis:
	lea		FP_SCR0(%a6),%a0	# pass: result addr
	mov.l		L_SCR3(%a6),%d1		# pass: rnd prec,mode
	bsr.l		unf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# unf_res may have set 'Z'
	fmovm.x		FP_SCR0(%a6),&0x80	# return default result in fp0
	rts

#
# operand will underflow AND underflow or inexact is enabled.
# Therefore, we must return the result rounded to extended precision.
#
fin_sd_unfl_ena:
	mov.l		FP_SCR0_HI(%a6),FP_SCR1_HI(%a6)
	mov.l		FP_SCR0_LO(%a6),FP_SCR1_LO(%a6)
	mov.w		FP_SCR0_EX(%a6),%d1	# load current exponent

	mov.l		%d2,-(%sp)		# save d2
	mov.w		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	sub.l		%d0,%d1			# subtract scale factor
	andi.w		&0x8000,%d2		# extract old sign
	addi.l		&0x6000,%d1		# add new bias
	andi.w		&0x7fff,%d1
	or.w		%d1,%d2			# concat old sign,new exp
	mov.w		%d2,FP_SCR1_EX(%a6)	# insert new exponent
	fmovm.x		FP_SCR1(%a6),&0x40	# return EXOP in fp1
	mov.l		(%sp)+,%d2		# restore d2
	bra.b		fin_sd_unfl_dis

#
# operand WILL overflow.
#
fin_sd_ovfl:
	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fmov.x		FP_SCR0(%a6),%fp0	# perform move

	fmov.l		&0x0,%fpcr		# clear FPCR
	fmov.l		%fpsr,%d1		# save FPSR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

fin_sd_ovfl_tst:
	or.l		&ovfl_inx_mask,USER_FPSR(%a6) # set ovfl/aovfl/ainex

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x13,%d1		# is OVFL or INEX enabled?
	bne.b		fin_sd_ovfl_ena		# yes

#
# OVFL is not enabled; therefore, we must create the default result by
# calling ovf_res().
#
fin_sd_ovfl_dis:
	btst		&neg_bit,FPSR_CC(%a6)	# is result negative?
	sne		%d1			# set sign param accordingly
	mov.l		L_SCR3(%a6),%d0		# pass: prec,mode
	bsr.l		ovf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# set INF,N if applicable
	fmovm.x		(%a0),&0x80		# return default result in fp0
	rts

#
# OVFL is enabled.
# the INEX2 bit has already been updated by the round to the correct precision.
# now, round to extended(and don't alter the FPSR).
#
fin_sd_ovfl_ena:
	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	sub.l		&0x6000,%d1		# subtract bias
	andi.w		&0x7fff,%d1
	or.w		%d2,%d1
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	bra.b		fin_sd_ovfl_dis

#
# the move in MAY overflow. so...
#
fin_sd_may_ovfl:
	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fmov.x		FP_SCR0(%a6),%fp0	# perform the move

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	fabs.x		%fp0,%fp1		# make a copy of result
	fcmp.b		%fp1,&0x2		# is |result| >= 2.b?
	fbge.w		fin_sd_ovfl_tst		# yes; overflow has occurred

# no, it didn't overflow; we have correct result
	bra.w		fin_sd_normal_exit

##########################################################################

#
# operand is not a NORM: check its optype and branch accordingly
#
fin_not_norm:
	cmpi.b		%d1,&DENORM		# weed out DENORM
	beq.w		fin_denorm
	cmpi.b		%d1,&SNAN		# weed out SNANs
	beq.l		res_snan_1op
	cmpi.b		%d1,&QNAN		# weed out QNANs
	beq.l		res_qnan_1op

#
# do the fmove in; at this point, only possible ops are ZERO and INF.
# use fmov to determine ccodes.
# prec:mode should be zero at this point but it won't affect answer anyways.
#
	fmov.x		SRC(%a0),%fp0		# do fmove in
	fmov.l		%fpsr,%d0		# no exceptions possible
	rol.l		&0x8,%d0		# put ccodes in lo byte
	mov.b		%d0,FPSR_CC(%a6)	# insert correct ccodes
	rts

#########################################################################
# XDEF ****************************************************************	#
#	fdiv(): emulates the fdiv instruction				#
#	fsdiv(): emulates the fsdiv instruction				#
#	fddiv(): emulates the fddiv instruction				#
#									#
# XREF ****************************************************************	#
#	scale_to_zero_src() - scale src exponent to zero		#
#	scale_to_zero_dst() - scale dst exponent to zero		#
#	unf_res() - return default underflow result			#
#	ovf_res() - return default overflow result			#
#	res_qnan() - return QNAN result					#
#	res_snan() - return SNAN result					#
#									#
# INPUT ***************************************************************	#
#	a0 = pointer to extended precision source operand		#
#	a1 = pointer to extended precision destination operand		#
#	d0  rnd prec,mode						#
#									#
# OUTPUT **************************************************************	#
#	fp0 = result							#
#	fp1 = EXOP (if exception occurred)				#
#									#
# ALGORITHM ***********************************************************	#
#	Handle NANs, infinities, and zeroes as special cases. Divide	#
# norms/denorms into ext/sgl/dbl precision.				#
#	For norms/denorms, scale the exponents such that a divide	#
# instruction won't cause an exception. Use the regular fdiv to		#
# compute a result. Check if the regular operands would have taken	#
# an exception. If so, return the default overflow/underflow result	#
# and return the EXOP if exceptions are enabled. Else, scale the	#
# result operand to the proper exponent.				#
#									#
#########################################################################

	align		0x10
tbl_fdiv_unfl:
	long		0x3fff - 0x0000		# ext_unfl
	long		0x3fff - 0x3f81		# sgl_unfl
	long		0x3fff - 0x3c01		# dbl_unfl

tbl_fdiv_ovfl:
	long		0x3fff - 0x7ffe		# ext overflow exponent
	long		0x3fff - 0x407e		# sgl overflow exponent
	long		0x3fff - 0x43fe		# dbl overflow exponent

	global		fsdiv
fsdiv:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&s_mode*0x10,%d0	# insert sgl prec
	bra.b		fdiv

	global		fddiv
fddiv:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&d_mode*0x10,%d0	# insert dbl prec

	global		fdiv
fdiv:
	mov.l		%d0,L_SCR3(%a6)		# store rnd info

	clr.w		%d1
	mov.b		DTAG(%a6),%d1
	lsl.b		&0x3,%d1
	or.b		STAG(%a6),%d1		# combine src tags

	bne.w		fdiv_not_norm		# optimize on non-norm input

#
# DIVIDE: NORMs and DENORMs ONLY!
#
fdiv_norm:
	mov.w		DST_EX(%a1),FP_SCR1_EX(%a6)
	mov.l		DST_HI(%a1),FP_SCR1_HI(%a6)
	mov.l		DST_LO(%a1),FP_SCR1_LO(%a6)

	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)

	bsr.l		scale_to_zero_src	# scale src exponent
	mov.l		%d0,-(%sp)		# save scale factor 1

	bsr.l		scale_to_zero_dst	# scale dst exponent

	neg.l		(%sp)			# SCALE FACTOR = scale1 - scale2
	add.l		%d0,(%sp)

	mov.w		2+L_SCR3(%a6),%d1	# fetch precision
	lsr.b		&0x6,%d1		# shift to lo bits
	mov.l		(%sp)+,%d0		# load S.F.
	cmp.l		%d0,(tbl_fdiv_ovfl.b,%pc,%d1.w*4) # will result overflow?
	ble.w		fdiv_may_ovfl		# result will overflow

	cmp.l		%d0,(tbl_fdiv_unfl.w,%pc,%d1.w*4) # will result underflow?
	beq.w		fdiv_may_unfl		# maybe
	bgt.w		fdiv_unfl		# yes; go handle underflow

fdiv_normal:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		L_SCR3(%a6),%fpcr	# save FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fdiv.x		FP_SCR0(%a6),%fp0	# perform divide

	fmov.l		%fpsr,%d1		# save FPSR
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

fdiv_normal_exit:
	fmovm.x		&0x80,FP_SCR0(%a6)	# store result on stack
	mov.l		%d2,-(%sp)		# store d2
	mov.w		FP_SCR0_EX(%a6),%d1	# load {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	or.w		%d2,%d1			# concat old sign,new exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x80	# return result in fp0
	rts

tbl_fdiv_ovfl2:
	long		0x7fff
	long		0x407f
	long		0x43ff

fdiv_no_ovfl:
	mov.l		(%sp)+,%d0		# restore scale factor
	bra.b		fdiv_normal_exit

fdiv_may_ovfl:
	mov.l		%d0,-(%sp)		# save scale factor

	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# set FPSR

	fdiv.x		FP_SCR0(%a6),%fp0	# execute divide

	fmov.l		%fpsr,%d0
	fmov.l		&0x0,%fpcr

	or.l		%d0,USER_FPSR(%a6)	# save INEX,N

	fmovm.x		&0x01,-(%sp)		# save result to stack
	mov.w		(%sp),%d0		# fetch new exponent
	add.l		&0xc,%sp		# clear result from stack
	andi.l		&0x7fff,%d0		# strip sign
	sub.l		(%sp),%d0		# add scale factor
	cmp.l		%d0,(tbl_fdiv_ovfl2.b,%pc,%d1.w*4)
	blt.b		fdiv_no_ovfl
	mov.l		(%sp)+,%d0

fdiv_ovfl_tst:
	or.l		&ovfl_inx_mask,USER_FPSR(%a6) # set ovfl/aovfl/ainex

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x13,%d1		# is OVFL or INEX enabled?
	bne.b		fdiv_ovfl_ena		# yes

fdiv_ovfl_dis:
	btst		&neg_bit,FPSR_CC(%a6)	# is result negative?
	sne		%d1			# set sign param accordingly
	mov.l		L_SCR3(%a6),%d0		# pass prec:rnd
	bsr.l		ovf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# set INF if applicable
	fmovm.x		(%a0),&0x80		# return default result in fp0
	rts

fdiv_ovfl_ena:
	mov.l		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# is precision extended?
	bne.b		fdiv_ovfl_ena_sd	# no, do sgl or dbl

fdiv_ovfl_ena_cont:
	fmovm.x		&0x80,FP_SCR0(%a6)	# move result to stack

	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.w		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	sub.l		%d0,%d1			# add scale factor
	subi.l		&0x6000,%d1		# subtract bias
	andi.w		&0x7fff,%d1		# clear sign bit
	andi.w		&0x8000,%d2		# keep old sign
	or.w		%d2,%d1			# concat old sign,new exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	bra.b		fdiv_ovfl_dis

fdiv_ovfl_ena_sd:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst operand

	mov.l		L_SCR3(%a6),%d1
	andi.b		&0x30,%d1		# keep rnd mode
	fmov.l		%d1,%fpcr		# set FPCR

	fdiv.x		FP_SCR0(%a6),%fp0	# execute divide

	fmov.l		&0x0,%fpcr		# clear FPCR
	bra.b		fdiv_ovfl_ena_cont

fdiv_unfl:
	bset		&unfl_bit,FPSR_EXCEPT(%a6) # set unfl exc bit

	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		&rz_mode*0x10,%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fdiv.x		FP_SCR0(%a6),%fp0	# execute divide

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x0b,%d1		# is UNFL or INEX enabled?
	bne.b		fdiv_unfl_ena		# yes

fdiv_unfl_dis:
	fmovm.x		&0x80,FP_SCR0(%a6)	# store out result

	lea		FP_SCR0(%a6),%a0	# pass: result addr
	mov.l		L_SCR3(%a6),%d1		# pass: rnd prec,mode
	bsr.l		unf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# 'Z' may have been set
	fmovm.x		FP_SCR0(%a6),&0x80	# return default result in fp0
	rts

#
# UNFL is enabled.
#
fdiv_unfl_ena:
	fmovm.x		FP_SCR1(%a6),&0x40	# load dst op

	mov.l		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# is precision extended?
	bne.b		fdiv_unfl_ena_sd	# no, sgl or dbl

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

fdiv_unfl_ena_cont:
	fmov.l		&0x0,%fpsr		# clear FPSR

	fdiv.x		FP_SCR0(%a6),%fp1	# execute divide

	fmov.l		&0x0,%fpcr		# clear FPCR

	fmovm.x		&0x40,FP_SCR0(%a6)	# save result to stack
	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factoer
	addi.l		&0x6000,%d1		# add bias
	andi.w		&0x7fff,%d1
	or.w		%d2,%d1			# concat old sign,new exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exp
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	bra.w		fdiv_unfl_dis

fdiv_unfl_ena_sd:
	mov.l		L_SCR3(%a6),%d1
	andi.b		&0x30,%d1		# use only rnd mode
	fmov.l		%d1,%fpcr		# set FPCR

	bra.b		fdiv_unfl_ena_cont

#
# the divide operation MAY underflow:
#
fdiv_may_unfl:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fdiv.x		FP_SCR0(%a6),%fp0	# execute divide

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	fabs.x		%fp0,%fp1		# make a copy of result
	fcmp.b		%fp1,&0x1		# is |result| > 1.b?
	fbgt.w		fdiv_normal_exit	# no; no underflow occurred
	fblt.w		fdiv_unfl		# yes; underflow occurred

#
# we still don't know if underflow occurred. result is ~ equal to 1. but,
# we don't know if the result was an underflow that rounded up to a 1
# or a normalized number that rounded down to a 1. so, redo the entire
# operation using RZ as the rounding mode to see what the pre-rounded
# result is. this case should be relatively rare.
#
	fmovm.x		FP_SCR1(%a6),&0x40	# load dst op into fp1

	mov.l		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# keep rnd prec
	ori.b		&rz_mode*0x10,%d1	# insert RZ

	fmov.l		%d1,%fpcr		# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fdiv.x		FP_SCR0(%a6),%fp1	# execute divide

	fmov.l		&0x0,%fpcr		# clear FPCR
	fabs.x		%fp1			# make absolute value
	fcmp.b		%fp1,&0x1		# is |result| < 1.b?
	fbge.w		fdiv_normal_exit	# no; no underflow occurred
	bra.w		fdiv_unfl		# yes; underflow occurred

############################################################################

#
# Divide: inputs are not both normalized; what are they?
#
fdiv_not_norm:
	mov.w		(tbl_fdiv_op.b,%pc,%d1.w*2),%d1
	jmp		(tbl_fdiv_op.b,%pc,%d1.w*1)

	swbeg		&48
tbl_fdiv_op:
	short		fdiv_norm	- tbl_fdiv_op # NORM / NORM
	short		fdiv_inf_load	- tbl_fdiv_op # NORM / ZERO
	short		fdiv_zero_load	- tbl_fdiv_op # NORM / INF
	short		fdiv_res_qnan	- tbl_fdiv_op # NORM / QNAN
	short		fdiv_norm	- tbl_fdiv_op # NORM / DENORM
	short		fdiv_res_snan	- tbl_fdiv_op # NORM / SNAN
	short		tbl_fdiv_op	- tbl_fdiv_op #
	short		tbl_fdiv_op	- tbl_fdiv_op #

	short		fdiv_zero_load	- tbl_fdiv_op # ZERO / NORM
	short		fdiv_res_operr	- tbl_fdiv_op # ZERO / ZERO
	short		fdiv_zero_load	- tbl_fdiv_op # ZERO / INF
	short		fdiv_res_qnan	- tbl_fdiv_op # ZERO / QNAN
	short		fdiv_zero_load	- tbl_fdiv_op # ZERO / DENORM
	short		fdiv_res_snan	- tbl_fdiv_op # ZERO / SNAN
	short		tbl_fdiv_op	- tbl_fdiv_op #
	short		tbl_fdiv_op	- tbl_fdiv_op #

	short		fdiv_inf_dst	- tbl_fdiv_op # INF / NORM
	short		fdiv_inf_dst	- tbl_fdiv_op # INF / ZERO
	short		fdiv_res_operr	- tbl_fdiv_op # INF / INF
	short		fdiv_res_qnan	- tbl_fdiv_op # INF / QNAN
	short		fdiv_inf_dst	- tbl_fdiv_op # INF / DENORM
	short		fdiv_res_snan	- tbl_fdiv_op # INF / SNAN
	short		tbl_fdiv_op	- tbl_fdiv_op #
	short		tbl_fdiv_op	- tbl_fdiv_op #

	short		fdiv_res_qnan	- tbl_fdiv_op # QNAN / NORM
	short		fdiv_res_qnan	- tbl_fdiv_op # QNAN / ZERO
	short		fdiv_res_qnan	- tbl_fdiv_op # QNAN / INF
	short		fdiv_res_qnan	- tbl_fdiv_op # QNAN / QNAN
	short		fdiv_res_qnan	- tbl_fdiv_op # QNAN / DENORM
	short		fdiv_res_snan	- tbl_fdiv_op # QNAN / SNAN
	short		tbl_fdiv_op	- tbl_fdiv_op #
	short		tbl_fdiv_op	- tbl_fdiv_op #

	short		fdiv_norm	- tbl_fdiv_op # DENORM / NORM
	short		fdiv_inf_load	- tbl_fdiv_op # DENORM / ZERO
	short		fdiv_zero_load	- tbl_fdiv_op # DENORM / INF
	short		fdiv_res_qnan	- tbl_fdiv_op # DENORM / QNAN
	short		fdiv_norm	- tbl_fdiv_op # DENORM / DENORM
	short		fdiv_res_snan	- tbl_fdiv_op # DENORM / SNAN
	short		tbl_fdiv_op	- tbl_fdiv_op #
	short		tbl_fdiv_op	- tbl_fdiv_op #

	short		fdiv_res_snan	- tbl_fdiv_op # SNAN / NORM
	short		fdiv_res_snan	- tbl_fdiv_op # SNAN / ZERO
	short		fdiv_res_snan	- tbl_fdiv_op # SNAN / INF
	short		fdiv_res_snan	- tbl_fdiv_op # SNAN / QNAN
	short		fdiv_res_snan	- tbl_fdiv_op # SNAN / DENORM
	short		fdiv_res_snan	- tbl_fdiv_op # SNAN / SNAN
	short		tbl_fdiv_op	- tbl_fdiv_op #
	short		tbl_fdiv_op	- tbl_fdiv_op #

fdiv_res_qnan:
	bra.l		res_qnan
fdiv_res_snan:
	bra.l		res_snan
fdiv_res_operr:
	bra.l		res_operr

	global		fdiv_zero_load		# global for fsgldiv
fdiv_zero_load:
	mov.b		SRC_EX(%a0),%d0		# result sign is exclusive
	mov.b		DST_EX(%a1),%d1		# or of input signs.
	eor.b		%d0,%d1
	bpl.b		fdiv_zero_load_p	# result is positive
	fmov.s		&0x80000000,%fp0	# load a -ZERO
	mov.b		&z_bmask+neg_bmask,FPSR_CC(%a6)	# set Z/N
	rts
fdiv_zero_load_p:
	fmov.s		&0x00000000,%fp0	# load a +ZERO
	mov.b		&z_bmask,FPSR_CC(%a6)	# set Z
	rts

#
# The destination was In Range and the source was a ZERO. The result,
# Therefore, is an INF w/ the proper sign.
# So, determine the sign and return a new INF (w/ the j-bit cleared).
#
	global		fdiv_inf_load		# global for fsgldiv
fdiv_inf_load:
	ori.w		&dz_mask+adz_mask,2+USER_FPSR(%a6) # no; set DZ/ADZ
	mov.b		SRC_EX(%a0),%d0		# load both signs
	mov.b		DST_EX(%a1),%d1
	eor.b		%d0,%d1
	bpl.b		fdiv_inf_load_p		# result is positive
	fmov.s		&0xff800000,%fp0	# make result -INF
	mov.b		&inf_bmask+neg_bmask,FPSR_CC(%a6) # set INF/N
	rts
fdiv_inf_load_p:
	fmov.s		&0x7f800000,%fp0	# make result +INF
	mov.b		&inf_bmask,FPSR_CC(%a6)	# set INF
	rts

#
# The destination was an INF w/ an In Range or ZERO source, the result is
# an INF w/ the proper sign.
# The 68881/882 returns the destination INF w/ the new sign(if the j-bit of the
# dst INF is set, then then j-bit of the result INF is also set).
#
	global		fdiv_inf_dst		# global for fsgldiv
fdiv_inf_dst:
	mov.b		DST_EX(%a1),%d0		# load both signs
	mov.b		SRC_EX(%a0),%d1
	eor.b		%d0,%d1
	bpl.b		fdiv_inf_dst_p		# result is positive

	fmovm.x		DST(%a1),&0x80		# return result in fp0
	fabs.x		%fp0			# clear sign bit
	fneg.x		%fp0			# set sign bit
	mov.b		&inf_bmask+neg_bmask,FPSR_CC(%a6) # set INF/NEG
	rts

fdiv_inf_dst_p:
	fmovm.x		DST(%a1),&0x80		# return result in fp0
	fabs.x		%fp0			# return positive INF
	mov.b		&inf_bmask,FPSR_CC(%a6) # set INF
	rts

#########################################################################
# XDEF ****************************************************************	#
#	fneg(): emulates the fneg instruction				#
#	fsneg(): emulates the fsneg instruction				#
#	fdneg(): emulates the fdneg instruction				#
#									#
# XREF ****************************************************************	#
#	norm() - normalize a denorm to provide EXOP			#
#	scale_to_zero_src() - scale sgl/dbl source exponent		#
#	ovf_res() - return default overflow result			#
#	unf_res() - return default underflow result			#
#	res_qnan_1op() - return QNAN result				#
#	res_snan_1op() - return SNAN result				#
#									#
# INPUT ***************************************************************	#
#	a0 = pointer to extended precision source operand		#
#	d0 = rnd prec,mode						#
#									#
# OUTPUT **************************************************************	#
#	fp0 = result							#
#	fp1 = EXOP (if exception occurred)				#
#									#
# ALGORITHM ***********************************************************	#
#	Handle NANs, zeroes, and infinities as special cases. Separate	#
# norms/denorms into ext/sgl/dbl precisions. Extended precision can be	#
# emulated by simply setting sign bit. Sgl/dbl operands must be scaled	#
# and an actual fneg performed to see if overflow/underflow would have	#
# occurred. If so, return default underflow/overflow result. Else,	#
# scale the result exponent and return result. FPSR gets set based on	#
# the result value.							#
#									#
#########################################################################

	global		fsneg
fsneg:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&s_mode*0x10,%d0	# insert sgl precision
	bra.b		fneg

	global		fdneg
fdneg:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&d_mode*0x10,%d0	# insert dbl prec

	global		fneg
fneg:
	mov.l		%d0,L_SCR3(%a6)		# store rnd info
	mov.b		STAG(%a6),%d1
	bne.w		fneg_not_norm		# optimize on non-norm input

#
# NEGATE SIGN : norms and denorms ONLY!
#
fneg_norm:
	andi.b		&0xc0,%d0		# is precision extended?
	bne.w		fneg_not_ext		# no; go handle sgl or dbl

#
# precision selected is extended. so...we can not get an underflow
# or overflow because of rounding to the correct precision. so...
# skip the scaling and unscaling...
#
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	mov.w		SRC_EX(%a0),%d0
	eori.w		&0x8000,%d0		# negate sign
	bpl.b		fneg_norm_load		# sign is positive
	mov.b		&neg_bmask,FPSR_CC(%a6)	# set 'N' ccode bit
fneg_norm_load:
	mov.w		%d0,FP_SCR0_EX(%a6)
	fmovm.x		FP_SCR0(%a6),&0x80	# return result in fp0
	rts

#
# for an extended precision DENORM, the UNFL exception bit is set
# the accrued bit is NOT set in this instance(no inexactness!)
#
fneg_denorm:
	andi.b		&0xc0,%d0		# is precision extended?
	bne.b		fneg_not_ext		# no; go handle sgl or dbl

	bset		&unfl_bit,FPSR_EXCEPT(%a6) # set unfl exc bit

	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	mov.w		SRC_EX(%a0),%d0
	eori.w		&0x8000,%d0		# negate sign
	bpl.b		fneg_denorm_done	# no
	mov.b		&neg_bmask,FPSR_CC(%a6)	# yes, set 'N' ccode bit
fneg_denorm_done:
	mov.w		%d0,FP_SCR0_EX(%a6)
	fmovm.x		FP_SCR0(%a6),&0x80	# return default result in fp0

	btst		&unfl_bit,FPCR_ENABLE(%a6) # is UNFL enabled?
	bne.b		fneg_ext_unfl_ena	# yes
	rts

#
# the input is an extended DENORM and underflow is enabled in the FPCR.
# normalize the mantissa and add the bias of 0x6000 to the resulting negative
# exponent and insert back into the operand.
#
fneg_ext_unfl_ena:
	lea		FP_SCR0(%a6),%a0	# pass: ptr to operand
	bsr.l		norm			# normalize result
	neg.w		%d0			# new exponent = -(shft val)
	addi.w		&0x6000,%d0		# add new bias to exponent
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch old sign,exp
	andi.w		&0x8000,%d1		# keep old sign
	andi.w		&0x7fff,%d0		# clear sign position
	or.w		%d1,%d0			# concat old sign, new exponent
	mov.w		%d0,FP_SCR0_EX(%a6)	# insert new exponent
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	rts

#
# operand is either single or double
#
fneg_not_ext:
	cmpi.b		%d0,&s_mode*0x10	# separate sgl/dbl prec
	bne.b		fneg_dbl

#
# operand is to be rounded to single precision
#
fneg_sgl:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	bsr.l		scale_to_zero_src	# calculate scale factor

	cmpi.l		%d0,&0x3fff-0x3f80	# will move in underflow?
	bge.w		fneg_sd_unfl		# yes; go handle underflow
	cmpi.l		%d0,&0x3fff-0x407e	# will move in overflow?
	beq.w		fneg_sd_may_ovfl	# maybe; go check
	blt.w		fneg_sd_ovfl		# yes; go handle overflow

#
# operand will NOT overflow or underflow when moved in to the fp reg file
#
fneg_sd_normal:
	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fneg.x		FP_SCR0(%a6),%fp0	# perform negation

	fmov.l		%fpsr,%d1		# save FPSR
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

fneg_sd_normal_exit:
	mov.l		%d2,-(%sp)		# save d2
	fmovm.x		&0x80,FP_SCR0(%a6)	# store out result
	mov.w		FP_SCR0_EX(%a6),%d1	# load sgn,exp
	mov.w		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	sub.l		%d0,%d1			# add scale factor
	andi.w		&0x8000,%d2		# keep old sign
	or.w		%d1,%d2			# concat old sign,new exp
	mov.w		%d2,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x80	# return result in fp0
	rts

#
# operand is to be rounded to double precision
#
fneg_dbl:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	bsr.l		scale_to_zero_src	# calculate scale factor

	cmpi.l		%d0,&0x3fff-0x3c00	# will move in underflow?
	bge.b		fneg_sd_unfl		# yes; go handle underflow
	cmpi.l		%d0,&0x3fff-0x43fe	# will move in overflow?
	beq.w		fneg_sd_may_ovfl	# maybe; go check
	blt.w		fneg_sd_ovfl		# yes; go handle overflow
	bra.w		fneg_sd_normal		# no; ho handle normalized op

#
# operand WILL underflow when moved in to the fp register file
#
fneg_sd_unfl:
	bset		&unfl_bit,FPSR_EXCEPT(%a6) # set unfl exc bit

	eori.b		&0x80,FP_SCR0_EX(%a6)	# negate sign
	bpl.b		fneg_sd_unfl_tst
	bset		&neg_bit,FPSR_CC(%a6)	# set 'N' ccode bit

# if underflow or inexact is enabled, go calculate EXOP first.
fneg_sd_unfl_tst:
	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x0b,%d1		# is UNFL or INEX enabled?
	bne.b		fneg_sd_unfl_ena	# yes

fneg_sd_unfl_dis:
	lea		FP_SCR0(%a6),%a0	# pass: result addr
	mov.l		L_SCR3(%a6),%d1		# pass: rnd prec,mode
	bsr.l		unf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# unf_res may have set 'Z'
	fmovm.x		FP_SCR0(%a6),&0x80	# return default result in fp0
	rts

#
# operand will underflow AND underflow is enabled.
# Therefore, we must return the result rounded to extended precision.
#
fneg_sd_unfl_ena:
	mov.l		FP_SCR0_HI(%a6),FP_SCR1_HI(%a6)
	mov.l		FP_SCR0_LO(%a6),FP_SCR1_LO(%a6)
	mov.w		FP_SCR0_EX(%a6),%d1	# load current exponent

	mov.l		%d2,-(%sp)		# save d2
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# subtract scale factor
	addi.l		&0x6000,%d1		# add new bias
	andi.w		&0x7fff,%d1
	or.w		%d2,%d1			# concat new sign,new exp
	mov.w		%d1,FP_SCR1_EX(%a6)	# insert new exp
	fmovm.x		FP_SCR1(%a6),&0x40	# return EXOP in fp1
	mov.l		(%sp)+,%d2		# restore d2
	bra.b		fneg_sd_unfl_dis

#
# operand WILL overflow.
#
fneg_sd_ovfl:
	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fneg.x		FP_SCR0(%a6),%fp0	# perform negation

	fmov.l		&0x0,%fpcr		# clear FPCR
	fmov.l		%fpsr,%d1		# save FPSR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

fneg_sd_ovfl_tst:
	or.l		&ovfl_inx_mask,USER_FPSR(%a6) # set ovfl/aovfl/ainex

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x13,%d1		# is OVFL or INEX enabled?
	bne.b		fneg_sd_ovfl_ena	# yes

#
# OVFL is not enabled; therefore, we must create the default result by
# calling ovf_res().
#
fneg_sd_ovfl_dis:
	btst		&neg_bit,FPSR_CC(%a6)	# is result negative?
	sne		%d1			# set sign param accordingly
	mov.l		L_SCR3(%a6),%d0		# pass: prec,mode
	bsr.l		ovf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# set INF,N if applicable
	fmovm.x		(%a0),&0x80		# return default result in fp0
	rts

#
# OVFL is enabled.
# the INEX2 bit has already been updated by the round to the correct precision.
# now, round to extended(and don't alter the FPSR).
#
fneg_sd_ovfl_ena:
	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	subi.l		&0x6000,%d1		# subtract bias
	andi.w		&0x7fff,%d1
	or.w		%d2,%d1			# concat sign,exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	mov.l		(%sp)+,%d2		# restore d2
	bra.b		fneg_sd_ovfl_dis

#
# the move in MAY underflow. so...
#
fneg_sd_may_ovfl:
	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fneg.x		FP_SCR0(%a6),%fp0	# perform negation

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	fabs.x		%fp0,%fp1		# make a copy of result
	fcmp.b		%fp1,&0x2		# is |result| >= 2.b?
	fbge.w		fneg_sd_ovfl_tst	# yes; overflow has occurred

# no, it didn't overflow; we have correct result
	bra.w		fneg_sd_normal_exit

##########################################################################

#
# input is not normalized; what is it?
#
fneg_not_norm:
	cmpi.b		%d1,&DENORM		# weed out DENORM
	beq.w		fneg_denorm
	cmpi.b		%d1,&SNAN		# weed out SNAN
	beq.l		res_snan_1op
	cmpi.b		%d1,&QNAN		# weed out QNAN
	beq.l		res_qnan_1op

#
# do the fneg; at this point, only possible ops are ZERO and INF.
# use fneg to determine ccodes.
# prec:mode should be zero at this point but it won't affect answer anyways.
#
	fneg.x		SRC_EX(%a0),%fp0	# do fneg
	fmov.l		%fpsr,%d0
	rol.l		&0x8,%d0		# put ccodes in lo byte
	mov.b		%d0,FPSR_CC(%a6)	# insert correct ccodes
	rts

#########################################################################
# XDEF ****************************************************************	#
#	ftst(): emulates the ftest instruction				#
#									#
# XREF ****************************************************************	#
#	res{s,q}nan_1op() - set NAN result for monadic instruction	#
#									#
# INPUT ***************************************************************	#
#	a0 = pointer to extended precision source operand		#
#									#
# OUTPUT **************************************************************	#
#	none								#
#									#
# ALGORITHM ***********************************************************	#
#	Check the source operand tag (STAG) and set the FPCR according	#
# to the operand type and sign.						#
#									#
#########################################################################

	global		ftst
ftst:
	mov.b		STAG(%a6),%d1
	bne.b		ftst_not_norm		# optimize on non-norm input

#
# Norm:
#
ftst_norm:
	tst.b		SRC_EX(%a0)		# is operand negative?
	bmi.b		ftst_norm_m		# yes
	rts
ftst_norm_m:
	mov.b		&neg_bmask,FPSR_CC(%a6)	# set 'N' ccode bit
	rts

#
# input is not normalized; what is it?
#
ftst_not_norm:
	cmpi.b		%d1,&ZERO		# weed out ZERO
	beq.b		ftst_zero
	cmpi.b		%d1,&INF		# weed out INF
	beq.b		ftst_inf
	cmpi.b		%d1,&SNAN		# weed out SNAN
	beq.l		res_snan_1op
	cmpi.b		%d1,&QNAN		# weed out QNAN
	beq.l		res_qnan_1op

#
# Denorm:
#
ftst_denorm:
	tst.b		SRC_EX(%a0)		# is operand negative?
	bmi.b		ftst_denorm_m		# yes
	rts
ftst_denorm_m:
	mov.b		&neg_bmask,FPSR_CC(%a6)	# set 'N' ccode bit
	rts

#
# Infinity:
#
ftst_inf:
	tst.b		SRC_EX(%a0)		# is operand negative?
	bmi.b		ftst_inf_m		# yes
ftst_inf_p:
	mov.b		&inf_bmask,FPSR_CC(%a6)	# set 'I' ccode bit
	rts
ftst_inf_m:
	mov.b		&inf_bmask+neg_bmask,FPSR_CC(%a6) # set 'I','N' ccode bits
	rts

#
# Zero:
#
ftst_zero:
	tst.b		SRC_EX(%a0)		# is operand negative?
	bmi.b		ftst_zero_m		# yes
ftst_zero_p:
	mov.b		&z_bmask,FPSR_CC(%a6)	# set 'N' ccode bit
	rts
ftst_zero_m:
	mov.b		&z_bmask+neg_bmask,FPSR_CC(%a6)	# set 'Z','N' ccode bits
	rts

#########################################################################
# XDEF ****************************************************************	#
#	fint(): emulates the fint instruction				#
#									#
# XREF ****************************************************************	#
#	res_{s,q}nan_1op() - set NAN result for monadic operation	#
#									#
# INPUT ***************************************************************	#
#	a0 = pointer to extended precision source operand		#
#	d0 = round precision/mode					#
#									#
# OUTPUT **************************************************************	#
#	fp0 = result							#
#									#
# ALGORITHM ***********************************************************	#
#	Separate according to operand type. Unnorms don't pass through	#
# here. For norms, load the rounding mode/prec, execute a "fint", then	#
# store the resulting FPSR bits.					#
#	For denorms, force the j-bit to a one and do the same as for	#
# norms. Denorms are so low that the answer will either be a zero or a	#
# one.									#
#	For zeroes/infs/NANs, return the same while setting the FPSR	#
# as appropriate.							#
#									#
#########################################################################

	global		fint
fint:
	mov.b		STAG(%a6),%d1
	bne.b		fint_not_norm		# optimize on non-norm input

#
# Norm:
#
fint_norm:
	andi.b		&0x30,%d0		# set prec = ext

	fmov.l		%d0,%fpcr		# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fint.x		SRC(%a0),%fp0		# execute fint

	fmov.l		&0x0,%fpcr		# clear FPCR
	fmov.l		%fpsr,%d0		# save FPSR
	or.l		%d0,USER_FPSR(%a6)	# set exception bits

	rts

#
# input is not normalized; what is it?
#
fint_not_norm:
	cmpi.b		%d1,&ZERO		# weed out ZERO
	beq.b		fint_zero
	cmpi.b		%d1,&INF		# weed out INF
	beq.b		fint_inf
	cmpi.b		%d1,&DENORM		# weed out DENORM
	beq.b		fint_denorm
	cmpi.b		%d1,&SNAN		# weed out SNAN
	beq.l		res_snan_1op
	bra.l		res_qnan_1op		# weed out QNAN

#
# Denorm:
#
# for DENORMs, the result will be either (+/-)ZERO or (+/-)1.
# also, the INEX2 and AINEX exception bits will be set.
# so, we could either set these manually or force the DENORM
# to a very small NORM and ship it to the NORM routine.
# I do the latter.
#
fint_denorm:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6) # copy sign, zero exp
	mov.b		&0x80,FP_SCR0_HI(%a6)	# force DENORM ==> small NORM
	lea		FP_SCR0(%a6),%a0
	bra.b		fint_norm

#
# Zero:
#
fint_zero:
	tst.b		SRC_EX(%a0)		# is ZERO negative?
	bmi.b		fint_zero_m		# yes
fint_zero_p:
	fmov.s		&0x00000000,%fp0	# return +ZERO in fp0
	mov.b		&z_bmask,FPSR_CC(%a6)	# set 'Z' ccode bit
	rts
fint_zero_m:
	fmov.s		&0x80000000,%fp0	# return -ZERO in fp0
	mov.b		&z_bmask+neg_bmask,FPSR_CC(%a6) # set 'Z','N' ccode bits
	rts

#
# Infinity:
#
fint_inf:
	fmovm.x		SRC(%a0),&0x80		# return result in fp0
	tst.b		SRC_EX(%a0)		# is INF negative?
	bmi.b		fint_inf_m		# yes
fint_inf_p:
	mov.b		&inf_bmask,FPSR_CC(%a6)	# set 'I' ccode bit
	rts
fint_inf_m:
	mov.b		&inf_bmask+neg_bmask,FPSR_CC(%a6) # set 'N','I' ccode bits
	rts

#########################################################################
# XDEF ****************************************************************	#
#	fintrz(): emulates the fintrz instruction			#
#									#
# XREF ****************************************************************	#
#	res_{s,q}nan_1op() - set NAN result for monadic operation	#
#									#
# INPUT ***************************************************************	#
#	a0 = pointer to extended precision source operand		#
#	d0 = round precision/mode					#
#									#
# OUTPUT **************************************************************	#
#	fp0 = result							#
#									#
# ALGORITHM ***********************************************************	#
#	Separate according to operand type. Unnorms don't pass through	#
# here. For norms, load the rounding mode/prec, execute a "fintrz",	#
# then store the resulting FPSR bits.					#
#	For denorms, force the j-bit to a one and do the same as for	#
# norms. Denorms are so low that the answer will either be a zero or a	#
# one.									#
#	For zeroes/infs/NANs, return the same while setting the FPSR	#
# as appropriate.							#
#									#
#########################################################################

	global		fintrz
fintrz:
	mov.b		STAG(%a6),%d1
	bne.b		fintrz_not_norm		# optimize on non-norm input

#
# Norm:
#
fintrz_norm:
	fmov.l		&0x0,%fpsr		# clear FPSR

	fintrz.x	SRC(%a0),%fp0		# execute fintrz

	fmov.l		%fpsr,%d0		# save FPSR
	or.l		%d0,USER_FPSR(%a6)	# set exception bits

	rts

#
# input is not normalized; what is it?
#
fintrz_not_norm:
	cmpi.b		%d1,&ZERO		# weed out ZERO
	beq.b		fintrz_zero
	cmpi.b		%d1,&INF		# weed out INF
	beq.b		fintrz_inf
	cmpi.b		%d1,&DENORM		# weed out DENORM
	beq.b		fintrz_denorm
	cmpi.b		%d1,&SNAN		# weed out SNAN
	beq.l		res_snan_1op
	bra.l		res_qnan_1op		# weed out QNAN

#
# Denorm:
#
# for DENORMs, the result will be (+/-)ZERO.
# also, the INEX2 and AINEX exception bits will be set.
# so, we could either set these manually or force the DENORM
# to a very small NORM and ship it to the NORM routine.
# I do the latter.
#
fintrz_denorm:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6) # copy sign, zero exp
	mov.b		&0x80,FP_SCR0_HI(%a6)	# force DENORM ==> small NORM
	lea		FP_SCR0(%a6),%a0
	bra.b		fintrz_norm

#
# Zero:
#
fintrz_zero:
	tst.b		SRC_EX(%a0)		# is ZERO negative?
	bmi.b		fintrz_zero_m		# yes
fintrz_zero_p:
	fmov.s		&0x00000000,%fp0	# return +ZERO in fp0
	mov.b		&z_bmask,FPSR_CC(%a6)	# set 'Z' ccode bit
	rts
fintrz_zero_m:
	fmov.s		&0x80000000,%fp0	# return -ZERO in fp0
	mov.b		&z_bmask+neg_bmask,FPSR_CC(%a6) # set 'Z','N' ccode bits
	rts

#
# Infinity:
#
fintrz_inf:
	fmovm.x		SRC(%a0),&0x80		# return result in fp0
	tst.b		SRC_EX(%a0)		# is INF negative?
	bmi.b		fintrz_inf_m		# yes
fintrz_inf_p:
	mov.b		&inf_bmask,FPSR_CC(%a6)	# set 'I' ccode bit
	rts
fintrz_inf_m:
	mov.b		&inf_bmask+neg_bmask,FPSR_CC(%a6) # set 'N','I' ccode bits
	rts

#########################################################################
# XDEF ****************************************************************	#
#	fabs():  emulates the fabs instruction				#
#	fsabs(): emulates the fsabs instruction				#
#	fdabs(): emulates the fdabs instruction				#
#									#
# XREF **************************************************************** #
#	norm() - normalize denorm mantissa to provide EXOP		#
#	scale_to_zero_src() - make exponent. = 0; get scale factor	#
#	unf_res() - calculate underflow result				#
#	ovf_res() - calculate overflow result				#
#	res_{s,q}nan_1op() - set NAN result for monadic operation	#
#									#
# INPUT *************************************************************** #
#	a0 = pointer to extended precision source operand		#
#	d0 = rnd precision/mode						#
#									#
# OUTPUT ************************************************************** #
#	fp0 = result							#
#	fp1 = EXOP (if exception occurred)				#
#									#
# ALGORITHM ***********************************************************	#
#	Handle NANs, infinities, and zeroes as special cases. Divide	#
# norms into extended, single, and double precision.			#
#	Simply clear sign for extended precision norm. Ext prec denorm	#
# gets an EXOP created for it since it's an underflow.			#
#	Double and single precision can overflow and underflow. First,	#
# scale the operand such that the exponent is zero. Perform an "fabs"	#
# using the correct rnd mode/prec. Check to see if the original		#
# exponent would take an exception. If so, use unf_res() or ovf_res()	#
# to calculate the default result. Also, create the EXOP for the	#
# exceptional case. If no exception should occur, insert the correct	#
# result exponent and return.						#
#	Unnorms don't pass through here.				#
#									#
#########################################################################

	global		fsabs
fsabs:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&s_mode*0x10,%d0	# insert sgl precision
	bra.b		fabs

	global		fdabs
fdabs:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&d_mode*0x10,%d0	# insert dbl precision

	global		fabs
fabs:
	mov.l		%d0,L_SCR3(%a6)		# store rnd info
	mov.b		STAG(%a6),%d1
	bne.w		fabs_not_norm		# optimize on non-norm input

#
# ABSOLUTE VALUE: norms and denorms ONLY!
#
fabs_norm:
	andi.b		&0xc0,%d0		# is precision extended?
	bne.b		fabs_not_ext		# no; go handle sgl or dbl

#
# precision selected is extended. so...we can not get an underflow
# or overflow because of rounding to the correct precision. so...
# skip the scaling and unscaling...
#
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	mov.w		SRC_EX(%a0),%d1
	bclr		&15,%d1			# force absolute value
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert exponent
	fmovm.x		FP_SCR0(%a6),&0x80	# return result in fp0
	rts

#
# for an extended precision DENORM, the UNFL exception bit is set
# the accrued bit is NOT set in this instance(no inexactness!)
#
fabs_denorm:
	andi.b		&0xc0,%d0		# is precision extended?
	bne.b		fabs_not_ext		# no

	bset		&unfl_bit,FPSR_EXCEPT(%a6) # set unfl exc bit

	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	mov.w		SRC_EX(%a0),%d0
	bclr		&15,%d0			# clear sign
	mov.w		%d0,FP_SCR0_EX(%a6)	# insert exponent

	fmovm.x		FP_SCR0(%a6),&0x80	# return default result in fp0

	btst		&unfl_bit,FPCR_ENABLE(%a6) # is UNFL enabled?
	bne.b		fabs_ext_unfl_ena
	rts

#
# the input is an extended DENORM and underflow is enabled in the FPCR.
# normalize the mantissa and add the bias of 0x6000 to the resulting negative
# exponent and insert back into the operand.
#
fabs_ext_unfl_ena:
	lea		FP_SCR0(%a6),%a0	# pass: ptr to operand
	bsr.l		norm			# normalize result
	neg.w		%d0			# new exponent = -(shft val)
	addi.w		&0x6000,%d0		# add new bias to exponent
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch old sign,exp
	andi.w		&0x8000,%d1		# keep old sign
	andi.w		&0x7fff,%d0		# clear sign position
	or.w		%d1,%d0			# concat old sign, new exponent
	mov.w		%d0,FP_SCR0_EX(%a6)	# insert new exponent
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	rts

#
# operand is either single or double
#
fabs_not_ext:
	cmpi.b		%d0,&s_mode*0x10	# separate sgl/dbl prec
	bne.b		fabs_dbl

#
# operand is to be rounded to single precision
#
fabs_sgl:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	bsr.l		scale_to_zero_src	# calculate scale factor

	cmpi.l		%d0,&0x3fff-0x3f80	# will move in underflow?
	bge.w		fabs_sd_unfl		# yes; go handle underflow
	cmpi.l		%d0,&0x3fff-0x407e	# will move in overflow?
	beq.w		fabs_sd_may_ovfl	# maybe; go check
	blt.w		fabs_sd_ovfl		# yes; go handle overflow

#
# operand will NOT overflow or underflow when moved in to the fp reg file
#
fabs_sd_normal:
	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fabs.x		FP_SCR0(%a6),%fp0	# perform absolute

	fmov.l		%fpsr,%d1		# save FPSR
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

fabs_sd_normal_exit:
	mov.l		%d2,-(%sp)		# save d2
	fmovm.x		&0x80,FP_SCR0(%a6)	# store out result
	mov.w		FP_SCR0_EX(%a6),%d1	# load sgn,exp
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	sub.l		%d0,%d1			# add scale factor
	andi.w		&0x8000,%d2		# keep old sign
	or.w		%d1,%d2			# concat old sign,new exp
	mov.w		%d2,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x80	# return result in fp0
	rts

#
# operand is to be rounded to double precision
#
fabs_dbl:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	bsr.l		scale_to_zero_src	# calculate scale factor

	cmpi.l		%d0,&0x3fff-0x3c00	# will move in underflow?
	bge.b		fabs_sd_unfl		# yes; go handle underflow
	cmpi.l		%d0,&0x3fff-0x43fe	# will move in overflow?
	beq.w		fabs_sd_may_ovfl	# maybe; go check
	blt.w		fabs_sd_ovfl		# yes; go handle overflow
	bra.w		fabs_sd_normal		# no; ho handle normalized op

#
# operand WILL underflow when moved in to the fp register file
#
fabs_sd_unfl:
	bset		&unfl_bit,FPSR_EXCEPT(%a6) # set unfl exc bit

	bclr		&0x7,FP_SCR0_EX(%a6)	# force absolute value

# if underflow or inexact is enabled, go calculate EXOP first.
	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x0b,%d1		# is UNFL or INEX enabled?
	bne.b		fabs_sd_unfl_ena	# yes

fabs_sd_unfl_dis:
	lea		FP_SCR0(%a6),%a0	# pass: result addr
	mov.l		L_SCR3(%a6),%d1		# pass: rnd prec,mode
	bsr.l		unf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# set possible 'Z' ccode
	fmovm.x		FP_SCR0(%a6),&0x80	# return default result in fp0
	rts

#
# operand will underflow AND underflow is enabled.
# Therefore, we must return the result rounded to extended precision.
#
fabs_sd_unfl_ena:
	mov.l		FP_SCR0_HI(%a6),FP_SCR1_HI(%a6)
	mov.l		FP_SCR0_LO(%a6),FP_SCR1_LO(%a6)
	mov.w		FP_SCR0_EX(%a6),%d1	# load current exponent

	mov.l		%d2,-(%sp)		# save d2
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# subtract scale factor
	addi.l		&0x6000,%d1		# add new bias
	andi.w		&0x7fff,%d1
	or.w		%d2,%d1			# concat new sign,new exp
	mov.w		%d1,FP_SCR1_EX(%a6)	# insert new exp
	fmovm.x		FP_SCR1(%a6),&0x40	# return EXOP in fp1
	mov.l		(%sp)+,%d2		# restore d2
	bra.b		fabs_sd_unfl_dis

#
# operand WILL overflow.
#
fabs_sd_ovfl:
	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fabs.x		FP_SCR0(%a6),%fp0	# perform absolute

	fmov.l		&0x0,%fpcr		# clear FPCR
	fmov.l		%fpsr,%d1		# save FPSR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

fabs_sd_ovfl_tst:
	or.l		&ovfl_inx_mask,USER_FPSR(%a6) # set ovfl/aovfl/ainex

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x13,%d1		# is OVFL or INEX enabled?
	bne.b		fabs_sd_ovfl_ena	# yes

#
# OVFL is not enabled; therefore, we must create the default result by
# calling ovf_res().
#
fabs_sd_ovfl_dis:
	btst		&neg_bit,FPSR_CC(%a6)	# is result negative?
	sne		%d1			# set sign param accordingly
	mov.l		L_SCR3(%a6),%d0		# pass: prec,mode
	bsr.l		ovf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# set INF,N if applicable
	fmovm.x		(%a0),&0x80		# return default result in fp0
	rts

#
# OVFL is enabled.
# the INEX2 bit has already been updated by the round to the correct precision.
# now, round to extended(and don't alter the FPSR).
#
fabs_sd_ovfl_ena:
	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	subi.l		&0x6000,%d1		# subtract bias
	andi.w		&0x7fff,%d1
	or.w		%d2,%d1			# concat sign,exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	mov.l		(%sp)+,%d2		# restore d2
	bra.b		fabs_sd_ovfl_dis

#
# the move in MAY underflow. so...
#
fabs_sd_may_ovfl:
	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fabs.x		FP_SCR0(%a6),%fp0	# perform absolute

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	fabs.x		%fp0,%fp1		# make a copy of result
	fcmp.b		%fp1,&0x2		# is |result| >= 2.b?
	fbge.w		fabs_sd_ovfl_tst	# yes; overflow has occurred

# no, it didn't overflow; we have correct result
	bra.w		fabs_sd_normal_exit

##########################################################################

#
# input is not normalized; what is it?
#
fabs_not_norm:
	cmpi.b		%d1,&DENORM		# weed out DENORM
	beq.w		fabs_denorm
	cmpi.b		%d1,&SNAN		# weed out SNAN
	beq.l		res_snan_1op
	cmpi.b		%d1,&QNAN		# weed out QNAN
	beq.l		res_qnan_1op

	fabs.x		SRC(%a0),%fp0		# force absolute value

	cmpi.b		%d1,&INF		# weed out INF
	beq.b		fabs_inf
fabs_zero:
	mov.b		&z_bmask,FPSR_CC(%a6)	# set 'Z' ccode bit
	rts
fabs_inf:
	mov.b		&inf_bmask,FPSR_CC(%a6)	# set 'I' ccode bit
	rts

#########################################################################
# XDEF ****************************************************************	#
#	fcmp(): fp compare op routine					#
#									#
# XREF ****************************************************************	#
#	res_qnan() - return QNAN result					#
#	res_snan() - return SNAN result					#
#									#
# INPUT ***************************************************************	#
#	a0 = pointer to extended precision source operand		#
#	a1 = pointer to extended precision destination operand		#
#	d0 = round prec/mode						#
#									#
# OUTPUT ************************************************************** #
#	None								#
#									#
# ALGORITHM ***********************************************************	#
#	Handle NANs and denorms as special cases. For everything else,	#
# just use the actual fcmp instruction to produce the correct condition	#
# codes.								#
#									#
#########################################################################

	global		fcmp
fcmp:
	clr.w		%d1
	mov.b		DTAG(%a6),%d1
	lsl.b		&0x3,%d1
	or.b		STAG(%a6),%d1
	bne.b		fcmp_not_norm		# optimize on non-norm input

#
# COMPARE FP OPs : NORMs, ZEROs, INFs, and "corrected" DENORMs
#
fcmp_norm:
	fmovm.x		DST(%a1),&0x80		# load dst op

	fcmp.x		%fp0,SRC(%a0)		# do compare

	fmov.l		%fpsr,%d0		# save FPSR
	rol.l		&0x8,%d0		# extract ccode bits
	mov.b		%d0,FPSR_CC(%a6)	# set ccode bits(no exc bits are set)

	rts

#
# fcmp: inputs are not both normalized; what are they?
#
fcmp_not_norm:
	mov.w		(tbl_fcmp_op.b,%pc,%d1.w*2),%d1
	jmp		(tbl_fcmp_op.b,%pc,%d1.w*1)

	swbeg		&48
tbl_fcmp_op:
	short		fcmp_norm	- tbl_fcmp_op # NORM - NORM
	short		fcmp_norm	- tbl_fcmp_op # NORM - ZERO
	short		fcmp_norm	- tbl_fcmp_op # NORM - INF
	short		fcmp_res_qnan	- tbl_fcmp_op # NORM - QNAN
	short		fcmp_nrm_dnrm	- tbl_fcmp_op # NORM - DENORM
	short		fcmp_res_snan	- tbl_fcmp_op # NORM - SNAN
	short		tbl_fcmp_op	- tbl_fcmp_op #
	short		tbl_fcmp_op	- tbl_fcmp_op #

	short		fcmp_norm	- tbl_fcmp_op # ZERO - NORM
	short		fcmp_norm	- tbl_fcmp_op # ZERO - ZERO
	short		fcmp_norm	- tbl_fcmp_op # ZERO - INF
	short		fcmp_res_qnan	- tbl_fcmp_op # ZERO - QNAN
	short		fcmp_dnrm_s	- tbl_fcmp_op # ZERO - DENORM
	short		fcmp_res_snan	- tbl_fcmp_op # ZERO - SNAN
	short		tbl_fcmp_op	- tbl_fcmp_op #
	short		tbl_fcmp_op	- tbl_fcmp_op #

	short		fcmp_norm	- tbl_fcmp_op # INF - NORM
	short		fcmp_norm	- tbl_fcmp_op # INF - ZERO
	short		fcmp_norm	- tbl_fcmp_op # INF - INF
	short		fcmp_res_qnan	- tbl_fcmp_op # INF - QNAN
	short		fcmp_dnrm_s	- tbl_fcmp_op # INF - DENORM
	short		fcmp_res_snan	- tbl_fcmp_op # INF - SNAN
	short		tbl_fcmp_op	- tbl_fcmp_op #
	short		tbl_fcmp_op	- tbl_fcmp_op #

	short		fcmp_res_qnan	- tbl_fcmp_op # QNAN - NORM
	short		fcmp_res_qnan	- tbl_fcmp_op # QNAN - ZERO
	short		fcmp_res_qnan	- tbl_fcmp_op # QNAN - INF
	short		fcmp_res_qnan	- tbl_fcmp_op # QNAN - QNAN
	short		fcmp_res_qnan	- tbl_fcmp_op # QNAN - DENORM
	short		fcmp_res_snan	- tbl_fcmp_op # QNAN - SNAN
	short		tbl_fcmp_op	- tbl_fcmp_op #
	short		tbl_fcmp_op	- tbl_fcmp_op #

	short		fcmp_dnrm_nrm	- tbl_fcmp_op # DENORM - NORM
	short		fcmp_dnrm_d	- tbl_fcmp_op # DENORM - ZERO
	short		fcmp_dnrm_d	- tbl_fcmp_op # DENORM - INF
	short		fcmp_res_qnan	- tbl_fcmp_op # DENORM - QNAN
	short		fcmp_dnrm_sd	- tbl_fcmp_op # DENORM - DENORM
	short		fcmp_res_snan	- tbl_fcmp_op # DENORM - SNAN
	short		tbl_fcmp_op	- tbl_fcmp_op #
	short		tbl_fcmp_op	- tbl_fcmp_op #

	short		fcmp_res_snan	- tbl_fcmp_op # SNAN - NORM
	short		fcmp_res_snan	- tbl_fcmp_op # SNAN - ZERO
	short		fcmp_res_snan	- tbl_fcmp_op # SNAN - INF
	short		fcmp_res_snan	- tbl_fcmp_op # SNAN - QNAN
	short		fcmp_res_snan	- tbl_fcmp_op # SNAN - DENORM
	short		fcmp_res_snan	- tbl_fcmp_op # SNAN - SNAN
	short		tbl_fcmp_op	- tbl_fcmp_op #
	short		tbl_fcmp_op	- tbl_fcmp_op #

# unlike all other functions for QNAN and SNAN, fcmp does NOT set the
# 'N' bit for a negative QNAN or SNAN input so we must squelch it here.
fcmp_res_qnan:
	bsr.l		res_qnan
	andi.b		&0xf7,FPSR_CC(%a6)
	rts
fcmp_res_snan:
	bsr.l		res_snan
	andi.b		&0xf7,FPSR_CC(%a6)
	rts

#
# DENORMs are a little more difficult.
# If you have a 2 DENORMs, then you can just force the j-bit to a one
# and use the fcmp_norm routine.
# If you have a DENORM and an INF or ZERO, just force the DENORM's j-bit to a one
# and use the fcmp_norm routine.
# If you have a DENORM and a NORM with opposite signs, then use fcmp_norm, also.
# But with a DENORM and a NORM of the same sign, the neg bit is set if the
# (1) signs are (+) and the DENORM is the dst or
# (2) signs are (-) and the DENORM is the src
#

fcmp_dnrm_s:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),%d0
	bset		&31,%d0			# DENORM src; make into small norm
	mov.l		%d0,FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	lea		FP_SCR0(%a6),%a0
	bra.w		fcmp_norm

fcmp_dnrm_d:
	mov.l		DST_EX(%a1),FP_SCR0_EX(%a6)
	mov.l		DST_HI(%a1),%d0
	bset		&31,%d0			# DENORM src; make into small norm
	mov.l		%d0,FP_SCR0_HI(%a6)
	mov.l		DST_LO(%a1),FP_SCR0_LO(%a6)
	lea		FP_SCR0(%a6),%a1
	bra.w		fcmp_norm

fcmp_dnrm_sd:
	mov.w		DST_EX(%a1),FP_SCR1_EX(%a6)
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		DST_HI(%a1),%d0
	bset		&31,%d0			# DENORM dst; make into small norm
	mov.l		%d0,FP_SCR1_HI(%a6)
	mov.l		SRC_HI(%a0),%d0
	bset		&31,%d0			# DENORM dst; make into small norm
	mov.l		%d0,FP_SCR0_HI(%a6)
	mov.l		DST_LO(%a1),FP_SCR1_LO(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	lea		FP_SCR1(%a6),%a1
	lea		FP_SCR0(%a6),%a0
	bra.w		fcmp_norm

fcmp_nrm_dnrm:
	mov.b		SRC_EX(%a0),%d0		# determine if like signs
	mov.b		DST_EX(%a1),%d1
	eor.b		%d0,%d1
	bmi.w		fcmp_dnrm_s

# signs are the same, so must determine the answer ourselves.
	tst.b		%d0			# is src op negative?
	bmi.b		fcmp_nrm_dnrm_m		# yes
	rts
fcmp_nrm_dnrm_m:
	mov.b		&neg_bmask,FPSR_CC(%a6)	# set 'Z' ccode bit
	rts

fcmp_dnrm_nrm:
	mov.b		SRC_EX(%a0),%d0		# determine if like signs
	mov.b		DST_EX(%a1),%d1
	eor.b		%d0,%d1
	bmi.w		fcmp_dnrm_d

# signs are the same, so must determine the answer ourselves.
	tst.b		%d0			# is src op negative?
	bpl.b		fcmp_dnrm_nrm_m		# no
	rts
fcmp_dnrm_nrm_m:
	mov.b		&neg_bmask,FPSR_CC(%a6)	# set 'Z' ccode bit
	rts

#########################################################################
# XDEF ****************************************************************	#
#	fsglmul(): emulates the fsglmul instruction			#
#									#
# XREF ****************************************************************	#
#	scale_to_zero_src() - scale src exponent to zero		#
#	scale_to_zero_dst() - scale dst exponent to zero		#
#	unf_res4() - return default underflow result for sglop		#
#	ovf_res() - return default overflow result			#
#	res_qnan() - return QNAN result					#
#	res_snan() - return SNAN result					#
#									#
# INPUT ***************************************************************	#
#	a0 = pointer to extended precision source operand		#
#	a1 = pointer to extended precision destination operand		#
#	d0  rnd prec,mode						#
#									#
# OUTPUT **************************************************************	#
#	fp0 = result							#
#	fp1 = EXOP (if exception occurred)				#
#									#
# ALGORITHM ***********************************************************	#
#	Handle NANs, infinities, and zeroes as special cases. Divide	#
# norms/denorms into ext/sgl/dbl precision.				#
#	For norms/denorms, scale the exponents such that a multiply	#
# instruction won't cause an exception. Use the regular fsglmul to	#
# compute a result. Check if the regular operands would have taken	#
# an exception. If so, return the default overflow/underflow result	#
# and return the EXOP if exceptions are enabled. Else, scale the	#
# result operand to the proper exponent.				#
#									#
#########################################################################

	global		fsglmul
fsglmul:
	mov.l		%d0,L_SCR3(%a6)		# store rnd info

	clr.w		%d1
	mov.b		DTAG(%a6),%d1
	lsl.b		&0x3,%d1
	or.b		STAG(%a6),%d1

	bne.w		fsglmul_not_norm	# optimize on non-norm input

fsglmul_norm:
	mov.w		DST_EX(%a1),FP_SCR1_EX(%a6)
	mov.l		DST_HI(%a1),FP_SCR1_HI(%a6)
	mov.l		DST_LO(%a1),FP_SCR1_LO(%a6)

	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)

	bsr.l		scale_to_zero_src	# scale exponent
	mov.l		%d0,-(%sp)		# save scale factor 1

	bsr.l		scale_to_zero_dst	# scale dst exponent

	add.l		(%sp)+,%d0		# SCALE_FACTOR = scale1 + scale2

	cmpi.l		%d0,&0x3fff-0x7ffe	# would result ovfl?
	beq.w		fsglmul_may_ovfl	# result may rnd to overflow
	blt.w		fsglmul_ovfl		# result will overflow

	cmpi.l		%d0,&0x3fff+0x0001	# would result unfl?
	beq.w		fsglmul_may_unfl	# result may rnd to no unfl
	bgt.w		fsglmul_unfl		# result will underflow

fsglmul_normal:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsglmul.x	FP_SCR0(%a6),%fp0	# execute sgl multiply

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

fsglmul_normal_exit:
	fmovm.x		&0x80,FP_SCR0(%a6)	# store out result
	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# load {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	or.w		%d2,%d1			# concat old sign,new exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x80	# return result in fp0
	rts

fsglmul_ovfl:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsglmul.x	FP_SCR0(%a6),%fp0	# execute sgl multiply

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

fsglmul_ovfl_tst:

# save setting this until now because this is where fsglmul_may_ovfl may jump in
	or.l		&ovfl_inx_mask, USER_FPSR(%a6) # set ovfl/aovfl/ainex

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x13,%d1		# is OVFL or INEX enabled?
	bne.b		fsglmul_ovfl_ena	# yes

fsglmul_ovfl_dis:
	btst		&neg_bit,FPSR_CC(%a6)	# is result negative?
	sne		%d1			# set sign param accordingly
	mov.l		L_SCR3(%a6),%d0		# pass prec:rnd
	andi.b		&0x30,%d0		# force prec = ext
	bsr.l		ovf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# set INF,N if applicable
	fmovm.x		(%a0),&0x80		# return default result in fp0
	rts

fsglmul_ovfl_ena:
	fmovm.x		&0x80,FP_SCR0(%a6)	# move result to stack

	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	sub.l		%d0,%d1			# add scale factor
	subi.l		&0x6000,%d1		# subtract bias
	andi.w		&0x7fff,%d1
	andi.w		&0x8000,%d2		# keep old sign
	or.w		%d2,%d1			# concat old sign,new exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	bra.b		fsglmul_ovfl_dis

fsglmul_may_ovfl:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsglmul.x	FP_SCR0(%a6),%fp0	# execute sgl multiply

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	fabs.x		%fp0,%fp1		# make a copy of result
	fcmp.b		%fp1,&0x2		# is |result| >= 2.b?
	fbge.w		fsglmul_ovfl_tst	# yes; overflow has occurred

# no, it didn't overflow; we have correct result
	bra.w		fsglmul_normal_exit

fsglmul_unfl:
	bset		&unfl_bit,FPSR_EXCEPT(%a6) # set unfl exc bit

	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		&rz_mode*0x10,%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsglmul.x	FP_SCR0(%a6),%fp0	# execute sgl multiply

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x0b,%d1		# is UNFL or INEX enabled?
	bne.b		fsglmul_unfl_ena	# yes

fsglmul_unfl_dis:
	fmovm.x		&0x80,FP_SCR0(%a6)	# store out result

	lea		FP_SCR0(%a6),%a0	# pass: result addr
	mov.l		L_SCR3(%a6),%d1		# pass: rnd prec,mode
	bsr.l		unf_res4		# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# 'Z' bit may have been set
	fmovm.x		FP_SCR0(%a6),&0x80	# return default result in fp0
	rts

#
# UNFL is enabled.
#
fsglmul_unfl_ena:
	fmovm.x		FP_SCR1(%a6),&0x40	# load dst op

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsglmul.x	FP_SCR0(%a6),%fp1	# execute sgl multiply

	fmov.l		&0x0,%fpcr		# clear FPCR

	fmovm.x		&0x40,FP_SCR0(%a6)	# save result to stack
	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	addi.l		&0x6000,%d1		# add bias
	andi.w		&0x7fff,%d1
	or.w		%d2,%d1			# concat old sign,new exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	bra.w		fsglmul_unfl_dis

fsglmul_may_unfl:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsglmul.x	FP_SCR0(%a6),%fp0	# execute sgl multiply

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	fabs.x		%fp0,%fp1		# make a copy of result
	fcmp.b		%fp1,&0x2		# is |result| > 2.b?
	fbgt.w		fsglmul_normal_exit	# no; no underflow occurred
	fblt.w		fsglmul_unfl		# yes; underflow occurred

#
# we still don't know if underflow occurred. result is ~ equal to 2. but,
# we don't know if the result was an underflow that rounded up to a 2 or
# a normalized number that rounded down to a 2. so, redo the entire operation
# using RZ as the rounding mode to see what the pre-rounded result is.
# this case should be relatively rare.
#
	fmovm.x		FP_SCR1(%a6),&0x40	# load dst op into fp1

	mov.l		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# keep rnd prec
	ori.b		&rz_mode*0x10,%d1	# insert RZ

	fmov.l		%d1,%fpcr		# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsglmul.x	FP_SCR0(%a6),%fp1	# execute sgl multiply

	fmov.l		&0x0,%fpcr		# clear FPCR
	fabs.x		%fp1			# make absolute value
	fcmp.b		%fp1,&0x2		# is |result| < 2.b?
	fbge.w		fsglmul_normal_exit	# no; no underflow occurred
	bra.w		fsglmul_unfl		# yes, underflow occurred

##############################################################################

#
# Single Precision Multiply: inputs are not both normalized; what are they?
#
fsglmul_not_norm:
	mov.w		(tbl_fsglmul_op.b,%pc,%d1.w*2),%d1
	jmp		(tbl_fsglmul_op.b,%pc,%d1.w*1)

	swbeg		&48
tbl_fsglmul_op:
	short		fsglmul_norm		- tbl_fsglmul_op # NORM x NORM
	short		fsglmul_zero		- tbl_fsglmul_op # NORM x ZERO
	short		fsglmul_inf_src		- tbl_fsglmul_op # NORM x INF
	short		fsglmul_res_qnan	- tbl_fsglmul_op # NORM x QNAN
	short		fsglmul_norm		- tbl_fsglmul_op # NORM x DENORM
	short		fsglmul_res_snan	- tbl_fsglmul_op # NORM x SNAN
	short		tbl_fsglmul_op		- tbl_fsglmul_op #
	short		tbl_fsglmul_op		- tbl_fsglmul_op #

	short		fsglmul_zero		- tbl_fsglmul_op # ZERO x NORM
	short		fsglmul_zero		- tbl_fsglmul_op # ZERO x ZERO
	short		fsglmul_res_operr	- tbl_fsglmul_op # ZERO x INF
	short		fsglmul_res_qnan	- tbl_fsglmul_op # ZERO x QNAN
	short		fsglmul_zero		- tbl_fsglmul_op # ZERO x DENORM
	short		fsglmul_res_snan	- tbl_fsglmul_op # ZERO x SNAN
	short		tbl_fsglmul_op		- tbl_fsglmul_op #
	short		tbl_fsglmul_op		- tbl_fsglmul_op #

	short		fsglmul_inf_dst		- tbl_fsglmul_op # INF x NORM
	short		fsglmul_res_operr	- tbl_fsglmul_op # INF x ZERO
	short		fsglmul_inf_dst		- tbl_fsglmul_op # INF x INF
	short		fsglmul_res_qnan	- tbl_fsglmul_op # INF x QNAN
	short		fsglmul_inf_dst		- tbl_fsglmul_op # INF x DENORM
	short		fsglmul_res_snan	- tbl_fsglmul_op # INF x SNAN
	short		tbl_fsglmul_op		- tbl_fsglmul_op #
	short		tbl_fsglmul_op		- tbl_fsglmul_op #

	short		fsglmul_res_qnan	- tbl_fsglmul_op # QNAN x NORM
	short		fsglmul_res_qnan	- tbl_fsglmul_op # QNAN x ZERO
	short		fsglmul_res_qnan	- tbl_fsglmul_op # QNAN x INF
	short		fsglmul_res_qnan	- tbl_fsglmul_op # QNAN x QNAN
	short		fsglmul_res_qnan	- tbl_fsglmul_op # QNAN x DENORM
	short		fsglmul_res_snan	- tbl_fsglmul_op # QNAN x SNAN
	short		tbl_fsglmul_op		- tbl_fsglmul_op #
	short		tbl_fsglmul_op		- tbl_fsglmul_op #

	short		fsglmul_norm		- tbl_fsglmul_op # NORM x NORM
	short		fsglmul_zero		- tbl_fsglmul_op # NORM x ZERO
	short		fsglmul_inf_src		- tbl_fsglmul_op # NORM x INF
	short		fsglmul_res_qnan	- tbl_fsglmul_op # NORM x QNAN
	short		fsglmul_norm		- tbl_fsglmul_op # NORM x DENORM
	short		fsglmul_res_snan	- tbl_fsglmul_op # NORM x SNAN
	short		tbl_fsglmul_op		- tbl_fsglmul_op #
	short		tbl_fsglmul_op		- tbl_fsglmul_op #

	short		fsglmul_res_snan	- tbl_fsglmul_op # SNAN x NORM
	short		fsglmul_res_snan	- tbl_fsglmul_op # SNAN x ZERO
	short		fsglmul_res_snan	- tbl_fsglmul_op # SNAN x INF
	short		fsglmul_res_snan	- tbl_fsglmul_op # SNAN x QNAN
	short		fsglmul_res_snan	- tbl_fsglmul_op # SNAN x DENORM
	short		fsglmul_res_snan	- tbl_fsglmul_op # SNAN x SNAN
	short		tbl_fsglmul_op		- tbl_fsglmul_op #
	short		tbl_fsglmul_op		- tbl_fsglmul_op #

fsglmul_res_operr:
	bra.l		res_operr
fsglmul_res_snan:
	bra.l		res_snan
fsglmul_res_qnan:
	bra.l		res_qnan
fsglmul_zero:
	bra.l		fmul_zero
fsglmul_inf_src:
	bra.l		fmul_inf_src
fsglmul_inf_dst:
	bra.l		fmul_inf_dst

#########################################################################
# XDEF ****************************************************************	#
#	fsgldiv(): emulates the fsgldiv instruction			#
#									#
# XREF ****************************************************************	#
#	scale_to_zero_src() - scale src exponent to zero		#
#	scale_to_zero_dst() - scale dst exponent to zero		#
#	unf_res4() - return default underflow result for sglop		#
#	ovf_res() - return default overflow result			#
#	res_qnan() - return QNAN result					#
#	res_snan() - return SNAN result					#
#									#
# INPUT ***************************************************************	#
#	a0 = pointer to extended precision source operand		#
#	a1 = pointer to extended precision destination operand		#
#	d0  rnd prec,mode						#
#									#
# OUTPUT **************************************************************	#
#	fp0 = result							#
#	fp1 = EXOP (if exception occurred)				#
#									#
# ALGORITHM ***********************************************************	#
#	Handle NANs, infinities, and zeroes as special cases. Divide	#
# norms/denorms into ext/sgl/dbl precision.				#
#	For norms/denorms, scale the exponents such that a divide	#
# instruction won't cause an exception. Use the regular fsgldiv to	#
# compute a result. Check if the regular operands would have taken	#
# an exception. If so, return the default overflow/underflow result	#
# and return the EXOP if exceptions are enabled. Else, scale the	#
# result operand to the proper exponent.				#
#									#
#########################################################################

	global		fsgldiv
fsgldiv:
	mov.l		%d0,L_SCR3(%a6)		# store rnd info

	clr.w		%d1
	mov.b		DTAG(%a6),%d1
	lsl.b		&0x3,%d1
	or.b		STAG(%a6),%d1		# combine src tags

	bne.w		fsgldiv_not_norm	# optimize on non-norm input

#
# DIVIDE: NORMs and DENORMs ONLY!
#
fsgldiv_norm:
	mov.w		DST_EX(%a1),FP_SCR1_EX(%a6)
	mov.l		DST_HI(%a1),FP_SCR1_HI(%a6)
	mov.l		DST_LO(%a1),FP_SCR1_LO(%a6)

	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)

	bsr.l		scale_to_zero_src	# calculate scale factor 1
	mov.l		%d0,-(%sp)		# save scale factor 1

	bsr.l		scale_to_zero_dst	# calculate scale factor 2

	neg.l		(%sp)			# S.F. = scale1 - scale2
	add.l		%d0,(%sp)

	mov.w		2+L_SCR3(%a6),%d1	# fetch precision,mode
	lsr.b		&0x6,%d1
	mov.l		(%sp)+,%d0
	cmpi.l		%d0,&0x3fff-0x7ffe
	ble.w		fsgldiv_may_ovfl

	cmpi.l		%d0,&0x3fff-0x0000	# will result underflow?
	beq.w		fsgldiv_may_unfl	# maybe
	bgt.w		fsgldiv_unfl		# yes; go handle underflow

fsgldiv_normal:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		L_SCR3(%a6),%fpcr	# save FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsgldiv.x	FP_SCR0(%a6),%fp0	# perform sgl divide

	fmov.l		%fpsr,%d1		# save FPSR
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

fsgldiv_normal_exit:
	fmovm.x		&0x80,FP_SCR0(%a6)	# store result on stack
	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# load {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	or.w		%d2,%d1			# concat old sign,new exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x80	# return result in fp0
	rts

fsgldiv_may_ovfl:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# set FPSR

	fsgldiv.x	FP_SCR0(%a6),%fp0	# execute divide

	fmov.l		%fpsr,%d1
	fmov.l		&0x0,%fpcr

	or.l		%d1,USER_FPSR(%a6)	# save INEX,N

	fmovm.x		&0x01,-(%sp)		# save result to stack
	mov.w		(%sp),%d1		# fetch new exponent
	add.l		&0xc,%sp		# clear result
	andi.l		&0x7fff,%d1		# strip sign
	sub.l		%d0,%d1			# add scale factor
	cmp.l		%d1,&0x7fff		# did divide overflow?
	blt.b		fsgldiv_normal_exit

fsgldiv_ovfl_tst:
	or.w		&ovfl_inx_mask,2+USER_FPSR(%a6) # set ovfl/aovfl/ainex

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x13,%d1		# is OVFL or INEX enabled?
	bne.b		fsgldiv_ovfl_ena	# yes

fsgldiv_ovfl_dis:
	btst		&neg_bit,FPSR_CC(%a6)	# is result negative
	sne		%d1			# set sign param accordingly
	mov.l		L_SCR3(%a6),%d0		# pass prec:rnd
	andi.b		&0x30,%d0		# kill precision
	bsr.l		ovf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# set INF if applicable
	fmovm.x		(%a0),&0x80		# return default result in fp0
	rts

fsgldiv_ovfl_ena:
	fmovm.x		&0x80,FP_SCR0(%a6)	# move result to stack

	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	subi.l		&0x6000,%d1		# subtract new bias
	andi.w		&0x7fff,%d1		# clear ms bit
	or.w		%d2,%d1			# concat old sign,new exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	bra.b		fsgldiv_ovfl_dis

fsgldiv_unfl:
	bset		&unfl_bit,FPSR_EXCEPT(%a6) # set unfl exc bit

	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		&rz_mode*0x10,%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsgldiv.x	FP_SCR0(%a6),%fp0	# execute sgl divide

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x0b,%d1		# is UNFL or INEX enabled?
	bne.b		fsgldiv_unfl_ena	# yes

fsgldiv_unfl_dis:
	fmovm.x		&0x80,FP_SCR0(%a6)	# store out result

	lea		FP_SCR0(%a6),%a0	# pass: result addr
	mov.l		L_SCR3(%a6),%d1		# pass: rnd prec,mode
	bsr.l		unf_res4		# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# 'Z' bit may have been set
	fmovm.x		FP_SCR0(%a6),&0x80	# return default result in fp0
	rts

#
# UNFL is enabled.
#
fsgldiv_unfl_ena:
	fmovm.x		FP_SCR1(%a6),&0x40	# load dst op

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsgldiv.x	FP_SCR0(%a6),%fp1	# execute sgl divide

	fmov.l		&0x0,%fpcr		# clear FPCR

	fmovm.x		&0x40,FP_SCR0(%a6)	# save result to stack
	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	addi.l		&0x6000,%d1		# add bias
	andi.w		&0x7fff,%d1		# clear top bit
	or.w		%d2,%d1			# concat old sign, new exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	bra.b		fsgldiv_unfl_dis

#
# the divide operation MAY underflow:
#
fsgldiv_may_unfl:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsgldiv.x	FP_SCR0(%a6),%fp0	# execute sgl divide

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	fabs.x		%fp0,%fp1		# make a copy of result
	fcmp.b		%fp1,&0x1		# is |result| > 1.b?
	fbgt.w		fsgldiv_normal_exit	# no; no underflow occurred
	fblt.w		fsgldiv_unfl		# yes; underflow occurred

#
# we still don't know if underflow occurred. result is ~ equal to 1. but,
# we don't know if the result was an underflow that rounded up to a 1
# or a normalized number that rounded down to a 1. so, redo the entire
# operation using RZ as the rounding mode to see what the pre-rounded
# result is. this case should be relatively rare.
#
	fmovm.x		FP_SCR1(%a6),&0x40	# load dst op into %fp1

	clr.l		%d1			# clear scratch register
	ori.b		&rz_mode*0x10,%d1	# force RZ rnd mode

	fmov.l		%d1,%fpcr		# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsgldiv.x	FP_SCR0(%a6),%fp1	# execute sgl divide

	fmov.l		&0x0,%fpcr		# clear FPCR
	fabs.x		%fp1			# make absolute value
	fcmp.b		%fp1,&0x1		# is |result| < 1.b?
	fbge.w		fsgldiv_normal_exit	# no; no underflow occurred
	bra.w		fsgldiv_unfl		# yes; underflow occurred

############################################################################

#
# Divide: inputs are not both normalized; what are they?
#
fsgldiv_not_norm:
	mov.w		(tbl_fsgldiv_op.b,%pc,%d1.w*2),%d1
	jmp		(tbl_fsgldiv_op.b,%pc,%d1.w*1)

	swbeg		&48
tbl_fsgldiv_op:
	short		fsgldiv_norm		- tbl_fsgldiv_op # NORM / NORM
	short		fsgldiv_inf_load	- tbl_fsgldiv_op # NORM / ZERO
	short		fsgldiv_zero_load	- tbl_fsgldiv_op # NORM / INF
	short		fsgldiv_res_qnan	- tbl_fsgldiv_op # NORM / QNAN
	short		fsgldiv_norm		- tbl_fsgldiv_op # NORM / DENORM
	short		fsgldiv_res_snan	- tbl_fsgldiv_op # NORM / SNAN
	short		tbl_fsgldiv_op		- tbl_fsgldiv_op #
	short		tbl_fsgldiv_op		- tbl_fsgldiv_op #

	short		fsgldiv_zero_load	- tbl_fsgldiv_op # ZERO / NORM
	short		fsgldiv_res_operr	- tbl_fsgldiv_op # ZERO / ZERO
	short		fsgldiv_zero_load	- tbl_fsgldiv_op # ZERO / INF
	short		fsgldiv_res_qnan	- tbl_fsgldiv_op # ZERO / QNAN
	short		fsgldiv_zero_load	- tbl_fsgldiv_op # ZERO / DENORM
	short		fsgldiv_res_snan	- tbl_fsgldiv_op # ZERO / SNAN
	short		tbl_fsgldiv_op		- tbl_fsgldiv_op #
	short		tbl_fsgldiv_op		- tbl_fsgldiv_op #

	short		fsgldiv_inf_dst		- tbl_fsgldiv_op # INF / NORM
	short		fsgldiv_inf_dst		- tbl_fsgldiv_op # INF / ZERO
	short		fsgldiv_res_operr	- tbl_fsgldiv_op # INF / INF
	short		fsgldiv_res_qnan	- tbl_fsgldiv_op # INF / QNAN
	short		fsgldiv_inf_dst		- tbl_fsgldiv_op # INF / DENORM
	short		fsgldiv_res_snan	- tbl_fsgldiv_op # INF / SNAN
	short		tbl_fsgldiv_op		- tbl_fsgldiv_op #
	short		tbl_fsgldiv_op		- tbl_fsgldiv_op #

	short		fsgldiv_res_qnan	- tbl_fsgldiv_op # QNAN / NORM
	short		fsgldiv_res_qnan	- tbl_fsgldiv_op # QNAN / ZERO
	short		fsgldiv_res_qnan	- tbl_fsgldiv_op # QNAN / INF
	short		fsgldiv_res_qnan	- tbl_fsgldiv_op # QNAN / QNAN
	short		fsgldiv_res_qnan	- tbl_fsgldiv_op # QNAN / DENORM
	short		fsgldiv_res_snan	- tbl_fsgldiv_op # QNAN / SNAN
	short		tbl_fsgldiv_op		- tbl_fsgldiv_op #
	short		tbl_fsgldiv_op		- tbl_fsgldiv_op #

	short		fsgldiv_norm		- tbl_fsgldiv_op # DENORM / NORM
	short		fsgldiv_inf_load	- tbl_fsgldiv_op # DENORM / ZERO
	short		fsgldiv_zero_load	- tbl_fsgldiv_op # DENORM / INF
	short		fsgldiv_res_qnan	- tbl_fsgldiv_op # DENORM / QNAN
	short		fsgldiv_norm		- tbl_fsgldiv_op # DENORM / DENORM
	short		fsgldiv_res_snan	- tbl_fsgldiv_op # DENORM / SNAN
	short		tbl_fsgldiv_op		- tbl_fsgldiv_op #
	short		tbl_fsgldiv_op		- tbl_fsgldiv_op #

	short		fsgldiv_res_snan	- tbl_fsgldiv_op # SNAN / NORM
	short		fsgldiv_res_snan	- tbl_fsgldiv_op # SNAN / ZERO
	short		fsgldiv_res_snan	- tbl_fsgldiv_op # SNAN / INF
	short		fsgldiv_res_snan	- tbl_fsgldiv_op # SNAN / QNAN
	short		fsgldiv_res_snan	- tbl_fsgldiv_op # SNAN / DENORM
	short		fsgldiv_res_snan	- tbl_fsgldiv_op # SNAN / SNAN
	short		tbl_fsgldiv_op		- tbl_fsgldiv_op #
	short		tbl_fsgldiv_op		- tbl_fsgldiv_op #

fsgldiv_res_qnan:
	bra.l		res_qnan
fsgldiv_res_snan:
	bra.l		res_snan
fsgldiv_res_operr:
	bra.l		res_operr
fsgldiv_inf_load:
	bra.l		fdiv_inf_load
fsgldiv_zero_load:
	bra.l		fdiv_zero_load
fsgldiv_inf_dst:
	bra.l		fdiv_inf_dst

#########################################################################
# XDEF ****************************************************************	#
#	fadd(): emulates the fadd instruction				#
#	fsadd(): emulates the fadd instruction				#
#	fdadd(): emulates the fdadd instruction				#
#									#
# XREF ****************************************************************	#
#	addsub_scaler2() - scale the operands so they won't take exc	#
#	ovf_res() - return default overflow result			#
#	unf_res() - return default underflow result			#
#	res_qnan() - set QNAN result					#
#	res_snan() - set SNAN result					#
#	res_operr() - set OPERR result					#
#	scale_to_zero_src() - set src operand exponent equal to zero	#
#	scale_to_zero_dst() - set dst operand exponent equal to zero	#
#									#
# INPUT ***************************************************************	#
#	a0 = pointer to extended precision source operand		#
#	a1 = pointer to extended precision destination operand		#
#									#
# OUTPUT **************************************************************	#
#	fp0 = result							#
#	fp1 = EXOP (if exception occurred)				#
#									#
# ALGORITHM ***********************************************************	#
#	Handle NANs, infinities, and zeroes as special cases. Divide	#
# norms into extended, single, and double precision.			#
#	Do addition after scaling exponents such that exception won't	#
# occur. Then, check result exponent to see if exception would have	#
# occurred. If so, return default result and maybe EXOP. Else, insert	#
# the correct result exponent and return. Set FPSR bits as appropriate.	#
#									#
#########################################################################

	global		fsadd
fsadd:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&s_mode*0x10,%d0	# insert sgl prec
	bra.b		fadd

	global		fdadd
fdadd:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&d_mode*0x10,%d0	# insert dbl prec

	global		fadd
fadd:
	mov.l		%d0,L_SCR3(%a6)		# store rnd info

	clr.w		%d1
	mov.b		DTAG(%a6),%d1
	lsl.b		&0x3,%d1
	or.b		STAG(%a6),%d1		# combine src tags

	bne.w		fadd_not_norm		# optimize on non-norm input

#
# ADD: norms and denorms
#
fadd_norm:
	bsr.l		addsub_scaler2		# scale exponents

fadd_zero_entry:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fadd.x		FP_SCR0(%a6),%fp0	# execute add

	fmov.l		&0x0,%fpcr		# clear FPCR
	fmov.l		%fpsr,%d1		# fetch INEX2,N,Z

	or.l		%d1,USER_FPSR(%a6)	# save exc and ccode bits

	fbeq.w		fadd_zero_exit		# if result is zero, end now

	mov.l		%d2,-(%sp)		# save d2

	fmovm.x		&0x01,-(%sp)		# save result to stack

	mov.w		2+L_SCR3(%a6),%d1
	lsr.b		&0x6,%d1

	mov.w		(%sp),%d2		# fetch new sign, exp
	andi.l		&0x7fff,%d2		# strip sign
	sub.l		%d0,%d2			# add scale factor

	cmp.l		%d2,(tbl_fadd_ovfl.b,%pc,%d1.w*4) # is it an overflow?
	bge.b		fadd_ovfl		# yes

	cmp.l		%d2,(tbl_fadd_unfl.b,%pc,%d1.w*4) # is it an underflow?
	blt.w		fadd_unfl		# yes
	beq.w		fadd_may_unfl		# maybe; go find out

fadd_normal:
	mov.w		(%sp),%d1
	andi.w		&0x8000,%d1		# keep sign
	or.w		%d2,%d1			# concat sign,new exp
	mov.w		%d1,(%sp)		# insert new exponent

	fmovm.x		(%sp)+,&0x80		# return result in fp0

	mov.l		(%sp)+,%d2		# restore d2
	rts

fadd_zero_exit:
#	fmov.s		&0x00000000,%fp0	# return zero in fp0
	rts

tbl_fadd_ovfl:
	long		0x7fff			# ext ovfl
	long		0x407f			# sgl ovfl
	long		0x43ff			# dbl ovfl

tbl_fadd_unfl:
	long	        0x0000			# ext unfl
	long		0x3f81			# sgl unfl
	long		0x3c01			# dbl unfl

fadd_ovfl:
	or.l		&ovfl_inx_mask,USER_FPSR(%a6) # set ovfl/aovfl/ainex

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x13,%d1		# is OVFL or INEX enabled?
	bne.b		fadd_ovfl_ena		# yes

	add.l		&0xc,%sp
fadd_ovfl_dis:
	btst		&neg_bit,FPSR_CC(%a6)	# is result negative?
	sne		%d1			# set sign param accordingly
	mov.l		L_SCR3(%a6),%d0		# pass prec:rnd
	bsr.l		ovf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# set INF,N if applicable
	fmovm.x		(%a0),&0x80		# return default result in fp0
	mov.l		(%sp)+,%d2		# restore d2
	rts

fadd_ovfl_ena:
	mov.b		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# is precision extended?
	bne.b		fadd_ovfl_ena_sd	# no; prec = sgl or dbl

fadd_ovfl_ena_cont:
	mov.w		(%sp),%d1
	andi.w		&0x8000,%d1		# keep sign
	subi.l		&0x6000,%d2		# add extra bias
	andi.w		&0x7fff,%d2
	or.w		%d2,%d1			# concat sign,new exp
	mov.w		%d1,(%sp)		# insert new exponent

	fmovm.x		(%sp)+,&0x40		# return EXOP in fp1
	bra.b		fadd_ovfl_dis

fadd_ovfl_ena_sd:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	mov.l		L_SCR3(%a6),%d1
	andi.b		&0x30,%d1		# keep rnd mode
	fmov.l		%d1,%fpcr		# set FPCR

	fadd.x		FP_SCR0(%a6),%fp0	# execute add

	fmov.l		&0x0,%fpcr		# clear FPCR

	add.l		&0xc,%sp
	fmovm.x		&0x01,-(%sp)
	bra.b		fadd_ovfl_ena_cont

fadd_unfl:
	bset		&unfl_bit,FPSR_EXCEPT(%a6) # set unfl exc bit

	add.l		&0xc,%sp

	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		&rz_mode*0x10,%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fadd.x		FP_SCR0(%a6),%fp0	# execute add

	fmov.l		&0x0,%fpcr		# clear FPCR
	fmov.l		%fpsr,%d1		# save status

	or.l		%d1,USER_FPSR(%a6)	# save INEX,N

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x0b,%d1		# is UNFL or INEX enabled?
	bne.b		fadd_unfl_ena		# yes

fadd_unfl_dis:
	fmovm.x		&0x80,FP_SCR0(%a6)	# store out result

	lea		FP_SCR0(%a6),%a0	# pass: result addr
	mov.l		L_SCR3(%a6),%d1		# pass: rnd prec,mode
	bsr.l		unf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# 'Z' bit may have been set
	fmovm.x		FP_SCR0(%a6),&0x80	# return default result in fp0
	mov.l		(%sp)+,%d2		# restore d2
	rts

fadd_unfl_ena:
	fmovm.x		FP_SCR1(%a6),&0x40	# load dst op

	mov.l		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# is precision extended?
	bne.b		fadd_unfl_ena_sd	# no; sgl or dbl

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

fadd_unfl_ena_cont:
	fmov.l		&0x0,%fpsr		# clear FPSR

	fadd.x		FP_SCR0(%a6),%fp1	# execute multiply

	fmov.l		&0x0,%fpcr		# clear FPCR

	fmovm.x		&0x40,FP_SCR0(%a6)	# save result to stack
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	addi.l		&0x6000,%d1		# add new bias
	andi.w		&0x7fff,%d1		# clear top bit
	or.w		%d2,%d1			# concat sign,new exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	bra.w		fadd_unfl_dis

fadd_unfl_ena_sd:
	mov.l		L_SCR3(%a6),%d1
	andi.b		&0x30,%d1		# use only rnd mode
	fmov.l		%d1,%fpcr		# set FPCR

	bra.b		fadd_unfl_ena_cont

#
# result is equal to the smallest normalized number in the selected precision
# if the precision is extended, this result could not have come from an
# underflow that rounded up.
#
fadd_may_unfl:
	mov.l		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1
	beq.w		fadd_normal		# yes; no underflow occurred

	mov.l		0x4(%sp),%d1		# extract hi(man)
	cmpi.l		%d1,&0x80000000		# is hi(man) = 0x80000000?
	bne.w		fadd_normal		# no; no underflow occurred

	tst.l		0x8(%sp)		# is lo(man) = 0x0?
	bne.w		fadd_normal		# no; no underflow occurred

	btst		&inex2_bit,FPSR_EXCEPT(%a6) # is INEX2 set?
	beq.w		fadd_normal		# no; no underflow occurred

#
# ok, so now the result has a exponent equal to the smallest normalized
# exponent for the selected precision. also, the mantissa is equal to
# 0x8000000000000000 and this mantissa is the result of rounding non-zero
# g,r,s.
# now, we must determine whether the pre-rounded result was an underflow
# rounded "up" or a normalized number rounded "down".
# so, we do this be re-executing the add using RZ as the rounding mode and
# seeing if the new result is smaller or equal to the current result.
#
	fmovm.x		FP_SCR1(%a6),&0x40	# load dst op into fp1

	mov.l		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# keep rnd prec
	ori.b		&rz_mode*0x10,%d1	# insert rnd mode
	fmov.l		%d1,%fpcr		# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fadd.x		FP_SCR0(%a6),%fp1	# execute add

	fmov.l		&0x0,%fpcr		# clear FPCR

	fabs.x		%fp0			# compare absolute values
	fabs.x		%fp1
	fcmp.x		%fp0,%fp1		# is first result > second?

	fbgt.w		fadd_unfl		# yes; it's an underflow
	bra.w		fadd_normal		# no; it's not an underflow

##########################################################################

#
# Add: inputs are not both normalized; what are they?
#
fadd_not_norm:
	mov.w		(tbl_fadd_op.b,%pc,%d1.w*2),%d1
	jmp		(tbl_fadd_op.b,%pc,%d1.w*1)

	swbeg		&48
tbl_fadd_op:
	short		fadd_norm	- tbl_fadd_op # NORM + NORM
	short		fadd_zero_src	- tbl_fadd_op # NORM + ZERO
	short		fadd_inf_src	- tbl_fadd_op # NORM + INF
	short		fadd_res_qnan	- tbl_fadd_op # NORM + QNAN
	short		fadd_norm	- tbl_fadd_op # NORM + DENORM
	short		fadd_res_snan	- tbl_fadd_op # NORM + SNAN
	short		tbl_fadd_op	- tbl_fadd_op #
	short		tbl_fadd_op	- tbl_fadd_op #

	short		fadd_zero_dst	- tbl_fadd_op # ZERO + NORM
	short		fadd_zero_2	- tbl_fadd_op # ZERO + ZERO
	short		fadd_inf_src	- tbl_fadd_op # ZERO + INF
	short		fadd_res_qnan	- tbl_fadd_op # NORM + QNAN
	short		fadd_zero_dst	- tbl_fadd_op # ZERO + DENORM
	short		fadd_res_snan	- tbl_fadd_op # NORM + SNAN
	short		tbl_fadd_op	- tbl_fadd_op #
	short		tbl_fadd_op	- tbl_fadd_op #

	short		fadd_inf_dst	- tbl_fadd_op # INF + NORM
	short		fadd_inf_dst	- tbl_fadd_op # INF + ZERO
	short		fadd_inf_2	- tbl_fadd_op # INF + INF
	short		fadd_res_qnan	- tbl_fadd_op # NORM + QNAN
	short		fadd_inf_dst	- tbl_fadd_op # INF + DENORM
	short		fadd_res_snan	- tbl_fadd_op # NORM + SNAN
	short		tbl_fadd_op	- tbl_fadd_op #
	short		tbl_fadd_op	- tbl_fadd_op #

	short		fadd_res_qnan	- tbl_fadd_op # QNAN + NORM
	short		fadd_res_qnan	- tbl_fadd_op # QNAN + ZERO
	short		fadd_res_qnan	- tbl_fadd_op # QNAN + INF
	short		fadd_res_qnan	- tbl_fadd_op # QNAN + QNAN
	short		fadd_res_qnan	- tbl_fadd_op # QNAN + DENORM
	short		fadd_res_snan	- tbl_fadd_op # QNAN + SNAN
	short		tbl_fadd_op	- tbl_fadd_op #
	short		tbl_fadd_op	- tbl_fadd_op #

	short		fadd_norm	- tbl_fadd_op # DENORM + NORM
	short		fadd_zero_src	- tbl_fadd_op # DENORM + ZERO
	short		fadd_inf_src	- tbl_fadd_op # DENORM + INF
	short		fadd_res_qnan	- tbl_fadd_op # NORM + QNAN
	short		fadd_norm	- tbl_fadd_op # DENORM + DENORM
	short		fadd_res_snan	- tbl_fadd_op # NORM + SNAN
	short		tbl_fadd_op	- tbl_fadd_op #
	short		tbl_fadd_op	- tbl_fadd_op #

	short		fadd_res_snan	- tbl_fadd_op # SNAN + NORM
	short		fadd_res_snan	- tbl_fadd_op # SNAN + ZERO
	short		fadd_res_snan	- tbl_fadd_op # SNAN + INF
	short		fadd_res_snan	- tbl_fadd_op # SNAN + QNAN
	short		fadd_res_snan	- tbl_fadd_op # SNAN + DENORM
	short		fadd_res_snan	- tbl_fadd_op # SNAN + SNAN
	short		tbl_fadd_op	- tbl_fadd_op #
	short		tbl_fadd_op	- tbl_fadd_op #

fadd_res_qnan:
	bra.l		res_qnan
fadd_res_snan:
	bra.l		res_snan

#
# both operands are ZEROes
#
fadd_zero_2:
	mov.b		SRC_EX(%a0),%d0		# are the signs opposite
	mov.b		DST_EX(%a1),%d1
	eor.b		%d0,%d1
	bmi.w		fadd_zero_2_chk_rm	# weed out (-ZERO)+(+ZERO)

# the signs are the same. so determine whether they are positive or negative
# and return the appropriately signed zero.
	tst.b		%d0			# are ZEROes positive or negative?
	bmi.b		fadd_zero_rm		# negative
	fmov.s		&0x00000000,%fp0	# return +ZERO
	mov.b		&z_bmask,FPSR_CC(%a6)	# set Z
	rts

#
# the ZEROes have opposite signs:
# - Therefore, we return +ZERO if the rounding modes are RN,RZ, or RP.
# - -ZERO is returned in the case of RM.
#
fadd_zero_2_chk_rm:
	mov.b		3+L_SCR3(%a6),%d1
	andi.b		&0x30,%d1		# extract rnd mode
	cmpi.b		%d1,&rm_mode*0x10	# is rnd mode == RM?
	beq.b		fadd_zero_rm		# yes
	fmov.s		&0x00000000,%fp0	# return +ZERO
	mov.b		&z_bmask,FPSR_CC(%a6)	# set Z
	rts

fadd_zero_rm:
	fmov.s		&0x80000000,%fp0	# return -ZERO
	mov.b		&neg_bmask+z_bmask,FPSR_CC(%a6) # set NEG/Z
	rts

#
# one operand is a ZERO and the other is a DENORM or NORM. scale
# the DENORM or NORM and jump to the regular fadd routine.
#
fadd_zero_dst:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	bsr.l		scale_to_zero_src	# scale the operand
	clr.w		FP_SCR1_EX(%a6)
	clr.l		FP_SCR1_HI(%a6)
	clr.l		FP_SCR1_LO(%a6)
	bra.w		fadd_zero_entry		# go execute fadd

fadd_zero_src:
	mov.w		DST_EX(%a1),FP_SCR1_EX(%a6)
	mov.l		DST_HI(%a1),FP_SCR1_HI(%a6)
	mov.l		DST_LO(%a1),FP_SCR1_LO(%a6)
	bsr.l		scale_to_zero_dst	# scale the operand
	clr.w		FP_SCR0_EX(%a6)
	clr.l		FP_SCR0_HI(%a6)
	clr.l		FP_SCR0_LO(%a6)
	bra.w		fadd_zero_entry		# go execute fadd

#
# both operands are INFs. an OPERR will result if the INFs have
# different signs. else, an INF of the same sign is returned
#
fadd_inf_2:
	mov.b		SRC_EX(%a0),%d0		# exclusive or the signs
	mov.b		DST_EX(%a1),%d1
	eor.b		%d1,%d0
	bmi.l		res_operr		# weed out (-INF)+(+INF)

# ok, so it's not an OPERR. but, we do have to remember to return the
# src INF since that's where the 881/882 gets the j-bit from...

#
# operands are INF and one of {ZERO, INF, DENORM, NORM}
#
fadd_inf_src:
	fmovm.x		SRC(%a0),&0x80		# return src INF
	tst.b		SRC_EX(%a0)		# is INF positive?
	bpl.b		fadd_inf_done		# yes; we're done
	mov.b		&neg_bmask+inf_bmask,FPSR_CC(%a6) # set INF/NEG
	rts

#
# operands are INF and one of {ZERO, INF, DENORM, NORM}
#
fadd_inf_dst:
	fmovm.x		DST(%a1),&0x80		# return dst INF
	tst.b		DST_EX(%a1)		# is INF positive?
	bpl.b		fadd_inf_done		# yes; we're done
	mov.b		&neg_bmask+inf_bmask,FPSR_CC(%a6) # set INF/NEG
	rts

fadd_inf_done:
	mov.b		&inf_bmask,FPSR_CC(%a6) # set INF
	rts

#########################################################################
# XDEF ****************************************************************	#
#	fsub(): emulates the fsub instruction				#
#	fssub(): emulates the fssub instruction				#
#	fdsub(): emulates the fdsub instruction				#
#									#
# XREF ****************************************************************	#
#	addsub_scaler2() - scale the operands so they won't take exc	#
#	ovf_res() - return default overflow result			#
#	unf_res() - return default underflow result			#
#	res_qnan() - set QNAN result					#
#	res_snan() - set SNAN result					#
#	res_operr() - set OPERR result					#
#	scale_to_zero_src() - set src operand exponent equal to zero	#
#	scale_to_zero_dst() - set dst operand exponent equal to zero	#
#									#
# INPUT ***************************************************************	#
#	a0 = pointer to extended precision source operand		#
#	a1 = pointer to extended precision destination operand		#
#									#
# OUTPUT **************************************************************	#
#	fp0 = result							#
#	fp1 = EXOP (if exception occurred)				#
#									#
# ALGORITHM ***********************************************************	#
#	Handle NANs, infinities, and zeroes as special cases. Divide	#
# norms into extended, single, and double precision.			#
#	Do subtraction after scaling exponents such that exception won't#
# occur. Then, check result exponent to see if exception would have	#
# occurred. If so, return default result and maybe EXOP. Else, insert	#
# the correct result exponent and return. Set FPSR bits as appropriate.	#
#									#
#########################################################################

	global		fssub
fssub:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&s_mode*0x10,%d0	# insert sgl prec
	bra.b		fsub

	global		fdsub
fdsub:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&d_mode*0x10,%d0	# insert dbl prec

	global		fsub
fsub:
	mov.l		%d0,L_SCR3(%a6)		# store rnd info

	clr.w		%d1
	mov.b		DTAG(%a6),%d1
	lsl.b		&0x3,%d1
	or.b		STAG(%a6),%d1		# combine src tags

	bne.w		fsub_not_norm		# optimize on non-norm input

#
# SUB: norms and denorms
#
fsub_norm:
	bsr.l		addsub_scaler2		# scale exponents

fsub_zero_entry:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fsub.x		FP_SCR0(%a6),%fp0	# execute subtract

	fmov.l		&0x0,%fpcr		# clear FPCR
	fmov.l		%fpsr,%d1		# fetch INEX2, N, Z

	or.l		%d1,USER_FPSR(%a6)	# save exc and ccode bits

	fbeq.w		fsub_zero_exit		# if result zero, end now

	mov.l		%d2,-(%sp)		# save d2

	fmovm.x		&0x01,-(%sp)		# save result to stack

	mov.w		2+L_SCR3(%a6),%d1
	lsr.b		&0x6,%d1

	mov.w		(%sp),%d2		# fetch new exponent
	andi.l		&0x7fff,%d2		# strip sign
	sub.l		%d0,%d2			# add scale factor

	cmp.l		%d2,(tbl_fsub_ovfl.b,%pc,%d1.w*4) # is it an overflow?
	bge.b		fsub_ovfl		# yes

	cmp.l		%d2,(tbl_fsub_unfl.b,%pc,%d1.w*4) # is it an underflow?
	blt.w		fsub_unfl		# yes
	beq.w		fsub_may_unfl		# maybe; go find out

fsub_normal:
	mov.w		(%sp),%d1
	andi.w		&0x8000,%d1		# keep sign
	or.w		%d2,%d1			# insert new exponent
	mov.w		%d1,(%sp)		# insert new exponent

	fmovm.x		(%sp)+,&0x80		# return result in fp0

	mov.l		(%sp)+,%d2		# restore d2
	rts

fsub_zero_exit:
#	fmov.s		&0x00000000,%fp0	# return zero in fp0
	rts

tbl_fsub_ovfl:
	long		0x7fff			# ext ovfl
	long		0x407f			# sgl ovfl
	long		0x43ff			# dbl ovfl

tbl_fsub_unfl:
	long	        0x0000			# ext unfl
	long		0x3f81			# sgl unfl
	long		0x3c01			# dbl unfl

fsub_ovfl:
	or.l		&ovfl_inx_mask,USER_FPSR(%a6) # set ovfl/aovfl/ainex

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x13,%d1		# is OVFL or INEX enabled?
	bne.b		fsub_ovfl_ena		# yes

	add.l		&0xc,%sp
fsub_ovfl_dis:
	btst		&neg_bit,FPSR_CC(%a6)	# is result negative?
	sne		%d1			# set sign param accordingly
	mov.l		L_SCR3(%a6),%d0		# pass prec:rnd
	bsr.l		ovf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# set INF,N if applicable
	fmovm.x		(%a0),&0x80		# return default result in fp0
	mov.l		(%sp)+,%d2		# restore d2
	rts

fsub_ovfl_ena:
	mov.b		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# is precision extended?
	bne.b		fsub_ovfl_ena_sd	# no

fsub_ovfl_ena_cont:
	mov.w		(%sp),%d1		# fetch {sgn,exp}
	andi.w		&0x8000,%d1		# keep sign
	subi.l		&0x6000,%d2		# subtract new bias
	andi.w		&0x7fff,%d2		# clear top bit
	or.w		%d2,%d1			# concat sign,exp
	mov.w		%d1,(%sp)		# insert new exponent

	fmovm.x		(%sp)+,&0x40		# return EXOP in fp1
	bra.b		fsub_ovfl_dis

fsub_ovfl_ena_sd:
	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	mov.l		L_SCR3(%a6),%d1
	andi.b		&0x30,%d1		# clear rnd prec
	fmov.l		%d1,%fpcr		# set FPCR

	fsub.x		FP_SCR0(%a6),%fp0	# execute subtract

	fmov.l		&0x0,%fpcr		# clear FPCR

	add.l		&0xc,%sp
	fmovm.x		&0x01,-(%sp)
	bra.b		fsub_ovfl_ena_cont

fsub_unfl:
	bset		&unfl_bit,FPSR_EXCEPT(%a6) # set unfl exc bit

	add.l		&0xc,%sp

	fmovm.x		FP_SCR1(%a6),&0x80	# load dst op

	fmov.l		&rz_mode*0x10,%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsub.x		FP_SCR0(%a6),%fp0	# execute subtract

	fmov.l		&0x0,%fpcr		# clear FPCR
	fmov.l		%fpsr,%d1		# save status

	or.l		%d1,USER_FPSR(%a6)

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x0b,%d1		# is UNFL or INEX enabled?
	bne.b		fsub_unfl_ena		# yes

fsub_unfl_dis:
	fmovm.x		&0x80,FP_SCR0(%a6)	# store out result

	lea		FP_SCR0(%a6),%a0	# pass: result addr
	mov.l		L_SCR3(%a6),%d1		# pass: rnd prec,mode
	bsr.l		unf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# 'Z' may have been set
	fmovm.x		FP_SCR0(%a6),&0x80	# return default result in fp0
	mov.l		(%sp)+,%d2		# restore d2
	rts

fsub_unfl_ena:
	fmovm.x		FP_SCR1(%a6),&0x40

	mov.l		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# is precision extended?
	bne.b		fsub_unfl_ena_sd	# no

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

fsub_unfl_ena_cont:
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsub.x		FP_SCR0(%a6),%fp1	# execute subtract

	fmov.l		&0x0,%fpcr		# clear FPCR

	fmovm.x		&0x40,FP_SCR0(%a6)	# store result to stack
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	addi.l		&0x6000,%d1		# subtract new bias
	andi.w		&0x7fff,%d1		# clear top bit
	or.w		%d2,%d1			# concat sgn,exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	bra.w		fsub_unfl_dis

fsub_unfl_ena_sd:
	mov.l		L_SCR3(%a6),%d1
	andi.b		&0x30,%d1		# clear rnd prec
	fmov.l		%d1,%fpcr		# set FPCR

	bra.b		fsub_unfl_ena_cont

#
# result is equal to the smallest normalized number in the selected precision
# if the precision is extended, this result could not have come from an
# underflow that rounded up.
#
fsub_may_unfl:
	mov.l		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# fetch rnd prec
	beq.w		fsub_normal		# yes; no underflow occurred

	mov.l		0x4(%sp),%d1
	cmpi.l		%d1,&0x80000000		# is hi(man) = 0x80000000?
	bne.w		fsub_normal		# no; no underflow occurred

	tst.l		0x8(%sp)		# is lo(man) = 0x0?
	bne.w		fsub_normal		# no; no underflow occurred

	btst		&inex2_bit,FPSR_EXCEPT(%a6) # is INEX2 set?
	beq.w		fsub_normal		# no; no underflow occurred

#
# ok, so now the result has a exponent equal to the smallest normalized
# exponent for the selected precision. also, the mantissa is equal to
# 0x8000000000000000 and this mantissa is the result of rounding non-zero
# g,r,s.
# now, we must determine whether the pre-rounded result was an underflow
# rounded "up" or a normalized number rounded "down".
# so, we do this be re-executing the add using RZ as the rounding mode and
# seeing if the new result is smaller or equal to the current result.
#
	fmovm.x		FP_SCR1(%a6),&0x40	# load dst op into fp1

	mov.l		L_SCR3(%a6),%d1
	andi.b		&0xc0,%d1		# keep rnd prec
	ori.b		&rz_mode*0x10,%d1	# insert rnd mode
	fmov.l		%d1,%fpcr		# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsub.x		FP_SCR0(%a6),%fp1	# execute subtract

	fmov.l		&0x0,%fpcr		# clear FPCR

	fabs.x		%fp0			# compare absolute values
	fabs.x		%fp1
	fcmp.x		%fp0,%fp1		# is first result > second?

	fbgt.w		fsub_unfl		# yes; it's an underflow
	bra.w		fsub_normal		# no; it's not an underflow

##########################################################################

#
# Sub: inputs are not both normalized; what are they?
#
fsub_not_norm:
	mov.w		(tbl_fsub_op.b,%pc,%d1.w*2),%d1
	jmp		(tbl_fsub_op.b,%pc,%d1.w*1)

	swbeg		&48
tbl_fsub_op:
	short		fsub_norm	- tbl_fsub_op # NORM - NORM
	short		fsub_zero_src	- tbl_fsub_op # NORM - ZERO
	short		fsub_inf_src	- tbl_fsub_op # NORM - INF
	short		fsub_res_qnan	- tbl_fsub_op # NORM - QNAN
	short		fsub_norm	- tbl_fsub_op # NORM - DENORM
	short		fsub_res_snan	- tbl_fsub_op # NORM - SNAN
	short		tbl_fsub_op	- tbl_fsub_op #
	short		tbl_fsub_op	- tbl_fsub_op #

	short		fsub_zero_dst	- tbl_fsub_op # ZERO - NORM
	short		fsub_zero_2	- tbl_fsub_op # ZERO - ZERO
	short		fsub_inf_src	- tbl_fsub_op # ZERO - INF
	short		fsub_res_qnan	- tbl_fsub_op # NORM - QNAN
	short		fsub_zero_dst	- tbl_fsub_op # ZERO - DENORM
	short		fsub_res_snan	- tbl_fsub_op # NORM - SNAN
	short		tbl_fsub_op	- tbl_fsub_op #
	short		tbl_fsub_op	- tbl_fsub_op #

	short		fsub_inf_dst	- tbl_fsub_op # INF - NORM
	short		fsub_inf_dst	- tbl_fsub_op # INF - ZERO
	short		fsub_inf_2	- tbl_fsub_op # INF - INF
	short		fsub_res_qnan	- tbl_fsub_op # NORM - QNAN
	short		fsub_inf_dst	- tbl_fsub_op # INF - DENORM
	short		fsub_res_snan	- tbl_fsub_op # NORM - SNAN
	short		tbl_fsub_op	- tbl_fsub_op #
	short		tbl_fsub_op	- tbl_fsub_op #

	short		fsub_res_qnan	- tbl_fsub_op # QNAN - NORM
	short		fsub_res_qnan	- tbl_fsub_op # QNAN - ZERO
	short		fsub_res_qnan	- tbl_fsub_op # QNAN - INF
	short		fsub_res_qnan	- tbl_fsub_op # QNAN - QNAN
	short		fsub_res_qnan	- tbl_fsub_op # QNAN - DENORM
	short		fsub_res_snan	- tbl_fsub_op # QNAN - SNAN
	short		tbl_fsub_op	- tbl_fsub_op #
	short		tbl_fsub_op	- tbl_fsub_op #

	short		fsub_norm	- tbl_fsub_op # DENORM - NORM
	short		fsub_zero_src	- tbl_fsub_op # DENORM - ZERO
	short		fsub_inf_src	- tbl_fsub_op # DENORM - INF
	short		fsub_res_qnan	- tbl_fsub_op # NORM - QNAN
	short		fsub_norm	- tbl_fsub_op # DENORM - DENORM
	short		fsub_res_snan	- tbl_fsub_op # NORM - SNAN
	short		tbl_fsub_op	- tbl_fsub_op #
	short		tbl_fsub_op	- tbl_fsub_op #

	short		fsub_res_snan	- tbl_fsub_op # SNAN - NORM
	short		fsub_res_snan	- tbl_fsub_op # SNAN - ZERO
	short		fsub_res_snan	- tbl_fsub_op # SNAN - INF
	short		fsub_res_snan	- tbl_fsub_op # SNAN - QNAN
	short		fsub_res_snan	- tbl_fsub_op # SNAN - DENORM
	short		fsub_res_snan	- tbl_fsub_op # SNAN - SNAN
	short		tbl_fsub_op	- tbl_fsub_op #
	short		tbl_fsub_op	- tbl_fsub_op #

fsub_res_qnan:
	bra.l		res_qnan
fsub_res_snan:
	bra.l		res_snan

#
# both operands are ZEROes
#
fsub_zero_2:
	mov.b		SRC_EX(%a0),%d0
	mov.b		DST_EX(%a1),%d1
	eor.b		%d1,%d0
	bpl.b		fsub_zero_2_chk_rm

# the signs are opposite, so, return a ZERO w/ the sign of the dst ZERO
	tst.b		%d0			# is dst negative?
	bmi.b		fsub_zero_2_rm		# yes
	fmov.s		&0x00000000,%fp0	# no; return +ZERO
	mov.b		&z_bmask,FPSR_CC(%a6)	# set Z
	rts

#
# the ZEROes have the same signs:
# - Therefore, we return +ZERO if the rounding mode is RN,RZ, or RP
# - -ZERO is returned in the case of RM.
#
fsub_zero_2_chk_rm:
	mov.b		3+L_SCR3(%a6),%d1
	andi.b		&0x30,%d1		# extract rnd mode
	cmpi.b		%d1,&rm_mode*0x10	# is rnd mode = RM?
	beq.b		fsub_zero_2_rm		# yes
	fmov.s		&0x00000000,%fp0	# no; return +ZERO
	mov.b		&z_bmask,FPSR_CC(%a6)	# set Z
	rts

fsub_zero_2_rm:
	fmov.s		&0x80000000,%fp0	# return -ZERO
	mov.b		&z_bmask+neg_bmask,FPSR_CC(%a6)	# set Z/NEG
	rts

#
# one operand is a ZERO and the other is a DENORM or a NORM.
# scale the DENORM or NORM and jump to the regular fsub routine.
#
fsub_zero_dst:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)
	bsr.l		scale_to_zero_src	# scale the operand
	clr.w		FP_SCR1_EX(%a6)
	clr.l		FP_SCR1_HI(%a6)
	clr.l		FP_SCR1_LO(%a6)
	bra.w		fsub_zero_entry		# go execute fsub

fsub_zero_src:
	mov.w		DST_EX(%a1),FP_SCR1_EX(%a6)
	mov.l		DST_HI(%a1),FP_SCR1_HI(%a6)
	mov.l		DST_LO(%a1),FP_SCR1_LO(%a6)
	bsr.l		scale_to_zero_dst	# scale the operand
	clr.w		FP_SCR0_EX(%a6)
	clr.l		FP_SCR0_HI(%a6)
	clr.l		FP_SCR0_LO(%a6)
	bra.w		fsub_zero_entry		# go execute fsub

#
# both operands are INFs. an OPERR will result if the INFs have the
# same signs. else,
#
fsub_inf_2:
	mov.b		SRC_EX(%a0),%d0		# exclusive or the signs
	mov.b		DST_EX(%a1),%d1
	eor.b		%d1,%d0
	bpl.l		res_operr		# weed out (-INF)+(+INF)

# ok, so it's not an OPERR. but we do have to remember to return
# the src INF since that's where the 881/882 gets the j-bit.

fsub_inf_src:
	fmovm.x		SRC(%a0),&0x80		# return src INF
	fneg.x		%fp0			# invert sign
	fbge.w		fsub_inf_done		# sign is now positive
	mov.b		&neg_bmask+inf_bmask,FPSR_CC(%a6) # set INF/NEG
	rts

fsub_inf_dst:
	fmovm.x		DST(%a1),&0x80		# return dst INF
	tst.b		DST_EX(%a1)		# is INF negative?
	bpl.b		fsub_inf_done		# no
	mov.b		&neg_bmask+inf_bmask,FPSR_CC(%a6) # set INF/NEG
	rts

fsub_inf_done:
	mov.b		&inf_bmask,FPSR_CC(%a6)	# set INF
	rts

#########################################################################
# XDEF ****************************************************************	#
#	fsqrt(): emulates the fsqrt instruction				#
#	fssqrt(): emulates the fssqrt instruction			#
#	fdsqrt(): emulates the fdsqrt instruction			#
#									#
# XREF ****************************************************************	#
#	scale_sqrt() - scale the source operand				#
#	unf_res() - return default underflow result			#
#	ovf_res() - return default overflow result			#
#	res_qnan_1op() - return QNAN result				#
#	res_snan_1op() - return SNAN result				#
#									#
# INPUT ***************************************************************	#
#	a0 = pointer to extended precision source operand		#
#	d0  rnd prec,mode						#
#									#
# OUTPUT **************************************************************	#
#	fp0 = result							#
#	fp1 = EXOP (if exception occurred)				#
#									#
# ALGORITHM ***********************************************************	#
#	Handle NANs, infinities, and zeroes as special cases. Divide	#
# norms/denorms into ext/sgl/dbl precision.				#
#	For norms/denorms, scale the exponents such that a sqrt		#
# instruction won't cause an exception. Use the regular fsqrt to	#
# compute a result. Check if the regular operands would have taken	#
# an exception. If so, return the default overflow/underflow result	#
# and return the EXOP if exceptions are enabled. Else, scale the	#
# result operand to the proper exponent.				#
#									#
#########################################################################

	global		fssqrt
fssqrt:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&s_mode*0x10,%d0	# insert sgl precision
	bra.b		fsqrt

	global		fdsqrt
fdsqrt:
	andi.b		&0x30,%d0		# clear rnd prec
	ori.b		&d_mode*0x10,%d0	# insert dbl precision

	global		fsqrt
fsqrt:
	mov.l		%d0,L_SCR3(%a6)		# store rnd info
	clr.w		%d1
	mov.b		STAG(%a6),%d1
	bne.w		fsqrt_not_norm		# optimize on non-norm input

#
# SQUARE ROOT: norms and denorms ONLY!
#
fsqrt_norm:
	tst.b		SRC_EX(%a0)		# is operand negative?
	bmi.l		res_operr		# yes

	andi.b		&0xc0,%d0		# is precision extended?
	bne.b		fsqrt_not_ext		# no; go handle sgl or dbl

	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsqrt.x		(%a0),%fp0		# execute square root

	fmov.l		%fpsr,%d1
	or.l		%d1,USER_FPSR(%a6)	# set N,INEX

	rts

fsqrt_denorm:
	tst.b		SRC_EX(%a0)		# is operand negative?
	bmi.l		res_operr		# yes

	andi.b		&0xc0,%d0		# is precision extended?
	bne.b		fsqrt_not_ext		# no; go handle sgl or dbl

	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)

	bsr.l		scale_sqrt		# calculate scale factor

	bra.w		fsqrt_sd_normal

#
# operand is either single or double
#
fsqrt_not_ext:
	cmpi.b		%d0,&s_mode*0x10	# separate sgl/dbl prec
	bne.w		fsqrt_dbl

#
# operand is to be rounded to single precision
#
fsqrt_sgl:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)

	bsr.l		scale_sqrt		# calculate scale factor

	cmpi.l		%d0,&0x3fff-0x3f81	# will move in underflow?
	beq.w		fsqrt_sd_may_unfl
	bgt.w		fsqrt_sd_unfl		# yes; go handle underflow
	cmpi.l		%d0,&0x3fff-0x407f	# will move in overflow?
	beq.w		fsqrt_sd_may_ovfl	# maybe; go check
	blt.w		fsqrt_sd_ovfl		# yes; go handle overflow

#
# operand will NOT overflow or underflow when moved in to the fp reg file
#
fsqrt_sd_normal:
	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fsqrt.x		FP_SCR0(%a6),%fp0	# perform absolute

	fmov.l		%fpsr,%d1		# save FPSR
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

fsqrt_sd_normal_exit:
	mov.l		%d2,-(%sp)		# save d2
	fmovm.x		&0x80,FP_SCR0(%a6)	# store out result
	mov.w		FP_SCR0_EX(%a6),%d1	# load sgn,exp
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	sub.l		%d0,%d1			# add scale factor
	andi.w		&0x8000,%d2		# keep old sign
	or.w		%d1,%d2			# concat old sign,new exp
	mov.w		%d2,FP_SCR0_EX(%a6)	# insert new exponent
	mov.l		(%sp)+,%d2		# restore d2
	fmovm.x		FP_SCR0(%a6),&0x80	# return result in fp0
	rts

#
# operand is to be rounded to double precision
#
fsqrt_dbl:
	mov.w		SRC_EX(%a0),FP_SCR0_EX(%a6)
	mov.l		SRC_HI(%a0),FP_SCR0_HI(%a6)
	mov.l		SRC_LO(%a0),FP_SCR0_LO(%a6)

	bsr.l		scale_sqrt		# calculate scale factor

	cmpi.l		%d0,&0x3fff-0x3c01	# will move in underflow?
	beq.w		fsqrt_sd_may_unfl
	bgt.b		fsqrt_sd_unfl		# yes; go handle underflow
	cmpi.l		%d0,&0x3fff-0x43ff	# will move in overflow?
	beq.w		fsqrt_sd_may_ovfl	# maybe; go check
	blt.w		fsqrt_sd_ovfl		# yes; go handle overflow
	bra.w		fsqrt_sd_normal		# no; ho handle normalized op

# we're on the line here and the distinguising characteristic is whether
# the exponent is 3fff or 3ffe. if it's 3ffe, then it's a safe number
# elsewise fall through to underflow.
fsqrt_sd_may_unfl:
	btst		&0x0,1+FP_SCR0_EX(%a6)	# is exponent 0x3fff?
	bne.w		fsqrt_sd_normal		# yes, so no underflow

#
# operand WILL underflow when moved in to the fp register file
#
fsqrt_sd_unfl:
	bset		&unfl_bit,FPSR_EXCEPT(%a6) # set unfl exc bit

	fmov.l		&rz_mode*0x10,%fpcr	# set FPCR
	fmov.l		&0x0,%fpsr		# clear FPSR

	fsqrt.x		FP_SCR0(%a6),%fp0	# execute square root

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

# if underflow or inexact is enabled, go calculate EXOP first.
	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x0b,%d1		# is UNFL or INEX enabled?
	bne.b		fsqrt_sd_unfl_ena	# yes

fsqrt_sd_unfl_dis:
	fmovm.x		&0x80,FP_SCR0(%a6)	# store out result

	lea		FP_SCR0(%a6),%a0	# pass: result addr
	mov.l		L_SCR3(%a6),%d1		# pass: rnd prec,mode
	bsr.l		unf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# set possible 'Z' ccode
	fmovm.x		FP_SCR0(%a6),&0x80	# return default result in fp0
	rts

#
# operand will underflow AND underflow is enabled.
# Therefore, we must return the result rounded to extended precision.
#
fsqrt_sd_unfl_ena:
	mov.l		FP_SCR0_HI(%a6),FP_SCR1_HI(%a6)
	mov.l		FP_SCR0_LO(%a6),FP_SCR1_LO(%a6)
	mov.w		FP_SCR0_EX(%a6),%d1	# load current exponent

	mov.l		%d2,-(%sp)		# save d2
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# subtract scale factor
	addi.l		&0x6000,%d1		# add new bias
	andi.w		&0x7fff,%d1
	or.w		%d2,%d1			# concat new sign,new exp
	mov.w		%d1,FP_SCR1_EX(%a6)	# insert new exp
	fmovm.x		FP_SCR1(%a6),&0x40	# return EXOP in fp1
	mov.l		(%sp)+,%d2		# restore d2
	bra.b		fsqrt_sd_unfl_dis

#
# operand WILL overflow.
#
fsqrt_sd_ovfl:
	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fsqrt.x		FP_SCR0(%a6),%fp0	# perform square root

	fmov.l		&0x0,%fpcr		# clear FPCR
	fmov.l		%fpsr,%d1		# save FPSR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

fsqrt_sd_ovfl_tst:
	or.l		&ovfl_inx_mask,USER_FPSR(%a6) # set ovfl/aovfl/ainex

	mov.b		FPCR_ENABLE(%a6),%d1
	andi.b		&0x13,%d1		# is OVFL or INEX enabled?
	bne.b		fsqrt_sd_ovfl_ena	# yes

#
# OVFL is not enabled; therefore, we must create the default result by
# calling ovf_res().
#
fsqrt_sd_ovfl_dis:
	btst		&neg_bit,FPSR_CC(%a6)	# is result negative?
	sne		%d1			# set sign param accordingly
	mov.l		L_SCR3(%a6),%d0		# pass: prec,mode
	bsr.l		ovf_res			# calculate default result
	or.b		%d0,FPSR_CC(%a6)	# set INF,N if applicable
	fmovm.x		(%a0),&0x80		# return default result in fp0
	rts

#
# OVFL is enabled.
# the INEX2 bit has already been updated by the round to the correct precision.
# now, round to extended(and don't alter the FPSR).
#
fsqrt_sd_ovfl_ena:
	mov.l		%d2,-(%sp)		# save d2
	mov.w		FP_SCR0_EX(%a6),%d1	# fetch {sgn,exp}
	mov.l		%d1,%d2			# make a copy
	andi.l		&0x7fff,%d1		# strip sign
	andi.w		&0x8000,%d2		# keep old sign
	sub.l		%d0,%d1			# add scale factor
	subi.l		&0x6000,%d1		# subtract bias
	andi.w		&0x7fff,%d1
	or.w		%d2,%d1			# concat sign,exp
	mov.w		%d1,FP_SCR0_EX(%a6)	# insert new exponent
	fmovm.x		FP_SCR0(%a6),&0x40	# return EXOP in fp1
	mov.l		(%sp)+,%d2		# restore d2
	bra.b		fsqrt_sd_ovfl_dis

#
# the move in MAY underflow. so...
#
fsqrt_sd_may_ovfl:
	btst		&0x0,1+FP_SCR0_EX(%a6)	# is exponent 0x3fff?
	bne.w		fsqrt_sd_ovfl		# yes, so overflow

	fmov.l		&0x0,%fpsr		# clear FPSR
	fmov.l		L_SCR3(%a6),%fpcr	# set FPCR

	fsqrt.x		FP_SCR0(%a6),%fp0	# perform absolute

	fmov.l		%fpsr,%d1		# save status
	fmov.l		&0x0,%fpcr		# clear FPCR

	or.l		%d1,USER_FPSR(%a6)	# save INEX2,N

	fmov.x		%fp0,%fp1		# make a copy of result
	fcmp.b		%fp1,&0x1		# is |result| >= 1.b?
	fbge.w		fsqrt_sd_ovfl_tst	# yes; overflow has occurred

# no, it didn't overflow; we have correct result
	bra.w		fsqrt_sd_normal_exit

##########################################################################

#
# input is not normalized; what is it?
#
fsqrt_not_norm:
	cmpi.b		%d1,&DENORM		# weed out DENORM
	beq.w		fsqrt_denorm
	cmpi.b		%d1,&ZERO		# weed out ZERO
	beq.b		fsqrt_zero
	cmpi.b		%d1,&INF		# weed out INF
	beq.b		fsqrt_inf
	cmpi.b		%d1,&SNAN		# weed out SNAN
	beq.l		res_snan_1op
	bra.l		res_qnan_1op

#
#	fsqrt(+0) = +0
#	fsqrt(-0) = -0
#	fsqrt(+INF) = +INF
#	fsqrt(-INF) = OPERR
#
fsqrt_zero:
	tst.b		SRC_EX(%a0)		# is ZERO positive or negative?
	bmi.b		fsqrt_zero_m		# negative
fsqrt_zero_p:
	fmov.s		&0x00000000,%fp0	# return +ZERO
	mov.b		&z_bmask,FPSR_CC(%a6)	# set 'Z' ccode bit
	rts
fsqrt_zero_m:
	fmov.s		&0x80000000,%fp0	# return -ZERO
	mov.b		&z_bmask+neg_bmask,FPSR_CC(%a6)	# set 'Z','N' ccode bits
	rts

fsqrt_inf:
	tst.b		SRC_EX(%a0)		# is INF positive or negative?
	bmi.l		res_operr		# negative
fsqrt_inf_p:
	fmovm.x		SRC(%a0),&0x80		# return +INF in fp0
	mov.b		&inf_bmask,FPSR_CC(%a6)	# set 'I' ccode bit
	rts
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -



