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