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