root/arch/i386/math-emu/reg_ld_str.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. reg_load_extended
  2. reg_load_double
  3. reg_load_single
  4. reg_load_int64
  5. reg_load_int32
  6. reg_load_int16
  7. reg_load_bcd
  8. reg_store_extended
  9. reg_store_double
  10. reg_store_single
  11. reg_store_int64
  12. reg_store_int32
  13. reg_store_int16
  14. reg_store_bcd
  15. round_to_int
  16. fldenv
  17. frstor
  18. tag_word
  19. fstenv
  20. fsave
  21. write_to_extended

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

/* [previous][next][first][last][top][bottom][index][help] */