int FPU_store_double()

in math-emu/reg_ld_str.c [379:561]


int FPU_store_double(FPU_REG *st0_ptr, u_char st0_tag, double __user *dfloat)
{
	unsigned long l[2];
	unsigned long increment = 0;	/* avoid gcc warnings */
	int precision_loss;
	int exp;
	FPU_REG tmp;

	l[0] = 0;
	l[1] = 0;
	if (st0_tag == TAG_Valid) {
		reg_copy(st0_ptr, &tmp);
		exp = exponent(&tmp);

		if (exp < DOUBLE_Emin) {	/* It may be a denormal */
			addexponent(&tmp, -DOUBLE_Emin + 52);	/* largest exp to be 51 */
denormal_arg:
			if ((precision_loss = FPU_round_to_int(&tmp, st0_tag))) {
#ifdef PECULIAR_486
				/* Did it round to a non-denormal ? */
				/* This behaviour might be regarded as peculiar, it appears
				   that the 80486 rounds to the dest precision, then
				   converts to decide underflow. */
				if (!
				    ((tmp.sigh == 0x00100000) && (tmp.sigl == 0)
				     && (st0_ptr->sigl & 0x000007ff)))
#endif /* PECULIAR_486 */
				{
					EXCEPTION(EX_Underflow);
					/* This is a special case: see sec 16.2.5.1 of
					   the 80486 book */
					if (!(control_word & CW_Underflow))
						return 0;
				}
				EXCEPTION(precision_loss);
				if (!(control_word & CW_Precision))
					return 0;
			}
			l[0] = tmp.sigl;
			l[1] = tmp.sigh;
		} else {
			if (tmp.sigl & 0x000007ff) {
				precision_loss = 1;
				switch (control_word & CW_RC) {
				case RC_RND:
					/* Rounding can get a little messy.. */
					increment = ((tmp.sigl & 0x7ff) > 0x400) |	/* nearest */
					    ((tmp.sigl & 0xc00) == 0xc00);	/* odd -> even */
					break;
				case RC_DOWN:	/* towards -infinity */
					increment =
					    signpositive(&tmp) ? 0 : tmp.
					    sigl & 0x7ff;
					break;
				case RC_UP:	/* towards +infinity */
					increment =
					    signpositive(&tmp) ? tmp.
					    sigl & 0x7ff : 0;
					break;
				case RC_CHOP:
					increment = 0;
					break;
				}

				/* Truncate the mantissa */
				tmp.sigl &= 0xfffff800;

				if (increment) {
					if (tmp.sigl >= 0xfffff800) {
						/* the sigl part overflows */
						if (tmp.sigh == 0xffffffff) {
							/* The sigh part overflows */
							tmp.sigh = 0x80000000;
							exp++;
							if (exp >= EXP_OVER)
								goto overflow;
						} else {
							tmp.sigh++;
						}
						tmp.sigl = 0x00000000;
					} else {
						/* We only need to increment sigl */
						tmp.sigl += 0x00000800;
					}
				}
			} else
				precision_loss = 0;

			l[0] = (tmp.sigl >> 11) | (tmp.sigh << 21);
			l[1] = ((tmp.sigh >> 11) & 0xfffff);

			if (exp > DOUBLE_Emax) {
			      overflow:
				EXCEPTION(EX_Overflow);
				if (!(control_word & CW_Overflow))
					return 0;
				set_precision_flag_up();
				if (!(control_word & CW_Precision))
					return 0;

				/* This is a special case: see sec 16.2.5.1 of the 80486 book */
				/* Overflow to infinity */
				l[1] = 0x7ff00000;	/* Set to + INF */
			} else {
				if (precision_loss) {
					if (increment)
						set_precision_flag_up();
					else
						set_precision_flag_down();
				}
				/* Add the exponent */
				l[1] |= (((exp + DOUBLE_Ebias) & 0x7ff) << 20);
			}
		}
	} else if (st0_tag == TAG_Zero) {
		/* Number is zero */
	} else if (st0_tag == TAG_Special) {
		st0_tag = FPU_Special(st0_ptr);
		if (st0_tag == TW_Denormal) {
			/* A denormal will always underflow. */
#ifndef PECULIAR_486
			/* An 80486 is supposed to be able to generate
			   a denormal exception here, but... */
			/* Underflow has priority. */
			if (control_word & CW_Underflow)
				denormal_operand();
#endif /* PECULIAR_486 */
			reg_copy(st0_ptr, &tmp);
			goto denormal_arg;
		} else if (st0_tag == TW_Infinity) {
			l[1] = 0x7ff00000;
		} else if (st0_tag == TW_NaN) {
			/* Is it really a NaN ? */
			if ((exponent(st0_ptr) == EXP_OVER)
			    && (st0_ptr->sigh & 0x80000000)) {
				/* See if we can get a valid NaN from the FPU_REG */
				l[0] =
				    (st0_ptr->sigl >> 11) | (st0_ptr->
							     sigh << 21);
				l[1] = ((st0_ptr->sigh >> 11) & 0xfffff);
				if (!(st0_ptr->sigh & 0x40000000)) {
					/* It is a signalling NaN */
					EXCEPTION(EX_Invalid);
					if (!(control_word & CW_Invalid))
						return 0;
					l[1] |= (0x40000000 >> 11);
				}
				l[1] |= 0x7ff00000;
			} else {
				/* It is an unsupported data type */
				EXCEPTION(EX_Invalid);
				if (!(control_word & CW_Invalid))
					return 0;
				l[1] = 0xfff80000;
			}
		}
	} else if (st0_tag == TAG_Empty) {
		/* Empty register (stack underflow) */
		EXCEPTION(EX_StackUnder);
		if (control_word & CW_Invalid) {
			/* The masked response */
			/* Put out the QNaN indefinite */
			RE_ENTRANT_CHECK_OFF;
			FPU_access_ok(dfloat, 8);
			FPU_put_user(0, (unsigned long __user *)dfloat);
			FPU_put_user(0xfff80000,
				     1 + (unsigned long __user *)dfloat);
			RE_ENTRANT_CHECK_ON;
			return 1;
		} else
			return 0;
	}
	if (getsign(st0_ptr))
		l[1] |= 0x80000000;

	RE_ENTRANT_CHECK_OFF;
	FPU_access_ok(dfloat, 8);
	FPU_put_user(l[0], (unsigned long __user *)dfloat);
	FPU_put_user(l[1], 1 + (unsigned long __user *)dfloat);
	RE_ENTRANT_CHECK_ON;

	return 1;
}