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