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