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