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