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