cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

errors.c (18116B)


      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
    182		switch (tagi) {
    183		case TAG_Empty:
    184			continue;
    185		case TAG_Zero:
    186		case TAG_Special:
    187			/* Update tagi for the printk below */
    188			tagi = FPU_Special(r);
    189			fallthrough;
    190		case TAG_Valid:
    191			printk("st(%d)  %c .%04lx %04lx %04lx %04lx e%+-6d ", i,
    192			       getsign(r) ? '-' : '+',
    193			       (long)(r->sigh >> 16),
    194			       (long)(r->sigh & 0xFFFF),
    195			       (long)(r->sigl >> 16),
    196			       (long)(r->sigl & 0xFFFF),
    197			       exponent(r) - EXP_BIAS + 1);
    198			break;
    199		default:
    200			printk("Whoops! Error in errors.c: tag%d is %d ", i,
    201			       tagi);
    202			continue;
    203		}
    204		printk("%s\n", tag_desc[(int)(unsigned)tagi]);
    205	}
    206
    207	RE_ENTRANT_CHECK_ON;
    208
    209}
    210
    211static struct {
    212	int type;
    213	const char *name;
    214} exception_names[] = {
    215	{
    216	EX_StackOver, "stack overflow"}, {
    217	EX_StackUnder, "stack underflow"}, {
    218	EX_Precision, "loss of precision"}, {
    219	EX_Underflow, "underflow"}, {
    220	EX_Overflow, "overflow"}, {
    221	EX_ZeroDiv, "divide by zero"}, {
    222	EX_Denormal, "denormalized operand"}, {
    223	EX_Invalid, "invalid operation"}, {
    224	EX_INTERNAL, "INTERNAL BUG in " FPU_VERSION}, {
    225	0, NULL}
    226};
    227
    228/*
    229 EX_INTERNAL is always given with a code which indicates where the
    230 error was detected.
    231
    232 Internal error types:
    233       0x14   in fpu_etc.c
    234       0x1nn  in a *.c file:
    235              0x101  in reg_add_sub.c
    236              0x102  in reg_mul.c
    237              0x104  in poly_atan.c
    238              0x105  in reg_mul.c
    239              0x107  in fpu_trig.c
    240	      0x108  in reg_compare.c
    241	      0x109  in reg_compare.c
    242	      0x110  in reg_add_sub.c
    243	      0x111  in fpe_entry.c
    244	      0x112  in fpu_trig.c
    245	      0x113  in errors.c
    246	      0x115  in fpu_trig.c
    247	      0x116  in fpu_trig.c
    248	      0x117  in fpu_trig.c
    249	      0x118  in fpu_trig.c
    250	      0x119  in fpu_trig.c
    251	      0x120  in poly_atan.c
    252	      0x121  in reg_compare.c
    253	      0x122  in reg_compare.c
    254	      0x123  in reg_compare.c
    255	      0x125  in fpu_trig.c
    256	      0x126  in fpu_entry.c
    257	      0x127  in poly_2xm1.c
    258	      0x128  in fpu_entry.c
    259	      0x129  in fpu_entry.c
    260	      0x130  in get_address.c
    261	      0x131  in get_address.c
    262	      0x132  in get_address.c
    263	      0x133  in get_address.c
    264	      0x140  in load_store.c
    265	      0x141  in load_store.c
    266              0x150  in poly_sin.c
    267              0x151  in poly_sin.c
    268	      0x160  in reg_ld_str.c
    269	      0x161  in reg_ld_str.c
    270	      0x162  in reg_ld_str.c
    271	      0x163  in reg_ld_str.c
    272	      0x164  in reg_ld_str.c
    273	      0x170  in fpu_tags.c
    274	      0x171  in fpu_tags.c
    275	      0x172  in fpu_tags.c
    276	      0x180  in reg_convert.c
    277       0x2nn  in an *.S file:
    278              0x201  in reg_u_add.S
    279              0x202  in reg_u_div.S
    280              0x203  in reg_u_div.S
    281              0x204  in reg_u_div.S
    282              0x205  in reg_u_mul.S
    283              0x206  in reg_u_sub.S
    284              0x207  in wm_sqrt.S
    285	      0x208  in reg_div.S
    286              0x209  in reg_u_sub.S
    287              0x210  in reg_u_sub.S
    288              0x211  in reg_u_sub.S
    289              0x212  in reg_u_sub.S
    290	      0x213  in wm_sqrt.S
    291	      0x214  in wm_sqrt.S
    292	      0x215  in wm_sqrt.S
    293	      0x220  in reg_norm.S
    294	      0x221  in reg_norm.S
    295	      0x230  in reg_round.S
    296	      0x231  in reg_round.S
    297	      0x232  in reg_round.S
    298	      0x233  in reg_round.S
    299	      0x234  in reg_round.S
    300	      0x235  in reg_round.S
    301	      0x236  in reg_round.S
    302	      0x240  in div_Xsig.S
    303	      0x241  in div_Xsig.S
    304	      0x242  in div_Xsig.S
    305 */
    306
    307asmlinkage __visible void FPU_exception(int n)
    308{
    309	int i, int_type;
    310
    311	int_type = 0;		/* Needed only to stop compiler warnings */
    312	if (n & EX_INTERNAL) {
    313		int_type = n - EX_INTERNAL;
    314		n = EX_INTERNAL;
    315		/* Set lots of exception bits! */
    316		partial_status |= (SW_Exc_Mask | SW_Summary | SW_Backward);
    317	} else {
    318		/* Extract only the bits which we use to set the status word */
    319		n &= (SW_Exc_Mask);
    320		/* Set the corresponding exception bit */
    321		partial_status |= n;
    322		/* Set summary bits iff exception isn't masked */
    323		if (partial_status & ~control_word & CW_Exceptions)
    324			partial_status |= (SW_Summary | SW_Backward);
    325		if (n & (SW_Stack_Fault | EX_Precision)) {
    326			if (!(n & SW_C1))
    327				/* This bit distinguishes over- from underflow for a stack fault,
    328				   and roundup from round-down for precision loss. */
    329				partial_status &= ~SW_C1;
    330		}
    331	}
    332
    333	RE_ENTRANT_CHECK_OFF;
    334	if ((~control_word & n & CW_Exceptions) || (n == EX_INTERNAL)) {
    335		/* Get a name string for error reporting */
    336		for (i = 0; exception_names[i].type; i++)
    337			if ((exception_names[i].type & n) ==
    338			    exception_names[i].type)
    339				break;
    340
    341		if (exception_names[i].type) {
    342#ifdef PRINT_MESSAGES
    343			printk("FP Exception: %s!\n", exception_names[i].name);
    344#endif /* PRINT_MESSAGES */
    345		} else
    346			printk("FPU emulator: Unknown Exception: 0x%04x!\n", n);
    347
    348		if (n == EX_INTERNAL) {
    349			printk("FPU emulator: Internal error type 0x%04x\n",
    350			       int_type);
    351			FPU_printall();
    352		}
    353#ifdef PRINT_MESSAGES
    354		else
    355			FPU_printall();
    356#endif /* PRINT_MESSAGES */
    357
    358		/*
    359		 * The 80486 generates an interrupt on the next non-control FPU
    360		 * instruction. So we need some means of flagging it.
    361		 * We use the ES (Error Summary) bit for this.
    362		 */
    363	}
    364	RE_ENTRANT_CHECK_ON;
    365
    366#ifdef __DEBUG__
    367	math_abort(FPU_info, SIGFPE);
    368#endif /* __DEBUG__ */
    369
    370}
    371
    372/* Real operation attempted on a NaN. */
    373/* Returns < 0 if the exception is unmasked */
    374int real_1op_NaN(FPU_REG *a)
    375{
    376	int signalling, isNaN;
    377
    378	isNaN = (exponent(a) == EXP_OVER) && (a->sigh & 0x80000000);
    379
    380	/* The default result for the case of two "equal" NaNs (signs may
    381	   differ) is chosen to reproduce 80486 behaviour */
    382	signalling = isNaN && !(a->sigh & 0x40000000);
    383
    384	if (!signalling) {
    385		if (!isNaN) {	/* pseudo-NaN, or other unsupported? */
    386			if (control_word & CW_Invalid) {
    387				/* Masked response */
    388				reg_copy(&CONST_QNaN, a);
    389			}
    390			EXCEPTION(EX_Invalid);
    391			return (!(control_word & CW_Invalid) ? FPU_Exception :
    392				0) | TAG_Special;
    393		}
    394		return TAG_Special;
    395	}
    396
    397	if (control_word & CW_Invalid) {
    398		/* The masked response */
    399		if (!(a->sigh & 0x80000000)) {	/* pseudo-NaN ? */
    400			reg_copy(&CONST_QNaN, a);
    401		}
    402		/* ensure a Quiet NaN */
    403		a->sigh |= 0x40000000;
    404	}
    405
    406	EXCEPTION(EX_Invalid);
    407
    408	return (!(control_word & CW_Invalid) ? FPU_Exception : 0) | TAG_Special;
    409}
    410
    411/* Real operation attempted on two operands, one a NaN. */
    412/* Returns < 0 if the exception is unmasked */
    413int real_2op_NaN(FPU_REG const *b, u_char tagb,
    414		 int deststnr, FPU_REG const *defaultNaN)
    415{
    416	FPU_REG *dest = &st(deststnr);
    417	FPU_REG const *a = dest;
    418	u_char taga = FPU_gettagi(deststnr);
    419	FPU_REG const *x;
    420	int signalling, unsupported;
    421
    422	if (taga == TAG_Special)
    423		taga = FPU_Special(a);
    424	if (tagb == TAG_Special)
    425		tagb = FPU_Special(b);
    426
    427	/* TW_NaN is also used for unsupported data types. */
    428	unsupported = ((taga == TW_NaN)
    429		       && !((exponent(a) == EXP_OVER)
    430			    && (a->sigh & 0x80000000)))
    431	    || ((tagb == TW_NaN)
    432		&& !((exponent(b) == EXP_OVER) && (b->sigh & 0x80000000)));
    433	if (unsupported) {
    434		if (control_word & CW_Invalid) {
    435			/* Masked response */
    436			FPU_copy_to_regi(&CONST_QNaN, TAG_Special, deststnr);
    437		}
    438		EXCEPTION(EX_Invalid);
    439		return (!(control_word & CW_Invalid) ? FPU_Exception : 0) |
    440		    TAG_Special;
    441	}
    442
    443	if (taga == TW_NaN) {
    444		x = a;
    445		if (tagb == TW_NaN) {
    446			signalling = !(a->sigh & b->sigh & 0x40000000);
    447			if (significand(b) > significand(a))
    448				x = b;
    449			else if (significand(b) == significand(a)) {
    450				/* The default result for the case of two "equal" NaNs (signs may
    451				   differ) is chosen to reproduce 80486 behaviour */
    452				x = defaultNaN;
    453			}
    454		} else {
    455			/* return the quiet version of the NaN in a */
    456			signalling = !(a->sigh & 0x40000000);
    457		}
    458	} else
    459#ifdef PARANOID
    460	if (tagb == TW_NaN)
    461#endif /* PARANOID */
    462	{
    463		signalling = !(b->sigh & 0x40000000);
    464		x = b;
    465	}
    466#ifdef PARANOID
    467	else {
    468		signalling = 0;
    469		EXCEPTION(EX_INTERNAL | 0x113);
    470		x = &CONST_QNaN;
    471	}
    472#endif /* PARANOID */
    473
    474	if ((!signalling) || (control_word & CW_Invalid)) {
    475		if (!x)
    476			x = b;
    477
    478		if (!(x->sigh & 0x80000000))	/* pseudo-NaN ? */
    479			x = &CONST_QNaN;
    480
    481		FPU_copy_to_regi(x, TAG_Special, deststnr);
    482
    483		if (!signalling)
    484			return TAG_Special;
    485
    486		/* ensure a Quiet NaN */
    487		dest->sigh |= 0x40000000;
    488	}
    489
    490	EXCEPTION(EX_Invalid);
    491
    492	return (!(control_word & CW_Invalid) ? FPU_Exception : 0) | TAG_Special;
    493}
    494
    495/* Invalid arith operation on Valid registers */
    496/* Returns < 0 if the exception is unmasked */
    497asmlinkage __visible int arith_invalid(int deststnr)
    498{
    499
    500	EXCEPTION(EX_Invalid);
    501
    502	if (control_word & CW_Invalid) {
    503		/* The masked response */
    504		FPU_copy_to_regi(&CONST_QNaN, TAG_Special, deststnr);
    505	}
    506
    507	return (!(control_word & CW_Invalid) ? FPU_Exception : 0) | TAG_Valid;
    508
    509}
    510
    511/* Divide a finite number by zero */
    512asmlinkage __visible int FPU_divide_by_zero(int deststnr, u_char sign)
    513{
    514	FPU_REG *dest = &st(deststnr);
    515	int tag = TAG_Valid;
    516
    517	if (control_word & CW_ZeroDiv) {
    518		/* The masked response */
    519		FPU_copy_to_regi(&CONST_INF, TAG_Special, deststnr);
    520		setsign(dest, sign);
    521		tag = TAG_Special;
    522	}
    523
    524	EXCEPTION(EX_ZeroDiv);
    525
    526	return (!(control_word & CW_ZeroDiv) ? FPU_Exception : 0) | tag;
    527
    528}
    529
    530/* This may be called often, so keep it lean */
    531int set_precision_flag(int flags)
    532{
    533	if (control_word & CW_Precision) {
    534		partial_status &= ~(SW_C1 & flags);
    535		partial_status |= flags;	/* The masked response */
    536		return 0;
    537	} else {
    538		EXCEPTION(flags);
    539		return 1;
    540	}
    541}
    542
    543/* This may be called often, so keep it lean */
    544asmlinkage __visible void set_precision_flag_up(void)
    545{
    546	if (control_word & CW_Precision)
    547		partial_status |= (SW_Precision | SW_C1);	/* The masked response */
    548	else
    549		EXCEPTION(EX_Precision | SW_C1);
    550}
    551
    552/* This may be called often, so keep it lean */
    553asmlinkage __visible void set_precision_flag_down(void)
    554{
    555	if (control_word & CW_Precision) {	/* The masked response */
    556		partial_status &= ~SW_C1;
    557		partial_status |= SW_Precision;
    558	} else
    559		EXCEPTION(EX_Precision);
    560}
    561
    562asmlinkage __visible int denormal_operand(void)
    563{
    564	if (control_word & CW_Denormal) {	/* The masked response */
    565		partial_status |= SW_Denorm_Op;
    566		return TAG_Special;
    567	} else {
    568		EXCEPTION(EX_Denormal);
    569		return TAG_Special | FPU_Exception;
    570	}
    571}
    572
    573asmlinkage __visible int arith_overflow(FPU_REG *dest)
    574{
    575	int tag = TAG_Valid;
    576
    577	if (control_word & CW_Overflow) {
    578		/* The masked response */
    579/* ###### The response here depends upon the rounding mode */
    580		reg_copy(&CONST_INF, dest);
    581		tag = TAG_Special;
    582	} else {
    583		/* Subtract the magic number from the exponent */
    584		addexponent(dest, (-3 * (1 << 13)));
    585	}
    586
    587	EXCEPTION(EX_Overflow);
    588	if (control_word & CW_Overflow) {
    589		/* The overflow exception is masked. */
    590		/* By definition, precision is lost.
    591		   The roundup bit (C1) is also set because we have
    592		   "rounded" upwards to Infinity. */
    593		EXCEPTION(EX_Precision | SW_C1);
    594		return tag;
    595	}
    596
    597	return tag;
    598
    599}
    600
    601asmlinkage __visible int arith_underflow(FPU_REG *dest)
    602{
    603	int tag = TAG_Valid;
    604
    605	if (control_word & CW_Underflow) {
    606		/* The masked response */
    607		if (exponent16(dest) <= EXP_UNDER - 63) {
    608			reg_copy(&CONST_Z, dest);
    609			partial_status &= ~SW_C1;	/* Round down. */
    610			tag = TAG_Zero;
    611		} else {
    612			stdexp(dest);
    613		}
    614	} else {
    615		/* Add the magic number to the exponent. */
    616		addexponent(dest, (3 * (1 << 13)) + EXTENDED_Ebias);
    617	}
    618
    619	EXCEPTION(EX_Underflow);
    620	if (control_word & CW_Underflow) {
    621		/* The underflow exception is masked. */
    622		EXCEPTION(EX_Precision);
    623		return tag;
    624	}
    625
    626	return tag;
    627
    628}
    629
    630void FPU_stack_overflow(void)
    631{
    632
    633	if (control_word & CW_Invalid) {
    634		/* The masked response */
    635		top--;
    636		FPU_copy_to_reg0(&CONST_QNaN, TAG_Special);
    637	}
    638
    639	EXCEPTION(EX_StackOver);
    640
    641	return;
    642
    643}
    644
    645void FPU_stack_underflow(void)
    646{
    647
    648	if (control_word & CW_Invalid) {
    649		/* The masked response */
    650		FPU_copy_to_reg0(&CONST_QNaN, TAG_Special);
    651	}
    652
    653	EXCEPTION(EX_StackUnder);
    654
    655	return;
    656
    657}
    658
    659void FPU_stack_underflow_i(int i)
    660{
    661
    662	if (control_word & CW_Invalid) {
    663		/* The masked response */
    664		FPU_copy_to_regi(&CONST_QNaN, TAG_Special, i);
    665	}
    666
    667	EXCEPTION(EX_StackUnder);
    668
    669	return;
    670
    671}
    672
    673void FPU_stack_underflow_pop(int i)
    674{
    675
    676	if (control_word & CW_Invalid) {
    677		/* The masked response */
    678		FPU_copy_to_regi(&CONST_QNaN, TAG_Special, i);
    679		FPU_pop();
    680	}
    681
    682	EXCEPTION(EX_StackUnder);
    683
    684	return;
    685
    686}