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 
  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 exp;
 470       FPU_REG tmp;
 471 
 472       reg_move(st0_ptr, &tmp);
 473       exp = tmp.exp - EXP_BIAS;
 474 
 475       if ( exp < DOUBLE_Emin )     /* It may be a denormal */
 476         {
 477           int precision_loss;
 478 
 479           /* A denormal will always underflow. */
 480 #ifndef PECULIAR_486
 481           /* An 80486 is supposed to be able to generate
 482              a denormal exception here, but... */
 483           if ( st0_ptr->exp <= EXP_UNDER )
 484             {
 485               /* Underflow has priority. */
 486               if ( control_word & CW_Underflow )
 487                 denormal_operand();
 488             }
 489 #endif PECULIAR_486
 490 
 491           tmp.exp += -DOUBLE_Emin + 52;  /* largest exp to be 51 */
 492 
 493           if ( (precision_loss = round_to_int(&tmp)) )
 494             {
 495 #ifdef PECULIAR_486
 496               /* Did it round to a non-denormal ? */
 497               /* This behaviour might be regarded as peculiar, it appears
 498                  that the 80486 rounds to the dest precision, then
 499                  converts to decide underflow. */
 500               if ( !((tmp.sigh == 0x00100000) && (tmp.sigl == 0) &&
 501                   (st0_ptr->sigl & 0x000007ff)) )
 502 #endif PECULIAR_486
 503                 {
 504                   EXCEPTION(EX_Underflow);
 505                   /* This is a special case: see sec 16.2.5.1 of
 506                      the 80486 book */
 507                   if ( !(control_word & CW_Underflow) )
 508                     return 0;
 509                 }
 510               EXCEPTION(precision_loss);
 511               if ( !(control_word & CW_Precision) )
 512                 return 0;
 513             }
 514           l[0] = tmp.sigl;
 515           l[1] = tmp.sigh;
 516         }
 517       else
 518         {
 519           if ( tmp.sigl & 0x000007ff )
 520             {
 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                   set_precision_flag_up();
 545 
 546                   if ( tmp.sigl >= 0xfffff800 )
 547                     {
 548                       /* the sigl part overflows */
 549                       if ( tmp.sigh == 0xffffffff )
 550                         {
 551                           /* The sigh part overflows */
 552                           tmp.sigh = 0x80000000;
 553                           exp++;
 554                           if (exp >= EXP_OVER)
 555                             goto overflow;
 556                         }
 557                       else
 558                         {
 559                           tmp.sigh ++;
 560                         }
 561                       tmp.sigl = 0x00000000;
 562                     }
 563                   else
 564                     {
 565                       /* We only need to increment sigl */
 566                       tmp.sigl += 0x00000800;
 567                     }
 568                 }
 569               else
 570                 set_precision_flag_down();
 571             }
 572           
 573           l[0] = (tmp.sigl >> 11) | (tmp.sigh << 21);
 574           l[1] = ((tmp.sigh >> 11) & 0xfffff);
 575 
 576           if ( exp > DOUBLE_Emax )
 577             {
 578             overflow:
 579               EXCEPTION(EX_Overflow);
 580               if ( !(control_word & CW_Overflow) )
 581                 return 0;
 582               set_precision_flag_up();
 583               if ( !(control_word & CW_Precision) )
 584                 return 0;
 585 
 586               /* This is a special case: see sec 16.2.5.1 of the 80486 book */
 587               /* Overflow to infinity */
 588               l[0] = 0x00000000;        /* Set to */
 589               l[1] = 0x7ff00000;        /* + INF */
 590             }
 591           else
 592             {
 593               /* Add the exponent */
 594               l[1] |= (((exp+DOUBLE_Ebias) & 0x7ff) << 20);
 595             }
 596         }
 597     }
 598   else if (st0_tag == TW_Zero)
 599     {
 600       /* Number is zero */
 601       l[0] = 0;
 602       l[1] = 0;
 603     }
 604   else if (st0_tag == TW_Infinity)
 605     {
 606       l[0] = 0;
 607       l[1] = 0x7ff00000;
 608     }
 609   else if (st0_tag == TW_NaN)
 610     {
 611       /* See if we can get a valid NaN from the FPU_REG */
 612       l[0] = (st0_ptr->sigl >> 11) | (st0_ptr->sigh << 21);
 613       l[1] = ((st0_ptr->sigh >> 11) & 0xfffff);
 614       if ( !(st0_ptr->sigh & 0x40000000) )
 615         {
 616           /* It is a signalling NaN */
 617           EXCEPTION(EX_Invalid);
 618           if ( !(control_word & CW_Invalid) )
 619             return 0;
 620           l[1] |= (0x40000000 >> 11);
 621         }
 622       l[1] |= 0x7ff00000;
 623     }
 624   else if ( st0_tag == TW_Empty )
 625     {
 626       /* Empty register (stack underflow) */
 627       EXCEPTION(EX_StackUnder);
 628       if ( control_word & CW_Invalid )
 629         {
 630           /* The masked response */
 631           /* Put out the QNaN indefinite */
 632           RE_ENTRANT_CHECK_OFF;
 633           FPU_verify_area(VERIFY_WRITE,(void *)dfloat,8);
 634           put_fs_long(0, (unsigned long *) dfloat);
 635           put_fs_long(0xfff80000, 1 + (unsigned long *) dfloat);
 636           RE_ENTRANT_CHECK_ON;
 637           return 1;
 638         }
 639       else
 640         return 0;
 641     }
 642   if ( st0_ptr->sign )
 643     l[1] |= 0x80000000;
 644 
 645   RE_ENTRANT_CHECK_OFF;
 646   FPU_verify_area(VERIFY_WRITE,(void *)dfloat,8);
 647   put_fs_long(l[0], (unsigned long *)dfloat);
 648   put_fs_long(l[1], 1 + (unsigned long *)dfloat);
 649   RE_ENTRANT_CHECK_ON;
 650 
 651   return 1;
 652 }
 653 
 654 
 655 /* Put a float into user memory */
 656 int reg_store_single(float *single, FPU_REG *st0_ptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 657 {
 658   long templ;
 659   unsigned long increment = 0;          /* avoid gcc warnings */
 660   char st0_tag = st0_ptr->tag;
 661 
 662   if (st0_tag == TW_Valid)
 663     {
 664       int exp;
 665       FPU_REG tmp;
 666 
 667       reg_move(st0_ptr, &tmp);
 668       exp = tmp.exp - EXP_BIAS;
 669 
 670       if ( exp < SINGLE_Emin )
 671         {
 672           int precision_loss;
 673 
 674           /* A denormal will always underflow. */
 675 #ifndef PECULIAR_486
 676           /* An 80486 is supposed to be able to generate
 677              a denormal exception here, but... */
 678           if ( st0_ptr->exp <= EXP_UNDER )
 679             {
 680               /* Underflow has priority. */
 681               if ( control_word & CW_Underflow )
 682                 denormal_operand();
 683             }
 684 #endif PECULIAR_486
 685 
 686           tmp.exp += -SINGLE_Emin + 23;  /* largest exp to be 22 */
 687 
 688           if ( (precision_loss = round_to_int(&tmp)) )
 689             {
 690 #ifdef PECULIAR_486
 691               /* Did it round to a non-denormal ? */
 692               /* This behaviour might be regarded as peculiar, it appears
 693                  that the 80486 rounds to the dest precision, then
 694                  converts to decide underflow. */
 695               if ( !((tmp.sigl == 0x00800000) &&
 696                   ((st0_ptr->sigh & 0x000000ff) || st0_ptr->sigl)) )
 697 #endif PECULIAR_486
 698                 {
 699                   EXCEPTION(EX_Underflow);
 700                   /* This is a special case: see sec 16.2.5.1 of
 701                      the 80486 book */
 702                   if ( !(control_word & EX_Underflow) )
 703                     return 0;
 704                 }
 705               EXCEPTION(precision_loss);
 706               if ( !(control_word & EX_Precision) )
 707                 return 0;
 708             }
 709           templ = tmp.sigl;
 710         }
 711       else
 712         {
 713           if ( tmp.sigl | (tmp.sigh & 0x000000ff) )
 714             {
 715               unsigned long sigh = tmp.sigh;
 716               unsigned long sigl = tmp.sigl;
 717               
 718               switch (control_word & CW_RC)
 719                 {
 720                 case RC_RND:
 721                   increment = ((sigh & 0xff) > 0x80)       /* more than half */
 722                     || (((sigh & 0xff) == 0x80) && sigl)   /* more than half */
 723                       || ((sigh & 0x180) == 0x180);        /* round to even */
 724                   break;
 725                 case RC_DOWN:   /* towards -infinity */
 726                   increment = (tmp.sign == SIGN_POS)
 727                               ? 0 : (sigl | (sigh & 0xff));
 728                   break;
 729                 case RC_UP:     /* towards +infinity */
 730                   increment = (tmp.sign == SIGN_POS)
 731                               ? (sigl | (sigh & 0xff)) : 0;
 732                   break;
 733                 case RC_CHOP:
 734                   increment = 0;
 735                   break;
 736                 }
 737           
 738               /* Truncate part of the mantissa */
 739               tmp.sigl = 0;
 740           
 741               if (increment)
 742                 {
 743                   set_precision_flag_up();
 744 
 745                   if ( sigh >= 0xffffff00 )
 746                     {
 747                       /* The sigh part overflows */
 748                       tmp.sigh = 0x80000000;
 749                       exp++;
 750                       if ( exp >= EXP_OVER )
 751                         goto overflow;
 752                     }
 753                   else
 754                     {
 755                       tmp.sigh &= 0xffffff00;
 756                       tmp.sigh += 0x100;
 757                     }
 758                 }
 759               else
 760                 {
 761                   set_precision_flag_down();
 762                   tmp.sigh &= 0xffffff00;  /* Finish the truncation */
 763                 }
 764             }
 765 
 766           templ = (tmp.sigh >> 8) & 0x007fffff;
 767 
 768           if ( exp > SINGLE_Emax )
 769             {
 770             overflow:
 771               EXCEPTION(EX_Overflow);
 772               if ( !(control_word & CW_Overflow) )
 773                 return 0;
 774               set_precision_flag_up();
 775               if ( !(control_word & CW_Precision) )
 776                 return 0;
 777 
 778               /* This is a special case: see sec 16.2.5.1 of the 80486 book. */
 779               /* Masked response is overflow to infinity. */
 780               templ = 0x7f800000;
 781             }
 782           else
 783             templ |= ((exp+SINGLE_Ebias) & 0xff) << 23;
 784         }
 785     }
 786   else if (st0_tag == TW_Zero)
 787     {
 788       templ = 0;
 789     }
 790   else if (st0_tag == TW_Infinity)
 791     {
 792       templ = 0x7f800000;
 793     }
 794   else if (st0_tag == TW_NaN)
 795     {
 796       /* See if we can get a valid NaN from the FPU_REG */
 797       templ = st0_ptr->sigh >> 8;
 798       if ( !(st0_ptr->sigh & 0x40000000) )
 799         {
 800           /* It is a signalling NaN */
 801           EXCEPTION(EX_Invalid);
 802           if ( !(control_word & CW_Invalid) )
 803             return 0;
 804           templ |= (0x40000000 >> 8);
 805         }
 806       templ |= 0x7f800000;
 807     }
 808   else if ( st0_tag == TW_Empty )
 809     {
 810       /* Empty register (stack underflow) */
 811       EXCEPTION(EX_StackUnder);
 812       if ( control_word & EX_Invalid )
 813         {
 814           /* The masked response */
 815           /* Put out the QNaN indefinite */
 816           RE_ENTRANT_CHECK_OFF;
 817           FPU_verify_area(VERIFY_WRITE,(void *)single,4);
 818           put_fs_long(0xffc00000, (unsigned long *) single);
 819           RE_ENTRANT_CHECK_ON;
 820           return 1;
 821         }
 822       else
 823         return 0;
 824     }
 825 #ifdef PARANOID
 826   else
 827     {
 828       EXCEPTION(EX_INTERNAL|0x163);
 829       return 0;
 830     }
 831 #endif
 832   if (st0_ptr->sign)
 833     templ |= 0x80000000;
 834 
 835   RE_ENTRANT_CHECK_OFF;
 836   FPU_verify_area(VERIFY_WRITE,(void *)single,4);
 837   put_fs_long(templ,(unsigned long *) single);
 838   RE_ENTRANT_CHECK_ON;
 839 
 840   return 1;
 841 }
 842 
 843 
 844 /* Put a long long into user memory */
 845 int reg_store_int64(long long *d, FPU_REG *st0_ptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 846 {
 847   FPU_REG t;
 848   long long tll;
 849   int precision_loss;
 850   char st0_tag = st0_ptr->tag;
 851 
 852   if ( st0_tag == TW_Empty )
 853     {
 854       /* Empty register (stack underflow) */
 855       EXCEPTION(EX_StackUnder);
 856       goto invalid_operand;
 857     }
 858   else if ( (st0_tag == TW_Infinity) ||
 859            (st0_tag == TW_NaN) )
 860     {
 861       EXCEPTION(EX_Invalid);
 862       goto invalid_operand;
 863     }
 864 
 865   reg_move(st0_ptr, &t);
 866   precision_loss = round_to_int(&t);
 867   ((long *)&tll)[0] = t.sigl;
 868   ((long *)&tll)[1] = t.sigh;
 869   if ( (precision_loss == 1) ||
 870       ((t.sigh & 0x80000000) &&
 871        !((t.sigh == 0x80000000) && (t.sigl == 0) &&
 872          (t.sign == SIGN_NEG))) )
 873     {
 874       EXCEPTION(EX_Invalid);
 875       /* This is a special case: see sec 16.2.5.1 of the 80486 book */
 876     invalid_operand:
 877       if ( control_word & EX_Invalid )
 878         {
 879           /* Produce something like QNaN "indefinite" */
 880           tll = 0x8000000000000000LL;
 881         }
 882       else
 883         return 0;
 884     }
 885   else
 886     {
 887       if ( precision_loss )
 888         set_precision_flag(precision_loss);
 889       if ( t.sign )
 890         tll = - tll;
 891     }
 892 
 893   RE_ENTRANT_CHECK_OFF;
 894   FPU_verify_area(VERIFY_WRITE,(void *)d,8);
 895   put_fs_long(((long *)&tll)[0],(unsigned long *) d);
 896   put_fs_long(((long *)&tll)[1],1 + (unsigned long *) d);
 897   RE_ENTRANT_CHECK_ON;
 898 
 899   return 1;
 900 }
 901 
 902 
 903 /* Put a long into user memory */
 904 int reg_store_int32(long *d, FPU_REG *st0_ptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 905 {
 906   FPU_REG t;
 907   int precision_loss;
 908   char st0_tag = st0_ptr->tag;
 909 
 910   if ( st0_tag == TW_Empty )
 911     {
 912       /* Empty register (stack underflow) */
 913       EXCEPTION(EX_StackUnder);
 914       goto invalid_operand;
 915     }
 916   else if ( (st0_tag == TW_Infinity) ||
 917            (st0_tag == TW_NaN) )
 918     {
 919       EXCEPTION(EX_Invalid);
 920       goto invalid_operand;
 921     }
 922 
 923   reg_move(st0_ptr, &t);
 924   precision_loss = round_to_int(&t);
 925   if (t.sigh ||
 926       ((t.sigl & 0x80000000) &&
 927        !((t.sigl == 0x80000000) && (t.sign == SIGN_NEG))) )
 928     {
 929       EXCEPTION(EX_Invalid);
 930       /* This is a special case: see sec 16.2.5.1 of the 80486 book */
 931     invalid_operand:
 932       if ( control_word & EX_Invalid )
 933         {
 934           /* Produce something like QNaN "indefinite" */
 935           t.sigl = 0x80000000;
 936         }
 937       else
 938         return 0;
 939     }
 940   else
 941     {
 942       if ( precision_loss )
 943         set_precision_flag(precision_loss);
 944       if ( t.sign )
 945         t.sigl = -(long)t.sigl;
 946     }
 947 
 948   RE_ENTRANT_CHECK_OFF;
 949   FPU_verify_area(VERIFY_WRITE,d,4);
 950   put_fs_long(t.sigl, (unsigned long *) d);
 951   RE_ENTRANT_CHECK_ON;
 952 
 953   return 1;
 954 }
 955 
 956 
 957 /* Put a short into user memory */
 958 int reg_store_int16(short *d, FPU_REG *st0_ptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 959 {
 960   FPU_REG t;
 961   int precision_loss;
 962   char st0_tag = st0_ptr->tag;
 963 
 964   if ( st0_tag == TW_Empty )
 965     {
 966       /* Empty register (stack underflow) */
 967       EXCEPTION(EX_StackUnder);
 968       goto invalid_operand;
 969     }
 970   else if ( (st0_tag == TW_Infinity) ||
 971            (st0_tag == TW_NaN) )
 972     {
 973       EXCEPTION(EX_Invalid);
 974       goto invalid_operand;
 975     }
 976 
 977   reg_move(st0_ptr, &t);
 978   precision_loss = round_to_int(&t);
 979   if (t.sigh ||
 980       ((t.sigl & 0xffff8000) &&
 981        !((t.sigl == 0x8000) && (t.sign == SIGN_NEG))) )
 982     {
 983       EXCEPTION(EX_Invalid);
 984       /* This is a special case: see sec 16.2.5.1 of the 80486 book */
 985     invalid_operand:
 986       if ( control_word & EX_Invalid )
 987         {
 988           /* Produce something like QNaN "indefinite" */
 989           t.sigl = 0x8000;
 990         }
 991       else
 992         return 0;
 993     }
 994   else
 995     {
 996       if ( precision_loss )
 997         set_precision_flag(precision_loss);
 998       if ( t.sign )
 999         t.sigl = -t.sigl;
1000     }
1001 
1002   RE_ENTRANT_CHECK_OFF;
1003   FPU_verify_area(VERIFY_WRITE,d,2);
1004   put_fs_word((short)t.sigl,(short *) d);
1005   RE_ENTRANT_CHECK_ON;
1006 
1007   return 1;
1008 }
1009 
1010 
1011 /* Put a packed bcd array into user memory */
1012 int reg_store_bcd(char *d, FPU_REG *st0_ptr)
     /* [previous][next][first][last][top][bottom][index][help] */
1013 {
1014   FPU_REG t;
1015   unsigned long long ll;
1016   unsigned char b;
1017   int i, precision_loss;
1018   unsigned char sign = (st0_ptr->sign == SIGN_NEG) ? 0x80 : 0;
1019   char st0_tag = st0_ptr->tag;
1020 
1021   if ( st0_tag == TW_Empty )
1022     {
1023       /* Empty register (stack underflow) */
1024       EXCEPTION(EX_StackUnder);
1025       goto invalid_operand;
1026     }
1027 
1028   reg_move(st0_ptr, &t);
1029   precision_loss = round_to_int(&t);
1030   ll = significand(&t);
1031 
1032   /* Check for overflow, by comparing with 999999999999999999 decimal. */
1033   if ( (t.sigh > 0x0de0b6b3) ||
1034       ((t.sigh == 0x0de0b6b3) && (t.sigl > 0xa763ffff)) )
1035     {
1036       EXCEPTION(EX_Invalid);
1037       /* This is a special case: see sec 16.2.5.1 of the 80486 book */
1038     invalid_operand:
1039       if ( control_word & CW_Invalid )
1040         {
1041           /* Produce the QNaN "indefinite" */
1042           RE_ENTRANT_CHECK_OFF;
1043           FPU_verify_area(VERIFY_WRITE,d,10);
1044           for ( i = 0; i < 7; i++)
1045             put_fs_byte(0, (unsigned char *) d+i); /* These bytes "undefined" */
1046           put_fs_byte(0xc0, (unsigned char *) d+7); /* This byte "undefined" */
1047           put_fs_byte(0xff, (unsigned char *) d+8);
1048           put_fs_byte(0xff, (unsigned char *) d+9);
1049           RE_ENTRANT_CHECK_ON;
1050           return 1;
1051         }
1052       else
1053         return 0;
1054     }
1055   else if ( precision_loss )
1056     {
1057       /* Precision loss doesn't stop the data transfer */
1058       set_precision_flag(precision_loss);
1059     }
1060 
1061   RE_ENTRANT_CHECK_OFF;
1062   FPU_verify_area(VERIFY_WRITE,d,10);
1063   RE_ENTRANT_CHECK_ON;
1064   for ( i = 0; i < 9; i++)
1065     {
1066       b = div_small(&ll, 10);
1067       b |= (div_small(&ll, 10)) << 4;
1068       RE_ENTRANT_CHECK_OFF;
1069       put_fs_byte(b,(unsigned char *) d+i);
1070       RE_ENTRANT_CHECK_ON;
1071     }
1072   RE_ENTRANT_CHECK_OFF;
1073   put_fs_byte(sign,(unsigned char *) d+9);
1074   RE_ENTRANT_CHECK_ON;
1075 
1076   return 1;
1077 }
1078 
1079 /*===========================================================================*/
1080 
1081 /* r gets mangled such that sig is int, sign: 
1082    it is NOT normalized */
1083 /* The return value (in eax) is zero if the result is exact,
1084    if bits are changed due to rounding, truncation, etc, then
1085    a non-zero value is returned */
1086 /* Overflow is signalled by a non-zero return value (in eax).
1087    In the case of overflow, the returned significand always has the
1088    largest possible value */
1089 int round_to_int(FPU_REG *r)
     /* [previous][next][first][last][top][bottom][index][help] */
1090 {
1091   char     very_big;
1092   unsigned eax;
1093 
1094   if (r->tag == TW_Zero)
1095     {
1096       /* Make sure that zero is returned */
1097       significand(r) = 0;
1098       return 0;        /* o.k. */
1099     }
1100   
1101   if (r->exp > EXP_BIAS + 63)
1102     {
1103       r->sigl = r->sigh = ~0;      /* The largest representable number */
1104       return 1;        /* overflow */
1105     }
1106 
1107   eax = shrxs(&r->sigl, EXP_BIAS + 63 - r->exp);
1108   very_big = !(~(r->sigh) | ~(r->sigl));  /* test for 0xfff...fff */
1109 #define half_or_more    (eax & 0x80000000)
1110 #define frac_part       (eax)
1111 #define more_than_half  ((eax & 0x80000001) == 0x80000001)
1112   switch (control_word & CW_RC)
1113     {
1114     case RC_RND:
1115       if ( more_than_half                       /* nearest */
1116           || (half_or_more && (r->sigl & 1)) )  /* odd -> even */
1117         {
1118           if ( very_big ) return 1;        /* overflow */
1119           significand(r) ++;
1120           return PRECISION_LOST_UP;
1121         }
1122       break;
1123     case RC_DOWN:
1124       if (frac_part && r->sign)
1125         {
1126           if ( very_big ) return 1;        /* overflow */
1127           significand(r) ++;
1128           return PRECISION_LOST_UP;
1129         }
1130       break;
1131     case RC_UP:
1132       if (frac_part && !r->sign)
1133         {
1134           if ( very_big ) return 1;        /* overflow */
1135           significand(r) ++;
1136           return PRECISION_LOST_UP;
1137         }
1138       break;
1139     case RC_CHOP:
1140       break;
1141     }
1142 
1143   return eax ? PRECISION_LOST_DOWN : 0;
1144 
1145 }
1146 
1147 /*===========================================================================*/
1148 
1149 char *fldenv(fpu_addr_modes addr_modes, char *s)
     /* [previous][next][first][last][top][bottom][index][help] */
1150 {
1151   unsigned short tag_word = 0;
1152   unsigned char tag;
1153   int i;
1154 
1155   if ( (addr_modes.default_mode == VM86) ||
1156       ((addr_modes.default_mode == PM16)
1157       ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX)) )
1158     {
1159       RE_ENTRANT_CHECK_OFF;
1160       FPU_verify_area(VERIFY_READ, s, 0x0e);
1161       control_word = get_fs_word((unsigned short *) s);
1162       partial_status = get_fs_word((unsigned short *) (s+2));
1163       tag_word = get_fs_word((unsigned short *) (s+4));
1164       instruction_address.offset = get_fs_word((unsigned short *) (s+6));
1165       instruction_address.selector = get_fs_word((unsigned short *) (s+8));
1166       operand_address.offset = get_fs_word((unsigned short *) (s+0x0a));
1167       operand_address.selector = get_fs_word((unsigned short *) (s+0x0c));
1168       RE_ENTRANT_CHECK_ON;
1169       s += 0x0e;
1170       if ( addr_modes.default_mode == VM86 )
1171         {
1172           instruction_address.offset
1173             += (instruction_address.selector & 0xf000) << 4;
1174           operand_address.offset += (operand_address.selector & 0xf000) << 4;
1175         }
1176     }
1177   else
1178     {
1179       RE_ENTRANT_CHECK_OFF;
1180       FPU_verify_area(VERIFY_READ, s, 0x1c);
1181       control_word = get_fs_word((unsigned short *) s);
1182       partial_status = get_fs_word((unsigned short *) (s+4));
1183       tag_word = get_fs_word((unsigned short *) (s+8));
1184       instruction_address.offset = get_fs_long((unsigned long *) (s+0x0c));
1185       instruction_address.selector = get_fs_word((unsigned short *) (s+0x10));
1186       instruction_address.opcode = get_fs_word((unsigned short *) (s+0x12));
1187       operand_address.offset = get_fs_long((unsigned long *) (s+0x14));
1188       operand_address.selector = get_fs_long((unsigned long *) (s+0x18));
1189       RE_ENTRANT_CHECK_ON;
1190       s += 0x1c;
1191     }
1192 
1193 #ifdef PECULIAR_486
1194   control_word &= ~0xe080;
1195 #endif PECULIAR_486
1196 
1197   top = (partial_status >> SW_Top_Shift) & 7;
1198 
1199   if ( partial_status & ~control_word & CW_Exceptions )
1200     partial_status |= (SW_Summary | SW_Backward);
1201   else
1202     partial_status &= ~(SW_Summary | SW_Backward);
1203 
1204   for ( i = 0; i < 8; i++ )
1205     {
1206       tag = tag_word & 3;
1207       tag_word >>= 2;
1208 
1209       if ( tag == 3 )
1210         /* New tag is empty.  Accept it */
1211         regs[i].tag = TW_Empty;
1212       else if ( regs[i].tag == TW_Empty )
1213         {
1214           /* Old tag is empty and new tag is not empty.  New tag is determined
1215              by old reg contents */
1216           if ( regs[i].exp == EXP_BIAS - EXTENDED_Ebias )
1217             {
1218               if ( !(regs[i].sigl | regs[i].sigh) )
1219                 regs[i].tag = TW_Zero;
1220               else
1221                 regs[i].tag = TW_Valid;
1222             }
1223           else if ( regs[i].exp == 0x7fff + EXP_BIAS - EXTENDED_Ebias )
1224             {
1225               if ( !((regs[i].sigh & ~0x80000000) | regs[i].sigl) )
1226                 regs[i].tag = TW_Infinity;
1227               else
1228                 regs[i].tag = TW_NaN;
1229             }
1230           else
1231             regs[i].tag = TW_Valid;
1232         }
1233       /* Else old tag is not empty and new tag is not empty.  Old tag
1234          remains correct */
1235     }
1236 
1237   return s;
1238 }
1239 
1240 
1241 void frstor(fpu_addr_modes addr_modes, char *data_address)
     /* [previous][next][first][last][top][bottom][index][help] */
1242 {
1243   int i, stnr;
1244   unsigned char tag;
1245   char *s = fldenv(addr_modes, data_address);
1246 
1247   for ( i = 0; i < 8; i++ )
1248     {
1249       /* Load each register. */
1250       stnr = (i+top) & 7;
1251       tag = regs[stnr].tag;   /* Derived from the fldenv() loaded tag word. */
1252       reg_load_extended((long double *)(s+i*10), &regs[stnr]);
1253       if ( tag == TW_Empty )  /* The loaded data over-rides all other cases. */
1254         regs[stnr].tag = tag;
1255     }
1256 
1257 }
1258 
1259 
1260 unsigned short tag_word(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1261 {
1262   unsigned short word = 0;
1263   unsigned char tag;
1264   int i;
1265 
1266   for ( i = 7; i >= 0; i-- )
1267     {
1268       switch ( tag = regs[i].tag )
1269         {
1270         case TW_Valid:
1271           if ( regs[i].exp <= (EXP_BIAS - EXTENDED_Ebias) )
1272             tag = 2;
1273           break;
1274         case TW_Infinity:
1275         case TW_NaN:
1276           tag = 2;
1277           break;
1278         case TW_Empty:
1279           tag = 3;
1280           break;
1281           /* TW_Zero already has the correct value */
1282         }
1283       word <<= 2;
1284       word |= tag;
1285     }
1286   return word;
1287 }
1288 
1289 
1290 char *fstenv(fpu_addr_modes addr_modes, char *d)
     /* [previous][next][first][last][top][bottom][index][help] */
1291 {
1292   if ( (addr_modes.default_mode == VM86) ||
1293       ((addr_modes.default_mode == PM16)
1294       ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX)) )
1295     {
1296       RE_ENTRANT_CHECK_OFF;
1297       FPU_verify_area(VERIFY_WRITE,d,14);
1298 #ifdef PECULIAR_486
1299       put_fs_long(control_word & ~0xe080, (unsigned short *) d);
1300 #else
1301       put_fs_word(control_word, (unsigned short *) d);
1302 #endif PECULIAR_486
1303       put_fs_word(status_word(), (unsigned short *) (d+2));
1304       put_fs_word(tag_word(), (unsigned short *) (d+4));
1305       put_fs_word(instruction_address.offset, (unsigned short *) (d+6));
1306       put_fs_word(operand_address.offset, (unsigned short *) (d+0x0a));
1307       if ( addr_modes.default_mode == VM86 )
1308         {
1309           put_fs_word((instruction_address.offset & 0xf0000) >> 4,
1310                       (unsigned short *) (d+8));
1311           put_fs_word((operand_address.offset & 0xf0000) >> 4,
1312                       (unsigned short *) (d+0x0c));
1313         }
1314       else
1315         {
1316           put_fs_word(instruction_address.selector, (unsigned short *) (d+8));
1317           put_fs_word(operand_address.selector, (unsigned short *) (d+0x0c));
1318         }
1319       RE_ENTRANT_CHECK_ON;
1320       d += 0x0e;
1321     }
1322   else
1323     {
1324       RE_ENTRANT_CHECK_OFF;
1325       FPU_verify_area(VERIFY_WRITE,d,28);
1326 #ifdef PECULIAR_486
1327       /* An 80486 sets all the reserved bits to 1. */
1328       put_fs_long(0xffff0040 | (control_word & ~0xe080), (unsigned long *) d);
1329       put_fs_long(0xffff0000 | status_word(), (unsigned long *) (d+4));
1330       put_fs_long(0xffff0000 | tag_word(), (unsigned long *) (d+8));
1331 #else
1332       put_fs_word(control_word, (unsigned short *) d);
1333       put_fs_word(status_word(), (unsigned short *) (d+4));
1334       put_fs_word(tag_word(), (unsigned short *) (d+8));
1335 #endif PECULIAR_486
1336       put_fs_long(instruction_address.offset, (unsigned long *) (d+0x0c));
1337       put_fs_word(instruction_address.selector, (unsigned short *) (d+0x10));
1338       put_fs_word(instruction_address.opcode, (unsigned short *) (d+0x12));
1339       put_fs_long(operand_address.offset, (unsigned long *) (d+0x14));
1340 #ifdef PECULIAR_486
1341       /* An 80486 sets all the reserved bits to 1. */
1342       put_fs_word(operand_address.selector, (unsigned short *) (d+0x18));
1343       put_fs_word(0xffff, (unsigned short *) (d+0x1a));
1344 #else
1345       put_fs_long(operand_address.selector, (unsigned long *) (d+0x18));
1346 #endif PECULIAR_486
1347       RE_ENTRANT_CHECK_ON;
1348       d += 0x1c;
1349     }
1350   
1351   control_word |= CW_Exceptions;
1352   partial_status &= ~(SW_Summary | SW_Backward);
1353 
1354   return d;
1355 }
1356 
1357 
1358 void fsave(fpu_addr_modes addr_modes, char *data_address)
     /* [previous][next][first][last][top][bottom][index][help] */
1359 {
1360   char *d;
1361   int i;
1362 
1363   d = fstenv(addr_modes, data_address);
1364   RE_ENTRANT_CHECK_OFF;
1365   FPU_verify_area(VERIFY_WRITE,d,80);
1366   RE_ENTRANT_CHECK_ON;
1367   for ( i = 0; i < 8; i++ )
1368     write_to_extended(&regs[(top + i) & 7], d + 10 * i);
1369 
1370   finit();
1371 
1372 }
1373 
1374 /*===========================================================================*/
1375 
1376 /*
1377   A call to this function must be preceded by a call to
1378   FPU_verify_area() to verify access to the 10 bytes at d
1379   */
1380 static void write_to_extended(FPU_REG *rp, char *d)
     /* [previous][next][first][last][top][bottom][index][help] */
1381 {
1382   long e;
1383   FPU_REG tmp;
1384   
1385   e = rp->exp - EXP_BIAS + EXTENDED_Ebias;
1386 
1387 #ifdef PARANOID
1388   switch ( rp->tag )
1389     {
1390     case TW_Zero:
1391       if ( rp->sigh | rp->sigl | e )
1392         EXCEPTION(EX_INTERNAL | 0x160);
1393       break;
1394     case TW_Infinity:
1395     case TW_NaN:
1396       if ( (e ^ 0x7fff) | !(rp->sigh & 0x80000000) )
1397         EXCEPTION(EX_INTERNAL | 0x161);
1398       break;
1399     default:
1400       if (e > 0x7fff || e < -63)
1401         EXCEPTION(EX_INTERNAL | 0x162);
1402     }
1403 #endif PARANOID
1404 
1405   /*
1406     All numbers except denormals are stored internally in a
1407     format which is compatible with the extended real number
1408     format.
1409    */
1410   if ( e > 0 )
1411     {
1412       /* just copy the reg */
1413       RE_ENTRANT_CHECK_OFF;
1414       put_fs_long(rp->sigl, (unsigned long *) d);
1415       put_fs_long(rp->sigh, (unsigned long *) (d + 4));
1416       RE_ENTRANT_CHECK_ON;
1417     }
1418   else
1419     {
1420       /*
1421         The number is a de-normal stored as a normal using our
1422         extra exponent range, or is Zero.
1423         Convert it back to a de-normal, or leave it as Zero.
1424        */
1425       reg_move(rp, &tmp);
1426       tmp.exp += -EXTENDED_Emin + 63;  /* largest exp to be 63 */
1427       round_to_int(&tmp);
1428       e = 0;
1429       RE_ENTRANT_CHECK_OFF;
1430       put_fs_long(tmp.sigl, (unsigned long *) d);
1431       put_fs_long(tmp.sigh, (unsigned long *) (d + 4));
1432       RE_ENTRANT_CHECK_ON;
1433     }
1434   e |= rp->sign == SIGN_POS ? 0 : 0x8000;
1435   RE_ENTRANT_CHECK_OFF;
1436   put_fs_word(e, (unsigned short *) (d + 8));
1437   RE_ENTRANT_CHECK_ON;
1438 }

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