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