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