1 // SPDX-License-Identifier: GPL-2.0-or-later
3 NetWinder Floating Point Emulator
4 (c) Rebel.COM, 1998,1999
5 (c) Philip Blundell, 1999, 2001
7 Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
15 #include "fpmodule.inl"
16 #include "softfloat.h"
18 unsigned int PerformFLT(const unsigned int opcode
);
19 unsigned int PerformFIX(const unsigned int opcode
);
21 static unsigned int PerformComparison(const unsigned int opcode
);
23 unsigned int EmulateCPRT(const unsigned int opcode
)
26 if (opcode
& 0x800000) {
27 /* This is some variant of a comparison (PerformComparison
28 will sort out which one). Since most of the other CPRT
29 instructions are oddball cases of some sort or other it
30 makes sense to pull this out into a fast path. */
31 return PerformComparison(opcode
);
34 /* Hint to GCC that we'd like a jump table rather than a load of CMPs */
35 switch ((opcode
& 0x700000) >> 20) {
37 return PerformFLT(opcode
);
40 return PerformFIX(opcode
);
44 writeFPSR(readRegister(getRd(opcode
)));
47 writeRegister(getRd(opcode
), readFPSR());
57 unsigned int PerformFLT(const unsigned int opcode
)
59 FPA11
*fpa11
= GET_FPA11();
60 struct roundingData roundData
;
62 roundData
.mode
= SetRoundingMode(opcode
);
63 roundData
.precision
= SetRoundingPrecision(opcode
);
64 roundData
.exception
= 0;
66 switch (opcode
& MASK_ROUNDING_PRECISION
) {
69 fpa11
->fType
[getFn(opcode
)] = typeSingle
;
70 fpa11
->fpreg
[getFn(opcode
)].fSingle
= int32_to_float32(&roundData
, readRegister(getRd(opcode
)));
76 fpa11
->fType
[getFn(opcode
)] = typeDouble
;
77 fpa11
->fpreg
[getFn(opcode
)].fDouble
= int32_to_float64(readRegister(getRd(opcode
)));
81 #ifdef CONFIG_FPE_NWFPE_XP
84 fpa11
->fType
[getFn(opcode
)] = typeExtended
;
85 fpa11
->fpreg
[getFn(opcode
)].fExtended
= int32_to_floatx80(readRegister(getRd(opcode
)));
94 if (roundData
.exception
)
95 float_raise(roundData
.exception
);
100 unsigned int PerformFIX(const unsigned int opcode
)
102 FPA11
*fpa11
= GET_FPA11();
103 unsigned int Fn
= getFm(opcode
);
104 struct roundingData roundData
;
106 roundData
.mode
= SetRoundingMode(opcode
);
107 roundData
.precision
= SetRoundingPrecision(opcode
);
108 roundData
.exception
= 0;
110 switch (fpa11
->fType
[Fn
]) {
113 writeRegister(getRd(opcode
), float32_to_int32(&roundData
, fpa11
->fpreg
[Fn
].fSingle
));
119 writeRegister(getRd(opcode
), float64_to_int32(&roundData
, fpa11
->fpreg
[Fn
].fDouble
));
123 #ifdef CONFIG_FPE_NWFPE_XP
126 writeRegister(getRd(opcode
), floatx80_to_int32(&roundData
, fpa11
->fpreg
[Fn
].fExtended
));
135 if (roundData
.exception
)
136 float_raise(roundData
.exception
);
141 /* This instruction sets the flags N, Z, C, V in the FPSR. */
142 static unsigned int PerformComparison(const unsigned int opcode
)
144 FPA11
*fpa11
= GET_FPA11();
145 unsigned int Fn
= getFn(opcode
), Fm
= getFm(opcode
);
146 int e_flag
= opcode
& 0x400000; /* 1 if CxFE */
147 int n_flag
= opcode
& 0x200000; /* 1 if CNxx */
148 unsigned int flags
= 0;
150 #ifdef CONFIG_FPE_NWFPE_XP
153 /* Check for unordered condition and convert all operands to 80-bit
155 ?? Might be some mileage in avoiding this conversion if possible.
156 Eg, if both operands are 32-bit, detect this and do a 32-bit
157 comparison (cheaper than an 80-bit one). */
158 switch (fpa11
->fType
[Fn
]) {
160 //printk("single.\n");
161 if (float32_is_nan(fpa11
->fpreg
[Fn
].fSingle
))
163 rFn
= float32_to_floatx80(fpa11
->fpreg
[Fn
].fSingle
);
167 //printk("double.\n");
168 if (float64_is_nan(fpa11
->fpreg
[Fn
].fDouble
))
170 rFn
= float64_to_floatx80(fpa11
->fpreg
[Fn
].fDouble
);
174 //printk("extended.\n");
175 if (floatx80_is_nan(fpa11
->fpreg
[Fn
].fExtended
))
177 rFn
= fpa11
->fpreg
[Fn
].fExtended
;
184 if (CONSTANT_FM(opcode
)) {
185 //printk("Fm is a constant: #%d.\n",Fm);
186 rFm
= getExtendedConstant(Fm
);
187 if (floatx80_is_nan(rFm
))
190 //printk("Fm = r%d which contains a ",Fm);
191 switch (fpa11
->fType
[Fm
]) {
193 //printk("single.\n");
194 if (float32_is_nan(fpa11
->fpreg
[Fm
].fSingle
))
196 rFm
= float32_to_floatx80(fpa11
->fpreg
[Fm
].fSingle
);
200 //printk("double.\n");
201 if (float64_is_nan(fpa11
->fpreg
[Fm
].fDouble
))
203 rFm
= float64_to_floatx80(fpa11
->fpreg
[Fm
].fDouble
);
207 //printk("extended.\n");
208 if (floatx80_is_nan(fpa11
->fpreg
[Fm
].fExtended
))
210 rFm
= fpa11
->fpreg
[Fm
].fExtended
;
221 /* test for less than condition */
222 if (floatx80_lt(rFn
, rFm
))
223 flags
|= CC_NEGATIVE
;
225 /* test for equal condition */
226 if (floatx80_eq(rFn
, rFm
))
229 /* test for greater than or equal condition */
230 if (floatx80_lt(rFm
, rFn
))
234 if (CONSTANT_FM(opcode
)) {
235 /* Fm is a constant. Do the comparison in whatever precision
236 Fn happens to be stored in. */
237 if (fpa11
->fType
[Fn
] == typeSingle
) {
238 float32 rFm
= getSingleConstant(Fm
);
239 float32 rFn
= fpa11
->fpreg
[Fn
].fSingle
;
241 if (float32_is_nan(rFn
))
247 /* test for less than condition */
248 if (float32_lt_nocheck(rFn
, rFm
))
249 flags
|= CC_NEGATIVE
;
251 /* test for equal condition */
252 if (float32_eq_nocheck(rFn
, rFm
))
255 /* test for greater than or equal condition */
256 if (float32_lt_nocheck(rFm
, rFn
))
259 float64 rFm
= getDoubleConstant(Fm
);
260 float64 rFn
= fpa11
->fpreg
[Fn
].fDouble
;
262 if (float64_is_nan(rFn
))
266 rFm
^= 0x8000000000000000ULL
;
268 /* test for less than condition */
269 if (float64_lt_nocheck(rFn
, rFm
))
270 flags
|= CC_NEGATIVE
;
272 /* test for equal condition */
273 if (float64_eq_nocheck(rFn
, rFm
))
276 /* test for greater than or equal condition */
277 if (float64_lt_nocheck(rFm
, rFn
))
281 /* Both operands are in registers. */
282 if (fpa11
->fType
[Fn
] == typeSingle
283 && fpa11
->fType
[Fm
] == typeSingle
) {
284 float32 rFm
= fpa11
->fpreg
[Fm
].fSingle
;
285 float32 rFn
= fpa11
->fpreg
[Fn
].fSingle
;
287 if (float32_is_nan(rFn
)
288 || float32_is_nan(rFm
))
294 /* test for less than condition */
295 if (float32_lt_nocheck(rFn
, rFm
))
296 flags
|= CC_NEGATIVE
;
298 /* test for equal condition */
299 if (float32_eq_nocheck(rFn
, rFm
))
302 /* test for greater than or equal condition */
303 if (float32_lt_nocheck(rFm
, rFn
))
306 /* Promote 32-bit operand to 64 bits. */
309 rFm
= (fpa11
->fType
[Fm
] == typeSingle
) ?
310 float32_to_float64(fpa11
->fpreg
[Fm
].fSingle
)
311 : fpa11
->fpreg
[Fm
].fDouble
;
313 rFn
= (fpa11
->fType
[Fn
] == typeSingle
) ?
314 float32_to_float64(fpa11
->fpreg
[Fn
].fSingle
)
315 : fpa11
->fpreg
[Fn
].fDouble
;
317 if (float64_is_nan(rFn
)
318 || float64_is_nan(rFm
))
322 rFm
^= 0x8000000000000000ULL
;
324 /* test for less than condition */
325 if (float64_lt_nocheck(rFn
, rFm
))
326 flags
|= CC_NEGATIVE
;
328 /* test for equal condition */
329 if (float64_eq_nocheck(rFn
, rFm
))
332 /* test for greater than or equal condition */
333 if (float64_lt_nocheck(rFm
, rFn
))
340 writeConditionCodes(flags
);
345 /* ?? The FPA data sheet is pretty vague about this, in particular
346 about whether the non-E comparisons can ever raise exceptions.
347 This implementation is based on a combination of what it says in
348 the data sheet, observation of how the Acorn emulator actually
349 behaves (and how programs expect it to) and guesswork. */
350 flags
|= CC_OVERFLOW
;
351 flags
&= ~(CC_ZERO
| CC_NEGATIVE
);
353 if (BIT_AC
& readFPSR())
357 float_raise(float_flag_invalid
);
359 writeConditionCodes(flags
);