1 /* 2 NetWinder Floating Point Emulator 3 (c) Rebel.COM, 1998,1999 4 (c) Philip Blundell, 1999, 2001 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, write to the Free Software 20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 */ 22 23 #include "fpa11.h" 24 #include "fpopcode.h" 25 #include "fpa11.inl" 26 #include "fpmodule.h" 27 #include "fpmodule.inl" 28 #include "softfloat.h" 29 30 #ifdef CONFIG_FPE_NWFPE_XP 31 extern flag floatx80_is_nan(floatx80); 32 #endif 33 34 unsigned int PerformFLT(const unsigned int opcode); 35 unsigned int PerformFIX(const unsigned int opcode); 36 37 static unsigned int PerformComparison(const unsigned int opcode); 38 39 unsigned int EmulateCPRT(const unsigned int opcode) 40 { 41 42 if (opcode & 0x800000) { 43 /* This is some variant of a comparison (PerformComparison 44 will sort out which one). Since most of the other CPRT 45 instructions are oddball cases of some sort or other it 46 makes 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 case FLT_CODE >> 20: 53 return PerformFLT(opcode); 54 break; 55 case FIX_CODE >> 20: 56 return PerformFIX(opcode); 57 break; 58 59 case WFS_CODE >> 20: 60 writeFPSR(readRegister(getRd(opcode))); 61 break; 62 case RFS_CODE >> 20: 63 writeRegister(getRd(opcode), readFPSR()); 64 break; 65 66 default: 67 return 0; 68 } 69 70 return 1; 71 } 72 73 unsigned int PerformFLT(const unsigned int opcode) 74 { 75 FPA11 *fpa11 = GET_FPA11(); 76 struct roundingData roundData; 77 78 roundData.mode = SetRoundingMode(opcode); 79 roundData.precision = SetRoundingPrecision(opcode); 80 roundData.exception = 0; 81 82 switch (opcode & MASK_ROUNDING_PRECISION) { 83 case ROUND_SINGLE: 84 { 85 fpa11->fType[getFn(opcode)] = typeSingle; 86 fpa11->fpreg[getFn(opcode)].fSingle = int32_to_float32(&roundData, readRegister(getRd(opcode))); 87 } 88 break; 89 90 case ROUND_DOUBLE: 91 { 92 fpa11->fType[getFn(opcode)] = typeDouble; 93 fpa11->fpreg[getFn(opcode)].fDouble = int32_to_float64(readRegister(getRd(opcode))); 94 } 95 break; 96 97 #ifdef CONFIG_FPE_NWFPE_XP 98 case ROUND_EXTENDED: 99 { 100 fpa11->fType[getFn(opcode)] = typeExtended; 101 fpa11->fpreg[getFn(opcode)].fExtended = int32_to_floatx80(readRegister(getRd(opcode))); 102 } 103 break; 104 #endif 105 106 default: 107 return 0; 108 } 109 110 if (roundData.exception) 111 float_raise(roundData.exception); 112 113 return 1; 114 } 115 116 unsigned int PerformFIX(const unsigned int opcode) 117 { 118 FPA11 *fpa11 = GET_FPA11(); 119 unsigned int Fn = getFm(opcode); 120 struct roundingData roundData; 121 122 roundData.mode = SetRoundingMode(opcode); 123 roundData.precision = SetRoundingPrecision(opcode); 124 roundData.exception = 0; 125 126 switch (fpa11->fType[Fn]) { 127 case typeSingle: 128 { 129 writeRegister(getRd(opcode), float32_to_int32(&roundData, fpa11->fpreg[Fn].fSingle)); 130 } 131 break; 132 133 case typeDouble: 134 { 135 writeRegister(getRd(opcode), float64_to_int32(&roundData, fpa11->fpreg[Fn].fDouble)); 136 } 137 break; 138 139 #ifdef CONFIG_FPE_NWFPE_XP 140 case typeExtended: 141 { 142 writeRegister(getRd(opcode), floatx80_to_int32(&roundData, fpa11->fpreg[Fn].fExtended)); 143 } 144 break; 145 #endif 146 147 default: 148 return 0; 149 } 150 151 if (roundData.exception) 152 float_raise(roundData.exception); 153 154 return 1; 155 } 156 157 /* This instruction sets the flags N, Z, C, V in the FPSR. */ 158 static unsigned int PerformComparison(const unsigned int opcode) 159 { 160 FPA11 *fpa11 = GET_FPA11(); 161 unsigned int Fn = getFn(opcode), Fm = getFm(opcode); 162 int e_flag = opcode & 0x400000; /* 1 if CxFE */ 163 int n_flag = opcode & 0x200000; /* 1 if CNxx */ 164 unsigned int flags = 0; 165 166 #ifdef CONFIG_FPE_NWFPE_XP 167 floatx80 rFn, rFm; 168 169 /* Check for unordered condition and convert all operands to 80-bit 170 format. 171 ?? Might be some mileage in avoiding this conversion if possible. 172 Eg, if both operands are 32-bit, detect this and do a 32-bit 173 comparison (cheaper than an 80-bit one). */ 174 switch (fpa11->fType[Fn]) { 175 case typeSingle: 176 //printk("single.\n"); 177 if (float32_is_nan(fpa11->fpreg[Fn].fSingle)) 178 goto unordered; 179 rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle); 180 break; 181 182 case typeDouble: 183 //printk("double.\n"); 184 if (float64_is_nan(fpa11->fpreg[Fn].fDouble)) 185 goto unordered; 186 rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble); 187 break; 188 189 case typeExtended: 190 //printk("extended.\n"); 191 if (floatx80_is_nan(fpa11->fpreg[Fn].fExtended)) 192 goto unordered; 193 rFn = fpa11->fpreg[Fn].fExtended; 194 break; 195 196 default: 197 return 0; 198 } 199 200 if (CONSTANT_FM(opcode)) { 201 //printk("Fm is a constant: #%d.\n",Fm); 202 rFm = getExtendedConstant(Fm); 203 if (floatx80_is_nan(rFm)) 204 goto unordered; 205 } else { 206 //printk("Fm = r%d which contains a ",Fm); 207 switch (fpa11->fType[Fm]) { 208 case typeSingle: 209 //printk("single.\n"); 210 if (float32_is_nan(fpa11->fpreg[Fm].fSingle)) 211 goto unordered; 212 rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle); 213 break; 214 215 case typeDouble: 216 //printk("double.\n"); 217 if (float64_is_nan(fpa11->fpreg[Fm].fDouble)) 218 goto unordered; 219 rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble); 220 break; 221 222 case typeExtended: 223 //printk("extended.\n"); 224 if (floatx80_is_nan(fpa11->fpreg[Fm].fExtended)) 225 goto unordered; 226 rFm = fpa11->fpreg[Fm].fExtended; 227 break; 228 229 default: 230 return 0; 231 } 232 } 233 234 if (n_flag) 235 rFm.high ^= 0x8000; 236 237 /* test for less than condition */ 238 if (floatx80_lt(rFn, rFm)) 239 flags |= CC_NEGATIVE; 240 241 /* test for equal condition */ 242 if (floatx80_eq(rFn, rFm)) 243 flags |= CC_ZERO; 244 245 /* test for greater than or equal condition */ 246 if (floatx80_lt(rFm, rFn)) 247 flags |= CC_CARRY; 248 249 #else 250 if (CONSTANT_FM(opcode)) { 251 /* Fm is a constant. Do the comparison in whatever precision 252 Fn happens to be stored in. */ 253 if (fpa11->fType[Fn] == typeSingle) { 254 float32 rFm = getSingleConstant(Fm); 255 float32 rFn = fpa11->fpreg[Fn].fSingle; 256 257 if (float32_is_nan(rFn)) 258 goto unordered; 259 260 if (n_flag) 261 rFm ^= 0x80000000; 262 263 /* test for less than condition */ 264 if (float32_lt_nocheck(rFn, rFm)) 265 flags |= CC_NEGATIVE; 266 267 /* test for equal condition */ 268 if (float32_eq_nocheck(rFn, rFm)) 269 flags |= CC_ZERO; 270 271 /* test for greater than or equal condition */ 272 if (float32_lt_nocheck(rFm, rFn)) 273 flags |= CC_CARRY; 274 } else { 275 float64 rFm = getDoubleConstant(Fm); 276 float64 rFn = fpa11->fpreg[Fn].fDouble; 277 278 if (float64_is_nan(rFn)) 279 goto unordered; 280 281 if (n_flag) 282 rFm ^= 0x8000000000000000ULL; 283 284 /* test for less than condition */ 285 if (float64_lt_nocheck(rFn, rFm)) 286 flags |= CC_NEGATIVE; 287 288 /* test for equal condition */ 289 if (float64_eq_nocheck(rFn, rFm)) 290 flags |= CC_ZERO; 291 292 /* test for greater than or equal condition */ 293 if (float64_lt_nocheck(rFm, rFn)) 294 flags |= CC_CARRY; 295 } 296 } else { 297 /* Both operands are in registers. */ 298 if (fpa11->fType[Fn] == typeSingle 299 && fpa11->fType[Fm] == typeSingle) { 300 float32 rFm = fpa11->fpreg[Fm].fSingle; 301 float32 rFn = fpa11->fpreg[Fn].fSingle; 302 303 if (float32_is_nan(rFn) 304 || float32_is_nan(rFm)) 305 goto unordered; 306 307 if (n_flag) 308 rFm ^= 0x80000000; 309 310 /* test for less than condition */ 311 if (float32_lt_nocheck(rFn, rFm)) 312 flags |= CC_NEGATIVE; 313 314 /* test for equal condition */ 315 if (float32_eq_nocheck(rFn, rFm)) 316 flags |= CC_ZERO; 317 318 /* test for greater than or equal condition */ 319 if (float32_lt_nocheck(rFm, rFn)) 320 flags |= CC_CARRY; 321 } else { 322 /* Promote 32-bit operand to 64 bits. */ 323 float64 rFm, rFn; 324 325 rFm = (fpa11->fType[Fm] == typeSingle) ? 326 float32_to_float64(fpa11->fpreg[Fm].fSingle) 327 : fpa11->fpreg[Fm].fDouble; 328 329 rFn = (fpa11->fType[Fn] == typeSingle) ? 330 float32_to_float64(fpa11->fpreg[Fn].fSingle) 331 : fpa11->fpreg[Fn].fDouble; 332 333 if (float64_is_nan(rFn) 334 || float64_is_nan(rFm)) 335 goto unordered; 336 337 if (n_flag) 338 rFm ^= 0x8000000000000000ULL; 339 340 /* test for less than condition */ 341 if (float64_lt_nocheck(rFn, rFm)) 342 flags |= CC_NEGATIVE; 343 344 /* test for equal condition */ 345 if (float64_eq_nocheck(rFn, rFm)) 346 flags |= CC_ZERO; 347 348 /* test for greater than or equal condition */ 349 if (float64_lt_nocheck(rFm, rFn)) 350 flags |= CC_CARRY; 351 } 352 } 353 354 #endif 355 356 writeConditionCodes(flags); 357 358 return 1; 359 360 unordered: 361 /* ?? The FPA data sheet is pretty vague about this, in particular 362 about whether the non-E comparisons can ever raise exceptions. 363 This implementation is based on a combination of what it says in 364 the data sheet, observation of how the Acorn emulator actually 365 behaves (and how programs expect it to) and guesswork. */ 366 flags |= CC_OVERFLOW; 367 flags &= ~(CC_ZERO | CC_NEGATIVE); 368 369 if (BIT_AC & readFPSR()) 370 flags |= CC_CARRY; 371 372 if (e_flag) 373 float_raise(float_flag_invalid); 374 375 writeConditionCodes(flags); 376 return 1; 377 } 378