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