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_ACCESS) { 364 if (env->mmu.fault) { 365 cpu_abort(cs, "DOUBLE MMU FAULT\n"); 366 } 367 env->mmu.fault = true; 368 sp -= 4; 369 cpu_stl_kernel(env, sp, 0); /* push data 3 */ 370 sp -= 4; 371 cpu_stl_kernel(env, sp, 0); /* push data 2 */ 372 sp -= 4; 373 cpu_stl_kernel(env, sp, 0); /* push data 1 */ 374 sp -= 4; 375 cpu_stl_kernel(env, sp, 0); /* write back 1 / push data 0 */ 376 sp -= 4; 377 cpu_stl_kernel(env, sp, 0); /* write back 1 address */ 378 sp -= 4; 379 cpu_stl_kernel(env, sp, 0); /* write back 2 data */ 380 sp -= 4; 381 cpu_stl_kernel(env, sp, 0); /* write back 2 address */ 382 sp -= 4; 383 cpu_stl_kernel(env, sp, 0); /* write back 3 data */ 384 sp -= 4; 385 cpu_stl_kernel(env, sp, env->mmu.ar); /* write back 3 address */ 386 sp -= 4; 387 cpu_stl_kernel(env, sp, env->mmu.ar); /* fault address */ 388 sp -= 2; 389 cpu_stw_kernel(env, sp, 0); /* write back 1 status */ 390 sp -= 2; 391 cpu_stw_kernel(env, sp, 0); /* write back 2 status */ 392 sp -= 2; 393 cpu_stw_kernel(env, sp, 0); /* write back 3 status */ 394 sp -= 2; 395 cpu_stw_kernel(env, sp, env->mmu.ssw); /* special status word */ 396 sp -= 4; 397 cpu_stl_kernel(env, sp, env->mmu.ar); /* effective address */ 398 do_stack_frame(env, &sp, 7, oldsr, 0, retaddr); 399 env->mmu.fault = false; 400 if (qemu_loglevel_mask(CPU_LOG_INT)) { 401 qemu_log(" " 402 "ssw: %08x ea: %08x sfc: %d dfc: %d\n", 403 env->mmu.ssw, env->mmu.ar, env->sfc, env->dfc); 404 } 405 } else if (cs->exception_index == EXCP_ADDRESS) { 406 do_stack_frame(env, &sp, 2, oldsr, 0, retaddr); 407 } else if (cs->exception_index == EXCP_ILLEGAL || 408 cs->exception_index == EXCP_DIV0 || 409 cs->exception_index == EXCP_CHK || 410 cs->exception_index == EXCP_TRAPCC || 411 cs->exception_index == EXCP_TRACE) { 412 /* FIXME: addr is not only env->pc */ 413 do_stack_frame(env, &sp, 2, oldsr, env->pc, retaddr); 414 } else if (is_hw && oldsr & SR_M && 415 cs->exception_index >= EXCP_SPURIOUS && 416 cs->exception_index <= EXCP_INT_LEVEL_7) { 417 do_stack_frame(env, &sp, 0, oldsr, 0, retaddr); 418 oldsr = sr; 419 env->aregs[7] = sp; 420 cpu_m68k_set_sr(env, sr &= ~SR_M); 421 sp = env->aregs[7] & ~1; 422 do_stack_frame(env, &sp, 1, oldsr, 0, retaddr); 423 } else { 424 do_stack_frame(env, &sp, 0, oldsr, 0, retaddr); 425 } 426 427 env->aregs[7] = sp; 428 /* Jump to vector. */ 429 env->pc = cpu_ldl_kernel(env, env->vbr + vector); 430 } 431 432 static void do_interrupt_all(CPUM68KState *env, int is_hw) 433 { 434 if (m68k_feature(env, M68K_FEATURE_M68000)) { 435 m68k_interrupt_all(env, is_hw); 436 return; 437 } 438 cf_interrupt_all(env, is_hw); 439 } 440 441 void m68k_cpu_do_interrupt(CPUState *cs) 442 { 443 M68kCPU *cpu = M68K_CPU(cs); 444 CPUM68KState *env = &cpu->env; 445 446 do_interrupt_all(env, 0); 447 } 448 449 static inline void do_interrupt_m68k_hardirq(CPUM68KState *env) 450 { 451 do_interrupt_all(env, 1); 452 } 453 454 void m68k_cpu_unassigned_access(CPUState *cs, hwaddr addr, bool is_write, 455 bool is_exec, int is_asi, unsigned size) 456 { 457 M68kCPU *cpu = M68K_CPU(cs); 458 CPUM68KState *env = &cpu->env; 459 #ifdef DEBUG_UNASSIGNED 460 qemu_log_mask(CPU_LOG_INT, "Unassigned " TARGET_FMT_plx " wr=%d exe=%d\n", 461 addr, is_write, is_exec); 462 #endif 463 if (env == NULL) { 464 /* when called from gdb, env is NULL */ 465 return; 466 } 467 468 if (m68k_feature(env, M68K_FEATURE_M68040)) { 469 env->mmu.ssw |= M68K_ATC_040; 470 /* FIXME: manage MMU table access error */ 471 env->mmu.ssw &= ~M68K_TM_040; 472 if (env->sr & SR_S) { /* SUPERVISOR */ 473 env->mmu.ssw |= M68K_TM_040_SUPER; 474 } 475 if (is_exec) { /* instruction or data */ 476 env->mmu.ssw |= M68K_TM_040_CODE; 477 } else { 478 env->mmu.ssw |= M68K_TM_040_DATA; 479 } 480 env->mmu.ssw &= ~M68K_BA_SIZE_MASK; 481 switch (size) { 482 case 1: 483 env->mmu.ssw |= M68K_BA_SIZE_BYTE; 484 break; 485 case 2: 486 env->mmu.ssw |= M68K_BA_SIZE_WORD; 487 break; 488 case 4: 489 env->mmu.ssw |= M68K_BA_SIZE_LONG; 490 break; 491 } 492 493 if (!is_write) { 494 env->mmu.ssw |= M68K_RW_040; 495 } 496 497 env->mmu.ar = addr; 498 499 cs->exception_index = EXCP_ACCESS; 500 cpu_loop_exit(cs); 501 } 502 } 503 #endif 504 505 bool m68k_cpu_exec_interrupt(CPUState *cs, int interrupt_request) 506 { 507 M68kCPU *cpu = M68K_CPU(cs); 508 CPUM68KState *env = &cpu->env; 509 510 if (interrupt_request & CPU_INTERRUPT_HARD 511 && ((env->sr & SR_I) >> SR_I_SHIFT) < env->pending_level) { 512 /* Real hardware gets the interrupt vector via an IACK cycle 513 at this point. Current emulated hardware doesn't rely on 514 this, so we provide/save the vector when the interrupt is 515 first signalled. */ 516 cs->exception_index = env->pending_vector; 517 do_interrupt_m68k_hardirq(env); 518 return true; 519 } 520 return false; 521 } 522 523 static void raise_exception_ra(CPUM68KState *env, int tt, uintptr_t raddr) 524 { 525 CPUState *cs = CPU(m68k_env_get_cpu(env)); 526 527 cs->exception_index = tt; 528 cpu_loop_exit_restore(cs, raddr); 529 } 530 531 static void raise_exception(CPUM68KState *env, int tt) 532 { 533 raise_exception_ra(env, tt, 0); 534 } 535 536 void HELPER(raise_exception)(CPUM68KState *env, uint32_t tt) 537 { 538 raise_exception(env, tt); 539 } 540 541 void HELPER(divuw)(CPUM68KState *env, int destr, uint32_t den) 542 { 543 uint32_t num = env->dregs[destr]; 544 uint32_t quot, rem; 545 546 if (den == 0) { 547 raise_exception_ra(env, EXCP_DIV0, GETPC()); 548 } 549 quot = num / den; 550 rem = num % den; 551 552 env->cc_c = 0; /* always cleared, even if overflow */ 553 if (quot > 0xffff) { 554 env->cc_v = -1; 555 /* real 68040 keeps N and unset Z on overflow, 556 * whereas documentation says "undefined" 557 */ 558 env->cc_z = 1; 559 return; 560 } 561 env->dregs[destr] = deposit32(quot, 16, 16, rem); 562 env->cc_z = (int16_t)quot; 563 env->cc_n = (int16_t)quot; 564 env->cc_v = 0; 565 } 566 567 void HELPER(divsw)(CPUM68KState *env, int destr, int32_t den) 568 { 569 int32_t num = env->dregs[destr]; 570 uint32_t quot, rem; 571 572 if (den == 0) { 573 raise_exception_ra(env, EXCP_DIV0, GETPC()); 574 } 575 quot = num / den; 576 rem = num % den; 577 578 env->cc_c = 0; /* always cleared, even if overflow */ 579 if (quot != (int16_t)quot) { 580 env->cc_v = -1; 581 /* nothing else is modified */ 582 /* real 68040 keeps N and unset Z on overflow, 583 * whereas documentation says "undefined" 584 */ 585 env->cc_z = 1; 586 return; 587 } 588 env->dregs[destr] = deposit32(quot, 16, 16, rem); 589 env->cc_z = (int16_t)quot; 590 env->cc_n = (int16_t)quot; 591 env->cc_v = 0; 592 } 593 594 void HELPER(divul)(CPUM68KState *env, int numr, int regr, uint32_t den) 595 { 596 uint32_t num = env->dregs[numr]; 597 uint32_t quot, rem; 598 599 if (den == 0) { 600 raise_exception_ra(env, EXCP_DIV0, GETPC()); 601 } 602 quot = num / den; 603 rem = num % den; 604 605 env->cc_c = 0; 606 env->cc_z = quot; 607 env->cc_n = quot; 608 env->cc_v = 0; 609 610 if (m68k_feature(env, M68K_FEATURE_CF_ISA_A)) { 611 if (numr == regr) { 612 env->dregs[numr] = quot; 613 } else { 614 env->dregs[regr] = rem; 615 } 616 } else { 617 env->dregs[regr] = rem; 618 env->dregs[numr] = quot; 619 } 620 } 621 622 void HELPER(divsl)(CPUM68KState *env, int numr, int regr, int32_t den) 623 { 624 int32_t num = env->dregs[numr]; 625 int32_t quot, rem; 626 627 if (den == 0) { 628 raise_exception_ra(env, EXCP_DIV0, GETPC()); 629 } 630 quot = num / den; 631 rem = num % den; 632 633 env->cc_c = 0; 634 env->cc_z = quot; 635 env->cc_n = quot; 636 env->cc_v = 0; 637 638 if (m68k_feature(env, M68K_FEATURE_CF_ISA_A)) { 639 if (numr == regr) { 640 env->dregs[numr] = quot; 641 } else { 642 env->dregs[regr] = rem; 643 } 644 } else { 645 env->dregs[regr] = rem; 646 env->dregs[numr] = quot; 647 } 648 } 649 650 void HELPER(divull)(CPUM68KState *env, int numr, int regr, uint32_t den) 651 { 652 uint64_t num = deposit64(env->dregs[numr], 32, 32, env->dregs[regr]); 653 uint64_t quot; 654 uint32_t rem; 655 656 if (den == 0) { 657 raise_exception_ra(env, EXCP_DIV0, GETPC()); 658 } 659 quot = num / den; 660 rem = num % den; 661 662 env->cc_c = 0; /* always cleared, even if overflow */ 663 if (quot > 0xffffffffULL) { 664 env->cc_v = -1; 665 /* real 68040 keeps N and unset Z on overflow, 666 * whereas documentation says "undefined" 667 */ 668 env->cc_z = 1; 669 return; 670 } 671 env->cc_z = quot; 672 env->cc_n = quot; 673 env->cc_v = 0; 674 675 /* 676 * If Dq and Dr are the same, the quotient is returned. 677 * therefore we set Dq last. 678 */ 679 680 env->dregs[regr] = rem; 681 env->dregs[numr] = quot; 682 } 683 684 void HELPER(divsll)(CPUM68KState *env, int numr, int regr, int32_t den) 685 { 686 int64_t num = deposit64(env->dregs[numr], 32, 32, env->dregs[regr]); 687 int64_t quot; 688 int32_t rem; 689 690 if (den == 0) { 691 raise_exception_ra(env, EXCP_DIV0, GETPC()); 692 } 693 quot = num / den; 694 rem = num % den; 695 696 env->cc_c = 0; /* always cleared, even if overflow */ 697 if (quot != (int32_t)quot) { 698 env->cc_v = -1; 699 /* real 68040 keeps N and unset Z on overflow, 700 * whereas documentation says "undefined" 701 */ 702 env->cc_z = 1; 703 return; 704 } 705 env->cc_z = quot; 706 env->cc_n = quot; 707 env->cc_v = 0; 708 709 /* 710 * If Dq and Dr are the same, the quotient is returned. 711 * therefore we set Dq last. 712 */ 713 714 env->dregs[regr] = rem; 715 env->dregs[numr] = quot; 716 } 717 718 /* We're executing in a serial context -- no need to be atomic. */ 719 void HELPER(cas2w)(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2) 720 { 721 uint32_t Dc1 = extract32(regs, 9, 3); 722 uint32_t Dc2 = extract32(regs, 6, 3); 723 uint32_t Du1 = extract32(regs, 3, 3); 724 uint32_t Du2 = extract32(regs, 0, 3); 725 int16_t c1 = env->dregs[Dc1]; 726 int16_t c2 = env->dregs[Dc2]; 727 int16_t u1 = env->dregs[Du1]; 728 int16_t u2 = env->dregs[Du2]; 729 int16_t l1, l2; 730 uintptr_t ra = GETPC(); 731 732 l1 = cpu_lduw_data_ra(env, a1, ra); 733 l2 = cpu_lduw_data_ra(env, a2, ra); 734 if (l1 == c1 && l2 == c2) { 735 cpu_stw_data_ra(env, a1, u1, ra); 736 cpu_stw_data_ra(env, a2, u2, ra); 737 } 738 739 if (c1 != l1) { 740 env->cc_n = l1; 741 env->cc_v = c1; 742 } else { 743 env->cc_n = l2; 744 env->cc_v = c2; 745 } 746 env->cc_op = CC_OP_CMPW; 747 env->dregs[Dc1] = deposit32(env->dregs[Dc1], 0, 16, l1); 748 env->dregs[Dc2] = deposit32(env->dregs[Dc2], 0, 16, l2); 749 } 750 751 static void do_cas2l(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2, 752 bool parallel) 753 { 754 uint32_t Dc1 = extract32(regs, 9, 3); 755 uint32_t Dc2 = extract32(regs, 6, 3); 756 uint32_t Du1 = extract32(regs, 3, 3); 757 uint32_t Du2 = extract32(regs, 0, 3); 758 uint32_t c1 = env->dregs[Dc1]; 759 uint32_t c2 = env->dregs[Dc2]; 760 uint32_t u1 = env->dregs[Du1]; 761 uint32_t u2 = env->dregs[Du2]; 762 uint32_t l1, l2; 763 uintptr_t ra = GETPC(); 764 #if defined(CONFIG_ATOMIC64) && !defined(CONFIG_USER_ONLY) 765 int mmu_idx = cpu_mmu_index(env, 0); 766 TCGMemOpIdx oi; 767 #endif 768 769 if (parallel) { 770 /* We're executing in a parallel context -- must be atomic. */ 771 #ifdef CONFIG_ATOMIC64 772 uint64_t c, u, l; 773 if ((a1 & 7) == 0 && a2 == a1 + 4) { 774 c = deposit64(c2, 32, 32, c1); 775 u = deposit64(u2, 32, 32, u1); 776 #ifdef CONFIG_USER_ONLY 777 l = helper_atomic_cmpxchgq_be(env, a1, c, u); 778 #else 779 oi = make_memop_idx(MO_BEQ, mmu_idx); 780 l = helper_atomic_cmpxchgq_be_mmu(env, a1, c, u, oi, ra); 781 #endif 782 l1 = l >> 32; 783 l2 = l; 784 } else if ((a2 & 7) == 0 && a1 == a2 + 4) { 785 c = deposit64(c1, 32, 32, c2); 786 u = deposit64(u1, 32, 32, u2); 787 #ifdef CONFIG_USER_ONLY 788 l = helper_atomic_cmpxchgq_be(env, a2, c, u); 789 #else 790 oi = make_memop_idx(MO_BEQ, mmu_idx); 791 l = helper_atomic_cmpxchgq_be_mmu(env, a2, c, u, oi, ra); 792 #endif 793 l2 = l >> 32; 794 l1 = l; 795 } else 796 #endif 797 { 798 /* Tell the main loop we need to serialize this insn. */ 799 cpu_loop_exit_atomic(ENV_GET_CPU(env), ra); 800 } 801 } else { 802 /* We're executing in a serial context -- no need to be atomic. */ 803 l1 = cpu_ldl_data_ra(env, a1, ra); 804 l2 = cpu_ldl_data_ra(env, a2, ra); 805 if (l1 == c1 && l2 == c2) { 806 cpu_stl_data_ra(env, a1, u1, ra); 807 cpu_stl_data_ra(env, a2, u2, ra); 808 } 809 } 810 811 if (c1 != l1) { 812 env->cc_n = l1; 813 env->cc_v = c1; 814 } else { 815 env->cc_n = l2; 816 env->cc_v = c2; 817 } 818 env->cc_op = CC_OP_CMPL; 819 env->dregs[Dc1] = l1; 820 env->dregs[Dc2] = l2; 821 } 822 823 void HELPER(cas2l)(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2) 824 { 825 do_cas2l(env, regs, a1, a2, false); 826 } 827 828 void HELPER(cas2l_parallel)(CPUM68KState *env, uint32_t regs, uint32_t a1, 829 uint32_t a2) 830 { 831 do_cas2l(env, regs, a1, a2, true); 832 } 833 834 struct bf_data { 835 uint32_t addr; 836 uint32_t bofs; 837 uint32_t blen; 838 uint32_t len; 839 }; 840 841 static struct bf_data bf_prep(uint32_t addr, int32_t ofs, uint32_t len) 842 { 843 int bofs, blen; 844 845 /* Bound length; map 0 to 32. */ 846 len = ((len - 1) & 31) + 1; 847 848 /* Note that ofs is signed. */ 849 addr += ofs / 8; 850 bofs = ofs % 8; 851 if (bofs < 0) { 852 bofs += 8; 853 addr -= 1; 854 } 855 856 /* Compute the number of bytes required (minus one) to 857 satisfy the bitfield. */ 858 blen = (bofs + len - 1) / 8; 859 860 /* Canonicalize the bit offset for data loaded into a 64-bit big-endian 861 word. For the cases where BLEN is not a power of 2, adjust ADDR so 862 that we can use the next power of two sized load without crossing a 863 page boundary, unless the field itself crosses the boundary. */ 864 switch (blen) { 865 case 0: 866 bofs += 56; 867 break; 868 case 1: 869 bofs += 48; 870 break; 871 case 2: 872 if (addr & 1) { 873 bofs += 8; 874 addr -= 1; 875 } 876 /* fallthru */ 877 case 3: 878 bofs += 32; 879 break; 880 case 4: 881 if (addr & 3) { 882 bofs += 8 * (addr & 3); 883 addr &= -4; 884 } 885 break; 886 default: 887 g_assert_not_reached(); 888 } 889 890 return (struct bf_data){ 891 .addr = addr, 892 .bofs = bofs, 893 .blen = blen, 894 .len = len, 895 }; 896 } 897 898 static uint64_t bf_load(CPUM68KState *env, uint32_t addr, int blen, 899 uintptr_t ra) 900 { 901 switch (blen) { 902 case 0: 903 return cpu_ldub_data_ra(env, addr, ra); 904 case 1: 905 return cpu_lduw_data_ra(env, addr, ra); 906 case 2: 907 case 3: 908 return cpu_ldl_data_ra(env, addr, ra); 909 case 4: 910 return cpu_ldq_data_ra(env, addr, ra); 911 default: 912 g_assert_not_reached(); 913 } 914 } 915 916 static void bf_store(CPUM68KState *env, uint32_t addr, int blen, 917 uint64_t data, uintptr_t ra) 918 { 919 switch (blen) { 920 case 0: 921 cpu_stb_data_ra(env, addr, data, ra); 922 break; 923 case 1: 924 cpu_stw_data_ra(env, addr, data, ra); 925 break; 926 case 2: 927 case 3: 928 cpu_stl_data_ra(env, addr, data, ra); 929 break; 930 case 4: 931 cpu_stq_data_ra(env, addr, data, ra); 932 break; 933 default: 934 g_assert_not_reached(); 935 } 936 } 937 938 uint32_t HELPER(bfexts_mem)(CPUM68KState *env, uint32_t addr, 939 int32_t ofs, uint32_t len) 940 { 941 uintptr_t ra = GETPC(); 942 struct bf_data d = bf_prep(addr, ofs, len); 943 uint64_t data = bf_load(env, d.addr, d.blen, ra); 944 945 return (int64_t)(data << d.bofs) >> (64 - d.len); 946 } 947 948 uint64_t HELPER(bfextu_mem)(CPUM68KState *env, uint32_t addr, 949 int32_t ofs, uint32_t len) 950 { 951 uintptr_t ra = GETPC(); 952 struct bf_data d = bf_prep(addr, ofs, len); 953 uint64_t data = bf_load(env, d.addr, d.blen, ra); 954 955 /* Put CC_N at the top of the high word; put the zero-extended value 956 at the bottom of the low word. */ 957 data <<= d.bofs; 958 data >>= 64 - d.len; 959 data |= data << (64 - d.len); 960 961 return data; 962 } 963 964 uint32_t HELPER(bfins_mem)(CPUM68KState *env, uint32_t addr, uint32_t val, 965 int32_t ofs, uint32_t len) 966 { 967 uintptr_t ra = GETPC(); 968 struct bf_data d = bf_prep(addr, ofs, len); 969 uint64_t data = bf_load(env, d.addr, d.blen, ra); 970 uint64_t mask = -1ull << (64 - d.len) >> d.bofs; 971 972 data = (data & ~mask) | (((uint64_t)val << (64 - d.len)) >> d.bofs); 973 974 bf_store(env, d.addr, d.blen, data, ra); 975 976 /* The field at the top of the word is also CC_N for CC_OP_LOGIC. */ 977 return val << (32 - d.len); 978 } 979 980 uint32_t HELPER(bfchg_mem)(CPUM68KState *env, uint32_t addr, 981 int32_t ofs, uint32_t len) 982 { 983 uintptr_t ra = GETPC(); 984 struct bf_data d = bf_prep(addr, ofs, len); 985 uint64_t data = bf_load(env, d.addr, d.blen, ra); 986 uint64_t mask = -1ull << (64 - d.len) >> d.bofs; 987 988 bf_store(env, d.addr, d.blen, data ^ mask, ra); 989 990 return ((data & mask) << d.bofs) >> 32; 991 } 992 993 uint32_t HELPER(bfclr_mem)(CPUM68KState *env, uint32_t addr, 994 int32_t ofs, uint32_t len) 995 { 996 uintptr_t ra = GETPC(); 997 struct bf_data d = bf_prep(addr, ofs, len); 998 uint64_t data = bf_load(env, d.addr, d.blen, ra); 999 uint64_t mask = -1ull << (64 - d.len) >> d.bofs; 1000 1001 bf_store(env, d.addr, d.blen, data & ~mask, ra); 1002 1003 return ((data & mask) << d.bofs) >> 32; 1004 } 1005 1006 uint32_t HELPER(bfset_mem)(CPUM68KState *env, uint32_t addr, 1007 int32_t ofs, uint32_t len) 1008 { 1009 uintptr_t ra = GETPC(); 1010 struct bf_data d = bf_prep(addr, ofs, len); 1011 uint64_t data = bf_load(env, d.addr, d.blen, ra); 1012 uint64_t mask = -1ull << (64 - d.len) >> d.bofs; 1013 1014 bf_store(env, d.addr, d.blen, data | mask, ra); 1015 1016 return ((data & mask) << d.bofs) >> 32; 1017 } 1018 1019 uint32_t HELPER(bfffo_reg)(uint32_t n, uint32_t ofs, uint32_t len) 1020 { 1021 return (n ? clz32(n) : len) + ofs; 1022 } 1023 1024 uint64_t HELPER(bfffo_mem)(CPUM68KState *env, uint32_t addr, 1025 int32_t ofs, uint32_t len) 1026 { 1027 uintptr_t ra = GETPC(); 1028 struct bf_data d = bf_prep(addr, ofs, len); 1029 uint64_t data = bf_load(env, d.addr, d.blen, ra); 1030 uint64_t mask = -1ull << (64 - d.len) >> d.bofs; 1031 uint64_t n = (data & mask) << d.bofs; 1032 uint32_t ffo = helper_bfffo_reg(n >> 32, ofs, d.len); 1033 1034 /* Return FFO in the low word and N in the high word. 1035 Note that because of MASK and the shift, the low word 1036 is already zero. */ 1037 return n | ffo; 1038 } 1039 1040 void HELPER(chk)(CPUM68KState *env, int32_t val, int32_t ub) 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 = CPU(m68k_env_get_cpu(env)); 1056 1057 /* Recover PC and CC_OP for the beginning of the insn. */ 1058 cpu_restore_state(cs, GETPC()); 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 /* From the specs: 1073 * X: Not affected, N,V: Undefined, 1074 * Z: Set if val is equal to lb or ub 1075 * C: Set if val < lb or val > ub, cleared otherwise 1076 * We implement here values found from a real MC68040: 1077 * X,N,V: Not affected 1078 * Z: Set if val is equal to lb or ub 1079 * C: if lb <= ub: set if val < lb or val > ub, cleared otherwise 1080 * if lb > ub: set if val > ub and val < lb, cleared otherwise 1081 */ 1082 env->cc_z = val != lb && val != ub; 1083 env->cc_c = lb <= ub ? val < lb || val > ub : val > ub && val < lb; 1084 1085 if (env->cc_c) { 1086 CPUState *cs = CPU(m68k_env_get_cpu(env)); 1087 1088 /* Recover PC and CC_OP for the beginning of the insn. */ 1089 cpu_restore_state(cs, GETPC()); 1090 1091 /* flags have been modified by gen_flush_flags() */ 1092 env->cc_op = CC_OP_FLAGS; 1093 /* Adjust PC to end of the insn. */ 1094 env->pc += 4; 1095 1096 cs->exception_index = EXCP_CHK; 1097 cpu_loop_exit(cs); 1098 } 1099 } 1100