Initial import from FreeBSD RELENG_4:
[games.git] / sys / i386 / gnu / fpemul / errors.c
1 /*
2  *  errors.c
3  *
4  *  The error handling functions for wm-FPU-emu
5  *
6  *
7  * Copyright (C) 1992,1993,1994
8  *                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
9  *                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au
10  * All rights reserved.
11  *
12  * This copyright notice covers the redistribution and use of the
13  * FPU emulator developed by W. Metzenthen. It covers only its use
14  * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
15  * use is not permitted under this copyright.
16  *
17  * Redistribution and use in source and binary forms, with or without
18  * modification, are permitted provided that the following conditions
19  * are met:
20  * 1. Redistributions of source code must retain the above copyright
21  *    notice, this list of conditions and the following disclaimer.
22  * 2. Redistributions in binary form must include information specifying
23  *    that source code for the emulator is freely available and include
24  *    either:
25  *      a) an offer to provide the source code for a nominal distribution
26  *         fee, or
27  *      b) list at least two alternative methods whereby the source
28  *         can be obtained, e.g. a publically accessible bulletin board
29  *         and an anonymous ftp site from which the software can be
30  *         downloaded.
31  * 3. All advertising materials specifically mentioning features or use of
32  *    this emulator must acknowledge that it was developed by W. Metzenthen.
33  * 4. The name of W. Metzenthen may not be used to endorse or promote
34  *    products derived from this software without specific prior written
35  *    permission.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
38  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
39  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
40  * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
41  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
42  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
43  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
44  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
45  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
46  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47  *
48  *
49  * The purpose of this copyright, based upon the Berkeley copyright, is to
50  * ensure that the covered software remains freely available to everyone.
51  *
52  * The software (with necessary differences) is also available, but under
53  * the terms of the GNU copyleft, for the Linux operating system and for
54  * the djgpp ms-dos extender.
55  *
56  * W. Metzenthen   June 1994.
57  *
58  *
59  * $FreeBSD: src/sys/gnu/i386/fpemul/errors.c,v 1.10 1999/08/28 00:42:49 peter Exp $
60  *
61  */
62
63 /*---------------------------------------------------------------------------+
64  | Note:                                                                     |
65  |    The file contains code which accesses user memory.                     |
66  |    Emulator static data may change when user memory is accessed, due to   |
67  |    other processes using the emulator while swapping is in progress.      |
68  +---------------------------------------------------------------------------*/
69
70
71
72
73
74 #include <sys/param.h>
75 #include <sys/systm.h>
76 #include <sys/proc.h>
77 #include <machine/pcb.h>
78
79 #include <gnu/i386/fpemul/fpu_emu.h>
80 #include <gnu/i386/fpemul/fpu_system.h>
81 #include <gnu/i386/fpemul/exception.h>
82 #include <gnu/i386/fpemul/status_w.h>
83 #include <gnu/i386/fpemul/control_w.h>
84 #include <gnu/i386/fpemul/reg_constant.h>
85 #include <gnu/i386/fpemul/version.h>
86
87 /* */
88 #undef PRINT_MESSAGES
89 /* */
90
91
92 void
93 Un_impl(void)
94 {
95         unsigned char byte1, FPU_modrm;
96
97         REENTRANT_CHECK(OFF);
98         byte1 = fubyte((unsigned char *) FPU_ORIG_EIP);
99         FPU_modrm = fubyte(1 + (unsigned char *) FPU_ORIG_EIP);
100
101         printf("Unimplemented FPU Opcode at eip=%#08x : %02x ",
102             FPU_ORIG_EIP, byte1);
103
104         if (FPU_modrm >= 0300)
105                 printf("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7);
106         else
107                 printf("/%d\n", (FPU_modrm >> 3) & 7);
108         REENTRANT_CHECK(ON);
109
110         EXCEPTION(EX_Invalid);
111
112 }
113
114
115
116
117 void
118 emu_printall()
119 {
120         int     i;
121         static char *tag_desc[] = {"Valid", "Zero", "ERROR", "ERROR",
122         "DeNorm", "Inf", "NaN", "Empty"};
123         unsigned char byte1, FPU_modrm;
124
125         REENTRANT_CHECK(OFF);
126         byte1 = fubyte((unsigned char *) FPU_ORIG_EIP);
127         FPU_modrm = fubyte(1 + (unsigned char *) FPU_ORIG_EIP);
128
129 #ifdef DEBUGGING
130         if (status_word & SW_Backward)
131                 printf("SW: backward compatibility\n");
132         if (status_word & SW_C3)
133                 printf("SW: condition bit 3\n");
134         if (status_word & SW_C2)
135                 printf("SW: condition bit 2\n");
136         if (status_word & SW_C1)
137                 printf("SW: condition bit 1\n");
138         if (status_word & SW_C0)
139                 printf("SW: condition bit 0\n");
140         if (status_word & SW_Summary)
141                 printf("SW: exception summary\n");
142         if (status_word & SW_Stack_Fault)
143                 printf("SW: stack fault\n");
144         if (status_word & SW_Precision)
145                 printf("SW: loss of precision\n");
146         if (status_word & SW_Underflow)
147                 printf("SW: underflow\n");
148         if (status_word & SW_Overflow)
149                 printf("SW: overflow\n");
150         if (status_word & SW_Zero_Div)
151                 printf("SW: divide by zero\n");
152         if (status_word & SW_Denorm_Op)
153                 printf("SW: denormalized operand\n");
154         if (status_word & SW_Invalid)
155                 printf("SW: invalid operation\n");
156 #endif                          /* DEBUGGING */
157
158         status_word = status_word & ~SW_Top;
159         status_word |= (top & 7) << SW_Top_Shift;
160
161         printf("At %#08x: %02x ", FPU_ORIG_EIP, byte1);
162         if (FPU_modrm >= 0300)
163                 printf("%02x (%02x+%d)\n", FPU_modrm, FPU_modrm & 0xf8, FPU_modrm & 7);
164         else
165                 printf("/%d, mod=%d rm=%d\n",
166                     (FPU_modrm >> 3) & 7, (FPU_modrm >> 6) & 3, FPU_modrm & 7);
167
168         printf(" SW: b=%d st=%d es=%d sf=%d cc=%d%d%d%d ef=%d%d%d%d%d%d\n",
169             status_word & 0x8000 ? 1 : 0,       /* busy */
170             (int)((status_word & 0x3800) >> 11),        /* stack top pointer */
171             status_word & 0x80 ? 1 : 0, /* Error summary status */
172             status_word & 0x40 ? 1 : 0, /* Stack flag */
173             status_word & SW_C3 ? 1 : 0, status_word & SW_C2 ? 1 : 0,   /* cc */
174             status_word & SW_C1 ? 1 : 0, status_word & SW_C0 ? 1 : 0,   /* cc */
175             status_word & SW_Precision ? 1 : 0, status_word & SW_Underflow ? 1 : 0,
176             status_word & SW_Overflow ? 1 : 0, status_word & SW_Zero_Div ? 1 : 0,
177             status_word & SW_Denorm_Op ? 1 : 0, status_word & SW_Invalid ? 1 : 0);
178
179         printf(" CW: ic=%d rc=%d%d pc=%d%d iem=%d     ef=%d%d%d%d%d%d\n",
180             control_word & 0x1000 ? 1 : 0,
181             (int)((control_word & 0x800) >> 11),
182             (int)((control_word & 0x400) >> 10),
183             (int)((control_word & 0x200) >> 9),
184             (int)((control_word & 0x100) >> 8),
185             control_word & 0x80 ? 1 : 0,
186             control_word & SW_Precision ? 1 : 0, control_word & SW_Underflow ? 1 : 0,
187             control_word & SW_Overflow ? 1 : 0, control_word & SW_Zero_Div ? 1 : 0,
188             control_word & SW_Denorm_Op ? 1 : 0, control_word & SW_Invalid ? 1 : 0);
189
190         for (i = 0; i < 8; i++) {
191                 FPU_REG *r = &st(i);
192                 switch (r->tag) {
193                 case TW_Empty:
194                         continue;
195                         break;
196                 case TW_Zero:
197                         printf("st(%d)  %c .0000 0000 0000 0000         ",
198                             i, r->sign ? '-' : '+');
199                         break;
200                 case TW_Valid:
201                 case TW_NaN:
202                 case TW_Denormal:
203                 case TW_Infinity:
204                         printf("st(%d)  %c .%04lx %04lx %04lx %04lx e%+-6ld ",
205                             i, r->sign ? '-' : '+', r->sigh >> 16,
206                             r->sigh & 0xFFFF, r->sigl >> 16, r->sigl & 0xFFFF,
207                             r->exp - EXP_BIAS + 1);
208                         break;
209                 default:
210                         printf("Whoops! Error in errors.c      ");
211                         break;
212                 }
213                 printf("%s\n", tag_desc[(int) (unsigned) r->tag]);
214         }
215
216         printf("[data] %c .%04lx %04lx %04lx %04lx e%+-6ld ",
217             FPU_loaded_data.sign ? '-' : '+', FPU_loaded_data.sigh >> 16,
218             FPU_loaded_data.sigh & 0xFFFF, FPU_loaded_data.sigl >> 16,
219             FPU_loaded_data.sigl & 0xFFFF, FPU_loaded_data.exp - EXP_BIAS + 1);
220         printf("%s\n", tag_desc[(int) (unsigned) FPU_loaded_data.tag]);
221         REENTRANT_CHECK(ON);
222
223 }
224
225 static struct {
226         int     type;
227         char   *name;
228 }       exception_names[] = {
229         {
230                 EX_StackOver, "stack overflow"
231         },
232         {
233                 EX_StackUnder, "stack underflow"
234         },
235         {
236                 EX_Precision, "loss of precision"
237         },
238         {
239                 EX_Underflow, "underflow"
240         },
241         {
242                 EX_Overflow, "overflow"
243         },
244         {
245                 EX_ZeroDiv, "divide by zero"
246         },
247         {
248                 EX_Denormal, "denormalized operand"
249         },
250         {
251                 EX_Invalid, "invalid operation"
252         },
253         {
254                 EX_INTERNAL, "INTERNAL BUG in " FPU_VERSION
255         },
256         {
257                 0, NULL
258         }
259 };
260 /*
261  EX_INTERNAL is always given with a code which indicates where the
262  error was detected.
263
264  Internal error types:
265        0x14   in e14.c
266        0x1nn  in a *.c file:
267               0x101  in reg_add_sub.c
268               0x102  in reg_mul.c
269               0x103  in poly_sin.c
270               0x104  in poly_tan.c
271               0x105  in reg_mul.c
272               0x106  in reg_mov.c
273               0x107  in fpu_trig.c
274               0x108  in reg_compare.c
275               0x109  in reg_compare.c
276               0x110  in reg_add_sub.c
277               0x111  in interface.c
278               0x112  in fpu_trig.c
279               0x113  in reg_add_sub.c
280               0x114  in reg_ld_str.c
281               0x115  in fpu_trig.c
282               0x116  in fpu_trig.c
283               0x117  in fpu_trig.c
284               0x118  in fpu_trig.c
285               0x119  in fpu_trig.c
286               0x120  in poly_atan.c
287               0x121  in reg_compare.c
288               0x122  in reg_compare.c
289               0x123  in reg_compare.c
290        0x2nn  in an *.s file:
291               0x201  in reg_u_add.S
292               0x202  in reg_u_div.S
293               0x203  in reg_u_div.S
294               0x204  in reg_u_div.S
295               0x205  in reg_u_mul.S
296               0x206  in reg_u_sub.S
297               0x207  in wm_sqrt.S
298               0x208  in reg_div.S
299               0x209  in reg_u_sub.S
300               0x210  in reg_u_sub.S
301               0x211  in reg_u_sub.S
302               0x212  in reg_u_sub.S
303               0x213  in wm_sqrt.S
304               0x214  in wm_sqrt.S
305               0x215  in wm_sqrt.S
306               0x216  in reg_round.S
307               0x217  in reg_round.S
308               0x218  in reg_round.S
309  */
310
311 void
312 exception(int n)
313 {
314         int     i, int_type;
315
316         int_type = 0;           /* Needed only to stop compiler warnings */
317         if (n & EX_INTERNAL) {
318                 int_type = n - EX_INTERNAL;
319                 n = EX_INTERNAL;
320                 /* Set lots of exception bits! */
321                 status_word |= (SW_Exc_Mask | SW_Summary | FPU_BUSY);
322         } else {
323                 /* Extract only the bits which we use to set the status word */
324                 n &= (SW_Exc_Mask);
325                 /* Set the corresponding exception bit */
326                 status_word |= n;
327                 if (status_word & ~control_word & CW_Exceptions)
328                         status_word |= SW_Summary;
329                 if (n & (SW_Stack_Fault | EX_Precision)) {
330                         if (!(n & SW_C1))
331                                 /* This bit distinguishes over- from underflow
332                                  * for a stack fault, and roundup from
333                                  * round-down for precision loss. */
334                                 status_word &= ~SW_C1;
335                 }
336         }
337
338         REENTRANT_CHECK(OFF);
339         if ((~control_word & n & CW_Exceptions) || (n == EX_INTERNAL)) {
340 #ifdef PRINT_MESSAGES
341                 /* My message from the sponsor */
342                 printf(FPU_VERSION " " __DATE__ " (C) W. Metzenthen.\n");
343 #endif                          /* PRINT_MESSAGES */
344
345                 /* Get a name string for error reporting */
346                 for (i = 0; exception_names[i].type; i++)
347                         if ((exception_names[i].type & n) == exception_names[i].type)
348                                 break;
349
350                 if (exception_names[i].type) {
351 #ifdef PRINT_MESSAGES
352                         printf("FP Exception: %s!\n", exception_names[i].name);
353 #endif                          /* PRINT_MESSAGES */
354                 } else
355                         printf("FP emulator: Unknown Exception: 0x%04x!\n", n);
356
357                 if (n == EX_INTERNAL) {
358                         printf("FP emulator: Internal error type 0x%04x\n", int_type);
359                         emu_printall();
360                 }
361 #ifdef PRINT_MESSAGES
362                 else
363                         emu_printall();
364 #endif                          /* PRINT_MESSAGES */
365
366                 /* The 80486 generates an interrupt on the next non-control
367                  * FPU instruction. So we need some means of flagging it. We
368                  * use the ES (Error Summary) bit for this, assuming that this
369                  * is the way a real FPU does it (until I can check it out),
370                  * if not, then some method such as the following kludge might
371                  * be needed. */
372 /*      regs[0].tag |= TW_FPU_Interrupt; */
373         }
374         REENTRANT_CHECK(ON);
375
376 #ifdef __DEBUG__
377         math_abort(SIGFPE);
378 #endif                          /* __DEBUG__ */
379
380 }
381
382
383 /* Real operation attempted on two operands, one a NaN */
384 void
385 real_2op_NaN(FPU_REG * a, FPU_REG * b, FPU_REG * dest)
386 {
387         FPU_REG *x;
388         int     signalling;
389
390         x = a;
391         if (a->tag == TW_NaN) {
392                 if (b->tag == TW_NaN) {
393                         signalling = !(a->sigh & b->sigh & 0x40000000);
394                         /* find the "larger" */
395                         if (*(long long *) &(a->sigl) < *(long long *) &(b->sigl))
396                                 x = b;
397                 } else {
398                         /* return the quiet version of the NaN in a */
399                         signalling = !(a->sigh & 0x40000000);
400                 }
401         } else
402 #ifdef PARANOID
403                 if (b->tag == TW_NaN)
404 #endif                          /* PARANOID */
405                 {
406                         signalling = !(b->sigh & 0x40000000);
407                         x = b;
408                 }
409 #ifdef PARANOID
410                 else {
411                         signalling = 0;
412                         EXCEPTION(EX_INTERNAL | 0x113);
413                         x = &CONST_QNaN;
414                 }
415 #endif                          /* PARANOID */
416
417         if (!signalling) {
418                 if (!(x->sigh & 0x80000000))    /* pseudo-NaN ? */
419                         x = &CONST_QNaN;
420                 reg_move(x, dest);
421                 return;
422         }
423         if (control_word & CW_Invalid) {
424                 /* The masked response */
425                 if (!(x->sigh & 0x80000000))    /* pseudo-NaN ? */
426                         x = &CONST_QNaN;
427                 reg_move(x, dest);
428                 /* ensure a Quiet NaN */
429                 dest->sigh |= 0x40000000;
430         }
431         EXCEPTION(EX_Invalid);
432
433         return;
434 }
435 /* Invalid arith operation on Valid registers */
436 void
437 arith_invalid(FPU_REG * dest)
438 {
439
440         if (control_word & CW_Invalid) {
441                 /* The masked response */
442                 reg_move(&CONST_QNaN, dest);
443         }
444         EXCEPTION(EX_Invalid);
445
446         return;
447
448 }
449
450
451 /* Divide a finite number by zero */
452 void
453 divide_by_zero(int sign, FPU_REG * dest)
454 {
455
456         if (control_word & CW_ZeroDiv) {
457                 /* The masked response */
458                 reg_move(&CONST_INF, dest);
459                 dest->sign = (unsigned char) sign;
460         }
461         EXCEPTION(EX_ZeroDiv);
462
463         return;
464
465 }
466
467
468 /* This may be called often, so keep it lean */
469 void
470 set_precision_flag_up(void)
471 {
472         if (control_word & CW_Precision)
473                 status_word |= (SW_Precision | SW_C1);  /* The masked response */
474         else
475                 exception(EX_Precision | SW_C1);
476
477 }
478
479
480 /* This may be called often, so keep it lean */
481 void
482 set_precision_flag_down(void)
483 {
484         if (control_word & CW_Precision) {      /* The masked response */
485                 status_word &= ~SW_C1;
486                 status_word |= SW_Precision;
487         } else
488                 exception(EX_Precision);
489 }
490
491
492 int
493 denormal_operand(void)
494 {
495         if (control_word & CW_Denormal) {       /* The masked response */
496                 status_word |= SW_Denorm_Op;
497                 return 0;
498         } else {
499                 exception(EX_Denormal);
500                 return 1;
501         }
502 }
503
504
505 void
506 arith_overflow(FPU_REG * dest)
507 {
508
509         if (control_word & CW_Overflow) {
510                 char    sign;
511                 /* The masked response */
512 /* **** The response here depends upon the rounding mode */
513                 sign = dest->sign;
514                 reg_move(&CONST_INF, dest);
515                 dest->sign = sign;
516         } else {
517                 /* Subtract the magic number from the exponent */
518                 dest->exp -= (3 * (1 << 13));
519         }
520
521         /* By definition, precision is lost. It appears that the roundup bit
522          * (C1) is also set by convention. */
523         EXCEPTION(EX_Overflow | EX_Precision | SW_C1);
524
525         return;
526
527 }
528
529
530 void
531 arith_underflow(FPU_REG * dest)
532 {
533
534         if (control_word & CW_Underflow) {
535                 /* The masked response */
536                 if (dest->exp <= EXP_UNDER - 63)
537                         reg_move(&CONST_Z, dest);
538         } else {
539                 /* Add the magic number to the exponent */
540                 dest->exp += (3 * (1 << 13));
541         }
542
543         EXCEPTION(EX_Underflow);
544
545         return;
546 }
547
548
549 void
550 stack_overflow(void)
551 {
552
553         if (control_word & CW_Invalid) {
554                 /* The masked response */
555                 top--;
556                 reg_move(&CONST_QNaN, FPU_st0_ptr = &st(0));
557         }
558         EXCEPTION(EX_StackOver);
559
560         return;
561
562 }
563
564
565 void
566 stack_underflow(void)
567 {
568
569         if (control_word & CW_Invalid) {
570                 /* The masked response */
571                 reg_move(&CONST_QNaN, FPU_st0_ptr);
572         }
573         EXCEPTION(EX_StackUnder);
574
575         return;
576
577 }
578
579
580 void
581 stack_underflow_i(int i)
582 {
583
584         if (control_word & CW_Invalid) {
585                 /* The masked response */
586                 reg_move(&CONST_QNaN, &(st(i)));
587         }
588         EXCEPTION(EX_StackUnder);
589
590         return;
591
592 }
593
594
595 void
596 stack_underflow_pop(int i)
597 {
598
599         if (control_word & CW_Invalid) {
600                 /* The masked response */
601                 reg_move(&CONST_QNaN, &(st(i)));
602                 pop();
603         }
604         EXCEPTION(EX_StackUnder);
605
606         return;
607
608 }