1 /* 2 * M68K helper routines 3 * 4 * Copyright (c) 2007 CodeSourcery 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 18 */ 19 #include "qemu/osdep.h" 20 #include "cpu.h" 21 #include "exec/helper-proto.h" 22 #include "exec/exec-all.h" 23 #include "exec/cpu_ldst.h" 24 #include "exec/semihost.h" 25 26 #if defined(CONFIG_USER_ONLY) 27 28 void m68k_cpu_do_interrupt(CPUState *cs) 29 { 30 cs->exception_index = -1; 31 } 32 33 static inline void do_interrupt_m68k_hardirq(CPUM68KState *env) 34 { 35 } 36 37 #else 38 39 /* Try to fill the TLB and return an exception if error. If retaddr is 40 NULL, it means that the function was called in C code (i.e. not 41 from generated code or from helper.c) */ 42 void tlb_fill(CPUState *cs, target_ulong addr, int size, 43 MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) 44 { 45 int ret; 46 47 ret = m68k_cpu_handle_mmu_fault(cs, addr, size, access_type, mmu_idx); 48 if (unlikely(ret)) { 49 /* now we have a real cpu fault */ 50 cpu_loop_exit_restore(cs, retaddr); 51 } 52 } 53 54 static void cf_rte(CPUM68KState *env) 55 { 56 uint32_t sp; 57 uint32_t fmt; 58 59 sp = env->aregs[7]; 60 fmt = cpu_ldl_kernel(env, sp); 61 env->pc = cpu_ldl_kernel(env, sp + 4); 62 sp |= (fmt >> 28) & 3; 63 env->aregs[7] = sp + 8; 64 65 cpu_m68k_set_sr(env, fmt); 66 } 67 68 static void m68k_rte(CPUM68KState *env) 69 { 70 uint32_t sp; 71 uint16_t fmt; 72 uint16_t sr; 73 74 sp = env->aregs[7]; 75 throwaway: 76 sr = cpu_lduw_kernel(env, sp); 77 sp += 2; 78 env->pc = cpu_ldl_kernel(env, sp); 79 sp += 4; 80 if (m68k_feature(env, M68K_FEATURE_QUAD_MULDIV)) { 81 /* all except 68000 */ 82 fmt = cpu_lduw_kernel(env, sp); 83 sp += 2; 84 switch (fmt >> 12) { 85 case 0: 86 break; 87 case 1: 88 env->aregs[7] = sp; 89 cpu_m68k_set_sr(env, sr); 90 goto throwaway; 91 case 2: 92 case 3: 93 sp += 4; 94 break; 95 case 4: 96 sp += 8; 97 break; 98 case 7: 99 sp += 52; 100 break; 101 } 102 } 103 env->aregs[7] = sp; 104 cpu_m68k_set_sr(env, sr); 105 } 106 107 static const char *m68k_exception_name(int index) 108 { 109 switch (index) { 110 case EXCP_ACCESS: 111 return "Access Fault"; 112 case EXCP_ADDRESS: 113 return "Address Error"; 114 case EXCP_ILLEGAL: 115 return "Illegal Instruction"; 116 case EXCP_DIV0: 117 return "Divide by Zero"; 118 case EXCP_CHK: 119 return "CHK/CHK2"; 120 case EXCP_TRAPCC: 121 return "FTRAPcc, TRAPcc, TRAPV"; 122 case EXCP_PRIVILEGE: 123 return "Privilege Violation"; 124 case EXCP_TRACE: 125 return "Trace"; 126 case EXCP_LINEA: 127 return "A-Line"; 128 case EXCP_LINEF: 129 return "F-Line"; 130 case EXCP_DEBEGBP: /* 68020/030 only */ 131 return "Copro Protocol Violation"; 132 case EXCP_FORMAT: 133 return "Format Error"; 134 case EXCP_UNINITIALIZED: 135 return "Unitialized Interruot"; 136 case EXCP_SPURIOUS: 137 return "Spurious Interrupt"; 138 case EXCP_INT_LEVEL_1: 139 return "Level 1 Interrupt"; 140 case EXCP_INT_LEVEL_1 + 1: 141 return "Level 2 Interrupt"; 142 case EXCP_INT_LEVEL_1 + 2: 143 return "Level 3 Interrupt"; 144 case EXCP_INT_LEVEL_1 + 3: 145 return "Level 4 Interrupt"; 146 case EXCP_INT_LEVEL_1 + 4: 147 return "Level 5 Interrupt"; 148 case EXCP_INT_LEVEL_1 + 5: 149 return "Level 6 Interrupt"; 150 case EXCP_INT_LEVEL_1 + 6: 151 return "Level 7 Interrupt"; 152 case EXCP_TRAP0: 153 return "TRAP #0"; 154 case EXCP_TRAP0 + 1: 155 return "TRAP #1"; 156 case EXCP_TRAP0 + 2: 157 return "TRAP #2"; 158 case EXCP_TRAP0 + 3: 159 return "TRAP #3"; 160 case EXCP_TRAP0 + 4: 161 return "TRAP #4"; 162 case EXCP_TRAP0 + 5: 163 return "TRAP #5"; 164 case EXCP_TRAP0 + 6: 165 return "TRAP #6"; 166 case EXCP_TRAP0 + 7: 167 return "TRAP #7"; 168 case EXCP_TRAP0 + 8: 169 return "TRAP #8"; 170 case EXCP_TRAP0 + 9: 171 return "TRAP #9"; 172 case EXCP_TRAP0 + 10: 173 return "TRAP #10"; 174 case EXCP_TRAP0 + 11: 175 return "TRAP #11"; 176 case EXCP_TRAP0 + 12: 177 return "TRAP #12"; 178 case EXCP_TRAP0 + 13: 179 return "TRAP #13"; 180 case EXCP_TRAP0 + 14: 181 return "TRAP #14"; 182 case EXCP_TRAP0 + 15: 183 return "TRAP #15"; 184 case EXCP_FP_BSUN: 185 return "FP Branch/Set on unordered condition"; 186 case EXCP_FP_INEX: 187 return "FP Inexact Result"; 188 case EXCP_FP_DZ: 189 return "FP Divide by Zero"; 190 case EXCP_FP_UNFL: 191 return "FP Underflow"; 192 case EXCP_FP_OPERR: 193 return "FP Operand Error"; 194 case EXCP_FP_OVFL: 195 return "FP Overflow"; 196 case EXCP_FP_SNAN: 197 return "FP Signaling NAN"; 198 case EXCP_FP_UNIMP: 199 return "FP Unimplemented Data Type"; 200 case EXCP_MMU_CONF: /* 68030/68851 only */ 201 return "MMU Configuration Error"; 202 case EXCP_MMU_ILLEGAL: /* 68851 only */ 203 return "MMU Illegal Operation"; 204 case EXCP_MMU_ACCESS: /* 68851 only */ 205 return "MMU Access Level Violation"; 206 case 64 ... 255: 207 return "User Defined Vector"; 208 } 209 return "Unassigned"; 210 } 211 212 static void cf_interrupt_all(CPUM68KState *env, int is_hw) 213 { 214 CPUState *cs = CPU(m68k_env_get_cpu(env)); 215 uint32_t sp; 216 uint32_t sr; 217 uint32_t fmt; 218 uint32_t retaddr; 219 uint32_t vector; 220 221 fmt = 0; 222 retaddr = env->pc; 223 224 if (!is_hw) { 225 switch (cs->exception_index) { 226 case EXCP_RTE: 227 /* Return from an exception. */ 228 cf_rte(env); 229 return; 230 case EXCP_HALT_INSN: 231 if (semihosting_enabled() 232 && (env->sr & SR_S) != 0 233 && (env->pc & 3) == 0 234 && cpu_lduw_code(env, env->pc - 4) == 0x4e71 235 && cpu_ldl_code(env, env->pc) == 0x4e7bf000) { 236 env->pc += 4; 237 do_m68k_semihosting(env, env->dregs[0]); 238 return; 239 } 240 cs->halted = 1; 241 cs->exception_index = EXCP_HLT; 242 cpu_loop_exit(cs); 243 return; 244 } 245 if (cs->exception_index >= EXCP_TRAP0 246 && cs->exception_index <= EXCP_TRAP15) { 247 /* Move the PC after the trap instruction. */ 248 retaddr += 2; 249 } 250 } 251 252 vector = cs->exception_index << 2; 253 254 sr = env->sr | cpu_m68k_get_ccr(env); 255 if (qemu_loglevel_mask(CPU_LOG_INT)) { 256 static int count; 257 qemu_log("INT %6d: %s(%#x) pc=%08x sp=%08x sr=%04x\n", 258 ++count, m68k_exception_name(cs->exception_index), 259 vector, env->pc, env->aregs[7], sr); 260 } 261 262 fmt |= 0x40000000; 263 fmt |= vector << 16; 264 fmt |= sr; 265 266 env->sr |= SR_S; 267 if (is_hw) { 268 env->sr = (env->sr & ~SR_I) | (env->pending_level << SR_I_SHIFT); 269 env->sr &= ~SR_M; 270 } 271 m68k_switch_sp(env); 272 sp = env->aregs[7]; 273 fmt |= (sp & 3) << 28; 274 275 /* ??? This could cause MMU faults. */ 276 sp &= ~3; 277 sp -= 4; 278 cpu_stl_kernel(env, sp, retaddr); 279 sp -= 4; 280 cpu_stl_kernel(env, sp, fmt); 281 env->aregs[7] = sp; 282 /* Jump to vector. */ 283 env->pc = cpu_ldl_kernel(env, env->vbr + vector); 284 } 285 286 static inline void do_stack_frame(CPUM68KState *env, uint32_t *sp, 287 uint16_t format, uint16_t sr, 288 uint32_t addr, uint32_t retaddr) 289 { 290 CPUState *cs = CPU(m68k_env_get_cpu(env)); 291 switch (format) { 292 case 4: 293 *sp -= 4; 294 cpu_stl_kernel(env, *sp, env->pc); 295 *sp -= 4; 296 cpu_stl_kernel(env, *sp, addr); 297 break; 298 case 3: 299 case 2: 300 *sp -= 4; 301 cpu_stl_kernel(env, *sp, addr); 302 break; 303 } 304 *sp -= 2; 305 cpu_stw_kernel(env, *sp, (format << 12) + (cs->exception_index << 2)); 306 *sp -= 4; 307 cpu_stl_kernel(env, *sp, retaddr); 308 *sp -= 2; 309 cpu_stw_kernel(env, *sp, sr); 310 } 311 312 static void m68k_interrupt_all(CPUM68KState *env, int is_hw) 313 { 314 CPUState *cs = CPU(m68k_env_get_cpu(env)); 315 uint32_t sp; 316 uint32_t retaddr; 317 uint32_t vector; 318 uint16_t sr, oldsr; 319 320 retaddr = env->pc; 321 322 if (!is_hw) { 323 switch (cs->exception_index) { 324 case EXCP_RTE: 325 /* Return from an exception. */ 326 m68k_rte(env); 327 return; 328 case EXCP_TRAP0 ... EXCP_TRAP15: 329 /* Move the PC after the trap instruction. */ 330 retaddr += 2; 331 break; 332 } 333 } 334 335 vector = cs->exception_index << 2; 336 337 sr = env->sr | cpu_m68k_get_ccr(env); 338 if (qemu_loglevel_mask(CPU_LOG_INT)) { 339 static int count; 340 qemu_log("INT %6d: %s(%#x) pc=%08x sp=%08x sr=%04x\n", 341 ++count, m68k_exception_name(cs->exception_index), 342 vector, env->pc, env->aregs[7], sr); 343 } 344 345 /* 346 * MC68040UM/AD, chapter 9.3.10 347 */ 348 349 /* "the processor first make an internal copy" */ 350 oldsr = sr; 351 /* "set the mode to supervisor" */ 352 sr |= SR_S; 353 /* "suppress tracing" */ 354 sr &= ~SR_T; 355 /* "sets the processor interrupt mask" */ 356 if (is_hw) { 357 sr |= (env->sr & ~SR_I) | (env->pending_level << SR_I_SHIFT); 358 } 359 cpu_m68k_set_sr(env, sr); 360 sp = env->aregs[7]; 361 362 sp &= ~1; 363 if (cs->exception_index == EXCP_ADDRESS) { 364 do_stack_frame(env, &sp, 2, oldsr, 0, retaddr); 365 } else if (cs->exception_index == EXCP_ILLEGAL || 366 cs->exception_index == EXCP_DIV0 || 367 cs->exception_index == EXCP_CHK || 368 cs->exception_index == EXCP_TRAPCC || 369 cs->exception_index == EXCP_TRACE) { 370 /* FIXME: addr is not only env->pc */ 371 do_stack_frame(env, &sp, 2, oldsr, env->pc, retaddr); 372 } else if (is_hw && oldsr & SR_M && 373 cs->exception_index >= EXCP_SPURIOUS && 374 cs->exception_index <= EXCP_INT_LEVEL_7) { 375 do_stack_frame(env, &sp, 0, oldsr, 0, retaddr); 376 oldsr = sr; 377 env->aregs[7] = sp; 378 cpu_m68k_set_sr(env, sr &= ~SR_M); 379 sp = env->aregs[7] & ~1; 380 do_stack_frame(env, &sp, 1, oldsr, 0, retaddr); 381 } else { 382 do_stack_frame(env, &sp, 0, oldsr, 0, retaddr); 383 } 384 385 env->aregs[7] = sp; 386 /* Jump to vector. */ 387 env->pc = cpu_ldl_kernel(env, env->vbr + vector); 388 } 389 390 static void do_interrupt_all(CPUM68KState *env, int is_hw) 391 { 392 if (m68k_feature(env, M68K_FEATURE_M68000)) { 393 m68k_interrupt_all(env, is_hw); 394 return; 395 } 396 cf_interrupt_all(env, is_hw); 397 } 398 399 void m68k_cpu_do_interrupt(CPUState *cs) 400 { 401 M68kCPU *cpu = M68K_CPU(cs); 402 CPUM68KState *env = &cpu->env; 403 404 do_interrupt_all(env, 0); 405 } 406 407 static inline void do_interrupt_m68k_hardirq(CPUM68KState *env) 408 { 409 do_interrupt_all(env, 1); 410 } 411 #endif 412 413 bool m68k_cpu_exec_interrupt(CPUState *cs, int interrupt_request) 414 { 415 M68kCPU *cpu = M68K_CPU(cs); 416 CPUM68KState *env = &cpu->env; 417 418 if (interrupt_request & CPU_INTERRUPT_HARD 419 && ((env->sr & SR_I) >> SR_I_SHIFT) < env->pending_level) { 420 /* Real hardware gets the interrupt vector via an IACK cycle 421 at this point. Current emulated hardware doesn't rely on 422 this, so we provide/save the vector when the interrupt is 423 first signalled. */ 424 cs->exception_index = env->pending_vector; 425 do_interrupt_m68k_hardirq(env); 426 return true; 427 } 428 return false; 429 } 430 431 static void raise_exception_ra(CPUM68KState *env, int tt, uintptr_t raddr) 432 { 433 CPUState *cs = CPU(m68k_env_get_cpu(env)); 434 435 cs->exception_index = tt; 436 cpu_loop_exit_restore(cs, raddr); 437 } 438 439 static void raise_exception(CPUM68KState *env, int tt) 440 { 441 raise_exception_ra(env, tt, 0); 442 } 443 444 void HELPER(raise_exception)(CPUM68KState *env, uint32_t tt) 445 { 446 raise_exception(env, tt); 447 } 448 449 void HELPER(divuw)(CPUM68KState *env, int destr, uint32_t den) 450 { 451 uint32_t num = env->dregs[destr]; 452 uint32_t quot, rem; 453 454 if (den == 0) { 455 raise_exception_ra(env, EXCP_DIV0, GETPC()); 456 } 457 quot = num / den; 458 rem = num % den; 459 460 env->cc_c = 0; /* always cleared, even if overflow */ 461 if (quot > 0xffff) { 462 env->cc_v = -1; 463 /* real 68040 keeps N and unset Z on overflow, 464 * whereas documentation says "undefined" 465 */ 466 env->cc_z = 1; 467 return; 468 } 469 env->dregs[destr] = deposit32(quot, 16, 16, rem); 470 env->cc_z = (int16_t)quot; 471 env->cc_n = (int16_t)quot; 472 env->cc_v = 0; 473 } 474 475 void HELPER(divsw)(CPUM68KState *env, int destr, int32_t den) 476 { 477 int32_t num = env->dregs[destr]; 478 uint32_t quot, rem; 479 480 if (den == 0) { 481 raise_exception_ra(env, EXCP_DIV0, GETPC()); 482 } 483 quot = num / den; 484 rem = num % den; 485 486 env->cc_c = 0; /* always cleared, even if overflow */ 487 if (quot != (int16_t)quot) { 488 env->cc_v = -1; 489 /* nothing else is modified */ 490 /* real 68040 keeps N and unset Z on overflow, 491 * whereas documentation says "undefined" 492 */ 493 env->cc_z = 1; 494 return; 495 } 496 env->dregs[destr] = deposit32(quot, 16, 16, rem); 497 env->cc_z = (int16_t)quot; 498 env->cc_n = (int16_t)quot; 499 env->cc_v = 0; 500 } 501 502 void HELPER(divul)(CPUM68KState *env, int numr, int regr, uint32_t den) 503 { 504 uint32_t num = env->dregs[numr]; 505 uint32_t quot, rem; 506 507 if (den == 0) { 508 raise_exception_ra(env, EXCP_DIV0, GETPC()); 509 } 510 quot = num / den; 511 rem = num % den; 512 513 env->cc_c = 0; 514 env->cc_z = quot; 515 env->cc_n = quot; 516 env->cc_v = 0; 517 518 if (m68k_feature(env, M68K_FEATURE_CF_ISA_A)) { 519 if (numr == regr) { 520 env->dregs[numr] = quot; 521 } else { 522 env->dregs[regr] = rem; 523 } 524 } else { 525 env->dregs[regr] = rem; 526 env->dregs[numr] = quot; 527 } 528 } 529 530 void HELPER(divsl)(CPUM68KState *env, int numr, int regr, int32_t den) 531 { 532 int32_t num = env->dregs[numr]; 533 int32_t quot, rem; 534 535 if (den == 0) { 536 raise_exception_ra(env, EXCP_DIV0, GETPC()); 537 } 538 quot = num / den; 539 rem = num % den; 540 541 env->cc_c = 0; 542 env->cc_z = quot; 543 env->cc_n = quot; 544 env->cc_v = 0; 545 546 if (m68k_feature(env, M68K_FEATURE_CF_ISA_A)) { 547 if (numr == regr) { 548 env->dregs[numr] = quot; 549 } else { 550 env->dregs[regr] = rem; 551 } 552 } else { 553 env->dregs[regr] = rem; 554 env->dregs[numr] = quot; 555 } 556 } 557 558 void HELPER(divull)(CPUM68KState *env, int numr, int regr, uint32_t den) 559 { 560 uint64_t num = deposit64(env->dregs[numr], 32, 32, env->dregs[regr]); 561 uint64_t quot; 562 uint32_t rem; 563 564 if (den == 0) { 565 raise_exception_ra(env, EXCP_DIV0, GETPC()); 566 } 567 quot = num / den; 568 rem = num % den; 569 570 env->cc_c = 0; /* always cleared, even if overflow */ 571 if (quot > 0xffffffffULL) { 572 env->cc_v = -1; 573 /* real 68040 keeps N and unset Z on overflow, 574 * whereas documentation says "undefined" 575 */ 576 env->cc_z = 1; 577 return; 578 } 579 env->cc_z = quot; 580 env->cc_n = quot; 581 env->cc_v = 0; 582 583 /* 584 * If Dq and Dr are the same, the quotient is returned. 585 * therefore we set Dq last. 586 */ 587 588 env->dregs[regr] = rem; 589 env->dregs[numr] = quot; 590 } 591 592 void HELPER(divsll)(CPUM68KState *env, int numr, int regr, int32_t den) 593 { 594 int64_t num = deposit64(env->dregs[numr], 32, 32, env->dregs[regr]); 595 int64_t quot; 596 int32_t rem; 597 598 if (den == 0) { 599 raise_exception_ra(env, EXCP_DIV0, GETPC()); 600 } 601 quot = num / den; 602 rem = num % den; 603 604 env->cc_c = 0; /* always cleared, even if overflow */ 605 if (quot != (int32_t)quot) { 606 env->cc_v = -1; 607 /* real 68040 keeps N and unset Z on overflow, 608 * whereas documentation says "undefined" 609 */ 610 env->cc_z = 1; 611 return; 612 } 613 env->cc_z = quot; 614 env->cc_n = quot; 615 env->cc_v = 0; 616 617 /* 618 * If Dq and Dr are the same, the quotient is returned. 619 * therefore we set Dq last. 620 */ 621 622 env->dregs[regr] = rem; 623 env->dregs[numr] = quot; 624 } 625 626 /* We're executing in a serial context -- no need to be atomic. */ 627 void HELPER(cas2w)(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2) 628 { 629 uint32_t Dc1 = extract32(regs, 9, 3); 630 uint32_t Dc2 = extract32(regs, 6, 3); 631 uint32_t Du1 = extract32(regs, 3, 3); 632 uint32_t Du2 = extract32(regs, 0, 3); 633 int16_t c1 = env->dregs[Dc1]; 634 int16_t c2 = env->dregs[Dc2]; 635 int16_t u1 = env->dregs[Du1]; 636 int16_t u2 = env->dregs[Du2]; 637 int16_t l1, l2; 638 uintptr_t ra = GETPC(); 639 640 l1 = cpu_lduw_data_ra(env, a1, ra); 641 l2 = cpu_lduw_data_ra(env, a2, ra); 642 if (l1 == c1 && l2 == c2) { 643 cpu_stw_data_ra(env, a1, u1, ra); 644 cpu_stw_data_ra(env, a2, u2, ra); 645 } 646 647 if (c1 != l1) { 648 env->cc_n = l1; 649 env->cc_v = c1; 650 } else { 651 env->cc_n = l2; 652 env->cc_v = c2; 653 } 654 env->cc_op = CC_OP_CMPW; 655 env->dregs[Dc1] = deposit32(env->dregs[Dc1], 0, 16, l1); 656 env->dregs[Dc2] = deposit32(env->dregs[Dc2], 0, 16, l2); 657 } 658 659 static void do_cas2l(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2, 660 bool parallel) 661 { 662 uint32_t Dc1 = extract32(regs, 9, 3); 663 uint32_t Dc2 = extract32(regs, 6, 3); 664 uint32_t Du1 = extract32(regs, 3, 3); 665 uint32_t Du2 = extract32(regs, 0, 3); 666 uint32_t c1 = env->dregs[Dc1]; 667 uint32_t c2 = env->dregs[Dc2]; 668 uint32_t u1 = env->dregs[Du1]; 669 uint32_t u2 = env->dregs[Du2]; 670 uint32_t l1, l2; 671 uintptr_t ra = GETPC(); 672 #if defined(CONFIG_ATOMIC64) && !defined(CONFIG_USER_ONLY) 673 int mmu_idx = cpu_mmu_index(env, 0); 674 TCGMemOpIdx oi; 675 #endif 676 677 if (parallel) { 678 /* We're executing in a parallel context -- must be atomic. */ 679 #ifdef CONFIG_ATOMIC64 680 uint64_t c, u, l; 681 if ((a1 & 7) == 0 && a2 == a1 + 4) { 682 c = deposit64(c2, 32, 32, c1); 683 u = deposit64(u2, 32, 32, u1); 684 #ifdef CONFIG_USER_ONLY 685 l = helper_atomic_cmpxchgq_be(env, a1, c, u); 686 #else 687 oi = make_memop_idx(MO_BEQ, mmu_idx); 688 l = helper_atomic_cmpxchgq_be_mmu(env, a1, c, u, oi, ra); 689 #endif 690 l1 = l >> 32; 691 l2 = l; 692 } else if ((a2 & 7) == 0 && a1 == a2 + 4) { 693 c = deposit64(c1, 32, 32, c2); 694 u = deposit64(u1, 32, 32, u2); 695 #ifdef CONFIG_USER_ONLY 696 l = helper_atomic_cmpxchgq_be(env, a2, c, u); 697 #else 698 oi = make_memop_idx(MO_BEQ, mmu_idx); 699 l = helper_atomic_cmpxchgq_be_mmu(env, a2, c, u, oi, ra); 700 #endif 701 l2 = l >> 32; 702 l1 = l; 703 } else 704 #endif 705 { 706 /* Tell the main loop we need to serialize this insn. */ 707 cpu_loop_exit_atomic(ENV_GET_CPU(env), ra); 708 } 709 } else { 710 /* We're executing in a serial context -- no need to be atomic. */ 711 l1 = cpu_ldl_data_ra(env, a1, ra); 712 l2 = cpu_ldl_data_ra(env, a2, ra); 713 if (l1 == c1 && l2 == c2) { 714 cpu_stl_data_ra(env, a1, u1, ra); 715 cpu_stl_data_ra(env, a2, u2, ra); 716 } 717 } 718 719 if (c1 != l1) { 720 env->cc_n = l1; 721 env->cc_v = c1; 722 } else { 723 env->cc_n = l2; 724 env->cc_v = c2; 725 } 726 env->cc_op = CC_OP_CMPL; 727 env->dregs[Dc1] = l1; 728 env->dregs[Dc2] = l2; 729 } 730 731 void HELPER(cas2l)(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2) 732 { 733 do_cas2l(env, regs, a1, a2, false); 734 } 735 736 void HELPER(cas2l_parallel)(CPUM68KState *env, uint32_t regs, uint32_t a1, 737 uint32_t a2) 738 { 739 do_cas2l(env, regs, a1, a2, true); 740 } 741 742 struct bf_data { 743 uint32_t addr; 744 uint32_t bofs; 745 uint32_t blen; 746 uint32_t len; 747 }; 748 749 static struct bf_data bf_prep(uint32_t addr, int32_t ofs, uint32_t len) 750 { 751 int bofs, blen; 752 753 /* Bound length; map 0 to 32. */ 754 len = ((len - 1) & 31) + 1; 755 756 /* Note that ofs is signed. */ 757 addr += ofs / 8; 758 bofs = ofs % 8; 759 if (bofs < 0) { 760 bofs += 8; 761 addr -= 1; 762 } 763 764 /* Compute the number of bytes required (minus one) to 765 satisfy the bitfield. */ 766 blen = (bofs + len - 1) / 8; 767 768 /* Canonicalize the bit offset for data loaded into a 64-bit big-endian 769 word. For the cases where BLEN is not a power of 2, adjust ADDR so 770 that we can use the next power of two sized load without crossing a 771 page boundary, unless the field itself crosses the boundary. */ 772 switch (blen) { 773 case 0: 774 bofs += 56; 775 break; 776 case 1: 777 bofs += 48; 778 break; 779 case 2: 780 if (addr & 1) { 781 bofs += 8; 782 addr -= 1; 783 } 784 /* fallthru */ 785 case 3: 786 bofs += 32; 787 break; 788 case 4: 789 if (addr & 3) { 790 bofs += 8 * (addr & 3); 791 addr &= -4; 792 } 793 break; 794 default: 795 g_assert_not_reached(); 796 } 797 798 return (struct bf_data){ 799 .addr = addr, 800 .bofs = bofs, 801 .blen = blen, 802 .len = len, 803 }; 804 } 805 806 static uint64_t bf_load(CPUM68KState *env, uint32_t addr, int blen, 807 uintptr_t ra) 808 { 809 switch (blen) { 810 case 0: 811 return cpu_ldub_data_ra(env, addr, ra); 812 case 1: 813 return cpu_lduw_data_ra(env, addr, ra); 814 case 2: 815 case 3: 816 return cpu_ldl_data_ra(env, addr, ra); 817 case 4: 818 return cpu_ldq_data_ra(env, addr, ra); 819 default: 820 g_assert_not_reached(); 821 } 822 } 823 824 static void bf_store(CPUM68KState *env, uint32_t addr, int blen, 825 uint64_t data, uintptr_t ra) 826 { 827 switch (blen) { 828 case 0: 829 cpu_stb_data_ra(env, addr, data, ra); 830 break; 831 case 1: 832 cpu_stw_data_ra(env, addr, data, ra); 833 break; 834 case 2: 835 case 3: 836 cpu_stl_data_ra(env, addr, data, ra); 837 break; 838 case 4: 839 cpu_stq_data_ra(env, addr, data, ra); 840 break; 841 default: 842 g_assert_not_reached(); 843 } 844 } 845 846 uint32_t HELPER(bfexts_mem)(CPUM68KState *env, uint32_t addr, 847 int32_t ofs, uint32_t len) 848 { 849 uintptr_t ra = GETPC(); 850 struct bf_data d = bf_prep(addr, ofs, len); 851 uint64_t data = bf_load(env, d.addr, d.blen, ra); 852 853 return (int64_t)(data << d.bofs) >> (64 - d.len); 854 } 855 856 uint64_t HELPER(bfextu_mem)(CPUM68KState *env, uint32_t addr, 857 int32_t ofs, uint32_t len) 858 { 859 uintptr_t ra = GETPC(); 860 struct bf_data d = bf_prep(addr, ofs, len); 861 uint64_t data = bf_load(env, d.addr, d.blen, ra); 862 863 /* Put CC_N at the top of the high word; put the zero-extended value 864 at the bottom of the low word. */ 865 data <<= d.bofs; 866 data >>= 64 - d.len; 867 data |= data << (64 - d.len); 868 869 return data; 870 } 871 872 uint32_t HELPER(bfins_mem)(CPUM68KState *env, uint32_t addr, uint32_t val, 873 int32_t ofs, uint32_t len) 874 { 875 uintptr_t ra = GETPC(); 876 struct bf_data d = bf_prep(addr, ofs, len); 877 uint64_t data = bf_load(env, d.addr, d.blen, ra); 878 uint64_t mask = -1ull << (64 - d.len) >> d.bofs; 879 880 data = (data & ~mask) | (((uint64_t)val << (64 - d.len)) >> d.bofs); 881 882 bf_store(env, d.addr, d.blen, data, ra); 883 884 /* The field at the top of the word is also CC_N for CC_OP_LOGIC. */ 885 return val << (32 - d.len); 886 } 887 888 uint32_t HELPER(bfchg_mem)(CPUM68KState *env, uint32_t addr, 889 int32_t ofs, uint32_t len) 890 { 891 uintptr_t ra = GETPC(); 892 struct bf_data d = bf_prep(addr, ofs, len); 893 uint64_t data = bf_load(env, d.addr, d.blen, ra); 894 uint64_t mask = -1ull << (64 - d.len) >> d.bofs; 895 896 bf_store(env, d.addr, d.blen, data ^ mask, ra); 897 898 return ((data & mask) << d.bofs) >> 32; 899 } 900 901 uint32_t HELPER(bfclr_mem)(CPUM68KState *env, uint32_t addr, 902 int32_t ofs, uint32_t len) 903 { 904 uintptr_t ra = GETPC(); 905 struct bf_data d = bf_prep(addr, ofs, len); 906 uint64_t data = bf_load(env, d.addr, d.blen, ra); 907 uint64_t mask = -1ull << (64 - d.len) >> d.bofs; 908 909 bf_store(env, d.addr, d.blen, data & ~mask, ra); 910 911 return ((data & mask) << d.bofs) >> 32; 912 } 913 914 uint32_t HELPER(bfset_mem)(CPUM68KState *env, uint32_t addr, 915 int32_t ofs, uint32_t len) 916 { 917 uintptr_t ra = GETPC(); 918 struct bf_data d = bf_prep(addr, ofs, len); 919 uint64_t data = bf_load(env, d.addr, d.blen, ra); 920 uint64_t mask = -1ull << (64 - d.len) >> d.bofs; 921 922 bf_store(env, d.addr, d.blen, data | mask, ra); 923 924 return ((data & mask) << d.bofs) >> 32; 925 } 926 927 uint32_t HELPER(bfffo_reg)(uint32_t n, uint32_t ofs, uint32_t len) 928 { 929 return (n ? clz32(n) : len) + ofs; 930 } 931 932 uint64_t HELPER(bfffo_mem)(CPUM68KState *env, uint32_t addr, 933 int32_t ofs, uint32_t len) 934 { 935 uintptr_t ra = GETPC(); 936 struct bf_data d = bf_prep(addr, ofs, len); 937 uint64_t data = bf_load(env, d.addr, d.blen, ra); 938 uint64_t mask = -1ull << (64 - d.len) >> d.bofs; 939 uint64_t n = (data & mask) << d.bofs; 940 uint32_t ffo = helper_bfffo_reg(n >> 32, ofs, d.len); 941 942 /* Return FFO in the low word and N in the high word. 943 Note that because of MASK and the shift, the low word 944 is already zero. */ 945 return n | ffo; 946 } 947 948 void HELPER(chk)(CPUM68KState *env, int32_t val, int32_t ub) 949 { 950 /* From the specs: 951 * X: Not affected, C,V,Z: Undefined, 952 * N: Set if val < 0; cleared if val > ub, undefined otherwise 953 * We implement here values found from a real MC68040: 954 * X,V,Z: Not affected 955 * N: Set if val < 0; cleared if val >= 0 956 * C: if 0 <= ub: set if val < 0 or val > ub, cleared otherwise 957 * if 0 > ub: set if val > ub and val < 0, cleared otherwise 958 */ 959 env->cc_n = val; 960 env->cc_c = 0 <= ub ? val < 0 || val > ub : val > ub && val < 0; 961 962 if (val < 0 || val > ub) { 963 CPUState *cs = CPU(m68k_env_get_cpu(env)); 964 965 /* Recover PC and CC_OP for the beginning of the insn. */ 966 cpu_restore_state(cs, GETPC()); 967 968 /* flags have been modified by gen_flush_flags() */ 969 env->cc_op = CC_OP_FLAGS; 970 /* Adjust PC to end of the insn. */ 971 env->pc += 2; 972 973 cs->exception_index = EXCP_CHK; 974 cpu_loop_exit(cs); 975 } 976 } 977 978 void HELPER(chk2)(CPUM68KState *env, int32_t val, int32_t lb, int32_t ub) 979 { 980 /* From the specs: 981 * X: Not affected, N,V: Undefined, 982 * Z: Set if val is equal to lb or ub 983 * C: Set if val < lb or val > ub, cleared otherwise 984 * We implement here values found from a real MC68040: 985 * X,N,V: Not affected 986 * Z: Set if val is equal to lb or ub 987 * C: if lb <= ub: set if val < lb or val > ub, cleared otherwise 988 * if lb > ub: set if val > ub and val < lb, cleared otherwise 989 */ 990 env->cc_z = val != lb && val != ub; 991 env->cc_c = lb <= ub ? val < lb || val > ub : val > ub && val < lb; 992 993 if (env->cc_c) { 994 CPUState *cs = CPU(m68k_env_get_cpu(env)); 995 996 /* Recover PC and CC_OP for the beginning of the insn. */ 997 cpu_restore_state(cs, GETPC()); 998 999 /* flags have been modified by gen_flush_flags() */ 1000 env->cc_op = CC_OP_FLAGS; 1001 /* Adjust PC to end of the insn. */ 1002 env->pc += 4; 1003 1004 cs->exception_index = EXCP_CHK; 1005 cpu_loop_exit(cs); 1006 } 1007 } 1008