xref: /csrg-svn/sys/tahoe/math/fpe.c (revision 25665)
1*25665Ssam /*	fpe.c	1.1	86/01/03	*/
2*25665Ssam 
3*25665Ssam #include "../tahoe/psl.h"
4*25665Ssam #include "../tahoe/reg.h"
5*25665Ssam #include "../tahoe/pte.h"
6*25665Ssam #include "../tahoe/mtpr.h"
7*25665Ssam #include "../tahoemath/Kfp.h"
8*25665Ssam 
9*25665Ssam #include "param.h"
10*25665Ssam #include "systm.h"
11*25665Ssam #include "dir.h"
12*25665Ssam #include "user.h"
13*25665Ssam #include "proc.h"
14*25665Ssam #include "seg.h"
15*25665Ssam #include "acct.h"
16*25665Ssam #include "kernel.h"
17*25665Ssam 
18*25665Ssam /*
19*25665Ssam  * Floating point emulation support.
20*25665Ssam  */
21*25665Ssam extern	float Kcvtlf(), Kaddf(), Ksubf(), Kmulf(), Kdivf();
22*25665Ssam extern	double Kcvtld(), Kaddd(), Ksubd(), Kmuld(), Kdivd();
23*25665Ssam extern	float Ksinf(), Kcosf(), Katanf(), Klogf(), Ksqrtf(), Kexpf();
24*25665Ssam 
25*25665Ssam #define	OP(dop)		((dop) &~ 01)	/* precision-less version of opcode */
26*25665Ssam #define	isdouble(op)	((op) & 01)	/* is opcode double or float */
27*25665Ssam 
28*25665Ssam struct	fpetab {
29*25665Ssam 	int	fpe_op;		/* base opcode emulating */
30*25665Ssam 	float	(*fpe_ffunc)();	/* float version of op */
31*25665Ssam 	double	(*fpe_dfunc)();	/* double version of op */
32*25665Ssam } fpetab[] = {
33*25665Ssam 	{ OP(CVLD),	Kcvtlf,	Kcvtld },
34*25665Ssam 	{ OP(ADDD),	Kaddf,	Kaddd },
35*25665Ssam 	{ OP(SUBD),	Ksubf,	Ksubd },
36*25665Ssam 	{ OP(MULD),	Kmulf,	Kmuld },
37*25665Ssam 	{ OP(DIVD),	Kdivf,	Kdivd },
38*25665Ssam 	{ SINF,		Ksinf,	0 },
39*25665Ssam 	{ COSF,		Kcosf,	0 },
40*25665Ssam 	{ ATANF,	Katanf,	0 },
41*25665Ssam 	{ LOGF,		Klogf,	0 },
42*25665Ssam 	{ SQRTF,	Ksqrtf,	0 },
43*25665Ssam 	{ EXPF,		Kexpf,	0 },
44*25665Ssam };
45*25665Ssam #define	NFPETAB	(sizeof (fpetab) / sizeof (fpetab[0]))
46*25665Ssam 
47*25665Ssam /*
48*25665Ssam  * Emulate the FP opcode. Update psl as necessary.
49*25665Ssam  * If OK, set opcode to 0, else to the FP exception #.
50*25665Ssam  * Not all parameter longwords are relevant, depends on opcode.
51*25665Ssam  *
52*25665Ssam  * The entry mask is set by locore.s so ALL registers are saved.
53*25665Ssam  * This enables FP opcodes to change user registers on return.
54*25665Ssam  */
55*25665Ssam /* WARNING!!!! THIS CODE MUST NOT PRODUCE ANY FLOATING POINT EXCEPTIONS */
56*25665Ssam /*ARGSUSED*/
57*25665Ssam fpemulate(hfsreg, acc_most, acc_least, dbl, op_most, op_least, opcode, pc, psl)
58*25665Ssam {
59*25665Ssam 	int r0, r1;			/* must reserve space */
60*25665Ssam 	register int *locr0 = ((int *)&psl)-PS;
61*25665Ssam 	register struct fpetab *fp;
62*25665Ssam 	int hfs = 0; 			/* returned data about exceptions */
63*25665Ssam 	int type;			/* opcode type, FLOAT or DOUBLE */
64*25665Ssam 	union { float ff; int fi; } f_res;
65*25665Ssam 	union { double dd; int di[2]; } d_res;
66*25665Ssam 
67*25665Ssam #ifdef lint
68*25665Ssam 	r0 = 0; r0 = r0; r1 = 0; r1 = r1;
69*25665Ssam #endif
70*25665Ssam 	type = isdouble(opcode) ? DOUBLE : FLOAT;
71*25665Ssam 	for (fp = fpetab; fp < &fpetab[NFPETAB]; fp++)
72*25665Ssam 		if ((opcode & 0xfe) == fp->fpe_op)
73*25665Ssam 			break;
74*25665Ssam 	if (type == DOUBLE) {
75*25665Ssam 		if (fp->fpe_dfunc == 0)
76*25665Ssam 			fp = &fpetab[NFPETAB];
77*25665Ssam 		else
78*25665Ssam 			locr0[PS] &= ~PSL_DBL;
79*25665Ssam 	}
80*25665Ssam 	if (fp >= &fpetab[NFPETAB]) {
81*25665Ssam 		opcode = DIV0_EXC;	/* generate SIGILL - XXX */
82*25665Ssam 		return;
83*25665Ssam 	}
84*25665Ssam 	switch (type) {
85*25665Ssam 
86*25665Ssam 	case DOUBLE:
87*25665Ssam 		d_res.dd = (*fp->fpe_dfunc)(acc_most, acc_least, op_most,
88*25665Ssam 		    op_least, &hfs);
89*25665Ssam 		if (d_res.di[0] == 0 && d_res.di[1] == 0)
90*25665Ssam 			locr0[PS] |= PSL_Z;
91*25665Ssam 		if (d_res.di[0] < 0)
92*25665Ssam 			locr0[PS] |= PSL_N;
93*25665Ssam 		break;
94*25665Ssam 
95*25665Ssam 	case FLOAT:
96*25665Ssam 		f_res.ff = (*fp->fpe_ffunc)(acc_most, acc_least, op_most,
97*25665Ssam 		    op_least, &hfs);
98*25665Ssam 		if (f_res.fi == 0)
99*25665Ssam 			locr0[PS] |= PSL_Z;
100*25665Ssam 		if (f_res.fi ==  0)
101*25665Ssam 			locr0[PS] |= PSL_N;
102*25665Ssam 		break;
103*25665Ssam 	}
104*25665Ssam 	if (hfs & HFS_OVF) {
105*25665Ssam 		locr0[PS] |= PSL_V;	/* turn on overflow bit */
106*25665Ssam #ifdef notdef
107*25665Ssam 		if (locr0[PS] & PSL_IV)   {  /* overflow enabled? */
108*25665Ssam #endif
109*25665Ssam 			opcode = OVF_EXC;
110*25665Ssam 			u.u_error = (hfs & HFS_DOM) ? EDOM : ERANGE;
111*25665Ssam 			return;
112*25665Ssam #ifdef notdef
113*25665Ssam 		}
114*25665Ssam #endif
115*25665Ssam 	} else if (hfs & HFS_UNDF) {
116*25665Ssam 		if (locr0[PS] &  PSL_FU) {  /* underflow enabled? */
117*25665Ssam 			opcode = UNDF_EXC;
118*25665Ssam 			u.u_error = (hfs & HFS_DOM) ? EDOM : ERANGE;
119*25665Ssam 			return;
120*25665Ssam 		}
121*25665Ssam 	} else if (hfs & HFS_DIVZ) {
122*25665Ssam 		opcode = DIV0_EXC;
123*25665Ssam 		return;
124*25665Ssam 	} else if (hfs & HFS_DOM)
125*25665Ssam 		u.u_error = EDOM;
126*25665Ssam 	else if (hfs & HFS_RANGE)
127*25665Ssam 		u.u_error = ERANGE;
128*25665Ssam 	switch (type) {
129*25665Ssam 
130*25665Ssam 	case DOUBLE:
131*25665Ssam 		if (hfs & (HFS_OVF|HFS_UNDF)) {
132*25665Ssam 			d_res.dd = 0.0;
133*25665Ssam 			locr0[PS] |= PSL_Z;
134*25665Ssam 		}
135*25665Ssam 		mvtodacc(d_res.di[0], d_res.di[1], &acc_most);
136*25665Ssam 		break;
137*25665Ssam 
138*25665Ssam 	case FLOAT:
139*25665Ssam 		if (hfs & (HFS_OVF|HFS_UNDF)) {
140*25665Ssam 			f_res.ff = 0.0;
141*25665Ssam 			locr0[PS] |= PSL_Z;
142*25665Ssam 		}
143*25665Ssam 		mvtofacc(f_res.ff, &acc_most);
144*25665Ssam 		break;
145*25665Ssam 	}
146*25665Ssam 	opcode = 0;
147*25665Ssam }
148