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