* better
[mascara-docs.git] / i386 / linux-2.3.21 / arch / arm / nwfpe / fpa11_cprt.c
blobcbfde092ee7b6ec0618b68828026ebfdc5c61f0e
1 /*
2 NetWinder Floating Point Emulator
3 (c) Rebel.com, 1998-1999
4 (c) Philip Blundell, 1999
6 Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
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.
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.
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.
23 #include "config.h"
24 #include "milieu.h"
25 #include "softfloat.h"
26 #include "fpopcode.h"
27 #include "fpa11.h"
28 #include "fpa11.inl"
29 #include "fpmodule.h"
30 #include "fpmodule.inl"
32 extern flag floatx80_is_nan(floatx80);
33 extern flag float64_is_nan( float64);
34 extern flag float32_is_nan( float32);
36 void SetRoundingMode(const unsigned int opcode);
38 unsigned int PerformFLT(const unsigned int opcode);
39 unsigned int PerformFIX(const unsigned int opcode);
41 static unsigned int
42 PerformComparison(const unsigned int opcode);
44 unsigned int EmulateCPRT(const unsigned int opcode)
46 unsigned int nRc = 1;
48 //fp_printk("EmulateCPRT(0x%08x)\n",opcode);
50 if (opcode & 0x800000)
52 /* This is some variant of a comparison (PerformComparison will
53 sort out which one). Since most of the other CPRT
54 instructions are oddball cases of some sort or other it makes
55 sense to pull this out into a fast path. */
56 return PerformComparison(opcode);
59 /* Hint to GCC that we'd like a jump table rather than a load of CMPs */
60 switch ((opcode & 0x700000) >> 20)
62 case FLT_CODE >> 20: nRc = PerformFLT(opcode); break;
63 case FIX_CODE >> 20: nRc = PerformFIX(opcode); break;
65 case WFS_CODE >> 20: writeFPSR(readRegister(getRd(opcode))); break;
66 case RFS_CODE >> 20: writeRegister(getRd(opcode),readFPSR()); break;
68 #if 0
69 /* ?? Not at all sure about the mode checks here. Linux never
70 calls the emulator from a non-USR fault but we always run in SVC
71 mode. Is there even any point trying to emulate the way FPA11
72 behaves in this respect?
74 No - and I quote: 'The FPCR may only be present in some
75 implementations: it is there to control the hardware in an
76 implementation-specific manner, ... The user mode of the
77 ARM is not permitted to use this register, and the WFC and
78 RFC instructions will trap if tried from user mode.'
79 Therefore, we do not provide the RFC and WFC instructions.
80 (rmk, 3/05/1999)
82 case WFC_CODE >> 20:
84 int mode = 0;
85 __asm__ volatile ("mrs %0, cpsr; and %0, %0, #0x1f;" : : "g" (mode));
86 nRc = (0x13 == mode) ? 1 : 0; /* in SVC processor mode? */
87 if (nRc) writeFPCR(readRegister(getRd(opcode)));
89 break;
91 case RFC_CODE >> 20:
93 int mode = 0;
94 __asm__ volatile ("mrs %0, cpsr; and %0, %0, #0x1f;" : : "g" (mode));
95 nRc = (0x13 == mode) ? 1 : 0; /* in SVC processor mode? */
96 if (nRc) writeRegister(getRd(opcode),readFPCR()); break;
98 break;
99 #endif
101 default: nRc = 0;
104 return nRc;
107 unsigned int PerformFLT(const unsigned int opcode)
109 unsigned int nRc = 1;
110 SetRoundingMode(opcode);
111 SetRoundingPrecision(opcode);
113 switch (opcode & MASK_ROUNDING_PRECISION)
115 case ROUND_SINGLE:
117 fpa11->fpreg[getFn(opcode)].fType = typeSingle;
118 fpa11->fpreg[getFn(opcode)].fValue.fSingle =
119 int32_to_float32(readRegister(getRd(opcode)));
121 break;
123 case ROUND_DOUBLE:
125 fpa11->fpreg[getFn(opcode)].fType = typeDouble;
126 fpa11->fpreg[getFn(opcode)].fValue.fDouble =
127 int32_to_float64(readRegister(getRd(opcode)));
129 break;
131 case ROUND_EXTENDED:
133 fpa11->fpreg[getFn(opcode)].fType = typeExtended;
134 fpa11->fpreg[getFn(opcode)].fValue.fExtended =
135 int32_to_floatx80(readRegister(getRd(opcode)));
137 break;
139 default: nRc = 0;
142 return nRc;
145 unsigned int PerformFIX(const unsigned int opcode)
147 unsigned int nRc = 1;
148 unsigned int Fn = getFm(opcode);
150 SetRoundingMode(opcode);
152 switch (fpa11->fpreg[Fn].fType)
154 case typeSingle:
156 writeRegister(getRd(opcode),
157 float32_to_int32(fpa11->fpreg[Fn].fValue.fSingle));
159 break;
161 case typeDouble:
163 writeRegister(getRd(opcode),
164 float64_to_int32(fpa11->fpreg[Fn].fValue.fDouble));
166 break;
168 case typeExtended:
170 writeRegister(getRd(opcode),
171 floatx80_to_int32(fpa11->fpreg[Fn].fValue.fExtended));
173 break;
175 default: nRc = 0;
178 return nRc;
182 static unsigned int __inline__
183 PerformComparisonOperation(floatx80 Fn, floatx80 Fm)
185 unsigned int flags = 0;
187 /* test for less than condition */
188 if (floatx80_lt(Fn,Fm))
190 flags |= CC_NEGATIVE;
193 /* test for equal condition */
194 if (floatx80_eq(Fn,Fm))
196 flags |= CC_ZERO;
199 /* test for greater than or equal condition */
200 if (floatx80_lt(Fm,Fn))
202 flags |= CC_CARRY;
205 writeConditionCodes(flags);
206 return 1;
209 /* This instruction sets the flags N, Z, C, V in the FPSR. */
211 static unsigned int PerformComparison(const unsigned int opcode)
213 unsigned int Fn, Fm;
214 floatx80 rFn, rFm;
215 int e_flag = opcode & 0x400000; /* 1 if CxFE */
216 int n_flag = opcode & 0x200000; /* 1 if CNxx */
217 unsigned int flags = 0;
219 //fp_printk("PerformComparison(0x%08x)\n",opcode);
221 Fn = getFn(opcode);
222 Fm = getFm(opcode);
224 /* Check for unordered condition and convert all operands to 80-bit
225 format.
226 ?? Might be some mileage in avoiding this conversion if possible.
227 Eg, if both operands are 32-bit, detect this and do a 32-bit
228 comparison (cheaper than an 80-bit one). */
229 switch (fpa11->fpreg[Fn].fType)
231 case typeSingle:
232 //fp_printk("single.\n");
233 if (float32_is_nan(fpa11->fpreg[Fn].fValue.fSingle))
234 goto unordered;
235 rFn = float32_to_floatx80(fpa11->fpreg[Fn].fValue.fSingle);
236 break;
238 case typeDouble:
239 //fp_printk("double.\n");
240 if (float64_is_nan(fpa11->fpreg[Fn].fValue.fDouble))
241 goto unordered;
242 rFn = float64_to_floatx80(fpa11->fpreg[Fn].fValue.fDouble);
243 break;
245 case typeExtended:
246 //fp_printk("extended.\n");
247 if (floatx80_is_nan(fpa11->fpreg[Fn].fValue.fExtended))
248 goto unordered;
249 rFn = fpa11->fpreg[Fn].fValue.fExtended;
250 break;
252 default: return 0;
255 if (CONSTANT_FM(opcode))
257 //fp_printk("Fm is a constant: #%d.\n",Fm);
258 rFm = getExtendedConstant(Fm);
259 if (floatx80_is_nan(rFm))
260 goto unordered;
262 else
264 //fp_printk("Fm = r%d which contains a ",Fm);
265 switch (fpa11->fpreg[Fm].fType)
267 case typeSingle:
268 //fp_printk("single.\n");
269 if (float32_is_nan(fpa11->fpreg[Fm].fValue.fSingle))
270 goto unordered;
271 rFm = float32_to_floatx80(fpa11->fpreg[Fm].fValue.fSingle);
272 break;
274 case typeDouble:
275 //fp_printk("double.\n");
276 if (float64_is_nan(fpa11->fpreg[Fm].fValue.fDouble))
277 goto unordered;
278 rFm = float64_to_floatx80(fpa11->fpreg[Fm].fValue.fDouble);
279 break;
281 case typeExtended:
282 //fp_printk("extended.\n");
283 if (floatx80_is_nan(fpa11->fpreg[Fm].fValue.fExtended))
284 goto unordered;
285 rFm = fpa11->fpreg[Fm].fValue.fExtended;
286 break;
288 default: return 0;
292 if (n_flag)
294 rFm.high ^= 0x8000;
297 return PerformComparisonOperation(rFn,rFm);
299 unordered:
300 /* ?? The FPA data sheet is pretty vague about this, in particular
301 about whether the non-E comparisons can ever raise exceptions.
302 This implementation is based on a combination of what it says in
303 the data sheet, observation of how the Acorn emulator actually
304 behaves (and how programs expect it to) and guesswork. */
305 flags |= CC_OVERFLOW;
307 if (BIT_AC & readFPSR()) flags |= CC_CARRY;
309 if (e_flag) float_raise(float_flag_invalid);
311 writeConditionCodes(flags);
312 return 1;