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

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

DEFINITIONS

This source file includes following definitions.
  1. Un_impl
  2. FPU_illegal
  3. emu_printall
  4. exception
  5. real_2op_NaN
  6. arith_invalid
  7. divide_by_zero
  8. set_precision_flag
  9. set_precision_flag_up
  10. set_precision_flag_down
  11. denormal_operand
  12. arith_overflow
  13. arith_underflow
  14. stack_overflow
  15. stack_underflow
  16. stack_underflow_i
  17. stack_underflow_pop

   1 /*---------------------------------------------------------------------------+
   2  |  errors.c                                                                 |
   3  |                                                                           |
   4  |  The error handling functions for wm-FPU-emu                              |
   5  |                                                                           |
   6  | Copyright (C) 1992,1993,1994,1996                                         |
   7  |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
   8  |                  E-mail   billm@jacobi.maths.monash.edu.au                |
   9  |                                                                           |
  10  |                                                                           |
  11  +---------------------------------------------------------------------------*/
  12 
  13 /*---------------------------------------------------------------------------+
  14  | Note:                                                                     |
  15  |    The file contains code which accesses user memory.                     |
  16  |    Emulator static data may change when user memory is accessed, due to   |
  17  |    other processes using the emulator while swapping is in progress.      |
  18  +---------------------------------------------------------------------------*/
  19 
  20 #include <linux/signal.h>
  21 
  22 #include <asm/segment.h>
  23 
  24 #include "fpu_system.h"
  25 #include "exception.h"
  26 #include "fpu_emu.h"
  27 #include "status_w.h"
  28 #include "control_w.h"
  29 #include "reg_constant.h"
  30 #include "version.h"
  31 
  32 /* */
  33 #undef PRINT_MESSAGES
  34 /* */
  35 
  36 
  37 void Un_impl(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  38 {
  39   unsigned char byte1, FPU_modrm;
  40   unsigned long address = FPU_ORIG_EIP;
  41 
  42   RE_ENTRANT_CHECK_OFF;
  43   /* No need to verify_area(), we have previously fetched these bytes. */
  44   printk("Unimplemented FPU Opcode at eip=%p : ", (void *) address);
  45   if ( FPU_CS == USER_CS )
  46     {
  47       while ( 1 )
  48         {
  49           byte1 = get_fs_byte((unsigned char *) address);
  50           if ( (byte1 & 0xf8) == 0xd8 ) break;
  51           printk("[%02x]", byte1);
  52           address++;
  53         }
  54       printk("%02x ", byte1);
  55       FPU_modrm = get_fs_byte(1 + (unsigned char *) address);
  56       
  57       if (FPU_modrm >= 0300)
  58         printk("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7);
  59       else
  60         printk("/%d\n", (FPU_modrm >> 3) & 7);
  61     }
  62   else
  63     {
  64       printk("cs selector = %04x\n", FPU_CS);
  65     }
  66 
  67   RE_ENTRANT_CHECK_ON;
  68 
  69   EXCEPTION(EX_Invalid);
  70 
  71 }
  72 
  73 
  74 /*
  75    Called for opcodes which are illegal and which are known to result in a
  76    SIGILL with a real 80486.
  77    */
  78 void FPU_illegal(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  79 {
  80   math_abort(FPU_info,SIGILL);
  81 }
  82 
  83 
  84 
  85 void emu_printall(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  86 {
  87   int i;
  88   static const char *tag_desc[] = { "Valid", "Zero", "ERROR", "ERROR",
  89                               "DeNorm", "Inf", "NaN", "Empty" };
  90   unsigned char byte1, FPU_modrm;
  91   unsigned long address = FPU_ORIG_EIP;
  92 
  93   RE_ENTRANT_CHECK_OFF;
  94   /* No need to verify_area(), we have previously fetched these bytes. */
  95   printk("At %p:", (void *) address);
  96   if ( FPU_CS == USER_CS )
  97     {
  98 #define MAX_PRINTED_BYTES 20
  99       for ( i = 0; i < MAX_PRINTED_BYTES; i++ )
 100         {
 101           byte1 = get_fs_byte((unsigned char *) address);
 102           if ( (byte1 & 0xf8) == 0xd8 )
 103             {
 104               printk(" %02x", byte1);
 105               break;
 106             }
 107           printk(" [%02x]", byte1);
 108           address++;
 109         }
 110       if ( i == MAX_PRINTED_BYTES )
 111         printk(" [more..]\n");
 112       else
 113         {
 114           FPU_modrm = get_fs_byte(1 + (unsigned char *) address);
 115           
 116           if (FPU_modrm >= 0300)
 117             printk(" %02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7);
 118           else
 119             printk(" /%d, mod=%d rm=%d\n",
 120                    (FPU_modrm >> 3) & 7, (FPU_modrm >> 6) & 3, FPU_modrm & 7);
 121         }
 122     }
 123   else
 124     {
 125       printk("%04x\n", FPU_CS);
 126     }
 127 
 128   partial_status = status_word();
 129 
 130 #ifdef DEBUGGING
 131 if ( partial_status & SW_Backward )    printk("SW: backward compatibility\n");
 132 if ( partial_status & SW_C3 )          printk("SW: condition bit 3\n");
 133 if ( partial_status & SW_C2 )          printk("SW: condition bit 2\n");
 134 if ( partial_status & SW_C1 )          printk("SW: condition bit 1\n");
 135 if ( partial_status & SW_C0 )          printk("SW: condition bit 0\n");
 136 if ( partial_status & SW_Summary )     printk("SW: exception summary\n");
 137 if ( partial_status & SW_Stack_Fault ) printk("SW: stack fault\n");
 138 if ( partial_status & SW_Precision )   printk("SW: loss of precision\n");
 139 if ( partial_status & SW_Underflow )   printk("SW: underflow\n");
 140 if ( partial_status & SW_Overflow )    printk("SW: overflow\n");
 141 if ( partial_status & SW_Zero_Div )    printk("SW: divide by zero\n");
 142 if ( partial_status & SW_Denorm_Op )   printk("SW: denormalized operand\n");
 143 if ( partial_status & SW_Invalid )     printk("SW: invalid operation\n");
 144 #endif DEBUGGING
 145 
 146   printk(" SW: b=%d st=%ld es=%d sf=%d cc=%d%d%d%d ef=%d%d%d%d%d%d\n",
 147          partial_status & 0x8000 ? 1 : 0,   /* busy */
 148          (partial_status & 0x3800) >> 11,   /* stack top pointer */
 149          partial_status & 0x80 ? 1 : 0,     /* Error summary status */
 150          partial_status & 0x40 ? 1 : 0,     /* Stack flag */
 151          partial_status & SW_C3?1:0, partial_status & SW_C2?1:0, /* cc */
 152          partial_status & SW_C1?1:0, partial_status & SW_C0?1:0, /* cc */
 153          partial_status & SW_Precision?1:0, partial_status & SW_Underflow?1:0,
 154          partial_status & SW_Overflow?1:0, partial_status & SW_Zero_Div?1:0,
 155          partial_status & SW_Denorm_Op?1:0, partial_status & SW_Invalid?1:0);
 156   
 157 printk(" CW: ic=%d rc=%ld%ld pc=%ld%ld iem=%d     ef=%d%d%d%d%d%d\n",
 158          control_word & 0x1000 ? 1 : 0,
 159          (control_word & 0x800) >> 11, (control_word & 0x400) >> 10,
 160          (control_word & 0x200) >> 9, (control_word & 0x100) >> 8,
 161          control_word & 0x80 ? 1 : 0,
 162          control_word & SW_Precision?1:0, control_word & SW_Underflow?1:0,
 163          control_word & SW_Overflow?1:0, control_word & SW_Zero_Div?1:0,
 164          control_word & SW_Denorm_Op?1:0, control_word & SW_Invalid?1:0);
 165 
 166   for ( i = 0; i < 8; i++ )
 167     {
 168       FPU_REG *r = &st(i);
 169       char tagi = r->tag;
 170       switch (tagi)
 171         {
 172         case TW_Empty:
 173           continue;
 174           break;
 175         case TW_Zero:
 176 #if 0
 177           printk("st(%d)  %c .0000 0000 0000 0000         ",
 178                  i, r->sign ? '-' : '+');
 179           break;
 180 #endif
 181         case TW_Valid:
 182         case TW_NaN:
 183 /*      case TW_Denormal: */
 184         case TW_Infinity:
 185           printk("st(%d)  %c .%04lx %04lx %04lx %04lx e%+-6ld ", i,
 186                  r->sign ? '-' : '+',
 187                  (long)(r->sigh >> 16),
 188                  (long)(r->sigh & 0xFFFF),
 189                  (long)(r->sigl >> 16),
 190                  (long)(r->sigl & 0xFFFF),
 191                  r->exp - EXP_BIAS + 1);
 192           break;
 193         default:
 194           printk("Whoops! Error in errors.c: tag%d is %d ", i, tagi);
 195           continue;
 196           break;
 197         }
 198       printk("%s\n", tag_desc[(int) (unsigned) tagi]);
 199     }
 200 
 201   RE_ENTRANT_CHECK_ON;
 202 
 203 }
 204 
 205 static struct {
 206   int type;
 207   const char *name;
 208 } exception_names[] = {
 209   { EX_StackOver, "stack overflow" },
 210   { EX_StackUnder, "stack underflow" },
 211   { EX_Precision, "loss of precision" },
 212   { EX_Underflow, "underflow" },
 213   { EX_Overflow, "overflow" },
 214   { EX_ZeroDiv, "divide by zero" },
 215   { EX_Denormal, "denormalized operand" },
 216   { EX_Invalid, "invalid operation" },
 217   { EX_INTERNAL, "INTERNAL BUG in "FPU_VERSION },
 218   { 0, NULL }
 219 };
 220 
 221 /*
 222  EX_INTERNAL is always given with a code which indicates where the
 223  error was detected.
 224 
 225  Internal error types:
 226        0x14   in fpu_etc.c
 227        0x1nn  in a *.c file:
 228               0x101  in reg_add_sub.c
 229               0x102  in reg_mul.c
 230               0x104  in poly_atan.c
 231               0x105  in reg_mul.c
 232               0x107  in fpu_trig.c
 233               0x108  in reg_compare.c
 234               0x109  in reg_compare.c
 235               0x110  in reg_add_sub.c
 236               0x111  in fpe_entry.c
 237               0x112  in fpu_trig.c
 238               0x113  in errors.c
 239               0x115  in fpu_trig.c
 240               0x116  in fpu_trig.c
 241               0x117  in fpu_trig.c
 242               0x118  in fpu_trig.c
 243               0x119  in fpu_trig.c
 244               0x120  in poly_atan.c
 245               0x121  in reg_compare.c
 246               0x122  in reg_compare.c
 247               0x123  in reg_compare.c
 248               0x125  in fpu_trig.c
 249               0x126  in fpu_entry.c
 250               0x127  in poly_2xm1.c
 251               0x128  in fpu_entry.c
 252               0x129  in fpu_entry.c
 253               0x130  in get_address.c
 254               0x131  in get_address.c
 255               0x132  in get_address.c
 256               0x133  in get_address.c
 257               0x140  in load_store.c
 258               0x141  in load_store.c
 259               0x150  in poly_sin.c
 260               0x151  in poly_sin.c
 261               0x160  in reg_ld_str.c
 262               0x161  in reg_ld_str.c
 263               0x162  in reg_ld_str.c
 264               0x163  in reg_ld_str.c
 265        0x2nn  in an *.S file:
 266               0x201  in reg_u_add.S
 267               0x202  in reg_u_div.S
 268               0x203  in reg_u_div.S
 269               0x204  in reg_u_div.S
 270               0x205  in reg_u_mul.S
 271               0x206  in reg_u_sub.S
 272               0x207  in wm_sqrt.S
 273               0x208  in reg_div.S
 274               0x209  in reg_u_sub.S
 275               0x210  in reg_u_sub.S
 276               0x211  in reg_u_sub.S
 277               0x212  in reg_u_sub.S
 278               0x213  in wm_sqrt.S
 279               0x214  in wm_sqrt.S
 280               0x215  in wm_sqrt.S
 281               0x220  in reg_norm.S
 282               0x221  in reg_norm.S
 283               0x230  in reg_round.S
 284               0x231  in reg_round.S
 285               0x232  in reg_round.S
 286               0x233  in reg_round.S
 287               0x234  in reg_round.S
 288               0x235  in reg_round.S
 289               0x236  in reg_round.S
 290               0x240  in div_Xsig.S
 291               0x241  in div_Xsig.S
 292               0x242  in div_Xsig.S
 293  */
 294 
 295 void exception(int n)
     /* [previous][next][first][last][top][bottom][index][help] */
 296 {
 297   int i, int_type;
 298 
 299   int_type = 0;         /* Needed only to stop compiler warnings */
 300   if ( n & EX_INTERNAL )
 301     {
 302       int_type = n - EX_INTERNAL;
 303       n = EX_INTERNAL;
 304       /* Set lots of exception bits! */
 305       partial_status |= (SW_Exc_Mask | SW_Summary | SW_Backward);
 306     }
 307   else
 308     {
 309       /* Extract only the bits which we use to set the status word */
 310       n &= (SW_Exc_Mask);
 311       /* Set the corresponding exception bit */
 312       partial_status |= n;
 313       /* Set summary bits iff exception isn't masked */
 314       if ( partial_status & ~control_word & CW_Exceptions )
 315         partial_status |= (SW_Summary | SW_Backward);
 316       if ( n & (SW_Stack_Fault | EX_Precision) )
 317         {
 318           if ( !(n & SW_C1) )
 319             /* This bit distinguishes over- from underflow for a stack fault,
 320                and roundup from round-down for precision loss. */
 321             partial_status &= ~SW_C1;
 322         }
 323     }
 324 
 325   RE_ENTRANT_CHECK_OFF;
 326   if ( (~control_word & n & CW_Exceptions) || (n == EX_INTERNAL) )
 327     {
 328 #ifdef PRINT_MESSAGES
 329       /* My message from the sponsor */
 330       printk(FPU_VERSION" "__DATE__" (C) W. Metzenthen.\n");
 331 #endif PRINT_MESSAGES
 332       
 333       /* Get a name string for error reporting */
 334       for (i=0; exception_names[i].type; i++)
 335         if ( (exception_names[i].type & n) == exception_names[i].type )
 336           break;
 337       
 338       if (exception_names[i].type)
 339         {
 340 #ifdef PRINT_MESSAGES
 341           printk("FP Exception: %s!\n", exception_names[i].name);
 342 #endif PRINT_MESSAGES
 343         }
 344       else
 345         printk("FPU emulator: Unknown Exception: 0x%04x!\n", n);
 346       
 347       if ( n == EX_INTERNAL )
 348         {
 349           printk("FPU emulator: Internal error type 0x%04x\n", int_type);
 350           emu_printall();
 351         }
 352 #ifdef PRINT_MESSAGES
 353       else
 354         emu_printall();
 355 #endif PRINT_MESSAGES
 356 
 357       /*
 358        * The 80486 generates an interrupt on the next non-control FPU
 359        * instruction. So we need some means of flagging it.
 360        * We use the ES (Error Summary) bit for this.
 361        */
 362     }
 363   RE_ENTRANT_CHECK_ON;
 364 
 365 #ifdef __DEBUG__
 366   math_abort(FPU_info,SIGFPE);
 367 #endif __DEBUG__
 368 
 369 }
 370 
 371 
 372 /* Real operation attempted on two operands, one a NaN. */
 373 /* Returns nz if the exception is unmasked */
 374 asmlinkage int real_2op_NaN(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest)
     /* [previous][next][first][last][top][bottom][index][help] */
 375 {
 376   FPU_REG const *x;
 377   int signalling;
 378 
 379   /* The default result for the case of two "equal" NaNs (signs may
 380      differ) is chosen to reproduce 80486 behaviour */
 381   x = a;
 382   if (a->tag == TW_NaN)
 383     {
 384       if (b->tag == TW_NaN)
 385         {
 386           signalling = !(a->sigh & b->sigh & 0x40000000);
 387           /* find the "larger" */
 388           if ( significand(a) < significand(b) )
 389             x = b;
 390         }
 391       else
 392         {
 393           /* return the quiet version of the NaN in a */
 394           signalling = !(a->sigh & 0x40000000);
 395         }
 396     }
 397   else
 398 #ifdef PARANOID
 399     if (b->tag == TW_NaN)
 400 #endif PARANOID
 401     {
 402       signalling = !(b->sigh & 0x40000000);
 403       x = b;
 404     }
 405 #ifdef PARANOID
 406   else
 407     {
 408       signalling = 0;
 409       EXCEPTION(EX_INTERNAL|0x113);
 410       x = &CONST_QNaN;
 411     }
 412 #endif PARANOID
 413 
 414   if ( !signalling )
 415     {
 416       if ( !(x->sigh & 0x80000000) )  /* pseudo-NaN ? */
 417         x = &CONST_QNaN;
 418       reg_move(x, dest);
 419       return 0;
 420     }
 421 
 422   if ( control_word & CW_Invalid )
 423     {
 424       /* The masked response */
 425       if ( !(x->sigh & 0x80000000) )  /* pseudo-NaN ? */
 426         x = &CONST_QNaN;
 427       reg_move(x, dest);
 428       /* ensure a Quiet NaN */
 429       dest->sigh |= 0x40000000;
 430     }
 431 
 432   EXCEPTION(EX_Invalid);
 433   
 434   return !(control_word & CW_Invalid);
 435 }
 436 
 437 
 438 /* Invalid arith operation on Valid registers */
 439 /* Returns nz if the exception is unmasked */
 440 asmlinkage int arith_invalid(FPU_REG *dest)
     /* [previous][next][first][last][top][bottom][index][help] */
 441 {
 442 
 443   EXCEPTION(EX_Invalid);
 444   
 445   if ( control_word & CW_Invalid )
 446     {
 447       /* The masked response */
 448       reg_move(&CONST_QNaN, dest);
 449     }
 450   
 451   return !(control_word & CW_Invalid);
 452 
 453 }
 454 
 455 
 456 /* Divide a finite number by zero */
 457 asmlinkage int divide_by_zero(int sign, FPU_REG *dest)
     /* [previous][next][first][last][top][bottom][index][help] */
 458 {
 459 
 460   if ( control_word & CW_ZeroDiv )
 461     {
 462       /* The masked response */
 463       reg_move(&CONST_INF, dest);
 464       dest->sign = (unsigned char)sign;
 465     }
 466  
 467   EXCEPTION(EX_ZeroDiv);
 468 
 469   return !(control_word & CW_ZeroDiv);
 470 
 471 }
 472 
 473 
 474 /* This may be called often, so keep it lean */
 475 int set_precision_flag(int flags)
     /* [previous][next][first][last][top][bottom][index][help] */
 476 {
 477   if ( control_word & CW_Precision )
 478     {
 479       partial_status &= ~(SW_C1 & flags);
 480       partial_status |= flags;   /* The masked response */
 481       return 0;
 482     }
 483   else
 484     {
 485       exception(flags);
 486       return 1;
 487     }
 488 }
 489 
 490 
 491 /* This may be called often, so keep it lean */
 492 asmlinkage void set_precision_flag_up(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 493 {
 494   if ( control_word & CW_Precision )
 495     partial_status |= (SW_Precision | SW_C1);   /* The masked response */
 496   else
 497     exception(EX_Precision | SW_C1);
 498 
 499 }
 500 
 501 
 502 /* This may be called often, so keep it lean */
 503 asmlinkage void set_precision_flag_down(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 504 {
 505   if ( control_word & CW_Precision )
 506     {   /* The masked response */
 507       partial_status &= ~SW_C1;
 508       partial_status |= SW_Precision;
 509     }
 510   else
 511     exception(EX_Precision);
 512 }
 513 
 514 
 515 asmlinkage int denormal_operand(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 516 {
 517   if ( control_word & CW_Denormal )
 518     {   /* The masked response */
 519       partial_status |= SW_Denorm_Op;
 520       return 0;
 521     }
 522   else
 523     {
 524       exception(EX_Denormal);
 525       return 1;
 526     }
 527 }
 528 
 529 
 530 asmlinkage int arith_overflow(FPU_REG *dest)
     /* [previous][next][first][last][top][bottom][index][help] */
 531 {
 532 
 533   if ( control_word & CW_Overflow )
 534     {
 535       char sign;
 536       /* The masked response */
 537 /* ###### The response here depends upon the rounding mode */
 538       sign = dest->sign;
 539       reg_move(&CONST_INF, dest);
 540       dest->sign = sign;
 541     }
 542   else
 543     {
 544       /* Subtract the magic number from the exponent */
 545       dest->exp -= (3 * (1 << 13));
 546     }
 547 
 548   EXCEPTION(EX_Overflow);
 549   if ( control_word & CW_Overflow )
 550     {
 551       /* The overflow exception is masked. */
 552       /* By definition, precision is lost.
 553          The roundup bit (C1) is also set because we have
 554          "rounded" upwards to Infinity. */
 555       EXCEPTION(EX_Precision | SW_C1);
 556       return !(control_word & CW_Precision);
 557     }
 558 
 559   return 0;
 560 
 561 }
 562 
 563 
 564 asmlinkage int arith_underflow(FPU_REG *dest)
     /* [previous][next][first][last][top][bottom][index][help] */
 565 {
 566 
 567   if ( control_word & CW_Underflow )
 568     {
 569       /* The masked response */
 570       if ( dest->exp <= EXP_UNDER - 63 )
 571         {
 572           reg_move(&CONST_Z, dest);
 573           partial_status &= ~SW_C1;       /* Round down. */
 574         }
 575     }
 576   else
 577     {
 578       /* Add the magic number to the exponent. */
 579       dest->exp += (3 * (1 << 13));
 580     }
 581 
 582   EXCEPTION(EX_Underflow);
 583   if ( control_word & CW_Underflow )
 584     {
 585       /* The underflow exception is masked. */
 586       EXCEPTION(EX_Precision);
 587       return !(control_word & CW_Precision);
 588     }
 589 
 590   return 0;
 591 
 592 }
 593 
 594 
 595 void stack_overflow(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 596 {
 597 
 598  if ( control_word & CW_Invalid )
 599     {
 600       /* The masked response */
 601       top--;
 602       reg_move(&CONST_QNaN, &st(0));
 603     }
 604 
 605   EXCEPTION(EX_StackOver);
 606 
 607   return;
 608 
 609 }
 610 
 611 
 612 void stack_underflow(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 613 {
 614 
 615  if ( control_word & CW_Invalid )
 616     {
 617       /* The masked response */
 618       reg_move(&CONST_QNaN, &st(0));
 619     }
 620 
 621   EXCEPTION(EX_StackUnder);
 622 
 623   return;
 624 
 625 }
 626 
 627 
 628 void stack_underflow_i(int i)
     /* [previous][next][first][last][top][bottom][index][help] */
 629 {
 630 
 631  if ( control_word & CW_Invalid )
 632     {
 633       /* The masked response */
 634       reg_move(&CONST_QNaN, &(st(i)));
 635     }
 636 
 637   EXCEPTION(EX_StackUnder);
 638 
 639   return;
 640 
 641 }
 642 
 643 
 644 void stack_underflow_pop(int i)
     /* [previous][next][first][last][top][bottom][index][help] */
 645 {
 646 
 647  if ( control_word & CW_Invalid )
 648     {
 649       /* The masked response */
 650       reg_move(&CONST_QNaN, &(st(i)));
 651       pop();
 652     }
 653 
 654   EXCEPTION(EX_StackUnder);
 655 
 656   return;
 657 
 658 }
 659 

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