root/kernel/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. fstenv
  19. fsave

   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    W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
   7  |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
   8  |                                                                           |
   9  |                                                                           |
  10  +---------------------------------------------------------------------------*/
  11 
  12 /*---------------------------------------------------------------------------+
  13  | Note:                                                                     |
  14  |    The file contains code which accesses user memory.                     |
  15  |    Emulator static data may change when user memory is accessed, due to   |
  16  |    other processes using the emulator while swapping is in progress.      |
  17  +---------------------------------------------------------------------------*/
  18 
  19 #include <asm/segment.h>
  20 
  21 #include "fpu_system.h"
  22 #include "exception.h"
  23 #include "reg_constant.h"
  24 #include "fpu_emu.h"
  25 #include "control_w.h"
  26 
  27 
  28 #define EXTENDED_Emax 0x3fff     /* largest valid exponent */
  29 #define EXTENDED_Ebias 0x3fff
  30 #define EXTENDED_Emin (-0x3ffe)  /* smallest valid exponent */
  31 
  32 #define DOUBLE_Emax 1023         /* largest valid exponent */
  33 #define DOUBLE_Ebias 1023
  34 #define DOUBLE_Emin (-1022)      /* smallest valid exponent */
  35 
  36 #define SINGLE_Emax 127          /* largest valid exponent */
  37 #define SINGLE_Ebias 127
  38 #define SINGLE_Emin (-126)       /* smallest valid exponent */
  39 
  40 
  41 FPU_REG FPU_loaded_data;
  42 
  43 
  44 /* Get a long double from user memory */
  45 void reg_load_extended(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  46 {
  47   long double *s = (long double *)FPU_data_address;
  48   unsigned long sigl, sigh, exp;
  49 
  50   RE_ENTRANT_CHECK_OFF
  51   /* Use temporary variables here because FPU_loaded data is
  52      static and hence re-entrancy problems can arise */
  53   sigl = get_fs_long((unsigned long *) s);
  54   sigh = get_fs_long(1 + (unsigned long *) s);
  55   exp = get_fs_word(4 + (unsigned short *) s);
  56   RE_ENTRANT_CHECK_ON
  57 
  58   FPU_loaded_data.sigl = sigl;
  59   FPU_loaded_data.sigh = sigh;
  60   FPU_loaded_data.exp = exp;
  61 
  62   if (FPU_loaded_data.exp & 0x8000)
  63     FPU_loaded_data.sign = SIGN_NEG;
  64   else
  65     FPU_loaded_data.sign = SIGN_POS;
  66   if ( (FPU_loaded_data.exp &= 0x7fff) == 0 )
  67     {
  68       if ( !(FPU_loaded_data.sigl | FPU_loaded_data.sigh) )
  69         {
  70           FPU_loaded_data.tag = TW_Zero;
  71           return;
  72         }
  73       /* The number is de-normal */
  74       /* The default behaviour will take care of this */
  75     }
  76   else if ( FPU_loaded_data.exp == 0x7fff )
  77     {
  78       FPU_loaded_data.exp = EXTENDED_Emax;
  79       if ( (FPU_loaded_data.sigh == 0x80000000)
  80           && (FPU_loaded_data.sigl == 0) )
  81         {
  82           FPU_loaded_data.tag = TW_Infinity;
  83           return;
  84         }
  85       if ( !(FPU_loaded_data.sigh & 0x80000000) )
  86         {
  87           /* Unsupported data type */
  88           EXCEPTION(EX_Invalid);
  89           FPU_loaded_data.tag = TW_NaN;
  90           return;
  91         }
  92       FPU_loaded_data.tag = TW_NaN;
  93       return;
  94     }
  95   FPU_loaded_data.exp = (FPU_loaded_data.exp & 0x7fff) - EXTENDED_Ebias
  96     + EXP_BIAS;
  97   FPU_loaded_data.tag = TW_Valid;
  98 
  99   normalize(&FPU_loaded_data);
 100 }
 101 
 102 
 103 /* Get a double from user memory */
 104 void reg_load_double(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 105 {
 106   double *dfloat = (double *)FPU_data_address;
 107   int exp;
 108   unsigned m64, l64;
 109 
 110   RE_ENTRANT_CHECK_OFF
 111   m64 = get_fs_long(1 + (unsigned long *) dfloat);
 112   l64 = get_fs_long((unsigned long *) dfloat);
 113   RE_ENTRANT_CHECK_ON
 114 
 115   if (m64 & 0x80000000)
 116     FPU_loaded_data.sign = SIGN_NEG;
 117   else
 118     FPU_loaded_data.sign = SIGN_POS;
 119   exp = ((m64 & 0x7ff00000) >> 20) - DOUBLE_Ebias;
 120   m64 &= 0xfffff;
 121   if (exp > DOUBLE_Emax)
 122     {
 123       /* Infinity or NaN */
 124       if ((m64 == 0) && (l64 == 0))
 125         {
 126           /* +- infinity */
 127           FPU_loaded_data.exp = EXTENDED_Emax;
 128           FPU_loaded_data.tag = TW_Infinity;
 129           return;
 130         }
 131       else
 132         {
 133           /* Must be a signaling or quiet NaN */
 134           FPU_loaded_data.exp = EXTENDED_Emax;
 135           FPU_loaded_data.tag = TW_NaN;
 136           FPU_loaded_data.sigh = (m64 << 11) | 0x80000000;
 137           FPU_loaded_data.sigh |= l64 >> 21;
 138           FPU_loaded_data.sigl = l64 << 11;
 139           return;
 140         }
 141     }
 142   else if ( exp < DOUBLE_Emin )
 143     {
 144       /* Zero or de-normal */
 145       if ((m64 == 0) && (l64 == 0))
 146         {
 147           /* Zero */
 148           int c = FPU_loaded_data.sign;
 149           reg_move(&CONST_Z, &FPU_loaded_data);
 150           FPU_loaded_data.sign = c;
 151           return;
 152         }
 153       else
 154         {
 155           /* De-normal */
 156           FPU_loaded_data.exp = DOUBLE_Emin + EXP_BIAS;
 157           FPU_loaded_data.tag = TW_Valid;
 158           FPU_loaded_data.sigh = m64 << 11;
 159           FPU_loaded_data.sigh |= l64 >> 21;
 160           FPU_loaded_data.sigl = l64 << 11;
 161           normalize(&FPU_loaded_data);
 162           return;
 163         }
 164     }
 165   else
 166     {
 167       FPU_loaded_data.exp = exp + EXP_BIAS;
 168       FPU_loaded_data.tag = TW_Valid;
 169       FPU_loaded_data.sigh = (m64 << 11) | 0x80000000;
 170       FPU_loaded_data.sigh |= l64 >> 21;
 171       FPU_loaded_data.sigl = l64 << 11;
 172 
 173       return;
 174     }
 175 }
 176 
 177 
 178 /* Get a float from user memory */
 179 void reg_load_single(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 180 {
 181   float *single = (float *)FPU_data_address;
 182   unsigned m32;
 183   int exp;
 184 
 185   RE_ENTRANT_CHECK_OFF
 186   m32 = get_fs_long((unsigned long *) single);
 187   RE_ENTRANT_CHECK_ON
 188 
 189   if (m32 & 0x80000000)
 190     FPU_loaded_data.sign = SIGN_NEG;
 191   else
 192     FPU_loaded_data.sign = SIGN_POS;
 193   if (!(m32 & 0x7fffffff))
 194     {
 195       /* Zero */
 196       int c = FPU_loaded_data.sign;
 197       reg_move(&CONST_Z, &FPU_loaded_data);
 198       FPU_loaded_data.sign = c;
 199       return;
 200     }
 201   exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias;
 202   m32 = (m32 & 0x7fffff) << 8;
 203   if ( exp < SINGLE_Emin )
 204     {
 205       /* De-normals */
 206       FPU_loaded_data.exp = SINGLE_Emin + EXP_BIAS;
 207       FPU_loaded_data.tag = TW_Valid;
 208       FPU_loaded_data.sigh = m32;
 209       FPU_loaded_data.sigl = 0;
 210       normalize(&FPU_loaded_data);
 211       return;
 212     }
 213   else if ( exp > SINGLE_Emax )
 214     {
 215     /* Infinity or NaN */
 216       if ( m32 == 0 )
 217         {
 218           /* +- infinity */
 219           FPU_loaded_data.exp = EXTENDED_Emax;
 220           FPU_loaded_data.tag = TW_Infinity;
 221           return;
 222         }
 223       else
 224         {
 225           /* Must be a signaling or quiet NaN */
 226           FPU_loaded_data.exp = EXTENDED_Emax;
 227           FPU_loaded_data.tag = TW_NaN;
 228           FPU_loaded_data.sigh = m32 | 0x80000000;
 229           FPU_loaded_data.sigl = 0;
 230           return;
 231         }
 232     }
 233   else
 234     {
 235       FPU_loaded_data.exp = exp + EXP_BIAS;
 236       FPU_loaded_data.sigh = m32 | 0x80000000;
 237       FPU_loaded_data.sigl = 0;
 238       FPU_loaded_data.tag = TW_Valid;
 239     }
 240 }
 241 
 242 
 243 /* Get a long long from user memory */
 244 void reg_load_int64(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 245 {
 246   long long *_s = (long long *)FPU_data_address;
 247   int e;
 248   long long s;
 249 
 250   RE_ENTRANT_CHECK_OFF
 251   ((unsigned long *)&s)[0] = get_fs_long((unsigned long *) _s);
 252   ((unsigned long *)&s)[1] = get_fs_long(1 + (unsigned long *) _s);
 253   RE_ENTRANT_CHECK_ON
 254 
 255   if (s == 0)
 256     { reg_move(&CONST_Z, &FPU_loaded_data); return; }
 257 
 258   if (s > 0)
 259     FPU_loaded_data.sign = SIGN_POS;
 260   else
 261   {
 262     s = -s;
 263     FPU_loaded_data.sign = SIGN_NEG;
 264   }
 265 
 266   e = EXP_BIAS + 63;
 267   *((long long *)&FPU_loaded_data.sigl) = s;
 268   FPU_loaded_data.exp = e;
 269   FPU_loaded_data.tag = TW_Valid;
 270   normalize(&FPU_loaded_data);
 271 }
 272 
 273 
 274 /* Get a long from user memory */
 275 void reg_load_int32(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 276 {
 277   long *_s = (long *)FPU_data_address;
 278   long s;
 279   int e;
 280 
 281   RE_ENTRANT_CHECK_OFF
 282   s = (long)get_fs_long((unsigned long *) _s);
 283   RE_ENTRANT_CHECK_ON
 284 
 285   if (s == 0)
 286     { reg_move(&CONST_Z, &FPU_loaded_data); return; }
 287 
 288   if (s > 0)
 289     FPU_loaded_data.sign = SIGN_POS;
 290   else
 291   {
 292     s = -s;
 293     FPU_loaded_data.sign = SIGN_NEG;
 294   }
 295 
 296   e = EXP_BIAS + 31;
 297   FPU_loaded_data.sigh = s;
 298   FPU_loaded_data.sigl = 0;
 299   FPU_loaded_data.exp = e;
 300   FPU_loaded_data.tag = TW_Valid;
 301   normalize(&FPU_loaded_data);
 302 }
 303 
 304 
 305 /* Get a short from user memory */
 306 void reg_load_int16(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 307 {
 308   short *_s = (short *)FPU_data_address;
 309   int s, e;
 310 
 311   RE_ENTRANT_CHECK_OFF
 312   s = (int)get_fs_word((unsigned short *) _s);
 313   RE_ENTRANT_CHECK_ON
 314 
 315   if (s == 0)
 316     { reg_move(&CONST_Z, &FPU_loaded_data); return; }
 317 
 318   if (s > 0)
 319     FPU_loaded_data.sign = SIGN_POS;
 320   else
 321   {
 322     s = -s;
 323     FPU_loaded_data.sign = SIGN_NEG;
 324   }
 325 
 326   e = EXP_BIAS + 15;
 327   FPU_loaded_data.sigh = s << 16;
 328 
 329   FPU_loaded_data.sigl = 0;
 330   FPU_loaded_data.exp = e;
 331   FPU_loaded_data.tag = TW_Valid;
 332   normalize(&FPU_loaded_data);
 333 }
 334 
 335 
 336 /* Get a packed bcd array from user memory */
 337 void reg_load_bcd(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 338 {
 339   char *s = (char *)FPU_data_address;
 340   int pos;
 341   unsigned char bcd;
 342   long long l=0;
 343 
 344   for ( pos = 8; pos >= 0; pos--)
 345     {
 346       l *= 10;
 347       RE_ENTRANT_CHECK_OFF
 348       bcd = (unsigned char)get_fs_byte((unsigned char *) s+pos);
 349       RE_ENTRANT_CHECK_ON
 350       l += bcd >> 4;
 351       l *= 10;
 352       l += bcd & 0x0f;
 353     }
 354   
 355   /* Finish all access to user memory before putting stuff into
 356      the static FPU_loaded_data */
 357   RE_ENTRANT_CHECK_OFF
 358   FPU_loaded_data.sign =
 359     ((unsigned char)get_fs_byte((unsigned char *) s+9)) & 0x80 ?
 360       SIGN_NEG : SIGN_POS;
 361   RE_ENTRANT_CHECK_ON
 362 
 363   if (l == 0)
 364     {
 365       char sign = FPU_loaded_data.sign;
 366       reg_move(&CONST_Z, &FPU_loaded_data);
 367       FPU_loaded_data.sign = sign;
 368     }
 369   else
 370     {
 371       *((long long *)&FPU_loaded_data.sigl) = l;
 372       FPU_loaded_data.exp = EXP_BIAS + 63;
 373       FPU_loaded_data.tag = TW_Valid;
 374       normalize(&FPU_loaded_data);
 375     }
 376 }
 377 
 378 /*===========================================================================*/
 379 
 380 /* Put a long double into user memory */
 381 int reg_store_extended(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 382 {
 383   long double *d = (long double *)FPU_data_address;
 384   long e = FPU_st0_ptr->exp - EXP_BIAS + EXTENDED_Ebias;
 385   unsigned short sign = FPU_st0_ptr->sign*0x8000;
 386   unsigned long ls, ms;
 387 
 388 
 389   if ( FPU_st0_tag == TW_Valid )
 390     {
 391       if ( e >= 0x7fff )
 392         {
 393           EXCEPTION(EX_Overflow);  /* Overflow */
 394           /* This is a special case: see sec 16.2.5.1 of the 80486 book */
 395           if ( control_word & EX_Overflow )
 396             {
 397               /* Overflow to infinity */
 398               ls = 0;
 399               ms = 0x80000000;
 400               e = 0x7fff;
 401             }
 402           else
 403             return 0;
 404         }
 405       else if ( e <= 0 )
 406         {
 407           if ( e == 0 )
 408             {
 409               EXCEPTION(EX_Denormal);  /* Pseudo de-normal */
 410               ls = FPU_st0_ptr->sigl;
 411               ms = FPU_st0_ptr->sigh;
 412             }
 413           else if ( e > -64 )
 414             {
 415               /* Make a de-normal */
 416               FPU_REG tmp;
 417               EXCEPTION(EX_Denormal);  /* De-normal */
 418               reg_move(FPU_st0_ptr, &tmp);
 419               tmp.exp += -EXTENDED_Emin + 64;  /* largest exp to be 63 */
 420               round_to_int(&tmp);
 421               e = 0;
 422               ls = tmp.sigl;
 423               ms = tmp.sigh;
 424             }
 425           else
 426             {
 427               EXCEPTION(EX_Underflow);  /* Underflow */
 428               /* This is a special case: see sec 16.2.5.1 of the 80486 book */
 429               if ( control_word & EX_Underflow )
 430                 {
 431                   /* Underflow to zero */
 432                   ls = 0;
 433                   ms = 0;
 434                   e = 0;
 435                 }
 436               else
 437                 return 0;
 438             }
 439         }
 440       else
 441         {
 442           ls = FPU_st0_ptr->sigl;
 443           ms = FPU_st0_ptr->sigh;
 444         }
 445     }
 446   else if ( FPU_st0_tag == TW_Zero )
 447     {
 448       ls = ms = 0;
 449       e = 0;
 450     }
 451   else if ( FPU_st0_tag == TW_Infinity )
 452     {
 453       ls = 0;
 454       ms = 0x80000000;
 455       e = 0x7fff;
 456     }
 457   else if ( FPU_st0_tag == TW_NaN )
 458     {
 459       ls = FPU_st0_ptr->sigl;
 460       ms = FPU_st0_ptr->sigh;
 461       e = 0x7fff;
 462     }
 463   else if ( FPU_st0_tag == TW_Empty )
 464     {
 465       /* Empty register (stack underflow) */
 466       EXCEPTION(EX_StackUnder);
 467       if ( control_word & EX_Invalid )
 468         {
 469           /* The masked response */
 470           /* Put out the QNaN indefinite */
 471           ls = 0;
 472           ms = 0xc0000000;
 473           e = 0xffff;
 474         }
 475       else
 476         return 0;
 477     }
 478   else
 479     {
 480       /* We don't use TW_Denormal yet ... perhaps never! */
 481       EXCEPTION(EX_Invalid);
 482       /* Store a NaN */
 483       e = 0x7fff;
 484       ls = 1;
 485       ms = 0x80000000;
 486     }
 487   RE_ENTRANT_CHECK_OFF
 488   verify_area(d,10);
 489   put_fs_long(ls, (unsigned long *) d);
 490   put_fs_long(ms, 1 + (unsigned long *) d);
 491   put_fs_word((unsigned short)e | sign, 4 + (short *) d);
 492   RE_ENTRANT_CHECK_ON
 493 
 494   return 1;
 495 
 496 }
 497 
 498 
 499 /* Put a double into user memory */
 500 int reg_store_double(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 501 {
 502   double *dfloat = (double *)FPU_data_address;
 503   unsigned long l[2];
 504 
 505 
 506   if (FPU_st0_tag == TW_Valid)
 507     {
 508       /* Rounding can get a little messy.. */
 509       int exp = FPU_st0_ptr->exp - EXP_BIAS;
 510       int increment = ((FPU_st0_ptr->sigl & 0x7ff) > 0x400) |   /* nearest */
 511         ((FPU_st0_ptr->sigl & 0xc00) == 0xc00);                 /* odd -> even */
 512       if ( increment )
 513         {
 514           if ( FPU_st0_ptr->sigl >= 0xfffff800 )
 515             {
 516               /* the sigl part overflows */
 517               if ( FPU_st0_ptr->sigh == 0xffffffff )
 518                 {
 519                   /* The sigh part overflows */
 520                   l[0] = l[1] = 0;
 521                   exp++;        /* no need to check here for overflow */
 522                 }
 523               else
 524                 {
 525                   /* No overflow of sigh will happen, can safely increment */
 526                   l[0] = (FPU_st0_ptr->sigh+1) << 21;
 527                   l[1] = (((FPU_st0_ptr->sigh+1) >> 11) & 0xfffff);
 528                 }
 529             }
 530           else
 531             {
 532               /* We only need to increment sigl */
 533               l[0] = ((FPU_st0_ptr->sigl+0x800) >> 11) | (FPU_st0_ptr->sigh << 21);
 534               l[1] = ((FPU_st0_ptr->sigh >> 11) & 0xfffff);
 535             }
 536         }
 537       else
 538         {
 539           /* No increment required */
 540           l[0] = (FPU_st0_ptr->sigl >> 11) | (FPU_st0_ptr->sigh << 21);
 541           l[1] = ((FPU_st0_ptr->sigh >> 11) & 0xfffff);
 542         }
 543 
 544       if ( exp > DOUBLE_Emax )
 545         {
 546           EXCEPTION(EX_Overflow);
 547           /* This is a special case: see sec 16.2.5.1 of the 80486 book */
 548           if ( control_word & EX_Overflow )
 549             {
 550               /* Overflow to infinity */
 551               l[0] = 0x00000000;        /* Set to */
 552               l[1] = 0x7ff00000;        /* + INF */
 553             }
 554           else
 555             return 0;
 556         }
 557       else if ( exp < DOUBLE_Emin )
 558         {
 559           if ( exp > DOUBLE_Emin-53 )
 560             {
 561               /* Make a de-normal */
 562               FPU_REG tmp;
 563               EXCEPTION(EX_Denormal);
 564               reg_move(FPU_st0_ptr, &tmp);
 565               tmp.exp += -DOUBLE_Emin + 52;  /* largest exp to be 51 */
 566               round_to_int(&tmp);
 567               l[0] = tmp.sigl;
 568               l[1] = tmp.sigh;
 569             }
 570           else
 571             {
 572               EXCEPTION(EX_Underflow);
 573               /* This is a special case: see sec 16.2.5.1 of the 80486 book */
 574               if ( control_word & EX_Underflow )
 575                 {
 576                   /* Underflow to zero */
 577                   l[0] = l[1] = 0;
 578                 }
 579               else
 580                 return 0;
 581             }
 582         }
 583       else
 584         {
 585           /* Add the exponent */
 586           l[1] |= (((exp+DOUBLE_Ebias) & 0x7ff) << 20);
 587         }
 588     }
 589   else if (FPU_st0_tag == TW_Zero)
 590     {
 591       /* Number is zero */
 592       l[0] = l[1] = 0;
 593     }
 594   else if (FPU_st0_tag == TW_Infinity)
 595     {
 596       l[0] = 0;
 597       l[1] = 0x7ff00000;
 598     }
 599   else if (FPU_st0_tag == TW_NaN)
 600     {
 601       /* See if we can get a valid NaN from the FPU_REG */
 602       l[0] = (FPU_st0_ptr->sigl >> 11) | (FPU_st0_ptr->sigh << 21);
 603       l[1] = ((FPU_st0_ptr->sigh >> 11) & 0xfffff);
 604       if ( !(l[0] | l[1]) )
 605         {
 606           /* This case does not seem to be handled by the 80486 specs */
 607           EXCEPTION(EX_Invalid);
 608           /* Make the quiet NaN "real indefinite" */
 609           goto put_indefinite;
 610         }
 611       l[1] |= 0x7ff00000;
 612     }
 613   else if ( FPU_st0_tag == TW_Empty )
 614     {
 615       /* Empty register (stack underflow) */
 616       EXCEPTION(EX_StackUnder);
 617       if ( control_word & EX_Invalid )
 618         {
 619           /* The masked response */
 620           /* Put out the QNaN indefinite */
 621 put_indefinite:
 622           RE_ENTRANT_CHECK_OFF
 623           verify_area((void *)dfloat,8);
 624           put_fs_long(0, (unsigned long *) dfloat);
 625           put_fs_long(0xfff80000, 1 + (unsigned long *) dfloat);
 626           RE_ENTRANT_CHECK_ON
 627           return 1;
 628         }
 629       else
 630         return 0;
 631     }
 632   else if (FPU_st0_tag == TW_Denormal)
 633     {
 634       /* Extended real -> double real will always underflow */
 635       l[0] = l[1] = 0;
 636       EXCEPTION(EX_Underflow);
 637     }
 638   if (FPU_st0_ptr->sign)
 639     l[1] |= 0x80000000;
 640 
 641   RE_ENTRANT_CHECK_OFF
 642   verify_area((void *)dfloat,8);
 643   put_fs_long(l[0], (unsigned long *)dfloat);
 644   put_fs_long(l[1], 1 + (unsigned long *)dfloat);
 645   RE_ENTRANT_CHECK_ON
 646 
 647   return 1;
 648 
 649 }
 650 
 651 
 652 /* Put a float into user memory */
 653 int reg_store_single(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 654 {
 655   float *single = (float *)FPU_data_address;
 656   long templ;
 657   int exp = FPU_st0_ptr->exp - EXP_BIAS;
 658   unsigned long sigh = FPU_st0_ptr->sigh;
 659 
 660 
 661   if (FPU_st0_tag == TW_Valid)
 662     {
 663       if ( ((sigh & 0xff) > 0x80)           /* more than half */
 664           || ((sigh & 0x180) == 0x180) )    /* round to even */
 665         {
 666           /* Round up */
 667           if ( sigh >= 0xffffff00 )
 668             {
 669               /* sigh would overflow */
 670               exp++;
 671               sigh = 0x80000000;
 672             }
 673           else
 674             {
 675               sigh += 0x100;
 676             }
 677         }
 678       templ = (sigh >> 8) & 0x007fffff;
 679       if ( exp > SINGLE_Emax )
 680         {
 681           EXCEPTION(EX_Overflow);
 682           /* This is a special case: see sec 16.2.5.1 of the 80486 book */
 683           if ( control_word & EX_Overflow )
 684             {
 685               /* Overflow to infinity */
 686               templ = 0x7f800000;
 687             }
 688           else
 689             return 0;
 690         }
 691       else if ( exp < SINGLE_Emin )
 692         {
 693           if ( exp > SINGLE_Emin-24 )
 694             {
 695               /* Make a de-normal */
 696               FPU_REG tmp;
 697               EXCEPTION(EX_Denormal);
 698               reg_move(FPU_st0_ptr, &tmp);
 699               tmp.exp += -SINGLE_Emin + 23;  /* largest exp to be 22 */
 700               round_to_int(&tmp);
 701               templ = tmp.sigl;
 702             }
 703           else
 704             {
 705               EXCEPTION(EX_Underflow);
 706               /* This is a special case: see sec 16.2.5.1 of the 80486 book */
 707               if ( control_word & EX_Underflow )
 708                 {
 709                   /* Underflow to zero */
 710                   templ = 0;
 711                 }
 712               else
 713                 return 0;
 714             }
 715         }
 716       else
 717         templ |= ((exp+SINGLE_Ebias) & 0xff) << 23;
 718     }
 719   else if (FPU_st0_tag == TW_Zero)
 720     {
 721       templ = 0;
 722     }
 723   else if (FPU_st0_tag == TW_Infinity)
 724     {
 725       templ = 0x7f800000;
 726     }
 727   else if (FPU_st0_tag == TW_NaN)
 728     {
 729       /* See if we can get a valid NaN from the FPU_REG */
 730       templ = FPU_st0_ptr->sigh >> 8;
 731       if ( !(templ & 0x3fffff) )
 732         {
 733           /* This case does not seem to be handled by the 80486 specs */
 734           EXCEPTION(EX_Invalid);
 735           /* Make the quiet NaN "real indefinite" */
 736           goto put_indefinite;
 737         }
 738       templ |= 0x7f800000;
 739     }
 740   else if ( FPU_st0_tag == TW_Empty )
 741     {
 742       /* Empty register (stack underflow) */
 743       EXCEPTION(EX_StackUnder);
 744       if ( control_word & EX_Invalid )
 745         {
 746           /* The masked response */
 747           /* Put out the QNaN indefinite */
 748 put_indefinite:
 749           RE_ENTRANT_CHECK_OFF
 750           verify_area((void *)single,4);
 751           put_fs_long(0xffc00000, (unsigned long *) single);
 752           RE_ENTRANT_CHECK_ON
 753           return 1;
 754         }
 755       else
 756         return 0;
 757     }
 758   else if (FPU_st0_tag == TW_Denormal)
 759     {
 760       /* Extended real -> real will always underflow */
 761       templ = 0;
 762       EXCEPTION(EX_Underflow);
 763     }
 764 #ifdef PARANOID
 765   else
 766     {
 767       EXCEPTION(EX_INTERNAL|0x106);
 768       return 0;
 769     }
 770 #endif
 771   if (FPU_st0_ptr->sign)
 772     templ |= 0x80000000;
 773 
 774   RE_ENTRANT_CHECK_OFF
 775   verify_area((void *)single,4);
 776   put_fs_long(templ,(unsigned long *) single);
 777   RE_ENTRANT_CHECK_ON
 778 
 779   return 1;
 780 }
 781 
 782 
 783 /* Put a long long into user memory */
 784 int reg_store_int64(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 785 {
 786   long long *d = (long long *)FPU_data_address;
 787   FPU_REG t;
 788   long long tll;
 789 
 790   if ( FPU_st0_tag == TW_Empty )
 791     {
 792       /* Empty register (stack underflow) */
 793       EXCEPTION(EX_StackUnder);
 794       if ( control_word & EX_Invalid )
 795         {
 796           /* The masked response */
 797           /* Put out the QNaN indefinite */
 798           goto put_indefinite;
 799         }
 800       else
 801         return 0;
 802     }
 803 
 804   reg_move(FPU_st0_ptr, &t);
 805   round_to_int(&t);
 806   ((long *)&tll)[0] = t.sigl;
 807   ((long *)&tll)[1] = t.sigh;
 808   if ( (t.sigh & 0x80000000) &&
 809       !((t.sigh == 0x80000000) && (t.sigl == 0) && (t.sign == SIGN_NEG)) )
 810     {
 811       EXCEPTION(EX_Invalid);
 812       /* This is a special case: see sec 16.2.5.1 of the 80486 book */
 813       if ( control_word & EX_Invalid )
 814         {
 815           /* Produce "indefinite" */
 816 put_indefinite:
 817           ((long *)&tll)[1] = 0x80000000;
 818           ((long *)&tll)[0] = 0;
 819         }
 820       else
 821         return 0;
 822     }
 823   else if ( t.sign )
 824     tll = - tll;
 825 
 826   RE_ENTRANT_CHECK_OFF
 827   verify_area((void *)d,8);
 828   put_fs_long(((long *)&tll)[0],(unsigned long *) d);
 829   put_fs_long(((long *)&tll)[1],1 + (unsigned long *) d);
 830   RE_ENTRANT_CHECK_ON
 831 
 832   return 1;
 833 }
 834 
 835 
 836 /* Put a long into user memory */
 837 int reg_store_int32(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 838 {
 839   long *d = (long *)FPU_data_address;
 840   FPU_REG t;
 841 
 842   if ( FPU_st0_tag == TW_Empty )
 843     {
 844       /* Empty register (stack underflow) */
 845       EXCEPTION(EX_StackUnder);
 846       if ( control_word & EX_Invalid )
 847         {
 848           /* The masked response */
 849           /* Put out the QNaN indefinite */
 850           RE_ENTRANT_CHECK_OFF
 851           verify_area(d,4);
 852           put_fs_long(0x80000000, (unsigned long *) d);
 853           RE_ENTRANT_CHECK_ON
 854           return 1;
 855         }
 856       else
 857         return 0;
 858     }
 859 
 860   reg_move(FPU_st0_ptr, &t);
 861   round_to_int(&t);
 862   if (t.sigh ||
 863       ((t.sigl & 0x80000000) &&
 864        !((t.sigl == 0x80000000) && (t.sign == SIGN_NEG))) )
 865     {
 866       EXCEPTION(EX_Invalid);
 867       /* This is a special case: see sec 16.2.5.1 of the 80486 book */
 868       if ( control_word & EX_Invalid )
 869         {
 870           /* Produce "indefinite" */
 871           t.sigl = 0x80000000;
 872         }
 873       else
 874         return 0;
 875     }
 876   else if ( t.sign )
 877     t.sigl = -(long)t.sigl;
 878 
 879   RE_ENTRANT_CHECK_OFF
 880   verify_area(d,4);
 881   put_fs_long(t.sigl, (unsigned long *) d);
 882   RE_ENTRANT_CHECK_ON
 883 
 884   return 1;
 885 }
 886 
 887 
 888 /* Put a short into user memory */
 889 int reg_store_int16(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 890 {
 891   short *d = (short *)FPU_data_address;
 892   FPU_REG t;
 893   short ts;
 894 
 895   if ( FPU_st0_tag == TW_Empty )
 896     {
 897       /* Empty register (stack underflow) */
 898       EXCEPTION(EX_StackUnder);
 899       if ( control_word & EX_Invalid )
 900         {
 901           /* The masked response */
 902           /* Put out the QNaN indefinite */
 903           RE_ENTRANT_CHECK_OFF
 904           verify_area(d,2);
 905           put_fs_word(0x8000, (unsigned short *) d);
 906           RE_ENTRANT_CHECK_ON
 907           return 1;
 908         }
 909       else
 910         return 0;
 911     }
 912 
 913   reg_move(FPU_st0_ptr, &t);
 914   round_to_int(&t);
 915   if (t.sigh ||
 916       ((t.sigl & 0xffff8000) &&
 917        !((t.sigl == 0x8000) && (t.sign == SIGN_NEG))) )
 918     {
 919       EXCEPTION(EX_Invalid);
 920       /* This is a special case: see sec 16.2.5.1 of the 80486 book */
 921       if ( control_word & EX_Invalid )
 922         {
 923           /* Produce "indefinite" */
 924           ts = 0x8000;
 925         }
 926       else
 927         return 0;
 928     }
 929   else if ( t.sign )
 930     t.sigl = -t.sigl;
 931 
 932   RE_ENTRANT_CHECK_OFF
 933   verify_area(d,2);
 934   put_fs_word((short)t.sigl,(short *) d);
 935   RE_ENTRANT_CHECK_ON
 936 
 937   return 1;
 938 }
 939 
 940 
 941 /* Put a packed bcd array into user memory */
 942 int reg_store_bcd(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 943 {
 944   char *d = (char *)FPU_data_address;
 945   FPU_REG t;
 946   long long ll;
 947   unsigned char b;
 948   int i;
 949   unsigned char sign = (FPU_st0_ptr->sign == SIGN_NEG) ? 0x80 : 0;
 950 
 951   if ( FPU_st0_tag == TW_Empty )
 952     {
 953       /* Empty register (stack underflow) */
 954       EXCEPTION(EX_StackUnder);
 955       if ( control_word & EX_Invalid )
 956         {
 957           /* The masked response */
 958           /* Put out the QNaN indefinite */
 959           goto put_indefinite;
 960         }
 961       else
 962         return 0;
 963     }
 964 
 965   reg_move(FPU_st0_ptr, &t);
 966   round_to_int(&t);
 967   ll = *(long long *)(&t.sigl);
 968 
 969   /* Check for overflow, by comparing with 999999999999999999 decimal. */
 970   if ( (t.sigh > 0x0de0b6b3) ||
 971       ((t.sigh == 0x0de0b6b3) && (t.sigl > 0xa763ffff)) )
 972     {
 973       EXCEPTION(EX_Invalid);
 974       /* This is a special case: see sec 16.2.5.1 of the 80486 book */
 975       if ( control_word & EX_Invalid )
 976         {
 977 put_indefinite:
 978           /* Produce "indefinite" */
 979           RE_ENTRANT_CHECK_OFF
 980           verify_area(d,10);
 981           put_fs_byte(0xff,(unsigned char *) d+7);
 982           put_fs_byte(0xff,(unsigned char *) d+8);
 983           put_fs_byte(0xff,(unsigned char *) d+9);
 984           RE_ENTRANT_CHECK_ON
 985           return 1;
 986         }
 987       else
 988         return 0;
 989     }
 990 
 991   verify_area(d,10);
 992   for ( i = 0; i < 9; i++)
 993     {
 994       b = div_small(&ll, 10);
 995       b |= (div_small(&ll, 10)) << 4;
 996       RE_ENTRANT_CHECK_OFF
 997       put_fs_byte(b,(unsigned char *) d+i);
 998       RE_ENTRANT_CHECK_ON
 999     }
1000   RE_ENTRANT_CHECK_OFF
1001   put_fs_byte(sign,(unsigned char *) d+9);
1002   RE_ENTRANT_CHECK_ON
1003 
1004   return 1;
1005 }
1006 
1007 /*===========================================================================*/
1008 
1009 /* r gets mangled such that sig is int, sign: 
1010    it is NOT normalized*/
1011 /* Overflow is signalled by a non-zero return value (in eax).
1012    In the case of overflow, the returned significand always has the
1013    the largest possible value */
1014 /* The value returned in eax is never actually needed :-) */
1015 int round_to_int(FPU_REG *r)
     /* [previous][next][first][last][top][bottom][index][help] */
1016 {
1017   char     very_big;
1018   unsigned eax;
1019 
1020   if (r->tag == TW_Zero)
1021     {
1022       /* Make sure that zero is returned */
1023       *(long long *)&r->sigl = 0;
1024       return 0;        /* o.k. */
1025     }
1026   
1027   if (r->exp > EXP_BIAS + 63)
1028     {
1029       r->sigl = r->sigh = ~0;      /* The largest representable number */
1030       return 1;        /* overflow */
1031     }
1032 
1033   eax = shrxs(&r->sigl, EXP_BIAS + 63 - r->exp);
1034   very_big = !(~(r->sigh) | ~(r->sigl));  /* test for 0xfff...fff */
1035 #define half_or_more    (eax & 0x80000000)
1036 #define frac_part       (eax)
1037 #define more_than_half  ((eax & 0x80000001) == 0x80000001)
1038   switch (control_word & CW_RC)
1039     {
1040     case RC_RND:
1041       if ( more_than_half                       /* nearest */
1042           || (half_or_more && (r->sigl & 1)) )  /* odd -> even */
1043         {
1044           if ( very_big ) return 1;        /* overflow */
1045           (*(long long *)(&r->sigl)) ++;
1046         }
1047       break;
1048     case RC_DOWN:
1049       if (frac_part && r->sign)
1050         {
1051           if ( very_big ) return 1;        /* overflow */
1052           (*(long long *)(&r->sigl)) ++;
1053         }
1054       break;
1055     case RC_UP:
1056       if (frac_part && !r->sign)
1057         {
1058           if ( very_big ) return 1;        /* overflow */
1059           (*(long long *)(&r->sigl)) ++;
1060         }
1061       break;
1062     case RC_CHOP:
1063       break;
1064     }
1065 
1066   return 0;           /* o.k. */
1067 }
1068 
1069 /*===========================================================================*/
1070 
1071 char *fldenv(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1072 {
1073   char *s = (char *)FPU_data_address;
1074   unsigned short tag_word = 0;
1075   unsigned char tag;
1076   int i;
1077 
1078   RE_ENTRANT_CHECK_OFF
1079   control_word = get_fs_word((unsigned short *) s);
1080   status_word = get_fs_word((unsigned short *) (s+4));
1081   tag_word = get_fs_word((unsigned short *) (s+8));
1082   ip_offset = get_fs_long((unsigned long *) (s+0x0c));
1083   cs_selector = get_fs_long((unsigned long *) (s+0x10));
1084   data_operand_offset = get_fs_long((unsigned long *) (s+0x14));
1085   operand_selector = get_fs_long((unsigned long *) (s+0x18));
1086   RE_ENTRANT_CHECK_ON
1087 
1088 
1089   for ( i = 7; i >= 0; i-- )
1090     {
1091       tag = tag_word & 3;
1092       tag_word <<= 2;
1093 
1094       switch ( tag )
1095         {
1096         case 0:
1097           regs[i].tag = TW_Valid;
1098           break;
1099         case 1:
1100           regs[i].tag = TW_Zero;
1101           break;
1102         case 2:
1103           regs[i].tag = TW_NaN;
1104           break;
1105         case 3:
1106           regs[i].tag = TW_Empty;
1107           break;
1108         }
1109     }
1110 
1111   FPU_data_address = (void *)data_operand_offset;  /* We want no net effect */
1112   FPU_entry_eip = ip_offset;               /* We want no net effect */
1113 
1114   return s + 0x1c;
1115 }
1116 
1117 
1118 void frstor(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1119 {
1120   int i;
1121   unsigned char tag;
1122   FPU_REG *s = (FPU_REG *)fldenv();
1123 
1124   for ( i = 0; i < 8; i++ )
1125     {
1126       /* load each register */
1127       FPU_data_address = (void *)&(s[i]);
1128       reg_load_extended();
1129       tag = regs[i].tag;
1130       reg_move(&FPU_loaded_data, &regs[i]);
1131       if ( tag == TW_NaN )
1132         {
1133           unsigned char t = regs[i].tag;
1134           if ( (t == TW_Valid) || (t == TW_Zero) )
1135             regs[i].tag = TW_NaN;
1136         }
1137       else
1138         regs[i].tag = tag;
1139     }
1140 
1141   FPU_data_address = (void *)data_operand_offset;  /* We want no net effect */
1142 }
1143 
1144 
1145 char *fstenv(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1146 {
1147   char *d = (char *)FPU_data_address;
1148   unsigned short tag_word = 0;
1149   unsigned char tag;
1150   int i;
1151 
1152   verify_area(d,28);
1153 
1154   for ( i = 7; i >= 0; i-- )
1155     {
1156       switch ( tag = regs[i].tag )
1157         {
1158         case TW_Denormal:
1159         case TW_Infinity:
1160         case TW_NaN:
1161           tag = 2;
1162           break;
1163         case TW_Empty:
1164           tag = 3;
1165           break;
1166           /* TW_Valid and TW_Zero already have the correct value */
1167         }
1168       tag_word <<= 2;
1169       tag_word |= tag;
1170     }
1171 
1172   /* This is not what should be done ... but saves overheads. */
1173   *(unsigned short *)&cs_selector = FPU_CS;
1174   *(unsigned short *)&operand_selector = FPU_DS;
1175 
1176   RE_ENTRANT_CHECK_OFF
1177   put_fs_word(control_word, (unsigned short *) d);
1178   put_fs_word(status_word, (unsigned short *) (d+4));
1179   put_fs_word(tag_word, (unsigned short *) (d+8));
1180   put_fs_long(ip_offset, (unsigned long *) (d+0x0c));
1181   put_fs_long(cs_selector, (unsigned long *) (d+0x10));
1182   put_fs_long(data_operand_offset, (unsigned long *) (d+0x14));
1183   put_fs_long(operand_selector, (unsigned long *) (d+0x18));
1184   RE_ENTRANT_CHECK_ON
1185 
1186   return d + 0x1c;
1187 }
1188 
1189 
1190 void fsave(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1191 {
1192   char *d;
1193   FPU_REG tmp, *rp;
1194   int i;
1195   short e;
1196 
1197   d = fstenv();
1198   verify_area(d,80);
1199   for ( i = 0; i < 8; i++ )
1200     {
1201       /* store each register */
1202       rp = &regs[i];
1203 
1204       e = rp->exp - EXP_BIAS + EXTENDED_Ebias;
1205 
1206       if ( rp->tag == TW_Valid )
1207         {
1208           if ( e >= 0x7fff )
1209             {
1210               /* Overflow to infinity */
1211               RE_ENTRANT_CHECK_OFF
1212               put_fs_long(0, (unsigned long *) (d+i*10+2));
1213               put_fs_long(0x80000000, (unsigned long *) (d+i*10+6));
1214               RE_ENTRANT_CHECK_ON
1215               e = 0x7fff;
1216             }
1217           else if ( e <= 0 )
1218             {
1219               if ( e == 0 )
1220                 {
1221                   /* Pseudo de-normal */
1222                   RE_ENTRANT_CHECK_OFF
1223                   put_fs_long(rp->sigl, (unsigned long *) (d+i*10+2));
1224                   put_fs_long(rp->sigh, (unsigned long *) (d+i*10+6));
1225                   RE_ENTRANT_CHECK_ON
1226                 }
1227               else if ( e > -64 )
1228                 {
1229                   /* Make a de-normal */
1230                   reg_move(rp, &tmp);
1231                   tmp.exp += -EXTENDED_Emin + 64;  /* largest exp to be 63 */
1232                   round_to_int(&tmp);
1233                   e = 0;
1234                   RE_ENTRANT_CHECK_OFF
1235                   put_fs_long(tmp.sigl, (unsigned long *) (d+i*10+2));
1236                   put_fs_long(tmp.sigh, (unsigned long *) (d+i*10+6));
1237                   RE_ENTRANT_CHECK_ON
1238                 }
1239               else
1240                 {
1241                   /* Underflow to zero */
1242                   RE_ENTRANT_CHECK_OFF
1243                   put_fs_long(0, (unsigned long *) (d+i*10+2));
1244                   put_fs_long(0, (unsigned long *) (d+i*10+6));
1245                   RE_ENTRANT_CHECK_ON
1246                   e = 0;
1247                 }
1248             }
1249           else
1250             {
1251               RE_ENTRANT_CHECK_OFF
1252               put_fs_long(rp->sigl, (unsigned long *) (d+i*10+2));
1253               put_fs_long(rp->sigh, (unsigned long *) (d+i*10+6));
1254               RE_ENTRANT_CHECK_ON
1255             }
1256         }
1257       else if ( rp->tag == TW_Zero )
1258         {
1259           RE_ENTRANT_CHECK_OFF
1260           put_fs_long(0, (unsigned long *) (d+i*10+2));
1261           put_fs_long(0, (unsigned long *) (d+i*10+6));
1262           RE_ENTRANT_CHECK_ON
1263           e = 0;
1264         }
1265       else if ( rp->tag == TW_Infinity )
1266         {
1267           RE_ENTRANT_CHECK_OFF
1268           put_fs_long(0, (unsigned long *) (d+i*10+2));
1269           put_fs_long(0x80000000, (unsigned long *) (d+i*10+6));
1270           RE_ENTRANT_CHECK_ON
1271           e = 0x7fff;
1272         }
1273       else if ( rp->tag == TW_NaN )
1274         {
1275           RE_ENTRANT_CHECK_OFF
1276           put_fs_long(rp->sigl, (unsigned long *) (d+i*10+2));
1277           put_fs_long(rp->sigh, (unsigned long *) (d+i*10+6));
1278           RE_ENTRANT_CHECK_ON
1279           e = 0x7fff;
1280         }
1281       else if ( rp->tag == TW_Empty )
1282         {
1283           /* just copy the reg */
1284           RE_ENTRANT_CHECK_OFF
1285           put_fs_long(rp->sigl, (unsigned long *) (d+i*10+2));
1286           put_fs_long(rp->sigh, (unsigned long *) (d+i*10+6));
1287           RE_ENTRANT_CHECK_ON
1288         }
1289       RE_ENTRANT_CHECK_OFF
1290       put_fs_word(e, (unsigned short *) (d+i*10));
1291       RE_ENTRANT_CHECK_ON
1292     }
1293 
1294 }
1295 
1296 /*===========================================================================*/

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