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