blob: 31c4eeec18b07654fe925eca4be95861e262f87f [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
Linus Torvalds1da177e2005-04-16 15:20:36 -070030unsigned int PerformFLT(const unsigned int opcode);
31unsigned int PerformFIX(const unsigned int opcode);
32
33static unsigned int PerformComparison(const unsigned int opcode);
34
35unsigned int EmulateCPRT(const unsigned int opcode)
36{
37
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);
44 }
45
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;
54
55 case WFS_CODE >> 20:
56 writeFPSR(readRegister(getRd(opcode)));
57 break;
58 case RFS_CODE >> 20:
59 writeRegister(getRd(opcode), readFPSR());
60 break;
61
62 default:
63 return 0;
64 }
65
66 return 1;
67}
68
69unsigned int PerformFLT(const unsigned int opcode)
70{
71 FPA11 *fpa11 = GET_FPA11();
Richard Purdief148af22005-08-03 19:49:17 +010072 struct roundingData roundData;
73
74 roundData.mode = SetRoundingMode(opcode);
75 roundData.precision = SetRoundingPrecision(opcode);
76 roundData.exception = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070077
78 switch (opcode & MASK_ROUNDING_PRECISION) {
79 case ROUND_SINGLE:
80 {
81 fpa11->fType[getFn(opcode)] = typeSingle;
Richard Purdief148af22005-08-03 19:49:17 +010082 fpa11->fpreg[getFn(opcode)].fSingle = int32_to_float32(&roundData, readRegister(getRd(opcode)));
Linus Torvalds1da177e2005-04-16 15:20:36 -070083 }
84 break;
85
86 case ROUND_DOUBLE:
87 {
88 fpa11->fType[getFn(opcode)] = typeDouble;
89 fpa11->fpreg[getFn(opcode)].fDouble = int32_to_float64(readRegister(getRd(opcode)));
90 }
91 break;
92
93#ifdef CONFIG_FPE_NWFPE_XP
94 case ROUND_EXTENDED:
95 {
96 fpa11->fType[getFn(opcode)] = typeExtended;
97 fpa11->fpreg[getFn(opcode)].fExtended = int32_to_floatx80(readRegister(getRd(opcode)));
98 }
99 break;
100#endif
101
102 default:
103 return 0;
104 }
105
Richard Purdief148af22005-08-03 19:49:17 +0100106 if (roundData.exception)
107 float_raise(roundData.exception);
108
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109 return 1;
110}
111
112unsigned int PerformFIX(const unsigned int opcode)
113{
114 FPA11 *fpa11 = GET_FPA11();
115 unsigned int Fn = getFm(opcode);
Richard Purdief148af22005-08-03 19:49:17 +0100116 struct roundingData roundData;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117
Richard Purdief148af22005-08-03 19:49:17 +0100118 roundData.mode = SetRoundingMode(opcode);
119 roundData.precision = SetRoundingPrecision(opcode);
120 roundData.exception = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121
122 switch (fpa11->fType[Fn]) {
123 case typeSingle:
124 {
Richard Purdief148af22005-08-03 19:49:17 +0100125 writeRegister(getRd(opcode), float32_to_int32(&roundData, fpa11->fpreg[Fn].fSingle));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126 }
127 break;
128
129 case typeDouble:
130 {
Richard Purdief148af22005-08-03 19:49:17 +0100131 writeRegister(getRd(opcode), float64_to_int32(&roundData, fpa11->fpreg[Fn].fDouble));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 }
133 break;
134
135#ifdef CONFIG_FPE_NWFPE_XP
136 case typeExtended:
137 {
Richard Purdief148af22005-08-03 19:49:17 +0100138 writeRegister(getRd(opcode), floatx80_to_int32(&roundData, fpa11->fpreg[Fn].fExtended));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 }
140 break;
141#endif
142
143 default:
144 return 0;
145 }
146
Richard Purdief148af22005-08-03 19:49:17 +0100147 if (roundData.exception)
148 float_raise(roundData.exception);
149
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 return 1;
151}
152
153/* This instruction sets the flags N, Z, C, V in the FPSR. */
154static unsigned int PerformComparison(const unsigned int opcode)
155{
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;
161
162#ifdef CONFIG_FPE_NWFPE_XP
163 floatx80 rFn, rFm;
164
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;
177
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;
184
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;
191
192 default:
193 return 0;
194 }
195
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;
210
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;
217
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;
224
225 default:
226 return 0;
227 }
228 }
229
230 if (n_flag)
231 rFm.high ^= 0x8000;
232
233 /* test for less than condition */
234 if (floatx80_lt(rFn, rFm))
235 flags |= CC_NEGATIVE;
236
237 /* test for equal condition */
238 if (floatx80_eq(rFn, rFm))
239 flags |= CC_ZERO;
240
241 /* test for greater than or equal condition */
242 if (floatx80_lt(rFm, rFn))
243 flags |= CC_CARRY;
244
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;
252
253 if (float32_is_nan(rFn))
254 goto unordered;
255
256 if (n_flag)
257 rFm ^= 0x80000000;
258
259 /* test for less than condition */
260 if (float32_lt_nocheck(rFn, rFm))
261 flags |= CC_NEGATIVE;
262
263 /* test for equal condition */
264 if (float32_eq_nocheck(rFn, rFm))
265 flags |= CC_ZERO;
266
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;
273
274 if (float64_is_nan(rFn))
275 goto unordered;
276
277 if (n_flag)
278 rFm ^= 0x8000000000000000ULL;
279
280 /* test for less than condition */
281 if (float64_lt_nocheck(rFn, rFm))
282 flags |= CC_NEGATIVE;
283
284 /* test for equal condition */
285 if (float64_eq_nocheck(rFn, rFm))
286 flags |= CC_ZERO;
287
288 /* test for greater than or equal condition */
289 if (float64_lt_nocheck(rFm, rFn))
290 flags |= CC_CARRY;
291 }
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;
298
299 if (float32_is_nan(rFn)
300 || float32_is_nan(rFm))
301 goto unordered;
302
303 if (n_flag)
304 rFm ^= 0x80000000;
305
306 /* test for less than condition */
307 if (float32_lt_nocheck(rFn, rFm))
308 flags |= CC_NEGATIVE;
309
310 /* test for equal condition */
311 if (float32_eq_nocheck(rFn, rFm))
312 flags |= CC_ZERO;
313
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;
320
321 rFm = (fpa11->fType[Fm] == typeSingle) ?
322 float32_to_float64(fpa11->fpreg[Fm].fSingle)
323 : fpa11->fpreg[Fm].fDouble;
324
325 rFn = (fpa11->fType[Fn] == typeSingle) ?
326 float32_to_float64(fpa11->fpreg[Fn].fSingle)
327 : fpa11->fpreg[Fn].fDouble;
328
329 if (float64_is_nan(rFn)
330 || float64_is_nan(rFm))
331 goto unordered;
332
333 if (n_flag)
334 rFm ^= 0x8000000000000000ULL;
335
336 /* test for less than condition */
337 if (float64_lt_nocheck(rFn, rFm))
338 flags |= CC_NEGATIVE;
339
340 /* test for equal condition */
341 if (float64_eq_nocheck(rFn, rFm))
342 flags |= CC_ZERO;
343
344 /* test for greater than or equal condition */
345 if (float64_lt_nocheck(rFm, rFn))
346 flags |= CC_CARRY;
347 }
348 }
349
350#endif
351
352 writeConditionCodes(flags);
353
354 return 1;
355
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);
364
365 if (BIT_AC & readFPSR())
366 flags |= CC_CARRY;
367
368 if (e_flag)
369 float_raise(float_flag_invalid);
370
371 writeConditionCodes(flags);
372 return 1;
373}