13ebdd119Saurel32 /*
23ebdd119Saurel32 NetWinder Floating Point Emulator
33ebdd119Saurel32 (c) Rebel.COM, 1998,1999
43ebdd119Saurel32 (c) Philip Blundell, 1999
53ebdd119Saurel32
63ebdd119Saurel32 Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
73ebdd119Saurel32
83ebdd119Saurel32 This program is free software; you can redistribute it and/or modify
93ebdd119Saurel32 it under the terms of the GNU General Public License as published by
103ebdd119Saurel32 the Free Software Foundation; either version 2 of the License, or
113ebdd119Saurel32 (at your option) any later version.
123ebdd119Saurel32
133ebdd119Saurel32 This program is distributed in the hope that it will be useful,
143ebdd119Saurel32 but WITHOUT ANY WARRANTY; without even the implied warranty of
153ebdd119Saurel32 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
163ebdd119Saurel32 GNU General Public License for more details.
173ebdd119Saurel32
183ebdd119Saurel32 You should have received a copy of the GNU General Public License
1970539e18SBlue Swirl along with this program; if not, see <http://www.gnu.org/licenses/>.
203ebdd119Saurel32 */
213ebdd119Saurel32
22*d39594e9SPeter Maydell #include "qemu/osdep.h"
233ebdd119Saurel32 #include "fpa11.h"
246b4c305cSPaolo Bonzini #include "fpu/softfloat.h"
253ebdd119Saurel32 #include "fpopcode.h"
263ebdd119Saurel32 #include "fpa11.inl"
273ebdd119Saurel32 //#include "fpmodule.h"
283ebdd119Saurel32 //#include "fpmodule.inl"
293ebdd119Saurel32
303ebdd119Saurel32 unsigned int PerformFLT(const unsigned int opcode);
313ebdd119Saurel32 unsigned int PerformFIX(const unsigned int opcode);
323ebdd119Saurel32
333ebdd119Saurel32 static unsigned int
343ebdd119Saurel32 PerformComparison(const unsigned int opcode);
353ebdd119Saurel32
EmulateCPRT(const unsigned int opcode)363ebdd119Saurel32 unsigned int EmulateCPRT(const unsigned int opcode)
373ebdd119Saurel32 {
383ebdd119Saurel32 unsigned int nRc = 1;
393ebdd119Saurel32
403ebdd119Saurel32 //printk("EmulateCPRT(0x%08x)\n",opcode);
413ebdd119Saurel32
423ebdd119Saurel32 if (opcode & 0x800000)
433ebdd119Saurel32 {
443ebdd119Saurel32 /* This is some variant of a comparison (PerformComparison will
453ebdd119Saurel32 sort out which one). Since most of the other CPRT
463ebdd119Saurel32 instructions are oddball cases of some sort or other it makes
473ebdd119Saurel32 sense to pull this out into a fast path. */
483ebdd119Saurel32 return PerformComparison(opcode);
493ebdd119Saurel32 }
503ebdd119Saurel32
513ebdd119Saurel32 /* Hint to GCC that we'd like a jump table rather than a load of CMPs */
523ebdd119Saurel32 switch ((opcode & 0x700000) >> 20)
533ebdd119Saurel32 {
543ebdd119Saurel32 case FLT_CODE >> 20: nRc = PerformFLT(opcode); break;
553ebdd119Saurel32 case FIX_CODE >> 20: nRc = PerformFIX(opcode); break;
563ebdd119Saurel32
573ebdd119Saurel32 case WFS_CODE >> 20: writeFPSR(readRegister(getRd(opcode))); break;
583ebdd119Saurel32 case RFS_CODE >> 20: writeRegister(getRd(opcode),readFPSR()); break;
593ebdd119Saurel32
603ebdd119Saurel32 #if 0 /* We currently have no use for the FPCR, so there's no point
613ebdd119Saurel32 in emulating it. */
623ebdd119Saurel32 case WFC_CODE >> 20: writeFPCR(readRegister(getRd(opcode)));
633ebdd119Saurel32 case RFC_CODE >> 20: writeRegister(getRd(opcode),readFPCR()); break;
643ebdd119Saurel32 #endif
653ebdd119Saurel32
663ebdd119Saurel32 default: nRc = 0;
673ebdd119Saurel32 }
683ebdd119Saurel32
693ebdd119Saurel32 return nRc;
703ebdd119Saurel32 }
713ebdd119Saurel32
PerformFLT(const unsigned int opcode)723ebdd119Saurel32 unsigned int PerformFLT(const unsigned int opcode)
733ebdd119Saurel32 {
743ebdd119Saurel32 FPA11 *fpa11 = GET_FPA11();
753ebdd119Saurel32
763ebdd119Saurel32 unsigned int nRc = 1;
773ebdd119Saurel32 SetRoundingMode(opcode);
783ebdd119Saurel32
793ebdd119Saurel32 switch (opcode & MASK_ROUNDING_PRECISION)
803ebdd119Saurel32 {
813ebdd119Saurel32 case ROUND_SINGLE:
823ebdd119Saurel32 {
833ebdd119Saurel32 fpa11->fType[getFn(opcode)] = typeSingle;
843ebdd119Saurel32 fpa11->fpreg[getFn(opcode)].fSingle =
853ebdd119Saurel32 int32_to_float32(readRegister(getRd(opcode)), &fpa11->fp_status);
863ebdd119Saurel32 }
873ebdd119Saurel32 break;
883ebdd119Saurel32
893ebdd119Saurel32 case ROUND_DOUBLE:
903ebdd119Saurel32 {
913ebdd119Saurel32 fpa11->fType[getFn(opcode)] = typeDouble;
923ebdd119Saurel32 fpa11->fpreg[getFn(opcode)].fDouble =
933ebdd119Saurel32 int32_to_float64(readRegister(getRd(opcode)), &fpa11->fp_status);
943ebdd119Saurel32 }
953ebdd119Saurel32 break;
963ebdd119Saurel32
973ebdd119Saurel32 case ROUND_EXTENDED:
983ebdd119Saurel32 {
993ebdd119Saurel32 fpa11->fType[getFn(opcode)] = typeExtended;
1003ebdd119Saurel32 fpa11->fpreg[getFn(opcode)].fExtended =
1013ebdd119Saurel32 int32_to_floatx80(readRegister(getRd(opcode)), &fpa11->fp_status);
1023ebdd119Saurel32 }
1033ebdd119Saurel32 break;
1043ebdd119Saurel32
1053ebdd119Saurel32 default: nRc = 0;
1063ebdd119Saurel32 }
1073ebdd119Saurel32
1083ebdd119Saurel32 return nRc;
1093ebdd119Saurel32 }
1103ebdd119Saurel32
PerformFIX(const unsigned int opcode)1113ebdd119Saurel32 unsigned int PerformFIX(const unsigned int opcode)
1123ebdd119Saurel32 {
1133ebdd119Saurel32 FPA11 *fpa11 = GET_FPA11();
1143ebdd119Saurel32 unsigned int nRc = 1;
1153ebdd119Saurel32 unsigned int Fn = getFm(opcode);
1163ebdd119Saurel32
1173ebdd119Saurel32 SetRoundingMode(opcode);
1183ebdd119Saurel32
1193ebdd119Saurel32 switch (fpa11->fType[Fn])
1203ebdd119Saurel32 {
1213ebdd119Saurel32 case typeSingle:
1223ebdd119Saurel32 {
1233ebdd119Saurel32 writeRegister(getRd(opcode),
1243ebdd119Saurel32 float32_to_int32(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status));
1253ebdd119Saurel32 }
1263ebdd119Saurel32 break;
1273ebdd119Saurel32
1283ebdd119Saurel32 case typeDouble:
1293ebdd119Saurel32 {
1303ebdd119Saurel32 //printf("F%d is 0x%" PRIx64 "\n",Fn,fpa11->fpreg[Fn].fDouble);
1313ebdd119Saurel32 writeRegister(getRd(opcode),
1323ebdd119Saurel32 float64_to_int32(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status));
1333ebdd119Saurel32 }
1343ebdd119Saurel32 break;
1353ebdd119Saurel32
1363ebdd119Saurel32 case typeExtended:
1373ebdd119Saurel32 {
1383ebdd119Saurel32 writeRegister(getRd(opcode),
1393ebdd119Saurel32 floatx80_to_int32(fpa11->fpreg[Fn].fExtended, &fpa11->fp_status));
1403ebdd119Saurel32 }
1413ebdd119Saurel32 break;
1423ebdd119Saurel32
1433ebdd119Saurel32 default: nRc = 0;
1443ebdd119Saurel32 }
1453ebdd119Saurel32
1463ebdd119Saurel32 return nRc;
1473ebdd119Saurel32 }
1483ebdd119Saurel32
1493ebdd119Saurel32
15086178a57SJuan Quintela static __inline unsigned int
PerformComparisonOperation(floatx80 Fn,floatx80 Fm)1513ebdd119Saurel32 PerformComparisonOperation(floatx80 Fn, floatx80 Fm)
1523ebdd119Saurel32 {
1533ebdd119Saurel32 FPA11 *fpa11 = GET_FPA11();
1543ebdd119Saurel32 unsigned int flags = 0;
1553ebdd119Saurel32
1563ebdd119Saurel32 /* test for less than condition */
1573ebdd119Saurel32 if (floatx80_lt(Fn,Fm, &fpa11->fp_status))
1583ebdd119Saurel32 {
1593ebdd119Saurel32 flags |= CC_NEGATIVE;
1603ebdd119Saurel32 }
1613ebdd119Saurel32
1623ebdd119Saurel32 /* test for equal condition */
163211315fbSAurelien Jarno if (floatx80_eq_quiet(Fn,Fm, &fpa11->fp_status))
1643ebdd119Saurel32 {
1653ebdd119Saurel32 flags |= CC_ZERO;
1663ebdd119Saurel32 }
1673ebdd119Saurel32
1683ebdd119Saurel32 /* test for greater than or equal condition */
1693ebdd119Saurel32 if (floatx80_lt(Fm,Fn, &fpa11->fp_status))
1703ebdd119Saurel32 {
1713ebdd119Saurel32 flags |= CC_CARRY;
1723ebdd119Saurel32 }
1733ebdd119Saurel32
1743ebdd119Saurel32 writeConditionCodes(flags);
1753ebdd119Saurel32 return 1;
1763ebdd119Saurel32 }
1773ebdd119Saurel32
1783ebdd119Saurel32 /* This instruction sets the flags N, Z, C, V in the FPSR. */
1793ebdd119Saurel32
PerformComparison(const unsigned int opcode)1803ebdd119Saurel32 static unsigned int PerformComparison(const unsigned int opcode)
1813ebdd119Saurel32 {
1823ebdd119Saurel32 FPA11 *fpa11 = GET_FPA11();
1833ebdd119Saurel32 unsigned int Fn, Fm;
1843ebdd119Saurel32 floatx80 rFn, rFm;
1853ebdd119Saurel32 int e_flag = opcode & 0x400000; /* 1 if CxFE */
1863ebdd119Saurel32 int n_flag = opcode & 0x200000; /* 1 if CNxx */
1873ebdd119Saurel32 unsigned int flags = 0;
1883ebdd119Saurel32
1893ebdd119Saurel32 //printk("PerformComparison(0x%08x)\n",opcode);
1903ebdd119Saurel32
1913ebdd119Saurel32 Fn = getFn(opcode);
1923ebdd119Saurel32 Fm = getFm(opcode);
1933ebdd119Saurel32
1943ebdd119Saurel32 /* Check for unordered condition and convert all operands to 80-bit
1953ebdd119Saurel32 format.
1963ebdd119Saurel32 ?? Might be some mileage in avoiding this conversion if possible.
1973ebdd119Saurel32 Eg, if both operands are 32-bit, detect this and do a 32-bit
1983ebdd119Saurel32 comparison (cheaper than an 80-bit one). */
1993ebdd119Saurel32 switch (fpa11->fType[Fn])
2003ebdd119Saurel32 {
2013ebdd119Saurel32 case typeSingle:
2023ebdd119Saurel32 //printk("single.\n");
2033ebe80c2SPeter Maydell if (float32_is_any_nan(fpa11->fpreg[Fn].fSingle))
2043ebdd119Saurel32 goto unordered;
2053ebdd119Saurel32 rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status);
2063ebdd119Saurel32 break;
2073ebdd119Saurel32
2083ebdd119Saurel32 case typeDouble:
2093ebdd119Saurel32 //printk("double.\n");
2103ebe80c2SPeter Maydell if (float64_is_any_nan(fpa11->fpreg[Fn].fDouble))
2113ebdd119Saurel32 goto unordered;
2123ebdd119Saurel32 rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status);
2133ebdd119Saurel32 break;
2143ebdd119Saurel32
2153ebdd119Saurel32 case typeExtended:
2163ebdd119Saurel32 //printk("extended.\n");
2173ebe80c2SPeter Maydell if (floatx80_is_any_nan(fpa11->fpreg[Fn].fExtended))
2183ebdd119Saurel32 goto unordered;
2193ebdd119Saurel32 rFn = fpa11->fpreg[Fn].fExtended;
2203ebdd119Saurel32 break;
2213ebdd119Saurel32
2223ebdd119Saurel32 default: return 0;
2233ebdd119Saurel32 }
2243ebdd119Saurel32
2253ebdd119Saurel32 if (CONSTANT_FM(opcode))
2263ebdd119Saurel32 {
2273ebdd119Saurel32 //printk("Fm is a constant: #%d.\n",Fm);
2283ebdd119Saurel32 rFm = getExtendedConstant(Fm);
2293ebe80c2SPeter Maydell if (floatx80_is_any_nan(rFm))
2303ebdd119Saurel32 goto unordered;
2313ebdd119Saurel32 }
2323ebdd119Saurel32 else
2333ebdd119Saurel32 {
2343ebdd119Saurel32 //printk("Fm = r%d which contains a ",Fm);
2353ebdd119Saurel32 switch (fpa11->fType[Fm])
2363ebdd119Saurel32 {
2373ebdd119Saurel32 case typeSingle:
2383ebdd119Saurel32 //printk("single.\n");
2393ebe80c2SPeter Maydell if (float32_is_any_nan(fpa11->fpreg[Fm].fSingle))
2403ebdd119Saurel32 goto unordered;
2413ebdd119Saurel32 rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle, &fpa11->fp_status);
2423ebdd119Saurel32 break;
2433ebdd119Saurel32
2443ebdd119Saurel32 case typeDouble:
2453ebdd119Saurel32 //printk("double.\n");
2463ebe80c2SPeter Maydell if (float64_is_any_nan(fpa11->fpreg[Fm].fDouble))
2473ebdd119Saurel32 goto unordered;
2483ebdd119Saurel32 rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble, &fpa11->fp_status);
2493ebdd119Saurel32 break;
2503ebdd119Saurel32
2513ebdd119Saurel32 case typeExtended:
2523ebdd119Saurel32 //printk("extended.\n");
2533ebe80c2SPeter Maydell if (floatx80_is_any_nan(fpa11->fpreg[Fm].fExtended))
2543ebdd119Saurel32 goto unordered;
2553ebdd119Saurel32 rFm = fpa11->fpreg[Fm].fExtended;
2563ebdd119Saurel32 break;
2573ebdd119Saurel32
2583ebdd119Saurel32 default: return 0;
2593ebdd119Saurel32 }
2603ebdd119Saurel32 }
2613ebdd119Saurel32
2623ebdd119Saurel32 if (n_flag)
2633ebdd119Saurel32 {
2643ebdd119Saurel32 rFm.high ^= 0x8000;
2653ebdd119Saurel32 }
2663ebdd119Saurel32
2673ebdd119Saurel32 return PerformComparisonOperation(rFn,rFm);
2683ebdd119Saurel32
2693ebdd119Saurel32 unordered:
2703ebdd119Saurel32 /* ?? The FPA data sheet is pretty vague about this, in particular
2713ebdd119Saurel32 about whether the non-E comparisons can ever raise exceptions.
2723ebdd119Saurel32 This implementation is based on a combination of what it says in
2733ebdd119Saurel32 the data sheet, observation of how the Acorn emulator actually
2743ebdd119Saurel32 behaves (and how programs expect it to) and guesswork. */
2753ebdd119Saurel32 flags |= CC_OVERFLOW;
2763ebdd119Saurel32 flags &= ~(CC_ZERO | CC_NEGATIVE);
2773ebdd119Saurel32
2783ebdd119Saurel32 if (BIT_AC & readFPSR()) flags |= CC_CARRY;
2793ebdd119Saurel32
2803ebdd119Saurel32 if (e_flag) float_raise(float_flag_invalid, &fpa11->fp_status);
2813ebdd119Saurel32
2823ebdd119Saurel32 writeConditionCodes(flags);
2833ebdd119Saurel32 return 1;
2843ebdd119Saurel32 }
285