blob: 32859fa8dcfcf5c5d85c200c65b588c4e73831c2 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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
32static 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
39static 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
55static 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 */
Lennert Buytenhekbedf1422005-11-07 21:12:08 +000062#ifdef __ARMEB__
63 get_user(p[1], &pMem[1]); /* ms bits */
64 get_user(p[2], &pMem[2]); /* ls bits */
65#else
Linus Torvalds1da177e2005-04-16 15:20:36 -070066 get_user(p[1], &pMem[2]); /* ls bits */
67 get_user(p[2], &pMem[1]); /* ms bits */
Lennert Buytenhekbedf1422005-11-07 21:12:08 +000068#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070069}
70#endif
71
72static 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
Richard Purdief148af22005-08-03 19:49:17 +0100104static inline void storeSingle(struct roundingData *roundData, const unsigned int Fn, unsigned int __user *pMem)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105{
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:
Richard Purdief148af22005-08-03 19:49:17 +0100114 val.f = float64_to_float32(roundData, fpa11->fpreg[Fn].fDouble);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 break;
116
117#ifdef CONFIG_FPE_NWFPE_XP
118 case typeExtended:
Richard Purdief148af22005-08-03 19:49:17 +0100119 val.f = floatx80_to_float32(roundData, fpa11->fpreg[Fn].fExtended);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120 break;
121#endif
122
123 default:
124 val.f = fpa11->fpreg[Fn].fSingle;
125 }
126
127 put_user(val.i[0], pMem);
128}
129
Richard Purdief148af22005-08-03 19:49:17 +0100130static inline void storeDouble(struct roundingData *roundData, const unsigned int Fn, unsigned int __user *pMem)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131{
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:
Richard Purdief148af22005-08-03 19:49:17 +0100145 val.f = floatx80_to_float64(roundData, fpa11->fpreg[Fn].fExtended);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 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
163static 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 */
Lennert Buytenhekbedf1422005-11-07 21:12:08 +0000185#ifdef __ARMEB__
186 put_user(val.i[1], &pMem[1]); /* msw */
187 put_user(val.i[2], &pMem[2]);
188#else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 put_user(val.i[1], &pMem[2]);
190 put_user(val.i[2], &pMem[1]); /* msw */
Lennert Buytenhekbedf1422005-11-07 21:12:08 +0000191#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192}
193#endif
194
195static 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
225unsigned 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
268unsigned int PerformSTF(const unsigned int opcode)
269{
270 unsigned int __user *pBase, *pAddress, *pFinal;
271 unsigned int nRc = 1, write_back = WRITE_BACK(opcode);
Richard Purdief148af22005-08-03 19:49:17 +0100272 struct roundingData roundData;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273
Richard Purdief148af22005-08-03 19:49:17 +0100274 roundData.mode = SetRoundingMode(opcode);
275 roundData.precision = SetRoundingPrecision(opcode);
276 roundData.exception = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277
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:
Richard Purdief148af22005-08-03 19:49:17 +0100297 storeSingle(&roundData, getFd(opcode), pAddress);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 break;
299 case TRANSFER_DOUBLE:
Richard Purdief148af22005-08-03 19:49:17 +0100300 storeDouble(&roundData, getFd(opcode), pAddress);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 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
Richard Purdief148af22005-08-03 19:49:17 +0100311 if (roundData.exception)
312 float_raise(roundData.exception);
313
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 if (write_back)
315 writeRegister(getRn(opcode), (unsigned long) pFinal);
316 return nRc;
317}
318
319unsigned 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
355unsigned 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
391unsigned 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}