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