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

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