Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.13 1370 lines 34 kB view raw
1/*---------------------------------------------------------------------------+ 2 | reg_ld_str.c | 3 | | 4 | All of the functions which transfer data between user memory and FPU_REGs.| 5 | | 6 | Copyright (C) 1992,1993,1994,1996,1997 | 7 | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | 8 | E-mail billm@suburbia.net | 9 | | 10 | | 11 +---------------------------------------------------------------------------*/ 12 13/*---------------------------------------------------------------------------+ 14 | Note: | 15 | The file contains code which accesses user memory. | 16 | Emulator static data may change when user memory is accessed, due to | 17 | other processes using the emulator while swapping is in progress. | 18 +---------------------------------------------------------------------------*/ 19 20#include "fpu_emu.h" 21 22#include <asm/uaccess.h> 23 24#include "fpu_system.h" 25#include "exception.h" 26#include "reg_constant.h" 27#include "control_w.h" 28#include "status_w.h" 29 30 31#define DOUBLE_Emax 1023 /* largest valid exponent */ 32#define DOUBLE_Ebias 1023 33#define DOUBLE_Emin (-1022) /* smallest valid exponent */ 34 35#define SINGLE_Emax 127 /* largest valid exponent */ 36#define SINGLE_Ebias 127 37#define SINGLE_Emin (-126) /* smallest valid exponent */ 38 39 40static u_char normalize_no_excep(FPU_REG *r, int exp, int sign) 41{ 42 u_char tag; 43 44 setexponent16(r, exp); 45 46 tag = FPU_normalize_nuo(r); 47 stdexp(r); 48 if ( sign ) 49 setnegative(r); 50 51 return tag; 52} 53 54 55int FPU_tagof(FPU_REG *ptr) 56{ 57 int exp; 58 59 exp = exponent16(ptr) & 0x7fff; 60 if ( exp == 0 ) 61 { 62 if ( !(ptr->sigh | ptr->sigl) ) 63 { 64 return TAG_Zero; 65 } 66 /* The number is a de-normal or pseudodenormal. */ 67 return TAG_Special; 68 } 69 70 if ( exp == 0x7fff ) 71 { 72 /* Is an Infinity, a NaN, or an unsupported data type. */ 73 return TAG_Special; 74 } 75 76 if ( !(ptr->sigh & 0x80000000) ) 77 { 78 /* Unsupported data type. */ 79 /* Valid numbers have the ms bit set to 1. */ 80 /* Unnormal. */ 81 return TAG_Special; 82 } 83 84 return TAG_Valid; 85} 86 87 88/* Get a long double from user memory */ 89int FPU_load_extended(long double __user *s, int stnr) 90{ 91 FPU_REG *sti_ptr = &st(stnr); 92 93 RE_ENTRANT_CHECK_OFF; 94 FPU_access_ok(VERIFY_READ, s, 10); 95 __copy_from_user(sti_ptr, s, 10); 96 RE_ENTRANT_CHECK_ON; 97 98 return FPU_tagof(sti_ptr); 99} 100 101 102/* Get a double from user memory */ 103int FPU_load_double(double __user *dfloat, FPU_REG *loaded_data) 104{ 105 int exp, tag, negative; 106 unsigned m64, l64; 107 108 RE_ENTRANT_CHECK_OFF; 109 FPU_access_ok(VERIFY_READ, dfloat, 8); 110 FPU_get_user(m64, 1 + (unsigned long __user *) dfloat); 111 FPU_get_user(l64, (unsigned long __user *) dfloat); 112 RE_ENTRANT_CHECK_ON; 113 114 negative = (m64 & 0x80000000) ? SIGN_Negative : SIGN_Positive; 115 exp = ((m64 & 0x7ff00000) >> 20) - DOUBLE_Ebias + EXTENDED_Ebias; 116 m64 &= 0xfffff; 117 if ( exp > DOUBLE_Emax + EXTENDED_Ebias ) 118 { 119 /* Infinity or NaN */ 120 if ((m64 == 0) && (l64 == 0)) 121 { 122 /* +- infinity */ 123 loaded_data->sigh = 0x80000000; 124 loaded_data->sigl = 0x00000000; 125 exp = EXP_Infinity + EXTENDED_Ebias; 126 tag = TAG_Special; 127 } 128 else 129 { 130 /* Must be a signaling or quiet NaN */ 131 exp = EXP_NaN + EXTENDED_Ebias; 132 loaded_data->sigh = (m64 << 11) | 0x80000000; 133 loaded_data->sigh |= l64 >> 21; 134 loaded_data->sigl = l64 << 11; 135 tag = TAG_Special; /* The calling function must look for NaNs */ 136 } 137 } 138 else if ( exp < DOUBLE_Emin + EXTENDED_Ebias ) 139 { 140 /* Zero or de-normal */ 141 if ((m64 == 0) && (l64 == 0)) 142 { 143 /* Zero */ 144 reg_copy(&CONST_Z, loaded_data); 145 exp = 0; 146 tag = TAG_Zero; 147 } 148 else 149 { 150 /* De-normal */ 151 loaded_data->sigh = m64 << 11; 152 loaded_data->sigh |= l64 >> 21; 153 loaded_data->sigl = l64 << 11; 154 155 return normalize_no_excep(loaded_data, DOUBLE_Emin, negative) 156 | (denormal_operand() < 0 ? FPU_Exception : 0); 157 } 158 } 159 else 160 { 161 loaded_data->sigh = (m64 << 11) | 0x80000000; 162 loaded_data->sigh |= l64 >> 21; 163 loaded_data->sigl = l64 << 11; 164 165 tag = TAG_Valid; 166 } 167 168 setexponent16(loaded_data, exp | negative); 169 170 return tag; 171} 172 173 174/* Get a float from user memory */ 175int FPU_load_single(float __user *single, FPU_REG *loaded_data) 176{ 177 unsigned m32; 178 int exp, tag, negative; 179 180 RE_ENTRANT_CHECK_OFF; 181 FPU_access_ok(VERIFY_READ, single, 4); 182 FPU_get_user(m32, (unsigned long __user *) single); 183 RE_ENTRANT_CHECK_ON; 184 185 negative = (m32 & 0x80000000) ? SIGN_Negative : SIGN_Positive; 186 187 if (!(m32 & 0x7fffffff)) 188 { 189 /* Zero */ 190 reg_copy(&CONST_Z, loaded_data); 191 addexponent(loaded_data, negative); 192 return TAG_Zero; 193 } 194 exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias + EXTENDED_Ebias; 195 m32 = (m32 & 0x7fffff) << 8; 196 if ( exp < SINGLE_Emin + EXTENDED_Ebias ) 197 { 198 /* De-normals */ 199 loaded_data->sigh = m32; 200 loaded_data->sigl = 0; 201 202 return normalize_no_excep(loaded_data, SINGLE_Emin, negative) 203 | (denormal_operand() < 0 ? FPU_Exception : 0); 204 } 205 else if ( exp > SINGLE_Emax + EXTENDED_Ebias ) 206 { 207 /* Infinity or NaN */ 208 if ( m32 == 0 ) 209 { 210 /* +- infinity */ 211 loaded_data->sigh = 0x80000000; 212 loaded_data->sigl = 0x00000000; 213 exp = EXP_Infinity + EXTENDED_Ebias; 214 tag = TAG_Special; 215 } 216 else 217 { 218 /* Must be a signaling or quiet NaN */ 219 exp = EXP_NaN + EXTENDED_Ebias; 220 loaded_data->sigh = m32 | 0x80000000; 221 loaded_data->sigl = 0; 222 tag = TAG_Special; /* The calling function must look for NaNs */ 223 } 224 } 225 else 226 { 227 loaded_data->sigh = m32 | 0x80000000; 228 loaded_data->sigl = 0; 229 tag = TAG_Valid; 230 } 231 232 setexponent16(loaded_data, exp | negative); /* Set the sign. */ 233 234 return tag; 235} 236 237 238/* Get a long long from user memory */ 239int FPU_load_int64(long long __user *_s) 240{ 241 long long s; 242 int sign; 243 FPU_REG *st0_ptr = &st(0); 244 245 RE_ENTRANT_CHECK_OFF; 246 FPU_access_ok(VERIFY_READ, _s, 8); 247 copy_from_user(&s,_s,8); 248 RE_ENTRANT_CHECK_ON; 249 250 if (s == 0) 251 { 252 reg_copy(&CONST_Z, st0_ptr); 253 return TAG_Zero; 254 } 255 256 if (s > 0) 257 sign = SIGN_Positive; 258 else 259 { 260 s = -s; 261 sign = SIGN_Negative; 262 } 263 264 significand(st0_ptr) = s; 265 266 return normalize_no_excep(st0_ptr, 63, sign); 267} 268 269 270/* Get a long from user memory */ 271int FPU_load_int32(long __user *_s, FPU_REG *loaded_data) 272{ 273 long s; 274 int negative; 275 276 RE_ENTRANT_CHECK_OFF; 277 FPU_access_ok(VERIFY_READ, _s, 4); 278 FPU_get_user(s, _s); 279 RE_ENTRANT_CHECK_ON; 280 281 if (s == 0) 282 { reg_copy(&CONST_Z, loaded_data); return TAG_Zero; } 283 284 if (s > 0) 285 negative = SIGN_Positive; 286 else 287 { 288 s = -s; 289 negative = SIGN_Negative; 290 } 291 292 loaded_data->sigh = s; 293 loaded_data->sigl = 0; 294 295 return normalize_no_excep(loaded_data, 31, negative); 296} 297 298 299/* Get a short from user memory */ 300int FPU_load_int16(short __user *_s, FPU_REG *loaded_data) 301{ 302 int s, negative; 303 304 RE_ENTRANT_CHECK_OFF; 305 FPU_access_ok(VERIFY_READ, _s, 2); 306 /* Cast as short to get the sign extended. */ 307 FPU_get_user(s, _s); 308 RE_ENTRANT_CHECK_ON; 309 310 if (s == 0) 311 { reg_copy(&CONST_Z, loaded_data); return TAG_Zero; } 312 313 if (s > 0) 314 negative = SIGN_Positive; 315 else 316 { 317 s = -s; 318 negative = SIGN_Negative; 319 } 320 321 loaded_data->sigh = s << 16; 322 loaded_data->sigl = 0; 323 324 return normalize_no_excep(loaded_data, 15, negative); 325} 326 327 328/* Get a packed bcd array from user memory */ 329int FPU_load_bcd(u_char __user *s) 330{ 331 FPU_REG *st0_ptr = &st(0); 332 int pos; 333 u_char bcd; 334 long long l=0; 335 int sign; 336 337 RE_ENTRANT_CHECK_OFF; 338 FPU_access_ok(VERIFY_READ, s, 10); 339 RE_ENTRANT_CHECK_ON; 340 for ( pos = 8; pos >= 0; pos--) 341 { 342 l *= 10; 343 RE_ENTRANT_CHECK_OFF; 344 FPU_get_user(bcd, s+pos); 345 RE_ENTRANT_CHECK_ON; 346 l += bcd >> 4; 347 l *= 10; 348 l += bcd & 0x0f; 349 } 350 351 RE_ENTRANT_CHECK_OFF; 352 FPU_get_user(sign, s+9); 353 sign = sign & 0x80 ? SIGN_Negative : SIGN_Positive; 354 RE_ENTRANT_CHECK_ON; 355 356 if ( l == 0 ) 357 { 358 reg_copy(&CONST_Z, st0_ptr); 359 addexponent(st0_ptr, sign); /* Set the sign. */ 360 return TAG_Zero; 361 } 362 else 363 { 364 significand(st0_ptr) = l; 365 return normalize_no_excep(st0_ptr, 63, sign); 366 } 367} 368 369/*===========================================================================*/ 370 371/* Put a long double into user memory */ 372int FPU_store_extended(FPU_REG *st0_ptr, u_char st0_tag, long double __user *d) 373{ 374 /* 375 The only exception raised by an attempt to store to an 376 extended format is the Invalid Stack exception, i.e. 377 attempting to store from an empty register. 378 */ 379 380 if ( st0_tag != TAG_Empty ) 381 { 382 RE_ENTRANT_CHECK_OFF; 383 FPU_access_ok(VERIFY_WRITE, d, 10); 384 385 FPU_put_user(st0_ptr->sigl, (unsigned long __user *) d); 386 FPU_put_user(st0_ptr->sigh, (unsigned long __user *) ((u_char __user *)d + 4)); 387 FPU_put_user(exponent16(st0_ptr), (unsigned short __user *) ((u_char __user *)d + 8)); 388 RE_ENTRANT_CHECK_ON; 389 390 return 1; 391 } 392 393 /* Empty register (stack underflow) */ 394 EXCEPTION(EX_StackUnder); 395 if ( control_word & CW_Invalid ) 396 { 397 /* The masked response */ 398 /* Put out the QNaN indefinite */ 399 RE_ENTRANT_CHECK_OFF; 400 FPU_access_ok(VERIFY_WRITE,d,10); 401 FPU_put_user(0, (unsigned long __user *) d); 402 FPU_put_user(0xc0000000, 1 + (unsigned long __user *) d); 403 FPU_put_user(0xffff, 4 + (short __user *) d); 404 RE_ENTRANT_CHECK_ON; 405 return 1; 406 } 407 else 408 return 0; 409 410} 411 412 413/* Put a double into user memory */ 414int FPU_store_double(FPU_REG *st0_ptr, u_char st0_tag, double __user *dfloat) 415{ 416 unsigned long l[2]; 417 unsigned long increment = 0; /* avoid gcc warnings */ 418 int precision_loss; 419 int exp; 420 FPU_REG tmp; 421 422 if ( st0_tag == TAG_Valid ) 423 { 424 reg_copy(st0_ptr, &tmp); 425 exp = exponent(&tmp); 426 427 if ( exp < DOUBLE_Emin ) /* It may be a denormal */ 428 { 429 addexponent(&tmp, -DOUBLE_Emin + 52); /* largest exp to be 51 */ 430 431 denormal_arg: 432 433 if ( (precision_loss = FPU_round_to_int(&tmp, st0_tag)) ) 434 { 435#ifdef PECULIAR_486 436 /* Did it round to a non-denormal ? */ 437 /* This behaviour might be regarded as peculiar, it appears 438 that the 80486 rounds to the dest precision, then 439 converts to decide underflow. */ 440 if ( !((tmp.sigh == 0x00100000) && (tmp.sigl == 0) && 441 (st0_ptr->sigl & 0x000007ff)) ) 442#endif /* PECULIAR_486 */ 443 { 444 EXCEPTION(EX_Underflow); 445 /* This is a special case: see sec 16.2.5.1 of 446 the 80486 book */ 447 if ( !(control_word & CW_Underflow) ) 448 return 0; 449 } 450 EXCEPTION(precision_loss); 451 if ( !(control_word & CW_Precision) ) 452 return 0; 453 } 454 l[0] = tmp.sigl; 455 l[1] = tmp.sigh; 456 } 457 else 458 { 459 if ( tmp.sigl & 0x000007ff ) 460 { 461 precision_loss = 1; 462 switch (control_word & CW_RC) 463 { 464 case RC_RND: 465 /* Rounding can get a little messy.. */ 466 increment = ((tmp.sigl & 0x7ff) > 0x400) | /* nearest */ 467 ((tmp.sigl & 0xc00) == 0xc00); /* odd -> even */ 468 break; 469 case RC_DOWN: /* towards -infinity */ 470 increment = signpositive(&tmp) ? 0 : tmp.sigl & 0x7ff; 471 break; 472 case RC_UP: /* towards +infinity */ 473 increment = signpositive(&tmp) ? tmp.sigl & 0x7ff : 0; 474 break; 475 case RC_CHOP: 476 increment = 0; 477 break; 478 } 479 480 /* Truncate the mantissa */ 481 tmp.sigl &= 0xfffff800; 482 483 if ( increment ) 484 { 485 if ( tmp.sigl >= 0xfffff800 ) 486 { 487 /* the sigl part overflows */ 488 if ( tmp.sigh == 0xffffffff ) 489 { 490 /* The sigh part overflows */ 491 tmp.sigh = 0x80000000; 492 exp++; 493 if (exp >= EXP_OVER) 494 goto overflow; 495 } 496 else 497 { 498 tmp.sigh ++; 499 } 500 tmp.sigl = 0x00000000; 501 } 502 else 503 { 504 /* We only need to increment sigl */ 505 tmp.sigl += 0x00000800; 506 } 507 } 508 } 509 else 510 precision_loss = 0; 511 512 l[0] = (tmp.sigl >> 11) | (tmp.sigh << 21); 513 l[1] = ((tmp.sigh >> 11) & 0xfffff); 514 515 if ( exp > DOUBLE_Emax ) 516 { 517 overflow: 518 EXCEPTION(EX_Overflow); 519 if ( !(control_word & CW_Overflow) ) 520 return 0; 521 set_precision_flag_up(); 522 if ( !(control_word & CW_Precision) ) 523 return 0; 524 525 /* This is a special case: see sec 16.2.5.1 of the 80486 book */ 526 /* Overflow to infinity */ 527 l[0] = 0x00000000; /* Set to */ 528 l[1] = 0x7ff00000; /* + INF */ 529 } 530 else 531 { 532 if ( precision_loss ) 533 { 534 if ( increment ) 535 set_precision_flag_up(); 536 else 537 set_precision_flag_down(); 538 } 539 /* Add the exponent */ 540 l[1] |= (((exp+DOUBLE_Ebias) & 0x7ff) << 20); 541 } 542 } 543 } 544 else if (st0_tag == TAG_Zero) 545 { 546 /* Number is zero */ 547 l[0] = 0; 548 l[1] = 0; 549 } 550 else if ( st0_tag == TAG_Special ) 551 { 552 st0_tag = FPU_Special(st0_ptr); 553 if ( st0_tag == TW_Denormal ) 554 { 555 /* A denormal will always underflow. */ 556#ifndef PECULIAR_486 557 /* An 80486 is supposed to be able to generate 558 a denormal exception here, but... */ 559 /* Underflow has priority. */ 560 if ( control_word & CW_Underflow ) 561 denormal_operand(); 562#endif /* PECULIAR_486 */ 563 reg_copy(st0_ptr, &tmp); 564 goto denormal_arg; 565 } 566 else if (st0_tag == TW_Infinity) 567 { 568 l[0] = 0; 569 l[1] = 0x7ff00000; 570 } 571 else if (st0_tag == TW_NaN) 572 { 573 /* Is it really a NaN ? */ 574 if ( (exponent(st0_ptr) == EXP_OVER) 575 && (st0_ptr->sigh & 0x80000000) ) 576 { 577 /* See if we can get a valid NaN from the FPU_REG */ 578 l[0] = (st0_ptr->sigl >> 11) | (st0_ptr->sigh << 21); 579 l[1] = ((st0_ptr->sigh >> 11) & 0xfffff); 580 if ( !(st0_ptr->sigh & 0x40000000) ) 581 { 582 /* It is a signalling NaN */ 583 EXCEPTION(EX_Invalid); 584 if ( !(control_word & CW_Invalid) ) 585 return 0; 586 l[1] |= (0x40000000 >> 11); 587 } 588 l[1] |= 0x7ff00000; 589 } 590 else 591 { 592 /* It is an unsupported data type */ 593 EXCEPTION(EX_Invalid); 594 if ( !(control_word & CW_Invalid) ) 595 return 0; 596 l[0] = 0; 597 l[1] = 0xfff80000; 598 } 599 } 600 } 601 else if ( st0_tag == TAG_Empty ) 602 { 603 /* Empty register (stack underflow) */ 604 EXCEPTION(EX_StackUnder); 605 if ( control_word & CW_Invalid ) 606 { 607 /* The masked response */ 608 /* Put out the QNaN indefinite */ 609 RE_ENTRANT_CHECK_OFF; 610 FPU_access_ok(VERIFY_WRITE,dfloat,8); 611 FPU_put_user(0, (unsigned long __user *) dfloat); 612 FPU_put_user(0xfff80000, 1 + (unsigned long __user *) dfloat); 613 RE_ENTRANT_CHECK_ON; 614 return 1; 615 } 616 else 617 return 0; 618 } 619 if ( getsign(st0_ptr) ) 620 l[1] |= 0x80000000; 621 622 RE_ENTRANT_CHECK_OFF; 623 FPU_access_ok(VERIFY_WRITE,dfloat,8); 624 FPU_put_user(l[0], (unsigned long __user *)dfloat); 625 FPU_put_user(l[1], 1 + (unsigned long __user *)dfloat); 626 RE_ENTRANT_CHECK_ON; 627 628 return 1; 629} 630 631 632/* Put a float into user memory */ 633int FPU_store_single(FPU_REG *st0_ptr, u_char st0_tag, float __user *single) 634{ 635 long templ = 0; 636 unsigned long increment = 0; /* avoid gcc warnings */ 637 int precision_loss; 638 int exp; 639 FPU_REG tmp; 640 641 if ( st0_tag == TAG_Valid ) 642 { 643 644 reg_copy(st0_ptr, &tmp); 645 exp = exponent(&tmp); 646 647 if ( exp < SINGLE_Emin ) 648 { 649 addexponent(&tmp, -SINGLE_Emin + 23); /* largest exp to be 22 */ 650 651 denormal_arg: 652 653 if ( (precision_loss = FPU_round_to_int(&tmp, st0_tag)) ) 654 { 655#ifdef PECULIAR_486 656 /* Did it round to a non-denormal ? */ 657 /* This behaviour might be regarded as peculiar, it appears 658 that the 80486 rounds to the dest precision, then 659 converts to decide underflow. */ 660 if ( !((tmp.sigl == 0x00800000) && 661 ((st0_ptr->sigh & 0x000000ff) || st0_ptr->sigl)) ) 662#endif /* PECULIAR_486 */ 663 { 664 EXCEPTION(EX_Underflow); 665 /* This is a special case: see sec 16.2.5.1 of 666 the 80486 book */ 667 if ( !(control_word & CW_Underflow) ) 668 return 0; 669 } 670 EXCEPTION(precision_loss); 671 if ( !(control_word & CW_Precision) ) 672 return 0; 673 } 674 templ = tmp.sigl; 675 } 676 else 677 { 678 if ( tmp.sigl | (tmp.sigh & 0x000000ff) ) 679 { 680 unsigned long sigh = tmp.sigh; 681 unsigned long sigl = tmp.sigl; 682 683 precision_loss = 1; 684 switch (control_word & CW_RC) 685 { 686 case RC_RND: 687 increment = ((sigh & 0xff) > 0x80) /* more than half */ 688 || (((sigh & 0xff) == 0x80) && sigl) /* more than half */ 689 || ((sigh & 0x180) == 0x180); /* round to even */ 690 break; 691 case RC_DOWN: /* towards -infinity */ 692 increment = signpositive(&tmp) 693 ? 0 : (sigl | (sigh & 0xff)); 694 break; 695 case RC_UP: /* towards +infinity */ 696 increment = signpositive(&tmp) 697 ? (sigl | (sigh & 0xff)) : 0; 698 break; 699 case RC_CHOP: 700 increment = 0; 701 break; 702 } 703 704 /* Truncate part of the mantissa */ 705 tmp.sigl = 0; 706 707 if (increment) 708 { 709 if ( sigh >= 0xffffff00 ) 710 { 711 /* The sigh part overflows */ 712 tmp.sigh = 0x80000000; 713 exp++; 714 if ( exp >= EXP_OVER ) 715 goto overflow; 716 } 717 else 718 { 719 tmp.sigh &= 0xffffff00; 720 tmp.sigh += 0x100; 721 } 722 } 723 else 724 { 725 tmp.sigh &= 0xffffff00; /* Finish the truncation */ 726 } 727 } 728 else 729 precision_loss = 0; 730 731 templ = (tmp.sigh >> 8) & 0x007fffff; 732 733 if ( exp > SINGLE_Emax ) 734 { 735 overflow: 736 EXCEPTION(EX_Overflow); 737 if ( !(control_word & CW_Overflow) ) 738 return 0; 739 set_precision_flag_up(); 740 if ( !(control_word & CW_Precision) ) 741 return 0; 742 743 /* This is a special case: see sec 16.2.5.1 of the 80486 book. */ 744 /* Masked response is overflow to infinity. */ 745 templ = 0x7f800000; 746 } 747 else 748 { 749 if ( precision_loss ) 750 { 751 if ( increment ) 752 set_precision_flag_up(); 753 else 754 set_precision_flag_down(); 755 } 756 /* Add the exponent */ 757 templ |= ((exp+SINGLE_Ebias) & 0xff) << 23; 758 } 759 } 760 } 761 else if (st0_tag == TAG_Zero) 762 { 763 templ = 0; 764 } 765 else if ( st0_tag == TAG_Special ) 766 { 767 st0_tag = FPU_Special(st0_ptr); 768 if (st0_tag == TW_Denormal) 769 { 770 reg_copy(st0_ptr, &tmp); 771 772 /* A denormal will always underflow. */ 773#ifndef PECULIAR_486 774 /* An 80486 is supposed to be able to generate 775 a denormal exception here, but... */ 776 /* Underflow has priority. */ 777 if ( control_word & CW_Underflow ) 778 denormal_operand(); 779#endif /* PECULIAR_486 */ 780 goto denormal_arg; 781 } 782 else if (st0_tag == TW_Infinity) 783 { 784 templ = 0x7f800000; 785 } 786 else if (st0_tag == TW_NaN) 787 { 788 /* Is it really a NaN ? */ 789 if ( (exponent(st0_ptr) == EXP_OVER) && (st0_ptr->sigh & 0x80000000) ) 790 { 791 /* See if we can get a valid NaN from the FPU_REG */ 792 templ = st0_ptr->sigh >> 8; 793 if ( !(st0_ptr->sigh & 0x40000000) ) 794 { 795 /* It is a signalling NaN */ 796 EXCEPTION(EX_Invalid); 797 if ( !(control_word & CW_Invalid) ) 798 return 0; 799 templ |= (0x40000000 >> 8); 800 } 801 templ |= 0x7f800000; 802 } 803 else 804 { 805 /* It is an unsupported data type */ 806 EXCEPTION(EX_Invalid); 807 if ( !(control_word & CW_Invalid) ) 808 return 0; 809 templ = 0xffc00000; 810 } 811 } 812#ifdef PARANOID 813 else 814 { 815 EXCEPTION(EX_INTERNAL|0x164); 816 return 0; 817 } 818#endif 819 } 820 else if ( st0_tag == TAG_Empty ) 821 { 822 /* Empty register (stack underflow) */ 823 EXCEPTION(EX_StackUnder); 824 if ( control_word & EX_Invalid ) 825 { 826 /* The masked response */ 827 /* Put out the QNaN indefinite */ 828 RE_ENTRANT_CHECK_OFF; 829 FPU_access_ok(VERIFY_WRITE,single,4); 830 FPU_put_user(0xffc00000, (unsigned long __user *) single); 831 RE_ENTRANT_CHECK_ON; 832 return 1; 833 } 834 else 835 return 0; 836 } 837#ifdef PARANOID 838 else 839 { 840 EXCEPTION(EX_INTERNAL|0x163); 841 return 0; 842 } 843#endif 844 if ( getsign(st0_ptr) ) 845 templ |= 0x80000000; 846 847 RE_ENTRANT_CHECK_OFF; 848 FPU_access_ok(VERIFY_WRITE,single,4); 849 FPU_put_user(templ,(unsigned long __user *) single); 850 RE_ENTRANT_CHECK_ON; 851 852 return 1; 853} 854 855 856/* Put a long long into user memory */ 857int FPU_store_int64(FPU_REG *st0_ptr, u_char st0_tag, long long __user *d) 858{ 859 FPU_REG t; 860 long long tll; 861 int precision_loss; 862 863 if ( st0_tag == TAG_Empty ) 864 { 865 /* Empty register (stack underflow) */ 866 EXCEPTION(EX_StackUnder); 867 goto invalid_operand; 868 } 869 else if ( st0_tag == TAG_Special ) 870 { 871 st0_tag = FPU_Special(st0_ptr); 872 if ( (st0_tag == TW_Infinity) || 873 (st0_tag == TW_NaN) ) 874 { 875 EXCEPTION(EX_Invalid); 876 goto invalid_operand; 877 } 878 } 879 880 reg_copy(st0_ptr, &t); 881 precision_loss = FPU_round_to_int(&t, st0_tag); 882 ((long *)&tll)[0] = t.sigl; 883 ((long *)&tll)[1] = t.sigh; 884 if ( (precision_loss == 1) || 885 ((t.sigh & 0x80000000) && 886 !((t.sigh == 0x80000000) && (t.sigl == 0) && 887 signnegative(&t))) ) 888 { 889 EXCEPTION(EX_Invalid); 890 /* This is a special case: see sec 16.2.5.1 of the 80486 book */ 891 invalid_operand: 892 if ( control_word & EX_Invalid ) 893 { 894 /* Produce something like QNaN "indefinite" */ 895 tll = 0x8000000000000000LL; 896 } 897 else 898 return 0; 899 } 900 else 901 { 902 if ( precision_loss ) 903 set_precision_flag(precision_loss); 904 if ( signnegative(&t) ) 905 tll = - tll; 906 } 907 908 RE_ENTRANT_CHECK_OFF; 909 FPU_access_ok(VERIFY_WRITE,d,8); 910 copy_to_user(d, &tll, 8); 911 RE_ENTRANT_CHECK_ON; 912 913 return 1; 914} 915 916 917/* Put a long into user memory */ 918int FPU_store_int32(FPU_REG *st0_ptr, u_char st0_tag, long __user *d) 919{ 920 FPU_REG t; 921 int precision_loss; 922 923 if ( st0_tag == TAG_Empty ) 924 { 925 /* Empty register (stack underflow) */ 926 EXCEPTION(EX_StackUnder); 927 goto invalid_operand; 928 } 929 else if ( st0_tag == TAG_Special ) 930 { 931 st0_tag = FPU_Special(st0_ptr); 932 if ( (st0_tag == TW_Infinity) || 933 (st0_tag == TW_NaN) ) 934 { 935 EXCEPTION(EX_Invalid); 936 goto invalid_operand; 937 } 938 } 939 940 reg_copy(st0_ptr, &t); 941 precision_loss = FPU_round_to_int(&t, st0_tag); 942 if (t.sigh || 943 ((t.sigl & 0x80000000) && 944 !((t.sigl == 0x80000000) && signnegative(&t))) ) 945 { 946 EXCEPTION(EX_Invalid); 947 /* This is a special case: see sec 16.2.5.1 of the 80486 book */ 948 invalid_operand: 949 if ( control_word & EX_Invalid ) 950 { 951 /* Produce something like QNaN "indefinite" */ 952 t.sigl = 0x80000000; 953 } 954 else 955 return 0; 956 } 957 else 958 { 959 if ( precision_loss ) 960 set_precision_flag(precision_loss); 961 if ( signnegative(&t) ) 962 t.sigl = -(long)t.sigl; 963 } 964 965 RE_ENTRANT_CHECK_OFF; 966 FPU_access_ok(VERIFY_WRITE,d,4); 967 FPU_put_user(t.sigl, (unsigned long __user *) d); 968 RE_ENTRANT_CHECK_ON; 969 970 return 1; 971} 972 973 974/* Put a short into user memory */ 975int FPU_store_int16(FPU_REG *st0_ptr, u_char st0_tag, short __user *d) 976{ 977 FPU_REG t; 978 int precision_loss; 979 980 if ( st0_tag == TAG_Empty ) 981 { 982 /* Empty register (stack underflow) */ 983 EXCEPTION(EX_StackUnder); 984 goto invalid_operand; 985 } 986 else if ( st0_tag == TAG_Special ) 987 { 988 st0_tag = FPU_Special(st0_ptr); 989 if ( (st0_tag == TW_Infinity) || 990 (st0_tag == TW_NaN) ) 991 { 992 EXCEPTION(EX_Invalid); 993 goto invalid_operand; 994 } 995 } 996 997 reg_copy(st0_ptr, &t); 998 precision_loss = FPU_round_to_int(&t, st0_tag); 999 if (t.sigh || 1000 ((t.sigl & 0xffff8000) && 1001 !((t.sigl == 0x8000) && signnegative(&t))) ) 1002 { 1003 EXCEPTION(EX_Invalid); 1004 /* This is a special case: see sec 16.2.5.1 of the 80486 book */ 1005 invalid_operand: 1006 if ( control_word & EX_Invalid ) 1007 { 1008 /* Produce something like QNaN "indefinite" */ 1009 t.sigl = 0x8000; 1010 } 1011 else 1012 return 0; 1013 } 1014 else 1015 { 1016 if ( precision_loss ) 1017 set_precision_flag(precision_loss); 1018 if ( signnegative(&t) ) 1019 t.sigl = -t.sigl; 1020 } 1021 1022 RE_ENTRANT_CHECK_OFF; 1023 FPU_access_ok(VERIFY_WRITE,d,2); 1024 FPU_put_user((short)t.sigl, d); 1025 RE_ENTRANT_CHECK_ON; 1026 1027 return 1; 1028} 1029 1030 1031/* Put a packed bcd array into user memory */ 1032int FPU_store_bcd(FPU_REG *st0_ptr, u_char st0_tag, u_char __user *d) 1033{ 1034 FPU_REG t; 1035 unsigned long long ll; 1036 u_char b; 1037 int i, precision_loss; 1038 u_char sign = (getsign(st0_ptr) == SIGN_NEG) ? 0x80 : 0; 1039 1040 if ( st0_tag == TAG_Empty ) 1041 { 1042 /* Empty register (stack underflow) */ 1043 EXCEPTION(EX_StackUnder); 1044 goto invalid_operand; 1045 } 1046 else if ( st0_tag == TAG_Special ) 1047 { 1048 st0_tag = FPU_Special(st0_ptr); 1049 if ( (st0_tag == TW_Infinity) || 1050 (st0_tag == TW_NaN) ) 1051 { 1052 EXCEPTION(EX_Invalid); 1053 goto invalid_operand; 1054 } 1055 } 1056 1057 reg_copy(st0_ptr, &t); 1058 precision_loss = FPU_round_to_int(&t, st0_tag); 1059 ll = significand(&t); 1060 1061 /* Check for overflow, by comparing with 999999999999999999 decimal. */ 1062 if ( (t.sigh > 0x0de0b6b3) || 1063 ((t.sigh == 0x0de0b6b3) && (t.sigl > 0xa763ffff)) ) 1064 { 1065 EXCEPTION(EX_Invalid); 1066 /* This is a special case: see sec 16.2.5.1 of the 80486 book */ 1067 invalid_operand: 1068 if ( control_word & CW_Invalid ) 1069 { 1070 /* Produce the QNaN "indefinite" */ 1071 RE_ENTRANT_CHECK_OFF; 1072 FPU_access_ok(VERIFY_WRITE,d,10); 1073 for ( i = 0; i < 7; i++) 1074 FPU_put_user(0, d+i); /* These bytes "undefined" */ 1075 FPU_put_user(0xc0, d+7); /* This byte "undefined" */ 1076 FPU_put_user(0xff, d+8); 1077 FPU_put_user(0xff, d+9); 1078 RE_ENTRANT_CHECK_ON; 1079 return 1; 1080 } 1081 else 1082 return 0; 1083 } 1084 else if ( precision_loss ) 1085 { 1086 /* Precision loss doesn't stop the data transfer */ 1087 set_precision_flag(precision_loss); 1088 } 1089 1090 RE_ENTRANT_CHECK_OFF; 1091 FPU_access_ok(VERIFY_WRITE,d,10); 1092 RE_ENTRANT_CHECK_ON; 1093 for ( i = 0; i < 9; i++) 1094 { 1095 b = FPU_div_small(&ll, 10); 1096 b |= (FPU_div_small(&ll, 10)) << 4; 1097 RE_ENTRANT_CHECK_OFF; 1098 FPU_put_user(b, d+i); 1099 RE_ENTRANT_CHECK_ON; 1100 } 1101 RE_ENTRANT_CHECK_OFF; 1102 FPU_put_user(sign, d+9); 1103 RE_ENTRANT_CHECK_ON; 1104 1105 return 1; 1106} 1107 1108/*===========================================================================*/ 1109 1110/* r gets mangled such that sig is int, sign: 1111 it is NOT normalized */ 1112/* The return value (in eax) is zero if the result is exact, 1113 if bits are changed due to rounding, truncation, etc, then 1114 a non-zero value is returned */ 1115/* Overflow is signalled by a non-zero return value (in eax). 1116 In the case of overflow, the returned significand always has the 1117 largest possible value */ 1118int FPU_round_to_int(FPU_REG *r, u_char tag) 1119{ 1120 u_char very_big; 1121 unsigned eax; 1122 1123 if (tag == TAG_Zero) 1124 { 1125 /* Make sure that zero is returned */ 1126 significand(r) = 0; 1127 return 0; /* o.k. */ 1128 } 1129 1130 if (exponent(r) > 63) 1131 { 1132 r->sigl = r->sigh = ~0; /* The largest representable number */ 1133 return 1; /* overflow */ 1134 } 1135 1136 eax = FPU_shrxs(&r->sigl, 63 - exponent(r)); 1137 very_big = !(~(r->sigh) | ~(r->sigl)); /* test for 0xfff...fff */ 1138#define half_or_more (eax & 0x80000000) 1139#define frac_part (eax) 1140#define more_than_half ((eax & 0x80000001) == 0x80000001) 1141 switch (control_word & CW_RC) 1142 { 1143 case RC_RND: 1144 if ( more_than_half /* nearest */ 1145 || (half_or_more && (r->sigl & 1)) ) /* odd -> even */ 1146 { 1147 if ( very_big ) return 1; /* overflow */ 1148 significand(r) ++; 1149 return PRECISION_LOST_UP; 1150 } 1151 break; 1152 case RC_DOWN: 1153 if (frac_part && getsign(r)) 1154 { 1155 if ( very_big ) return 1; /* overflow */ 1156 significand(r) ++; 1157 return PRECISION_LOST_UP; 1158 } 1159 break; 1160 case RC_UP: 1161 if (frac_part && !getsign(r)) 1162 { 1163 if ( very_big ) return 1; /* overflow */ 1164 significand(r) ++; 1165 return PRECISION_LOST_UP; 1166 } 1167 break; 1168 case RC_CHOP: 1169 break; 1170 } 1171 1172 return eax ? PRECISION_LOST_DOWN : 0; 1173 1174} 1175 1176/*===========================================================================*/ 1177 1178u_char __user *fldenv(fpu_addr_modes addr_modes, u_char __user *s) 1179{ 1180 unsigned short tag_word = 0; 1181 u_char tag; 1182 int i; 1183 1184 if ( (addr_modes.default_mode == VM86) || 1185 ((addr_modes.default_mode == PM16) 1186 ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX)) ) 1187 { 1188 RE_ENTRANT_CHECK_OFF; 1189 FPU_access_ok(VERIFY_READ, s, 0x0e); 1190 FPU_get_user(control_word, (unsigned short __user *) s); 1191 FPU_get_user(partial_status, (unsigned short __user *) (s+2)); 1192 FPU_get_user(tag_word, (unsigned short __user *) (s+4)); 1193 FPU_get_user(instruction_address.offset, (unsigned short __user *) (s+6)); 1194 FPU_get_user(instruction_address.selector, (unsigned short __user *) (s+8)); 1195 FPU_get_user(operand_address.offset, (unsigned short __user *) (s+0x0a)); 1196 FPU_get_user(operand_address.selector, (unsigned short __user *) (s+0x0c)); 1197 RE_ENTRANT_CHECK_ON; 1198 s += 0x0e; 1199 if ( addr_modes.default_mode == VM86 ) 1200 { 1201 instruction_address.offset 1202 += (instruction_address.selector & 0xf000) << 4; 1203 operand_address.offset += (operand_address.selector & 0xf000) << 4; 1204 } 1205 } 1206 else 1207 { 1208 RE_ENTRANT_CHECK_OFF; 1209 FPU_access_ok(VERIFY_READ, s, 0x1c); 1210 FPU_get_user(control_word, (unsigned short __user *) s); 1211 FPU_get_user(partial_status, (unsigned short __user *) (s+4)); 1212 FPU_get_user(tag_word, (unsigned short __user *) (s+8)); 1213 FPU_get_user(instruction_address.offset, (unsigned long __user *) (s+0x0c)); 1214 FPU_get_user(instruction_address.selector, (unsigned short __user *) (s+0x10)); 1215 FPU_get_user(instruction_address.opcode, (unsigned short __user *) (s+0x12)); 1216 FPU_get_user(operand_address.offset, (unsigned long __user *) (s+0x14)); 1217 FPU_get_user(operand_address.selector, (unsigned long __user *) (s+0x18)); 1218 RE_ENTRANT_CHECK_ON; 1219 s += 0x1c; 1220 } 1221 1222#ifdef PECULIAR_486 1223 control_word &= ~0xe080; 1224#endif /* PECULIAR_486 */ 1225 1226 top = (partial_status >> SW_Top_Shift) & 7; 1227 1228 if ( partial_status & ~control_word & CW_Exceptions ) 1229 partial_status |= (SW_Summary | SW_Backward); 1230 else 1231 partial_status &= ~(SW_Summary | SW_Backward); 1232 1233 for ( i = 0; i < 8; i++ ) 1234 { 1235 tag = tag_word & 3; 1236 tag_word >>= 2; 1237 1238 if ( tag == TAG_Empty ) 1239 /* New tag is empty. Accept it */ 1240 FPU_settag(i, TAG_Empty); 1241 else if ( FPU_gettag(i) == TAG_Empty ) 1242 { 1243 /* Old tag is empty and new tag is not empty. New tag is determined 1244 by old reg contents */ 1245 if ( exponent(&fpu_register(i)) == - EXTENDED_Ebias ) 1246 { 1247 if ( !(fpu_register(i).sigl | fpu_register(i).sigh) ) 1248 FPU_settag(i, TAG_Zero); 1249 else 1250 FPU_settag(i, TAG_Special); 1251 } 1252 else if ( exponent(&fpu_register(i)) == 0x7fff - EXTENDED_Ebias ) 1253 { 1254 FPU_settag(i, TAG_Special); 1255 } 1256 else if ( fpu_register(i).sigh & 0x80000000 ) 1257 FPU_settag(i, TAG_Valid); 1258 else 1259 FPU_settag(i, TAG_Special); /* An Un-normal */ 1260 } 1261 /* Else old tag is not empty and new tag is not empty. Old tag 1262 remains correct */ 1263 } 1264 1265 return s; 1266} 1267 1268 1269void frstor(fpu_addr_modes addr_modes, u_char __user *data_address) 1270{ 1271 int i, regnr; 1272 u_char __user *s = fldenv(addr_modes, data_address); 1273 int offset = (top & 7) * 10, other = 80 - offset; 1274 1275 /* Copy all registers in stack order. */ 1276 RE_ENTRANT_CHECK_OFF; 1277 FPU_access_ok(VERIFY_READ,s,80); 1278 __copy_from_user(register_base+offset, s, other); 1279 if ( offset ) 1280 __copy_from_user(register_base, s+other, offset); 1281 RE_ENTRANT_CHECK_ON; 1282 1283 for ( i = 0; i < 8; i++ ) 1284 { 1285 regnr = (i+top) & 7; 1286 if ( FPU_gettag(regnr) != TAG_Empty ) 1287 /* The loaded data over-rides all other cases. */ 1288 FPU_settag(regnr, FPU_tagof(&st(i))); 1289 } 1290 1291} 1292 1293 1294u_char __user *fstenv(fpu_addr_modes addr_modes, u_char __user *d) 1295{ 1296 if ( (addr_modes.default_mode == VM86) || 1297 ((addr_modes.default_mode == PM16) 1298 ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX)) ) 1299 { 1300 RE_ENTRANT_CHECK_OFF; 1301 FPU_access_ok(VERIFY_WRITE,d,14); 1302#ifdef PECULIAR_486 1303 FPU_put_user(control_word & ~0xe080, (unsigned long __user *) d); 1304#else 1305 FPU_put_user(control_word, (unsigned short __user *) d); 1306#endif /* PECULIAR_486 */ 1307 FPU_put_user(status_word(), (unsigned short __user *) (d+2)); 1308 FPU_put_user(fpu_tag_word, (unsigned short __user *) (d+4)); 1309 FPU_put_user(instruction_address.offset, (unsigned short __user *) (d+6)); 1310 FPU_put_user(operand_address.offset, (unsigned short __user *) (d+0x0a)); 1311 if ( addr_modes.default_mode == VM86 ) 1312 { 1313 FPU_put_user((instruction_address.offset & 0xf0000) >> 4, 1314 (unsigned short __user *) (d+8)); 1315 FPU_put_user((operand_address.offset & 0xf0000) >> 4, 1316 (unsigned short __user *) (d+0x0c)); 1317 } 1318 else 1319 { 1320 FPU_put_user(instruction_address.selector, (unsigned short __user *) (d+8)); 1321 FPU_put_user(operand_address.selector, (unsigned short __user *) (d+0x0c)); 1322 } 1323 RE_ENTRANT_CHECK_ON; 1324 d += 0x0e; 1325 } 1326 else 1327 { 1328 RE_ENTRANT_CHECK_OFF; 1329 FPU_access_ok(VERIFY_WRITE, d, 7*4); 1330#ifdef PECULIAR_486 1331 control_word &= ~0xe080; 1332 /* An 80486 sets nearly all of the reserved bits to 1. */ 1333 control_word |= 0xffff0040; 1334 partial_status = status_word() | 0xffff0000; 1335 fpu_tag_word |= 0xffff0000; 1336 I387.soft.fcs &= ~0xf8000000; 1337 I387.soft.fos |= 0xffff0000; 1338#endif /* PECULIAR_486 */ 1339 __copy_to_user(d, &control_word, 7*4); 1340 RE_ENTRANT_CHECK_ON; 1341 d += 0x1c; 1342 } 1343 1344 control_word |= CW_Exceptions; 1345 partial_status &= ~(SW_Summary | SW_Backward); 1346 1347 return d; 1348} 1349 1350 1351void fsave(fpu_addr_modes addr_modes, u_char __user *data_address) 1352{ 1353 u_char __user *d; 1354 int offset = (top & 7) * 10, other = 80 - offset; 1355 1356 d = fstenv(addr_modes, data_address); 1357 1358 RE_ENTRANT_CHECK_OFF; 1359 FPU_access_ok(VERIFY_WRITE,d,80); 1360 1361 /* Copy all registers in stack order. */ 1362 __copy_to_user(d, register_base+offset, other); 1363 if ( offset ) 1364 __copy_to_user(d+other, register_base, offset); 1365 RE_ENTRANT_CHECK_ON; 1366 1367 finit(); 1368} 1369 1370/*===========================================================================*/