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