100406dffSbellard /* 200406dffSbellard NetWinder Floating Point Emulator 300406dffSbellard (c) Rebel.COM, 1998,1999 400406dffSbellard (c) Philip Blundell, 1999 500406dffSbellard 600406dffSbellard Direct questions, comments to Scott Bambrough <scottb@netwinder.org> 700406dffSbellard 800406dffSbellard This program is free software; you can redistribute it and/or modify 900406dffSbellard it under the terms of the GNU General Public License as published by 1000406dffSbellard the Free Software Foundation; either version 2 of the License, or 1100406dffSbellard (at your option) any later version. 1200406dffSbellard 1300406dffSbellard This program is distributed in the hope that it will be useful, 1400406dffSbellard but WITHOUT ANY WARRANTY; without even the implied warranty of 1500406dffSbellard MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1600406dffSbellard GNU General Public License for more details. 1700406dffSbellard 1800406dffSbellard 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/>. 2000406dffSbellard */ 2100406dffSbellard 2200406dffSbellard #include "fpa11.h" 2300406dffSbellard #include "softfloat.h" 2400406dffSbellard #include "fpopcode.h" 2500406dffSbellard #include "fpa11.inl" 2600406dffSbellard //#include "fpmodule.h" 2700406dffSbellard //#include "fpmodule.inl" 2800406dffSbellard 2900406dffSbellard unsigned int PerformFLT(const unsigned int opcode); 3000406dffSbellard unsigned int PerformFIX(const unsigned int opcode); 3100406dffSbellard 3200406dffSbellard static unsigned int 3300406dffSbellard PerformComparison(const unsigned int opcode); 3400406dffSbellard 3500406dffSbellard unsigned int EmulateCPRT(const unsigned int opcode) 3600406dffSbellard { 3700406dffSbellard unsigned int nRc = 1; 3800406dffSbellard 3900406dffSbellard //printk("EmulateCPRT(0x%08x)\n",opcode); 4000406dffSbellard 4100406dffSbellard if (opcode & 0x800000) 4200406dffSbellard { 4300406dffSbellard /* This is some variant of a comparison (PerformComparison will 4400406dffSbellard sort out which one). Since most of the other CPRT 4500406dffSbellard instructions are oddball cases of some sort or other it makes 4600406dffSbellard sense to pull this out into a fast path. */ 4700406dffSbellard return PerformComparison(opcode); 4800406dffSbellard } 4900406dffSbellard 5000406dffSbellard /* Hint to GCC that we'd like a jump table rather than a load of CMPs */ 5100406dffSbellard switch ((opcode & 0x700000) >> 20) 5200406dffSbellard { 5300406dffSbellard case FLT_CODE >> 20: nRc = PerformFLT(opcode); break; 5400406dffSbellard case FIX_CODE >> 20: nRc = PerformFIX(opcode); break; 5500406dffSbellard 5600406dffSbellard case WFS_CODE >> 20: writeFPSR(readRegister(getRd(opcode))); break; 5700406dffSbellard case RFS_CODE >> 20: writeRegister(getRd(opcode),readFPSR()); break; 5800406dffSbellard 5900406dffSbellard #if 0 /* We currently have no use for the FPCR, so there's no point 6000406dffSbellard in emulating it. */ 6100406dffSbellard case WFC_CODE >> 20: writeFPCR(readRegister(getRd(opcode))); 6200406dffSbellard case RFC_CODE >> 20: writeRegister(getRd(opcode),readFPCR()); break; 6300406dffSbellard #endif 6400406dffSbellard 6500406dffSbellard default: nRc = 0; 6600406dffSbellard } 6700406dffSbellard 6800406dffSbellard return nRc; 6900406dffSbellard } 7000406dffSbellard 7100406dffSbellard unsigned int PerformFLT(const unsigned int opcode) 7200406dffSbellard { 7300406dffSbellard FPA11 *fpa11 = GET_FPA11(); 7400406dffSbellard 7500406dffSbellard unsigned int nRc = 1; 7600406dffSbellard SetRoundingMode(opcode); 7700406dffSbellard 7800406dffSbellard switch (opcode & MASK_ROUNDING_PRECISION) 7900406dffSbellard { 8000406dffSbellard case ROUND_SINGLE: 8100406dffSbellard { 8200406dffSbellard fpa11->fType[getFn(opcode)] = typeSingle; 8300406dffSbellard fpa11->fpreg[getFn(opcode)].fSingle = 8420495218Sbellard int32_to_float32(readRegister(getRd(opcode)), &fpa11->fp_status); 8500406dffSbellard } 8600406dffSbellard break; 8700406dffSbellard 8800406dffSbellard case ROUND_DOUBLE: 8900406dffSbellard { 9000406dffSbellard fpa11->fType[getFn(opcode)] = typeDouble; 9100406dffSbellard fpa11->fpreg[getFn(opcode)].fDouble = 9220495218Sbellard int32_to_float64(readRegister(getRd(opcode)), &fpa11->fp_status); 9300406dffSbellard } 9400406dffSbellard break; 9500406dffSbellard 9600406dffSbellard case ROUND_EXTENDED: 9700406dffSbellard { 9800406dffSbellard fpa11->fType[getFn(opcode)] = typeExtended; 9900406dffSbellard fpa11->fpreg[getFn(opcode)].fExtended = 10020495218Sbellard int32_to_floatx80(readRegister(getRd(opcode)), &fpa11->fp_status); 10100406dffSbellard } 10200406dffSbellard break; 10300406dffSbellard 10400406dffSbellard default: nRc = 0; 10500406dffSbellard } 10600406dffSbellard 10700406dffSbellard return nRc; 10800406dffSbellard } 10900406dffSbellard 11000406dffSbellard unsigned int PerformFIX(const unsigned int opcode) 11100406dffSbellard { 11200406dffSbellard FPA11 *fpa11 = GET_FPA11(); 11300406dffSbellard unsigned int nRc = 1; 11400406dffSbellard unsigned int Fn = getFm(opcode); 11500406dffSbellard 11600406dffSbellard SetRoundingMode(opcode); 11700406dffSbellard 11800406dffSbellard switch (fpa11->fType[Fn]) 11900406dffSbellard { 12000406dffSbellard case typeSingle: 12100406dffSbellard { 12200406dffSbellard writeRegister(getRd(opcode), 12320495218Sbellard float32_to_int32(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status)); 12400406dffSbellard } 12500406dffSbellard break; 12600406dffSbellard 12700406dffSbellard case typeDouble: 12800406dffSbellard { 12926a76461Sbellard //printf("F%d is 0x%" PRIx64 "\n",Fn,fpa11->fpreg[Fn].fDouble); 13000406dffSbellard writeRegister(getRd(opcode), 13120495218Sbellard float64_to_int32(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status)); 13200406dffSbellard } 13300406dffSbellard break; 13400406dffSbellard 13500406dffSbellard case typeExtended: 13600406dffSbellard { 13700406dffSbellard writeRegister(getRd(opcode), 13820495218Sbellard floatx80_to_int32(fpa11->fpreg[Fn].fExtended, &fpa11->fp_status)); 13900406dffSbellard } 14000406dffSbellard break; 14100406dffSbellard 14200406dffSbellard default: nRc = 0; 14300406dffSbellard } 14400406dffSbellard 14500406dffSbellard return nRc; 14600406dffSbellard } 14700406dffSbellard 14800406dffSbellard 14986178a57SJuan Quintela static __inline unsigned int 15000406dffSbellard PerformComparisonOperation(floatx80 Fn, floatx80 Fm) 15100406dffSbellard { 15220495218Sbellard FPA11 *fpa11 = GET_FPA11(); 15300406dffSbellard unsigned int flags = 0; 15400406dffSbellard 15500406dffSbellard /* test for less than condition */ 15620495218Sbellard if (floatx80_lt(Fn,Fm, &fpa11->fp_status)) 15700406dffSbellard { 15800406dffSbellard flags |= CC_NEGATIVE; 15900406dffSbellard } 16000406dffSbellard 16100406dffSbellard /* test for equal condition */ 16220495218Sbellard if (floatx80_eq(Fn,Fm, &fpa11->fp_status)) 16300406dffSbellard { 16400406dffSbellard flags |= CC_ZERO; 16500406dffSbellard } 16600406dffSbellard 16700406dffSbellard /* test for greater than or equal condition */ 16820495218Sbellard if (floatx80_lt(Fm,Fn, &fpa11->fp_status)) 16900406dffSbellard { 17000406dffSbellard flags |= CC_CARRY; 17100406dffSbellard } 17200406dffSbellard 17300406dffSbellard writeConditionCodes(flags); 17400406dffSbellard return 1; 17500406dffSbellard } 17600406dffSbellard 17700406dffSbellard /* This instruction sets the flags N, Z, C, V in the FPSR. */ 17800406dffSbellard 17900406dffSbellard static unsigned int PerformComparison(const unsigned int opcode) 18000406dffSbellard { 18100406dffSbellard FPA11 *fpa11 = GET_FPA11(); 18200406dffSbellard unsigned int Fn, Fm; 18300406dffSbellard floatx80 rFn, rFm; 18400406dffSbellard int e_flag = opcode & 0x400000; /* 1 if CxFE */ 18500406dffSbellard int n_flag = opcode & 0x200000; /* 1 if CNxx */ 18600406dffSbellard unsigned int flags = 0; 18700406dffSbellard 18800406dffSbellard //printk("PerformComparison(0x%08x)\n",opcode); 18900406dffSbellard 19000406dffSbellard Fn = getFn(opcode); 19100406dffSbellard Fm = getFm(opcode); 19200406dffSbellard 19300406dffSbellard /* Check for unordered condition and convert all operands to 80-bit 19400406dffSbellard format. 19500406dffSbellard ?? Might be some mileage in avoiding this conversion if possible. 19600406dffSbellard Eg, if both operands are 32-bit, detect this and do a 32-bit 19700406dffSbellard comparison (cheaper than an 80-bit one). */ 19800406dffSbellard switch (fpa11->fType[Fn]) 19900406dffSbellard { 20000406dffSbellard case typeSingle: 20100406dffSbellard //printk("single.\n"); 202*3ebe80c2SPeter Maydell if (float32_is_any_nan(fpa11->fpreg[Fn].fSingle)) 20300406dffSbellard goto unordered; 20420495218Sbellard rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status); 20500406dffSbellard break; 20600406dffSbellard 20700406dffSbellard case typeDouble: 20800406dffSbellard //printk("double.\n"); 209*3ebe80c2SPeter Maydell if (float64_is_any_nan(fpa11->fpreg[Fn].fDouble)) 21000406dffSbellard goto unordered; 21120495218Sbellard rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status); 21200406dffSbellard break; 21300406dffSbellard 21400406dffSbellard case typeExtended: 21500406dffSbellard //printk("extended.\n"); 216*3ebe80c2SPeter Maydell if (floatx80_is_any_nan(fpa11->fpreg[Fn].fExtended)) 21700406dffSbellard goto unordered; 21800406dffSbellard rFn = fpa11->fpreg[Fn].fExtended; 21900406dffSbellard break; 22000406dffSbellard 22100406dffSbellard default: return 0; 22200406dffSbellard } 22300406dffSbellard 22400406dffSbellard if (CONSTANT_FM(opcode)) 22500406dffSbellard { 22600406dffSbellard //printk("Fm is a constant: #%d.\n",Fm); 22700406dffSbellard rFm = getExtendedConstant(Fm); 228*3ebe80c2SPeter Maydell if (floatx80_is_any_nan(rFm)) 22900406dffSbellard goto unordered; 23000406dffSbellard } 23100406dffSbellard else 23200406dffSbellard { 23300406dffSbellard //printk("Fm = r%d which contains a ",Fm); 23400406dffSbellard switch (fpa11->fType[Fm]) 23500406dffSbellard { 23600406dffSbellard case typeSingle: 23700406dffSbellard //printk("single.\n"); 238*3ebe80c2SPeter Maydell if (float32_is_any_nan(fpa11->fpreg[Fm].fSingle)) 23900406dffSbellard goto unordered; 24020495218Sbellard rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle, &fpa11->fp_status); 24100406dffSbellard break; 24200406dffSbellard 24300406dffSbellard case typeDouble: 24400406dffSbellard //printk("double.\n"); 245*3ebe80c2SPeter Maydell if (float64_is_any_nan(fpa11->fpreg[Fm].fDouble)) 24600406dffSbellard goto unordered; 24720495218Sbellard rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble, &fpa11->fp_status); 24800406dffSbellard break; 24900406dffSbellard 25000406dffSbellard case typeExtended: 25100406dffSbellard //printk("extended.\n"); 252*3ebe80c2SPeter Maydell if (floatx80_is_any_nan(fpa11->fpreg[Fm].fExtended)) 25300406dffSbellard goto unordered; 25400406dffSbellard rFm = fpa11->fpreg[Fm].fExtended; 25500406dffSbellard break; 25600406dffSbellard 25700406dffSbellard default: return 0; 25800406dffSbellard } 25900406dffSbellard } 26000406dffSbellard 26100406dffSbellard if (n_flag) 26200406dffSbellard { 26300406dffSbellard rFm.high ^= 0x8000; 26400406dffSbellard } 26500406dffSbellard 26600406dffSbellard return PerformComparisonOperation(rFn,rFm); 26700406dffSbellard 26800406dffSbellard unordered: 26900406dffSbellard /* ?? The FPA data sheet is pretty vague about this, in particular 27000406dffSbellard about whether the non-E comparisons can ever raise exceptions. 27100406dffSbellard This implementation is based on a combination of what it says in 27200406dffSbellard the data sheet, observation of how the Acorn emulator actually 27300406dffSbellard behaves (and how programs expect it to) and guesswork. */ 27400406dffSbellard flags |= CC_OVERFLOW; 27500406dffSbellard flags &= ~(CC_ZERO | CC_NEGATIVE); 27600406dffSbellard 27700406dffSbellard if (BIT_AC & readFPSR()) flags |= CC_CARRY; 27800406dffSbellard 27920495218Sbellard if (e_flag) float_raise(float_flag_invalid, &fpa11->fp_status); 28000406dffSbellard 28100406dffSbellard writeConditionCodes(flags); 28200406dffSbellard return 1; 28300406dffSbellard } 284