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