2 NetWinder Floating Point Emulator
3 (c) Rebel.COM, 1998,1999
4 (c) Philip Blundell, 1999, 2001
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 <linux/config.h>
28 #include "fpmodule.inl"
30 #ifdef CONFIG_FPE_NWFPE_XP
31 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 PerformComparison(const unsigned int opcode
);
43 unsigned int EmulateCPRT(const unsigned int opcode
)
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
);
54 /* Hint to GCC that we'd like a jump table rather than a load of CMPs */
55 switch ((opcode
& 0x700000) >> 20) {
57 return PerformFLT(opcode
);
60 return PerformFIX(opcode
);
64 writeFPSR(readRegister(getRd(opcode
)));
67 writeRegister(getRd(opcode
), readFPSR());
77 unsigned int PerformFLT(const unsigned int opcode
)
79 FPA11
*fpa11
= GET_FPA11();
80 SetRoundingMode(opcode
);
81 SetRoundingPrecision(opcode
);
83 switch (opcode
& MASK_ROUNDING_PRECISION
) {
86 fpa11
->fType
[getFn(opcode
)] = typeSingle
;
87 fpa11
->fpreg
[getFn(opcode
)].fSingle
= int32_to_float32(readRegister(getRd(opcode
)));
93 fpa11
->fType
[getFn(opcode
)] = typeDouble
;
94 fpa11
->fpreg
[getFn(opcode
)].fDouble
= int32_to_float64(readRegister(getRd(opcode
)));
98 #ifdef CONFIG_FPE_NWFPE_XP
101 fpa11
->fType
[getFn(opcode
)] = typeExtended
;
102 fpa11
->fpreg
[getFn(opcode
)].fExtended
= int32_to_floatx80(readRegister(getRd(opcode
)));
114 unsigned int PerformFIX(const unsigned int opcode
)
116 FPA11
*fpa11
= GET_FPA11();
117 unsigned int Fn
= getFm(opcode
);
119 SetRoundingMode(opcode
);
121 switch (fpa11
->fType
[Fn
]) {
124 writeRegister(getRd(opcode
), float32_to_int32(fpa11
->fpreg
[Fn
].fSingle
));
130 writeRegister(getRd(opcode
), float64_to_int32(fpa11
->fpreg
[Fn
].fDouble
));
134 #ifdef CONFIG_FPE_NWFPE_XP
137 writeRegister(getRd(opcode
), floatx80_to_int32(fpa11
->fpreg
[Fn
].fExtended
));
149 /* This instruction sets the flags N, Z, C, V in the FPSR. */
150 static unsigned int PerformComparison(const unsigned int opcode
)
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;
158 #ifdef CONFIG_FPE_NWFPE_XP
161 /* Check for unordered condition and convert all operands to 80-bit
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
]) {
168 //printk("single.\n");
169 if (float32_is_nan(fpa11
->fpreg
[Fn
].fSingle
))
171 rFn
= float32_to_floatx80(fpa11
->fpreg
[Fn
].fSingle
);
175 //printk("double.\n");
176 if (float64_is_nan(fpa11
->fpreg
[Fn
].fDouble
))
178 rFn
= float64_to_floatx80(fpa11
->fpreg
[Fn
].fDouble
);
182 //printk("extended.\n");
183 if (floatx80_is_nan(fpa11
->fpreg
[Fn
].fExtended
))
185 rFn
= fpa11
->fpreg
[Fn
].fExtended
;
192 if (CONSTANT_FM(opcode
)) {
193 //printk("Fm is a constant: #%d.\n",Fm);
194 rFm
= getExtendedConstant(Fm
);
195 if (floatx80_is_nan(rFm
))
198 //printk("Fm = r%d which contains a ",Fm);
199 switch (fpa11
->fType
[Fm
]) {
201 //printk("single.\n");
202 if (float32_is_nan(fpa11
->fpreg
[Fm
].fSingle
))
204 rFm
= float32_to_floatx80(fpa11
->fpreg
[Fm
].fSingle
);
208 //printk("double.\n");
209 if (float64_is_nan(fpa11
->fpreg
[Fm
].fDouble
))
211 rFm
= float64_to_floatx80(fpa11
->fpreg
[Fm
].fDouble
);
215 //printk("extended.\n");
216 if (floatx80_is_nan(fpa11
->fpreg
[Fm
].fExtended
))
218 rFm
= fpa11
->fpreg
[Fm
].fExtended
;
229 /* test for less than condition */
230 if (floatx80_lt(rFn
, rFm
))
231 flags
|= CC_NEGATIVE
;
233 /* test for equal condition */
234 if (floatx80_eq(rFn
, rFm
))
237 /* test for greater than or equal condition */
238 if (floatx80_lt(rFm
, rFn
))
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
;
249 if (float32_is_nan(rFn
))
255 /* test for less than condition */
256 if (float32_lt_nocheck(rFn
, rFm
))
257 flags
|= CC_NEGATIVE
;
259 /* test for equal condition */
260 if (float32_eq_nocheck(rFn
, rFm
))
263 /* test for greater than or equal condition */
264 if (float32_lt_nocheck(rFm
, rFn
))
267 float64 rFm
= getDoubleConstant(Fm
);
268 float64 rFn
= fpa11
->fpreg
[Fn
].fDouble
;
270 if (float64_is_nan(rFn
))
274 rFm
^= 0x8000000000000000ULL
;
276 /* test for less than condition */
277 if (float64_lt_nocheck(rFn
, rFm
))
278 flags
|= CC_NEGATIVE
;
280 /* test for equal condition */
281 if (float64_eq_nocheck(rFn
, rFm
))
284 /* test for greater than or equal condition */
285 if (float64_lt_nocheck(rFm
, rFn
))
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
;
295 if (float32_is_nan(rFn
)
296 || float32_is_nan(rFm
))
302 /* test for less than condition */
303 if (float32_lt_nocheck(rFn
, rFm
))
304 flags
|= CC_NEGATIVE
;
306 /* test for equal condition */
307 if (float32_eq_nocheck(rFn
, rFm
))
310 /* test for greater than or equal condition */
311 if (float32_lt_nocheck(rFm
, rFn
))
314 /* Promote 32-bit operand to 64 bits. */
317 rFm
= (fpa11
->fType
[Fm
] == typeSingle
) ?
318 float32_to_float64(fpa11
->fpreg
[Fm
].fSingle
)
319 : fpa11
->fpreg
[Fm
].fDouble
;
321 rFn
= (fpa11
->fType
[Fn
] == typeSingle
) ?
322 float32_to_float64(fpa11
->fpreg
[Fn
].fSingle
)
323 : fpa11
->fpreg
[Fn
].fDouble
;
325 if (float64_is_nan(rFn
)
326 || float64_is_nan(rFm
))
330 rFm
^= 0x8000000000000000ULL
;
332 /* test for less than condition */
333 if (float64_lt_nocheck(rFn
, rFm
))
334 flags
|= CC_NEGATIVE
;
336 /* test for equal condition */
337 if (float64_eq_nocheck(rFn
, rFm
))
340 /* test for greater than or equal condition */
341 if (float64_lt_nocheck(rFm
, rFn
))
348 writeConditionCodes(flags
);
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
);
361 if (BIT_AC
& readFPSR())
365 float_raise(float_flag_invalid
);
367 writeConditionCodes(flags
);