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"
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
);
42 PerformComparison(const unsigned int opcode
);
44 unsigned int EmulateCPRT(const unsigned int opcode
)
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;
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.
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
)));
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;
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
)
117 fpa11
->fpreg
[getFn(opcode
)].fType
= typeSingle
;
118 fpa11
->fpreg
[getFn(opcode
)].fValue
.fSingle
=
119 int32_to_float32(readRegister(getRd(opcode
)));
125 fpa11
->fpreg
[getFn(opcode
)].fType
= typeDouble
;
126 fpa11
->fpreg
[getFn(opcode
)].fValue
.fDouble
=
127 int32_to_float64(readRegister(getRd(opcode
)));
133 fpa11
->fpreg
[getFn(opcode
)].fType
= typeExtended
;
134 fpa11
->fpreg
[getFn(opcode
)].fValue
.fExtended
=
135 int32_to_floatx80(readRegister(getRd(opcode
)));
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
)
156 writeRegister(getRd(opcode
),
157 float32_to_int32(fpa11
->fpreg
[Fn
].fValue
.fSingle
));
163 writeRegister(getRd(opcode
),
164 float64_to_int32(fpa11
->fpreg
[Fn
].fValue
.fDouble
));
170 writeRegister(getRd(opcode
),
171 floatx80_to_int32(fpa11
->fpreg
[Fn
].fValue
.fExtended
));
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
))
199 /* test for greater than or equal condition */
200 if (floatx80_lt(Fm
,Fn
))
205 writeConditionCodes(flags
);
209 /* This instruction sets the flags N, Z, C, V in the FPSR. */
211 static unsigned int PerformComparison(const unsigned int opcode
)
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);
224 /* Check for unordered condition and convert all operands to 80-bit
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
)
232 //fp_printk("single.\n");
233 if (float32_is_nan(fpa11
->fpreg
[Fn
].fValue
.fSingle
))
235 rFn
= float32_to_floatx80(fpa11
->fpreg
[Fn
].fValue
.fSingle
);
239 //fp_printk("double.\n");
240 if (float64_is_nan(fpa11
->fpreg
[Fn
].fValue
.fDouble
))
242 rFn
= float64_to_floatx80(fpa11
->fpreg
[Fn
].fValue
.fDouble
);
246 //fp_printk("extended.\n");
247 if (floatx80_is_nan(fpa11
->fpreg
[Fn
].fValue
.fExtended
))
249 rFn
= fpa11
->fpreg
[Fn
].fValue
.fExtended
;
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
))
264 //fp_printk("Fm = r%d which contains a ",Fm);
265 switch (fpa11
->fpreg
[Fm
].fType
)
268 //fp_printk("single.\n");
269 if (float32_is_nan(fpa11
->fpreg
[Fm
].fValue
.fSingle
))
271 rFm
= float32_to_floatx80(fpa11
->fpreg
[Fm
].fValue
.fSingle
);
275 //fp_printk("double.\n");
276 if (float64_is_nan(fpa11
->fpreg
[Fm
].fValue
.fDouble
))
278 rFm
= float64_to_floatx80(fpa11
->fpreg
[Fm
].fValue
.fDouble
);
282 //fp_printk("extended.\n");
283 if (floatx80_is_nan(fpa11
->fpreg
[Fm
].fValue
.fExtended
))
285 rFm
= fpa11
->fpreg
[Fm
].fValue
.fExtended
;
297 return PerformComparisonOperation(rFn
,rFm
);
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
);