summaryrefslogtreecommitdiff
path: root/sys/arch/kbus/fpu/fpu.c
diff options
context:
space:
mode:
authorgingold <gingold@cvs.openbsd.org>1997-10-14 07:25:35 +0000
committergingold <gingold@cvs.openbsd.org>1997-10-14 07:25:35 +0000
commitf6491d400ca651a8a1493d72c6a74c622aa231b1 (patch)
tree39f29255154f230f637c12e9214f12a9e64bd9d4 /sys/arch/kbus/fpu/fpu.c
parent7a9e3739a66bd0fadfdc611c72e879fcc6f9ef01 (diff)
Gingold's port for kbus Series5 machine. Not fully finished and not very stable
Diffstat (limited to 'sys/arch/kbus/fpu/fpu.c')
-rw-r--r--sys/arch/kbus/fpu/fpu.c394
1 files changed, 394 insertions, 0 deletions
diff --git a/sys/arch/kbus/fpu/fpu.c b/sys/arch/kbus/fpu/fpu.c
new file mode 100644
index 00000000000..2eaef3290b1
--- /dev/null
+++ b/sys/arch/kbus/fpu/fpu.c
@@ -0,0 +1,394 @@
+/* $NetBSD: fpu.c,v 1.3 1996/03/14 19:41:49 christos Exp $ */
+
+/*
+ * 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. 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, Berkeley and its contributors.
+ * 4. 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 <fpu/fpu_emu.h>
+#include <fpu/fpu_extern.h>
+
+/*
+ * 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[] = {
+ FPE_FLTRES,
+ FPE_FLTDIV,
+ FPE_FLTUND,
+ FPE_FLTOVF,
+ FPE_FLTINV,
+};
+
+/*
+ * 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 fpstate *fs;
+{
+ register int i, fsr = fs->fs_fsr, error;
+ union instr instr;
+ caddr_t pc = (caddr_t)p->p_md.md_tf->tf_pc; /* XXX only approximate */
+ struct fpemu fe;
+
+ switch ((fsr >> FSR_FTT_SHIFT) & FSR_FTT_MASK) {
+
+ case FSR_TT_NONE:
+ panic("fpu_cleanup 1"); /* ??? */
+ 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 - i], pc);
+ break; /* XXX should return, but queue remains */
+
+ case FSR_TT_UNFIN:
+ case FSR_TT_UNIMP:
+ if (fs->fs_qsize == 0)
+ panic("fpu_cleanup 2");
+ 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, pc); /* ??? */
+ goto out;
+
+ default:
+ printf("fsr=%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(&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], pc);
+ break;
+
+ case NOTFPU:
+ trapsignal(p, SIGILL, 0, ILL_COPROC, pc);
+ break;
+
+ default:
+ panic("fpu_cleanup 3");
+ /* NOTREACHED */
+ }
+ /* XXX should stop here, but queue remains */
+ }
+out:
+ fs->fs_qsize = 0;
+}
+
+#ifdef notyet
+/*
+ * If we have no FPU at all (are there any machines like this out
+ * there!?) we have to emulate each instruction, and we need a pointer
+ * to the trapframe so that we can step over them and do FBfcc's.
+ * We know the `queue' is empty, though; we just want to emulate
+ * the instruction at tf->tf_pc.
+ */
+fpu_emulate(p, tf, fs)
+ struct proc *p;
+ register struct trapframe *tf;
+ register struct fpstate *fs;
+{
+
+ do {
+ fetch instr from pc
+ decode
+ if (integer instr) {
+ /*
+ * We do this here, rather than earlier, to avoid
+ * losing even more badly than usual.
+ */
+ if (p->p_addr->u_pcb.pcb_uw) {
+ write_user_windows();
+ if (rwindow_save(p))
+ sigexit(p, SIGILL);
+ }
+ if (loadstore) {
+ do_it;
+ pc = npc, npc += 4
+ } else if (fbfcc) {
+ do_annul_stuff;
+ } else
+ return;
+ } else if (fpu instr) {
+ fe.fe_fsr = fs->fs_fsr &= ~FSR_CX;
+ error = fpu_execute(&fe, fs, instr);
+ switch (error) {
+ etc;
+ }
+ } else
+ return;
+ if (want to reschedule)
+ return;
+ } while (error == 0);
+}
+#endif
+
+/*
+ * 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;
+ union instr instr;
+{
+ register struct fpn *fp;
+ register int opf, rs1, rs2, rd, type, mask, fsr, cx;
+ register struct fpstate *fs;
+ 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.
+ */
+ 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;
+ fe->fe_fsr = fs->fs_fsr & ~FSR_CX;
+ fe->fe_cx = 0;
+ switch (opf >>= 2) {
+
+ default:
+ return (NOTFPU);
+
+ case FMOV >> 2: /* these should all be pretty obvious */
+ rs1 = fs->fs_regs[rs2];
+ goto mov;
+
+ case FNEG >> 2:
+ rs1 = fs->fs_regs[rs2] ^ (1 << 31);
+ goto mov;
+
+ case FABS >> 2:
+ rs1 = fs->fs_regs[rs2] & ~(1 << 31);
+ mov:
+ fs->fs_regs[rd] = rs1;
+ fs->fs_fsr = fe->fe_fsr;
+ return (0); /* success */
+
+ case FSQRT >> 2:
+ fpu_explode(fe, &fe->fe_f1, type, rs2);
+ fp = fpu_sqrt(fe);
+ break;
+
+ case FADD >> 2:
+ fpu_explode(fe, &fe->fe_f1, type, rs1);
+ fpu_explode(fe, &fe->fe_f2, type, rs2);
+ fp = fpu_add(fe);
+ break;
+
+ case FSUB >> 2:
+ fpu_explode(fe, &fe->fe_f1, type, rs1);
+ fpu_explode(fe, &fe->fe_f2, type, rs2);
+ fp = fpu_sub(fe);
+ break;
+
+ case FMUL >> 2:
+ fpu_explode(fe, &fe->fe_f1, type, rs1);
+ fpu_explode(fe, &fe->fe_f2, type, rs2);
+ fp = fpu_mul(fe);
+ break;
+
+ case FDIV >> 2:
+ fpu_explode(fe, &fe->fe_f1, type, rs1);
+ fpu_explode(fe, &fe->fe_f2, type, rs2);
+ fp = fpu_div(fe);
+ break;
+
+ case FCMP >> 2:
+ 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:
+ 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 FSMULD >> 2:
+ case FDMULX >> 2:
+ 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);
+ break;
+
+ case FTOS >> 2:
+ case FTOD >> 2:
+ case FTOX >> 2:
+ case FTOI >> 2:
+ fpu_explode(fe, fp = &fe->fe_f1, type, rs2);
+ type = opf & 3; /* sneaky; depends on instruction encoding */
+ break;
+ }
+
+ /*
+ * 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) {
+ 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;
+ fs->fs_regs[rd] = space[0];
+ if (type >= FTYPE_DBL) {
+ 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];
+ }
+ }
+ return (0); /* success */
+}