1/*
2    NetWinder Floating Point Emulator
3    (c) Rebel.COM, 1998,1999
4    (c) Philip Blundell, 1999, 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 "fpopcode.h"
25#include "fpa11.inl"
26#include "fpmodule.h"
27#include "fpmodule.inl"
28#include "softfloat.h"
29
30unsigned int PerformFLT(const unsigned int opcode);
31unsigned int PerformFIX(const unsigned int opcode);
32
33static unsigned int PerformComparison(const unsigned int opcode);
34
35unsigned int EmulateCPRT(const unsigned int opcode)
36{
37
38	if (opcode & 0x800000) {
39		/* This is some variant of a comparison (PerformComparison
40		   will sort out which one).  Since most of the other CPRT
41		   instructions are oddball cases of some sort or other it
42		   makes sense to pull this out into a fast path.  */
43		return PerformComparison(opcode);
44	}
45
46	/* Hint to GCC that we'd like a jump table rather than a load of CMPs */
47	switch ((opcode & 0x700000) >> 20) {
48	case FLT_CODE >> 20:
49		return PerformFLT(opcode);
50		break;
51	case FIX_CODE >> 20:
52		return PerformFIX(opcode);
53		break;
54
55	case WFS_CODE >> 20:
56		writeFPSR(readRegister(getRd(opcode)));
57		break;
58	case RFS_CODE >> 20:
59		writeRegister(getRd(opcode), readFPSR());
60		break;
61
62	default:
63		return 0;
64	}
65
66	return 1;
67}
68
69unsigned int PerformFLT(const unsigned int opcode)
70{
71	FPA11 *fpa11 = GET_FPA11();
72	struct roundingData roundData;
73
74	roundData.mode = SetRoundingMode(opcode);
75	roundData.precision = SetRoundingPrecision(opcode);
76	roundData.exception = 0;
77
78	switch (opcode & MASK_ROUNDING_PRECISION) {
79	case ROUND_SINGLE:
80		{
81			fpa11->fType[getFn(opcode)] = typeSingle;
82			fpa11->fpreg[getFn(opcode)].fSingle = int32_to_float32(&roundData, readRegister(getRd(opcode)));
83		}
84		break;
85
86	case ROUND_DOUBLE:
87		{
88			fpa11->fType[getFn(opcode)] = typeDouble;
89			fpa11->fpreg[getFn(opcode)].fDouble = int32_to_float64(readRegister(getRd(opcode)));
90		}
91		break;
92
93#ifdef CONFIG_FPE_NWFPE_XP
94	case ROUND_EXTENDED:
95		{
96			fpa11->fType[getFn(opcode)] = typeExtended;
97			fpa11->fpreg[getFn(opcode)].fExtended = int32_to_floatx80(readRegister(getRd(opcode)));
98		}
99		break;
100#endif
101
102	default:
103		return 0;
104	}
105
106	if (roundData.exception)
107		float_raise(roundData.exception);
108
109	return 1;
110}
111
112unsigned int PerformFIX(const unsigned int opcode)
113{
114	FPA11 *fpa11 = GET_FPA11();
115	unsigned int Fn = getFm(opcode);
116	struct roundingData roundData;
117
118	roundData.mode = SetRoundingMode(opcode);
119	roundData.precision = SetRoundingPrecision(opcode);
120	roundData.exception = 0;
121
122	switch (fpa11->fType[Fn]) {
123	case typeSingle:
124		{
125			writeRegister(getRd(opcode), float32_to_int32(&roundData, fpa11->fpreg[Fn].fSingle));
126		}
127		break;
128
129	case typeDouble:
130		{
131			writeRegister(getRd(opcode), float64_to_int32(&roundData, fpa11->fpreg[Fn].fDouble));
132		}
133		break;
134
135#ifdef CONFIG_FPE_NWFPE_XP
136	case typeExtended:
137		{
138			writeRegister(getRd(opcode), floatx80_to_int32(&roundData, fpa11->fpreg[Fn].fExtended));
139		}
140		break;
141#endif
142
143	default:
144		return 0;
145	}
146
147	if (roundData.exception)
148		float_raise(roundData.exception);
149
150	return 1;
151}
152
153/* This instruction sets the flags N, Z, C, V in the FPSR. */
154static unsigned int PerformComparison(const unsigned int opcode)
155{
156	FPA11 *fpa11 = GET_FPA11();
157	unsigned int Fn = getFn(opcode), Fm = getFm(opcode);
158	int e_flag = opcode & 0x400000;	/* 1 if CxFE */
159	int n_flag = opcode & 0x200000;	/* 1 if CNxx */
160	unsigned int flags = 0;
161
162#ifdef CONFIG_FPE_NWFPE_XP
163	floatx80 rFn, rFm;
164
165	/* Check for unordered condition and convert all operands to 80-bit
166	   format.
167	   ?? Might be some mileage in avoiding this conversion if possible.
168	   Eg, if both operands are 32-bit, detect this and do a 32-bit
169	   comparison (cheaper than an 80-bit one).  */
170	switch (fpa11->fType[Fn]) {
171	case typeSingle:
172		//printk("single.\n");
173		if (float32_is_nan(fpa11->fpreg[Fn].fSingle))
174			goto unordered;
175		rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle);
176		break;
177
178	case typeDouble:
179		//printk("double.\n");
180		if (float64_is_nan(fpa11->fpreg[Fn].fDouble))
181			goto unordered;
182		rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble);
183		break;
184
185	case typeExtended:
186		//printk("extended.\n");
187		if (floatx80_is_nan(fpa11->fpreg[Fn].fExtended))
188			goto unordered;
189		rFn = fpa11->fpreg[Fn].fExtended;
190		break;
191
192	default:
193		return 0;
194	}
195
196	if (CONSTANT_FM(opcode)) {
197		//printk("Fm is a constant: #%d.\n",Fm);
198		rFm = getExtendedConstant(Fm);
199		if (floatx80_is_nan(rFm))
200			goto unordered;
201	} else {
202		//printk("Fm = r%d which contains a ",Fm);
203		switch (fpa11->fType[Fm]) {
204		case typeSingle:
205			//printk("single.\n");
206			if (float32_is_nan(fpa11->fpreg[Fm].fSingle))
207				goto unordered;
208			rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle);
209			break;
210
211		case typeDouble:
212			//printk("double.\n");
213			if (float64_is_nan(fpa11->fpreg[Fm].fDouble))
214				goto unordered;
215			rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble);
216			break;
217
218		case typeExtended:
219			//printk("extended.\n");
220			if (floatx80_is_nan(fpa11->fpreg[Fm].fExtended))
221				goto unordered;
222			rFm = fpa11->fpreg[Fm].fExtended;
223			break;
224
225		default:
226			return 0;
227		}
228	}
229
230	if (n_flag)
231		rFm.high ^= 0x8000;
232
233	/* test for less than condition */
234	if (floatx80_lt(rFn, rFm))
235		flags |= CC_NEGATIVE;
236
237	/* test for equal condition */
238	if (floatx80_eq(rFn, rFm))
239		flags |= CC_ZERO;
240
241	/* test for greater than or equal condition */
242	if (floatx80_lt(rFm, rFn))
243		flags |= CC_CARRY;
244
245#else
246	if (CONSTANT_FM(opcode)) {
247		/* Fm is a constant.  Do the comparison in whatever precision
248		   Fn happens to be stored in.  */
249		if (fpa11->fType[Fn] == typeSingle) {
250			float32 rFm = getSingleConstant(Fm);
251			float32 rFn = fpa11->fpreg[Fn].fSingle;
252
253			if (float32_is_nan(rFn))
254				goto unordered;
255
256			if (n_flag)
257				rFm ^= 0x80000000;
258
259			/* test for less than condition */
260			if (float32_lt_nocheck(rFn, rFm))
261				flags |= CC_NEGATIVE;
262
263			/* test for equal condition */
264			if (float32_eq_nocheck(rFn, rFm))
265				flags |= CC_ZERO;
266
267			/* test for greater than or equal condition */
268			if (float32_lt_nocheck(rFm, rFn))
269				flags |= CC_CARRY;
270		} else {
271			float64 rFm = getDoubleConstant(Fm);
272			float64 rFn = fpa11->fpreg[Fn].fDouble;
273
274			if (float64_is_nan(rFn))
275				goto unordered;
276
277			if (n_flag)
278				rFm ^= 0x8000000000000000ULL;
279
280			/* test for less than condition */
281			if (float64_lt_nocheck(rFn, rFm))
282				flags |= CC_NEGATIVE;
283
284			/* test for equal condition */
285			if (float64_eq_nocheck(rFn, rFm))
286				flags |= CC_ZERO;
287
288			/* test for greater than or equal condition */
289			if (float64_lt_nocheck(rFm, rFn))
290				flags |= CC_CARRY;
291		}
292	} else {
293		/* Both operands are in registers.  */
294		if (fpa11->fType[Fn] == typeSingle
295		    && fpa11->fType[Fm] == typeSingle) {
296			float32 rFm = fpa11->fpreg[Fm].fSingle;
297			float32 rFn = fpa11->fpreg[Fn].fSingle;
298
299			if (float32_is_nan(rFn)
300			    || float32_is_nan(rFm))
301				goto unordered;
302
303			if (n_flag)
304				rFm ^= 0x80000000;
305
306			/* test for less than condition */
307			if (float32_lt_nocheck(rFn, rFm))
308				flags |= CC_NEGATIVE;
309
310			/* test for equal condition */
311			if (float32_eq_nocheck(rFn, rFm))
312				flags |= CC_ZERO;
313
314			/* test for greater than or equal condition */
315			if (float32_lt_nocheck(rFm, rFn))
316				flags |= CC_CARRY;
317		} else {
318			/* Promote 32-bit operand to 64 bits.  */
319			float64 rFm, rFn;
320
321			rFm = (fpa11->fType[Fm] == typeSingle) ?
322			    float32_to_float64(fpa11->fpreg[Fm].fSingle)
323			    : fpa11->fpreg[Fm].fDouble;
324
325			rFn = (fpa11->fType[Fn] == typeSingle) ?
326			    float32_to_float64(fpa11->fpreg[Fn].fSingle)
327			    : fpa11->fpreg[Fn].fDouble;
328
329			if (float64_is_nan(rFn)
330			    || float64_is_nan(rFm))
331				goto unordered;
332
333			if (n_flag)
334				rFm ^= 0x8000000000000000ULL;
335
336			/* test for less than condition */
337			if (float64_lt_nocheck(rFn, rFm))
338				flags |= CC_NEGATIVE;
339
340			/* test for equal condition */
341			if (float64_eq_nocheck(rFn, rFm))
342				flags |= CC_ZERO;
343
344			/* test for greater than or equal condition */
345			if (float64_lt_nocheck(rFm, rFn))
346				flags |= CC_CARRY;
347		}
348	}
349
350#endif
351
352	writeConditionCodes(flags);
353
354	return 1;
355
356      unordered:
357	/* ?? The FPA data sheet is pretty vague about this, in particular
358	   about whether the non-E comparisons can ever raise exceptions.
359	   This implementation is based on a combination of what it says in
360	   the data sheet, observation of how the Acorn emulator actually
361	   behaves (and how programs expect it to) and guesswork.  */
362	flags |= CC_OVERFLOW;
363	flags &= ~(CC_ZERO | CC_NEGATIVE);
364
365	if (BIT_AC & readFPSR())
366		flags |= CC_CARRY;
367
368	if (e_flag)
369		float_raise(float_flag_invalid);
370
371	writeConditionCodes(flags);
372	return 1;
373}
374