/* --- Copyright University of Sussex 1995. All rights reserved. ----------
 * File:            C.mips/src/afloat.s
 * Purpose:         Floating point arithmetic for MIPS R2010
 * Author:          Rob Duncan, Feb 13 1990 (see revisions)
 */


#_<

#_INCLUDE 'declare.ph'

constant
	procedure Sys$-Float_qrem
	;

lconstant macro	(

	;;; Pop ddecimal structure fields:
	;;; _DD_1 = MS word, _DD_2 = LS word

	_DD_1		= @@DD_1,
	_DD_2		= @@DD_2,

	;;; offset of true from false on the assumption that poplink
	;;; generates booleans in the order: false, true

	_TRUE_OFFS	= @@(struct BOOLEAN)++,

);

	;;; Layout of double floats in memory (depends on byte ordering)
#_IF DEF BIG_ENDIAN
lconstant macro (
	_LS_WORD	= _int(4),
	_MS_WORD	= _int(0),
	_MS_HALF	= _int(0),
	_MS_BYTE	= _int(0),
);
#_ELSE
lconstant macro (
	_LS_WORD	= _int(0),
	_MS_WORD	= _int(4),
	_MS_HALF	= _int(6),
	_MS_BYTE	= _int(7),
);
#_ENDIF


>_#

#_INCLUDE 'pop_regdef.h'


/************************* wrapping structures ************************/

	.data
	.word	Ldata_size
	.word	C_LAB(Sys$-objmod_pad_key)
Ldata_start:

/**********************************************************************/


	.text


;;; -- MOVEMENT AND TYPE CONVERSION ---------------------------------------

;;; _PFCOPY:
;;;	Copies a double float.

;;; Call:
;;;	_pfcopy(_DST_ADDR, _SRC_ADDR)

;;; Registers:
;;;	a0	dst address
;;;	a1	src address
;;;	t0,t1	double float value

DEF_C_LAB(_pfcopy)

	.ent	$pfcopy
$pfcopy:

	lw	a0, 4(usp)
	lw	a1, (usp)
	addu	usp, 8
	ld	t0, (a1)
	sd	t0, (a0)
	j	ra

	.end	$pfcopy


;;; _PF_SFLOAT_DEC:
;;;	Converts pop decimal to single float.

;;; Call:
;;;	_pf_sfloat_dec(DECIMAL) -> _SFLOAT

;;; Registers:
;;;	a0	decimal argument
;;;	v0	sfloat result

DEF_C_LAB(_pf_sfloat_dec)

	.ent	$pf_sfloat_dec
$pf_sfloat_dec:

	lw	a0, (usp)
	subu	v0, a0, 1		;;; clear tag bit from decimal
	sw	v0, (usp)
	j	ra

	.end	$pf_sfloat_dec


;;; _PF_DFLOAT_INT:
;;;	Converts system integer to double float.

;;; Call:
;;;	_pf_dfloat_int(_INT, _DFLOAT_ADDR)

;;; Registers:
;;;	a0	double float address
;;;	f0	conversion

DEF_C_LAB(_pf_dfloat_int)

	.ent	$pf_dfloat_int
$pf_dfloat_int:

	lw	a0, (usp)	;;; _dfloat_addr
	lwc1	$f0, 4(usp)	;;; _int
	addu	usp, 8
	cvt.d.w	$f0, $f0	;;; cvt int to double
	s.d	$f0, (a0)	;;; store result
	j	ra

	.end	$pf_dfloat_int


;;; _PF_DFLOAT_DEC:
;;;	Converts pop decimal to double float.

;;; Call:
;;;	_pf_dfloat_dec(DEC, _DFLOAT_ADDR)

;;; Registers:
;;;	a0	pop decimal
;;;	a1	double float address
;;;	f0	conversion

DEF_C_LAB(_pf_dfloat_dec)

	.ent	$pf_dfloat_dec
$pf_dfloat_dec:

	lw	a0, 4(usp)
	lw	a1, (usp)
	addu	usp, 8

	subu	a0, 1		;;; clear tag bit from decimal
	mtc1	a0, $f0		;;; move to FPU
	cvt.d.s	$f0, $f0	;;; cvt single to double
	s.d	$f0, (a1)	;;; store result

	j	ra

	.end	$pf_dfloat_dec


;;; _PF_DFLOAT_DDEC:
;;;	Converts pop ddecimal to double float.

;;; Call:
;;;	_pf_dfloat_ddec(DDEC, _DFLOAT_ADDR)

;;; Registers:
;;;	a0	ddecimal
;;;	a1	double float address
;;;	t0,t1	double float

DEF_C_LAB(_pf_dfloat_ddec)

	.ent	$pf_dfloat_ddec
$pf_dfloat_ddec:

	lw	a0, 4(usp)
	lw	a1, (usp)
	addu	usp, 8

	lw	t0, _DD_2(a0)	;;; low word
	lw	t1, _DD_1(a0)	;;; high word
	sw	t0, _LS_WORD(a1)
	sw	t1, _MS_WORD(a1)

	j	ra

	.end	$pf_dfloat_ddec


;;; _PF_CVT_TO_DEC:
;;;	Converts double float to pop decimal. Returns <false> on overflow.
;;;	Mantissa has to be rounded to 21 bits to make room for tag bits.

;;; Call:
;;;	_pf_cvt_to_dec(_DFLOAT_ADDR) -> DEC
;;;	_pf_cvt_to_dec(_DFLOAT_ADDR) -> <false>

;;; Registers:
;;;	a0	double float address
;;;	v0	single float/decimal result
;;;	t0	low word of double float
;;;	t2	exponent
;;;	f0	conversion

DEF_C_LAB(_pf_cvt_to_dec)

	.ent	$pf_cvt_to_dec
$pf_cvt_to_dec:

	lw	a0, (usp)
	lw	t0, _LS_WORD(a0)

	;;; Set bits in low word of dfloat to force rounding of 22nd bit
	;;; of mantissa in all cases except where tied and 21st bit is
	;;; even (0)

	beq	t0, 0x40000000, 1f	;;; tied and even: won't round
	or	t0, 0x38000000		;;; set bits 23-25

1:	;;; Load to FPU

	mtc1	t0, $f0
	lwc1	$f1, _MS_WORD(a0)

	;;; Convert and read back to v0

	cvt.s.d	$f0, $f0
	mfc1	v0, $f0

	;;; We can only spot overflow by examining the result:
	;;; isolate exponent bits of result in t2

	srl	t2, v0, 23
	and	t2, 0xff

	;;; Exponent all zero means zero or denormal;
	;;; exponent all ones means overflow

	beqz	t2, 1f
	beq	t2, 0xff, 2f

	;;; Result OK: set bottom bit and return

	or	v0, 3
	xor	v0, 2
	sw	v0, (usp)
	j	ra

1:	;;; Zero or denormal: return pop 0.0

	li	v0, 1
	sw	v0, (usp)
	j	ra

2:	;;; Overflow: return false

	sw	false, (usp)
	j	ra

	.end	$pf_cvt_to_dec


;;; _PF_CVT_TO_DDEC:
;;;	Converts double float to pop ddecimal.

;;; Call:
;;;	_pf_cvt_to_ddec(_DFLOAT_ADDR, DDEC)

;;; Registers:
;;;	a0	double float address
;;;	a1	ddecimal
;;;	t0,t1	double float value
;;;	t2	exponent bits of double float

DEF_C_LAB(_pf_cvt_to_ddec)

	.ent	$pf_cvt_to_ddec
$pf_cvt_to_ddec:

	lw	a0, 4(usp)
	lw	a1, (usp)
	lw	t0, _LS_WORD(a0)
	lw	t1, _MS_WORD(a0)
	addu	usp, 8

	;;; Isolate exponent bits of result in t2:
	;;; if zero, value is zero or denormal

	and	t2, t1, 0x7ff00000
	beqz	t2, 1f

	;;; Store low and high words to ddecimal

	sw	t0, _DD_2(a1)
	sw	t1, _DD_1(a1)
	j	ra

1:	;;; Zero or denormal: make result properly zero

	sw	zero, _DD_2(a1)
	sw	zero, _DD_1(a1)
	j	ra

	.end	$pf_cvt_to_ddec


;;; _PF_ROUND_D_TO_S:
;;;	Round double float to single. Result is written back to the
;;;	address argument. Returns non-false if rounding was OK, <false>
;;;	for overflow.

;;; Call:
;;;	_pf_round_d_to_s(_DFLOAT_ADDR) -> BOOL

;;; Registers:
;;;	a0	double float address
;;;	v0	single float result
;;;	t0	exponent bits
;;;	f0	conversion

DEF_C_LAB(_pf_round_d_to_s)

	.ent	$pf_round_d_to_s
$pf_round_d_to_s:

	lw	a0, (usp)
	l.d	$f0, (a0)
	cvt.s.d	$f0, $f0
	mfc1	v0, $f0
	sw	v0, (a0)

	;;; Overflow if exponent is all ones

	and	t0, v0, 0x7f800000
	beq	t0, 0x7f800000, 1f
	j	ra
1:	sw	false, (usp)
	j	ra

	.end	$pf_round_d_to_s


;;; _PF_EXTEND_S_TO_D:
;;;	Converts single float to double; returns <false> for Inf or NaN.

;;; Call:
;;;	_pf_extend_s_to_d(_DFLOAT_ADDR) -> _DFLOAT_ADDR
;;;	_pf_extend_s_to_d(_DFLOAT_ADDR) -> <false>

;;; Registers:
;;;	a0	double float address
;;;	v0	single float
;;;	t0	exponent
;;;	f0	conversion

DEF_C_LAB(_pf_extend_s_to_d)

	.ent	$pf_extend_s_to_d
$pf_extend_s_to_d:

	;;; Load single from _dfaddr

	lw	a0, (usp)
	lw	v0, (a0)

	;;; Check exponent for Inf or NaN

	srl	t0, v0, 23
	and	t0, 0xff
	beq	t0, 0xff, 1f

	;;; OK -- do conversion

	mtc1	v0, $f0
	cvt.d.s	$f0, $f0
	s.d	$f0, (a0)
	j	ra

1:	;;; Inf or NaN -- return <false>

	sw	false, (usp)
	j	ra

	.end	$pf_extend_s_to_d


;;; _PF_CHECK_D:
;;;	Check double float for Inf or NaN.

;;; Call:
;;;	_pf_check_d(_DFLOAT_ADDR) -> _DFLOAT_ADDR
;;;	_pf_check_d(_DFLOAT_ADDR) -> <false>

;;; Registers:
;;;	a0	address argument
;;; 	t0	exponent half word

DEF_C_LAB(_pf_check_d)

	.ent	$pf_check_d
$pf_check_d:

	;;; Load exponent half word

	lw	a0, (usp)
	lhu	t0, _MS_HALF(a0)

	;;; Check exponent for Inf or NaN

	and	t0, 0x7ff0
	beq	t0, 0x7ff0, 1f
	j	ra

1:	sw	false, (usp)
	j	ra

	.end	$pf_check_d


;;; _PF_INTOF:
;;;	Truncates double float to integer. Returns either the integer and
;;;	non-<false> or <false> only if overflow.

;;; Call:
;;;	_pf_intof(_DFLOAT_ADDR) -> _DFLOAT_ADDR -> _INT
;;;	_pf_intof(_DFLOAT_ADDR) -> <false>

;;; Notes:
;;;	The MIPS cvt.w.d instruction is no use because it doesn't indicate
;;;	overflow in any way. Instead, we have to truncate the number in
;;;	double format using the C function -trunc- and compare against the
;;;	largest integer.

;;; Registers:
;;;	a0	double float address
;;;	f0	double float result
;;;	f2	most positive integer
;;;	f4	most negative integer
;;;	f12	double float value (argument to trunc)

DEF_C_LAB(_pf_intof)

	.ent	$pf_intof
$pf_intof:
	.set	noreorder
	CPLOAD	t9
	.set	reorder

	;;; Stack frame for function call

	.frame	sp, 32, ra
	.mask	0x91000000, -4
	subu	sp, 32
	sw	ra, 28(sp)
	sw	gp, 24(sp)
	sw	svb, 20(sp)

	;;; Make the call

	lw	a0, (usp)
	l.d	$f12, (a0)
	jal	trunc
	lw	gp, 24(sp)
	lw	svb, 20(sp)

	;;; Check: -(2**31) <= f0 <= 2**31 - 1

	li.d	$f2, 2147483647.0
	c.ole.d	$f0, $f2
	bc1f	1f
	li.d	$f4, -2147483648.0
	c.olt.d	$f0, $f4
	bc1t	1f

	;;; In range: convert to integer

	cvt.w.d	$f0, $f0

	;;; Return result and <true>

	swc1	$f0, (usp)
	la	t0, _TRUE_OFFS(false)
	sw	t0, -4(usp)
	subu	usp, 4

	lw	ra, 28(sp)
	addu	sp, 32
	j	ra

1:	;;; Overflow: return <false>

	sw	false, (usp)

	lw	ra, 28(sp)
	addu	sp, 32
	j	ra

	.end	$pf_intof


;;; _PFMODF:
;;;	Separates a double float into integer and fractional parts.

;;; Call:
;;;	_pfmodf(_FRAC_ADDR, _DFLOAT_ADDR)

;;; Notes:
;;;	uses the C function
;;;		double modf(double value, *iptr)
;;;	which returns the *positive* fractional part and writes the integer
;;;	part back to iptr.
;;;	The MIPS calling convention requires:
;;;		value -> $f12, iptr -> a2

;;; Registers:
;;;	a2	double float address (iptr argument to modf)
;;;	t0	fractional result address
;;;	t3	high word of double float
;;;	f0	fractional result
;;;	f12	double float (value argument to modf)

DEF_C_LAB(_pfmodf)

	.ent	$pfmodf
$pfmodf:
	.set	noreorder
	CPLOAD	t9
	.set	reorder

	;;; Stack frame

	.frame	sp, 32, ra
	.mask	0x91000000, -4
	subu	sp, 32
	sw	ra, 28(sp)
	sw	gp, 24(sp)
	sw	svb, 20(sp)

	;;; Load arguments for function call

	lw	a2, (usp)
	l.d	$f12, (a2)

	;;; Check for a negative value:

	lw	t3, _MS_WORD(a2)
	bltz	t3, 1f

	;;; Positive

	jal	modf
	lw	gp, 24(sp)
	lw	svb, 20(sp)
	b	2f

1:	;;; Negative:
	;;; negate the argument to the call ...

	neg.d	$f12, $f12
	jal	modf
	lw	gp, 24(sp)
	lw	svb, 20(sp)

	;;; ... and then both parts of the result

	neg.d	$f0, $f0		;;; fractional part
	lw	a2, (usp)
	lw	t3, _MS_WORD(a2)	;;; high word of integer part
	or	t3, 0x80000000
	sw	t3, _MS_WORD(a2)

2:
	lw	t0, 4(usp)
	s.d	$f0, (t0)
	addu	usp, 8

	lw	ra, 28(sp)
	addu	sp, 32
	j	ra

	.end	$pfmodf


;;; -- ARITHMETIC ---------------------------------------------------------

;;; _PFABS:
;;;	Double float absolute value.

;;; Call:
;;;	_pfabs(_DFLOAT_ADDR)

;;; Registers:
;;;	a0	double float address
;;;	t0	sign byte of double float

DEF_C_LAB(_pfabs)

	.ent	$pfabs
$pfabs:

	lw	a0, (usp)
	addu	usp, 4

	lbu	t0, _MS_BYTE(a0)	;;; load sign byte
	and	t0, 0x7f		;;; clear sign bit
	sb	t0, _MS_BYTE(a0)	;;; store back sign byte

	j	ra

	.end	$pfabs


;;; _PFNEGATE:
;;;	Double float negation.

;;; Call:
;;;	_pfnegate(_DFLOAT_ADDR)

;;; Registers:
;;;	a0	double float address
;;;	t0	sign byte of double float

DEF_C_LAB(_pfnegate)

	.ent	$pfnegate
$pfnegate:

	lw	a0, (usp)
	addu	usp, 4

	lbu	t0, _MS_BYTE(a0)	;;; load sign byte
	xor	t0, 0x80		;;; complement sign bit
	sb	t0, _MS_BYTE(a0)	;;; store back sign byte

	j	ra

	.end	$pfnegate


;;; _PFADD:
;;; _PFSUB:
;;; _PFMULT:
;;; _PFDIV:
;;;	Double float dyadic maths operations. Return non-false (actually
;;;	the first argument) if the operation succeeds, or <false> for
;;;	overflow etc. The result of the operation is written back to the
;;;	first argument.

;;; Example call:
;;;	_pfadd(_DFLOAT_ADDR_1, _DFLOAT_ADDR_2) -> BOOL

;;; Registers:
;;;	a0	double float result/source1 address
;;;	a1	double float source2 address
;;;	t1	exponent word of result
;;;	f0	result/source1
;;;	f2	source2

DEF_C_LAB(_pfadd)

	.ent	$pfadd
$pfadd:

	lw	a0, 4(usp)
	lw	a1, (usp)
	addu	usp, 4
	l.d	$f0, (a0)
	l.d	$f2, (a1)
	add.d	$f0, $f2
	b	check_overflow

	.end	$pfadd

DEF_C_LAB(_pfsub)

	.ent	$pfsub
$pfsub:

	lw	a0, 4(usp)
	lw	a1, (usp)
	addu	usp, 4
	l.d	$f0, (a0)
	l.d	$f2, (a1)
	sub.d	$f0, $f2
	b	check_overflow

	.end	$pfsub

DEF_C_LAB(_pfmult)

	.ent	$pfmult
$pfmult:

	lw	a0, 4(usp)
	lw	a1, (usp)
	addu	usp, 4
	l.d	$f0, (a0)
	l.d	$f2, (a1)
	mul.d	$f0, $f2
	b	check_overflow

	.end	$pfmult

DEF_C_LAB(_pfdiv)

	.ent	$pfdiv
$pfdiv:

	lw	a0, 4(usp)
	lw	a1, (usp)
	addu	usp, 4
	l.d	$f0, (a0)
	l.d	$f2, (a1)
	div.d	$f0, $f2
	b	check_overflow

	.end	$pfdiv

	.ent	check_overflow
check_overflow:

	mfc1	t1, $f1		;;; load high word of result to t1
	srl	t1, 20		;;; isolate exponent bits
	and	t1, 0x7ff	;;; clear sign bit
	beq	t1, 0x7ff, 1f	;;; branch if result infinite
	s.d	$f0, (a0)
	j	ra
1:	sw	false, (usp)
	j	ra

	.end	check_overflow


;;; _PFQREM:
;;;	Double float remainder. Chains -Float_qrem-.

DEF_C_LAB(_pfqrem)

	.ent	$pfqrem
$pfqrem:
	.set	noreorder
	CPLOAD	t9
	.set	reorder

	la	t9, XC_LAB(Sys$-Float_qrem)
	j	t9

	.end	$pfqrem


;;; -- PREDICATES ---------------------------------------------------------

;;; _PFZERO:
;;;	Tests for double float zero (+/-)

;;; Call:
;;;	_pfzero(_DFLOAT_ADDR) -> BOOL

;;; Registers:
;;;	a0	double float address
;;;	t0,t1	double float value
;;;	t1	work

DEF_C_LAB(_pfzero)

	.ent	$pfzero
$pfzero:

	;;; Load dfloat to t0/t1

	lw	a0, (usp)
	lw	t0, _LS_WORD(a0)
	lw	t1, _MS_WORD(a0)

	;;; Value is zero if both words are zero
	;;; (discounting the sign bit in the high word)

	sll	t1, 1
	or	t2, t0, t1
	bnez	t2, 1f

	la	t2, _TRUE_OFFS(false)
	sw	t2, (usp)
	j	ra

1:	sw	false, (usp)
	j	ra

	.end	$pfzero


;;; _PFNEG:
;;;	Tests double float for true negative (i.e. not including -0.0)

;;; Call:
;;;	_pfneg(_DFLOAT_ADDR) -> BOOL

;;; Registers:
;;;	a0	double float address
;;;	t2	result
;;;	t0,t1	double float value

DEF_C_LAB(_pfneg)

	.ent	$pfneg
$pfneg:

	;;; Load dfloat to t0/t1

	lw	a0, (usp)
	lw	t0, _LS_WORD(a0)
	lw	t1, _MS_WORD(a0)

	;;; If high word is >= 0, value must be positive

	bgez	t1, 1f

	;;; Likewise if both words are zero
	;;; (discounting the sign bit in the high word)

	sll	t1, 1
	or	t2, t0, t1
	beqz	t2, 1f

	la	t2, _TRUE_OFFS(false)
	sw      t2, (usp)
	j	ra

1:	sw	false, (usp)
	j	ra

	.end	$pfneg


;;; _PFEQ:
;;;	Tests two double floats for equality.

;;; Call:
;;;	_pfeq(_DFLOAT_ADDR_1, _DFLOAT_ADDR_2) -> BOOL

;;; Registers:
;;;	a0	double float address 1
;;;	a1	double float address 2
;;;	t0,t1	double float 1
;;;	t2,t3	double float 2

DEF_C_LAB(_pfeq)

	.ent	$pfeq
$pfeq:

	;;; Load dfloats to t0/t1 and t2/t3

	lw	a0, 4(usp)
	lw	a1, (usp)
	addu	usp, 4
	ld	t0, (a0)
	ld	t2, (a1)

	;;; Values are the same if their low and high words are the same

	bne	t0, t2, 1f
	bne	t1, t3, 1f

	la	t4, _TRUE_OFFS(false)
	sw      t4, (usp)
	j	ra

1:	sw	false, (usp)
	j	ra

	.end	$pfeq


;;; _PFSGR:
;;;	Double float signed greater-than.

;;; Call:
;;;	_pfsgr(_DFLOAT_ADDR_1, _DFLOAT_ADDR_2) -> BOOL

;;; Registers:
;;;	a0	double float address 1
;;;	a1	double float address 2
;;;	f0	double float 1
;;;	f2	double float 2

DEF_C_LAB(_pfsgr)

	.ent	$pfsgr
$pfsgr:

	;;; Load values to f0 and f2

	lw	a0, 4(usp)
	lw	a1, (usp)
	addu	usp, 4
	l.d	$f0, (a0)
	l.d	$f2, (a1)

	;;; Do the comparison (f0 <= f2)

	c.le.d	$f0, $f2
	bc1t	1f

	la	t0, _TRUE_OFFS(false)
	sw      t0, (usp)
	j	ra

1:	sw	false, (usp)
	j	ra

	.end	$pfsgr


;;; _PFSGREQ:
;;;	Double float signed greater-than-or-equal.

;;; Call:
;;;	_pfsgreq(_DFLOAT_ADDR_1, _DFLOAT_ADDR_2) -> BOOL

;;; Registers:
;;;	a0	double float address 1
;;;	a1	double float address 2
;;;	f0	double float 1
;;;	f2	double float 2

DEF_C_LAB(_pfsgreq)

	.ent	$pfsgreq
$pfsgreq:

	;;; Load values to f0 and f2

	lw	a0, 4(usp)
	lw	a1, (usp)
	addu	usp, 4
	l.d	$f0, (a0)
	l.d	$f2, (a1)

	;;; Do the comparison (f0 < f2)

	c.lt.d	$f0, $f2
	bc1t	1f

	la	t0, _TRUE_OFFS(false)
	sw      t0, (usp)
	j	ra

1:	sw	false, (usp)
	j	ra

	.end	$pfsgreq


;;; -- GET/SET EXPONENT ---------------------------------------------------

;;; _PF_EXPOF:
;;;	Get/set the exponent E of a double float, where E is the number of
;;;	bits needed for the integer part, e.g. 1 for 1.0, 0 for 0.5,
;;;	-1 for 0.05 etc. The updater returns a flag indicating whether
;;;	the new exponent was OK.

;;; Call:
;;;	_pf_expof(_DFLOAT_ADDR) -> E
;;;	E -> _pf_expof(_DFLOAT_ADDR) -> BOOL

;;; Registers:
;;;	a0	double float address
;;;	t0	exponent half word of dfloat
;;;	v0	E

DEF_C_LAB(_pf_expof)

	.ent	$pf_expof
$pf_expof:

	lw	a0, (usp)		;;; double float address
	lhu	t0, _MS_HALF(a0)	;;; exponent half word of dfloat
	srl	v0, t0, 4
	and	v0, 0x7ff		;;; exponent (biased)
	subu	v0, 1022		;;; E
	sw	v0, (usp)
	j	ra

	.end	$pf_expof

DEF_C_LAB(-> _pf_expof)

	.ent	u$pf_expof
u$pf_expof:

	lw	v0, 4(usp)		;;; E
	lw	a0, (usp)		;;; double float address
	addu	usp, 4
	addu	v0, 1022		;;; exponent (biased)
	blez	v0, 1f			;;; too small if zero or negative
	bge	v0, 0x7ff, 1f		;;; too big if >= 2047
	sll	v0, 4
	lhu	t0, _MS_HALF(a0)	;;; exponent half word of dfloat
	and	t0, 0x800f		;;; clear exponent
	or	t0, v0			;;; set new
	sh	t0, _MS_HALF(a0)	;;; store back
	j	ra

1:	;;; Bad exponent: return <false>

	sw	false, (usp)
	j	ra

	.end	u$pf_expof


/***************** end labels for wrapping structures *****************/

	.data
Ldata_end:
Ldata_size = 0 ##PATCH## Ldata_size Ldata_end Ldata_start

/**********************************************************************/


/* --- Revision History ---------------------------------------------------
--- John Gibson, Feb 25 1995
	Removed _m*ath1/2 subroutines (library math functions now called
	via _extern)
--- Robert John Duncan, May 24 1994
	No longer needs to set _sys*error
--- Robert John Duncan, Mar 22 1994
	Removed procedure assignments to a0 (again).
	Changed external jumps to go off t9.
--- Robert John Duncan, Mar 15 1994
	Removed the wrapping structure from the text section
--- Robert John Duncan, Mar 15 1994
	Pop calls must now set a0 to the procedure address
--- Robert John Duncan, Mar  9 1994
	Added directives for position-independent code. Improved stack
	frame layout, allowing a minimum 16-byte argument save area for all
	calls and with double alignment. Added saves and restores for
	special var block reg.
--- Robert John Duncan, Mar  8 1994
	Added .ent/.end directives
--- Robert John Duncan, Mar  7 1994
	Changed to use register $t9 for function calls.
--- Robert John Duncan, Mar 26 1991
	Changed _pf_extend_s_to_d to check for Inf and NaN;
	added _pf_check_d to do likewise.
--- Robert John Duncan, Jan 29 1991
	__pop_*fpe_handler now defined in "asignals.s"
--- Robert John Duncan, Jul 25 1990
	Added an extra 8 bytes to stack frame in math1. Some maths library
	functions seem to need it; no idea why.
 */
