summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--linux-user/main.c7
-rw-r--r--linux-user/signal.c7
-rw-r--r--target/m68k/cpu.c20
-rw-r--r--target/m68k/cpu.h84
-rw-r--r--target/m68k/gdbstub.c2
-rw-r--r--target/m68k/helper.c99
-rw-r--r--target/m68k/helper.h11
-rw-r--r--target/m68k/monitor.c1
-rw-r--r--target/m68k/op_helper.c338
-rw-r--r--target/m68k/translate.c497
10 files changed, 937 insertions, 129 deletions
diff --git a/linux-user/main.c b/linux-user/main.c
index 71696ed33d..99a551b04f 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -2985,6 +2985,13 @@ void cpu_loop(CPUM68KState *env)
             info._sifields._sigfault._addr = env->pc;
             queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
             break;
+        case EXCP_CHK:
+            info.si_signo = TARGET_SIGFPE;
+            info.si_errno = 0;
+            info.si_code = TARGET_FPE_INTOVF;
+            info._sifields._sigfault._addr = env->pc;
+            queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
+            break;
         case EXCP_DIV0:
             info.si_signo = TARGET_SIGFPE;
             info.si_errno = 0;
diff --git a/linux-user/signal.c b/linux-user/signal.c
index dae14d4a89..74fa03f96d 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -5612,13 +5612,14 @@ struct target_rt_sigframe
 static void setup_sigcontext(struct target_sigcontext *sc, CPUM68KState *env,
                              abi_ulong mask)
 {
+    uint32_t sr = (env->sr & 0xff00) | cpu_m68k_get_ccr(env);
     __put_user(mask, &sc->sc_mask);
     __put_user(env->aregs[7], &sc->sc_usp);
     __put_user(env->dregs[0], &sc->sc_d0);
     __put_user(env->dregs[1], &sc->sc_d1);
     __put_user(env->aregs[0], &sc->sc_a0);
     __put_user(env->aregs[1], &sc->sc_a1);
-    __put_user(env->sr, &sc->sc_sr);
+    __put_user(sr, &sc->sc_sr);
     __put_user(env->pc, &sc->sc_pc);
 }
 
@@ -5634,7 +5635,7 @@ restore_sigcontext(CPUM68KState *env, struct target_sigcontext *sc)
     __get_user(env->aregs[1], &sc->sc_a1);
     __get_user(env->pc, &sc->sc_pc);
     __get_user(temp, &sc->sc_sr);
-    env->sr = (env->sr & 0xff00) | (temp & 0xff);
+    cpu_m68k_set_ccr(env, temp);
 }
 
 /*
@@ -5726,7 +5727,7 @@ static inline int target_rt_setup_ucontext(struct target_ucontext *uc,
                                            CPUM68KState *env)
 {
     target_greg_t *gregs = uc->tuc_mcontext.gregs;
-    uint32_t sr = cpu_m68k_get_ccr(env);
+    uint32_t sr = (env->sr & 0xff00) | cpu_m68k_get_ccr(env);
 
     __put_user(TARGET_MCONTEXT_VERSION, &uc->tuc_mcontext.version);
     __put_user(env->dregs[0], &gregs[0]);
diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c
index 0a3dd83548..03126ba543 100644
--- a/target/m68k/cpu.c
+++ b/target/m68k/cpu.c
@@ -55,17 +55,17 @@ static void m68k_cpu_reset(CPUState *s)
     mcc->parent_reset(s);
 
     memset(env, 0, offsetof(CPUM68KState, end_reset_fields));
-#if !defined(CONFIG_USER_ONLY)
-    env->sr = 0x2700;
+#ifdef CONFIG_SOFTMMU
+    cpu_m68k_set_sr(env, SR_S | SR_I);
+#else
+    cpu_m68k_set_sr(env, 0);
 #endif
-    m68k_switch_sp(env);
     for (i = 0; i < 8; i++) {
         env->fregs[i].d = nan;
     }
     cpu_m68k_set_fpcr(env, 0);
     env->fpsr = 0;
 
-    cpu_m68k_set_ccr(env, 0);
     /* TODO: We should set PC from the interrupt vector.  */
     env->pc = 0;
 }
@@ -134,9 +134,18 @@ static void m68020_cpu_initfn(Object *obj)
     m68k_set_feature(env, M68K_FEATURE_CAS);
     m68k_set_feature(env, M68K_FEATURE_BKPT);
     m68k_set_feature(env, M68K_FEATURE_RTD);
+    m68k_set_feature(env, M68K_FEATURE_CHK2);
 }
 #define m68030_cpu_initfn m68020_cpu_initfn
-#define m68040_cpu_initfn m68020_cpu_initfn
+
+static void m68040_cpu_initfn(Object *obj)
+{
+    M68kCPU *cpu = M68K_CPU(obj);
+    CPUM68KState *env = &cpu->env;
+
+    m68020_cpu_initfn(obj);
+    m68k_set_feature(env, M68K_FEATURE_M68040);
+}
 
 static void m68060_cpu_initfn(Object *obj)
 {
@@ -156,6 +165,7 @@ static void m68060_cpu_initfn(Object *obj)
     m68k_set_feature(env, M68K_FEATURE_CAS);
     m68k_set_feature(env, M68K_FEATURE_BKPT);
     m68k_set_feature(env, M68K_FEATURE_RTD);
+    m68k_set_feature(env, M68K_FEATURE_CHK2);
 }
 
 static void m5208_cpu_initfn(Object *obj)
diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h
index 5d03764eab..2985b039e1 100644
--- a/target/m68k/cpu.h
+++ b/target/m68k/cpu.h
@@ -45,6 +45,8 @@
 #define EXCP_ADDRESS        3   /* Address error.  */
 #define EXCP_ILLEGAL        4   /* Illegal instruction.  */
 #define EXCP_DIV0           5   /* Divide by zero */
+#define EXCP_CHK            6   /* CHK, CHK2 Instructions */
+#define EXCP_TRAPCC         7   /* FTRAPcc, TRAPcc, TRAPV Instructions */
 #define EXCP_PRIVILEGE      8   /* Privilege violation.  */
 #define EXCP_TRACE          9
 #define EXCP_LINEA          10  /* Unimplemented line-A (MAC) opcode.  */
@@ -53,6 +55,9 @@
 #define EXCP_DEBEGBP        13  /* Breakpoint debug interrupt.  */
 #define EXCP_FORMAT         14  /* RTE format error.  */
 #define EXCP_UNINITIALIZED  15
+#define EXCP_SPURIOUS       24  /* Spurious interrupt */
+#define EXCP_INT_LEVEL_1    25  /* Level 1 Interrupt autovector */
+#define EXCP_INT_LEVEL_7    31  /* Level 7 Interrupt autovector */
 #define EXCP_TRAP0          32   /* User trap #0.  */
 #define EXCP_TRAP15         47   /* User trap #15.  */
 #define EXCP_FP_BSUN        48 /* Branch Set on Unordered */
@@ -63,6 +68,9 @@
 #define EXCP_FP_OVFL        53 /* Overflow */
 #define EXCP_FP_SNAN        54 /* Signaling Not-A-Number */
 #define EXCP_FP_UNIMP       55 /* Unimplemented Data type */
+#define EXCP_MMU_CONF       56  /* MMU Configuration Error */
+#define EXCP_MMU_ILLEGAL    57  /* MMU Illegal Operation Error */
+#define EXCP_MMU_ACCESS     58  /* MMU Access Level Violation Error */
 #define EXCP_UNSUPPORTED    61
 
 #define EXCP_RTE            0x100
@@ -81,7 +89,7 @@ typedef struct CPUM68KState {
 
     /* SSP and USP.  The current_sp is stored in aregs[7], the other here.  */
     int current_sp;
-    uint32_t sp[2];
+    uint32_t sp[3];
 
     /* Condition flags.  */
     uint32_t cc_op;
@@ -170,6 +178,7 @@ int cpu_m68k_signal_handler(int host_signum, void *pinfo,
                            void *puc);
 uint32_t cpu_m68k_get_ccr(CPUM68KState *env);
 void cpu_m68k_set_ccr(CPUM68KState *env, uint32_t);
+void cpu_m68k_set_sr(CPUM68KState *env, uint32_t);
 void cpu_m68k_set_fpcr(CPUM68KState *env, uint32_t val);
 
 
@@ -210,10 +219,79 @@ typedef enum {
 #define SR_I  0x0700
 #define SR_M  0x1000
 #define SR_S  0x2000
-#define SR_T  0x8000
+#define SR_T_SHIFT 14
+#define SR_T  0xc000
 
 #define M68K_SSP    0
 #define M68K_USP    1
+#define M68K_ISP    2
+
+/* m68k Control Registers */
+
+/* ColdFire */
+/* Memory Management Control Registers */
+#define M68K_CR_ASID     0x003
+#define M68K_CR_ACR0     0x004
+#define M68K_CR_ACR1     0x005
+#define M68K_CR_ACR2     0x006
+#define M68K_CR_ACR3     0x007
+#define M68K_CR_MMUBAR   0x008
+
+/* Processor Miscellaneous Registers */
+#define M68K_CR_PC       0x80F
+
+/* Local Memory and Module Control Registers */
+#define M68K_CR_ROMBAR0  0xC00
+#define M68K_CR_ROMBAR1  0xC01
+#define M68K_CR_RAMBAR0  0xC04
+#define M68K_CR_RAMBAR1  0xC05
+#define M68K_CR_MPCR     0xC0C
+#define M68K_CR_EDRAMBAR 0xC0D
+#define M68K_CR_SECMBAR  0xC0E
+#define M68K_CR_MBAR     0xC0F
+
+/* Local Memory Address Permutation Control Registers */
+#define M68K_CR_PCR1U0   0xD02
+#define M68K_CR_PCR1L0   0xD03
+#define M68K_CR_PCR2U0   0xD04
+#define M68K_CR_PCR2L0   0xD05
+#define M68K_CR_PCR3U0   0xD06
+#define M68K_CR_PCR3L0   0xD07
+#define M68K_CR_PCR1U1   0xD0A
+#define M68K_CR_PCR1L1   0xD0B
+#define M68K_CR_PCR2U1   0xD0C
+#define M68K_CR_PCR2L1   0xD0D
+#define M68K_CR_PCR3U1   0xD0E
+#define M68K_CR_PCR3L1   0xD0F
+
+/* MC680x0 */
+/* MC680[1234]0/CPU32 */
+#define M68K_CR_SFC      0x000
+#define M68K_CR_DFC      0x001
+#define M68K_CR_USP      0x800
+#define M68K_CR_VBR      0x801 /* + Coldfire */
+
+/* MC680[234]0 */
+#define M68K_CR_CACR     0x002 /* + Coldfire */
+#define M68K_CR_CAAR     0x802 /* MC68020 and MC68030 only */
+#define M68K_CR_MSP      0x803
+#define M68K_CR_ISP      0x804
+
+/* MC68040/MC68LC040 */
+#define M68K_CR_TC       0x003
+#define M68K_CR_ITT0     0x004
+#define M68K_CR_ITT1     0x005
+#define M68K_CR_DTT0     0x006
+#define M68K_CR_DTT1     0x007
+#define M68K_CR_MMUSR    0x805
+#define M68K_CR_URP      0x806
+#define M68K_CR_SRP      0x807
+
+/* MC68EC040 */
+#define M68K_CR_IACR0    0x004
+#define M68K_CR_IACR1    0x005
+#define M68K_CR_DACR0    0x006
+#define M68K_CR_DACR1    0x007
 
 #define M68K_FPIAR_SHIFT  0
 #define M68K_FPIAR        (1 << M68K_FPIAR_SHIFT)
@@ -296,6 +374,8 @@ enum m68k_features {
     M68K_FEATURE_CAS,
     M68K_FEATURE_BKPT,
     M68K_FEATURE_RTD,
+    M68K_FEATURE_CHK2,
+    M68K_FEATURE_M68040, /* instructions specific to MC68040 */
 };
 
 static inline int m68k_feature(CPUM68KState *env, int feature)
diff --git a/target/m68k/gdbstub.c b/target/m68k/gdbstub.c
index c7f44c9bb3..99e5be8132 100644
--- a/target/m68k/gdbstub.c
+++ b/target/m68k/gdbstub.c
@@ -63,7 +63,7 @@ int m68k_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
     } else {
         switch (n) {
         case 16:
-            env->sr = tmp;
+            cpu_m68k_set_sr(env, tmp);
             break;
         case 17:
             env->pc = tmp;
diff --git a/target/m68k/helper.c b/target/m68k/helper.c
index 7e50ff5871..a999389e9a 100644
--- a/target/m68k/helper.c
+++ b/target/m68k/helper.c
@@ -171,28 +171,84 @@ void m68k_cpu_init_gdb(M68kCPU *cpu)
     /* TODO: Add [E]MAC registers.  */
 }
 
-void HELPER(movec)(CPUM68KState *env, uint32_t reg, uint32_t val)
+void HELPER(cf_movec_to)(CPUM68KState *env, uint32_t reg, uint32_t val)
 {
     M68kCPU *cpu = m68k_env_get_cpu(env);
 
     switch (reg) {
-    case 0x02: /* CACR */
+    case M68K_CR_CACR:
         env->cacr = val;
         m68k_switch_sp(env);
         break;
-    case 0x04: case 0x05: case 0x06: case 0x07: /* ACR[0-3] */
+    case M68K_CR_ACR0:
+    case M68K_CR_ACR1:
+    case M68K_CR_ACR2:
+    case M68K_CR_ACR3:
         /* TODO: Implement Access Control Registers.  */
         break;
-    case 0x801: /* VBR */
+    case M68K_CR_VBR:
         env->vbr = val;
         break;
     /* TODO: Implement control registers.  */
     default:
-        cpu_abort(CPU(cpu), "Unimplemented control register write 0x%x = 0x%x\n",
+        cpu_abort(CPU(cpu),
+                  "Unimplemented control register write 0x%x = 0x%x\n",
                   reg, val);
     }
 }
 
+void HELPER(m68k_movec_to)(CPUM68KState *env, uint32_t reg, uint32_t val)
+{
+    M68kCPU *cpu = m68k_env_get_cpu(env);
+
+    switch (reg) {
+    /* MC680[1234]0 */
+    case M68K_CR_VBR:
+        env->vbr = val;
+        return;
+    /* MC680[234]0 */
+    case M68K_CR_CACR:
+        env->cacr = val;
+        m68k_switch_sp(env);
+        return;
+    /* MC680[34]0 */
+    case M68K_CR_USP:
+        env->sp[M68K_USP] = val;
+        return;
+    case M68K_CR_MSP:
+        env->sp[M68K_SSP] = val;
+        return;
+    case M68K_CR_ISP:
+        env->sp[M68K_ISP] = val;
+        return;
+    }
+    cpu_abort(CPU(cpu), "Unimplemented control register write 0x%x = 0x%x\n",
+              reg, val);
+}
+
+uint32_t HELPER(m68k_movec_from)(CPUM68KState *env, uint32_t reg)
+{
+    M68kCPU *cpu = m68k_env_get_cpu(env);
+
+    switch (reg) {
+    /* MC680[1234]0 */
+    case M68K_CR_VBR:
+        return env->vbr;
+    /* MC680[234]0 */
+    case M68K_CR_CACR:
+        return env->cacr;
+    /* MC680[34]0 */
+    case M68K_CR_USP:
+        return env->sp[M68K_USP];
+    case M68K_CR_MSP:
+        return env->sp[M68K_SSP];
+    case M68K_CR_ISP:
+        return env->sp[M68K_ISP];
+    }
+    cpu_abort(CPU(cpu), "Unimplemented control register read 0x%x\n",
+              reg);
+}
+
 void HELPER(set_macsr)(CPUM68KState *env, uint32_t val)
 {
     uint32_t acc;
@@ -232,8 +288,20 @@ void m68k_switch_sp(CPUM68KState *env)
     int new_sp;
 
     env->sp[env->current_sp] = env->aregs[7];
-    new_sp = (env->sr & SR_S && env->cacr & M68K_CACR_EUSP)
-             ? M68K_SSP : M68K_USP;
+    if (m68k_feature(env, M68K_FEATURE_M68000)) {
+        if (env->sr & SR_S) {
+            if (env->sr & SR_M) {
+                new_sp = M68K_SSP;
+            } else {
+                new_sp = M68K_ISP;
+            }
+        } else {
+            new_sp = M68K_USP;
+        }
+    } else {
+        new_sp = (env->sr & SR_S && env->cacr & M68K_CACR_EUSP)
+                 ? M68K_SSP : M68K_USP;
+    }
     env->aregs[7] = env->sp[new_sp];
     env->current_sp = new_sp;
 }
@@ -316,13 +384,17 @@ uint32_t HELPER(sats)(uint32_t val, uint32_t v)
     return val;
 }
 
-void HELPER(set_sr)(CPUM68KState *env, uint32_t val)
+void cpu_m68k_set_sr(CPUM68KState *env, uint32_t sr)
 {
-    env->sr = val & 0xffe0;
-    cpu_m68k_set_ccr(env, val);
+    env->sr = sr & 0xffe0;
+    cpu_m68k_set_ccr(env, sr);
     m68k_switch_sp(env);
 }
 
+void HELPER(set_sr)(CPUM68KState *env, uint32_t val)
+{
+    cpu_m68k_set_sr(env, val);
+}
 
 /* MAC unit.  */
 /* FIXME: The MAC unit implementation is a bit of a mess.  Some helpers
@@ -707,3 +779,10 @@ void HELPER(set_mac_extu)(CPUM68KState *env, uint32_t val, uint32_t acc)
     res |= (uint64_t)(val & 0xffff0000) << 16;
     env->macc[acc + 1] = res;
 }
+
+#if defined(CONFIG_SOFTMMU)
+void HELPER(reset)(CPUM68KState *env)
+{
+    /* FIXME: reset all except CPU */
+}
+#endif
diff --git a/target/m68k/helper.h b/target/m68k/helper.h
index eebe52dae5..57f210aa14 100644
--- a/target/m68k/helper.h
+++ b/target/m68k/helper.h
@@ -8,7 +8,9 @@ DEF_HELPER_4(divsl, void, env, int, int, s32)
 DEF_HELPER_4(divull, void, env, int, int, i32)
 DEF_HELPER_4(divsll, void, env, int, int, s32)
 DEF_HELPER_2(set_sr, void, env, i32)
-DEF_HELPER_3(movec, void, env, i32, i32)
+DEF_HELPER_3(cf_movec_to, void, env, i32, i32)
+DEF_HELPER_3(m68k_movec_to, void, env, i32, i32)
+DEF_HELPER_2(m68k_movec_from, i32, env, i32)
 DEF_HELPER_4(cas2w, void, env, i32, i32, i32)
 DEF_HELPER_4(cas2l, void, env, i32, i32, i32)
 DEF_HELPER_4(cas2l_parallel, void, env, i32, i32, i32)
@@ -94,3 +96,10 @@ DEF_HELPER_FLAGS_4(bfchg_mem, TCG_CALL_NO_WG, i32, env, i32, s32, i32)
 DEF_HELPER_FLAGS_4(bfclr_mem, TCG_CALL_NO_WG, i32, env, i32, s32, i32)
 DEF_HELPER_FLAGS_4(bfset_mem, TCG_CALL_NO_WG, i32, env, i32, s32, i32)
 DEF_HELPER_FLAGS_4(bfffo_mem, TCG_CALL_NO_WG, i64, env, i32, s32, i32)
+
+DEF_HELPER_3(chk, void, env, s32, s32)
+DEF_HELPER_4(chk2, void, env, s32, s32, s32)
+
+#if defined(CONFIG_SOFTMMU)
+DEF_HELPER_FLAGS_1(reset, TCG_CALL_NO_RWG, void, env)
+#endif
diff --git a/target/m68k/monitor.c b/target/m68k/monitor.c
index 5605323a81..52781e85f0 100644
--- a/target/m68k/monitor.c
+++ b/target/m68k/monitor.c
@@ -30,6 +30,7 @@ static const MonitorDef monitor_defs[] = {
     { "sr", offsetof(CPUM68KState, sr) },
     { "ssp", offsetof(CPUM68KState, sp[0]) },
     { "usp", offsetof(CPUM68KState, sp[1]) },
+    { "isp", offsetof(CPUM68KState, sp[2]) },
     { NULL },
 };
 
diff --git a/target/m68k/op_helper.c b/target/m68k/op_helper.c
index 78bfb9f0cc..c61ca9392f 100644
--- a/target/m68k/op_helper.c
+++ b/target/m68k/op_helper.c
@@ -51,7 +51,7 @@ void tlb_fill(CPUState *cs, target_ulong addr, MMUAccessType access_type,
     }
 }
 
-static void do_rte(CPUM68KState *env)
+static void cf_rte(CPUM68KState *env)
 {
     uint32_t sp;
     uint32_t fmt;
@@ -62,13 +62,158 @@ static void do_rte(CPUM68KState *env)
     sp |= (fmt >> 28) & 3;
     env->aregs[7] = sp + 8;
 
-    helper_set_sr(env, fmt);
+    cpu_m68k_set_sr(env, fmt);
 }
 
-static void do_interrupt_all(CPUM68KState *env, int is_hw)
+static void m68k_rte(CPUM68KState *env)
+{
+    uint32_t sp;
+    uint16_t fmt;
+    uint16_t sr;
+
+    sp = env->aregs[7];
+throwaway:
+    sr = cpu_lduw_kernel(env, sp);
+    sp += 2;
+    env->pc = cpu_ldl_kernel(env, sp);
+    sp += 4;
+    if (m68k_feature(env, M68K_FEATURE_QUAD_MULDIV)) {
+        /*  all except 68000 */
+        fmt = cpu_lduw_kernel(env, sp);
+        sp += 2;
+        switch (fmt >> 12) {
+        case 0:
+            break;
+        case 1:
+            env->aregs[7] = sp;
+            cpu_m68k_set_sr(env, sr);
+            goto throwaway;
+        case 2:
+        case 3:
+            sp += 4;
+            break;
+        case 4:
+            sp += 8;
+            break;
+        case 7:
+            sp += 52;
+            break;
+        }
+    }
+    env->aregs[7] = sp;
+    cpu_m68k_set_sr(env, sr);
+}
+
+static const char *m68k_exception_name(int index)
+{
+    switch (index) {
+    case EXCP_ACCESS:
+        return "Access Fault";
+    case EXCP_ADDRESS:
+        return "Address Error";
+    case EXCP_ILLEGAL:
+        return "Illegal Instruction";
+    case EXCP_DIV0:
+        return "Divide by Zero";
+    case EXCP_CHK:
+        return "CHK/CHK2";
+    case EXCP_TRAPCC:
+        return "FTRAPcc, TRAPcc, TRAPV";
+    case EXCP_PRIVILEGE:
+        return "Privilege Violation";
+    case EXCP_TRACE:
+        return "Trace";
+    case EXCP_LINEA:
+        return "A-Line";
+    case EXCP_LINEF:
+        return "F-Line";
+    case EXCP_DEBEGBP: /* 68020/030 only */
+        return "Copro Protocol Violation";
+    case EXCP_FORMAT:
+        return "Format Error";
+    case EXCP_UNINITIALIZED:
+        return "Unitialized Interruot";
+    case EXCP_SPURIOUS:
+        return "Spurious Interrupt";
+    case EXCP_INT_LEVEL_1:
+        return "Level 1 Interrupt";
+    case EXCP_INT_LEVEL_1 + 1:
+        return "Level 2 Interrupt";
+    case EXCP_INT_LEVEL_1 + 2:
+        return "Level 3 Interrupt";
+    case EXCP_INT_LEVEL_1 + 3:
+        return "Level 4 Interrupt";
+    case EXCP_INT_LEVEL_1 + 4:
+        return "Level 5 Interrupt";
+    case EXCP_INT_LEVEL_1 + 5:
+        return "Level 6 Interrupt";
+    case EXCP_INT_LEVEL_1 + 6:
+        return "Level 7 Interrupt";
+    case EXCP_TRAP0:
+        return "TRAP #0";
+    case EXCP_TRAP0 + 1:
+        return "TRAP #1";
+    case EXCP_TRAP0 + 2:
+        return "TRAP #2";
+    case EXCP_TRAP0 + 3:
+        return "TRAP #3";
+    case EXCP_TRAP0 + 4:
+        return "TRAP #4";
+    case EXCP_TRAP0 + 5:
+        return "TRAP #5";
+    case EXCP_TRAP0 + 6:
+        return "TRAP #6";
+    case EXCP_TRAP0 + 7:
+        return "TRAP #7";
+    case EXCP_TRAP0 + 8:
+        return "TRAP #8";
+    case EXCP_TRAP0 + 9:
+        return "TRAP #9";
+    case EXCP_TRAP0 + 10:
+        return "TRAP #10";
+    case EXCP_TRAP0 + 11:
+        return "TRAP #11";
+    case EXCP_TRAP0 + 12:
+        return "TRAP #12";
+    case EXCP_TRAP0 + 13:
+        return "TRAP #13";
+    case EXCP_TRAP0 + 14:
+        return "TRAP #14";
+    case EXCP_TRAP0 + 15:
+        return "TRAP #15";
+    case EXCP_FP_BSUN:
+        return "FP Branch/Set on unordered condition";
+    case EXCP_FP_INEX:
+        return "FP Inexact Result";
+    case EXCP_FP_DZ:
+        return "FP Divide by Zero";
+    case EXCP_FP_UNFL:
+        return "FP Underflow";
+    case EXCP_FP_OPERR:
+        return "FP Operand Error";
+    case EXCP_FP_OVFL:
+        return "FP Overflow";
+    case EXCP_FP_SNAN:
+        return "FP Signaling NAN";
+    case EXCP_FP_UNIMP:
+        return "FP Unimplemented Data Type";
+    case EXCP_MMU_CONF: /* 68030/68851 only */
+        return "MMU Configuration Error";
+    case EXCP_MMU_ILLEGAL: /* 68851 only */
+        return "MMU Illegal Operation";
+    case EXCP_MMU_ACCESS: /* 68851 only */
+        return "MMU Access Level Violation";
+    case 64 ... 255:
+        return "User Defined Vector";
+    }
+    return "Unassigned";
+}
+
+static void cf_interrupt_all(CPUM68KState *env, int is_hw)
 {
     CPUState *cs = CPU(m68k_env_get_cpu(env));
     uint32_t sp;
+    uint32_t sr;
     uint32_t fmt;
     uint32_t retaddr;
     uint32_t vector;
@@ -80,7 +225,7 @@ static void do_interrupt_all(CPUM68KState *env, int is_hw)
         switch (cs->exception_index) {
         case EXCP_RTE:
             /* Return from an exception.  */
-            do_rte(env);
+            cf_rte(env);
             return;
         case EXCP_HALT_INSN:
             if (semihosting_enabled()
@@ -106,10 +251,17 @@ static void do_interrupt_all(CPUM68KState *env, int is_hw)
 
     vector = cs->exception_index << 2;
 
+    sr = env->sr | cpu_m68k_get_ccr(env);
+    if (qemu_loglevel_mask(CPU_LOG_INT)) {
+        static int count;
+        qemu_log("INT %6d: %s(%#x) pc=%08x sp=%08x sr=%04x\n",
+                 ++count, m68k_exception_name(cs->exception_index),
+                 vector, env->pc, env->aregs[7], sr);
+    }
+
     fmt |= 0x40000000;
     fmt |= vector << 16;
-    fmt |= env->sr;
-    fmt |= cpu_m68k_get_ccr(env);
+    fmt |= sr;
 
     env->sr |= SR_S;
     if (is_hw) {
@@ -131,6 +283,119 @@ static void do_interrupt_all(CPUM68KState *env, int is_hw)
     env->pc = cpu_ldl_kernel(env, env->vbr + vector);
 }
 
+static inline void do_stack_frame(CPUM68KState *env, uint32_t *sp,
+                                  uint16_t format, uint16_t sr,
+                                  uint32_t addr, uint32_t retaddr)
+{
+    CPUState *cs = CPU(m68k_env_get_cpu(env));
+    switch (format) {
+    case 4:
+        *sp -= 4;
+        cpu_stl_kernel(env, *sp, env->pc);
+        *sp -= 4;
+        cpu_stl_kernel(env, *sp, addr);
+        break;
+    case 3:
+    case 2:
+        *sp -= 4;
+        cpu_stl_kernel(env, *sp, addr);
+        break;
+    }
+    *sp -= 2;
+    cpu_stw_kernel(env, *sp, (format << 12) + (cs->exception_index << 2));
+    *sp -= 4;
+    cpu_stl_kernel(env, *sp, retaddr);
+    *sp -= 2;
+    cpu_stw_kernel(env, *sp, sr);
+}
+
+static void m68k_interrupt_all(CPUM68KState *env, int is_hw)
+{
+    CPUState *cs = CPU(m68k_env_get_cpu(env));
+    uint32_t sp;
+    uint32_t retaddr;
+    uint32_t vector;
+    uint16_t sr, oldsr;
+
+    retaddr = env->pc;
+
+    if (!is_hw) {
+        switch (cs->exception_index) {
+        case EXCP_RTE:
+            /* Return from an exception.  */
+            m68k_rte(env);
+            return;
+        case EXCP_TRAP0 ...  EXCP_TRAP15:
+            /* Move the PC after the trap instruction.  */
+            retaddr += 2;
+            break;
+        }
+    }
+
+    vector = cs->exception_index << 2;
+
+    sr = env->sr | cpu_m68k_get_ccr(env);
+    if (qemu_loglevel_mask(CPU_LOG_INT)) {
+        static int count;
+        qemu_log("INT %6d: %s(%#x) pc=%08x sp=%08x sr=%04x\n",
+                 ++count, m68k_exception_name(cs->exception_index),
+                 vector, env->pc, env->aregs[7], sr);
+    }
+
+    /*
+     * MC68040UM/AD,  chapter 9.3.10
+     */
+
+    /* "the processor first make an internal copy" */
+    oldsr = sr;
+    /* "set the mode to supervisor" */
+    sr |= SR_S;
+    /* "suppress tracing" */
+    sr &= ~SR_T;
+    /* "sets the processor interrupt mask" */
+    if (is_hw) {
+        sr |= (env->sr & ~SR_I) | (env->pending_level << SR_I_SHIFT);
+    }
+    cpu_m68k_set_sr(env, sr);
+    sp = env->aregs[7];
+
+    sp &= ~1;
+    if (cs->exception_index == EXCP_ADDRESS) {
+        do_stack_frame(env, &sp, 2, oldsr, 0, retaddr);
+    } else if (cs->exception_index == EXCP_ILLEGAL ||
+               cs->exception_index == EXCP_DIV0 ||
+               cs->exception_index == EXCP_CHK ||
+               cs->exception_index == EXCP_TRAPCC ||
+               cs->exception_index == EXCP_TRACE) {
+        /* FIXME: addr is not only env->pc */
+        do_stack_frame(env, &sp, 2, oldsr, env->pc, retaddr);
+    } else if (is_hw && oldsr & SR_M &&
+               cs->exception_index >= EXCP_SPURIOUS &&
+               cs->exception_index <= EXCP_INT_LEVEL_7) {
+        do_stack_frame(env, &sp, 0, oldsr, 0, retaddr);
+        oldsr = sr;
+        env->aregs[7] = sp;
+        cpu_m68k_set_sr(env, sr &= ~SR_M);
+        sp = env->aregs[7] & ~1;
+        do_stack_frame(env, &sp, 1, oldsr, 0, retaddr);
+    } else {
+        do_stack_frame(env, &sp, 0, oldsr, 0, retaddr);
+    }
+
+    env->aregs[7] = sp;
+    /* Jump to vector.  */
+    env->pc = cpu_ldl_kernel(env, env->vbr + vector);
+}
+
+static void do_interrupt_all(CPUM68KState *env, int is_hw)
+{
+    if (m68k_feature(env, M68K_FEATURE_M68000)) {
+        m68k_interrupt_all(env, is_hw);
+        return;
+    }
+    cf_interrupt_all(env, is_hw);
+}
+
 void m68k_cpu_do_interrupt(CPUState *cs)
 {
     M68kCPU *cpu = M68K_CPU(cs);
@@ -679,3 +944,64 @@ uint64_t HELPER(bfffo_mem)(CPUM68KState *env, uint32_t addr,
        is already zero.  */
     return n | ffo;
 }
+
+void HELPER(chk)(CPUM68KState *env, int32_t val, int32_t ub)
+{
+    /* From the specs:
+     *   X: Not affected, C,V,Z: Undefined,
+     *   N: Set if val < 0; cleared if val > ub, undefined otherwise
+     * We implement here values found from a real MC68040:
+     *   X,V,Z: Not affected
+     *   N: Set if val < 0; cleared if val >= 0
+     *   C: if 0 <= ub: set if val < 0 or val > ub, cleared otherwise
+     *      if 0 > ub: set if val > ub and val < 0, cleared otherwise
+     */
+    env->cc_n = val;
+    env->cc_c = 0 <= ub ? val < 0 || val > ub : val > ub && val < 0;
+
+    if (val < 0 || val > ub) {
+        CPUState *cs = CPU(m68k_env_get_cpu(env));
+
+        /* Recover PC and CC_OP for the beginning of the insn.  */
+        cpu_restore_state(cs, GETPC());
+
+        /* flags have been modified by gen_flush_flags() */
+        env->cc_op = CC_OP_FLAGS;
+        /* Adjust PC to end of the insn.  */
+        env->pc += 2;
+
+        cs->exception_index = EXCP_CHK;
+        cpu_loop_exit(cs);
+    }
+}
+
+void HELPER(chk2)(CPUM68KState *env, int32_t val, int32_t lb, int32_t ub)
+{
+    /* From the specs:
+     *   X: Not affected, N,V: Undefined,
+     *   Z: Set if val is equal to lb or ub
+     *   C: Set if val < lb or val > ub, cleared otherwise
+     * We implement here values found from a real MC68040:
+     *   X,N,V: Not affected
+     *   Z: Set if val is equal to lb or ub
+     *   C: if lb <= ub: set if val < lb or val > ub, cleared otherwise
+     *      if lb > ub: set if val > ub and val < lb, cleared otherwise
+     */
+    env->cc_z = val != lb && val != ub;
+    env->cc_c = lb <= ub ? val < lb || val > ub : val > ub && val < lb;
+
+    if (env->cc_c) {
+        CPUState *cs = CPU(m68k_env_get_cpu(env));
+
+        /* Recover PC and CC_OP for the beginning of the insn.  */
+        cpu_restore_state(cs, GETPC());
+
+        /* flags have been modified by gen_flush_flags() */
+        env->cc_op = CC_OP_FLAGS;
+        /* Adjust PC to end of the insn.  */
+        env->pc += 4;
+
+        cs->exception_index = EXCP_CHK;
+        cpu_loop_exit(cs);
+    }
+}
diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index 09226eba81..f0e86a73d4 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -270,7 +270,6 @@ static void gen_raise_exception(int nr)
 
 static void gen_exception(DisasContext *s, uint32_t where, int nr)
 {
-    update_cc_op(s);
     gen_jmp_im(s, where);
     gen_raise_exception(nr);
 }
@@ -1510,12 +1509,12 @@ DISAS_INSN(dbcc)
 
 DISAS_INSN(undef_mac)
 {
-    gen_exception(s, s->pc - 2, EXCP_LINEA);
+    gen_exception(s, s->insn_pc, EXCP_LINEA);
 }
 
 DISAS_INSN(undef_fpu)
 {
-    gen_exception(s, s->pc - 2, EXCP_LINEF);
+    gen_exception(s, s->insn_pc, EXCP_LINEF);
 }
 
 DISAS_INSN(undef)
@@ -1524,8 +1523,8 @@ DISAS_INSN(undef)
        for the 680x0 series, as well as those that are implemented
        but actually illegal for CPU32 or pre-68020.  */
     qemu_log_mask(LOG_UNIMP, "Illegal instruction: %04x @ %08x",
-                  insn, s->pc - 2);
-    gen_exception(s, s->pc - 2, EXCP_UNSUPPORTED);
+                  insn, s->insn_pc);
+    gen_exception(s, s->insn_pc, EXCP_UNSUPPORTED);
 }
 
 DISAS_INSN(mulw)
@@ -2132,6 +2131,68 @@ DISAS_INSN(bitop_im)
     }
 }
 
+static TCGv gen_get_ccr(DisasContext *s)
+{
+    TCGv dest;
+
+    update_cc_op(s);
+    dest = tcg_temp_new();
+    gen_helper_get_ccr(dest, cpu_env);
+    return dest;
+}
+
+static TCGv gen_get_sr(DisasContext *s)
+{
+    TCGv ccr;
+    TCGv sr;
+
+    ccr = gen_get_ccr(s);
+    sr = tcg_temp_new();
+    tcg_gen_andi_i32(sr, QREG_SR, 0xffe0);
+    tcg_gen_or_i32(sr, sr, ccr);
+    return sr;
+}
+
+static void gen_set_sr_im(DisasContext *s, uint16_t val, int ccr_only)
+{
+    if (ccr_only) {
+        tcg_gen_movi_i32(QREG_CC_C, val & CCF_C ? 1 : 0);
+        tcg_gen_movi_i32(QREG_CC_V, val & CCF_V ? -1 : 0);
+        tcg_gen_movi_i32(QREG_CC_Z, val & CCF_Z ? 0 : 1);
+        tcg_gen_movi_i32(QREG_CC_N, val & CCF_N ? -1 : 0);
+        tcg_gen_movi_i32(QREG_CC_X, val & CCF_X ? 1 : 0);
+    } else {
+        TCGv sr = tcg_const_i32(val);
+        gen_helper_set_sr(cpu_env, sr);
+        tcg_temp_free(sr);
+    }
+    set_cc_op(s, CC_OP_FLAGS);
+}
+
+static void gen_set_sr(DisasContext *s, TCGv val, int ccr_only)
+{
+    if (ccr_only) {
+        gen_helper_set_ccr(cpu_env, val);
+    } else {
+        gen_helper_set_sr(cpu_env, val);
+    }
+    set_cc_op(s, CC_OP_FLAGS);
+}
+
+static void gen_move_to_sr(CPUM68KState *env, DisasContext *s, uint16_t insn,
+                           bool ccr_only)
+{
+    if ((insn & 0x3f) == 0x3c) {
+        uint16_t val;
+        val = read_im16(env, s);
+        gen_set_sr_im(s, val, ccr_only);
+    } else {
+        TCGv src;
+        SRC_EA(env, src, OS_WORD, 0, NULL);
+        gen_set_sr(s, src, ccr_only);
+    }
+}
+
 DISAS_INSN(arith_im)
 {
     int op;
@@ -2140,6 +2201,7 @@ DISAS_INSN(arith_im)
     TCGv dest;
     TCGv addr;
     int opsize;
+    bool with_SR = ((insn & 0x3f) == 0x3c);
 
     op = (insn >> 9) & 7;
     opsize = insn_opsize(insn);
@@ -2156,32 +2218,73 @@ DISAS_INSN(arith_im)
     default:
        abort();
     }
-    SRC_EA(env, src1, opsize, 1, (op == 6) ? NULL : &addr);
+
+    if (with_SR) {
+        /* SR/CCR can only be used with andi/eori/ori */
+        if (op == 2 || op == 3 || op == 6) {
+            disas_undef(env, s, insn);
+            return;
+        }
+        switch (opsize) {
+        case OS_BYTE:
+            src1 = gen_get_ccr(s);
+            break;
+        case OS_WORD:
+            if (IS_USER(s)) {
+                gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+                return;
+            }
+            src1 = gen_get_sr(s);
+            break;
+        case OS_LONG:
+            disas_undef(env, s, insn);
+            return;
+        }
+    } else {
+        SRC_EA(env, src1, opsize, 1, (op == 6) ? NULL : &addr);
+    }
     dest = tcg_temp_new();
     switch (op) {
     case 0: /* ori */
         tcg_gen_or_i32(dest, src1, im);
-        gen_logic_cc(s, dest, opsize);
+        if (with_SR) {
+            gen_set_sr(s, dest, opsize == OS_BYTE);
+        } else {
+            DEST_EA(env, insn, opsize, dest, &addr);
+            gen_logic_cc(s, dest, opsize);
+        }
         break;
     case 1: /* andi */
         tcg_gen_and_i32(dest, src1, im);
-        gen_logic_cc(s, dest, opsize);
+        if (with_SR) {
+            gen_set_sr(s, dest, opsize == OS_BYTE);
+        } else {
+            DEST_EA(env, insn, opsize, dest, &addr);
+            gen_logic_cc(s, dest, opsize);
+        }
         break;
     case 2: /* subi */
         tcg_gen_setcond_i32(TCG_COND_LTU, QREG_CC_X, src1, im);
         tcg_gen_sub_i32(dest, src1, im);
         gen_update_cc_add(dest, im, opsize);
         set_cc_op(s, CC_OP_SUBB + opsize);
+        DEST_EA(env, insn, opsize, dest, &addr);
         break;
     case 3: /* addi */
         tcg_gen_add_i32(dest, src1, im);
         gen_update_cc_add(dest, im, opsize);
         tcg_gen_setcond_i32(TCG_COND_LTU, QREG_CC_X, dest, im);
         set_cc_op(s, CC_OP_ADDB + opsize);
+        DEST_EA(env, insn, opsize, dest, &addr);
         break;
     case 5: /* eori */
         tcg_gen_xor_i32(dest, src1, im);
-        gen_logic_cc(s, dest, opsize);
+        if (with_SR) {
+            gen_set_sr(s, dest, opsize == OS_BYTE);
+        } else {
+            DEST_EA(env, insn, opsize, dest, &addr);
+            gen_logic_cc(s, dest, opsize);
+        }
         break;
     case 6: /* cmpi */
         gen_update_cc_cmp(s, src1, im, opsize);
@@ -2190,9 +2293,6 @@ DISAS_INSN(arith_im)
         abort();
     }
     tcg_temp_free(im);
-    if (op != 6) {
-        DEST_EA(env, insn, opsize, dest, &addr);
-    }
     tcg_temp_free(dest);
 }
 
@@ -2475,17 +2575,6 @@ DISAS_INSN(clr)
     tcg_temp_free(zero);
 }
 
-static TCGv gen_get_ccr(DisasContext *s)
-{
-    TCGv dest;
-
-    gen_flush_flags(s);
-    update_cc_op(s);
-    dest = tcg_temp_new();
-    gen_helper_get_ccr(dest, cpu_env);
-    return dest;
-}
-
 DISAS_INSN(move_from_ccr)
 {
     TCGv ccr;
@@ -2512,43 +2601,9 @@ DISAS_INSN(neg)
     tcg_temp_free(dest);
 }
 
-static void gen_set_sr_im(DisasContext *s, uint16_t val, int ccr_only)
-{
-    if (ccr_only) {
-        tcg_gen_movi_i32(QREG_CC_C, val & CCF_C ? 1 : 0);
-        tcg_gen_movi_i32(QREG_CC_V, val & CCF_V ? -1 : 0);
-        tcg_gen_movi_i32(QREG_CC_Z, val & CCF_Z ? 0 : 1);
-        tcg_gen_movi_i32(QREG_CC_N, val & CCF_N ? -1 : 0);
-        tcg_gen_movi_i32(QREG_CC_X, val & CCF_X ? 1 : 0);
-    } else {
-        gen_helper_set_sr(cpu_env, tcg_const_i32(val));
-    }
-    set_cc_op(s, CC_OP_FLAGS);
-}
-
-static void gen_set_sr(CPUM68KState *env, DisasContext *s, uint16_t insn,
-                       int ccr_only)
-{
-    if ((insn & 0x38) == 0) {
-        if (ccr_only) {
-            gen_helper_set_ccr(cpu_env, DREG(insn, 0));
-        } else {
-            gen_helper_set_sr(cpu_env, DREG(insn, 0));
-        }
-        set_cc_op(s, CC_OP_FLAGS);
-    } else if ((insn & 0x3f) == 0x3c) {
-        uint16_t val;
-        val = read_im16(env, s);
-        gen_set_sr_im(s, val, ccr_only);
-    } else {
-        disas_undef(env, s, insn);
-    }
-}
-
-
 DISAS_INSN(move_to_ccr)
 {
-    gen_set_sr(env, s, insn, 1);
+    gen_move_to_sr(env, s, insn, true);
 }
 
 DISAS_INSN(not)
@@ -2585,7 +2640,7 @@ DISAS_INSN(swap)
 
 DISAS_INSN(bkpt)
 {
-    gen_exception(s, s->pc - 2, EXCP_DEBUG);
+    gen_exception(s, s->insn_pc, EXCP_DEBUG);
 }
 
 DISAS_INSN(pea)
@@ -2638,7 +2693,7 @@ DISAS_INSN(pulse)
 
 DISAS_INSN(illegal)
 {
-    gen_exception(s, s->pc - 2, EXCP_ILLEGAL);
+    gen_exception(s, s->insn_pc, EXCP_ILLEGAL);
 }
 
 /* ??? This should be atomic.  */
@@ -2668,7 +2723,7 @@ DISAS_INSN(mull)
 
     if (ext & 0x400) {
         if (!m68k_feature(s->env, M68K_FEATURE_QUAD_MULDIV)) {
-            gen_exception(s, s->pc - 4, EXCP_UNSUPPORTED);
+            gen_exception(s, s->insn_pc, EXCP_UNSUPPORTED);
             return;
         }
 
@@ -2764,6 +2819,18 @@ DISAS_INSN(unlk)
     tcg_temp_free(src);
 }
 
+#if defined(CONFIG_SOFTMMU)
+DISAS_INSN(reset)
+{
+    if (IS_USER(s)) {
+        gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+        return;
+    }
+
+    gen_helper_reset(cpu_env);
+}
+#endif
+
 DISAS_INSN(nop)
 {
 }
@@ -2897,6 +2964,7 @@ DISAS_INSN(branch)
         gen_jmp_tb(s, 0, s->pc);
     } else {
         /* Unconditional branch.  */
+        update_cc_op(s);
         gen_jmp_tb(s, 0, base + offset);
     }
 }
@@ -4204,16 +4272,148 @@ DISAS_INSN(ff1)
     gen_helper_ff1(reg, reg);
 }
 
-static TCGv gen_get_sr(DisasContext *s)
+DISAS_INSN(chk)
 {
-    TCGv ccr;
-    TCGv sr;
+    TCGv src, reg;
+    int opsize;
 
-    ccr = gen_get_ccr(s);
-    sr = tcg_temp_new();
-    tcg_gen_andi_i32(sr, QREG_SR, 0xffe0);
-    tcg_gen_or_i32(sr, sr, ccr);
-    return sr;
+    switch ((insn >> 7) & 3) {
+    case 3:
+        opsize = OS_WORD;
+        break;
+    case 2:
+        if (m68k_feature(env, M68K_FEATURE_CHK2)) {
+            opsize = OS_LONG;
+            break;
+        }
+        /* fallthru */
+    default:
+        gen_exception(s, s->insn_pc, EXCP_ILLEGAL);
+        return;
+    }
+    SRC_EA(env, src, opsize, 1, NULL);
+    reg = gen_extend(DREG(insn, 9), opsize, 1);
+
+    gen_flush_flags(s);
+    gen_helper_chk(cpu_env, reg, src);
+}
+
+DISAS_INSN(chk2)
+{
+    uint16_t ext;
+    TCGv addr1, addr2, bound1, bound2, reg;
+    int opsize;
+
+    switch ((insn >> 9) & 3) {
+    case 0:
+        opsize = OS_BYTE;
+        break;
+    case 1:
+        opsize = OS_WORD;
+        break;
+    case 2:
+        opsize = OS_LONG;
+        break;
+    default:
+        gen_exception(s, s->insn_pc, EXCP_ILLEGAL);
+        return;
+    }
+
+    ext = read_im16(env, s);
+    if ((ext & 0x0800) == 0) {
+        gen_exception(s, s->insn_pc, EXCP_ILLEGAL);
+        return;
+    }
+
+    addr1 = gen_lea(env, s, insn, OS_UNSIZED);
+    addr2 = tcg_temp_new();
+    tcg_gen_addi_i32(addr2, addr1, opsize_bytes(opsize));
+
+    bound1 = gen_load(s, opsize, addr1, 1);
+    tcg_temp_free(addr1);
+    bound2 = gen_load(s, opsize, addr2, 1);
+    tcg_temp_free(addr2);
+
+    reg = tcg_temp_new();
+    if (ext & 0x8000) {
+        tcg_gen_mov_i32(reg, AREG(ext, 12));
+    } else {
+        gen_ext(reg, DREG(ext, 12), opsize, 1);
+    }
+
+    gen_flush_flags(s);
+    gen_helper_chk2(cpu_env, reg, bound1, bound2);
+    tcg_temp_free(reg);
+}
+
+static void m68k_copy_line(TCGv dst, TCGv src, int index)
+{
+    TCGv addr;
+    TCGv_i64 t0, t1;
+
+    addr = tcg_temp_new();
+
+    t0 = tcg_temp_new_i64();
+    t1 = tcg_temp_new_i64();
+
+    tcg_gen_andi_i32(addr, src, ~15);
+    tcg_gen_qemu_ld64(t0, addr, index);
+    tcg_gen_addi_i32(addr, addr, 8);
+    tcg_gen_qemu_ld64(t1, addr, index);
+
+    tcg_gen_andi_i32(addr, dst, ~15);
+    tcg_gen_qemu_st64(t0, addr, index);
+    tcg_gen_addi_i32(addr, addr, 8);
+    tcg_gen_qemu_st64(t1, addr, index);
+
+    tcg_temp_free_i64(t0);
+    tcg_temp_free_i64(t1);
+    tcg_temp_free(addr);
+}
+
+DISAS_INSN(move16_reg)
+{
+    int index = IS_USER(s);
+    TCGv tmp;
+    uint16_t ext;
+
+    ext = read_im16(env, s);
+    if ((ext & (1 << 15)) == 0) {
+        gen_exception(s, s->insn_pc, EXCP_ILLEGAL);
+    }
+
+    m68k_copy_line(AREG(ext, 12), AREG(insn, 0), index);
+
+    /* Ax can be Ay, so save Ay before incrementing Ax */
+    tmp = tcg_temp_new();
+    tcg_gen_mov_i32(tmp, AREG(ext, 12));
+    tcg_gen_addi_i32(AREG(insn, 0), AREG(insn, 0), 16);
+    tcg_gen_addi_i32(AREG(ext, 12), tmp, 16);
+    tcg_temp_free(tmp);
+}
+
+DISAS_INSN(move16_mem)
+{
+    int index = IS_USER(s);
+    TCGv reg, addr;
+
+    reg = AREG(insn, 0);
+    addr = tcg_const_i32(read_im32(env, s));
+
+    if ((insn >> 3) & 1) {
+        /* MOVE16 (xxx).L, (Ay) */
+        m68k_copy_line(reg, addr, index);
+    } else {
+        /* MOVE16 (Ay), (xxx).L */
+        m68k_copy_line(addr, reg, index);
+    }
+
+    tcg_temp_free(addr);
+
+    if (((insn >> 3) & 2) == 0) {
+        /* (Ay)+ */
+        tcg_gen_addi_i32(reg, reg, 16);
+    }
 }
 
 DISAS_INSN(strldsr)
@@ -4241,27 +4441,28 @@ DISAS_INSN(move_from_sr)
     TCGv sr;
 
     if (IS_USER(s) && !m68k_feature(env, M68K_FEATURE_M68000)) {
-        gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+        gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
         return;
     }
     sr = gen_get_sr(s);
     DEST_EA(env, insn, OS_WORD, sr, NULL);
 }
 
+#if defined(CONFIG_SOFTMMU)
 DISAS_INSN(move_to_sr)
 {
     if (IS_USER(s)) {
-        gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+        gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
         return;
     }
-    gen_set_sr(env, s, insn, 0);
+    gen_move_to_sr(env, s, insn, false);
     gen_lookup_tb(s);
 }
 
 DISAS_INSN(move_from_usp)
 {
     if (IS_USER(s)) {
-        gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+        gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
         return;
     }
     tcg_gen_ld_i32(AREG(insn, 0), cpu_env,
@@ -4271,7 +4472,7 @@ DISAS_INSN(move_from_usp)
 DISAS_INSN(move_to_usp)
 {
     if (IS_USER(s)) {
-        gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+        gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
         return;
     }
     tcg_gen_st_i32(AREG(insn, 0), cpu_env,
@@ -4280,6 +4481,11 @@ DISAS_INSN(move_to_usp)
 
 DISAS_INSN(halt)
 {
+    if (IS_USER(s)) {
+        gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+        return;
+    }
+
     gen_exception(s, s->pc, EXCP_HALT_INSN);
 }
 
@@ -4288,7 +4494,7 @@ DISAS_INSN(stop)
     uint16_t ext;
 
     if (IS_USER(s)) {
-        gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+        gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
         return;
     }
 
@@ -4302,19 +4508,19 @@ DISAS_INSN(stop)
 DISAS_INSN(rte)
 {
     if (IS_USER(s)) {
-        gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+        gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
         return;
     }
-    gen_exception(s, s->pc - 2, EXCP_RTE);
+    gen_exception(s, s->insn_pc, EXCP_RTE);
 }
 
-DISAS_INSN(movec)
+DISAS_INSN(cf_movec)
 {
     uint16_t ext;
     TCGv reg;
 
     if (IS_USER(s)) {
-        gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+        gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
         return;
     }
 
@@ -4325,14 +4531,39 @@ DISAS_INSN(movec)
     } else {
         reg = DREG(ext, 12);
     }
-    gen_helper_movec(cpu_env, tcg_const_i32(ext & 0xfff), reg);
+    gen_helper_cf_movec_to(cpu_env, tcg_const_i32(ext & 0xfff), reg);
+    gen_lookup_tb(s);
+}
+
+DISAS_INSN(m68k_movec)
+{
+    uint16_t ext;
+    TCGv reg;
+
+    if (IS_USER(s)) {
+        gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+        return;
+    }
+
+    ext = read_im16(env, s);
+
+    if (ext & 0x8000) {
+        reg = AREG(ext, 12);
+    } else {
+        reg = DREG(ext, 12);
+    }
+    if (insn & 1) {
+        gen_helper_m68k_movec_to(cpu_env, tcg_const_i32(ext & 0xfff), reg);
+    } else {
+        gen_helper_m68k_movec_from(reg, cpu_env, tcg_const_i32(ext & 0xfff));
+    }
     gen_lookup_tb(s);
 }
 
 DISAS_INSN(intouch)
 {
     if (IS_USER(s)) {
-        gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+        gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
         return;
     }
     /* ICache fetch.  Implement as no-op.  */
@@ -4341,15 +4572,33 @@ DISAS_INSN(intouch)
 DISAS_INSN(cpushl)
 {
     if (IS_USER(s)) {
-        gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+        gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
         return;
     }
     /* Cache push/invalidate.  Implement as no-op.  */
 }
 
+DISAS_INSN(cpush)
+{
+    if (IS_USER(s)) {
+        gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+        return;
+    }
+    /* Cache push/invalidate.  Implement as no-op.  */
+}
+
+DISAS_INSN(cinv)
+{
+    if (IS_USER(s)) {
+        gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+        return;
+    }
+    /* Invalidate cache line.  Implement as no-op.  */
+}
+
 DISAS_INSN(wddata)
 {
-    gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+    gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
 }
 
 DISAS_INSN(wdebug)
@@ -4357,16 +4606,17 @@ DISAS_INSN(wdebug)
     M68kCPU *cpu = m68k_env_get_cpu(env);
 
     if (IS_USER(s)) {
-        gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+        gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
         return;
     }
     /* TODO: Implement wdebug.  */
     cpu_abort(CPU(cpu), "WDEBUG not implemented");
 }
+#endif
 
 DISAS_INSN(trap)
 {
-    gen_exception(s, s->pc - 2, EXCP_TRAP0 + (insn & 0xf));
+    gen_exception(s, s->insn_pc, EXCP_TRAP0 + (insn & 0xf));
 }
 
 static void gen_load_fcr(DisasContext *s, TCGv res, int reg)
@@ -4875,6 +5125,7 @@ static void gen_fjmpcc(DisasContext *s, int cond, TCGLabel *l1)
     DisasCompare c;
 
     gen_fcc_cond(&c, s, cond);
+    update_cc_op(s);
     tcg_gen_brcond_i32(c.tcond, c.v1, c.v2, l1);
     free_cond(&c);
 }
@@ -4919,21 +5170,40 @@ DISAS_INSN(fscc)
     tcg_temp_free(tmp);
 }
 
+#if defined(CONFIG_SOFTMMU)
 DISAS_INSN(frestore)
 {
-    M68kCPU *cpu = m68k_env_get_cpu(env);
+    TCGv addr;
 
-    /* TODO: Implement frestore.  */
-    cpu_abort(CPU(cpu), "FRESTORE not implemented");
+    if (IS_USER(s)) {
+        gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+        return;
+    }
+    if (m68k_feature(s->env, M68K_FEATURE_M68040)) {
+        SRC_EA(env, addr, OS_LONG, 0, NULL);
+        /* FIXME: check the state frame */
+    } else {
+        disas_undef(env, s, insn);
+    }
 }
 
 DISAS_INSN(fsave)
 {
-    M68kCPU *cpu = m68k_env_get_cpu(env);
+    if (IS_USER(s)) {
+        gen_exception(s, s->insn_pc, EXCP_PRIVILEGE);
+        return;
+    }
 
-    /* TODO: Implement fsave.  */
-    cpu_abort(CPU(cpu), "FSAVE not implemented");
+    if (m68k_feature(s->env, M68K_FEATURE_M68040)) {
+        /* always write IDLE */
+        TCGv idle = tcg_const_i32(0x41000000);
+        DEST_EA(env, insn, OS_LONG, idle, NULL);
+        tcg_temp_free(idle);
+    } else {
+        disas_undef(env, s, insn);
+    }
 }
+#endif
 
 static inline TCGv gen_mac_extract_word(DisasContext *s, TCGv val, int upper)
 {
@@ -5306,7 +5576,7 @@ void register_m68k_insns (CPUM68KState *env)
     BASE(undef,     0000, 0000);
     INSN(arith_im,  0080, fff8, CF_ISA_A);
     INSN(arith_im,  0000, ff00, M68000);
-    INSN(undef,     00c0, ffc0, M68000);
+    INSN(chk2,      00c0, f9c0, CHK2);
     INSN(bitrev,    00c0, fff8, CF_ISA_APLUSC);
     BASE(bitop_reg, 0100, f1c0);
     BASE(bitop_reg, 0140, f1c0);
@@ -5339,6 +5609,7 @@ void register_m68k_insns (CPUM68KState *env)
     BASE(move,      1000, f000);
     BASE(move,      2000, f000);
     BASE(move,      3000, f000);
+    INSN(chk,       4000, f040, M68000);
     INSN(strldsr,   40e7, ffff, CF_ISA_APLUSC);
     INSN(negx,      4080, fff8, CF_ISA_A);
     INSN(negx,      4000, ff00, M68000);
@@ -5356,8 +5627,9 @@ void register_m68k_insns (CPUM68KState *env)
     BASE(move_to_ccr, 44c0, ffc0);
     INSN(not,       4680, fff8, CF_ISA_A);
     INSN(not,       4600, ff00, M68000);
-    INSN(undef,     46c0, ffc0, M68000);
-    INSN(move_to_sr, 46c0, ffc0, CF_ISA_A);
+#if defined(CONFIG_SOFTMMU)
+    BASE(move_to_sr, 46c0, ffc0);
+#endif
     INSN(nbcd,      4800, ffc0, M68000);
     INSN(linkl,     4808, fff8, M68000);
     BASE(pea,       4840, ffc0);
@@ -5372,7 +5644,9 @@ void register_m68k_insns (CPUM68KState *env)
     BASE(tst,       4a00, ff00);
     INSN(tas,       4ac0, ffc0, CF_ISA_B);
     INSN(tas,       4ac0, ffc0, M68000);
+#if defined(CONFIG_SOFTMMU)
     INSN(halt,      4ac8, ffff, CF_ISA_A);
+#endif
     INSN(pulse,     4acc, ffff, CF_ISA_A);
     BASE(illegal,   4afc, ffff);
     INSN(mull,      4c00, ffc0, CF_ISA_A);
@@ -5383,14 +5657,18 @@ void register_m68k_insns (CPUM68KState *env)
     BASE(trap,      4e40, fff0);
     BASE(link,      4e50, fff8);
     BASE(unlk,      4e58, fff8);
+#if defined(CONFIG_SOFTMMU)
     INSN(move_to_usp, 4e60, fff8, USP);
     INSN(move_from_usp, 4e68, fff8, USP);
-    BASE(nop,       4e71, ffff);
+    INSN(reset,     4e70, ffff, M68000);
     BASE(stop,      4e72, ffff);
     BASE(rte,       4e73, ffff);
+    INSN(cf_movec,  4e7b, ffff, CF_ISA_A);
+    INSN(m68k_movec, 4e7a, fffe, M68000);
+#endif
+    BASE(nop,       4e71, ffff);
     INSN(rtd,       4e74, ffff, RTD);
     BASE(rts,       4e75, ffff);
-    INSN(movec,     4e7b, ffff, CF_ISA_A);
     BASE(jump,      4e80, ffc0);
     BASE(jump,      4ec0, ffc0);
     INSN(addsubq,   5000, f080, M68000);
@@ -5494,17 +5772,23 @@ void register_m68k_insns (CPUM68KState *env)
     BASE(undef_fpu, f000, f000);
     INSN(fpu,       f200, ffc0, CF_FPU);
     INSN(fbcc,      f280, ffc0, CF_FPU);
-    INSN(frestore,  f340, ffc0, CF_FPU);
-    INSN(fsave,     f300, ffc0, CF_FPU);
     INSN(fpu,       f200, ffc0, FPU);
     INSN(fscc,      f240, ffc0, FPU);
     INSN(fbcc,      f280, ff80, FPU);
+#if defined(CONFIG_SOFTMMU)
+    INSN(frestore,  f340, ffc0, CF_FPU);
+    INSN(fsave,     f300, ffc0, CF_FPU);
     INSN(frestore,  f340, ffc0, FPU);
     INSN(fsave,     f300, ffc0, FPU);
     INSN(intouch,   f340, ffc0, CF_ISA_A);
     INSN(cpushl,    f428, ff38, CF_ISA_A);
+    INSN(cpush,     f420, ff20, M68040);
+    INSN(cinv,      f400, ff20, M68040);
     INSN(wddata,    fb00, ff00, CF_ISA_A);
     INSN(wdebug,    fbc0, ffc0, CF_ISA_A);
+#endif
+    INSN(move16_mem, f600, ffe0, M68040);
+    INSN(move16_reg, f620, fff8, M68040);
 #undef INSN
 }
 
@@ -5652,9 +5936,12 @@ void m68k_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
     }
     cpu_fprintf (f, "PC = %08x   ", env->pc);
     sr = env->sr | cpu_m68k_get_ccr(env);
-    cpu_fprintf(f, "SR = %04x %c%c%c%c%c ", sr, (sr & CCF_X) ? 'X' : '-',
-                (sr & CCF_N) ? 'N' : '-', (sr & CCF_Z) ? 'Z' : '-',
-                (sr & CCF_V) ? 'V' : '-', (sr & CCF_C) ? 'C' : '-');
+    cpu_fprintf(f, "SR = %04x T:%x I:%x %c%c %c%c%c%c%c\n",
+                sr, (sr & SR_T) >> SR_T_SHIFT, (sr & SR_I) >> SR_I_SHIFT,
+                (sr & SR_S) ? 'S' : 'U', (sr & SR_M) ? '%' : 'I',
+                (sr & CCF_X) ? 'X' : '-', (sr & CCF_N) ? 'N' : '-',
+                (sr & CCF_Z) ? 'Z' : '-', (sr & CCF_V) ? 'V' : '-',
+                (sr & CCF_C) ? 'C' : '-');
     cpu_fprintf(f, "FPSR = %08x %c%c%c%c ", env->fpsr,
                 (env->fpsr & FPSR_CC_A) ? 'A' : '-',
                 (env->fpsr & FPSR_CC_I) ? 'I' : '-',
@@ -5687,6 +5974,14 @@ void m68k_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
         cpu_fprintf(f, "RP ");
         break;
     }
+    cpu_fprintf(f, "\n");
+#ifdef CONFIG_SOFTMMU
+    cpu_fprintf(f, "%sA7(MSP) = %08x %sA7(USP) = %08x %sA7(ISP) = %08x\n",
+               env->current_sp == M68K_SSP ? "->" : "  ", env->sp[M68K_SSP],
+               env->current_sp == M68K_USP ? "->" : "  ", env->sp[M68K_USP],
+               env->current_sp == M68K_ISP ? "->" : "  ", env->sp[M68K_ISP]);
+    cpu_fprintf(f, "VBR = 0x%08x\n", env->vbr);
+#endif
 }
 
 void restore_state_to_opc(CPUM68KState *env, TranslationBlock *tb,