Home | History | Annotate | Line # | Download | only in fpe
fpu_implode.c revision 1.6
      1 /*	$NetBSD: fpu_implode.c,v 1.6 2003/07/15 02:43:10 lukem Exp $ */
      2 
      3 /*
      4  * Copyright (c) 1992, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * This software was developed by the Computer Systems Engineering group
      8  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
      9  * contributed to Berkeley.
     10  *
     11  * All advertising materials mentioning features or use of this software
     12  * must display the following acknowledgement:
     13  *	This product includes software developed by the University of
     14  *	California, Lawrence Berkeley Laboratory.
     15  *
     16  * Redistribution and use in source and binary forms, with or without
     17  * modification, are permitted provided that the following conditions
     18  * are met:
     19  * 1. Redistributions of source code must retain the above copyright
     20  *    notice, this list of conditions and the following disclaimer.
     21  * 2. Redistributions in binary form must reproduce the above copyright
     22  *    notice, this list of conditions and the following disclaimer in the
     23  *    documentation and/or other materials provided with the distribution.
     24  * 3. All advertising materials mentioning features or use of this software
     25  *    must display the following acknowledgement:
     26  *	This product includes software developed by the University of
     27  *	California, Berkeley and its contributors.
     28  * 4. Neither the name of the University nor the names of its contributors
     29  *    may be used to endorse or promote products derived from this software
     30  *    without specific prior written permission.
     31  *
     32  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     33  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     34  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     35  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     36  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     37  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     38  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     39  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     40  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     41  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     42  * SUCH DAMAGE.
     43  *
     44  *	@(#)fpu_implode.c	8.1 (Berkeley) 6/11/93
     45  */
     46 
     47 /*
     48  * FPU subroutines: `implode' internal format numbers into the machine's
     49  * `packed binary' format.
     50  */
     51 
     52 #include <sys/cdefs.h>
     53 __KERNEL_RCSID(0, "$NetBSD: fpu_implode.c,v 1.6 2003/07/15 02:43:10 lukem Exp $");
     54 
     55 #include <sys/types.h>
     56 #include <sys/systm.h>
     57 
     58 #include "ieee.h"
     59 #include <machine/reg.h>
     60 
     61 #include "fpu_emulate.h"
     62 #include "fpu_arith.h"
     63 
     64 /* Conversion from internal format -- note asymmetry. */
     65 static u_int	fpu_ftoi __P((struct fpemu *fe, struct fpn *fp));
     66 static u_int	fpu_ftos __P((struct fpemu *fe, struct fpn *fp));
     67 static u_int	fpu_ftod __P((struct fpemu *fe, struct fpn *fp, u_int *));
     68 static u_int	fpu_ftox __P((struct fpemu *fe, struct fpn *fp, u_int *));
     69 
     70 /*
     71  * Round a number (algorithm from Motorola MC68882 manual, modified for
     72  * our internal format).  Set inexact exception if rounding is required.
     73  * Return true iff we rounded up.
     74  *
     75  * After rounding, we discard the guard and round bits by shifting right
     76  * 2 bits (a la fpu_shr(), but we do not bother with fp->fp_sticky).
     77  * This saves effort later.
     78  *
     79  * Note that we may leave the value 2.0 in fp->fp_mant; it is the caller's
     80  * responsibility to fix this if necessary.
     81  */
     82 int
     83 fpu_round(register struct fpemu *fe, register struct fpn *fp)
     84 {
     85 	register u_int m0, m1, m2;
     86 	register int gr, s;
     87 
     88 	m0 = fp->fp_mant[0];
     89 	m1 = fp->fp_mant[1];
     90 	m2 = fp->fp_mant[2];
     91 	gr = m2 & 3;
     92 	s = fp->fp_sticky;
     93 
     94 	/* mant >>= FP_NG */
     95 	m2 = (m2 >> FP_NG) | (m1 << (32 - FP_NG));
     96 	m1 = (m1 >> FP_NG) | (m0 << (32 - FP_NG));
     97 	m0 >>= FP_NG;
     98 
     99 	if ((gr | s) == 0)	/* result is exact: no rounding needed */
    100 		goto rounddown;
    101 
    102 	fe->fe_fpsr |= FPSR_INEX2;	/* inexact */
    103 
    104 	/* Go to rounddown to round down; break to round up. */
    105 	switch (fe->fe_fpcr & FPCR_ROUND) {
    106 
    107 	case FPCR_NEAR:
    108 	default:
    109 		/*
    110 		 * Round only if guard is set (gr & 2).  If guard is set,
    111 		 * but round & sticky both clear, then we want to round
    112 		 * but have a tie, so round to even, i.e., add 1 iff odd.
    113 		 */
    114 		if ((gr & 2) == 0)
    115 			goto rounddown;
    116 		if ((gr & 1) || fp->fp_sticky || (m2 & 1))
    117 			break;
    118 		goto rounddown;
    119 
    120 	case FPCR_ZERO:
    121 		/* Round towards zero, i.e., down. */
    122 		goto rounddown;
    123 
    124 	case FPCR_MINF:
    125 		/* Round towards -Inf: up if negative, down if positive. */
    126 		if (fp->fp_sign)
    127 			break;
    128 		goto rounddown;
    129 
    130 	case FPCR_PINF:
    131 		/* Round towards +Inf: up if positive, down otherwise. */
    132 		if (!fp->fp_sign)
    133 			break;
    134 		goto rounddown;
    135 	}
    136 
    137 	/* Bump low bit of mantissa, with carry. */
    138 	if (++m2 == 0 && ++m1 == 0)
    139 		m0++;
    140 	fp->fp_sticky = 0;
    141 	fp->fp_mant[0] = m0;
    142 	fp->fp_mant[1] = m1;
    143 	fp->fp_mant[2] = m2;
    144 	return (1);
    145 
    146 rounddown:
    147 	fp->fp_sticky = 0;
    148 	fp->fp_mant[0] = m0;
    149 	fp->fp_mant[1] = m1;
    150 	fp->fp_mant[2] = m2;
    151 	return (0);
    152 }
    153 
    154 /*
    155  * For overflow: return true if overflow is to go to +/-Inf, according
    156  * to the sign of the overflowing result.  If false, overflow is to go
    157  * to the largest magnitude value instead.
    158  */
    159 static int
    160 toinf(struct fpemu *fe, int sign)
    161 {
    162 	int inf;
    163 
    164 	/* look at rounding direction */
    165 	switch (fe->fe_fpcr & FPCR_ROUND) {
    166 
    167 	default:
    168 	case FPCR_NEAR:		/* the nearest value is always Inf */
    169 		inf = 1;
    170 		break;
    171 
    172 	case FPCR_ZERO:		/* toward 0 => never towards Inf */
    173 		inf = 0;
    174 		break;
    175 
    176 	case FPCR_PINF:		/* toward +Inf iff positive */
    177 		inf = (sign == 0);
    178 		break;
    179 
    180 	case FPCR_MINF:		/* toward -Inf iff negative */
    181 		inf = sign;
    182 		break;
    183 	}
    184 	return (inf);
    185 }
    186 
    187 /*
    188  * fpn -> int (int value returned as return value).
    189  *
    190  * N.B.: this conversion always rounds towards zero (this is a peculiarity
    191  * of the SPARC instruction set).
    192  */
    193 static u_int
    194 fpu_ftoi(fe, fp)
    195 	struct fpemu *fe;
    196 	register struct fpn *fp;
    197 {
    198 	register u_int i;
    199 	register int sign, exp;
    200 
    201 	sign = fp->fp_sign;
    202 	switch (fp->fp_class) {
    203 
    204 	case FPC_ZERO:
    205 		return (0);
    206 
    207 	case FPC_NUM:
    208 		/*
    209 		 * If exp >= 2^32, overflow.  Otherwise shift value right
    210 		 * into last mantissa word (this will not exceed 0xffffffff),
    211 		 * shifting any guard and round bits out into the sticky
    212 		 * bit.  Then ``round'' towards zero, i.e., just set an
    213 		 * inexact exception if sticky is set (see fpu_round()).
    214 		 * If the result is > 0x80000000, or is positive and equals
    215 		 * 0x80000000, overflow; otherwise the last fraction word
    216 		 * is the result.
    217 		 */
    218 		if ((exp = fp->fp_exp) >= 32)
    219 			break;
    220 		/* NB: the following includes exp < 0 cases */
    221 		if (fpu_shr(fp, FP_NMANT - 1 - FP_NG - exp) != 0)
    222 			/* m68881/2 do not underflow when
    223 			   converting to integer */;
    224 		fpu_round(fe, fp);
    225 		i = fp->fp_mant[2];
    226 		if (i >= ((u_int)0x80000000 + sign))
    227 			break;
    228 		return (sign ? -i : i);
    229 
    230 	default:		/* Inf, qNaN, sNaN */
    231 		break;
    232 	}
    233 	/* overflow: replace any inexact exception with invalid */
    234 	fe->fe_fpsr = (fe->fe_fpsr & ~FPSR_INEX2) | FPSR_OPERR;
    235 	return (0x7fffffff + sign);
    236 }
    237 
    238 /*
    239  * fpn -> single (32 bit single returned as return value).
    240  * We assume <= 29 bits in a single-precision fraction (1.f part).
    241  */
    242 static u_int
    243 fpu_ftos(fe, fp)
    244 	struct fpemu *fe;
    245 	register struct fpn *fp;
    246 {
    247 	register u_int sign = fp->fp_sign << 31;
    248 	register int exp;
    249 
    250 #define	SNG_EXP(e)	((e) << SNG_FRACBITS)	/* makes e an exponent */
    251 #define	SNG_MASK	(SNG_EXP(1) - 1)	/* mask for fraction */
    252 
    253 	/* Take care of non-numbers first. */
    254 	if (ISNAN(fp)) {
    255 		/*
    256 		 * Preserve upper bits of NaN, per SPARC V8 appendix N.
    257 		 * Note that fp->fp_mant[0] has the quiet bit set,
    258 		 * even if it is classified as a signalling NaN.
    259 		 */
    260 		(void) fpu_shr(fp, FP_NMANT - 1 - SNG_FRACBITS);
    261 		exp = SNG_EXP_INFNAN;
    262 		goto done;
    263 	}
    264 	if (ISINF(fp))
    265 		return (sign | SNG_EXP(SNG_EXP_INFNAN));
    266 	if (ISZERO(fp))
    267 		return (sign);
    268 
    269 	/*
    270 	 * Normals (including subnormals).  Drop all the fraction bits
    271 	 * (including the explicit ``implied'' 1 bit) down into the
    272 	 * single-precision range.  If the number is subnormal, move
    273 	 * the ``implied'' 1 into the explicit range as well, and shift
    274 	 * right to introduce leading zeroes.  Rounding then acts
    275 	 * differently for normals and subnormals: the largest subnormal
    276 	 * may round to the smallest normal (1.0 x 2^minexp), or may
    277 	 * remain subnormal.  In the latter case, signal an underflow
    278 	 * if the result was inexact or if underflow traps are enabled.
    279 	 *
    280 	 * Rounding a normal, on the other hand, always produces another
    281 	 * normal (although either way the result might be too big for
    282 	 * single precision, and cause an overflow).  If rounding a
    283 	 * normal produces 2.0 in the fraction, we need not adjust that
    284 	 * fraction at all, since both 1.0 and 2.0 are zero under the
    285 	 * fraction mask.
    286 	 *
    287 	 * Note that the guard and round bits vanish from the number after
    288 	 * rounding.
    289 	 */
    290 	if ((exp = fp->fp_exp + SNG_EXP_BIAS) <= 0) {	/* subnormal */
    291 		fe->fe_fpsr |= FPSR_UNFL;
    292 		/* -NG for g,r; -SNG_FRACBITS-exp for fraction */
    293 		(void) fpu_shr(fp, FP_NMANT - FP_NG - SNG_FRACBITS - exp);
    294 		if (fpu_round(fe, fp) && fp->fp_mant[2] == SNG_EXP(1))
    295 			return (sign | SNG_EXP(1) | 0);
    296 		if (fe->fe_fpsr & FPSR_INEX2)
    297 			fe->fe_fpsr |= FPSR_UNFL
    298 			/* mc68881/2 don't underflow when converting */;
    299 		return (sign | SNG_EXP(0) | fp->fp_mant[2]);
    300 	}
    301 	/* -FP_NG for g,r; -1 for implied 1; -SNG_FRACBITS for fraction */
    302 	(void) fpu_shr(fp, FP_NMANT - FP_NG - 1 - SNG_FRACBITS);
    303 #ifdef DIAGNOSTIC
    304 	if ((fp->fp_mant[2] & SNG_EXP(1 << FP_NG)) == 0)
    305 		panic("fpu_ftos");
    306 #endif
    307 	if (fpu_round(fe, fp) && fp->fp_mant[2] == SNG_EXP(2))
    308 		exp++;
    309 	if (exp >= SNG_EXP_INFNAN) {
    310 		/* overflow to inf or to max single */
    311 		fe->fe_fpsr |= FPSR_OPERR | FPSR_INEX2 | FPSR_OVFL;
    312 		if (toinf(fe, sign))
    313 			return (sign | SNG_EXP(SNG_EXP_INFNAN));
    314 		return (sign | SNG_EXP(SNG_EXP_INFNAN - 1) | SNG_MASK);
    315 	}
    316 done:
    317 	/* phew, made it */
    318 	return (sign | SNG_EXP(exp) | (fp->fp_mant[2] & SNG_MASK));
    319 }
    320 
    321 /*
    322  * fpn -> double (32 bit high-order result returned; 32-bit low order result
    323  * left in res[1]).  Assumes <= 61 bits in double precision fraction.
    324  *
    325  * This code mimics fpu_ftos; see it for comments.
    326  */
    327 static u_int
    328 fpu_ftod(fe, fp, res)
    329 	struct fpemu *fe;
    330 	register struct fpn *fp;
    331 	u_int *res;
    332 {
    333 	register u_int sign = fp->fp_sign << 31;
    334 	register int exp;
    335 
    336 #define	DBL_EXP(e)	((e) << (DBL_FRACBITS & 31))
    337 #define	DBL_MASK	(DBL_EXP(1) - 1)
    338 
    339 	if (ISNAN(fp)) {
    340 		(void) fpu_shr(fp, FP_NMANT - 1 - DBL_FRACBITS);
    341 		exp = DBL_EXP_INFNAN;
    342 		goto done;
    343 	}
    344 	if (ISINF(fp)) {
    345 		sign |= DBL_EXP(DBL_EXP_INFNAN);
    346 		res[1] = 0;
    347 		return (sign);
    348 	}
    349 	if (ISZERO(fp)) {
    350 		res[1] = 0;
    351 		return (sign);
    352 	}
    353 
    354 	if ((exp = fp->fp_exp + DBL_EXP_BIAS) <= 0) {
    355 		fe->fe_fpsr |= FPSR_UNFL;
    356 		(void) fpu_shr(fp, FP_NMANT - FP_NG - DBL_FRACBITS - exp);
    357 		if (fpu_round(fe, fp) && fp->fp_mant[1] == DBL_EXP(1)) {
    358 			res[1] = 0;
    359 			return (sign | DBL_EXP(1) | 0);
    360 		}
    361 		if (fe->fe_fpsr & FPSR_INEX2)
    362                         fe->fe_fpsr |= FPSR_UNFL
    363 			/* mc68881/2 don't underflow when converting */;
    364 		exp = 0;
    365 		goto done;
    366 	}
    367 	(void) fpu_shr(fp, FP_NMANT - FP_NG - 1 - DBL_FRACBITS);
    368 	if (fpu_round(fe, fp) && fp->fp_mant[1] == DBL_EXP(2))
    369 		exp++;
    370 	if (exp >= DBL_EXP_INFNAN) {
    371 		fe->fe_fpsr |= FPSR_OPERR | FPSR_INEX2 | FPSR_OVFL;
    372 		if (toinf(fe, sign)) {
    373 			res[1] = 0;
    374 			return (sign | DBL_EXP(DBL_EXP_INFNAN) | 0);
    375 		}
    376 		res[1] = ~0;
    377 		return (sign | DBL_EXP(DBL_EXP_INFNAN) | DBL_MASK);
    378 	}
    379 done:
    380 	res[1] = fp->fp_mant[2];
    381 	return (sign | DBL_EXP(exp) | (fp->fp_mant[1] & DBL_MASK));
    382 }
    383 
    384 /*
    385  * fpn -> 68k extended (32 bit high-order result returned; two 32-bit low
    386  * order result left in res[1] & res[2]).  Assumes == 64 bits in extended
    387  * precision fraction.
    388  *
    389  * This code mimics fpu_ftos; see it for comments.
    390  */
    391 static u_int
    392 fpu_ftox(fe, fp, res)
    393 	struct fpemu *fe;
    394 	register struct fpn *fp;
    395 	u_int *res;
    396 {
    397 	register u_int sign = fp->fp_sign << 31;
    398 	register int exp;
    399 
    400 #define	EXT_EXP(e)	((e) << 16)
    401 /*
    402  * on m68k extended prec, significand does not share the same long
    403  * word with exponent
    404  */
    405 #define	EXT_MASK	0
    406 #define EXT_EXPLICIT1	(1UL << (63 & 31))
    407 #define EXT_EXPLICIT2	(1UL << (64 & 31))
    408 
    409 	if (ISNAN(fp)) {
    410 		(void) fpu_shr(fp, FP_NMANT - EXT_FRACBITS);
    411 		exp = EXT_EXP_INFNAN;
    412 		goto done;
    413 	}
    414 	if (ISINF(fp)) {
    415 		sign |= EXT_EXP(EXT_EXP_INFNAN);
    416 		res[1] = res[2] = 0;
    417 		return (sign);
    418 	}
    419 	if (ISZERO(fp)) {
    420 		res[1] = res[2] = 0;
    421 		return (sign);
    422 	}
    423 
    424 	if ((exp = fp->fp_exp + EXT_EXP_BIAS) < 0) {
    425 		fe->fe_fpsr |= FPSR_UNFL;
    426 		/* I'm not sure about this <=... exp==0 doesn't mean
    427 		   it's a denormal in extended format */
    428 		(void) fpu_shr(fp, FP_NMANT - FP_NG - EXT_FRACBITS - exp);
    429 		if (fpu_round(fe, fp) && fp->fp_mant[1] == EXT_EXPLICIT1) {
    430 			res[1] = res[2] = 0;
    431 			return (sign | EXT_EXP(1) | 0);
    432 		}
    433 		if (fe->fe_fpsr & FPSR_INEX2)
    434                         fe->fe_fpsr |= FPSR_UNFL
    435 			/* mc68881/2 don't underflow */;
    436 		exp = 0;
    437 		goto done;
    438 	}
    439 #if (FP_NMANT - FP_NG - EXT_FRACBITS) > 0
    440 	(void) fpu_shr(fp, FP_NMANT - FP_NG - EXT_FRACBITS);
    441 #endif
    442 	if (fpu_round(fe, fp) && fp->fp_mant[0] == EXT_EXPLICIT2)
    443 		exp++;
    444 	if (exp >= EXT_EXP_INFNAN) {
    445 		fe->fe_fpsr |= FPSR_OPERR | FPSR_INEX2 | FPSR_OVFL;
    446 		if (toinf(fe, sign)) {
    447 			res[1] = res[2] = 0;
    448 			return (sign | EXT_EXP(EXT_EXP_INFNAN) | 0);
    449 		}
    450 		res[1] = res[2] = ~0;
    451 		return (sign | EXT_EXP(EXT_EXP_INFNAN) | EXT_MASK);
    452 	}
    453 done:
    454 	res[1] = fp->fp_mant[1];
    455 	res[2] = fp->fp_mant[2];
    456 	return (sign | EXT_EXP(exp));
    457 }
    458 
    459 /*
    460  * Implode an fpn, writing the result into the given space.
    461  */
    462 void
    463 fpu_implode(fe, fp, type, space)
    464 	struct fpemu *fe;
    465 	register struct fpn *fp;
    466 	int type;
    467 	register u_int *space;
    468 {
    469 	/* XXX Dont delete exceptions set here: fe->fe_fpsr &= ~FPSR_EXCP; */
    470 
    471 	switch (type) {
    472 	case FTYPE_LNG:
    473 		space[0] = fpu_ftoi(fe, fp);
    474 		break;
    475 
    476 	case FTYPE_SNG:
    477 		space[0] = fpu_ftos(fe, fp);
    478 		break;
    479 
    480 	case FTYPE_DBL:
    481 		space[0] = fpu_ftod(fe, fp, space);
    482 		break;
    483 
    484 	case FTYPE_EXT:
    485 		/* funky rounding precision options ?? */
    486 		space[0] = fpu_ftox(fe, fp, space);
    487 		break;
    488 
    489 	default:
    490 		panic("fpu_implode");
    491 	}
    492 }
    493