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