0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
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
0032 #define DOUBLE_Ebias 1023
0033 #define DOUBLE_Emin (-1022)
0034
0035 #define SINGLE_Emax 127
0036 #define SINGLE_Ebias 127
0037 #define SINGLE_Emin (-126)
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
0063 return TAG_Special;
0064 }
0065
0066 if (exp == 0x7fff) {
0067
0068 return TAG_Special;
0069 }
0070
0071 if (!(ptr->sigh & 0x80000000)) {
0072
0073
0074
0075 return TAG_Special;
0076 }
0077
0078 return TAG_Valid;
0079 }
0080
0081
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
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
0111 if ((m64 == 0) && (l64 == 0)) {
0112
0113 loaded_data->sigh = 0x80000000;
0114 loaded_data->sigl = 0x00000000;
0115 exp = EXP_Infinity + EXTENDED_Ebias;
0116 tag = TAG_Special;
0117 } else {
0118
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;
0124 }
0125 } else if (exp < DOUBLE_Emin + EXTENDED_Ebias) {
0126
0127 if ((m64 == 0) && (l64 == 0)) {
0128
0129 reg_copy(&CONST_Z, loaded_data);
0130 exp = 0;
0131 tag = TAG_Zero;
0132 } else {
0133
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
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
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
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
0185 if (m32 == 0) {
0186
0187 loaded_data->sigh = 0x80000000;
0188 loaded_data->sigl = 0x00000000;
0189 exp = EXP_Infinity + EXTENDED_Ebias;
0190 tag = TAG_Special;
0191 } else {
0192
0193 exp = EXP_NaN + EXTENDED_Ebias;
0194 loaded_data->sigh = m32 | 0x80000000;
0195 loaded_data->sigl = 0;
0196 tag = TAG_Special;
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);
0205
0206 return tag;
0207 }
0208
0209
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
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
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
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
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);
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
0337 int FPU_store_extended(FPU_REG *st0_ptr, u_char st0_tag,
0338 long double __user * d)
0339 {
0340
0341
0342
0343
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
0362 EXCEPTION(EX_StackUnder);
0363 if (control_word & CW_Invalid) {
0364
0365
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
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;
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) {
0394 addexponent(&tmp, -DOUBLE_Emin + 52);
0395 denormal_arg:
0396 if ((precision_loss = FPU_round_to_int(&tmp, st0_tag))) {
0397 #ifdef PECULIAR_486
0398
0399
0400
0401
0402 if (!
0403 ((tmp.sigh == 0x00100000) && (tmp.sigl == 0)
0404 && (st0_ptr->sigl & 0x000007ff)))
0405 #endif
0406 {
0407 EXCEPTION(EX_Underflow);
0408
0409
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
0425 increment = ((tmp.sigl & 0x7ff) > 0x400) |
0426 ((tmp.sigl & 0xc00) == 0xc00);
0427 break;
0428 case RC_DOWN:
0429 increment =
0430 signpositive(&tmp) ? 0 : tmp.
0431 sigl & 0x7ff;
0432 break;
0433 case RC_UP:
0434 increment =
0435 signpositive(&tmp) ? tmp.
0436 sigl & 0x7ff : 0;
0437 break;
0438 case RC_CHOP:
0439 increment = 0;
0440 break;
0441 }
0442
0443
0444 tmp.sigl &= 0xfffff800;
0445
0446 if (increment) {
0447 if (tmp.sigl >= 0xfffff800) {
0448
0449 if (tmp.sigh == 0xffffffff) {
0450
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
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
0480
0481 l[1] = 0x7ff00000;
0482 } else {
0483 if (precision_loss) {
0484 if (increment)
0485 set_precision_flag_up();
0486 else
0487 set_precision_flag_down();
0488 }
0489
0490 l[1] |= (((exp + DOUBLE_Ebias) & 0x7ff) << 20);
0491 }
0492 }
0493 } else if (st0_tag == TAG_Zero) {
0494
0495 } else if (st0_tag == TAG_Special) {
0496 st0_tag = FPU_Special(st0_ptr);
0497 if (st0_tag == TW_Denormal) {
0498
0499 #ifndef PECULIAR_486
0500
0501
0502
0503 if (control_word & CW_Underflow)
0504 denormal_operand();
0505 #endif
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
0512 if ((exponent(st0_ptr) == EXP_OVER)
0513 && (st0_ptr->sigh & 0x80000000)) {
0514
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
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
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
0537 EXCEPTION(EX_StackUnder);
0538 if (control_word & CW_Invalid) {
0539
0540
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
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;
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);
0579
0580 denormal_arg:
0581
0582 if ((precision_loss = FPU_round_to_int(&tmp, st0_tag))) {
0583 #ifdef PECULIAR_486
0584
0585
0586
0587
0588 if (!((tmp.sigl == 0x00800000) &&
0589 ((st0_ptr->sigh & 0x000000ff)
0590 || st0_ptr->sigl)))
0591 #endif
0592 {
0593 EXCEPTION(EX_Underflow);
0594
0595
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)
0613 ||(((sigh & 0xff) == 0x80) && sigl)
0614 ||((sigh & 0x180) == 0x180);
0615 break;
0616 case RC_DOWN:
0617 increment = signpositive(&tmp)
0618 ? 0 : (sigl | (sigh & 0xff));
0619 break;
0620 case RC_UP:
0621 increment = signpositive(&tmp)
0622 ? (sigl | (sigh & 0xff)) : 0;
0623 break;
0624 case RC_CHOP:
0625 increment = 0;
0626 break;
0627 }
0628
0629
0630 tmp.sigl = 0;
0631
0632 if (increment) {
0633 if (sigh >= 0xffffff00) {
0634
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;
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
0661
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
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
0682 #ifndef PECULIAR_486
0683
0684
0685
0686 if (control_word & CW_Underflow)
0687 denormal_operand();
0688 #endif
0689 goto denormal_arg;
0690 } else if (st0_tag == TW_Infinity) {
0691 templ = 0x7f800000;
0692 } else if (st0_tag == TW_NaN) {
0693
0694 if ((exponent(st0_ptr) == EXP_OVER)
0695 && (st0_ptr->sigh & 0x80000000)) {
0696
0697 templ = st0_ptr->sigh >> 8;
0698 if (!(st0_ptr->sigh & 0x40000000)) {
0699
0700 EXCEPTION(EX_Invalid);
0701 if (!(control_word & CW_Invalid))
0702 return 0;
0703 templ |= (0x40000000 >> 8);
0704 }
0705 templ |= 0x7f800000;
0706 } else {
0707
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
0722 EXCEPTION(EX_StackUnder);
0723 if (control_word & EX_Invalid) {
0724
0725
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
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
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
0780 invalid_operand:
0781 if (control_word & EX_Invalid) {
0782
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
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
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
0827 invalid_operand:
0828 if (control_word & EX_Invalid) {
0829
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
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
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
0873 invalid_operand:
0874 if (control_word & EX_Invalid) {
0875
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
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
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
0920 if ((t.sigh > 0x0de0b6b3) ||
0921 ((t.sigh == 0x0de0b6b3) && (t.sigl > 0xa763ffff))) {
0922 EXCEPTION(EX_Invalid);
0923
0924 invalid_operand:
0925 if (control_word & CW_Invalid) {
0926
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);
0931 FPU_put_user(0xc0, d + 7);
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
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
0963
0964
0965
0966
0967
0968
0969
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
0977 significand(r) = 0;
0978 return 0;
0979 }
0980
0981 if (exponent(r) > 63) {
0982 r->sigl = r->sigh = ~0;
0983 return 1;
0984 }
0985
0986 eax = FPU_shrxs(&r->sigl, 63 - exponent(r));
0987 very_big = !(~(r->sigh) | ~(r->sigl));
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
0994 || (half_or_more && (r->sigl & 1))) {
0995 if (very_big)
0996 return 1;
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;
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;
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
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
1094 FPU_settag(i, TAG_Empty);
1095 else if (FPU_gettag(i) == TAG_Empty) {
1096
1097
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);
1112 }
1113
1114
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
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
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
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
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
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
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