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