1*74ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds NetWinder Floating Point Emulator
41da177e4SLinus Torvalds (c) Rebel.COM, 1998,1999
51da177e4SLinus Torvalds (c) Philip Blundell, 1999, 2001
61da177e4SLinus Torvalds
71da177e4SLinus Torvalds Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
81da177e4SLinus Torvalds
91da177e4SLinus Torvalds */
101da177e4SLinus Torvalds
111da177e4SLinus Torvalds #include "fpa11.h"
121da177e4SLinus Torvalds #include "fpopcode.h"
131da177e4SLinus Torvalds #include "fpa11.inl"
141da177e4SLinus Torvalds #include "fpmodule.h"
151da177e4SLinus Torvalds #include "fpmodule.inl"
166ec5e7f3SBen Dooks #include "softfloat.h"
171da177e4SLinus Torvalds
181da177e4SLinus Torvalds unsigned int PerformFLT(const unsigned int opcode);
191da177e4SLinus Torvalds unsigned int PerformFIX(const unsigned int opcode);
201da177e4SLinus Torvalds
211da177e4SLinus Torvalds static unsigned int PerformComparison(const unsigned int opcode);
221da177e4SLinus Torvalds
EmulateCPRT(const unsigned int opcode)231da177e4SLinus Torvalds unsigned int EmulateCPRT(const unsigned int opcode)
241da177e4SLinus Torvalds {
251da177e4SLinus Torvalds
261da177e4SLinus Torvalds if (opcode & 0x800000) {
271da177e4SLinus Torvalds /* This is some variant of a comparison (PerformComparison
281da177e4SLinus Torvalds will sort out which one). Since most of the other CPRT
291da177e4SLinus Torvalds instructions are oddball cases of some sort or other it
301da177e4SLinus Torvalds makes sense to pull this out into a fast path. */
311da177e4SLinus Torvalds return PerformComparison(opcode);
321da177e4SLinus Torvalds }
331da177e4SLinus Torvalds
341da177e4SLinus Torvalds /* Hint to GCC that we'd like a jump table rather than a load of CMPs */
351da177e4SLinus Torvalds switch ((opcode & 0x700000) >> 20) {
361da177e4SLinus Torvalds case FLT_CODE >> 20:
371da177e4SLinus Torvalds return PerformFLT(opcode);
381da177e4SLinus Torvalds break;
391da177e4SLinus Torvalds case FIX_CODE >> 20:
401da177e4SLinus Torvalds return PerformFIX(opcode);
411da177e4SLinus Torvalds break;
421da177e4SLinus Torvalds
431da177e4SLinus Torvalds case WFS_CODE >> 20:
441da177e4SLinus Torvalds writeFPSR(readRegister(getRd(opcode)));
451da177e4SLinus Torvalds break;
461da177e4SLinus Torvalds case RFS_CODE >> 20:
471da177e4SLinus Torvalds writeRegister(getRd(opcode), readFPSR());
481da177e4SLinus Torvalds break;
491da177e4SLinus Torvalds
501da177e4SLinus Torvalds default:
511da177e4SLinus Torvalds return 0;
521da177e4SLinus Torvalds }
531da177e4SLinus Torvalds
541da177e4SLinus Torvalds return 1;
551da177e4SLinus Torvalds }
561da177e4SLinus Torvalds
PerformFLT(const unsigned int opcode)571da177e4SLinus Torvalds unsigned int PerformFLT(const unsigned int opcode)
581da177e4SLinus Torvalds {
591da177e4SLinus Torvalds FPA11 *fpa11 = GET_FPA11();
60f148af25SRichard Purdie struct roundingData roundData;
61f148af25SRichard Purdie
62f148af25SRichard Purdie roundData.mode = SetRoundingMode(opcode);
63f148af25SRichard Purdie roundData.precision = SetRoundingPrecision(opcode);
64f148af25SRichard Purdie roundData.exception = 0;
651da177e4SLinus Torvalds
661da177e4SLinus Torvalds switch (opcode & MASK_ROUNDING_PRECISION) {
671da177e4SLinus Torvalds case ROUND_SINGLE:
681da177e4SLinus Torvalds {
691da177e4SLinus Torvalds fpa11->fType[getFn(opcode)] = typeSingle;
70f148af25SRichard Purdie fpa11->fpreg[getFn(opcode)].fSingle = int32_to_float32(&roundData, readRegister(getRd(opcode)));
711da177e4SLinus Torvalds }
721da177e4SLinus Torvalds break;
731da177e4SLinus Torvalds
741da177e4SLinus Torvalds case ROUND_DOUBLE:
751da177e4SLinus Torvalds {
761da177e4SLinus Torvalds fpa11->fType[getFn(opcode)] = typeDouble;
771da177e4SLinus Torvalds fpa11->fpreg[getFn(opcode)].fDouble = int32_to_float64(readRegister(getRd(opcode)));
781da177e4SLinus Torvalds }
791da177e4SLinus Torvalds break;
801da177e4SLinus Torvalds
811da177e4SLinus Torvalds #ifdef CONFIG_FPE_NWFPE_XP
821da177e4SLinus Torvalds case ROUND_EXTENDED:
831da177e4SLinus Torvalds {
841da177e4SLinus Torvalds fpa11->fType[getFn(opcode)] = typeExtended;
851da177e4SLinus Torvalds fpa11->fpreg[getFn(opcode)].fExtended = int32_to_floatx80(readRegister(getRd(opcode)));
861da177e4SLinus Torvalds }
871da177e4SLinus Torvalds break;
881da177e4SLinus Torvalds #endif
891da177e4SLinus Torvalds
901da177e4SLinus Torvalds default:
911da177e4SLinus Torvalds return 0;
921da177e4SLinus Torvalds }
931da177e4SLinus Torvalds
94f148af25SRichard Purdie if (roundData.exception)
95f148af25SRichard Purdie float_raise(roundData.exception);
96f148af25SRichard Purdie
971da177e4SLinus Torvalds return 1;
981da177e4SLinus Torvalds }
991da177e4SLinus Torvalds
PerformFIX(const unsigned int opcode)1001da177e4SLinus Torvalds unsigned int PerformFIX(const unsigned int opcode)
1011da177e4SLinus Torvalds {
1021da177e4SLinus Torvalds FPA11 *fpa11 = GET_FPA11();
1031da177e4SLinus Torvalds unsigned int Fn = getFm(opcode);
104f148af25SRichard Purdie struct roundingData roundData;
1051da177e4SLinus Torvalds
106f148af25SRichard Purdie roundData.mode = SetRoundingMode(opcode);
107f148af25SRichard Purdie roundData.precision = SetRoundingPrecision(opcode);
108f148af25SRichard Purdie roundData.exception = 0;
1091da177e4SLinus Torvalds
1101da177e4SLinus Torvalds switch (fpa11->fType[Fn]) {
1111da177e4SLinus Torvalds case typeSingle:
1121da177e4SLinus Torvalds {
113f148af25SRichard Purdie writeRegister(getRd(opcode), float32_to_int32(&roundData, fpa11->fpreg[Fn].fSingle));
1141da177e4SLinus Torvalds }
1151da177e4SLinus Torvalds break;
1161da177e4SLinus Torvalds
1171da177e4SLinus Torvalds case typeDouble:
1181da177e4SLinus Torvalds {
119f148af25SRichard Purdie writeRegister(getRd(opcode), float64_to_int32(&roundData, fpa11->fpreg[Fn].fDouble));
1201da177e4SLinus Torvalds }
1211da177e4SLinus Torvalds break;
1221da177e4SLinus Torvalds
1231da177e4SLinus Torvalds #ifdef CONFIG_FPE_NWFPE_XP
1241da177e4SLinus Torvalds case typeExtended:
1251da177e4SLinus Torvalds {
126f148af25SRichard Purdie writeRegister(getRd(opcode), floatx80_to_int32(&roundData, fpa11->fpreg[Fn].fExtended));
1271da177e4SLinus Torvalds }
1281da177e4SLinus Torvalds break;
1291da177e4SLinus Torvalds #endif
1301da177e4SLinus Torvalds
1311da177e4SLinus Torvalds default:
1321da177e4SLinus Torvalds return 0;
1331da177e4SLinus Torvalds }
1341da177e4SLinus Torvalds
135f148af25SRichard Purdie if (roundData.exception)
136f148af25SRichard Purdie float_raise(roundData.exception);
137f148af25SRichard Purdie
1381da177e4SLinus Torvalds return 1;
1391da177e4SLinus Torvalds }
1401da177e4SLinus Torvalds
1411da177e4SLinus Torvalds /* This instruction sets the flags N, Z, C, V in the FPSR. */
PerformComparison(const unsigned int opcode)1421da177e4SLinus Torvalds static unsigned int PerformComparison(const unsigned int opcode)
1431da177e4SLinus Torvalds {
1441da177e4SLinus Torvalds FPA11 *fpa11 = GET_FPA11();
1451da177e4SLinus Torvalds unsigned int Fn = getFn(opcode), Fm = getFm(opcode);
1461da177e4SLinus Torvalds int e_flag = opcode & 0x400000; /* 1 if CxFE */
1471da177e4SLinus Torvalds int n_flag = opcode & 0x200000; /* 1 if CNxx */
1481da177e4SLinus Torvalds unsigned int flags = 0;
1491da177e4SLinus Torvalds
1501da177e4SLinus Torvalds #ifdef CONFIG_FPE_NWFPE_XP
1511da177e4SLinus Torvalds floatx80 rFn, rFm;
1521da177e4SLinus Torvalds
1531da177e4SLinus Torvalds /* Check for unordered condition and convert all operands to 80-bit
1541da177e4SLinus Torvalds format.
1551da177e4SLinus Torvalds ?? Might be some mileage in avoiding this conversion if possible.
1561da177e4SLinus Torvalds Eg, if both operands are 32-bit, detect this and do a 32-bit
1571da177e4SLinus Torvalds comparison (cheaper than an 80-bit one). */
1581da177e4SLinus Torvalds switch (fpa11->fType[Fn]) {
1591da177e4SLinus Torvalds case typeSingle:
1601da177e4SLinus Torvalds //printk("single.\n");
1611da177e4SLinus Torvalds if (float32_is_nan(fpa11->fpreg[Fn].fSingle))
1621da177e4SLinus Torvalds goto unordered;
1631da177e4SLinus Torvalds rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle);
1641da177e4SLinus Torvalds break;
1651da177e4SLinus Torvalds
1661da177e4SLinus Torvalds case typeDouble:
1671da177e4SLinus Torvalds //printk("double.\n");
1681da177e4SLinus Torvalds if (float64_is_nan(fpa11->fpreg[Fn].fDouble))
1691da177e4SLinus Torvalds goto unordered;
1701da177e4SLinus Torvalds rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble);
1711da177e4SLinus Torvalds break;
1721da177e4SLinus Torvalds
1731da177e4SLinus Torvalds case typeExtended:
1741da177e4SLinus Torvalds //printk("extended.\n");
1751da177e4SLinus Torvalds if (floatx80_is_nan(fpa11->fpreg[Fn].fExtended))
1761da177e4SLinus Torvalds goto unordered;
1771da177e4SLinus Torvalds rFn = fpa11->fpreg[Fn].fExtended;
1781da177e4SLinus Torvalds break;
1791da177e4SLinus Torvalds
1801da177e4SLinus Torvalds default:
1811da177e4SLinus Torvalds return 0;
1821da177e4SLinus Torvalds }
1831da177e4SLinus Torvalds
1841da177e4SLinus Torvalds if (CONSTANT_FM(opcode)) {
1851da177e4SLinus Torvalds //printk("Fm is a constant: #%d.\n",Fm);
1861da177e4SLinus Torvalds rFm = getExtendedConstant(Fm);
1871da177e4SLinus Torvalds if (floatx80_is_nan(rFm))
1881da177e4SLinus Torvalds goto unordered;
1891da177e4SLinus Torvalds } else {
1901da177e4SLinus Torvalds //printk("Fm = r%d which contains a ",Fm);
1911da177e4SLinus Torvalds switch (fpa11->fType[Fm]) {
1921da177e4SLinus Torvalds case typeSingle:
1931da177e4SLinus Torvalds //printk("single.\n");
1941da177e4SLinus Torvalds if (float32_is_nan(fpa11->fpreg[Fm].fSingle))
1951da177e4SLinus Torvalds goto unordered;
1961da177e4SLinus Torvalds rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle);
1971da177e4SLinus Torvalds break;
1981da177e4SLinus Torvalds
1991da177e4SLinus Torvalds case typeDouble:
2001da177e4SLinus Torvalds //printk("double.\n");
2011da177e4SLinus Torvalds if (float64_is_nan(fpa11->fpreg[Fm].fDouble))
2021da177e4SLinus Torvalds goto unordered;
2031da177e4SLinus Torvalds rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble);
2041da177e4SLinus Torvalds break;
2051da177e4SLinus Torvalds
2061da177e4SLinus Torvalds case typeExtended:
2071da177e4SLinus Torvalds //printk("extended.\n");
2081da177e4SLinus Torvalds if (floatx80_is_nan(fpa11->fpreg[Fm].fExtended))
2091da177e4SLinus Torvalds goto unordered;
2101da177e4SLinus Torvalds rFm = fpa11->fpreg[Fm].fExtended;
2111da177e4SLinus Torvalds break;
2121da177e4SLinus Torvalds
2131da177e4SLinus Torvalds default:
2141da177e4SLinus Torvalds return 0;
2151da177e4SLinus Torvalds }
2161da177e4SLinus Torvalds }
2171da177e4SLinus Torvalds
2181da177e4SLinus Torvalds if (n_flag)
2191da177e4SLinus Torvalds rFm.high ^= 0x8000;
2201da177e4SLinus Torvalds
2211da177e4SLinus Torvalds /* test for less than condition */
2221da177e4SLinus Torvalds if (floatx80_lt(rFn, rFm))
2231da177e4SLinus Torvalds flags |= CC_NEGATIVE;
2241da177e4SLinus Torvalds
2251da177e4SLinus Torvalds /* test for equal condition */
2261da177e4SLinus Torvalds if (floatx80_eq(rFn, rFm))
2271da177e4SLinus Torvalds flags |= CC_ZERO;
2281da177e4SLinus Torvalds
2291da177e4SLinus Torvalds /* test for greater than or equal condition */
2301da177e4SLinus Torvalds if (floatx80_lt(rFm, rFn))
2311da177e4SLinus Torvalds flags |= CC_CARRY;
2321da177e4SLinus Torvalds
2331da177e4SLinus Torvalds #else
2341da177e4SLinus Torvalds if (CONSTANT_FM(opcode)) {
2351da177e4SLinus Torvalds /* Fm is a constant. Do the comparison in whatever precision
2361da177e4SLinus Torvalds Fn happens to be stored in. */
2371da177e4SLinus Torvalds if (fpa11->fType[Fn] == typeSingle) {
2381da177e4SLinus Torvalds float32 rFm = getSingleConstant(Fm);
2391da177e4SLinus Torvalds float32 rFn = fpa11->fpreg[Fn].fSingle;
2401da177e4SLinus Torvalds
2411da177e4SLinus Torvalds if (float32_is_nan(rFn))
2421da177e4SLinus Torvalds goto unordered;
2431da177e4SLinus Torvalds
2441da177e4SLinus Torvalds if (n_flag)
2451da177e4SLinus Torvalds rFm ^= 0x80000000;
2461da177e4SLinus Torvalds
2471da177e4SLinus Torvalds /* test for less than condition */
2481da177e4SLinus Torvalds if (float32_lt_nocheck(rFn, rFm))
2491da177e4SLinus Torvalds flags |= CC_NEGATIVE;
2501da177e4SLinus Torvalds
2511da177e4SLinus Torvalds /* test for equal condition */
2521da177e4SLinus Torvalds if (float32_eq_nocheck(rFn, rFm))
2531da177e4SLinus Torvalds flags |= CC_ZERO;
2541da177e4SLinus Torvalds
2551da177e4SLinus Torvalds /* test for greater than or equal condition */
2561da177e4SLinus Torvalds if (float32_lt_nocheck(rFm, rFn))
2571da177e4SLinus Torvalds flags |= CC_CARRY;
2581da177e4SLinus Torvalds } else {
2591da177e4SLinus Torvalds float64 rFm = getDoubleConstant(Fm);
2601da177e4SLinus Torvalds float64 rFn = fpa11->fpreg[Fn].fDouble;
2611da177e4SLinus Torvalds
2621da177e4SLinus Torvalds if (float64_is_nan(rFn))
2631da177e4SLinus Torvalds goto unordered;
2641da177e4SLinus Torvalds
2651da177e4SLinus Torvalds if (n_flag)
2661da177e4SLinus Torvalds rFm ^= 0x8000000000000000ULL;
2671da177e4SLinus Torvalds
2681da177e4SLinus Torvalds /* test for less than condition */
2691da177e4SLinus Torvalds if (float64_lt_nocheck(rFn, rFm))
2701da177e4SLinus Torvalds flags |= CC_NEGATIVE;
2711da177e4SLinus Torvalds
2721da177e4SLinus Torvalds /* test for equal condition */
2731da177e4SLinus Torvalds if (float64_eq_nocheck(rFn, rFm))
2741da177e4SLinus Torvalds flags |= CC_ZERO;
2751da177e4SLinus Torvalds
2761da177e4SLinus Torvalds /* test for greater than or equal condition */
2771da177e4SLinus Torvalds if (float64_lt_nocheck(rFm, rFn))
2781da177e4SLinus Torvalds flags |= CC_CARRY;
2791da177e4SLinus Torvalds }
2801da177e4SLinus Torvalds } else {
2811da177e4SLinus Torvalds /* Both operands are in registers. */
2821da177e4SLinus Torvalds if (fpa11->fType[Fn] == typeSingle
2831da177e4SLinus Torvalds && fpa11->fType[Fm] == typeSingle) {
2841da177e4SLinus Torvalds float32 rFm = fpa11->fpreg[Fm].fSingle;
2851da177e4SLinus Torvalds float32 rFn = fpa11->fpreg[Fn].fSingle;
2861da177e4SLinus Torvalds
2871da177e4SLinus Torvalds if (float32_is_nan(rFn)
2881da177e4SLinus Torvalds || float32_is_nan(rFm))
2891da177e4SLinus Torvalds goto unordered;
2901da177e4SLinus Torvalds
2911da177e4SLinus Torvalds if (n_flag)
2921da177e4SLinus Torvalds rFm ^= 0x80000000;
2931da177e4SLinus Torvalds
2941da177e4SLinus Torvalds /* test for less than condition */
2951da177e4SLinus Torvalds if (float32_lt_nocheck(rFn, rFm))
2961da177e4SLinus Torvalds flags |= CC_NEGATIVE;
2971da177e4SLinus Torvalds
2981da177e4SLinus Torvalds /* test for equal condition */
2991da177e4SLinus Torvalds if (float32_eq_nocheck(rFn, rFm))
3001da177e4SLinus Torvalds flags |= CC_ZERO;
3011da177e4SLinus Torvalds
3021da177e4SLinus Torvalds /* test for greater than or equal condition */
3031da177e4SLinus Torvalds if (float32_lt_nocheck(rFm, rFn))
3041da177e4SLinus Torvalds flags |= CC_CARRY;
3051da177e4SLinus Torvalds } else {
3061da177e4SLinus Torvalds /* Promote 32-bit operand to 64 bits. */
3071da177e4SLinus Torvalds float64 rFm, rFn;
3081da177e4SLinus Torvalds
3091da177e4SLinus Torvalds rFm = (fpa11->fType[Fm] == typeSingle) ?
3101da177e4SLinus Torvalds float32_to_float64(fpa11->fpreg[Fm].fSingle)
3111da177e4SLinus Torvalds : fpa11->fpreg[Fm].fDouble;
3121da177e4SLinus Torvalds
3131da177e4SLinus Torvalds rFn = (fpa11->fType[Fn] == typeSingle) ?
3141da177e4SLinus Torvalds float32_to_float64(fpa11->fpreg[Fn].fSingle)
3151da177e4SLinus Torvalds : fpa11->fpreg[Fn].fDouble;
3161da177e4SLinus Torvalds
3171da177e4SLinus Torvalds if (float64_is_nan(rFn)
3181da177e4SLinus Torvalds || float64_is_nan(rFm))
3191da177e4SLinus Torvalds goto unordered;
3201da177e4SLinus Torvalds
3211da177e4SLinus Torvalds if (n_flag)
3221da177e4SLinus Torvalds rFm ^= 0x8000000000000000ULL;
3231da177e4SLinus Torvalds
3241da177e4SLinus Torvalds /* test for less than condition */
3251da177e4SLinus Torvalds if (float64_lt_nocheck(rFn, rFm))
3261da177e4SLinus Torvalds flags |= CC_NEGATIVE;
3271da177e4SLinus Torvalds
3281da177e4SLinus Torvalds /* test for equal condition */
3291da177e4SLinus Torvalds if (float64_eq_nocheck(rFn, rFm))
3301da177e4SLinus Torvalds flags |= CC_ZERO;
3311da177e4SLinus Torvalds
3321da177e4SLinus Torvalds /* test for greater than or equal condition */
3331da177e4SLinus Torvalds if (float64_lt_nocheck(rFm, rFn))
3341da177e4SLinus Torvalds flags |= CC_CARRY;
3351da177e4SLinus Torvalds }
3361da177e4SLinus Torvalds }
3371da177e4SLinus Torvalds
3381da177e4SLinus Torvalds #endif
3391da177e4SLinus Torvalds
3401da177e4SLinus Torvalds writeConditionCodes(flags);
3411da177e4SLinus Torvalds
3421da177e4SLinus Torvalds return 1;
3431da177e4SLinus Torvalds
3441da177e4SLinus Torvalds unordered:
3451da177e4SLinus Torvalds /* ?? The FPA data sheet is pretty vague about this, in particular
3461da177e4SLinus Torvalds about whether the non-E comparisons can ever raise exceptions.
3471da177e4SLinus Torvalds This implementation is based on a combination of what it says in
3481da177e4SLinus Torvalds the data sheet, observation of how the Acorn emulator actually
3491da177e4SLinus Torvalds behaves (and how programs expect it to) and guesswork. */
3501da177e4SLinus Torvalds flags |= CC_OVERFLOW;
3511da177e4SLinus Torvalds flags &= ~(CC_ZERO | CC_NEGATIVE);
3521da177e4SLinus Torvalds
3531da177e4SLinus Torvalds if (BIT_AC & readFPSR())
3541da177e4SLinus Torvalds flags |= CC_CARRY;
3551da177e4SLinus Torvalds
3561da177e4SLinus Torvalds if (e_flag)
3571da177e4SLinus Torvalds float_raise(float_flag_invalid);
3581da177e4SLinus Torvalds
3591da177e4SLinus Torvalds writeConditionCodes(flags);
3601da177e4SLinus Torvalds return 1;
3611da177e4SLinus Torvalds }
362