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 */
75void
76fpu_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 */
134u_int
135fpu_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 */
146void
147fpu_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
209done:
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
301completed:
302	if (rd != 0)
303		frame->tf_r[rd] = cc;
304}
305