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