xref: /csrg-svn/sys/tahoe/math/fpe.c (revision 45701)
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