1*45701Sbostic /* fpe.c 1.4 90/12/04 */ 225665Ssam 3*45701Sbostic #include "../include/psl.h" 4*45701Sbostic #include "../include/reg.h" 5*45701Sbostic #include "../include/pte.h" 6*45701Sbostic #include "../include/mtpr.h" 7*45701Sbostic #include "../math/Kfp.h" 825665Ssam 9*45701Sbostic #include "sys/param.h" 10*45701Sbostic #include "sys/systm.h" 11*45701Sbostic #include "sys/user.h" 12*45701Sbostic #include "sys/proc.h" 13*45701Sbostic #include "sys/seg.h" 14*45701Sbostic #include "sys/acct.h" 15*45701Sbostic #include "sys/kernel.h" 1625665Ssam 1725665Ssam /* 1825665Ssam * Floating point emulation support. 1925665Ssam */ 2025665Ssam extern float Kcvtlf(), Kaddf(), Ksubf(), Kmulf(), Kdivf(); 2125665Ssam extern double Kcvtld(), Kaddd(), Ksubd(), Kmuld(), Kdivd(); 2225665Ssam extern float Ksinf(), Kcosf(), Katanf(), Klogf(), Ksqrtf(), Kexpf(); 2325665Ssam 2425665Ssam #define OP(dop) ((dop) &~ 01) /* precision-less version of opcode */ 2525665Ssam #define isdouble(op) ((op) & 01) /* is opcode double or float */ 2625665Ssam 2725665Ssam struct fpetab { 2825665Ssam int fpe_op; /* base opcode emulating */ 2925665Ssam float (*fpe_ffunc)(); /* float version of op */ 3025665Ssam double (*fpe_dfunc)(); /* double version of op */ 3125665Ssam } fpetab[] = { 3225665Ssam { OP(CVLD), Kcvtlf, Kcvtld }, 3325665Ssam { OP(ADDD), Kaddf, Kaddd }, 3425665Ssam { OP(SUBD), Ksubf, Ksubd }, 3525665Ssam { OP(MULD), Kmulf, Kmuld }, 3625665Ssam { OP(DIVD), Kdivf, Kdivd }, 3725665Ssam { SINF, Ksinf, 0 }, 3825665Ssam { COSF, Kcosf, 0 }, 3925665Ssam { ATANF, Katanf, 0 }, 4025665Ssam { LOGF, Klogf, 0 }, 4125665Ssam { SQRTF, Ksqrtf, 0 }, 4225665Ssam { EXPF, Kexpf, 0 }, 4325665Ssam }; 4425665Ssam #define NFPETAB (sizeof (fpetab) / sizeof (fpetab[0])) 4525665Ssam 4625665Ssam /* 4725665Ssam * Emulate the FP opcode. Update psl as necessary. 4825665Ssam * If OK, set opcode to 0, else to the FP exception #. 4925665Ssam * Not all parameter longwords are relevant, depends on opcode. 5025665Ssam * 5125665Ssam * The entry mask is set by locore.s so ALL registers are saved. 5225665Ssam * This enables FP opcodes to change user registers on return. 5325665Ssam */ 5425665Ssam /* WARNING!!!! THIS CODE MUST NOT PRODUCE ANY FLOATING POINT EXCEPTIONS */ 5525665Ssam /*ARGSUSED*/ 5625665Ssam fpemulate(hfsreg, acc_most, acc_least, dbl, op_most, op_least, opcode, pc, psl) 5725665Ssam { 5825665Ssam int r0, r1; /* must reserve space */ 5925665Ssam register int *locr0 = ((int *)&psl)-PS; 6025665Ssam register struct fpetab *fp; 6125665Ssam int hfs = 0; /* returned data about exceptions */ 6225665Ssam int type; /* opcode type, FLOAT or DOUBLE */ 6325665Ssam union { float ff; int fi; } f_res; 6425665Ssam union { double dd; int di[2]; } d_res; 6543385Smckusick int error = 0; 6625665Ssam 6725665Ssam #ifdef lint 6825665Ssam r0 = 0; r0 = r0; r1 = 0; r1 = r1; 6925665Ssam #endif 7025665Ssam type = isdouble(opcode) ? DOUBLE : FLOAT; 7125665Ssam for (fp = fpetab; fp < &fpetab[NFPETAB]; fp++) 7225665Ssam if ((opcode & 0xfe) == fp->fpe_op) 7325665Ssam break; 7425665Ssam if (type == DOUBLE) { 7525665Ssam if (fp->fpe_dfunc == 0) 7625665Ssam fp = &fpetab[NFPETAB]; 7725665Ssam else 7825665Ssam locr0[PS] &= ~PSL_DBL; 7925665Ssam } 8025665Ssam if (fp >= &fpetab[NFPETAB]) { 8125665Ssam opcode = DIV0_EXC; /* generate SIGILL - XXX */ 8243385Smckusick return (0); 8325665Ssam } 8425665Ssam switch (type) { 8525665Ssam 8625665Ssam case DOUBLE: 8725665Ssam d_res.dd = (*fp->fpe_dfunc)(acc_most, acc_least, op_most, 8825665Ssam op_least, &hfs); 8925665Ssam if (d_res.di[0] == 0 && d_res.di[1] == 0) 9025665Ssam locr0[PS] |= PSL_Z; 9125665Ssam if (d_res.di[0] < 0) 9225665Ssam locr0[PS] |= PSL_N; 9325665Ssam break; 9425665Ssam 9525665Ssam case FLOAT: 9625665Ssam f_res.ff = (*fp->fpe_ffunc)(acc_most, acc_least, op_most, 9725665Ssam op_least, &hfs); 9825665Ssam if (f_res.fi == 0) 9925665Ssam locr0[PS] |= PSL_Z; 10025665Ssam if (f_res.fi == 0) 10125665Ssam locr0[PS] |= PSL_N; 10225665Ssam break; 10325665Ssam } 10425665Ssam if (hfs & HFS_OVF) { 10525665Ssam locr0[PS] |= PSL_V; /* turn on overflow bit */ 10625665Ssam #ifdef notdef 10725665Ssam if (locr0[PS] & PSL_IV) { /* overflow enabled? */ 10825665Ssam #endif 10925665Ssam opcode = OVF_EXC; 11043385Smckusick return ((hfs & HFS_DOM) ? EDOM : ERANGE); 11125665Ssam #ifdef notdef 11225665Ssam } 11325665Ssam #endif 11425665Ssam } else if (hfs & HFS_UNDF) { 11525665Ssam if (locr0[PS] & PSL_FU) { /* underflow enabled? */ 11625665Ssam opcode = UNDF_EXC; 11743385Smckusick return ((hfs & HFS_DOM) ? EDOM : ERANGE); 11825665Ssam } 11925665Ssam } else if (hfs & HFS_DIVZ) { 12025665Ssam opcode = DIV0_EXC; 12143385Smckusick return (0); 12225665Ssam } else if (hfs & HFS_DOM) 12343385Smckusick error = EDOM; 12425665Ssam else if (hfs & HFS_RANGE) 12543385Smckusick error = ERANGE; 12625665Ssam switch (type) { 12725665Ssam 12825665Ssam case DOUBLE: 12925665Ssam if (hfs & (HFS_OVF|HFS_UNDF)) { 13025665Ssam d_res.dd = 0.0; 13125665Ssam locr0[PS] |= PSL_Z; 13225665Ssam } 13325665Ssam mvtodacc(d_res.di[0], d_res.di[1], &acc_most); 13425665Ssam break; 13525665Ssam 13625665Ssam case FLOAT: 13725665Ssam if (hfs & (HFS_OVF|HFS_UNDF)) { 13825665Ssam f_res.ff = 0.0; 13925665Ssam locr0[PS] |= PSL_Z; 14025665Ssam } 14125665Ssam mvtofacc(f_res.ff, &acc_most); 14225665Ssam break; 14325665Ssam } 14425665Ssam opcode = 0; 14543385Smckusick return (error); 14625665Ssam } 147