/* --- Copyright University of Sussex 1995. All rights reserved. ----------
 * File:	C.hppa/src/afloat.s
 * Purpose:	Floating point arithmetic for HP PA-RISC 1.1
 * Author:	Julian Clinton, January 1993 (see revisions)
 */


#_<

#_INCLUDE 'declare.ph'


lconstant macro	(

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

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

);

	;;; Layout of double floats in memory

lconstant macro (
	_LS_WORD	= _int(4),
	_MS_WORD	= _int(0),
	_MS_HALF	= _int(0),
	_MS_BYTE	= _int(0),
);

section $-Sys;

constant procedure (
	Float_qrem,
);

vars
	Extern$- _saved_sp,
;

endsection;

lconstant macro (
	_SAVED_SP	= [_SVB_OFFS(Sys$-Extern$- _saved_sp)],
);

>_#

#_INCLUDE 'asm_macros.h'

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

	.code
	.word	Lcode_end-Lcode_start, C_LAB(Sys$-objmod_pad_key)
Lcode_start
	.data
	.word	Ldata_end-Ldata_start, C_LAB(Sys$-objmod_pad_key)
Ldata_start

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


	.code


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

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

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

;;; Registers:
;;;	%t1	src address
;;;	%t2	dst address
;;;	%t3,%t4	double float value

DEF_C_LAB(_pfcopy)

	ldwm		4(%usp), %t1		;;; _SRC_ADDR
	ldwm		4(%usp), %t2		;;; _DST_ADDR
	ldw		_MS_WORD(%t1), %t3
	ldw		_LS_WORD(%t1), %t4
	stw		%t3, _MS_WORD(%t2)
	RETE
	stw		%t4, _LS_WORD(%t2)


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

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

;;; Registers:
;;;	%t1	decimal argument/sfloat result

DEF_C_LAB(_pf_sfloat_dec)

	ldwm		4(%usp), %t1
	dep		0, 31, 2, %t1
	RETE
	stwm		%t1, -4(%usp)		;;; branch delay slot


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

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

;;; Registers:
;;;	%t1	double float address
;;;	%fr8L	_int
;;;	%fr9	conversion

DEF_C_LAB(_pf_dfloat_int)

	ldwm		4(%usp), %t1		;;; _dfloat_addr
	fldws,ma	4(%usp), %fr8L		;;; _int
	fcnvxf,sgl,dbl	%fr8L, %fr9
	RETE
	fstds		%fr9, (%t1)		;;; branch delay slot


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

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

;;; Registers:
;;;	%t1	double float address
;;;	%t2	pop decimal
;;;	%fr8	sys decimal
;;;	%fr9	conversion

DEF_C_LAB(_pf_dfloat_dec)

	ldwm		4(%usp), %t1		;;; _dfloat_addr
	ldwm		4(%usp), %t2		;;; dec
	dep		0, 31, 2, %t2		;;; -> _sfloat
	stwm		%t2, -4(%usp)
	fldws,ma	4(%usp), %fr8L		;;; _sfloat
	fcnvff,sgl,dbl	%fr8L, %fr9
	RETE
	fstds		%fr9, (%t1)		;;; branch delay slot


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

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

;;; Registers:
;;;	%t1	double float address
;;;	%t2	ddecimal
;;;	%t3,%t4	double float

DEF_C_LAB(_pf_dfloat_ddec)

	ldwm		4(%usp), %t1
	ldwm		4(%usp), %t2
	ldw		_DD_2(%t2), %t3		;;; low word
	ldw		_DD_1(%t2), %t4		;;; high word
	stw		%t3, _LS_WORD(%t1)
	RETE
	stw		%t4, _MS_WORD(%t1)	;;; branch delay slot


;;; _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:
;;;	%arg0	double float address
;;;	%t1	single float/decimal result
;;;	%t2	low word of double float
;;;	%t3	bit mask/exponent
;;;	%fr8/9	conversion

DEF_C_LAB(_pf_cvt_to_dec)

	ldwm		4(%usp), %arg0
	ldw		_LS_WORD(%arg0), %t2

	;;; 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)
	zdepi		1, 1, 1, %t3		;;; 0x40000000 in %t3
	comb,=,n	%t2, %t3, L$10		;;; tied and even: won't round
	depi		7, 4, 3, %t2		;;; set bits 23-25
	stw		%t2, _LS_WORD(%arg0)	;;; store back

L$10	;;; Load to FPU, convert and read back to %t1
	fldds		(%arg0), %fr8
	fcnvff,dbl,sgl	%fr8, %fr9L
	fstws,mb	%fr9L, -4(%usp)
	ldwm		4(%usp), %t1

	;;; Check result for overflow/underflow by examining the
	;;; exponent bits
	extrs		%t1, 8, 8, %t3		;;; exponent
	comib,=,n	0, %t3, L$11		;;; all 0s => zero/denormal
	comib,=,n	-1, %t3, L$12		;;; all 1s => overflow

	;;; Result OK: set bottom bits for pop decimal and return
	depi		1, 31, 2, %t1
	RETE
	stwm		%t1, -4(%usp)		;;; branch delay slot

L$11	;;; Zero or denormal: return pop 0.0
	zdepi		1, 31, 2, %t1
	RETE
	stwm		%t1, -4(%usp)		;;; branch delay slot

L$12	;;; Overflow: return <false>
	RETE
	stwm		%false, -4(%usp)	;;; branch delay slot


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

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

;;; Registers:
;;;	%arg0	double float address
;;;	%arg1	ddecimal
;;;	%t1,%t2	double float value
;;;	%t3	exponent bits of double float

DEF_C_LAB(_pf_cvt_to_ddec)

	ldwm		4(%usp), %arg1
	ldwm		4(%usp), %arg0
	ldw		_LS_WORD(%arg0), %t1
	ldw	 	_MS_WORD(%arg0), %t2

	;;; Isolate exponent bits of result in %t3:
	;;; if zero, value is zero or denormal
	extru,<>	%t2, 11, 11, %t3
	b,n		L$15			;;; branch if 0

	;;; Store low and high words to ddecimal
	stw		%t1, _DD_2(%arg1)
	RETE
	stw		%t2, _DD_1(%arg1)	;;; branch delay slot

L$15	;;; Zero or denormal: make result properly zero
	stw		0, _DD_2(%arg1)
	RETE
	stw		0, _DD_1(%arg1)		;;; branch delay slot


;;; _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:
;;;	%t1	double float address
;;;	%t2	single float result
;;;	%t3	exponent bits
;;;	%fr8/9	conversion

DEF_C_LAB(_pf_round_d_to_s)

	ldwm		4(%usp), %t1
	fldds		(%t1), %fr8
	fcnvff,dbl,sgl	%fr8, %fr9L
	fstws		%fr9L, (%t1)
	ldw		(%t1), %t2

	;;; Overflow if exponent is all ones
	extrs		%t2, 8, 8, %t3
	comib,=,n	-1, %t3, L$20

	;;; No overflow: stack address
	RETE
	stwm		%t1, -4(%usp)

L$20	;;; Overflow: stack false
	RETE
	stwm		%false, -4(%usp)


;;; _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:
;;;	%t1	double float address
;;;	%t2	single float
;;;	%t3	exponent
;;;	%fr8/9	conversion

DEF_C_LAB(_pf_extend_s_to_d)

	;;; Load single from _dfaddr
	ldwm		4(%usp), %t1
	ldw		(%t1), %t2

	;;; Overflow if exponent is all ones (NaN or Inf)
	extrs		%t2, 8, 8, %t3
	comib,=,n	-1, %t3, L$22

	;;; No overflow: do conversion, store result and stack address
	fldws		(%t1), %fr8L
	fcnvff,sgl,dbl	%fr8L, %fr9
	fstds		%fr9, (%t1)
	RETE
	stwm		%t1, -4(%usp)

L$22	;;; Overflow: stack false
	RETE
	stwm		%false, -4(%usp)


;;; _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:
;;;	%t1	_DFLOAT_ADDR
;;;	%t2	exponent word
;;;	%t3	exponent

DEF_C_LAB(_pf_check_d)

	;;; Load exponent word
	ldwm		4(%usp), %t1
	ldw		_MS_WORD(%t1), %t2

	;;; Check exponent for Inf or NaN
	extrs		%t2, 11, 11, %t3
	comib,=,n	-1, %t3, L$24

	;;; Normal: return _DFLOAT_ADDR
	RETE
	stwm		%t1, -4(%usp)

L$24	;;; Inf or NaN: return <false>
	RETE
	stwm		%false, -4(%usp)


;;; _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>

;;; Registers:
;;;	%t1	FP status word
;;;	%t2	_DFLOAT_ADDR
;;;	%fr4	double float value/integer result

DEF_C_LAB(_pf_intof)

	;;; local var for status word
	.data
status	.double		0
	.code

	;;; load dfloat to %fr4
	ldwm		4(%usp), %t2
	fldds		(%t2), %fr4

	;;; synchronise and clear outstanding exceptions
	ldil		L'status, %t1
	ldo		R'status(%t1), %t1
	fstds		%fr0, (%t1)

	;;; do the conversion: this may raise Unimplemented or IEEE
	;;; Inexact exceptions. Inexact is of no interest and we assume
	;;; it's disabled, but Unimplemented indicates that the result is
	;;; out of range, which is just what we want to know
	fcnvfxt,dbl,sgl	%fr4, %fr4

	;;; store the status word again: this will clear any exception
	;;; and prevent it trapping, but leave the T bit set in the
	;;; stored word
	fstds		%fr0, (%t1)

	;;; check the status word to see if an exception did occur
	ldw		(%t1), %t1
	bb,<,n		%t1, 25, L$26

	;;; OK -- return result and _DFLOAT_ADDR
	fstws,mb	%fr4, -4(%usp)
	RETE
	stwm		%t2, -4(%usp)

L$26	;;; overflow -- return <false>
	RETE
	stwm		%false, -4(%usp)


;;; _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.

;;; Registers:
;;; NB: -modf- appears to require double parameters in general
;;; registers rather than floating point registers. Therefore
;;; the double float is passed in %arg0/1 rather than %fr5.
;;;	%arg0/1	double float arg to -modf- (low/high words respectively)
;;;	%arg2	double float address (iptr argument to -modf-)
;;;	%t1	fractional result address
;;;	%fr4	fractional result


DEF_C_LAB(_pfmodf)

	ldwm		4(%usp), %arg2		;;; address of float in %arg2

	;;; Stack frame for function call.
	stwm		%r31, 4(%sp)		;;; save return address
	stw		%sp, _SAVED_SP(%svb)
	ldo		127(%sp), %sp
	dep		0, 31, 6, %sp		;;; make %sp 64-byte aligned

	/* This is the call we mean to make ...
	bl		EXTERN_NAME(modf), %rp
	fldds		(%arg2), %fr5
	ldwm		-4(%usp), %t1
	fstds		%fr4, (%t1)
	*/

	;;; In pratice, calling modf directly opens up a can of worms
	;;; w.r.t what extra code the linker might add to relocate
	;;; arguments in registers etc. By doing an indirect call via
	;;; the plabel we can maintain a stndard interface using the
	;;; general registers only.
	ldw		_MS_WORD(%arg2), %arg1
	ldw		_LS_WORD(%arg2), %arg0
	ldil		LP'modf, %t1
	ldo		RP'modf(%t1), %t1
	bl		$$dyncall, %r31
	copy		%r31, %rp
	ldwm		4(%usp), %t1
	stw		%ret0, _MS_WORD(%t1)
	stw		%ret1, _LS_WORD(%t1)

	;;; Unwind stack frame and return
	ldw		_SAVED_SP(%svb), %sp
	ldwm		-4(%sp), %r31
	RETE
	stw		0, _SAVED_SP(%svb)


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

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

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

;;; Registers:
;;;	%t1	double float address
;;;	%t2	sign byte of double float

DEF_C_LAB(_pfabs)

	ldwm		4(%usp), %t1
	fldds		(%t1), %fr8
	fabs,dbl	%fr8, %fr9
	RETE
	fstds		%fr9, (%t1)


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

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

;;; Registers:
;;;	%t1	double float address
;;;	%t2	sign byte of double float
;;;	%t3	128 (0x80)

DEF_C_LAB(_pfnegate)

	ldwm		4(%usp), %t1
	ldb		_MS_BYTE(%t1), %t2	;;; load sign byte
	ldi		128, %t3
	xor		%t2, %t3, %t2		;;; complement sign bit
	RETE
	stb		%t2, _MS_BYTE(%t1)	;;; save sign byte


;;; _PFADD:
;;; _PFSUB:
;;; _PFMULT:
;;; _PFDIV:
;;; _PFQREM:
;;;	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:
;;;	%t1	double float result/source1 address
;;;	%t2	double float source2 address
;;;	%t3	result/result exponent
;;;	%t4	0x7FF
;;;	%fr8	double float 1/result
;;;	%fr9	double float 2

DEF_C_LAB(_pfadd)

	ldwm		4(%usp), %t2
	ldwm		4(%usp), %t1
	fldds		(%t2), %fr9
	fldds		(%t1), %fr8
	b		return
	fadd,dbl	%fr8, %fr9, %fr8	;;; branch delay slot


DEF_C_LAB(_pfsub)

	ldwm		4(%usp), %t2
	ldwm		4(%usp), %t1
	fldds		(%t2), %fr9
	fldds		(%t1), %fr8
	b		return
	fsub,dbl	%fr8, %fr9, %fr8	;;; branch delay slot


DEF_C_LAB(_pfmult)

	ldwm		4(%usp), %t2
	ldwm		4(%usp), %t1
	fldds		(%t2), %fr9
	fldds		(%t1), %fr8
	b		return
	fmpy,dbl	%fr8, %fr9, %fr8	;;; branch delay slot


DEF_C_LAB(_pfdiv)

	ldwm		4(%usp), %t2
	ldwm		4(%usp), %t1
	fldds		(%t2), %fr9
	fldds		(%t1), %fr8
	b		return
	fdiv,dbl	%fr8, %fr9, %fr8	;;; branch delay slot

return

	;;; Check MS word of result for overflow
	fstws,mb	%fr8L, -4(%usp)
	ldwm		4(%usp), %t3
	extrs		%t3, 11, 11, %t3	;;; get exponent bits
	comib,=,n	-1, %t3, L$30		;;; overflow if all 1s

	;;; Value is okay: return it plus non-false value
	fstds		%fr8, (%t1)
	RETE
	stwm		%t1, -4(%usp)

L$30	;;; Overflow: return <false>
	RETE
	stwm		%false, -4(%usp)


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

DEF_C_LAB(_pfqrem)

	CHAINSYS	XC_LAB(Sys$-Float_qrem)
	nop


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

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

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

;;; Registers:
;;;	%t1	double float address
;;;	%t2	result
;;;	%t3,%t4	double float value

DEF_C_LAB(_pfzero)

	;;; Load dfloat to %t3/%t4
	ldwm		4(%usp), %t1
	ldw		_MS_WORD(%t1), %t4
	ldw		_LS_WORD(%t1), %t3

	;;; Value is zero if both words are zero
	;;; (discounting the sign bit in the high word)
	extru		%t4, 31, 31, %t4
	or		%t3, %t4, %t2
	comb,<>,n	0, %t2, L$32

	;;; Value is 0: return <true>
	LDA32		C_LAB(true), %t2
	RETE
	stwm		%t2, -4(%usp)		;;; branch delay slot

L$32	;;; Some bits are set: return <false>
	RETE
	stwm		%false, -4(%usp)


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

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

;;; Registers:
;;;	%t1	double float address
;;;	%t2	result
;;;	%t3,%t4	double float value

DEF_C_LAB(_pfneg)

	;;; Load dfloat to %t3/%t4
	ldwm		4(%usp), %t1
	fldds		(%t1), %fr8
	fcmp,dbl,<	%fr8, %fr0		;;; check if < 0.0
	ftest
	b,n		L$34			;;; else branch

	;;; Double is negative: return <true>
	LDA32		C_LAB(true), %t2
	RETE
	stwm		%t2, -4(%usp)		;;; branch delay slot

L$34	;;; Double is not negative: return <false>
	RETE
	stwm		%false, -4(%usp)


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

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

;;; Registers:
;;;	%t1	double float address 1
;;;	%t2	double float address 2
;;;	%t3	true, if condition was true
;;;	%fr8	double float 1
;;;	%fr9	double float 2

DEF_C_LAB(_pfeq)

	ldwm		4(%usp), %t2
	ldwm		4(%usp), %t1
	fldds		(%t2), %fr9
	fldds		(%t1), %fr8

	;;; Do the compare and then call ftest to modify the nullify bit.
	;;; A branch after the ftest will only be executed if the test
	;;; was false.
	fcmp,dbl,=	%fr8, %fr9
	ftest
	b,n		L_pfalse

	;;; Doubles are equal: return <true>
	LDA32		C_LAB(true), %t3
	RETE
	stwm		%t3, -4(%usp)


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

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

;;; Registers:
;;;	%t1	double float address 1
;;;	%t2	double float address 2
;;;	%t3	true, if condition was true
;;;	%fr8	double float 1
;;;	%fr9	double float 2

DEF_C_LAB(_pfsgr)

	ldwm		4(%usp), %t2
	ldwm		4(%usp), %t1
	fldds		(%t2), %fr9
	fldds		(%t1), %fr8

	;;; Do the compare and then call ftest to modify the nullify bit.
	;;; A branch after the ftest will only be executed if the test
	;;; was false.
	fcmp,dbl,>	%fr8, %fr9
	ftest
	b,n		L_pfalse

	;;; Double 1 > double 2: return <true>
	LDA32		C_LAB(true), %t3
	RETE
	stwm		%t3, -4(%usp)


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

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

;;; Registers:
;;;	%t1	double float address 1
;;;	%t2	double float address 2
;;;	%t3	true, if condition was true
;;;	%fr8	double float 1
;;;	%fr9	double float 2

DEF_C_LAB(_pfsgreq)

	ldwm		4(%usp), %t2
	ldwm		4(%usp), %t1
	fldds		(%t2), %fr9
	fldds		(%t1), %fr8

	;;; double 1 >= double 2
	fcmp,dbl,>=	%fr8, %fr9
	ftest
	b,n		L_pfalse

	;;; Double 2 > double 1: return <true>
	LDA32		C_LAB(true), %t3
	RETE
	stwm		%t3, -4(%usp)


L_pfalse	;;; Test failed so return <false>
	RETE
	stwm		%false, -4(%usp)


;;; -- 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:
;;;	%t1	double float address
;;;	%t2	most significant word of dfloat (contains exponent)
;;;	%t3	E
;;;	%t4	2047

DEF_C_LAB(_pf_expof)

	ldwm		4(%usp), %t1
	ldw		_MS_WORD(%t1), %t2
	extru		%t2, 11, 11, %t3	;;; exponent (biased)
	addi		-1022, %t3, %t3		;;; E
	RETE
	stwm		%t3, -4(%usp)


DEF_C_LAB(-> _pf_expof)

	ldwm		4(%usp), %t1		;;; double float address
	ldwm		4(%usp), %t3		;;; E
	addi,>		1022, %t3, %t3		;;; exponent (biased)
	b		L$40			;;; to small if 0 or negative
	ldi		2047, %t4
	comb,>>=,n	%t3, %t4, L$40		;;; too big if >= 2047
	ldw		_MS_WORD(%t1), %t2	;;; word containing exponent
	dep		%t3, 11, 11, %t2	;;; transfer exponent
	stw		%t2, _MS_WORD(%t1)	;;; save word/exponent
	RETE
	stwm		%t1, -4(%usp)		;;; return non-false value

L$40	;;; Bad exponent: return <false>
	RETE
	stwm		%false, -4(%usp)


	.code
	.import		C_LAB(Sys$-objmod_pad_key), data
	.import		C_LAB(true), data
	.import		I_LAB(Sys$-Extern$- _saved_sp), data
	.import		XC_LAB($-Sys$-Float_qrem), data
	.import		EXTERN_NAME(modf), code
	.import		$$dyncall, millicode


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

	.code
	.align  8
Lcode_end
	.data
	.align  8
Ldata_end

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



/* --- 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
 */
