blob: db01fbc97216829b52db0b5117b88e2457c1253e [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
36void SetRoundingMode(const unsigned int opcode);
37
38unsigned int PerformFLT(const unsigned int opcode);
39unsigned int PerformFIX(const unsigned int opcode);
40
41static unsigned int PerformComparison(const unsigned int opcode);
42
43unsigned int EmulateCPRT(const unsigned int opcode)
44{
45
46 if (opcode & 0x800000) {
47 /* This is some variant of a comparison (PerformComparison
48 will sort out which one). Since most of the other CPRT
49 instructions are oddball cases of some sort or other it
50 makes sense to pull this out into a fast path. */
51 return PerformComparison(opcode);
52 }
53
54 /* Hint to GCC that we'd like a jump table rather than a load of CMPs */
55 switch ((opcode & 0x700000) >> 20) {
56 case FLT_CODE >> 20:
57 return PerformFLT(opcode);
58 break;
59 case FIX_CODE >> 20:
60 return PerformFIX(opcode);
61 break;
62
63 case WFS_CODE >> 20:
64 writeFPSR(readRegister(getRd(opcode)));
65 break;
66 case RFS_CODE >> 20:
67 writeRegister(getRd(opcode), readFPSR());
68 break;
69
70 default:
71 return 0;
72 }
73
74 return 1;
75}
76
77unsigned int PerformFLT(const unsigned int opcode)
78{
79 FPA11 *fpa11 = GET_FPA11();
80 SetRoundingMode(opcode);
81 SetRoundingPrecision(opcode);
82
83 switch (opcode & MASK_ROUNDING_PRECISION) {
84 case ROUND_SINGLE:
85 {
86 fpa11->fType[getFn(opcode)] = typeSingle;
87 fpa11->fpreg[getFn(opcode)].fSingle = int32_to_float32(readRegister(getRd(opcode)));
88 }
89 break;
90
91 case ROUND_DOUBLE:
92 {
93 fpa11->fType[getFn(opcode)] = typeDouble;
94 fpa11->fpreg[getFn(opcode)].fDouble = int32_to_float64(readRegister(getRd(opcode)));
95 }
96 break;
97
98#ifdef CONFIG_FPE_NWFPE_XP
99 case ROUND_EXTENDED:
100 {
101 fpa11->fType[getFn(opcode)] = typeExtended;
102 fpa11->fpreg[getFn(opcode)].fExtended = int32_to_floatx80(readRegister(getRd(opcode)));
103 }
104 break;
105#endif
106
107 default:
108 return 0;
109 }
110
111 return 1;
112}
113
114unsigned int PerformFIX(const unsigned int opcode)
115{
116 FPA11 *fpa11 = GET_FPA11();
117 unsigned int Fn = getFm(opcode);
118
119 SetRoundingMode(opcode);
120
121 switch (fpa11->fType[Fn]) {
122 case typeSingle:
123 {
124 writeRegister(getRd(opcode), float32_to_int32(fpa11->fpreg[Fn].fSingle));
125 }
126 break;
127
128 case typeDouble:
129 {
130 writeRegister(getRd(opcode), float64_to_int32(fpa11->fpreg[Fn].fDouble));
131 }
132 break;
133
134#ifdef CONFIG_FPE_NWFPE_XP
135 case typeExtended:
136 {
137 writeRegister(getRd(opcode), floatx80_to_int32(fpa11->fpreg[Fn].fExtended));
138 }
139 break;
140#endif
141
142 default:
143 return 0;
144 }
145
146 return 1;
147}
148
149/* This instruction sets the flags N, Z, C, V in the FPSR. */
150static unsigned int PerformComparison(const unsigned int opcode)
151{
152 FPA11 *fpa11 = GET_FPA11();
153 unsigned int Fn = getFn(opcode), Fm = getFm(opcode);
154 int e_flag = opcode & 0x400000; /* 1 if CxFE */
155 int n_flag = opcode & 0x200000; /* 1 if CNxx */
156 unsigned int flags = 0;
157
158#ifdef CONFIG_FPE_NWFPE_XP
159 floatx80 rFn, rFm;
160
161 /* Check for unordered condition and convert all operands to 80-bit
162 format.
163 ?? Might be some mileage in avoiding this conversion if possible.
164 Eg, if both operands are 32-bit, detect this and do a 32-bit
165 comparison (cheaper than an 80-bit one). */
166 switch (fpa11->fType[Fn]) {
167 case typeSingle:
168 //printk("single.\n");
169 if (float32_is_nan(fpa11->fpreg[Fn].fSingle))
170 goto unordered;
171 rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle);
172 break;
173
174 case typeDouble:
175 //printk("double.\n");
176 if (float64_is_nan(fpa11->fpreg[Fn].fDouble))
177 goto unordered;
178 rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble);
179 break;
180
181 case typeExtended:
182 //printk("extended.\n");
183 if (floatx80_is_nan(fpa11->fpreg[Fn].fExtended))
184 goto unordered;
185 rFn = fpa11->fpreg[Fn].fExtended;
186 break;
187
188 default:
189 return 0;
190 }
191
192 if (CONSTANT_FM(opcode)) {
193 //printk("Fm is a constant: #%d.\n",Fm);
194 rFm = getExtendedConstant(Fm);
195 if (floatx80_is_nan(rFm))
196 goto unordered;
197 } else {
198 //printk("Fm = r%d which contains a ",Fm);
199 switch (fpa11->fType[Fm]) {
200 case typeSingle:
201 //printk("single.\n");
202 if (float32_is_nan(fpa11->fpreg[Fm].fSingle))
203 goto unordered;
204 rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle);
205 break;
206
207 case typeDouble:
208 //printk("double.\n");
209 if (float64_is_nan(fpa11->fpreg[Fm].fDouble))
210 goto unordered;
211 rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble);
212 break;
213
214 case typeExtended:
215 //printk("extended.\n");
216 if (floatx80_is_nan(fpa11->fpreg[Fm].fExtended))
217 goto unordered;
218 rFm = fpa11->fpreg[Fm].fExtended;
219 break;
220
221 default:
222 return 0;
223 }
224 }
225
226 if (n_flag)
227 rFm.high ^= 0x8000;
228
229 /* test for less than condition */
230 if (floatx80_lt(rFn, rFm))
231 flags |= CC_NEGATIVE;
232
233 /* test for equal condition */
234 if (floatx80_eq(rFn, rFm))
235 flags |= CC_ZERO;
236
237 /* test for greater than or equal condition */
238 if (floatx80_lt(rFm, rFn))
239 flags |= CC_CARRY;
240
241#else
242 if (CONSTANT_FM(opcode)) {
243 /* Fm is a constant. Do the comparison in whatever precision
244 Fn happens to be stored in. */
245 if (fpa11->fType[Fn] == typeSingle) {
246 float32 rFm = getSingleConstant(Fm);
247 float32 rFn = fpa11->fpreg[Fn].fSingle;
248
249 if (float32_is_nan(rFn))
250 goto unordered;
251
252 if (n_flag)
253 rFm ^= 0x80000000;
254
255 /* test for less than condition */
256 if (float32_lt_nocheck(rFn, rFm))
257 flags |= CC_NEGATIVE;
258
259 /* test for equal condition */
260 if (float32_eq_nocheck(rFn, rFm))
261 flags |= CC_ZERO;
262
263 /* test for greater than or equal condition */
264 if (float32_lt_nocheck(rFm, rFn))
265 flags |= CC_CARRY;
266 } else {
267 float64 rFm = getDoubleConstant(Fm);
268 float64 rFn = fpa11->fpreg[Fn].fDouble;
269
270 if (float64_is_nan(rFn))
271 goto unordered;
272
273 if (n_flag)
274 rFm ^= 0x8000000000000000ULL;
275
276 /* test for less than condition */
277 if (float64_lt_nocheck(rFn, rFm))
278 flags |= CC_NEGATIVE;
279
280 /* test for equal condition */
281 if (float64_eq_nocheck(rFn, rFm))
282 flags |= CC_ZERO;
283
284 /* test for greater than or equal condition */
285 if (float64_lt_nocheck(rFm, rFn))
286 flags |= CC_CARRY;
287 }
288 } else {
289 /* Both operands are in registers. */
290 if (fpa11->fType[Fn] == typeSingle
291 && fpa11->fType[Fm] == typeSingle) {
292 float32 rFm = fpa11->fpreg[Fm].fSingle;
293 float32 rFn = fpa11->fpreg[Fn].fSingle;
294
295 if (float32_is_nan(rFn)
296 || float32_is_nan(rFm))
297 goto unordered;
298
299 if (n_flag)
300 rFm ^= 0x80000000;
301
302 /* test for less than condition */
303 if (float32_lt_nocheck(rFn, rFm))
304 flags |= CC_NEGATIVE;
305
306 /* test for equal condition */
307 if (float32_eq_nocheck(rFn, rFm))
308 flags |= CC_ZERO;
309
310 /* test for greater than or equal condition */
311 if (float32_lt_nocheck(rFm, rFn))
312 flags |= CC_CARRY;
313 } else {
314 /* Promote 32-bit operand to 64 bits. */
315 float64 rFm, rFn;
316
317 rFm = (fpa11->fType[Fm] == typeSingle) ?
318 float32_to_float64(fpa11->fpreg[Fm].fSingle)
319 : fpa11->fpreg[Fm].fDouble;
320
321 rFn = (fpa11->fType[Fn] == typeSingle) ?
322 float32_to_float64(fpa11->fpreg[Fn].fSingle)
323 : fpa11->fpreg[Fn].fDouble;
324
325 if (float64_is_nan(rFn)
326 || float64_is_nan(rFm))
327 goto unordered;
328
329 if (n_flag)
330 rFm ^= 0x8000000000000000ULL;
331
332 /* test for less than condition */
333 if (float64_lt_nocheck(rFn, rFm))
334 flags |= CC_NEGATIVE;
335
336 /* test for equal condition */
337 if (float64_eq_nocheck(rFn, rFm))
338 flags |= CC_ZERO;
339
340 /* test for greater than or equal condition */
341 if (float64_lt_nocheck(rFm, rFn))
342 flags |= CC_CARRY;
343 }
344 }
345
346#endif
347
348 writeConditionCodes(flags);
349
350 return 1;
351
352 unordered:
353 /* ?? The FPA data sheet is pretty vague about this, in particular
354 about whether the non-E comparisons can ever raise exceptions.
355 This implementation is based on a combination of what it says in
356 the data sheet, observation of how the Acorn emulator actually
357 behaves (and how programs expect it to) and guesswork. */
358 flags |= CC_OVERFLOW;
359 flags &= ~(CC_ZERO | CC_NEGATIVE);
360
361 if (BIT_AC & readFPSR())
362 flags |= CC_CARRY;
363
364 if (e_flag)
365 float_raise(float_flag_invalid);
366
367 writeConditionCodes(flags);
368 return 1;
369}