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