1 /* $NetBSD: fpu_emulate.c,v 1.29 2009/03/14 14:46:01 dsl Exp $ */
4 * Copyright (c) 1995 Gordon W. Ross
5 * some portion Copyright (c) 1995 Ken Nakata
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 * 4. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by Gordon Ross
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 * XXX - Just a start at it for now...
39 #include <sys/cdefs.h>
40 __KERNEL_RCSID(0, "$NetBSD: fpu_emulate.c,v 1.29 2009/03/14 14:46:01 dsl Exp $");
42 #include <sys/param.h>
43 #include <sys/types.h>
44 #include <sys/signal.h>
45 #include <sys/systm.h>
46 #include <machine/frame.h>
48 #if defined(DDB) && defined(DEBUG_FPE)
49 # include <m68k/db_machdep.h>
52 #include "fpu_emulate.h"
54 #define fpe_abort(tfp, ksi, signo, code) \
56 (ksi)->ksi_signo = (signo); \
57 (ksi)->ksi_code = (code); \
58 (ksi)->ksi_addr = (void *)(frame)->f_pc; \
60 } while (/*CONSTCOND*/0)
62 static int fpu_emul_fmovmcr(struct fpemu
*fe
, struct instruction
*insn
);
63 static int fpu_emul_fmovm(struct fpemu
*fe
, struct instruction
*insn
);
64 static int fpu_emul_arith(struct fpemu
*fe
, struct instruction
*insn
);
65 static int fpu_emul_type1(struct fpemu
*fe
, struct instruction
*insn
);
66 static int fpu_emul_brcc(struct fpemu
*fe
, struct instruction
*insn
);
67 static int test_cc(struct fpemu
*fe
, int pred
);
68 static struct fpn
*fpu_cmp(struct fpemu
*fe
);
71 # define DUMP_INSN(insn) \
72 printf("fpu_emulate: insn={adv=%d,siz=%d,op=%04x,w1=%04x}\n", \
73 (insn)->is_advance, (insn)->is_datasize, \
74 (insn)->is_opcode, (insn)->is_word1)
76 # define DUMP_INSN(insn)
80 * Emulate a floating-point instruction.
81 * Return zero for success, else signal number.
82 * (Typically: zero, SIGFPE, SIGILL, SIGSEGV)
85 fpu_emulate(struct frame
*frame
, struct fpframe
*fpf
, ksiginfo_t
*ksi
)
87 static struct instruction insn
;
88 static struct fpemu fe
;
89 int word
, optype
, sig
;
92 /* initialize insn.is_datasize to tell it is *not* initialized */
93 insn
.is_datasize
= -1;
97 fe
.fe_fpsr
= fpf
->fpf_fpsr
;
98 fe
.fe_fpcr
= fpf
->fpf_fpcr
;
101 printf("ENTERING fpu_emulate: FPSR=%08x, FPCR=%08x\n",
102 fe
.fe_fpsr
, fe
.fe_fpcr
);
105 /* always set this (to avoid a warning) */
106 insn
.is_pc
= frame
->f_pc
;
108 if (frame
->f_format
== 4) {
110 * A format 4 is generated by the 68{EC,LC}040. The PC is
111 * already set to the instruction following the faulting
112 * instruction. We need to calculate that, anyway. The
113 * fslw is the PC of the faulted instruction, which is what
114 * we expect to be in f_pc.
116 * XXX - This is a hack; it assumes we at least know the
117 * sizes of all instructions we run across.
118 * XXX TODO: This may not be true, so we might want to save the PC
119 * in order to restore it later.
121 /* insn.is_nextpc = frame->f_pc; */
122 insn
.is_pc
= frame
->f_fmt4
.f_fslw
;
123 frame
->f_pc
= insn
.is_pc
;
126 word
= fusword((void *) (insn
.is_pc
));
129 printf("fpu_emulate: fault reading opcode\n");
131 fpe_abort(frame
, ksi
, SIGSEGV
, SEGV_ACCERR
);
134 if ((word
& 0xf000) != 0xf000) {
136 printf("fpu_emulate: not coproc. insn.: opcode=0x%x\n", word
);
138 fpe_abort(frame
, ksi
, SIGILL
, ILL_ILLOPC
);
141 if ((word
& 0x0E00) != 0x0200) {
143 printf("fpu_emulate: bad coproc. id: opcode=0x%x\n", word
);
145 fpe_abort(frame
, ksi
, SIGILL
, ILL_ILLOPC
);
148 insn
.is_opcode
= word
;
149 optype
= (word
& 0x01C0);
151 word
= fusword((void *) (insn
.is_pc
+ 2));
154 printf("fpu_emulate: fault reading word1\n");
156 fpe_abort(frame
, ksi
, SIGSEGV
, SEGV_ACCERR
);
158 insn
.is_word1
= word
;
159 /* all FPU instructions are at least 4-byte long */
165 * Which family (or type) of opcode is it?
166 * Tests ordered by likelihood (hopefully).
167 * Certainly, type 0 is the most common.
169 if (optype
== 0x0000) {
170 /* type=0: generic */
171 if ((word
& 0xc000) == 0xc000) {
173 printf("fpu_emulate: fmovm FPr\n");
175 sig
= fpu_emul_fmovm(&fe
, &insn
);
176 } else if ((word
& 0xc000) == 0x8000) {
178 printf("fpu_emulate: fmovm FPcr\n");
180 sig
= fpu_emul_fmovmcr(&fe
, &insn
);
181 } else if ((word
& 0xe000) == 0x6000) {
182 /* fstore = fmove FPn,mem */
184 printf("fpu_emulate: fmove to mem\n");
186 sig
= fpu_emul_fstore(&fe
, &insn
);
187 } else if ((word
& 0xfc00) == 0x5c00) {
190 printf("fpu_emulate: fmovecr\n");
192 sig
= fpu_emul_fmovecr(&fe
, &insn
);
193 } else if ((word
& 0xa07f) == 0x26) {
196 printf("fpu_emulate: fscale\n");
198 sig
= fpu_emul_fscale(&fe
, &insn
);
201 printf("fpu_emulate: other type0\n");
203 /* all other type0 insns are arithmetic */
204 sig
= fpu_emul_arith(&fe
, &insn
);
208 printf("fpu_emulate: type 0 returned 0\n");
210 sig
= fpu_upd_excp(&fe
);
212 } else if (optype
== 0x0080 || optype
== 0x00C0) {
213 /* type=2 or 3: fbcc, short or long disp. */
215 printf("fpu_emulate: fbcc %s\n",
216 (optype
& 0x40) ? "long" : "short");
218 sig
= fpu_emul_brcc(&fe
, &insn
);
219 } else if (optype
== 0x0040) {
220 /* type=1: fdbcc, fscc, ftrapcc */
222 printf("fpu_emulate: type1\n");
224 sig
= fpu_emul_type1(&fe
, &insn
);
226 /* type=4: fsave (privileged) */
227 /* type=5: frestore (privileged) */
228 /* type=6: reserved */
229 /* type=7: reserved */
231 printf("fpu_emulate: bad opcode type: opcode=0x%x\n", insn
.is_opcode
);
239 * XXX it is not clear to me, if we should progress the PC always,
240 * for SIGFPE || 0, or only for 0; however, without SIGFPE, we
241 * don't pass the signalling regression tests. -is
243 if ((sig
== 0) || (sig
== SIGFPE
))
244 frame
->f_pc
+= insn
.is_advance
;
245 #if defined(DDB) && defined(DEBUG_FPE)
247 printf("fpu_emulate: sig=%d, opcode=%x, word1=%x\n",
248 sig
, insn
.is_opcode
, insn
.is_word1
);
249 kdb_trap(-1, (db_regs_t
*)&frame
);
252 #if 0 /* XXX something is wrong */
253 if (frame
->f_format
== 4) {
254 /* XXX Restore PC -- 68{EC,LC}040 only */
256 frame
->f_pc
= insn
.is_nextpc
;
261 printf("EXITING fpu_emulate: w/FPSR=%08x, FPCR=%08x\n",
262 fe
.fe_fpsr
, fe
.fe_fpcr
);
266 fpe_abort(frame
, ksi
, sig
, 0);
270 /* update accrued exception bits and see if there's an FP exception */
272 fpu_upd_excp(struct fpemu
*fe
)
279 /* update fpsr accrued exception bits; each insn doesn't have to
281 if (fpsr
& (FPSR_BSUN
| FPSR_SNAN
| FPSR_OPERR
)) {
284 if (fpsr
& FPSR_OVFL
) {
287 if ((fpsr
& FPSR_UNFL
) && (fpsr
& FPSR_INEX2
)) {
290 if (fpsr
& FPSR_DZ
) {
293 if (fpsr
& (FPSR_INEX1
| FPSR_INEX2
| FPSR_OVFL
)) {
297 fe
->fe_fpframe
->fpf_fpsr
= fe
->fe_fpsr
= fpsr
;
299 return (fpsr
& fpcr
& FPSR_EXCP
) ? SIGFPE
: 0;
302 /* update fpsr according to fp (= result of an fp op) */
304 fpu_upd_fpsr(struct fpemu
*fe
, struct fpn
*fp
)
309 printf("fpu_upd_fpsr: previous fpsr=%08x\n", fe
->fe_fpsr
);
311 /* clear all condition code */
312 fpsr
= fe
->fe_fpsr
& ~FPSR_CCB
;
315 printf("fpu_upd_fpsr: result is a ");
328 switch (fp
->fp_class
) {
331 printf("signaling NAN\n");
333 fpsr
|= (FPSR_NAN
| FPSR_SNAN
);
337 printf("quiet NAN\n");
357 /* anything else is treated as if it is a number */
361 fe
->fe_fpsr
= fe
->fe_fpframe
->fpf_fpsr
= fpsr
;
364 printf("fpu_upd_fpsr: new fpsr=%08x\n", fe
->fe_fpframe
->fpf_fpsr
);
371 fpu_emul_fmovmcr(struct fpemu
*fe
, struct instruction
*insn
)
373 struct frame
*frame
= fe
->fe_frame
;
374 struct fpframe
*fpf
= fe
->fe_fpframe
;
379 /* move to/from control registers */
380 reglist
= (insn
->is_word1
& 0x1c00) >> 10;
381 /* Bit 13 selects direction (FPU to/from Mem) */
382 fpu_to_mem
= insn
->is_word1
& 0x2000;
384 insn
->is_datasize
= 4;
385 insn
->is_advance
= 4;
386 sig
= fpu_decode_ea(frame
, insn
, &insn
->is_ea
, insn
->is_opcode
);
387 if (sig
) { return sig
; }
389 if (reglist
!= 1 && reglist
!= 2 && reglist
!= 4 &&
390 (insn
->is_ea
.ea_flags
& EA_DIRECT
)) {
391 /* attempted to copy more than one FPcr to CPU regs */
393 printf("fpu_emul_fmovmcr: tried to copy too many FPcr\n");
400 if ((insn
->is_ea
.ea_flags
& EA_DIRECT
) &&
401 insn
->is_ea
.ea_regnum
>= 8 /* address reg */) {
402 /* attempted to copy FPCR to An */
404 printf("fpu_emul_fmovmcr: tried to copy FPCR from/to A%d\n",
405 insn
->is_ea
.ea_regnum
& 7);
410 sig
= fpu_store_ea(frame
, insn
, &insn
->is_ea
,
411 (char *)&fpf
->fpf_fpcr
);
413 sig
= fpu_load_ea(frame
, insn
, &insn
->is_ea
,
414 (char *)&fpf
->fpf_fpcr
);
417 if (sig
) { return sig
; }
421 if ((insn
->is_ea
.ea_flags
& EA_DIRECT
) &&
422 insn
->is_ea
.ea_regnum
>= 8 /* address reg */) {
423 /* attempted to copy FPSR to An */
425 printf("fpu_emul_fmovmcr: tried to copy FPSR from/to A%d\n",
426 insn
->is_ea
.ea_regnum
& 7);
431 sig
= fpu_store_ea(frame
, insn
, &insn
->is_ea
,
432 (char *)&fpf
->fpf_fpsr
);
434 sig
= fpu_load_ea(frame
, insn
, &insn
->is_ea
,
435 (char *)&fpf
->fpf_fpsr
);
438 if (sig
) { return sig
; }
441 /* fpiar - can be moved to/from An */
443 sig
= fpu_store_ea(frame
, insn
, &insn
->is_ea
,
444 (char *)&fpf
->fpf_fpiar
);
446 sig
= fpu_load_ea(frame
, insn
, &insn
->is_ea
,
447 (char *)&fpf
->fpf_fpiar
);
455 * Separated out of fpu_emul_type0 for efficiency.
456 * In this function, we know:
457 * (opcode & 0x01C0) == 0
458 * (word1 & 0x8000) == 0x8000
460 * No conversion or rounding is done by this instruction,
461 * and the FPSR is not affected.
464 fpu_emul_fmovm(struct fpemu
*fe
, struct instruction
*insn
)
466 struct frame
*frame
= fe
->fe_frame
;
467 struct fpframe
*fpf
= fe
->fe_fpframe
;
469 int reglist
, regmask
, regnum
;
470 int fpu_to_mem
, order
;
474 insn
->is_advance
= 4;
475 insn
->is_datasize
= 12;
476 word1
= insn
->is_word1
;
478 /* Bit 13 selects direction (FPU to/from Mem) */
479 fpu_to_mem
= word1
& 0x2000;
482 * Bits 12,11 select register list mode:
483 * 0,0: Static reg list, pre-decr.
484 * 0,1: Dynamic reg list, pre-decr.
485 * 1,0: Static reg list, post-incr.
486 * 1,1: Dynamic reg list, post-incr
488 w1_post_incr
= word1
& 0x1000;
489 if (word1
& 0x0800) {
490 /* dynamic reg list */
491 reglist
= frame
->f_regs
[(word1
& 0x70) >> 4];
497 /* Get effective address. (modreg=opcode&077) */
498 sig
= fpu_decode_ea(frame
, insn
, &insn
->is_ea
, insn
->is_opcode
);
499 if (sig
) { return sig
; }
501 /* Get address of soft coprocessor regs. */
502 fpregs
= &fpf
->fpf_regs
[0];
504 if (insn
->is_ea
.ea_flags
& EA_PREDECR
) {
513 while ((0 <= regnum
) && (regnum
< 8)) {
514 if (regmask
& reglist
) {
516 sig
= fpu_store_ea(frame
, insn
, &insn
->is_ea
,
517 (char*)&fpregs
[regnum
* 3]);
519 printf("fpu_emul_fmovm: FP%d (%08x,%08x,%08x) saved\n",
520 regnum
, fpregs
[regnum
* 3], fpregs
[regnum
* 3 + 1],
521 fpregs
[regnum
* 3 + 2]);
523 } else { /* mem to fpu */
524 sig
= fpu_load_ea(frame
, insn
, &insn
->is_ea
,
525 (char*)&fpregs
[regnum
* 3]);
527 printf("fpu_emul_fmovm: FP%d (%08x,%08x,%08x) loaded\n",
528 regnum
, fpregs
[regnum
* 3], fpregs
[regnum
* 3 + 1],
529 fpregs
[regnum
* 3 + 2]);
542 fpu_cmp(struct fpemu
*fe
)
544 struct fpn
*x
= &fe
->fe_f1
, *y
= &fe
->fe_f2
;
546 /* take care of special cases */
547 if (x
->fp_class
< 0 || y
->fp_class
< 0) {
548 /* if either of two is a SNAN, result is SNAN */
549 x
->fp_class
= (y
->fp_class
< x
->fp_class
) ? y
->fp_class
: x
->fp_class
;
550 } else if (x
->fp_class
== FPC_INF
) {
551 if (y
->fp_class
== FPC_INF
) {
552 /* both infinities */
553 if (x
->fp_sign
== y
->fp_sign
) {
554 x
->fp_class
= FPC_ZERO
; /* return a signed zero */
556 x
->fp_class
= FPC_NUM
; /* return a faked number w/x's sign */
558 x
->fp_mant
[0] = FP_1
;
562 x
->fp_class
= FPC_NUM
; /* return a forged number w/x's sign */
564 x
->fp_mant
[0] = FP_1
;
566 } else if (y
->fp_class
== FPC_INF
) {
567 /* x is a Num but y is an Inf */
568 /* return a forged number w/y's sign inverted */
569 x
->fp_class
= FPC_NUM
;
570 x
->fp_sign
= !y
->fp_sign
;
572 x
->fp_mant
[0] = FP_1
;
574 /* x and y are both numbers or zeros, or pair of a number and a zero */
575 y
->fp_sign
= !y
->fp_sign
;
576 x
= fpu_add(fe
); /* (x - y) */
578 * FCMP does not set Inf bit in CC, so return a forged number
579 * (value doesn't matter) if Inf is the result of fsub.
581 if (x
->fp_class
== FPC_INF
) {
582 x
->fp_class
= FPC_NUM
;
584 x
->fp_mant
[0] = FP_1
;
591 * arithmetic oprations
594 fpu_emul_arith(struct fpemu
*fe
, struct instruction
*insn
)
596 struct frame
*frame
= fe
->fe_frame
;
597 u_int
*fpregs
= &(fe
->fe_fpframe
->fpf_regs
[0]);
601 int discard_result
= 0;
608 fe
->fe_fpsr
&= ~FPSR_EXCP
;
613 printf("fpu_emul_arith: FPSR = %08x, FPCR = %08x\n",
614 fe
->fe_fpsr
, fe
->fe_fpcr
);
617 word1
= insn
->is_word1
;
618 format
= (word1
>> 10) & 7;
619 regnum
= (word1
>> 7) & 7;
621 /* fetch a source operand : may not be used */
623 printf("fpu_emul_arith: dst/src FP%d=%08x,%08x,%08x\n",
624 regnum
, fpregs
[regnum
*3], fpregs
[regnum
*3+1],
628 fpu_explode(fe
, &fe
->fe_f1
, FTYPE_EXT
, &fpregs
[regnum
* 3]);
632 /* get the other operand which is always the source */
633 if ((word1
& 0x4000) == 0) {
635 printf("fpu_emul_arith: FP%d op FP%d => FP%d\n",
636 format
, regnum
, regnum
);
637 printf("fpu_emul_arith: src opr FP%d=%08x,%08x,%08x\n",
638 format
, fpregs
[format
*3], fpregs
[format
*3+1],
641 fpu_explode(fe
, &fe
->fe_f2
, FTYPE_EXT
, &fpregs
[format
* 3]);
643 /* the operand is in memory */
644 if (format
== FTYPE_DBL
) {
645 insn
->is_datasize
= 8;
646 } else if (format
== FTYPE_SNG
|| format
== FTYPE_LNG
) {
647 insn
->is_datasize
= 4;
648 } else if (format
== FTYPE_WRD
) {
649 insn
->is_datasize
= 2;
650 } else if (format
== FTYPE_BYT
) {
651 insn
->is_datasize
= 1;
652 } else if (format
== FTYPE_EXT
) {
653 insn
->is_datasize
= 12;
655 /* invalid or unsupported operand format */
660 /* Get effective address. (modreg=opcode&077) */
661 sig
= fpu_decode_ea(frame
, insn
, &insn
->is_ea
, insn
->is_opcode
);
664 printf("fpu_emul_arith: error in fpu_decode_ea\n");
672 printf("fpu_emul_arith: addr mode = ");
673 flags
= insn
->is_ea
.ea_flags
;
674 regname
= (insn
->is_ea
.ea_regnum
& 8) ? 'a' : 'd';
676 if (flags
& EA_DIRECT
) {
678 regname
, insn
->is_ea
.ea_regnum
& 7);
679 } else if (flags
& EA_PC_REL
) {
680 if (flags
& EA_OFFSET
) {
681 printf("pc@(%d)\n", insn
->is_ea
.ea_offset
);
682 } else if (flags
& EA_INDEXED
) {
683 printf("pc@(...)\n");
685 } else if (flags
& EA_PREDECR
) {
687 regname
, insn
->is_ea
.ea_regnum
& 7);
688 } else if (flags
& EA_POSTINCR
) {
689 printf("%c%d@+\n", regname
, insn
->is_ea
.ea_regnum
& 7);
690 } else if (flags
& EA_OFFSET
) {
691 printf("%c%d@(%d)\n", regname
, insn
->is_ea
.ea_regnum
& 7,
692 insn
->is_ea
.ea_offset
);
693 } else if (flags
& EA_INDEXED
) {
694 printf("%c%d@(...)\n", regname
, insn
->is_ea
.ea_regnum
& 7);
695 } else if (flags
& EA_ABS
) {
696 printf("0x%08x\n", insn
->is_ea
.ea_absaddr
);
697 } else if (flags
& EA_IMMED
) {
699 printf("#0x%08x,%08x,%08x\n", insn
->is_ea
.ea_immed
[0],
700 insn
->is_ea
.ea_immed
[1], insn
->is_ea
.ea_immed
[2]);
702 printf("%c%d@\n", regname
, insn
->is_ea
.ea_regnum
& 7);
704 #endif /* DEBUG_FPE */
706 fpu_load_ea(frame
, insn
, &insn
->is_ea
, (char*)buf
);
707 if (format
== FTYPE_WRD
) {
710 if (buf
[0] & 0x8000) {
711 buf
[0] |= 0xffff0000;
714 } else if (format
== FTYPE_BYT
) {
718 buf
[0] |= 0xffffff00;
723 printf("fpu_emul_arith: src = %08x %08x %08x, siz = %d\n",
724 buf
[0], buf
[1], buf
[2], insn
->is_datasize
);
726 fpu_explode(fe
, &fe
->fe_f2
, format
, buf
);
731 /* An arithmetic instruction emulate function has a prototype of
732 * struct fpn *fpu_op(struct fpemu *);
734 * 1) If the instruction is monadic, then fpu_op() must use
735 * fe->fe_f2 as its operand, and return a pointer to the
738 * 2) If the instruction is diadic, then fpu_op() must use
739 * fe->fe_f1 and fe->fe_f2 as its two operands, and return a
740 * pointer to the result.
744 switch (word1
& 0x7f) {
745 case 0x00: /* fmove */
749 case 0x01: /* fint */
753 case 0x02: /* fsinh */
757 case 0x03: /* fintrz */
761 case 0x04: /* fsqrt */
765 case 0x06: /* flognp1 */
766 res
= fpu_lognp1(fe
);
769 case 0x08: /* fetoxm1 */
770 res
= fpu_etoxm1(fe
);
773 case 0x09: /* ftanh */
777 case 0x0A: /* fatan */
781 case 0x0C: /* fasin */
785 case 0x0D: /* fatanh */
789 case 0x0E: /* fsin */
793 case 0x0F: /* ftan */
797 case 0x10: /* fetox */
801 case 0x11: /* ftwotox */
802 res
= fpu_twotox(fe
);
805 case 0x12: /* ftentox */
806 res
= fpu_tentox(fe
);
809 case 0x14: /* flogn */
813 case 0x15: /* flog10 */
817 case 0x16: /* flog2 */
821 case 0x18: /* fabs */
822 fe
->fe_f2
.fp_sign
= 0;
826 case 0x19: /* fcosh */
830 case 0x1A: /* fneg */
831 fe
->fe_f2
.fp_sign
= !fe
->fe_f2
.fp_sign
;
835 case 0x1C: /* facos */
839 case 0x1D: /* fcos */
843 case 0x1E: /* fgetexp */
844 res
= fpu_getexp(fe
);
847 case 0x1F: /* fgetman */
848 res
= fpu_getman(fe
);
851 case 0x20: /* fdiv */
852 case 0x24: /* fsgldiv: cheating - better than nothing */
856 case 0x21: /* fmod */
860 case 0x28: /* fsub */
861 fe
->fe_f2
.fp_sign
= !fe
->fe_f2
.fp_sign
; /* f2 = -f2 */
862 case 0x22: /* fadd */
866 case 0x23: /* fmul */
867 case 0x27: /* fsglmul: cheating - better than nothing */
871 case 0x25: /* frem */
876 /* fscale is handled by a separate function */
886 case 0x37: /* fsincos */
887 res
= fpu_sincos(fe
, word1
& 7);
890 case 0x38: /* fcmp */
895 case 0x3A: /* ftst */
900 default: /* possibly 040/060 instructions */
902 printf("fpu_emul_arith: bad opcode=0x%x, word1=0x%x\n",
903 insn
->is_opcode
, insn
->is_word1
);
906 } /* switch (word1 & 0x3f) */
912 if (!discard_result
&& sig
== 0) {
913 fpu_implode(fe
, res
, FTYPE_EXT
, &fpregs
[regnum
* 3]);
915 /* update fpsr according to the result of operation */
916 fpu_upd_fpsr(fe
, res
);
918 printf("fpu_emul_arith: %08x,%08x,%08x stored in FP%d\n",
919 fpregs
[regnum
*3], fpregs
[regnum
*3+1],
920 fpregs
[regnum
*3+2], regnum
);
921 } else if (sig
== 0) {
922 static const char *class_name
[] =
923 { "SNAN", "QNAN", "ZERO", "NUM", "INF" };
924 printf("fpu_emul_arith: result(%s,%c,%d,%08x,%08x,%08x) discarded\n",
925 class_name
[res
->fp_class
+ 2],
926 res
->fp_sign
? '-' : '+', res
->fp_exp
,
927 res
->fp_mant
[0], res
->fp_mant
[1],
930 printf("fpu_emul_arith: received signal %d\n", sig
);
935 printf("fpu_emul_arith: FPSR = %08x, FPCR = %08x\n",
936 fe
->fe_fpsr
, fe
->fe_fpcr
);
944 /* test condition code according to the predicate in the opcode.
945 * returns -1 when the predicate evaluates to true, 0 when false.
946 * signal numbers are returned when an error is detected.
949 test_cc(struct fpemu
*fe
, int pred
)
951 int result
, sig_bsun
, invert
;
956 fpsr
&= ~FPSR_EXCP
; /* clear all exceptions */
958 printf("test_cc: fpsr=0x%08x\n", fpsr
);
960 pred
&= 0x3f; /* lowest 6 bits */
968 } else if (pred
& 0x10) {
969 /* IEEE nonaware tests */
971 pred
&= 0x0f; /* lower 4 bits */
973 /* IEEE aware tests */
984 /* predicate is "NOT ..." */
985 pred
^= 0xf; /* invert */
989 case 0: /* (Signaling) False */
995 case 1: /* (Signaling) Equal */
999 result
= -((fpsr
& FPSR_ZERO
) == FPSR_ZERO
);
1001 case 2: /* Greater Than */
1005 result
= -((fpsr
& (FPSR_NAN
|FPSR_ZERO
|FPSR_NEG
)) == 0);
1007 case 3: /* Greater or Equal */
1011 result
= -((fpsr
& FPSR_ZERO
) ||
1012 (fpsr
& (FPSR_NAN
|FPSR_NEG
)) == 0);
1014 case 4: /* Less Than */
1018 result
= -((fpsr
& (FPSR_NAN
|FPSR_ZERO
|FPSR_NEG
)) == FPSR_NEG
);
1020 case 5: /* Less or Equal */
1024 result
= -((fpsr
& FPSR_ZERO
) ||
1025 ((fpsr
& (FPSR_NAN
|FPSR_NEG
)) == FPSR_NEG
));
1027 case 6: /* Greater or Less than */
1031 result
= -((fpsr
& (FPSR_NAN
|FPSR_ZERO
)) == 0);
1033 case 7: /* Greater, Less or Equal */
1037 result
= -((fpsr
& FPSR_NAN
) == 0);
1040 /* invalid predicate */
1043 result
^= invert
; /* if the predicate is "NOT ...", then
1044 invert the result */
1046 printf("=> %s (%d)\n", result
? "true" : "false", result
);
1048 /* if it's an IEEE unaware test and NAN is set, BSUN is set */
1049 if (sig_bsun
&& (fpsr
& FPSR_NAN
)) {
1054 fe
->fe_fpframe
->fpf_fpsr
= fe
->fe_fpsr
= fpsr
;
1060 * type 1: fdbcc, fscc, ftrapcc
1061 * In this function, we know:
1062 * (opcode & 0x01C0) == 0x0040
1065 fpu_emul_type1(struct fpemu
*fe
, struct instruction
*insn
)
1067 struct frame
*frame
= fe
->fe_frame
;
1068 int advance
, sig
, branch
, displ
;
1070 branch
= test_cc(fe
, insn
->is_word1
);
1071 fe
->fe_fpframe
->fpf_fpsr
= fe
->fe_fpsr
;
1073 insn
->is_advance
= 4;
1076 switch (insn
->is_opcode
& 070) {
1077 case 010: /* fdbcc */
1080 insn
->is_advance
= 6;
1081 } else if (!branch
) {
1082 /* decrement Dn and if (Dn != -1) branch */
1083 u_int16_t count
= frame
->f_regs
[insn
->is_opcode
& 7];
1086 displ
= fusword((void *) (insn
->is_pc
+ insn
->is_advance
));
1089 printf("fpu_emul_type1: fault reading displacement\n");
1093 /* sign-extend the displacement */
1095 if (displ
& 0x8000) {
1096 displ
|= 0xffff0000;
1098 insn
->is_advance
+= displ
;
1099 /* XXX insn->is_nextpc = insn->is_pc + insn->is_advance; */
1101 insn
->is_advance
= 6;
1104 frame
->f_regs
[insn
->is_opcode
& 7] &= 0xffff0000;
1105 frame
->f_regs
[insn
->is_opcode
& 7] |= (u_int32_t
)count
;
1106 } else { /* got a signal */
1111 case 070: /* ftrapcc or fscc */
1113 if ((insn
->is_opcode
& 07) >= 2) {
1114 switch (insn
->is_opcode
& 07) {
1115 case 3: /* long opr */
1117 case 2: /* word opr */
1119 case 4: /* no opr */
1128 insn
->is_advance
= advance
;
1135 } /* if ((insn->is_opcode & 7) < 2), fall through to FScc */
1138 insn
->is_advance
= 4;
1139 insn
->is_datasize
= 1; /* always byte */
1140 sig
= fpu_decode_ea(frame
, insn
, &insn
->is_ea
, insn
->is_opcode
);
1144 if (branch
== -1 || branch
== 0) {
1146 sig
= fpu_store_ea(frame
, insn
, &insn
->is_ea
, (char *)&branch
);
1148 /* got an exception */
1157 * Type 2 or 3: fbcc (also fnop)
1158 * In this function, we know:
1159 * (opcode & 0x0180) == 0x0080
1162 fpu_emul_brcc(struct fpemu
*fe
, struct instruction
*insn
)
1168 * Get branch displacement.
1170 insn
->is_advance
= 4;
1171 displ
= insn
->is_word1
;
1173 if (insn
->is_opcode
& 0x40) {
1174 word2
= fusword((void *) (insn
->is_pc
+ insn
->is_advance
));
1177 printf("fpu_emul_brcc: fault reading word2\n");
1183 insn
->is_advance
+= 2;
1184 } else /* displacement is word sized */
1186 displ
|= 0xFFFF0000;
1188 /* XXX: If CC, insn->is_pc += displ */
1189 sig
= test_cc(fe
, insn
->is_opcode
);
1190 fe
->fe_fpframe
->fpf_fpsr
= fe
->fe_fpsr
;
1192 if (fe
->fe_fpsr
& fe
->fe_fpcr
& FPSR_EXCP
) {
1193 return SIGFPE
; /* caught an exception */
1196 /* branch does take place; 2 is the offset to the 1st disp word */
1197 insn
->is_advance
= displ
+ 2;
1198 /* XXX insn->is_nextpc = insn->is_pc + insn->is_advance; */
1200 return SIGILL
; /* got a signal */
1203 printf("fpu_emul_brcc: %s insn @ %x (%x+%x) (disp=%x)\n",
1204 (sig
== -1) ? "BRANCH to" : "NEXT",
1205 insn
->is_pc
+ insn
->is_advance
, insn
->is_pc
, insn
->is_advance
,