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