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