xref: /csrg-svn/lib/libc/sparc/gen/modf.s (revision 61170)
154388Storek/*
2*61170Sbostic * Copyright (c) 1992, 1993
3*61170Sbostic *	The Regents of the University of California.  All rights reserved.
454388Storek *
554388Storek * This software was developed by the Computer Systems Engineering group
654388Storek * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
754388Storek * contributed to Berkeley.
854388Storek *
954388Storek * %sccs.include.redist.c%
1054388Storek *
1154388Storek * from: $Header: modf.s,v 1.3 92/06/20 00:00:54 torek Exp $
1254388Storek */
1354388Storek
1454388Storek#if defined(LIBC_SCCS) && !defined(lint)
15*61170Sbostic	.asciz "@(#)modf.s	8.1 (Berkeley) 06/04/93"
1654388Storek#endif /* LIBC_SCCS and not lint */
1754388Storek
1854388Storek#include "DEFS.h"
1954388Storek#include <machine/fsr.h>
2054388Storek
2154388Storek/*
2254388Storek * double modf(double val, double *iptr)
2354388Storek *
2454388Storek * Returns the fractional part of `val', storing the integer part of
2554388Storek * `val' in *iptr.  Both *iptr and the return value have the same sign
2654388Storek * as `val'.
2754388Storek *
2854388Storek * Method:
2954388Storek *
3054388Storek * We use the fpu's normalization hardware to compute the integer portion
3154388Storek * of the double precision argument.  Sun IEEE double precision numbers
3254388Storek * have 52 bits of mantissa, 11 bits of exponent, and one bit of sign,
3354388Storek * with the sign occupying bit 31 of word 0, and the exponent bits 30:20
3454388Storek * of word 0.  Thus, values >= 2^52 are by definition integers.
3554388Storek *
3654388Storek * If we take a value that is in the range [+0..2^52) and add 2^52, all
3754388Storek * of the fractional bits fall out and all of the integer bits are summed
3854388Storek * with 2^52.  If we then subtract 2^52, we get those integer bits back.
3954388Storek * This must be done with rounding set to `towards 0' or `towards -inf'.
4054388Storek * `Toward -inf' fails when the value is 0 (we get -0 back)....
4154388Storek *
4254388Storek * Note that this method will work anywhere, but is machine dependent in
4354388Storek * various aspects.
4454388Storek *
4554388Storek * Stack usage:
4654388Storek *	4@[%fp - 4]	saved %fsr
4754388Storek *	4@[%fp - 8]	new %fsr with rounding set to `towards 0'
4854388Storek *	8@[%fp - 16]	space for moving between %i and %f registers
4954388Storek * Register usage:
5054388Storek *	%i0%i1		double val;
5154388Storek *	%l0		scratch
5254388Storek *	%l1		sign bit (0x80000000)
5354388Storek *	%i2		double *iptr;
5454388Storek *	%f2:f3		`magic number' 2^52, in fpu registers
5554388Storek *	%f4:f5		double v, in fpu registers
5654388Storek */
5754388Storek
5854388Storek	.align	8
5954388StorekLmagic:
6054388Storek	.word	0x43300000	! sign = 0, exponent = 52 + 1023, mantissa = 0
6154388Storek	.word	0		! (i.e., .double 0r4503599627370496e+00)
6254388Storek
6354388StorekL0:
6454388Storek	.word	0		! 0.0
6554388Storek	.word	0
6654388Storek
6754388StorekENTRY(modf)
6854388Storek	save	%sp, -64-16, %sp
6954388Storek
7054388Storek	/*
7154388Storek	 * First, compute v = abs(val) by clearing sign bit,
7254388Storek	 * and then set up the fpu registers.  This would be
7354388Storek	 * much easier if we could do alu operations on fpu registers!
7454388Storek	 */
7554388Storek	sethi	0x80000000, %l1		! sign bit
7654388Storek	andn	%i0, %l1, %l0
7754388Storek	st	%l0, [%fp - 16]
7854388Storek	sethi	%hi(Lmagic), %l0
7954388Storek	ldd	[%l0 + %lo(Lmagic)], %f2
8054388Storek	st	%i1, [%fp - 12]
8154388Storek	ldd	[%fp - 16], %f4		! %f4:f5 = v
8254388Storek
8354388Storek	/*
8454388Storek	 * Is %f4:f5 >= %f2:f3 ?  If so, it is all integer bits.
8554388Storek	 * It is probably less, though.
8654388Storek	 */
8754388Storek	fcmped	%f4, %f2
8854388Storek	nop				! fpop2 delay
8954388Storek	fbuge	Lbig			! if >= (or unordered), go out
9054388Storek	nop
9154388Storek
9254388Storek	/*
9354388Storek	 * v < 2^52, so add 2^52, then subtract 2^52, but do it all
9454388Storek	 * with rounding set towards zero.  We leave any enabled
9554388Storek	 * traps enabled, but change the rounding mode.  This might
9654388Storek	 * not be so good.  Oh well....
9754388Storek	 */
9854388Storek	st	%fsr, [%fp - 4]		! %l5 = current FSR mode
9954388Storek	set	FSR_RD, %l3		! %l3 = rounding direction mask
10054388Storek	ld	[%fp - 4], %l5
10154388Storek	set	FSR_RD_RZ << FSR_RD_SHIFT, %l4
10254388Storek	andn	%l5, %l3, %l6
10354388Storek	or	%l6, %l4, %l6		! round towards zero, please
10454388Storek	and	%l5, %l3, %l5		! save original rounding mode
10554388Storek	st	%l6, [%fp - 8]
10654388Storek	ld	[%fp - 8], %fsr
10754388Storek
10854388Storek	faddd	%f4, %f2, %f4		! %f4:f5 += 2^52
10954388Storek	fsubd	%f4, %f2, %f4		! %f4:f5 -= 2^52
11054388Storek
11154388Storek	/*
11254388Storek	 * Restore %fsr, but leave exceptions accrued.
11354388Storek	 */
11454388Storek	st	%fsr, [%fp - 4]
11554388Storek	ld	[%fp - 4], %l6
11654388Storek	andn	%l6, %l3, %l6		! %l6 = %fsr & ~FSR_RD;
11754388Storek	or	%l5, %l6, %l5		! %l5 |= %l6;
11854388Storek	st	%l5, [%fp - 4]
11954388Storek	ld	[%fp - 4], %fsr		! restore %fsr, leaving accrued stuff
12054388Storek
12154388Storek	/*
12254388Storek	 * Now insert the original sign in %f4:f5.
12354388Storek	 * This is a lot of work, so it is conditional here.
12454388Storek	 */
12554388Storek	btst	%l1, %i0
12654388Storek	be	1f
12754388Storek	nop
12854388Storek	st	%f4, [%fp - 16]
12954388Storek	ld	[%fp - 16], %g1
13054388Storek	or	%l1, %g1, %g1
13154388Storek	st	%g1, [%fp - 16]
13254388Storek	ld	[%fp - 16], %f4
13354388Storek1:
13454388Storek
13554388Storek	/*
13654388Storek	 * The value in %f4:f5 is now the integer portion of the original
13754388Storek	 * argument.  We need to store this in *ival (%i2), subtract it
13854388Storek	 * from the original value argument (%i0:i1), and return the result.
13954388Storek	 */
14054388Storek	std	%f4, [%i2]		! *ival = %f4:f5;
14154388Storek	std	%i0, [%fp - 16]
14254388Storek	ldd	[%fp - 16], %f0		! %f0:f1 = val;
14354388Storek	fsubd	%f0, %f4, %f0		! %f0:f1 -= %f4:f5;
14454388Storek	ret
14554388Storek	restore
14654388Storek
14754388StorekLbig:
14854388Storek	/*
14954388Storek	 * We get here if the original comparison of %f4:f5 (v) to
15054388Storek	 * %f2:f3 (2^52) came out `greater or unordered'.  In this
15154388Storek	 * case the integer part is the original value, and the
15254388Storek	 * fractional part is 0.
15354388Storek	 */
15454388Storek	sethi	%hi(L0), %l0
15554388Storek	std	%f0, [%i2]		! *ival = val;
15654388Storek	ldd	[%l0 + %lo(L0)], %f0	! return 0.0;
15754388Storek	ret
15854388Storek	restore
159