Merge tag 'v3.3.7' into 3.3/master
[zen-stable.git] / arch / arm / nwfpe / fpa11_cprt.c
blob31c4eeec18b07654fe925eca4be95861e262f87f
1 /*
2 NetWinder Floating Point Emulator
3 (c) Rebel.COM, 1998,1999
4 (c) Philip Blundell, 1999, 2001
6 Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
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.
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.
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.
23 #include "fpa11.h"
24 #include "fpopcode.h"
25 #include "fpa11.inl"
26 #include "fpmodule.h"
27 #include "fpmodule.inl"
28 #include "softfloat.h"
30 unsigned int PerformFLT(const unsigned int opcode);
31 unsigned int PerformFIX(const unsigned int opcode);
33 static unsigned int PerformComparison(const unsigned int opcode);
35 unsigned int EmulateCPRT(const unsigned int opcode)
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);
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;
55 case WFS_CODE >> 20:
56 writeFPSR(readRegister(getRd(opcode)));
57 break;
58 case RFS_CODE >> 20:
59 writeRegister(getRd(opcode), readFPSR());
60 break;
62 default:
63 return 0;
66 return 1;
69 unsigned int PerformFLT(const unsigned int opcode)
71 FPA11 *fpa11 = GET_FPA11();
72 struct roundingData roundData;
74 roundData.mode = SetRoundingMode(opcode);
75 roundData.precision = SetRoundingPrecision(opcode);
76 roundData.exception = 0;
78 switch (opcode & MASK_ROUNDING_PRECISION) {
79 case ROUND_SINGLE:
81 fpa11->fType[getFn(opcode)] = typeSingle;
82 fpa11->fpreg[getFn(opcode)].fSingle = int32_to_float32(&roundData, readRegister(getRd(opcode)));
84 break;
86 case ROUND_DOUBLE:
88 fpa11->fType[getFn(opcode)] = typeDouble;
89 fpa11->fpreg[getFn(opcode)].fDouble = int32_to_float64(readRegister(getRd(opcode)));
91 break;
93 #ifdef CONFIG_FPE_NWFPE_XP
94 case ROUND_EXTENDED:
96 fpa11->fType[getFn(opcode)] = typeExtended;
97 fpa11->fpreg[getFn(opcode)].fExtended = int32_to_floatx80(readRegister(getRd(opcode)));
99 break;
100 #endif
102 default:
103 return 0;
106 if (roundData.exception)
107 float_raise(roundData.exception);
109 return 1;
112 unsigned int PerformFIX(const unsigned int opcode)
114 FPA11 *fpa11 = GET_FPA11();
115 unsigned int Fn = getFm(opcode);
116 struct roundingData roundData;
118 roundData.mode = SetRoundingMode(opcode);
119 roundData.precision = SetRoundingPrecision(opcode);
120 roundData.exception = 0;
122 switch (fpa11->fType[Fn]) {
123 case typeSingle:
125 writeRegister(getRd(opcode), float32_to_int32(&roundData, fpa11->fpreg[Fn].fSingle));
127 break;
129 case typeDouble:
131 writeRegister(getRd(opcode), float64_to_int32(&roundData, fpa11->fpreg[Fn].fDouble));
133 break;
135 #ifdef CONFIG_FPE_NWFPE_XP
136 case typeExtended:
138 writeRegister(getRd(opcode), floatx80_to_int32(&roundData, fpa11->fpreg[Fn].fExtended));
140 break;
141 #endif
143 default:
144 return 0;
147 if (roundData.exception)
148 float_raise(roundData.exception);
150 return 1;
153 /* This instruction sets the flags N, Z, C, V in the FPSR. */
154 static unsigned int PerformComparison(const unsigned int opcode)
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;
162 #ifdef CONFIG_FPE_NWFPE_XP
163 floatx80 rFn, rFm;
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;
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;
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;
192 default:
193 return 0;
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;
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;
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;
225 default:
226 return 0;
230 if (n_flag)
231 rFm.high ^= 0x8000;
233 /* test for less than condition */
234 if (floatx80_lt(rFn, rFm))
235 flags |= CC_NEGATIVE;
237 /* test for equal condition */
238 if (floatx80_eq(rFn, rFm))
239 flags |= CC_ZERO;
241 /* test for greater than or equal condition */
242 if (floatx80_lt(rFm, rFn))
243 flags |= CC_CARRY;
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;
253 if (float32_is_nan(rFn))
254 goto unordered;
256 if (n_flag)
257 rFm ^= 0x80000000;
259 /* test for less than condition */
260 if (float32_lt_nocheck(rFn, rFm))
261 flags |= CC_NEGATIVE;
263 /* test for equal condition */
264 if (float32_eq_nocheck(rFn, rFm))
265 flags |= CC_ZERO;
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;
274 if (float64_is_nan(rFn))
275 goto unordered;
277 if (n_flag)
278 rFm ^= 0x8000000000000000ULL;
280 /* test for less than condition */
281 if (float64_lt_nocheck(rFn, rFm))
282 flags |= CC_NEGATIVE;
284 /* test for equal condition */
285 if (float64_eq_nocheck(rFn, rFm))
286 flags |= CC_ZERO;
288 /* test for greater than or equal condition */
289 if (float64_lt_nocheck(rFm, rFn))
290 flags |= CC_CARRY;
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;
299 if (float32_is_nan(rFn)
300 || float32_is_nan(rFm))
301 goto unordered;
303 if (n_flag)
304 rFm ^= 0x80000000;
306 /* test for less than condition */
307 if (float32_lt_nocheck(rFn, rFm))
308 flags |= CC_NEGATIVE;
310 /* test for equal condition */
311 if (float32_eq_nocheck(rFn, rFm))
312 flags |= CC_ZERO;
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;
321 rFm = (fpa11->fType[Fm] == typeSingle) ?
322 float32_to_float64(fpa11->fpreg[Fm].fSingle)
323 : fpa11->fpreg[Fm].fDouble;
325 rFn = (fpa11->fType[Fn] == typeSingle) ?
326 float32_to_float64(fpa11->fpreg[Fn].fSingle)
327 : fpa11->fpreg[Fn].fDouble;
329 if (float64_is_nan(rFn)
330 || float64_is_nan(rFm))
331 goto unordered;
333 if (n_flag)
334 rFm ^= 0x8000000000000000ULL;
336 /* test for less than condition */
337 if (float64_lt_nocheck(rFn, rFm))
338 flags |= CC_NEGATIVE;
340 /* test for equal condition */
341 if (float64_eq_nocheck(rFn, rFm))
342 flags |= CC_ZERO;
344 /* test for greater than or equal condition */
345 if (float64_lt_nocheck(rFm, rFn))
346 flags |= CC_CARRY;
350 #endif
352 writeConditionCodes(flags);
354 return 1;
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);
365 if (BIT_AC & readFPSR())
366 flags |= CC_CARRY;
368 if (e_flag)
369 float_raise(float_flag_invalid);
371 writeConditionCodes(flags);
372 return 1;