fpu_emulate.c revision 1.32 1 /* $NetBSD: fpu_emulate.c,v 1.32 2011/05/23 14:52:31 tsutsui Exp $ */
2
3 /*
4 * Copyright (c) 1995 Gordon W. Ross
5 * some portion Copyright (c) 1995 Ken Nakata
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
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
21 *
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.
32 */
33
34 /*
35 * mc68881 emulator
36 * XXX - Just a start at it for now...
37 */
38
39 #include <sys/cdefs.h>
40 __KERNEL_RCSID(0, "$NetBSD: fpu_emulate.c,v 1.32 2011/05/23 14:52:31 tsutsui Exp $");
41
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>
47
48 #if defined(DDB) && defined(DEBUG_FPE)
49 # include <m68k/db_machdep.h>
50 #endif
51
52 #include "fpu_emulate.h"
53
54 #define fpe_abort(tfp, ksi, signo, code) \
55 do { \
56 (ksi)->ksi_signo = (signo); \
57 (ksi)->ksi_code = (code); \
58 (ksi)->ksi_addr = (void *)(frame)->f_pc; \
59 return -1; \
60 } while (/* CONSTCOND */ 0)
61
62 static int fpu_emul_fmovmcr(struct fpemu *, struct instruction *);
63 static int fpu_emul_fmovm(struct fpemu *, struct instruction *);
64 static int fpu_emul_arith(struct fpemu *, struct instruction *);
65 static int fpu_emul_type1(struct fpemu *, struct instruction *);
66 static int fpu_emul_brcc(struct fpemu *, struct instruction *);
67 static int test_cc(struct fpemu *, int);
68 static struct fpn *fpu_cmp(struct fpemu *);
69
70 #if DEBUG_FPE
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)
75 #else
76 #define DUMP_INSN(insn)
77 #endif
78
79 /*
80 * Emulate a floating-point instruction.
81 * Return zero for success, else signal number.
82 * (Typically: zero, SIGFPE, SIGILL, SIGSEGV)
83 */
84 int
85 fpu_emulate(struct frame *frame, struct fpframe *fpf, ksiginfo_t *ksi)
86 {
87 static struct instruction insn;
88 static struct fpemu fe;
89 int word, optype, sig;
90
91
92 /* initialize insn.is_datasize to tell it is *not* initialized */
93 insn.is_datasize = -1;
94
95 fe.fe_frame = frame;
96 fe.fe_fpframe = fpf;
97 fe.fe_fpsr = fpf->fpf_fpsr;
98 fe.fe_fpcr = fpf->fpf_fpcr;
99
100 #if DEBUG_FPE
101 printf("ENTERING fpu_emulate: FPSR=%08x, FPCR=%08x\n",
102 fe.fe_fpsr, fe.fe_fpcr);
103 #endif
104
105 /* always set this (to avoid a warning) */
106 insn.is_pc = frame->f_pc;
107 insn.is_nextpc = 0;
108 if (frame->f_format == 4) {
109 /*
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.
115 *
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
119 * the PC in order to restore it later.
120 */
121 #if 0
122 insn.is_nextpc = frame->f_pc;
123 #endif
124 insn.is_pc = frame->f_fmt4.f_fslw;
125 frame->f_pc = insn.is_pc;
126 }
127
128 word = fusword((void *)(insn.is_pc));
129 if (word < 0) {
130 #ifdef DEBUG
131 printf("fpu_emulate: fault reading opcode\n");
132 #endif
133 fpe_abort(frame, ksi, SIGSEGV, SEGV_ACCERR);
134 }
135
136 if ((word & 0xf000) != 0xf000) {
137 #ifdef DEBUG
138 printf("fpu_emulate: not coproc. insn.: opcode=0x%x\n", word);
139 #endif
140 fpe_abort(frame, ksi, SIGILL, ILL_ILLOPC);
141 }
142
143 if ((word & 0x0E00) != 0x0200) {
144 #ifdef DEBUG
145 printf("fpu_emulate: bad coproc. id: opcode=0x%x\n", word);
146 #endif
147 fpe_abort(frame, ksi, SIGILL, ILL_ILLOPC);
148 }
149
150 insn.is_opcode = word;
151 optype = (word & 0x01C0);
152
153 word = fusword((void *)(insn.is_pc + 2));
154 if (word < 0) {
155 #ifdef DEBUG
156 printf("fpu_emulate: fault reading word1\n");
157 #endif
158 fpe_abort(frame, ksi, SIGSEGV, SEGV_ACCERR);
159 }
160 insn.is_word1 = word;
161 /* all FPU instructions are at least 4-byte long */
162 insn.is_advance = 4;
163
164 DUMP_INSN(&insn);
165
166 /*
167 * Which family (or type) of opcode is it?
168 * Tests ordered by likelihood (hopefully).
169 * Certainly, type 0 is the most common.
170 */
171 if (optype == 0x0000) {
172 /* type=0: generic */
173 if ((word & 0xc000) == 0xc000) {
174 #if DEBUG_FPE
175 printf("fpu_emulate: fmovm FPr\n");
176 #endif
177 sig = fpu_emul_fmovm(&fe, &insn);
178 } else if ((word & 0xc000) == 0x8000) {
179 #if DEBUG_FPE
180 printf("fpu_emulate: fmovm FPcr\n");
181 #endif
182 sig = fpu_emul_fmovmcr(&fe, &insn);
183 } else if ((word & 0xe000) == 0x6000) {
184 /* fstore = fmove FPn,mem */
185 #if DEBUG_FPE
186 printf("fpu_emulate: fmove to mem\n");
187 #endif
188 sig = fpu_emul_fstore(&fe, &insn);
189 } else if ((word & 0xfc00) == 0x5c00) {
190 /* fmovecr */
191 #if DEBUG_FPE
192 printf("fpu_emulate: fmovecr\n");
193 #endif
194 sig = fpu_emul_fmovecr(&fe, &insn);
195 } else if ((word & 0xa07f) == 0x26) {
196 /* fscale */
197 #if DEBUG_FPE
198 printf("fpu_emulate: fscale\n");
199 #endif
200 sig = fpu_emul_fscale(&fe, &insn);
201 } else {
202 #if DEBUG_FPE
203 printf("fpu_emulate: other type0\n");
204 #endif
205 /* all other type0 insns are arithmetic */
206 sig = fpu_emul_arith(&fe, &insn);
207 }
208 if (sig == 0) {
209 #if DEBUG_FPE
210 printf("fpu_emulate: type 0 returned 0\n");
211 #endif
212 sig = fpu_upd_excp(&fe);
213 }
214 } else if (optype == 0x0080 || optype == 0x00C0) {
215 /* type=2 or 3: fbcc, short or long disp. */
216 #if DEBUG_FPE
217 printf("fpu_emulate: fbcc %s\n",
218 (optype & 0x40) ? "long" : "short");
219 #endif
220 sig = fpu_emul_brcc(&fe, &insn);
221 } else if (optype == 0x0040) {
222 /* type=1: fdbcc, fscc, ftrapcc */
223 #if DEBUG_FPE
224 printf("fpu_emulate: type1\n");
225 #endif
226 sig = fpu_emul_type1(&fe, &insn);
227 } else {
228 /* type=4: fsave (privileged) */
229 /* type=5: frestore (privileged) */
230 /* type=6: reserved */
231 /* type=7: reserved */
232 #ifdef DEBUG
233 printf("fpu_emulate: bad opcode type: opcode=0x%x\n",
234 insn.is_opcode);
235 #endif
236 sig = SIGILL;
237 }
238
239 DUMP_INSN(&insn);
240
241 /*
242 * XXX it is not clear to me, if we should progress the PC always,
243 * for SIGFPE || 0, or only for 0; however, without SIGFPE, we
244 * don't pass the signalling regression tests. -is
245 */
246 if ((sig == 0) || (sig == SIGFPE))
247 frame->f_pc += insn.is_advance;
248 #if defined(DDB) && defined(DEBUG_FPE)
249 else {
250 printf("fpu_emulate: sig=%d, opcode=%x, word1=%x\n",
251 sig, insn.is_opcode, insn.is_word1);
252 kdb_trap(-1, (db_regs_t *)&frame);
253 }
254 #endif
255 #if 0 /* XXX something is wrong */
256 if (frame->f_format == 4) {
257 /* XXX Restore PC -- 68{EC,LC}040 only */
258 if (insn.is_nextpc)
259 frame->f_pc = insn.is_nextpc;
260 }
261 #endif
262
263 #if DEBUG_FPE
264 printf("EXITING fpu_emulate: w/FPSR=%08x, FPCR=%08x\n",
265 fe.fe_fpsr, fe.fe_fpcr);
266 #endif
267
268 if (sig)
269 fpe_abort(frame, ksi, sig, 0);
270 return sig;
271 }
272
273 /* update accrued exception bits and see if there's an FP exception */
274 int
275 fpu_upd_excp(struct fpemu *fe)
276 {
277 u_int fpsr;
278 u_int fpcr;
279
280 fpsr = fe->fe_fpsr;
281 fpcr = fe->fe_fpcr;
282 /*
283 * update fpsr accrued exception bits; each insn doesn't have to
284 * update this
285 */
286 if (fpsr & (FPSR_BSUN | FPSR_SNAN | FPSR_OPERR)) {
287 fpsr |= FPSR_AIOP;
288 }
289 if (fpsr & FPSR_OVFL) {
290 fpsr |= FPSR_AOVFL;
291 }
292 if ((fpsr & FPSR_UNFL) && (fpsr & FPSR_INEX2)) {
293 fpsr |= FPSR_AUNFL;
294 }
295 if (fpsr & FPSR_DZ) {
296 fpsr |= FPSR_ADZ;
297 }
298 if (fpsr & (FPSR_INEX1 | FPSR_INEX2 | FPSR_OVFL)) {
299 fpsr |= FPSR_AINEX;
300 }
301
302 fe->fe_fpframe->fpf_fpsr = fe->fe_fpsr = fpsr;
303
304 return (fpsr & fpcr & FPSR_EXCP) ? SIGFPE : 0;
305 }
306
307 /* update fpsr according to fp (= result of an fp op) */
308 u_int
309 fpu_upd_fpsr(struct fpemu *fe, struct fpn *fp)
310 {
311 u_int fpsr;
312
313 #if DEBUG_FPE
314 printf("fpu_upd_fpsr: previous fpsr=%08x\n", fe->fe_fpsr);
315 #endif
316 /* clear all condition code */
317 fpsr = fe->fe_fpsr & ~FPSR_CCB;
318
319 #if DEBUG_FPE
320 printf("fpu_upd_fpsr: result is a ");
321 #endif
322 if (fp->fp_sign) {
323 #if DEBUG_FPE
324 printf("negative ");
325 #endif
326 fpsr |= FPSR_NEG;
327 #if DEBUG_FPE
328 } else {
329 printf("positive ");
330 #endif
331 }
332
333 switch (fp->fp_class) {
334 case FPC_SNAN:
335 #if DEBUG_FPE
336 printf("signaling NAN\n");
337 #endif
338 fpsr |= (FPSR_NAN | FPSR_SNAN);
339 break;
340 case FPC_QNAN:
341 #if DEBUG_FPE
342 printf("quiet NAN\n");
343 #endif
344 fpsr |= FPSR_NAN;
345 break;
346 case FPC_ZERO:
347 #if DEBUG_FPE
348 printf("Zero\n");
349 #endif
350 fpsr |= FPSR_ZERO;
351 break;
352 case FPC_INF:
353 #if DEBUG_FPE
354 printf("Inf\n");
355 #endif
356 fpsr |= FPSR_INF;
357 break;
358 default:
359 #if DEBUG_FPE
360 printf("Number\n");
361 #endif
362 /* anything else is treated as if it is a number */
363 break;
364 }
365
366 fe->fe_fpsr = fe->fe_fpframe->fpf_fpsr = fpsr;
367
368 #if DEBUG_FPE
369 printf("fpu_upd_fpsr: new fpsr=%08x\n", fe->fe_fpframe->fpf_fpsr);
370 #endif
371
372 return fpsr;
373 }
374
375 static int
376 fpu_emul_fmovmcr(struct fpemu *fe, struct instruction *insn)
377 {
378 struct frame *frame = fe->fe_frame;
379 struct fpframe *fpf = fe->fe_fpframe;
380 int sig;
381 int reglist;
382 int fpu_to_mem;
383
384 /* move to/from control registers */
385 reglist = (insn->is_word1 & 0x1c00) >> 10;
386 /* Bit 13 selects direction (FPU to/from Mem) */
387 fpu_to_mem = insn->is_word1 & 0x2000;
388
389 insn->is_datasize = 4;
390 insn->is_advance = 4;
391 sig = fpu_decode_ea(frame, insn, &insn->is_ea, insn->is_opcode);
392 if (sig)
393 return sig;
394
395 if (reglist != 1 && reglist != 2 && reglist != 4 &&
396 (insn->is_ea.ea_flags & EA_DIRECT)) {
397 /* attempted to copy more than one FPcr to CPU regs */
398 #ifdef DEBUG
399 printf("fpu_emul_fmovmcr: tried to copy too many FPcr\n");
400 #endif
401 return SIGILL;
402 }
403
404 if (reglist & 4) {
405 /* fpcr */
406 if ((insn->is_ea.ea_flags & EA_DIRECT) &&
407 insn->is_ea.ea_regnum >= 8 /* address reg */) {
408 /* attempted to copy FPCR to An */
409 #ifdef DEBUG
410 printf("fpu_emul_fmovmcr: tried to copy FPCR from/to "
411 "A%d\n", insn->is_ea.ea_regnum & 7);
412 #endif
413 return SIGILL;
414 }
415 if (fpu_to_mem) {
416 sig = fpu_store_ea(frame, insn, &insn->is_ea,
417 (char *)&fpf->fpf_fpcr);
418 } else {
419 sig = fpu_load_ea(frame, insn, &insn->is_ea,
420 (char *)&fpf->fpf_fpcr);
421 }
422 }
423 if (sig)
424 return sig;
425
426 if (reglist & 2) {
427 /* fpsr */
428 if ((insn->is_ea.ea_flags & EA_DIRECT) &&
429 insn->is_ea.ea_regnum >= 8 /* address reg */) {
430 /* attempted to copy FPSR to An */
431 #ifdef DEBUG
432 printf("fpu_emul_fmovmcr: tried to copy FPSR from/to "
433 "A%d\n", insn->is_ea.ea_regnum & 7);
434 #endif
435 return SIGILL;
436 }
437 if (fpu_to_mem) {
438 sig = fpu_store_ea(frame, insn, &insn->is_ea,
439 (char *)&fpf->fpf_fpsr);
440 } else {
441 sig = fpu_load_ea(frame, insn, &insn->is_ea,
442 (char *)&fpf->fpf_fpsr);
443 }
444 }
445 if (sig)
446 return sig;
447
448 if (reglist & 1) {
449 /* fpiar - can be moved to/from An */
450 if (fpu_to_mem) {
451 sig = fpu_store_ea(frame, insn, &insn->is_ea,
452 (char *)&fpf->fpf_fpiar);
453 } else {
454 sig = fpu_load_ea(frame, insn, &insn->is_ea,
455 (char *)&fpf->fpf_fpiar);
456 }
457 }
458 return sig;
459 }
460
461 /*
462 * type 0: fmovem
463 * Separated out of fpu_emul_type0 for efficiency.
464 * In this function, we know:
465 * (opcode & 0x01C0) == 0
466 * (word1 & 0x8000) == 0x8000
467 *
468 * No conversion or rounding is done by this instruction,
469 * and the FPSR is not affected.
470 */
471 static int
472 fpu_emul_fmovm(struct fpemu *fe, struct instruction *insn)
473 {
474 struct frame *frame = fe->fe_frame;
475 struct fpframe *fpf = fe->fe_fpframe;
476 int word1, sig;
477 int reglist, regmask, regnum;
478 int fpu_to_mem, order;
479 int w1_post_incr;
480 int *fpregs;
481
482 insn->is_advance = 4;
483 insn->is_datasize = 12;
484 word1 = insn->is_word1;
485
486 /* Bit 13 selects direction (FPU to/from Mem) */
487 fpu_to_mem = word1 & 0x2000;
488
489 /*
490 * Bits 12,11 select register list mode:
491 * 0,0: Static reg list, pre-decr.
492 * 0,1: Dynamic reg list, pre-decr.
493 * 1,0: Static reg list, post-incr.
494 * 1,1: Dynamic reg list, post-incr
495 */
496 w1_post_incr = word1 & 0x1000;
497 if (word1 & 0x0800) {
498 /* dynamic reg list */
499 reglist = frame->f_regs[(word1 & 0x70) >> 4];
500 } else {
501 reglist = word1;
502 }
503 reglist &= 0xFF;
504
505 /* Get effective address. (modreg=opcode&077) */
506 sig = fpu_decode_ea(frame, insn, &insn->is_ea, insn->is_opcode);
507 if (sig)
508 return sig;
509
510 /* Get address of soft coprocessor regs. */
511 fpregs = &fpf->fpf_regs[0];
512
513 if (insn->is_ea.ea_flags & EA_PREDECR) {
514 regnum = 7;
515 order = -1;
516 } else {
517 regnum = 0;
518 order = 1;
519 }
520
521 regmask = 0x80;
522 while ((0 <= regnum) && (regnum < 8)) {
523 if (regmask & reglist) {
524 if (fpu_to_mem) {
525 sig = fpu_store_ea(frame, insn, &insn->is_ea,
526 (char *)&fpregs[regnum * 3]);
527 #if DEBUG_FPE
528 printf("fpu_emul_fmovm: FP%d (%08x,%08x,%08x) "
529 "saved\n", regnum,
530 fpregs[regnum * 3],
531 fpregs[regnum * 3 + 1],
532 fpregs[regnum * 3 + 2]);
533 #endif
534 } else { /* mem to fpu */
535 sig = fpu_load_ea(frame, insn, &insn->is_ea,
536 (char *)&fpregs[regnum * 3]);
537 #if DEBUG_FPE
538 printf("fpu_emul_fmovm: FP%d (%08x,%08x,%08x) "
539 "loaded\n", regnum,
540 fpregs[regnum * 3],
541 fpregs[regnum * 3 + 1],
542 fpregs[regnum * 3 + 2]);
543 #endif
544 }
545 if (sig)
546 break;
547 }
548 regnum += order;
549 regmask >>= 1;
550 }
551
552 return sig;
553 }
554
555 static struct fpn *
556 fpu_cmp(struct fpemu *fe)
557 {
558 struct fpn *x = &fe->fe_f1, *y = &fe->fe_f2;
559
560 /* take care of special cases */
561 if (x->fp_class < 0 || y->fp_class < 0) {
562 /* if either of two is a SNAN, result is SNAN */
563 x->fp_class =
564 (y->fp_class < x->fp_class) ? y->fp_class : x->fp_class;
565 } else if (x->fp_class == FPC_INF) {
566 if (y->fp_class == FPC_INF) {
567 /* both infinities */
568 if (x->fp_sign == y->fp_sign) {
569 /* return a signed zero */
570 x->fp_class = FPC_ZERO;
571 } else {
572 /* return a faked number w/x's sign */
573 x->fp_class = FPC_NUM;
574 x->fp_exp = 16383;
575 x->fp_mant[0] = FP_1;
576 }
577 } else {
578 /* y is a number */
579 /* return a forged number w/x's sign */
580 x->fp_class = FPC_NUM;
581 x->fp_exp = 16383;
582 x->fp_mant[0] = FP_1;
583 }
584 } else if (y->fp_class == FPC_INF) {
585 /* x is a Num but y is an Inf */
586 /* return a forged number w/y's sign inverted */
587 x->fp_class = FPC_NUM;
588 x->fp_sign = !y->fp_sign;
589 x->fp_exp = 16383;
590 x->fp_mant[0] = FP_1;
591 } else {
592 /*
593 * x and y are both numbers or zeros,
594 * or pair of a number and a zero
595 */
596 y->fp_sign = !y->fp_sign;
597 x = fpu_add(fe); /* (x - y) */
598 /*
599 * FCMP does not set Inf bit in CC, so return a forged number
600 * (value doesn't matter) if Inf is the result of fsub.
601 */
602 if (x->fp_class == FPC_INF) {
603 x->fp_class = FPC_NUM;
604 x->fp_exp = 16383;
605 x->fp_mant[0] = FP_1;
606 }
607 }
608 return x;
609 }
610
611 /*
612 * arithmetic oprations
613 */
614 static int
615 fpu_emul_arith(struct fpemu *fe, struct instruction *insn)
616 {
617 struct frame *frame = fe->fe_frame;
618 u_int *fpregs = &(fe->fe_fpframe->fpf_regs[0]);
619 struct fpn *res;
620 int word1, sig = 0;
621 int regnum, format;
622 int discard_result = 0;
623 u_int buf[3];
624 #if DEBUG_FPE
625 int flags;
626 char regname;
627 #endif
628
629 fe->fe_fpsr &= ~FPSR_EXCP;
630
631 DUMP_INSN(insn);
632
633 #if DEBUG_FPE
634 printf("fpu_emul_arith: FPSR = %08x, FPCR = %08x\n",
635 fe->fe_fpsr, fe->fe_fpcr);
636 #endif
637
638 word1 = insn->is_word1;
639 format = (word1 >> 10) & 7;
640 regnum = (word1 >> 7) & 7;
641
642 /* fetch a source operand : may not be used */
643 #if DEBUG_FPE
644 printf("fpu_emul_arith: dst/src FP%d=%08x,%08x,%08x\n",
645 regnum, fpregs[regnum * 3], fpregs[regnum * 3 + 1],
646 fpregs[regnum * 3 + 2]);
647 #endif
648
649 fpu_explode(fe, &fe->fe_f1, FTYPE_EXT, &fpregs[regnum * 3]);
650
651 DUMP_INSN(insn);
652
653 /* get the other operand which is always the source */
654 if ((word1 & 0x4000) == 0) {
655 #if DEBUG_FPE
656 printf("fpu_emul_arith: FP%d op FP%d => FP%d\n",
657 format, regnum, regnum);
658 printf("fpu_emul_arith: src opr FP%d=%08x,%08x,%08x\n",
659 format, fpregs[format * 3], fpregs[format * 3 + 1],
660 fpregs[format * 3 + 2]);
661 #endif
662 fpu_explode(fe, &fe->fe_f2, FTYPE_EXT, &fpregs[format * 3]);
663 } else {
664 /* the operand is in memory */
665 if (format == FTYPE_DBL) {
666 insn->is_datasize = 8;
667 } else if (format == FTYPE_SNG || format == FTYPE_LNG) {
668 insn->is_datasize = 4;
669 } else if (format == FTYPE_WRD) {
670 insn->is_datasize = 2;
671 } else if (format == FTYPE_BYT) {
672 insn->is_datasize = 1;
673 } else if (format == FTYPE_EXT) {
674 insn->is_datasize = 12;
675 } else {
676 /* invalid or unsupported operand format */
677 sig = SIGFPE;
678 return sig;
679 }
680
681 /* Get effective address. (modreg=opcode&077) */
682 sig = fpu_decode_ea(frame, insn, &insn->is_ea, insn->is_opcode);
683 if (sig) {
684 #if DEBUG_FPE
685 printf("fpu_emul_arith: error in fpu_decode_ea\n");
686 #endif
687 return sig;
688 }
689
690 DUMP_INSN(insn);
691
692 #if DEBUG_FPE
693 printf("fpu_emul_arith: addr mode = ");
694 flags = insn->is_ea.ea_flags;
695 regname = (insn->is_ea.ea_regnum & 8) ? 'a' : 'd';
696
697 if (flags & EA_DIRECT) {
698 printf("%c%d\n",
699 regname, insn->is_ea.ea_regnum & 7);
700 } else if (flags & EA_PC_REL) {
701 if (flags & EA_OFFSET) {
702 printf("pc@(%d)\n", insn->is_ea.ea_offset);
703 } else if (flags & EA_INDEXED) {
704 printf("pc@(...)\n");
705 }
706 } else if (flags & EA_PREDECR) {
707 printf("%c%d@-\n",
708 regname, insn->is_ea.ea_regnum & 7);
709 } else if (flags & EA_POSTINCR) {
710 printf("%c%d@+\n", regname, insn->is_ea.ea_regnum & 7);
711 } else if (flags & EA_OFFSET) {
712 printf("%c%d@(%d)\n", regname,
713 insn->is_ea.ea_regnum & 7,
714 insn->is_ea.ea_offset);
715 } else if (flags & EA_INDEXED) {
716 printf("%c%d@(...)\n", regname,
717 insn->is_ea.ea_regnum & 7);
718 } else if (flags & EA_ABS) {
719 printf("0x%08x\n", insn->is_ea.ea_absaddr);
720 } else if (flags & EA_IMMED) {
721 printf("#0x%08x,%08x,%08x\n", insn->is_ea.ea_immed[0],
722 insn->is_ea.ea_immed[1], insn->is_ea.ea_immed[2]);
723 } else {
724 printf("%c%d@\n", regname, insn->is_ea.ea_regnum & 7);
725 }
726 #endif /* DEBUG_FPE */
727
728 fpu_load_ea(frame, insn, &insn->is_ea, (char*)buf);
729 if (format == FTYPE_WRD) {
730 /* sign-extend */
731 buf[0] &= 0xffff;
732 if (buf[0] & 0x8000)
733 buf[0] |= 0xffff0000;
734 format = FTYPE_LNG;
735 } else if (format == FTYPE_BYT) {
736 /* sign-extend */
737 buf[0] &= 0xff;
738 if (buf[0] & 0x80)
739 buf[0] |= 0xffffff00;
740 format = FTYPE_LNG;
741 }
742 #if DEBUG_FPE
743 printf("fpu_emul_arith: src = %08x %08x %08x, siz = %d\n",
744 buf[0], buf[1], buf[2], insn->is_datasize);
745 #endif
746 fpu_explode(fe, &fe->fe_f2, format, buf);
747 }
748
749 DUMP_INSN(insn);
750
751 /*
752 * An arithmetic instruction emulate function has a prototype of
753 * struct fpn *fpu_op(struct fpemu *);
754 *
755 * 1) If the instruction is monadic, then fpu_op() must use
756 * fe->fe_f2 as its operand, and return a pointer to the
757 * result.
758 *
759 * 2) If the instruction is diadic, then fpu_op() must use
760 * fe->fe_f1 and fe->fe_f2 as its two operands, and return a
761 * pointer to the result.
762 *
763 */
764 res = NULL;
765 switch (word1 & 0x7f) {
766 case 0x00: /* fmove */
767 res = &fe->fe_f2;
768 break;
769
770 case 0x01: /* fint */
771 res = fpu_int(fe);
772 break;
773
774 case 0x02: /* fsinh */
775 res = fpu_sinh(fe);
776 break;
777
778 case 0x03: /* fintrz */
779 res = fpu_intrz(fe);
780 break;
781
782 case 0x04: /* fsqrt */
783 res = fpu_sqrt(fe);
784 break;
785
786 case 0x06: /* flognp1 */
787 res = fpu_lognp1(fe);
788 break;
789
790 case 0x08: /* fetoxm1 */
791 res = fpu_etoxm1(fe);
792 break;
793
794 case 0x09: /* ftanh */
795 res = fpu_tanh(fe);
796 break;
797
798 case 0x0A: /* fatan */
799 res = fpu_atan(fe);
800 break;
801
802 case 0x0C: /* fasin */
803 res = fpu_asin(fe);
804 break;
805
806 case 0x0D: /* fatanh */
807 res = fpu_atanh(fe);
808 break;
809
810 case 0x0E: /* fsin */
811 res = fpu_sin(fe);
812 break;
813
814 case 0x0F: /* ftan */
815 res = fpu_tan(fe);
816 break;
817
818 case 0x10: /* fetox */
819 res = fpu_etox(fe);
820 break;
821
822 case 0x11: /* ftwotox */
823 res = fpu_twotox(fe);
824 break;
825
826 case 0x12: /* ftentox */
827 res = fpu_tentox(fe);
828 break;
829
830 case 0x14: /* flogn */
831 res = fpu_logn(fe);
832 break;
833
834 case 0x15: /* flog10 */
835 res = fpu_log10(fe);
836 break;
837
838 case 0x16: /* flog2 */
839 res = fpu_log2(fe);
840 break;
841
842 case 0x18: /* fabs */
843 fe->fe_f2.fp_sign = 0;
844 res = &fe->fe_f2;
845 break;
846
847 case 0x19: /* fcosh */
848 res = fpu_cosh(fe);
849 break;
850
851 case 0x1A: /* fneg */
852 fe->fe_f2.fp_sign = !fe->fe_f2.fp_sign;
853 res = &fe->fe_f2;
854 break;
855
856 case 0x1C: /* facos */
857 res = fpu_acos(fe);
858 break;
859
860 case 0x1D: /* fcos */
861 res = fpu_cos(fe);
862 break;
863
864 case 0x1E: /* fgetexp */
865 res = fpu_getexp(fe);
866 break;
867
868 case 0x1F: /* fgetman */
869 res = fpu_getman(fe);
870 break;
871
872 case 0x20: /* fdiv */
873 case 0x24: /* fsgldiv: cheating - better than nothing */
874 res = fpu_div(fe);
875 break;
876
877 case 0x21: /* fmod */
878 res = fpu_mod(fe);
879 break;
880
881 case 0x28: /* fsub */
882 fe->fe_f2.fp_sign = !fe->fe_f2.fp_sign; /* f2 = -f2 */
883 /* FALLTHROUGH */
884 case 0x22: /* fadd */
885 res = fpu_add(fe);
886 break;
887
888 case 0x23: /* fmul */
889 case 0x27: /* fsglmul: cheating - better than nothing */
890 res = fpu_mul(fe);
891 break;
892
893 case 0x25: /* frem */
894 res = fpu_rem(fe);
895 break;
896
897 case 0x26:
898 /* fscale is handled by a separate function */
899 break;
900
901 case 0x30:
902 case 0x31:
903 case 0x32:
904 case 0x33:
905 case 0x34:
906 case 0x35:
907 case 0x36:
908 case 0x37: /* fsincos */
909 res = fpu_sincos(fe, word1 & 7);
910 break;
911
912 case 0x38: /* fcmp */
913 res = fpu_cmp(fe);
914 discard_result = 1;
915 break;
916
917 case 0x3A: /* ftst */
918 res = &fe->fe_f2;
919 discard_result = 1;
920 break;
921
922 default: /* possibly 040/060 instructions */
923 #ifdef DEBUG
924 printf("fpu_emul_arith: bad opcode=0x%x, word1=0x%x\n",
925 insn->is_opcode, insn->is_word1);
926 #endif
927 sig = SIGILL;
928 }
929
930 /* for sanity */
931 if (res == NULL)
932 sig = SIGILL;
933
934 if (sig == 0) {
935 if (!discard_result)
936 fpu_implode(fe, res, FTYPE_EXT, &fpregs[regnum * 3]);
937
938 /* update fpsr according to the result of operation */
939 fpu_upd_fpsr(fe, res);
940 #if DEBUG_FPE
941 if (!discard_result) {
942 printf("fpu_emul_arith: %08x,%08x,%08x stored in "
943 "FP%d\n",
944 fpregs[regnum * 3],
945 fpregs[regnum * 3 + 1],
946 fpregs[regnum * 3 + 2],
947 regnum);
948 } else {
949 static const char *class_name[] =
950 { "SNAN", "QNAN", "ZERO", "NUM", "INF" };
951 printf("fpu_emul_arith: result(%s,%c,%d,%08x,%08x,%08x)"
952 " discarded\n",
953 class_name[res->fp_class + 2],
954 res->fp_sign ? '-' : '+', res->fp_exp,
955 res->fp_mant[0], res->fp_mant[1],
956 res->fp_mant[2]);
957 }
958 #endif
959 }
960 #if DEBUG_FPE
961 else {
962 printf("fpu_emul_arith: received signal %d\n", sig);
963 }
964 #endif
965
966 #if DEBUG_FPE
967 printf("fpu_emul_arith: FPSR = %08x, FPCR = %08x\n",
968 fe->fe_fpsr, fe->fe_fpcr);
969 #endif
970
971 DUMP_INSN(insn);
972
973 return sig;
974 }
975
976 /*
977 * test condition code according to the predicate in the opcode.
978 * returns -1 when the predicate evaluates to true, 0 when false.
979 * signal numbers are returned when an error is detected.
980 */
981 static int
982 test_cc(struct fpemu *fe, int pred)
983 {
984 int result, sig_bsun, invert;
985 int fpsr;
986
987 fpsr = fe->fe_fpsr;
988 invert = 0;
989 fpsr &= ~FPSR_EXCP; /* clear all exceptions */
990 #if DEBUG_FPE
991 printf("test_cc: fpsr=0x%08x\n", fpsr);
992 #endif
993 pred &= 0x3f; /* lowest 6 bits */
994
995 #if DEBUG_FPE
996 printf("test_cc: ");
997 #endif
998
999 if (pred >= 0x20) {
1000 return SIGILL;
1001 } else if (pred & 0x10) {
1002 /* IEEE nonaware tests */
1003 sig_bsun = 1;
1004 pred &= 0x0f; /* lower 4 bits */
1005 } else {
1006 /* IEEE aware tests */
1007 #if DEBUG_FPE
1008 printf("IEEE ");
1009 #endif
1010 sig_bsun = 0;
1011 }
1012
1013 if (pred & 0x08) {
1014 #if DEBUG_FPE
1015 printf("Not ");
1016 #endif
1017 /* predicate is "NOT ..." */
1018 pred ^= 0xf; /* invert */
1019 invert = -1;
1020 }
1021 switch (pred) {
1022 case 0: /* (Signaling) False */
1023 #if DEBUG_FPE
1024 printf("False");
1025 #endif
1026 result = 0;
1027 break;
1028 case 1: /* (Signaling) Equal */
1029 #if DEBUG_FPE
1030 printf("Equal");
1031 #endif
1032 result = -((fpsr & FPSR_ZERO) == FPSR_ZERO);
1033 break;
1034 case 2: /* Greater Than */
1035 #if DEBUG_FPE
1036 printf("GT");
1037 #endif
1038 result = -((fpsr & (FPSR_NAN|FPSR_ZERO|FPSR_NEG)) == 0);
1039 break;
1040 case 3: /* Greater or Equal */
1041 #if DEBUG_FPE
1042 printf("GE");
1043 #endif
1044 result = -((fpsr & FPSR_ZERO) ||
1045 (fpsr & (FPSR_NAN|FPSR_NEG)) == 0);
1046 break;
1047 case 4: /* Less Than */
1048 #if DEBUG_FPE
1049 printf("LT");
1050 #endif
1051 result = -((fpsr & (FPSR_NAN|FPSR_ZERO|FPSR_NEG)) == FPSR_NEG);
1052 break;
1053 case 5: /* Less or Equal */
1054 #if DEBUG_FPE
1055 printf("LE");
1056 #endif
1057 result = -((fpsr & FPSR_ZERO) ||
1058 ((fpsr & (FPSR_NAN|FPSR_NEG)) == FPSR_NEG));
1059 break;
1060 case 6: /* Greater or Less than */
1061 #if DEBUG_FPE
1062 printf("GLT");
1063 #endif
1064 result = -((fpsr & (FPSR_NAN|FPSR_ZERO)) == 0);
1065 break;
1066 case 7: /* Greater, Less or Equal */
1067 #if DEBUG_FPE
1068 printf("GLE");
1069 #endif
1070 result = -((fpsr & FPSR_NAN) == 0);
1071 break;
1072 default:
1073 /* invalid predicate */
1074 return SIGILL;
1075 }
1076 /* if the predicate is "NOT ...", then invert the result */
1077 result ^= invert;
1078 #if DEBUG_FPE
1079 printf("=> %s (%d)\n", result ? "true" : "false", result);
1080 #endif
1081 /* if it's an IEEE unaware test and NAN is set, BSUN is set */
1082 if (sig_bsun && (fpsr & FPSR_NAN)) {
1083 fpsr |= FPSR_BSUN;
1084 }
1085
1086 /* put fpsr back */
1087 fe->fe_fpframe->fpf_fpsr = fe->fe_fpsr = fpsr;
1088
1089 return result;
1090 }
1091
1092 /*
1093 * type 1: fdbcc, fscc, ftrapcc
1094 * In this function, we know:
1095 * (opcode & 0x01C0) == 0x0040
1096 */
1097 static int
1098 fpu_emul_type1(struct fpemu *fe, struct instruction *insn)
1099 {
1100 struct frame *frame = fe->fe_frame;
1101 int advance, sig, branch, displ;
1102
1103 branch = test_cc(fe, insn->is_word1);
1104 fe->fe_fpframe->fpf_fpsr = fe->fe_fpsr;
1105
1106 insn->is_advance = 4;
1107 sig = 0;
1108
1109 switch (insn->is_opcode & 070) {
1110 case 010: /* fdbcc */
1111 if (branch == -1) {
1112 /* advance */
1113 insn->is_advance = 6;
1114 } else if (!branch) {
1115 /* decrement Dn and if (Dn != -1) branch */
1116 uint16_t count = frame->f_regs[insn->is_opcode & 7];
1117
1118 if (count-- != 0) {
1119 displ = fusword((void *)(insn->is_pc +
1120 insn->is_advance));
1121 if (displ < 0) {
1122 #ifdef DEBUG
1123 printf("fpu_emul_type1: "
1124 "fault reading displacement\n");
1125 #endif
1126 return SIGSEGV;
1127 }
1128 /* sign-extend the displacement */
1129 displ &= 0xffff;
1130 if (displ & 0x8000) {
1131 displ |= 0xffff0000;
1132 }
1133 insn->is_advance += displ;
1134 #if 0 /* XXX */
1135 insn->is_nextpc = insn->is_pc +
1136 insn->is_advance;
1137 #endif
1138 } else {
1139 insn->is_advance = 6;
1140 }
1141 /* write it back */
1142 frame->f_regs[insn->is_opcode & 7] &= 0xffff0000;
1143 frame->f_regs[insn->is_opcode & 7] |= (uint32_t)count;
1144 } else { /* got a signal */
1145 sig = SIGFPE;
1146 }
1147 break;
1148
1149 case 070: /* ftrapcc or fscc */
1150 advance = 4;
1151 if ((insn->is_opcode & 07) >= 2) {
1152 switch (insn->is_opcode & 07) {
1153 case 3: /* long opr */
1154 advance += 2;
1155 case 2: /* word opr */
1156 advance += 2;
1157 case 4: /* no opr */
1158 break;
1159 default:
1160 return SIGILL;
1161 break;
1162 }
1163
1164 if (branch == 0) {
1165 /* no trap */
1166 insn->is_advance = advance;
1167 sig = 0;
1168 } else {
1169 /* trap */
1170 sig = SIGFPE;
1171 }
1172 break;
1173 }
1174 /* FALLTHROUGH */
1175
1176 default: /* fscc */
1177 insn->is_advance = 4;
1178 insn->is_datasize = 1; /* always byte */
1179 sig = fpu_decode_ea(frame, insn, &insn->is_ea, insn->is_opcode);
1180 if (sig) {
1181 break;
1182 }
1183 if (branch == -1 || branch == 0) {
1184 /* set result */
1185 sig = fpu_store_ea(frame, insn, &insn->is_ea,
1186 (char *)&branch);
1187 } else {
1188 /* got an exception */
1189 sig = branch;
1190 }
1191 break;
1192 }
1193 return sig;
1194 }
1195
1196 /*
1197 * Type 2 or 3: fbcc (also fnop)
1198 * In this function, we know:
1199 * (opcode & 0x0180) == 0x0080
1200 */
1201 static int
1202 fpu_emul_brcc(struct fpemu *fe, struct instruction *insn)
1203 {
1204 int displ, word2;
1205 int sig;
1206
1207 /*
1208 * Get branch displacement.
1209 */
1210 insn->is_advance = 4;
1211 displ = insn->is_word1;
1212
1213 if (insn->is_opcode & 0x40) {
1214 word2 = fusword((void *)(insn->is_pc + insn->is_advance));
1215 if (word2 < 0) {
1216 #ifdef DEBUG
1217 printf("fpu_emul_brcc: fault reading word2\n");
1218 #endif
1219 return SIGSEGV;
1220 }
1221 displ <<= 16;
1222 displ |= word2;
1223 insn->is_advance += 2;
1224 } else {
1225 /* displacement is word sized */
1226 if (displ & 0x8000)
1227 displ |= 0xFFFF0000;
1228 }
1229
1230 /* XXX: If CC, insn->is_pc += displ */
1231 sig = test_cc(fe, insn->is_opcode);
1232 fe->fe_fpframe->fpf_fpsr = fe->fe_fpsr;
1233
1234 if (fe->fe_fpsr & fe->fe_fpcr & FPSR_EXCP) {
1235 return SIGFPE; /* caught an exception */
1236 }
1237 if (sig == -1) {
1238 /*
1239 * branch does take place; 2 is the offset to the 1st disp word
1240 */
1241 insn->is_advance = displ + 2;
1242 #if 0 /* XXX */
1243 insn->is_nextpc = insn->is_pc + insn->is_advance;
1244 #endif
1245 } else if (sig)
1246 return SIGILL; /* got a signal */
1247 #if DEBUG_FPE
1248 printf("fpu_emul_brcc: %s insn @ %x (%x+%x) (disp=%x)\n",
1249 (sig == -1) ? "BRANCH to" : "NEXT",
1250 insn->is_pc + insn->is_advance, insn->is_pc, insn->is_advance,
1251 displ);
1252 #endif
1253 return 0;
1254 }
1255