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