1 /* 2 NetWinder Floating Point Emulator 3 (c) Rebel.COM, 1998,1999 4 (c) Philip Blundell, 1999 5 6 Direct questions, comments to Scott Bambrough <scottb@netwinder.org> 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 2 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program; if not, see <http://www.gnu.org/licenses/>. 20 */ 21 22 #include "fpa11.h" 23 #include "fpu/softfloat.h" 24 #include "fpopcode.h" 25 #include "fpa11.inl" 26 //#include "fpmodule.h" 27 //#include "fpmodule.inl" 28 29 unsigned int PerformFLT(const unsigned int opcode); 30 unsigned int PerformFIX(const unsigned int opcode); 31 32 static unsigned int 33 PerformComparison(const unsigned int opcode); 34 35 unsigned int EmulateCPRT(const unsigned int opcode) 36 { 37 unsigned int nRc = 1; 38 39 //printk("EmulateCPRT(0x%08x)\n",opcode); 40 41 if (opcode & 0x800000) 42 { 43 /* This is some variant of a comparison (PerformComparison will 44 sort out which one). Since most of the other CPRT 45 instructions are oddball cases of some sort or other it makes 46 sense to pull this out into a fast path. */ 47 return PerformComparison(opcode); 48 } 49 50 /* Hint to GCC that we'd like a jump table rather than a load of CMPs */ 51 switch ((opcode & 0x700000) >> 20) 52 { 53 case FLT_CODE >> 20: nRc = PerformFLT(opcode); break; 54 case FIX_CODE >> 20: nRc = PerformFIX(opcode); break; 55 56 case WFS_CODE >> 20: writeFPSR(readRegister(getRd(opcode))); break; 57 case RFS_CODE >> 20: writeRegister(getRd(opcode),readFPSR()); break; 58 59 #if 0 /* We currently have no use for the FPCR, so there's no point 60 in emulating it. */ 61 case WFC_CODE >> 20: writeFPCR(readRegister(getRd(opcode))); 62 case RFC_CODE >> 20: writeRegister(getRd(opcode),readFPCR()); break; 63 #endif 64 65 default: nRc = 0; 66 } 67 68 return nRc; 69 } 70 71 unsigned int PerformFLT(const unsigned int opcode) 72 { 73 FPA11 *fpa11 = GET_FPA11(); 74 75 unsigned int nRc = 1; 76 SetRoundingMode(opcode); 77 78 switch (opcode & MASK_ROUNDING_PRECISION) 79 { 80 case ROUND_SINGLE: 81 { 82 fpa11->fType[getFn(opcode)] = typeSingle; 83 fpa11->fpreg[getFn(opcode)].fSingle = 84 int32_to_float32(readRegister(getRd(opcode)), &fpa11->fp_status); 85 } 86 break; 87 88 case ROUND_DOUBLE: 89 { 90 fpa11->fType[getFn(opcode)] = typeDouble; 91 fpa11->fpreg[getFn(opcode)].fDouble = 92 int32_to_float64(readRegister(getRd(opcode)), &fpa11->fp_status); 93 } 94 break; 95 96 case ROUND_EXTENDED: 97 { 98 fpa11->fType[getFn(opcode)] = typeExtended; 99 fpa11->fpreg[getFn(opcode)].fExtended = 100 int32_to_floatx80(readRegister(getRd(opcode)), &fpa11->fp_status); 101 } 102 break; 103 104 default: nRc = 0; 105 } 106 107 return nRc; 108 } 109 110 unsigned int PerformFIX(const unsigned int opcode) 111 { 112 FPA11 *fpa11 = GET_FPA11(); 113 unsigned int nRc = 1; 114 unsigned int Fn = getFm(opcode); 115 116 SetRoundingMode(opcode); 117 118 switch (fpa11->fType[Fn]) 119 { 120 case typeSingle: 121 { 122 writeRegister(getRd(opcode), 123 float32_to_int32(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status)); 124 } 125 break; 126 127 case typeDouble: 128 { 129 //printf("F%d is 0x%" PRIx64 "\n",Fn,fpa11->fpreg[Fn].fDouble); 130 writeRegister(getRd(opcode), 131 float64_to_int32(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status)); 132 } 133 break; 134 135 case typeExtended: 136 { 137 writeRegister(getRd(opcode), 138 floatx80_to_int32(fpa11->fpreg[Fn].fExtended, &fpa11->fp_status)); 139 } 140 break; 141 142 default: nRc = 0; 143 } 144 145 return nRc; 146 } 147 148 149 static __inline unsigned int 150 PerformComparisonOperation(floatx80 Fn, floatx80 Fm) 151 { 152 FPA11 *fpa11 = GET_FPA11(); 153 unsigned int flags = 0; 154 155 /* test for less than condition */ 156 if (floatx80_lt(Fn,Fm, &fpa11->fp_status)) 157 { 158 flags |= CC_NEGATIVE; 159 } 160 161 /* test for equal condition */ 162 if (floatx80_eq_quiet(Fn,Fm, &fpa11->fp_status)) 163 { 164 flags |= CC_ZERO; 165 } 166 167 /* test for greater than or equal condition */ 168 if (floatx80_lt(Fm,Fn, &fpa11->fp_status)) 169 { 170 flags |= CC_CARRY; 171 } 172 173 writeConditionCodes(flags); 174 return 1; 175 } 176 177 /* This instruction sets the flags N, Z, C, V in the FPSR. */ 178 179 static unsigned int PerformComparison(const unsigned int opcode) 180 { 181 FPA11 *fpa11 = GET_FPA11(); 182 unsigned int Fn, Fm; 183 floatx80 rFn, rFm; 184 int e_flag = opcode & 0x400000; /* 1 if CxFE */ 185 int n_flag = opcode & 0x200000; /* 1 if CNxx */ 186 unsigned int flags = 0; 187 188 //printk("PerformComparison(0x%08x)\n",opcode); 189 190 Fn = getFn(opcode); 191 Fm = getFm(opcode); 192 193 /* Check for unordered condition and convert all operands to 80-bit 194 format. 195 ?? Might be some mileage in avoiding this conversion if possible. 196 Eg, if both operands are 32-bit, detect this and do a 32-bit 197 comparison (cheaper than an 80-bit one). */ 198 switch (fpa11->fType[Fn]) 199 { 200 case typeSingle: 201 //printk("single.\n"); 202 if (float32_is_any_nan(fpa11->fpreg[Fn].fSingle)) 203 goto unordered; 204 rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status); 205 break; 206 207 case typeDouble: 208 //printk("double.\n"); 209 if (float64_is_any_nan(fpa11->fpreg[Fn].fDouble)) 210 goto unordered; 211 rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status); 212 break; 213 214 case typeExtended: 215 //printk("extended.\n"); 216 if (floatx80_is_any_nan(fpa11->fpreg[Fn].fExtended)) 217 goto unordered; 218 rFn = fpa11->fpreg[Fn].fExtended; 219 break; 220 221 default: return 0; 222 } 223 224 if (CONSTANT_FM(opcode)) 225 { 226 //printk("Fm is a constant: #%d.\n",Fm); 227 rFm = getExtendedConstant(Fm); 228 if (floatx80_is_any_nan(rFm)) 229 goto unordered; 230 } 231 else 232 { 233 //printk("Fm = r%d which contains a ",Fm); 234 switch (fpa11->fType[Fm]) 235 { 236 case typeSingle: 237 //printk("single.\n"); 238 if (float32_is_any_nan(fpa11->fpreg[Fm].fSingle)) 239 goto unordered; 240 rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle, &fpa11->fp_status); 241 break; 242 243 case typeDouble: 244 //printk("double.\n"); 245 if (float64_is_any_nan(fpa11->fpreg[Fm].fDouble)) 246 goto unordered; 247 rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble, &fpa11->fp_status); 248 break; 249 250 case typeExtended: 251 //printk("extended.\n"); 252 if (floatx80_is_any_nan(fpa11->fpreg[Fm].fExtended)) 253 goto unordered; 254 rFm = fpa11->fpreg[Fm].fExtended; 255 break; 256 257 default: return 0; 258 } 259 } 260 261 if (n_flag) 262 { 263 rFm.high ^= 0x8000; 264 } 265 266 return PerformComparisonOperation(rFn,rFm); 267 268 unordered: 269 /* ?? The FPA data sheet is pretty vague about this, in particular 270 about whether the non-E comparisons can ever raise exceptions. 271 This implementation is based on a combination of what it says in 272 the data sheet, observation of how the Acorn emulator actually 273 behaves (and how programs expect it to) and guesswork. */ 274 flags |= CC_OVERFLOW; 275 flags &= ~(CC_ZERO | CC_NEGATIVE); 276 277 if (BIT_AC & readFPSR()) flags |= CC_CARRY; 278 279 if (e_flag) float_raise(float_flag_invalid, &fpa11->fp_status); 280 281 writeConditionCodes(flags); 282 return 1; 283 } 284