1 /* 2 * M68K helper routines 3 * 4 * Copyright (c) 2007 CodeSourcery 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 18 */ 19 #include "qemu/osdep.h" 20 #include "cpu.h" 21 #include "exec/helper-proto.h" 22 #include "exec/exec-all.h" 23 #include "exec/cpu_ldst.h" 24 #include "hw/semihosting/semihost.h" 25 26 #if defined(CONFIG_USER_ONLY) 27 28 void m68k_cpu_do_interrupt(CPUState *cs) 29 { 30 cs->exception_index = -1; 31 } 32 33 static inline void do_interrupt_m68k_hardirq(CPUM68KState *env) 34 { 35 } 36 37 #else 38 39 static void cf_rte(CPUM68KState *env) 40 { 41 uint32_t sp; 42 uint32_t fmt; 43 44 sp = env->aregs[7]; 45 fmt = cpu_ldl_mmuidx_ra(env, sp, MMU_KERNEL_IDX, 0); 46 env->pc = cpu_ldl_mmuidx_ra(env, sp + 4, MMU_KERNEL_IDX, 0); 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_mmuidx_ra(env, sp, MMU_KERNEL_IDX, 0); 62 sp += 2; 63 env->pc = cpu_ldl_mmuidx_ra(env, sp, MMU_KERNEL_IDX, 0); 64 sp += 4; 65 if (m68k_feature(env, M68K_FEATURE_QUAD_MULDIV)) { 66 /* all except 68000 */ 67 fmt = cpu_lduw_mmuidx_ra(env, sp, MMU_KERNEL_IDX, 0); 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 "Uninitialized Interrupt"; 121 case EXCP_SPURIOUS: 122 return "Spurious Interrupt"; 123 case EXCP_INT_LEVEL_1: 124 return "Level 1 Interrupt"; 125 case EXCP_INT_LEVEL_1 + 1: 126 return "Level 2 Interrupt"; 127 case EXCP_INT_LEVEL_1 + 2: 128 return "Level 3 Interrupt"; 129 case EXCP_INT_LEVEL_1 + 3: 130 return "Level 4 Interrupt"; 131 case EXCP_INT_LEVEL_1 + 4: 132 return "Level 5 Interrupt"; 133 case EXCP_INT_LEVEL_1 + 5: 134 return "Level 6 Interrupt"; 135 case EXCP_INT_LEVEL_1 + 6: 136 return "Level 7 Interrupt"; 137 case EXCP_TRAP0: 138 return "TRAP #0"; 139 case EXCP_TRAP0 + 1: 140 return "TRAP #1"; 141 case EXCP_TRAP0 + 2: 142 return "TRAP #2"; 143 case EXCP_TRAP0 + 3: 144 return "TRAP #3"; 145 case EXCP_TRAP0 + 4: 146 return "TRAP #4"; 147 case EXCP_TRAP0 + 5: 148 return "TRAP #5"; 149 case EXCP_TRAP0 + 6: 150 return "TRAP #6"; 151 case EXCP_TRAP0 + 7: 152 return "TRAP #7"; 153 case EXCP_TRAP0 + 8: 154 return "TRAP #8"; 155 case EXCP_TRAP0 + 9: 156 return "TRAP #9"; 157 case EXCP_TRAP0 + 10: 158 return "TRAP #10"; 159 case EXCP_TRAP0 + 11: 160 return "TRAP #11"; 161 case EXCP_TRAP0 + 12: 162 return "TRAP #12"; 163 case EXCP_TRAP0 + 13: 164 return "TRAP #13"; 165 case EXCP_TRAP0 + 14: 166 return "TRAP #14"; 167 case EXCP_TRAP0 + 15: 168 return "TRAP #15"; 169 case EXCP_FP_BSUN: 170 return "FP Branch/Set on unordered condition"; 171 case EXCP_FP_INEX: 172 return "FP Inexact Result"; 173 case EXCP_FP_DZ: 174 return "FP Divide by Zero"; 175 case EXCP_FP_UNFL: 176 return "FP Underflow"; 177 case EXCP_FP_OPERR: 178 return "FP Operand Error"; 179 case EXCP_FP_OVFL: 180 return "FP Overflow"; 181 case EXCP_FP_SNAN: 182 return "FP Signaling NAN"; 183 case EXCP_FP_UNIMP: 184 return "FP Unimplemented Data Type"; 185 case EXCP_MMU_CONF: /* 68030/68851 only */ 186 return "MMU Configuration Error"; 187 case EXCP_MMU_ILLEGAL: /* 68851 only */ 188 return "MMU Illegal Operation"; 189 case EXCP_MMU_ACCESS: /* 68851 only */ 190 return "MMU Access Level Violation"; 191 case 64 ... 255: 192 return "User Defined Vector"; 193 } 194 return "Unassigned"; 195 } 196 197 static void cf_interrupt_all(CPUM68KState *env, int is_hw) 198 { 199 CPUState *cs = env_cpu(env); 200 uint32_t sp; 201 uint32_t sr; 202 uint32_t fmt; 203 uint32_t retaddr; 204 uint32_t vector; 205 206 fmt = 0; 207 retaddr = env->pc; 208 209 if (!is_hw) { 210 switch (cs->exception_index) { 211 case EXCP_RTE: 212 /* Return from an exception. */ 213 cf_rte(env); 214 return; 215 case EXCP_HALT_INSN: 216 if (semihosting_enabled() 217 && (env->sr & SR_S) != 0 218 && (env->pc & 3) == 0 219 && cpu_lduw_code(env, env->pc - 4) == 0x4e71 220 && cpu_ldl_code(env, env->pc) == 0x4e7bf000) { 221 env->pc += 4; 222 do_m68k_semihosting(env, env->dregs[0]); 223 return; 224 } 225 cs->halted = 1; 226 cs->exception_index = EXCP_HLT; 227 cpu_loop_exit(cs); 228 return; 229 } 230 if (cs->exception_index >= EXCP_TRAP0 231 && cs->exception_index <= EXCP_TRAP15) { 232 /* Move the PC after the trap instruction. */ 233 retaddr += 2; 234 } 235 } 236 237 vector = cs->exception_index << 2; 238 239 sr = env->sr | cpu_m68k_get_ccr(env); 240 if (qemu_loglevel_mask(CPU_LOG_INT)) { 241 static int count; 242 qemu_log("INT %6d: %s(%#x) pc=%08x sp=%08x sr=%04x\n", 243 ++count, m68k_exception_name(cs->exception_index), 244 vector, env->pc, env->aregs[7], sr); 245 } 246 247 fmt |= 0x40000000; 248 fmt |= vector << 16; 249 fmt |= sr; 250 251 env->sr |= SR_S; 252 if (is_hw) { 253 env->sr = (env->sr & ~SR_I) | (env->pending_level << SR_I_SHIFT); 254 env->sr &= ~SR_M; 255 } 256 m68k_switch_sp(env); 257 sp = env->aregs[7]; 258 fmt |= (sp & 3) << 28; 259 260 /* ??? This could cause MMU faults. */ 261 sp &= ~3; 262 sp -= 4; 263 cpu_stl_mmuidx_ra(env, sp, retaddr, MMU_KERNEL_IDX, 0); 264 sp -= 4; 265 cpu_stl_mmuidx_ra(env, sp, fmt, MMU_KERNEL_IDX, 0); 266 env->aregs[7] = sp; 267 /* Jump to vector. */ 268 env->pc = cpu_ldl_mmuidx_ra(env, env->vbr + vector, MMU_KERNEL_IDX, 0); 269 } 270 271 static inline void do_stack_frame(CPUM68KState *env, uint32_t *sp, 272 uint16_t format, uint16_t sr, 273 uint32_t addr, uint32_t retaddr) 274 { 275 if (m68k_feature(env, M68K_FEATURE_QUAD_MULDIV)) { 276 /* all except 68000 */ 277 CPUState *cs = env_cpu(env); 278 switch (format) { 279 case 4: 280 *sp -= 4; 281 cpu_stl_mmuidx_ra(env, *sp, env->pc, MMU_KERNEL_IDX, 0); 282 *sp -= 4; 283 cpu_stl_mmuidx_ra(env, *sp, addr, MMU_KERNEL_IDX, 0); 284 break; 285 case 3: 286 case 2: 287 *sp -= 4; 288 cpu_stl_mmuidx_ra(env, *sp, addr, MMU_KERNEL_IDX, 0); 289 break; 290 } 291 *sp -= 2; 292 cpu_stw_mmuidx_ra(env, *sp, (format << 12) + (cs->exception_index << 2), 293 MMU_KERNEL_IDX, 0); 294 } 295 *sp -= 4; 296 cpu_stl_mmuidx_ra(env, *sp, retaddr, MMU_KERNEL_IDX, 0); 297 *sp -= 2; 298 cpu_stw_mmuidx_ra(env, *sp, sr, MMU_KERNEL_IDX, 0); 299 } 300 301 static void m68k_interrupt_all(CPUM68KState *env, int is_hw) 302 { 303 CPUState *cs = env_cpu(env); 304 uint32_t sp; 305 uint32_t retaddr; 306 uint32_t vector; 307 uint16_t sr, oldsr; 308 309 retaddr = env->pc; 310 311 if (!is_hw) { 312 switch (cs->exception_index) { 313 case EXCP_RTE: 314 /* Return from an exception. */ 315 m68k_rte(env); 316 return; 317 case EXCP_TRAP0 ... EXCP_TRAP15: 318 /* Move the PC after the trap instruction. */ 319 retaddr += 2; 320 break; 321 } 322 } 323 324 vector = cs->exception_index << 2; 325 326 sr = env->sr | cpu_m68k_get_ccr(env); 327 if (qemu_loglevel_mask(CPU_LOG_INT)) { 328 static int count; 329 qemu_log("INT %6d: %s(%#x) pc=%08x sp=%08x sr=%04x\n", 330 ++count, m68k_exception_name(cs->exception_index), 331 vector, env->pc, env->aregs[7], sr); 332 } 333 334 /* 335 * MC68040UM/AD, chapter 9.3.10 336 */ 337 338 /* "the processor first make an internal copy" */ 339 oldsr = sr; 340 /* "set the mode to supervisor" */ 341 sr |= SR_S; 342 /* "suppress tracing" */ 343 sr &= ~SR_T; 344 /* "sets the processor interrupt mask" */ 345 if (is_hw) { 346 sr |= (env->sr & ~SR_I) | (env->pending_level << SR_I_SHIFT); 347 } 348 cpu_m68k_set_sr(env, sr); 349 sp = env->aregs[7]; 350 351 sp &= ~1; 352 if (cs->exception_index == EXCP_ACCESS) { 353 if (env->mmu.fault) { 354 cpu_abort(cs, "DOUBLE MMU FAULT\n"); 355 } 356 env->mmu.fault = true; 357 /* push data 3 */ 358 sp -= 4; 359 cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0); 360 /* push data 2 */ 361 sp -= 4; 362 cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0); 363 /* push data 1 */ 364 sp -= 4; 365 cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0); 366 /* write back 1 / push data 0 */ 367 sp -= 4; 368 cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0); 369 /* write back 1 address */ 370 sp -= 4; 371 cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0); 372 /* write back 2 data */ 373 sp -= 4; 374 cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0); 375 /* write back 2 address */ 376 sp -= 4; 377 cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0); 378 /* write back 3 data */ 379 sp -= 4; 380 cpu_stl_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0); 381 /* write back 3 address */ 382 sp -= 4; 383 cpu_stl_mmuidx_ra(env, sp, env->mmu.ar, MMU_KERNEL_IDX, 0); 384 /* fault address */ 385 sp -= 4; 386 cpu_stl_mmuidx_ra(env, sp, env->mmu.ar, MMU_KERNEL_IDX, 0); 387 /* write back 1 status */ 388 sp -= 2; 389 cpu_stw_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0); 390 /* write back 2 status */ 391 sp -= 2; 392 cpu_stw_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0); 393 /* write back 3 status */ 394 sp -= 2; 395 cpu_stw_mmuidx_ra(env, sp, 0, MMU_KERNEL_IDX, 0); 396 /* special status word */ 397 sp -= 2; 398 cpu_stw_mmuidx_ra(env, sp, env->mmu.ssw, MMU_KERNEL_IDX, 0); 399 /* effective address */ 400 sp -= 4; 401 cpu_stl_mmuidx_ra(env, sp, env->mmu.ar, MMU_KERNEL_IDX, 0); 402 403 do_stack_frame(env, &sp, 7, oldsr, 0, retaddr); 404 env->mmu.fault = false; 405 if (qemu_loglevel_mask(CPU_LOG_INT)) { 406 qemu_log(" " 407 "ssw: %08x ea: %08x sfc: %d dfc: %d\n", 408 env->mmu.ssw, env->mmu.ar, env->sfc, env->dfc); 409 } 410 } else if (cs->exception_index == EXCP_ADDRESS) { 411 do_stack_frame(env, &sp, 2, oldsr, 0, retaddr); 412 } else if (cs->exception_index == EXCP_ILLEGAL || 413 cs->exception_index == EXCP_DIV0 || 414 cs->exception_index == EXCP_CHK || 415 cs->exception_index == EXCP_TRAPCC || 416 cs->exception_index == EXCP_TRACE) { 417 /* FIXME: addr is not only env->pc */ 418 do_stack_frame(env, &sp, 2, oldsr, env->pc, retaddr); 419 } else if (is_hw && oldsr & SR_M && 420 cs->exception_index >= EXCP_SPURIOUS && 421 cs->exception_index <= EXCP_INT_LEVEL_7) { 422 do_stack_frame(env, &sp, 0, oldsr, 0, retaddr); 423 oldsr = sr; 424 env->aregs[7] = sp; 425 cpu_m68k_set_sr(env, sr &= ~SR_M); 426 sp = env->aregs[7] & ~1; 427 do_stack_frame(env, &sp, 1, oldsr, 0, retaddr); 428 } else { 429 do_stack_frame(env, &sp, 0, oldsr, 0, retaddr); 430 } 431 432 env->aregs[7] = sp; 433 /* Jump to vector. */ 434 env->pc = cpu_ldl_mmuidx_ra(env, env->vbr + vector, MMU_KERNEL_IDX, 0); 435 } 436 437 static void do_interrupt_all(CPUM68KState *env, int is_hw) 438 { 439 if (m68k_feature(env, M68K_FEATURE_M68000)) { 440 m68k_interrupt_all(env, is_hw); 441 return; 442 } 443 cf_interrupt_all(env, is_hw); 444 } 445 446 void m68k_cpu_do_interrupt(CPUState *cs) 447 { 448 M68kCPU *cpu = M68K_CPU(cs); 449 CPUM68KState *env = &cpu->env; 450 451 do_interrupt_all(env, 0); 452 } 453 454 static inline void do_interrupt_m68k_hardirq(CPUM68KState *env) 455 { 456 do_interrupt_all(env, 1); 457 } 458 459 void m68k_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, 460 unsigned size, MMUAccessType access_type, 461 int mmu_idx, MemTxAttrs attrs, 462 MemTxResult response, uintptr_t retaddr) 463 { 464 M68kCPU *cpu = M68K_CPU(cs); 465 CPUM68KState *env = &cpu->env; 466 467 cpu_restore_state(cs, retaddr, true); 468 469 if (m68k_feature(env, M68K_FEATURE_M68040)) { 470 env->mmu.mmusr = 0; 471 env->mmu.ssw |= M68K_ATC_040; 472 /* FIXME: manage MMU table access error */ 473 env->mmu.ssw &= ~M68K_TM_040; 474 if (env->sr & SR_S) { /* SUPERVISOR */ 475 env->mmu.ssw |= M68K_TM_040_SUPER; 476 } 477 if (access_type == MMU_INST_FETCH) { /* instruction or data */ 478 env->mmu.ssw |= M68K_TM_040_CODE; 479 } else { 480 env->mmu.ssw |= M68K_TM_040_DATA; 481 } 482 env->mmu.ssw &= ~M68K_BA_SIZE_MASK; 483 switch (size) { 484 case 1: 485 env->mmu.ssw |= M68K_BA_SIZE_BYTE; 486 break; 487 case 2: 488 env->mmu.ssw |= M68K_BA_SIZE_WORD; 489 break; 490 case 4: 491 env->mmu.ssw |= M68K_BA_SIZE_LONG; 492 break; 493 } 494 495 if (access_type != MMU_DATA_STORE) { 496 env->mmu.ssw |= M68K_RW_040; 497 } 498 499 env->mmu.ar = addr; 500 501 cs->exception_index = EXCP_ACCESS; 502 cpu_loop_exit(cs); 503 } 504 } 505 #endif 506 507 bool m68k_cpu_exec_interrupt(CPUState *cs, int interrupt_request) 508 { 509 M68kCPU *cpu = M68K_CPU(cs); 510 CPUM68KState *env = &cpu->env; 511 512 if (interrupt_request & CPU_INTERRUPT_HARD 513 && ((env->sr & SR_I) >> SR_I_SHIFT) < env->pending_level) { 514 /* 515 * Real hardware gets the interrupt vector via an IACK cycle 516 * at this point. Current emulated hardware doesn't rely on 517 * this, so we provide/save the vector when the interrupt is 518 * first signalled. 519 */ 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 = env_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 /* 560 * real 68040 keeps N and unset Z on overflow, 561 * whereas documentation says "undefined" 562 */ 563 env->cc_z = 1; 564 return; 565 } 566 env->dregs[destr] = deposit32(quot, 16, 16, rem); 567 env->cc_z = (int16_t)quot; 568 env->cc_n = (int16_t)quot; 569 env->cc_v = 0; 570 } 571 572 void HELPER(divsw)(CPUM68KState *env, int destr, int32_t den) 573 { 574 int32_t num = env->dregs[destr]; 575 uint32_t quot, rem; 576 577 if (den == 0) { 578 raise_exception_ra(env, EXCP_DIV0, GETPC()); 579 } 580 quot = num / den; 581 rem = num % den; 582 583 env->cc_c = 0; /* always cleared, even if overflow */ 584 if (quot != (int16_t)quot) { 585 env->cc_v = -1; 586 /* nothing else is modified */ 587 /* 588 * real 68040 keeps N and unset Z on overflow, 589 * whereas documentation says "undefined" 590 */ 591 env->cc_z = 1; 592 return; 593 } 594 env->dregs[destr] = deposit32(quot, 16, 16, rem); 595 env->cc_z = (int16_t)quot; 596 env->cc_n = (int16_t)quot; 597 env->cc_v = 0; 598 } 599 600 void HELPER(divul)(CPUM68KState *env, int numr, int regr, uint32_t den) 601 { 602 uint32_t num = env->dregs[numr]; 603 uint32_t quot, rem; 604 605 if (den == 0) { 606 raise_exception_ra(env, EXCP_DIV0, GETPC()); 607 } 608 quot = num / den; 609 rem = num % den; 610 611 env->cc_c = 0; 612 env->cc_z = quot; 613 env->cc_n = quot; 614 env->cc_v = 0; 615 616 if (m68k_feature(env, M68K_FEATURE_CF_ISA_A)) { 617 if (numr == regr) { 618 env->dregs[numr] = quot; 619 } else { 620 env->dregs[regr] = rem; 621 } 622 } else { 623 env->dregs[regr] = rem; 624 env->dregs[numr] = quot; 625 } 626 } 627 628 void HELPER(divsl)(CPUM68KState *env, int numr, int regr, int32_t den) 629 { 630 int32_t num = env->dregs[numr]; 631 int32_t quot, rem; 632 633 if (den == 0) { 634 raise_exception_ra(env, EXCP_DIV0, GETPC()); 635 } 636 quot = num / den; 637 rem = num % den; 638 639 env->cc_c = 0; 640 env->cc_z = quot; 641 env->cc_n = quot; 642 env->cc_v = 0; 643 644 if (m68k_feature(env, M68K_FEATURE_CF_ISA_A)) { 645 if (numr == regr) { 646 env->dregs[numr] = quot; 647 } else { 648 env->dregs[regr] = rem; 649 } 650 } else { 651 env->dregs[regr] = rem; 652 env->dregs[numr] = quot; 653 } 654 } 655 656 void HELPER(divull)(CPUM68KState *env, int numr, int regr, uint32_t den) 657 { 658 uint64_t num = deposit64(env->dregs[numr], 32, 32, env->dregs[regr]); 659 uint64_t quot; 660 uint32_t rem; 661 662 if (den == 0) { 663 raise_exception_ra(env, EXCP_DIV0, GETPC()); 664 } 665 quot = num / den; 666 rem = num % den; 667 668 env->cc_c = 0; /* always cleared, even if overflow */ 669 if (quot > 0xffffffffULL) { 670 env->cc_v = -1; 671 /* 672 * real 68040 keeps N and unset Z on overflow, 673 * whereas documentation says "undefined" 674 */ 675 env->cc_z = 1; 676 return; 677 } 678 env->cc_z = quot; 679 env->cc_n = quot; 680 env->cc_v = 0; 681 682 /* 683 * If Dq and Dr are the same, the quotient is returned. 684 * therefore we set Dq last. 685 */ 686 687 env->dregs[regr] = rem; 688 env->dregs[numr] = quot; 689 } 690 691 void HELPER(divsll)(CPUM68KState *env, int numr, int regr, int32_t den) 692 { 693 int64_t num = deposit64(env->dregs[numr], 32, 32, env->dregs[regr]); 694 int64_t quot; 695 int32_t rem; 696 697 if (den == 0) { 698 raise_exception_ra(env, EXCP_DIV0, GETPC()); 699 } 700 quot = num / den; 701 rem = num % den; 702 703 env->cc_c = 0; /* always cleared, even if overflow */ 704 if (quot != (int32_t)quot) { 705 env->cc_v = -1; 706 /* 707 * real 68040 keeps N and unset Z on overflow, 708 * whereas documentation says "undefined" 709 */ 710 env->cc_z = 1; 711 return; 712 } 713 env->cc_z = quot; 714 env->cc_n = quot; 715 env->cc_v = 0; 716 717 /* 718 * If Dq and Dr are the same, the quotient is returned. 719 * therefore we set Dq last. 720 */ 721 722 env->dregs[regr] = rem; 723 env->dregs[numr] = quot; 724 } 725 726 /* We're executing in a serial context -- no need to be atomic. */ 727 void HELPER(cas2w)(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2) 728 { 729 uint32_t Dc1 = extract32(regs, 9, 3); 730 uint32_t Dc2 = extract32(regs, 6, 3); 731 uint32_t Du1 = extract32(regs, 3, 3); 732 uint32_t Du2 = extract32(regs, 0, 3); 733 int16_t c1 = env->dregs[Dc1]; 734 int16_t c2 = env->dregs[Dc2]; 735 int16_t u1 = env->dregs[Du1]; 736 int16_t u2 = env->dregs[Du2]; 737 int16_t l1, l2; 738 uintptr_t ra = GETPC(); 739 740 l1 = cpu_lduw_data_ra(env, a1, ra); 741 l2 = cpu_lduw_data_ra(env, a2, ra); 742 if (l1 == c1 && l2 == c2) { 743 cpu_stw_data_ra(env, a1, u1, ra); 744 cpu_stw_data_ra(env, a2, u2, ra); 745 } 746 747 if (c1 != l1) { 748 env->cc_n = l1; 749 env->cc_v = c1; 750 } else { 751 env->cc_n = l2; 752 env->cc_v = c2; 753 } 754 env->cc_op = CC_OP_CMPW; 755 env->dregs[Dc1] = deposit32(env->dregs[Dc1], 0, 16, l1); 756 env->dregs[Dc2] = deposit32(env->dregs[Dc2], 0, 16, l2); 757 } 758 759 static void do_cas2l(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2, 760 bool parallel) 761 { 762 uint32_t Dc1 = extract32(regs, 9, 3); 763 uint32_t Dc2 = extract32(regs, 6, 3); 764 uint32_t Du1 = extract32(regs, 3, 3); 765 uint32_t Du2 = extract32(regs, 0, 3); 766 uint32_t c1 = env->dregs[Dc1]; 767 uint32_t c2 = env->dregs[Dc2]; 768 uint32_t u1 = env->dregs[Du1]; 769 uint32_t u2 = env->dregs[Du2]; 770 uint32_t l1, l2; 771 uintptr_t ra = GETPC(); 772 #if defined(CONFIG_ATOMIC64) && !defined(CONFIG_USER_ONLY) 773 int mmu_idx = cpu_mmu_index(env, 0); 774 TCGMemOpIdx oi; 775 #endif 776 777 if (parallel) { 778 /* We're executing in a parallel context -- must be atomic. */ 779 #ifdef CONFIG_ATOMIC64 780 uint64_t c, u, l; 781 if ((a1 & 7) == 0 && a2 == a1 + 4) { 782 c = deposit64(c2, 32, 32, c1); 783 u = deposit64(u2, 32, 32, u1); 784 #ifdef CONFIG_USER_ONLY 785 l = helper_atomic_cmpxchgq_be(env, a1, c, u); 786 #else 787 oi = make_memop_idx(MO_BEQ, mmu_idx); 788 l = helper_atomic_cmpxchgq_be_mmu(env, a1, c, u, oi, ra); 789 #endif 790 l1 = l >> 32; 791 l2 = l; 792 } else if ((a2 & 7) == 0 && a1 == a2 + 4) { 793 c = deposit64(c1, 32, 32, c2); 794 u = deposit64(u1, 32, 32, u2); 795 #ifdef CONFIG_USER_ONLY 796 l = helper_atomic_cmpxchgq_be(env, a2, c, u); 797 #else 798 oi = make_memop_idx(MO_BEQ, mmu_idx); 799 l = helper_atomic_cmpxchgq_be_mmu(env, a2, c, u, oi, ra); 800 #endif 801 l2 = l >> 32; 802 l1 = l; 803 } else 804 #endif 805 { 806 /* Tell the main loop we need to serialize this insn. */ 807 cpu_loop_exit_atomic(env_cpu(env), ra); 808 } 809 } else { 810 /* We're executing in a serial context -- no need to be atomic. */ 811 l1 = cpu_ldl_data_ra(env, a1, ra); 812 l2 = cpu_ldl_data_ra(env, a2, ra); 813 if (l1 == c1 && l2 == c2) { 814 cpu_stl_data_ra(env, a1, u1, ra); 815 cpu_stl_data_ra(env, a2, u2, ra); 816 } 817 } 818 819 if (c1 != l1) { 820 env->cc_n = l1; 821 env->cc_v = c1; 822 } else { 823 env->cc_n = l2; 824 env->cc_v = c2; 825 } 826 env->cc_op = CC_OP_CMPL; 827 env->dregs[Dc1] = l1; 828 env->dregs[Dc2] = l2; 829 } 830 831 void HELPER(cas2l)(CPUM68KState *env, uint32_t regs, uint32_t a1, uint32_t a2) 832 { 833 do_cas2l(env, regs, a1, a2, false); 834 } 835 836 void HELPER(cas2l_parallel)(CPUM68KState *env, uint32_t regs, uint32_t a1, 837 uint32_t a2) 838 { 839 do_cas2l(env, regs, a1, a2, true); 840 } 841 842 struct bf_data { 843 uint32_t addr; 844 uint32_t bofs; 845 uint32_t blen; 846 uint32_t len; 847 }; 848 849 static struct bf_data bf_prep(uint32_t addr, int32_t ofs, uint32_t len) 850 { 851 int bofs, blen; 852 853 /* Bound length; map 0 to 32. */ 854 len = ((len - 1) & 31) + 1; 855 856 /* Note that ofs is signed. */ 857 addr += ofs / 8; 858 bofs = ofs % 8; 859 if (bofs < 0) { 860 bofs += 8; 861 addr -= 1; 862 } 863 864 /* 865 * Compute the number of bytes required (minus one) to 866 * satisfy the bitfield. 867 */ 868 blen = (bofs + len - 1) / 8; 869 870 /* 871 * Canonicalize the bit offset for data loaded into a 64-bit big-endian 872 * word. For the cases where BLEN is not a power of 2, adjust ADDR so 873 * that we can use the next power of two sized load without crossing a 874 * page boundary, unless the field itself crosses the boundary. 875 */ 876 switch (blen) { 877 case 0: 878 bofs += 56; 879 break; 880 case 1: 881 bofs += 48; 882 break; 883 case 2: 884 if (addr & 1) { 885 bofs += 8; 886 addr -= 1; 887 } 888 /* fallthru */ 889 case 3: 890 bofs += 32; 891 break; 892 case 4: 893 if (addr & 3) { 894 bofs += 8 * (addr & 3); 895 addr &= -4; 896 } 897 break; 898 default: 899 g_assert_not_reached(); 900 } 901 902 return (struct bf_data){ 903 .addr = addr, 904 .bofs = bofs, 905 .blen = blen, 906 .len = len, 907 }; 908 } 909 910 static uint64_t bf_load(CPUM68KState *env, uint32_t addr, int blen, 911 uintptr_t ra) 912 { 913 switch (blen) { 914 case 0: 915 return cpu_ldub_data_ra(env, addr, ra); 916 case 1: 917 return cpu_lduw_data_ra(env, addr, ra); 918 case 2: 919 case 3: 920 return cpu_ldl_data_ra(env, addr, ra); 921 case 4: 922 return cpu_ldq_data_ra(env, addr, ra); 923 default: 924 g_assert_not_reached(); 925 } 926 } 927 928 static void bf_store(CPUM68KState *env, uint32_t addr, int blen, 929 uint64_t data, uintptr_t ra) 930 { 931 switch (blen) { 932 case 0: 933 cpu_stb_data_ra(env, addr, data, ra); 934 break; 935 case 1: 936 cpu_stw_data_ra(env, addr, data, ra); 937 break; 938 case 2: 939 case 3: 940 cpu_stl_data_ra(env, addr, data, ra); 941 break; 942 case 4: 943 cpu_stq_data_ra(env, addr, data, ra); 944 break; 945 default: 946 g_assert_not_reached(); 947 } 948 } 949 950 uint32_t HELPER(bfexts_mem)(CPUM68KState *env, uint32_t addr, 951 int32_t ofs, uint32_t len) 952 { 953 uintptr_t ra = GETPC(); 954 struct bf_data d = bf_prep(addr, ofs, len); 955 uint64_t data = bf_load(env, d.addr, d.blen, ra); 956 957 return (int64_t)(data << d.bofs) >> (64 - d.len); 958 } 959 960 uint64_t HELPER(bfextu_mem)(CPUM68KState *env, uint32_t addr, 961 int32_t ofs, uint32_t len) 962 { 963 uintptr_t ra = GETPC(); 964 struct bf_data d = bf_prep(addr, ofs, len); 965 uint64_t data = bf_load(env, d.addr, d.blen, ra); 966 967 /* 968 * Put CC_N at the top of the high word; put the zero-extended value 969 * at the bottom of the low word. 970 */ 971 data <<= d.bofs; 972 data >>= 64 - d.len; 973 data |= data << (64 - d.len); 974 975 return data; 976 } 977 978 uint32_t HELPER(bfins_mem)(CPUM68KState *env, uint32_t addr, uint32_t val, 979 int32_t ofs, uint32_t len) 980 { 981 uintptr_t ra = GETPC(); 982 struct bf_data d = bf_prep(addr, ofs, len); 983 uint64_t data = bf_load(env, d.addr, d.blen, ra); 984 uint64_t mask = -1ull << (64 - d.len) >> d.bofs; 985 986 data = (data & ~mask) | (((uint64_t)val << (64 - d.len)) >> d.bofs); 987 988 bf_store(env, d.addr, d.blen, data, ra); 989 990 /* The field at the top of the word is also CC_N for CC_OP_LOGIC. */ 991 return val << (32 - d.len); 992 } 993 994 uint32_t HELPER(bfchg_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(bfclr_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(bfset_mem)(CPUM68KState *env, uint32_t addr, 1021 int32_t ofs, uint32_t len) 1022 { 1023 uintptr_t ra = GETPC(); 1024 struct bf_data d = bf_prep(addr, ofs, len); 1025 uint64_t data = bf_load(env, d.addr, d.blen, ra); 1026 uint64_t mask = -1ull << (64 - d.len) >> d.bofs; 1027 1028 bf_store(env, d.addr, d.blen, data | mask, ra); 1029 1030 return ((data & mask) << d.bofs) >> 32; 1031 } 1032 1033 uint32_t HELPER(bfffo_reg)(uint32_t n, uint32_t ofs, uint32_t len) 1034 { 1035 return (n ? clz32(n) : len) + ofs; 1036 } 1037 1038 uint64_t HELPER(bfffo_mem)(CPUM68KState *env, uint32_t addr, 1039 int32_t ofs, uint32_t len) 1040 { 1041 uintptr_t ra = GETPC(); 1042 struct bf_data d = bf_prep(addr, ofs, len); 1043 uint64_t data = bf_load(env, d.addr, d.blen, ra); 1044 uint64_t mask = -1ull << (64 - d.len) >> d.bofs; 1045 uint64_t n = (data & mask) << d.bofs; 1046 uint32_t ffo = helper_bfffo_reg(n >> 32, ofs, d.len); 1047 1048 /* 1049 * Return FFO in the low word and N in the high word. 1050 * Note that because of MASK and the shift, the low word 1051 * is already zero. 1052 */ 1053 return n | ffo; 1054 } 1055 1056 void HELPER(chk)(CPUM68KState *env, int32_t val, int32_t ub) 1057 { 1058 /* 1059 * From the specs: 1060 * X: Not affected, C,V,Z: Undefined, 1061 * N: Set if val < 0; cleared if val > ub, undefined otherwise 1062 * We implement here values found from a real MC68040: 1063 * X,V,Z: Not affected 1064 * N: Set if val < 0; cleared if val >= 0 1065 * C: if 0 <= ub: set if val < 0 or val > ub, cleared otherwise 1066 * if 0 > ub: set if val > ub and val < 0, cleared otherwise 1067 */ 1068 env->cc_n = val; 1069 env->cc_c = 0 <= ub ? val < 0 || val > ub : val > ub && val < 0; 1070 1071 if (val < 0 || val > ub) { 1072 CPUState *cs = env_cpu(env); 1073 1074 /* Recover PC and CC_OP for the beginning of the insn. */ 1075 cpu_restore_state(cs, GETPC(), true); 1076 1077 /* flags have been modified by gen_flush_flags() */ 1078 env->cc_op = CC_OP_FLAGS; 1079 /* Adjust PC to end of the insn. */ 1080 env->pc += 2; 1081 1082 cs->exception_index = EXCP_CHK; 1083 cpu_loop_exit(cs); 1084 } 1085 } 1086 1087 void HELPER(chk2)(CPUM68KState *env, int32_t val, int32_t lb, int32_t ub) 1088 { 1089 /* 1090 * From the specs: 1091 * X: Not affected, N,V: Undefined, 1092 * Z: Set if val is equal to lb or ub 1093 * C: Set if val < lb or val > ub, cleared otherwise 1094 * We implement here values found from a real MC68040: 1095 * X,N,V: Not affected 1096 * Z: Set if val is equal to lb or ub 1097 * C: if lb <= ub: set if val < lb or val > ub, cleared otherwise 1098 * if lb > ub: set if val > ub and val < lb, cleared otherwise 1099 */ 1100 env->cc_z = val != lb && val != ub; 1101 env->cc_c = lb <= ub ? val < lb || val > ub : val > ub && val < lb; 1102 1103 if (env->cc_c) { 1104 CPUState *cs = env_cpu(env); 1105 1106 /* Recover PC and CC_OP for the beginning of the insn. */ 1107 cpu_restore_state(cs, GETPC(), true); 1108 1109 /* flags have been modified by gen_flush_flags() */ 1110 env->cc_op = CC_OP_FLAGS; 1111 /* Adjust PC to end of the insn. */ 1112 env->pc += 4; 1113 1114 cs->exception_index = EXCP_CHK; 1115 cpu_loop_exit(cs); 1116 } 1117 } 1118