blob: 9843dc533047d7912ed913d549d714356523cd40 [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
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#include "fpa11.h"
24#include "fpopcode.h"
25#include "fpa11.inl"
26#include "fpmodule.h"
27#include "fpmodule.inl"
Ben Dooks6ec5e7f2005-10-12 19:58:10 +010028#include "softfloat.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070029
30#ifdef CONFIG_FPE_NWFPE_XP
31extern flag floatx80_is_nan(floatx80);
32#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070033
Linus Torvalds1da177e2005-04-16 15:20:36 -070034unsigned int PerformFLT(const unsigned int opcode);
35unsigned int PerformFIX(const unsigned int opcode);
36
37static unsigned int PerformComparison(const unsigned int opcode);
38
39unsigned int EmulateCPRT(const unsigned int opcode)
40{
41
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);
48 }
49
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;
58
59 case WFS_CODE >> 20:
60 writeFPSR(readRegister(getRd(opcode)));
61 break;
62 case RFS_CODE >> 20:
63 writeRegister(getRd(opcode), readFPSR());
64 break;
65
66 default:
67 return 0;
68 }
69
70 return 1;
71}
72
73unsigned int PerformFLT(const unsigned int opcode)
74{
75 FPA11 *fpa11 = GET_FPA11();
Richard Purdief148af22005-08-03 19:49:17 +010076 struct roundingData roundData;
77
78 roundData.mode = SetRoundingMode(opcode);
79 roundData.precision = SetRoundingPrecision(opcode);
80 roundData.exception = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070081
82 switch (opcode & MASK_ROUNDING_PRECISION) {
83 case ROUND_SINGLE:
84 {
85 fpa11->fType[getFn(opcode)] = typeSingle;
Richard Purdief148af22005-08-03 19:49:17 +010086 fpa11->fpreg[getFn(opcode)].fSingle = int32_to_float32(&roundData, readRegister(getRd(opcode)));
Linus Torvalds1da177e2005-04-16 15:20:36 -070087 }
88 break;
89
90 case ROUND_DOUBLE:
91 {
92 fpa11->fType[getFn(opcode)] = typeDouble;
93 fpa11->fpreg[getFn(opcode)].fDouble = int32_to_float64(readRegister(getRd(opcode)));
94 }
95 break;
96
97#ifdef CONFIG_FPE_NWFPE_XP
98 case ROUND_EXTENDED:
99 {
100 fpa11->fType[getFn(opcode)] = typeExtended;
101 fpa11->fpreg[getFn(opcode)].fExtended = int32_to_floatx80(readRegister(getRd(opcode)));
102 }
103 break;
104#endif
105
106 default:
107 return 0;
108 }
109
Richard Purdief148af22005-08-03 19:49:17 +0100110 if (roundData.exception)
111 float_raise(roundData.exception);
112
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 return 1;
114}
115
116unsigned int PerformFIX(const unsigned int opcode)
117{
118 FPA11 *fpa11 = GET_FPA11();
119 unsigned int Fn = getFm(opcode);
Richard Purdief148af22005-08-03 19:49:17 +0100120 struct roundingData roundData;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121
Richard Purdief148af22005-08-03 19:49:17 +0100122 roundData.mode = SetRoundingMode(opcode);
123 roundData.precision = SetRoundingPrecision(opcode);
124 roundData.exception = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125
126 switch (fpa11->fType[Fn]) {
127 case typeSingle:
128 {
Richard Purdief148af22005-08-03 19:49:17 +0100129 writeRegister(getRd(opcode), float32_to_int32(&roundData, fpa11->fpreg[Fn].fSingle));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 }
131 break;
132
133 case typeDouble:
134 {
Richard Purdief148af22005-08-03 19:49:17 +0100135 writeRegister(getRd(opcode), float64_to_int32(&roundData, fpa11->fpreg[Fn].fDouble));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 }
137 break;
138
139#ifdef CONFIG_FPE_NWFPE_XP
140 case typeExtended:
141 {
Richard Purdief148af22005-08-03 19:49:17 +0100142 writeRegister(getRd(opcode), floatx80_to_int32(&roundData, fpa11->fpreg[Fn].fExtended));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 }
144 break;
145#endif
146
147 default:
148 return 0;
149 }
150
Richard Purdief148af22005-08-03 19:49:17 +0100151 if (roundData.exception)
152 float_raise(roundData.exception);
153
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 return 1;
155}
156
157/* This instruction sets the flags N, Z, C, V in the FPSR. */
158static unsigned int PerformComparison(const unsigned int opcode)
159{
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;
165
166#ifdef CONFIG_FPE_NWFPE_XP
167 floatx80 rFn, rFm;
168
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;
181
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;
188
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;
195
196 default:
197 return 0;
198 }
199
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;
214
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;
221
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;
228
229 default:
230 return 0;
231 }
232 }
233
234 if (n_flag)
235 rFm.high ^= 0x8000;
236
237 /* test for less than condition */
238 if (floatx80_lt(rFn, rFm))
239 flags |= CC_NEGATIVE;
240
241 /* test for equal condition */
242 if (floatx80_eq(rFn, rFm))
243 flags |= CC_ZERO;
244
245 /* test for greater than or equal condition */
246 if (floatx80_lt(rFm, rFn))
247 flags |= CC_CARRY;
248
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;
256
257 if (float32_is_nan(rFn))
258 goto unordered;
259
260 if (n_flag)
261 rFm ^= 0x80000000;
262
263 /* test for less than condition */
264 if (float32_lt_nocheck(rFn, rFm))
265 flags |= CC_NEGATIVE;
266
267 /* test for equal condition */
268 if (float32_eq_nocheck(rFn, rFm))
269 flags |= CC_ZERO;
270
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;
277
278 if (float64_is_nan(rFn))
279 goto unordered;
280
281 if (n_flag)
282 rFm ^= 0x8000000000000000ULL;
283
284 /* test for less than condition */
285 if (float64_lt_nocheck(rFn, rFm))
286 flags |= CC_NEGATIVE;
287
288 /* test for equal condition */
289 if (float64_eq_nocheck(rFn, rFm))
290 flags |= CC_ZERO;
291
292 /* test for greater than or equal condition */
293 if (float64_lt_nocheck(rFm, rFn))
294 flags |= CC_CARRY;
295 }
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;
302
303 if (float32_is_nan(rFn)
304 || float32_is_nan(rFm))
305 goto unordered;
306
307 if (n_flag)
308 rFm ^= 0x80000000;
309
310 /* test for less than condition */
311 if (float32_lt_nocheck(rFn, rFm))
312 flags |= CC_NEGATIVE;
313
314 /* test for equal condition */
315 if (float32_eq_nocheck(rFn, rFm))
316 flags |= CC_ZERO;
317
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;
324
325 rFm = (fpa11->fType[Fm] == typeSingle) ?
326 float32_to_float64(fpa11->fpreg[Fm].fSingle)
327 : fpa11->fpreg[Fm].fDouble;
328
329 rFn = (fpa11->fType[Fn] == typeSingle) ?
330 float32_to_float64(fpa11->fpreg[Fn].fSingle)
331 : fpa11->fpreg[Fn].fDouble;
332
333 if (float64_is_nan(rFn)
334 || float64_is_nan(rFm))
335 goto unordered;
336
337 if (n_flag)
338 rFm ^= 0x8000000000000000ULL;
339
340 /* test for less than condition */
341 if (float64_lt_nocheck(rFn, rFm))
342 flags |= CC_NEGATIVE;
343
344 /* test for equal condition */
345 if (float64_eq_nocheck(rFn, rFm))
346 flags |= CC_ZERO;
347
348 /* test for greater than or equal condition */
349 if (float64_lt_nocheck(rFm, rFn))
350 flags |= CC_CARRY;
351 }
352 }
353
354#endif
355
356 writeConditionCodes(flags);
357
358 return 1;
359
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);
368
369 if (BIT_AC & readFPSR())
370 flags |= CC_CARRY;
371
372 if (e_flag)
373 float_raise(float_flag_invalid);
374
375 writeConditionCodes(flags);
376 return 1;
377}