Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*---------------------------------------------------------------------------+
0003  |  reg_ld_str.c                                                             |
0004  |                                                                           |
0005  | All of the functions which transfer data between user memory and FPU_REGs.|
0006  |                                                                           |
0007  | Copyright (C) 1992,1993,1994,1996,1997                                    |
0008  |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
0009  |                  E-mail   billm@suburbia.net                              |
0010  |                                                                           |
0011  |                                                                           |
0012  +---------------------------------------------------------------------------*/
0013 
0014 /*---------------------------------------------------------------------------+
0015  | Note:                                                                     |
0016  |    The file contains code which accesses user memory.                     |
0017  |    Emulator static data may change when user memory is accessed, due to   |
0018  |    other processes using the emulator while swapping is in progress.      |
0019  +---------------------------------------------------------------------------*/
0020 
0021 #include "fpu_emu.h"
0022 
0023 #include <linux/uaccess.h>
0024 
0025 #include "fpu_system.h"
0026 #include "exception.h"
0027 #include "reg_constant.h"
0028 #include "control_w.h"
0029 #include "status_w.h"
0030 
0031 #define DOUBLE_Emax 1023    /* largest valid exponent */
0032 #define DOUBLE_Ebias 1023
0033 #define DOUBLE_Emin (-1022) /* smallest valid exponent */
0034 
0035 #define SINGLE_Emax 127     /* largest valid exponent */
0036 #define SINGLE_Ebias 127
0037 #define SINGLE_Emin (-126)  /* smallest valid exponent */
0038 
0039 static u_char normalize_no_excep(FPU_REG *r, int exp, int sign)
0040 {
0041     u_char tag;
0042 
0043     setexponent16(r, exp);
0044 
0045     tag = FPU_normalize_nuo(r);
0046     stdexp(r);
0047     if (sign)
0048         setnegative(r);
0049 
0050     return tag;
0051 }
0052 
0053 int FPU_tagof(FPU_REG *ptr)
0054 {
0055     int exp;
0056 
0057     exp = exponent16(ptr) & 0x7fff;
0058     if (exp == 0) {
0059         if (!(ptr->sigh | ptr->sigl)) {
0060             return TAG_Zero;
0061         }
0062         /* The number is a de-normal or pseudodenormal. */
0063         return TAG_Special;
0064     }
0065 
0066     if (exp == 0x7fff) {
0067         /* Is an Infinity, a NaN, or an unsupported data type. */
0068         return TAG_Special;
0069     }
0070 
0071     if (!(ptr->sigh & 0x80000000)) {
0072         /* Unsupported data type. */
0073         /* Valid numbers have the ms bit set to 1. */
0074         /* Unnormal. */
0075         return TAG_Special;
0076     }
0077 
0078     return TAG_Valid;
0079 }
0080 
0081 /* Get a long double from user memory */
0082 int FPU_load_extended(long double __user *s, int stnr)
0083 {
0084     FPU_REG *sti_ptr = &st(stnr);
0085 
0086     RE_ENTRANT_CHECK_OFF;
0087     FPU_access_ok(s, 10);
0088     FPU_copy_from_user(sti_ptr, s, 10);
0089     RE_ENTRANT_CHECK_ON;
0090 
0091     return FPU_tagof(sti_ptr);
0092 }
0093 
0094 /* Get a double from user memory */
0095 int FPU_load_double(double __user *dfloat, FPU_REG *loaded_data)
0096 {
0097     int exp, tag, negative;
0098     unsigned m64, l64;
0099 
0100     RE_ENTRANT_CHECK_OFF;
0101     FPU_access_ok(dfloat, 8);
0102     FPU_get_user(m64, 1 + (unsigned long __user *)dfloat);
0103     FPU_get_user(l64, (unsigned long __user *)dfloat);
0104     RE_ENTRANT_CHECK_ON;
0105 
0106     negative = (m64 & 0x80000000) ? SIGN_Negative : SIGN_Positive;
0107     exp = ((m64 & 0x7ff00000) >> 20) - DOUBLE_Ebias + EXTENDED_Ebias;
0108     m64 &= 0xfffff;
0109     if (exp > DOUBLE_Emax + EXTENDED_Ebias) {
0110         /* Infinity or NaN */
0111         if ((m64 == 0) && (l64 == 0)) {
0112             /* +- infinity */
0113             loaded_data->sigh = 0x80000000;
0114             loaded_data->sigl = 0x00000000;
0115             exp = EXP_Infinity + EXTENDED_Ebias;
0116             tag = TAG_Special;
0117         } else {
0118             /* Must be a signaling or quiet NaN */
0119             exp = EXP_NaN + EXTENDED_Ebias;
0120             loaded_data->sigh = (m64 << 11) | 0x80000000;
0121             loaded_data->sigh |= l64 >> 21;
0122             loaded_data->sigl = l64 << 11;
0123             tag = TAG_Special;  /* The calling function must look for NaNs */
0124         }
0125     } else if (exp < DOUBLE_Emin + EXTENDED_Ebias) {
0126         /* Zero or de-normal */
0127         if ((m64 == 0) && (l64 == 0)) {
0128             /* Zero */
0129             reg_copy(&CONST_Z, loaded_data);
0130             exp = 0;
0131             tag = TAG_Zero;
0132         } else {
0133             /* De-normal */
0134             loaded_data->sigh = m64 << 11;
0135             loaded_data->sigh |= l64 >> 21;
0136             loaded_data->sigl = l64 << 11;
0137 
0138             return normalize_no_excep(loaded_data, DOUBLE_Emin,
0139                           negative)
0140                 | (denormal_operand() < 0 ? FPU_Exception : 0);
0141         }
0142     } else {
0143         loaded_data->sigh = (m64 << 11) | 0x80000000;
0144         loaded_data->sigh |= l64 >> 21;
0145         loaded_data->sigl = l64 << 11;
0146 
0147         tag = TAG_Valid;
0148     }
0149 
0150     setexponent16(loaded_data, exp | negative);
0151 
0152     return tag;
0153 }
0154 
0155 /* Get a float from user memory */
0156 int FPU_load_single(float __user *single, FPU_REG *loaded_data)
0157 {
0158     unsigned m32;
0159     int exp, tag, negative;
0160 
0161     RE_ENTRANT_CHECK_OFF;
0162     FPU_access_ok(single, 4);
0163     FPU_get_user(m32, (unsigned long __user *)single);
0164     RE_ENTRANT_CHECK_ON;
0165 
0166     negative = (m32 & 0x80000000) ? SIGN_Negative : SIGN_Positive;
0167 
0168     if (!(m32 & 0x7fffffff)) {
0169         /* Zero */
0170         reg_copy(&CONST_Z, loaded_data);
0171         addexponent(loaded_data, negative);
0172         return TAG_Zero;
0173     }
0174     exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias + EXTENDED_Ebias;
0175     m32 = (m32 & 0x7fffff) << 8;
0176     if (exp < SINGLE_Emin + EXTENDED_Ebias) {
0177         /* De-normals */
0178         loaded_data->sigh = m32;
0179         loaded_data->sigl = 0;
0180 
0181         return normalize_no_excep(loaded_data, SINGLE_Emin, negative)
0182             | (denormal_operand() < 0 ? FPU_Exception : 0);
0183     } else if (exp > SINGLE_Emax + EXTENDED_Ebias) {
0184         /* Infinity or NaN */
0185         if (m32 == 0) {
0186             /* +- infinity */
0187             loaded_data->sigh = 0x80000000;
0188             loaded_data->sigl = 0x00000000;
0189             exp = EXP_Infinity + EXTENDED_Ebias;
0190             tag = TAG_Special;
0191         } else {
0192             /* Must be a signaling or quiet NaN */
0193             exp = EXP_NaN + EXTENDED_Ebias;
0194             loaded_data->sigh = m32 | 0x80000000;
0195             loaded_data->sigl = 0;
0196             tag = TAG_Special;  /* The calling function must look for NaNs */
0197         }
0198     } else {
0199         loaded_data->sigh = m32 | 0x80000000;
0200         loaded_data->sigl = 0;
0201         tag = TAG_Valid;
0202     }
0203 
0204     setexponent16(loaded_data, exp | negative); /* Set the sign. */
0205 
0206     return tag;
0207 }
0208 
0209 /* Get a long long from user memory */
0210 int FPU_load_int64(long long __user *_s)
0211 {
0212     long long s;
0213     int sign;
0214     FPU_REG *st0_ptr = &st(0);
0215 
0216     RE_ENTRANT_CHECK_OFF;
0217     FPU_access_ok(_s, 8);
0218     if (copy_from_user(&s, _s, 8))
0219         FPU_abort;
0220     RE_ENTRANT_CHECK_ON;
0221 
0222     if (s == 0) {
0223         reg_copy(&CONST_Z, st0_ptr);
0224         return TAG_Zero;
0225     }
0226 
0227     if (s > 0)
0228         sign = SIGN_Positive;
0229     else {
0230         s = -s;
0231         sign = SIGN_Negative;
0232     }
0233 
0234     significand(st0_ptr) = s;
0235 
0236     return normalize_no_excep(st0_ptr, 63, sign);
0237 }
0238 
0239 /* Get a long from user memory */
0240 int FPU_load_int32(long __user *_s, FPU_REG *loaded_data)
0241 {
0242     long s;
0243     int negative;
0244 
0245     RE_ENTRANT_CHECK_OFF;
0246     FPU_access_ok(_s, 4);
0247     FPU_get_user(s, _s);
0248     RE_ENTRANT_CHECK_ON;
0249 
0250     if (s == 0) {
0251         reg_copy(&CONST_Z, loaded_data);
0252         return TAG_Zero;
0253     }
0254 
0255     if (s > 0)
0256         negative = SIGN_Positive;
0257     else {
0258         s = -s;
0259         negative = SIGN_Negative;
0260     }
0261 
0262     loaded_data->sigh = s;
0263     loaded_data->sigl = 0;
0264 
0265     return normalize_no_excep(loaded_data, 31, negative);
0266 }
0267 
0268 /* Get a short from user memory */
0269 int FPU_load_int16(short __user *_s, FPU_REG *loaded_data)
0270 {
0271     int s, negative;
0272 
0273     RE_ENTRANT_CHECK_OFF;
0274     FPU_access_ok(_s, 2);
0275     /* Cast as short to get the sign extended. */
0276     FPU_get_user(s, _s);
0277     RE_ENTRANT_CHECK_ON;
0278 
0279     if (s == 0) {
0280         reg_copy(&CONST_Z, loaded_data);
0281         return TAG_Zero;
0282     }
0283 
0284     if (s > 0)
0285         negative = SIGN_Positive;
0286     else {
0287         s = -s;
0288         negative = SIGN_Negative;
0289     }
0290 
0291     loaded_data->sigh = s << 16;
0292     loaded_data->sigl = 0;
0293 
0294     return normalize_no_excep(loaded_data, 15, negative);
0295 }
0296 
0297 /* Get a packed bcd array from user memory */
0298 int FPU_load_bcd(u_char __user *s)
0299 {
0300     FPU_REG *st0_ptr = &st(0);
0301     int pos;
0302     u_char bcd;
0303     long long l = 0;
0304     int sign;
0305 
0306     RE_ENTRANT_CHECK_OFF;
0307     FPU_access_ok(s, 10);
0308     RE_ENTRANT_CHECK_ON;
0309     for (pos = 8; pos >= 0; pos--) {
0310         l *= 10;
0311         RE_ENTRANT_CHECK_OFF;
0312         FPU_get_user(bcd, s + pos);
0313         RE_ENTRANT_CHECK_ON;
0314         l += bcd >> 4;
0315         l *= 10;
0316         l += bcd & 0x0f;
0317     }
0318 
0319     RE_ENTRANT_CHECK_OFF;
0320     FPU_get_user(sign, s + 9);
0321     sign = sign & 0x80 ? SIGN_Negative : SIGN_Positive;
0322     RE_ENTRANT_CHECK_ON;
0323 
0324     if (l == 0) {
0325         reg_copy(&CONST_Z, st0_ptr);
0326         addexponent(st0_ptr, sign); /* Set the sign. */
0327         return TAG_Zero;
0328     } else {
0329         significand(st0_ptr) = l;
0330         return normalize_no_excep(st0_ptr, 63, sign);
0331     }
0332 }
0333 
0334 /*===========================================================================*/
0335 
0336 /* Put a long double into user memory */
0337 int FPU_store_extended(FPU_REG *st0_ptr, u_char st0_tag,
0338                long double __user * d)
0339 {
0340     /*
0341        The only exception raised by an attempt to store to an
0342        extended format is the Invalid Stack exception, i.e.
0343        attempting to store from an empty register.
0344      */
0345 
0346     if (st0_tag != TAG_Empty) {
0347         RE_ENTRANT_CHECK_OFF;
0348         FPU_access_ok(d, 10);
0349 
0350         FPU_put_user(st0_ptr->sigl, (unsigned long __user *)d);
0351         FPU_put_user(st0_ptr->sigh,
0352                  (unsigned long __user *)((u_char __user *) d + 4));
0353         FPU_put_user(exponent16(st0_ptr),
0354                  (unsigned short __user *)((u_char __user *) d +
0355                                8));
0356         RE_ENTRANT_CHECK_ON;
0357 
0358         return 1;
0359     }
0360 
0361     /* Empty register (stack underflow) */
0362     EXCEPTION(EX_StackUnder);
0363     if (control_word & CW_Invalid) {
0364         /* The masked response */
0365         /* Put out the QNaN indefinite */
0366         RE_ENTRANT_CHECK_OFF;
0367         FPU_access_ok(d, 10);
0368         FPU_put_user(0, (unsigned long __user *)d);
0369         FPU_put_user(0xc0000000, 1 + (unsigned long __user *)d);
0370         FPU_put_user(0xffff, 4 + (short __user *)d);
0371         RE_ENTRANT_CHECK_ON;
0372         return 1;
0373     } else
0374         return 0;
0375 
0376 }
0377 
0378 /* Put a double into user memory */
0379 int FPU_store_double(FPU_REG *st0_ptr, u_char st0_tag, double __user *dfloat)
0380 {
0381     unsigned long l[2];
0382     unsigned long increment = 0;    /* avoid gcc warnings */
0383     int precision_loss;
0384     int exp;
0385     FPU_REG tmp;
0386 
0387     l[0] = 0;
0388     l[1] = 0;
0389     if (st0_tag == TAG_Valid) {
0390         reg_copy(st0_ptr, &tmp);
0391         exp = exponent(&tmp);
0392 
0393         if (exp < DOUBLE_Emin) {    /* It may be a denormal */
0394             addexponent(&tmp, -DOUBLE_Emin + 52);   /* largest exp to be 51 */
0395 denormal_arg:
0396             if ((precision_loss = FPU_round_to_int(&tmp, st0_tag))) {
0397 #ifdef PECULIAR_486
0398                 /* Did it round to a non-denormal ? */
0399                 /* This behaviour might be regarded as peculiar, it appears
0400                    that the 80486 rounds to the dest precision, then
0401                    converts to decide underflow. */
0402                 if (!
0403                     ((tmp.sigh == 0x00100000) && (tmp.sigl == 0)
0404                      && (st0_ptr->sigl & 0x000007ff)))
0405 #endif /* PECULIAR_486 */
0406                 {
0407                     EXCEPTION(EX_Underflow);
0408                     /* This is a special case: see sec 16.2.5.1 of
0409                        the 80486 book */
0410                     if (!(control_word & CW_Underflow))
0411                         return 0;
0412                 }
0413                 EXCEPTION(precision_loss);
0414                 if (!(control_word & CW_Precision))
0415                     return 0;
0416             }
0417             l[0] = tmp.sigl;
0418             l[1] = tmp.sigh;
0419         } else {
0420             if (tmp.sigl & 0x000007ff) {
0421                 precision_loss = 1;
0422                 switch (control_word & CW_RC) {
0423                 case RC_RND:
0424                     /* Rounding can get a little messy.. */
0425                     increment = ((tmp.sigl & 0x7ff) > 0x400) |  /* nearest */
0426                         ((tmp.sigl & 0xc00) == 0xc00);  /* odd -> even */
0427                     break;
0428                 case RC_DOWN:   /* towards -infinity */
0429                     increment =
0430                         signpositive(&tmp) ? 0 : tmp.
0431                         sigl & 0x7ff;
0432                     break;
0433                 case RC_UP: /* towards +infinity */
0434                     increment =
0435                         signpositive(&tmp) ? tmp.
0436                         sigl & 0x7ff : 0;
0437                     break;
0438                 case RC_CHOP:
0439                     increment = 0;
0440                     break;
0441                 }
0442 
0443                 /* Truncate the mantissa */
0444                 tmp.sigl &= 0xfffff800;
0445 
0446                 if (increment) {
0447                     if (tmp.sigl >= 0xfffff800) {
0448                         /* the sigl part overflows */
0449                         if (tmp.sigh == 0xffffffff) {
0450                             /* The sigh part overflows */
0451                             tmp.sigh = 0x80000000;
0452                             exp++;
0453                             if (exp >= EXP_OVER)
0454                                 goto overflow;
0455                         } else {
0456                             tmp.sigh++;
0457                         }
0458                         tmp.sigl = 0x00000000;
0459                     } else {
0460                         /* We only need to increment sigl */
0461                         tmp.sigl += 0x00000800;
0462                     }
0463                 }
0464             } else
0465                 precision_loss = 0;
0466 
0467             l[0] = (tmp.sigl >> 11) | (tmp.sigh << 21);
0468             l[1] = ((tmp.sigh >> 11) & 0xfffff);
0469 
0470             if (exp > DOUBLE_Emax) {
0471                   overflow:
0472                 EXCEPTION(EX_Overflow);
0473                 if (!(control_word & CW_Overflow))
0474                     return 0;
0475                 set_precision_flag_up();
0476                 if (!(control_word & CW_Precision))
0477                     return 0;
0478 
0479                 /* This is a special case: see sec 16.2.5.1 of the 80486 book */
0480                 /* Overflow to infinity */
0481                 l[1] = 0x7ff00000;  /* Set to + INF */
0482             } else {
0483                 if (precision_loss) {
0484                     if (increment)
0485                         set_precision_flag_up();
0486                     else
0487                         set_precision_flag_down();
0488                 }
0489                 /* Add the exponent */
0490                 l[1] |= (((exp + DOUBLE_Ebias) & 0x7ff) << 20);
0491             }
0492         }
0493     } else if (st0_tag == TAG_Zero) {
0494         /* Number is zero */
0495     } else if (st0_tag == TAG_Special) {
0496         st0_tag = FPU_Special(st0_ptr);
0497         if (st0_tag == TW_Denormal) {
0498             /* A denormal will always underflow. */
0499 #ifndef PECULIAR_486
0500             /* An 80486 is supposed to be able to generate
0501                a denormal exception here, but... */
0502             /* Underflow has priority. */
0503             if (control_word & CW_Underflow)
0504                 denormal_operand();
0505 #endif /* PECULIAR_486 */
0506             reg_copy(st0_ptr, &tmp);
0507             goto denormal_arg;
0508         } else if (st0_tag == TW_Infinity) {
0509             l[1] = 0x7ff00000;
0510         } else if (st0_tag == TW_NaN) {
0511             /* Is it really a NaN ? */
0512             if ((exponent(st0_ptr) == EXP_OVER)
0513                 && (st0_ptr->sigh & 0x80000000)) {
0514                 /* See if we can get a valid NaN from the FPU_REG */
0515                 l[0] =
0516                     (st0_ptr->sigl >> 11) | (st0_ptr->
0517                                  sigh << 21);
0518                 l[1] = ((st0_ptr->sigh >> 11) & 0xfffff);
0519                 if (!(st0_ptr->sigh & 0x40000000)) {
0520                     /* It is a signalling NaN */
0521                     EXCEPTION(EX_Invalid);
0522                     if (!(control_word & CW_Invalid))
0523                         return 0;
0524                     l[1] |= (0x40000000 >> 11);
0525                 }
0526                 l[1] |= 0x7ff00000;
0527             } else {
0528                 /* It is an unsupported data type */
0529                 EXCEPTION(EX_Invalid);
0530                 if (!(control_word & CW_Invalid))
0531                     return 0;
0532                 l[1] = 0xfff80000;
0533             }
0534         }
0535     } else if (st0_tag == TAG_Empty) {
0536         /* Empty register (stack underflow) */
0537         EXCEPTION(EX_StackUnder);
0538         if (control_word & CW_Invalid) {
0539             /* The masked response */
0540             /* Put out the QNaN indefinite */
0541             RE_ENTRANT_CHECK_OFF;
0542             FPU_access_ok(dfloat, 8);
0543             FPU_put_user(0, (unsigned long __user *)dfloat);
0544             FPU_put_user(0xfff80000,
0545                      1 + (unsigned long __user *)dfloat);
0546             RE_ENTRANT_CHECK_ON;
0547             return 1;
0548         } else
0549             return 0;
0550     }
0551     if (getsign(st0_ptr))
0552         l[1] |= 0x80000000;
0553 
0554     RE_ENTRANT_CHECK_OFF;
0555     FPU_access_ok(dfloat, 8);
0556     FPU_put_user(l[0], (unsigned long __user *)dfloat);
0557     FPU_put_user(l[1], 1 + (unsigned long __user *)dfloat);
0558     RE_ENTRANT_CHECK_ON;
0559 
0560     return 1;
0561 }
0562 
0563 /* Put a float into user memory */
0564 int FPU_store_single(FPU_REG *st0_ptr, u_char st0_tag, float __user *single)
0565 {
0566     long templ = 0;
0567     unsigned long increment = 0;    /* avoid gcc warnings */
0568     int precision_loss;
0569     int exp;
0570     FPU_REG tmp;
0571 
0572     if (st0_tag == TAG_Valid) {
0573 
0574         reg_copy(st0_ptr, &tmp);
0575         exp = exponent(&tmp);
0576 
0577         if (exp < SINGLE_Emin) {
0578             addexponent(&tmp, -SINGLE_Emin + 23);   /* largest exp to be 22 */
0579 
0580               denormal_arg:
0581 
0582             if ((precision_loss = FPU_round_to_int(&tmp, st0_tag))) {
0583 #ifdef PECULIAR_486
0584                 /* Did it round to a non-denormal ? */
0585                 /* This behaviour might be regarded as peculiar, it appears
0586                    that the 80486 rounds to the dest precision, then
0587                    converts to decide underflow. */
0588                 if (!((tmp.sigl == 0x00800000) &&
0589                       ((st0_ptr->sigh & 0x000000ff)
0590                        || st0_ptr->sigl)))
0591 #endif /* PECULIAR_486 */
0592                 {
0593                     EXCEPTION(EX_Underflow);
0594                     /* This is a special case: see sec 16.2.5.1 of
0595                        the 80486 book */
0596                     if (!(control_word & CW_Underflow))
0597                         return 0;
0598                 }
0599                 EXCEPTION(precision_loss);
0600                 if (!(control_word & CW_Precision))
0601                     return 0;
0602             }
0603             templ = tmp.sigl;
0604         } else {
0605             if (tmp.sigl | (tmp.sigh & 0x000000ff)) {
0606                 unsigned long sigh = tmp.sigh;
0607                 unsigned long sigl = tmp.sigl;
0608 
0609                 precision_loss = 1;
0610                 switch (control_word & CW_RC) {
0611                 case RC_RND:
0612                     increment = ((sigh & 0xff) > 0x80)  /* more than half */
0613                         ||(((sigh & 0xff) == 0x80) && sigl) /* more than half */
0614                         ||((sigh & 0x180) == 0x180);    /* round to even */
0615                     break;
0616                 case RC_DOWN:   /* towards -infinity */
0617                     increment = signpositive(&tmp)
0618                         ? 0 : (sigl | (sigh & 0xff));
0619                     break;
0620                 case RC_UP: /* towards +infinity */
0621                     increment = signpositive(&tmp)
0622                         ? (sigl | (sigh & 0xff)) : 0;
0623                     break;
0624                 case RC_CHOP:
0625                     increment = 0;
0626                     break;
0627                 }
0628 
0629                 /* Truncate part of the mantissa */
0630                 tmp.sigl = 0;
0631 
0632                 if (increment) {
0633                     if (sigh >= 0xffffff00) {
0634                         /* The sigh part overflows */
0635                         tmp.sigh = 0x80000000;
0636                         exp++;
0637                         if (exp >= EXP_OVER)
0638                             goto overflow;
0639                     } else {
0640                         tmp.sigh &= 0xffffff00;
0641                         tmp.sigh += 0x100;
0642                     }
0643                 } else {
0644                     tmp.sigh &= 0xffffff00; /* Finish the truncation */
0645                 }
0646             } else
0647                 precision_loss = 0;
0648 
0649             templ = (tmp.sigh >> 8) & 0x007fffff;
0650 
0651             if (exp > SINGLE_Emax) {
0652                   overflow:
0653                 EXCEPTION(EX_Overflow);
0654                 if (!(control_word & CW_Overflow))
0655                     return 0;
0656                 set_precision_flag_up();
0657                 if (!(control_word & CW_Precision))
0658                     return 0;
0659 
0660                 /* This is a special case: see sec 16.2.5.1 of the 80486 book. */
0661                 /* Masked response is overflow to infinity. */
0662                 templ = 0x7f800000;
0663             } else {
0664                 if (precision_loss) {
0665                     if (increment)
0666                         set_precision_flag_up();
0667                     else
0668                         set_precision_flag_down();
0669                 }
0670                 /* Add the exponent */
0671                 templ |= ((exp + SINGLE_Ebias) & 0xff) << 23;
0672             }
0673         }
0674     } else if (st0_tag == TAG_Zero) {
0675         templ = 0;
0676     } else if (st0_tag == TAG_Special) {
0677         st0_tag = FPU_Special(st0_ptr);
0678         if (st0_tag == TW_Denormal) {
0679             reg_copy(st0_ptr, &tmp);
0680 
0681             /* A denormal will always underflow. */
0682 #ifndef PECULIAR_486
0683             /* An 80486 is supposed to be able to generate
0684                a denormal exception here, but... */
0685             /* Underflow has priority. */
0686             if (control_word & CW_Underflow)
0687                 denormal_operand();
0688 #endif /* PECULIAR_486 */
0689             goto denormal_arg;
0690         } else if (st0_tag == TW_Infinity) {
0691             templ = 0x7f800000;
0692         } else if (st0_tag == TW_NaN) {
0693             /* Is it really a NaN ? */
0694             if ((exponent(st0_ptr) == EXP_OVER)
0695                 && (st0_ptr->sigh & 0x80000000)) {
0696                 /* See if we can get a valid NaN from the FPU_REG */
0697                 templ = st0_ptr->sigh >> 8;
0698                 if (!(st0_ptr->sigh & 0x40000000)) {
0699                     /* It is a signalling NaN */
0700                     EXCEPTION(EX_Invalid);
0701                     if (!(control_word & CW_Invalid))
0702                         return 0;
0703                     templ |= (0x40000000 >> 8);
0704                 }
0705                 templ |= 0x7f800000;
0706             } else {
0707                 /* It is an unsupported data type */
0708                 EXCEPTION(EX_Invalid);
0709                 if (!(control_word & CW_Invalid))
0710                     return 0;
0711                 templ = 0xffc00000;
0712             }
0713         }
0714 #ifdef PARANOID
0715         else {
0716             EXCEPTION(EX_INTERNAL | 0x164);
0717             return 0;
0718         }
0719 #endif
0720     } else if (st0_tag == TAG_Empty) {
0721         /* Empty register (stack underflow) */
0722         EXCEPTION(EX_StackUnder);
0723         if (control_word & EX_Invalid) {
0724             /* The masked response */
0725             /* Put out the QNaN indefinite */
0726             RE_ENTRANT_CHECK_OFF;
0727             FPU_access_ok(single, 4);
0728             FPU_put_user(0xffc00000,
0729                      (unsigned long __user *)single);
0730             RE_ENTRANT_CHECK_ON;
0731             return 1;
0732         } else
0733             return 0;
0734     }
0735 #ifdef PARANOID
0736     else {
0737         EXCEPTION(EX_INTERNAL | 0x163);
0738         return 0;
0739     }
0740 #endif
0741     if (getsign(st0_ptr))
0742         templ |= 0x80000000;
0743 
0744     RE_ENTRANT_CHECK_OFF;
0745     FPU_access_ok(single, 4);
0746     FPU_put_user(templ, (unsigned long __user *)single);
0747     RE_ENTRANT_CHECK_ON;
0748 
0749     return 1;
0750 }
0751 
0752 /* Put a long long into user memory */
0753 int FPU_store_int64(FPU_REG *st0_ptr, u_char st0_tag, long long __user *d)
0754 {
0755     FPU_REG t;
0756     long long tll;
0757     int precision_loss;
0758 
0759     if (st0_tag == TAG_Empty) {
0760         /* Empty register (stack underflow) */
0761         EXCEPTION(EX_StackUnder);
0762         goto invalid_operand;
0763     } else if (st0_tag == TAG_Special) {
0764         st0_tag = FPU_Special(st0_ptr);
0765         if ((st0_tag == TW_Infinity) || (st0_tag == TW_NaN)) {
0766             EXCEPTION(EX_Invalid);
0767             goto invalid_operand;
0768         }
0769     }
0770 
0771     reg_copy(st0_ptr, &t);
0772     precision_loss = FPU_round_to_int(&t, st0_tag);
0773     ((long *)&tll)[0] = t.sigl;
0774     ((long *)&tll)[1] = t.sigh;
0775     if ((precision_loss == 1) ||
0776         ((t.sigh & 0x80000000) &&
0777          !((t.sigh == 0x80000000) && (t.sigl == 0) && signnegative(&t)))) {
0778         EXCEPTION(EX_Invalid);
0779         /* This is a special case: see sec 16.2.5.1 of the 80486 book */
0780           invalid_operand:
0781         if (control_word & EX_Invalid) {
0782             /* Produce something like QNaN "indefinite" */
0783             tll = 0x8000000000000000LL;
0784         } else
0785             return 0;
0786     } else {
0787         if (precision_loss)
0788             set_precision_flag(precision_loss);
0789         if (signnegative(&t))
0790             tll = -tll;
0791     }
0792 
0793     RE_ENTRANT_CHECK_OFF;
0794     FPU_access_ok(d, 8);
0795     if (copy_to_user(d, &tll, 8))
0796         FPU_abort;
0797     RE_ENTRANT_CHECK_ON;
0798 
0799     return 1;
0800 }
0801 
0802 /* Put a long into user memory */
0803 int FPU_store_int32(FPU_REG *st0_ptr, u_char st0_tag, long __user *d)
0804 {
0805     FPU_REG t;
0806     int precision_loss;
0807 
0808     if (st0_tag == TAG_Empty) {
0809         /* Empty register (stack underflow) */
0810         EXCEPTION(EX_StackUnder);
0811         goto invalid_operand;
0812     } else if (st0_tag == TAG_Special) {
0813         st0_tag = FPU_Special(st0_ptr);
0814         if ((st0_tag == TW_Infinity) || (st0_tag == TW_NaN)) {
0815             EXCEPTION(EX_Invalid);
0816             goto invalid_operand;
0817         }
0818     }
0819 
0820     reg_copy(st0_ptr, &t);
0821     precision_loss = FPU_round_to_int(&t, st0_tag);
0822     if (t.sigh ||
0823         ((t.sigl & 0x80000000) &&
0824          !((t.sigl == 0x80000000) && signnegative(&t)))) {
0825         EXCEPTION(EX_Invalid);
0826         /* This is a special case: see sec 16.2.5.1 of the 80486 book */
0827           invalid_operand:
0828         if (control_word & EX_Invalid) {
0829             /* Produce something like QNaN "indefinite" */
0830             t.sigl = 0x80000000;
0831         } else
0832             return 0;
0833     } else {
0834         if (precision_loss)
0835             set_precision_flag(precision_loss);
0836         if (signnegative(&t))
0837             t.sigl = -(long)t.sigl;
0838     }
0839 
0840     RE_ENTRANT_CHECK_OFF;
0841     FPU_access_ok(d, 4);
0842     FPU_put_user(t.sigl, (unsigned long __user *)d);
0843     RE_ENTRANT_CHECK_ON;
0844 
0845     return 1;
0846 }
0847 
0848 /* Put a short into user memory */
0849 int FPU_store_int16(FPU_REG *st0_ptr, u_char st0_tag, short __user *d)
0850 {
0851     FPU_REG t;
0852     int precision_loss;
0853 
0854     if (st0_tag == TAG_Empty) {
0855         /* Empty register (stack underflow) */
0856         EXCEPTION(EX_StackUnder);
0857         goto invalid_operand;
0858     } else if (st0_tag == TAG_Special) {
0859         st0_tag = FPU_Special(st0_ptr);
0860         if ((st0_tag == TW_Infinity) || (st0_tag == TW_NaN)) {
0861             EXCEPTION(EX_Invalid);
0862             goto invalid_operand;
0863         }
0864     }
0865 
0866     reg_copy(st0_ptr, &t);
0867     precision_loss = FPU_round_to_int(&t, st0_tag);
0868     if (t.sigh ||
0869         ((t.sigl & 0xffff8000) &&
0870          !((t.sigl == 0x8000) && signnegative(&t)))) {
0871         EXCEPTION(EX_Invalid);
0872         /* This is a special case: see sec 16.2.5.1 of the 80486 book */
0873           invalid_operand:
0874         if (control_word & EX_Invalid) {
0875             /* Produce something like QNaN "indefinite" */
0876             t.sigl = 0x8000;
0877         } else
0878             return 0;
0879     } else {
0880         if (precision_loss)
0881             set_precision_flag(precision_loss);
0882         if (signnegative(&t))
0883             t.sigl = -t.sigl;
0884     }
0885 
0886     RE_ENTRANT_CHECK_OFF;
0887     FPU_access_ok(d, 2);
0888     FPU_put_user((short)t.sigl, d);
0889     RE_ENTRANT_CHECK_ON;
0890 
0891     return 1;
0892 }
0893 
0894 /* Put a packed bcd array into user memory */
0895 int FPU_store_bcd(FPU_REG *st0_ptr, u_char st0_tag, u_char __user *d)
0896 {
0897     FPU_REG t;
0898     unsigned long long ll;
0899     u_char b;
0900     int i, precision_loss;
0901     u_char sign = (getsign(st0_ptr) == SIGN_NEG) ? 0x80 : 0;
0902 
0903     if (st0_tag == TAG_Empty) {
0904         /* Empty register (stack underflow) */
0905         EXCEPTION(EX_StackUnder);
0906         goto invalid_operand;
0907     } else if (st0_tag == TAG_Special) {
0908         st0_tag = FPU_Special(st0_ptr);
0909         if ((st0_tag == TW_Infinity) || (st0_tag == TW_NaN)) {
0910             EXCEPTION(EX_Invalid);
0911             goto invalid_operand;
0912         }
0913     }
0914 
0915     reg_copy(st0_ptr, &t);
0916     precision_loss = FPU_round_to_int(&t, st0_tag);
0917     ll = significand(&t);
0918 
0919     /* Check for overflow, by comparing with 999999999999999999 decimal. */
0920     if ((t.sigh > 0x0de0b6b3) ||
0921         ((t.sigh == 0x0de0b6b3) && (t.sigl > 0xa763ffff))) {
0922         EXCEPTION(EX_Invalid);
0923         /* This is a special case: see sec 16.2.5.1 of the 80486 book */
0924           invalid_operand:
0925         if (control_word & CW_Invalid) {
0926             /* Produce the QNaN "indefinite" */
0927             RE_ENTRANT_CHECK_OFF;
0928             FPU_access_ok(d, 10);
0929             for (i = 0; i < 7; i++)
0930                 FPU_put_user(0, d + i); /* These bytes "undefined" */
0931             FPU_put_user(0xc0, d + 7);  /* This byte "undefined" */
0932             FPU_put_user(0xff, d + 8);
0933             FPU_put_user(0xff, d + 9);
0934             RE_ENTRANT_CHECK_ON;
0935             return 1;
0936         } else
0937             return 0;
0938     } else if (precision_loss) {
0939         /* Precision loss doesn't stop the data transfer */
0940         set_precision_flag(precision_loss);
0941     }
0942 
0943     RE_ENTRANT_CHECK_OFF;
0944     FPU_access_ok(d, 10);
0945     RE_ENTRANT_CHECK_ON;
0946     for (i = 0; i < 9; i++) {
0947         b = FPU_div_small(&ll, 10);
0948         b |= (FPU_div_small(&ll, 10)) << 4;
0949         RE_ENTRANT_CHECK_OFF;
0950         FPU_put_user(b, d + i);
0951         RE_ENTRANT_CHECK_ON;
0952     }
0953     RE_ENTRANT_CHECK_OFF;
0954     FPU_put_user(sign, d + 9);
0955     RE_ENTRANT_CHECK_ON;
0956 
0957     return 1;
0958 }
0959 
0960 /*===========================================================================*/
0961 
0962 /* r gets mangled such that sig is int, sign: 
0963    it is NOT normalized */
0964 /* The return value (in eax) is zero if the result is exact,
0965    if bits are changed due to rounding, truncation, etc, then
0966    a non-zero value is returned */
0967 /* Overflow is signaled by a non-zero return value (in eax).
0968    In the case of overflow, the returned significand always has the
0969    largest possible value */
0970 int FPU_round_to_int(FPU_REG *r, u_char tag)
0971 {
0972     u_char very_big;
0973     unsigned eax;
0974 
0975     if (tag == TAG_Zero) {
0976         /* Make sure that zero is returned */
0977         significand(r) = 0;
0978         return 0;   /* o.k. */
0979     }
0980 
0981     if (exponent(r) > 63) {
0982         r->sigl = r->sigh = ~0; /* The largest representable number */
0983         return 1;   /* overflow */
0984     }
0985 
0986     eax = FPU_shrxs(&r->sigl, 63 - exponent(r));
0987     very_big = !(~(r->sigh) | ~(r->sigl));  /* test for 0xfff...fff */
0988 #define half_or_more    (eax & 0x80000000)
0989 #define frac_part   (eax)
0990 #define more_than_half  ((eax & 0x80000001) == 0x80000001)
0991     switch (control_word & CW_RC) {
0992     case RC_RND:
0993         if (more_than_half  /* nearest */
0994             || (half_or_more && (r->sigl & 1))) {   /* odd -> even */
0995             if (very_big)
0996                 return 1;   /* overflow */
0997             significand(r)++;
0998             return PRECISION_LOST_UP;
0999         }
1000         break;
1001     case RC_DOWN:
1002         if (frac_part && getsign(r)) {
1003             if (very_big)
1004                 return 1;   /* overflow */
1005             significand(r)++;
1006             return PRECISION_LOST_UP;
1007         }
1008         break;
1009     case RC_UP:
1010         if (frac_part && !getsign(r)) {
1011             if (very_big)
1012                 return 1;   /* overflow */
1013             significand(r)++;
1014             return PRECISION_LOST_UP;
1015         }
1016         break;
1017     case RC_CHOP:
1018         break;
1019     }
1020 
1021     return eax ? PRECISION_LOST_DOWN : 0;
1022 
1023 }
1024 
1025 /*===========================================================================*/
1026 
1027 u_char __user *fldenv(fpu_addr_modes addr_modes, u_char __user *s)
1028 {
1029     unsigned short tag_word = 0;
1030     u_char tag;
1031     int i;
1032 
1033     if ((addr_modes.default_mode == VM86) ||
1034         ((addr_modes.default_mode == PM16)
1035          ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX))) {
1036         RE_ENTRANT_CHECK_OFF;
1037         FPU_access_ok(s, 0x0e);
1038         FPU_get_user(control_word, (unsigned short __user *)s);
1039         FPU_get_user(partial_status, (unsigned short __user *)(s + 2));
1040         FPU_get_user(tag_word, (unsigned short __user *)(s + 4));
1041         FPU_get_user(instruction_address.offset,
1042                  (unsigned short __user *)(s + 6));
1043         FPU_get_user(instruction_address.selector,
1044                  (unsigned short __user *)(s + 8));
1045         FPU_get_user(operand_address.offset,
1046                  (unsigned short __user *)(s + 0x0a));
1047         FPU_get_user(operand_address.selector,
1048                  (unsigned short __user *)(s + 0x0c));
1049         RE_ENTRANT_CHECK_ON;
1050         s += 0x0e;
1051         if (addr_modes.default_mode == VM86) {
1052             instruction_address.offset
1053                 += (instruction_address.selector & 0xf000) << 4;
1054             operand_address.offset +=
1055                 (operand_address.selector & 0xf000) << 4;
1056         }
1057     } else {
1058         RE_ENTRANT_CHECK_OFF;
1059         FPU_access_ok(s, 0x1c);
1060         FPU_get_user(control_word, (unsigned short __user *)s);
1061         FPU_get_user(partial_status, (unsigned short __user *)(s + 4));
1062         FPU_get_user(tag_word, (unsigned short __user *)(s + 8));
1063         FPU_get_user(instruction_address.offset,
1064                  (unsigned long __user *)(s + 0x0c));
1065         FPU_get_user(instruction_address.selector,
1066                  (unsigned short __user *)(s + 0x10));
1067         FPU_get_user(instruction_address.opcode,
1068                  (unsigned short __user *)(s + 0x12));
1069         FPU_get_user(operand_address.offset,
1070                  (unsigned long __user *)(s + 0x14));
1071         FPU_get_user(operand_address.selector,
1072                  (unsigned long __user *)(s + 0x18));
1073         RE_ENTRANT_CHECK_ON;
1074         s += 0x1c;
1075     }
1076 
1077 #ifdef PECULIAR_486
1078     control_word &= ~0xe080;
1079 #endif /* PECULIAR_486 */
1080 
1081     top = (partial_status >> SW_Top_Shift) & 7;
1082 
1083     if (partial_status & ~control_word & CW_Exceptions)
1084         partial_status |= (SW_Summary | SW_Backward);
1085     else
1086         partial_status &= ~(SW_Summary | SW_Backward);
1087 
1088     for (i = 0; i < 8; i++) {
1089         tag = tag_word & 3;
1090         tag_word >>= 2;
1091 
1092         if (tag == TAG_Empty)
1093             /* New tag is empty.  Accept it */
1094             FPU_settag(i, TAG_Empty);
1095         else if (FPU_gettag(i) == TAG_Empty) {
1096             /* Old tag is empty and new tag is not empty.  New tag is determined
1097                by old reg contents */
1098             if (exponent(&fpu_register(i)) == -EXTENDED_Ebias) {
1099                 if (!
1100                     (fpu_register(i).sigl | fpu_register(i).
1101                      sigh))
1102                     FPU_settag(i, TAG_Zero);
1103                 else
1104                     FPU_settag(i, TAG_Special);
1105             } else if (exponent(&fpu_register(i)) ==
1106                    0x7fff - EXTENDED_Ebias) {
1107                 FPU_settag(i, TAG_Special);
1108             } else if (fpu_register(i).sigh & 0x80000000)
1109                 FPU_settag(i, TAG_Valid);
1110             else
1111                 FPU_settag(i, TAG_Special); /* An Un-normal */
1112         }
1113         /* Else old tag is not empty and new tag is not empty.  Old tag
1114            remains correct */
1115     }
1116 
1117     return s;
1118 }
1119 
1120 void FPU_frstor(fpu_addr_modes addr_modes, u_char __user *data_address)
1121 {
1122     int i, regnr;
1123     u_char __user *s = fldenv(addr_modes, data_address);
1124     int offset = (top & 7) * 10, other = 80 - offset;
1125 
1126     /* Copy all registers in stack order. */
1127     RE_ENTRANT_CHECK_OFF;
1128     FPU_access_ok(s, 80);
1129     FPU_copy_from_user(register_base + offset, s, other);
1130     if (offset)
1131         FPU_copy_from_user(register_base, s + other, offset);
1132     RE_ENTRANT_CHECK_ON;
1133 
1134     for (i = 0; i < 8; i++) {
1135         regnr = (i + top) & 7;
1136         if (FPU_gettag(regnr) != TAG_Empty)
1137             /* The loaded data over-rides all other cases. */
1138             FPU_settag(regnr, FPU_tagof(&st(i)));
1139     }
1140 
1141 }
1142 
1143 u_char __user *fstenv(fpu_addr_modes addr_modes, u_char __user *d)
1144 {
1145     if ((addr_modes.default_mode == VM86) ||
1146         ((addr_modes.default_mode == PM16)
1147          ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX))) {
1148         RE_ENTRANT_CHECK_OFF;
1149         FPU_access_ok(d, 14);
1150 #ifdef PECULIAR_486
1151         FPU_put_user(control_word & ~0xe080, (unsigned long __user *)d);
1152 #else
1153         FPU_put_user(control_word, (unsigned short __user *)d);
1154 #endif /* PECULIAR_486 */
1155         FPU_put_user(status_word(), (unsigned short __user *)(d + 2));
1156         FPU_put_user(fpu_tag_word, (unsigned short __user *)(d + 4));
1157         FPU_put_user(instruction_address.offset,
1158                  (unsigned short __user *)(d + 6));
1159         FPU_put_user(operand_address.offset,
1160                  (unsigned short __user *)(d + 0x0a));
1161         if (addr_modes.default_mode == VM86) {
1162             FPU_put_user((instruction_address.
1163                       offset & 0xf0000) >> 4,
1164                      (unsigned short __user *)(d + 8));
1165             FPU_put_user((operand_address.offset & 0xf0000) >> 4,
1166                      (unsigned short __user *)(d + 0x0c));
1167         } else {
1168             FPU_put_user(instruction_address.selector,
1169                      (unsigned short __user *)(d + 8));
1170             FPU_put_user(operand_address.selector,
1171                      (unsigned short __user *)(d + 0x0c));
1172         }
1173         RE_ENTRANT_CHECK_ON;
1174         d += 0x0e;
1175     } else {
1176         RE_ENTRANT_CHECK_OFF;
1177         FPU_access_ok(d, 7 * 4);
1178 #ifdef PECULIAR_486
1179         control_word &= ~0xe080;
1180         /* An 80486 sets nearly all of the reserved bits to 1. */
1181         control_word |= 0xffff0040;
1182         partial_status = status_word() | 0xffff0000;
1183         fpu_tag_word |= 0xffff0000;
1184         I387->soft.fcs &= ~0xf8000000;
1185         I387->soft.fos |= 0xffff0000;
1186 #endif /* PECULIAR_486 */
1187         if (__copy_to_user(d, &control_word, 7 * 4))
1188             FPU_abort;
1189         RE_ENTRANT_CHECK_ON;
1190         d += 0x1c;
1191     }
1192 
1193     control_word |= CW_Exceptions;
1194     partial_status &= ~(SW_Summary | SW_Backward);
1195 
1196     return d;
1197 }
1198 
1199 void fsave(fpu_addr_modes addr_modes, u_char __user *data_address)
1200 {
1201     u_char __user *d;
1202     int offset = (top & 7) * 10, other = 80 - offset;
1203 
1204     d = fstenv(addr_modes, data_address);
1205 
1206     RE_ENTRANT_CHECK_OFF;
1207     FPU_access_ok(d, 80);
1208 
1209     /* Copy all registers in stack order. */
1210     if (__copy_to_user(d, register_base + offset, other))
1211         FPU_abort;
1212     if (offset)
1213         if (__copy_to_user(d + other, register_base, offset))
1214             FPU_abort;
1215     RE_ENTRANT_CHECK_ON;
1216 
1217     finit();
1218 }
1219 
1220 /*===========================================================================*/