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

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