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