1 /*
2 NetWinder Floating Point Emulator
3 (c) Rebel.COM, 1998,1999
4 (c) Philip Blundell, 1999, 2001
5
6 Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
7
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.
12
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.
17
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.
21 */
22
23 #include "fpa11.h"
24 #include "fpopcode.h"
25 #include "fpa11.inl"
26 #include "fpmodule.h"
27 #include "fpmodule.inl"
28
29 #ifdef CONFIG_FPE_NWFPE_XP
30 extern flag floatx80_is_nan(floatx80);
31 #endif
32 extern flag float64_is_nan(float64);
33 extern flag float32_is_nan(float32);
34
35 void SetRoundingMode(const unsigned int opcode);
36
37 unsigned int PerformFLT(const unsigned int opcode);
38 unsigned int PerformFIX(const unsigned int opcode);
39
40 static unsigned int PerformComparison(const unsigned int opcode);
41
EmulateCPRT(const unsigned int opcode)42 unsigned int EmulateCPRT(const unsigned int opcode)
43 {
44
45 if (opcode & 0x800000) {
46 /* This is some variant of a comparison (PerformComparison
47 will sort out which one). Since most of the other CPRT
48 instructions are oddball cases of some sort or other it
49 makes sense to pull this out into a fast path. */
50 return PerformComparison(opcode);
51 }
52
53 /* Hint to GCC that we'd like a jump table rather than a load of CMPs */
54 switch ((opcode & 0x700000) >> 20) {
55 case FLT_CODE >> 20:
56 return PerformFLT(opcode);
57 break;
58 case FIX_CODE >> 20:
59 return PerformFIX(opcode);
60 break;
61
62 case WFS_CODE >> 20:
63 writeFPSR(readRegister(getRd(opcode)));
64 break;
65 case RFS_CODE >> 20:
66 writeRegister(getRd(opcode), readFPSR());
67 break;
68
69 default:
70 return 0;
71 }
72
73 return 1;
74 }
75
PerformFLT(const unsigned int opcode)76 unsigned int PerformFLT(const unsigned int opcode)
77 {
78 FPA11 *fpa11 = GET_FPA11();
79 SetRoundingMode(opcode);
80 SetRoundingPrecision(opcode);
81
82 switch (opcode & MASK_ROUNDING_PRECISION) {
83 case ROUND_SINGLE:
84 {
85 fpa11->fType[getFn(opcode)] = typeSingle;
86 fpa11->fpreg[getFn(opcode)].fSingle = int32_to_float32(readRegister(getRd(opcode)));
87 }
88 break;
89
90 case ROUND_DOUBLE:
91 {
92 fpa11->fType[getFn(opcode)] = typeDouble;
93 fpa11->fpreg[getFn(opcode)].fDouble = int32_to_float64(readRegister(getRd(opcode)));
94 }
95 break;
96
97 #ifdef CONFIG_FPE_NWFPE_XP
98 case ROUND_EXTENDED:
99 {
100 fpa11->fType[getFn(opcode)] = typeExtended;
101 fpa11->fpreg[getFn(opcode)].fExtended = int32_to_floatx80(readRegister(getRd(opcode)));
102 }
103 break;
104 #endif
105
106 default:
107 return 0;
108 }
109
110 return 1;
111 }
112
PerformFIX(const unsigned int opcode)113 unsigned int PerformFIX(const unsigned int opcode)
114 {
115 FPA11 *fpa11 = GET_FPA11();
116 unsigned int Fn = getFm(opcode);
117
118 SetRoundingMode(opcode);
119
120 switch (fpa11->fType[Fn]) {
121 case typeSingle:
122 {
123 writeRegister(getRd(opcode), float32_to_int32(fpa11->fpreg[Fn].fSingle));
124 }
125 break;
126
127 case typeDouble:
128 {
129 writeRegister(getRd(opcode), float64_to_int32(fpa11->fpreg[Fn].fDouble));
130 }
131 break;
132
133 #ifdef CONFIG_FPE_NWFPE_XP
134 case typeExtended:
135 {
136 writeRegister(getRd(opcode), floatx80_to_int32(fpa11->fpreg[Fn].fExtended));
137 }
138 break;
139 #endif
140
141 default:
142 return 0;
143 }
144
145 return 1;
146 }
147
148 /* This instruction sets the flags N, Z, C, V in the FPSR. */
PerformComparison(const unsigned int opcode)149 static unsigned int PerformComparison(const unsigned int opcode)
150 {
151 FPA11 *fpa11 = GET_FPA11();
152 unsigned int Fn = getFn(opcode), Fm = getFm(opcode);
153 int e_flag = opcode & 0x400000; /* 1 if CxFE */
154 int n_flag = opcode & 0x200000; /* 1 if CNxx */
155 unsigned int flags = 0;
156
157 #ifdef CONFIG_FPE_NWFPE_XP
158 floatx80 rFn, rFm;
159
160 /* Check for unordered condition and convert all operands to 80-bit
161 format.
162 ?? Might be some mileage in avoiding this conversion if possible.
163 Eg, if both operands are 32-bit, detect this and do a 32-bit
164 comparison (cheaper than an 80-bit one). */
165 switch (fpa11->fType[Fn]) {
166 case typeSingle:
167 //printk("single.\n");
168 if (float32_is_nan(fpa11->fpreg[Fn].fSingle))
169 goto unordered;
170 rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle);
171 break;
172
173 case typeDouble:
174 //printk("double.\n");
175 if (float64_is_nan(fpa11->fpreg[Fn].fDouble))
176 goto unordered;
177 rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble);
178 break;
179
180 case typeExtended:
181 //printk("extended.\n");
182 if (floatx80_is_nan(fpa11->fpreg[Fn].fExtended))
183 goto unordered;
184 rFn = fpa11->fpreg[Fn].fExtended;
185 break;
186
187 default:
188 return 0;
189 }
190
191 if (CONSTANT_FM(opcode)) {
192 //printk("Fm is a constant: #%d.\n",Fm);
193 rFm = getExtendedConstant(Fm);
194 if (floatx80_is_nan(rFm))
195 goto unordered;
196 } else {
197 //printk("Fm = r%d which contains a ",Fm);
198 switch (fpa11->fType[Fm]) {
199 case typeSingle:
200 //printk("single.\n");
201 if (float32_is_nan(fpa11->fpreg[Fm].fSingle))
202 goto unordered;
203 rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle);
204 break;
205
206 case typeDouble:
207 //printk("double.\n");
208 if (float64_is_nan(fpa11->fpreg[Fm].fDouble))
209 goto unordered;
210 rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble);
211 break;
212
213 case typeExtended:
214 //printk("extended.\n");
215 if (floatx80_is_nan(fpa11->fpreg[Fm].fExtended))
216 goto unordered;
217 rFm = fpa11->fpreg[Fm].fExtended;
218 break;
219
220 default:
221 return 0;
222 }
223 }
224
225 if (n_flag)
226 rFm.high ^= 0x8000;
227
228 /* test for less than condition */
229 if (floatx80_lt(rFn, rFm))
230 flags |= CC_NEGATIVE;
231
232 /* test for equal condition */
233 if (floatx80_eq(rFn, rFm))
234 flags |= CC_ZERO;
235
236 /* test for greater than or equal condition */
237 if (floatx80_lt(rFm, rFn))
238 flags |= CC_CARRY;
239
240 #else
241 if (CONSTANT_FM(opcode)) {
242 /* Fm is a constant. Do the comparison in whatever precision
243 Fn happens to be stored in. */
244 if (fpa11->fType[Fn] == typeSingle) {
245 float32 rFm = getSingleConstant(Fm);
246 float32 rFn = fpa11->fpreg[Fn].fSingle;
247
248 if (float32_is_nan(rFn))
249 goto unordered;
250
251 if (n_flag)
252 rFm ^= 0x80000000;
253
254 /* test for less than condition */
255 if (float32_lt_nocheck(rFn, rFm))
256 flags |= CC_NEGATIVE;
257
258 /* test for equal condition */
259 if (float32_eq_nocheck(rFn, rFm))
260 flags |= CC_ZERO;
261
262 /* test for greater than or equal condition */
263 if (float32_lt_nocheck(rFm, rFn))
264 flags |= CC_CARRY;
265 } else {
266 float64 rFm = getDoubleConstant(Fm);
267 float64 rFn = fpa11->fpreg[Fn].fDouble;
268
269 if (float64_is_nan(rFn))
270 goto unordered;
271
272 if (n_flag)
273 rFm ^= 0x8000000000000000ULL;
274
275 /* test for less than condition */
276 if (float64_lt_nocheck(rFn, rFm))
277 flags |= CC_NEGATIVE;
278
279 /* test for equal condition */
280 if (float64_eq_nocheck(rFn, rFm))
281 flags |= CC_ZERO;
282
283 /* test for greater than or equal condition */
284 if (float64_lt_nocheck(rFm, rFn))
285 flags |= CC_CARRY;
286 }
287 } else {
288 /* Both operands are in registers. */
289 if (fpa11->fType[Fn] == typeSingle
290 && fpa11->fType[Fm] == typeSingle) {
291 float32 rFm = fpa11->fpreg[Fm].fSingle;
292 float32 rFn = fpa11->fpreg[Fn].fSingle;
293
294 if (float32_is_nan(rFn)
295 || float32_is_nan(rFm))
296 goto unordered;
297
298 if (n_flag)
299 rFm ^= 0x80000000;
300
301 /* test for less than condition */
302 if (float32_lt_nocheck(rFn, rFm))
303 flags |= CC_NEGATIVE;
304
305 /* test for equal condition */
306 if (float32_eq_nocheck(rFn, rFm))
307 flags |= CC_ZERO;
308
309 /* test for greater than or equal condition */
310 if (float32_lt_nocheck(rFm, rFn))
311 flags |= CC_CARRY;
312 } else {
313 /* Promote 32-bit operand to 64 bits. */
314 float64 rFm, rFn;
315
316 rFm = (fpa11->fType[Fm] == typeSingle) ?
317 float32_to_float64(fpa11->fpreg[Fm].fSingle)
318 : fpa11->fpreg[Fm].fDouble;
319
320 rFn = (fpa11->fType[Fn] == typeSingle) ?
321 float32_to_float64(fpa11->fpreg[Fn].fSingle)
322 : fpa11->fpreg[Fn].fDouble;
323
324 if (float64_is_nan(rFn)
325 || float64_is_nan(rFm))
326 goto unordered;
327
328 if (n_flag)
329 rFm ^= 0x8000000000000000ULL;
330
331 /* test for less than condition */
332 if (float64_lt_nocheck(rFn, rFm))
333 flags |= CC_NEGATIVE;
334
335 /* test for equal condition */
336 if (float64_eq_nocheck(rFn, rFm))
337 flags |= CC_ZERO;
338
339 /* test for greater than or equal condition */
340 if (float64_lt_nocheck(rFm, rFn))
341 flags |= CC_CARRY;
342 }
343 }
344
345 #endif
346
347 writeConditionCodes(flags);
348
349 return 1;
350
351 unordered:
352 /* ?? The FPA data sheet is pretty vague about this, in particular
353 about whether the non-E comparisons can ever raise exceptions.
354 This implementation is based on a combination of what it says in
355 the data sheet, observation of how the Acorn emulator actually
356 behaves (and how programs expect it to) and guesswork. */
357 flags |= CC_OVERFLOW;
358 flags &= ~(CC_ZERO | CC_NEGATIVE);
359
360 if (BIT_AC & readFPSR())
361 flags |= CC_CARRY;
362
363 if (e_flag)
364 float_raise(float_flag_invalid);
365
366 writeConditionCodes(flags);
367 return 1;
368 }
369