xref: /openbmc/linux/arch/m68k/math-emu/fp_arith.c (revision 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2)
1*1da177e4SLinus Torvalds /*
2*1da177e4SLinus Torvalds 
3*1da177e4SLinus Torvalds    fp_arith.c: floating-point math routines for the Linux-m68k
4*1da177e4SLinus Torvalds    floating point emulator.
5*1da177e4SLinus Torvalds 
6*1da177e4SLinus Torvalds    Copyright (c) 1998-1999 David Huggins-Daines.
7*1da177e4SLinus Torvalds 
8*1da177e4SLinus Torvalds    Somewhat based on the AlphaLinux floating point emulator, by David
9*1da177e4SLinus Torvalds    Mosberger-Tang.
10*1da177e4SLinus Torvalds 
11*1da177e4SLinus Torvalds    You may copy, modify, and redistribute this file under the terms of
12*1da177e4SLinus Torvalds    the GNU General Public License, version 2, or any later version, at
13*1da177e4SLinus Torvalds    your convenience.
14*1da177e4SLinus Torvalds  */
15*1da177e4SLinus Torvalds 
16*1da177e4SLinus Torvalds #include "fp_emu.h"
17*1da177e4SLinus Torvalds #include "multi_arith.h"
18*1da177e4SLinus Torvalds #include "fp_arith.h"
19*1da177e4SLinus Torvalds 
20*1da177e4SLinus Torvalds const struct fp_ext fp_QNaN =
21*1da177e4SLinus Torvalds {
22*1da177e4SLinus Torvalds 	.exp = 0x7fff,
23*1da177e4SLinus Torvalds 	.mant = { .m64 = ~0 }
24*1da177e4SLinus Torvalds };
25*1da177e4SLinus Torvalds 
26*1da177e4SLinus Torvalds const struct fp_ext fp_Inf =
27*1da177e4SLinus Torvalds {
28*1da177e4SLinus Torvalds 	.exp = 0x7fff,
29*1da177e4SLinus Torvalds };
30*1da177e4SLinus Torvalds 
31*1da177e4SLinus Torvalds /* let's start with the easy ones */
32*1da177e4SLinus Torvalds 
33*1da177e4SLinus Torvalds struct fp_ext *
34*1da177e4SLinus Torvalds fp_fabs(struct fp_ext *dest, struct fp_ext *src)
35*1da177e4SLinus Torvalds {
36*1da177e4SLinus Torvalds 	dprint(PINSTR, "fabs\n");
37*1da177e4SLinus Torvalds 
38*1da177e4SLinus Torvalds 	fp_monadic_check(dest, src);
39*1da177e4SLinus Torvalds 
40*1da177e4SLinus Torvalds 	dest->sign = 0;
41*1da177e4SLinus Torvalds 
42*1da177e4SLinus Torvalds 	return dest;
43*1da177e4SLinus Torvalds }
44*1da177e4SLinus Torvalds 
45*1da177e4SLinus Torvalds struct fp_ext *
46*1da177e4SLinus Torvalds fp_fneg(struct fp_ext *dest, struct fp_ext *src)
47*1da177e4SLinus Torvalds {
48*1da177e4SLinus Torvalds 	dprint(PINSTR, "fneg\n");
49*1da177e4SLinus Torvalds 
50*1da177e4SLinus Torvalds 	fp_monadic_check(dest, src);
51*1da177e4SLinus Torvalds 
52*1da177e4SLinus Torvalds 	dest->sign = !dest->sign;
53*1da177e4SLinus Torvalds 
54*1da177e4SLinus Torvalds 	return dest;
55*1da177e4SLinus Torvalds }
56*1da177e4SLinus Torvalds 
57*1da177e4SLinus Torvalds /* Now, the slightly harder ones */
58*1da177e4SLinus Torvalds 
59*1da177e4SLinus Torvalds /* fp_fadd: Implements the kernel of the FADD, FSADD, FDADD, FSUB,
60*1da177e4SLinus Torvalds    FDSUB, and FCMP instructions. */
61*1da177e4SLinus Torvalds 
62*1da177e4SLinus Torvalds struct fp_ext *
63*1da177e4SLinus Torvalds fp_fadd(struct fp_ext *dest, struct fp_ext *src)
64*1da177e4SLinus Torvalds {
65*1da177e4SLinus Torvalds 	int diff;
66*1da177e4SLinus Torvalds 
67*1da177e4SLinus Torvalds 	dprint(PINSTR, "fadd\n");
68*1da177e4SLinus Torvalds 
69*1da177e4SLinus Torvalds 	fp_dyadic_check(dest, src);
70*1da177e4SLinus Torvalds 
71*1da177e4SLinus Torvalds 	if (IS_INF(dest)) {
72*1da177e4SLinus Torvalds 		/* infinity - infinity == NaN */
73*1da177e4SLinus Torvalds 		if (IS_INF(src) && (src->sign != dest->sign))
74*1da177e4SLinus Torvalds 			fp_set_nan(dest);
75*1da177e4SLinus Torvalds 		return dest;
76*1da177e4SLinus Torvalds 	}
77*1da177e4SLinus Torvalds 	if (IS_INF(src)) {
78*1da177e4SLinus Torvalds 		fp_copy_ext(dest, src);
79*1da177e4SLinus Torvalds 		return dest;
80*1da177e4SLinus Torvalds 	}
81*1da177e4SLinus Torvalds 
82*1da177e4SLinus Torvalds 	if (IS_ZERO(dest)) {
83*1da177e4SLinus Torvalds 		if (IS_ZERO(src)) {
84*1da177e4SLinus Torvalds 			if (src->sign != dest->sign) {
85*1da177e4SLinus Torvalds 				if (FPDATA->rnd == FPCR_ROUND_RM)
86*1da177e4SLinus Torvalds 					dest->sign = 1;
87*1da177e4SLinus Torvalds 				else
88*1da177e4SLinus Torvalds 					dest->sign = 0;
89*1da177e4SLinus Torvalds 			}
90*1da177e4SLinus Torvalds 		} else
91*1da177e4SLinus Torvalds 			fp_copy_ext(dest, src);
92*1da177e4SLinus Torvalds 		return dest;
93*1da177e4SLinus Torvalds 	}
94*1da177e4SLinus Torvalds 
95*1da177e4SLinus Torvalds 	dest->lowmant = src->lowmant = 0;
96*1da177e4SLinus Torvalds 
97*1da177e4SLinus Torvalds 	if ((diff = dest->exp - src->exp) > 0)
98*1da177e4SLinus Torvalds 		fp_denormalize(src, diff);
99*1da177e4SLinus Torvalds 	else if ((diff = -diff) > 0)
100*1da177e4SLinus Torvalds 		fp_denormalize(dest, diff);
101*1da177e4SLinus Torvalds 
102*1da177e4SLinus Torvalds 	if (dest->sign == src->sign) {
103*1da177e4SLinus Torvalds 		if (fp_addmant(dest, src))
104*1da177e4SLinus Torvalds 			if (!fp_addcarry(dest))
105*1da177e4SLinus Torvalds 				return dest;
106*1da177e4SLinus Torvalds 	} else {
107*1da177e4SLinus Torvalds 		if (dest->mant.m64 < src->mant.m64) {
108*1da177e4SLinus Torvalds 			fp_submant(dest, src, dest);
109*1da177e4SLinus Torvalds 			dest->sign = !dest->sign;
110*1da177e4SLinus Torvalds 		} else
111*1da177e4SLinus Torvalds 			fp_submant(dest, dest, src);
112*1da177e4SLinus Torvalds 	}
113*1da177e4SLinus Torvalds 
114*1da177e4SLinus Torvalds 	return dest;
115*1da177e4SLinus Torvalds }
116*1da177e4SLinus Torvalds 
117*1da177e4SLinus Torvalds /* fp_fsub: Implements the kernel of the FSUB, FSSUB, and FDSUB
118*1da177e4SLinus Torvalds    instructions.
119*1da177e4SLinus Torvalds 
120*1da177e4SLinus Torvalds    Remember that the arguments are in assembler-syntax order! */
121*1da177e4SLinus Torvalds 
122*1da177e4SLinus Torvalds struct fp_ext *
123*1da177e4SLinus Torvalds fp_fsub(struct fp_ext *dest, struct fp_ext *src)
124*1da177e4SLinus Torvalds {
125*1da177e4SLinus Torvalds 	dprint(PINSTR, "fsub ");
126*1da177e4SLinus Torvalds 
127*1da177e4SLinus Torvalds 	src->sign = !src->sign;
128*1da177e4SLinus Torvalds 	return fp_fadd(dest, src);
129*1da177e4SLinus Torvalds }
130*1da177e4SLinus Torvalds 
131*1da177e4SLinus Torvalds 
132*1da177e4SLinus Torvalds struct fp_ext *
133*1da177e4SLinus Torvalds fp_fcmp(struct fp_ext *dest, struct fp_ext *src)
134*1da177e4SLinus Torvalds {
135*1da177e4SLinus Torvalds 	dprint(PINSTR, "fcmp ");
136*1da177e4SLinus Torvalds 
137*1da177e4SLinus Torvalds 	FPDATA->temp[1] = *dest;
138*1da177e4SLinus Torvalds 	src->sign = !src->sign;
139*1da177e4SLinus Torvalds 	return fp_fadd(&FPDATA->temp[1], src);
140*1da177e4SLinus Torvalds }
141*1da177e4SLinus Torvalds 
142*1da177e4SLinus Torvalds struct fp_ext *
143*1da177e4SLinus Torvalds fp_ftst(struct fp_ext *dest, struct fp_ext *src)
144*1da177e4SLinus Torvalds {
145*1da177e4SLinus Torvalds 	dprint(PINSTR, "ftst\n");
146*1da177e4SLinus Torvalds 
147*1da177e4SLinus Torvalds 	(void)dest;
148*1da177e4SLinus Torvalds 
149*1da177e4SLinus Torvalds 	return src;
150*1da177e4SLinus Torvalds }
151*1da177e4SLinus Torvalds 
152*1da177e4SLinus Torvalds struct fp_ext *
153*1da177e4SLinus Torvalds fp_fmul(struct fp_ext *dest, struct fp_ext *src)
154*1da177e4SLinus Torvalds {
155*1da177e4SLinus Torvalds 	union fp_mant128 temp;
156*1da177e4SLinus Torvalds 	int exp;
157*1da177e4SLinus Torvalds 
158*1da177e4SLinus Torvalds 	dprint(PINSTR, "fmul\n");
159*1da177e4SLinus Torvalds 
160*1da177e4SLinus Torvalds 	fp_dyadic_check(dest, src);
161*1da177e4SLinus Torvalds 
162*1da177e4SLinus Torvalds 	/* calculate the correct sign now, as it's necessary for infinities */
163*1da177e4SLinus Torvalds 	dest->sign = src->sign ^ dest->sign;
164*1da177e4SLinus Torvalds 
165*1da177e4SLinus Torvalds 	/* Handle infinities */
166*1da177e4SLinus Torvalds 	if (IS_INF(dest)) {
167*1da177e4SLinus Torvalds 		if (IS_ZERO(src))
168*1da177e4SLinus Torvalds 			fp_set_nan(dest);
169*1da177e4SLinus Torvalds 		return dest;
170*1da177e4SLinus Torvalds 	}
171*1da177e4SLinus Torvalds 	if (IS_INF(src)) {
172*1da177e4SLinus Torvalds 		if (IS_ZERO(dest))
173*1da177e4SLinus Torvalds 			fp_set_nan(dest);
174*1da177e4SLinus Torvalds 		else
175*1da177e4SLinus Torvalds 			fp_copy_ext(dest, src);
176*1da177e4SLinus Torvalds 		return dest;
177*1da177e4SLinus Torvalds 	}
178*1da177e4SLinus Torvalds 
179*1da177e4SLinus Torvalds 	/* Of course, as we all know, zero * anything = zero.  You may
180*1da177e4SLinus Torvalds 	   not have known that it might be a positive or negative
181*1da177e4SLinus Torvalds 	   zero... */
182*1da177e4SLinus Torvalds 	if (IS_ZERO(dest) || IS_ZERO(src)) {
183*1da177e4SLinus Torvalds 		dest->exp = 0;
184*1da177e4SLinus Torvalds 		dest->mant.m64 = 0;
185*1da177e4SLinus Torvalds 		dest->lowmant = 0;
186*1da177e4SLinus Torvalds 
187*1da177e4SLinus Torvalds 		return dest;
188*1da177e4SLinus Torvalds 	}
189*1da177e4SLinus Torvalds 
190*1da177e4SLinus Torvalds 	exp = dest->exp + src->exp - 0x3ffe;
191*1da177e4SLinus Torvalds 
192*1da177e4SLinus Torvalds 	/* shift up the mantissa for denormalized numbers,
193*1da177e4SLinus Torvalds 	   so that the highest bit is set, this makes the
194*1da177e4SLinus Torvalds 	   shift of the result below easier */
195*1da177e4SLinus Torvalds 	if ((long)dest->mant.m32[0] >= 0)
196*1da177e4SLinus Torvalds 		exp -= fp_overnormalize(dest);
197*1da177e4SLinus Torvalds 	if ((long)src->mant.m32[0] >= 0)
198*1da177e4SLinus Torvalds 		exp -= fp_overnormalize(src);
199*1da177e4SLinus Torvalds 
200*1da177e4SLinus Torvalds 	/* now, do a 64-bit multiply with expansion */
201*1da177e4SLinus Torvalds 	fp_multiplymant(&temp, dest, src);
202*1da177e4SLinus Torvalds 
203*1da177e4SLinus Torvalds 	/* normalize it back to 64 bits and stuff it back into the
204*1da177e4SLinus Torvalds 	   destination struct */
205*1da177e4SLinus Torvalds 	if ((long)temp.m32[0] > 0) {
206*1da177e4SLinus Torvalds 		exp--;
207*1da177e4SLinus Torvalds 		fp_putmant128(dest, &temp, 1);
208*1da177e4SLinus Torvalds 	} else
209*1da177e4SLinus Torvalds 		fp_putmant128(dest, &temp, 0);
210*1da177e4SLinus Torvalds 
211*1da177e4SLinus Torvalds 	if (exp >= 0x7fff) {
212*1da177e4SLinus Torvalds 		fp_set_ovrflw(dest);
213*1da177e4SLinus Torvalds 		return dest;
214*1da177e4SLinus Torvalds 	}
215*1da177e4SLinus Torvalds 	dest->exp = exp;
216*1da177e4SLinus Torvalds 	if (exp < 0) {
217*1da177e4SLinus Torvalds 		fp_set_sr(FPSR_EXC_UNFL);
218*1da177e4SLinus Torvalds 		fp_denormalize(dest, -exp);
219*1da177e4SLinus Torvalds 	}
220*1da177e4SLinus Torvalds 
221*1da177e4SLinus Torvalds 	return dest;
222*1da177e4SLinus Torvalds }
223*1da177e4SLinus Torvalds 
224*1da177e4SLinus Torvalds /* fp_fdiv: Implements the "kernel" of the FDIV, FSDIV, FDDIV and
225*1da177e4SLinus Torvalds    FSGLDIV instructions.
226*1da177e4SLinus Torvalds 
227*1da177e4SLinus Torvalds    Note that the order of the operands is counter-intuitive: instead
228*1da177e4SLinus Torvalds    of src / dest, the result is actually dest / src. */
229*1da177e4SLinus Torvalds 
230*1da177e4SLinus Torvalds struct fp_ext *
231*1da177e4SLinus Torvalds fp_fdiv(struct fp_ext *dest, struct fp_ext *src)
232*1da177e4SLinus Torvalds {
233*1da177e4SLinus Torvalds 	union fp_mant128 temp;
234*1da177e4SLinus Torvalds 	int exp;
235*1da177e4SLinus Torvalds 
236*1da177e4SLinus Torvalds 	dprint(PINSTR, "fdiv\n");
237*1da177e4SLinus Torvalds 
238*1da177e4SLinus Torvalds 	fp_dyadic_check(dest, src);
239*1da177e4SLinus Torvalds 
240*1da177e4SLinus Torvalds 	/* calculate the correct sign now, as it's necessary for infinities */
241*1da177e4SLinus Torvalds 	dest->sign = src->sign ^ dest->sign;
242*1da177e4SLinus Torvalds 
243*1da177e4SLinus Torvalds 	/* Handle infinities */
244*1da177e4SLinus Torvalds 	if (IS_INF(dest)) {
245*1da177e4SLinus Torvalds 		/* infinity / infinity = NaN (quiet, as always) */
246*1da177e4SLinus Torvalds 		if (IS_INF(src))
247*1da177e4SLinus Torvalds 			fp_set_nan(dest);
248*1da177e4SLinus Torvalds 		/* infinity / anything else = infinity (with approprate sign) */
249*1da177e4SLinus Torvalds 		return dest;
250*1da177e4SLinus Torvalds 	}
251*1da177e4SLinus Torvalds 	if (IS_INF(src)) {
252*1da177e4SLinus Torvalds 		/* anything / infinity = zero (with appropriate sign) */
253*1da177e4SLinus Torvalds 		dest->exp = 0;
254*1da177e4SLinus Torvalds 		dest->mant.m64 = 0;
255*1da177e4SLinus Torvalds 		dest->lowmant = 0;
256*1da177e4SLinus Torvalds 
257*1da177e4SLinus Torvalds 		return dest;
258*1da177e4SLinus Torvalds 	}
259*1da177e4SLinus Torvalds 
260*1da177e4SLinus Torvalds 	/* zeroes */
261*1da177e4SLinus Torvalds 	if (IS_ZERO(dest)) {
262*1da177e4SLinus Torvalds 		/* zero / zero = NaN */
263*1da177e4SLinus Torvalds 		if (IS_ZERO(src))
264*1da177e4SLinus Torvalds 			fp_set_nan(dest);
265*1da177e4SLinus Torvalds 		/* zero / anything else = zero */
266*1da177e4SLinus Torvalds 		return dest;
267*1da177e4SLinus Torvalds 	}
268*1da177e4SLinus Torvalds 	if (IS_ZERO(src)) {
269*1da177e4SLinus Torvalds 		/* anything / zero = infinity (with appropriate sign) */
270*1da177e4SLinus Torvalds 		fp_set_sr(FPSR_EXC_DZ);
271*1da177e4SLinus Torvalds 		dest->exp = 0x7fff;
272*1da177e4SLinus Torvalds 		dest->mant.m64 = 0;
273*1da177e4SLinus Torvalds 
274*1da177e4SLinus Torvalds 		return dest;
275*1da177e4SLinus Torvalds 	}
276*1da177e4SLinus Torvalds 
277*1da177e4SLinus Torvalds 	exp = dest->exp - src->exp + 0x3fff;
278*1da177e4SLinus Torvalds 
279*1da177e4SLinus Torvalds 	/* shift up the mantissa for denormalized numbers,
280*1da177e4SLinus Torvalds 	   so that the highest bit is set, this makes lots
281*1da177e4SLinus Torvalds 	   of things below easier */
282*1da177e4SLinus Torvalds 	if ((long)dest->mant.m32[0] >= 0)
283*1da177e4SLinus Torvalds 		exp -= fp_overnormalize(dest);
284*1da177e4SLinus Torvalds 	if ((long)src->mant.m32[0] >= 0)
285*1da177e4SLinus Torvalds 		exp -= fp_overnormalize(src);
286*1da177e4SLinus Torvalds 
287*1da177e4SLinus Torvalds 	/* now, do the 64-bit divide */
288*1da177e4SLinus Torvalds 	fp_dividemant(&temp, dest, src);
289*1da177e4SLinus Torvalds 
290*1da177e4SLinus Torvalds 	/* normalize it back to 64 bits and stuff it back into the
291*1da177e4SLinus Torvalds 	   destination struct */
292*1da177e4SLinus Torvalds 	if (!temp.m32[0]) {
293*1da177e4SLinus Torvalds 		exp--;
294*1da177e4SLinus Torvalds 		fp_putmant128(dest, &temp, 32);
295*1da177e4SLinus Torvalds 	} else
296*1da177e4SLinus Torvalds 		fp_putmant128(dest, &temp, 31);
297*1da177e4SLinus Torvalds 
298*1da177e4SLinus Torvalds 	if (exp >= 0x7fff) {
299*1da177e4SLinus Torvalds 		fp_set_ovrflw(dest);
300*1da177e4SLinus Torvalds 		return dest;
301*1da177e4SLinus Torvalds 	}
302*1da177e4SLinus Torvalds 	dest->exp = exp;
303*1da177e4SLinus Torvalds 	if (exp < 0) {
304*1da177e4SLinus Torvalds 		fp_set_sr(FPSR_EXC_UNFL);
305*1da177e4SLinus Torvalds 		fp_denormalize(dest, -exp);
306*1da177e4SLinus Torvalds 	}
307*1da177e4SLinus Torvalds 
308*1da177e4SLinus Torvalds 	return dest;
309*1da177e4SLinus Torvalds }
310*1da177e4SLinus Torvalds 
311*1da177e4SLinus Torvalds struct fp_ext *
312*1da177e4SLinus Torvalds fp_fsglmul(struct fp_ext *dest, struct fp_ext *src)
313*1da177e4SLinus Torvalds {
314*1da177e4SLinus Torvalds 	int exp;
315*1da177e4SLinus Torvalds 
316*1da177e4SLinus Torvalds 	dprint(PINSTR, "fsglmul\n");
317*1da177e4SLinus Torvalds 
318*1da177e4SLinus Torvalds 	fp_dyadic_check(dest, src);
319*1da177e4SLinus Torvalds 
320*1da177e4SLinus Torvalds 	/* calculate the correct sign now, as it's necessary for infinities */
321*1da177e4SLinus Torvalds 	dest->sign = src->sign ^ dest->sign;
322*1da177e4SLinus Torvalds 
323*1da177e4SLinus Torvalds 	/* Handle infinities */
324*1da177e4SLinus Torvalds 	if (IS_INF(dest)) {
325*1da177e4SLinus Torvalds 		if (IS_ZERO(src))
326*1da177e4SLinus Torvalds 			fp_set_nan(dest);
327*1da177e4SLinus Torvalds 		return dest;
328*1da177e4SLinus Torvalds 	}
329*1da177e4SLinus Torvalds 	if (IS_INF(src)) {
330*1da177e4SLinus Torvalds 		if (IS_ZERO(dest))
331*1da177e4SLinus Torvalds 			fp_set_nan(dest);
332*1da177e4SLinus Torvalds 		else
333*1da177e4SLinus Torvalds 			fp_copy_ext(dest, src);
334*1da177e4SLinus Torvalds 		return dest;
335*1da177e4SLinus Torvalds 	}
336*1da177e4SLinus Torvalds 
337*1da177e4SLinus Torvalds 	/* Of course, as we all know, zero * anything = zero.  You may
338*1da177e4SLinus Torvalds 	   not have known that it might be a positive or negative
339*1da177e4SLinus Torvalds 	   zero... */
340*1da177e4SLinus Torvalds 	if (IS_ZERO(dest) || IS_ZERO(src)) {
341*1da177e4SLinus Torvalds 		dest->exp = 0;
342*1da177e4SLinus Torvalds 		dest->mant.m64 = 0;
343*1da177e4SLinus Torvalds 		dest->lowmant = 0;
344*1da177e4SLinus Torvalds 
345*1da177e4SLinus Torvalds 		return dest;
346*1da177e4SLinus Torvalds 	}
347*1da177e4SLinus Torvalds 
348*1da177e4SLinus Torvalds 	exp = dest->exp + src->exp - 0x3ffe;
349*1da177e4SLinus Torvalds 
350*1da177e4SLinus Torvalds 	/* do a 32-bit multiply */
351*1da177e4SLinus Torvalds 	fp_mul64(dest->mant.m32[0], dest->mant.m32[1],
352*1da177e4SLinus Torvalds 		 dest->mant.m32[0] & 0xffffff00,
353*1da177e4SLinus Torvalds 		 src->mant.m32[0] & 0xffffff00);
354*1da177e4SLinus Torvalds 
355*1da177e4SLinus Torvalds 	if (exp >= 0x7fff) {
356*1da177e4SLinus Torvalds 		fp_set_ovrflw(dest);
357*1da177e4SLinus Torvalds 		return dest;
358*1da177e4SLinus Torvalds 	}
359*1da177e4SLinus Torvalds 	dest->exp = exp;
360*1da177e4SLinus Torvalds 	if (exp < 0) {
361*1da177e4SLinus Torvalds 		fp_set_sr(FPSR_EXC_UNFL);
362*1da177e4SLinus Torvalds 		fp_denormalize(dest, -exp);
363*1da177e4SLinus Torvalds 	}
364*1da177e4SLinus Torvalds 
365*1da177e4SLinus Torvalds 	return dest;
366*1da177e4SLinus Torvalds }
367*1da177e4SLinus Torvalds 
368*1da177e4SLinus Torvalds struct fp_ext *
369*1da177e4SLinus Torvalds fp_fsgldiv(struct fp_ext *dest, struct fp_ext *src)
370*1da177e4SLinus Torvalds {
371*1da177e4SLinus Torvalds 	int exp;
372*1da177e4SLinus Torvalds 	unsigned long quot, rem;
373*1da177e4SLinus Torvalds 
374*1da177e4SLinus Torvalds 	dprint(PINSTR, "fsgldiv\n");
375*1da177e4SLinus Torvalds 
376*1da177e4SLinus Torvalds 	fp_dyadic_check(dest, src);
377*1da177e4SLinus Torvalds 
378*1da177e4SLinus Torvalds 	/* calculate the correct sign now, as it's necessary for infinities */
379*1da177e4SLinus Torvalds 	dest->sign = src->sign ^ dest->sign;
380*1da177e4SLinus Torvalds 
381*1da177e4SLinus Torvalds 	/* Handle infinities */
382*1da177e4SLinus Torvalds 	if (IS_INF(dest)) {
383*1da177e4SLinus Torvalds 		/* infinity / infinity = NaN (quiet, as always) */
384*1da177e4SLinus Torvalds 		if (IS_INF(src))
385*1da177e4SLinus Torvalds 			fp_set_nan(dest);
386*1da177e4SLinus Torvalds 		/* infinity / anything else = infinity (with approprate sign) */
387*1da177e4SLinus Torvalds 		return dest;
388*1da177e4SLinus Torvalds 	}
389*1da177e4SLinus Torvalds 	if (IS_INF(src)) {
390*1da177e4SLinus Torvalds 		/* anything / infinity = zero (with appropriate sign) */
391*1da177e4SLinus Torvalds 		dest->exp = 0;
392*1da177e4SLinus Torvalds 		dest->mant.m64 = 0;
393*1da177e4SLinus Torvalds 		dest->lowmant = 0;
394*1da177e4SLinus Torvalds 
395*1da177e4SLinus Torvalds 		return dest;
396*1da177e4SLinus Torvalds 	}
397*1da177e4SLinus Torvalds 
398*1da177e4SLinus Torvalds 	/* zeroes */
399*1da177e4SLinus Torvalds 	if (IS_ZERO(dest)) {
400*1da177e4SLinus Torvalds 		/* zero / zero = NaN */
401*1da177e4SLinus Torvalds 		if (IS_ZERO(src))
402*1da177e4SLinus Torvalds 			fp_set_nan(dest);
403*1da177e4SLinus Torvalds 		/* zero / anything else = zero */
404*1da177e4SLinus Torvalds 		return dest;
405*1da177e4SLinus Torvalds 	}
406*1da177e4SLinus Torvalds 	if (IS_ZERO(src)) {
407*1da177e4SLinus Torvalds 		/* anything / zero = infinity (with appropriate sign) */
408*1da177e4SLinus Torvalds 		fp_set_sr(FPSR_EXC_DZ);
409*1da177e4SLinus Torvalds 		dest->exp = 0x7fff;
410*1da177e4SLinus Torvalds 		dest->mant.m64 = 0;
411*1da177e4SLinus Torvalds 
412*1da177e4SLinus Torvalds 		return dest;
413*1da177e4SLinus Torvalds 	}
414*1da177e4SLinus Torvalds 
415*1da177e4SLinus Torvalds 	exp = dest->exp - src->exp + 0x3fff;
416*1da177e4SLinus Torvalds 
417*1da177e4SLinus Torvalds 	dest->mant.m32[0] &= 0xffffff00;
418*1da177e4SLinus Torvalds 	src->mant.m32[0] &= 0xffffff00;
419*1da177e4SLinus Torvalds 
420*1da177e4SLinus Torvalds 	/* do the 32-bit divide */
421*1da177e4SLinus Torvalds 	if (dest->mant.m32[0] >= src->mant.m32[0]) {
422*1da177e4SLinus Torvalds 		fp_sub64(dest->mant, src->mant);
423*1da177e4SLinus Torvalds 		fp_div64(quot, rem, dest->mant.m32[0], 0, src->mant.m32[0]);
424*1da177e4SLinus Torvalds 		dest->mant.m32[0] = 0x80000000 | (quot >> 1);
425*1da177e4SLinus Torvalds 		dest->mant.m32[1] = (quot & 1) | rem;	/* only for rounding */
426*1da177e4SLinus Torvalds 	} else {
427*1da177e4SLinus Torvalds 		fp_div64(quot, rem, dest->mant.m32[0], 0, src->mant.m32[0]);
428*1da177e4SLinus Torvalds 		dest->mant.m32[0] = quot;
429*1da177e4SLinus Torvalds 		dest->mant.m32[1] = rem;		/* only for rounding */
430*1da177e4SLinus Torvalds 		exp--;
431*1da177e4SLinus Torvalds 	}
432*1da177e4SLinus Torvalds 
433*1da177e4SLinus Torvalds 	if (exp >= 0x7fff) {
434*1da177e4SLinus Torvalds 		fp_set_ovrflw(dest);
435*1da177e4SLinus Torvalds 		return dest;
436*1da177e4SLinus Torvalds 	}
437*1da177e4SLinus Torvalds 	dest->exp = exp;
438*1da177e4SLinus Torvalds 	if (exp < 0) {
439*1da177e4SLinus Torvalds 		fp_set_sr(FPSR_EXC_UNFL);
440*1da177e4SLinus Torvalds 		fp_denormalize(dest, -exp);
441*1da177e4SLinus Torvalds 	}
442*1da177e4SLinus Torvalds 
443*1da177e4SLinus Torvalds 	return dest;
444*1da177e4SLinus Torvalds }
445*1da177e4SLinus Torvalds 
446*1da177e4SLinus Torvalds /* fp_roundint: Internal rounding function for use by several of these
447*1da177e4SLinus Torvalds    emulated instructions.
448*1da177e4SLinus Torvalds 
449*1da177e4SLinus Torvalds    This one rounds off the fractional part using the rounding mode
450*1da177e4SLinus Torvalds    specified. */
451*1da177e4SLinus Torvalds 
452*1da177e4SLinus Torvalds static void fp_roundint(struct fp_ext *dest, int mode)
453*1da177e4SLinus Torvalds {
454*1da177e4SLinus Torvalds 	union fp_mant64 oldmant;
455*1da177e4SLinus Torvalds 	unsigned long mask;
456*1da177e4SLinus Torvalds 
457*1da177e4SLinus Torvalds 	if (!fp_normalize_ext(dest))
458*1da177e4SLinus Torvalds 		return;
459*1da177e4SLinus Torvalds 
460*1da177e4SLinus Torvalds 	/* infinities and zeroes */
461*1da177e4SLinus Torvalds 	if (IS_INF(dest) || IS_ZERO(dest))
462*1da177e4SLinus Torvalds 		return;
463*1da177e4SLinus Torvalds 
464*1da177e4SLinus Torvalds 	/* first truncate the lower bits */
465*1da177e4SLinus Torvalds 	oldmant = dest->mant;
466*1da177e4SLinus Torvalds 	switch (dest->exp) {
467*1da177e4SLinus Torvalds 	case 0 ... 0x3ffe:
468*1da177e4SLinus Torvalds 		dest->mant.m64 = 0;
469*1da177e4SLinus Torvalds 		break;
470*1da177e4SLinus Torvalds 	case 0x3fff ... 0x401e:
471*1da177e4SLinus Torvalds 		dest->mant.m32[0] &= 0xffffffffU << (0x401e - dest->exp);
472*1da177e4SLinus Torvalds 		dest->mant.m32[1] = 0;
473*1da177e4SLinus Torvalds 		if (oldmant.m64 == dest->mant.m64)
474*1da177e4SLinus Torvalds 			return;
475*1da177e4SLinus Torvalds 		break;
476*1da177e4SLinus Torvalds 	case 0x401f ... 0x403e:
477*1da177e4SLinus Torvalds 		dest->mant.m32[1] &= 0xffffffffU << (0x403e - dest->exp);
478*1da177e4SLinus Torvalds 		if (oldmant.m32[1] == dest->mant.m32[1])
479*1da177e4SLinus Torvalds 			return;
480*1da177e4SLinus Torvalds 		break;
481*1da177e4SLinus Torvalds 	default:
482*1da177e4SLinus Torvalds 		return;
483*1da177e4SLinus Torvalds 	}
484*1da177e4SLinus Torvalds 	fp_set_sr(FPSR_EXC_INEX2);
485*1da177e4SLinus Torvalds 
486*1da177e4SLinus Torvalds 	/* We might want to normalize upwards here... however, since
487*1da177e4SLinus Torvalds 	   we know that this is only called on the output of fp_fdiv,
488*1da177e4SLinus Torvalds 	   or with the input to fp_fint or fp_fintrz, and the inputs
489*1da177e4SLinus Torvalds 	   to all these functions are either normal or denormalized
490*1da177e4SLinus Torvalds 	   (no subnormals allowed!), there's really no need.
491*1da177e4SLinus Torvalds 
492*1da177e4SLinus Torvalds 	   In the case of fp_fdiv, observe that 0x80000000 / 0xffff =
493*1da177e4SLinus Torvalds 	   0xffff8000, and the same holds for 128-bit / 64-bit. (i.e. the
494*1da177e4SLinus Torvalds 	   smallest possible normal dividend and the largest possible normal
495*1da177e4SLinus Torvalds 	   divisor will still produce a normal quotient, therefore, (normal
496*1da177e4SLinus Torvalds 	   << 64) / normal is normal in all cases) */
497*1da177e4SLinus Torvalds 
498*1da177e4SLinus Torvalds 	switch (mode) {
499*1da177e4SLinus Torvalds 	case FPCR_ROUND_RN:
500*1da177e4SLinus Torvalds 		switch (dest->exp) {
501*1da177e4SLinus Torvalds 		case 0 ... 0x3ffd:
502*1da177e4SLinus Torvalds 			return;
503*1da177e4SLinus Torvalds 		case 0x3ffe:
504*1da177e4SLinus Torvalds 			/* As noted above, the input is always normal, so the
505*1da177e4SLinus Torvalds 			   guard bit (bit 63) is always set.  therefore, the
506*1da177e4SLinus Torvalds 			   only case in which we will NOT round to 1.0 is when
507*1da177e4SLinus Torvalds 			   the input is exactly 0.5. */
508*1da177e4SLinus Torvalds 			if (oldmant.m64 == (1ULL << 63))
509*1da177e4SLinus Torvalds 				return;
510*1da177e4SLinus Torvalds 			break;
511*1da177e4SLinus Torvalds 		case 0x3fff ... 0x401d:
512*1da177e4SLinus Torvalds 			mask = 1 << (0x401d - dest->exp);
513*1da177e4SLinus Torvalds 			if (!(oldmant.m32[0] & mask))
514*1da177e4SLinus Torvalds 				return;
515*1da177e4SLinus Torvalds 			if (oldmant.m32[0] & (mask << 1))
516*1da177e4SLinus Torvalds 				break;
517*1da177e4SLinus Torvalds 			if (!(oldmant.m32[0] << (dest->exp - 0x3ffd)) &&
518*1da177e4SLinus Torvalds 					!oldmant.m32[1])
519*1da177e4SLinus Torvalds 				return;
520*1da177e4SLinus Torvalds 			break;
521*1da177e4SLinus Torvalds 		case 0x401e:
522*1da177e4SLinus Torvalds 			if (!(oldmant.m32[1] >= 0))
523*1da177e4SLinus Torvalds 				return;
524*1da177e4SLinus Torvalds 			if (oldmant.m32[0] & 1)
525*1da177e4SLinus Torvalds 				break;
526*1da177e4SLinus Torvalds 			if (!(oldmant.m32[1] << 1))
527*1da177e4SLinus Torvalds 				return;
528*1da177e4SLinus Torvalds 			break;
529*1da177e4SLinus Torvalds 		case 0x401f ... 0x403d:
530*1da177e4SLinus Torvalds 			mask = 1 << (0x403d - dest->exp);
531*1da177e4SLinus Torvalds 			if (!(oldmant.m32[1] & mask))
532*1da177e4SLinus Torvalds 				return;
533*1da177e4SLinus Torvalds 			if (oldmant.m32[1] & (mask << 1))
534*1da177e4SLinus Torvalds 				break;
535*1da177e4SLinus Torvalds 			if (!(oldmant.m32[1] << (dest->exp - 0x401d)))
536*1da177e4SLinus Torvalds 				return;
537*1da177e4SLinus Torvalds 			break;
538*1da177e4SLinus Torvalds 		default:
539*1da177e4SLinus Torvalds 			return;
540*1da177e4SLinus Torvalds 		}
541*1da177e4SLinus Torvalds 		break;
542*1da177e4SLinus Torvalds 	case FPCR_ROUND_RZ:
543*1da177e4SLinus Torvalds 		return;
544*1da177e4SLinus Torvalds 	default:
545*1da177e4SLinus Torvalds 		if (dest->sign ^ (mode - FPCR_ROUND_RM))
546*1da177e4SLinus Torvalds 			break;
547*1da177e4SLinus Torvalds 		return;
548*1da177e4SLinus Torvalds 	}
549*1da177e4SLinus Torvalds 
550*1da177e4SLinus Torvalds 	switch (dest->exp) {
551*1da177e4SLinus Torvalds 	case 0 ... 0x3ffe:
552*1da177e4SLinus Torvalds 		dest->exp = 0x3fff;
553*1da177e4SLinus Torvalds 		dest->mant.m64 = 1ULL << 63;
554*1da177e4SLinus Torvalds 		break;
555*1da177e4SLinus Torvalds 	case 0x3fff ... 0x401e:
556*1da177e4SLinus Torvalds 		mask = 1 << (0x401e - dest->exp);
557*1da177e4SLinus Torvalds 		if (dest->mant.m32[0] += mask)
558*1da177e4SLinus Torvalds 			break;
559*1da177e4SLinus Torvalds 		dest->mant.m32[0] = 0x80000000;
560*1da177e4SLinus Torvalds 		dest->exp++;
561*1da177e4SLinus Torvalds 		break;
562*1da177e4SLinus Torvalds 	case 0x401f ... 0x403e:
563*1da177e4SLinus Torvalds 		mask = 1 << (0x403e - dest->exp);
564*1da177e4SLinus Torvalds 		if (dest->mant.m32[1] += mask)
565*1da177e4SLinus Torvalds 			break;
566*1da177e4SLinus Torvalds 		if (dest->mant.m32[0] += 1)
567*1da177e4SLinus Torvalds                         break;
568*1da177e4SLinus Torvalds 		dest->mant.m32[0] = 0x80000000;
569*1da177e4SLinus Torvalds                 dest->exp++;
570*1da177e4SLinus Torvalds 		break;
571*1da177e4SLinus Torvalds 	}
572*1da177e4SLinus Torvalds }
573*1da177e4SLinus Torvalds 
574*1da177e4SLinus Torvalds /* modrem_kernel: Implementation of the FREM and FMOD instructions
575*1da177e4SLinus Torvalds    (which are exactly the same, except for the rounding used on the
576*1da177e4SLinus Torvalds    intermediate value) */
577*1da177e4SLinus Torvalds 
578*1da177e4SLinus Torvalds static struct fp_ext *
579*1da177e4SLinus Torvalds modrem_kernel(struct fp_ext *dest, struct fp_ext *src, int mode)
580*1da177e4SLinus Torvalds {
581*1da177e4SLinus Torvalds 	struct fp_ext tmp;
582*1da177e4SLinus Torvalds 
583*1da177e4SLinus Torvalds 	fp_dyadic_check(dest, src);
584*1da177e4SLinus Torvalds 
585*1da177e4SLinus Torvalds 	/* Infinities and zeros */
586*1da177e4SLinus Torvalds 	if (IS_INF(dest) || IS_ZERO(src)) {
587*1da177e4SLinus Torvalds 		fp_set_nan(dest);
588*1da177e4SLinus Torvalds 		return dest;
589*1da177e4SLinus Torvalds 	}
590*1da177e4SLinus Torvalds 	if (IS_ZERO(dest) || IS_INF(src))
591*1da177e4SLinus Torvalds 		return dest;
592*1da177e4SLinus Torvalds 
593*1da177e4SLinus Torvalds 	/* FIXME: there is almost certainly a smarter way to do this */
594*1da177e4SLinus Torvalds 	fp_copy_ext(&tmp, dest);
595*1da177e4SLinus Torvalds 	fp_fdiv(&tmp, src);		/* NOTE: src might be modified */
596*1da177e4SLinus Torvalds 	fp_roundint(&tmp, mode);
597*1da177e4SLinus Torvalds 	fp_fmul(&tmp, src);
598*1da177e4SLinus Torvalds 	fp_fsub(dest, &tmp);
599*1da177e4SLinus Torvalds 
600*1da177e4SLinus Torvalds 	/* set the quotient byte */
601*1da177e4SLinus Torvalds 	fp_set_quotient((dest->mant.m64 & 0x7f) | (dest->sign << 7));
602*1da177e4SLinus Torvalds 	return dest;
603*1da177e4SLinus Torvalds }
604*1da177e4SLinus Torvalds 
605*1da177e4SLinus Torvalds /* fp_fmod: Implements the kernel of the FMOD instruction.
606*1da177e4SLinus Torvalds 
607*1da177e4SLinus Torvalds    Again, the argument order is backwards.  The result, as defined in
608*1da177e4SLinus Torvalds    the Motorola manuals, is:
609*1da177e4SLinus Torvalds 
610*1da177e4SLinus Torvalds    fmod(src,dest) = (dest - (src * floor(dest / src))) */
611*1da177e4SLinus Torvalds 
612*1da177e4SLinus Torvalds struct fp_ext *
613*1da177e4SLinus Torvalds fp_fmod(struct fp_ext *dest, struct fp_ext *src)
614*1da177e4SLinus Torvalds {
615*1da177e4SLinus Torvalds 	dprint(PINSTR, "fmod\n");
616*1da177e4SLinus Torvalds 	return modrem_kernel(dest, src, FPCR_ROUND_RZ);
617*1da177e4SLinus Torvalds }
618*1da177e4SLinus Torvalds 
619*1da177e4SLinus Torvalds /* fp_frem: Implements the kernel of the FREM instruction.
620*1da177e4SLinus Torvalds 
621*1da177e4SLinus Torvalds    frem(src,dest) = (dest - (src * round(dest / src)))
622*1da177e4SLinus Torvalds  */
623*1da177e4SLinus Torvalds 
624*1da177e4SLinus Torvalds struct fp_ext *
625*1da177e4SLinus Torvalds fp_frem(struct fp_ext *dest, struct fp_ext *src)
626*1da177e4SLinus Torvalds {
627*1da177e4SLinus Torvalds 	dprint(PINSTR, "frem\n");
628*1da177e4SLinus Torvalds 	return modrem_kernel(dest, src, FPCR_ROUND_RN);
629*1da177e4SLinus Torvalds }
630*1da177e4SLinus Torvalds 
631*1da177e4SLinus Torvalds struct fp_ext *
632*1da177e4SLinus Torvalds fp_fint(struct fp_ext *dest, struct fp_ext *src)
633*1da177e4SLinus Torvalds {
634*1da177e4SLinus Torvalds 	dprint(PINSTR, "fint\n");
635*1da177e4SLinus Torvalds 
636*1da177e4SLinus Torvalds 	fp_copy_ext(dest, src);
637*1da177e4SLinus Torvalds 
638*1da177e4SLinus Torvalds 	fp_roundint(dest, FPDATA->rnd);
639*1da177e4SLinus Torvalds 
640*1da177e4SLinus Torvalds 	return dest;
641*1da177e4SLinus Torvalds }
642*1da177e4SLinus Torvalds 
643*1da177e4SLinus Torvalds struct fp_ext *
644*1da177e4SLinus Torvalds fp_fintrz(struct fp_ext *dest, struct fp_ext *src)
645*1da177e4SLinus Torvalds {
646*1da177e4SLinus Torvalds 	dprint(PINSTR, "fintrz\n");
647*1da177e4SLinus Torvalds 
648*1da177e4SLinus Torvalds 	fp_copy_ext(dest, src);
649*1da177e4SLinus Torvalds 
650*1da177e4SLinus Torvalds 	fp_roundint(dest, FPCR_ROUND_RZ);
651*1da177e4SLinus Torvalds 
652*1da177e4SLinus Torvalds 	return dest;
653*1da177e4SLinus Torvalds }
654*1da177e4SLinus Torvalds 
655*1da177e4SLinus Torvalds struct fp_ext *
656*1da177e4SLinus Torvalds fp_fscale(struct fp_ext *dest, struct fp_ext *src)
657*1da177e4SLinus Torvalds {
658*1da177e4SLinus Torvalds 	int scale, oldround;
659*1da177e4SLinus Torvalds 
660*1da177e4SLinus Torvalds 	dprint(PINSTR, "fscale\n");
661*1da177e4SLinus Torvalds 
662*1da177e4SLinus Torvalds 	fp_dyadic_check(dest, src);
663*1da177e4SLinus Torvalds 
664*1da177e4SLinus Torvalds 	/* Infinities */
665*1da177e4SLinus Torvalds 	if (IS_INF(src)) {
666*1da177e4SLinus Torvalds 		fp_set_nan(dest);
667*1da177e4SLinus Torvalds 		return dest;
668*1da177e4SLinus Torvalds 	}
669*1da177e4SLinus Torvalds 	if (IS_INF(dest))
670*1da177e4SLinus Torvalds 		return dest;
671*1da177e4SLinus Torvalds 
672*1da177e4SLinus Torvalds 	/* zeroes */
673*1da177e4SLinus Torvalds 	if (IS_ZERO(src) || IS_ZERO(dest))
674*1da177e4SLinus Torvalds 		return dest;
675*1da177e4SLinus Torvalds 
676*1da177e4SLinus Torvalds 	/* Source exponent out of range */
677*1da177e4SLinus Torvalds 	if (src->exp >= 0x400c) {
678*1da177e4SLinus Torvalds 		fp_set_ovrflw(dest);
679*1da177e4SLinus Torvalds 		return dest;
680*1da177e4SLinus Torvalds 	}
681*1da177e4SLinus Torvalds 
682*1da177e4SLinus Torvalds 	/* src must be rounded with round to zero. */
683*1da177e4SLinus Torvalds 	oldround = FPDATA->rnd;
684*1da177e4SLinus Torvalds 	FPDATA->rnd = FPCR_ROUND_RZ;
685*1da177e4SLinus Torvalds 	scale = fp_conv_ext2long(src);
686*1da177e4SLinus Torvalds 	FPDATA->rnd = oldround;
687*1da177e4SLinus Torvalds 
688*1da177e4SLinus Torvalds 	/* new exponent */
689*1da177e4SLinus Torvalds 	scale += dest->exp;
690*1da177e4SLinus Torvalds 
691*1da177e4SLinus Torvalds 	if (scale >= 0x7fff) {
692*1da177e4SLinus Torvalds 		fp_set_ovrflw(dest);
693*1da177e4SLinus Torvalds 	} else if (scale <= 0) {
694*1da177e4SLinus Torvalds 		fp_set_sr(FPSR_EXC_UNFL);
695*1da177e4SLinus Torvalds 		fp_denormalize(dest, -scale);
696*1da177e4SLinus Torvalds 	} else
697*1da177e4SLinus Torvalds 		dest->exp = scale;
698*1da177e4SLinus Torvalds 
699*1da177e4SLinus Torvalds 	return dest;
700*1da177e4SLinus Torvalds }
701*1da177e4SLinus Torvalds 
702