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"
29 #include "softfloat.h"
31 #ifdef CONFIG_FPE_NWFPE_XP
32 extern flag
floatx80_is_nan(floatx80
);
35 unsigned int PerformFLT(const unsigned int opcode
);
36 unsigned int PerformFIX(const unsigned int opcode
);
38 static unsigned int PerformComparison(const unsigned int opcode
);
40 unsigned int EmulateCPRT(const unsigned int opcode
)
43 if (opcode
& 0x800000) {
44 /* This is some variant of a comparison (PerformComparison
45 will sort out which one). Since most of the other CPRT
46 instructions are oddball cases of some sort or other it
47 makes sense to pull this out into a fast path. */
48 return PerformComparison(opcode
);
51 /* Hint to GCC that we'd like a jump table rather than a load of CMPs */
52 switch ((opcode
& 0x700000) >> 20) {
54 return PerformFLT(opcode
);
57 return PerformFIX(opcode
);
61 writeFPSR(readRegister(getRd(opcode
)));
64 writeRegister(getRd(opcode
), readFPSR());
74 unsigned int PerformFLT(const unsigned int opcode
)
76 FPA11
*fpa11
= GET_FPA11();
77 struct roundingData roundData
;
79 roundData
.mode
= SetRoundingMode(opcode
);
80 roundData
.precision
= SetRoundingPrecision(opcode
);
81 roundData
.exception
= 0;
83 switch (opcode
& MASK_ROUNDING_PRECISION
) {
86 fpa11
->fType
[getFn(opcode
)] = typeSingle
;
87 fpa11
->fpreg
[getFn(opcode
)].fSingle
= int32_to_float32(&roundData
, 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
)));
111 if (roundData
.exception
)
112 float_raise(roundData
.exception
);
117 unsigned int PerformFIX(const unsigned int opcode
)
119 FPA11
*fpa11
= GET_FPA11();
120 unsigned int Fn
= getFm(opcode
);
121 struct roundingData roundData
;
123 roundData
.mode
= SetRoundingMode(opcode
);
124 roundData
.precision
= SetRoundingPrecision(opcode
);
125 roundData
.exception
= 0;
127 switch (fpa11
->fType
[Fn
]) {
130 writeRegister(getRd(opcode
), float32_to_int32(&roundData
, fpa11
->fpreg
[Fn
].fSingle
));
136 writeRegister(getRd(opcode
), float64_to_int32(&roundData
, fpa11
->fpreg
[Fn
].fDouble
));
140 #ifdef CONFIG_FPE_NWFPE_XP
143 writeRegister(getRd(opcode
), floatx80_to_int32(&roundData
, fpa11
->fpreg
[Fn
].fExtended
));
152 if (roundData
.exception
)
153 float_raise(roundData
.exception
);
158 /* This instruction sets the flags N, Z, C, V in the FPSR. */
159 static unsigned int PerformComparison(const unsigned int opcode
)
161 FPA11
*fpa11
= GET_FPA11();
162 unsigned int Fn
= getFn(opcode
), Fm
= getFm(opcode
);
163 int e_flag
= opcode
& 0x400000; /* 1 if CxFE */
164 int n_flag
= opcode
& 0x200000; /* 1 if CNxx */
165 unsigned int flags
= 0;
167 #ifdef CONFIG_FPE_NWFPE_XP
170 /* Check for unordered condition and convert all operands to 80-bit
172 ?? Might be some mileage in avoiding this conversion if possible.
173 Eg, if both operands are 32-bit, detect this and do a 32-bit
174 comparison (cheaper than an 80-bit one). */
175 switch (fpa11
->fType
[Fn
]) {
177 //printk("single.\n");
178 if (float32_is_nan(fpa11
->fpreg
[Fn
].fSingle
))
180 rFn
= float32_to_floatx80(fpa11
->fpreg
[Fn
].fSingle
);
184 //printk("double.\n");
185 if (float64_is_nan(fpa11
->fpreg
[Fn
].fDouble
))
187 rFn
= float64_to_floatx80(fpa11
->fpreg
[Fn
].fDouble
);
191 //printk("extended.\n");
192 if (floatx80_is_nan(fpa11
->fpreg
[Fn
].fExtended
))
194 rFn
= fpa11
->fpreg
[Fn
].fExtended
;
201 if (CONSTANT_FM(opcode
)) {
202 //printk("Fm is a constant: #%d.\n",Fm);
203 rFm
= getExtendedConstant(Fm
);
204 if (floatx80_is_nan(rFm
))
207 //printk("Fm = r%d which contains a ",Fm);
208 switch (fpa11
->fType
[Fm
]) {
210 //printk("single.\n");
211 if (float32_is_nan(fpa11
->fpreg
[Fm
].fSingle
))
213 rFm
= float32_to_floatx80(fpa11
->fpreg
[Fm
].fSingle
);
217 //printk("double.\n");
218 if (float64_is_nan(fpa11
->fpreg
[Fm
].fDouble
))
220 rFm
= float64_to_floatx80(fpa11
->fpreg
[Fm
].fDouble
);
224 //printk("extended.\n");
225 if (floatx80_is_nan(fpa11
->fpreg
[Fm
].fExtended
))
227 rFm
= fpa11
->fpreg
[Fm
].fExtended
;
238 /* test for less than condition */
239 if (floatx80_lt(rFn
, rFm
))
240 flags
|= CC_NEGATIVE
;
242 /* test for equal condition */
243 if (floatx80_eq(rFn
, rFm
))
246 /* test for greater than or equal condition */
247 if (floatx80_lt(rFm
, rFn
))
251 if (CONSTANT_FM(opcode
)) {
252 /* Fm is a constant. Do the comparison in whatever precision
253 Fn happens to be stored in. */
254 if (fpa11
->fType
[Fn
] == typeSingle
) {
255 float32 rFm
= getSingleConstant(Fm
);
256 float32 rFn
= fpa11
->fpreg
[Fn
].fSingle
;
258 if (float32_is_nan(rFn
))
264 /* test for less than condition */
265 if (float32_lt_nocheck(rFn
, rFm
))
266 flags
|= CC_NEGATIVE
;
268 /* test for equal condition */
269 if (float32_eq_nocheck(rFn
, rFm
))
272 /* test for greater than or equal condition */
273 if (float32_lt_nocheck(rFm
, rFn
))
276 float64 rFm
= getDoubleConstant(Fm
);
277 float64 rFn
= fpa11
->fpreg
[Fn
].fDouble
;
279 if (float64_is_nan(rFn
))
283 rFm
^= 0x8000000000000000ULL
;
285 /* test for less than condition */
286 if (float64_lt_nocheck(rFn
, rFm
))
287 flags
|= CC_NEGATIVE
;
289 /* test for equal condition */
290 if (float64_eq_nocheck(rFn
, rFm
))
293 /* test for greater than or equal condition */
294 if (float64_lt_nocheck(rFm
, rFn
))
298 /* Both operands are in registers. */
299 if (fpa11
->fType
[Fn
] == typeSingle
300 && fpa11
->fType
[Fm
] == typeSingle
) {
301 float32 rFm
= fpa11
->fpreg
[Fm
].fSingle
;
302 float32 rFn
= fpa11
->fpreg
[Fn
].fSingle
;
304 if (float32_is_nan(rFn
)
305 || float32_is_nan(rFm
))
311 /* test for less than condition */
312 if (float32_lt_nocheck(rFn
, rFm
))
313 flags
|= CC_NEGATIVE
;
315 /* test for equal condition */
316 if (float32_eq_nocheck(rFn
, rFm
))
319 /* test for greater than or equal condition */
320 if (float32_lt_nocheck(rFm
, rFn
))
323 /* Promote 32-bit operand to 64 bits. */
326 rFm
= (fpa11
->fType
[Fm
] == typeSingle
) ?
327 float32_to_float64(fpa11
->fpreg
[Fm
].fSingle
)
328 : fpa11
->fpreg
[Fm
].fDouble
;
330 rFn
= (fpa11
->fType
[Fn
] == typeSingle
) ?
331 float32_to_float64(fpa11
->fpreg
[Fn
].fSingle
)
332 : fpa11
->fpreg
[Fn
].fDouble
;
334 if (float64_is_nan(rFn
)
335 || float64_is_nan(rFm
))
339 rFm
^= 0x8000000000000000ULL
;
341 /* test for less than condition */
342 if (float64_lt_nocheck(rFn
, rFm
))
343 flags
|= CC_NEGATIVE
;
345 /* test for equal condition */
346 if (float64_eq_nocheck(rFn
, rFm
))
349 /* test for greater than or equal condition */
350 if (float64_lt_nocheck(rFm
, rFn
))
357 writeConditionCodes(flags
);
362 /* ?? The FPA data sheet is pretty vague about this, in particular
363 about whether the non-E comparisons can ever raise exceptions.
364 This implementation is based on a combination of what it says in
365 the data sheet, observation of how the Acorn emulator actually
366 behaves (and how programs expect it to) and guesswork. */
367 flags
|= CC_OVERFLOW
;
368 flags
&= ~(CC_ZERO
| CC_NEGATIVE
);
370 if (BIT_AC
& readFPSR())
374 float_raise(float_flag_invalid
);
376 writeConditionCodes(flags
);