Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
fpa11_cprt.c
Go to the documentation of this file.
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 <[email protected]>
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)
370 
371  writeConditionCodes(flags);
372  return 1;
373 }