diff options
Diffstat (limited to 'sys/arch/sparc64/fpu/fpu.c')
-rw-r--r-- | sys/arch/sparc64/fpu/fpu.c | 961 |
1 files changed, 683 insertions, 278 deletions
diff --git a/sys/arch/sparc64/fpu/fpu.c b/sys/arch/sparc64/fpu/fpu.c index 9f48f341bb9..e12465598de 100644 --- a/sys/arch/sparc64/fpu/fpu.c +++ b/sys/arch/sparc64/fpu/fpu.c @@ -1,7 +1,38 @@ -/* $OpenBSD: fpu.c,v 1.3 2001/09/10 16:03:02 jason Exp $ */ +/* $OpenBSD: fpu.c,v 1.4 2001/09/15 23:42:36 jason Exp $ */ /* $NetBSD: fpu.c,v 1.11 2000/12/06 01:47:50 mrg Exp $ */ /* + * Copyright (c) 2001 Jason L. Wright (jason@thought.net) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Jason L. Wright + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * @@ -58,8 +89,27 @@ #include <sparc64/fpu/fpu_emu.h> #include <sparc64/fpu/fpu_extern.h> +int fpu_regoffset __P((int, int)); +int fpu_insn_fmov __P((struct fpstate64 *, struct fpemu *, union instr)); +int fpu_insn_fabs __P((struct fpstate64 *, struct fpemu *, union instr)); +int fpu_insn_fneg __P((struct fpstate64 *, struct fpemu *, union instr)); +int fpu_insn_itof __P((struct fpemu *, union instr, int, int *, + int *, u_int *)); +int fpu_insn_ftoi __P((struct fpemu *, union instr, int *, int, u_int *)); +int fpu_insn_ftof __P((struct fpemu *, union instr, int *, int *, u_int *)); +int fpu_insn_fsqrt __P((struct fpemu *, union instr, int *, int *, u_int *)); +int fpu_insn_fcmp __P((struct fpstate64 *, struct fpemu *, union instr, int)); +int fpu_insn_fmul __P((struct fpemu *, union instr, int *, int *, u_int *)); +int fpu_insn_fmulx __P((struct fpemu *, union instr, int *, int *, u_int *)); +int fpu_insn_fdiv __P((struct fpemu *, union instr, int *, int *, u_int *)); +int fpu_insn_fadd __P((struct fpemu *, union instr, int *, int *, u_int *)); +int fpu_insn_fsub __P((struct fpemu *, union instr, int *, int *, u_int *)); +int fpu_insn_fmovcc __P((struct fpstate64 *, union instr)); +int fpu_insn_fmovr __P((struct fpstate64 *, union instr)); +void fpu_fcopy __P((u_int *, u_int *, int)); + #ifdef DEBUG -int fpe_debug = 0xf; +int fpe_debug = 0; /* * Dump a `fpn' structure. @@ -73,17 +123,14 @@ fpu_dumpfpn(struct fpn *fp) fp->fp_sign ? '-' : ' ', fp->fp_mant[0], fp->fp_mant[1], fp->fp_mant[2], fp->fp_mant[3], fp->fp_exp); } - void -fpu_dumpstate(struct fpstate64 *fp) +fpu_dumpstate(struct fpstate64 *fs) { int i; - for (i = 0; i < 64; i++) { - printf("%%f%02d: %08x ", i, fp->fs_regs[i]); - if ((i & 3) == 3) - printf("\n"); - } + for (i = 0; i < 64; i++) + printf("%%f%02d: %08x%s", + i, fs->fs_regs[i], ((i & 3) == 3) ? "\n" : " "); } #endif @@ -127,6 +174,21 @@ static int fpu_types[] = { FPE_FLTINV, }; +void +fpu_fcopy(src, dst, type) + u_int *src, *dst; + int type; +{ + *dst++ = *src++; + if (type == FTYPE_SNG || type == FTYPE_INT) + return; + *dst++ = *src++; + if (type != FTYPE_EXT) + return; + *dst++ = *src++; + *dst = *src; +} + /* * The FPU gave us an exception. Clean up the mess. Note that the * fp queue can only have FPops in it, never load/store FP registers @@ -285,305 +347,154 @@ fpu_emulate(p, tf, fs) #endif /* + * Compute offset given a register and type. For 32 bit sparc, bits 1 and 0 + * must be zero for ext types, and bit 0 must be 0 for double and long types. + * For 64bit sparc, bit 1 must be zero for quad types, and bit 0 becomes bit + * 5 in the register offset for long, double, and quad types. + */ +int +fpu_regoffset(rx, type) + int rx, type; +{ + if (type == FTYPE_LNG || type == FTYPE_DBL || type == FTYPE_EXT) { + rx |= (rx & 1) << 5; + rx &= 0x3e; + if ((type == FTYPE_EXT) && (rx & 2)) + return (-1); + } + return (rx); +} + +/* * Execute an FPU instruction (one that runs entirely in the FPU; not * FBfcc or STF, for instance). On return, fe->fe_fs->fs_fsr will be * modified to reflect the setting the hardware would have left. - * - * Note that we do not catch all illegal opcodes, so you can, for instance, - * multiply two integers this way. */ int fpu_execute(fe, instr) - register struct fpemu *fe; + struct fpemu *fe; union instr instr; { - register struct fpn *fp; -#ifndef SUN4U - register int opf, rs1, rs2, rd, type, mask, fsr, cx; - register struct fpstate *fs; -#else /* SUN4U */ - register int opf, rs1, rs2, rd, type, mask, fsr, cx, i, cond; - register struct fpstate64 *fs; -#endif /* SUN4U */ + struct fpstate *fs; + int opf, rdtype, rd, err, mask, cx, fsr; u_int space[4]; - /* - * `Decode' and execute instruction. Start with no exceptions. - * The type of any i_opf opcode is in the bottom two bits, so we - * squish them out here. - */ + DPRINTF(FPE_INSN, ("op3: %x, opf %x\n", instr.i_opf.i_op3, + instr.i_opf.i_opf)); + DPRINTF(FPE_STATE, ("BEFORE:\n")); + DUMPSTATE(FPE_STATE, fe->fe_fpstate); opf = instr.i_opf.i_opf; - type = opf & 3; - mask = "\0\0\1\3"[type]; - rs1 = instr.i_opf.i_rs1 & ~mask; - rs2 = instr.i_opf.i_rs2 & ~mask; - rd = instr.i_opf.i_rd & ~mask; -#ifdef notdef - if ((rs1 | rs2 | rd) & mask) - return (BADREG); -#endif fs = fe->fe_fpstate; - DPRINTF(FPE_REG, ("BEFORE:\n")); - DUMPSTATE(FPE_REG, fs); fe->fe_fsr = fs->fs_fsr & ~FSR_CX; fe->fe_cx = 0; -#ifdef SUN4U - /* - * Check to see if we're dealing with a fancy cmove and handle - * it first. - */ - if (instr.i_op3.i_op3 == IOP3_FPop2 && (opf&0xff0) != (FCMP&0xff0)) { - switch (opf >>= 2) { - case FMVFC0 >> 2: - DPRINTF(FPE_INSN, ("fpu_execute: FMVFC0\n")); - cond = (fs->fs_fsr>>FSR_FCC_SHIFT)&FSR_FCC_MASK; - if (instr.i_fmovcc.i_cond != cond) return(0); /* success */ - rs1 = fs->fs_regs[rs2]; - goto mov; - case FMVFC1 >> 2: - DPRINTF(FPE_INSN, ("fpu_execute: FMVFC1\n")); - cond = (fs->fs_fsr>>FSR_FCC1_SHIFT)&FSR_FCC_MASK; - if (instr.i_fmovcc.i_cond != cond) return(0); /* success */ - rs1 = fs->fs_regs[rs2]; - goto mov; - case FMVFC2 >> 2: - DPRINTF(FPE_INSN, ("fpu_execute: FMVFC2\n")); - cond = (fs->fs_fsr>>FSR_FCC2_SHIFT)&FSR_FCC_MASK; - if (instr.i_fmovcc.i_cond != cond) return(0); /* success */ - rs1 = fs->fs_regs[rs2]; - goto mov; - case FMVFC3 >> 2: - DPRINTF(FPE_INSN, ("fpu_execute: FMVFC3\n")); - cond = (fs->fs_fsr>>FSR_FCC3_SHIFT)&FSR_FCC_MASK; - if (instr.i_fmovcc.i_cond != cond) return(0); /* success */ - rs1 = fs->fs_regs[rs2]; - goto mov; - case FMVIC >> 2: - /* Presume we're curproc */ - DPRINTF(FPE_INSN, ("fpu_execute: FMVIC\n")); - cond = (curproc->p_md.md_tf->tf_tstate>>TSTATE_CCR_SHIFT)&PSR_ICC; - if (instr.i_fmovcc.i_cond != cond) return(0); /* success */ - rs1 = fs->fs_regs[rs2]; - goto mov; - case FMVXC >> 2: - /* Presume we're curproc */ - DPRINTF(FPE_INSN, ("fpu_execute: FMVXC\n")); - cond = (curproc->p_md.md_tf->tf_tstate>>(TSTATE_CCR_SHIFT+XCC_SHIFT))&PSR_ICC; - if (instr.i_fmovcc.i_cond != cond) return(0); /* success */ - rs1 = fs->fs_regs[rs2]; - goto mov; - case FMVRZ >> 2: - /* Presume we're curproc */ - DPRINTF(FPE_INSN, ("fpu_execute: FMVRZ\n")); - rs1 = instr.i_fmovr.i_rs1; - if (rs1 != 0 && (int64_t)curproc->p_md.md_tf->tf_global[rs1] != 0) - return (0); /* success */ - rs1 = fs->fs_regs[rs2]; - goto mov; - case FMVRLEZ >> 2: - /* Presume we're curproc */ - DPRINTF(FPE_INSN, ("fpu_execute: FMVRLEZ\n")); - rs1 = instr.i_fmovr.i_rs1; - if (rs1 != 0 && (int64_t)curproc->p_md.md_tf->tf_global[rs1] > 0) - return (0); /* success */ - rs1 = fs->fs_regs[rs2]; - goto mov; - case FMVRLZ >> 2: - /* Presume we're curproc */ - DPRINTF(FPE_INSN, ("fpu_execute: FMVRLZ\n")); - rs1 = instr.i_fmovr.i_rs1; - if (rs1 == 0 || (int64_t)curproc->p_md.md_tf->tf_global[rs1] >= 0) - return (0); /* success */ - rs1 = fs->fs_regs[rs2]; - goto mov; - case FMVRNZ >> 2: - /* Presume we're curproc */ - DPRINTF(FPE_INSN, ("fpu_execute: FMVRNZ\n")); - rs1 = instr.i_fmovr.i_rs1; - if (rs1 == 0 || (int64_t)curproc->p_md.md_tf->tf_global[rs1] == 0) - return (0); /* success */ - rs1 = fs->fs_regs[rs2]; - goto mov; - case FMVRGZ >> 2: - /* Presume we're curproc */ - DPRINTF(FPE_INSN, ("fpu_execute: FMVRGZ\n")); - rs1 = instr.i_fmovr.i_rs1; - if (rs1 == 0 || (int64_t)curproc->p_md.md_tf->tf_global[rs1] <= 0) - return (0); /* success */ - rs1 = fs->fs_regs[rs2]; - goto mov; - case FMVRGEZ >> 2: - /* Presume we're curproc */ - DPRINTF(FPE_INSN, ("fpu_execute: FMVRGEZ\n")); - rs1 = instr.i_fmovr.i_rs1; - if (rs1 != 0 && (int64_t)curproc->p_md.md_tf->tf_global[rs1] < 0) - return (0); /* success */ - rs1 = fs->fs_regs[rs2]; - goto mov; - default: - DPRINTF(FPE_INSN, - ("fpu_execute: unknown v9 FP inst %x opf %x\n", - instr.i_int, opf)); - return (NOTFPU); - } - } -#endif /* SUN4U */ - DPRINTF(FPE_INSN, ("opf: %x\n", opf)); - switch (opf >>= 2) { - default: - DPRINTF(FPE_INSN, - ("fpu_execute: unknown basic FP inst %x opf %x\n", - instr.i_int, opf)); + if ((instr.i_int & 0xc0000000) != 0x80000000) return (NOTFPU); - case FMOV >> 2: /* these should all be pretty obvious */ - DPRINTF(FPE_INSN, ("fpu_execute: FMOV\n")); - rs1 = fs->fs_regs[rs2]; - goto mov; + if (instr.i_opf.i_op3 == IOP3_FPop2) { + switch (opf) { + case FCMPS: case FCMPD: case FCMPQ: + return (fpu_insn_fcmp(fs, fe, instr, 0)); + + case FCMPES: case FCMPED: case FCMPEQ: + return (fpu_insn_fcmp(fs, fe, instr, 1)); + + case FMVFC0S: case FMVFC0D: case FMVFC0Q: + case FMVFC1S: case FMVFC1D: case FMVFC1Q: + case FMVFC2S: case FMVFC2D: case FMVFC2Q: + case FMVFC3S: case FMVFC3D: case FMVFC3Q: + case FMVICS: case FMVICD: case FMVICQ: + case FMVXCS: case FMVXCD: case FMVXCQ: + return (fpu_insn_fmovcc(fs, instr)); + + case FMOVZS: case FMOVZD: case FMOVZQ: + case FMOVLEZS: case FMOVLEZD: case FMOVLEZQ: + case FMOVLZS: case FMOVLZD: case FMOVLZQ: + case FMOVNZS: case FMOVNZD: case FMOVNZQ: + case FMOVGZS: case FMOVGZD: case FMOVGZQ: + case FMOVGEZS: case FMOVGEZD: case FMOVGEZQ: + return (fpu_insn_fmovr(fs, instr)); + } + return (NOTFPU); + } - case FNEG >> 2: - DPRINTF(FPE_INSN, ("fpu_execute: FNEG\n")); - rs1 = fs->fs_regs[rs2] ^ (1 << 31); - goto mov; + if (instr.i_opf.i_op3 != IOP3_FPop1) + return (NOTFPU); - case FABS >> 2: - DPRINTF(FPE_INSN, ("fpu_execute: FABS\n")); - rs1 = fs->fs_regs[rs2] & ~(1 << 31); - mov: -#ifndef SUN4U - fs->fs_regs[rd] = rs1; -#else /* SUN4U */ - i = 1<<type; - fs->fs_regs[rd++] = rs1; - while (--i) - fs->fs_regs[rd++] = fs->fs_regs[++rs2]; -#endif /* SUN4U */ - fs->fs_fsr = fe->fe_fsr; - return (0); /* success */ + switch (instr.i_opf.i_opf) { + case FSTOX: case FDTOX: case FQTOX: + rdtype = FTYPE_LNG; + if ((err = fpu_insn_ftoi(fe, instr, &rd, rdtype, space)) != 0) + return (err); + break; - case FSQRT >> 2: - DPRINTF(FPE_INSN, ("fpu_execute: FSQRT\n")); - fpu_explode(fe, &fe->fe_f1, type, rs2); - fp = fpu_sqrt(fe); + case FSTOI: case FDTOI: case FQTOI: + rdtype = FTYPE_INT; + if ((err = fpu_insn_ftoi(fe, instr, &rd, rdtype, space)) != 0) + return (err); break; - case FADD >> 2: - DPRINTF(FPE_INSN, ("fpu_execute: FADD (%d %d -> %d)\n", - instr.i_opf.i_rs1, instr.i_opf.i_rs2, instr.i_opf.i_rd)); - fpu_explode(fe, &fe->fe_f1, type, rs1); - fpu_explode(fe, &fe->fe_f2, type, rs2); - fp = fpu_add(fe); + case FITOS: case FITOD: case FITOQ: + if ((err = fpu_insn_itof(fe, instr, FTYPE_INT, &rd, + &rdtype, space)) != 0) + return (err); break; - case FSUB >> 2: - DPRINTF(FPE_INSN, ("fpu_execute: FSUB\n")); - fpu_explode(fe, &fe->fe_f1, type, rs1); - fpu_explode(fe, &fe->fe_f2, type, rs2); - fp = fpu_sub(fe); + case FXTOS: case FXTOD: case FXTOQ: + if ((err = fpu_insn_itof(fe, instr, FTYPE_LNG, &rd, + &rdtype, space)) != 0) + return (err); break; - case FMUL >> 2: - DPRINTF(FPE_INSN, ("fpu_execute: FMUL\n")); - fpu_explode(fe, &fe->fe_f1, type, rs1); - fpu_explode(fe, &fe->fe_f2, type, rs2); - fp = fpu_mul(fe); + case FSTOD: case FSTOQ: + case FDTOS: case FDTOQ: + case FQTOS: case FQTOD: + if ((err = fpu_insn_ftof(fe, instr, &rd, &rdtype, space)) != 0) + return (err); break; - case FDIV >> 2: - DPRINTF(FPE_INSN, ("fpu_execute: FDIV\n")); - fpu_explode(fe, &fe->fe_f1, type, rs1); - fpu_explode(fe, &fe->fe_f2, type, rs2); - fp = fpu_div(fe); + case FMOVS: case FMOVD: case FMOVQ: + return (fpu_insn_fmov(fs, fe, instr)); + + case FNEGS: case FNEGD: case FNEGQ: + return (fpu_insn_fneg(fs, fe, instr)); + + case FABSS: case FABSD: case FABSQ: + return (fpu_insn_fabs(fs, fe, instr)); + + case FSQRTS: case FSQRTD: case FSQRTQ: + if ((err = fpu_insn_fsqrt(fe, instr, &rd, &rdtype, space)) != 0) + return (err); break; - case FCMP >> 2: - DPRINTF(FPE_INSN, ("fpu_execute: FCMP\n")); - fpu_explode(fe, &fe->fe_f1, type, rs1); - fpu_explode(fe, &fe->fe_f2, type, rs2); - fpu_compare(fe, 0); - goto cmpdone; - - case FCMPE >> 2: - DPRINTF(FPE_INSN, ("fpu_execute: FCMPE\n")); - fpu_explode(fe, &fe->fe_f1, type, rs1); - fpu_explode(fe, &fe->fe_f2, type, rs2); - fpu_compare(fe, 1); - cmpdone: - /* - * The only possible exception here is NV; catch it - * early and get out, as there is no result register. - */ - cx = fe->fe_cx; - fsr = fe->fe_fsr | (cx << FSR_CX_SHIFT); - if (cx != 0) { - if (fsr & (FSR_NV << FSR_TEM_SHIFT)) { - fs->fs_fsr = (fsr & ~FSR_FTT) | - (FSR_TT_IEEE << FSR_FTT_SHIFT); - return (FPE); - } - fsr |= FSR_NV << FSR_AX_SHIFT; - } - fs->fs_fsr = fsr; - return (0); + case FMULS: case FMULD: case FMULQ: + if ((err = fpu_insn_fmul(fe, instr, &rd, &rdtype, space)) != 0) + return (err); + break; - case FSMULD >> 2: - case FDMULX >> 2: - DPRINTF(FPE_INSN, ("fpu_execute: FSMULx\n")); - if (type == FTYPE_EXT) - return (NOTFPU); - fpu_explode(fe, &fe->fe_f1, type, rs1); - fpu_explode(fe, &fe->fe_f2, type, rs2); - type++; /* single to double, or double to quad */ - fp = fpu_mul(fe); + case FDIVS: case FDIVD: case FDIVQ: + if ((err = fpu_insn_fdiv(fe, instr, &rd, &rdtype, space)) != 0) + return (err); break; -#ifdef SUN4U - case FXTOS >> 2: - case FXTOD >> 2: - case FXTOQ >> 2: - DPRINTF(FPE_INSN, ("fpu_execute: FXTOx (%d -> %d)\n", - instr.i_opf.i_rs2, instr.i_opf.i_rd)); - type = FTYPE_LNG; - fpu_explode(fe, fp = &fe->fe_f1, type, rs2); - type = opf & 3; /* sneaky; depends on instruction encoding */ + case FSMULD: case FDMULQ: + if ((err = fpu_insn_fmulx(fe, instr, &rd, &rdtype, space)) != 0) + return (err); break; - case FTOX >> 2: - DPRINTF(FPE_INSN, ("fpu_execute: FTOX\n")); - rd = instr.i_opf.i_rd & (~1); - fpu_explode(fe, fp = &fe->fe_f1, type, rs2); - type = FTYPE_LNG; + case FADDS: case FADDD: case FADDQ: + if ((err = fpu_insn_fadd(fe, instr, &rd, &rdtype, space)) != 0) + return (err); break; -#endif /* SUN4U */ - case FTOS >> 2: - case FTOI >> 2: - rd = instr.i_opf.i_rd; - goto fto; - case FTOD >> 2: - rd = instr.i_opf.i_rd & (~1); - goto fto; - - case FTOQ >> 2: - rd = instr.i_opf.i_rd & (~3); - goto fto; - -fto: - DPRINTF(FPE_INSN, ("fpu_execute: FTOx (%d -> %d)\n", - instr.i_opf.i_rs2, instr.i_opf.i_rd)); - fpu_explode(fe, fp = &fe->fe_f1, type, rs2); - type = opf & 3; /* sneaky; depends on instruction encoding */ + case FSUBS: case FSUBD: case FSUBQ: + if ((err = fpu_insn_fsub(fe, instr, &rd, &rdtype, space)) != 0) + return (err); break; + default: + return (NOTFPU); } - /* - * ALU operation is complete. Collapse the result and then check - * for exceptions. If we got any, and they are enabled, do not - * alter the destination register, just stop with an exception. - * Otherwise set new current exceptions and accrue. - */ - fpu_implode(fe, fp, type, space); cx = fe->fe_cx; fsr = fe->fe_fsr; if (cx != 0) { @@ -598,16 +509,510 @@ fto: fsr |= (cx << FSR_CX_SHIFT) | (cx << FSR_AX_SHIFT); } fs->fs_fsr = fsr; - fs->fs_regs[rd] = space[0]; - if (type >= FTYPE_DBL || type == FTYPE_LNG) { - fs->fs_regs[rd + 1] = space[1]; - if (type > FTYPE_DBL) { - fs->fs_regs[rd + 2] = space[2]; - fs->fs_regs[rd + 3] = space[3]; + fpu_fcopy(space, fs->fs_regs + rd, rdtype); + DPRINTF(FPE_STATE, ("AFTER:\n")); + DUMPSTATE(FPE_STATE, fs); + return (0); +} + +/* + * Handler for FMOV[SDQ] emulation. + */ +int +fpu_insn_fmov(fs, fe, instr) + struct fpstate64 *fs; + struct fpemu *fe; + union instr instr; +{ + int opf = instr.i_opf.i_opf, rs, rd, rtype; + + rtype = opf & 3; + if (rtype == 0) + return (NOTFPU); + if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0) + return (NOTFPU); + if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0) + return (NOTFPU); + fpu_fcopy(fs->fs_regs + rs, fs->fs_regs + rd, rtype); + fs->fs_fsr = fe->fe_fsr; + return (0); +} + +/* + * Handler for FABS[SDQ] emulation. + */ +int +fpu_insn_fabs(fs, fe, instr) + struct fpstate64 *fs; + struct fpemu *fe; + union instr instr; +{ + int opf = instr.i_opf.i_opf, rs, rd, rtype; + + rtype = opf & 3; + if (rtype == 0) + return (NOTFPU); + if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0) + return (NOTFPU); + if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0) + return (NOTFPU); + fpu_fcopy(fs->fs_regs + rs, fs->fs_regs + rd, rtype); + fs->fs_regs[rd] = fs->fs_regs[rd] & ~(1 << 31); + fs->fs_fsr = fe->fe_fsr; + return (0); +} + +/* + * Handler for FNEG[SDQ] emulation. + */ +int +fpu_insn_fneg(fs, fe, instr) + struct fpstate64 *fs; + struct fpemu *fe; + union instr instr; +{ + int opf = instr.i_opf.i_opf, rs, rd, rtype; + + rtype = opf & 3; + if (rtype == 0) + return (NOTFPU); + if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0) + return (NOTFPU); + if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0) + return (NOTFPU); + fpu_fcopy(fs->fs_regs + rs, fs->fs_regs + rd, rtype); + fs->fs_regs[rd] = fs->fs_regs[rd] ^ (1 << 31); + fs->fs_fsr = fe->fe_fsr; + return (0); +} + +/* + * Handler for F[XI]TO[SDQ] emulation. + */ +int +fpu_insn_itof(fe, instr, rstype, rdp, rdtypep, space) + struct fpemu *fe; + union instr instr; + u_int *space; + int rstype, *rdp, *rdtypep; +{ + int opf = instr.i_opf.i_opf, rs, rd, rdtype; + + if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rstype)) < 0) + return (NOTFPU); + + rdtype = (opf >> 2) & 3; + if (rdtype == 0) + return (NOTFPU); + if ((rd = fpu_regoffset(instr.i_opf.i_rd, rdtype)) < 0) + return (NOTFPU); + + DPRINTF(FPE_INSN, ("itof %%f%d(%d, %d) -> %%f%d(%d, %d)\n", + rs, rstype, instr.i_opf.i_rs2, rd, rdtype, instr.i_opf.i_rd)); + fpu_explode(fe, &fe->fe_f1, rstype, rs); + fpu_implode(fe, &fe->fe_f1, rdtype, space); + *rdp = rd; + *rdtypep = rdtype; + return (0); +} + +/* + * Handler for F[SDQ]TO[XI] emulation. + */ +int +fpu_insn_ftoi(fe, instr, rdp, rdtype, space) + struct fpemu *fe; + union instr instr; + u_int *space; + int *rdp, rdtype; +{ + int opf = instr.i_opf.i_opf, rd, rstype, rs; + + rstype = opf & 3; + if (rstype == 0) + return (NOTFPU); + if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rstype)) < 0) + return (NOTFPU); + if ((rd = fpu_regoffset(instr.i_opf.i_rd, rdtype)) < 0) + return (NOTFPU); + + fpu_explode(fe, &fe->fe_f1, rstype, rs); + fpu_implode(fe, &fe->fe_f1, rdtype, space); + *rdp = rd; + return (0); +} + +/* + * Handler for F[SDQ]TO[SDQ] emulation. + */ +int +fpu_insn_ftof(fe, instr, rdp, rdtypep, space) + struct fpemu *fe; + union instr instr; + u_int *space; + int *rdp, *rdtypep; +{ + int opf = instr.i_opf.i_opf, rd, rs, rdtype, rstype; + + rstype = opf & 3; + rdtype = (opf >> 2) & 3; + + if ((rstype == rdtype) || (rstype == 0) || (rdtype == 0)) + return (NOTFPU); + + if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rstype)) < 0) + return (NOTFPU); + if ((rd = fpu_regoffset(instr.i_opf.i_rd, rdtype)) < 0) + return (NOTFPU); + + DPRINTF(FPE_INSN, ("ftof %%f%d(%d, %d) -> %%f%d(%d, %d)\n", + rs, rstype, instr.i_opf.i_rs2, rd, rdtype, instr.i_opf.i_rd)); + + fpu_explode(fe, &fe->fe_f1, rstype, rs); + fpu_implode(fe, &fe->fe_f1, rdtype, space); + *rdp = rd; + *rdtypep = rdtype; + return (0); +} + +/* + * Handler for FQSRT[SDQ] emulation. + */ +int +fpu_insn_fsqrt(fe, instr, rdp, rdtypep, space) + struct fpemu *fe; + union instr instr; + u_int *space; + int *rdp, *rdtypep; +{ + int opf = instr.i_opf.i_opf, rd, rs, rtype; + struct fpn *fp; + + rtype = opf & 3; + if (rtype == 0) + return (NOTFPU); + if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0) + return (NOTFPU); + if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0) + return (NOTFPU); + + fpu_explode(fe, &fe->fe_f1, rtype, rs); + fp = fpu_sqrt(fe); + fpu_implode(fe, fp, rtype, space); + *rdp = rd; + *rdtypep = rtype; + return (0); +} + +/* + * Handler for FCMP{E}[SDQ] emulation. + */ +int +fpu_insn_fcmp(fs, fe, instr, cmpe) + struct fpstate64 *fs; + struct fpemu *fe; + union instr instr; + int cmpe; +{ + int opf = instr.i_opf.i_opf, rs1, rs2, rtype, cx, fsr; + + rtype = opf & 3; + if (rtype == 0) + return (NOTFPU); + if ((rs1 = fpu_regoffset(instr.i_opf.i_rs1, rtype)) < 0) + return (NOTFPU); + if ((rs2 = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0) + return (NOTFPU); + + fpu_explode(fe, &fe->fe_f1, rtype, rs1); + fpu_explode(fe, &fe->fe_f2, rtype, rs2); + fpu_compare(fe, cmpe); + + /* + * The only possible exception here is NV; catch it early + * and get out, as there is no result register. + */ + cx = fe->fe_cx; + fsr = fe->fe_fsr | (cx << FSR_CX_SHIFT); + if (cx != 0) { + if (fsr & (FSR_NV << FSR_TEM_SHIFT)) { + fs->fs_fsr = (fsr & ~FSR_FTT) | + (FSR_TT_IEEE << FSR_FTT_SHIFT); + return (FPE); } + fsr |= FSR_NV << FSR_AX_SHIFT; + } + fs->fs_fsr = fsr; + return (0); +} + +/* + * Handler for FMUL[SDQ] emulation. + */ +int +fpu_insn_fmul(fe, instr, rdp, rdtypep, space) + struct fpemu *fe; + union instr instr; + int *rdp, *rdtypep; + u_int *space; +{ + struct fpn *fp; + int opf = instr.i_opf.i_opf, rd, rtype, rs1, rs2; + + rtype = opf & 3; + if (rtype == 0) + return (NOTFPU); + if ((rs1 = fpu_regoffset(instr.i_opf.i_rs1, rtype)) < 0) + return (NOTFPU); + if ((rs2 = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0) + return (NOTFPU); + if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0) + return (NOTFPU); + + fpu_explode(fe, &fe->fe_f1, rtype, rs1); + fpu_explode(fe, &fe->fe_f2, rtype, rs2); + fp = fpu_mul(fe); + fpu_implode(fe, fp, rtype, space); + *rdp = rd; + *rdtypep = rtype; + return (0); +} + +/* + * Handler for FSMULD, FDMULQ emulation. + */ +int +fpu_insn_fmulx(fe, instr, rdp, rdtypep, space) + struct fpemu *fe; + union instr instr; + int *rdp, *rdtypep; + u_int *space; +{ + struct fpn *fp; + int opf = instr.i_opf.i_opf, rd, rdtype, rstype, rs1, rs2; + + rstype = opf & 3; + rdtype = (opf >> 2) & 3; + if ((rstype != rdtype + 1) || (rstype == 0) || (rdtype == 0)) + return (NOTFPU); + if ((rs1 = fpu_regoffset(instr.i_opf.i_rs1, rstype)) < 0) + return (NOTFPU); + if ((rs2 = fpu_regoffset(instr.i_opf.i_rs2, rstype)) < 0) + return (NOTFPU); + if ((rd = fpu_regoffset(instr.i_opf.i_rd, rdtype)) < 0) + return (NOTFPU); + + fpu_explode(fe, &fe->fe_f1, rstype, rs1); + fpu_explode(fe, &fe->fe_f2, rstype, rs2); + fp = fpu_mul(fe); + fpu_implode(fe, fp, rdtype, space); + *rdp = rd; + *rdtypep = rdtype; + return (0); +} + +/* + * Handler for FDIV[SDQ] emulation. + */ +int +fpu_insn_fdiv(fe, instr, rdp, rdtypep, space) + struct fpemu *fe; + union instr instr; + int *rdp, *rdtypep; + u_int *space; +{ + struct fpn *fp; + int opf = instr.i_opf.i_opf, rd, rtype, rs1, rs2; + + rtype = opf & 3; + if (rtype == 0) + return (NOTFPU); + if ((rs1 = fpu_regoffset(instr.i_opf.i_rs1, rtype)) < 0) + return (NOTFPU); + if ((rs2 = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0) + return (NOTFPU); + if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0) + return (NOTFPU); + + fpu_explode(fe, &fe->fe_f1, rtype, rs1); + fpu_explode(fe, &fe->fe_f2, rtype, rs2); + fp = fpu_div(fe); + fpu_implode(fe, fp, rtype, space); + *rdp = rd; + *rdtypep = rtype; + return (0); +} + +/* + * Handler for FADD[SDQ] emulation. + */ +int +fpu_insn_fadd(fe, instr, rdp, rdtypep, space) + struct fpemu *fe; + union instr instr; + int *rdp, *rdtypep; + u_int *space; +{ + struct fpn *fp; + int opf = instr.i_opf.i_opf, rd, rtype, rs1, rs2; + + rtype = opf & 3; + if (rtype == 0) + return (NOTFPU); + if ((rs1 = fpu_regoffset(instr.i_opf.i_rs1, rtype)) < 0) + return (NOTFPU); + if ((rs2 = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0) + return (NOTFPU); + if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0) + return (NOTFPU); + + fpu_explode(fe, &fe->fe_f1, rtype, rs1); + fpu_explode(fe, &fe->fe_f2, rtype, rs2); + fp = fpu_add(fe); + fpu_implode(fe, fp, rtype, space); + *rdp = rd; + *rdtypep = rtype; + return (0); +} + +/* + * Handler for FSUB[SDQ] emulation. + */ +int +fpu_insn_fsub(fe, instr, rdp, rdtypep, space) + struct fpemu *fe; + union instr instr; + int *rdp, *rdtypep; + u_int *space; +{ + struct fpn *fp; + int opf = instr.i_opf.i_opf, rd, rtype, rs1, rs2; + + rtype = opf & 3; + if (rtype == 0) + return (NOTFPU); + if ((rs1 = fpu_regoffset(instr.i_opf.i_rs1, rtype)) < 0) + return (NOTFPU); + if ((rs2 = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0) + return (NOTFPU); + if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0) + return (NOTFPU); + + fpu_explode(fe, &fe->fe_f1, rtype, rs1); + fpu_explode(fe, &fe->fe_f2, rtype, rs2); + fp = fpu_sub(fe); + fpu_implode(fe, fp, rtype, space); + *rdp = rd; + *rdtypep = rtype; + return (0); +} + +/* + * Handler for FMOV[SDQ][cond] emulation. XXX Assumes we are curproc. + */ +int +fpu_insn_fmovcc(fs, instr) + struct fpstate64 *fs; + union instr instr; +{ + int rtype, rd, rs, cond; + + rtype = instr.i_fmovcc.i_opf_low & 3; + if ((rtype == 0) || (instr.i_int & 0x00040000)) + return (NOTFPU); + + if ((rd = fpu_regoffset(instr.i_fmovcc.i_rd, rtype)) < 0) + return (NOTFPU); + if ((rs = fpu_regoffset(instr.i_fmovcc.i_rs2, rtype)) < 0) + return (NOTFPU); + + switch (instr.i_fmovcc.i_opf_cc) { + case 0: + cond = (fs->fs_fsr >> FSR_FCC_SHIFT) & FSR_FCC_MASK; + break; + case 1: + cond = (fs->fs_fsr >> FSR_FCC1_SHIFT) & FSR_FCC_MASK; + break; + case 2: + cond = (fs->fs_fsr >> FSR_FCC2_SHIFT) & FSR_FCC_MASK; + break; + case 3: + cond = (fs->fs_fsr >> FSR_FCC3_SHIFT) & FSR_FCC_MASK; + break; + case 4: + cond = (curproc->p_md.md_tf->tf_tstate >> TSTATE_CCR_SHIFT) & + PSR_ICC; + break; + case 6: + cond = (curproc->p_md.md_tf->tf_tstate >> + (TSTATE_CCR_SHIFT + XCC_SHIFT)) & PSR_ICC; + break; + default: + return (NOTFPU); } - DPRINTF(FPE_REG, ("AFTER (rd %d, type %d, space %x/%x/%x/%x):\n", rd, type, - space[0], space[1], space[2], space[3])); - DUMPSTATE(FPE_REG, fs); - return (0); /* success */ + + if (instr.i_fmovcc.i_cond != cond) + return (0); + + fpu_fcopy(fs->fs_regs + rs, fs->fs_regs + rd, rtype); + return (0); +} + +/* + * Handler for FMOVR[icond][SDQ] emulation. XXX Assumes we are curproc. + */ +int +fpu_insn_fmovr(fs, instr) + struct fpstate64 *fs; + union instr instr; +{ + int rtype, rd, rs2, rs1; + + rtype = instr.i_fmovcc.i_opf_low & 3; + if ((rtype == 0) || (instr.i_int & 0x00002000)) + return (NOTFPU); + + if ((rd = fpu_regoffset(instr.i_fmovr.i_rd, rtype)) < 0) + return (NOTFPU); + if ((rs2 = fpu_regoffset(instr.i_fmovr.i_rs2, rtype)) < 0) + return (NOTFPU); + rs1 = instr.i_fmovr.i_rs1; + + switch (instr.i_fmovr.i_rcond) { + case 1: /* Z */ + if (rs1 != 0 && + (int64_t)curproc->p_md.md_tf->tf_global[rs1] != 0) + return (0); + break; + case 2: /* LEZ */ + if (rs1 != 0 && + (int64_t)curproc->p_md.md_tf->tf_global[rs1] > 0) + return (0); + break; + case 3: /* LZ */ + if (rs1 == 0 || + (int64_t)curproc->p_md.md_tf->tf_global[rs1] >= 0) + return (0); + break; + case 5: /* NZ */ + if (rs1 == 0 || + (int64_t)curproc->p_md.md_tf->tf_global[rs1] == 0) + return (0); + break; + case 6: /* NGZ */ + if (rs1 == 0 || + (int64_t)curproc->p_md.md_tf->tf_global[rs1] <= 0) + return (0); + break; + case 7: /* NGEZ */ + if (rs1 != 0 && + (int64_t)curproc->p_md.md_tf->tf_global[rs1] < 0) + return (0); + break; + default: + return (NOTFPU); + } + + fpu_fcopy(fs->fs_regs + rs2, fs->fs_regs + rd, rtype); + return (0); } |