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.
25 #include "softfloat.h"
29 #include "fpmodule.inl"
31 extern flag
floatx80_is_nan(floatx80
);
32 extern flag
float64_is_nan( float64
);
33 extern flag
float32_is_nan( float32
);
35 void SetRoundingMode(const unsigned int opcode
);
37 unsigned int PerformFLT(const unsigned int opcode
);
38 unsigned int PerformFIX(const unsigned int opcode
);
41 PerformComparison(const unsigned int opcode
);
43 unsigned int EmulateCPRT(const unsigned int opcode
)
47 //printk("EmulateCPRT(0x%08x)\n",opcode);
49 if (opcode
& 0x800000)
51 /* This is some variant of a comparison (PerformComparison will
52 sort out which one). Since most of the other CPRT
53 instructions are oddball cases of some sort or other it makes
54 sense to pull this out into a fast path. */
55 return PerformComparison(opcode
);
58 /* Hint to GCC that we'd like a jump table rather than a load of CMPs */
59 switch ((opcode
& 0x700000) >> 20)
61 case FLT_CODE
>> 20: nRc
= PerformFLT(opcode
); break;
62 case FIX_CODE
>> 20: nRc
= PerformFIX(opcode
); break;
64 case WFS_CODE
>> 20: writeFPSR(readRegister(getRd(opcode
))); break;
65 case RFS_CODE
>> 20: writeRegister(getRd(opcode
),readFPSR()); break;
67 #if 0 /* We currently have no use for the FPCR, so there's no point
69 case WFC_CODE
>> 20: writeFPCR(readRegister(getRd(opcode
)));
70 case RFC_CODE
>> 20: writeRegister(getRd(opcode
),readFPCR()); break;
79 unsigned int PerformFLT(const unsigned int opcode
)
81 FPA11
*fpa11
= GET_FPA11();
84 SetRoundingMode(opcode
);
86 switch (opcode
& MASK_ROUNDING_PRECISION
)
90 fpa11
->fType
[getFn(opcode
)] = typeSingle
;
91 fpa11
->fpreg
[getFn(opcode
)].fSingle
=
92 int32_to_float32(readRegister(getRd(opcode
)));
98 fpa11
->fType
[getFn(opcode
)] = typeDouble
;
99 fpa11
->fpreg
[getFn(opcode
)].fDouble
=
100 int32_to_float64(readRegister(getRd(opcode
)));
106 fpa11
->fType
[getFn(opcode
)] = typeExtended
;
107 fpa11
->fpreg
[getFn(opcode
)].fExtended
=
108 int32_to_floatx80(readRegister(getRd(opcode
)));
118 unsigned int PerformFIX(const unsigned int opcode
)
120 FPA11
*fpa11
= GET_FPA11();
121 unsigned int nRc
= 1;
122 unsigned int Fn
= getFm(opcode
);
124 SetRoundingMode(opcode
);
126 switch (fpa11
->fType
[Fn
])
130 writeRegister(getRd(opcode
),
131 float32_to_int32(fpa11
->fpreg
[Fn
].fSingle
));
137 writeRegister(getRd(opcode
),
138 float64_to_int32(fpa11
->fpreg
[Fn
].fDouble
));
144 writeRegister(getRd(opcode
),
145 floatx80_to_int32(fpa11
->fpreg
[Fn
].fExtended
));
156 static unsigned int __inline__
157 PerformComparisonOperation(floatx80 Fn
, floatx80 Fm
)
159 unsigned int flags
= 0;
161 /* test for less than condition */
162 if (floatx80_lt(Fn
,Fm
))
164 flags
|= CC_NEGATIVE
;
167 /* test for equal condition */
168 if (floatx80_eq(Fn
,Fm
))
173 /* test for greater than or equal condition */
174 if (floatx80_lt(Fm
,Fn
))
179 writeConditionCodes(flags
);
183 /* This instruction sets the flags N, Z, C, V in the FPSR. */
185 static unsigned int PerformComparison(const unsigned int opcode
)
187 FPA11
*fpa11
= GET_FPA11();
190 int e_flag
= opcode
& 0x400000; /* 1 if CxFE */
191 int n_flag
= opcode
& 0x200000; /* 1 if CNxx */
192 unsigned int flags
= 0;
194 //printk("PerformComparison(0x%08x)\n",opcode);
199 /* Check for unordered condition and convert all operands to 80-bit
201 ?? Might be some mileage in avoiding this conversion if possible.
202 Eg, if both operands are 32-bit, detect this and do a 32-bit
203 comparison (cheaper than an 80-bit one). */
204 switch (fpa11
->fType
[Fn
])
207 //printk("single.\n");
208 if (float32_is_nan(fpa11
->fpreg
[Fn
].fSingle
))
210 rFn
= float32_to_floatx80(fpa11
->fpreg
[Fn
].fSingle
);
214 //printk("double.\n");
215 if (float64_is_nan(fpa11
->fpreg
[Fn
].fDouble
))
217 rFn
= float64_to_floatx80(fpa11
->fpreg
[Fn
].fDouble
);
221 //printk("extended.\n");
222 if (floatx80_is_nan(fpa11
->fpreg
[Fn
].fExtended
))
224 rFn
= fpa11
->fpreg
[Fn
].fExtended
;
230 if (CONSTANT_FM(opcode
))
232 //printk("Fm is a constant: #%d.\n",Fm);
233 rFm
= getExtendedConstant(Fm
);
234 if (floatx80_is_nan(rFm
))
239 //printk("Fm = r%d which contains a ",Fm);
240 switch (fpa11
->fType
[Fm
])
243 //printk("single.\n");
244 if (float32_is_nan(fpa11
->fpreg
[Fm
].fSingle
))
246 rFm
= float32_to_floatx80(fpa11
->fpreg
[Fm
].fSingle
);
250 //printk("double.\n");
251 if (float64_is_nan(fpa11
->fpreg
[Fm
].fDouble
))
253 rFm
= float64_to_floatx80(fpa11
->fpreg
[Fm
].fDouble
);
257 //printk("extended.\n");
258 if (floatx80_is_nan(fpa11
->fpreg
[Fm
].fExtended
))
260 rFm
= fpa11
->fpreg
[Fm
].fExtended
;
272 return PerformComparisonOperation(rFn
,rFm
);
275 /* ?? The FPA data sheet is pretty vague about this, in particular
276 about whether the non-E comparisons can ever raise exceptions.
277 This implementation is based on a combination of what it says in
278 the data sheet, observation of how the Acorn emulator actually
279 behaves (and how programs expect it to) and guesswork. */
280 flags
|= CC_OVERFLOW
;
281 flags
&= ~(CC_ZERO
| CC_NEGATIVE
);
283 if (BIT_AC
& readFPSR()) flags
|= CC_CARRY
;
285 if (e_flag
) float_raise(float_flag_invalid
);
287 writeConditionCodes(flags
);