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
38  void 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     */
FPU_illegal(void)77  void FPU_illegal(void)
78  {
79  	math_abort(FPU_info, SIGILL);
80  }
81  
FPU_printall(void)82  void 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  
211  static 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  
FPU_exception(int n)307  asmlinkage __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 */
real_1op_NaN(FPU_REG * a)374  int 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 */
real_2op_NaN(FPU_REG const * b,u_char tagb,int deststnr,FPU_REG const * defaultNaN)413  int 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 */
arith_invalid(int deststnr)497  asmlinkage __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 */
FPU_divide_by_zero(int deststnr,u_char sign)512  asmlinkage __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 */
set_precision_flag(int flags)531  int 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 */
set_precision_flag_up(void)544  asmlinkage __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 */
set_precision_flag_down(void)553  asmlinkage __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  
denormal_operand(void)562  asmlinkage __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  
arith_overflow(FPU_REG * dest)573  asmlinkage __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  
arith_underflow(FPU_REG * dest)601  asmlinkage __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  
FPU_stack_overflow(void)630  void 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  
FPU_stack_underflow(void)645  void 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  
FPU_stack_underflow_i(int i)659  void 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  
FPU_stack_underflow_pop(int i)673  void 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  }
687