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