Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 <linux/config.h> | |
24 | #include "fpa11.h" | |
25 | #include "fpopcode.h" | |
26 | #include "fpa11.inl" | |
27 | #include "fpmodule.h" | |
28 | #include "fpmodule.inl" | |
6ec5e7f3 | 29 | #include "softfloat.h" |
1da177e4 LT |
30 | |
31 | #ifdef CONFIG_FPE_NWFPE_XP | |
32 | extern flag floatx80_is_nan(floatx80); | |
33 | #endif | |
1da177e4 | 34 | |
1da177e4 LT |
35 | unsigned int PerformFLT(const unsigned int opcode); |
36 | unsigned int PerformFIX(const unsigned int opcode); | |
37 | ||
38 | static unsigned int PerformComparison(const unsigned int opcode); | |
39 | ||
40 | unsigned int EmulateCPRT(const unsigned int opcode) | |
41 | { | |
42 | ||
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); | |
49 | } | |
50 | ||
51 | /* Hint to GCC that we'd like a jump table rather than a load of CMPs */ | |
52 | switch ((opcode & 0x700000) >> 20) { | |
53 | case FLT_CODE >> 20: | |
54 | return PerformFLT(opcode); | |
55 | break; | |
56 | case FIX_CODE >> 20: | |
57 | return PerformFIX(opcode); | |
58 | break; | |
59 | ||
60 | case WFS_CODE >> 20: | |
61 | writeFPSR(readRegister(getRd(opcode))); | |
62 | break; | |
63 | case RFS_CODE >> 20: | |
64 | writeRegister(getRd(opcode), readFPSR()); | |
65 | break; | |
66 | ||
67 | default: | |
68 | return 0; | |
69 | } | |
70 | ||
71 | return 1; | |
72 | } | |
73 | ||
74 | unsigned int PerformFLT(const unsigned int opcode) | |
75 | { | |
76 | FPA11 *fpa11 = GET_FPA11(); | |
f148af25 RP |
77 | struct roundingData roundData; |
78 | ||
79 | roundData.mode = SetRoundingMode(opcode); | |
80 | roundData.precision = SetRoundingPrecision(opcode); | |
81 | roundData.exception = 0; | |
1da177e4 LT |
82 | |
83 | switch (opcode & MASK_ROUNDING_PRECISION) { | |
84 | case ROUND_SINGLE: | |
85 | { | |
86 | fpa11->fType[getFn(opcode)] = typeSingle; | |
f148af25 | 87 | fpa11->fpreg[getFn(opcode)].fSingle = int32_to_float32(&roundData, readRegister(getRd(opcode))); |
1da177e4 LT |
88 | } |
89 | break; | |
90 | ||
91 | case ROUND_DOUBLE: | |
92 | { | |
93 | fpa11->fType[getFn(opcode)] = typeDouble; | |
94 | fpa11->fpreg[getFn(opcode)].fDouble = int32_to_float64(readRegister(getRd(opcode))); | |
95 | } | |
96 | break; | |
97 | ||
98 | #ifdef CONFIG_FPE_NWFPE_XP | |
99 | case ROUND_EXTENDED: | |
100 | { | |
101 | fpa11->fType[getFn(opcode)] = typeExtended; | |
102 | fpa11->fpreg[getFn(opcode)].fExtended = int32_to_floatx80(readRegister(getRd(opcode))); | |
103 | } | |
104 | break; | |
105 | #endif | |
106 | ||
107 | default: | |
108 | return 0; | |
109 | } | |
110 | ||
f148af25 RP |
111 | if (roundData.exception) |
112 | float_raise(roundData.exception); | |
113 | ||
1da177e4 LT |
114 | return 1; |
115 | } | |
116 | ||
117 | unsigned int PerformFIX(const unsigned int opcode) | |
118 | { | |
119 | FPA11 *fpa11 = GET_FPA11(); | |
120 | unsigned int Fn = getFm(opcode); | |
f148af25 | 121 | struct roundingData roundData; |
1da177e4 | 122 | |
f148af25 RP |
123 | roundData.mode = SetRoundingMode(opcode); |
124 | roundData.precision = SetRoundingPrecision(opcode); | |
125 | roundData.exception = 0; | |
1da177e4 LT |
126 | |
127 | switch (fpa11->fType[Fn]) { | |
128 | case typeSingle: | |
129 | { | |
f148af25 | 130 | writeRegister(getRd(opcode), float32_to_int32(&roundData, fpa11->fpreg[Fn].fSingle)); |
1da177e4 LT |
131 | } |
132 | break; | |
133 | ||
134 | case typeDouble: | |
135 | { | |
f148af25 | 136 | writeRegister(getRd(opcode), float64_to_int32(&roundData, fpa11->fpreg[Fn].fDouble)); |
1da177e4 LT |
137 | } |
138 | break; | |
139 | ||
140 | #ifdef CONFIG_FPE_NWFPE_XP | |
141 | case typeExtended: | |
142 | { | |
f148af25 | 143 | writeRegister(getRd(opcode), floatx80_to_int32(&roundData, fpa11->fpreg[Fn].fExtended)); |
1da177e4 LT |
144 | } |
145 | break; | |
146 | #endif | |
147 | ||
148 | default: | |
149 | return 0; | |
150 | } | |
151 | ||
f148af25 RP |
152 | if (roundData.exception) |
153 | float_raise(roundData.exception); | |
154 | ||
1da177e4 LT |
155 | return 1; |
156 | } | |
157 | ||
158 | /* This instruction sets the flags N, Z, C, V in the FPSR. */ | |
159 | static unsigned int PerformComparison(const unsigned int opcode) | |
160 | { | |
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; | |
166 | ||
167 | #ifdef CONFIG_FPE_NWFPE_XP | |
168 | floatx80 rFn, rFm; | |
169 | ||
170 | /* Check for unordered condition and convert all operands to 80-bit | |
171 | format. | |
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]) { | |
176 | case typeSingle: | |
177 | //printk("single.\n"); | |
178 | if (float32_is_nan(fpa11->fpreg[Fn].fSingle)) | |
179 | goto unordered; | |
180 | rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle); | |
181 | break; | |
182 | ||
183 | case typeDouble: | |
184 | //printk("double.\n"); | |
185 | if (float64_is_nan(fpa11->fpreg[Fn].fDouble)) | |
186 | goto unordered; | |
187 | rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble); | |
188 | break; | |
189 | ||
190 | case typeExtended: | |
191 | //printk("extended.\n"); | |
192 | if (floatx80_is_nan(fpa11->fpreg[Fn].fExtended)) | |
193 | goto unordered; | |
194 | rFn = fpa11->fpreg[Fn].fExtended; | |
195 | break; | |
196 | ||
197 | default: | |
198 | return 0; | |
199 | } | |
200 | ||
201 | if (CONSTANT_FM(opcode)) { | |
202 | //printk("Fm is a constant: #%d.\n",Fm); | |
203 | rFm = getExtendedConstant(Fm); | |
204 | if (floatx80_is_nan(rFm)) | |
205 | goto unordered; | |
206 | } else { | |
207 | //printk("Fm = r%d which contains a ",Fm); | |
208 | switch (fpa11->fType[Fm]) { | |
209 | case typeSingle: | |
210 | //printk("single.\n"); | |
211 | if (float32_is_nan(fpa11->fpreg[Fm].fSingle)) | |
212 | goto unordered; | |
213 | rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle); | |
214 | break; | |
215 | ||
216 | case typeDouble: | |
217 | //printk("double.\n"); | |
218 | if (float64_is_nan(fpa11->fpreg[Fm].fDouble)) | |
219 | goto unordered; | |
220 | rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble); | |
221 | break; | |
222 | ||
223 | case typeExtended: | |
224 | //printk("extended.\n"); | |
225 | if (floatx80_is_nan(fpa11->fpreg[Fm].fExtended)) | |
226 | goto unordered; | |
227 | rFm = fpa11->fpreg[Fm].fExtended; | |
228 | break; | |
229 | ||
230 | default: | |
231 | return 0; | |
232 | } | |
233 | } | |
234 | ||
235 | if (n_flag) | |
236 | rFm.high ^= 0x8000; | |
237 | ||
238 | /* test for less than condition */ | |
239 | if (floatx80_lt(rFn, rFm)) | |
240 | flags |= CC_NEGATIVE; | |
241 | ||
242 | /* test for equal condition */ | |
243 | if (floatx80_eq(rFn, rFm)) | |
244 | flags |= CC_ZERO; | |
245 | ||
246 | /* test for greater than or equal condition */ | |
247 | if (floatx80_lt(rFm, rFn)) | |
248 | flags |= CC_CARRY; | |
249 | ||
250 | #else | |
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; | |
257 | ||
258 | if (float32_is_nan(rFn)) | |
259 | goto unordered; | |
260 | ||
261 | if (n_flag) | |
262 | rFm ^= 0x80000000; | |
263 | ||
264 | /* test for less than condition */ | |
265 | if (float32_lt_nocheck(rFn, rFm)) | |
266 | flags |= CC_NEGATIVE; | |
267 | ||
268 | /* test for equal condition */ | |
269 | if (float32_eq_nocheck(rFn, rFm)) | |
270 | flags |= CC_ZERO; | |
271 | ||
272 | /* test for greater than or equal condition */ | |
273 | if (float32_lt_nocheck(rFm, rFn)) | |
274 | flags |= CC_CARRY; | |
275 | } else { | |
276 | float64 rFm = getDoubleConstant(Fm); | |
277 | float64 rFn = fpa11->fpreg[Fn].fDouble; | |
278 | ||
279 | if (float64_is_nan(rFn)) | |
280 | goto unordered; | |
281 | ||
282 | if (n_flag) | |
283 | rFm ^= 0x8000000000000000ULL; | |
284 | ||
285 | /* test for less than condition */ | |
286 | if (float64_lt_nocheck(rFn, rFm)) | |
287 | flags |= CC_NEGATIVE; | |
288 | ||
289 | /* test for equal condition */ | |
290 | if (float64_eq_nocheck(rFn, rFm)) | |
291 | flags |= CC_ZERO; | |
292 | ||
293 | /* test for greater than or equal condition */ | |
294 | if (float64_lt_nocheck(rFm, rFn)) | |
295 | flags |= CC_CARRY; | |
296 | } | |
297 | } else { | |
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; | |
303 | ||
304 | if (float32_is_nan(rFn) | |
305 | || float32_is_nan(rFm)) | |
306 | goto unordered; | |
307 | ||
308 | if (n_flag) | |
309 | rFm ^= 0x80000000; | |
310 | ||
311 | /* test for less than condition */ | |
312 | if (float32_lt_nocheck(rFn, rFm)) | |
313 | flags |= CC_NEGATIVE; | |
314 | ||
315 | /* test for equal condition */ | |
316 | if (float32_eq_nocheck(rFn, rFm)) | |
317 | flags |= CC_ZERO; | |
318 | ||
319 | /* test for greater than or equal condition */ | |
320 | if (float32_lt_nocheck(rFm, rFn)) | |
321 | flags |= CC_CARRY; | |
322 | } else { | |
323 | /* Promote 32-bit operand to 64 bits. */ | |
324 | float64 rFm, rFn; | |
325 | ||
326 | rFm = (fpa11->fType[Fm] == typeSingle) ? | |
327 | float32_to_float64(fpa11->fpreg[Fm].fSingle) | |
328 | : fpa11->fpreg[Fm].fDouble; | |
329 | ||
330 | rFn = (fpa11->fType[Fn] == typeSingle) ? | |
331 | float32_to_float64(fpa11->fpreg[Fn].fSingle) | |
332 | : fpa11->fpreg[Fn].fDouble; | |
333 | ||
334 | if (float64_is_nan(rFn) | |
335 | || float64_is_nan(rFm)) | |
336 | goto unordered; | |
337 | ||
338 | if (n_flag) | |
339 | rFm ^= 0x8000000000000000ULL; | |
340 | ||
341 | /* test for less than condition */ | |
342 | if (float64_lt_nocheck(rFn, rFm)) | |
343 | flags |= CC_NEGATIVE; | |
344 | ||
345 | /* test for equal condition */ | |
346 | if (float64_eq_nocheck(rFn, rFm)) | |
347 | flags |= CC_ZERO; | |
348 | ||
349 | /* test for greater than or equal condition */ | |
350 | if (float64_lt_nocheck(rFm, rFn)) | |
351 | flags |= CC_CARRY; | |
352 | } | |
353 | } | |
354 | ||
355 | #endif | |
356 | ||
357 | writeConditionCodes(flags); | |
358 | ||
359 | return 1; | |
360 | ||
361 | unordered: | |
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); | |
369 | ||
370 | if (BIT_AC & readFPSR()) | |
371 | flags |= CC_CARRY; | |
372 | ||
373 | if (e_flag) | |
374 | float_raise(float_flag_invalid); | |
375 | ||
376 | writeConditionCodes(flags); | |
377 | return 1; | |
378 | } |