blob: adf8d3000540f9c6f774024ff22d720507a2e24f [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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
31extern flag floatx80_is_nan(floatx80);
32#endif
33extern flag float64_is_nan(float64);
34extern flag float32_is_nan(float32);
35
Linus Torvalds1da177e2005-04-16 15:20:36 -070036unsigned int PerformFLT(const unsigned int opcode);
37unsigned int PerformFIX(const unsigned int opcode);
38
39static unsigned int PerformComparison(const unsigned int opcode);
40
41unsigned 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
75unsigned int PerformFLT(const unsigned int opcode)
76{
77 FPA11 *fpa11 = GET_FPA11();
Richard Purdief148af22005-08-03 19:49:17 +010078 struct roundingData roundData;
79
80 roundData.mode = SetRoundingMode(opcode);
81 roundData.precision = SetRoundingPrecision(opcode);
82 roundData.exception = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070083
84 switch (opcode & MASK_ROUNDING_PRECISION) {
85 case ROUND_SINGLE:
86 {
87 fpa11->fType[getFn(opcode)] = typeSingle;
Richard Purdief148af22005-08-03 19:49:17 +010088 fpa11->fpreg[getFn(opcode)].fSingle = int32_to_float32(&roundData, readRegister(getRd(opcode)));
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 }
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
Richard Purdief148af22005-08-03 19:49:17 +0100112 if (roundData.exception)
113 float_raise(roundData.exception);
114
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 return 1;
116}
117
118unsigned int PerformFIX(const unsigned int opcode)
119{
120 FPA11 *fpa11 = GET_FPA11();
121 unsigned int Fn = getFm(opcode);
Richard Purdief148af22005-08-03 19:49:17 +0100122 struct roundingData roundData;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123
Richard Purdief148af22005-08-03 19:49:17 +0100124 roundData.mode = SetRoundingMode(opcode);
125 roundData.precision = SetRoundingPrecision(opcode);
126 roundData.exception = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127
128 switch (fpa11->fType[Fn]) {
129 case typeSingle:
130 {
Richard Purdief148af22005-08-03 19:49:17 +0100131 writeRegister(getRd(opcode), float32_to_int32(&roundData, fpa11->fpreg[Fn].fSingle));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 }
133 break;
134
135 case typeDouble:
136 {
Richard Purdief148af22005-08-03 19:49:17 +0100137 writeRegister(getRd(opcode), float64_to_int32(&roundData, fpa11->fpreg[Fn].fDouble));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 }
139 break;
140
141#ifdef CONFIG_FPE_NWFPE_XP
142 case typeExtended:
143 {
Richard Purdief148af22005-08-03 19:49:17 +0100144 writeRegister(getRd(opcode), floatx80_to_int32(&roundData, fpa11->fpreg[Fn].fExtended));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 }
146 break;
147#endif
148
149 default:
150 return 0;
151 }
152
Richard Purdief148af22005-08-03 19:49:17 +0100153 if (roundData.exception)
154 float_raise(roundData.exception);
155
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 return 1;
157}
158
159/* This instruction sets the flags N, Z, C, V in the FPSR. */
160static 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}