/* $OpenBSD: fpu.c,v 1.12 2006/06/21 19:24:38 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.
*
* 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.
*
* This software was developed by the Computer Systems Engineering group
* at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
* contributed to Berkeley.
*
* All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Lawrence Berkeley Laboratory.
*
* 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. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
*
* @(#)fpu.c 8.1 (Berkeley) 6/11/93
*/
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/signal.h>
#include <sys/systm.h>
#include <sys/syslog.h>
#include <sys/signalvar.h>
#include <machine/instr.h>
#include <machine/reg.h>
#include <sparc64/fpu/fpu_emu.h>
#include <sparc64/fpu/fpu_extern.h>
int fpu_regoffset(int, int);
int fpu_insn_fmov(struct fpstate64 *, struct fpemu *, union instr);
int fpu_insn_fabs(struct fpstate64 *, struct fpemu *, union instr);
int fpu_insn_fneg(struct fpstate64 *, struct fpemu *, union instr);
int fpu_insn_itof(struct fpemu *, union instr, int, int *,
int *, u_int *);
int fpu_insn_ftoi(struct fpemu *, union instr, int *, int, u_int *);
int fpu_insn_ftof(struct fpemu *, union instr, int *, int *, u_int *);
int fpu_insn_fsqrt(struct fpemu *, union instr, int *, int *, u_int *);
int fpu_insn_fcmp(struct fpstate64 *, struct fpemu *, union instr, int);
int fpu_insn_fmul(struct fpemu *, union instr, int *, int *, u_int *);
int fpu_insn_fmulx(struct fpemu *, union instr, int *, int *, u_int *);
int fpu_insn_fdiv(struct fpemu *, union instr, int *, int *, u_int *);
int fpu_insn_fadd(struct fpemu *, union instr, int *, int *, u_int *);
int fpu_insn_fsub(struct fpemu *, union instr, int *, int *, u_int *);
int fpu_insn_fmovcc(struct proc *, struct fpstate64 *, union instr);
int fpu_insn_fmovr(struct proc *, struct fpstate64 *, union instr);
void fpu_fcopy(u_int *, u_int *, int);
#ifdef DEBUG
int fpe_debug = 0;
/*
* Dump a `fpn' structure.
*/
void
fpu_dumpfpn(struct fpn *fp)
{
static char *class[] = { "SNAN", "QNAN", "ZERO", "NUM", "INF" };
printf("%s %c.%x %x %x %xE%d", class[fp->fp_class + 2],
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 *fs)
{
int i;
for (i = 0; i < 64; i++)
printf("%%f%02d: %08x%s",
i, fs->fs_regs[i], ((i & 3) == 3) ? "\n" : " ");
}
#endif
/*
* fpu_execute returns the following error numbers (0 = no error):
*/
#define FPE 1 /* take a floating point exception */
#define NOTFPU 2 /* not an FPU instruction */
/*
* Translate current exceptions into `first' exception. The
* bits go the wrong way for ffs() (0x10 is most important, etc).
* There are only 5, so do it the obvious way.
*/
#define X1(x) x
#define X2(x) x,x
#define X4(x) x,x,x,x
#define X8(x) X4(x),X4(x)
#define X16(x) X8(x),X8(x)
static char cx_to_trapx[] = {
X1(FSR_NX),
X2(FSR_DZ),
X4(FSR_UF),
X8(FSR_OF),
X16(FSR_NV)
};
static u_char fpu_codes[] = {
X1(FPE_FLTINEX_TRAP),
X2(FPE_FLTDIV_TRAP),
X4(FPE_FLTUND_TRAP),
X8(FPE_FLTOVF_TRAP),
X16(FPE_FLTOPERR_TRAP)
};
static int fpu_types[] = {
X1(FPE_FLTRES),
X2(FPE_FLTDIV),
X4(FPE_FLTUND),
X8(FPE_FLTOVF),
X16(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
* nor FBfcc instructions. Experiments with `crashme' prove that
* unknown FPops do enter the queue, however.
*/
void
fpu_cleanup(p, fs)
register struct proc *p;
register struct fpstate64 *fs;
{
register int i, fsr = fs->fs_fsr, error;
union instr instr;
union sigval sv;
struct fpemu fe;
sv.sival_int = p->p_md.md_tf->tf_pc; /* XXX only approximate */
switch ((fsr >> FSR_FTT_SHIFT) & FSR_FTT_MASK) {
case FSR_TT_NONE:
#if 0
/* XXX I'm not sure how we get here, but ignoring the trap */
/* XXX seems to work in my limited tests */
/* XXX More research to be done =) */
panic("fpu_cleanup 1"); /* ??? */
#else
printf("fpu_cleanup 1\n");
#endif
break;
case FSR_TT_IEEE:
if ((i = fsr & FSR_CX) == 0)
panic("fpu ieee trap, but no exception");
trapsignal(p, SIGFPE, fpu_codes[i - 1], fpu_types[i - 1], sv);
break; /* XXX should return, but queue remains */
case FSR_TT_UNFIN:
if (fs->fs_qsize == 0) {
printf("fpu_cleanup: unfinished fpop");
/* The book says reexecute or emulate. */
return;
}
break;
case FSR_TT_UNIMP:
if (fs->fs_qsize == 0)
panic("fpu_cleanup: unimplemented fpop");
break;
case FSR_TT_SEQ:
panic("fpu sequence error");
/* NOTREACHED */
case FSR_TT_HWERR:
log(LOG_ERR, "fpu hardware error (%s[%d])\n",
p->p_comm, p->p_pid);
uprintf("%s[%d]: fpu hardware error\n", p->p_comm, p->p_pid);
trapsignal(p, SIGFPE, -1, FPE_FLTINV, sv); /* ??? */
goto out;
default:
printf("fsr=0x%x\n", fsr);
panic("fpu error");
}
/* emulate the instructions left in the queue */
fe.fe_fpstate = fs;
for (i = 0; i < fs->fs_qsize; i++) {
instr.i_int = fs->fs_queue[i].fq_instr;
if (instr.i_any.i_op != IOP_reg ||
(instr.i_op3.i_op3 != IOP3_FPop1 &&
instr.i_op3.i_op3 != IOP3_FPop2))
panic("bogus fpu queue");
error = fpu_execute(p, &fe, instr);
switch (error) {
case 0:
continue;
case FPE:
trapsignal(p, SIGFPE,
fpu_codes[(fs->fs_fsr & FSR_CX) - 1],
fpu_types[(fs->fs_fsr & FSR_CX) - 1], sv);
break;
case NOTFPU:
trapsignal(p, SIGILL, 0, ILL_COPROC, sv);
break;
default:
panic("fpu_cleanup 3");
/* NOTREACHED */
}
/* XXX should stop here, but queue remains */
}
out:
fs->fs_qsize = 0;
}
/*
* 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.
*/
int
fpu_execute(fpproc, fe, instr)
struct proc *fpproc;
struct fpemu *fe;
union instr instr;
{
struct fpstate *fs;
int opf, rdtype, rd, err, mask, cx, fsr;
u_int space[4];
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;
fs = fe->fe_fpstate;
fe->fe_fsr = fs->fs_fsr & ~FSR_CX;
fe->fe_cx = 0;
if ((instr.i_int & 0xc0000000) != 0x80000000)
return (NOTFPU);
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(fpproc, 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(fpproc, fs, instr));
}
return (NOTFPU);
}
if (instr.i_opf.i_op3 != IOP3_FPop1)
return (NOTFPU);
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 FSTOI: case FDTOI: case FQTOI:
rdtype = FTYPE_INT;
if ((err = fpu_insn_ftoi(fe, instr, &rd, rdtype, space)) != 0)
return (err);
break;
case FITOS: case FITOD: case FITOQ:
if ((err = fpu_insn_itof(fe, instr, FTYPE_INT, &rd,
&rdtype, space)) != 0)
return (err);
break;
case FXTOS: case FXTOD: case FXTOQ:
if ((err = fpu_insn_itof(fe, instr, FTYPE_LNG, &rd,
&rdtype, space)) != 0)
return (err);
break;
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 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 FMULS: case FMULD: case FMULQ:
if ((err = fpu_insn_fmul(fe, instr, &rd, &rdtype, space)) != 0)
return (err);
break;
case FDIVS: case FDIVD: case FDIVQ:
if ((err = fpu_insn_fdiv(fe, instr, &rd, &rdtype, space)) != 0)
return (err);
break;
case FSMULD: case FDMULQ:
if ((err = fpu_insn_fmulx(fe, instr, &rd, &rdtype, space)) != 0)
return (err);
break;
case FADDS: case FADDD: case FADDQ:
if ((err = fpu_insn_fadd(fe, instr, &rd, &rdtype, space)) != 0)
return (err);
break;
case FSUBS: case FSUBD: case FSUBQ:
if ((err = fpu_insn_fsub(fe, instr, &rd, &rdtype, space)) != 0)
return (err);
break;
default:
return (NOTFPU);
}
cx = fe->fe_cx;
fsr = fe->fe_fsr;
if (cx != 0) {
mask = (fsr >> FSR_TEM_SHIFT) & FSR_TEM_MASK;
if (cx & mask) {
/* not accrued??? */
fs->fs_fsr = (fsr & ~FSR_FTT) |
(FSR_TT_IEEE << FSR_FTT_SHIFT) |
(cx_to_trapx[(cx & mask) - 1] << FSR_CX_SHIFT);
return (FPE);
}
fsr |= (cx << FSR_CX_SHIFT) | (cx << FSR_AX_SHIFT);
}
fs->fs_fsr = fsr;
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.
*/
int
fpu_insn_fmovcc(fpproc, fs, instr)
struct proc *fpproc;
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 = (fpproc->p_md.md_tf->tf_tstate >> TSTATE_CCR_SHIFT) &
PSR_ICC;
break;
case 6:
cond = (fpproc->p_md.md_tf->tf_tstate >>
(TSTATE_CCR_SHIFT + XCC_SHIFT)) & PSR_ICC;
break;
default:
return (NOTFPU);
}
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.
*/
int
fpu_insn_fmovr(fpproc, fs, instr)
struct proc *fpproc;
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)fpproc->p_md.md_tf->tf_global[rs1] != 0)
return (0);
break;
case 2: /* LEZ */
if (rs1 != 0 &&
(int64_t)fpproc->p_md.md_tf->tf_global[rs1] > 0)
return (0);
break;
case 3: /* LZ */
if (rs1 == 0 ||
(int64_t)fpproc->p_md.md_tf->tf_global[rs1] >= 0)
return (0);
break;
case 5: /* NZ */
if (rs1 == 0 ||
(int64_t)fpproc->p_md.md_tf->tf_global[rs1] == 0)
return (0);
break;
case 6: /* NGZ */
if (rs1 == 0 ||
(int64_t)fpproc->p_md.md_tf->tf_global[rs1] <= 0)
return (0);
break;
case 7: /* NGEZ */
if (rs1 != 0 &&
(int64_t)fpproc->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);
}