in math-emu/reg_ld_str.c [564:750]
int FPU_store_single(FPU_REG *st0_ptr, u_char st0_tag, float __user *single)
{
long templ = 0;
unsigned long increment = 0; /* avoid gcc warnings */
int precision_loss;
int exp;
FPU_REG tmp;
if (st0_tag == TAG_Valid) {
reg_copy(st0_ptr, &tmp);
exp = exponent(&tmp);
if (exp < SINGLE_Emin) {
addexponent(&tmp, -SINGLE_Emin + 23); /* largest exp to be 22 */
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.sigl == 0x00800000) &&
((st0_ptr->sigh & 0x000000ff)
|| st0_ptr->sigl)))
#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;
}
templ = tmp.sigl;
} else {
if (tmp.sigl | (tmp.sigh & 0x000000ff)) {
unsigned long sigh = tmp.sigh;
unsigned long sigl = tmp.sigl;
precision_loss = 1;
switch (control_word & CW_RC) {
case RC_RND:
increment = ((sigh & 0xff) > 0x80) /* more than half */
||(((sigh & 0xff) == 0x80) && sigl) /* more than half */
||((sigh & 0x180) == 0x180); /* round to even */
break;
case RC_DOWN: /* towards -infinity */
increment = signpositive(&tmp)
? 0 : (sigl | (sigh & 0xff));
break;
case RC_UP: /* towards +infinity */
increment = signpositive(&tmp)
? (sigl | (sigh & 0xff)) : 0;
break;
case RC_CHOP:
increment = 0;
break;
}
/* Truncate part of the mantissa */
tmp.sigl = 0;
if (increment) {
if (sigh >= 0xffffff00) {
/* The sigh part overflows */
tmp.sigh = 0x80000000;
exp++;
if (exp >= EXP_OVER)
goto overflow;
} else {
tmp.sigh &= 0xffffff00;
tmp.sigh += 0x100;
}
} else {
tmp.sigh &= 0xffffff00; /* Finish the truncation */
}
} else
precision_loss = 0;
templ = (tmp.sigh >> 8) & 0x007fffff;
if (exp > SINGLE_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. */
/* Masked response is overflow to infinity. */
templ = 0x7f800000;
} else {
if (precision_loss) {
if (increment)
set_precision_flag_up();
else
set_precision_flag_down();
}
/* Add the exponent */
templ |= ((exp + SINGLE_Ebias) & 0xff) << 23;
}
}
} else if (st0_tag == TAG_Zero) {
templ = 0;
} else if (st0_tag == TAG_Special) {
st0_tag = FPU_Special(st0_ptr);
if (st0_tag == TW_Denormal) {
reg_copy(st0_ptr, &tmp);
/* 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 */
goto denormal_arg;
} else if (st0_tag == TW_Infinity) {
templ = 0x7f800000;
} 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 */
templ = st0_ptr->sigh >> 8;
if (!(st0_ptr->sigh & 0x40000000)) {
/* It is a signalling NaN */
EXCEPTION(EX_Invalid);
if (!(control_word & CW_Invalid))
return 0;
templ |= (0x40000000 >> 8);
}
templ |= 0x7f800000;
} else {
/* It is an unsupported data type */
EXCEPTION(EX_Invalid);
if (!(control_word & CW_Invalid))
return 0;
templ = 0xffc00000;
}
}
#ifdef PARANOID
else {
EXCEPTION(EX_INTERNAL | 0x164);
return 0;
}
#endif
} else if (st0_tag == TAG_Empty) {
/* Empty register (stack underflow) */
EXCEPTION(EX_StackUnder);
if (control_word & EX_Invalid) {
/* The masked response */
/* Put out the QNaN indefinite */
RE_ENTRANT_CHECK_OFF;
FPU_access_ok(single, 4);
FPU_put_user(0xffc00000,
(unsigned long __user *)single);
RE_ENTRANT_CHECK_ON;
return 1;
} else
return 0;
}
#ifdef PARANOID
else {
EXCEPTION(EX_INTERNAL | 0x163);
return 0;
}
#endif
if (getsign(st0_ptr))
templ |= 0x80000000;
RE_ENTRANT_CHECK_OFF;
FPU_access_ok(single, 4);
FPU_put_user(templ, (unsigned long __user *)single);
RE_ENTRANT_CHECK_ON;
return 1;
}