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