Pull bugzilla-7897 into release branch
[linux-2.6] / arch / arm / nwfpe / fpa11_cprt.c
1 /*
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 "fpa11.h"
24 #include "fpopcode.h"
25 #include "fpa11.inl"
26 #include "fpmodule.h"
27 #include "fpmodule.inl"
28 #include "softfloat.h"
29
30 #ifdef CONFIG_FPE_NWFPE_XP
31 extern flag floatx80_is_nan(floatx80);
32 #endif
33
34 unsigned int PerformFLT(const unsigned int opcode);
35 unsigned int PerformFIX(const unsigned int opcode);
36
37 static unsigned int PerformComparison(const unsigned int opcode);
38
39 unsigned 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
73 unsigned int PerformFLT(const unsigned int opcode)
74 {
75         FPA11 *fpa11 = GET_FPA11();
76         struct roundingData roundData;
77
78         roundData.mode = SetRoundingMode(opcode);
79         roundData.precision = SetRoundingPrecision(opcode);
80         roundData.exception = 0;
81
82         switch (opcode & MASK_ROUNDING_PRECISION) {
83         case ROUND_SINGLE:
84                 {
85                         fpa11->fType[getFn(opcode)] = typeSingle;
86                         fpa11->fpreg[getFn(opcode)].fSingle = int32_to_float32(&roundData, readRegister(getRd(opcode)));
87                 }
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
110         if (roundData.exception)
111                 float_raise(roundData.exception);
112
113         return 1;
114 }
115
116 unsigned int PerformFIX(const unsigned int opcode)
117 {
118         FPA11 *fpa11 = GET_FPA11();
119         unsigned int Fn = getFm(opcode);
120         struct roundingData roundData;
121
122         roundData.mode = SetRoundingMode(opcode);
123         roundData.precision = SetRoundingPrecision(opcode);
124         roundData.exception = 0;
125
126         switch (fpa11->fType[Fn]) {
127         case typeSingle:
128                 {
129                         writeRegister(getRd(opcode), float32_to_int32(&roundData, fpa11->fpreg[Fn].fSingle));
130                 }
131                 break;
132
133         case typeDouble:
134                 {
135                         writeRegister(getRd(opcode), float64_to_int32(&roundData, fpa11->fpreg[Fn].fDouble));
136                 }
137                 break;
138
139 #ifdef CONFIG_FPE_NWFPE_XP
140         case typeExtended:
141                 {
142                         writeRegister(getRd(opcode), floatx80_to_int32(&roundData, fpa11->fpreg[Fn].fExtended));
143                 }
144                 break;
145 #endif
146
147         default:
148                 return 0;
149         }
150
151         if (roundData.exception)
152                 float_raise(roundData.exception);
153
154         return 1;
155 }
156
157 /* This instruction sets the flags N, Z, C, V in the FPSR. */
158 static 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 }