xref: /csrg-svn/lib/libc/sparc/gen/modf.s (revision 54388)
1*54388Storek/*
2*54388Storek * Copyright (c) 1992 The Regents of the University of California.
3*54388Storek * All rights reserved.
4*54388Storek *
5*54388Storek * This software was developed by the Computer Systems Engineering group
6*54388Storek * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
7*54388Storek * contributed to Berkeley.
8*54388Storek *
9*54388Storek * %sccs.include.redist.c%
10*54388Storek *
11*54388Storek * from: $Header: modf.s,v 1.3 92/06/20 00:00:54 torek Exp $
12*54388Storek */
13*54388Storek
14*54388Storek#if defined(LIBC_SCCS) && !defined(lint)
15*54388Storek	.asciz "@(#)modf.s	5.1 (Berkeley) 06/25/92"
16*54388Storek#endif /* LIBC_SCCS and not lint */
17*54388Storek
18*54388Storek#include "DEFS.h"
19*54388Storek#include <machine/fsr.h>
20*54388Storek
21*54388Storek/*
22*54388Storek * double modf(double val, double *iptr)
23*54388Storek *
24*54388Storek * Returns the fractional part of `val', storing the integer part of
25*54388Storek * `val' in *iptr.  Both *iptr and the return value have the same sign
26*54388Storek * as `val'.
27*54388Storek *
28*54388Storek * Method:
29*54388Storek *
30*54388Storek * We use the fpu's normalization hardware to compute the integer portion
31*54388Storek * of the double precision argument.  Sun IEEE double precision numbers
32*54388Storek * have 52 bits of mantissa, 11 bits of exponent, and one bit of sign,
33*54388Storek * with the sign occupying bit 31 of word 0, and the exponent bits 30:20
34*54388Storek * of word 0.  Thus, values >= 2^52 are by definition integers.
35*54388Storek *
36*54388Storek * If we take a value that is in the range [+0..2^52) and add 2^52, all
37*54388Storek * of the fractional bits fall out and all of the integer bits are summed
38*54388Storek * with 2^52.  If we then subtract 2^52, we get those integer bits back.
39*54388Storek * This must be done with rounding set to `towards 0' or `towards -inf'.
40*54388Storek * `Toward -inf' fails when the value is 0 (we get -0 back)....
41*54388Storek *
42*54388Storek * Note that this method will work anywhere, but is machine dependent in
43*54388Storek * various aspects.
44*54388Storek *
45*54388Storek * Stack usage:
46*54388Storek *	4@[%fp - 4]	saved %fsr
47*54388Storek *	4@[%fp - 8]	new %fsr with rounding set to `towards 0'
48*54388Storek *	8@[%fp - 16]	space for moving between %i and %f registers
49*54388Storek * Register usage:
50*54388Storek *	%i0%i1		double val;
51*54388Storek *	%l0		scratch
52*54388Storek *	%l1		sign bit (0x80000000)
53*54388Storek *	%i2		double *iptr;
54*54388Storek *	%f2:f3		`magic number' 2^52, in fpu registers
55*54388Storek *	%f4:f5		double v, in fpu registers
56*54388Storek */
57*54388Storek
58*54388Storek	.align	8
59*54388StorekLmagic:
60*54388Storek	.word	0x43300000	! sign = 0, exponent = 52 + 1023, mantissa = 0
61*54388Storek	.word	0		! (i.e., .double 0r4503599627370496e+00)
62*54388Storek
63*54388StorekL0:
64*54388Storek	.word	0		! 0.0
65*54388Storek	.word	0
66*54388Storek
67*54388StorekENTRY(modf)
68*54388Storek	save	%sp, -64-16, %sp
69*54388Storek
70*54388Storek	/*
71*54388Storek	 * First, compute v = abs(val) by clearing sign bit,
72*54388Storek	 * and then set up the fpu registers.  This would be
73*54388Storek	 * much easier if we could do alu operations on fpu registers!
74*54388Storek	 */
75*54388Storek	sethi	0x80000000, %l1		! sign bit
76*54388Storek	andn	%i0, %l1, %l0
77*54388Storek	st	%l0, [%fp - 16]
78*54388Storek	sethi	%hi(Lmagic), %l0
79*54388Storek	ldd	[%l0 + %lo(Lmagic)], %f2
80*54388Storek	st	%i1, [%fp - 12]
81*54388Storek	ldd	[%fp - 16], %f4		! %f4:f5 = v
82*54388Storek
83*54388Storek	/*
84*54388Storek	 * Is %f4:f5 >= %f2:f3 ?  If so, it is all integer bits.
85*54388Storek	 * It is probably less, though.
86*54388Storek	 */
87*54388Storek	fcmped	%f4, %f2
88*54388Storek	nop				! fpop2 delay
89*54388Storek	fbuge	Lbig			! if >= (or unordered), go out
90*54388Storek	nop
91*54388Storek
92*54388Storek	/*
93*54388Storek	 * v < 2^52, so add 2^52, then subtract 2^52, but do it all
94*54388Storek	 * with rounding set towards zero.  We leave any enabled
95*54388Storek	 * traps enabled, but change the rounding mode.  This might
96*54388Storek	 * not be so good.  Oh well....
97*54388Storek	 */
98*54388Storek	st	%fsr, [%fp - 4]		! %l5 = current FSR mode
99*54388Storek	set	FSR_RD, %l3		! %l3 = rounding direction mask
100*54388Storek	ld	[%fp - 4], %l5
101*54388Storek	set	FSR_RD_RZ << FSR_RD_SHIFT, %l4
102*54388Storek	andn	%l5, %l3, %l6
103*54388Storek	or	%l6, %l4, %l6		! round towards zero, please
104*54388Storek	and	%l5, %l3, %l5		! save original rounding mode
105*54388Storek	st	%l6, [%fp - 8]
106*54388Storek	ld	[%fp - 8], %fsr
107*54388Storek
108*54388Storek	faddd	%f4, %f2, %f4		! %f4:f5 += 2^52
109*54388Storek	fsubd	%f4, %f2, %f4		! %f4:f5 -= 2^52
110*54388Storek
111*54388Storek	/*
112*54388Storek	 * Restore %fsr, but leave exceptions accrued.
113*54388Storek	 */
114*54388Storek	st	%fsr, [%fp - 4]
115*54388Storek	ld	[%fp - 4], %l6
116*54388Storek	andn	%l6, %l3, %l6		! %l6 = %fsr & ~FSR_RD;
117*54388Storek	or	%l5, %l6, %l5		! %l5 |= %l6;
118*54388Storek	st	%l5, [%fp - 4]
119*54388Storek	ld	[%fp - 4], %fsr		! restore %fsr, leaving accrued stuff
120*54388Storek
121*54388Storek	/*
122*54388Storek	 * Now insert the original sign in %f4:f5.
123*54388Storek	 * This is a lot of work, so it is conditional here.
124*54388Storek	 */
125*54388Storek	btst	%l1, %i0
126*54388Storek	be	1f
127*54388Storek	nop
128*54388Storek	st	%f4, [%fp - 16]
129*54388Storek	ld	[%fp - 16], %g1
130*54388Storek	or	%l1, %g1, %g1
131*54388Storek	st	%g1, [%fp - 16]
132*54388Storek	ld	[%fp - 16], %f4
133*54388Storek1:
134*54388Storek
135*54388Storek	/*
136*54388Storek	 * The value in %f4:f5 is now the integer portion of the original
137*54388Storek	 * argument.  We need to store this in *ival (%i2), subtract it
138*54388Storek	 * from the original value argument (%i0:i1), and return the result.
139*54388Storek	 */
140*54388Storek	std	%f4, [%i2]		! *ival = %f4:f5;
141*54388Storek	std	%i0, [%fp - 16]
142*54388Storek	ldd	[%fp - 16], %f0		! %f0:f1 = val;
143*54388Storek	fsubd	%f0, %f4, %f0		! %f0:f1 -= %f4:f5;
144*54388Storek	ret
145*54388Storek	restore
146*54388Storek
147*54388StorekLbig:
148*54388Storek	/*
149*54388Storek	 * We get here if the original comparison of %f4:f5 (v) to
150*54388Storek	 * %f2:f3 (2^52) came out `greater or unordered'.  In this
151*54388Storek	 * case the integer part is the original value, and the
152*54388Storek	 * fractional part is 0.
153*54388Storek	 */
154*54388Storek	sethi	%hi(L0), %l0
155*54388Storek	std	%f0, [%i2]		! *ival = val;
156*54388Storek	ldd	[%l0 + %lo(L0)], %f0	! return 0.0;
157*54388Storek	ret
158*54388Storek	restore
159