1 /* 2 NetWinder Floating Point Emulator 3 (c) Rebel.com, 1998-1999 4 (c) Philip Blundell, 1998, 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 <linux/config.h> 24 #include "fpa11.h" 25 #include "softfloat.h" 26 #include "fpopcode.h" 27 #include "fpmodule.h" 28 #include "fpmodule.inl" 29 30 #include <asm/uaccess.h> 31 32 static inline void loadSingle(const unsigned int Fn, const unsigned int __user *pMem) 33 { 34 FPA11 *fpa11 = GET_FPA11(); 35 fpa11->fType[Fn] = typeSingle; 36 get_user(fpa11->fpreg[Fn].fSingle, pMem); 37 } 38 39 static inline void loadDouble(const unsigned int Fn, const unsigned int __user *pMem) 40 { 41 FPA11 *fpa11 = GET_FPA11(); 42 unsigned int *p; 43 p = (unsigned int *) &fpa11->fpreg[Fn].fDouble; 44 fpa11->fType[Fn] = typeDouble; 45 #ifdef __ARMEB__ 46 get_user(p[0], &pMem[0]); /* sign & exponent */ 47 get_user(p[1], &pMem[1]); 48 #else 49 get_user(p[0], &pMem[1]); 50 get_user(p[1], &pMem[0]); /* sign & exponent */ 51 #endif 52 } 53 54 #ifdef CONFIG_FPE_NWFPE_XP 55 static inline void loadExtended(const unsigned int Fn, const unsigned int __user *pMem) 56 { 57 FPA11 *fpa11 = GET_FPA11(); 58 unsigned int *p; 59 p = (unsigned int *) &fpa11->fpreg[Fn].fExtended; 60 fpa11->fType[Fn] = typeExtended; 61 get_user(p[0], &pMem[0]); /* sign & exponent */ 62 get_user(p[1], &pMem[2]); /* ls bits */ 63 get_user(p[2], &pMem[1]); /* ms bits */ 64 } 65 #endif 66 67 static inline void loadMultiple(const unsigned int Fn, const unsigned int __user *pMem) 68 { 69 FPA11 *fpa11 = GET_FPA11(); 70 register unsigned int *p; 71 unsigned long x; 72 73 p = (unsigned int *) &(fpa11->fpreg[Fn]); 74 get_user(x, &pMem[0]); 75 fpa11->fType[Fn] = (x >> 14) & 0x00000003; 76 77 switch (fpa11->fType[Fn]) { 78 case typeSingle: 79 case typeDouble: 80 { 81 get_user(p[0], &pMem[2]); /* Single */ 82 get_user(p[1], &pMem[1]); /* double msw */ 83 p[2] = 0; /* empty */ 84 } 85 break; 86 87 #ifdef CONFIG_FPE_NWFPE_XP 88 case typeExtended: 89 { 90 get_user(p[1], &pMem[2]); 91 get_user(p[2], &pMem[1]); /* msw */ 92 p[0] = (x & 0x80003fff); 93 } 94 break; 95 #endif 96 } 97 } 98 99 static inline void storeSingle(struct roundingData *roundData, const unsigned int Fn, unsigned int __user *pMem) 100 { 101 FPA11 *fpa11 = GET_FPA11(); 102 union { 103 float32 f; 104 unsigned int i[1]; 105 } val; 106 107 switch (fpa11->fType[Fn]) { 108 case typeDouble: 109 val.f = float64_to_float32(roundData, fpa11->fpreg[Fn].fDouble); 110 break; 111 112 #ifdef CONFIG_FPE_NWFPE_XP 113 case typeExtended: 114 val.f = floatx80_to_float32(roundData, fpa11->fpreg[Fn].fExtended); 115 break; 116 #endif 117 118 default: 119 val.f = fpa11->fpreg[Fn].fSingle; 120 } 121 122 put_user(val.i[0], pMem); 123 } 124 125 static inline void storeDouble(struct roundingData *roundData, const unsigned int Fn, unsigned int __user *pMem) 126 { 127 FPA11 *fpa11 = GET_FPA11(); 128 union { 129 float64 f; 130 unsigned int i[2]; 131 } val; 132 133 switch (fpa11->fType[Fn]) { 134 case typeSingle: 135 val.f = float32_to_float64(fpa11->fpreg[Fn].fSingle); 136 break; 137 138 #ifdef CONFIG_FPE_NWFPE_XP 139 case typeExtended: 140 val.f = floatx80_to_float64(roundData, fpa11->fpreg[Fn].fExtended); 141 break; 142 #endif 143 144 default: 145 val.f = fpa11->fpreg[Fn].fDouble; 146 } 147 148 #ifdef __ARMEB__ 149 put_user(val.i[0], &pMem[0]); /* msw */ 150 put_user(val.i[1], &pMem[1]); /* lsw */ 151 #else 152 put_user(val.i[1], &pMem[0]); /* msw */ 153 put_user(val.i[0], &pMem[1]); /* lsw */ 154 #endif 155 } 156 157 #ifdef CONFIG_FPE_NWFPE_XP 158 static inline void storeExtended(const unsigned int Fn, unsigned int __user *pMem) 159 { 160 FPA11 *fpa11 = GET_FPA11(); 161 union { 162 floatx80 f; 163 unsigned int i[3]; 164 } val; 165 166 switch (fpa11->fType[Fn]) { 167 case typeSingle: 168 val.f = float32_to_floatx80(fpa11->fpreg[Fn].fSingle); 169 break; 170 171 case typeDouble: 172 val.f = float64_to_floatx80(fpa11->fpreg[Fn].fDouble); 173 break; 174 175 default: 176 val.f = fpa11->fpreg[Fn].fExtended; 177 } 178 179 put_user(val.i[0], &pMem[0]); /* sign & exp */ 180 put_user(val.i[1], &pMem[2]); 181 put_user(val.i[2], &pMem[1]); /* msw */ 182 } 183 #endif 184 185 static inline void storeMultiple(const unsigned int Fn, unsigned int __user *pMem) 186 { 187 FPA11 *fpa11 = GET_FPA11(); 188 register unsigned int nType, *p; 189 190 p = (unsigned int *) &(fpa11->fpreg[Fn]); 191 nType = fpa11->fType[Fn]; 192 193 switch (nType) { 194 case typeSingle: 195 case typeDouble: 196 { 197 put_user(p[0], &pMem[2]); /* single */ 198 put_user(p[1], &pMem[1]); /* double msw */ 199 put_user(nType << 14, &pMem[0]); 200 } 201 break; 202 203 #ifdef CONFIG_FPE_NWFPE_XP 204 case typeExtended: 205 { 206 put_user(p[2], &pMem[1]); /* msw */ 207 put_user(p[1], &pMem[2]); 208 put_user((p[0] & 0x80003fff) | (nType << 14), &pMem[0]); 209 } 210 break; 211 #endif 212 } 213 } 214 215 unsigned int PerformLDF(const unsigned int opcode) 216 { 217 unsigned int __user *pBase, *pAddress, *pFinal; 218 unsigned int nRc = 1, write_back = WRITE_BACK(opcode); 219 220 pBase = (unsigned int __user *) readRegister(getRn(opcode)); 221 if (REG_PC == getRn(opcode)) { 222 pBase += 2; 223 write_back = 0; 224 } 225 226 pFinal = pBase; 227 if (BIT_UP_SET(opcode)) 228 pFinal += getOffset(opcode); 229 else 230 pFinal -= getOffset(opcode); 231 232 if (PREINDEXED(opcode)) 233 pAddress = pFinal; 234 else 235 pAddress = pBase; 236 237 switch (opcode & MASK_TRANSFER_LENGTH) { 238 case TRANSFER_SINGLE: 239 loadSingle(getFd(opcode), pAddress); 240 break; 241 case TRANSFER_DOUBLE: 242 loadDouble(getFd(opcode), pAddress); 243 break; 244 #ifdef CONFIG_FPE_NWFPE_XP 245 case TRANSFER_EXTENDED: 246 loadExtended(getFd(opcode), pAddress); 247 break; 248 #endif 249 default: 250 nRc = 0; 251 } 252 253 if (write_back) 254 writeRegister(getRn(opcode), (unsigned long) pFinal); 255 return nRc; 256 } 257 258 unsigned int PerformSTF(const unsigned int opcode) 259 { 260 unsigned int __user *pBase, *pAddress, *pFinal; 261 unsigned int nRc = 1, write_back = WRITE_BACK(opcode); 262 struct roundingData roundData; 263 264 roundData.mode = SetRoundingMode(opcode); 265 roundData.precision = SetRoundingPrecision(opcode); 266 roundData.exception = 0; 267 268 pBase = (unsigned int __user *) readRegister(getRn(opcode)); 269 if (REG_PC == getRn(opcode)) { 270 pBase += 2; 271 write_back = 0; 272 } 273 274 pFinal = pBase; 275 if (BIT_UP_SET(opcode)) 276 pFinal += getOffset(opcode); 277 else 278 pFinal -= getOffset(opcode); 279 280 if (PREINDEXED(opcode)) 281 pAddress = pFinal; 282 else 283 pAddress = pBase; 284 285 switch (opcode & MASK_TRANSFER_LENGTH) { 286 case TRANSFER_SINGLE: 287 storeSingle(&roundData, getFd(opcode), pAddress); 288 break; 289 case TRANSFER_DOUBLE: 290 storeDouble(&roundData, getFd(opcode), pAddress); 291 break; 292 #ifdef CONFIG_FPE_NWFPE_XP 293 case TRANSFER_EXTENDED: 294 storeExtended(getFd(opcode), pAddress); 295 break; 296 #endif 297 default: 298 nRc = 0; 299 } 300 301 if (roundData.exception) 302 float_raise(roundData.exception); 303 304 if (write_back) 305 writeRegister(getRn(opcode), (unsigned long) pFinal); 306 return nRc; 307 } 308 309 unsigned int PerformLFM(const unsigned int opcode) 310 { 311 unsigned int __user *pBase, *pAddress, *pFinal; 312 unsigned int i, Fd, write_back = WRITE_BACK(opcode); 313 314 pBase = (unsigned int __user *) readRegister(getRn(opcode)); 315 if (REG_PC == getRn(opcode)) { 316 pBase += 2; 317 write_back = 0; 318 } 319 320 pFinal = pBase; 321 if (BIT_UP_SET(opcode)) 322 pFinal += getOffset(opcode); 323 else 324 pFinal -= getOffset(opcode); 325 326 if (PREINDEXED(opcode)) 327 pAddress = pFinal; 328 else 329 pAddress = pBase; 330 331 Fd = getFd(opcode); 332 for (i = getRegisterCount(opcode); i > 0; i--) { 333 loadMultiple(Fd, pAddress); 334 pAddress += 3; 335 Fd++; 336 if (Fd == 8) 337 Fd = 0; 338 } 339 340 if (write_back) 341 writeRegister(getRn(opcode), (unsigned long) pFinal); 342 return 1; 343 } 344 345 unsigned int PerformSFM(const unsigned int opcode) 346 { 347 unsigned int __user *pBase, *pAddress, *pFinal; 348 unsigned int i, Fd, write_back = WRITE_BACK(opcode); 349 350 pBase = (unsigned int __user *) readRegister(getRn(opcode)); 351 if (REG_PC == getRn(opcode)) { 352 pBase += 2; 353 write_back = 0; 354 } 355 356 pFinal = pBase; 357 if (BIT_UP_SET(opcode)) 358 pFinal += getOffset(opcode); 359 else 360 pFinal -= getOffset(opcode); 361 362 if (PREINDEXED(opcode)) 363 pAddress = pFinal; 364 else 365 pAddress = pBase; 366 367 Fd = getFd(opcode); 368 for (i = getRegisterCount(opcode); i > 0; i--) { 369 storeMultiple(Fd, pAddress); 370 pAddress += 3; 371 Fd++; 372 if (Fd == 8) 373 Fd = 0; 374 } 375 376 if (write_back) 377 writeRegister(getRn(opcode), (unsigned long) pFinal); 378 return 1; 379 } 380 381 unsigned int EmulateCPDT(const unsigned int opcode) 382 { 383 unsigned int nRc = 0; 384 385 if (LDF_OP(opcode)) { 386 nRc = PerformLDF(opcode); 387 } else if (LFM_OP(opcode)) { 388 nRc = PerformLFM(opcode); 389 } else if (STF_OP(opcode)) { 390 nRc = PerformSTF(opcode); 391 } else if (SFM_OP(opcode)) { 392 nRc = PerformSFM(opcode); 393 } else { 394 nRc = 0; 395 } 396 397 return nRc; 398 } 399