Linux-2.6.12-rc2
[linux-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 void SetRoundingMode(const unsigned int opcode);
37
38 unsigned int PerformFLT(const unsigned int opcode);
39 unsigned int PerformFIX(const unsigned int opcode);
40
41 static unsigned int PerformComparison(const unsigned int opcode);
42
43 unsigned 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
77 unsigned 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
114 unsigned 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. */
150 static 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 }