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