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