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