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