xref: /openbmc/linux/arch/arm/nwfpe/fpa11_cpdt.c (revision bedf142b8bba4331ed93161292a4ce4f8cde7308)
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 }
409