fpu_emulate.c revision 1.25 1 /* $NetBSD: fpu_emulate.c,v 1.25 2003/09/22 14:18:34 cl 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.25 2003/09/22 14:18:34 cl Exp $");
41
42 #include <sys/types.h>
43 #include <sys/signal.h>
44 #include <sys/systm.h>
45 #include <machine/frame.h>
46
47 #if defined(DDB) && defined(DEBUG_FPE)
48 # include <m68k/db_machdep.h>
49 #endif
50
51 #include "fpu_emulate.h"
52
53 #define fpe_abort(tfp, ksi, signo, code) \
54 do { \
55 (ksi)->ksi_signo = (signo); \
56 (ksi)->ksi_code = (code); \
57 (ksi)->ksi_addr = (void *)(frame)->f_pc; \
58 return -1; \
59 } while (/*CONSTCOND*/0)
60
61 static int fpu_emul_fmovmcr __P((struct fpemu *fe, struct instruction *insn));
62 static int fpu_emul_fmovm __P((struct fpemu *fe, struct instruction *insn));
63 static int fpu_emul_arith __P((struct fpemu *fe, struct instruction *insn));
64 static int fpu_emul_type1 __P((struct fpemu *fe, struct instruction *insn));
65 static int fpu_emul_brcc __P((struct fpemu *fe, struct instruction *insn));
66 static int test_cc __P((struct fpemu *fe, int pred));
67 static struct fpn *fpu_cmp __P((struct fpemu *fe));
68
69 #if DEBUG_FPE
70 # define DUMP_INSN(insn) \
71 printf("fpu_emulate: insn={adv=%d,siz=%d,op=%04x,w1=%04x}\n", \
72 (insn)->is_advance, (insn)->is_datasize, \
73 (insn)->is_opcode, (insn)->is_word1)
74 #else
75 # define DUMP_INSN(insn)
76 #endif
77
78 /*
79 * Emulate a floating-point instruction.
80 * Return zero for success, else signal number.
81 * (Typically: zero, SIGFPE, SIGILL, SIGSEGV)
82 */
83 int
84 fpu_emulate(frame, fpf, ksi)
85 struct frame *frame;
86 struct fpframe *fpf;
87 ksiginfo_t *ksi;
88 {
89 static struct instruction insn;
90 static struct fpemu fe;
91 int word, optype, sig;
92
93
94 /* initialize insn.is_datasize to tell it is *not* initialized */
95 insn.is_datasize = -1;
96
97 fe.fe_frame = frame;
98 fe.fe_fpframe = fpf;
99 fe.fe_fpsr = fpf->fpf_fpsr;
100 fe.fe_fpcr = fpf->fpf_fpcr;
101
102 #if DEBUG_FPE
103 printf("ENTERING fpu_emulate: FPSR=%08x, FPCR=%08x\n",
104 fe.fe_fpsr, fe.fe_fpcr);
105 #endif
106
107 /* always set this (to avoid a warning) */
108 insn.is_pc = frame->f_pc;
109 insn.is_nextpc = 0;
110 if (frame->f_format == 4) {
111 /*
112 * A format 4 is generated by the 68{EC,LC}040. The PC is
113 * already set to the instruction following the faulting
114 * instruction. We need to calculate that, anyway. The
115 * fslw is the PC of the faulted instruction, which is what
116 * we expect to be in f_pc.
117 *
118 * XXX - This is a hack; it assumes we at least know the
119 * sizes of all instructions we run across.
120 * XXX TODO: This may not be true, so we might want to save the PC
121 * in order to restore it later.
122 */
123 /* insn.is_nextpc = frame->f_pc; */
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", insn.is_opcode);
234 #endif
235 sig = SIGILL;
236 }
237
238 DUMP_INSN(&insn);
239
240 /*
241 * XXX it is not clear to me, if we should progress the PC always,
242 * for SIGFPE || 0, or only for 0; however, without SIGFPE, we
243 * don't pass the signalling regression tests. -is
244 */
245 if ((sig == 0) || (sig == SIGFPE))
246 frame->f_pc += insn.is_advance;
247 #if defined(DDB) && defined(DEBUG_FPE)
248 else {
249 printf("fpu_emulate: sig=%d, opcode=%x, word1=%x\n",
250 sig, insn.is_opcode, insn.is_word1);
251 kdb_trap(-1, (db_regs_t *)&frame);
252 }
253 #endif
254 #if 0 /* XXX something is wrong */
255 if (frame->f_format == 4) {
256 /* XXX Restore PC -- 68{EC,LC}040 only */
257 if (insn.is_nextpc)
258 frame->f_pc = insn.is_nextpc;
259 }
260 #endif
261
262 #if DEBUG_FPE
263 printf("EXITING fpu_emulate: w/FPSR=%08x, FPCR=%08x\n",
264 fe.fe_fpsr, fe.fe_fpcr);
265 #endif
266
267 if (sig)
268 fpe_abort(frame, ksi, sig, 0);
269 return (sig);
270 }
271
272 /* update accrued exception bits and see if there's an FP exception */
273 int
274 fpu_upd_excp(fe)
275 struct fpemu *fe;
276 {
277 u_int fpsr;
278 u_int fpcr;
279
280 fpsr = fe->fe_fpsr;
281 fpcr = fe->fe_fpcr;
282 /* update fpsr accrued exception bits; each insn doesn't have to
283 update this */
284 if (fpsr & (FPSR_BSUN | FPSR_SNAN | FPSR_OPERR)) {
285 fpsr |= FPSR_AIOP;
286 }
287 if (fpsr & FPSR_OVFL) {
288 fpsr |= FPSR_AOVFL;
289 }
290 if ((fpsr & FPSR_UNFL) && (fpsr & FPSR_INEX2)) {
291 fpsr |= FPSR_AUNFL;
292 }
293 if (fpsr & FPSR_DZ) {
294 fpsr |= FPSR_ADZ;
295 }
296 if (fpsr & (FPSR_INEX1 | FPSR_INEX2 | FPSR_OVFL)) {
297 fpsr |= FPSR_AINEX;
298 }
299
300 fe->fe_fpframe->fpf_fpsr = fe->fe_fpsr = fpsr;
301
302 return (fpsr & fpcr & FPSR_EXCP) ? SIGFPE : 0;
303 }
304
305 /* update fpsr according to fp (= result of an fp op) */
306 u_int
307 fpu_upd_fpsr(fe, fp)
308 struct fpemu *fe;
309 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(fe, insn)
377 struct fpemu *fe;
378 struct instruction *insn;
379 {
380 struct frame *frame = fe->fe_frame;
381 struct fpframe *fpf = fe->fe_fpframe;
382 int sig;
383 int reglist;
384 int fpu_to_mem;
385
386 /* move to/from control registers */
387 reglist = (insn->is_word1 & 0x1c00) >> 10;
388 /* Bit 13 selects direction (FPU to/from Mem) */
389 fpu_to_mem = insn->is_word1 & 0x2000;
390
391 insn->is_datasize = 4;
392 insn->is_advance = 4;
393 sig = fpu_decode_ea(frame, insn, &insn->is_ea, insn->is_opcode);
394 if (sig) { return sig; }
395
396 if (reglist != 1 && reglist != 2 && reglist != 4 &&
397 (insn->is_ea.ea_flags & EA_DIRECT)) {
398 /* attempted to copy more than one FPcr to CPU regs */
399 #ifdef DEBUG
400 printf("fpu_emul_fmovmcr: tried to copy too many FPcr\n");
401 #endif
402 return SIGILL;
403 }
404
405 if (reglist & 4) {
406 /* fpcr */
407 if ((insn->is_ea.ea_flags & EA_DIRECT) &&
408 insn->is_ea.ea_regnum >= 8 /* address reg */) {
409 /* attempted to copy FPCR to An */
410 #ifdef DEBUG
411 printf("fpu_emul_fmovmcr: tried to copy FPCR from/to A%d\n",
412 insn->is_ea.ea_regnum & 7);
413 #endif
414 return SIGILL;
415 }
416 if (fpu_to_mem) {
417 sig = fpu_store_ea(frame, insn, &insn->is_ea,
418 (char *)&fpf->fpf_fpcr);
419 } else {
420 sig = fpu_load_ea(frame, insn, &insn->is_ea,
421 (char *)&fpf->fpf_fpcr);
422 }
423 }
424 if (sig) { 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 A%d\n",
433 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) { return sig; }
446
447 if (reglist & 1) {
448 /* fpiar - can be moved to/from An */
449 if (fpu_to_mem) {
450 sig = fpu_store_ea(frame, insn, &insn->is_ea,
451 (char *)&fpf->fpf_fpiar);
452 } else {
453 sig = fpu_load_ea(frame, insn, &insn->is_ea,
454 (char *)&fpf->fpf_fpiar);
455 }
456 }
457 return sig;
458 }
459
460 /*
461 * type 0: fmovem
462 * Separated out of fpu_emul_type0 for efficiency.
463 * In this function, we know:
464 * (opcode & 0x01C0) == 0
465 * (word1 & 0x8000) == 0x8000
466 *
467 * No conversion or rounding is done by this instruction,
468 * and the FPSR is not affected.
469 */
470 static int
471 fpu_emul_fmovm(fe, insn)
472 struct fpemu *fe;
473 struct instruction *insn;
474 {
475 struct frame *frame = fe->fe_frame;
476 struct fpframe *fpf = fe->fe_fpframe;
477 int word1, sig;
478 int reglist, regmask, regnum;
479 int fpu_to_mem, order;
480 int w1_post_incr;
481 int *fpregs;
482
483 insn->is_advance = 4;
484 insn->is_datasize = 12;
485 word1 = insn->is_word1;
486
487 /* Bit 13 selects direction (FPU to/from Mem) */
488 fpu_to_mem = word1 & 0x2000;
489
490 /*
491 * Bits 12,11 select register list mode:
492 * 0,0: Static reg list, pre-decr.
493 * 0,1: Dynamic reg list, pre-decr.
494 * 1,0: Static reg list, post-incr.
495 * 1,1: Dynamic reg list, post-incr
496 */
497 w1_post_incr = word1 & 0x1000;
498 if (word1 & 0x0800) {
499 /* dynamic reg list */
500 reglist = frame->f_regs[(word1 & 0x70) >> 4];
501 } else {
502 reglist = word1;
503 }
504 reglist &= 0xFF;
505
506 /* Get effective address. (modreg=opcode&077) */
507 sig = fpu_decode_ea(frame, insn, &insn->is_ea, insn->is_opcode);
508 if (sig) { 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) saved\n",
529 regnum, fpregs[regnum * 3], fpregs[regnum * 3 + 1],
530 fpregs[regnum * 3 + 2]);
531 #endif
532 } else { /* mem to fpu */
533 sig = fpu_load_ea(frame, insn, &insn->is_ea,
534 (char*)&fpregs[regnum * 3]);
535 #if DEBUG_FPE
536 printf("fpu_emul_fmovm: FP%d (%08x,%08x,%08x) loaded\n",
537 regnum, fpregs[regnum * 3], fpregs[regnum * 3 + 1],
538 fpregs[regnum * 3 + 2]);
539 #endif
540 }
541 if (sig) { break; }
542 }
543 regnum += order;
544 regmask >>= 1;
545 }
546
547 return sig;
548 }
549
550 static struct fpn *
551 fpu_cmp(fe)
552 struct fpemu *fe;
553 {
554 struct fpn *x = &fe->fe_f1, *y = &fe->fe_f2;
555
556 /* take care of special cases */
557 if (x->fp_class < 0 || y->fp_class < 0) {
558 /* if either of two is a SNAN, result is SNAN */
559 x->fp_class = (y->fp_class < x->fp_class) ? y->fp_class : x->fp_class;
560 } else if (x->fp_class == FPC_INF) {
561 if (y->fp_class == FPC_INF) {
562 /* both infinities */
563 if (x->fp_sign == y->fp_sign) {
564 x->fp_class = FPC_ZERO; /* return a signed zero */
565 } else {
566 x->fp_class = FPC_NUM; /* return a faked number w/x's sign */
567 x->fp_exp = 16383;
568 x->fp_mant[0] = FP_1;
569 }
570 } else {
571 /* y is a number */
572 x->fp_class = FPC_NUM; /* return a forged number w/x's sign */
573 x->fp_exp = 16383;
574 x->fp_mant[0] = FP_1;
575 }
576 } else if (y->fp_class == FPC_INF) {
577 /* x is a Num but y is an Inf */
578 /* return a forged number w/y's sign inverted */
579 x->fp_class = FPC_NUM;
580 x->fp_sign = !y->fp_sign;
581 x->fp_exp = 16383;
582 x->fp_mant[0] = FP_1;
583 } else {
584 /* x and y are both numbers or zeros, or pair of a number and a zero */
585 y->fp_sign = !y->fp_sign;
586 x = fpu_add(fe); /* (x - y) */
587 /*
588 * FCMP does not set Inf bit in CC, so return a forged number
589 * (value doesn't matter) if Inf is the result of fsub.
590 */
591 if (x->fp_class == FPC_INF) {
592 x->fp_class = FPC_NUM;
593 x->fp_exp = 16383;
594 x->fp_mant[0] = FP_1;
595 }
596 }
597 return x;
598 }
599
600 /*
601 * arithmetic oprations
602 */
603 static int
604 fpu_emul_arith(fe, insn)
605 struct fpemu *fe;
606 struct instruction *insn;
607 {
608 struct frame *frame = fe->fe_frame;
609 u_int *fpregs = &(fe->fe_fpframe->fpf_regs[0]);
610 struct fpn *res;
611 int word1, sig = 0;
612 int regnum, format;
613 int discard_result = 0;
614 u_int buf[3];
615 #if DEBUG_FPE
616 int flags;
617 char regname;
618 #endif
619
620 fe->fe_fpsr &= ~FPSR_EXCP;
621
622 DUMP_INSN(insn);
623
624 #if DEBUG_FPE
625 printf("fpu_emul_arith: FPSR = %08x, FPCR = %08x\n",
626 fe->fe_fpsr, fe->fe_fpcr);
627 #endif
628
629 word1 = insn->is_word1;
630 format = (word1 >> 10) & 7;
631 regnum = (word1 >> 7) & 7;
632
633 /* fetch a source operand : may not be used */
634 #if DEBUG_FPE
635 printf("fpu_emul_arith: dst/src FP%d=%08x,%08x,%08x\n",
636 regnum, fpregs[regnum*3], fpregs[regnum*3+1],
637 fpregs[regnum*3+2]);
638 #endif
639
640 fpu_explode(fe, &fe->fe_f1, FTYPE_EXT, &fpregs[regnum * 3]);
641
642 DUMP_INSN(insn);
643
644 /* get the other operand which is always the source */
645 if ((word1 & 0x4000) == 0) {
646 #if DEBUG_FPE
647 printf("fpu_emul_arith: FP%d op FP%d => FP%d\n",
648 format, regnum, regnum);
649 printf("fpu_emul_arith: src opr FP%d=%08x,%08x,%08x\n",
650 format, fpregs[format*3], fpregs[format*3+1],
651 fpregs[format*3+2]);
652 #endif
653 fpu_explode(fe, &fe->fe_f2, FTYPE_EXT, &fpregs[format * 3]);
654 } else {
655 /* the operand is in memory */
656 if (format == FTYPE_DBL) {
657 insn->is_datasize = 8;
658 } else if (format == FTYPE_SNG || format == FTYPE_LNG) {
659 insn->is_datasize = 4;
660 } else if (format == FTYPE_WRD) {
661 insn->is_datasize = 2;
662 } else if (format == FTYPE_BYT) {
663 insn->is_datasize = 1;
664 } else if (format == FTYPE_EXT) {
665 insn->is_datasize = 12;
666 } else {
667 /* invalid or unsupported operand format */
668 sig = SIGFPE;
669 return sig;
670 }
671
672 /* Get effective address. (modreg=opcode&077) */
673 sig = fpu_decode_ea(frame, insn, &insn->is_ea, insn->is_opcode);
674 if (sig) {
675 #if DEBUG_FPE
676 printf("fpu_emul_arith: error in fpu_decode_ea\n");
677 #endif
678 return sig;
679 }
680
681 DUMP_INSN(insn);
682
683 #if DEBUG_FPE
684 printf("fpu_emul_arith: addr mode = ");
685 flags = insn->is_ea.ea_flags;
686 regname = (insn->is_ea.ea_regnum & 8) ? 'a' : 'd';
687
688 if (flags & EA_DIRECT) {
689 printf("%c%d\n",
690 regname, insn->is_ea.ea_regnum & 7);
691 } else if (flags & EA_PC_REL) {
692 if (flags & EA_OFFSET) {
693 printf("pc@(%d)\n", insn->is_ea.ea_offset);
694 } else if (flags & EA_INDEXED) {
695 printf("pc@(...)\n");
696 }
697 } else if (flags & EA_PREDECR) {
698 printf("%c%d@-\n",
699 regname, insn->is_ea.ea_regnum & 7);
700 } else if (flags & EA_POSTINCR) {
701 printf("%c%d@+\n", regname, insn->is_ea.ea_regnum & 7);
702 } else if (flags & EA_OFFSET) {
703 printf("%c%d@(%d)\n", regname, insn->is_ea.ea_regnum & 7,
704 insn->is_ea.ea_offset);
705 } else if (flags & EA_INDEXED) {
706 printf("%c%d@(...)\n", regname, insn->is_ea.ea_regnum & 7);
707 } else if (flags & EA_ABS) {
708 printf("0x%08x\n", insn->is_ea.ea_absaddr);
709 } else if (flags & EA_IMMED) {
710
711 printf("#0x%08x,%08x,%08x\n", insn->is_ea.ea_immed[0],
712 insn->is_ea.ea_immed[1], insn->is_ea.ea_immed[2]);
713 } else {
714 printf("%c%d@\n", regname, insn->is_ea.ea_regnum & 7);
715 }
716 #endif /* DEBUG_FPE */
717
718 fpu_load_ea(frame, insn, &insn->is_ea, (char*)buf);
719 if (format == FTYPE_WRD) {
720 /* sign-extend */
721 buf[0] &= 0xffff;
722 if (buf[0] & 0x8000) {
723 buf[0] |= 0xffff0000;
724 }
725 format = FTYPE_LNG;
726 } else if (format == FTYPE_BYT) {
727 /* sign-extend */
728 buf[0] &= 0xff;
729 if (buf[0] & 0x80) {
730 buf[0] |= 0xffffff00;
731 }
732 format = FTYPE_LNG;
733 }
734 #if DEBUG_FPE
735 printf("fpu_emul_arith: src = %08x %08x %08x, siz = %d\n",
736 buf[0], buf[1], buf[2], insn->is_datasize);
737 #endif
738 fpu_explode(fe, &fe->fe_f2, format, buf);
739 }
740
741 DUMP_INSN(insn);
742
743 /* An arithmetic instruction emulate function has a prototype of
744 * struct fpn *fpu_op(struct fpemu *);
745
746 * 1) If the instruction is monadic, then fpu_op() must use
747 * fe->fe_f2 as its operand, and return a pointer to the
748 * result.
749
750 * 2) If the instruction is diadic, then fpu_op() must use
751 * fe->fe_f1 and fe->fe_f2 as its two operands, and return a
752 * pointer to the result.
753
754 */
755 res = 0;
756 switch (word1 & 0x3f) {
757 case 0x00: /* fmove */
758 res = &fe->fe_f2;
759 break;
760
761 case 0x01: /* fint */
762 res = fpu_int(fe);
763 break;
764
765 case 0x02: /* fsinh */
766 res = fpu_sinh(fe);
767 break;
768
769 case 0x03: /* fintrz */
770 res = fpu_intrz(fe);
771 break;
772
773 case 0x04: /* fsqrt */
774 res = fpu_sqrt(fe);
775 break;
776
777 case 0x06: /* flognp1 */
778 res = fpu_lognp1(fe);
779 break;
780
781 case 0x08: /* fetoxm1 */
782 res = fpu_etoxm1(fe);
783 break;
784
785 case 0x09: /* ftanh */
786 res = fpu_tanh(fe);
787 break;
788
789 case 0x0A: /* fatan */
790 res = fpu_atan(fe);
791 break;
792
793 case 0x0C: /* fasin */
794 res = fpu_asin(fe);
795 break;
796
797 case 0x0D: /* fatanh */
798 res = fpu_atanh(fe);
799 break;
800
801 case 0x0E: /* fsin */
802 res = fpu_sin(fe);
803 break;
804
805 case 0x0F: /* ftan */
806 res = fpu_tan(fe);
807 break;
808
809 case 0x10: /* fetox */
810 res = fpu_etox(fe);
811 break;
812
813 case 0x11: /* ftwotox */
814 res = fpu_twotox(fe);
815 break;
816
817 case 0x12: /* ftentox */
818 res = fpu_tentox(fe);
819 break;
820
821 case 0x14: /* flogn */
822 res = fpu_logn(fe);
823 break;
824
825 case 0x15: /* flog10 */
826 res = fpu_log10(fe);
827 break;
828
829 case 0x16: /* flog2 */
830 res = fpu_log2(fe);
831 break;
832
833 case 0x18: /* fabs */
834 fe->fe_f2.fp_sign = 0;
835 res = &fe->fe_f2;
836 break;
837
838 case 0x19: /* fcosh */
839 res = fpu_cosh(fe);
840 break;
841
842 case 0x1A: /* fneg */
843 fe->fe_f2.fp_sign = !fe->fe_f2.fp_sign;
844 res = &fe->fe_f2;
845 break;
846
847 case 0x1C: /* facos */
848 res = fpu_acos(fe);
849 break;
850
851 case 0x1D: /* fcos */
852 res = fpu_cos(fe);
853 break;
854
855 case 0x1E: /* fgetexp */
856 res = fpu_getexp(fe);
857 break;
858
859 case 0x1F: /* fgetman */
860 res = fpu_getman(fe);
861 break;
862
863 case 0x20: /* fdiv */
864 case 0x24: /* fsgldiv: cheating - better than nothing */
865 res = fpu_div(fe);
866 break;
867
868 case 0x21: /* fmod */
869 res = fpu_mod(fe);
870 break;
871
872 case 0x28: /* fsub */
873 fe->fe_f2.fp_sign = !fe->fe_f2.fp_sign; /* f2 = -f2 */
874 case 0x22: /* fadd */
875 res = fpu_add(fe);
876 break;
877
878 case 0x23: /* fmul */
879 case 0x27: /* fsglmul: cheating - better than nothing */
880 res = fpu_mul(fe);
881 break;
882
883 case 0x25: /* frem */
884 res = fpu_rem(fe);
885 break;
886
887 case 0x26:
888 /* fscale is handled by a separate function */
889 break;
890
891 case 0x30:
892 case 0x31:
893 case 0x32:
894 case 0x33:
895 case 0x34:
896 case 0x35:
897 case 0x36:
898 case 0x37: /* fsincos */
899 res = fpu_sincos(fe, word1 & 7);
900 break;
901
902 case 0x38: /* fcmp */
903 res = fpu_cmp(fe);
904 discard_result = 1;
905 break;
906
907 case 0x3A: /* ftst */
908 res = &fe->fe_f2;
909 discard_result = 1;
910 break;
911
912 default:
913 #ifdef DEBUG
914 printf("fpu_emul_arith: bad opcode=0x%x, word1=0x%x\n",
915 insn->is_opcode, insn->is_word1);
916 #endif
917 sig = SIGILL;
918 } /* switch (word1 & 0x3f) */
919
920 if (!discard_result && sig == 0) {
921 fpu_implode(fe, res, FTYPE_EXT, &fpregs[regnum * 3]);
922 #if DEBUG_FPE
923 printf("fpu_emul_arith: %08x,%08x,%08x stored in FP%d\n",
924 fpregs[regnum*3], fpregs[regnum*3+1],
925 fpregs[regnum*3+2], regnum);
926 } else if (sig == 0) {
927 static char *class_name[] = { "SNAN", "QNAN", "ZERO", "NUM", "INF" };
928 printf("fpu_emul_arith: result(%s,%c,%d,%08x,%08x,%08x) discarded\n",
929 class_name[res->fp_class + 2],
930 res->fp_sign ? '-' : '+', res->fp_exp,
931 res->fp_mant[0], res->fp_mant[1],
932 res->fp_mant[2]);
933 } else {
934 printf("fpu_emul_arith: received signal %d\n", sig);
935 #endif
936 }
937
938 /* update fpsr according to the result of operation */
939 fpu_upd_fpsr(fe, res);
940
941 #if DEBUG_FPE
942 printf("fpu_emul_arith: FPSR = %08x, FPCR = %08x\n",
943 fe->fe_fpsr, fe->fe_fpcr);
944 #endif
945
946 DUMP_INSN(insn);
947
948 return sig;
949 }
950
951 /* test condition code according to the predicate in the opcode.
952 * returns -1 when the predicate evaluates to true, 0 when false.
953 * signal numbers are returned when an error is detected.
954 */
955 static int
956 test_cc(fe, pred)
957 struct fpemu *fe;
958 int pred;
959 {
960 int result, sig_bsun, invert;
961 int fpsr;
962
963 fpsr = fe->fe_fpsr;
964 invert = 0;
965 fpsr &= ~FPSR_EXCP; /* clear all exceptions */
966 #if DEBUG_FPE
967 printf("test_cc: fpsr=0x%08x\n", fpsr);
968 #endif
969 pred &= 0x3f; /* lowest 6 bits */
970
971 #if DEBUG_FPE
972 printf("test_cc: ");
973 #endif
974
975 if (pred >= 0x20) {
976 return SIGILL;
977 } else if (pred & 0x10) {
978 /* IEEE nonaware tests */
979 sig_bsun = 1;
980 pred &= 0x0f; /* lower 4 bits */
981 } else {
982 /* IEEE aware tests */
983 #if DEBUG_FPE
984 printf("IEEE ");
985 #endif
986 sig_bsun = 0;
987 }
988
989 if (pred & 0x08) {
990 #if DEBUG_FPE
991 printf("Not ");
992 #endif
993 /* predicate is "NOT ..." */
994 pred ^= 0xf; /* invert */
995 invert = -1;
996 }
997 switch (pred) {
998 case 0: /* (Signaling) False */
999 #if DEBUG_FPE
1000 printf("False");
1001 #endif
1002 result = 0;
1003 break;
1004 case 1: /* (Signaling) Equal */
1005 #if DEBUG_FPE
1006 printf("Equal");
1007 #endif
1008 result = -((fpsr & FPSR_ZERO) == FPSR_ZERO);
1009 break;
1010 case 2: /* Greater Than */
1011 #if DEBUG_FPE
1012 printf("GT");
1013 #endif
1014 result = -((fpsr & (FPSR_NAN|FPSR_ZERO|FPSR_NEG)) == 0);
1015 break;
1016 case 3: /* Greater or Equal */
1017 #if DEBUG_FPE
1018 printf("GE");
1019 #endif
1020 result = -((fpsr & FPSR_ZERO) ||
1021 (fpsr & (FPSR_NAN|FPSR_NEG)) == 0);
1022 break;
1023 case 4: /* Less Than */
1024 #if DEBUG_FPE
1025 printf("LT");
1026 #endif
1027 result = -((fpsr & (FPSR_NAN|FPSR_ZERO|FPSR_NEG)) == FPSR_NEG);
1028 break;
1029 case 5: /* Less or Equal */
1030 #if DEBUG_FPE
1031 printf("LE");
1032 #endif
1033 result = -((fpsr & FPSR_ZERO) ||
1034 ((fpsr & (FPSR_NAN|FPSR_NEG)) == FPSR_NEG));
1035 break;
1036 case 6: /* Greater or Less than */
1037 #if DEBUG_FPE
1038 printf("GLT");
1039 #endif
1040 result = -((fpsr & (FPSR_NAN|FPSR_ZERO)) == 0);
1041 break;
1042 case 7: /* Greater, Less or Equal */
1043 #if DEBUG_FPE
1044 printf("GLE");
1045 #endif
1046 result = -((fpsr & FPSR_NAN) == 0);
1047 break;
1048 default:
1049 /* invalid predicate */
1050 return SIGILL;
1051 }
1052 result ^= invert; /* if the predicate is "NOT ...", then
1053 invert the result */
1054 #if DEBUG_FPE
1055 printf("=> %s (%d)\n", result ? "true" : "false", result);
1056 #endif
1057 /* if it's an IEEE unaware test and NAN is set, BSUN is set */
1058 if (sig_bsun && (fpsr & FPSR_NAN)) {
1059 fpsr |= FPSR_BSUN;
1060 }
1061
1062 /* put fpsr back */
1063 fe->fe_fpframe->fpf_fpsr = fe->fe_fpsr = fpsr;
1064
1065 return result;
1066 }
1067
1068 /*
1069 * type 1: fdbcc, fscc, ftrapcc
1070 * In this function, we know:
1071 * (opcode & 0x01C0) == 0x0040
1072 */
1073 static int
1074 fpu_emul_type1(fe, insn)
1075 struct fpemu *fe;
1076 struct instruction *insn;
1077 {
1078 struct frame *frame = fe->fe_frame;
1079 int advance, sig, branch, displ;
1080
1081 branch = test_cc(fe, insn->is_word1);
1082 fe->fe_fpframe->fpf_fpsr = fe->fe_fpsr;
1083
1084 insn->is_advance = 4;
1085 sig = 0;
1086
1087 switch (insn->is_opcode & 070) {
1088 case 010: /* fdbcc */
1089 if (branch == -1) {
1090 /* advance */
1091 insn->is_advance = 6;
1092 } else if (!branch) {
1093 /* decrement Dn and if (Dn != -1) branch */
1094 u_int16_t count = frame->f_regs[insn->is_opcode & 7];
1095
1096 if (count-- != 0) {
1097 displ = fusword((void *) (insn->is_pc + insn->is_advance));
1098 if (displ < 0) {
1099 #ifdef DEBUG
1100 printf("fpu_emul_type1: fault reading displacement\n");
1101 #endif
1102 return SIGSEGV;
1103 }
1104 /* sign-extend the displacement */
1105 displ &= 0xffff;
1106 if (displ & 0x8000) {
1107 displ |= 0xffff0000;
1108 }
1109 insn->is_advance += displ;
1110 /* XXX insn->is_nextpc = insn->is_pc + insn->is_advance; */
1111 } else {
1112 insn->is_advance = 6;
1113 }
1114 /* write it back */
1115 frame->f_regs[insn->is_opcode & 7] &= 0xffff0000;
1116 frame->f_regs[insn->is_opcode & 7] |= (u_int32_t)count;
1117 } else { /* got a signal */
1118 sig = SIGFPE;
1119 }
1120 break;
1121
1122 case 070: /* ftrapcc or fscc */
1123 advance = 4;
1124 if ((insn->is_opcode & 07) >= 2) {
1125 switch (insn->is_opcode & 07) {
1126 case 3: /* long opr */
1127 advance += 2;
1128 case 2: /* word opr */
1129 advance += 2;
1130 case 4: /* no opr */
1131 break;
1132 default:
1133 return SIGILL;
1134 break;
1135 }
1136
1137 if (branch == 0) {
1138 /* no trap */
1139 insn->is_advance = advance;
1140 sig = 0;
1141 } else {
1142 /* trap */
1143 sig = SIGFPE;
1144 }
1145 break;
1146 } /* if ((insn->is_opcode & 7) < 2), fall through to FScc */
1147
1148 default: /* fscc */
1149 insn->is_advance = 4;
1150 insn->is_datasize = 1; /* always byte */
1151 sig = fpu_decode_ea(frame, insn, &insn->is_ea, insn->is_opcode);
1152 if (sig) {
1153 break;
1154 }
1155 if (branch == -1 || branch == 0) {
1156 /* set result */
1157 sig = fpu_store_ea(frame, insn, &insn->is_ea, (char *)&branch);
1158 } else {
1159 /* got an exception */
1160 sig = branch;
1161 }
1162 break;
1163 }
1164 return sig;
1165 }
1166
1167 /*
1168 * Type 2 or 3: fbcc (also fnop)
1169 * In this function, we know:
1170 * (opcode & 0x0180) == 0x0080
1171 */
1172 static int
1173 fpu_emul_brcc(fe, insn)
1174 struct fpemu *fe;
1175 struct instruction *insn;
1176 {
1177 int displ, word2;
1178 int sig;
1179
1180 /*
1181 * Get branch displacement.
1182 */
1183 insn->is_advance = 4;
1184 displ = insn->is_word1;
1185
1186 if (insn->is_opcode & 0x40) {
1187 word2 = fusword((void *) (insn->is_pc + insn->is_advance));
1188 if (word2 < 0) {
1189 #ifdef DEBUG
1190 printf("fpu_emul_brcc: fault reading word2\n");
1191 #endif
1192 return SIGSEGV;
1193 }
1194 displ <<= 16;
1195 displ |= word2;
1196 insn->is_advance += 2;
1197 } else /* displacement is word sized */
1198 if (displ & 0x8000)
1199 displ |= 0xFFFF0000;
1200
1201 /* XXX: If CC, insn->is_pc += displ */
1202 sig = test_cc(fe, insn->is_opcode);
1203 fe->fe_fpframe->fpf_fpsr = fe->fe_fpsr;
1204
1205 if (fe->fe_fpsr & fe->fe_fpcr & FPSR_EXCP) {
1206 return SIGFPE; /* caught an exception */
1207 }
1208 if (sig == -1) {
1209 /* branch does take place; 2 is the offset to the 1st disp word */
1210 insn->is_advance = displ + 2;
1211 /* XXX insn->is_nextpc = insn->is_pc + insn->is_advance; */
1212 } else if (sig) {
1213 return SIGILL; /* got a signal */
1214 }
1215 #if DEBUG_FPE
1216 printf("fpu_emul_brcc: %s insn @ %x (%x+%x) (disp=%x)\n",
1217 (sig == -1) ? "BRANCH to" : "NEXT",
1218 insn->is_pc + insn->is_advance, insn->is_pc, insn->is_advance,
1219 displ);
1220 #endif
1221 return 0;
1222 }
1223