Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / arch / arm / nwfpe / fpa11_cpdt.c
1 /*
2     NetWinder Floating Point Emulator
3     (c) Rebel.com, 1998-1999
4     (c) Philip Blundell, 1998, 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 "softfloat.h"
26 #include "fpopcode.h"
27 #include "fpmodule.h"
28 #include "fpmodule.inl"
29
30 #include <asm/uaccess.h>
31
32 static inline void loadSingle(const unsigned int Fn, const unsigned int __user *pMem)
33 {
34         FPA11 *fpa11 = GET_FPA11();
35         fpa11->fType[Fn] = typeSingle;
36         get_user(fpa11->fpreg[Fn].fSingle, pMem);
37 }
38
39 static inline void loadDouble(const unsigned int Fn, const unsigned int __user *pMem)
40 {
41         FPA11 *fpa11 = GET_FPA11();
42         unsigned int *p;
43         p = (unsigned int *) &fpa11->fpreg[Fn].fDouble;
44         fpa11->fType[Fn] = typeDouble;
45 #ifdef __ARMEB__
46         get_user(p[0], &pMem[0]);       /* sign & exponent */
47         get_user(p[1], &pMem[1]);
48 #else
49         get_user(p[0], &pMem[1]);
50         get_user(p[1], &pMem[0]);       /* sign & exponent */
51 #endif
52 }
53
54 #ifdef CONFIG_FPE_NWFPE_XP
55 static inline void loadExtended(const unsigned int Fn, const unsigned int __user *pMem)
56 {
57         FPA11 *fpa11 = GET_FPA11();
58         unsigned int *p;
59         p = (unsigned int *) &fpa11->fpreg[Fn].fExtended;
60         fpa11->fType[Fn] = typeExtended;
61         get_user(p[0], &pMem[0]);       /* sign & exponent */
62 #ifdef __ARMEB__
63         get_user(p[1], &pMem[1]);       /* ms bits */
64         get_user(p[2], &pMem[2]);       /* ls bits */
65 #else
66         get_user(p[1], &pMem[2]);       /* ls bits */
67         get_user(p[2], &pMem[1]);       /* ms bits */
68 #endif
69 }
70 #endif
71
72 static inline void loadMultiple(const unsigned int Fn, const unsigned int __user *pMem)
73 {
74         FPA11 *fpa11 = GET_FPA11();
75         register unsigned int *p;
76         unsigned long x;
77
78         p = (unsigned int *) &(fpa11->fpreg[Fn]);
79         get_user(x, &pMem[0]);
80         fpa11->fType[Fn] = (x >> 14) & 0x00000003;
81
82         switch (fpa11->fType[Fn]) {
83         case typeSingle:
84         case typeDouble:
85                 {
86                         get_user(p[0], &pMem[2]);       /* Single */
87                         get_user(p[1], &pMem[1]);       /* double msw */
88                         p[2] = 0;                       /* empty */
89                 }
90                 break;
91
92 #ifdef CONFIG_FPE_NWFPE_XP
93         case typeExtended:
94                 {
95                         get_user(p[1], &pMem[2]);
96                         get_user(p[2], &pMem[1]);       /* msw */
97                         p[0] = (x & 0x80003fff);
98                 }
99                 break;
100 #endif
101         }
102 }
103
104 static inline void storeSingle(struct roundingData *roundData, const unsigned int Fn, unsigned int __user *pMem)
105 {
106         FPA11 *fpa11 = GET_FPA11();
107         union {
108                 float32 f;
109                 unsigned int i[1];
110         } val;
111
112         switch (fpa11->fType[Fn]) {
113         case typeDouble:
114                 val.f = float64_to_float32(roundData, fpa11->fpreg[Fn].fDouble);
115                 break;
116
117 #ifdef CONFIG_FPE_NWFPE_XP
118         case typeExtended:
119                 val.f = floatx80_to_float32(roundData, fpa11->fpreg[Fn].fExtended);
120                 break;
121 #endif
122
123         default:
124                 val.f = fpa11->fpreg[Fn].fSingle;
125         }
126
127         put_user(val.i[0], pMem);
128 }
129
130 static inline void storeDouble(struct roundingData *roundData, const unsigned int Fn, unsigned int __user *pMem)
131 {
132         FPA11 *fpa11 = GET_FPA11();
133         union {
134                 float64 f;
135                 unsigned int i[2];
136         } val;
137
138         switch (fpa11->fType[Fn]) {
139         case typeSingle:
140                 val.f = float32_to_float64(fpa11->fpreg[Fn].fSingle);
141                 break;
142
143 #ifdef CONFIG_FPE_NWFPE_XP
144         case typeExtended:
145                 val.f = floatx80_to_float64(roundData, fpa11->fpreg[Fn].fExtended);
146                 break;
147 #endif
148
149         default:
150                 val.f = fpa11->fpreg[Fn].fDouble;
151         }
152
153 #ifdef __ARMEB__
154         put_user(val.i[0], &pMem[0]);   /* msw */
155         put_user(val.i[1], &pMem[1]);   /* lsw */
156 #else
157         put_user(val.i[1], &pMem[0]);   /* msw */
158         put_user(val.i[0], &pMem[1]);   /* lsw */
159 #endif
160 }
161
162 #ifdef CONFIG_FPE_NWFPE_XP
163 static inline void storeExtended(const unsigned int Fn, unsigned int __user *pMem)
164 {
165         FPA11 *fpa11 = GET_FPA11();
166         union {
167                 floatx80 f;
168                 unsigned int i[3];
169         } val;
170
171         switch (fpa11->fType[Fn]) {
172         case typeSingle:
173                 val.f = float32_to_floatx80(fpa11->fpreg[Fn].fSingle);
174                 break;
175
176         case typeDouble:
177                 val.f = float64_to_floatx80(fpa11->fpreg[Fn].fDouble);
178                 break;
179
180         default:
181                 val.f = fpa11->fpreg[Fn].fExtended;
182         }
183
184         put_user(val.i[0], &pMem[0]);   /* sign & exp */
185 #ifdef __ARMEB__
186         put_user(val.i[1], &pMem[1]);   /* msw */
187         put_user(val.i[2], &pMem[2]);
188 #else
189         put_user(val.i[1], &pMem[2]);
190         put_user(val.i[2], &pMem[1]);   /* msw */
191 #endif
192 }
193 #endif
194
195 static inline void storeMultiple(const unsigned int Fn, unsigned int __user *pMem)
196 {
197         FPA11 *fpa11 = GET_FPA11();
198         register unsigned int nType, *p;
199
200         p = (unsigned int *) &(fpa11->fpreg[Fn]);
201         nType = fpa11->fType[Fn];
202
203         switch (nType) {
204         case typeSingle:
205         case typeDouble:
206                 {
207                         put_user(p[0], &pMem[2]);       /* single */
208                         put_user(p[1], &pMem[1]);       /* double msw */
209                         put_user(nType << 14, &pMem[0]);
210                 }
211                 break;
212
213 #ifdef CONFIG_FPE_NWFPE_XP
214         case typeExtended:
215                 {
216                         put_user(p[2], &pMem[1]);       /* msw */
217                         put_user(p[1], &pMem[2]);
218                         put_user((p[0] & 0x80003fff) | (nType << 14), &pMem[0]);
219                 }
220                 break;
221 #endif
222         }
223 }
224
225 unsigned int PerformLDF(const unsigned int opcode)
226 {
227         unsigned int __user *pBase, *pAddress, *pFinal;
228         unsigned int nRc = 1, write_back = WRITE_BACK(opcode);
229
230         pBase = (unsigned int __user *) readRegister(getRn(opcode));
231         if (REG_PC == getRn(opcode)) {
232                 pBase += 2;
233                 write_back = 0;
234         }
235
236         pFinal = pBase;
237         if (BIT_UP_SET(opcode))
238                 pFinal += getOffset(opcode);
239         else
240                 pFinal -= getOffset(opcode);
241
242         if (PREINDEXED(opcode))
243                 pAddress = pFinal;
244         else
245                 pAddress = pBase;
246
247         switch (opcode & MASK_TRANSFER_LENGTH) {
248         case TRANSFER_SINGLE:
249                 loadSingle(getFd(opcode), pAddress);
250                 break;
251         case TRANSFER_DOUBLE:
252                 loadDouble(getFd(opcode), pAddress);
253                 break;
254 #ifdef CONFIG_FPE_NWFPE_XP
255         case TRANSFER_EXTENDED:
256                 loadExtended(getFd(opcode), pAddress);
257                 break;
258 #endif
259         default:
260                 nRc = 0;
261         }
262
263         if (write_back)
264                 writeRegister(getRn(opcode), (unsigned long) pFinal);
265         return nRc;
266 }
267
268 unsigned int PerformSTF(const unsigned int opcode)
269 {
270         unsigned int __user *pBase, *pAddress, *pFinal;
271         unsigned int nRc = 1, write_back = WRITE_BACK(opcode);
272         struct roundingData roundData;
273
274         roundData.mode = SetRoundingMode(opcode);
275         roundData.precision = SetRoundingPrecision(opcode);
276         roundData.exception = 0;
277
278         pBase = (unsigned int __user *) readRegister(getRn(opcode));
279         if (REG_PC == getRn(opcode)) {
280                 pBase += 2;
281                 write_back = 0;
282         }
283
284         pFinal = pBase;
285         if (BIT_UP_SET(opcode))
286                 pFinal += getOffset(opcode);
287         else
288                 pFinal -= getOffset(opcode);
289
290         if (PREINDEXED(opcode))
291                 pAddress = pFinal;
292         else
293                 pAddress = pBase;
294
295         switch (opcode & MASK_TRANSFER_LENGTH) {
296         case TRANSFER_SINGLE:
297                 storeSingle(&roundData, getFd(opcode), pAddress);
298                 break;
299         case TRANSFER_DOUBLE:
300                 storeDouble(&roundData, getFd(opcode), pAddress);
301                 break;
302 #ifdef CONFIG_FPE_NWFPE_XP
303         case TRANSFER_EXTENDED:
304                 storeExtended(getFd(opcode), pAddress);
305                 break;
306 #endif
307         default:
308                 nRc = 0;
309         }
310
311         if (roundData.exception)
312                 float_raise(roundData.exception);
313
314         if (write_back)
315                 writeRegister(getRn(opcode), (unsigned long) pFinal);
316         return nRc;
317 }
318
319 unsigned int PerformLFM(const unsigned int opcode)
320 {
321         unsigned int __user *pBase, *pAddress, *pFinal;
322         unsigned int i, Fd, write_back = WRITE_BACK(opcode);
323
324         pBase = (unsigned int __user *) readRegister(getRn(opcode));
325         if (REG_PC == getRn(opcode)) {
326                 pBase += 2;
327                 write_back = 0;
328         }
329
330         pFinal = pBase;
331         if (BIT_UP_SET(opcode))
332                 pFinal += getOffset(opcode);
333         else
334                 pFinal -= getOffset(opcode);
335
336         if (PREINDEXED(opcode))
337                 pAddress = pFinal;
338         else
339                 pAddress = pBase;
340
341         Fd = getFd(opcode);
342         for (i = getRegisterCount(opcode); i > 0; i--) {
343                 loadMultiple(Fd, pAddress);
344                 pAddress += 3;
345                 Fd++;
346                 if (Fd == 8)
347                         Fd = 0;
348         }
349
350         if (write_back)
351                 writeRegister(getRn(opcode), (unsigned long) pFinal);
352         return 1;
353 }
354
355 unsigned int PerformSFM(const unsigned int opcode)
356 {
357         unsigned int __user *pBase, *pAddress, *pFinal;
358         unsigned int i, Fd, write_back = WRITE_BACK(opcode);
359
360         pBase = (unsigned int __user *) readRegister(getRn(opcode));
361         if (REG_PC == getRn(opcode)) {
362                 pBase += 2;
363                 write_back = 0;
364         }
365
366         pFinal = pBase;
367         if (BIT_UP_SET(opcode))
368                 pFinal += getOffset(opcode);
369         else
370                 pFinal -= getOffset(opcode);
371
372         if (PREINDEXED(opcode))
373                 pAddress = pFinal;
374         else
375                 pAddress = pBase;
376
377         Fd = getFd(opcode);
378         for (i = getRegisterCount(opcode); i > 0; i--) {
379                 storeMultiple(Fd, pAddress);
380                 pAddress += 3;
381                 Fd++;
382                 if (Fd == 8)
383                         Fd = 0;
384         }
385
386         if (write_back)
387                 writeRegister(getRn(opcode), (unsigned long) pFinal);
388         return 1;
389 }
390
391 unsigned int EmulateCPDT(const unsigned int opcode)
392 {
393         unsigned int nRc = 0;
394
395         if (LDF_OP(opcode)) {
396                 nRc = PerformLDF(opcode);
397         } else if (LFM_OP(opcode)) {
398                 nRc = PerformLFM(opcode);
399         } else if (STF_OP(opcode)) {
400                 nRc = PerformSTF(opcode);
401         } else if (SFM_OP(opcode)) {
402                 nRc = PerformSFM(opcode);
403         } else {
404                 nRc = 0;
405         }
406
407         return nRc;
408 }