diff options
Diffstat (limited to 'sys')
25 files changed, 6606 insertions, 530 deletions
diff --git a/sys/arch/m68k/fpe/README b/sys/arch/m68k/fpe/README new file mode 100644 index 00000000000..2605d310b80 --- /dev/null +++ b/sys/arch/m68k/fpe/README @@ -0,0 +1,167 @@ +* $NetBSD: README,v 1.4 1995/11/05 04:23:00 briggs Exp $ +* NetBSD/m68k FPE (floating point emulation) README file +* Created Oct/??/95 by kenn@remus.rutgers.edu (Ken Nakata) +* Last updated Nov/04/95 by kenn + +1. INSTALLATION AND COMPILATION + +To compile a kernel with FPE built-in, do the following: + +1) Add a line "options FPU_EMULATE" to your config file. If you are +going to use the resulted kernel on a machine with an FPU for +debugging purpose, add "options DEBUG_WITH_FPU" as well. + +2) Follow the usual procedure to build a new kernel. + +NOTE: If you add "options DEBUG_WITH_FPU", FPE will accept cpID=6 as +emulated FPU. You will need a modified gas that generates cpID=6 for +floating point instructions, instead of normal cpID=1. Mount unionfs +or copy the gas source directory and apply the following patch: + +*** /usr/src/gnu/usr.bin/gas/config/tc-m68k.c Mon Nov 21 16:30:41 1994 +--- gas/config/tc-m68k.c Fri Sep 29 07:59:06 1995 +*************** +*** 1275,1281 **** + /* memcpy((char *)(&the_ins.operands[1]), (char *)(&the_ins.operands[0]), opsfound*sizeof(the_ins.operands[0])); */ + memset((char *)(&the_ins.operands[0]), '\0', sizeof(the_ins.operands[0])); + the_ins.operands[0].mode=MSCR; +! the_ins.operands[0].reg=COPNUM; /* COP #1 */ + opsfound++; + } + +--- 1275,1281 ---- + /* memcpy((char *)(&the_ins.operands[1]), (char *)(&the_ins.operands[0]), opsfound*sizeof(the_ins.operands[0])); */ + memset((char *)(&the_ins.operands[0]), '\0', sizeof(the_ins.operands[0])); + the_ins.operands[0].mode=MSCR; +! the_ins.operands[0].reg=COP5; /* COP #6 */ + opsfound++; + } + + +Also, with the DEBUG_WITH_FPU option, you will be able to run only ONE +process that uses FPE at once to get correct results. + + +2. MISSING PARTS + +For missing instructions, refer to the Section 3. Other than that, +there is one thing that is missing from this version of FPE: packed +BCD support. + +I have no plan to support it since it's rarely used. However, all we +need to support it is explosion/implosion functions between the +internal FP representation and the m68k PBCD format, so you are more +than welcome to write such functions if you wish to. + + +3. IMPLEMENTED INSTRUCTIONS + +This is the list of implemented and unimplemented FPU instructions. +All 040's directly supported type 0 instructions are already +implemented except FSGLDIV and FSGLMUL. + +Type field = bit 8-6 of opcode word + +* Implemented Instructions + +Type=0: FMOVE (mem->FPr), FINT, FINTRZ, FSQRT, FABS, FNEG, FGETEXP, + FGETMAN, FDIV, FADD, FMUL, FSGLDIV(*), FSCALE, FSGLMUL(*), FSUB, + FCMP, FTST, FMOVE (FPr->mem), FMOVEM (FPr), FMOVEM (FPcr), + FMOVECR, FLOGNP1, FLOGN, FLOG10, FLOG2, FMOD, FREM + +Type=1: FDBcc, FScc, FTRAPcc, + +Type=2: FBcc (word, incl. FNOP) + +Type=3: FBcc (long) + +Type=4: none + +Type=5: none + + *: currently FSGLMUL and FSGLDIV are just aliases of + FMUL and FDIV, respectively + +* Unimplemented Instructions + +Type=0: FSINH, FETOXM1, FTANH, FATAN, FASIN, FATANH, FSIN, FTAN, + FETOX, FTWOTOX, FTENTOX, FCOSH, FACOS, FCOS, FSINCOS + +Type=1: none + +Type=2: none + +Type=3: none + +Type=4: FSAVE + +Type=5: FRESTORE + + +4. HOW TO ADD A NEW INSTRUCTION SUPPORT + +Since we need not support FSAVE and FRESTORE operations, all +instructions we have to implement are type 0, all of which are +arithmetic operations. It is particularly easy to add a new +arithmetic instruction to the existing ones (not that it is easy to +write a "stable" function to perform floating point operation. That's +entirely another matter). In "fpu_emulate.c", there's a function +fpu_emul_arith() which calls emulation functions for all arithmetic +operations. In it, there's a large switch() { case ... } which +dispatches each instruction emulator. An emulation function of any +type 0 arithmetic instruction follows this prototype: + + struct fpn *fpu_op(struct fpemu *fe); + +Where fe is a pointer to a struct fpemu in which frame, fpframe, and +fetched operands are accessible. That's right, you don't have to +fetch the operands by yourself in your emulation funtion. For +instance, the parts calling FSQRT, FSUB, FADD and FTST look like: + + switch(word1 & 0x3F) { +[...] + case 0x04: /* fsqrt */ + res = fpu_sqrt(fe); + break; +[...] + case 0x28: /* fsub */ + fe->fe_f2.fp_sign = !fe->fe_f2.fp_sign; /* f2 = -f2 */ + case 0x22: /* fadd */ + res = fpu_add(fe); + break; +[...] + case 0x3A: /* ftst */ + res = &fe->fe_f2; + no_store = 1; + break; +[...] + default: + sig = SIGILL; + } /* switch */ + +Here, fe->fe_f1 and fe->fe_f2 are fetched operands. You can use +fe->fe_f3 for storing the result, or you can return a pointer to +either operand if you want to. At any rate, you have to follow +the following rules: + + 1) A dyadic instruction takes two operands fe->fe_f1 and fe->fe_f2. + 2) A monadic instruction takes one operands fe->fe_f2 (NOT fe_f1). + 3) Must return a pointer to struct fpn where the result is stored, + and assign the pointer to the variable "res". + 4) If exceptions are detected, set corresponding bits in fe->fe_fpsr. + The rest is taken care of in fpu_emul_arith(). + 5) Condition code need not be calculated. It's taken care of in + fpu_emul_arith(). + +Actually, after above was written, stubs for the missing functions were +added to the source, so you do not have to change fpu_emul_arith() at +all. Function names and prototypes are in fpu_arith_proto.h, and all +except fpu_sincos() follow the rules above. fpu_sincos() is declared +as + + struct fpn *fpu_sincos(struct fpemu *fe, int cosreg); + +where cosreg is the FP register number to which cosine of the argument +is calculated and assigned. Sine of the argument is stored into the +destination register in the same manner as the other arithmetic +functions. diff --git a/sys/arch/m68k/fpe/files.fpe b/sys/arch/m68k/fpe/files.fpe new file mode 100644 index 00000000000..416fcc417d1 --- /dev/null +++ b/sys/arch/m68k/fpe/files.fpe @@ -0,0 +1,24 @@ +# $NetBSD: files.fpe,v 1.2 1995/11/03 04:51:51 briggs Exp $ + +# Config(.new) file for m68k floating point emulator. +# Included by ports that need it. + +file arch/m68k/fpe/fpu_add.c fpu_emulate +file arch/m68k/fpe/fpu_calcea.c fpu_emulate +file arch/m68k/fpe/fpu_div.c fpu_emulate +file arch/m68k/fpe/fpu_emulate.c fpu_emulate +file arch/m68k/fpe/fpu_exp.c fpu_emulate +file arch/m68k/fpe/fpu_explode.c fpu_emulate +file arch/m68k/fpe/fpu_fmovecr.c fpu_emulate +file arch/m68k/fpe/fpu_fscale.c fpu_emulate +file arch/m68k/fpe/fpu_fstore.c fpu_emulate +file arch/m68k/fpe/fpu_getexp.c fpu_emulate +file arch/m68k/fpe/fpu_hyperb.c fpu_emulate +file arch/m68k/fpe/fpu_implode.c fpu_emulate +file arch/m68k/fpe/fpu_int.c fpu_emulate +file arch/m68k/fpe/fpu_log.c fpu_emulate +file arch/m68k/fpe/fpu_mul.c fpu_emulate +file arch/m68k/fpe/fpu_rem.c fpu_emulate +file arch/m68k/fpe/fpu_sqrt.c fpu_emulate +file arch/m68k/fpe/fpu_subr.c fpu_emulate +file arch/m68k/fpe/fpu_trig.c fpu_emulate diff --git a/sys/arch/m68k/fpe/fpu_add.c b/sys/arch/m68k/fpe/fpu_add.c new file mode 100644 index 00000000000..48e332b3dcd --- /dev/null +++ b/sys/arch/m68k/fpe/fpu_add.c @@ -0,0 +1,211 @@ +/* $NetBSD: fpu_add.c,v 1.1 1995/11/03 04:46:58 briggs 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_add.c 8.1 (Berkeley) 6/11/93 + */ + +/* + * Perform an FPU add (return x + y). + * + * To subtract, negate y and call add. + */ + +#include <sys/types.h> + +#include <machine/reg.h> + +#include "fpu_arith.h" +#include "fpu_emulate.h" + +struct fpn * +fpu_add(fe) + register struct fpemu *fe; +{ + register struct fpn *x = &fe->fe_f1, *y = &fe->fe_f2, *r; + register u_int r0, r1, r2, r3; + register int rd; + + /* + * Put the `heavier' operand on the right (see fpu_emu.h). + * Then we will have one of the following cases, taken in the + * following order: + * + * - y = NaN. Implied: if only one is a signalling NaN, y is. + * The result is y. + * - y = Inf. Implied: x != NaN (is 0, number, or Inf: the NaN + * case was taken care of earlier). + * If x = -y, the result is NaN. Otherwise the result + * is y (an Inf of whichever sign). + * - y is 0. Implied: x = 0. + * If x and y differ in sign (one positive, one negative), + * the result is +0 except when rounding to -Inf. If same: + * +0 + +0 = +0; -0 + -0 = -0. + * - x is 0. Implied: y != 0. + * Result is y. + * - other. Implied: both x and y are numbers. + * Do addition a la Hennessey & Patterson. + */ + ORDER(x, y); + if (ISNAN(y)) + return (y); + if (ISINF(y)) { + if (ISINF(x) && x->fp_sign != y->fp_sign) + return (fpu_newnan(fe)); + return (y); + } + rd = (fe->fe_fpcr & FPCR_ROUND); + if (ISZERO(y)) { + if (rd != FPCR_MINF) /* only -0 + -0 gives -0 */ + y->fp_sign &= x->fp_sign; + else /* any -0 operand gives -0 */ + y->fp_sign |= x->fp_sign; + return (y); + } + if (ISZERO(x)) + return (y); + /* + * We really have two numbers to add, although their signs may + * differ. Make the exponents match, by shifting the smaller + * number right (e.g., 1.011 => 0.1011) and increasing its + * exponent (2^3 => 2^4). Note that we do not alter the exponents + * of x and y here. + */ + r = &fe->fe_f3; + r->fp_class = FPC_NUM; + if (x->fp_exp == y->fp_exp) { + r->fp_exp = x->fp_exp; + r->fp_sticky = 0; + } else { + if (x->fp_exp < y->fp_exp) { + /* + * Try to avoid subtract case iii (see below). + * This also guarantees that x->fp_sticky = 0. + */ + SWAP(x, y); + } + /* now x->fp_exp > y->fp_exp */ + r->fp_exp = x->fp_exp; + r->fp_sticky = fpu_shr(y, x->fp_exp - y->fp_exp); + } + r->fp_sign = x->fp_sign; + if (x->fp_sign == y->fp_sign) { + FPU_DECL_CARRY + + /* + * The signs match, so we simply add the numbers. The result + * may be `supernormal' (as big as 1.111...1 + 1.111...1, or + * 11.111...0). If so, a single bit shift-right will fix it + * (but remember to adjust the exponent). + */ + /* r->fp_mant = x->fp_mant + y->fp_mant */ + FPU_ADDS(r->fp_mant[3], x->fp_mant[3], y->fp_mant[3]); + FPU_ADDCS(r->fp_mant[2], x->fp_mant[2], y->fp_mant[2]); + FPU_ADDCS(r->fp_mant[1], x->fp_mant[1], y->fp_mant[1]); + FPU_ADDC(r0, x->fp_mant[0], y->fp_mant[0]); + if ((r->fp_mant[0] = r0) >= FP_2) { + (void) fpu_shr(r, 1); + r->fp_exp++; + } + } else { + FPU_DECL_CARRY + + /* + * The signs differ, so things are rather more difficult. + * H&P would have us negate the negative operand and add; + * this is the same as subtracting the negative operand. + * This is quite a headache. Instead, we will subtract + * y from x, regardless of whether y itself is the negative + * operand. When this is done one of three conditions will + * hold, depending on the magnitudes of x and y: + * case i) |x| > |y|. The result is just x - y, + * with x's sign, but it may need to be normalized. + * case ii) |x| = |y|. The result is 0 (maybe -0) + * so must be fixed up. + * case iii) |x| < |y|. We goofed; the result should + * be (y - x), with the same sign as y. + * We could compare |x| and |y| here and avoid case iii, + * but that would take just as much work as the subtract. + * We can tell case iii has occurred by an overflow. + * + * N.B.: since x->fp_exp >= y->fp_exp, x->fp_sticky = 0. + */ + /* r->fp_mant = x->fp_mant - y->fp_mant */ + FPU_SET_CARRY(y->fp_sticky); + FPU_SUBCS(r3, x->fp_mant[3], y->fp_mant[3]); + FPU_SUBCS(r2, x->fp_mant[2], y->fp_mant[2]); + FPU_SUBCS(r1, x->fp_mant[1], y->fp_mant[1]); + FPU_SUBC(r0, x->fp_mant[0], y->fp_mant[0]); + if (r0 < FP_2) { + /* cases i and ii */ + if ((r0 | r1 | r2 | r3) == 0) { + /* case ii */ + r->fp_class = FPC_ZERO; + r->fp_sign = (rd == FPCR_MINF); + return (r); + } + } else { + /* + * Oops, case iii. This can only occur when the + * exponents were equal, in which case neither + * x nor y have sticky bits set. Flip the sign + * (to y's sign) and negate the result to get y - x. + */ +#ifdef DIAGNOSTIC + if (x->fp_exp != y->fp_exp || r->fp_sticky) + panic("fpu_add"); +#endif + r->fp_sign = y->fp_sign; + FPU_SUBS(r3, 0, r3); + FPU_SUBCS(r2, 0, r2); + FPU_SUBCS(r1, 0, r1); + FPU_SUBC(r0, 0, r0); + } + r->fp_mant[3] = r3; + r->fp_mant[2] = r2; + r->fp_mant[1] = r1; + r->fp_mant[0] = r0; + if (r0 < FP_1) + fpu_norm(r); + } + return (r); +} diff --git a/sys/arch/m68k/fpe/fpu_arith.h b/sys/arch/m68k/fpe/fpu_arith.h new file mode 100644 index 00000000000..e5f380708d6 --- /dev/null +++ b/sys/arch/m68k/fpe/fpu_arith.h @@ -0,0 +1,153 @@ +/* $NetBSD: fpu_arith.h,v 1.1 1995/11/03 04:46:59 briggs 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_arith.h 8.1 (Berkeley) 6/11/93 + */ + +/* + * Extended-precision arithmetic. + * + * We hold the notion of a `carry register', which may or may not be a + * machine carry bit or register. On the SPARC, it is just the machine's + * carry bit. + * + * In the worst case, you can compute the carry from x+y as + * (unsigned)(x + y) < (unsigned)x + * and from x+y+c as + * ((unsigned)(x + y + c) <= (unsigned)x && (y|c) != 0) + * for example. + */ + +#ifdef sparc + +/* set up for extended-precision arithemtic */ +#define FPU_DECL_CARRY + +/* + * We have three kinds of add: + * add with carry: r = x + y + c + * add (ignoring current carry) and set carry: c'r = x + y + 0 + * add with carry and set carry: c'r = x + y + c + * The macros use `C' for `use carry' and `S' for `set carry'. + * Note that the state of the carry is undefined after ADDC and SUBC, + * so if all you have for these is `add with carry and set carry', + * that is OK. + * + * The same goes for subtract, except that we compute x - y - c. + * + * Finally, we have a way to get the carry into a `regular' variable, + * or set it from a value. SET_CARRY turns 0 into no-carry, nonzero + * into carry; GET_CARRY sets its argument to 0 or 1. + */ +#define FPU_ADDC(r, x, y) \ + asm volatile("addx %1,%2,%0" : "=r"(r) : "r"(x), "r"(y)) +#define FPU_ADDS(r, x, y) \ + asm volatile("addcc %1,%2,%0" : "=r"(r) : "r"(x), "r"(y)) +#define FPU_ADDCS(r, x, y) \ + asm volatile("addxcc %1,%2,%0" : "=r"(r) : "r"(x), "r"(y)) +#define FPU_SUBC(r, x, y) \ + asm volatile("subx %1,%2,%0" : "=r"(r) : "r"(x), "r"(y)) +#define FPU_SUBS(r, x, y) \ + asm volatile("subcc %1,%2,%0" : "=r"(r) : "r"(x), "r"(y)) +#define FPU_SUBCS(r, x, y) \ + asm volatile("subxcc %1,%2,%0" : "=r"(r) : "r"(x), "r"(y)) + +#define FPU_GET_CARRY(r) asm volatile("addx %%g0,%%g0,%0" : "=r"(r)) +#define FPU_SET_CARRY(v) asm volatile("addcc %0,-1,%%g0" : : "r"(v)) + +#define FPU_SHL1_BY_ADD /* shift left 1 faster by ADDC than (a<<1)|(b>>31) */ + +#else /* non sparc */ + +/* set up for extended-precision arithemtic */ +#define FPU_DECL_CARRY quad_t fpu_carry, fpu_tmp; + +/* + * We have three kinds of add: + * add with carry: r = x + y + c + * add (ignoring current carry) and set carry: c'r = x + y + 0 + * add with carry and set carry: c'r = x + y + c + * The macros use `C' for `use carry' and `S' for `set carry'. + * Note that the state of the carry is undefined after ADDC and SUBC, + * so if all you have for these is `add with carry and set carry', + * that is OK. + * + * The same goes for subtract, except that we compute x - y - c. + * + * Finally, we have a way to get the carry into a `regular' variable, + * or set it from a value. SET_CARRY turns 0 into no-carry, nonzero + * into carry; GET_CARRY sets its argument to 0 or 1. + */ +#define FPU_ADDC(r, x, y) \ + (r) = (x) + (y) + (!!fpu_carry) +#define FPU_ADDS(r, x, y) \ + { \ + fpu_tmp = (quad_t)(x) + (quad_t)(y); \ + (r) = (u_int)fpu_tmp; \ + fpu_carry = ((fpu_tmp & 0xffffffff00000000LL) != 0); \ + } +#define FPU_ADDCS(r, x, y) \ + { \ + fpu_tmp = (quad_t)(x) + (quad_t)(y) + (!!fpu_carry); \ + (r) = (u_int)fpu_tmp; \ + fpu_carry = ((fpu_tmp & 0xffffffff00000000LL) != 0); \ + } +#define FPU_SUBC(r, x, y) \ + (r) = (x) - (y) - (!!fpu_carry) +#define FPU_SUBS(r, x, y) \ + { \ + fpu_tmp = (quad_t)(x) - (quad_t)(y); \ + (r) = (u_int)fpu_tmp; \ + fpu_carry = ((fpu_tmp & 0xffffffff00000000LL) != 0); \ + } +#define FPU_SUBCS(r, x, y) \ + { \ + fpu_tmp = (quad_t)(x) - (quad_t)(y) - (!!fpu_carry); \ + (r) = (u_int)fpu_tmp; \ + fpu_carry = ((fpu_tmp & 0xffffffff00000000LL) != 0); \ + } + +#define FPU_GET_CARRY(r) (r) = (!!fpu_carry) +#define FPU_SET_CARRY(v) fpu_carry = ((v) != 0) + +#endif diff --git a/sys/arch/m68k/fpe/fpu_arith_proto.h b/sys/arch/m68k/fpe/fpu_arith_proto.h new file mode 100644 index 00000000000..c85422c04a7 --- /dev/null +++ b/sys/arch/m68k/fpe/fpu_arith_proto.h @@ -0,0 +1,94 @@ +/* $NetBSD: fpu_arith_proto.h,v 1.1 1995/11/03 04:47:00 briggs Exp $ */ + +/* + * Copyright (c) 1995 Ken Nakata + * 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. Neither the name of the author 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 AUTHOR 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 AUTHOR 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_arith_proto.c 10/24/95 + */ + +#ifndef _FPU_ARITH_PROTO_H_ +#define _FPU_ARITH_PROTO_H_ + +/* + * Arithmetic functions - called from fpu_emul_arith(). + * Each of these may modify its inputs (f1,f2) and/or the temporary. + * Each returns a pointer to the result and/or sets exceptions. + */ + +/* fpu_add.c */ +struct fpn * fpu_add __P((struct fpemu *fe)); + +/* fpu_div.c */ +struct fpn * fpu_div __P((struct fpemu *fe)); + +/* fpu_exp.c */ +struct fpn * fpu_etox __P((struct fpemu *fe)); +struct fpn * fpu_etoxm1 __P((struct fpemu *fe)); +struct fpn * fpu_tentox __P((struct fpemu *fe)); +struct fpn * fpu_twotox __P((struct fpemu *fe)); + +/* fpu_getexp.c */ +struct fpn * fpu_getexp __P((struct fpemu *fe)); +struct fpn * fpu_getman __P((struct fpemu *fe)); + +/* fpu_hyperb.c */ +struct fpn * fpu_atanh __P((struct fpemu *fe)); +struct fpn * fpu_cosh __P((struct fpemu *fe)); +struct fpn * fpu_sinh __P((struct fpemu *fe)); +struct fpn * fpu_tanh __P((struct fpemu *fe)); + +/* fpu_int.c */ +struct fpn * fpu_intrz __P((struct fpemu *fe)); +struct fpn * fpu_int __P((struct fpemu *fe)); + +/* fpu_log.c */ +struct fpn * fpu_log10 __P((struct fpemu *fe)); +struct fpn * fpu_log2 __P((struct fpemu *fe)); +struct fpn * fpu_logn __P((struct fpemu *fe)); +struct fpn * fpu_lognp1 __P((struct fpemu *fe)); + +/* fpu_mulc */ +struct fpn * fpu_mul __P((struct fpemu *fe)); + +/* fpu_rem.c */ +struct fpn * fpu_rem __P((struct fpemu *fe)); +struct fpn * fpu_mod __P((struct fpemu *fe)); + +/* fpu_sqrt.c */ +struct fpn * fpu_sqrt __P((struct fpemu *fe)); + +/* fpu_trig.c */ +struct fpn * fpu_acos __P((struct fpemu *fe)); +struct fpn * fpu_asin __P((struct fpemu *fe)); +struct fpn * fpu_atan __P((struct fpemu *fe)); +struct fpn * fpu_cos __P((struct fpemu *fe)); +struct fpn * fpu_sin __P((struct fpemu *fe)); +struct fpn * fpu_tan __P((struct fpemu *fe)); +struct fpn * fpu_sincos __P((struct fpemu *fe, int regc)); + +#endif /* _FPU_ARITH_PROTO_H_ */ diff --git a/sys/arch/m68k/fpe/fpu_calcea.c b/sys/arch/m68k/fpe/fpu_calcea.c new file mode 100644 index 00000000000..6fe69572601 --- /dev/null +++ b/sys/arch/m68k/fpe/fpu_calcea.c @@ -0,0 +1,710 @@ +/* $NetBSD: fpu_calcea.c,v 1.2 1995/11/05 00:35:15 briggs Exp $ */ + +/* + * Copyright (c) 1995 Gordon W. Ross + * portion Copyright (c) 1995 Ken Nakata + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * 4. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Gordon Ross + * + * 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. + */ + +#include <stddef.h> +#include <sys/types.h> +#include <sys/signal.h> +#include <machine/frame.h> + +#include "fpu_emulate.h" + +/* + * Prototypes of static functions + */ +static int decode_ea6 __P((struct frame *frame, struct instruction *insn, + struct insn_ea *ea, int modreg)); +static int fetch_immed __P((struct frame *frame, struct instruction *insn, + int *dst)); +static int fetch_disp __P((struct frame *frame, struct instruction *insn, + int size, int *res)); +static int calc_ea __P((struct insn_ea *ea, char *ptr, char **eaddr)); + +/* + * Helper routines for dealing with "effective address" values. + */ + +/* + * Decode an effective address into internal form. + * Returns zero on success, else signal number. + */ +int +fpu_decode_ea(frame, insn, ea, modreg) + struct frame *frame; + struct instruction *insn; + struct insn_ea *ea; + int modreg; +{ + int data, sig; + +#ifdef DEBUG + if (insn->is_datasize < 0) { + panic("decode_ea: called with uninitialized datasize\n"); + } +#endif + + sig = 0; + + /* Set the most common value here. */ + ea->ea_regnum = 8 + (modreg & 7); + + switch (modreg & 070) { + case 0: /* Dn */ + ea->ea_regnum &= 7; + case 010: /* An */ + ea->ea_flags = EA_DIRECT; + if (fpu_debug_level & DL_DECODEEA) { + printf(" decode_ea: register direct reg=%d\n", ea->ea_regnum); + } + break; + + case 020: /* (An) */ + ea->ea_flags = 0; + if (fpu_debug_level & DL_DECODEEA) { + printf(" decode_ea: register indirect reg=%d\n", ea->ea_regnum); + } + break; + + case 030: /* (An)+ */ + ea->ea_flags = EA_POSTINCR; + if (fpu_debug_level & DL_DECODEEA) { + printf(" decode_ea: reg indirect postincrement reg=%d\n", + ea->ea_regnum); + } + break; + + case 040: /* -(An) */ + ea->ea_flags = EA_PREDECR; + if (fpu_debug_level & DL_DECODEEA) { + printf(" decode_ea: reg indirect predecrement reg=%d\n", + ea->ea_regnum); + } + break; + + case 050: /* (d16,An) */ + ea->ea_flags = EA_OFFSET; + sig = fetch_disp(frame, insn, 1, &ea->ea_offset); + if (fpu_debug_level & DL_DECODEEA) { + printf(" decode_ea: reg indirect with displacement reg=%d\n", + ea->ea_regnum); + } + break; + + case 060: /* (d8,An,Xn) */ + ea->ea_flags = EA_INDEXED; + sig = decode_ea6(frame, insn, ea, modreg); + break; + + case 070: /* misc. */ + ea->ea_regnum = (modreg & 7); + switch (modreg & 7) { + + case 0: /* (xxxx).W */ + ea->ea_flags = EA_ABS; + sig = fetch_disp(frame, insn, 1, &ea->ea_absaddr); + if (fpu_debug_level & DL_DECODEEA) { + printf(" decode_ea: absolute address (word)\n"); + } + break; + + case 1: /* (xxxxxxxx).L */ + ea->ea_flags = EA_ABS; + sig = fetch_disp(frame, insn, 2, &ea->ea_absaddr); + if (fpu_debug_level & DL_DECODEEA) { + printf(" decode_ea: absolute address (long)\n"); + } + break; + + case 2: /* (d16,PC) */ + ea->ea_flags = EA_PC_REL | EA_OFFSET; + sig = fetch_disp(frame, insn, 1, &ea->ea_absaddr); + if (fpu_debug_level & DL_DECODEEA) { + printf(" decode_ea: pc relative word displacement\n"); + } + break; + + case 3: /* (d8,PC,Xn) */ + ea->ea_flags = EA_PC_REL | EA_INDEXED; + sig = decode_ea6(frame, insn, ea, modreg); + break; + + case 4: /* #data */ + ea->ea_flags = EA_IMMED; + sig = fetch_immed(frame, insn, &ea->ea_immed[0]); + if (fpu_debug_level & DL_DECODEEA) { + printf(" decode_ea: immediate size=%d\n", insn->is_datasize); + } + break; + + default: + if (fpu_debug_level & DL_DECODEEA) { + printf(" decode_ea: invalid addr mode (7,%d)\n", modreg & 7); + } + return SIGILL; + } /* switch for mode 7 */ + break; + } /* switch mode */ + + ea->ea_tdisp = 0; + + return sig; +} + +/* + * Decode Mode=6 address modes + */ +static int +decode_ea6(frame, insn, ea, modreg) + struct frame *frame; + struct instruction *insn; + struct insn_ea *ea; + int modreg; +{ + int word, extword, idx; + int basedisp, outerdisp; + int bd_size, od_size; + int sig; + + extword = fusword(frame->f_pc + insn->is_advance); + if (extword < 0) { + return SIGSEGV; + } + insn->is_advance += 2; + + /* get register index */ + ea->ea_idxreg = (extword >> 12) & 0xf; + idx = frame->f_regs[ea->ea_idxreg]; + if ((extword & 0x0800) == 0) { + /* if word sized index, sign-extend */ + idx &= 0xffff; + if (idx & 0x8000) { + idx |= 0xffff0000; + } + } + /* scale register index */ + idx <<= ((extword >>9) & 3); + + if ((extword & 0x100) == 0) { + /* brief extention word - sign-extend the displacement */ + basedisp = (extword & 0xff); + if (basedisp & 0x80) { + basedisp |= 0xffffff00; + } + + ea->ea_basedisp = idx + basedisp; + ea->ea_outerdisp = 0; + if (fpu_debug_level & DL_DECODEEA) { + printf(" decode_ea6: brief ext word idxreg=%d, basedisp=%08x\n", + ea->ea_idxreg, ea->ea_basedisp); + } + } else { + /* full extention word */ + if (extword & 0x80) { + ea->ea_flags |= EA_BASE_SUPPRSS; + } + bd_size = ((extword >> 4) & 3) - 1; + od_size = (extword & 3) - 1; + sig = fetch_disp(frame, insn, bd_size, &basedisp); + if (sig) { + return sig; + } + if (od_size >= 0) { + ea->ea_flags |= EA_MEM_INDIR; + } + sig = fetch_disp(frame, insn, od_size, &outerdisp); + if (sig) { + return sig; + } + + switch (extword & 0x44) { + case 0: /* preindexed */ + ea->ea_basedisp = basedisp + idx; + ea->ea_outerdisp = outerdisp; + break; + case 4: /* postindexed */ + ea->ea_basedisp = basedisp; + ea->ea_outerdisp = outerdisp + idx; + break; + case 0x40: /* no index */ + ea->ea_basedisp = basedisp; + ea->ea_outerdisp = outerdisp; + break; + default: +#ifdef DEBUG + printf(" decode_ea6: invalid indirect mode: ext word %04x\n", + extword); +#endif + return SIGILL; + break; + } + if (fpu_debug_level & DL_DECODEEA) { + printf(" decode_ea6: full ext idxreg=%d, basedisp=%x, outerdisp=%x\n", + ea->ea_idxreg, ea->ea_basedisp, ea->ea_outerdisp); + } + } + if (fpu_debug_level & DL_DECODEEA) { + printf(" decode_ea6: regnum=%d, flags=%x\n", + ea->ea_regnum, ea->ea_flags); + } + return 0; +} + +/* + * Load a value from an effective address. + * Returns zero on success, else signal number. + */ +int +fpu_load_ea(frame, insn, ea, dst) + struct frame *frame; + struct instruction *insn; + struct insn_ea *ea; + char *dst; +{ + int *reg; + char *src; + int len, step; + int data, word, sig; + +#ifdef DIAGNOSTIC + if (ea->ea_regnum & ~0xF) { + panic(" load_ea: bad regnum"); + } +#endif + + if (fpu_debug_level & DL_LOADEA) { + printf(" load_ea: frame at %08x\n", frame); + } + /* The dst is always int or larger. */ + len = insn->is_datasize; + if (len < 4) { + dst += (4 - len); + } + step = (len == 1 && ea->ea_regnum == 15 /* sp */) ? 2 : len; + + if (ea->ea_flags & EA_DIRECT) { + if (len > 4) { +#ifdef DEBUG + printf(" load_ea: operand doesn't fit cpu reg\n"); +#endif + return SIGILL; + } + if (ea->ea_tdisp > 0) { +#ifdef DEBUG + printf(" load_ea: more than one move from cpu reg\n"); +#endif + return SIGILL; + } + src = (char *)&frame->f_regs[ea->ea_regnum]; + /* The source is an int. */ + if (len < 4) { + src += (4 - len); + if (fpu_debug_level & DL_LOADEA) { + printf(" load_ea: short/byte opr - addr adjusted\n"); + } + } + if (fpu_debug_level & DL_LOADEA) { + printf(" load_ea: src 0x%08x\n", src); + } + bcopy(src, dst, len); + } else if (ea->ea_flags & EA_IMMED) { + if (fpu_debug_level & DL_LOADEA) { + printf(" load_ea: immed %08x%08x%08x size %d\n", + ea->ea_immed[0], ea->ea_immed[1], ea->ea_immed[2], len); + } + src = (char *)&ea->ea_immed[0]; + if (len < 4) { + src += (4 - len); + if (fpu_debug_level & DL_LOADEA) { + printf(" load_ea: short/byte immed opr - addr adjusted\n"); + } + } + bcopy(src, dst, len); + } else if (ea->ea_flags & EA_ABS) { + if (fpu_debug_level & DL_LOADEA) { + printf(" load_ea: abs addr %08x\n", ea->ea_absaddr); + } + src = (char *)ea->ea_absaddr; + copyin(src, dst, len); + } else /* register indirect */ { + if (ea->ea_flags & EA_PC_REL) { + if (fpu_debug_level & DL_LOADEA) { + printf(" load_ea: using PC\n"); + } + reg = NULL; + /* Grab the register contents. 4 is offset to the first + extention word from the opcode */ + src = (char *)frame->f_pc + 4; + if (fpu_debug_level & DL_LOADEA) { + printf(" load_ea: pc relative pc+4 = 0x%08x\n", src); + } + } else /* not PC relative */ { + if (fpu_debug_level & DL_LOADEA) { + printf(" load_ea: using register %c%d\n", + (ea->ea_regnum >= 8) ? 'a' : 'd', ea->ea_regnum & 7); + } + /* point to the register */ + reg = &frame->f_regs[ea->ea_regnum]; + + if (ea->ea_flags & EA_PREDECR) { + if (fpu_debug_level & DL_LOADEA) { + printf(" load_ea: predecr mode - reg decremented\n"); + } + *reg -= step; + ea->ea_tdisp = 0; + } + + /* Grab the register contents. */ + src = (char *)*reg; + if (fpu_debug_level & DL_LOADEA) { + printf(" load_ea: reg indirect reg = 0x%08x\n", src); + } + } + + sig = calc_ea(ea, src, &src); + if (sig) + return sig; + + copyin(src + ea->ea_tdisp, dst, len); + + /* do post-increment */ + if (ea->ea_flags & EA_POSTINCR) { + if (ea->ea_flags & EA_PC_REL) { +#ifdef DEBUG + printf(" load_ea: tried to postincrement PC\n"); +#endif + return SIGILL; + } + *reg += step; + ea->ea_tdisp = 0; + if (fpu_debug_level & DL_LOADEA) { + printf(" load_ea: postinc mode - reg incremented\n"); + } + } else { + ea->ea_tdisp += len; + } + } + + return 0; +} + +/* + * Store a value at the effective address. + * Returns zero on success, else signal number. + */ +int +fpu_store_ea(frame, insn, ea, src) + struct frame *frame; + struct instruction *insn; + struct insn_ea *ea; + char *src; +{ + int *reg; + char *dst; + int len, step; + int data, word, sig; + +#ifdef DIAGNOSTIC + if (ea->ea_regnum & ~0xF) { + panic(" store_ea: bad regnum"); + } +#endif + + if (ea->ea_flags & (EA_IMMED|EA_PC_REL)) { + /* not alterable address mode */ +#ifdef DEBUG + printf(" store_ea: not alterable address mode\n"); +#endif + return SIGILL; + } + + if (fpu_debug_level & DL_STOREEA) { + printf(" store_ea: frame at %08x\n", frame); + } + /* The src is always int or larger. */ + len = insn->is_datasize; + if (len < 4) { + src += (4 - len); + } + step = (len == 1 && ea->ea_regnum == 15 /* sp */) ? 2 : len; + + if (ea->ea_flags & EA_ABS) { + if (fpu_debug_level & DL_STOREEA) { + printf(" store_ea: abs addr %08x\n", ea->ea_absaddr); + } + dst = (char *)ea->ea_absaddr; + copyout(src, dst + ea->ea_tdisp, len); + ea->ea_tdisp += len; + } else if (ea->ea_flags & EA_DIRECT) { + if (len > 4) { +#ifdef DEBUG + printf(" store_ea: operand doesn't fit cpu reg\n"); +#endif + return SIGILL; + } + if (ea->ea_tdisp > 0) { +#ifdef DEBUG + printf(" store_ea: more than one move to cpu reg\n"); +#endif + return SIGILL; + } + dst = (char*)&frame->f_regs[ea->ea_regnum]; + /* The destination is an int. */ + if (len < 4) { + dst += (4 - len); + if (fpu_debug_level & DL_STOREEA) { + printf(" store_ea: short/byte opr - dst addr adjusted\n"); + } + } + if (fpu_debug_level & DL_STOREEA) { + printf(" store_ea: dst 0x%08x\n", dst); + } + bcopy(src, dst, len); + } else /* One of MANY indirect forms... */ { + if (fpu_debug_level & DL_STOREEA) { + printf(" store_ea: using register %c%d\n", + (ea->ea_regnum >= 8) ? 'a' : 'd', ea->ea_regnum & 7); + } + /* point to the register */ + reg = &(frame->f_regs[ea->ea_regnum]); + + /* do pre-decrement */ + if (ea->ea_flags & EA_PREDECR) { + if (fpu_debug_level & DL_STOREEA) { + printf(" store_ea: predecr mode - reg decremented\n"); + } + *reg -= step; + ea->ea_tdisp = 0; + } + + /* calculate the effective address */ + sig = calc_ea(ea, (char *)*reg, &dst); + if (sig) + return sig; + + if (fpu_debug_level & DL_STOREEA) { + printf(" store_ea: dst addr=0x%08x+%d\n", dst, ea->ea_tdisp); + } + copyout(src, dst + ea->ea_tdisp, len); + + /* do post-increment */ + if (ea->ea_flags & EA_POSTINCR) { + *reg += step; + ea->ea_tdisp = 0; + if (fpu_debug_level & DL_STOREEA) { + printf(" store_ea: postinc mode - reg incremented\n"); + } + } else { + ea->ea_tdisp += len; + } + } + + return 0; +} + +/* + * fetch_immed: fetch immediate operand + */ +static int +fetch_immed(frame, insn, dst) + struct frame *frame; + struct instruction *insn; + int *dst; +{ + int data, ext_bytes; + + ext_bytes = insn->is_datasize; + + if (0 < ext_bytes) { + data = fusword(frame->f_pc + insn->is_advance); + if (data < 0) { + return SIGSEGV; + } + if (ext_bytes == 1) { + /* sign-extend byte to long */ + data &= 0xff; + if (data & 0x80) { + data |= 0xffffff00; + } + } else if (ext_bytes == 2) { + /* sign-extend word to long */ + data &= 0xffff; + if (data & 0x8000) { + data |= 0xffff0000; + } + } + insn->is_advance += 2; + dst[0] = data; + } + if (2 < ext_bytes) { + data = fusword(frame->f_pc + insn->is_advance); + if (data < 0) { + return SIGSEGV; + } + insn->is_advance += 2; + dst[0] <<= 16; + dst[0] |= data; + } + if (4 < ext_bytes) { + data = fusword(frame->f_pc + insn->is_advance); + if (data < 0) { + return SIGSEGV; + } + dst[1] = data << 16; + data = fusword(frame->f_pc + insn->is_advance + 2); + if (data < 0) { + return SIGSEGV; + } + insn->is_advance += 4; + dst[1] |= data; + } + if (8 < ext_bytes) { + data = fusword(frame->f_pc + insn->is_advance); + if (data < 0) { + return SIGSEGV; + } + dst[2] = data << 16; + data = fusword(frame->f_pc + insn->is_advance + 2); + if (data < 0) { + return SIGSEGV; + } + insn->is_advance += 4; + dst[2] |= data; + } + + return 0; +} + +/* + * fetch_disp: fetch displacement in full extention words + */ +static int +fetch_disp(frame, insn, size, res) + struct frame *frame; + struct instruction *insn; + int size, *res; +{ + int disp, word; + + if (size == 1) { + word = fusword(frame->f_pc + insn->is_advance); + if (word < 0) { + return SIGSEGV; + } + disp = word & 0xffff; + if (disp & 0x8000) { + /* sign-extend */ + disp |= 0xffff0000; + } + insn->is_advance += 2; + } else if (size == 2) { + word = fusword(frame->f_pc + insn->is_advance); + if (word < 0) { + return SIGSEGV; + } + disp = word << 16; + word = fusword(frame->f_pc + insn->is_advance + 2); + if (word < 0) { + return SIGSEGV; + } + disp |= (word & 0xffff); + insn->is_advance += 4; + } else { + disp = 0; + } + *res = disp; + return 0; +} + +/* + * Calculates an effective address for all address modes except for + * register direct, absolute, and immediate modes. However, it does + * not take care of predecrement/postincrement of register content. + * Returns a signal value (0 == no error). + */ +static int +calc_ea(ea, ptr, eaddr) + struct insn_ea *ea; + char *ptr; /* base address (usually a register content) */ + char **eaddr; /* pointer to result pointer */ +{ + int data, word, sig; + + if (fpu_debug_level & DL_EA) { + printf(" calc_ea: reg indirect (reg) = 0x%08x\n", ptr); + } + + if (ea->ea_flags & EA_OFFSET) { + /* apply the signed offset */ + if (fpu_debug_level & DL_EA) { + printf(" calc_ea: offset %d\n", ea->ea_offset); + } + ptr += ea->ea_offset; + } else if (ea->ea_flags & EA_INDEXED) { + if (fpu_debug_level & DL_EA) { + printf(" calc_ea: indexed mode\n"); + } + + if (ea->ea_flags & EA_BASE_SUPPRSS) { + /* base register is suppressed */ + ptr = (char *)ea->ea_basedisp; + } else { + ptr += ea->ea_basedisp; + } + + if (ea->ea_flags & EA_MEM_INDIR) { + if (fpu_debug_level & DL_EA) { + printf(" calc_ea: mem indir mode: basedisp=%08x, outerdisp=%08x\n", + ea->ea_basedisp, ea->ea_outerdisp); + printf(" calc_ea: addr fetched from 0x%08x\n", ptr); + } + /* memory indirect modes */ + word = fusword(ptr); + if (word < 0) { + return SIGSEGV; + } + word <<= 16; + data = fusword(ptr + 2); + if (data < 0) { + return SIGSEGV; + } + word |= data; + if (fpu_debug_level & DL_STOREEA) { + printf(" calc_ea: fetched ptr 0x%08x\n", word); + } + ptr = (char *)word + ea->ea_outerdisp; + } + } + + *eaddr = ptr; + + return 0; +} diff --git a/sys/arch/m68k/fpe/fpu_div.c b/sys/arch/m68k/fpe/fpu_div.c new file mode 100644 index 00000000000..8b6ec222a69 --- /dev/null +++ b/sys/arch/m68k/fpe/fpu_div.c @@ -0,0 +1,269 @@ +/* $NetBSD: fpu_div.c,v 1.1 1995/11/03 04:47:02 briggs 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_div.c 8.1 (Berkeley) 6/11/93 + */ + +/* + * Perform an FPU divide (return x / y). + */ + +#include <sys/types.h> + +#include <machine/reg.h> + +#include "fpu_arith.h" +#include "fpu_emulate.h" + +/* + * Division of normal numbers is done as follows: + * + * x and y are floating point numbers, i.e., in the form 1.bbbb * 2^e. + * If X and Y are the mantissas (1.bbbb's), the quotient is then: + * + * q = (X / Y) * 2^((x exponent) - (y exponent)) + * + * Since X and Y are both in [1.0,2.0), the quotient's mantissa (X / Y) + * will be in [0.5,2.0). Moreover, it will be less than 1.0 if and only + * if X < Y. In that case, it will have to be shifted left one bit to + * become a normal number, and the exponent decremented. Thus, the + * desired exponent is: + * + * left_shift = x->fp_mant < y->fp_mant; + * result_exp = x->fp_exp - y->fp_exp - left_shift; + * + * The quotient mantissa X/Y can then be computed one bit at a time + * using the following algorithm: + * + * Q = 0; -- Initial quotient. + * R = X; -- Initial remainder, + * if (left_shift) -- but fixed up in advance. + * R *= 2; + * for (bit = FP_NMANT; --bit >= 0; R *= 2) { + * if (R >= Y) { + * Q |= 1 << bit; + * R -= Y; + * } + * } + * + * The subtraction R -= Y always removes the uppermost bit from R (and + * can sometimes remove additional lower-order 1 bits); this proof is + * left to the reader. + * + * This loop correctly calculates the guard and round bits since they are + * included in the expanded internal representation. The sticky bit + * is to be set if and only if any other bits beyond guard and round + * would be set. From the above it is obvious that this is true if and + * only if the remainder R is nonzero when the loop terminates. + * + * Examining the loop above, we can see that the quotient Q is built + * one bit at a time ``from the top down''. This means that we can + * dispense with the multi-word arithmetic and just build it one word + * at a time, writing each result word when it is done. + * + * Furthermore, since X and Y are both in [1.0,2.0), we know that, + * initially, R >= Y. (Recall that, if X < Y, R is set to X * 2 and + * is therefore at in [2.0,4.0).) Thus Q is sure to have bit FP_NMANT-1 + * set, and R can be set initially to either X - Y (when X >= Y) or + * 2X - Y (when X < Y). In addition, comparing R and Y is difficult, + * so we will simply calculate R - Y and see if that underflows. + * This leads to the following revised version of the algorithm: + * + * R = X; + * bit = FP_1; + * D = R - Y; + * if (D >= 0) { + * result_exp = x->fp_exp - y->fp_exp; + * R = D; + * q = bit; + * bit >>= 1; + * } else { + * result_exp = x->fp_exp - y->fp_exp - 1; + * q = 0; + * } + * R <<= 1; + * do { + * D = R - Y; + * if (D >= 0) { + * q |= bit; + * R = D; + * } + * R <<= 1; + * } while ((bit >>= 1) != 0); + * Q[0] = q; + * for (i = 1; i < 4; i++) { + * q = 0, bit = 1 << 31; + * do { + * D = R - Y; + * if (D >= 0) { + * q |= bit; + * R = D; + * } + * R <<= 1; + * } while ((bit >>= 1) != 0); + * Q[i] = q; + * } + * + * This can be refined just a bit further by moving the `R <<= 1' + * calculations to the front of the do-loops and eliding the first one. + * The process can be terminated immediately whenever R becomes 0, but + * this is relatively rare, and we do not bother. + */ + +struct fpn * +fpu_div(fe) + register struct fpemu *fe; +{ + register struct fpn *x = &fe->fe_f1, *y = &fe->fe_f2; + register u_int q, bit; + register u_int r0, r1, r2, r3, d0, d1, d2, d3, y0, y1, y2, y3; + FPU_DECL_CARRY + + fe->fe_fpsr &= ~FPSR_EXCP; /* clear all exceptions */ + + /* + * Since divide is not commutative, we cannot just use ORDER. + * Check either operand for NaN first; if there is at least one, + * order the signalling one (if only one) onto the right, then + * return it. Otherwise we have the following cases: + * + * Inf / Inf = NaN, plus NV exception + * Inf / num = Inf [i.e., return x] + * Inf / 0 = Inf [i.e., return x] + * 0 / Inf = 0 [i.e., return x] + * 0 / num = 0 [i.e., return x] + * 0 / 0 = NaN, plus NV exception + * num / Inf = 0 + * num / num = num (do the divide) + * num / 0 = Inf, plus DZ exception + */ + if (ISNAN(x) || ISNAN(y)) { + ORDER(x, y); + return (y); + } + if (ISINF(x) || ISZERO(x)) { + if (x->fp_class == y->fp_class) + return (fpu_newnan(fe)); + return (x); + } + + /* all results at this point use XOR of operand signs */ + x->fp_sign ^= y->fp_sign; + if (ISINF(y)) { + x->fp_class = FPC_ZERO; + return (x); + } + if (ISZERO(y)) { + fe->fe_fpsr |= FPSR_DZ; + x->fp_class = FPC_INF; + return (x); + } + + /* + * Macros for the divide. See comments at top for algorithm. + * Note that we expand R, D, and Y here. + */ + +#define SUBTRACT /* D = R - Y */ \ + FPU_SUBS(d3, r3, y3); FPU_SUBCS(d2, r2, y2); \ + FPU_SUBCS(d1, r1, y1); FPU_SUBC(d0, r0, y0) + +#define NONNEGATIVE /* D >= 0 */ \ + ((int)d0 >= 0) + +#ifdef FPU_SHL1_BY_ADD +#define SHL1 /* R <<= 1 */ \ + FPU_ADDS(r3, r3, r3); FPU_ADDCS(r2, r2, r2); \ + FPU_ADDCS(r1, r1, r1); FPU_ADDC(r0, r0, r0) +#else +#define SHL1 \ + r0 = (r0 << 1) | (r1 >> 31), r1 = (r1 << 1) | (r2 >> 31), \ + r2 = (r2 << 1) | (r3 >> 31), r3 <<= 1 +#endif + +#define LOOP /* do ... while (bit >>= 1) */ \ + do { \ + SHL1; \ + SUBTRACT; \ + if (NONNEGATIVE) { \ + q |= bit; \ + r0 = d0, r1 = d1, r2 = d2, r3 = d3; \ + } \ + } while ((bit >>= 1) != 0) + +#define WORD(r, i) /* calculate r->fp_mant[i] */ \ + q = 0; \ + bit = 1 << 31; \ + LOOP; \ + (x)->fp_mant[i] = q + + /* Setup. Note that we put our result in x. */ + r0 = x->fp_mant[0]; + r1 = x->fp_mant[1]; + r2 = x->fp_mant[2]; + r3 = x->fp_mant[3]; + y0 = y->fp_mant[0]; + y1 = y->fp_mant[1]; + y2 = y->fp_mant[2]; + y3 = y->fp_mant[3]; + + bit = FP_1; + SUBTRACT; + if (NONNEGATIVE) { + x->fp_exp -= y->fp_exp; + r0 = d0, r1 = d1, r2 = d2, r3 = d3; + q = bit; + bit >>= 1; + } else { + x->fp_exp -= y->fp_exp + 1; + q = 0; + } + LOOP; + x->fp_mant[0] = q; + WORD(x, 1); + WORD(x, 2); + WORD(x, 3); + x->fp_sticky = r0 | r1 | r2 | r3; + + return (x); +} diff --git a/sys/arch/m68k/fpe/fpu_emulate.c b/sys/arch/m68k/fpe/fpu_emulate.c index 7fd5cd3dfed..ed86855d09a 100644 --- a/sys/arch/m68k/fpe/fpu_emulate.c +++ b/sys/arch/m68k/fpe/fpu_emulate.c @@ -1,7 +1,8 @@ -/* $NetBSD: fpu_emulate.c,v 1.2 1995/03/10 01:43:05 gwr Exp $ */ +/* $NetBSD: fpu_emulate.c,v 1.4 1995/11/05 00:35:17 briggs Exp $ */ /* * Copyright (c) 1995 Gordon W. Ross + * some portion Copyright (c) 1995 Ken Nakata * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -39,153 +40,390 @@ #include <sys/signal.h> #include <machine/frame.h> -#define DEBUG 1 /* XXX */ +#include "fpu_emulate.h" + +static int fpu_emul_fmovmcr __P((struct fpemu *fe, struct instruction *insn)); +static int fpu_emul_fmovm __P((struct fpemu *fe, struct instruction *insn)); +static int fpu_emul_arith __P((struct fpemu *fe, struct instruction *insn)); +static int fpu_emul_type1 __P((struct fpemu *fe, struct instruction *insn)); +static int fpu_emul_brcc __P((struct fpemu *fe, struct instruction *insn)); +static int test_cc __P((struct fpemu *fe, int pred)); +static struct fpn *fpu_cmp __P((struct fpemu *fe)); + +#if !defined(DL_DEFAULT) +# if defined(DEBUG_WITH_FPU) +# define DL_DEFAULT DL_ALL +# else +# define DL_DEFAULT 0 +# endif +#endif -/* - * Internal info about a decoded effective address. - */ -struct insn_ea { - int regnum; - int immed; - int flags; -#define EA_DIRECT 0x01 -#define EA_PREDECR 0x02 -#define EA_POSTINCR 0x04 -#define EA_OFFSET 0x08 /* mode 5: base+offset */ -#define EA_INDEXED 0x10 /* mode 6: complicated */ -#define EA_ABS 0x20 /* mode 7: reg 0 or 1 */ -#define EA_PC_REL 0x40 /* mode 7: reg 2 or 3 */ -#define EA_IMMED 0x80 /* mode 7: reg 4 */ -}; - -struct instruction { - int advance; /* length of instruction */ - int datasize; /* byte, word, long, float, double, ... */ - int opcode; - int word1; - struct insn_ea ea0; - struct insn_ea ea1; -}; - -int fpu_emul_fmovm(struct frame *frame, - struct fpframe *fpf, - struct instruction *insn); -int fpu_emul_type0(struct frame *frame, - struct fpframe *fpf, - struct instruction *insn); -int fpu_emul_type1(struct frame *frame, - struct fpframe *fpf, - struct instruction *insn); -int fpu_emul_brcc(struct frame *frame, - struct fpframe *fpf, - struct instruction *insn); - -static int decode_ea(struct frame *frame, - struct instruction *insn, - struct insn_ea *ea, - int modreg); -static int load_ea(struct frame *frame, - struct instruction *insn, - struct insn_ea *ea, - char *cpureg); -static int store_ea(struct frame *frame, - struct instruction *insn, - struct insn_ea *ea, - char *cpureg); +int fpu_debug_level; +static int global_debug_level = DL_DEFAULT; +#define DUMP_INSN(insn) \ +if (fpu_debug_level & DL_DUMPINSN) { \ + printf(" fpu_emulate: insn={adv=%d,siz=%d,op=%04x,w1=%04x}\n", \ + (insn)->is_advance, (insn)->is_datasize, \ + (insn)->is_opcode, (insn)->is_word1); \ +} + +#ifdef DEBUG_WITH_FPU +/* mock fpframe for FPE - it's never overwritten by the real fpframe */ +struct fpframe mockfpf; +#endif /* * Emulate a floating-point instruction. * Return zero for success, else signal number. * (Typically: zero, SIGFPE, SIGILL, SIGSEGV) */ -int fpu_emulate(struct frame *frame, struct fpframe *fpf) +int +fpu_emulate(frame, fpf) + struct frame *frame; + struct fpframe *fpf; { - struct instruction insn; - int word, optype, sig; - - word = fusword(frame->f_pc); - if (word < 0) { -#ifdef DEBUG - printf("fpu_emulate: fault reading opcode\n"); + static struct instruction insn; + static struct fpemu fe; + int word, optype, sig; + int i; + u_int *pt; + +#ifdef DEBUG + /* initialize insn.is_datasize to tell it is *not* initialized */ + insn.is_datasize = -1; +#endif + fe.fe_frame = frame; +#ifdef DEBUG_WITH_FPU + fe.fe_fpframe = &mockfpf; + fe.fe_fpsr = mockfpf.fpf_fpsr; + fe.fe_fpcr = mockfpf.fpf_fpcr; +#else + fe.fe_fpframe = fpf; + fe.fe_fpsr = fpf->fpf_fpsr; + fe.fe_fpcr = fpf->fpf_fpcr; #endif - return SIGSEGV; - } - if ((word & 0xF000) != 0xF000) { -#ifdef DEBUG - printf("fpu_emulate: not coproc. insn.: opcode=0x%x\n", word); +#ifdef DEBUG + if ((fpu_debug_level = (fe.fe_fpcr >> 16) & 0x0000ffff) == 0) { + /* set the default */ + fpu_debug_level = global_debug_level; + } #endif - return SIGILL; - } - if ((word & 0x0E00) != 0x0200) { -#ifdef DEBUG - printf("fpu_emulate: bad coproc. id: opcode=0x%x\n", word); + if (fpu_debug_level & DL_VERBOSE) { + printf("ENTERING fpu_emulate: FPSR=%08x, FPCR=%08x\n", + fe.fe_fpsr, fe.fe_fpcr); + } + word = fusword(frame->f_pc); + if (word < 0) { +#ifdef DEBUG + printf(" fpu_emulate: fault reading opcode\n"); #endif - return SIGILL; - } + return SIGSEGV; + } - insn.opcode = word; - optype = (word & 0x01C0); + if ((word & 0xf000) != 0xf000) { +#ifdef DEBUG + printf(" fpu_emulate: not coproc. insn.: opcode=0x%x\n", word); +#endif + return SIGILL; + } - word = fusword(frame->f_pc + 2); - if (word < 0) { -#ifdef DEBUG - printf("fpu_emulate: fault reading word1\n"); + if ( +#ifdef DEBUG_WITH_FPU + (word & 0x0E00) != 0x0c00 /* accept fake ID == 6 */ +#else + (word & 0x0E00) != 0x0200 #endif - return SIGSEGV; - } - insn.word1 = word; + ) { +#ifdef DEBUG + printf(" fpu_emulate: bad coproc. id: opcode=0x%x\n", word); +#endif + return SIGILL; + } - /* - * Which family (or type) of opcode is it? - * Tests ordered by likelihood (hopefully). - * Certainly, type 0 is the most common. - */ - if (optype == 0x0000) { - /* type=0: generic */ - if (insn.word1 & 0x8000) { - sig = fpu_emul_fmovm(frame, fpf, &insn); - } else { - sig = fpu_emul_type0(frame, fpf, &insn); - } - } - else if (optype == 0x0080) { - /* type=2: fbcc, short disp. */ - sig = fpu_emul_brcc(frame, fpf, &insn); - } - else if (optype == 0x00C0) { - /* type=3: fbcc, long disp. */ - sig = fpu_emul_brcc(frame, fpf, &insn); - } - else if (optype == 0x0040) { - /* type=1: fdbcc, fscc, ftrapcc */ - sig = fpu_emul_type1(frame, fpf, &insn); - } - else { - /* type=4: fsave (privileged) */ - /* type=5: frestore (privileged) */ - /* type=6: reserved */ - /* type=7: reserved */ -#ifdef DEBUG - printf("fpu_emulate: bad opcode type: opcode=0x%x\n", insn.opcode); + insn.is_opcode = word; + optype = (word & 0x01C0); + + word = fusword(frame->f_pc + 2); + if (word < 0) { +#ifdef DEBUG + printf(" fpu_emulate: fault reading word1\n"); #endif - sig = SIGILL; + return SIGSEGV; + } + insn.is_word1 = word; + /* all FPU instructions are at least 4-byte long */ + insn.is_advance = 4; + + DUMP_INSN(&insn); + + /* + * Which family (or type) of opcode is it? + * Tests ordered by likelihood (hopefully). + * Certainly, type 0 is the most common. + */ + if (optype == 0x0000) { + /* type=0: generic */ + if ((word & 0xc000) == 0xc000) { + if (fpu_debug_level & DL_INSN) + printf(" fpu_emulate: fmovm FPr\n"); + sig = fpu_emul_fmovm(&fe, &insn); + } else if ((word & 0xc000) == 0x8000) { + if (fpu_debug_level & DL_INSN) + printf(" fpu_emulate: fmovm FPcr\n"); + sig = fpu_emul_fmovmcr(&fe, &insn); + } else if ((word & 0xe000) == 0x6000) { + /* fstore = fmove FPn,mem */ + if (fpu_debug_level & DL_INSN) + printf(" fpu_emulate: fmove to mem\n"); + sig = fpu_emul_fstore(&fe, &insn); + } else if ((word & 0xfc00) == 0x5c00) { + /* fmovecr */ + if (fpu_debug_level & DL_INSN) + printf(" fpu_emulate: fmovecr\n"); + sig = fpu_emul_fmovecr(&fe, &insn); + } else if ((word & 0xa07f) == 0x26) { + /* fscale */ + if (fpu_debug_level & DL_INSN) + printf(" fpu_emulate: fscale\n"); + sig = fpu_emul_fscale(&fe, &insn); + } else { + if (fpu_debug_level & DL_INSN) + printf(" fpu_emulte: other type0\n"); + /* all other type0 insns are arithmetic */ + sig = fpu_emul_arith(&fe, &insn); } - if (sig == 0) { - frame->f_pc += insn.advance; + if (fpu_debug_level & DL_VERBOSE) + printf(" fpu_emulate: type 0 returned 0\n"); + sig = fpu_upd_excp(&fe); } + } else if (optype == 0x0080 || optype == 0x00C0) { + /* type=2 or 3: fbcc, short or long disp. */ + if (fpu_debug_level & DL_INSN) + printf(" fpu_emulate: fbcc %s\n", + (optype & 0x40) ? "long" : "short"); + sig = fpu_emul_brcc(&fe, &insn); + } else if (optype == 0x0040) { + /* type=1: fdbcc, fscc, ftrapcc */ + if (fpu_debug_level & DL_INSN) + printf(" fpu_emulate: type1\n"); + sig = fpu_emul_type1(&fe, &insn); + } else { + /* type=4: fsave (privileged) */ + /* type=5: frestore (privileged) */ + /* type=6: reserved */ + /* type=7: reserved */ +#ifdef DEBUG + printf(" fpu_emulate: bad opcode type: opcode=0x%x\n", insn.is_opcode); +#endif + sig = SIGILL; + } + + DUMP_INSN(&insn); + + if (sig == 0) { + frame->f_pc += insn.is_advance; + } #if defined(DDB) && defined(DEBUG) - else kdb_trap(-1, frame); + else { + printf(" fpu_emulate: sig=%d, opcode=%x, word1=%x\n", + sig, insn.is_opcode, insn.is_word1); + kdb_trap(-1, frame); + } #endif - return (sig); + if (fpu_debug_level & DL_VERBOSE) + printf("EXITING fpu_emulate: w/FPSR=%08x, FPCR=%08x\n", + fe.fe_fpsr, fe.fe_fpcr); + + return (sig); +} + +/* update accrued exception bits and see if there's an FP exception */ +int +fpu_upd_excp(fe) + struct fpemu *fe; +{ + u_int fpsr; + u_int fpcr; + + fpsr = fe->fe_fpsr; + fpcr = fe->fe_fpcr; + /* update fpsr accrued exception bits; each insn doesn't have to + update this */ + if (fpsr & (FPSR_BSUN | FPSR_SNAN | FPSR_OPERR)) { + fpsr |= FPSR_AIOP; + } + if (fpsr & FPSR_OVFL) { + fpsr |= FPSR_AOVFL; + } + if ((fpsr & FPSR_UNFL) && (fpsr & FPSR_INEX2)) { + fpsr |= FPSR_AUNFL; + } + if (fpsr & FPSR_DZ) { + fpsr |= FPSR_ADZ; + } + if (fpsr & (FPSR_INEX1 | FPSR_INEX2 | FPSR_OVFL)) { + fpsr |= FPSR_AINEX; + } + + fe->fe_fpframe->fpf_fpsr = fe->fe_fpsr = fpsr; + + return (fpsr & fpcr & FPSR_EXCP) ? SIGFPE : 0; +} + +/* update fpsr according to fp (= result of an fp op) */ +u_int +fpu_upd_fpsr(fe, fp) + struct fpemu *fe; + struct fpn *fp; +{ + u_int fpsr; + + if (fpu_debug_level & DL_RESULT) + printf(" fpu_upd_fpsr: previous fpsr=%08x\n", fe->fe_fpsr); + + /* clear all condition code */ + fpsr = fe->fe_fpsr & ~FPSR_CCB; + + if (fpu_debug_level & DL_RESULT) + printf(" fpu_upd_fpsr: result is a "); + + if (fp->fp_sign) { + if (fpu_debug_level & DL_RESULT) + printf("negative "); + fpsr |= FPSR_NEG; + } else { + if (fpu_debug_level & DL_RESULT) + printf("positive "); + } + + switch (fp->fp_class) { + case FPC_SNAN: + if (fpu_debug_level & DL_RESULT) + printf("signaling NAN\n"); + fpsr |= (FPSR_NAN | FPSR_SNAN); + break; + case FPC_QNAN: + if (fpu_debug_level & DL_RESULT) + printf("quiet NAN\n"); + fpsr |= FPSR_NAN; + break; + case FPC_ZERO: + if (fpu_debug_level & DL_RESULT) + printf("Zero\n"); + fpsr |= FPSR_ZERO; + break; + case FPC_INF: + if (fpu_debug_level & DL_RESULT) + printf("Inf\n"); + fpsr |= FPSR_INF; + break; + default: + if (fpu_debug_level & DL_RESULT) + printf("Number\n"); + /* anything else is treated as if it is a number */ + break; + } + + fe->fe_fpsr = fe->fe_fpframe->fpf_fpsr = fpsr; + + if (fpu_debug_level & DL_RESULT) + printf(" fpu_upd_fpsr: new fpsr=%08x\n", fe->fe_fpframe->fpf_fpsr); + + return fpsr; +} + +static int +fpu_emul_fmovmcr(fe, insn) + struct fpemu *fe; + struct instruction *insn; +{ + struct frame *frame = fe->fe_frame; + struct fpframe *fpf = fe->fe_fpframe; + int word1, sig; + int reglist, regmask, regnum; + int fpu_to_mem; + + /* 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_ea0, insn->is_opcode); + if (sig) { return sig; } + + if (reglist != 1 && reglist != 2 && reglist != 4 && + (insn->is_ea0.ea_flags & EA_DIRECT)) { + /* attempted to copy more than one FPcr to CPU regs */ +#ifdef DEBUG + printf(" fpu_emul_fmovmcr: tried to copy too many FPcr\n"); +#endif + return SIGILL; + } + + if (reglist & 4) { + /* fpcr */ + if ((insn->is_ea0.ea_flags & EA_DIRECT) && + insn->is_ea0.ea_regnum >= 8 /* address reg */) { + /* attempted to copy FPCR to An */ +#ifdef DEBUG + printf(" fpu_emul_fmovmcr: tried to copy FPCR from/to A%d\n", + insn->is_ea0.ea_regnum & 7); +#endif + return SIGILL; + } + if (fpu_to_mem) { + sig = fpu_store_ea(frame, insn, &insn->is_ea0, + (char *)&fpf->fpf_fpcr); + } else { + sig = fpu_load_ea(frame, insn, &insn->is_ea0, + (char *)&fpf->fpf_fpcr); + } + } + if (sig) { return sig; } + + if (reglist & 2) { + /* fpsr */ + if ((insn->is_ea0.ea_flags & EA_DIRECT) && + insn->is_ea0.ea_regnum >= 8 /* address reg */) { + /* attempted to copy FPSR to An */ +#ifdef DEBUG + printf(" fpu_emul_fmovmcr: tried to copy FPSR from/to A%d\n", + insn->is_ea0.ea_regnum & 7); +#endif + return SIGILL; + } + if (fpu_to_mem) { + sig = fpu_store_ea(frame, insn, &insn->is_ea0, + (char *)&fpf->fpf_fpsr); + } else { + sig = fpu_load_ea(frame, insn, &insn->is_ea0, + (char *)&fpf->fpf_fpsr); + } + } + 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_ea0, + (char *)&fpf->fpf_fpiar); + } else { + sig = fpu_load_ea(frame, insn, &insn->is_ea0, + (char *)&fpf->fpf_fpiar); + } + } + return sig; } /* - * type 0: fmovem, fmove <cr> + * type 0: fmovem * Separated out of fpu_emul_type0 for efficiency. * In this function, we know: * (opcode & 0x01C0) == 0 @@ -194,455 +432,747 @@ int fpu_emulate(struct frame *frame, struct fpframe *fpf) * No conversion or rounding is done by this instruction, * and the FPSR is not affected. */ -int fpu_emul_fmovm(struct frame *frame, - struct fpframe *fpf, - struct instruction *insn) +static int +fpu_emul_fmovm(fe, insn) + struct fpemu *fe; + struct instruction *insn; { - int word1, sig; - int reglist, regmask, regnum; - int fpu_to_mem, order; - int w1_post_incr; /* XXX - FP regs order? */ - int *fpregs; - - insn->advance = 4; - insn->datasize = 12; - word1 = insn->word1; - - /* Bit 14 selects FPn or FP control regs. */ - if (word1 & 0x4000) { - /* - * Bits 12,11 select register list mode: - * 0,0: Static reg list, pre-decr. - * 0,1: Dynamic reg list, pre-decr. - * 1,0: Static reg list, post-incr. - * 1,1: Dynamic reg list, post-incr - */ - w1_post_incr = word1 & 0x1000; - if (word1 & 0x0800) { - /* dynamic reg list */ - reglist = frame->f_regs[(word1 & 0x70) >> 4]; - } else - reglist = word1; - reglist &= 0xFF; - } else { - /* XXX: move to/from control registers */ - reglist = word1 & 0x1C00; - return SIGILL; + struct frame *frame = fe->fe_frame; + struct fpframe *fpf = fe->fe_fpframe; + int word1, sig; + int reglist, regmask, regnum; + int fpu_to_mem, order; + int w1_post_incr; /* XXX - FP regs order? */ + int *fpregs; + + insn->is_advance = 4; + insn->is_datasize = 12; + word1 = insn->is_word1; + + /* Bit 13 selects direction (FPU to/from Mem) */ + fpu_to_mem = word1 & 0x2000; + + /* + * Bits 12,11 select register list mode: + * 0,0: Static reg list, pre-decr. + * 0,1: Dynamic reg list, pre-decr. + * 1,0: Static reg list, post-incr. + * 1,1: Dynamic reg list, post-incr + */ + w1_post_incr = word1 & 0x1000; + if (word1 & 0x0800) { + /* dynamic reg list */ + reglist = frame->f_regs[(word1 & 0x70) >> 4]; + } else { + reglist = word1; + } + reglist &= 0xFF; + + /* Get effective address. (modreg=opcode&077) */ + sig = fpu_decode_ea(frame, insn, &insn->is_ea0, insn->is_opcode); + if (sig) { return sig; } + + /* Get address of soft coprocessor regs. */ + fpregs = &fpf->fpf_regs[0]; + + if (insn->is_ea0.ea_flags & EA_PREDECR) { + regnum = 7; + order = -1; + } else { + regnum = 0; + order = 1; + } + + while ((0 <= regnum) && (regnum < 8)) { + regmask = 1 << regnum; + if (regmask & reglist) { + if (fpu_to_mem) { + sig = fpu_store_ea(frame, insn, &insn->is_ea0, + (char*)&fpregs[regnum * 3]); + if (fpu_debug_level & DL_RESULT) + printf(" fpu_emul_fmovm: FP%d (%08x,%08x,%08x) saved\n", + regnum, fpregs[regnum * 3], fpregs[regnum * 3 + 1], + fpregs[regnum * 3 + 2]); + } else { /* mem to fpu */ + sig = fpu_load_ea(frame, insn, &insn->is_ea0, + (char*)&fpregs[regnum * 3]); + if (fpu_debug_level & DL_RESULT) + printf(" fpu_emul_fmovm: FP%d (%08x,%08x,%08x) loaded\n", + regnum, fpregs[regnum * 3], fpregs[regnum * 3 + 1], + fpregs[regnum * 3 + 2]); + } + if (sig) { break; } } + regnum += order; + } - /* Bit 13 selects direction (FPU to/from Mem) */ - fpu_to_mem = word1 & 0x2000; - - /* Get effective address. (modreg=opcode&077) */ - sig = decode_ea(frame, insn, &insn->ea0, insn->opcode); - if (sig) return sig; - - /* Get address of soft coprocessor regs. */ - fpregs = &fpf->fpf_regs[0]; + return sig; +} - if (insn->ea0.flags & EA_PREDECR) { - regnum = 7; - order = -1; +static struct fpn * +fpu_cmp(fe) + struct fpemu *fe; +{ + struct fpn *x = &fe->fe_f1, *y = &fe->fe_f2; + + /* take care of special cases */ + if (x->fp_class < 0 || y->fp_class < 0) { + /* if either of two is a SNAN, result is SNAN */ + x->fp_class = (y->fp_class < x->fp_class) ? y->fp_class : x->fp_class; + } else if (x->fp_class == FPC_INF) { + if (y->fp_class == FPC_INF) { + /* both infinities */ + if (x->fp_sign == y->fp_sign) { + x->fp_class = FPC_ZERO; /* return a signed zero */ + } else { + x->fp_class = FPC_NUM; /* return a faked number w/x's sign */ + x->fp_exp = 16383; + x->fp_mant[0] = FP_1; + } } else { - regnum = 0; - order = 1; + /* y is a number */ + x->fp_class = FPC_NUM; /* return a forged number w/x's sign */ + x->fp_exp = 16383; + x->fp_mant[0] = FP_1; } - - while ((0 <= regnum) && (regnum < 8)) { - regmask = 1 << regnum; - if (regmask & reglist) { - if (fpu_to_mem) - sig = store_ea(frame, insn, &insn->ea0, - (char*) &fpregs[regnum]); - else /* mem to fpu */ - sig = load_ea(frame, insn, &insn->ea0, - (char*) &fpregs[regnum]); - if (sig) break; - } - regnum += order; + } 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 { + /* x and y are both numbers or zeros, 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 0; -} - -int fpu_emul_type0(struct frame *frame, - struct fpframe *fpf, - struct instruction *insn) -{ - int sig; - - /* Get effective address */ - /* XXX */ - - switch(insn->word1 & 0x3F) { - - case 0x00: /* fmove */ - - case 0x01: /* fint */ - case 0x02: /* fsinh */ - case 0x03: /* fintrz */ - case 0x04: /* fsqrt */ - case 0x06: /* flognp1 */ - - case 0x09: /* ftanh */ - case 0x0A: /* fatan */ - case 0x0C: /* fasin */ - case 0x0D: /* fatanh */ - case 0x0E: /* fsin */ - case 0x0F: /* ftan */ - - case 0x10: /* fetox */ - case 0x11: /* ftwotox */ - case 0x12: /* ftentox */ - case 0x14: /* flogn */ - case 0x15: /* flog10 */ - case 0x16: /* flog2 */ - - case 0x18: /* fabs */ - case 0x19: /* fcosh */ - case 0x1A: /* fneg */ - case 0x1C: /* facos */ - case 0x1D: /* fcos */ - case 0x1E: /* fgetexp */ - case 0x1F: /* fgetman */ - - case 0x20: /* fdiv */ - case 0x21: /* fmod */ - case 0x22: /* fadd */ - case 0x23: /* fmul */ - case 0x24: /* fsgldiv */ - case 0x25: /* frem */ - case 0x26: /* fscale */ - case 0x27: /* fsglmul */ - - case 0x28: /* fsub */ - case 0x38: /* fcmp */ - case 0x3A: /* ftst */ - - default: -#ifdef DEBUG - printf("fpu_emul_type0: unknown: opcode=0x%x, word1=0x%x\n", - insn->opcode, insn->word1); -#endif - sig = SIGILL; - - } /* switch */ - return (sig); + } + return x; } /* - * type 1: fdbcc, fscc, ftrapcc - * In this function, we know: - * (opcode & 0x01C0) == 0x0040 + * arithmetic oprations */ -int fpu_emul_type1(struct frame *frame, - struct fpframe *fpf, - struct instruction *insn) +static int +fpu_emul_arith(fe, insn) + struct fpemu *fe; + struct instruction *insn; { - int sig; - - /* Get effective address */ - /* XXX */ - - switch (insn->opcode & 070) { + struct frame *frame = fe->fe_frame; + u_int *fpregs = &(fe->fe_fpframe->fpf_regs[0]); + struct fpn *res; + int word1, sig = 0; + int regnum, format; + int discard_result = 0; + u_int buf[3]; + int flags; + char regname; + + DUMP_INSN(insn); + + if (fpu_debug_level & DL_ARITH) { + printf(" fpu_emul_arith: FPSR = %08x, FPCR = %08x\n", + fe->fe_fpsr, fe->fe_fpcr); + } + + word1 = insn->is_word1; + format = (word1 >> 10) & 7; + regnum = (word1 >> 7) & 7; + + /* fetch a source operand : may not be used */ + if (fpu_debug_level & DL_ARITH) { + printf(" fpu_emul_arith: dst/src FP%d=%08x,%08x,%08x\n", + regnum, fpregs[regnum*3], fpregs[regnum*3+1], + fpregs[regnum*3+2]); + } + fpu_explode(fe, &fe->fe_f1, FTYPE_EXT, &fpregs[regnum * 3]); + + DUMP_INSN(insn); + + /* get the other operand which is always the source */ + if ((word1 & 0x4000) == 0) { + if (fpu_debug_level & DL_ARITH) { + printf(" fpu_emul_arith: FP%d op FP%d => FP%d\n", + format, regnum, regnum); + printf(" fpu_emul_arith: src opr FP%d=%08x,%08x,%08x\n", + format, fpregs[format*3], fpregs[format*3+1], + fpregs[format*3+2]); + } + fpu_explode(fe, &fe->fe_f2, FTYPE_EXT, &fpregs[format * 3]); + } else { + /* the operand is in memory */ + if (format == FTYPE_DBL) { + insn->is_datasize = 8; + } else if (format == FTYPE_SNG || format == FTYPE_LNG) { + insn->is_datasize = 4; + } else if (format == FTYPE_WRD) { + insn->is_datasize = 2; + } else if (format == FTYPE_BYT) { + insn->is_datasize = 1; + } else if (format == FTYPE_EXT) { + insn->is_datasize = 12; + } else { + /* invalid or unsupported operand format */ + sig = SIGFPE; + return sig; + } - case 010: /* fdbcc */ - /* XXX: If not CC { Decrement Dn; if (Dn >= 0) branch; } */ + /* Get effective address. (modreg=opcode&077) */ + sig = fpu_decode_ea(frame, insn, &insn->is_ea0, insn->is_opcode); + if (sig) { + if (fpu_debug_level & DL_ARITH) { + printf(" fpu_emul_arith: error in fpu_decode_ea\n"); + } + return sig; + } - case 070: /* fscc or ftrapcc */ - if ((insn->opcode & 07) > 1) { - /* ftrapcc */ - /* XXX: If CC, advance and return SIGFPE */ - break; + DUMP_INSN(insn); + + if (fpu_debug_level & DL_ARITH) { + printf(" fpu_emul_arith: addr mode = "); + flags = insn->is_ea0.ea_flags; + regname = (insn->is_ea0.ea_regnum & 8) ? 'a' : 'd'; + + if (flags & EA_DIRECT) { + printf("%c%d\n", + regname, insn->is_ea0.ea_regnum & 7); + } else if (flags & EA_PC_REL) { + if (flags & EA_OFFSET) { + printf("pc@(%d)\n", insn->is_ea0.ea_offset); + } else if (flags & EA_INDEXED) { + printf("pc@(...)\n"); } - /* fallthrough */ - default: /* fscc */ - /* XXX: If CC, store ones, else store zero */ - sig = SIGILL; - break; - + } else if (flags & EA_PREDECR) { + printf("%c%d@-\n", + regname, insn->is_ea0.ea_regnum & 7); + } else if (flags & EA_POSTINCR) { + printf("%c%d@+\n", regname, insn->is_ea0.ea_regnum & 7); + } else if (flags & EA_OFFSET) { + printf("%c%d@(%d)\n", regname, insn->is_ea0.ea_regnum & 7, + insn->is_ea0.ea_offset); + } else if (flags & EA_INDEXED) { + printf("%c%d@(...)\n", regname, insn->is_ea0.ea_regnum & 7); + } else if (flags & EA_ABS) { + printf("0x%08x\n", insn->is_ea0.ea_absaddr); + } else if (flags & EA_IMMED) { + + printf("#0x%08x,%08x,%08x\n", insn->is_ea0.ea_immed[0], + insn->is_ea0.ea_immed[1], insn->is_ea0.ea_immed[2]); + } else { + printf("%c%d@\n", regname, insn->is_ea0.ea_regnum & 7); + } + } /* if (fpu_debug_level & DL_ARITH) */ + + fpu_load_ea(frame, insn, &insn->is_ea0, (char*)buf); + if (format == FTYPE_WRD) { + /* sign-extend */ + buf[0] &= 0xffff; + if (buf[0] & 0x8000) { + buf[0] |= 0xffff0000; + } + format = FTYPE_LNG; + } else if (format == FTYPE_BYT) { + /* sign-extend */ + buf[0] &= 0xff; + if (buf[0] & 0x80) { + buf[0] |= 0xffffff00; + } + format = FTYPE_LNG; } - return (sig); -} - -/* - * Type 2 or 3: fbcc (also fnop) - * In this function, we know: - * (opcode & 0x0180) == 0x0080 - */ -int fpu_emul_brcc(struct frame *frame, - struct fpframe *fpf, - struct instruction *insn) -{ - int displ, word2; - int sig, advance; - - /* - * Get branch displacement. - */ - advance = 4; - displ = insn->word1; - if (displ & 0x8000) - displ |= 0xFFFF0000; - - if (insn->opcode & 0x40) { - word2 = fusword(frame->f_pc + 4); - if (word2 < 0) { -#ifdef DEBUG - printf("fpu_emul_brcc: fault reading word2\n"); + if (fpu_debug_level & DL_ARITH) { + printf(" fpu_emul_arith: src = %08x %08x %08x, siz = %d\n", + buf[0], buf[1], buf[2], insn->is_datasize); + } + fpu_explode(fe, &fe->fe_f2, format, buf); + } + + DUMP_INSN(insn); + + /* An arithmetic instruction emulate function has a prototype of + * struct fpn *fpu_op(struct fpemu *); + + * 1) If the instruction is monadic, then fpu_op() must use + * fe->fe_f2 as its operand, and return a pointer to the + * result. + + * 2) If the instruction is diadic, then fpu_op() must use + * fe->fe_f1 and fe->fe_f2 as its two operands, and return a + * pointer to the result. + + */ + switch (word1 & 0x3f) { + case 0x00: /* fmove */ + res = &fe->fe_f2; + break; + + case 0x01: /* fint */ + res = fpu_int(fe); + break; + + case 0x02: /* fsinh */ + res = fpu_sinh(fe); + break; + + case 0x03: /* fintrz */ + res = fpu_intrz(fe); + break; + + case 0x04: /* fsqrt */ + res = fpu_sqrt(fe); + break; + + case 0x06: /* flognp1 */ + res = fpu_lognp1(fe); + break; + + case 0x08: /* fetoxm1 */ + res = fpu_etoxm1(fe); + break; + + case 0x09: /* ftanh */ + res = fpu_tanh(fe); + break; + + case 0x0A: /* fatan */ + res = fpu_atan(fe); + break; + + case 0x0C: /* fasin */ + res = fpu_asin(fe); + break; + + case 0x0D: /* fatanh */ + res = fpu_atanh(fe); + break; + + case 0x0E: /* fsin */ + res = fpu_sin(fe); + break; + + case 0x0F: /* ftan */ + res = fpu_tan(fe); + break; + + case 0x10: /* fetox */ + res = fpu_etox(fe); + break; + + case 0x11: /* ftwotox */ + res = fpu_twotox(fe); + break; + + case 0x12: /* ftentox */ + res = fpu_tentox(fe); + break; + + case 0x14: /* flogn */ + res = fpu_logn(fe); + break; + + case 0x15: /* flog10 */ + res = fpu_log10(fe); + break; + + case 0x16: /* flog2 */ + res = fpu_log2(fe); + break; + + case 0x18: /* fabs */ + fe->fe_f2.fp_sign = 0; + res = &fe->fe_f2; + break; + + case 0x19: /* fcosh */ + res = fpu_cosh(fe); + break; + + case 0x1A: /* fneg */ + fe->fe_f2.fp_sign = !fe->fe_f2.fp_sign; + res = &fe->fe_f2; + break; + + case 0x1C: /* facos */ + res = fpu_acos(fe); + break; + + case 0x1D: /* fcos */ + res = fpu_cos(fe); + break; + + case 0x1E: /* fgetexp */ + res = fpu_getexp(fe); + break; + + case 0x1F: /* fgetman */ + res = fpu_getman(fe); + break; + + case 0x20: /* fdiv */ + case 0x24: /* fsgldiv: cheating - better than nothing */ + res = fpu_div(fe); + break; + + case 0x21: /* fmod */ + res = fpu_mod(fe); + break; + + case 0x28: /* fsub */ + fe->fe_f2.fp_sign = !fe->fe_f2.fp_sign; /* f2 = -f2 */ + case 0x22: /* fadd */ + res = fpu_add(fe); + break; + + case 0x23: /* fmul */ + case 0x27: /* fsglmul: cheating - better than nothing */ + res = fpu_mul(fe); + break; + + case 0x25: /* frem */ + res = fpu_rem(fe); + break; + + case 0x26: + /* fscale is handled by a separate function */ + break; + + case 0x30: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: /* fsincos */ + res = fpu_sincos(fe, word1 & 7); + break; + + case 0x38: /* fcmp */ + res = fpu_cmp(fe); + discard_result = 1; + break; + + case 0x3A: /* ftst */ + res = &fe->fe_f2; + discard_result = 1; + break; + + default: +#ifdef DEBUG + printf(" fpu_emul_arith: bad opcode=0x%x, word1=0x%x\n", + insn->is_opcode, insn->is_word1); #endif - return SIGSEGV; - } - displ << 16; - displ |= word2; - advance += 2; + sig = SIGILL; + } /* switch (word1 & 0x3f) */ + + if (!discard_result && sig == 0) { + fpu_implode(fe, res, FTYPE_EXT, &fpregs[regnum * 3]); + if (fpu_debug_level & DL_ARITH) { + printf(" fpu_emul_arith: %08x,%08x,%08x stored in FP%d\n", + fpregs[regnum*3], fpregs[regnum*3+1], + fpregs[regnum*3+2], regnum); } - - /* XXX: If CC, frame->f_pc += displ */ - return SIGILL; + } else if (sig == 0 && fpu_debug_level & DL_ARITH) { + static char *class_name[] = { "SNAN", "QNAN", "ZERO", "NUM", "INF" }; + printf(" fpu_emul_arith: result(%s,%c,%d,%08x,%08x,%08x,%08x) discarded\n", + class_name[res->fp_class + 2], + res->fp_sign ? '-' : '+', res->fp_exp, + res->fp_mant[0], res->fp_mant[1], + res->fp_mant[2], res->fp_mant[3]); + } else if (fpu_debug_level & DL_ARITH) { + printf(" fpu_emul_arith: received signal %d\n", sig); + } + + /* update fpsr according to the result of operation */ + fpu_upd_fpsr(fe, res); + + if (fpu_debug_level & DL_ARITH) { + printf(" fpu_emul_arith: FPSR = %08x, FPCR = %08x\n", + fe->fe_fpsr, fe->fe_fpcr); + } + + DUMP_INSN(insn); + + return sig; } -/* - * Helper routines for dealing with "effective address" values. - */ - -/* - * Decode an effective address into internal form. - * Returns zero on success, else signal number. +/* test condition code according to the predicate in the opcode. + * returns -1 when the predicate evaluates to true, 0 when false. + * signal numbers are returned when an error is detected. */ -static int decode_ea(struct frame *frame, - struct instruction *insn, - struct insn_ea *ea, - int modreg) +static int +test_cc(fe, pred) + struct fpemu *fe; + int pred; { - int immed_bytes = 0; - int data; - - /* Set the most common value here. */ - ea->regnum = 8 + (modreg & 7); - - switch (modreg & 070) { - - case 0: /* Dn */ - ea->regnum = (modreg & 7); - ea->flags = EA_DIRECT; - break; - - case 010: /* An */ - ea->flags = EA_DIRECT; - break; - - case 020: /* (An) */ - ea->flags = 0; - break; - - case 030: /* (An)+ */ - ea->flags = EA_POSTINCR; - break; - - case 040: /* -(An) */ - ea->flags = EA_PREDECR; - break; - - case 050: /* (d16,An) */ - ea->flags = EA_OFFSET; - immed_bytes = 2; - break; - - case 060: /* (d8,An,Xn) */ - ea->flags = EA_INDEXED; - immed_bytes = 2; - break; + int result, sig_bsun, invert; + int fpsr; + + fpsr = fe->fe_fpsr; + invert = 0; + fpsr &= ~FPSR_EXCP; /* clear all exceptions */ + if (fpu_debug_level & DL_TESTCC) { + printf(" test_cc: fpsr=0x%08x\n", fpsr); + } + pred &= 0x3f; /* lowest 6 bits */ + + if (fpu_debug_level & DL_TESTCC) { + printf(" test_cc: "); + } + + if (pred >= 040) { + return SIGILL; + } else if (pred & 0x10) { + /* IEEE nonaware tests */ + sig_bsun = 1; + pred &= 017; /* lower 4 bits */ + } else { + /* IEEE aware tests */ + if (fpu_debug_level & DL_TESTCC) { + printf("IEEE "); + } + sig_bsun = 0; + } - case 070: /* misc. */ - ea->regnum = (modreg & 7); - switch (modreg & 7) { - - case 0: /* (xxxx).W */ - ea->flags = EA_ABS; - immed_bytes = 2; - break; - - case 1: /* (xxxxxxxx).L */ - ea->flags = EA_ABS; - immed_bytes = 4; - break; - - case 2: /* (d16,PC) */ - ea->flags = EA_PC_REL | EA_OFFSET; - immed_bytes = 2; - break; - - case 3: /* (d8,PC,Xn) */ - ea->flags = EA_PC_REL | EA_INDEXED; - immed_bytes = 2; - break; - - case 4: /* #data */ - ea->flags = EA_IMMED; - immed_bytes = insn->datasize; - break; - - default: - return SIGILL; - } /* switch for mode 7 */ - break; - } /* switch mode */ - - /* Now fetch any immediate data and advance. */ - if (immed_bytes > 0) { - data = fusword(frame->f_pc + insn->advance); - if (data < 0) - return SIGSEGV; - insn->advance += 2; - if (data & 0x8000) - data |= 0xFFFF0000; - ea->immed = data; + if (pred >= 010) { + if (fpu_debug_level & DL_TESTCC) { + printf("Not "); + } + /* predicate is "NOT ..." */ + pred ^= 0xf; /* invert */ + invert = -1; + } + switch (pred) { + case 0: /* (Signaling) False */ + if (fpu_debug_level & DL_TESTCC) { + printf("False"); + } + result = 0; + break; + case 1: /* (Signaling) Equal */ + if (fpu_debug_level & DL_TESTCC) { + printf("Equal"); + } + result = -((fpsr & FPSR_ZERO) == FPSR_ZERO); + break; + case 2: /* Greater Than */ + if (fpu_debug_level & DL_TESTCC) { + printf("GT"); + } + result = -((fpsr & (FPSR_NAN|FPSR_ZERO|FPSR_NEG)) == 0); + break; + case 3: /* Greater or Equal */ + if (fpu_debug_level & DL_TESTCC) { + printf("GE"); + } + result = -((fpsr & FPSR_ZERO) || + (fpsr & (FPSR_NAN|FPSR_NEG)) == 0); + break; + case 4: /* Less Than */ + if (fpu_debug_level & DL_TESTCC) { + printf("LT"); } - if (immed_bytes > 2) { - data = fusword(frame->f_pc + insn->advance); - if (data < 0) - return SIGSEGV; - insn->advance += 2; - ea->immed <<= 16; - ea->immed |= data; + result = -((fpsr & (FPSR_NAN|FPSR_ZERO|FPSR_NEG)) == FPSR_NEG); + break; + case 5: /* Less or Equal */ + if (fpu_debug_level & DL_TESTCC) { + printf("LE"); } - return 0; + result = -((fpsr & FPSR_ZERO) || + ((fpsr & (FPSR_NAN|FPSR_NEG)) == FPSR_NEG)); + break; + case 6: /* Greater or Less than */ + if (fpu_debug_level & DL_TESTCC) { + printf("GLT"); + } + result = -((fpsr & (FPSR_NAN|FPSR_ZERO)) == 0); + break; + case 7: /* Greater, Less or Equal */ + if (fpu_debug_level & DL_TESTCC) { + printf("GLE"); + } + result = -((fpsr & FPSR_NAN) == 0); + break; + default: + /* invalid predicate */ + return SIGILL; + } + result ^= invert; /* if the predicate is "NOT ...", then + invert the result */ + if (fpu_debug_level & DL_TESTCC) { + printf(" => %s (%d)\n", result ? "true" : "false", result); + } + /* if it's an IEEE unaware test and NAN is set, BSUN is set */ + if (sig_bsun && (fpsr & FPSR_NAN)) { + fpsr |= FPSR_BSUN; + } + + /* put fpsr back */ + fe->fe_fpframe->fpf_fpsr = fe->fe_fpsr = fpsr; + + return result; } - /* - * Load a value from an effective address. - * Returns zero on success, else signal number. + * type 1: fdbcc, fscc, ftrapcc + * In this function, we know: + * (opcode & 0x01C0) == 0x0040 */ -static int load_ea(struct frame *frame, - struct instruction *insn, - struct insn_ea *ea, - char *dst) +static int +fpu_emul_type1(fe, insn) + struct fpemu *fe; + struct instruction *insn; { - int *reg; - char *src; - int len; - -#ifdef DIAGNOSTIC - if (ea->regnum & ~0xF) - panic("load_ea: bad regnum"); + struct frame *frame = fe->fe_frame; + struct fpframe *fpf = fe->fe_fpframe; + int advance, sig, branch, displ; + + branch = test_cc(fe, insn->is_word1); + 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) { + /* advance */ + insn->is_advance = 6; + } else if (!branch) { + /* decrement Dn and if (Dn != -1) branch */ + u_int16_t count = frame->f_regs[insn->is_opcode & 7]; + + if (count-- != 0) { + displ = fusword(frame->f_pc + insn->is_advance); + if (displ < 0) { +#ifdef DEBUG + printf(" fpu_emul_type1: fault reading displacement\n"); #endif + return SIGSEGV; + } + /* sign-extend the displacement */ + displ &= 0xffff; + if (displ & 0x8000) { + displ |= 0xffff0000; + } + insn->is_advance += displ; + } else { + insn->is_advance = 6; + } + /* write it back */ + frame->f_regs[insn->is_opcode & 7] &= 0xffff0000; + frame->f_regs[insn->is_opcode & 7] |= (u_int32_t)count; + } else { /* got a signal */ + sig = SIGFPE; + } + break; - /* The dst is always int or larger. */ - len = insn->datasize; - if (len < 4) - dst += (4 - len); - - /* point to the register */ - if (ea->flags & EA_PC_REL) - reg = &frame->f_pc; - else - reg = &frame->f_regs[ea->regnum]; - - if (ea->flags & (EA_DIRECT | EA_IMMED)) { - if (ea->flags & EA_DIRECT) - src = (char*) reg; - if (ea->flags & EA_IMMED) - src = (char*) &ea->immed; - if (len > 4) - return SIGILL; - /* The source is an int. */ - if (len < 4) - src += (4 - len); - bcopy(src, dst, len); + case 070: /* ftrapcc or fscc */ + advance = 4; + if ((insn->is_opcode & 07) >= 2) { + switch (insn->is_opcode & 07) { + case 3: /* long opr */ + advance += 2; + case 2: /* word opr */ + advance += 2; + case 4: /* no opr */ + break; + default: + return SIGILL; + break; + } + + if (branch == 0) { + /* no trap */ + insn->is_advance = advance; + sig = 0; + } else { + /* trap */ + sig = SIGFPE; + } + break; + } /* if ((insn->is_opcode & 7) < 2), fall through to FScc */ + + default: /* fscc */ + insn->is_advance = 4; + insn->is_datasize = 1; /* always byte */ + sig = fpu_decode_ea(frame, insn, &insn->is_ea0, insn->is_opcode); + if (sig) { + break; + } + if (branch == -1 || branch == 0) { + /* set result */ + sig = fpu_store_ea(frame, insn, &insn->is_ea0, (char *)&branch); } else { - /* One of MANY indirect forms... */ - - /* do pre-decrement */ - if (ea->flags & EA_PREDECR) - *reg -= len; - - /* Grab the register contents. */ - src = (char*) *reg; - - /* apply the signed offset */ - if (ea->flags & EA_OFFSET) - src += ea->immed; - - /* XXX - Don't know how to handle this yet. */ - if (ea->flags & EA_INDEXED) - return SIGILL; - - copyin(src, dst, len); - - /* do post-increment */ - if (ea->flags & EA_POSTINCR) - *reg += len; + /* got an exception */ + sig = branch; } - - return 0; + break; + } + return sig; } /* - * Store a value at the effective address. - * Returns zero on success, else signal number. + * Type 2 or 3: fbcc (also fnop) + * In this function, we know: + * (opcode & 0x0180) == 0x0080 */ -static int store_ea(struct frame *frame, - struct instruction *insn, - struct insn_ea *ea, - char *src) +static int +fpu_emul_brcc(fe, insn) + struct fpemu *fe; + struct instruction *insn; { - int *reg; - char *dst; - int len; - -#ifdef DIAGNOSTIC - if (ea->regnum & ~0xF) - panic("load_ea: bad regnum"); + struct frame *frame = fe->fe_frame; + struct fpframe *fpf = fe->fe_fpframe; + int displ, word2; + int sig, advance; + + /* + * Get branch displacement. + */ + insn->is_advance = 4; + displ = insn->is_word1; + + if (insn->is_opcode & 0x40) { + word2 = fusword(frame->f_pc + insn->is_advance); + if (word2 < 0) { +#ifdef DEBUG + printf(" fpu_emul_brcc: fault reading word2\n"); #endif - - /* The src is always int or larger. */ - len = insn->datasize; - if (len < 4) - src += (4 - len); - - /* point to the register */ - if (ea->flags & EA_PC_REL) - reg = &frame->f_pc; - else - reg = &frame->f_regs[ea->regnum]; - - if (ea->flags & EA_IMMED) - return SIGILL; - - if (ea->flags & EA_DIRECT) { - dst = (char*) reg; - if (len > 4) - return SIGILL; - /* The destination is an int. */ - if (len < 4) - dst += (4 - len); - bcopy(src, dst, len); - } else { - /* One of MANY indirect forms... */ - - /* do pre-decrement */ - if (ea->flags & EA_PREDECR) - *reg -= len; - - /* Grab the register contents. */ - dst = (char*) *reg; - - /* apply the signed offset */ - if (ea->flags & EA_OFFSET) - dst += ea->immed; - - /* XXX - Don't know how to handle this yet. */ - if (ea->flags & EA_INDEXED) - return SIGILL; - - copyout(src, dst, len); - - /* do post-increment */ - if (ea->flags & EA_POSTINCR) - *reg += len; + return SIGSEGV; } - - return 0; + displ <<= 16; + displ |= word2; + insn->is_advance += 2; + } else /* displacement is word sized */ + if (displ & 0x8000) + displ |= 0xFFFF0000; + + /* XXX: If CC, frame->f_pc += displ */ + sig = test_cc(fe, insn->is_opcode); + fe->fe_fpframe->fpf_fpsr = fe->fe_fpsr; + + if (fe->fe_fpsr & fe->fe_fpcr & FPSR_EXCP) { + return SIGFPE; /* caught an exception */ + } + if (sig == -1) { + /* branch does take place; 2 is the offset to the 1st disp word */ + insn->is_advance = displ + 2; + } else if (sig) { + return SIGILL; /* got a signal */ + } + if (fpu_debug_level & DL_BRANCH) { + printf(" fpu_emul_brcc: %s insn @ %x (%x+%x) (disp=%x)\n", + (sig == -1) ? "BRANCH to" : "NEXT", + frame->f_pc + insn->is_advance, frame->f_pc, insn->is_advance, + displ); + } + return 0; } diff --git a/sys/arch/m68k/fpe/fpu_emulate.h b/sys/arch/m68k/fpe/fpu_emulate.h new file mode 100644 index 00000000000..ae31c2bfe15 --- /dev/null +++ b/sys/arch/m68k/fpe/fpu_emulate.h @@ -0,0 +1,335 @@ +/* $NetBSD: fpu_emulate.h,v 1.2 1995/11/05 00:35:20 briggs Exp $ */ + +/* + * Copyright (c) 1995 Gordon Ross + * Copyright (c) 1995 Ken Nakata + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * 4. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Gordon Ross + * + * 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. + */ + +#ifndef _FPU_EMULATE_H_ +#define _FPU_EMULATE_H_ + +#include <sys/types.h> + +/* + * Floating point emulator (tailored for SPARC/modified for m68k, but + * structurally machine-independent). + * + * Floating point numbers are carried around internally in an `expanded' + * or `unpacked' form consisting of: + * - sign + * - unbiased exponent + * - mantissa (`1.' + 112-bit fraction + guard + round) + * - sticky bit + * Any implied `1' bit is inserted, giving a 113-bit mantissa that is + * always nonzero. Additional low-order `guard' and `round' bits are + * scrunched in, making the entire mantissa 115 bits long. This is divided + * into four 32-bit words, with `spare' bits left over in the upper part + * of the top word (the high bits of fp_mant[0]). An internal `exploded' + * number is thus kept within the half-open interval [1.0,2.0) (but see + * the `number classes' below). This holds even for denormalized numbers: + * when we explode an external denorm, we normalize it, introducing low-order + * zero bits, so that the rest of the code always sees normalized values. + * + * Note that a number of our algorithms use the `spare' bits at the top. + * The most demanding algorithm---the one for sqrt---depends on two such + * bits, so that it can represent values up to (but not including) 8.0, + * and then it needs a carry on top of that, so that we need three `spares'. + * + * The sticky-word is 32 bits so that we can use `OR' operators to goosh + * whole words from the mantissa into it. + * + * All operations are done in this internal extended precision. According + * to Hennesey & Patterson, Appendix A, rounding can be repeated---that is, + * it is OK to do a+b in extended precision and then round the result to + * single precision---provided single, double, and extended precisions are + * `far enough apart' (they always are), but we will try to avoid any such + * extra work where possible. + */ +struct fpn { + int fp_class; /* see below */ + int fp_sign; /* 0 => positive, 1 => negative */ + int fp_exp; /* exponent (unbiased) */ + int fp_sticky; /* nonzero bits lost at right end */ + u_int fp_mant[4]; /* 115-bit mantissa */ +}; + +#define FP_NMANT 115 /* total bits in mantissa (incl g,r) */ +#define FP_NG 2 /* number of low-order guard bits */ +#define FP_LG ((FP_NMANT - 1) & 31) /* log2(1.0) for fp_mant[0] */ +#define FP_QUIETBIT (1 << (FP_LG - 1)) /* Quiet bit in NaNs (0.5) */ +#define FP_1 (1 << FP_LG) /* 1.0 in fp_mant[0] */ +#define FP_2 (1 << (FP_LG + 1)) /* 2.0 in fp_mant[0] */ + +#define CPYFPN(dst, src) \ +if ((dst) != (src)) { \ + (dst)->fp_class = (src)->fp_class; \ + (dst)->fp_sign = (src)->fp_sign; \ + (dst)->fp_exp = (src)->fp_exp; \ + (dst)->fp_sticky = (src)->fp_sticky; \ + (dst)->fp_mant[0] = (src)->fp_mant[0]; \ + (dst)->fp_mant[1] = (src)->fp_mant[1]; \ + (dst)->fp_mant[2] = (src)->fp_mant[2]; \ + (dst)->fp_mant[3] = (src)->fp_mant[3]; \ +} + +/* + * Number classes. Since zero, Inf, and NaN cannot be represented using + * the above layout, we distinguish these from other numbers via a class. + */ +#define FPC_SNAN -2 /* signalling NaN (sign irrelevant) */ +#define FPC_QNAN -1 /* quiet NaN (sign irrelevant) */ +#define FPC_ZERO 0 /* zero (sign matters) */ +#define FPC_NUM 1 /* number (sign matters) */ +#define FPC_INF 2 /* infinity (sign matters) */ + +#define ISNAN(fp) ((fp)->fp_class < 0) +#define ISZERO(fp) ((fp)->fp_class == 0) +#define ISINF(fp) ((fp)->fp_class == FPC_INF) + +/* + * ORDER(x,y) `sorts' a pair of `fpn *'s so that the right operand (y) points + * to the `more significant' operand for our purposes. Appendix N says that + * the result of a computation involving two numbers are: + * + * If both are SNaN: operand 2, converted to Quiet + * If only one is SNaN: the SNaN operand, converted to Quiet + * If both are QNaN: operand 2 + * If only one is QNaN: the QNaN operand + * + * In addition, in operations with an Inf operand, the result is usually + * Inf. The class numbers are carefully arranged so that if + * (unsigned)class(op1) > (unsigned)class(op2) + * then op1 is the one we want; otherwise op2 is the one we want. + */ +#define ORDER(x, y) { \ + if ((u_int)(x)->fp_class > (u_int)(y)->fp_class) \ + SWAP(x, y); \ +} +#define SWAP(x, y) { \ + register struct fpn *swap; \ + swap = (x), (x) = (y), (y) = swap; \ +} + +/* + * Emulator state. + */ +struct fpemu { + struct frame *fe_frame; /* integer regs, etc */ + struct fpframe *fe_fpframe; /* FP registers, etc */ + u_int fe_fpsr; /* fpsr copy (modified during op) */ + u_int fe_fpcr; /* fpcr copy */ + struct fpn fe_f1; /* operand 1 */ + struct fpn fe_f2; /* operand 2, if required */ + struct fpn fe_f3; /* available storage for result */ +}; + +/***************************************************************************** + * End of definitions derived from Sparc FPE + *****************************************************************************/ + +/* + * Internal info about a decoded effective address. + */ +struct insn_ea { + int ea_regnum; + int ea_ext[3]; /* extention words if any */ + int ea_flags; /* flags == 0 means mode 2: An@ */ +#define EA_DIRECT 0x001 /* mode [01]: Dn or An */ +#define EA_PREDECR 0x002 /* mode 4: An@- */ +#define EA_POSTINCR 0x004 /* mode 3: An@+ */ +#define EA_OFFSET 0x008 /* mode 5 or (7,2): APC@(d16) */ +#define EA_INDEXED 0x010 /* mode 6 or (7,3): APC@(Xn:*:*,d8) etc */ +#define EA_ABS 0x020 /* mode (7,[01]): abs */ +#define EA_PC_REL 0x040 /* mode (7,[23]): PC@(d16) etc */ +#define EA_IMMED 0x080 /* mode (7,4): #immed */ +#define EA_MEM_INDIR 0x100 /* mode 6 or (7,3): APC@(Xn:*:*,*)@(*) etc */ +#define EA_BASE_SUPPRSS 0x200 /* mode 6 or (7,3): base register suppressed */ + int ea_tdisp; /* temp. displ. used to xfer many words */ +}; + +#define ea_offset ea_ext[0] /* mode 5: offset word */ +#define ea_absaddr ea_ext[0] /* mode (7,[01]): absolute address */ +#define ea_immed ea_ext /* mode (7,4): immediate value */ +#define ea_basedisp ea_ext[0] /* mode 6: base displacement */ +#define ea_outerdisp ea_ext[1] /* mode 6: outer displacement */ +#define ea_idxreg ea_ext[2] /* mode 6: index register number */ + +struct instruction { + int is_advance; /* length of instruction */ + int is_datasize; /* byte, word, long, float, double, ... */ + int is_opcode; /* opcode word */ + int is_word1; /* second word */ + struct insn_ea is_ea0; /* decoded effective address mode */ +}; + +/* + * FP data types + */ +#define FTYPE_LNG 0 /* Long Word Integer */ +#define FTYPE_SNG 1 /* Single Prec */ +#define FTYPE_EXT 2 /* Extended Prec */ +#define FTYPE_BCD 3 /* Packed BCD */ +#define FTYPE_WRD 4 /* Word Integer */ +#define FTYPE_DBL 5 /* Double Prec */ +#define FTYPE_BYT 6 /* Byte Integer */ + +/* + * MC68881/68882 FPcr bit definitions (should these go to <m68k/reg.h> + * or <m68k/fpu.h> or something?) + */ + +/* fpsr */ +#define FPSR_CCB 0xff000000 +# define FPSR_NEG 0x08000000 +# define FPSR_ZERO 0x04000000 +# define FPSR_INF 0x02000000 +# define FPSR_NAN 0x01000000 +#define FPSR_QTT 0x00ff0000 +# define FPSR_QSG 0x00800000 +# define FPSR_QUO 0x007f0000 +#define FPSR_EXCP 0x0000ff00 +# define FPSR_BSUN 0x00008000 +# define FPSR_SNAN 0x00004000 +# define FPSR_OPERR 0x00002000 +# define FPSR_OVFL 0x00001000 +# define FPSR_UNFL 0x00000800 +# define FPSR_DZ 0x00000400 +# define FPSR_INEX2 0x00000200 +# define FPSR_INEX1 0x00000100 +#define FPSR_AEX 0x000000ff +# define FPSR_AIOP 0x00000080 +# define FPSR_AOVFL 0x00000040 +# define FPSR_AUNFL 0x00000020 +# define FPSR_ADZ 0x00000010 +# define FPSR_AINEX 0x00000008 + +/* fpcr */ +#define FPCR_EXCP FPSR_EXCP +# define FPCR_BSUN FPSR_BSUN +# define FPCR_SNAN FPSR_SNAN +# define FPCR_OPERR FPSR_OPERR +# define FPCR_OVFL FPSR_OVFL +# define FPCR_UNFL FPSR_UNFL +# define FPCR_DZ FPSR_DZ +# define FPCR_INEX2 FPSR_INEX2 +# define FPCR_INEX1 FPSR_INEX1 +#define FPCR_MODE 0x000000ff +# define FPCR_PREC 0x000000c0 +# define FPCR_EXTD 0x00000000 +# define FPCR_SNGL 0x00000040 +# define FPCR_DBL 0x00000080 +# define FPCR_ROUND 0x00000030 +# define FPCR_NEAR 0x00000000 +# define FPCR_ZERO 0x00000010 +# define FPCR_MINF 0x00000020 +# define FPCR_PINF 0x00000030 + +/* + * Other functions. + */ + +/* Build a new Quiet NaN (sign=0, frac=all 1's). */ +struct fpn *fpu_newnan __P((struct fpemu *fe)); + +/* + * Shift a number right some number of bits, taking care of round/sticky. + * Note that the result is probably not a well-formed number (it will lack + * the normal 1-bit mant[0]&FP_1). + */ +int fpu_shr __P((struct fpn * fp, int shr)); +/* + * Round a number according to the round mode in FPCR + */ +int round __P((register struct fpemu *fe, register struct fpn *fp)); + +/* type conversion */ +void fpu_explode __P((struct fpemu *fe, struct fpn *fp, int t, u_int *src)); +void fpu_implode __P((struct fpemu *fe, struct fpn *fp, int t, u_int *dst)); + +/* + * non-static emulation functions + */ +/* type 0 */ +int fpu_emul_fmovecr __P((struct fpemu *fe, struct instruction *insn)); +int fpu_emul_fstore __P((struct fpemu *fe, struct instruction *insn)); +int fpu_emul_fscale __P((struct fpemu *fe, struct instruction *insn)); + +/* + * include function declarations of those which are called by fpu_emul_arith() + */ +#include "fpu_arith_proto.h" + +/* + * "helper" functions + */ +/* return values from constant rom */ +struct fpn *fpu_const __P((struct fpn *fp, u_int offset)); +/* update exceptions and FPSR */ +int fpu_upd_excp __P((struct fpemu *fe)); +u_int fpu_upd_fpsr __P((struct fpemu *fe, struct fpn *fp)); + +/* address mode decoder, and load/store */ +int fpu_decode_ea __P((struct frame *frame, struct instruction *insn, + struct insn_ea *ea, int modreg)); +int fpu_load_ea __P((struct frame *frame, struct instruction *insn, + struct insn_ea *ea, char *dst)); +int fpu_store_ea __P((struct frame *frame, struct instruction *insn, + struct insn_ea *ea, char *src)); + +/* macros for debugging */ +#define DEBUG /* XXX */ + +extern int fpu_debug_level; + +/* debug classes */ +#define DL_DUMPINSN 0x0001 +#define DL_DECODEEA 0x0002 +#define DL_LOADEA 0x0004 +#define DL_STOREEA 0x0008 +#define DL_OPERANDS 0x0010 +#define DL_RESULT 0x0020 +#define DL_TESTCC 0x0040 +#define DL_BRANCH 0x0080 +#define DL_FSTORE 0x0100 +#define DL_FSCALE 0x0200 +#define DL_ARITH 0x0400 +#define DL_INSN 0x0800 +#define DL_FMOVEM 0x1000 +/* not defined yet +#define DL_2000 0x2000 +#define DL_4000 0x4000 +*/ +#define DL_VERBOSE 0x8000 +/* composit debug classes */ +#define DL_EA (DL_DECODEEA|DL_LOADEA|DL_STOREEA) +#define DL_VALUES (DL_OPERANDS|DL_RESULT) +#define DL_COND (DL_TESTCC|DL_BRANCH) +#define DL_ALL 0xffff + +#endif /* _FPU_EMULATE_H_ */ diff --git a/sys/arch/m68k/fpe/fpu_exp.c b/sys/arch/m68k/fpe/fpu_exp.c new file mode 100644 index 00000000000..5e7966bf966 --- /dev/null +++ b/sys/arch/m68k/fpe/fpu_exp.c @@ -0,0 +1,70 @@ +/* $NetBSD: fpu_exp.c,v 1.1 1995/11/03 04:47:06 briggs Exp $ */ + +/* + * Copyright (c) 1995 Ken Nakata + * 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. Neither the name of the author 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 AUTHOR 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 AUTHOR 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_exp.c 10/24/95 + */ + +#include "fpu_emulate.h" + +/* + * fpu_exp.c: defines fpu_etox(), fpu_etoxm1(), fpu_tentox(), and fpu_twotox(); + */ + +struct fpn * +fpu_etox(fe) + struct fpemu *fe; +{ + /* stub */ + return &fe->fe_f2; +} + +struct fpn * +fpu_etoxm1(fe) + struct fpemu *fe; +{ + /* stub */ + return &fe->fe_f2; +} + +struct fpn * +fpu_tentox(fe) + struct fpemu *fe; +{ + /* stub */ + return &fe->fe_f2; +} + +struct fpn * +fpu_twotox(fe) + struct fpemu *fe; +{ + /* stub */ + return &fe->fe_f2; +} diff --git a/sys/arch/m68k/fpe/fpu_explode.c b/sys/arch/m68k/fpe/fpu_explode.c new file mode 100644 index 00000000000..c5c8bd2c9d0 --- /dev/null +++ b/sys/arch/m68k/fpe/fpu_explode.c @@ -0,0 +1,289 @@ +/* $NetBSD: fpu_explode.c,v 1.1 1995/11/03 04:47:07 briggs 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_explode.c 8.1 (Berkeley) 6/11/93 + */ + +/* + * FPU subroutines: `explode' the machine's `packed binary' format numbers + * into our internal format. + */ + +#include <sys/types.h> + +#include "ieee.h" +#include <machine/reg.h> + +#include "fpu_arith.h" +#include "fpu_emulate.h" + + +/* Conversion to internal format -- note asymmetry. */ +static int fpu_itof __P((struct fpn *fp, u_int i)); +static int fpu_stof __P((struct fpn *fp, u_int i)); +static int fpu_dtof __P((struct fpn *fp, u_int i, u_int j)); +static int fpu_xtof __P((struct fpn *fp, u_int i, u_int j, u_int k)); + +/* + * N.B.: in all of the following, we assume the FP format is + * + * --------------------------- + * | s | exponent | fraction | + * --------------------------- + * + * (which represents -1**s * 1.fraction * 2**exponent), so that the + * sign bit is way at the top (bit 31), the exponent is next, and + * then the remaining bits mark the fraction. A zero exponent means + * zero or denormalized (0.fraction rather than 1.fraction), and the + * maximum possible exponent, 2bias+1, signals inf (fraction==0) or NaN. + * + * Since the sign bit is always the topmost bit---this holds even for + * integers---we set that outside all the *tof functions. Each function + * returns the class code for the new number (but note that we use + * FPC_QNAN for all NaNs; fpu_explode will fix this if appropriate). + */ + +/* + * int -> fpn. + */ +static int +fpu_itof(fp, i) + register struct fpn *fp; + register u_int i; +{ + + if (i == 0) + return (FPC_ZERO); + /* + * The value FP_1 represents 2^FP_LG, so set the exponent + * there and let normalization fix it up. Convert negative + * numbers to sign-and-magnitude. Note that this relies on + * fpu_norm()'s handling of `supernormals'; see fpu_subr.c. + */ + fp->fp_exp = FP_LG; + fp->fp_mant[0] = (int)i < 0 ? -i : i; + fp->fp_mant[1] = 0; + fp->fp_mant[2] = 0; + fp->fp_mant[3] = 0; + fpu_norm(fp); + return (FPC_NUM); +} + +#define mask(nbits) ((1 << (nbits)) - 1) + +/* + * All external floating formats convert to internal in the same manner, + * as defined here. Note that only normals get an implied 1.0 inserted. + */ +#define FP_TOF(exp, expbias, allfrac, f0, f1, f2, f3) \ + if (exp == 0) { \ + if (allfrac == 0) \ + return (FPC_ZERO); \ + fp->fp_exp = 1 - expbias; \ + fp->fp_mant[0] = f0; \ + fp->fp_mant[1] = f1; \ + fp->fp_mant[2] = f2; \ + fp->fp_mant[3] = f3; \ + fpu_norm(fp); \ + return (FPC_NUM); \ + } \ + if (exp == (2 * expbias + 1)) { \ + if (allfrac == 0) \ + return (FPC_INF); \ + fp->fp_mant[0] = f0; \ + fp->fp_mant[1] = f1; \ + fp->fp_mant[2] = f2; \ + fp->fp_mant[3] = f3; \ + return (FPC_QNAN); \ + } \ + fp->fp_exp = exp - expbias; \ + fp->fp_mant[0] = FP_1 | f0; \ + fp->fp_mant[1] = f1; \ + fp->fp_mant[2] = f2; \ + fp->fp_mant[3] = f3; \ + return (FPC_NUM) + +/* + * 32-bit single precision -> fpn. + * We assume a single occupies at most (64-FP_LG) bits in the internal + * format: i.e., needs at most fp_mant[0] and fp_mant[1]. + */ +static int +fpu_stof(fp, i) + register struct fpn *fp; + register u_int i; +{ + register int exp; + register u_int frac, f0, f1; +#define SNG_SHIFT (SNG_FRACBITS - FP_LG) + + exp = (i >> (32 - 1 - SNG_EXPBITS)) & mask(SNG_EXPBITS); + frac = i & mask(SNG_FRACBITS); + f0 = frac >> SNG_SHIFT; + f1 = frac << (32 - SNG_SHIFT); + FP_TOF(exp, SNG_EXP_BIAS, frac, f0, f1, 0, 0); +} + +/* + * 64-bit double -> fpn. + * We assume this uses at most (96-FP_LG) bits. + */ +static int +fpu_dtof(fp, i, j) + register struct fpn *fp; + register u_int i, j; +{ + register int exp; + register u_int frac, f0, f1, f2; +#define DBL_SHIFT (DBL_FRACBITS - 32 - FP_LG) + + exp = (i >> (32 - 1 - DBL_EXPBITS)) & mask(DBL_EXPBITS); + frac = i & mask(DBL_FRACBITS - 32); + f0 = frac >> DBL_SHIFT; + f1 = (frac << (32 - DBL_SHIFT)) | (j >> DBL_SHIFT); + f2 = j << (32 - DBL_SHIFT); + frac |= j; + FP_TOF(exp, DBL_EXP_BIAS, frac, f0, f1, f2, 0); +} + +/* + * 96-bit extended -> fpn. + */ +static int +fpu_xtof(fp, i, j, k) + register struct fpn *fp; + register u_int i, j, k; +{ + register int exp; + register u_int frac, f0, f1, f2; +#define EXT_SHIFT (EXT_FRACBITS - 1 - 32 - FP_LG) + + exp = (i >> (32 - 1 - EXT_EXPBITS)) & mask(EXT_EXPBITS); + f0 = j >> EXT_SHIFT; + f1 = (j << (32 - EXT_SHIFT)) | (k >> EXT_SHIFT); + f2 = k << (32 - EXT_SHIFT); + frac = j | k; + + /* m68k extended does not imply denormal by exp==0 */ + if (exp == 0) { + if (frac == 0) + return (FPC_ZERO); + fp->fp_exp = - EXT_EXP_BIAS; + fp->fp_mant[0] = f0; + fp->fp_mant[1] = f1; + fp->fp_mant[2] = f2; + fp->fp_mant[3] = 0; + fpu_norm(fp); + return (FPC_NUM); + } + if (exp == (2 * EXT_EXP_BIAS + 1)) { + if (frac == 0) + return (FPC_INF); + fp->fp_mant[0] = f0; + fp->fp_mant[1] = f1; + fp->fp_mant[2] = f2; + fp->fp_mant[3] = 0; + return (FPC_QNAN); + } + fp->fp_exp = exp - EXT_EXP_BIAS; + fp->fp_mant[0] = FP_1 | f0; + fp->fp_mant[1] = f1; + fp->fp_mant[2] = f2; + fp->fp_mant[3] = 0; + return (FPC_NUM); +} + +/* + * Explode the contents of a memory operand. + */ +void +fpu_explode(fe, fp, type, space) + register struct fpemu *fe; + register struct fpn *fp; + int type; + register u_int *space; +{ + register u_int s; + + s = space[0]; + fp->fp_sign = s >> 31; + fp->fp_sticky = 0; + switch (type) { + + case FTYPE_BYT: + s >>= 8; + case FTYPE_WRD: + s >>= 16; + case FTYPE_LNG: + s = fpu_itof(fp, s); + break; + + case FTYPE_SNG: + s = fpu_stof(fp, s); + break; + + case FTYPE_DBL: + s = fpu_dtof(fp, s, space[1]); + break; + + case FTYPE_EXT: + s = fpu_xtof(fp, s, space[1], space[2]); + break; + + default: + panic("fpu_explode"); + } + if (s == FPC_QNAN && (fp->fp_mant[0] & FP_QUIETBIT) == 0) { + /* + * Input is a signalling NaN. All operations that return + * an input NaN operand put it through a ``NaN conversion'', + * which basically just means ``turn on the quiet bit''. + * We do this here so that all NaNs internally look quiet + * (we can tell signalling ones by their class). + */ + fp->fp_mant[0] |= FP_QUIETBIT; + fe->fe_fpsr |= FPSR_SNAN; /* assert SNAN exception */ + s = FPC_SNAN; + } + fp->fp_class = s; +} diff --git a/sys/arch/m68k/fpe/fpu_fmovecr.c b/sys/arch/m68k/fpe/fpu_fmovecr.c new file mode 100644 index 00000000000..7c244b736ed --- /dev/null +++ b/sys/arch/m68k/fpe/fpu_fmovecr.c @@ -0,0 +1,117 @@ +/* $NetBSD: fpu_fmovecr.c,v 1.2 1995/11/05 00:35:23 briggs Exp $ */ + +/* + * Copyright (c) 1995 Ken Nakata + * 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. Neither the name of the author 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 AUTHOR 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 AUTHOR 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_fmovecr.c 10/8/95 + */ + +#include <sys/types.h> +#include <machine/frame.h> + +#include <stddef.h> + +#include "fpu_emulate.h" + +static struct fpn constrom[] = { + /* fp_class, fp_sign, fp_exp, fp_sticky, fp_mant[0] ... [3] */ + { FPC_NUM, 0, 1, 0, 0x6487e, 0xd5110b46, 0x11a80000, 0x0 }, + { FPC_NUM, 0, -2, 0, 0x4d104, 0xd427de7f, 0xbcc00000, 0x0 }, + { FPC_NUM, 0, 1, 0, 0x56fc2, 0xa2c515da, 0x54d00000, 0x0 }, + { FPC_NUM, 0, 0, 0, 0x5c551, 0xd94ae0bf, 0x85e00000, 0x0 }, + { FPC_NUM, 0, -2, 0, 0x6f2de, 0xc549b943, 0x8ca80000, 0x0 }, + { FPC_ZERO, 0, 0, 0, 0x0, 0x0, 0x0, 0x0 }, + { FPC_NUM, 0, -1, 0, 0x58b90, 0xbfbe8e7b, 0xcd600000, 0x0 }, + { FPC_NUM, 0, 1, 0, 0x49aec, 0x6eed5545, 0x60b80000, 0x0 }, + { FPC_NUM, 0, 0, 0, 0x40000, 0x0, 0x0, 0x0 }, + { FPC_NUM, 0, 3, 0, 0x50000, 0x0, 0x0, 0x0 }, + { FPC_NUM, 0, 6, 0, 0x64000, 0x0, 0x0, 0x0 }, + { FPC_NUM, 0, 13, 0, 0x4e200, 0x0, 0x0, 0x0 }, + { FPC_NUM, 0, 26, 0, 0x5f5e1, 0x0, 0x0, 0x0 }, + { FPC_NUM, 0, 53, 0, 0x470de, 0x4df82000, 0x0, 0x0 }, + { FPC_NUM, 0, 106, 0, 0x4ee2d, 0x6d415b85, 0xacf00000, 0x0 }, + { FPC_NUM, 0, 212, 0, 0x613c0, 0xfa4ffe7d, 0x36a80000, 0x0 }, + { FPC_NUM, 0, 425, 0, 0x49dd2, 0x3e4c074c, 0x67000000, 0x0 }, + { FPC_NUM, 0, 850, 0, 0x553f7, 0x5fdcefce, 0xf4700000, 0x0 }, + { FPC_NUM, 0, 1700, 0, 0x718cd, 0x5753074, 0x8e380000, 0x0 }, + { FPC_NUM, 0, 3401, 0, 0x64bb3, 0xac340ba8, 0x60b80000, 0x0 }, + { FPC_NUM, 0, 6803, 0, 0x4f459, 0xdaee29ea, 0xef280000, 0x0 }, + { FPC_NUM, 0, 13606, 0, 0x62302, 0x90145104, 0xbcd80000, 0x0 }, +}; + +struct fpn * +fpu_const(fp, offset) + struct fpn *fp; + u_int offset; +{ + struct fpn *r; + int i; + +#ifdef DEBUG + if (fp == NULL) { + panic("fpu_const: NULL pointer passed\n"); + } +#endif + if (offset == 0) { + r = &constrom[0]; + } else if (0xb <= offset && offset <= 0xe) { + r = &constrom[offset - 0xb + 1]; + } else if (0x30 <= offset && offset <= 0x3f) { + r = &constrom[offset - 0x30 + 6]; + } else { + /* return 0.0 for anything else (incl. valid offset 0xf) */ + r = &constrom[5]; + } + + CPYFPN(fp, r); + + return fp; +} + +int +fpu_emul_fmovecr(fe, insn) + struct fpemu *fe; + struct instruction *insn; +{ + int dstreg, offset, sig; + u_int *fpreg; + + dstreg = (insn->is_word1 >> 7) & 0x7; + offset = insn->is_word1 & 0x7F; + fpreg = &(fe->fe_fpframe->fpf_regs[0]); + + (void)fpu_const(&fe->fe_f3, offset); + (void)fpu_upd_fpsr(fe, &fe->fe_f3); + fpu_implode(fe, &fe->fe_f3, FTYPE_EXT, &fpreg[dstreg * 3]); + if (fpu_debug_level & DL_RESULT) { + printf(" fpu_emul_fmovecr: result %08x,%08x,%08x to FP%d\n", + fpreg[dstreg * 3], fpreg[dstreg * 3 + 1], fpreg[dstreg * 3 + 2], + dstreg); + } + return 0; +} diff --git a/sys/arch/m68k/fpe/fpu_fscale.c b/sys/arch/m68k/fpe/fpu_fscale.c new file mode 100644 index 00000000000..ad98d165345 --- /dev/null +++ b/sys/arch/m68k/fpe/fpu_fscale.c @@ -0,0 +1,329 @@ +/* $NetBSD: fpu_fscale.c,v 1.2 1995/11/05 00:35:25 briggs Exp $ */ + +/* + * Copyright (c) 1995 Ken Nakata + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * 4. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Gordon Ross + * + * 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. + */ + +/* + * FSCALE - separated from the other type0 arithmetic instructions + * for performance reason; maybe unnecessary, but FSCALE assumes + * the source operand be an integer. It performs type conversion + * only if the source operand is *not* an integer. + */ + +#include <sys/types.h> +#include <sys/signal.h> +#include <machine/frame.h> + +#include "fpu_emulate.h" + +int +fpu_emul_fscale(fe, insn) + struct fpemu *fe; + struct instruction *insn; +{ + struct frame *frame; + u_int *fpregs; + int word1, sig; + int regnum, format; + int scale, sign, exp; + u_int m0, m1; + u_int buf[3], fpsr; + int flags; + char regname; + + sig = 0; + frame = fe->fe_frame; + fpregs = &(fe->fe_fpframe->fpf_regs[0]); + /* clear all exceptions and conditions */ + fpsr = fe->fe_fpsr & ~FPSR_EXCP & ~FPSR_CCB; + if (fpu_debug_level & DL_FSCALE) { + printf(" fpu_emul_fscale: FPSR = %08x, FPCR = %08x\n", fpsr, fe->fe_fpcr); + } + + word1 = insn->is_word1; + format = (word1 >> 10) & 7; + regnum = (word1 >> 7) & 7; + + fe->fe_fpcr &= FPCR_ROUND; + fe->fe_fpcr |= FPCR_ZERO; + + /* get the source operand */ + if ((word1 & 0x4000) == 0) { + if (fpu_debug_level & DL_FSCALE) { + printf(" fpu_emul_fscale: FP%d op FP%d => FP%d\n", + format, regnum, regnum); + } + /* the operand is an FP reg */ + if (fpu_debug_level & DL_FSCALE) { + printf(" fpu_emul_scale: src opr FP%d=%08x%08x%08x\n", + format, fpregs[format*3], fpregs[format*3+1], + fpregs[format*3+2]); + } + fpu_explode(fe, &fe->fe_f2, FTYPE_EXT, &fpregs[format * 3]); + fpu_implode(fe, &fe->fe_f2, FTYPE_LNG, buf); + } else { + /* the operand is in memory */ + if (format == FTYPE_DBL) { + insn->is_datasize = 8; + } else if (format == FTYPE_SNG || format == FTYPE_LNG) { + insn->is_datasize = 4; + } else if (format == FTYPE_WRD) { + insn->is_datasize = 2; + } else if (format == FTYPE_BYT) { + insn->is_datasize = 1; + } else if (format == FTYPE_EXT) { + insn->is_datasize = 12; + } else { + /* invalid or unsupported operand format */ + sig = SIGFPE; + return sig; + } + + /* Get effective address. (modreg=opcode&077) */ + sig = fpu_decode_ea(frame, insn, &insn->is_ea0, insn->is_opcode); + if (sig) { + if (fpu_debug_level & DL_FSCALE) { + printf(" fpu_emul_fscale: error in decode_ea\n"); + } + return sig; + } + + if (fpu_debug_level & DL_FSCALE) { + printf(" fpu_emul_fscale: addr mode = "); + flags = insn->is_ea0.ea_flags; + regname = (insn->is_ea0.ea_regnum & 8) ? 'a' : 'd'; + + if (flags & EA_DIRECT) { + printf("%c%d\n", regname, insn->is_ea0.ea_regnum & 7); + } else if (insn->is_ea0.ea_flags & EA_PREDECR) { + printf("%c%d@-\n", regname, insn->is_ea0.ea_regnum & 7); + } else if (insn->is_ea0.ea_flags & EA_POSTINCR) { + printf("%c%d@+\n", regname, insn->is_ea0.ea_regnum & 7); + } else if (insn->is_ea0.ea_flags & EA_OFFSET) { + printf("%c%d@(%d)\n", regname, insn->is_ea0.ea_regnum & 7, + insn->is_ea0.ea_offset); + } else if (insn->is_ea0.ea_flags & EA_INDEXED) { + printf("%c%d@(...)\n", regname, insn->is_ea0.ea_regnum & 7); + } else if (insn->is_ea0.ea_flags & EA_ABS) { + printf("0x%08x\n", insn->is_ea0.ea_absaddr); + } else if (insn->is_ea0.ea_flags & EA_PC_REL) { + printf("pc@(%d)\n", insn->is_ea0.ea_offset); + } else if (flags & EA_IMMED) { + printf("#0x%08x%08x%08x\n", + insn->is_ea0.ea_immed[0], insn->is_ea0.ea_immed[1], + insn->is_ea0.ea_immed[2]); + } else { + printf("%c%d@\n", regname, insn->is_ea0.ea_regnum & 7); + } + } + fpu_load_ea(frame, insn, &insn->is_ea0, (char*)buf); + + if (fpu_debug_level & DL_FSCALE) { + printf(" fpu_emul_fscale: src = %08x%08x%08x, siz = %d\n", + buf[0], buf[1], buf[2], insn->is_datasize); + } + if (format == FTYPE_LNG) { + /* nothing */ + } else if (format == FTYPE_WRD) { + /* sign-extend */ + scale = buf[0] & 0xffff; + if (scale & 0x8000) { + scale |= 0xffff0000; + } + } else if (format == FTYPE_BYT) { + /* sign-extend */ + scale = buf[0] & 0xff; + if (scale & 0x80) { + scale |= 0xffffff00; + } + } else if (format == FTYPE_DBL || format == FTYPE_SNG || + format == FTYPE_EXT) { + fpu_explode(fe, &fe->fe_f2, format, buf); + fpu_implode(fe, &fe->fe_f2, FTYPE_LNG, buf); + } + /* make it look like we've got an FP oprand */ + fe->fe_f2.fp_class = (buf[0] == 0) ? FPC_ZERO : FPC_NUM; + } + + /* assume there's no exception */ + sig = 0; + + /* it's barbaric but we're going to operate directly on + * the dst operand's bit pattern */ + sign = fpregs[regnum * 3] & 0x80000000; + exp = (fpregs[regnum * 3] & 0x7fff0000) >> 16; + m0 = fpregs[regnum * 3 + 1]; + m1 = fpregs[regnum * 3 + 2]; + + switch (fe->fe_f2.fp_class) { + case FPC_SNAN: + fpsr |= FPSR_SNAN; + case FPC_QNAN: + /* dst = NaN */ + exp = 0x7fff; + m0 = m1 = 0xffffffff; + break; + case FPC_ZERO: + case FPC_NUM: + if ((0 < exp && exp < 0x7fff) || + (exp == 0 && (m0 | m1) != 0)) { + /* normal or denormal */ + exp += scale; + if (exp < 0) { + /* underflow */ + u_int grs; /* guard, round and sticky */ + + exp = 0; + grs = m1 << (32 + exp); + m1 = m0 << (32 + exp) | m1 >> -exp; + m0 >>= -exp; + if (grs != 0) { + fpsr |= FPSR_INEX2; + + switch (fe->fe_fpcr & 0x30) { + case FPCR_MINF: + if (sign != 0) { + if (++m1 == 0 && + ++m0 == 0) { + m0 = 0x80000000; + exp++; + } + } + break; + case FPCR_NEAR: + if (grs == 0x80000000) { + /* tie */ + if ((m1 & 1) && + ++m1 == 0 && + ++m0 == 0) { + m0 = 0x80000000; + exp++; + } + } else if (grs & 0x80000000) { + if (++m1 == 0 && + ++m0 == 0) { + m0 = 0x80000000; + exp++; + } + } + break; + case FPCR_PINF: + if (sign == 0) { + if (++m1 == 0 && + ++m0 == 0) { + m0 = 0x80000000; + exp++; + } + } + break; + case FPCR_ZERO: + break; + } + } + if (exp == 0 && (m0 & 0x80000000) == 0) { + fpsr |= FPSR_UNFL; + if ((m0 | m1) == 0) { + fpsr |= FPSR_ZERO; + } + } + } else if (exp >= 0x7fff) { + /* overflow --> result = Inf */ + /* but first, try to normalize in case it's an unnormalized */ + while ((m0 & 0x80000000) == 0) { + exp--; + m0 = (m0 << 1) | (m1 >> 31); + m1 = m1 << 1; + } + /* if it's still too large, then return Inf */ + if (exp >= 0x7fff) { + exp = 0x7fff; + m0 = m1 = 0; + fpsr |= FPSR_OVFL | FPSR_INF; + } + } else if ((m0 & 0x80000000) == 0) { + /* + * it's a denormal; we try to normalize but + * result may and may not be a normal. + */ + while (exp > 0 && (m0 & 0x80000000) == 0) { + exp--; + m0 = (m0 << 1) | (m1 >> 31); + m1 = m1 << 1; + } + if ((m0 & 0x80000000) == 0) { + fpsr |= FPSR_UNFL; + } + } /* exp in range and mantissa normalized */ + } else if (exp == 0 && m0 == 0 && m1 == 0) { + /* dst is Zero */ + fpsr |= FPSR_ZERO; + } /* else we know exp == 0x7fff */ + else if ((m0 | m1) == 0) { + fpsr |= FPSR_INF; + } else if ((m0 & 0x40000000) == 0) { + /* a signaling NaN */ + fpsr |= FPSR_NAN | FPSR_SNAN; + } else { + /* a quiet NaN */ + fpsr |= FPSR_NAN; + } + break; + case FPC_INF: + /* dst = NaN */ + exp = 0x7fff; + m0 = m1 = 0xffffffff; + fpsr |= FPSR_OPERR | FPSR_NAN; + break; + default: +#ifdef DEBUG + panic(" fpu_emul_fscale: invalid fp class"); +#endif + break; + } + + /* store the result */ + fpregs[regnum * 3] = sign | (exp << 16); + fpregs[regnum * 3 + 1] = m0; + fpregs[regnum * 3 + 2] = m1; + + if (sign) { + fpsr |= FPSR_NEG; + } + + /* update fpsr according to the result of operation */ + fe->fe_fpframe->fpf_fpsr = fe->fe_fpsr = fpsr; + + if (fpu_debug_level & DL_FSCALE) { + printf(" fpu_emul_fscale: FPSR = %08x, FPCR = %08x\n", + fe->fe_fpsr, fe->fe_fpcr); + } + + return (fpsr & fe->fe_fpcr & FPSR_EXCP) ? SIGFPE : sig; +} diff --git a/sys/arch/m68k/fpe/fpu_fstore.c b/sys/arch/m68k/fpe/fpu_fstore.c new file mode 100644 index 00000000000..34de45f10fc --- /dev/null +++ b/sys/arch/m68k/fpe/fpu_fstore.c @@ -0,0 +1,127 @@ +/* $NetBSD: fpu_fstore.c,v 1.2 1995/11/05 00:35:29 briggs Exp $ */ + +/* + * Copyright (c) 1995 Ken Nakata + * 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. + */ + +#include <sys/types.h> +#include <sys/signal.h> +#include <machine/frame.h> + +#include "fpu_emulate.h" + +/* + * type 0: fmove mem/fpr->fpr + * In this function, we know + * (opcode & 0x01c0) == 0 + * (word1 & 0xe000) == 0x6000 + */ +int +fpu_emul_fstore(fe, insn) + struct fpemu *fe; + struct instruction *insn; +{ + struct frame *frame = fe->fe_frame; + u_int *fpregs = fe->fe_fpframe->fpf_regs; + int word1, sig; + int regnum; + int format; + u_int buf[3]; + u_int flags; + char regname; + + if (fpu_debug_level & DL_FSTORE) { + printf(" fpu_emul_fstore: frame at %08x fpframe at %08x\n", + frame, fe->fe_fpframe); + } + + word1 = insn->is_word1; + format = (word1 >> 10) & 7; + regnum = (word1 >> 7) & 7; + + insn->is_advance = 4; + + if (format == FTYPE_DBL) { + insn->is_datasize = 8; + } else if (format == FTYPE_SNG || format == FTYPE_LNG) { + insn->is_datasize = 4; + } else if (format == FTYPE_WRD) { + insn->is_datasize = 2; + format = FTYPE_LNG; + } else if (format == FTYPE_BYT) { + insn->is_datasize = 1; + format = FTYPE_LNG; + } else if (format == FTYPE_EXT) { + insn->is_datasize = 12; + } else { + /* invalid or unsupported operand format */ + if (fpu_debug_level & DL_FSTORE) { + printf(" fpu_emul_fstore: invalid format %d\n", format); + } + sig = SIGFPE; + } + if (fpu_debug_level & DL_FSTORE) { + printf(" fpu_emul_fstore: format %d, size %d\n", + format, insn->is_datasize); + } + + /* Get effective address. (modreg=opcode&077) */ + sig = fpu_decode_ea(frame, insn, &insn->is_ea0, insn->is_opcode); + if (sig) { + if (fpu_debug_level & DL_FSTORE) { + printf(" fpu_emul_fstore: failed in decode_ea sig=%d\n", sig); + } + return sig; + } + + if (insn->is_datasize > 4 && insn->is_ea0.ea_flags == EA_DIRECT) { + /* trying to store dbl or ext into a data register */ +#ifdef DEBUG + printf(" fpu_fstore: attempted to store dbl/ext to reg\n"); +#endif + return SIGILL; + } + + if (fpu_debug_level & DL_OPERANDS) + printf(" fpu_emul_fstore: saving FP%d (%08x,%08x,%08x)\n", + regnum, fpregs[regnum * 3], fpregs[regnum * 3 + 1], + fpregs[regnum * 3 + 2]); + fpu_explode(fe, &fe->fe_f3, FTYPE_EXT, &fpregs[regnum * 3]); + if (fpu_debug_level & DL_VALUES) { + static char *class_name[] = { "SNAN", "QNAN", "ZERO", "NUM", "INF" }; + printf(" fpu_emul_fstore: fpn (%s,%c,%d,%08x,%08x,%08x,%08x)\n", + class_name[fe->fe_f3.fp_class + 2], + fe->fe_f3.fp_sign ? '-' : '+', fe->fe_f3.fp_exp, + fe->fe_f3.fp_mant[0], fe->fe_f3.fp_mant[1], + fe->fe_f3.fp_mant[2], fe->fe_f3.fp_mant[3]); + } + fpu_implode(fe, &fe->fe_f3, format, buf); + + fpu_store_ea(frame, insn, &insn->is_ea0, (char *)buf); + if (fpu_debug_level & DL_RESULT) + printf(" fpu_emul_fstore: %08x,%08x,%08x size %d\n", + buf[0], buf[1], buf[2], insn->is_datasize); + + return 0; +} diff --git a/sys/arch/m68k/fpe/fpu_getexp.c b/sys/arch/m68k/fpe/fpu_getexp.c new file mode 100644 index 00000000000..c2fe60531ab --- /dev/null +++ b/sys/arch/m68k/fpe/fpu_getexp.c @@ -0,0 +1,76 @@ +/* $NetBSD: fpu_getexp.c,v 1.1 1995/11/03 04:47:11 briggs Exp $ */ + +/* + * Copyright (c) 1995 Ken Nakata + * 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. Neither the name of the author 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 AUTHOR 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 AUTHOR 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_getexp.c 10/8/95 + */ + +#include <sys/types.h> + +#include "fpu_emulate.h" + +struct fpn * +fpu_getexp(fe) + struct fpemu *fe; +{ + struct fpn *fp = &fe->fe_f2; + + fe->fe_fpsr &= ~FPSR_EXCP; /* clear all exceptions */ + + if (fp->fp_class == FPC_INF) { + fp = fpu_newnan(fe); + fe->fe_fpsr |= FPSR_OPERR; + } else if (fp->fp_class == FPC_NUM) { /* a number */ + fpu_explode(fe, &fe->fe_f3, FTYPE_LNG, &fp->fp_exp); + fp = &fe->fe_f3; + } else if (fp->fp_class == FPC_SNAN) { /* signaling NaN */ + fe->fe_fpsr |= FPSR_SNAN; + } /* else if fp == zero or fp == quiet NaN, return itself */ + return fp; +} + +struct fpn * +fpu_getman(fe) + struct fpemu *fe; +{ + struct fpn *fp = &fe->fe_f2; + + fe->fe_fpsr &= ~FPSR_EXCP; /* clear all exceptions */ + + if (fp->fp_class == FPC_INF) { + fp = fpu_newnan(fe); + fe->fe_fpsr |= FPSR_OPERR; + } else if (fp->fp_class == FPC_NUM) { /* a number */ + fp->fp_exp = 0; + } else if (fp->fp_class == FPC_SNAN) { /* signaling NaN */ + fe->fe_fpsr |= FPSR_SNAN; + } /* else if fp == zero or fp == quiet NaN, return itself */ + return fp; +} + diff --git a/sys/arch/m68k/fpe/fpu_hyperb.c b/sys/arch/m68k/fpe/fpu_hyperb.c new file mode 100644 index 00000000000..2405eff1f66 --- /dev/null +++ b/sys/arch/m68k/fpe/fpu_hyperb.c @@ -0,0 +1,72 @@ +/* $NetBSD: fpu_hyperb.c,v 1.1 1995/11/03 04:47:11 briggs Exp $ */ + +/* + * Copyright (c) 1995 Ken Nakata + * 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. Neither the name of the author 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 AUTHOR 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 AUTHOR 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_hyperb.c 10/24/95 + */ + +#include "fpu_emulate.h" + +/* + * fpu_hyperb.c: defines the following functions + * + * fpu_atanh(), fpu_cosh(), fpu_sinh(), and fpu_tanh() + */ + +struct fpn * +fpu_atanh(fe) + struct fpemu *fe; +{ + /* stub */ + return &fe->fe_f2; +} + +struct fpn * +fpu_cosh(fe) + struct fpemu *fe; +{ + /* stub */ + return &fe->fe_f2; +} + +struct fpn * +fpu_sinh(fe) + struct fpemu *fe; +{ + /* stub */ + return &fe->fe_f2; +} + +struct fpn * +fpu_tanh(fe) + struct fpemu *fe; +{ + /* stub */ + return &fe->fe_f2; +} diff --git a/sys/arch/m68k/fpe/fpu_implode.c b/sys/arch/m68k/fpe/fpu_implode.c new file mode 100644 index 00000000000..2e47ca40494 --- /dev/null +++ b/sys/arch/m68k/fpe/fpu_implode.c @@ -0,0 +1,483 @@ +/* $NetBSD: fpu_implode.c,v 1.1 1995/11/03 04:47:12 briggs 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_implode.c 8.1 (Berkeley) 6/11/93 + */ + +/* + * FPU subroutines: `implode' internal format numbers into the machine's + * `packed binary' format. + */ + +#include <sys/types.h> + +#include "ieee.h" +#include <machine/reg.h> + +#include "fpu_emulate.h" +#include "fpu_arith.h" + +/* Conversion from internal format -- note asymmetry. */ +static u_int fpu_ftoi __P((struct fpemu *fe, struct fpn *fp)); +static u_int fpu_ftos __P((struct fpemu *fe, struct fpn *fp)); +static u_int fpu_ftod __P((struct fpemu *fe, struct fpn *fp, u_int *)); +static u_int fpu_ftox __P((struct fpemu *fe, struct fpn *fp, u_int *)); + +/* + * Round a number (algorithm from Motorola MC68882 manual, modified for + * our internal format). Set inexact exception if rounding is required. + * Return true iff we rounded up. + * + * After rounding, we discard the guard and round bits by shifting right + * 2 bits (a la fpu_shr(), but we do not bother with fp->fp_sticky). + * This saves effort later. + * + * Note that we may leave the value 2.0 in fp->fp_mant; it is the caller's + * responsibility to fix this if necessary. + */ +int +round(register struct fpemu *fe, register struct fpn *fp) +{ + register u_int m0, m1, m2, m3; + register int gr, s, ret; + + m0 = fp->fp_mant[0]; + m1 = fp->fp_mant[1]; + m2 = fp->fp_mant[2]; + m3 = fp->fp_mant[3]; + gr = m3 & 3; + s = fp->fp_sticky; + + /* mant >>= FP_NG */ + m3 = (m3 >> FP_NG) | (m2 << (32 - FP_NG)); + m2 = (m2 >> FP_NG) | (m1 << (32 - FP_NG)); + m1 = (m1 >> FP_NG) | (m0 << (32 - FP_NG)); + m0 >>= FP_NG; + + if ((gr | s) == 0) /* result is exact: no rounding needed */ + goto rounddown; + + fe->fe_fpsr |= FPSR_INEX2; /* inexact */ + + /* Go to rounddown to round down; break to round up. */ + switch (fe->fe_fpcr & FPCR_ROUND) { + + case FPCR_NEAR: + default: + /* + * Round only if guard is set (gr & 2). If guard is set, + * but round & sticky both clear, then we want to round + * but have a tie, so round to even, i.e., add 1 iff odd. + */ + if ((gr & 2) == 0) + goto rounddown; + if ((gr & 1) || fp->fp_sticky || (m3 & 1)) + break; + goto rounddown; + + case FPCR_ZERO: + /* Round towards zero, i.e., down. */ + goto rounddown; + + case FPCR_MINF: + /* Round towards -Inf: up if negative, down if positive. */ + if (fp->fp_sign) + break; + goto rounddown; + + case FPCR_PINF: + /* Round towards +Inf: up if positive, down otherwise. */ + if (!fp->fp_sign) + break; + goto rounddown; + } + + /* Bump low bit of mantissa, with carry. */ +#ifdef sparc /* ``cheating'' (left out FPU_DECL_CARRY; know this is faster) */ + FPU_ADDS(m3, m3, 1); + FPU_ADDCS(m2, m2, 0); + FPU_ADDCS(m1, m1, 0); + FPU_ADDC(m0, m0, 0); +#else + if (++m3 == 0 && ++m2 == 0 && ++m1 == 0) + m0++; +#endif + fp->fp_mant[0] = m0; + fp->fp_mant[1] = m1; + fp->fp_mant[2] = m2; + fp->fp_mant[3] = m3; + return (1); + +rounddown: + fp->fp_mant[0] = m0; + fp->fp_mant[1] = m1; + fp->fp_mant[2] = m2; + fp->fp_mant[3] = m3; + return (0); +} + +/* + * For overflow: return true if overflow is to go to +/-Inf, according + * to the sign of the overflowing result. If false, overflow is to go + * to the largest magnitude value instead. + */ +static int +toinf(struct fpemu *fe, int sign) +{ + int inf; + + /* look at rounding direction */ + switch (fe->fe_fpcr & FPCR_ROUND) { + + default: + case FPCR_NEAR: /* the nearest value is always Inf */ + inf = 1; + break; + + case FPCR_ZERO: /* toward 0 => never towards Inf */ + inf = 0; + break; + + case FPCR_PINF: /* toward +Inf iff positive */ + inf = (sign == 0); + break; + + case FPCR_MINF: /* toward -Inf iff negative */ + inf = sign; + break; + } + return (inf); +} + +/* + * fpn -> int (int value returned as return value). + * + * N.B.: this conversion always rounds towards zero (this is a peculiarity + * of the SPARC instruction set). + */ +static u_int +fpu_ftoi(fe, fp) + struct fpemu *fe; + register struct fpn *fp; +{ + register u_int i; + register int sign, exp; + + sign = fp->fp_sign; + switch (fp->fp_class) { + + case FPC_ZERO: + return (0); + + case FPC_NUM: + /* + * If exp >= 2^32, overflow. Otherwise shift value right + * into last mantissa word (this will not exceed 0xffffffff), + * shifting any guard and round bits out into the sticky + * bit. Then ``round'' towards zero, i.e., just set an + * inexact exception if sticky is set (see round()). + * If the result is > 0x80000000, or is positive and equals + * 0x80000000, overflow; otherwise the last fraction word + * is the result. + */ + if ((exp = fp->fp_exp) >= 32) + break; + /* NB: the following includes exp < 0 cases */ + if (fpu_shr(fp, FP_NMANT - 1 - FP_NG - exp) != 0) + /* m68881/2 do not underflow when + converting to integer */; + round(fe, fp); + i = fp->fp_mant[3]; + if (i >= ((u_int)0x80000000 + sign)) + break; + return (sign ? -i : i); + + default: /* Inf, qNaN, sNaN */ + break; + } + /* overflow: replace any inexact exception with invalid */ + fe->fe_fpsr = (fe->fe_fpsr & ~FPSR_INEX2) | FPSR_OPERR; + return (0x7fffffff + sign); +} + +/* + * fpn -> single (32 bit single returned as return value). + * We assume <= 29 bits in a single-precision fraction (1.f part). + */ +static u_int +fpu_ftos(fe, fp) + struct fpemu *fe; + register struct fpn *fp; +{ + register u_int sign = fp->fp_sign << 31; + register int exp; + +#define SNG_EXP(e) ((e) << SNG_FRACBITS) /* makes e an exponent */ +#define SNG_MASK (SNG_EXP(1) - 1) /* mask for fraction */ + + /* Take care of non-numbers first. */ + if (ISNAN(fp)) { + /* + * Preserve upper bits of NaN, per SPARC V8 appendix N. + * Note that fp->fp_mant[0] has the quiet bit set, + * even if it is classified as a signalling NaN. + */ + (void) fpu_shr(fp, FP_NMANT - 1 - SNG_FRACBITS); + exp = SNG_EXP_INFNAN; + goto done; + } + if (ISINF(fp)) + return (sign | SNG_EXP(SNG_EXP_INFNAN)); + if (ISZERO(fp)) + return (sign); + + /* + * Normals (including subnormals). Drop all the fraction bits + * (including the explicit ``implied'' 1 bit) down into the + * single-precision range. If the number is subnormal, move + * the ``implied'' 1 into the explicit range as well, and shift + * right to introduce leading zeroes. Rounding then acts + * differently for normals and subnormals: the largest subnormal + * may round to the smallest normal (1.0 x 2^minexp), or may + * remain subnormal. In the latter case, signal an underflow + * if the result was inexact or if underflow traps are enabled. + * + * Rounding a normal, on the other hand, always produces another + * normal (although either way the result might be too big for + * single precision, and cause an overflow). If rounding a + * normal produces 2.0 in the fraction, we need not adjust that + * fraction at all, since both 1.0 and 2.0 are zero under the + * fraction mask. + * + * Note that the guard and round bits vanish from the number after + * rounding. + */ + if ((exp = fp->fp_exp + SNG_EXP_BIAS) <= 0) { /* subnormal */ + /* -NG for g,r; -SNG_FRACBITS-exp for fraction */ + (void) fpu_shr(fp, FP_NMANT - FP_NG - SNG_FRACBITS - exp); + if (round(fe, fp) && fp->fp_mant[3] == SNG_EXP(1)) + return (sign | SNG_EXP(1) | 0); + if (fe->fe_fpsr & FPSR_INEX2) + /* mc68881/2 don't underflow when converting */; + return (sign | SNG_EXP(0) | fp->fp_mant[3]); + } + /* -FP_NG for g,r; -1 for implied 1; -SNG_FRACBITS for fraction */ + (void) fpu_shr(fp, FP_NMANT - FP_NG - 1 - SNG_FRACBITS); +#ifdef DIAGNOSTIC + if ((fp->fp_mant[3] & SNG_EXP(1 << FP_NG)) == 0) + panic("fpu_ftos"); +#endif + if (round(fe, fp) && fp->fp_mant[3] == SNG_EXP(2)) + exp++; + if (exp >= SNG_EXP_INFNAN) { + /* overflow to inf or to max single */ + fe->fe_fpsr |= FPSR_OPERR | FPSR_INEX2; + if (toinf(fe, sign)) + return (sign | SNG_EXP(SNG_EXP_INFNAN)); + return (sign | SNG_EXP(SNG_EXP_INFNAN - 1) | SNG_MASK); + } +done: + /* phew, made it */ + return (sign | SNG_EXP(exp) | (fp->fp_mant[3] & SNG_MASK)); +} + +/* + * fpn -> double (32 bit high-order result returned; 32-bit low order result + * left in res[1]). Assumes <= 61 bits in double precision fraction. + * + * This code mimics fpu_ftos; see it for comments. + */ +static u_int +fpu_ftod(fe, fp, res) + struct fpemu *fe; + register struct fpn *fp; + u_int *res; +{ + register u_int sign = fp->fp_sign << 31; + register int exp; + +#define DBL_EXP(e) ((e) << (DBL_FRACBITS & 31)) +#define DBL_MASK (DBL_EXP(1) - 1) + + if (ISNAN(fp)) { + (void) fpu_shr(fp, FP_NMANT - 1 - DBL_FRACBITS); + exp = DBL_EXP_INFNAN; + goto done; + } + if (ISINF(fp)) { + sign |= DBL_EXP(DBL_EXP_INFNAN); + res[1] = 0; + return (sign); + } + if (ISZERO(fp)) { + res[1] = 0; + return (sign); + } + + if ((exp = fp->fp_exp + DBL_EXP_BIAS) <= 0) { + (void) fpu_shr(fp, FP_NMANT - FP_NG - DBL_FRACBITS - exp); + if (round(fe, fp) && fp->fp_mant[2] == DBL_EXP(1)) { + res[1] = 0; + return (sign | DBL_EXP(1) | 0); + } + if (fe->fe_fpsr & FPSR_INEX2) + /* mc68881/2 don't underflow when converting */; + exp = 0; + goto done; + } + (void) fpu_shr(fp, FP_NMANT - FP_NG - 1 - DBL_FRACBITS); + if (round(fe, fp) && fp->fp_mant[2] == DBL_EXP(2)) + exp++; + if (exp >= DBL_EXP_INFNAN) { + fe->fe_fpsr |= FPSR_OPERR | FPSR_INEX2; + if (toinf(fe, sign)) { + res[1] = 0; + return (sign | DBL_EXP(DBL_EXP_INFNAN) | 0); + } + res[1] = ~0; + return (sign | DBL_EXP(DBL_EXP_INFNAN) | DBL_MASK); + } +done: + res[1] = fp->fp_mant[3]; + return (sign | DBL_EXP(exp) | (fp->fp_mant[2] & DBL_MASK)); +} + +/* + * fpn -> 68k extended (32 bit high-order result returned; two 32-bit low + * order result left in res[1] & res[2]). Assumes == 64 bits in extended + * precision fraction. + * + * This code mimics fpu_ftos; see it for comments. + */ +static u_int +fpu_ftox(fe, fp, res) + struct fpemu *fe; + register struct fpn *fp; + u_int *res; +{ + register u_int sign = fp->fp_sign << 31; + register int exp; + +#define EXT_EXP(e) ((e) << 16) +#define EXT_MASK (EXT_EXP(1) - 1) + + if (ISNAN(fp)) { + (void) fpu_shr(fp, FP_NMANT - 1 - EXT_FRACBITS); + exp = EXT_EXP_INFNAN; + goto done; + } + if (ISINF(fp)) { + sign |= EXT_EXP(EXT_EXP_INFNAN); + res[1] = res[2] = 0; + return (sign); + } + if (ISZERO(fp)) { + res[1] = res[2] = 0; + return (sign); + } + + if ((exp = fp->fp_exp + EXT_EXP_BIAS) <= 0) { + /* I'm not sure about this <=... exp==0 doesn't mean + it's a denormal in extended format */ + (void) fpu_shr(fp, FP_NMANT - FP_NG - EXT_FRACBITS - exp); + if (round(fe, fp) && fp->fp_mant[2] == EXT_EXP(1)) { + res[1] = res[2] = 0; + return (sign | EXT_EXP(1) | 0); + } + if (fe->fe_fpsr & FPSR_INEX2) + /* mc68881/2 don't underflow */; + exp = 0; + goto done; + } + (void) fpu_shr(fp, FP_NMANT - FP_NG - EXT_FRACBITS); + if (round(fe, fp) && fp->fp_mant[2] == EXT_EXP(2)) + exp++; + if (exp >= EXT_EXP_INFNAN) { + fe->fe_fpsr |= FPSR_OPERR | FPSR_INEX2; + if (toinf(fe, sign)) { + res[1] = res[2] = 0; + return (sign | EXT_EXP(EXT_EXP_INFNAN) | 0); + } + res[1] = res[2] = ~0; + return (sign | EXT_EXP(EXT_EXP_INFNAN) | EXT_MASK); + } +done: + res[1] = fp->fp_mant[2]; + res[2] = fp->fp_mant[3]; + return (sign | EXT_EXP(exp)); +} + +/* + * Implode an fpn, writing the result into the given space. + */ +void +fpu_implode(fe, fp, type, space) + struct fpemu *fe; + register struct fpn *fp; + int type; + register u_int *space; +{ + fe->fe_fpsr &= ~FPSR_EXCP; + + switch (type) { + case FTYPE_LNG: + space[0] = fpu_ftoi(fe, fp); + break; + + case FTYPE_SNG: + space[0] = fpu_ftos(fe, fp); + break; + + case FTYPE_DBL: + space[0] = fpu_ftod(fe, fp, space); + break; + + case FTYPE_EXT: + /* funky rounding precision options ?? */ + space[0] = fpu_ftox(fe, fp, space); + break; + + default: + panic("fpu_implode"); + } +} diff --git a/sys/arch/m68k/fpe/fpu_int.c b/sys/arch/m68k/fpe/fpu_int.c new file mode 100644 index 00000000000..23a9b09f17c --- /dev/null +++ b/sys/arch/m68k/fpe/fpu_int.c @@ -0,0 +1,122 @@ +/* $NetBSD: fpu_int.c,v 1.1 1995/11/03 04:47:14 briggs Exp $ */ + +/* + * Copyright (c) 1995 Ken Nakata + * 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 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 AUTHOR 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_int.c + */ + +#include <sys/types.h> + +#include <machine/reg.h> + +#include "fpu_arith.h" +#include "fpu_emulate.h" + +/* FINTRZ - always round to zero */ +struct fpn * +fpu_intrz(fe) + struct fpemu *fe; +{ + register struct fpn *x = &fe->fe_f2; + register int sh, clr, mask, i; + + /* special cases first */ + if (x->fp_class != FPC_NUM) { + return x; + } + /* when |x| < 1.0 */ + if (x->fp_exp < 0) { + x->fp_class = FPC_ZERO; + x->fp_mant[0] = x->fp_mant[1] = x->fp_mant[2] = x->fp_mant[3] = 0; + return x; + } + + /* real work */ + sh = FP_NMANT - 1 - x->fp_exp; + if (sh <= 0) { + return x; + } + + clr = 3 - sh / 32; + mask = (0xffffffff << (sh % 32)); + + for (i = 3; i > clr; i--) { + x->fp_mant[i] = 0; + } + x->fp_mant[i] &= mask; + + return x; +} + +/* FINT */ +struct fpn * +fpu_int(fe) + struct fpemu *fe; +{ + register struct fpn *x = &fe->fe_f2; + register int rsh, lsh, wsh, i; + + /* special cases first */ + if (x->fp_class != FPC_NUM) { + return x; + } + /* even if we have exponent == -1, we still have possiblity + that the result >= 1.0 when mantissa ~= 1.0 and rounded up */ + if (x->fp_exp < -1) { + x->fp_class = FPC_ZERO; + x->fp_mant[0] = x->fp_mant[1] = x->fp_mant[2] = x->fp_mant[3] = 0; + return x; + } + + /* real work */ + rsh = FP_NMANT - 1 - x->fp_exp; + if (rsh - FP_NG <= 0) { + return x; + } + + fpu_shr(x, rsh - FP_NG); /* shift to the right */ + + if (round(fe, x) == 1 /* rounded up */ && + x->fp_mant[3 - (FP_NMANT-rsh)/32] & (1 << ((FP_NMANT-rsh)%32)) + /* x >= 2.0 */) { + rsh--; /* reduce shift count by 1 */ + x->fp_exp++; /* adjust exponent */ + } + + /* shift it back to the left */ + wsh = rsh / 32; + lsh = rsh % 32; + rsh = 32 - lsh; + for (i = 0; i + wsh < 3; i++) { + x->fp_mant[i] = (x->fp_mant[i+wsh] << lsh) | (x->fp_mant[i+wsh+1] >> rsh); + } + x->fp_mant[i++] = (x->fp_mant[i+wsh] << lsh); + for (; i < 4; i++) { + x->fp_mant[i] = 0; + } + + return x; +} diff --git a/sys/arch/m68k/fpe/fpu_log.c b/sys/arch/m68k/fpe/fpu_log.c new file mode 100644 index 00000000000..19788c39e97 --- /dev/null +++ b/sys/arch/m68k/fpe/fpu_log.c @@ -0,0 +1,594 @@ +/* $NetBSD: fpu_log.c,v 1.2 1995/11/05 00:35:31 briggs Exp $ */ + +/* + * Copyright (c) 1995 Ken Nakata + * 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. Neither the name of the author 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 AUTHOR 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 AUTHOR 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_log.c 10/8/95 + */ + +#include <sys/types.h> + +#include "fpu_emulate.h" + +static u_int logA6[] = { 0x3FC2499A, 0xB5E4040B }; +static u_int logA5[] = { 0xBFC555B5, 0x848CB7DB }; +static u_int logA4[] = { 0x3FC99999, 0x987D8730 }; +static u_int logA3[] = { 0xBFCFFFFF, 0xFF6F7E97 }; +static u_int logA2[] = { 0x3FD55555, 0x555555A4 }; +static u_int logA1[] = { 0xBFE00000, 0x00000008 }; + +static u_int logB5[] = { 0x3F175496, 0xADD7DAD6 }; +static u_int logB4[] = { 0x3F3C71C2, 0xFE80C7E0 }; +static u_int logB3[] = { 0x3F624924, 0x928BCCFF }; +static u_int logB2[] = { 0x3F899999, 0x999995EC }; +static u_int logB1[] = { 0x3FB55555, 0x55555555 }; + +/* sfpn = shortened fp number; can represent only positive numbers */ +static struct sfpn { + int sp_exp; + u_int sp_m0, sp_m1; +} logtbl[] = { + { 0x3FFE - 0x3fff, 0xFE03F80FU, 0xE03F80FEU }, + { 0x3FF7 - 0x3fff, 0xFF015358U, 0x833C47E2U }, + { 0x3FFE - 0x3fff, 0xFA232CF2U, 0x52138AC0U }, + { 0x3FF9 - 0x3fff, 0xBDC8D83EU, 0xAD88D549U }, + { 0x3FFE - 0x3fff, 0xF6603D98U, 0x0F6603DAU }, + { 0x3FFA - 0x3fff, 0x9CF43DCFU, 0xF5EAFD48U }, + { 0x3FFE - 0x3fff, 0xF2B9D648U, 0x0F2B9D65U }, + { 0x3FFA - 0x3fff, 0xDA16EB88U, 0xCB8DF614U }, + { 0x3FFE - 0x3fff, 0xEF2EB71FU, 0xC4345238U }, + { 0x3FFB - 0x3fff, 0x8B29B775U, 0x1BD70743U }, + { 0x3FFE - 0x3fff, 0xEBBDB2A5U, 0xC1619C8CU }, + { 0x3FFB - 0x3fff, 0xA8D839F8U, 0x30C1FB49U }, + { 0x3FFE - 0x3fff, 0xE865AC7BU, 0x7603A197U }, + { 0x3FFB - 0x3fff, 0xC61A2EB1U, 0x8CD907ADU }, + { 0x3FFE - 0x3fff, 0xE525982AU, 0xF70C880EU }, + { 0x3FFB - 0x3fff, 0xE2F2A47AU, 0xDE3A18AFU }, + { 0x3FFE - 0x3fff, 0xE1FC780EU, 0x1FC780E2U }, + { 0x3FFB - 0x3fff, 0xFF64898EU, 0xDF55D551U }, + { 0x3FFE - 0x3fff, 0xDEE95C4CU, 0xA037BA57U }, + { 0x3FFC - 0x3fff, 0x8DB956A9U, 0x7B3D0148U }, + { 0x3FFE - 0x3fff, 0xDBEB61EEU, 0xD19C5958U }, + { 0x3FFC - 0x3fff, 0x9B8FE100U, 0xF47BA1DEU }, + { 0x3FFE - 0x3fff, 0xD901B203U, 0x6406C80EU }, + { 0x3FFC - 0x3fff, 0xA9372F1DU, 0x0DA1BD17U }, + { 0x3FFE - 0x3fff, 0xD62B80D6U, 0x2B80D62CU }, + { 0x3FFC - 0x3fff, 0xB6B07F38U, 0xCE90E46BU }, + { 0x3FFE - 0x3fff, 0xD3680D36U, 0x80D3680DU }, + { 0x3FFC - 0x3fff, 0xC3FD0329U, 0x06488481U }, + { 0x3FFE - 0x3fff, 0xD0B69FCBU, 0xD2580D0BU }, + { 0x3FFC - 0x3fff, 0xD11DE0FFU, 0x15AB18CAU }, + { 0x3FFE - 0x3fff, 0xCE168A77U, 0x25080CE1U }, + { 0x3FFC - 0x3fff, 0xDE1433A1U, 0x6C66B150U }, + { 0x3FFE - 0x3fff, 0xCB8727C0U, 0x65C393E0U }, + { 0x3FFC - 0x3fff, 0xEAE10B5AU, 0x7DDC8ADDU }, + { 0x3FFE - 0x3fff, 0xC907DA4EU, 0x871146ADU }, + { 0x3FFC - 0x3fff, 0xF7856E5EU, 0xE2C9B291U }, + { 0x3FFE - 0x3fff, 0xC6980C69U, 0x80C6980CU }, + { 0x3FFD - 0x3fff, 0x82012CA5U, 0xA68206D7U }, + { 0x3FFE - 0x3fff, 0xC4372F85U, 0x5D824CA6U }, + { 0x3FFD - 0x3fff, 0x882C5FCDU, 0x7256A8C5U }, + { 0x3FFE - 0x3fff, 0xC1E4BBD5U, 0x95F6E947U }, + { 0x3FFD - 0x3fff, 0x8E44C60BU, 0x4CCFD7DEU }, + { 0x3FFE - 0x3fff, 0xBFA02FE8U, 0x0BFA02FFU }, + { 0x3FFD - 0x3fff, 0x944AD09EU, 0xF4351AF6U }, + { 0x3FFE - 0x3fff, 0xBD691047U, 0x07661AA3U }, + { 0x3FFD - 0x3fff, 0x9A3EECD4U, 0xC3EAA6B2U }, + { 0x3FFE - 0x3fff, 0xBB3EE721U, 0xA54D880CU }, + { 0x3FFD - 0x3fff, 0xA0218434U, 0x353F1DE8U }, + { 0x3FFE - 0x3fff, 0xB92143FAU, 0x36F5E02EU }, + { 0x3FFD - 0x3fff, 0xA5F2FCABU, 0xBBC506DAU }, + { 0x3FFE - 0x3fff, 0xB70FBB5AU, 0x19BE3659U }, + { 0x3FFD - 0x3fff, 0xABB3B8BAU, 0x2AD362A5U }, + { 0x3FFE - 0x3fff, 0xB509E68AU, 0x9B94821FU }, + { 0x3FFD - 0x3fff, 0xB1641795U, 0xCE3CA97BU }, + { 0x3FFE - 0x3fff, 0xB30F6352U, 0x8917C80BU }, + { 0x3FFD - 0x3fff, 0xB7047551U, 0x5D0F1C61U }, + { 0x3FFE - 0x3fff, 0xB11FD3B8U, 0x0B11FD3CU }, + { 0x3FFD - 0x3fff, 0xBC952AFEU, 0xEA3D13E1U }, + { 0x3FFE - 0x3fff, 0xAF3ADDC6U, 0x80AF3ADEU }, + { 0x3FFD - 0x3fff, 0xC2168ED0U, 0xF458BA4AU }, + { 0x3FFE - 0x3fff, 0xAD602B58U, 0x0AD602B6U }, + { 0x3FFD - 0x3fff, 0xC788F439U, 0xB3163BF1U }, + { 0x3FFE - 0x3fff, 0xAB8F69E2U, 0x8359CD11U }, + { 0x3FFD - 0x3fff, 0xCCECAC08U, 0xBF04565DU }, + { 0x3FFE - 0x3fff, 0xA9C84A47U, 0xA07F5638U }, + { 0x3FFD - 0x3fff, 0xD2420487U, 0x2DD85160U }, + { 0x3FFE - 0x3fff, 0xA80A80A8U, 0x0A80A80BU }, + { 0x3FFD - 0x3fff, 0xD7894992U, 0x3BC3588AU }, + { 0x3FFE - 0x3fff, 0xA655C439U, 0x2D7B73A8U }, + { 0x3FFD - 0x3fff, 0xDCC2C4B4U, 0x9887DACCU }, + { 0x3FFE - 0x3fff, 0xA4A9CF1DU, 0x96833751U }, + { 0x3FFD - 0x3fff, 0xE1EEBD3EU, 0x6D6A6B9EU }, + { 0x3FFE - 0x3fff, 0xA3065E3FU, 0xAE7CD0E0U }, + { 0x3FFD - 0x3fff, 0xE70D785CU, 0x2F9F5BDCU }, + { 0x3FFE - 0x3fff, 0xA16B312EU, 0xA8FC377DU }, + { 0x3FFD - 0x3fff, 0xEC1F392CU, 0x5179F283U }, + { 0x3FFE - 0x3fff, 0x9FD809FDU, 0x809FD80AU }, + { 0x3FFD - 0x3fff, 0xF12440D3U, 0xE36130E6U }, + { 0x3FFE - 0x3fff, 0x9E4CAD23U, 0xDD5F3A20U }, + { 0x3FFD - 0x3fff, 0xF61CCE92U, 0x346600BBU }, + { 0x3FFE - 0x3fff, 0x9CC8E160U, 0xC3FB19B9U }, + { 0x3FFD - 0x3fff, 0xFB091FD3U, 0x8145630AU }, + { 0x3FFE - 0x3fff, 0x9B4C6F9EU, 0xF03A3CAAU }, + { 0x3FFD - 0x3fff, 0xFFE97042U, 0xBFA4C2ADU }, + { 0x3FFE - 0x3fff, 0x99D722DAU, 0xBDE58F06U }, + { 0x3FFE - 0x3fff, 0x825EFCEDU, 0x49369330U }, + { 0x3FFE - 0x3fff, 0x9868C809U, 0x868C8098U }, + { 0x3FFE - 0x3fff, 0x84C37A7AU, 0xB9A905C9U }, + { 0x3FFE - 0x3fff, 0x97012E02U, 0x5C04B809U }, + { 0x3FFE - 0x3fff, 0x87224C2EU, 0x8E645FB7U }, + { 0x3FFE - 0x3fff, 0x95A02568U, 0x095A0257U }, + { 0x3FFE - 0x3fff, 0x897B8CACU, 0x9F7DE298U }, + { 0x3FFE - 0x3fff, 0x94458094U, 0x45809446U }, + { 0x3FFE - 0x3fff, 0x8BCF55DEU, 0xC4CD05FEU }, + { 0x3FFE - 0x3fff, 0x92F11384U, 0x0497889CU }, + { 0x3FFE - 0x3fff, 0x8E1DC0FBU, 0x89E125E5U }, + { 0x3FFE - 0x3fff, 0x91A2B3C4U, 0xD5E6F809U }, + { 0x3FFE - 0x3fff, 0x9066E68CU, 0x955B6C9BU }, + { 0x3FFE - 0x3fff, 0x905A3863U, 0x3E06C43BU }, + { 0x3FFE - 0x3fff, 0x92AADE74U, 0xC7BE59E0U }, + { 0x3FFE - 0x3fff, 0x8F1779D9U, 0xFDC3A219U }, + { 0x3FFE - 0x3fff, 0x94E9BFF6U, 0x15845643U }, + { 0x3FFE - 0x3fff, 0x8DDA5202U, 0x37694809U }, + { 0x3FFE - 0x3fff, 0x9723A1B7U, 0x20134203U }, + { 0x3FFE - 0x3fff, 0x8CA29C04U, 0x6514E023U }, + { 0x3FFE - 0x3fff, 0x995899C8U, 0x90EB8990U }, + { 0x3FFE - 0x3fff, 0x8B70344AU, 0x139BC75AU }, + { 0x3FFE - 0x3fff, 0x9B88BDAAU, 0x3A3DAE2FU }, + { 0x3FFE - 0x3fff, 0x8A42F870U, 0x5669DB46U }, + { 0x3FFE - 0x3fff, 0x9DB4224FU, 0xFFE1157CU }, + { 0x3FFE - 0x3fff, 0x891AC73AU, 0xE9819B50U }, + { 0x3FFE - 0x3fff, 0x9FDADC26U, 0x8B7A12DAU }, + { 0x3FFE - 0x3fff, 0x87F78087U, 0xF78087F8U }, + { 0x3FFE - 0x3fff, 0xA1FCFF17U, 0xCE733BD4U }, + { 0x3FFE - 0x3fff, 0x86D90544U, 0x7A34ACC6U }, + { 0x3FFE - 0x3fff, 0xA41A9E8FU, 0x5446FB9FU }, + { 0x3FFE - 0x3fff, 0x85BF3761U, 0x2CEE3C9BU }, + { 0x3FFE - 0x3fff, 0xA633CD7EU, 0x6771CD8BU }, + { 0x3FFE - 0x3fff, 0x84A9F9C8U, 0x084A9F9DU }, + { 0x3FFE - 0x3fff, 0xA8489E60U, 0x0B435A5EU }, + { 0x3FFE - 0x3fff, 0x83993052U, 0x3FBE3368U }, + { 0x3FFE - 0x3fff, 0xAA59233CU, 0xCCA4BD49U }, + { 0x3FFE - 0x3fff, 0x828CBFBEU, 0xB9A020A3U }, + { 0x3FFE - 0x3fff, 0xAC656DAEU, 0x6BCC4985U }, + { 0x3FFE - 0x3fff, 0x81848DA8U, 0xFAF0D277U }, + { 0x3FFE - 0x3fff, 0xAE6D8EE3U, 0x60BB2468U }, + { 0x3FFE - 0x3fff, 0x80808080U, 0x80808081U }, + { 0x3FFE - 0x3fff, 0xB07197A2U, 0x3C46C654U }, +}; + +static struct fpn *__fpu_logn __P((struct fpemu *fe)); + +/* + * natural log - algorithm taken from Motorola FPSP, + * except this doesn't bother to check for invalid input. + */ +static struct fpn * +__fpu_logn(fe) + struct fpemu *fe; +{ + static struct fpn X, F, U, V, W, KLOG2; + struct fpn *d; + int i, k; + + CPYFPN(&X, &fe->fe_f2); + + /* see if |X-1| < 1/16 approx. */ + if ((-1 == X.fp_exp && (0xf07d0000U >> (31 - FP_LG)) <= X.fp_mant[0]) || + (0 == X.fp_exp && X.fp_mant[0] <= (0x88410000U >> (31 - FP_LG)))) { + /* log near 1 */ + if (fpu_debug_level & DL_ARITH) + printf("__fpu_logn: log near 1\n"); + + fpu_const(&fe->fe_f1, 0x32); + /* X+1 */ + d = fpu_add(fe); + CPYFPN(&V, d); + + CPYFPN(&fe->fe_f1, &X); + fpu_const(&fe->fe_f2, 0x32); /* 1.0 */ + fe->fe_f2.fp_sign = 1; /* -1.0 */ + /* X-1 */ + d = fpu_add(fe); + CPYFPN(&fe->fe_f1, d); + /* 2(X-1) */ + fe->fe_f1.fp_exp++; /* *= 2 */ + CPYFPN(&fe->fe_f2, &V); + /* U=2(X-1)/(X+1) */ + d = fpu_div(fe); + CPYFPN(&U, d); + CPYFPN(&fe->fe_f1, d); + CPYFPN(&fe->fe_f2, d); + /* V=U*U */ + d = fpu_mul(fe); + CPYFPN(&V, d); + CPYFPN(&fe->fe_f1, d); + CPYFPN(&fe->fe_f2, d); + /* W=V*V */ + d = fpu_mul(fe); + CPYFPN(&W, d); + + /* calculate U+U*V*([B1+W*(B3+W*B5)]+[V*(B2+W*B4)]) */ + + /* B1+W*(B3+W*B5) part */ + CPYFPN(&fe->fe_f1, d); + fpu_explode(fe, &fe->fe_f2, FTYPE_DBL, logB5); + /* W*B5 */ + d = fpu_mul(fe); + CPYFPN(&fe->fe_f1, d); + fpu_explode(fe, &fe->fe_f2, FTYPE_DBL, logB3); + /* B3+W*B5 */ + d = fpu_add(fe); + CPYFPN(&fe->fe_f1, d); + CPYFPN(&fe->fe_f2, &W); + /* W*(B3+W*B5) */ + d = fpu_mul(fe); + CPYFPN(&fe->fe_f1, d); + fpu_explode(fe, &fe->fe_f2, FTYPE_DBL, logB1); + /* B1+W*(B3+W*B5) */ + d = fpu_add(fe); + CPYFPN(&X, d); + + /* [V*(B2+W*B4)] part */ + CPYFPN(&fe->fe_f1, &W); + fpu_explode(fe, &fe->fe_f2, FTYPE_DBL, logB4); + /* W*B4 */ + d = fpu_mul(fe); + CPYFPN(&fe->fe_f1, d); + fpu_explode(fe, &fe->fe_f2, FTYPE_DBL, logB2); + /* B2+W*B4 */ + d = fpu_add(fe); + CPYFPN(&fe->fe_f1, d); + CPYFPN(&fe->fe_f2, &V); + /* V*(B2+W*B4) */ + d = fpu_mul(fe); + CPYFPN(&fe->fe_f1, d); + CPYFPN(&fe->fe_f2, &X); + /* B1+W*(B3+W*B5)+V*(B2+W*B4) */ + d = fpu_add(fe); + CPYFPN(&fe->fe_f1, d); + CPYFPN(&fe->fe_f2, &V); + /* V*(B1+W*(B3+W*B5)+V*(B2+W*B4)) */ + d = fpu_mul(fe); + CPYFPN(&fe->fe_f1, d); + CPYFPN(&fe->fe_f2, &U); + /* U*V*(B1+W*(B3+W*B5)+V*(B2+W*B4)) */ + d = fpu_mul(fe); + CPYFPN(&fe->fe_f1, d); + CPYFPN(&fe->fe_f2, &U); + /* U+U*V*(B1+W*(B3+W*B5)+V*(B2+W*B4)) */ + d = fpu_add(fe); + } else /* the usual case */ { + if (fpu_debug_level & DL_ARITH) + printf("__fpu_logn: the usual case. X=(%d,%08x,%08x...)\n", + X.fp_exp, X.fp_mant[0], X.fp_mant[1]); + + k = X.fp_exp; + /* X <- Y */ + X.fp_exp = fe->fe_f2.fp_exp = 0; + + /* get the most significant 7 bits of X */ + F.fp_class = FPC_NUM; + F.fp_sign = 0; + F.fp_exp = X.fp_exp; + F.fp_mant[0] = X.fp_mant[0] & (0xfe000000U >> (31 - FP_LG)); + F.fp_mant[0] |= (0x01000000U >> (31 - FP_LG)); + F.fp_mant[1] = F.fp_mant[2] = F.fp_mant[3] = 0; + F.fp_sticky = 0; + + if (fpu_debug_level & DL_ARITH) { + printf("__fpu_logn: X=Y*2^k=(%d,%08x,%08x...)*2^%d\n", + fe->fe_f2.fp_exp, fe->fe_f2.fp_mant[0], + fe->fe_f2.fp_mant[1], k); + printf("__fpu_logn: F=(%d,%08x,%08x...)\n", + F.fp_exp, F.fp_mant[0], F.fp_mant[1]); + } + + /* index to the table */ + i = (F.fp_mant[0] >> (FP_LG - 7)) & 0x7e; + + if (fpu_debug_level & DL_ARITH) + printf("__fpu_logn: index to logtbl i=%d(%x)\n", i, i); + + CPYFPN(&fe->fe_f1, &F); + /* -F */ + fe->fe_f1.fp_sign = 1; + /* Y-F */ + d = fpu_add(fe); + CPYFPN(&fe->fe_f1, d); + + /* fe_f2 = 1/F */ + fe->fe_f2.fp_class = FPC_NUM; + fe->fe_f2.fp_sign = fe->fe_f2.fp_sticky = fe->fe_f2.fp_mant[3] = 0; + fe->fe_f2.fp_exp = logtbl[i].sp_exp; + fe->fe_f2.fp_mant[0] = (logtbl[i].sp_m0 >> (31 - FP_LG)); + fe->fe_f2.fp_mant[1] = (logtbl[i].sp_m0 << (FP_LG + 1)) | + (logtbl[i].sp_m1 >> (31 - FP_LG)); + fe->fe_f2.fp_mant[2] = (u_int)(logtbl[i].sp_m1 << (FP_LG + 1)); + + if (fpu_debug_level & DL_ARITH) + printf("__fpu_logn: 1/F=(%d,%08x,%08x...)\n", fe->fe_f2.fp_exp, + fe->fe_f2.fp_mant[0], fe->fe_f2.fp_mant[1]); + + /* U = (Y-F) * (1/F) */ + d = fpu_mul(fe); + CPYFPN(&U, d); + + /* KLOG2 = K * ln(2) */ + /* fe_f1 == (fpn)k */ + fpu_explode(fe, &fe->fe_f1, FTYPE_LNG, &k); + (void)fpu_const(&fe->fe_f2, 0x30 /* ln(2) */); + if (fpu_debug_level & DL_ARITH) { + printf("__fpu_logn: fp(k)=(%d,%08x,%08x...)\n", fe->fe_f1.fp_exp, + fe->fe_f1.fp_mant[0], fe->fe_f1.fp_mant[1]); + printf("__fpu_logn: ln(2)=(%d,%08x,%08x...)\n", fe->fe_f2.fp_exp, + fe->fe_f2.fp_mant[0], fe->fe_f2.fp_mant[1]); + } + /* K * LOGOF2 */ + d = fpu_mul(fe); + CPYFPN(&KLOG2, d); + + /* V=U*U */ + CPYFPN(&fe->fe_f1, &U); + CPYFPN(&fe->fe_f2, &U); + d = fpu_mul(fe); + CPYFPN(&V, d); + + /* + * approximation of LOG(1+U) by + * (U+V*(A1+V*(A3+V*A5)))+(U*V*(A2+V*(A4+V*A6))) + */ + + /* (U+V*(A1+V*(A3+V*A5))) part */ + CPYFPN(&fe->fe_f1, d); + fpu_explode(fe, &fe->fe_f2, FTYPE_DBL, logA5); + /* V*A5 */ + d = fpu_mul(fe); + + CPYFPN(&fe->fe_f1, d); + fpu_explode(fe, &fe->fe_f2, FTYPE_DBL, logA3); + /* A3+V*A5 */ + d = fpu_add(fe); + + CPYFPN(&fe->fe_f1, d); + CPYFPN(&fe->fe_f2, &V); + /* V*(A3+V*A5) */ + d = fpu_mul(fe); + + CPYFPN(&fe->fe_f1, d); + fpu_explode(fe, &fe->fe_f2, FTYPE_DBL, logA1); + /* A1+V*(A3+V*A5) */ + d = fpu_add(fe); + + CPYFPN(&fe->fe_f1, d); + CPYFPN(&fe->fe_f2, &V); + /* V*(A1+V*(A3+V*A5)) */ + d = fpu_mul(fe); + + CPYFPN(&fe->fe_f1, d); + CPYFPN(&fe->fe_f2, &U); + /* U+V*(A1+V*(A3+V*A5)) */ + d = fpu_add(fe); + + CPYFPN(&X, d); + + /* (U*V*(A2+V*(A4+V*A6))) part */ + CPYFPN(&fe->fe_f1, &V); + fpu_explode(fe, &fe->fe_f2, FTYPE_DBL, logA6); + /* V*A6 */ + d = fpu_mul(fe); + CPYFPN(&fe->fe_f1, d); + fpu_explode(fe, &fe->fe_f2, FTYPE_DBL, logA4); + /* A4+V*A6 */ + d = fpu_add(fe); + CPYFPN(&fe->fe_f1, d); + CPYFPN(&fe->fe_f2, &V); + /* V*(A4+V*A6) */ + d = fpu_mul(fe); + CPYFPN(&fe->fe_f1, d); + fpu_explode(fe, &fe->fe_f2, FTYPE_DBL, logA2); + /* A2+V*(A4+V*A6) */ + d = fpu_add(fe); + CPYFPN(&fe->fe_f1, d); + CPYFPN(&fe->fe_f2, &V); + /* V*(A2+V*(A4+V*A6)) */ + d = fpu_mul(fe); + CPYFPN(&fe->fe_f1, d); + CPYFPN(&fe->fe_f2, &U); + /* U*V*(A2+V*(A4+V*A6)) */ + d = fpu_mul(fe); + CPYFPN(&fe->fe_f1, d); + i++; + /* fe_f2 = logtbl[i+1] (== LOG(F)) */ + fe->fe_f2.fp_class = FPC_NUM; + fe->fe_f2.fp_sign = fe->fe_f2.fp_sticky = fe->fe_f2.fp_mant[3] = 0; + fe->fe_f2.fp_exp = logtbl[i].sp_exp; + fe->fe_f2.fp_mant[0] = (logtbl[i].sp_m0 >> (31 - FP_LG)); + fe->fe_f2.fp_mant[1] = (logtbl[i].sp_m0 << (FP_LG + 1)) | + (logtbl[i].sp_m1 >> (31 - FP_LG)); + fe->fe_f2.fp_mant[2] = (logtbl[i].sp_m1 << (FP_LG + 1)); + + if (fpu_debug_level & DL_ARITH) + printf("__fpu_logn: ln(F)=(%d,%08x,%08x,...)\n", fe->fe_f2.fp_exp, + fe->fe_f2.fp_mant[0], fe->fe_f2.fp_mant[1]); + + /* LOG(F)+U*V*(A2+V*(A4+V*A6)) */ + d = fpu_add(fe); + CPYFPN(&fe->fe_f1, d); + CPYFPN(&fe->fe_f2, &X); + /* LOG(F)+U+V*(A1+V*(A3+V*A5))+U*V*(A2+V*(A4+V*A6)) */ + d = fpu_add(fe); + + if (fpu_debug_level & DL_ARITH) + printf("__fpu_logn: ln(Y)=(%c,%d,%08x,%08x,%08x,%08x)\n", + d->fp_sign ? '-' : '+', d->fp_exp, + d->fp_mant[0], d->fp_mant[1], d->fp_mant[2], d->fp_mant[3]); + + CPYFPN(&fe->fe_f1, d); + CPYFPN(&fe->fe_f2, &KLOG2); + /* K*LOGOF2+LOG(F)+U+V*(A1+V*(A3+V*A5))+U*V*(A2+V*(A4+V*A6)) */ + d = fpu_add(fe); + } + + return d; +} + +struct fpn * +fpu_log10(fe) + struct fpemu *fe; +{ + struct fpn *fp = &fe->fe_f2; + u_int fpsr; + + fpsr = fe->fe_fpsr & ~FPSR_EXCP; /* clear all exceptions */ + + if (fp->fp_class >= FPC_NUM) { + if (fp->fp_sign) { /* negative number or Inf */ + fp = fpu_newnan(fe); + fpsr |= FPSR_OPERR; + } else if (fp->fp_class == FPC_NUM) { + /* the real work here */ + fp = __fpu_logn(fe); + if (fp != &fe->fe_f1) + CPYFPN(&fe->fe_f1, fp); + (void)fpu_const(&fe->fe_f2, 0x31 /* ln(10) */); + fp = fpu_div(fe); + } /* else if fp == +Inf, return +Inf */ + } else if (fp->fp_class == FPC_ZERO) { + /* return -Inf */ + fp->fp_class = FPC_INF; + fp->fp_sign = 1; + fpsr |= FPSR_DZ; + } else if (fp->fp_class == FPC_SNAN) { + fpsr |= FPSR_SNAN; + fp = fpu_newnan(fe); + } else { + fp = fpu_newnan(fe); + } + + fe->fe_fpsr = fpsr; + + return fp; +} + +struct fpn * +fpu_log2(fe) + struct fpemu *fe; +{ + struct fpn *fp = &fe->fe_f2; + u_int fpsr; + + fpsr = fe->fe_fpsr & ~FPSR_EXCP; /* clear all exceptions */ + + if (fp->fp_class >= FPC_NUM) { + if (fp->fp_sign) { /* negative number or Inf */ + fp = fpu_newnan(fe); + fpsr |= FPSR_OPERR; + } else if (fp->fp_class == FPC_NUM) { + /* the real work here */ + if (fp->fp_mant[0] == FP_1 && fp->fp_mant[1] == 0 && + fp->fp_mant[2] == 0 && fp->fp_mant[3] == 0) { + /* fp == 2.0 ^ exp <--> log2(fp) == exp */ + fpu_explode(fe, &fe->fe_f3, FTYPE_LNG, &fp->fp_exp); + fp = &fe->fe_f3; + } else { + fp = __fpu_logn(fe); + if (fp != &fe->fe_f1) + CPYFPN(&fe->fe_f1, fp); + (void)fpu_const(&fe->fe_f2, 0x30 /* ln(2) */); + fp = fpu_div(fe); + } + } /* else if fp == +Inf, return +Inf */ + } else if (fp->fp_class == FPC_ZERO) { + /* return -Inf */ + fp->fp_class = FPC_INF; + fp->fp_sign = 1; + fpsr |= FPSR_DZ; + } else if (fp->fp_class == FPC_SNAN) { + fpsr |= FPSR_SNAN; + fp = fpu_newnan(fe); + } else { + fp = fpu_newnan(fe); + } + + fe->fe_fpsr = fpsr; + return fp; +} + +struct fpn * +fpu_logn(fe) + struct fpemu *fe; +{ + struct fpn *fp = &fe->fe_f2; + u_int fpsr; + + fpsr = fe->fe_fpsr & ~FPSR_EXCP; /* clear all exceptions */ + + if (fp->fp_class >= FPC_NUM) { + if (fp->fp_sign) { /* negative number or Inf */ + fp = fpu_newnan(fe); + fpsr |= FPSR_OPERR; + } else if (fp->fp_class == FPC_NUM) { + /* the real work here */ + fp = __fpu_logn(fe); + } /* else if fp == +Inf, return +Inf */ + } else if (fp->fp_class == FPC_ZERO) { + /* return -Inf */ + fp->fp_class = FPC_INF; + fp->fp_sign = 1; + fpsr |= FPSR_DZ; + } else if (fp->fp_class == FPC_SNAN) { + fpsr |= FPSR_SNAN; + fp = fpu_newnan(fe); + } else { + fp = fpu_newnan(fe); + } + + fe->fe_fpsr = fpsr; + + return fp; +} + +struct fpn * +fpu_lognp1(fe) + struct fpemu *fe; +{ + struct fpn *fp; + + /* build a 1.0 */ + fp = fpu_const(&fe->fe_f1, 0x32); /* get 1.0 */ + /* fp = 1.0 + f2 */ + fp = fpu_add(fe); + + /* copy the result to the src opr */ + if (&fe->fe_f2 != fp) + CPYFPN(&fe->fe_f2, fp); + + return fpu_logn(fe); +} diff --git a/sys/arch/m68k/fpe/fpu_mul.c b/sys/arch/m68k/fpe/fpu_mul.c new file mode 100644 index 00000000000..7f150478c3a --- /dev/null +++ b/sys/arch/m68k/fpe/fpu_mul.c @@ -0,0 +1,226 @@ +/* $NetBSD: fpu_mul.c,v 1.1 1995/11/03 04:47:16 briggs 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_mul.c 8.1 (Berkeley) 6/11/93 + */ + +/* + * Perform an FPU multiply (return x * y). + */ + +#include <sys/types.h> + +#include <machine/reg.h> + +#include "fpu_arith.h" +#include "fpu_emulate.h" + +/* + * The multiplication algorithm for normal numbers is as follows: + * + * The fraction of the product is built in the usual stepwise fashion. + * Each step consists of shifting the accumulator right one bit + * (maintaining any guard bits) and, if the next bit in y is set, + * adding the multiplicand (x) to the accumulator. Then, in any case, + * we advance one bit leftward in y. Algorithmically: + * + * A = 0; + * for (bit = 0; bit < FP_NMANT; bit++) { + * sticky |= A & 1, A >>= 1; + * if (Y & (1 << bit)) + * A += X; + * } + * + * (X and Y here represent the mantissas of x and y respectively.) + * The resultant accumulator (A) is the product's mantissa. It may + * be as large as 11.11111... in binary and hence may need to be + * shifted right, but at most one bit. + * + * Since we do not have efficient multiword arithmetic, we code the + * accumulator as four separate words, just like any other mantissa. + * We use local `register' variables in the hope that this is faster + * than memory. We keep x->fp_mant in locals for the same reason. + * + * In the algorithm above, the bits in y are inspected one at a time. + * We will pick them up 32 at a time and then deal with those 32, one + * at a time. Note, however, that we know several things about y: + * + * - the guard and round bits at the bottom are sure to be zero; + * + * - often many low bits are zero (y is often from a single or double + * precision source); + * + * - bit FP_NMANT-1 is set, and FP_1*2 fits in a word. + * + * We can also test for 32-zero-bits swiftly. In this case, the center + * part of the loop---setting sticky, shifting A, and not adding---will + * run 32 times without adding X to A. We can do a 32-bit shift faster + * by simply moving words. Since zeros are common, we optimize this case. + * Furthermore, since A is initially zero, we can omit the shift as well + * until we reach a nonzero word. + */ +struct fpn * +fpu_mul(fe) + register struct fpemu *fe; +{ + register struct fpn *x = &fe->fe_f1, *y = &fe->fe_f2; + register u_int a3, a2, a1, a0, x3, x2, x1, x0, bit, m; + register int sticky; + FPU_DECL_CARRY + + /* + * Put the `heavier' operand on the right (see fpu_emu.h). + * Then we will have one of the following cases, taken in the + * following order: + * + * - y = NaN. Implied: if only one is a signalling NaN, y is. + * The result is y. + * - y = Inf. Implied: x != NaN (is 0, number, or Inf: the NaN + * case was taken care of earlier). + * If x = 0, the result is NaN. Otherwise the result + * is y, with its sign reversed if x is negative. + * - x = 0. Implied: y is 0 or number. + * The result is 0 (with XORed sign as usual). + * - other. Implied: both x and y are numbers. + * The result is x * y (XOR sign, multiply bits, add exponents). + */ + ORDER(x, y); + if (ISNAN(y)) { + y->fp_sign ^= x->fp_sign; + return (y); + } + if (ISINF(y)) { + if (ISZERO(x)) + return (fpu_newnan(fe)); + y->fp_sign ^= x->fp_sign; + return (y); + } + if (ISZERO(x)) { + x->fp_sign ^= y->fp_sign; + return (x); + } + + /* + * Setup. In the code below, the mask `m' will hold the current + * mantissa byte from y. The variable `bit' denotes the bit + * within m. We also define some macros to deal with everything. + */ + x3 = x->fp_mant[3]; + x2 = x->fp_mant[2]; + x1 = x->fp_mant[1]; + x0 = x->fp_mant[0]; + sticky = a3 = a2 = a1 = a0 = 0; + +#define ADD /* A += X */ \ + FPU_ADDS(a3, a3, x3); \ + FPU_ADDCS(a2, a2, x2); \ + FPU_ADDCS(a1, a1, x1); \ + FPU_ADDC(a0, a0, x0) + +#define SHR1 /* A >>= 1, with sticky */ \ + sticky |= a3 & 1, a3 = (a3 >> 1) | (a2 << 31), \ + a2 = (a2 >> 1) | (a1 << 31), a1 = (a1 >> 1) | (a0 << 31), a0 >>= 1 + +#define SHR32 /* A >>= 32, with sticky */ \ + sticky |= a3, a3 = a2, a2 = a1, a1 = a0, a0 = 0 + +#define STEP /* each 1-bit step of the multiplication */ \ + SHR1; if (bit & m) { ADD; }; bit <<= 1 + + /* + * We are ready to begin. The multiply loop runs once for each + * of the four 32-bit words. Some words, however, are special. + * As noted above, the low order bits of Y are often zero. Even + * if not, the first loop can certainly skip the guard bits. + * The last word of y has its highest 1-bit in position FP_NMANT-1, + * so we stop the loop when we move past that bit. + */ + if ((m = y->fp_mant[3]) == 0) { + /* SHR32; */ /* unneeded since A==0 */ + } else { + bit = 1 << FP_NG; + do { + STEP; + } while (bit != 0); + } + if ((m = y->fp_mant[2]) == 0) { + SHR32; + } else { + bit = 1; + do { + STEP; + } while (bit != 0); + } + if ((m = y->fp_mant[1]) == 0) { + SHR32; + } else { + bit = 1; + do { + STEP; + } while (bit != 0); + } + m = y->fp_mant[0]; /* definitely != 0 */ + bit = 1; + do { + STEP; + } while (bit <= m); + + /* + * Done with mantissa calculation. Get exponent and handle + * 11.111...1 case, then put result in place. We reuse x since + * it already has the right class (FP_NUM). + */ + m = x->fp_exp + y->fp_exp; + if (a0 >= FP_2) { + SHR1; + m++; + } + x->fp_sign ^= y->fp_sign; + x->fp_exp = m; + x->fp_sticky = sticky; + x->fp_mant[3] = a3; + x->fp_mant[2] = a2; + x->fp_mant[1] = a1; + x->fp_mant[0] = a0; + return (x); +} diff --git a/sys/arch/m68k/fpe/fpu_rem.c b/sys/arch/m68k/fpe/fpu_rem.c new file mode 100644 index 00000000000..ddf24ec5725 --- /dev/null +++ b/sys/arch/m68k/fpe/fpu_rem.c @@ -0,0 +1,235 @@ +/* $NetBSD: fpu_rem.c,v 1.1 1995/11/03 04:47:17 briggs Exp $ */ + +/* + * Copyright (c) 1995 Ken Nakata + * 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. Neither the name of the author 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 AUTHOR 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 AUTHOR 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_rem.c 10/24/95 + */ + +#include <sys/types.h> +#include <sys/signal.h> +#include <machine/frame.h> + +#include "fpu_emulate.h" + +/* + * ALGORITHM + * + * Step 1. Save and strip signs of X and Y: signX := sign(X), + * signY := sign(Y), X := *X*, Y := *Y*, + * signQ := signX EOR signY. Record whether MOD or REM + * is requested. + * + * Step 2. Set L := expo(X)-expo(Y), k := 0, Q := 0. + * If (L < 0) then + * R := X, go to Step 4. + * else + * R := 2^(-L)X, j := L. + * endif + * + * Step 3. Perform MOD(X,Y) + * 3.1 If R = Y, go to Step 9. + * 3.2 If R > Y, then { R := R - Y, Q := Q + 1} + * 3.3 If j = 0, go to Step 4. + * 3.4 k := k + 1, j := j - 1, Q := 2Q, R := 2R. Go to + * Step 3.1. + * + * Step 4. At this point, R = X - QY = MOD(X,Y). Set + * Last_Subtract := false (used in Step 7 below). If + * MOD is requested, go to Step 6. + * + * Step 5. R = MOD(X,Y), but REM(X,Y) is requested. + * 5.1 If R < Y/2, then R = MOD(X,Y) = REM(X,Y). Go to + * Step 6. + * 5.2 If R > Y/2, then { set Last_Subtract := true, + * Q := Q + 1, Y := signY*Y }. Go to Step 6. + * 5.3 This is the tricky case of R = Y/2. If Q is odd, + * then { Q := Q + 1, signX := -signX }. + * + * Step 6. R := signX*R. + * + * Step 7. If Last_Subtract = true, R := R - Y. + * + * Step 8. Return signQ, last 7 bits of Q, and R as required. + * + * Step 9. At this point, R = 2^(-j)*X - Q Y = Y. Thus, + * X = 2^(j)*(Q+1)Y. set Q := 2^(j)*(Q+1), + * R := 0. Return signQ, last 7 bits of Q, and R. + */ + +static struct fpn * __fpu_modrem __P((struct fpemu *fe, int modrem)); + +static struct fpn * +__fpu_modrem(fe, modrem) + struct fpemu *fe; + int modrem; +{ + static struct fpn X, Y; + struct fpn *x, *y, *r; + u_int signX, signY, signQ; + int i, j, k, l, q; + int Last_Subtract; + + CPYFPN(&X, &fe->fe_f1); + CPYFPN(&Y, &fe->fe_f2); + x = &X; + y = &Y; + r = &fe->fe_f2; + + /* + * Step 1 + */ + signX = x->fp_sign; + signY = y->fp_sign; + signQ = (signX ^ signY); + x->fp_sign = y->fp_sign = 0; + + /* + * Step 2 + */ + l = x->fp_exp - y->fp_exp; + k = 0; + q = 0; + if (l < 0) { + goto Step4; + } else { + CPYFPN(r, x); + r->fp_exp -= l; + j = l; + + /* + * Step 3 + */ + while (y->fp_exp != r->fp_exp || y->fp_mant[0] != r->fp_mant[0] || + y->fp_mant[1] != r->fp_mant[1] || + y->fp_mant[2] != r->fp_mant[2] || + y->fp_mant[3] != r->fp_mant[3]) { + + /* Step 3.2 */ + if (y->fp_exp < r->fp_exp || y->fp_mant[0] < r->fp_mant[0] || + y->fp_mant[1] < r->fp_mant[1] || + y->fp_mant[2] < r->fp_mant[2] || + y->fp_mant[3] < r->fp_mant[3]) { + CPYFPN(&fe->fe_f1, r); + CPYFPN(&fe->fe_f2, y); + fe->fe_f2.fp_sign = 1; + r = fpu_add(fe); + q++; + } + + /* Step 3.3 */ + if (j == 0) + goto Step4; + + /* Step 3.4 */ + k++; + j--; + q += q; + r->fp_exp++; + } + /* Step 9 */ + + } + Step4: + Last_Subtract = 0; + if (modrem == 0) + goto Step6; + + /* + * Step 5 + */ + /* Step 5.1 */ + if (r->fp_exp + 1 < y->fp_exp || + r->fp_exp + 1 == y->fp_exp && + (r->fp_mant[0] < y->fp_mant[0] || r->fp_mant[1] < y->fp_mant[1] || + r->fp_mant[2] < y->fp_mant[3] || r->fp_mant[4] < y->fp_mant[4])) + /* if r < y/2 */ + goto Step6; + /* Step 5.2 */ + if (r->fp_exp + 1 != y->fp_exp || + r->fp_mant[0] != y->fp_mant[0] || r->fp_mant[1] != y->fp_mant[1] || + r->fp_mant[2] != y->fp_mant[2] || r->fp_mant[3] != y->fp_mant[3]) { + /* if (!(r < y/2) && !(r == y/2)) */ + Last_Subtract = 1; + q++; + y->fp_sign = signY; + } else { + /* Step 5.3 */ + /* r == y/2 */ + if (q % 2) { + q++; + signX = !signX; + } + } + + Step6: + r->fp_sign = signX; + + /* + * Step 7 + */ + if (Last_Subtract) { + CPYFPN(&fe->fe_f1, r); + CPYFPN(&fe->fe_f2, y); + fe->fe_f2.fp_sign = !y->fp_sign; + r = fpu_add(fe); + } + /* + * Step 8 + */ + q &= 0x7f; + q |= (signQ << 7); + fe->fe_fpframe->fpf_fpsr = + fe->fe_fpsr = + (fe->fe_fpsr & ~FPSR_QTT) | (q << 16); + return r; + + Step9: + fe->fe_f1.fp_class = FPC_ZERO; + q++; + q &= 0x7f; + q |= (signQ << 7); + fe->fe_fpframe->fpf_fpsr = + fe->fe_fpsr = + (fe->fe_fpsr & ~FPSR_QTT) | (q << 16); + return &fe->fe_f1; +} + +struct fpn * +fpu_rem(fe) + struct fpemu *fe; +{ + return __fpu_modrem(fe, 1); +} + +struct fpn * +fpu_mod(fe) + struct fpemu *fe; +{ + return __fpu_modrem(fe, 0); +} diff --git a/sys/arch/m68k/fpe/fpu_sqrt.c b/sys/arch/m68k/fpe/fpu_sqrt.c new file mode 100644 index 00000000000..23cdea6fab7 --- /dev/null +++ b/sys/arch/m68k/fpe/fpu_sqrt.c @@ -0,0 +1,398 @@ +/* $NetBSD: fpu_sqrt.c,v 1.1 1995/11/03 04:47:18 briggs 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_sqrt.c 8.1 (Berkeley) 6/11/93 + */ + +/* + * Perform an FPU square root (return sqrt(x)). + */ + +#include <sys/types.h> + +#include <machine/reg.h> + +#include "fpu_arith.h" +#include "fpu_emulate.h" + +/* + * Our task is to calculate the square root of a floating point number x0. + * This number x normally has the form: + * + * exp + * x = mant * 2 (where 1 <= mant < 2 and exp is an integer) + * + * This can be left as it stands, or the mantissa can be doubled and the + * exponent decremented: + * + * exp-1 + * x = (2 * mant) * 2 (where 2 <= 2 * mant < 4) + * + * If the exponent `exp' is even, the square root of the number is best + * handled using the first form, and is by definition equal to: + * + * exp/2 + * sqrt(x) = sqrt(mant) * 2 + * + * If exp is odd, on the other hand, it is convenient to use the second + * form, giving: + * + * (exp-1)/2 + * sqrt(x) = sqrt(2 * mant) * 2 + * + * In the first case, we have + * + * 1 <= mant < 2 + * + * and therefore + * + * sqrt(1) <= sqrt(mant) < sqrt(2) + * + * while in the second case we have + * + * 2 <= 2*mant < 4 + * + * and therefore + * + * sqrt(2) <= sqrt(2*mant) < sqrt(4) + * + * so that in any case, we are sure that + * + * sqrt(1) <= sqrt(n * mant) < sqrt(4), n = 1 or 2 + * + * or + * + * 1 <= sqrt(n * mant) < 2, n = 1 or 2. + * + * This root is therefore a properly formed mantissa for a floating + * point number. The exponent of sqrt(x) is either exp/2 or (exp-1)/2 + * as above. This leaves us with the problem of finding the square root + * of a fixed-point number in the range [1..4). + * + * Though it may not be instantly obvious, the following square root + * algorithm works for any integer x of an even number of bits, provided + * that no overflows occur: + * + * let q = 0 + * for k = NBITS-1 to 0 step -1 do -- for each digit in the answer... + * x *= 2 -- multiply by radix, for next digit + * if x >= 2q + 2^k then -- if adding 2^k does not + * x -= 2q + 2^k -- exceed the correct root, + * q += 2^k -- add 2^k and adjust x + * fi + * done + * sqrt = q / 2^(NBITS/2) -- (and any remainder is in x) + * + * If NBITS is odd (so that k is initially even), we can just add another + * zero bit at the top of x. Doing so means that q is not going to acquire + * a 1 bit in the first trip around the loop (since x0 < 2^NBITS). If the + * final value in x is not needed, or can be off by a factor of 2, this is + * equivalant to moving the `x *= 2' step to the bottom of the loop: + * + * for k = NBITS-1 to 0 step -1 do if ... fi; x *= 2; done + * + * and the result q will then be sqrt(x0) * 2^floor(NBITS / 2). + * (Since the algorithm is destructive on x, we will call x's initial + * value, for which q is some power of two times its square root, x0.) + * + * If we insert a loop invariant y = 2q, we can then rewrite this using + * C notation as: + * + * q = y = 0; x = x0; + * for (k = NBITS; --k >= 0;) { + * #if (NBITS is even) + * x *= 2; + * #endif + * t = y + (1 << k); + * if (x >= t) { + * x -= t; + * q += 1 << k; + * y += 1 << (k + 1); + * } + * #if (NBITS is odd) + * x *= 2; + * #endif + * } + * + * If x0 is fixed point, rather than an integer, we can simply alter the + * scale factor between q and sqrt(x0). As it happens, we can easily arrange + * for the scale factor to be 2**0 or 1, so that sqrt(x0) == q. + * + * In our case, however, x0 (and therefore x, y, q, and t) are multiword + * integers, which adds some complication. But note that q is built one + * bit at a time, from the top down, and is not used itself in the loop + * (we use 2q as held in y instead). This means we can build our answer + * in an integer, one word at a time, which saves a bit of work. Also, + * since 1 << k is always a `new' bit in q, 1 << k and 1 << (k+1) are + * `new' bits in y and we can set them with an `or' operation rather than + * a full-blown multiword add. + * + * We are almost done, except for one snag. We must prove that none of our + * intermediate calculations can overflow. We know that x0 is in [1..4) + * and therefore the square root in q will be in [1..2), but what about x, + * y, and t? + * + * We know that y = 2q at the beginning of each loop. (The relation only + * fails temporarily while y and q are being updated.) Since q < 2, y < 4. + * The sum in t can, in our case, be as much as y+(1<<1) = y+2 < 6, and. + * Furthermore, we can prove with a bit of work that x never exceeds y by + * more than 2, so that even after doubling, 0 <= x < 8. (This is left as + * an exercise to the reader, mostly because I have become tired of working + * on this comment.) + * + * If our floating point mantissas (which are of the form 1.frac) occupy + * B+1 bits, our largest intermediary needs at most B+3 bits, or two extra. + * In fact, we want even one more bit (for a carry, to avoid compares), or + * three extra. There is a comment in fpu_emu.h reminding maintainers of + * this, so we have some justification in assuming it. + */ +struct fpn * +fpu_sqrt(fe) + struct fpemu *fe; +{ + register struct fpn *x = &fe->fe_f2; + register u_int bit, q, tt; + register u_int x0, x1, x2, x3; + register u_int y0, y1, y2, y3; + register u_int d0, d1, d2, d3; + register int e; + FPU_DECL_CARRY + + /* + * Take care of special cases first. In order: + * + * sqrt(NaN) = NaN + * sqrt(+0) = +0 + * sqrt(-0) = -0 + * sqrt(x < 0) = NaN (including sqrt(-Inf)) + * sqrt(+Inf) = +Inf + * + * Then all that remains are numbers with mantissas in [1..2). + */ + if (ISNAN(x) || ISZERO(x)) + return (x); + if (x->fp_sign) + return (fpu_newnan(fe)); + if (ISINF(x)) + return (x); + + /* + * Calculate result exponent. As noted above, this may involve + * doubling the mantissa. We will also need to double x each + * time around the loop, so we define a macro for this here, and + * we break out the multiword mantissa. + */ +#ifdef FPU_SHL1_BY_ADD +#define DOUBLE_X { \ + FPU_ADDS(x3, x3, x3); FPU_ADDCS(x2, x2, x2); \ + FPU_ADDCS(x1, x1, x1); FPU_ADDC(x0, x0, x0); \ +} +#else +#define DOUBLE_X { \ + x0 = (x0 << 1) | (x1 >> 31); x1 = (x1 << 1) | (x2 >> 31); \ + x2 = (x2 << 1) | (x3 >> 31); x3 <<= 1; \ +} +#endif +#if (FP_NMANT & 1) != 0 +# define ODD_DOUBLE DOUBLE_X +# define EVEN_DOUBLE /* nothing */ +#else +# define ODD_DOUBLE /* nothing */ +# define EVEN_DOUBLE DOUBLE_X +#endif + x0 = x->fp_mant[0]; + x1 = x->fp_mant[1]; + x2 = x->fp_mant[2]; + x3 = x->fp_mant[3]; + e = x->fp_exp; + if (e & 1) /* exponent is odd; use sqrt(2mant) */ + DOUBLE_X; + /* THE FOLLOWING ASSUMES THAT RIGHT SHIFT DOES SIGN EXTENSION */ + x->fp_exp = e >> 1; /* calculates (e&1 ? (e-1)/2 : e/2 */ + + /* + * Now calculate the mantissa root. Since x is now in [1..4), + * we know that the first trip around the loop will definitely + * set the top bit in q, so we can do that manually and start + * the loop at the next bit down instead. We must be sure to + * double x correctly while doing the `known q=1.0'. + * + * We do this one mantissa-word at a time, as noted above, to + * save work. To avoid `(1 << 31) << 1', we also do the top bit + * outside of each per-word loop. + * + * The calculation `t = y + bit' breaks down into `t0 = y0, ..., + * t3 = y3, t? |= bit' for the appropriate word. Since the bit + * is always a `new' one, this means that three of the `t?'s are + * just the corresponding `y?'; we use `#define's here for this. + * The variable `tt' holds the actual `t?' variable. + */ + + /* calculate q0 */ +#define t0 tt + bit = FP_1; + EVEN_DOUBLE; + /* if (x >= (t0 = y0 | bit)) { */ /* always true */ + q = bit; + x0 -= bit; + y0 = bit << 1; + /* } */ + ODD_DOUBLE; + while ((bit >>= 1) != 0) { /* for remaining bits in q0 */ + EVEN_DOUBLE; + t0 = y0 | bit; /* t = y + bit */ + if (x0 >= t0) { /* if x >= t then */ + x0 -= t0; /* x -= t */ + q |= bit; /* q += bit */ + y0 |= bit << 1; /* y += bit << 1 */ + } + ODD_DOUBLE; + } + x->fp_mant[0] = q; +#undef t0 + + /* calculate q1. note (y0&1)==0. */ +#define t0 y0 +#define t1 tt + q = 0; + y1 = 0; + bit = 1 << 31; + EVEN_DOUBLE; + t1 = bit; + FPU_SUBS(d1, x1, t1); + FPU_SUBC(d0, x0, t0); /* d = x - t */ + if ((int)d0 >= 0) { /* if d >= 0 (i.e., x >= t) then */ + x0 = d0, x1 = d1; /* x -= t */ + q = bit; /* q += bit */ + y0 |= 1; /* y += bit << 1 */ + } + ODD_DOUBLE; + while ((bit >>= 1) != 0) { /* for remaining bits in q1 */ + EVEN_DOUBLE; /* as before */ + t1 = y1 | bit; + FPU_SUBS(d1, x1, t1); + FPU_SUBC(d0, x0, t0); + if ((int)d0 >= 0) { + x0 = d0, x1 = d1; + q |= bit; + y1 |= bit << 1; + } + ODD_DOUBLE; + } + x->fp_mant[1] = q; +#undef t1 + + /* calculate q2. note (y1&1)==0; y0 (aka t0) is fixed. */ +#define t1 y1 +#define t2 tt + q = 0; + y2 = 0; + bit = 1 << 31; + EVEN_DOUBLE; + t2 = bit; + FPU_SUBS(d2, x2, t2); + FPU_SUBCS(d1, x1, t1); + FPU_SUBC(d0, x0, t0); + if ((int)d0 >= 0) { + x0 = d0, x1 = d1, x2 = d2; + q |= bit; + y1 |= 1; /* now t1, y1 are set in concrete */ + } + ODD_DOUBLE; + while ((bit >>= 1) != 0) { + EVEN_DOUBLE; + t2 = y2 | bit; + FPU_SUBS(d2, x2, t2); + FPU_SUBCS(d1, x1, t1); + FPU_SUBC(d0, x0, t0); + if ((int)d0 >= 0) { + x0 = d0, x1 = d1, x2 = d2; + q |= bit; + y2 |= bit << 1; + } + ODD_DOUBLE; + } + x->fp_mant[2] = q; +#undef t2 + + /* calculate q3. y0, t0, y1, t1 all fixed; y2, t2, almost done. */ +#define t2 y2 +#define t3 tt + q = 0; + y3 = 0; + bit = 1 << 31; + EVEN_DOUBLE; + t3 = bit; + FPU_SUBS(d3, x3, t3); + FPU_SUBCS(d2, x2, t2); + FPU_SUBCS(d1, x1, t1); + FPU_SUBC(d0, x0, t0); + ODD_DOUBLE; + if ((int)d0 >= 0) { + x0 = d0, x1 = d1, x2 = d2; + q |= bit; + y2 |= 1; + } + while ((bit >>= 1) != 0) { + EVEN_DOUBLE; + t3 = y3 | bit; + FPU_SUBS(d3, x3, t3); + FPU_SUBCS(d2, x2, t2); + FPU_SUBCS(d1, x1, t1); + FPU_SUBC(d0, x0, t0); + if ((int)d0 >= 0) { + x0 = d0, x1 = d1, x2 = d2; + q |= bit; + y3 |= bit << 1; + } + ODD_DOUBLE; + } + x->fp_mant[3] = q; + + /* + * The result, which includes guard and round bits, is exact iff + * x is now zero; any nonzero bits in x represent sticky bits. + */ + x->fp_sticky = x0 | x1 | x2 | x3; + return (x); +} diff --git a/sys/arch/m68k/fpe/fpu_subr.c b/sys/arch/m68k/fpe/fpu_subr.c new file mode 100644 index 00000000000..fed1294ce2f --- /dev/null +++ b/sys/arch/m68k/fpe/fpu_subr.c @@ -0,0 +1,218 @@ +/* $NetBSD: fpu_subr.c,v 1.1 1995/11/03 04:47:19 briggs 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_subr.c 8.1 (Berkeley) 6/11/93 + */ + +/* + * FPU subroutines. + */ + +#include <sys/types.h> + +#include <machine/reg.h> + +#include "fpu_emulate.h" +#include "fpu_arith.h" + +/* + * Shift the given number right rsh bits. Any bits that `fall off' will get + * shoved into the sticky field; we return the resulting sticky. Note that + * shifting NaNs is legal (this will never shift all bits out); a NaN's + * sticky field is ignored anyway. + */ +int +fpu_shr(register struct fpn *fp, register int rsh) +{ + register u_int m0, m1, m2, m3, s; + register int lsh; + +#ifdef DIAGNOSTIC + if (rsh <= 0 || (fp->fp_class != FPC_NUM && !ISNAN(fp))) + panic("fpu_rightshift 1"); +#endif + + m0 = fp->fp_mant[0]; + m1 = fp->fp_mant[1]; + m2 = fp->fp_mant[2]; + m3 = fp->fp_mant[3]; + + /* If shifting all the bits out, take a shortcut. */ + if (rsh >= FP_NMANT) { +#ifdef DIAGNOSTIC + if ((m0 | m1 | m2 | m3) == 0) + panic("fpu_rightshift 2"); +#endif + fp->fp_mant[0] = 0; + fp->fp_mant[1] = 0; + fp->fp_mant[2] = 0; + fp->fp_mant[3] = 0; +#ifdef notdef + if ((m0 | m1 | m2 | m3) == 0) + fp->fp_class = FPC_ZERO; + else +#endif + fp->fp_sticky = 1; + return (1); + } + + /* Squish out full words. */ + s = fp->fp_sticky; + if (rsh >= 32 * 3) { + s |= m3 | m2 | m1; + m3 = m0, m2 = 0, m1 = 0, m0 = 0; + } else if (rsh >= 32 * 2) { + s |= m3 | m2; + m3 = m1, m2 = m0, m1 = 0, m0 = 0; + } else if (rsh >= 32) { + s |= m3; + m3 = m2, m2 = m1, m1 = m0, m0 = 0; + } + + /* Handle any remaining partial word. */ + if ((rsh &= 31) != 0) { + lsh = 32 - rsh; + s |= m3 << lsh; + m3 = (m3 >> rsh) | (m2 << lsh); + m2 = (m2 >> rsh) | (m1 << lsh); + m1 = (m1 >> rsh) | (m0 << lsh); + m0 >>= rsh; + } + fp->fp_mant[0] = m0; + fp->fp_mant[1] = m1; + fp->fp_mant[2] = m2; + fp->fp_mant[3] = m3; + fp->fp_sticky = s; + return (s); +} + +/* + * Force a number to be normal, i.e., make its fraction have all zero + * bits before FP_1, then FP_1, then all 1 bits. This is used for denorms + * and (sometimes) for intermediate results. + * + * Internally, this may use a `supernormal' -- a number whose fp_mant + * is greater than or equal to 2.0 -- so as a side effect you can hand it + * a supernormal and it will fix it (provided fp->fp_mant[3] == 0). + */ +void +fpu_norm(register struct fpn *fp) +{ + register u_int m0, m1, m2, m3, top, sup, nrm; + register int lsh, rsh, exp; + + exp = fp->fp_exp; + m0 = fp->fp_mant[0]; + m1 = fp->fp_mant[1]; + m2 = fp->fp_mant[2]; + m3 = fp->fp_mant[3]; + + /* Handle severe subnormals with 32-bit moves. */ + if (m0 == 0) { + if (m1) + m0 = m1, m1 = m2, m2 = m3, m3 = 0, exp -= 32; + else if (m2) + m0 = m2, m1 = m3, m2 = 0, m3 = 0, exp -= 2 * 32; + else if (m3) + m0 = m3, m1 = 0, m2 = 0, m3 = 0, exp -= 3 * 32; + else { + fp->fp_class = FPC_ZERO; + return; + } + } + + /* Now fix any supernormal or remaining subnormal. */ + nrm = FP_1; + sup = nrm << 1; + if (m0 >= sup) { + /* + * We have a supernormal number. We need to shift it right. + * We may assume m3==0. + */ + for (rsh = 1, top = m0 >> 1; top >= sup; rsh++) /* XXX slow */ + top >>= 1; + exp += rsh; + lsh = 32 - rsh; + m3 = m2 << lsh; + m2 = (m2 >> rsh) | (m1 << lsh); + m1 = (m1 >> rsh) | (m0 << lsh); + m0 = top; + } else if (m0 < nrm) { + /* + * We have a regular denorm (a subnormal number), and need + * to shift it left. + */ + for (lsh = 1, top = m0 << 1; top < nrm; lsh++) /* XXX slow */ + top <<= 1; + exp -= lsh; + rsh = 32 - lsh; + m0 = top | (m1 >> rsh); + m1 = (m1 << lsh) | (m2 >> rsh); + m2 = (m2 << lsh) | (m3 >> rsh); + m3 <<= lsh; + } + + fp->fp_exp = exp; + fp->fp_mant[0] = m0; + fp->fp_mant[1] = m1; + fp->fp_mant[2] = m2; + fp->fp_mant[3] = m3; +} + +/* + * Concoct a `fresh' Quiet NaN per Appendix N. + * As a side effect, we set OPERR for the current exceptions. + */ +struct fpn * +fpu_newnan(register struct fpemu *fe) +{ + register struct fpn *fp; + + fe->fe_fpsr |= FPSR_OPERR; + fp = &fe->fe_f3; + fp->fp_class = FPC_QNAN; + fp->fp_sign = 0; + fp->fp_mant[0] = FP_1 - 1; + fp->fp_mant[1] = fp->fp_mant[2] = fp->fp_mant[3] = ~0; + return (fp); +} diff --git a/sys/arch/m68k/fpe/fpu_trig.c b/sys/arch/m68k/fpe/fpu_trig.c new file mode 100644 index 00000000000..0358c84878e --- /dev/null +++ b/sys/arch/m68k/fpe/fpu_trig.c @@ -0,0 +1,91 @@ +/* $NetBSD: fpu_trig.c,v 1.1 1995/11/03 04:47:20 briggs Exp $ */ + +/* + * Copyright (c) 1995 Ken Nakata + * 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. Neither the name of the author 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 AUTHOR 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 AUTHOR 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_trig.c 10/24/95 + */ + +#include "fpu_emulate.h" + +struct fpn * +fpu_acos(fe) + struct fpemu *fe; +{ + /* stub */ + return &fe->fe_f2; +} + +struct fpn * +fpu_asin(fe) + struct fpemu *fe; +{ + /* stub */ + return &fe->fe_f2; +} + +struct fpn * +fpu_atan(fe) + struct fpemu *fe; +{ + /* stub */ + return &fe->fe_f2; +} + +struct fpn * +fpu_cos(fe) + struct fpemu *fe; +{ + /* stub */ + return &fe->fe_f2; +} + +struct fpn * +fpu_sin(fe) + struct fpemu *fe; +{ + /* stub */ + return &fe->fe_f2; +} + +struct fpn * +fpu_tan(fe) + struct fpemu *fe; +{ + /* stub */ + return &fe->fe_f2; +} + +struct fpn * +fpu_sincos(fe, regc) + struct fpemu *fe; + int regc; +{ + /* stub */ + return &fe->fe_f2; +} diff --git a/sys/arch/m68k/fpe/ieee.h b/sys/arch/m68k/fpe/ieee.h new file mode 100644 index 00000000000..07ac5130af2 --- /dev/null +++ b/sys/arch/m68k/fpe/ieee.h @@ -0,0 +1,136 @@ +/* $NetBSD: ieee.h,v 1.1 1995/11/03 04:47:21 briggs 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. + * + * @(#)ieee.h 8.1 (Berkeley) 6/11/93 + */ + +/* + * ieee.h defines the machine-dependent layout of the machine's IEEE + * floating point. It does *not* define (yet?) any of the rounding + * mode bits, exceptions, and so forth. + */ + +/* + * Define the number of bits in each fraction and exponent. + * + * k k+1 + * Note that 1.0 x 2 == 0.1 x 2 and that denorms are represented + * + * (-exp_bias+1) + * as fractions that look like 0.fffff x 2 . This means that + * + * -126 + * the number 0.10000 x 2 , for instance, is the same as the normalized + * + * -127 -128 + * float 1.0 x 2 . Thus, to represent 2 , we need one leading zero + * + * -129 + * in the fraction; to represent 2 , we need two, and so on. This + * + * (-exp_bias-fracbits+1) + * implies that the smallest denormalized number is 2 + * + * for whichever format we are talking about: for single precision, for + * + * -126 -149 + * instance, we get .00000000000000000000001 x 2 , or 1.0 x 2 , and + * + * -149 == -127 - 23 + 1. + */ +#define SNG_EXPBITS 8 +#define SNG_FRACBITS 23 + +#define DBL_EXPBITS 11 +#define DBL_FRACBITS 52 + +#define EXT_EXPBITS 15 +#define EXT_FRACBITS 64 + +struct ieee_single { + u_int sng_sign:1; + u_int sng_exp:8; + u_int sng_frac:23; +}; + +struct ieee_double { + u_int dbl_sign:1; + u_int dbl_exp:11; + u_int dbl_frach:20; + u_int dbl_fracl; +}; + +struct ieee_ext { + u_int ext_sign:1; + u_int ext_exp:15; + u_int ext_zero:16; + u_int ext_int:1; + u_int ext_frach:31; + u_int ext_fracl; +}; + +/* + * Floats whose exponent is in [1..INFNAN) (of whatever type) are + * `normal'. Floats whose exponent is INFNAN are either Inf or NaN. + * Floats whose exponent is zero are either zero (iff all fraction + * bits are zero) or subnormal values. + * + * A NaN is a `signalling NaN' if its QUIETNAN bit is clear in its + * high fraction; if the bit is set, it is a `quiet NaN'. + */ +#define SNG_EXP_INFNAN 255 +#define DBL_EXP_INFNAN 2047 +#define EXT_EXP_INFNAN 32767 + +#if 0 +#define SNG_QUIETNAN (1 << 22) +#define DBL_QUIETNAN (1 << 19) +#define EXT_QUIETNAN (1 << 15) +#endif + +/* + * Exponent biases. + */ +#define SNG_EXP_BIAS 127 +#define DBL_EXP_BIAS 1023 +#define EXT_EXP_BIAS 16383 |