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