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