--- nono/fpe/fpu_emulate.c 2026/04/29 17:04:28 1.1.1.1 +++ nono/fpe/fpu_emulate.c 2026/04/29 17:05:35 1.1.1.6 @@ -1,4 +1,4 @@ -/* $NetBSD: fpu_emulate.c,v 1.38 2013/10/25 21:32:45 martin Exp $ */ +/* $NetBSD: fpu_emulate.c,v 1.49 2025/01/06 07:34:24 isaki Exp $ */ /* * Copyright (c) 1995 Gordon W. Ross @@ -79,8 +79,8 @@ fpu_emulate(struct frame *frame, struct { static struct instruction insn; static struct fpemu fe; - int word, optype, sig; - + int optype, sig; + unsigned short sval; /* initialize insn.is_datasize to tell it is *not* initialized */ insn.is_datasize = -1; @@ -116,32 +116,30 @@ fpu_emulate(struct frame *frame, struct frame->f_pc = insn.is_pc; } - word = fusword((void *)(insn.is_pc)); - if (word < 0) { + if (ufetch_short((void *)(insn.is_pc), &sval)) { DPRINTF(("%s: fault reading opcode\n", __func__)); fpe_abort(frame, ksi, SIGSEGV, SEGV_ACCERR); } - if ((word & 0xf000) != 0xf000) { + if ((sval & 0xf000) != 0xf000) { DPRINTF(("%s: not coproc. insn.: opcode=0x%x\n", - __func__, word)); + __func__, sval)); fpe_abort(frame, ksi, SIGILL, ILL_ILLOPC); } - if ((word & 0x0E00) != 0x0200) { - DPRINTF(("%s: bad coproc. id: opcode=0x%x\n", __func__, word)); + if ((sval & 0x0E00) != 0x0200) { + DPRINTF(("%s: bad coproc. id: opcode=0x%x\n", __func__, sval)); fpe_abort(frame, ksi, SIGILL, ILL_ILLOPC); } - insn.is_opcode = word; - optype = (word & 0x01C0); + insn.is_opcode = sval; + optype = (sval & 0x01C0); - word = fusword((void *)(insn.is_pc + 2)); - if (word < 0) { + if (ufetch_short((void *)(insn.is_pc + 2), &sval)) { DPRINTF(("%s: fault reading word1\n", __func__)); fpe_abort(frame, ksi, SIGSEGV, SEGV_ACCERR); } - insn.is_word1 = word; + insn.is_word1 = sval; /* all FPU instructions are at least 4-byte long */ insn.is_advance = 4; @@ -154,32 +152,36 @@ fpu_emulate(struct frame *frame, struct */ if (optype == 0x0000) { /* type=0: generic */ - if ((word & 0xc000) == 0xc000) { - DPRINTF(("%s: fmovm FPr\n", __func__)); - sig = fpu_emul_fmovm(&fe, &insn); - } else if ((word & 0xc000) == 0x8000) { - DPRINTF(("%s: fmovm FPcr\n", __func__)); - sig = fpu_emul_fmovmcr(&fe, &insn); - } else if ((word & 0xe000) == 0x6000) { - /* fstore = fmove FPn,mem */ - DPRINTF(("%s: fmove to mem\n", __func__)); - sig = fpu_emul_fstore(&fe, &insn); - } else if ((word & 0xfc00) == 0x5c00) { - /* fmovecr */ - DPRINTF(("%s: fmovecr\n", __func__)); - sig = fpu_emul_fmovecr(&fe, &insn); - } else if ((word & 0xa07f) == 0x26) { - /* fscale */ - DPRINTF(("%s: fscale\n", __func__)); - sig = fpu_emul_fscale(&fe, &insn); + if ((sval & 0x8000)) { + if ((sval & 0x4000)) { + DPRINTF(("%s: fmovm FPr\n", __func__)); + sig = fpu_emul_fmovm(&fe, &insn); + } else { + DPRINTF(("%s: fmovm FPcr\n", __func__)); + sig = fpu_emul_fmovmcr(&fe, &insn); + } } else { - DPRINTF(("%s: other type0\n", __func__)); - /* all other type0 insns are arithmetic */ - sig = fpu_emul_arith(&fe, &insn); - } - if (sig == 0) { - DPRINTF(("%s: type 0 returned 0\n", __func__)); - sig = fpu_upd_excp(&fe); + if ((sval & 0xe000) == 0x6000) { + /* fstore = fmove FPn,mem */ + DPRINTF(("%s: fmove to mem\n", __func__)); + sig = fpu_emul_fstore(&fe, &insn); + } else if ((sval & 0xfc00) == 0x5c00) { + /* fmovecr */ + DPRINTF(("%s: fmovecr\n", __func__)); + sig = fpu_emul_fmovecr(&fe, &insn); + } else if ((sval & 0xa07f) == 0x26) { + /* fscale */ + DPRINTF(("%s: fscale\n", __func__)); + sig = fpu_emul_fscale(&fe, &insn); + } else { + DPRINTF(("%s: other type0\n", __func__)); + /* all other type0 insns are arithmetic */ + sig = fpu_emul_arith(&fe, &insn); + } + if (sig == 0) { + DPRINTF(("%s: type 0 returned 0\n", __func__)); + sig = fpu_upd_excp(&fe); + } } } else if (optype == 0x0080 || optype == 0x00C0) { /* type=2 or 3: fbcc, short or long disp. */ @@ -190,6 +192,10 @@ fpu_emulate(struct frame *frame, struct /* type=1: fdbcc, fscc, ftrapcc */ DPRINTF(("%s: type1\n", __func__)); sig = fpu_emul_type1(&fe, &insn); + /* real FTRAPcc raises T_TRAPVINST if the condition is met. */ + if (sig == SIGFPE) { + ksi->ksi_trap = T_TRAPVINST; + } } else { /* type=4: fsave (privileged) */ /* type=5: frestore (privileged) */ @@ -343,74 +349,96 @@ fpu_emul_fmovmcr(struct fpemu *fe, struc struct fpframe *fpf = fe->fe_fpframe; int sig; int reglist; + int regcount; int fpu_to_mem; + int modreg; + uint32_t tmp[3]; /* move to/from control registers */ reglist = (insn->is_word1 & 0x1c00) >> 10; /* Bit 13 selects direction (FPU to/from Mem) */ fpu_to_mem = insn->is_word1 & 0x2000; - insn->is_datasize = 4; - insn->is_advance = 4; - sig = fpu_decode_ea(frame, insn, &insn->is_ea, insn->is_opcode); - if (sig) - return sig; - - if (reglist != 1 && reglist != 2 && reglist != 4 && - (insn->is_ea.ea_flags & EA_DIRECT)) { - /* attempted to copy more than one FPcr to CPU regs */ - DPRINTF(("%s: tried to copy too many FPcr\n", __func__)); - return SIGILL; - } - - if (reglist & 4) { - /* fpcr */ - if ((insn->is_ea.ea_flags & EA_DIRECT) && - insn->is_ea.ea_regnum >= 8 /* address reg */) { - /* attempted to copy FPCR to An */ - DPRINTF(("%s: tried to copy FPCR from/to A%d\n", - __func__, insn->is_ea.ea_regnum & 7)); + /* Check an illegal mod/reg. */ + modreg = insn->is_opcode & 077; + if (fpu_to_mem) { + /* PCrel, #imm are illegal. */ + if (modreg >= 072) { return SIGILL; } - if (fpu_to_mem) { - sig = fpu_store_ea(frame, insn, &insn->is_ea, - (char *)&fpf->fpf_fpcr); - } else { - sig = fpu_load_ea(frame, insn, &insn->is_ea, - (char *)&fpf->fpf_fpcr); + } else { + /* All mod/reg can be specified. */ + if (modreg >= 075) { + return SIGILL; } } + + /* + * If reglist is 0b000, treat it as FPIAR. This is not specification + * but the behavior described in the 6888x user's manual. + */ + if (reglist == 0) + reglist = 1; + + if (reglist == 7) { + regcount = 3; + } else if (reglist == 3 || reglist == 5 || reglist == 6) { + regcount = 2; + } else { + regcount = 1; + } + insn->is_datasize = regcount * 4; + sig = fpu_decode_ea(frame, insn, &insn->is_ea, modreg); if (sig) return sig; - if (reglist & 2) { - /* fpsr */ - if ((insn->is_ea.ea_flags & EA_DIRECT) && - insn->is_ea.ea_regnum >= 8 /* address reg */) { - /* attempted to copy FPSR to An */ - DPRINTF(("%s: tried to copy FPSR from/to A%d\n", - __func__, insn->is_ea.ea_regnum & 7)); - return SIGILL; - } - if (fpu_to_mem) { - sig = fpu_store_ea(frame, insn, &insn->is_ea, - (char *)&fpf->fpf_fpsr); + /* + * For data register, only single register can be transferred. + * For addr register, only FPIAR can be transferred. + */ + if ((insn->is_ea.ea_flags & EA_DIRECT)) { + if (insn->is_ea.ea_regnum < 8) { + if (regcount != 1) { + return SIGILL; + } } else { - sig = fpu_load_ea(frame, insn, &insn->is_ea, - (char *)&fpf->fpf_fpsr); + if (reglist != 1) { + return SIGILL; + } } } - if (sig) - return sig; - if (reglist & 1) { - /* fpiar - can be moved to/from An */ - if (fpu_to_mem) { - sig = fpu_store_ea(frame, insn, &insn->is_ea, - (char *)&fpf->fpf_fpiar); - } else { - sig = fpu_load_ea(frame, insn, &insn->is_ea, - (char *)&fpf->fpf_fpiar); + if (fpu_to_mem) { + uint32_t *s = &tmp[0]; + + if ((reglist & 4)) { + *s++ = fpf->fpf_fpcr; + } + if ((reglist & 2)) { + *s++ = fpf->fpf_fpsr; + } + if ((reglist & 1)) { + *s++ = fpf->fpf_fpiar; + } + + sig = fpu_store_ea(frame, insn, &insn->is_ea, (char *)tmp); + } else { + const uint32_t *d = &tmp[0]; + + sig = fpu_load_ea(frame, insn, &insn->is_ea, (char *)tmp); + if (sig) + return sig; + + if ((reglist & 4)) { + fpf->fpf_fpcr = *d++; + fpf->fpf_fpcr &= 0x0000fff0; + } + if ((reglist & 2)) { + fpf->fpf_fpsr = *d++; + fpf->fpf_fpsr &= 0x0ffffff8; + } + if ((reglist & 1)) { + fpf->fpf_fpiar = *d++; } } return sig; @@ -433,11 +461,11 @@ fpu_emul_fmovm(struct fpemu *fe, struct struct fpframe *fpf = fe->fe_fpframe; int word1, sig; int reglist, regmask, regnum; + int modreg; int fpu_to_mem, order; /* int w1_post_incr; */ int *fpregs; - insn->is_advance = 4; insn->is_datasize = 12; word1 = insn->is_word1; @@ -460,8 +488,22 @@ fpu_emul_fmovm(struct fpemu *fe, struct } reglist &= 0xFF; - /* Get effective address. (modreg=opcode&077) */ - sig = fpu_decode_ea(frame, insn, &insn->is_ea, insn->is_opcode); + /* Check an illegal mod/reg. */ + modreg = insn->is_opcode & 077; + if (fpu_to_mem) { + /* Dn, An, (An)+, PCrel, #imm are illegal. */ + if (modreg < 020 || (modreg >> 3) == 3 || modreg >= 072) { + return SIGILL; + } + } else { + /* Dn, An, -(An), #imm are illegal. */ + if (modreg < 020 || (modreg >> 3) == 4 || modreg >= 074) { + return SIGILL; + } + } + + /* Get effective address. */ + sig = fpu_decode_ea(frame, insn, &insn->is_ea, modreg); if (sig) return sig; @@ -507,73 +549,6 @@ fpu_emul_fmovm(struct fpemu *fe, struct } #endif /* !XM6i_FPE */ -struct fpn * -fpu_cmp(struct fpemu *fe) -{ - struct fpn *x = &fe->fe_f1, *y = &fe->fe_f2; - - /* take care of special cases */ - if (x->fp_class < 0) { - /* if x is a SNAN, result is x */ - return x; - } else if (y->fp_class < 0) { - /* if y is a SNAN, result is y */ - return y; - } else if (x->fp_class == FPC_INF) { - if (y->fp_class == FPC_INF) { - /* both infinities */ - if (x->fp_sign == y->fp_sign) { - /* return a signed zero */ - x->fp_class = FPC_ZERO; - } else { - /* return a faked number w/x's sign */ - x->fp_class = FPC_NUM; - x->fp_exp = 16383; - x->fp_mant[0] = FP_1; - } - } else { - /* y is a number */ - /* return a forged number w/x's sign */ - x->fp_class = FPC_NUM; - x->fp_exp = 16383; - x->fp_mant[0] = FP_1; - } - } else if (y->fp_class == FPC_INF) { - /* x is a Num but y is an Inf */ - /* return a forged number w/y's sign inverted */ - x->fp_class = FPC_NUM; - x->fp_sign = !y->fp_sign; - x->fp_exp = 16383; - x->fp_mant[0] = FP_1; - } else if (x->fp_class == FPC_ZERO && y->fp_class == FPC_ZERO) { - /* use x's sign */ - } else if (x->fp_sign == y->fp_sign && - x->fp_exp == y->fp_exp && - x->fp_mant[0] == y->fp_mant[0] && - x->fp_mant[1] == y->fp_mant[1] && - x->fp_mant[2] == y->fp_mant[2]) { - /* x and y are the same, return zero (with sign) */ - x->fp_class = FPC_ZERO; - } else { - /* - * x and y are both numbers, - * or pair of a number and a zero - */ - y->fp_sign = !y->fp_sign; - x = fpu_add(fe); /* (x - y) */ - /* - * FCMP does not set Inf bit in CC, so return a forged number - * (value doesn't matter) if Inf is the result of fsub. - */ - if (x->fp_class == FPC_INF) { - x->fp_class = FPC_NUM; - x->fp_exp = 16383; - x->fp_mant[0] = FP_1; - } - } - return x; -} - #if defined(XM6i_FPE) struct fpn * fpu_sglmul(struct fpemu *fe) @@ -653,7 +628,7 @@ fpu_sgldiv(struct fpemu *fe) #if !defined(XM6i_FPE) /* - * arithmetic oprations + * arithmetic operations */ static int fpu_emul_arith(struct fpemu *fe, struct instruction *insn) @@ -663,6 +638,7 @@ fpu_emul_arith(struct fpemu *fe, struct struct fpn *res; int word1, sig = 0; int regnum, format; + int modreg; int discard_result = 0; uint32_t buf[3]; #ifdef DEBUG_FPE @@ -716,13 +692,26 @@ fpu_emul_arith(struct fpemu *fe, struct return sig; } - /* Get effective address. (modreg=opcode&077) */ - sig = fpu_decode_ea(frame, insn, &insn->is_ea, insn->is_opcode); + /* Check an illegal mod/reg. */ + modreg = insn->is_opcode & 077; + if ((modreg >> 3) == 1/*An*/ || modreg >= 075) { + return SIGILL; + } + + /* Get effective address. */ + sig = fpu_decode_ea(frame, insn, &insn->is_ea, modreg); if (sig) { DPRINTF(("%s: error in fpu_decode_ea\n", __func__)); return sig; } + if (insn->is_ea.ea_flags == EA_DIRECT && + insn->is_datasize > 4) { + DPRINTF(("%s: attempted to fetch dbl/ext from reg\n", + __func__)); + return SIGILL; + } + DUMP_INSN(insn); #ifdef DEBUG_FPE @@ -1017,7 +1006,6 @@ test_cc(struct fpemu *fe, int pred) int fpsr; fpsr = fe->fe_fpsr; - fpsr &= ~FPSR_EXCP; /* clear all exceptions */ DPRINTF(("%s: fpsr=0x%08x\n", __func__, fpsr)); pred &= 0x3f; /* lowest 6 bits */ @@ -1091,6 +1079,10 @@ test_cc(struct fpemu *fe, int pred) if (sig_bsun && (fpsr & FPSR_NAN)) { fpsr |= FPSR_BSUN; } + /* if BSUN is set, IOP is set too */ + if ((fpsr & FPSR_BSUN)) { + fpsr |= FPSR_AIOP; + } /* put fpsr back */ fe->fe_fpframe->fpf_fpsr = fe->fe_fpsr = fpsr; @@ -1103,36 +1095,40 @@ test_cc(struct fpemu *fe, int pred) * type 1: fdbcc, fscc, ftrapcc * In this function, we know: * (opcode & 0x01C0) == 0x0040 + * return SIGILL for an illegal instruction. + * return SIGFPE if FTRAPcc's condition is met. */ static int fpu_emul_type1(struct fpemu *fe, struct instruction *insn) { struct frame *frame = fe->fe_frame; int advance, sig, branch, displ; + unsigned short sval; branch = test_cc(fe, insn->is_word1); + if (branch > 0) + return branch; fe->fe_fpframe->fpf_fpsr = fe->fe_fpsr; - insn->is_advance = 4; sig = 0; - switch (insn->is_opcode & 070) { case 010: /* fdbcc */ - if (branch == -1) { + if (branch) { /* advance */ insn->is_advance = 6; - } else if (!branch) { + } else { /* decrement Dn and if (Dn != -1) branch */ uint16_t count = frame->f_regs[insn->is_opcode & 7]; if (count-- != 0) { - displ = fusword((void *)(insn->is_pc + - insn->is_advance)); - if (displ < 0) { + if (ufetch_short((void *)(insn->is_pc + + insn->is_advance), + &sval)) { DPRINTF(("%s: fault reading " "displacement\n", __func__)); return SIGSEGV; } + displ = sval; /* sign-extend the displacement */ displ &= 0xffff; if (displ & 0x8000) { @@ -1149,8 +1145,6 @@ fpu_emul_type1(struct fpemu *fe, struct /* write it back */ frame->f_regs[insn->is_opcode & 7] &= 0xffff0000; frame->f_regs[insn->is_opcode & 7] |= (uint32_t)count; - } else { /* got a signal */ - sig = SIGFPE; } break; @@ -1168,12 +1162,9 @@ fpu_emul_type1(struct fpemu *fe, struct return SIGILL; break; } + insn->is_advance = advance; - if (branch == 0) { - /* no trap */ - insn->is_advance = advance; - sig = 0; - } else { + if (branch) { /* trap */ sig = SIGFPE; } @@ -1182,20 +1173,13 @@ fpu_emul_type1(struct fpemu *fe, struct /* FALLTHROUGH */ default: /* fscc */ - insn->is_advance = 4; insn->is_datasize = 1; /* always byte */ sig = fpu_decode_ea(frame, insn, &insn->is_ea, insn->is_opcode); if (sig) { break; } - if (branch == -1 || branch == 0) { - /* set result */ - sig = fpu_store_ea(frame, insn, &insn->is_ea, - (char *)&branch); - } else { - /* got an exception */ - sig = branch; - } + /* set result */ + sig = fpu_store_ea(frame, insn, &insn->is_ea, (char *)&branch); break; } return sig; @@ -1211,19 +1195,20 @@ fpu_emul_brcc(struct fpemu *fe, struct i { int displ, word2; int sig; + unsigned short sval; /* * Get branch displacement. */ - insn->is_advance = 4; displ = insn->is_word1; if (insn->is_opcode & 0x40) { - word2 = fusword((void *)(insn->is_pc + insn->is_advance)); - if (word2 < 0) { + if (ufetch_short((void *)(insn->is_pc + insn->is_advance), + &sval)) { DPRINTF(("%s: fault reading word2\n", __func__)); return SIGSEGV; } + word2 = sval; displ <<= 16; displ |= word2; insn->is_advance += 2; @@ -1257,3 +1242,23 @@ fpu_emul_brcc(struct fpemu *fe, struct i return 0; } #endif /* !XM6i_FPE */ + +#if defined(XM6i_FPE) +// fpn のタグ情報のうち、ゼロ、Inf、NAN、それ以外(ここでは正規化数)を返す。 +// 非正規化数とアンノーマル数はここに来る前に弾いてある。 +// 68040 の FSAVE で使う。 +int +fpu_gettag(const struct fpn *fpn) +{ + if (ISZERO(fpn)) { + return 1; + } + if (ISINF(fpn)) { + return 2; + } + if (ISNAN(fpn)) { + return 3; + } + return 0; +} +#endif