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