[ARM] 3117/1: nwfpe kernel memory info leak
[deliverable/linux.git] / arch / arm / nwfpe / fpa11_cprt.c
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 #include "softfloat.h"
30
31 #ifdef CONFIG_FPE_NWFPE_XP
32 extern flag floatx80_is_nan(floatx80);
33 #endif
34
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();
77 struct roundingData roundData;
78
79 roundData.mode = SetRoundingMode(opcode);
80 roundData.precision = SetRoundingPrecision(opcode);
81 roundData.exception = 0;
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(&roundData, 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 if (roundData.exception)
112 float_raise(roundData.exception);
113
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);
121 struct roundingData roundData;
122
123 roundData.mode = SetRoundingMode(opcode);
124 roundData.precision = SetRoundingPrecision(opcode);
125 roundData.exception = 0;
126
127 switch (fpa11->fType[Fn]) {
128 case typeSingle:
129 {
130 writeRegister(getRd(opcode), float32_to_int32(&roundData, fpa11->fpreg[Fn].fSingle));
131 }
132 break;
133
134 case typeDouble:
135 {
136 writeRegister(getRd(opcode), float64_to_int32(&roundData, fpa11->fpreg[Fn].fDouble));
137 }
138 break;
139
140 #ifdef CONFIG_FPE_NWFPE_XP
141 case typeExtended:
142 {
143 writeRegister(getRd(opcode), floatx80_to_int32(&roundData, fpa11->fpreg[Fn].fExtended));
144 }
145 break;
146 #endif
147
148 default:
149 return 0;
150 }
151
152 if (roundData.exception)
153 float_raise(roundData.exception);
154
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 }
This page took 0.073901 seconds and 5 git commands to generate.