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