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 /* Real hardware gets the interrupt vector via an IACK cycle 498 at this point. Current emulated hardware doesn't rely on 499 this, so we provide/save the vector when the interrupt is 500 first signalled. */ 501 cs->exception_index = env->pending_vector; 502 do_interrupt_m68k_hardirq(env); 503 return true; 504 } 505 return false; 506 } 507 508 static void raise_exception_ra(CPUM68KState *env, int tt, uintptr_t raddr) 509 { 510 CPUState *cs = env_cpu(env); 511 512 cs->exception_index = tt; 513 cpu_loop_exit_restore(cs, raddr); 514 } 515 516 static void raise_exception(CPUM68KState *env, int tt) 517 { 518 raise_exception_ra(env, tt, 0); 519 } 520 521 void HELPER(raise_exception)(CPUM68KState *env, uint32_t tt) 522 { 523 raise_exception(env, tt); 524 } 525 526 void HELPER(divuw)(CPUM68KState *env, int destr, uint32_t den) 527 { 528 uint32_t num = env->dregs[destr]; 529 uint32_t quot, rem; 530 531 if (den == 0) { 532 raise_exception_ra(env, EXCP_DIV0, GETPC()); 533 } 534 quot = num / den; 535 rem = num % den; 536 537 env->cc_c = 0; /* always cleared, even if overflow */ 538 if (quot > 0xffff) { 539 env->cc_v = -1; 540 /* real 68040 keeps N and unset Z on overflow, 541 * whereas documentation says "undefined" 542 */ 543 env->cc_z = 1; 544 return; 545 } 546 env->dregs[destr] = deposit32(quot, 16, 16, rem); 547 env->cc_z = (int16_t)quot; 548 env->cc_n = (int16_t)quot; 549 env->cc_v = 0; 550 } 551 552 void HELPER(divsw)(CPUM68KState *env, int destr, int32_t den) 553 { 554 int32_t num = env->dregs[destr]; 555 uint32_t quot, rem; 556 557 if (den == 0) { 558 raise_exception_ra(env, EXCP_DIV0, GETPC()); 559 } 560 quot = num / den; 561 rem = num % den; 562 563 env->cc_c = 0; /* always cleared, even if overflow */ 564 if (quot != (int16_t)quot) { 565 env->cc_v = -1; 566 /* nothing else is modified */ 567 /* real 68040 keeps N and unset Z on overflow, 568 * whereas documentation says "undefined" 569 */ 570 env->cc_z = 1; 571 return; 572 } 573 env->dregs[destr] = deposit32(quot, 16, 16, rem); 574 env->cc_z = (int16_t)quot; 575 env->cc_n = (int16_t)quot; 576 env->cc_v = 0; 577 } 578 579 void HELPER(divul)(CPUM68KState *env, int numr, int regr, uint32_t den) 580 { 581 uint32_t num = env->dregs[numr]; 582 uint32_t quot, rem; 583 584 if (den == 0) { 585 raise_exception_ra(env, EXCP_DIV0, GETPC()); 586 } 587 quot = num / den; 588 rem = num % den; 589 590 env->cc_c = 0; 591 env->cc_z = quot; 592 env->cc_n = quot; 593 env->cc_v = 0; 594 595 if (m68k_feature(env, M68K_FEATURE_CF_ISA_A)) { 596 if (numr == regr) { 597 env->dregs[numr] = quot; 598 } else { 599 env->dregs[regr] = rem; 600 } 601 } else { 602 env->dregs[regr] = rem; 603 env->dregs[numr] = quot; 604 } 605 } 606 607 void HELPER(divsl)(CPUM68KState *env, int numr, int regr, int32_t den) 608 { 609 int32_t num = env->dregs[numr]; 610 int32_t quot, rem; 611 612 if (den == 0) { 613 raise_exception_ra(env, EXCP_DIV0, GETPC()); 614 } 615 quot = num / den; 616 rem = num % den; 617 618 env->cc_c = 0; 619 env->cc_z = quot; 620 env->cc_n = quot; 621 env->cc_v = 0; 622 623 if (m68k_feature(env, M68K_FEATURE_CF_ISA_A)) { 624 if (numr == regr) { 625 env->dregs[numr] = quot; 626 } else { 627 env->dregs[regr] = rem; 628 } 629 } else { 630 env->dregs[regr] = rem; 631 env->dregs[numr] = quot; 632 } 633 } 634 635 void HELPER(divull)(CPUM68KState *env, int numr, int regr, uint32_t den) 636 { 637 uint64_t num = deposit64(env->dregs[numr], 32, 32, env->dregs[regr]); 638 uint64_t quot; 639 uint32_t rem; 640 641 if (den == 0) { 642 raise_exception_ra(env, EXCP_DIV0, GETPC()); 643 } 644 quot = num / den; 645 rem = num % den; 646 647 env->cc_c = 0; /* always cleared, even if overflow */ 648 if (quot > 0xffffffffULL) { 649 env->cc_v = -1; 650 /* real 68040 keeps N and unset Z on overflow, 651 * whereas documentation says "undefined" 652 */ 653 env->cc_z = 1; 654 return; 655 } 656 env->cc_z = quot; 657 env->cc_n = quot; 658 env->cc_v = 0; 659 660 /* 661 * If Dq and Dr are the same, the quotient is returned. 662 * therefore we set Dq last. 663 */ 664 665 env->dregs[regr] = rem; 666 env->dregs[numr] = quot; 667 } 668 669 void HELPER(divsll)(CPUM68KState *env, int numr, int regr, int32_t den) 670 { 671 int64_t num = deposit64(env->dregs[numr], 32, 32, env->dregs[regr]); 672 int64_t quot; 673 int32_t rem; 674 675 if (den == 0) { 676 raise_exception_ra(env, EXCP_DIV0, GETPC()); 677 } 678 quot = num / den; 679 rem = num % den; 680 681 env->cc_c = 0; /* always cleared, even if overflow */ 682 if (quot != (int32_t)quot) { 683 env->cc_v = -1; 684 /* real 68040 keeps N and unset Z on overflow, 685 * whereas documentation says "undefined" 686 */ 687 env->cc_z = 1; 688 return; 689 } 690 env->cc_z = quot; 691 env->cc_n = quot; 692 env->cc_v = 0; 693 694 /* 695 * If Dq and Dr are the same, the quotient is returned. 696 * therefore we set Dq last. 697 */ 698 699 env->dregs[regr] = rem; 700 env->dregs[numr] = quot; 701 } 702 703 /* We're executing in a serial context -- no need to be atomic. */ 704 void HELPER(cas2w)(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2) 705 { 706 uint32_t Dc1 = extract32(regs, 9, 3); 707 uint32_t Dc2 = extract32(regs, 6, 3); 708 uint32_t Du1 = extract32(regs, 3, 3); 709 uint32_t Du2 = extract32(regs, 0, 3); 710 int16_t c1 = env->dregs[Dc1]; 711 int16_t c2 = env->dregs[Dc2]; 712 int16_t u1 = env->dregs[Du1]; 713 int16_t u2 = env->dregs[Du2]; 714 int16_t l1, l2; 715 uintptr_t ra = GETPC(); 716 717 l1 = cpu_lduw_data_ra(env, a1, ra); 718 l2 = cpu_lduw_data_ra(env, a2, ra); 719 if (l1 == c1 && l2 == c2) { 720 cpu_stw_data_ra(env, a1, u1, ra); 721 cpu_stw_data_ra(env, a2, u2, ra); 722 } 723 724 if (c1 != l1) { 725 env->cc_n = l1; 726 env->cc_v = c1; 727 } else { 728 env->cc_n = l2; 729 env->cc_v = c2; 730 } 731 env->cc_op = CC_OP_CMPW; 732 env->dregs[Dc1] = deposit32(env->dregs[Dc1], 0, 16, l1); 733 env->dregs[Dc2] = deposit32(env->dregs[Dc2], 0, 16, l2); 734 } 735 736 static void do_cas2l(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2, 737 bool parallel) 738 { 739 uint32_t Dc1 = extract32(regs, 9, 3); 740 uint32_t Dc2 = extract32(regs, 6, 3); 741 uint32_t Du1 = extract32(regs, 3, 3); 742 uint32_t Du2 = extract32(regs, 0, 3); 743 uint32_t c1 = env->dregs[Dc1]; 744 uint32_t c2 = env->dregs[Dc2]; 745 uint32_t u1 = env->dregs[Du1]; 746 uint32_t u2 = env->dregs[Du2]; 747 uint32_t l1, l2; 748 uintptr_t ra = GETPC(); 749 #if defined(CONFIG_ATOMIC64) && !defined(CONFIG_USER_ONLY) 750 int mmu_idx = cpu_mmu_index(env, 0); 751 TCGMemOpIdx oi; 752 #endif 753 754 if (parallel) { 755 /* We're executing in a parallel context -- must be atomic. */ 756 #ifdef CONFIG_ATOMIC64 757 uint64_t c, u, l; 758 if ((a1 & 7) == 0 && a2 == a1 + 4) { 759 c = deposit64(c2, 32, 32, c1); 760 u = deposit64(u2, 32, 32, u1); 761 #ifdef CONFIG_USER_ONLY 762 l = helper_atomic_cmpxchgq_be(env, a1, c, u); 763 #else 764 oi = make_memop_idx(MO_BEQ, mmu_idx); 765 l = helper_atomic_cmpxchgq_be_mmu(env, a1, c, u, oi, ra); 766 #endif 767 l1 = l >> 32; 768 l2 = l; 769 } else if ((a2 & 7) == 0 && a1 == a2 + 4) { 770 c = deposit64(c1, 32, 32, c2); 771 u = deposit64(u1, 32, 32, u2); 772 #ifdef CONFIG_USER_ONLY 773 l = helper_atomic_cmpxchgq_be(env, a2, c, u); 774 #else 775 oi = make_memop_idx(MO_BEQ, mmu_idx); 776 l = helper_atomic_cmpxchgq_be_mmu(env, a2, c, u, oi, ra); 777 #endif 778 l2 = l >> 32; 779 l1 = l; 780 } else 781 #endif 782 { 783 /* Tell the main loop we need to serialize this insn. */ 784 cpu_loop_exit_atomic(env_cpu(env), ra); 785 } 786 } else { 787 /* We're executing in a serial context -- no need to be atomic. */ 788 l1 = cpu_ldl_data_ra(env, a1, ra); 789 l2 = cpu_ldl_data_ra(env, a2, ra); 790 if (l1 == c1 && l2 == c2) { 791 cpu_stl_data_ra(env, a1, u1, ra); 792 cpu_stl_data_ra(env, a2, u2, ra); 793 } 794 } 795 796 if (c1 != l1) { 797 env->cc_n = l1; 798 env->cc_v = c1; 799 } else { 800 env->cc_n = l2; 801 env->cc_v = c2; 802 } 803 env->cc_op = CC_OP_CMPL; 804 env->dregs[Dc1] = l1; 805 env->dregs[Dc2] = l2; 806 } 807 808 void HELPER(cas2l)(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2) 809 { 810 do_cas2l(env, regs, a1, a2, false); 811 } 812 813 void HELPER(cas2l_parallel)(CPUM68KState *env, uint32_t regs, uint32_t a1, 814 uint32_t a2) 815 { 816 do_cas2l(env, regs, a1, a2, true); 817 } 818 819 struct bf_data { 820 uint32_t addr; 821 uint32_t bofs; 822 uint32_t blen; 823 uint32_t len; 824 }; 825 826 static struct bf_data bf_prep(uint32_t addr, int32_t ofs, uint32_t len) 827 { 828 int bofs, blen; 829 830 /* Bound length; map 0 to 32. */ 831 len = ((len - 1) & 31) + 1; 832 833 /* Note that ofs is signed. */ 834 addr += ofs / 8; 835 bofs = ofs % 8; 836 if (bofs < 0) { 837 bofs += 8; 838 addr -= 1; 839 } 840 841 /* Compute the number of bytes required (minus one) to 842 satisfy the bitfield. */ 843 blen = (bofs + len - 1) / 8; 844 845 /* Canonicalize the bit offset for data loaded into a 64-bit big-endian 846 word. For the cases where BLEN is not a power of 2, adjust ADDR so 847 that we can use the next power of two sized load without crossing a 848 page boundary, unless the field itself crosses the boundary. */ 849 switch (blen) { 850 case 0: 851 bofs += 56; 852 break; 853 case 1: 854 bofs += 48; 855 break; 856 case 2: 857 if (addr & 1) { 858 bofs += 8; 859 addr -= 1; 860 } 861 /* fallthru */ 862 case 3: 863 bofs += 32; 864 break; 865 case 4: 866 if (addr & 3) { 867 bofs += 8 * (addr & 3); 868 addr &= -4; 869 } 870 break; 871 default: 872 g_assert_not_reached(); 873 } 874 875 return (struct bf_data){ 876 .addr = addr, 877 .bofs = bofs, 878 .blen = blen, 879 .len = len, 880 }; 881 } 882 883 static uint64_t bf_load(CPUM68KState *env, uint32_t addr, int blen, 884 uintptr_t ra) 885 { 886 switch (blen) { 887 case 0: 888 return cpu_ldub_data_ra(env, addr, ra); 889 case 1: 890 return cpu_lduw_data_ra(env, addr, ra); 891 case 2: 892 case 3: 893 return cpu_ldl_data_ra(env, addr, ra); 894 case 4: 895 return cpu_ldq_data_ra(env, addr, ra); 896 default: 897 g_assert_not_reached(); 898 } 899 } 900 901 static void bf_store(CPUM68KState *env, uint32_t addr, int blen, 902 uint64_t data, uintptr_t ra) 903 { 904 switch (blen) { 905 case 0: 906 cpu_stb_data_ra(env, addr, data, ra); 907 break; 908 case 1: 909 cpu_stw_data_ra(env, addr, data, ra); 910 break; 911 case 2: 912 case 3: 913 cpu_stl_data_ra(env, addr, data, ra); 914 break; 915 case 4: 916 cpu_stq_data_ra(env, addr, data, ra); 917 break; 918 default: 919 g_assert_not_reached(); 920 } 921 } 922 923 uint32_t HELPER(bfexts_mem)(CPUM68KState *env, uint32_t addr, 924 int32_t ofs, uint32_t len) 925 { 926 uintptr_t ra = GETPC(); 927 struct bf_data d = bf_prep(addr, ofs, len); 928 uint64_t data = bf_load(env, d.addr, d.blen, ra); 929 930 return (int64_t)(data << d.bofs) >> (64 - d.len); 931 } 932 933 uint64_t HELPER(bfextu_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 /* Put CC_N at the top of the high word; put the zero-extended value 941 at the bottom of the low word. */ 942 data <<= d.bofs; 943 data >>= 64 - d.len; 944 data |= data << (64 - d.len); 945 946 return data; 947 } 948 949 uint32_t HELPER(bfins_mem)(CPUM68KState *env, uint32_t addr, uint32_t val, 950 int32_t ofs, uint32_t len) 951 { 952 uintptr_t ra = GETPC(); 953 struct bf_data d = bf_prep(addr, ofs, len); 954 uint64_t data = bf_load(env, d.addr, d.blen, ra); 955 uint64_t mask = -1ull << (64 - d.len) >> d.bofs; 956 957 data = (data & ~mask) | (((uint64_t)val << (64 - d.len)) >> d.bofs); 958 959 bf_store(env, d.addr, d.blen, data, ra); 960 961 /* The field at the top of the word is also CC_N for CC_OP_LOGIC. */ 962 return val << (32 - d.len); 963 } 964 965 uint32_t HELPER(bfchg_mem)(CPUM68KState *env, uint32_t addr, 966 int32_t ofs, uint32_t len) 967 { 968 uintptr_t ra = GETPC(); 969 struct bf_data d = bf_prep(addr, ofs, len); 970 uint64_t data = bf_load(env, d.addr, d.blen, ra); 971 uint64_t mask = -1ull << (64 - d.len) >> d.bofs; 972 973 bf_store(env, d.addr, d.blen, data ^ mask, ra); 974 975 return ((data & mask) << d.bofs) >> 32; 976 } 977 978 uint32_t HELPER(bfclr_mem)(CPUM68KState *env, uint32_t addr, 979 int32_t ofs, uint32_t len) 980 { 981 uintptr_t ra = GETPC(); 982 struct bf_data d = bf_prep(addr, ofs, len); 983 uint64_t data = bf_load(env, d.addr, d.blen, ra); 984 uint64_t mask = -1ull << (64 - d.len) >> d.bofs; 985 986 bf_store(env, d.addr, d.blen, data & ~mask, ra); 987 988 return ((data & mask) << d.bofs) >> 32; 989 } 990 991 uint32_t HELPER(bfset_mem)(CPUM68KState *env, uint32_t addr, 992 int32_t ofs, uint32_t len) 993 { 994 uintptr_t ra = GETPC(); 995 struct bf_data d = bf_prep(addr, ofs, len); 996 uint64_t data = bf_load(env, d.addr, d.blen, ra); 997 uint64_t mask = -1ull << (64 - d.len) >> d.bofs; 998 999 bf_store(env, d.addr, d.blen, data | mask, ra); 1000 1001 return ((data & mask) << d.bofs) >> 32; 1002 } 1003 1004 uint32_t HELPER(bfffo_reg)(uint32_t n, uint32_t ofs, uint32_t len) 1005 { 1006 return (n ? clz32(n) : len) + ofs; 1007 } 1008 1009 uint64_t HELPER(bfffo_mem)(CPUM68KState *env, uint32_t addr, 1010 int32_t ofs, uint32_t len) 1011 { 1012 uintptr_t ra = GETPC(); 1013 struct bf_data d = bf_prep(addr, ofs, len); 1014 uint64_t data = bf_load(env, d.addr, d.blen, ra); 1015 uint64_t mask = -1ull << (64 - d.len) >> d.bofs; 1016 uint64_t n = (data & mask) << d.bofs; 1017 uint32_t ffo = helper_bfffo_reg(n >> 32, ofs, d.len); 1018 1019 /* Return FFO in the low word and N in the high word. 1020 Note that because of MASK and the shift, the low word 1021 is already zero. */ 1022 return n | ffo; 1023 } 1024 1025 void HELPER(chk)(CPUM68KState *env, int32_t val, int32_t ub) 1026 { 1027 /* From the specs: 1028 * X: Not affected, C,V,Z: Undefined, 1029 * N: Set if val < 0; cleared if val > ub, undefined otherwise 1030 * We implement here values found from a real MC68040: 1031 * X,V,Z: Not affected 1032 * N: Set if val < 0; cleared if val >= 0 1033 * C: if 0 <= ub: set if val < 0 or val > ub, cleared otherwise 1034 * if 0 > ub: set if val > ub and val < 0, cleared otherwise 1035 */ 1036 env->cc_n = val; 1037 env->cc_c = 0 <= ub ? val < 0 || val > ub : val > ub && val < 0; 1038 1039 if (val < 0 || val > ub) { 1040 CPUState *cs = env_cpu(env); 1041 1042 /* Recover PC and CC_OP for the beginning of the insn. */ 1043 cpu_restore_state(cs, GETPC(), true); 1044 1045 /* flags have been modified by gen_flush_flags() */ 1046 env->cc_op = CC_OP_FLAGS; 1047 /* Adjust PC to end of the insn. */ 1048 env->pc += 2; 1049 1050 cs->exception_index = EXCP_CHK; 1051 cpu_loop_exit(cs); 1052 } 1053 } 1054 1055 void HELPER(chk2)(CPUM68KState *env, int32_t val, int32_t lb, int32_t ub) 1056 { 1057 /* From the specs: 1058 * X: Not affected, N,V: Undefined, 1059 * Z: Set if val is equal to lb or ub 1060 * C: Set if val < lb or val > ub, cleared otherwise 1061 * We implement here values found from a real MC68040: 1062 * X,N,V: Not affected 1063 * Z: Set if val is equal to lb or ub 1064 * C: if lb <= ub: set if val < lb or val > ub, cleared otherwise 1065 * if lb > ub: set if val > ub and val < lb, cleared otherwise 1066 */ 1067 env->cc_z = val != lb && val != ub; 1068 env->cc_c = lb <= ub ? val < lb || val > ub : val > ub && val < lb; 1069 1070 if (env->cc_c) { 1071 CPUState *cs = env_cpu(env); 1072 1073 /* Recover PC and CC_OP for the beginning of the insn. */ 1074 cpu_restore_state(cs, GETPC(), true); 1075 1076 /* flags have been modified by gen_flush_flags() */ 1077 env->cc_op = CC_OP_FLAGS; 1078 /* Adjust PC to end of the insn. */ 1079 env->pc += 4; 1080 1081 cs->exception_index = EXCP_CHK; 1082 cpu_loop_exit(cs); 1083 } 1084 } 1085