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