--- nono/fpe/fpu_emulate.c 2026/04/29 17:05:27 1.1.1.4 +++ 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.41 2023/09/17 13:14:08 andvar Exp $ */ +/* $NetBSD: fpu_emulate.c,v 1.49 2025/01/06 07:34:24 isaki Exp $ */ /* * Copyright (c) 1995 Gordon W. Ross @@ -152,32 +152,36 @@ fpu_emulate(struct frame *frame, struct */ if (optype == 0x0000) { /* type=0: generic */ - if ((sval & 0xc000) == 0xc000) { - DPRINTF(("%s: fmovm FPr\n", __func__)); - sig = fpu_emul_fmovm(&fe, &insn); - } else if ((sval & 0xc000) == 0x8000) { - DPRINTF(("%s: fmovm FPcr\n", __func__)); - sig = fpu_emul_fmovmcr(&fe, &insn); - } else 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); + 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. */ @@ -188,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) */ @@ -341,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; @@ -431,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; @@ -458,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; @@ -594,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 @@ -647,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 @@ -948,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 */ @@ -1022,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; @@ -1034,6 +1095,8 @@ 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) @@ -1043,17 +1106,17 @@ fpu_emul_type1(struct fpemu *fe, struct 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]; @@ -1082,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; @@ -1101,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; } @@ -1115,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; @@ -1149,7 +1200,6 @@ fpu_emul_brcc(struct fpemu *fe, struct i /* * Get branch displacement. */ - insn->is_advance = 4; displ = insn->is_word1; if (insn->is_opcode & 0x40) { @@ -1192,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