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
31static 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
38static 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
54static 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
71static 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
103static 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
129static 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
162static 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
194static 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
224unsigned 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
267unsigned 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
318unsigned 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
354unsigned 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
390unsigned 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