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