xref: /openbsd-src/sys/arch/m88k/m88k/fpu.c (revision b8658fd18e97d3dff1d552af2014eb3b9282edae)
1 /*	$OpenBSD: fpu.c,v 1.4 2021/09/24 14:37:56 aoyama Exp $	*/
2 
3 /*
4  * Copyright (c) 2007, 2014, Miodrag Vallat.
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice, this permission notice, and the disclaimer below
9  * appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /*
21  * Common bits between the 88100 and the 88110 floating point completion
22  * code.
23  */
24 
25 #include <sys/param.h>
26 #include <sys/proc.h>
27 
28 #include <machine/fpu.h>
29 #include <machine/frame.h>
30 #include <machine/ieeefp.h>
31 
32 #include <lib/libkern/softfloat.h>
33 
34 #include <m88k/m88k/fpu.h>
35 
36 /*
37  * Values for individual bits in fcmp results.
38  */
39 #define	CC_UN	0x00000001	/* unordered */
40 #define	CC_LEG	0x00000002	/* less than, equal or greater than */
41 #define	CC_EQ	0x00000004	/* equal */
42 #define	CC_NE	0x00000008	/* not equal */
43 #define	CC_GT	0x00000010	/* greater than */
44 #define	CC_LE	0x00000020	/* less than or equal */
45 #define	CC_LT	0x00000040	/* less than */
46 #define	CC_GE	0x00000080	/* greater than or equal */
47 #define	CC_OU	0x00000100	/* out of range */
48 #define	CC_IB	0x00000200	/* in range or on boundary */
49 #define	CC_IN	0x00000400	/* in range */
50 #define	CC_OB	0x00000800	/* out of range or on boundary */
51 /* the following only on 88110 */
52 #define	CC_UE	0x00001000	/* unordered or equal */
53 #define	CC_LG	0x00002000	/* less than or greater than */
54 #define	CC_UG	0x00004000	/* unordered or greater than */
55 #define	CC_ULE	0x00008000	/* unordered or less than or equal */
56 #define	CC_UL	0x00010000	/* unordered or less than */
57 #define	CC_UGE	0x00020000	/* unordered or greater than or equal */
58 
59 /*
60  * Inlines from softfloat-specialize.h which are not made public, needed
61  * for fpu_compare.
62  */
63 #define	float32_is_nan(a) \
64 	(0xff000000 < (a << 1))
65 #define	float32_is_signaling_nan(a) \
66 	((((a >> 22) & 0x1ff) == 0x1fe) && (a & 0x003fffff))
67 
68 /*
69  * Store a floating-point result, converting it to the required format if it
70  * is of smaller precision.
71  *
72  * This assumes the original format (orig_width) is not FTYPE_INT, and the
73  * final format (width) <= orig_width.
74  */
75 void
fpu_store(struct trapframe * frame,u_int regno,u_int orig_width,u_int width,fparg * src)76 fpu_store(struct trapframe *frame, u_int regno, u_int orig_width, u_int width,
77     fparg *src)
78 {
79 	u_int32_t tmp;
80 	u_int rd;
81 
82 	switch (width) {
83 	case FTYPE_INT:
84 		rd = float_get_round(frame->tf_fpcr);
85 		switch (orig_width) {
86 		case FTYPE_SNG:
87 			if (rd == FP_RZ)
88 				tmp = float32_to_int32_round_to_zero(src->sng);
89 			else
90 				tmp = float32_to_int32(src->sng);
91 			break;
92 		case FTYPE_DBL:
93 			if (rd == FP_RZ)
94 				tmp = float64_to_int32_round_to_zero(src->dbl);
95 			else
96 				tmp = float64_to_int32(src->dbl);
97 			break;
98 		}
99 		if (regno != 0)
100 			frame->tf_r[regno] = tmp;
101 		break;
102 	case FTYPE_SNG:
103 		switch (orig_width) {
104 		case FTYPE_SNG:
105 			tmp = src->sng;
106 			break;
107 		case FTYPE_DBL:
108 			tmp = float64_to_float32(src->dbl);
109 			break;
110 		}
111 		if (regno != 0)
112 			frame->tf_r[regno] = tmp;
113 		break;
114 	case FTYPE_DBL:
115 		switch (orig_width) {
116 		case FTYPE_DBL:
117 			tmp = (u_int32_t)(src->dbl >> 32);
118 			if (regno != 0)
119 				frame->tf_r[regno] = tmp;
120 			tmp = (u_int32_t)src->dbl;
121 			if (regno != 31)
122 				frame->tf_r[regno + 1] = tmp;
123 			break;
124 		}
125 		break;
126 	}
127 }
128 
129 /*
130  * Return the largest precision of all precision inputs.
131  *
132  * This assumes none of the inputs is FTYPE_INT.
133  */
134 u_int
fpu_precision(u_int ts1,u_int ts2,u_int td)135 fpu_precision(u_int ts1, u_int ts2, u_int td)
136 {
137 	return max(td, max(ts1, ts2));
138 }
139 
140 /*
141  * Perform a compare instruction (fcmp, fcmpu).
142  *
143  * If either operand is NaN, the result is unordered.  This causes an
144  * reserved operand exception (except for nonsignalling NaNs for fcmpu).
145  */
146 void
fpu_compare(struct trapframe * frame,fparg * s1,fparg * s2,u_int width,u_int rd,u_int fcmpu)147 fpu_compare(struct trapframe *frame, fparg *s1, fparg *s2, u_int width,
148     u_int rd, u_int fcmpu)
149 {
150 	u_int32_t cc;
151 	int zero, s1positive, s2positive;
152 
153 	/*
154 	 * Handle NaNs first, and raise invalid if fcmp or signaling NaN.
155 	 */
156 	switch (width) {
157 	case FTYPE_SNG:
158 		if (float32_is_nan(s1->sng)) {
159 			if (!fcmpu || float32_is_signaling_nan(s1->sng))
160 				float_set_invalid();
161 			cc = CC_UN;
162 			goto done;
163 		}
164 		if (float32_is_nan(s2->sng)) {
165 			if (!fcmpu || float32_is_signaling_nan(s2->sng))
166 				float_set_invalid();
167 			cc = CC_UN;
168 			goto done;
169 		}
170 		break;
171 	case FTYPE_DBL:
172 		if (float64_is_nan(s1->dbl)) {
173 			if (!fcmpu || float64_is_signaling_nan(s1->dbl))
174 				float_set_invalid();
175 			cc = CC_UN;
176 			goto done;
177 		}
178 		if (float64_is_nan(s2->dbl)) {
179 			if (!fcmpu || float64_is_signaling_nan(s2->dbl))
180 				float_set_invalid();
181 			cc = CC_UN;
182 			goto done;
183 		}
184 		break;
185 	}
186 
187 	/*
188 	 * Now order the two numbers.
189 	 */
190 	switch (width) {
191 	case FTYPE_SNG:
192 		if (float32_eq(s1->sng, s2->sng))
193 			cc = CC_EQ;
194 		else if (float32_lt(s1->sng, s2->sng))
195 			cc = CC_LT | CC_NE;
196 		else
197 			cc = CC_GT | CC_NE;
198 		break;
199 	case FTYPE_DBL:
200 		if (float64_eq(s1->dbl, s2->dbl))
201 			cc = CC_EQ;
202 		else if (float64_lt(s1->dbl, s2->dbl))
203 			cc = CC_LT | CC_NE;
204 		else
205 			cc = CC_GT | CC_NE;
206 		break;
207 	}
208 
209 done:
210 
211 	/*
212 	 * Complete condition code mask.
213 	 */
214 
215 	if (cc & CC_UN)
216 		cc |= CC_NE | CC_UE | CC_UG | CC_ULE | CC_UL | CC_UGE;
217 	if (cc & CC_EQ)
218 		cc |= CC_LE | CC_GE | CC_UE;
219 	if (cc & CC_GT)
220 		cc |= CC_GE;
221 	if (cc & CC_LT)
222 		cc |= CC_LE;
223 	if (cc & (CC_LT | CC_GT))
224 		cc |= CC_LG;
225 	if (cc & (CC_LT | CC_GT | CC_EQ))
226 		cc |= CC_LEG;
227 	if (cc & CC_GT)
228 		cc |= CC_UG;
229 	if (cc & CC_LE)
230 		cc |= CC_ULE;
231 	if (cc & CC_LT)
232 		cc |= CC_UL;
233 	if (cc & CC_GE)
234 		cc |= CC_UGE;
235 
236 #ifdef M88100
237 	if (CPU_IS88100) {
238 		cc &= ~(CC_UE | CC_LG | CC_UG | CC_ULE | CC_UL | CC_UGE);
239 	}
240 #endif
241 
242 	/*
243 	 * Fill the interval bits.
244 	 * s1 is compared to the interval [0, s2] unless s2 is negative.
245 	 */
246 	if (!(cc & CC_UN)) {
247 		switch (width) {
248 		case FTYPE_SNG:
249 			s2positive = s2->sng >> 31 == 0;
250 			break;
251 		case FTYPE_DBL:
252 			s2positive = s2->dbl >> 63 == 0;
253 			break;
254 		}
255 		if (!s2positive)
256 			goto completed;
257 
258 		if (cc & CC_EQ) {
259 			/* if s1 and s2 are equal, s1 is on boundary */
260 			cc |= CC_IB | CC_OB;
261 			goto completed;
262 		}
263 
264 		/* s1 and s2 are either Zero, numbers or Inf */
265 		switch (width) {
266 		case FTYPE_SNG:
267 			zero = float32_eq(s1->sng, 0);
268 			break;
269 		case FTYPE_DBL:
270 			zero = float64_eq(s1->dbl, 0LL);
271 			break;
272 		}
273 		if (zero) {
274 			/* if s1 is zero, it is on boundary */
275 			cc |= CC_IB | CC_OB;
276 			goto completed;
277 		}
278 
279 		if (cc & CC_GT) {
280 			/* 0 <= s2 < s1 -> out of interval */
281 			cc |= CC_OU | CC_OB;
282 		} else {
283 			switch (width) {
284 			case FTYPE_SNG:
285 				s1positive = s1->sng >> 31 == 0;
286 				break;
287 			case FTYPE_DBL:
288 				s1positive = s1->dbl >> 63 == 0;
289 				break;
290 			}
291 			if (s1positive) {
292 				/* 0 < s1 < s2 -> in interval */
293 				cc |= CC_IB | CC_IN;
294 			} else {
295 				/* s1 < 0 <= s2 */
296 				cc |= CC_OU | CC_OB;
297 			}
298 		}
299 	}
300 
301 completed:
302 	if (rd != 0)
303 		frame->tf_r[rd] = cc;
304 }
305