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