xref: /illumos-gate/usr/src/lib/libm/common/m9x/__fex_i386.c (revision 9a686fbc186e8e2a64e9a5094d44c7d6fa0ea167)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
24  */
25 /*
26  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
27  * Use is subject to license terms.
28  */
29 
30 #include <stdio.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <signal.h>
35 #include <siginfo.h>
36 #include <ucontext.h>
37 #include <thread.h>
38 #include <math.h>
39 #if defined(__SUNPRO_C)
40 #include <sunmath.h>
41 #endif
42 #include <fenv.h>
43 #include <sys/regset.h>
44 #include "fex_handler.h"
45 #include "fenv_inlines.h"
46 
47 #if defined(__amd64)
48 #define test_sse_hw	1
49 #else
50 /*
51  * The following variable lives in libc on Solaris 10, where it
52  * gets set to a nonzero value at startup time on systems with SSE.
53  */
54 extern int _sse_hw;
55 #define test_sse_hw	_sse_hw
56 #endif
57 
58 static int accrued = 0;
59 static thread_key_t accrued_key;
60 static mutex_t accrued_key_lock = DEFAULTMUTEX;
61 
62 int *
63 __fex_accrued()
64 {
65 	int		*p;
66 
67 	if (thr_main())
68 		return &accrued;
69 	else {
70 		p = NULL;
71 		mutex_lock(&accrued_key_lock);
72 		if (thr_getspecific(accrued_key, (void **)&p) != 0 &&
73 			thr_keycreate(&accrued_key, free) != 0) {
74 			mutex_unlock(&accrued_key_lock);
75 			return NULL;
76 		}
77 		mutex_unlock(&accrued_key_lock);
78 		if (!p) {
79 			if ((p = (int*) malloc(sizeof(int))) == NULL)
80 				return NULL;
81 			if (thr_setspecific(accrued_key, (void *)p) != 0) {
82 				(void)free(p);
83 				return NULL;
84 			}
85 			*p = 0;
86 		}
87 		return p;
88 	}
89 }
90 
91 void
92 __fenv_getfsr(unsigned long *fsr)
93 {
94 	unsigned int	cwsw, mxcsr;
95 
96 	__fenv_getcwsw(&cwsw);
97 	/* clear reserved bits for no particularly good reason */
98 	cwsw &= ~0xe0c00000u;
99 	if (test_sse_hw) {
100 		/* pick up exception flags (excluding denormal operand
101 		   flag) from mxcsr */
102 		__fenv_getmxcsr(&mxcsr);
103 		cwsw |= (mxcsr & 0x3d);
104 	}
105 	cwsw |= *__fex_accrued();
106 	*fsr = cwsw ^ 0x003f0000u;
107 }
108 
109 void
110 __fenv_setfsr(const unsigned long *fsr)
111 {
112 	unsigned int	cwsw, mxcsr;
113 	int				te;
114 
115 	/* save accrued exception flags corresponding to enabled exceptions */
116 	cwsw = (unsigned int)*fsr;
117 	te = __fenv_get_te(cwsw);
118 	*__fex_accrued() = cwsw & te;
119 	cwsw = (cwsw & ~te) ^ 0x003f0000;
120 	if (test_sse_hw) {
121 		/* propagate rounding direction, masks, and exception flags
122 		   (excluding denormal operand mask and flag) to mxcsr */
123 		__fenv_getmxcsr(&mxcsr);
124 		mxcsr = (mxcsr & ~0x7ebd) | ((cwsw >> 13) & 0x6000) |
125 			((cwsw >> 9) & 0x1e80) | (cwsw & 0x3d);
126 		__fenv_setmxcsr(&mxcsr);
127 	}
128 	__fenv_setcwsw(&cwsw);
129 }
130 
131 /* Offsets into the fp environment save area (assumes 32-bit protected mode) */
132 #define CW	0	/* control word */
133 #define SW	1	/* status word */
134 #define TW	2	/* tag word */
135 #define IP	3	/* instruction pointer */
136 #define OP	4	/* opcode */
137 #define EA	5	/* operand address */
138 
139 /* macro for accessing fp registers in the save area */
140 #if defined(__amd64)
141 #define fpreg(u,x)	*(long double *)(10*(x)+(char*)&(u)->uc_mcontext.fpregs.fp_reg_set.fpchip_state.st)
142 #else
143 #define fpreg(u,x)	*(long double *)(10*(x)+(char*)&(u)->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[7])
144 #endif
145 
146 /*
147 *  Fix sip->si_code; the Solaris x86 kernel can get it wrong
148 */
149 void
150 __fex_get_x86_exc(siginfo_t *sip, ucontext_t *uap)
151 {
152 	unsigned	sw, cw;
153 
154 	sw = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.status;
155 #if defined(__amd64)
156 	cw = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.cw;
157 #else
158 	cw = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[CW];
159 #endif
160 	if ((sw & FE_INVALID) && !(cw & (1 << fp_trap_invalid)))
161 		/* store 0 for stack fault, FPE_FLTINV for IEEE invalid op */
162 		sip->si_code = ((sw & 0x40)? 0 : FPE_FLTINV);
163 	else if ((sw & FE_DIVBYZERO) && !(cw & (1 << fp_trap_division)))
164 		sip->si_code = FPE_FLTDIV;
165 	else if ((sw & FE_OVERFLOW) && !(cw & (1 << fp_trap_overflow)))
166 		sip->si_code = FPE_FLTOVF;
167 	else if ((sw & FE_UNDERFLOW) && !(cw & (1 << fp_trap_underflow)))
168 		sip->si_code = FPE_FLTUND;
169 	else if ((sw & FE_INEXACT) && !(cw & (1 << fp_trap_inexact)))
170 		sip->si_code = FPE_FLTRES;
171 	else
172 		sip->si_code = 0;
173 }
174 
175 static enum fp_class_type
176 my_fp_classf(float *x)
177 {
178 	int		i = *(int*)x & ~0x80000000;
179 
180 	if (i < 0x7f800000) {
181 		if (i < 0x00800000)
182 			return ((i == 0)? fp_zero : fp_subnormal);
183 		return fp_normal;
184 	}
185 	else if (i == 0x7f800000)
186 		return fp_infinity;
187 	else if (i & 0x400000)
188 		return fp_quiet;
189 	else
190 		return fp_signaling;
191 }
192 
193 static enum fp_class_type
194 my_fp_class(double *x)
195 {
196 	int		i = *(1+(int*)x) & ~0x80000000;
197 
198 	if (i < 0x7ff00000) {
199 		if (i < 0x00100000)
200 			return (((i | *(int*)x) == 0)? fp_zero : fp_subnormal);
201 		return fp_normal;
202 	}
203 	else if (i == 0x7ff00000 && *(int*)x == 0)
204 		return fp_infinity;
205 	else if (i & 0x80000)
206 		return fp_quiet;
207 	else
208 		return fp_signaling;
209 }
210 
211 static enum fp_class_type
212 my_fp_classl(long double *x)
213 {
214 	int		i = *(2+(int*)x) & 0x7fff;
215 
216 	if (i < 0x7fff) {
217 		if (i < 1) {
218 			if (*(1+(int*)x) < 0) return fp_normal; /* pseudo-denormal */
219 			return (((*(1+(int*)x) | *(int*)x) == 0)?
220 				fp_zero : fp_subnormal);
221 		}
222 		return ((*(1+(int*)x) < 0)? fp_normal :
223 			(enum fp_class_type) -1); /* unsupported format */
224 	}
225 	else if (*(1+(int*)x) == 0x80000000 && *(int*)x == 0)
226 		return fp_infinity;
227 	else if (*(1+(unsigned*)x) >= 0xc0000000)
228 		return fp_quiet;
229 	else if (*(1+(int*)x) < 0)
230 		return fp_signaling;
231 	else
232 		return (enum fp_class_type) -1; /* unsupported format */
233 }
234 
235 /*
236 *  Determine which type of invalid operation exception occurred
237 */
238 enum fex_exception
239 __fex_get_invalid_type(siginfo_t *sip, ucontext_t *uap)
240 {
241 	unsigned			op;
242 	unsigned long			ea;
243 	enum fp_class_type	t1, t2;
244 
245 	/* get the opcode and data address */
246 #if defined(__amd64)
247 	op = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.fop >> 16;
248 	ea = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.rdp;
249 #else
250 	op = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[OP] >> 16;
251 	ea = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[EA];
252 #endif
253 
254 	/* if the instruction is fld, the source must be snan (it can't be
255 	   an unsupported format, since fldt doesn't raise any exceptions) */
256 	switch (op & 0x7f8) {
257 	case 0x100:
258 	case 0x140:
259 	case 0x180:
260 	case 0x500:
261 	case 0x540:
262 	case 0x580:
263 		return fex_inv_snan;
264 	}
265 
266 	/* otherwise st is one of the operands; see if it's snan */
267 	t1 = my_fp_classl(&fpreg(uap, 0));
268 	if (t1 == fp_signaling)
269 		return fex_inv_snan;
270 	else if (t1 == (enum fp_class_type) -1)
271 		return (enum fex_exception) -1;
272 
273 	/* determine the class of the second operand if there is one */
274 	t2 = fp_normal;
275 	switch (op & 0x7e0) {
276 	case 0x600:
277 	case 0x620:
278 	case 0x640:
279 	case 0x660:
280 	case 0x680:
281 	case 0x6a0:
282 		/* short memory operand */
283 		if (!ea)
284 			return (enum fex_exception) -1;
285 		if (*(short *)ea == 0)
286 			t2 = fp_zero;
287 		break;
288 
289 	case 0x200:
290 	case 0x220:
291 	case 0x240:
292 	case 0x260:
293 	case 0x280:
294 	case 0x2a0:
295 		/* int memory operand */
296 		if (!ea)
297 			return (enum fex_exception) -1;
298 		if (*(int *)ea == 0)
299 			t2 = fp_zero;
300 		break;
301 
302 	case 0x000:
303 	case 0x020:
304 	case 0x040:
305 	case 0x060:
306 	case 0x080:
307 	case 0x0a0:
308 		/* single precision memory operand */
309 		if (!ea)
310 			return (enum fex_exception) -1;
311 		t2 = my_fp_classf((float *)ea);
312 		break;
313 
314 	case 0x400:
315 	case 0x420:
316 	case 0x440:
317 	case 0x460:
318 	case 0x480:
319 	case 0x4a0:
320 		/* double precision memory operand */
321 		if (!ea)
322 			return (enum fex_exception) -1;
323 		t2 = my_fp_class((double *)ea);
324 		break;
325 
326 	case 0x0c0:
327 	case 0x0e0:
328 	case 0x3e0:
329 	case 0x4c0:
330 	case 0x4e0:
331 	case 0x5e0:
332 	case 0x6c0:
333 	case 0x6e0:
334 	case 0x7e0:
335 		/* register operand determined by opcode */
336 		switch (op & 0x7f8) {
337 		case 0x3e0:
338 		case 0x3f8:
339 		case 0x5f0:
340 		case 0x5f8:
341 		case 0x7e0:
342 		case 0x7f8:
343 			/* weed out nonexistent opcodes */
344 			break;
345 
346 		default:
347 			t2 = my_fp_classl(&fpreg(uap, op & 7));
348 		}
349 		break;
350 
351 	case 0x1e0:
352 	case 0x2e0:
353 		/* special forms */
354 		switch (op) {
355 		case 0x1f1: /* fyl2x */
356 		case 0x1f3: /* fpatan */
357 		case 0x1f5: /* fprem1 */
358 		case 0x1f8: /* fprem */
359 		case 0x1f9: /* fyl2xp1 */
360 		case 0x1fd: /* fscale */
361 		case 0x2e9: /* fucompp */
362 			t2 = my_fp_classl(&fpreg(uap, 1));
363 			break;
364 		}
365 		break;
366 	}
367 
368 	/* see if the second op is snan */
369 	if (t2 == fp_signaling)
370 		return fex_inv_snan;
371 	else if (t2 == (enum fp_class_type) -1)
372 		return (enum fex_exception) -1;
373 
374 	/* determine the type of operation */
375 	switch (op & 0x7f8) {
376 	case 0x000:
377 	case 0x020:
378 	case 0x028:
379 	case 0x040:
380 	case 0x060:
381 	case 0x068:
382 	case 0x080:
383 	case 0x0a0:
384 	case 0x0a8:
385 	case 0x0c0:
386 	case 0x0e0:
387 	case 0x0e8:
388 	case 0x400:
389 	case 0x420:
390 	case 0x428:
391 	case 0x440:
392 	case 0x460:
393 	case 0x468:
394 	case 0x480:
395 	case 0x4a0:
396 	case 0x4a8:
397 	case 0x4c0:
398 	case 0x4e0:
399 	case 0x4e8:
400 	case 0x6c0:
401 	case 0x6e0:
402 	case 0x6e8:
403 		/* fadd, fsub, fsubr */
404 		if (t1 == fp_infinity && t2 == fp_infinity)
405 			return fex_inv_isi;
406 		break;
407 
408 	case 0x008:
409 	case 0x048:
410 	case 0x088:
411 	case 0x0c8:
412 	case 0x208:
413 	case 0x248:
414 	case 0x288:
415 	case 0x408:
416 	case 0x448:
417 	case 0x488:
418 	case 0x4c8:
419 	case 0x608:
420 	case 0x648:
421 	case 0x688:
422 	case 0x6c8:
423 		/* fmul */
424 		if ((t1 == fp_zero && t2 == fp_infinity) || (t2 == fp_zero &&
425 		  t1 == fp_infinity))
426 			return fex_inv_zmi;
427 		break;
428 
429 	case 0x030:
430 	case 0x038:
431 	case 0x070:
432 	case 0x078:
433 	case 0x0b0:
434 	case 0x0b8:
435 	case 0x0f0:
436 	case 0x0f8:
437 	case 0x230:
438 	case 0x238:
439 	case 0x270:
440 	case 0x278:
441 	case 0x2b0:
442 	case 0x2b8:
443 	case 0x430:
444 	case 0x438:
445 	case 0x470:
446 	case 0x478:
447 	case 0x4b0:
448 	case 0x4b8:
449 	case 0x4f0:
450 	case 0x4f8:
451 	case 0x630:
452 	case 0x638:
453 	case 0x670:
454 	case 0x678:
455 	case 0x6b0:
456 	case 0x6b8:
457 	case 0x6f0:
458 	case 0x6f8:
459 		/* fdiv */
460 		if (t1 == fp_zero && t2 == fp_zero)
461 			return fex_inv_zdz;
462 		else if (t1 == fp_infinity && t2 == fp_infinity)
463 			return fex_inv_idi;
464 		break;
465 
466 	case 0x1f0:
467 	case 0x1f8:
468 		/* fsqrt, other special ops */
469 		return fex_inv_sqrt;
470 
471 	case 0x010:
472 	case 0x018:
473 	case 0x050:
474 	case 0x058:
475 	case 0x090:
476 	case 0x098:
477 	case 0x0d0:
478 	case 0x0d8:
479 	case 0x210:
480 	case 0x218:
481 	case 0x250:
482 	case 0x258:
483 	case 0x290:
484 	case 0x298:
485 	case 0x2e8:
486 	case 0x3f0:
487 	case 0x410:
488 	case 0x418:
489 	case 0x450:
490 	case 0x458:
491 	case 0x490:
492 	case 0x498:
493 	case 0x4d0:
494 	case 0x4d8:
495 	case 0x5e0:
496 	case 0x5e8:
497 	case 0x610:
498 	case 0x618:
499 	case 0x650:
500 	case 0x658:
501 	case 0x690:
502 	case 0x698:
503 	case 0x6d0:
504 	case 0x6d8:
505 	case 0x7f0:
506 		/* fcom */
507 		if (t1 == fp_quiet || t2 == fp_quiet)
508 			return fex_inv_cmp;
509 		break;
510 
511 	case 0x1e0:
512 		/* ftst */
513 		if (op == 0x1e4 && t1 == fp_quiet)
514 			return fex_inv_cmp;
515 		break;
516 
517 	case 0x310:
518 	case 0x318:
519 	case 0x350:
520 	case 0x358:
521 	case 0x390:
522 	case 0x398:
523 	case 0x710:
524 	case 0x718:
525 	case 0x730:
526 	case 0x738:
527 	case 0x750:
528 	case 0x758:
529 	case 0x770:
530 	case 0x778:
531 	case 0x790:
532 	case 0x798:
533 	case 0x7b0:
534 	case 0x7b8:
535 		/* fist, fbst */
536 		return fex_inv_int;
537 	}
538 
539 	return (enum fex_exception) -1;
540 }
541 
542 /* scale factors for exponent unwrapping */
543 static const long double
544 	two12288 = 1.139165225263043370845938579315932009e+3699l,	/* 2^12288 */
545 	twom12288 = 8.778357852076208839765066529179033145e-3700l,	/* 2^-12288 */
546 	twom12288mulp = 8.778357852076208839289190796475222545e-3700l;
547 		/* (")*(1-2^-64) */
548 
549 /* inline templates */
550 extern long double f2xm1(long double);
551 extern long double fyl2x(long double, long double);
552 extern long double fptan(long double);
553 extern long double fpatan(long double, long double);
554 extern long double fxtract(long double);
555 extern long double fprem1(long double, long double);
556 extern long double fprem(long double, long double);
557 extern long double fyl2xp1(long double, long double);
558 extern long double fsqrt(long double);
559 extern long double fsincos(long double);
560 extern long double frndint(long double);
561 extern long double fscale(long double, long double);
562 extern long double fsin(long double);
563 extern long double fcos(long double);
564 
565 /*
566 *  Get the operands, generate the default untrapped result with
567 *  exceptions, and set a code indicating the type of operation
568 */
569 void
570 __fex_get_op(siginfo_t *sip, ucontext_t *uap, fex_info_t *info)
571 {
572 	fex_numeric_t			t;
573 	long double			op2v, x;
574 	unsigned int			cwsw, ex, sw, op;
575 	unsigned long			ea;
576 	volatile int			c;
577 
578 	/* get the exception type, status word, opcode, and data address */
579 	ex = sip->si_code;
580 	sw = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.status;
581 #if defined(__amd64)
582 	op = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.fop >> 16;
583 	ea = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.rdp;
584 #else
585 	op = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[OP] >> 16;
586 	ea = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[EA];
587 #endif
588 
589 	/* initialize res to the default untrapped result and ex to the
590 	   corresponding flags (assume trapping is disabled and flags
591 	   are clear) */
592 
593 	/* single operand instructions */
594 	info->op = fex_cnvt;
595 	info->op2.type = fex_nodata;
596 	switch (op & 0x7f8) {
597 	/* load instructions */
598 	case 0x100:
599 	case 0x140:
600 	case 0x180:
601 		if (!ea) {
602 			info->op = fex_other;
603 			info->op1.type = info->op2.type = info->res.type = fex_nodata;
604 			info->flags = 0;
605 			return;
606 		}
607 		info->op1.type = fex_float;
608 		info->op1.val.f = *(float *)ea;
609 		info->res.type = fex_ldouble;
610 		info->res.val.q = (long double) info->op1.val.f;
611 		goto done;
612 
613 	case 0x500:
614 	case 0x540:
615 	case 0x580:
616 		if (!ea) {
617 			info->op = fex_other;
618 			info->op1.type = info->op2.type = info->res.type = fex_nodata;
619 			info->flags = 0;
620 			return;
621 		}
622 		info->op1.type = fex_double;
623 		info->op1.val.d = *(double *)ea;
624 		info->res.type = fex_ldouble;
625 		info->res.val.q = (long double) info->op1.val.d;
626 		goto done;
627 
628 	/* store instructions */
629 	case 0x110:
630 	case 0x118:
631 	case 0x150:
632 	case 0x158:
633 	case 0x190:
634 	case 0x198:
635 		info->res.type = fex_float;
636 		if (ex == FPE_FLTRES && (op & 8) != 0) {
637 			/* inexact, stack popped */
638 			if (!ea) {
639 				info->op = fex_other;
640 				info->op1.type = info->op2.type = info->res.type = fex_nodata;
641 				info->flags = 0;
642 				return;
643 			}
644 			info->op1.type = fex_nodata;
645 			info->res.val.f = *(float *)ea;
646 			info->flags = FE_INEXACT;
647 			return;
648 		}
649 		info->op1.type = fex_ldouble;
650 		info->op1.val.q = fpreg(uap, 0);
651 		info->res.val.f = (float) info->op1.val.q;
652 		goto done;
653 
654 	case 0x310:
655 	case 0x318:
656 	case 0x350:
657 	case 0x358:
658 	case 0x390:
659 	case 0x398:
660 		info->res.type = fex_int;
661 		if (ex == FPE_FLTRES && (op & 8) != 0) {
662 			/* inexact, stack popped */
663 			if (!ea) {
664 				info->op = fex_other;
665 				info->op1.type = info->op2.type = info->res.type = fex_nodata;
666 				info->flags = 0;
667 				return;
668 			}
669 			info->op1.type = fex_nodata;
670 			info->res.val.i = *(int *)ea;
671 			info->flags = FE_INEXACT;
672 			return;
673 		}
674 		info->op1.type = fex_ldouble;
675 		info->op1.val.q = fpreg(uap, 0);
676 		info->res.val.i = (int) info->op1.val.q;
677 		goto done;
678 
679 	case 0x510:
680 	case 0x518:
681 	case 0x550:
682 	case 0x558:
683 	case 0x590:
684 	case 0x598:
685 		info->res.type = fex_double;
686 		if (ex == FPE_FLTRES && (op & 8) != 0) {
687 			/* inexact, stack popped */
688 			if (!ea) {
689 				info->op = fex_other;
690 				info->op1.type = info->op2.type = info->res.type = fex_nodata;
691 				info->flags = 0;
692 				return;
693 			}
694 			info->op1.type = fex_nodata;
695 			info->res.val.d = *(double *)ea;
696 			info->flags = FE_INEXACT;
697 			return;
698 		}
699 		info->op1.type = fex_ldouble;
700 		info->op1.val.q = fpreg(uap, 0);
701 		info->res.val.d = (double) info->op1.val.q;
702 		goto done;
703 
704 	case 0x710:
705 	case 0x718:
706 	case 0x750:
707 	case 0x758:
708 	case 0x790:
709 	case 0x798:
710 		info->res.type = fex_int;
711 		if (ex == FPE_FLTRES && (op & 8) != 0) {
712 			/* inexact, stack popped */
713 			if (!ea) {
714 				info->op = fex_other;
715 				info->op1.type = info->op2.type = info->res.type = fex_nodata;
716 				info->flags = 0;
717 				return;
718 			}
719 			info->op1.type = fex_nodata;
720 			info->res.val.i = *(short *)ea;
721 			info->flags = FE_INEXACT;
722 			return;
723 		}
724 		info->op1.type = fex_ldouble;
725 		info->op1.val.q = fpreg(uap, 0);
726 		info->res.val.i = (short) info->op1.val.q;
727 		goto done;
728 
729 	case 0x730:
730 	case 0x770:
731 	case 0x7b0:
732 		/* fbstp; don't bother */
733 		info->op = fex_other;
734 		info->op1.type = info->res.type = fex_nodata;
735 		info->flags = 0;
736 		return;
737 
738 	case 0x738:
739 	case 0x778:
740 	case 0x7b8:
741 		info->res.type = fex_llong;
742 		if (ex == FPE_FLTRES) {
743 			/* inexact, stack popped */
744 			if (!ea) {
745 				info->op = fex_other;
746 				info->op1.type = info->op2.type = info->res.type = fex_nodata;
747 				info->flags = 0;
748 				return;
749 			}
750 			info->op1.type = fex_nodata;
751 			info->res.val.l = *(long long *)ea;
752 			info->flags = FE_INEXACT;
753 			return;
754 		}
755 		info->op1.type = fex_ldouble;
756 		info->op1.val.q = fpreg(uap, 0);
757 		info->res.val.l = (long long) info->op1.val.q;
758 		goto done;
759 	}
760 
761 	/* all other ops (except compares) have destinations on the stack
762 	   so overflow, underflow, and inexact will stomp their operands */
763 	if (ex == FPE_FLTOVF || ex == FPE_FLTUND || ex == FPE_FLTRES) {
764 		/* find the trapped result */
765 		info->op1.type = info->op2.type = fex_nodata;
766 		info->res.type = fex_ldouble;
767 		switch (op & 0x7f8) {
768 		case 0x1f0:
769 			/* fptan pushes 1.0 afterward, so result is in st(1) */
770 			info->res.val.q = ((op == 0x1f2)? fpreg(uap, 1) :
771 				fpreg(uap, 0));
772 			break;
773 
774 		case 0x4c0:
775 		case 0x4c8:
776 		case 0x4e0:
777 		case 0x4e8:
778 		case 0x4f0:
779 		case 0x4f8:
780 			info->res.val.q = fpreg(uap, op & 7);
781 			break;
782 
783 		case 0x6c0:
784 		case 0x6c8:
785 		case 0x6e0:
786 		case 0x6e8:
787 		case 0x6f0:
788 		case 0x6f8:
789 			/* stack was popped afterward */
790 			info->res.val.q = fpreg(uap, (op - 1) & 7);
791 			break;
792 
793 		default:
794 			info->res.val.q = fpreg(uap, 0);
795 		}
796 
797 		/* reconstruct default untrapped result */
798 		if (ex == FPE_FLTOVF) {
799 			/* generate an overflow with the sign of the result */
800 			x = two12288;
801 			*(4+(short*)&x) |= (*(4+(short*)&info->res.val.q) & 0x8000);
802 			info->res.val.q = x * two12288;
803 			info->flags = FE_OVERFLOW | FE_INEXACT;
804 			__fenv_getcwsw(&cwsw);
805 			cwsw &= ~FE_ALL_EXCEPT;
806 			__fenv_setcwsw(&cwsw);
807 		}
808 		else if (ex == FPE_FLTUND) {
809 			/* undo the scaling; we can't distinguish a chopped result
810 			   from an exact one without futzing around to trap all in-
811 			   exact exceptions so as to keep the flag clear, so we just
812 			   punt */
813 			if (sw & 0x200) /* result was rounded up */
814 				info->res.val.q = (info->res.val.q * twom12288) * twom12288mulp;
815 			else
816 				info->res.val.q = (info->res.val.q * twom12288) * twom12288;
817 			__fenv_getcwsw(&cwsw);
818 			info->flags = (cwsw & FE_INEXACT) | FE_UNDERFLOW;
819 			cwsw &= ~FE_ALL_EXCEPT;
820 			__fenv_setcwsw(&cwsw);
821 		}
822 		else
823 			info->flags = FE_INEXACT;
824 
825 		/* determine the operation code */
826 		switch (op) {
827 		case 0x1f0: /* f2xm1 */
828 		case 0x1f1: /* fyl2x */
829 		case 0x1f2: /* fptan */
830 		case 0x1f3: /* fpatan */
831 		case 0x1f5: /* fprem1 */
832 		case 0x1f8: /* fprem */
833 		case 0x1f9: /* fyl2xp1 */
834 		case 0x1fb: /* fsincos */
835 		case 0x1fc: /* frndint */
836 		case 0x1fd: /* fscale */
837 		case 0x1fe: /* fsin */
838 		case 0x1ff: /* fcos */
839 			info->op = fex_other;
840 			return;
841 
842 		case 0x1fa: /* fsqrt */
843 			info->op = fex_sqrt;
844 			return;
845 		}
846 
847 		info->op = fex_other;
848 		switch (op & 0x7c0) {
849 		case 0x000:
850 		case 0x040:
851 		case 0x080:
852 		case 0x0c0:
853 		case 0x200:
854 		case 0x240:
855 		case 0x280:
856 		case 0x400:
857 		case 0x440:
858 		case 0x480:
859 		case 0x4c0:
860 		case 0x600:
861 		case 0x640:
862 		case 0x680:
863 		case 0x6c0:
864 			switch (op & 0x38) {
865 			case 0x00:
866 				info->op = fex_add;
867 				break;
868 
869 			case 0x08:
870 				info->op = fex_mul;
871 				break;
872 
873 			case 0x20:
874 			case 0x28:
875 				info->op = fex_sub;
876 				break;
877 
878 			case 0x30:
879 			case 0x38:
880 				info->op = fex_div;
881 				break;
882 			}
883 		}
884 		return;
885 	}
886 
887 	/* for other exceptions, the operands are preserved, so we can
888 	   just emulate the operation with traps disabled */
889 
890 	/* one operand is always in st */
891 	info->op1.type = fex_ldouble;
892 	info->op1.val.q = fpreg(uap, 0);
893 
894 	/* oddball instructions */
895 	info->op = fex_other;
896 	switch (op) {
897 	case 0x1e4: /* ftst */
898 		info->op = fex_cmp;
899 		info->op2.type = fex_ldouble;
900 		info->op2.val.q = 0.0l;
901 		info->res.type = fex_nodata;
902 		c = (info->op1.val.q < info->op2.val.q);
903 		goto done;
904 
905 	case 0x1f0: /* f2xm1 */
906 		info->res.type = fex_ldouble;
907 		info->res.val.q = f2xm1(info->op1.val.q);
908 		goto done;
909 
910 	case 0x1f1: /* fyl2x */
911 		info->op2.type = fex_ldouble;
912 		info->op2.val.q = fpreg(uap, 1);
913 		info->res.type = fex_ldouble;
914 		info->res.val.q = fyl2x(info->op1.val.q, info->op2.val.q);
915 		goto done;
916 
917 	case 0x1f2: /* fptan */
918 		info->res.type = fex_ldouble;
919 		info->res.val.q = fptan(info->op1.val.q);
920 		goto done;
921 
922 	case 0x1f3: /* fpatan */
923 		info->op2.type = fex_ldouble;
924 		info->op2.val.q = fpreg(uap, 1);
925 		info->res.type = fex_ldouble;
926 		info->res.val.q = fpatan(info->op1.val.q, info->op2.val.q);
927 		goto done;
928 
929 	case 0x1f4: /* fxtract */
930 		info->res.type = fex_ldouble;
931 		info->res.val.q = fxtract(info->op1.val.q);
932 		goto done;
933 
934 	case 0x1f5: /* fprem1 */
935 		info->op2.type = fex_ldouble;
936 		info->op2.val.q = fpreg(uap, 1);
937 		info->res.type = fex_ldouble;
938 		info->res.val.q = fprem1(info->op1.val.q, info->op2.val.q);
939 		goto done;
940 
941 	case 0x1f8: /* fprem */
942 		info->op2.type = fex_ldouble;
943 		info->op2.val.q = fpreg(uap, 1);
944 		info->res.type = fex_ldouble;
945 		info->res.val.q = fprem(info->op1.val.q, info->op2.val.q);
946 		goto done;
947 
948 	case 0x1f9: /* fyl2xp1 */
949 		info->op2.type = fex_ldouble;
950 		info->op2.val.q = fpreg(uap, 1);
951 		info->res.type = fex_ldouble;
952 		info->res.val.q = fyl2xp1(info->op1.val.q, info->op2.val.q);
953 		goto done;
954 
955 	case 0x1fa: /* fsqrt */
956 		info->op = fex_sqrt;
957 		info->res.type = fex_ldouble;
958 		info->res.val.q = fsqrt(info->op1.val.q);
959 		goto done;
960 
961 	case 0x1fb: /* fsincos */
962 		info->res.type = fex_ldouble;
963 		info->res.val.q = fsincos(info->op1.val.q);
964 		goto done;
965 
966 	case 0x1fc: /* frndint */
967 		info->res.type = fex_ldouble;
968 		info->res.val.q = frndint(info->op1.val.q);
969 		goto done;
970 
971 	case 0x1fd: /* fscale */
972 		info->op2.type = fex_ldouble;
973 		info->op2.val.q = fpreg(uap, 1);
974 		info->res.type = fex_ldouble;
975 		info->res.val.q = fscale(info->op1.val.q, info->op2.val.q);
976 		goto done;
977 
978 	case 0x1fe: /* fsin */
979 		info->res.type = fex_ldouble;
980 		info->res.val.q = fsin(info->op1.val.q);
981 		goto done;
982 
983 	case 0x1ff: /* fcos */
984 		info->res.type = fex_ldouble;
985 		info->res.val.q = fcos(info->op1.val.q);
986 		goto done;
987 
988 	case 0x2e9: /* fucompp */
989 		info->op = fex_cmp;
990 		info->op2.type = fex_ldouble;
991 		info->op2.val.q = fpreg(uap, 1);
992 		info->res.type = fex_nodata;
993 		c = (info->op1.val.q == info->op2.val.q);
994 		goto done;
995 	}
996 
997 	/* fucom[p], fcomi[p], fucomi[p] */
998 	switch (op & 0x7f8) {
999 	case 0x3e8:
1000 	case 0x5e0:
1001 	case 0x5e8:
1002 	case 0x7e8: /* unordered compares */
1003 		info->op = fex_cmp;
1004 		info->op2.type = fex_ldouble;
1005 		info->op2.val.q = fpreg(uap, op & 7);
1006 		info->res.type = fex_nodata;
1007 		c = (info->op1.val.q == info->op2.val.q);
1008 		goto done;
1009 
1010 	case 0x3f0:
1011 	case 0x7f0: /* ordered compares */
1012 		info->op = fex_cmp;
1013 		info->op2.type = fex_ldouble;
1014 		info->op2.val.q = fpreg(uap, op & 7);
1015 		info->res.type = fex_nodata;
1016 		c = (info->op1.val.q < info->op2.val.q);
1017 		goto done;
1018 	}
1019 
1020 	/* all other instructions come in groups of the form
1021 	   fadd, fmul, fcom, fcomp, fsub, fsubr, fdiv, fdivr */
1022 
1023 	/* get the second operand */
1024 	switch (op & 0x7c0) {
1025 	case 0x000:
1026 	case 0x040:
1027 	case 0x080:
1028 		if (!ea) {
1029 			info->op = fex_other;
1030 			info->op1.type = info->op2.type = info->res.type = fex_nodata;
1031 			info->flags = 0;
1032 			return;
1033 		}
1034 		info->op2.type = fex_float;
1035 		info->op2.val.f = *(float *)ea;
1036 		op2v = (long double) info->op2.val.f;
1037 		break;
1038 
1039 	case 0x0c0:
1040 		info->op2.type = fex_ldouble;
1041 		op2v = info->op2.val.q = fpreg(uap, op & 7);
1042 		break;
1043 
1044 	case 0x200:
1045 	case 0x240:
1046 	case 0x280:
1047 		if (!ea) {
1048 			info->op = fex_other;
1049 			info->op1.type = info->op2.type = info->res.type = fex_nodata;
1050 			info->flags = 0;
1051 			return;
1052 		}
1053 		info->op2.type = fex_int;
1054 		info->op2.val.i = *(int *)ea;
1055 		op2v = (long double) info->op2.val.i;
1056 		break;
1057 
1058 	case 0x400:
1059 	case 0x440:
1060 	case 0x480:
1061 		if (!ea) {
1062 			info->op = fex_other;
1063 			info->op1.type = info->op2.type = info->res.type = fex_nodata;
1064 			info->flags = 0;
1065 			return;
1066 		}
1067 		info->op2.type = fex_double;
1068 		info->op2.val.d = *(double *)ea;
1069 		op2v = (long double) info->op2.val.d;
1070 		break;
1071 
1072 	case 0x4c0:
1073 	case 0x6c0:
1074 		info->op2.type = fex_ldouble;
1075 		info->op2.val.q = fpreg(uap, op & 7);
1076 		t = info->op1;
1077 		info->op1 = info->op2;
1078 		info->op2 = t;
1079 		op2v = info->op2.val.q;
1080 		break;
1081 
1082 	case 0x600:
1083 	case 0x640:
1084 	case 0x680:
1085 		if (!ea) {
1086 			info->op = fex_other;
1087 			info->op1.type = info->op2.type = info->res.type = fex_nodata;
1088 			info->flags = 0;
1089 			return;
1090 		}
1091 		info->op2.type = fex_int;
1092 		info->op2.val.i = *(short *)ea;
1093 		op2v = (long double) info->op2.val.i;
1094 		break;
1095 
1096 	default:
1097 		info->op = fex_other;
1098 		info->op1.type = info->op2.type = info->res.type = fex_nodata;
1099 		info->flags = 0;
1100 		return;
1101 	}
1102 
1103 	/* distinguish different operations in the group */
1104 	info->res.type = fex_ldouble;
1105 	switch (op & 0x38) {
1106 	case 0x00:
1107 		info->op = fex_add;
1108 		info->res.val.q = info->op1.val.q + op2v;
1109 		break;
1110 
1111 	case 0x08:
1112 		info->op = fex_mul;
1113 		info->res.val.q = info->op1.val.q * op2v;
1114 		break;
1115 
1116 	case 0x10:
1117 	case 0x18:
1118 		info->op = fex_cmp;
1119 		info->res.type = fex_nodata;
1120 		c = (info->op1.val.q < op2v);
1121 		break;
1122 
1123 	case 0x20:
1124 		info->op = fex_sub;
1125 		info->res.val.q = info->op1.val.q - op2v;
1126 		break;
1127 
1128 	case 0x28:
1129 		info->op = fex_sub;
1130 		info->res.val.q = op2v - info->op1.val.q;
1131 		t = info->op1;
1132 		info->op1 = info->op2;
1133 		info->op2 = t;
1134 		break;
1135 
1136 	case 0x30:
1137 		info->op = fex_div;
1138 		info->res.val.q = info->op1.val.q / op2v;
1139 		break;
1140 
1141 	case 0x38:
1142 		info->op = fex_div;
1143 		info->res.val.q = op2v / info->op1.val.q;
1144 		t = info->op1;
1145 		info->op1 = info->op2;
1146 		info->op2 = t;
1147 		break;
1148 
1149 	default:
1150 		info->op = fex_other;
1151 		info->op1.type = info->op2.type = info->res.type = fex_nodata;
1152 		info->flags = 0;
1153 		return;
1154 	}
1155 
1156 done:
1157 	__fenv_getcwsw(&cwsw);
1158 	info->flags = cwsw & FE_ALL_EXCEPT;
1159 	cwsw &= ~FE_ALL_EXCEPT;
1160 	__fenv_setcwsw(&cwsw);
1161 }
1162 
1163 /* pop the saved stack */
1164 static void pop(ucontext_t *uap)
1165 {
1166 	unsigned top;
1167 
1168 	fpreg(uap, 0) = fpreg(uap, 1);
1169 	fpreg(uap, 1) = fpreg(uap, 2);
1170 	fpreg(uap, 2) = fpreg(uap, 3);
1171 	fpreg(uap, 3) = fpreg(uap, 4);
1172 	fpreg(uap, 4) = fpreg(uap, 5);
1173 	fpreg(uap, 5) = fpreg(uap, 6);
1174 	fpreg(uap, 6) = fpreg(uap, 7);
1175 #if defined(__amd64)
1176 	top = (uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw >> 10)
1177 		& 0xe;
1178 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.fctw |= (3 << top);
1179 	top = (top + 2) & 0xe;
1180 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw =
1181 		(uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw & ~0x3800)
1182 		| (top << 10);
1183 #else
1184 	top = (uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] >> 10)
1185 		& 0xe;
1186 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[TW] |= (3 << top);
1187 	top = (top + 2) & 0xe;
1188 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] =
1189 		(uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] & ~0x3800)
1190 		| (top << 10);
1191 #endif
1192 }
1193 
1194 /* push x onto the saved stack */
1195 static void push(long double x, ucontext_t *uap)
1196 {
1197 	unsigned top;
1198 
1199 	fpreg(uap, 7) = fpreg(uap, 6);
1200 	fpreg(uap, 6) = fpreg(uap, 5);
1201 	fpreg(uap, 5) = fpreg(uap, 4);
1202 	fpreg(uap, 4) = fpreg(uap, 3);
1203 	fpreg(uap, 3) = fpreg(uap, 2);
1204 	fpreg(uap, 2) = fpreg(uap, 1);
1205 	fpreg(uap, 1) = fpreg(uap, 0);
1206 	fpreg(uap, 0) = x;
1207 #if defined(__amd64)
1208 	top = (uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw >> 10)
1209 		& 0xe;
1210 	top = (top - 2) & 0xe;
1211 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.fctw &= ~(3 << top);
1212 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw =
1213 		(uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw & ~0x3800)
1214 		| (top << 10);
1215 #else
1216 	top = (uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] >> 10)
1217 		& 0xe;
1218 	top = (top - 2) & 0xe;
1219 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[TW] &= ~(3 << top);
1220 	uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] =
1221 		(uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] & ~0x3800)
1222 		| (top << 10);
1223 #endif
1224 }
1225 
1226 /* scale factors for exponent wrapping */
1227 static const float
1228 	fun = 7.922816251e+28f,	/* 2^96 */
1229 	fov = 1.262177448e-29f;	/* 2^-96 */
1230 static const double
1231 	dun = 1.552518092300708935e+231,	/* 2^768 */
1232 	dov = 6.441148769597133308e-232;	/* 2^-768 */
1233 
1234 /*
1235 *  Store the specified result; if no result is given but the exception
1236 *  is underflow or overflow, use the default trapped result
1237 */
1238 void
1239 __fex_st_result(siginfo_t *sip, ucontext_t *uap, fex_info_t *info)
1240 {
1241 	fex_numeric_t	r;
1242 	unsigned long		ex, op, ea, stack;
1243 
1244 	/* get the exception type, opcode, and data address */
1245 	ex = sip->si_code;
1246 #if defined(__amd64)
1247 	op = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.fop >> 16;
1248 	ea = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.rdp; /*???*/
1249 #else
1250 	op = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[OP] >> 16;
1251 	ea = uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[EA];
1252 #endif
1253 
1254 	/* if the instruction is a compare, set the condition codes
1255 	   to unordered and update the stack */
1256 	switch (op & 0x7f8) {
1257 	case 0x010:
1258 	case 0x050:
1259 	case 0x090:
1260 	case 0x0d0:
1261 	case 0x210:
1262 	case 0x250:
1263 	case 0x290:
1264 	case 0x410:
1265 	case 0x450:
1266 	case 0x490:
1267 	case 0x4d0:
1268 	case 0x5e0:
1269 	case 0x610:
1270 	case 0x650:
1271 	case 0x690:
1272 		/* f[u]com */
1273 #if defined(__amd64)
1274 		uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw |= 0x4500;
1275 #else
1276 		uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] |= 0x4500;
1277 #endif
1278 		return;
1279 
1280 	case 0x018:
1281 	case 0x058:
1282 	case 0x098:
1283 	case 0x0d8:
1284 	case 0x218:
1285 	case 0x258:
1286 	case 0x298:
1287 	case 0x418:
1288 	case 0x458:
1289 	case 0x498:
1290 	case 0x4d8:
1291 	case 0x5e8:
1292 	case 0x618:
1293 	case 0x658:
1294 	case 0x698:
1295 	case 0x6d0:
1296 		/* f[u]comp */
1297 #if defined(__amd64)
1298 		uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw |= 0x4500;
1299 #else
1300 		uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] |= 0x4500;
1301 #endif
1302 		pop(uap);
1303 		return;
1304 
1305 	case 0x2e8:
1306 	case 0x6d8:
1307 		/* f[u]compp */
1308 #if defined(__amd64)
1309 		uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw |= 0x4500;
1310 #else
1311 		uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] |= 0x4500;
1312 #endif
1313 		pop(uap);
1314 		pop(uap);
1315 		return;
1316 
1317 	case 0x1e0:
1318 		if (op == 0x1e4) { /* ftst */
1319 #if defined(__amd64)
1320 			uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw |= 0x4500;
1321 #else
1322 			uap->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[SW] |= 0x4500;
1323 #endif
1324 			return;
1325 		}
1326 		break;
1327 
1328 	case 0x3e8:
1329 	case 0x3f0:
1330 		/* f[u]comi */
1331 #if defined(__amd64)
1332 		uap->uc_mcontext.gregs[REG_PS] |= 0x45;
1333 #else
1334 		uap->uc_mcontext.gregs[EFL] |= 0x45;
1335 #endif
1336 		return;
1337 
1338 	case 0x7e8:
1339 	case 0x7f0:
1340 		/* f[u]comip */
1341 #if defined(__amd64)
1342 		uap->uc_mcontext.gregs[REG_PS] |= 0x45;
1343 #else
1344 		uap->uc_mcontext.gregs[EFL] |= 0x45;
1345 #endif
1346 		pop(uap);
1347 		return;
1348 	}
1349 
1350 	/* if there is no result available and the exception is overflow
1351 	   or underflow, use the wrapped result */
1352 	r = info->res;
1353 	if (r.type == fex_nodata) {
1354 		if (ex == FPE_FLTOVF || ex == FPE_FLTUND) {
1355 			/* for store instructions, do the scaling and store */
1356 			switch (op & 0x7f8) {
1357 			case 0x110:
1358 			case 0x118:
1359 			case 0x150:
1360 			case 0x158:
1361 			case 0x190:
1362 			case 0x198:
1363 				if (!ea)
1364 					return;
1365 				if (ex == FPE_FLTOVF)
1366 					*(float *)ea = (fpreg(uap, 0) * fov) * fov;
1367 				else
1368 					*(float *)ea = (fpreg(uap, 0) * fun) * fun;
1369 				if ((op & 8) != 0)
1370 					pop(uap);
1371 				break;
1372 
1373 			case 0x510:
1374 			case 0x518:
1375 			case 0x550:
1376 			case 0x558:
1377 			case 0x590:
1378 			case 0x598:
1379 				if (!ea)
1380 					return;
1381 				if (ex == FPE_FLTOVF)
1382 					*(double *)ea = (fpreg(uap, 0) * dov) * dov;
1383 				else
1384 					*(double *)ea = (fpreg(uap, 0) * dun) * dun;
1385 				if ((op & 8) != 0)
1386 					pop(uap);
1387 				break;
1388 			}
1389 		}
1390 #ifdef DEBUG
1391 		else if (ex != FPE_FLTRES)
1392 			printf("No result supplied, stack may be hosed\n");
1393 #endif
1394 		return;
1395 	}
1396 
1397 	/* otherwise convert the supplied result to the correct type,
1398 	   put it in the destination, and update the stack as need be */
1399 
1400 	/* store instructions */
1401 	switch (op & 0x7f8) {
1402 	case 0x110:
1403 	case 0x118:
1404 	case 0x150:
1405 	case 0x158:
1406 	case 0x190:
1407 	case 0x198:
1408 		if (!ea)
1409 			return;
1410 		switch (r.type) {
1411 		case fex_int:
1412 			*(float *)ea = (float) r.val.i;
1413 			break;
1414 
1415 		case fex_llong:
1416 			*(float *)ea = (float) r.val.l;
1417 			break;
1418 
1419 		case fex_float:
1420 			*(float *)ea = r.val.f;
1421 			break;
1422 
1423 		case fex_double:
1424 			*(float *)ea = (float) r.val.d;
1425 			break;
1426 
1427 		case fex_ldouble:
1428 			*(float *)ea = (float) r.val.q;
1429 			break;
1430 
1431 		default:
1432 			break;
1433 		}
1434 		if (ex != FPE_FLTRES && (op & 8) != 0)
1435 			pop(uap);
1436 		return;
1437 
1438 	case 0x310:
1439 	case 0x318:
1440 	case 0x350:
1441 	case 0x358:
1442 	case 0x390:
1443 	case 0x398:
1444 		if (!ea)
1445 			return;
1446 		switch (r.type) {
1447 		case fex_int:
1448 			*(int *)ea = r.val.i;
1449 			break;
1450 
1451 		case fex_llong:
1452 			*(int *)ea = (int) r.val.l;
1453 			break;
1454 
1455 		case fex_float:
1456 			*(int *)ea = (int) r.val.f;
1457 			break;
1458 
1459 		case fex_double:
1460 			*(int *)ea = (int) r.val.d;
1461 			break;
1462 
1463 		case fex_ldouble:
1464 			*(int *)ea = (int) r.val.q;
1465 			break;
1466 
1467 		default:
1468 			break;
1469 		}
1470 		if (ex != FPE_FLTRES && (op & 8) != 0)
1471 			pop(uap);
1472 		return;
1473 
1474 	case 0x510:
1475 	case 0x518:
1476 	case 0x550:
1477 	case 0x558:
1478 	case 0x590:
1479 	case 0x598:
1480 		if (!ea)
1481 			return;
1482 		switch (r.type) {
1483 		case fex_int:
1484 			*(double *)ea = (double) r.val.i;
1485 			break;
1486 
1487 		case fex_llong:
1488 			*(double *)ea = (double) r.val.l;
1489 			break;
1490 
1491 		case fex_float:
1492 			*(double *)ea = (double) r.val.f;
1493 			break;
1494 
1495 		case fex_double:
1496 			*(double *)ea = r.val.d;
1497 			break;
1498 
1499 		case fex_ldouble:
1500 			*(double *)ea = (double) r.val.q;
1501 			break;
1502 
1503 		default:
1504 			break;
1505 		}
1506 		if (ex != FPE_FLTRES && (op & 8) != 0)
1507 			pop(uap);
1508 		return;
1509 
1510 	case 0x710:
1511 	case 0x718:
1512 	case 0x750:
1513 	case 0x758:
1514 	case 0x790:
1515 	case 0x798:
1516 		if (!ea)
1517 			return;
1518 		switch (r.type) {
1519 		case fex_int:
1520 			*(short *)ea = (short) r.val.i;
1521 			break;
1522 
1523 		case fex_llong:
1524 			*(short *)ea = (short) r.val.l;
1525 			break;
1526 
1527 		case fex_float:
1528 			*(short *)ea = (short) r.val.f;
1529 			break;
1530 
1531 		case fex_double:
1532 			*(short *)ea = (short) r.val.d;
1533 			break;
1534 
1535 		case fex_ldouble:
1536 			*(short *)ea = (short) r.val.q;
1537 			break;
1538 
1539 		default:
1540 			break;
1541 		}
1542 		if (ex != FPE_FLTRES && (op & 8) != 0)
1543 			pop(uap);
1544 		return;
1545 
1546 	case 0x730:
1547 	case 0x770:
1548 	case 0x7b0:
1549 		/* fbstp; don't bother */
1550 		if (ea && ex != FPE_FLTRES)
1551 			pop(uap);
1552 		return;
1553 
1554 	case 0x738:
1555 	case 0x778:
1556 	case 0x7b8:
1557 		if (!ea)
1558 			return;
1559 		switch (r.type) {
1560 		case fex_int:
1561 			*(long long *)ea = (long long) r.val.i;
1562 			break;
1563 
1564 		case fex_llong:
1565 			*(long long *)ea = r.val.l;
1566 			break;
1567 
1568 		case fex_float:
1569 			*(long long *)ea = (long long) r.val.f;
1570 			break;
1571 
1572 		case fex_double:
1573 			*(long long *)ea = (long long) r.val.d;
1574 			break;
1575 
1576 		case fex_ldouble:
1577 			*(long long *)ea = (long long) r.val.q;
1578 			break;
1579 
1580 		default:
1581 			break;
1582 		}
1583 		if (ex != FPE_FLTRES)
1584 			pop(uap);
1585 		return;
1586 	}
1587 
1588 	/* for all other instructions, the result goes into a register */
1589 	switch (r.type) {
1590 	case fex_int:
1591 		r.val.q = (long double) r.val.i;
1592 		break;
1593 
1594 	case fex_llong:
1595 		r.val.q = (long double) r.val.l;
1596 		break;
1597 
1598 	case fex_float:
1599 		r.val.q = (long double) r.val.f;
1600 		break;
1601 
1602 	case fex_double:
1603 		r.val.q = (long double) r.val.d;
1604 		break;
1605 
1606 	default:
1607 		break;
1608 	}
1609 
1610 	/* for load instructions, push the result onto the stack */
1611 	switch (op & 0x7f8) {
1612 	case 0x100:
1613 	case 0x140:
1614 	case 0x180:
1615 	case 0x500:
1616 	case 0x540:
1617 	case 0x580:
1618 		if (ea)
1619 			push(r.val.q, uap);
1620 		return;
1621 	}
1622 
1623 	/* for all other instructions, if the exception is overflow,
1624 	   underflow, or inexact, the stack has already been updated */
1625 	stack = (ex == FPE_FLTOVF || ex == FPE_FLTUND || ex == FPE_FLTRES);
1626 	switch (op & 0x7f8) {
1627 	case 0x1f0: /* oddballs */
1628 		switch (op) {
1629 		case 0x1f1: /* fyl2x */
1630 		case 0x1f3: /* fpatan */
1631 		case 0x1f9: /* fyl2xp1 */
1632 			/* pop the stack, leaving the result in st */
1633 			if (!stack)
1634 				pop(uap);
1635 			fpreg(uap, 0) = r.val.q;
1636 			return;
1637 
1638 		case 0x1f2: /* fpatan */
1639 			/* fptan pushes 1.0 afterward */
1640 			if (stack)
1641 				fpreg(uap, 1) = r.val.q;
1642 			else {
1643 				fpreg(uap, 0) = r.val.q;
1644 				push(1.0L, uap);
1645 			}
1646 			return;
1647 
1648 		case 0x1f4: /* fxtract */
1649 		case 0x1fb: /* fsincos */
1650 			/* leave the supplied result in st */
1651 			if (stack)
1652 				fpreg(uap, 0) = r.val.q;
1653 			else {
1654 				fpreg(uap, 0) = 0.0; /* punt */
1655 				push(r.val.q, uap);
1656 			}
1657 			return;
1658 		}
1659 
1660 		/* all others leave the stack alone and the result in st */
1661 		fpreg(uap, 0) = r.val.q;
1662 		return;
1663 
1664 	case 0x4c0:
1665 	case 0x4c8:
1666 	case 0x4e0:
1667 	case 0x4e8:
1668 	case 0x4f0:
1669 	case 0x4f8:
1670 		fpreg(uap, op & 7) = r.val.q;
1671 		return;
1672 
1673 	case 0x6c0:
1674 	case 0x6c8:
1675 	case 0x6e0:
1676 	case 0x6e8:
1677 	case 0x6f0:
1678 	case 0x6f8:
1679 		/* stack is popped afterward */
1680 		if (stack)
1681 			fpreg(uap, (op - 1) & 7) = r.val.q;
1682 		else {
1683 			fpreg(uap, op & 7) = r.val.q;
1684 			pop(uap);
1685 		}
1686 		return;
1687 
1688 	default:
1689 		fpreg(uap, 0) = r.val.q;
1690 		return;
1691 	}
1692 }
1693