1*45761Sbostic /*-
2*45761Sbostic * Copyright (c) 1985 The Regents of the University of California.
3*45761Sbostic * All rights reserved.
4*45761Sbostic *
5*45761Sbostic * This code is derived from software contributed to Berkeley by
6*45761Sbostic * Computer Consoles Inc.
7*45761Sbostic *
8*45761Sbostic * %sccs.include.redist.c%
9*45761Sbostic *
10*45761Sbostic * @(#)fpe.c 7.1 (Berkeley) 12/06/90
11*45761Sbostic */
1225665Ssam
1345701Sbostic #include "../include/psl.h"
1445701Sbostic #include "../include/reg.h"
1545701Sbostic #include "../include/pte.h"
1645701Sbostic #include "../include/mtpr.h"
1745701Sbostic #include "../math/Kfp.h"
1825665Ssam
1945701Sbostic #include "sys/param.h"
2045701Sbostic #include "sys/systm.h"
2145701Sbostic #include "sys/user.h"
2245701Sbostic #include "sys/proc.h"
2345701Sbostic #include "sys/seg.h"
2445701Sbostic #include "sys/acct.h"
2545701Sbostic #include "sys/kernel.h"
2625665Ssam
2725665Ssam /*
2825665Ssam * Floating point emulation support.
2925665Ssam */
3025665Ssam extern float Kcvtlf(), Kaddf(), Ksubf(), Kmulf(), Kdivf();
3125665Ssam extern double Kcvtld(), Kaddd(), Ksubd(), Kmuld(), Kdivd();
3225665Ssam extern float Ksinf(), Kcosf(), Katanf(), Klogf(), Ksqrtf(), Kexpf();
3325665Ssam
3425665Ssam #define OP(dop) ((dop) &~ 01) /* precision-less version of opcode */
3525665Ssam #define isdouble(op) ((op) & 01) /* is opcode double or float */
3625665Ssam
3725665Ssam struct fpetab {
3825665Ssam int fpe_op; /* base opcode emulating */
3925665Ssam float (*fpe_ffunc)(); /* float version of op */
4025665Ssam double (*fpe_dfunc)(); /* double version of op */
4125665Ssam } fpetab[] = {
4225665Ssam { OP(CVLD), Kcvtlf, Kcvtld },
4325665Ssam { OP(ADDD), Kaddf, Kaddd },
4425665Ssam { OP(SUBD), Ksubf, Ksubd },
4525665Ssam { OP(MULD), Kmulf, Kmuld },
4625665Ssam { OP(DIVD), Kdivf, Kdivd },
4725665Ssam { SINF, Ksinf, 0 },
4825665Ssam { COSF, Kcosf, 0 },
4925665Ssam { ATANF, Katanf, 0 },
5025665Ssam { LOGF, Klogf, 0 },
5125665Ssam { SQRTF, Ksqrtf, 0 },
5225665Ssam { EXPF, Kexpf, 0 },
5325665Ssam };
5425665Ssam #define NFPETAB (sizeof (fpetab) / sizeof (fpetab[0]))
5525665Ssam
5625665Ssam /*
5725665Ssam * Emulate the FP opcode. Update psl as necessary.
5825665Ssam * If OK, set opcode to 0, else to the FP exception #.
5925665Ssam * Not all parameter longwords are relevant, depends on opcode.
6025665Ssam *
6125665Ssam * The entry mask is set by locore.s so ALL registers are saved.
6225665Ssam * This enables FP opcodes to change user registers on return.
6325665Ssam */
6425665Ssam /* WARNING!!!! THIS CODE MUST NOT PRODUCE ANY FLOATING POINT EXCEPTIONS */
6525665Ssam /*ARGSUSED*/
fpemulate(hfsreg,acc_most,acc_least,dbl,op_most,op_least,opcode,pc,psl)6625665Ssam fpemulate(hfsreg, acc_most, acc_least, dbl, op_most, op_least, opcode, pc, psl)
6725665Ssam {
6825665Ssam int r0, r1; /* must reserve space */
6925665Ssam register int *locr0 = ((int *)&psl)-PS;
7025665Ssam register struct fpetab *fp;
7125665Ssam int hfs = 0; /* returned data about exceptions */
7225665Ssam int type; /* opcode type, FLOAT or DOUBLE */
7325665Ssam union { float ff; int fi; } f_res;
7425665Ssam union { double dd; int di[2]; } d_res;
7543385Smckusick int error = 0;
7625665Ssam
7725665Ssam #ifdef lint
7825665Ssam r0 = 0; r0 = r0; r1 = 0; r1 = r1;
7925665Ssam #endif
8025665Ssam type = isdouble(opcode) ? DOUBLE : FLOAT;
8125665Ssam for (fp = fpetab; fp < &fpetab[NFPETAB]; fp++)
8225665Ssam if ((opcode & 0xfe) == fp->fpe_op)
8325665Ssam break;
8425665Ssam if (type == DOUBLE) {
8525665Ssam if (fp->fpe_dfunc == 0)
8625665Ssam fp = &fpetab[NFPETAB];
8725665Ssam else
8825665Ssam locr0[PS] &= ~PSL_DBL;
8925665Ssam }
9025665Ssam if (fp >= &fpetab[NFPETAB]) {
9125665Ssam opcode = DIV0_EXC; /* generate SIGILL - XXX */
9243385Smckusick return (0);
9325665Ssam }
9425665Ssam switch (type) {
9525665Ssam
9625665Ssam case DOUBLE:
9725665Ssam d_res.dd = (*fp->fpe_dfunc)(acc_most, acc_least, op_most,
9825665Ssam op_least, &hfs);
9925665Ssam if (d_res.di[0] == 0 && d_res.di[1] == 0)
10025665Ssam locr0[PS] |= PSL_Z;
10125665Ssam if (d_res.di[0] < 0)
10225665Ssam locr0[PS] |= PSL_N;
10325665Ssam break;
10425665Ssam
10525665Ssam case FLOAT:
10625665Ssam f_res.ff = (*fp->fpe_ffunc)(acc_most, acc_least, op_most,
10725665Ssam op_least, &hfs);
10825665Ssam if (f_res.fi == 0)
10925665Ssam locr0[PS] |= PSL_Z;
11025665Ssam if (f_res.fi == 0)
11125665Ssam locr0[PS] |= PSL_N;
11225665Ssam break;
11325665Ssam }
11425665Ssam if (hfs & HFS_OVF) {
11525665Ssam locr0[PS] |= PSL_V; /* turn on overflow bit */
11625665Ssam #ifdef notdef
11725665Ssam if (locr0[PS] & PSL_IV) { /* overflow enabled? */
11825665Ssam #endif
11925665Ssam opcode = OVF_EXC;
12043385Smckusick return ((hfs & HFS_DOM) ? EDOM : ERANGE);
12125665Ssam #ifdef notdef
12225665Ssam }
12325665Ssam #endif
12425665Ssam } else if (hfs & HFS_UNDF) {
12525665Ssam if (locr0[PS] & PSL_FU) { /* underflow enabled? */
12625665Ssam opcode = UNDF_EXC;
12743385Smckusick return ((hfs & HFS_DOM) ? EDOM : ERANGE);
12825665Ssam }
12925665Ssam } else if (hfs & HFS_DIVZ) {
13025665Ssam opcode = DIV0_EXC;
13143385Smckusick return (0);
13225665Ssam } else if (hfs & HFS_DOM)
13343385Smckusick error = EDOM;
13425665Ssam else if (hfs & HFS_RANGE)
13543385Smckusick error = ERANGE;
13625665Ssam switch (type) {
13725665Ssam
13825665Ssam case DOUBLE:
13925665Ssam if (hfs & (HFS_OVF|HFS_UNDF)) {
14025665Ssam d_res.dd = 0.0;
14125665Ssam locr0[PS] |= PSL_Z;
14225665Ssam }
14325665Ssam mvtodacc(d_res.di[0], d_res.di[1], &acc_most);
14425665Ssam break;
14525665Ssam
14625665Ssam case FLOAT:
14725665Ssam if (hfs & (HFS_OVF|HFS_UNDF)) {
14825665Ssam f_res.ff = 0.0;
14925665Ssam locr0[PS] |= PSL_Z;
15025665Ssam }
15125665Ssam mvtofacc(f_res.ff, &acc_most);
15225665Ssam break;
15325665Ssam }
15425665Ssam opcode = 0;
15543385Smckusick return (error);
15625665Ssam }
157