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