xref: /openbmc/linux/arch/arm/nwfpe/fpa11_cprt.c (revision 22d55f02)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3     NetWinder Floating Point Emulator
4     (c) Rebel.COM, 1998,1999
5     (c) Philip Blundell, 1999, 2001
6 
7     Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
8 
9 */
10 
11 #include "fpa11.h"
12 #include "fpopcode.h"
13 #include "fpa11.inl"
14 #include "fpmodule.h"
15 #include "fpmodule.inl"
16 #include "softfloat.h"
17 
18 unsigned int PerformFLT(const unsigned int opcode);
19 unsigned int PerformFIX(const unsigned int opcode);
20 
21 static unsigned int PerformComparison(const unsigned int opcode);
22 
23 unsigned int EmulateCPRT(const unsigned int opcode)
24 {
25 
26 	if (opcode & 0x800000) {
27 		/* This is some variant of a comparison (PerformComparison
28 		   will sort out which one).  Since most of the other CPRT
29 		   instructions are oddball cases of some sort or other it
30 		   makes sense to pull this out into a fast path.  */
31 		return PerformComparison(opcode);
32 	}
33 
34 	/* Hint to GCC that we'd like a jump table rather than a load of CMPs */
35 	switch ((opcode & 0x700000) >> 20) {
36 	case FLT_CODE >> 20:
37 		return PerformFLT(opcode);
38 		break;
39 	case FIX_CODE >> 20:
40 		return PerformFIX(opcode);
41 		break;
42 
43 	case WFS_CODE >> 20:
44 		writeFPSR(readRegister(getRd(opcode)));
45 		break;
46 	case RFS_CODE >> 20:
47 		writeRegister(getRd(opcode), readFPSR());
48 		break;
49 
50 	default:
51 		return 0;
52 	}
53 
54 	return 1;
55 }
56 
57 unsigned int PerformFLT(const unsigned int opcode)
58 {
59 	FPA11 *fpa11 = GET_FPA11();
60 	struct roundingData roundData;
61 
62 	roundData.mode = SetRoundingMode(opcode);
63 	roundData.precision = SetRoundingPrecision(opcode);
64 	roundData.exception = 0;
65 
66 	switch (opcode & MASK_ROUNDING_PRECISION) {
67 	case ROUND_SINGLE:
68 		{
69 			fpa11->fType[getFn(opcode)] = typeSingle;
70 			fpa11->fpreg[getFn(opcode)].fSingle = int32_to_float32(&roundData, readRegister(getRd(opcode)));
71 		}
72 		break;
73 
74 	case ROUND_DOUBLE:
75 		{
76 			fpa11->fType[getFn(opcode)] = typeDouble;
77 			fpa11->fpreg[getFn(opcode)].fDouble = int32_to_float64(readRegister(getRd(opcode)));
78 		}
79 		break;
80 
81 #ifdef CONFIG_FPE_NWFPE_XP
82 	case ROUND_EXTENDED:
83 		{
84 			fpa11->fType[getFn(opcode)] = typeExtended;
85 			fpa11->fpreg[getFn(opcode)].fExtended = int32_to_floatx80(readRegister(getRd(opcode)));
86 		}
87 		break;
88 #endif
89 
90 	default:
91 		return 0;
92 	}
93 
94 	if (roundData.exception)
95 		float_raise(roundData.exception);
96 
97 	return 1;
98 }
99 
100 unsigned int PerformFIX(const unsigned int opcode)
101 {
102 	FPA11 *fpa11 = GET_FPA11();
103 	unsigned int Fn = getFm(opcode);
104 	struct roundingData roundData;
105 
106 	roundData.mode = SetRoundingMode(opcode);
107 	roundData.precision = SetRoundingPrecision(opcode);
108 	roundData.exception = 0;
109 
110 	switch (fpa11->fType[Fn]) {
111 	case typeSingle:
112 		{
113 			writeRegister(getRd(opcode), float32_to_int32(&roundData, fpa11->fpreg[Fn].fSingle));
114 		}
115 		break;
116 
117 	case typeDouble:
118 		{
119 			writeRegister(getRd(opcode), float64_to_int32(&roundData, fpa11->fpreg[Fn].fDouble));
120 		}
121 		break;
122 
123 #ifdef CONFIG_FPE_NWFPE_XP
124 	case typeExtended:
125 		{
126 			writeRegister(getRd(opcode), floatx80_to_int32(&roundData, fpa11->fpreg[Fn].fExtended));
127 		}
128 		break;
129 #endif
130 
131 	default:
132 		return 0;
133 	}
134 
135 	if (roundData.exception)
136 		float_raise(roundData.exception);
137 
138 	return 1;
139 }
140 
141 /* This instruction sets the flags N, Z, C, V in the FPSR. */
142 static unsigned int PerformComparison(const unsigned int opcode)
143 {
144 	FPA11 *fpa11 = GET_FPA11();
145 	unsigned int Fn = getFn(opcode), Fm = getFm(opcode);
146 	int e_flag = opcode & 0x400000;	/* 1 if CxFE */
147 	int n_flag = opcode & 0x200000;	/* 1 if CNxx */
148 	unsigned int flags = 0;
149 
150 #ifdef CONFIG_FPE_NWFPE_XP
151 	floatx80 rFn, rFm;
152 
153 	/* Check for unordered condition and convert all operands to 80-bit
154 	   format.
155 	   ?? Might be some mileage in avoiding this conversion if possible.
156 	   Eg, if both operands are 32-bit, detect this and do a 32-bit
157 	   comparison (cheaper than an 80-bit one).  */
158 	switch (fpa11->fType[Fn]) {
159 	case typeSingle:
160 		//printk("single.\n");
161 		if (float32_is_nan(fpa11->fpreg[Fn].fSingle))
162 			goto unordered;
163 		rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle);
164 		break;
165 
166 	case typeDouble:
167 		//printk("double.\n");
168 		if (float64_is_nan(fpa11->fpreg[Fn].fDouble))
169 			goto unordered;
170 		rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble);
171 		break;
172 
173 	case typeExtended:
174 		//printk("extended.\n");
175 		if (floatx80_is_nan(fpa11->fpreg[Fn].fExtended))
176 			goto unordered;
177 		rFn = fpa11->fpreg[Fn].fExtended;
178 		break;
179 
180 	default:
181 		return 0;
182 	}
183 
184 	if (CONSTANT_FM(opcode)) {
185 		//printk("Fm is a constant: #%d.\n",Fm);
186 		rFm = getExtendedConstant(Fm);
187 		if (floatx80_is_nan(rFm))
188 			goto unordered;
189 	} else {
190 		//printk("Fm = r%d which contains a ",Fm);
191 		switch (fpa11->fType[Fm]) {
192 		case typeSingle:
193 			//printk("single.\n");
194 			if (float32_is_nan(fpa11->fpreg[Fm].fSingle))
195 				goto unordered;
196 			rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle);
197 			break;
198 
199 		case typeDouble:
200 			//printk("double.\n");
201 			if (float64_is_nan(fpa11->fpreg[Fm].fDouble))
202 				goto unordered;
203 			rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble);
204 			break;
205 
206 		case typeExtended:
207 			//printk("extended.\n");
208 			if (floatx80_is_nan(fpa11->fpreg[Fm].fExtended))
209 				goto unordered;
210 			rFm = fpa11->fpreg[Fm].fExtended;
211 			break;
212 
213 		default:
214 			return 0;
215 		}
216 	}
217 
218 	if (n_flag)
219 		rFm.high ^= 0x8000;
220 
221 	/* test for less than condition */
222 	if (floatx80_lt(rFn, rFm))
223 		flags |= CC_NEGATIVE;
224 
225 	/* test for equal condition */
226 	if (floatx80_eq(rFn, rFm))
227 		flags |= CC_ZERO;
228 
229 	/* test for greater than or equal condition */
230 	if (floatx80_lt(rFm, rFn))
231 		flags |= CC_CARRY;
232 
233 #else
234 	if (CONSTANT_FM(opcode)) {
235 		/* Fm is a constant.  Do the comparison in whatever precision
236 		   Fn happens to be stored in.  */
237 		if (fpa11->fType[Fn] == typeSingle) {
238 			float32 rFm = getSingleConstant(Fm);
239 			float32 rFn = fpa11->fpreg[Fn].fSingle;
240 
241 			if (float32_is_nan(rFn))
242 				goto unordered;
243 
244 			if (n_flag)
245 				rFm ^= 0x80000000;
246 
247 			/* test for less than condition */
248 			if (float32_lt_nocheck(rFn, rFm))
249 				flags |= CC_NEGATIVE;
250 
251 			/* test for equal condition */
252 			if (float32_eq_nocheck(rFn, rFm))
253 				flags |= CC_ZERO;
254 
255 			/* test for greater than or equal condition */
256 			if (float32_lt_nocheck(rFm, rFn))
257 				flags |= CC_CARRY;
258 		} else {
259 			float64 rFm = getDoubleConstant(Fm);
260 			float64 rFn = fpa11->fpreg[Fn].fDouble;
261 
262 			if (float64_is_nan(rFn))
263 				goto unordered;
264 
265 			if (n_flag)
266 				rFm ^= 0x8000000000000000ULL;
267 
268 			/* test for less than condition */
269 			if (float64_lt_nocheck(rFn, rFm))
270 				flags |= CC_NEGATIVE;
271 
272 			/* test for equal condition */
273 			if (float64_eq_nocheck(rFn, rFm))
274 				flags |= CC_ZERO;
275 
276 			/* test for greater than or equal condition */
277 			if (float64_lt_nocheck(rFm, rFn))
278 				flags |= CC_CARRY;
279 		}
280 	} else {
281 		/* Both operands are in registers.  */
282 		if (fpa11->fType[Fn] == typeSingle
283 		    && fpa11->fType[Fm] == typeSingle) {
284 			float32 rFm = fpa11->fpreg[Fm].fSingle;
285 			float32 rFn = fpa11->fpreg[Fn].fSingle;
286 
287 			if (float32_is_nan(rFn)
288 			    || float32_is_nan(rFm))
289 				goto unordered;
290 
291 			if (n_flag)
292 				rFm ^= 0x80000000;
293 
294 			/* test for less than condition */
295 			if (float32_lt_nocheck(rFn, rFm))
296 				flags |= CC_NEGATIVE;
297 
298 			/* test for equal condition */
299 			if (float32_eq_nocheck(rFn, rFm))
300 				flags |= CC_ZERO;
301 
302 			/* test for greater than or equal condition */
303 			if (float32_lt_nocheck(rFm, rFn))
304 				flags |= CC_CARRY;
305 		} else {
306 			/* Promote 32-bit operand to 64 bits.  */
307 			float64 rFm, rFn;
308 
309 			rFm = (fpa11->fType[Fm] == typeSingle) ?
310 			    float32_to_float64(fpa11->fpreg[Fm].fSingle)
311 			    : fpa11->fpreg[Fm].fDouble;
312 
313 			rFn = (fpa11->fType[Fn] == typeSingle) ?
314 			    float32_to_float64(fpa11->fpreg[Fn].fSingle)
315 			    : fpa11->fpreg[Fn].fDouble;
316 
317 			if (float64_is_nan(rFn)
318 			    || float64_is_nan(rFm))
319 				goto unordered;
320 
321 			if (n_flag)
322 				rFm ^= 0x8000000000000000ULL;
323 
324 			/* test for less than condition */
325 			if (float64_lt_nocheck(rFn, rFm))
326 				flags |= CC_NEGATIVE;
327 
328 			/* test for equal condition */
329 			if (float64_eq_nocheck(rFn, rFm))
330 				flags |= CC_ZERO;
331 
332 			/* test for greater than or equal condition */
333 			if (float64_lt_nocheck(rFm, rFn))
334 				flags |= CC_CARRY;
335 		}
336 	}
337 
338 #endif
339 
340 	writeConditionCodes(flags);
341 
342 	return 1;
343 
344       unordered:
345 	/* ?? The FPA data sheet is pretty vague about this, in particular
346 	   about whether the non-E comparisons can ever raise exceptions.
347 	   This implementation is based on a combination of what it says in
348 	   the data sheet, observation of how the Acorn emulator actually
349 	   behaves (and how programs expect it to) and guesswork.  */
350 	flags |= CC_OVERFLOW;
351 	flags &= ~(CC_ZERO | CC_NEGATIVE);
352 
353 	if (BIT_AC & readFPSR())
354 		flags |= CC_CARRY;
355 
356 	if (e_flag)
357 		float_raise(float_flag_invalid);
358 
359 	writeConditionCodes(flags);
360 	return 1;
361 }
362