1 /* 2 * m68k FPU helpers 3 * 4 * Copyright (c) 2006-2007 CodeSourcery 5 * Written by Paul Brook 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 19 */ 20 21 #include "qemu/osdep.h" 22 #include "cpu.h" 23 #include "exec/helper-proto.h" 24 #include "exec/exec-all.h" 25 #include "exec/cpu_ldst.h" 26 #include "softfloat.h" 27 28 /* Undefined offsets may be different on various FPU. 29 * On 68040 they return 0.0 (floatx80_zero) 30 */ 31 32 static const floatx80 fpu_rom[128] = { 33 [0x00] = make_floatx80_init(0x4000, 0xc90fdaa22168c235ULL), /* Pi */ 34 [0x0b] = make_floatx80_init(0x3ffd, 0x9a209a84fbcff798ULL), /* Log10(2) */ 35 [0x0c] = make_floatx80_init(0x4000, 0xadf85458a2bb4a9aULL), /* e */ 36 [0x0d] = make_floatx80_init(0x3fff, 0xb8aa3b295c17f0bcULL), /* Log2(e) */ 37 [0x0e] = make_floatx80_init(0x3ffd, 0xde5bd8a937287195ULL), /* Log10(e) */ 38 [0x0f] = make_floatx80_init(0x0000, 0x0000000000000000ULL), /* Zero */ 39 [0x30] = make_floatx80_init(0x3ffe, 0xb17217f7d1cf79acULL), /* ln(2) */ 40 [0x31] = make_floatx80_init(0x4000, 0x935d8dddaaa8ac17ULL), /* ln(10) */ 41 [0x32] = make_floatx80_init(0x3fff, 0x8000000000000000ULL), /* 10^0 */ 42 [0x33] = make_floatx80_init(0x4002, 0xa000000000000000ULL), /* 10^1 */ 43 [0x34] = make_floatx80_init(0x4005, 0xc800000000000000ULL), /* 10^2 */ 44 [0x35] = make_floatx80_init(0x400c, 0x9c40000000000000ULL), /* 10^4 */ 45 [0x36] = make_floatx80_init(0x4019, 0xbebc200000000000ULL), /* 10^8 */ 46 [0x37] = make_floatx80_init(0x4034, 0x8e1bc9bf04000000ULL), /* 10^16 */ 47 [0x38] = make_floatx80_init(0x4069, 0x9dc5ada82b70b59eULL), /* 10^32 */ 48 [0x39] = make_floatx80_init(0x40d3, 0xc2781f49ffcfa6d5ULL), /* 10^64 */ 49 [0x3a] = make_floatx80_init(0x41a8, 0x93ba47c980e98ce0ULL), /* 10^128 */ 50 [0x3b] = make_floatx80_init(0x4351, 0xaa7eebfb9df9de8eULL), /* 10^256 */ 51 [0x3c] = make_floatx80_init(0x46a3, 0xe319a0aea60e91c7ULL), /* 10^512 */ 52 [0x3d] = make_floatx80_init(0x4d48, 0xc976758681750c17ULL), /* 10^1024 */ 53 [0x3e] = make_floatx80_init(0x5a92, 0x9e8b3b5dc53d5de5ULL), /* 10^2048 */ 54 [0x3f] = make_floatx80_init(0x7525, 0xc46052028a20979bULL), /* 10^4096 */ 55 }; 56 57 int32_t HELPER(reds32)(CPUM68KState *env, FPReg *val) 58 { 59 return floatx80_to_int32(val->d, &env->fp_status); 60 } 61 62 float32 HELPER(redf32)(CPUM68KState *env, FPReg *val) 63 { 64 return floatx80_to_float32(val->d, &env->fp_status); 65 } 66 67 void HELPER(exts32)(CPUM68KState *env, FPReg *res, int32_t val) 68 { 69 res->d = int32_to_floatx80(val, &env->fp_status); 70 } 71 72 void HELPER(extf32)(CPUM68KState *env, FPReg *res, float32 val) 73 { 74 res->d = float32_to_floatx80(val, &env->fp_status); 75 } 76 77 void HELPER(extf64)(CPUM68KState *env, FPReg *res, float64 val) 78 { 79 res->d = float64_to_floatx80(val, &env->fp_status); 80 } 81 82 float64 HELPER(redf64)(CPUM68KState *env, FPReg *val) 83 { 84 return floatx80_to_float64(val->d, &env->fp_status); 85 } 86 87 void HELPER(firound)(CPUM68KState *env, FPReg *res, FPReg *val) 88 { 89 res->d = floatx80_round_to_int(val->d, &env->fp_status); 90 } 91 92 static void m68k_restore_precision_mode(CPUM68KState *env) 93 { 94 switch (env->fpcr & FPCR_PREC_MASK) { 95 case FPCR_PREC_X: /* extended */ 96 set_floatx80_rounding_precision(80, &env->fp_status); 97 break; 98 case FPCR_PREC_S: /* single */ 99 set_floatx80_rounding_precision(32, &env->fp_status); 100 break; 101 case FPCR_PREC_D: /* double */ 102 set_floatx80_rounding_precision(64, &env->fp_status); 103 break; 104 case FPCR_PREC_U: /* undefined */ 105 default: 106 break; 107 } 108 } 109 110 static void cf_restore_precision_mode(CPUM68KState *env) 111 { 112 if (env->fpcr & FPCR_PREC_S) { /* single */ 113 set_floatx80_rounding_precision(32, &env->fp_status); 114 } else { /* double */ 115 set_floatx80_rounding_precision(64, &env->fp_status); 116 } 117 } 118 119 static void restore_rounding_mode(CPUM68KState *env) 120 { 121 switch (env->fpcr & FPCR_RND_MASK) { 122 case FPCR_RND_N: /* round to nearest */ 123 set_float_rounding_mode(float_round_nearest_even, &env->fp_status); 124 break; 125 case FPCR_RND_Z: /* round to zero */ 126 set_float_rounding_mode(float_round_to_zero, &env->fp_status); 127 break; 128 case FPCR_RND_M: /* round toward minus infinity */ 129 set_float_rounding_mode(float_round_down, &env->fp_status); 130 break; 131 case FPCR_RND_P: /* round toward positive infinity */ 132 set_float_rounding_mode(float_round_up, &env->fp_status); 133 break; 134 } 135 } 136 137 void cpu_m68k_set_fpcr(CPUM68KState *env, uint32_t val) 138 { 139 env->fpcr = val & 0xffff; 140 141 if (m68k_feature(env, M68K_FEATURE_CF_FPU)) { 142 cf_restore_precision_mode(env); 143 } else { 144 m68k_restore_precision_mode(env); 145 } 146 restore_rounding_mode(env); 147 } 148 149 void HELPER(fitrunc)(CPUM68KState *env, FPReg *res, FPReg *val) 150 { 151 int rounding_mode = get_float_rounding_mode(&env->fp_status); 152 set_float_rounding_mode(float_round_to_zero, &env->fp_status); 153 res->d = floatx80_round_to_int(val->d, &env->fp_status); 154 set_float_rounding_mode(rounding_mode, &env->fp_status); 155 } 156 157 void HELPER(set_fpcr)(CPUM68KState *env, uint32_t val) 158 { 159 cpu_m68k_set_fpcr(env, val); 160 } 161 162 #define PREC_BEGIN(prec) \ 163 do { \ 164 int old; \ 165 old = get_floatx80_rounding_precision(&env->fp_status); \ 166 set_floatx80_rounding_precision(prec, &env->fp_status) \ 167 168 #define PREC_END() \ 169 set_floatx80_rounding_precision(old, &env->fp_status); \ 170 } while (0) 171 172 void HELPER(fsround)(CPUM68KState *env, FPReg *res, FPReg *val) 173 { 174 PREC_BEGIN(32); 175 res->d = floatx80_round(val->d, &env->fp_status); 176 PREC_END(); 177 } 178 179 void HELPER(fdround)(CPUM68KState *env, FPReg *res, FPReg *val) 180 { 181 PREC_BEGIN(64); 182 res->d = floatx80_round(val->d, &env->fp_status); 183 PREC_END(); 184 } 185 186 void HELPER(fsqrt)(CPUM68KState *env, FPReg *res, FPReg *val) 187 { 188 res->d = floatx80_sqrt(val->d, &env->fp_status); 189 } 190 191 void HELPER(fssqrt)(CPUM68KState *env, FPReg *res, FPReg *val) 192 { 193 PREC_BEGIN(32); 194 res->d = floatx80_sqrt(val->d, &env->fp_status); 195 PREC_END(); 196 } 197 198 void HELPER(fdsqrt)(CPUM68KState *env, FPReg *res, FPReg *val) 199 { 200 PREC_BEGIN(64); 201 res->d = floatx80_sqrt(val->d, &env->fp_status); 202 PREC_END(); 203 } 204 205 void HELPER(fabs)(CPUM68KState *env, FPReg *res, FPReg *val) 206 { 207 res->d = floatx80_round(floatx80_abs(val->d), &env->fp_status); 208 } 209 210 void HELPER(fsabs)(CPUM68KState *env, FPReg *res, FPReg *val) 211 { 212 PREC_BEGIN(32); 213 res->d = floatx80_round(floatx80_abs(val->d), &env->fp_status); 214 PREC_END(); 215 } 216 217 void HELPER(fdabs)(CPUM68KState *env, FPReg *res, FPReg *val) 218 { 219 PREC_BEGIN(64); 220 res->d = floatx80_round(floatx80_abs(val->d), &env->fp_status); 221 PREC_END(); 222 } 223 224 void HELPER(fneg)(CPUM68KState *env, FPReg *res, FPReg *val) 225 { 226 res->d = floatx80_round(floatx80_chs(val->d), &env->fp_status); 227 } 228 229 void HELPER(fsneg)(CPUM68KState *env, FPReg *res, FPReg *val) 230 { 231 PREC_BEGIN(32); 232 res->d = floatx80_round(floatx80_chs(val->d), &env->fp_status); 233 PREC_END(); 234 } 235 236 void HELPER(fdneg)(CPUM68KState *env, FPReg *res, FPReg *val) 237 { 238 PREC_BEGIN(64); 239 res->d = floatx80_round(floatx80_chs(val->d), &env->fp_status); 240 PREC_END(); 241 } 242 243 void HELPER(fadd)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) 244 { 245 res->d = floatx80_add(val0->d, val1->d, &env->fp_status); 246 } 247 248 void HELPER(fsadd)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) 249 { 250 PREC_BEGIN(32); 251 res->d = floatx80_add(val0->d, val1->d, &env->fp_status); 252 PREC_END(); 253 } 254 255 void HELPER(fdadd)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) 256 { 257 PREC_BEGIN(64); 258 res->d = floatx80_add(val0->d, val1->d, &env->fp_status); 259 PREC_END(); 260 } 261 262 void HELPER(fsub)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) 263 { 264 res->d = floatx80_sub(val1->d, val0->d, &env->fp_status); 265 } 266 267 void HELPER(fssub)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) 268 { 269 PREC_BEGIN(32); 270 res->d = floatx80_sub(val1->d, val0->d, &env->fp_status); 271 PREC_END(); 272 } 273 274 void HELPER(fdsub)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) 275 { 276 PREC_BEGIN(64); 277 res->d = floatx80_sub(val1->d, val0->d, &env->fp_status); 278 PREC_END(); 279 } 280 281 void HELPER(fmul)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) 282 { 283 res->d = floatx80_mul(val0->d, val1->d, &env->fp_status); 284 } 285 286 void HELPER(fsmul)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) 287 { 288 PREC_BEGIN(32); 289 res->d = floatx80_mul(val0->d, val1->d, &env->fp_status); 290 PREC_END(); 291 } 292 293 void HELPER(fdmul)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) 294 { 295 PREC_BEGIN(64); 296 res->d = floatx80_mul(val0->d, val1->d, &env->fp_status); 297 PREC_END(); 298 } 299 300 void HELPER(fsglmul)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) 301 { 302 int rounding_mode = get_float_rounding_mode(&env->fp_status); 303 floatx80 a, b; 304 305 PREC_BEGIN(32); 306 set_float_rounding_mode(float_round_to_zero, &env->fp_status); 307 a = floatx80_round(val0->d, &env->fp_status); 308 b = floatx80_round(val1->d, &env->fp_status); 309 set_float_rounding_mode(rounding_mode, &env->fp_status); 310 res->d = floatx80_mul(a, b, &env->fp_status); 311 PREC_END(); 312 } 313 314 void HELPER(fdiv)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) 315 { 316 res->d = floatx80_div(val1->d, val0->d, &env->fp_status); 317 } 318 319 void HELPER(fsdiv)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) 320 { 321 PREC_BEGIN(32); 322 res->d = floatx80_div(val1->d, val0->d, &env->fp_status); 323 PREC_END(); 324 } 325 326 void HELPER(fddiv)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) 327 { 328 PREC_BEGIN(64); 329 res->d = floatx80_div(val1->d, val0->d, &env->fp_status); 330 PREC_END(); 331 } 332 333 void HELPER(fsgldiv)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) 334 { 335 int rounding_mode = get_float_rounding_mode(&env->fp_status); 336 floatx80 a, b; 337 338 PREC_BEGIN(32); 339 set_float_rounding_mode(float_round_to_zero, &env->fp_status); 340 a = floatx80_round(val1->d, &env->fp_status); 341 b = floatx80_round(val0->d, &env->fp_status); 342 set_float_rounding_mode(rounding_mode, &env->fp_status); 343 res->d = floatx80_div(a, b, &env->fp_status); 344 PREC_END(); 345 } 346 347 static int float_comp_to_cc(int float_compare) 348 { 349 switch (float_compare) { 350 case float_relation_equal: 351 return FPSR_CC_Z; 352 case float_relation_less: 353 return FPSR_CC_N; 354 case float_relation_unordered: 355 return FPSR_CC_A; 356 case float_relation_greater: 357 return 0; 358 default: 359 g_assert_not_reached(); 360 } 361 } 362 363 void HELPER(fcmp)(CPUM68KState *env, FPReg *val0, FPReg *val1) 364 { 365 int float_compare; 366 367 float_compare = floatx80_compare(val1->d, val0->d, &env->fp_status); 368 env->fpsr = (env->fpsr & ~FPSR_CC_MASK) | float_comp_to_cc(float_compare); 369 } 370 371 void HELPER(ftst)(CPUM68KState *env, FPReg *val) 372 { 373 uint32_t cc = 0; 374 375 if (floatx80_is_neg(val->d)) { 376 cc |= FPSR_CC_N; 377 } 378 379 if (floatx80_is_any_nan(val->d)) { 380 cc |= FPSR_CC_A; 381 } else if (floatx80_is_infinity(val->d)) { 382 cc |= FPSR_CC_I; 383 } else if (floatx80_is_zero(val->d)) { 384 cc |= FPSR_CC_Z; 385 } 386 env->fpsr = (env->fpsr & ~FPSR_CC_MASK) | cc; 387 } 388 389 void HELPER(fconst)(CPUM68KState *env, FPReg *val, uint32_t offset) 390 { 391 val->d = fpu_rom[offset]; 392 } 393 394 typedef int (*float_access)(CPUM68KState *env, uint32_t addr, FPReg *fp, 395 uintptr_t ra); 396 397 static uint32_t fmovem_predec(CPUM68KState *env, uint32_t addr, uint32_t mask, 398 float_access access) 399 { 400 uintptr_t ra = GETPC(); 401 int i, size; 402 403 for (i = 7; i >= 0; i--, mask <<= 1) { 404 if (mask & 0x80) { 405 size = access(env, addr, &env->fregs[i], ra); 406 if ((mask & 0xff) != 0x80) { 407 addr -= size; 408 } 409 } 410 } 411 412 return addr; 413 } 414 415 static uint32_t fmovem_postinc(CPUM68KState *env, uint32_t addr, uint32_t mask, 416 float_access access) 417 { 418 uintptr_t ra = GETPC(); 419 int i, size; 420 421 for (i = 0; i < 8; i++, mask <<= 1) { 422 if (mask & 0x80) { 423 size = access(env, addr, &env->fregs[i], ra); 424 addr += size; 425 } 426 } 427 428 return addr; 429 } 430 431 static int cpu_ld_floatx80_ra(CPUM68KState *env, uint32_t addr, FPReg *fp, 432 uintptr_t ra) 433 { 434 uint32_t high; 435 uint64_t low; 436 437 high = cpu_ldl_data_ra(env, addr, ra); 438 low = cpu_ldq_data_ra(env, addr + 4, ra); 439 440 fp->l.upper = high >> 16; 441 fp->l.lower = low; 442 443 return 12; 444 } 445 446 static int cpu_st_floatx80_ra(CPUM68KState *env, uint32_t addr, FPReg *fp, 447 uintptr_t ra) 448 { 449 cpu_stl_data_ra(env, addr, fp->l.upper << 16, ra); 450 cpu_stq_data_ra(env, addr + 4, fp->l.lower, ra); 451 452 return 12; 453 } 454 455 static int cpu_ld_float64_ra(CPUM68KState *env, uint32_t addr, FPReg *fp, 456 uintptr_t ra) 457 { 458 uint64_t val; 459 460 val = cpu_ldq_data_ra(env, addr, ra); 461 fp->d = float64_to_floatx80(*(float64 *)&val, &env->fp_status); 462 463 return 8; 464 } 465 466 static int cpu_st_float64_ra(CPUM68KState *env, uint32_t addr, FPReg *fp, 467 uintptr_t ra) 468 { 469 float64 val; 470 471 val = floatx80_to_float64(fp->d, &env->fp_status); 472 cpu_stq_data_ra(env, addr, *(uint64_t *)&val, ra); 473 474 return 8; 475 } 476 477 uint32_t HELPER(fmovemx_st_predec)(CPUM68KState *env, uint32_t addr, 478 uint32_t mask) 479 { 480 return fmovem_predec(env, addr, mask, cpu_st_floatx80_ra); 481 } 482 483 uint32_t HELPER(fmovemx_st_postinc)(CPUM68KState *env, uint32_t addr, 484 uint32_t mask) 485 { 486 return fmovem_postinc(env, addr, mask, cpu_st_floatx80_ra); 487 } 488 489 uint32_t HELPER(fmovemx_ld_postinc)(CPUM68KState *env, uint32_t addr, 490 uint32_t mask) 491 { 492 return fmovem_postinc(env, addr, mask, cpu_ld_floatx80_ra); 493 } 494 495 uint32_t HELPER(fmovemd_st_predec)(CPUM68KState *env, uint32_t addr, 496 uint32_t mask) 497 { 498 return fmovem_predec(env, addr, mask, cpu_st_float64_ra); 499 } 500 501 uint32_t HELPER(fmovemd_st_postinc)(CPUM68KState *env, uint32_t addr, 502 uint32_t mask) 503 { 504 return fmovem_postinc(env, addr, mask, cpu_st_float64_ra); 505 } 506 507 uint32_t HELPER(fmovemd_ld_postinc)(CPUM68KState *env, uint32_t addr, 508 uint32_t mask) 509 { 510 return fmovem_postinc(env, addr, mask, cpu_ld_float64_ra); 511 } 512 513 static void make_quotient(CPUM68KState *env, floatx80 val) 514 { 515 int32_t quotient; 516 int sign; 517 518 if (floatx80_is_any_nan(val)) { 519 return; 520 } 521 522 quotient = floatx80_to_int32(val, &env->fp_status); 523 sign = quotient < 0; 524 if (sign) { 525 quotient = -quotient; 526 } 527 528 quotient = (sign << 7) | (quotient & 0x7f); 529 env->fpsr = (env->fpsr & ~FPSR_QT_MASK) | (quotient << FPSR_QT_SHIFT); 530 } 531 532 void HELPER(fmod)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) 533 { 534 res->d = floatx80_mod(val1->d, val0->d, &env->fp_status); 535 536 make_quotient(env, res->d); 537 } 538 539 void HELPER(frem)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) 540 { 541 res->d = floatx80_rem(val1->d, val0->d, &env->fp_status); 542 543 make_quotient(env, res->d); 544 } 545 546 void HELPER(fgetexp)(CPUM68KState *env, FPReg *res, FPReg *val) 547 { 548 res->d = floatx80_getexp(val->d, &env->fp_status); 549 } 550 551 void HELPER(fgetman)(CPUM68KState *env, FPReg *res, FPReg *val) 552 { 553 res->d = floatx80_getman(val->d, &env->fp_status); 554 } 555 556 void HELPER(fscale)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) 557 { 558 res->d = floatx80_scale(val1->d, val0->d, &env->fp_status); 559 } 560