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