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