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