All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 0/2] m68k: 680x0 processors family support
@ 2015-06-21 22:35 Laurent Vivier
  2015-06-21 22:35 ` [Qemu-devel] [PATCH 1/2] m68k: Add compatibility with 680x0 processors family Laurent Vivier
                   ` (3 more replies)
  0 siblings, 4 replies; 8+ messages in thread
From: Laurent Vivier @ 2015-06-21 22:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Andreas Schwab, Laurent Vivier, gerg

I carry this series for several years now.

I was reluctant to merge it with the mainstream because
I was sure it was breaking Coldfire support.

But with the kernel provided by Greg Ungerer I was able
to check and correct the support of ColdFire family
in my tree.

This series allows:
- to start a ColdFire semi-hosted kernel (m68k-softmmu)
- chroot or start a container of debian etch-m68k (m68k-linux-user)

I have another series providing privileged instructions and
allowing to boot a 680x0 debian kernel (Macintosh Quadra 800),
but this one needs more work (and love). Based on this work,
we will also be able to boot a NextStation (Bryce Lanham, GSoC)

This work is based on the work of Andreas Schwab.

Laurent Vivier (2):
  m68k: Add compatibility with 680x0 processors family
  m68k: Implement 680x0 processors family 96 bit FPU

 configure                  |    2 +-
 cpu-exec.c                 |    6 -
 disas.c                    |    4 +
 fpu/softfloat-specialize.h |   48 +-
 fpu/softfloat.c            |   38 +-
 gdb-xml/m68k-fp.xml        |   21 +
 include/fpu/softfloat.h    |   11 +-
 target-m68k/cpu.c          |   67 +-
 target-m68k/cpu.h          |   76 +-
 target-m68k/helper.c       | 1691 ++++++++++++++++++--
 target-m68k/helper.h       |  100 +-
 target-m68k/m68k-qreg.h    |   11 -
 target-m68k/op_helper.c    |  182 ++-
 target-m68k/qregs.def      |    6 +-
 target-m68k/translate.c    | 3682 +++++++++++++++++++++++++++++++++++---------
 15 files changed, 5013 insertions(+), 932 deletions(-)
 create mode 100644 gdb-xml/m68k-fp.xml
 delete mode 100644 target-m68k/m68k-qreg.h

-- 
2.4.3

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [Qemu-devel] [PATCH 1/2] m68k: Add compatibility with 680x0 processors family
  2015-06-21 22:35 [Qemu-devel] [PATCH 0/2] m68k: 680x0 processors family support Laurent Vivier
@ 2015-06-21 22:35 ` Laurent Vivier
  2015-06-21 22:35 ` [Qemu-devel] [PATCH 2/2] m68k: Implement 680x0 processors family 96 bit FPU Laurent Vivier
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 8+ messages in thread
From: Laurent Vivier @ 2015-06-21 22:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Andreas Schwab, Laurent Vivier, gerg

This patch adds all non-privileged instructions of the 680x0 family.
The FPU is not modified and we always use the ColdFire FPU emulation,
so some 680x0 FPU instructions are missing.

Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
 cpu-exec.c              |    6 -
 disas.c                 |    4 +
 target-m68k/cpu.c       |   58 +-
 target-m68k/cpu.h       |   38 +-
 target-m68k/helper.c    |  823 +++++++++++++----
 target-m68k/helper.h    |   56 +-
 target-m68k/m68k-qreg.h |   11 -
 target-m68k/op_helper.c |  182 +++-
 target-m68k/qregs.def   |    1 +
 target-m68k/translate.c | 2332 +++++++++++++++++++++++++++++++++++++----------
 10 files changed, 2836 insertions(+), 675 deletions(-)
 delete mode 100644 target-m68k/m68k-qreg.h

diff --git a/cpu-exec.c b/cpu-exec.c
index 2ffeb6e..e6c512e 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -183,12 +183,6 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, uint8_t *tb_ptr)
     if (qemu_loglevel_mask(CPU_LOG_TB_CPU)) {
 #if defined(TARGET_I386)
         log_cpu_state(cpu, CPU_DUMP_CCOP);
-#elif defined(TARGET_M68K)
-        /* ??? Should not modify env state for dumping.  */
-        cpu_m68k_flush_flags(env, env->cc_op);
-        env->cc_op = CC_OP_FLAGS;
-        env->sr = (env->sr & 0xffe0) | env->cc_dest | (env->cc_x << 4);
-        log_cpu_state(cpu, 0);
 #else
         log_cpu_state(cpu, 0);
 #endif
diff --git a/disas.c b/disas.c
index 44a019a..cee0b3a 100644
--- a/disas.c
+++ b/disas.c
@@ -193,6 +193,7 @@ static int print_insn_od_target(bfd_vma pc, disassemble_info *info)
     arm  - bit 0 = thumb, bit 1 = reverse endian, bit 2 = A64
     ppc  - bits 0:15 specify (optionally) the machine instruction set;
            bit 16 indicates little endian.
+    m68k - 1 means 680x0 family, otherwise coldfire
     other targets - unused
  */
 void target_disas(FILE *out, CPUArchState *env, target_ulong code,
@@ -269,6 +270,9 @@ void target_disas(FILE *out, CPUArchState *env, target_ulong code,
     print_insn = print_insn_ppc;
 #elif defined(TARGET_M68K)
     print_insn = print_insn_m68k;
+    if (flags) {
+        s.info.mach = bfd_mach_m68040;
+    }
 #elif defined(TARGET_MIPS)
 #ifdef TARGET_WORDS_BIGENDIAN
     print_insn = print_insn_big_mips;
diff --git a/target-m68k/cpu.c b/target-m68k/cpu.c
index 4cfb725..4c6760b 100644
--- a/target-m68k/cpu.c
+++ b/target-m68k/cpu.c
@@ -54,7 +54,7 @@ static void m68k_cpu_reset(CPUState *s)
     env->sr = 0x2700;
 #endif
     m68k_switch_sp(env);
-    /* ??? FP regs should be initialized to NaN.  */
+
     env->cc_op = CC_OP_FLAGS;
     /* TODO: We should set PC from the interrupt vector.  */
     env->pc = 0;
@@ -90,6 +90,55 @@ static void m5206_cpu_initfn(Object *obj)
     m68k_set_feature(env, M68K_FEATURE_CF_ISA_A);
 }
 
+static void m68000_cpu_initfn(Object *obj)
+{
+    M68kCPU *cpu = M68K_CPU(obj);
+    CPUM68KState *env = &cpu->env;
+
+    m68k_set_feature(env, M68K_FEATURE_M68000);
+    m68k_set_feature(env, M68K_FEATURE_USP);
+    m68k_set_feature(env, M68K_FEATURE_WORD_INDEX);
+}
+
+static void m68020_cpu_initfn(Object *obj)
+{
+    M68kCPU *cpu = M68K_CPU(obj);
+    CPUM68KState *env = &cpu->env;
+
+    m68k_set_feature(env, M68K_FEATURE_M68000);
+    m68k_set_feature(env, M68K_FEATURE_USP);
+    m68k_set_feature(env, M68K_FEATURE_WORD_INDEX);
+    m68k_set_feature(env, M68K_FEATURE_QUAD_MULDIV);
+    m68k_set_feature(env, M68K_FEATURE_BRAL);
+    m68k_set_feature(env, M68K_FEATURE_BCCL);
+    m68k_set_feature(env, M68K_FEATURE_BITFIELD);
+    m68k_set_feature(env, M68K_FEATURE_EXT_FULL);
+    m68k_set_feature(env, M68K_FEATURE_SCALED_INDEX);
+    m68k_set_feature(env, M68K_FEATURE_LONG_MULDIV);
+    m68k_set_feature(env, M68K_FEATURE_FPU);
+    m68k_set_feature(env, M68K_FEATURE_CAS);
+}
+#define m68030_cpu_initfn m68020_cpu_initfn
+#define m68040_cpu_initfn m68020_cpu_initfn
+
+static void m68060_cpu_initfn(Object *obj)
+{
+    M68kCPU *cpu = M68K_CPU(obj);
+    CPUM68KState *env = &cpu->env;
+
+    m68k_set_feature(env, M68K_FEATURE_M68000);
+    m68k_set_feature(env, M68K_FEATURE_USP);
+    m68k_set_feature(env, M68K_FEATURE_WORD_INDEX);
+    m68k_set_feature(env, M68K_FEATURE_BRAL);
+    m68k_set_feature(env, M68K_FEATURE_BCCL);
+    m68k_set_feature(env, M68K_FEATURE_BITFIELD);
+    m68k_set_feature(env, M68K_FEATURE_EXT_FULL);
+    m68k_set_feature(env, M68K_FEATURE_SCALED_INDEX);
+    m68k_set_feature(env, M68K_FEATURE_LONG_MULDIV);
+    m68k_set_feature(env, M68K_FEATURE_FPU);
+    m68k_set_feature(env, M68K_FEATURE_CAS);
+}
+
 static void m5208_cpu_initfn(Object *obj)
 {
     M68kCPU *cpu = M68K_CPU(obj);
@@ -140,6 +189,11 @@ typedef struct M68kCPUInfo {
 } M68kCPUInfo;
 
 static const M68kCPUInfo m68k_cpus[] = {
+    { .name = "m68000", .instance_init = m68000_cpu_initfn },
+    { .name = "m68020", .instance_init = m68020_cpu_initfn },
+    { .name = "m68030", .instance_init = m68030_cpu_initfn },
+    { .name = "m68040", .instance_init = m68040_cpu_initfn },
+    { .name = "m68060", .instance_init = m68060_cpu_initfn },
     { .name = "m5206", .instance_init = m5206_cpu_initfn },
     { .name = "m5208", .instance_init = m5208_cpu_initfn },
     { .name = "cfv4e", .instance_init = cfv4e_cpu_initfn },
@@ -206,8 +260,6 @@ static void m68k_cpu_class_init(ObjectClass *c, void *data)
 #else
     cc->get_phys_page_debug = m68k_cpu_get_phys_page_debug;
 #endif
-    cc->cpu_exec_enter = m68k_cpu_exec_enter;
-    cc->cpu_exec_exit = m68k_cpu_exec_exit;
 
     dc->vmsd = &vmstate_m68k_cpu;
     cc->gdb_num_core_regs = 18;
diff --git a/target-m68k/cpu.h b/target-m68k/cpu.h
index 5f165da..7a8eabf 100644
--- a/target-m68k/cpu.h
+++ b/target-m68k/cpu.h
@@ -90,6 +90,9 @@ typedef struct CPUM68KState {
     uint32_t div1;
     uint32_t div2;
 
+    /* Upper 32 bits of a 64bit operand for quad MUL/DIV.  */
+    uint32_t quadh;
+
     /* MMU status.  */
     struct {
         uint32_t ar;
@@ -123,20 +126,31 @@ int cpu_m68k_exec(CPUM68KState *s);
    is returned if the signal was handled by the virtual CPU.  */
 int cpu_m68k_signal_handler(int host_signum, void *pinfo,
                            void *puc);
-void cpu_m68k_flush_flags(CPUM68KState *, int);
 
-enum {
+typedef enum {
     CC_OP_DYNAMIC, /* Use env->cc_op  */
     CC_OP_FLAGS, /* CC_DEST = CVZN, CC_SRC = unused */
+    CC_OP_LOGICB, /* CC_DEST = result, CC_SRC = unused */
+    CC_OP_LOGICW, /* CC_DEST = result, CC_SRC = unused */
     CC_OP_LOGIC, /* CC_DEST = result, CC_SRC = unused */
+    CC_OP_ADDB,   /* CC_DEST = result, CC_SRC = source */
+    CC_OP_ADDW,   /* CC_DEST = result, CC_SRC = source */
     CC_OP_ADD,   /* CC_DEST = result, CC_SRC = source */
+    CC_OP_SUBB,   /* CC_DEST = result, CC_SRC = source */
+    CC_OP_SUBW,   /* CC_DEST = result, CC_SRC = source */
     CC_OP_SUB,   /* CC_DEST = result, CC_SRC = source */
-    CC_OP_CMPB,  /* CC_DEST = result, CC_SRC = source */
-    CC_OP_CMPW,  /* CC_DEST = result, CC_SRC = source */
+    CC_OP_ADDXB,  /* CC_DEST = result, CC_SRC = source */
+    CC_OP_ADDXW,  /* CC_DEST = result, CC_SRC = source */
     CC_OP_ADDX,  /* CC_DEST = result, CC_SRC = source */
+    CC_OP_SUBXB,  /* CC_DEST = result, CC_SRC = source */
+    CC_OP_SUBXW,  /* CC_DEST = result, CC_SRC = source */
     CC_OP_SUBX,  /* CC_DEST = result, CC_SRC = source */
+    CC_OP_SHIFTB, /* CC_DEST = result, CC_SRC = carry */
+    CC_OP_SHIFTW, /* CC_DEST = result, CC_SRC = carry */
     CC_OP_SHIFT, /* CC_DEST = result, CC_SRC = carry */
-};
+
+    CC_OP_NB,
+} CCOp;
 
 #define CCF_C 0x01
 #define CCF_V 0x02
@@ -179,6 +193,7 @@ void do_m68k_semihosting(CPUM68KState *env, int nr);
    ISA revisions mentioned.  */
 
 enum m68k_features {
+    M68K_FEATURE_M68000,
     M68K_FEATURE_CF_ISA_A,
     M68K_FEATURE_CF_ISA_B, /* (ISA B or C).  */
     M68K_FEATURE_CF_ISA_APLUSC, /* BIT/BITREV, FF1, STRLDSR (ISA A+ or C).  */
@@ -189,7 +204,14 @@ enum m68k_features {
     M68K_FEATURE_CF_EMAC_B, /* Revision B EMAC (dual accumulate).  */
     M68K_FEATURE_USP, /* User Stack Pointer.  (ISA A+, B or C).  */
     M68K_FEATURE_EXT_FULL, /* 68020+ full extension word.  */
-    M68K_FEATURE_WORD_INDEX /* word sized address index registers.  */
+    M68K_FEATURE_WORD_INDEX, /* word sized address index registers.  */
+    M68K_FEATURE_SCALED_INDEX, /* scaled address index registers.  */
+    M68K_FEATURE_LONG_MULDIV, /* 32 bit multiply/divide. */
+    M68K_FEATURE_QUAD_MULDIV, /* 64 bit multiply/divide. */
+    M68K_FEATURE_BCCL,        /* Long conditional branches.  */
+    M68K_FEATURE_BITFIELD,    /* Bit field insns.  */
+    M68K_FEATURE_FPU,
+    M68K_FEATURE_CAS
 };
 
 static inline int m68k_feature(CPUM68KState *env, int feature)
@@ -202,8 +224,8 @@ void m68k_cpu_list(FILE *f, fprintf_function cpu_fprintf);
 void register_m68k_insns (CPUM68KState *env);
 
 #ifdef CONFIG_USER_ONLY
-/* Linux uses 8k pages.  */
-#define TARGET_PAGE_BITS 13
+/* Linux uses 4k pages.  */
+#define TARGET_PAGE_BITS 12
 #else
 /* Smallest TLB entry size is 1k.  */
 #define TARGET_PAGE_BITS 10
diff --git a/target-m68k/helper.c b/target-m68k/helper.c
index 2a35de3..1b92b50 100644
--- a/target-m68k/helper.c
+++ b/target-m68k/helper.c
@@ -25,7 +25,47 @@
 
 #define SIGNBIT (1u << 31)
 
-/* Sort alphabetically, except for "any". */
+typedef struct M68kCPUListState {
+    fprintf_function cpu_fprintf;
+    FILE *file;
+} M68kCPUListState;
+
+/* modulo 33 table */
+const uint8_t rox32_table[64] = {
+    0,  1,  2,  3,  4,  5,  6,  7,
+    8,  9, 10, 11, 12, 13, 14, 15,
+   16, 17, 18, 19, 20, 21, 22, 23,
+   24, 25, 26, 27, 28, 29, 30, 31,
+   32,  0,  1,  2,  3,  4,  5,  6,
+    7,  8,  9, 10, 11, 12, 13, 14,
+   15, 16, 17, 18, 19, 20, 21, 22,
+   23, 24, 25, 26, 27, 28, 29, 30,
+};
+
+/* modulo 17 table */
+const uint8_t rox16_table[64] = {
+    0,  1,  2,  3,  4,  5,  6,  7,
+    8,  9, 10, 11, 12, 13, 14, 15,
+   16,  0,  1,  2,  3,  4,  5,  6,
+    7,  8,  9, 10, 11, 12, 13, 14,
+   15, 16,  0,  1,  2,  3,  4,  5,
+    6,  7,  8,  9, 10, 11, 12, 13,
+   14, 15, 16,  0,  1, 2,   3,  4,
+    5,  6,  7,  8,  9, 10, 11, 12,
+};
+
+/* modulo 9 table */
+const uint8_t rox8_table[64] = {
+    0, 1, 2, 3, 4, 5, 6, 7,
+    8, 0, 1, 2, 3, 4, 5, 6,
+    7, 8, 0, 1, 2, 3, 4, 5,
+    6, 7, 8, 0, 1, 2, 3, 4,
+    5, 6, 7, 8, 0, 1, 2, 3,
+    4, 5, 6, 7, 8, 0, 1, 2,
+    3, 4, 5, 6, 7, 8, 0, 1,
+    2, 3, 4, 5, 6, 7, 8, 0,
+};
+
 static gint m68k_cpu_list_compare(gconstpointer a, gconstpointer b)
 {
     ObjectClass *class_a = (ObjectClass *)a;
@@ -135,85 +175,144 @@ void m68k_cpu_init_gdb(M68kCPU *cpu)
     /* TODO: Add [E]MAC registers.  */
 }
 
-void cpu_m68k_flush_flags(CPUM68KState *env, int cc_op)
+static uint32_t cpu_m68k_flush_flags(CPUM68KState *env, int op)
 {
-    M68kCPU *cpu = m68k_env_get_cpu(env);
     int flags;
     uint32_t src;
     uint32_t dest;
     uint32_t tmp;
 
-#define HIGHBIT 0x80000000u
+#define HIGHBIT(type) (1u << (sizeof(type) * 8 - 1))
 
-#define SET_NZ(x) do { \
-    if ((x) == 0) \
-        flags |= CCF_Z; \
-    else if ((int32_t)(x) < 0) \
-        flags |= CCF_N; \
+#define SET_NZ(x, type) do { \
+        if ((type)(x) == 0) { \
+            flags |= CCF_Z; \
+        } else if ((type)(x) < 0) { \
+            flags |= CCF_N; \
+        } \
     } while (0)
 
 #define SET_FLAGS_SUB(type, utype) do { \
-    SET_NZ((type)dest); \
-    tmp = dest + src; \
-    if ((utype) tmp < (utype) src) \
-        flags |= CCF_C; \
-    if ((1u << (sizeof(type) * 8 - 1)) & (tmp ^ dest) & (tmp ^ src)) \
-        flags |= CCF_V; \
+        SET_NZ(dest, type); \
+        tmp = dest + src; \
+        if ((utype) tmp < (utype) src) { \
+            flags |= CCF_C; \
+        } \
+        if (HIGHBIT(type) & (tmp ^ dest) & (tmp ^ src)) { \
+            flags |= CCF_V; \
+        } \
+    } while (0)
+
+#define SET_FLAGS_ADD(type, utype) do { \
+        SET_NZ(dest, type); \
+        if ((utype) dest < (utype) src) { \
+            flags |= CCF_C; \
+        } \
+        tmp = dest - src; \
+        if (HIGHBIT(type) & (src ^ dest) & ~(tmp ^ src)) { \
+            flags |= CCF_V; \
+        } \
+    } while (0)
+
+#define SET_FLAGS_ADDX(type, utype) do { \
+        SET_NZ(dest, type); \
+        if ((utype) dest <= (utype) src) { \
+            flags |= CCF_C; \
+        } \
+        tmp = dest - src - 1; \
+        if (HIGHBIT(type) & (src ^ dest) & ~(tmp ^ src)) { \
+            flags |= CCF_V; \
+        } \
+    } while (0)
+
+#define SET_FLAGS_SUBX(type, utype) do { \
+        SET_NZ(dest, type); \
+        tmp = dest + src + 1; \
+        if ((utype) tmp <= (utype) src) { \
+            flags |= CCF_C; \
+        } \
+        if (HIGHBIT(type) & (tmp ^ dest) & (tmp ^ src)) { \
+            flags |= CCF_V; \
+        } \
+    } while (0)
+
+#define SET_FLAGS_SHIFT(type) do { \
+    SET_NZ(dest, type); \
+    flags |= src; \
     } while (0)
 
     flags = 0;
     src = env->cc_src;
     dest = env->cc_dest;
-    switch (cc_op) {
+    switch (op) {
     case CC_OP_FLAGS:
         flags = dest;
         break;
+    case CC_OP_LOGICB:
+        SET_NZ(dest, int8_t);
+        goto set_x;
+        break;
+    case CC_OP_LOGICW:
+        SET_NZ(dest, int16_t);
+        goto set_x;
+        break;
     case CC_OP_LOGIC:
-        SET_NZ(dest);
+        SET_NZ(dest, int32_t);
+set_x:
+        if (!m68k_feature(env, M68K_FEATURE_M68000)) {
+            /* Unlike m68k, coldfire always clears the overflow bit.  */
+            env->cc_x = 0;
+        }
+        break;
+    case CC_OP_ADDB:
+        SET_FLAGS_ADD(int8_t, uint8_t);
+        break;
+    case CC_OP_ADDW:
+        SET_FLAGS_ADD(int16_t, uint16_t);
         break;
     case CC_OP_ADD:
-        SET_NZ(dest);
-        if (dest < src)
-            flags |= CCF_C;
-        tmp = dest - src;
-        if (HIGHBIT & (src ^ dest) & ~(tmp ^ src))
-            flags |= CCF_V;
+        SET_FLAGS_ADD(int32_t, uint32_t);
+        break;
+    case CC_OP_SUBB:
+        SET_FLAGS_SUB(int8_t, uint8_t);
+        break;
+    case CC_OP_SUBW:
+        SET_FLAGS_SUB(int16_t, uint16_t);
         break;
     case CC_OP_SUB:
         SET_FLAGS_SUB(int32_t, uint32_t);
         break;
-    case CC_OP_CMPB:
-        SET_FLAGS_SUB(int8_t, uint8_t);
+    case CC_OP_ADDXB:
+        SET_FLAGS_ADDX(int8_t, uint8_t);
         break;
-    case CC_OP_CMPW:
-        SET_FLAGS_SUB(int16_t, uint16_t);
+    case CC_OP_ADDXW:
+        SET_FLAGS_ADDX(int16_t, uint16_t);
         break;
     case CC_OP_ADDX:
-        SET_NZ(dest);
-        if (dest <= src)
-            flags |= CCF_C;
-        tmp = dest - src - 1;
-        if (HIGHBIT & (src ^ dest) & ~(tmp ^ src))
-            flags |= CCF_V;
+        SET_FLAGS_ADDX(int32_t, uint32_t);
+        break;
+    case CC_OP_SUBXB:
+        SET_FLAGS_SUBX(int8_t, uint8_t);
+        break;
+    case CC_OP_SUBXW:
+        SET_FLAGS_SUBX(int16_t, uint16_t);
         break;
     case CC_OP_SUBX:
-        SET_NZ(dest);
-        tmp = dest + src + 1;
-        if (tmp <= src)
-            flags |= CCF_C;
-        if (HIGHBIT & (tmp ^ dest) & (tmp ^ src))
-            flags |= CCF_V;
+        SET_FLAGS_SUBX(int32_t, uint32_t);
+        break;
+    case CC_OP_SHIFTB:
+        SET_FLAGS_SHIFT(int8_t);
+        break;
+    case CC_OP_SHIFTW:
+        SET_FLAGS_SHIFT(int16_t);
         break;
     case CC_OP_SHIFT:
-        SET_NZ(dest);
-        if (src)
-            flags |= CCF_C;
+        SET_FLAGS_SHIFT(int32_t);
         break;
     default:
-        cpu_abort(CPU(cpu), "Bad CC_OP %d", cc_op);
+        g_assert_not_reached();
     }
-    env->cc_op = CC_OP_FLAGS;
-    env->cc_dest = flags;
+    return flags;
 }
 
 void HELPER(movec)(CPUM68KState *env, uint32_t reg, uint32_t val)
@@ -352,6 +451,40 @@ uint32_t HELPER(ff1)(uint32_t x)
     return n;
 }
 
+uint32_t HELPER(bfffo)(uint32_t arg, uint32_t width)
+{
+    int n;
+    uint32_t mask;
+    mask = 0x80000000;
+    for (n = 0; n < width; n++) {
+        if (arg & mask) {
+            break;
+        }
+        mask >>= 1;
+    }
+    return n;
+}
+
+uint32_t HELPER(rol32)(uint32_t val, uint32_t shift)
+{
+    uint32_t result;
+    if (shift == 0 || shift == 32) {
+        return val;
+    }
+    result = (val << shift) | (val >> (32 - shift));
+    return result;
+}
+
+uint32_t HELPER(ror32)(uint32_t val, uint32_t shift)
+{
+    uint32_t result;
+    if (shift == 0 || shift == 32) {
+        return val;
+    }
+    result = (val >> shift) | (val << (32 - shift));
+    return result;
+}
+
 uint32_t HELPER(sats)(uint32_t val, uint32_t ccr)
 {
     /* The result has the opposite sign to the original value.  */
@@ -360,53 +493,75 @@ uint32_t HELPER(sats)(uint32_t val, uint32_t ccr)
     return val;
 }
 
-uint32_t HELPER(subx_cc)(CPUM68KState *env, uint32_t op1, uint32_t op2)
-{
-    uint32_t res;
-    uint32_t old_flags;
+#define HELPER_SUBX(type, bits, size) \
+uint32_t HELPER(glue(glue(subx, bits), _cc))(CPUM68KState *env,         \
+                                            uint32_t op1, uint32_t op2) \
+{                                                                       \
+    type res;                                                           \
+    uint32_t old_flags;                                                 \
+    int op;                                                             \
+    old_flags = env->cc_dest;                                           \
+    if (env->cc_x) {                                                    \
+        env->cc_x = ((type)op1 <= (type)op2);                           \
+        op = glue(CC_OP_SUBX, size);                                    \
+        res = (type)op1 - ((type)op2 + 1);                              \
+    } else {                                                            \
+        env->cc_x = ((type)op1 < (type)op2);                            \
+        op = glue(CC_OP_SUB, size);                                     \
+        res = (type)op1 - (type)op2;                                    \
+    }                                                                   \
+    env->cc_dest = res;                                                 \
+    env->cc_src = (type)op2;                                            \
+    env->cc_dest = cpu_m68k_flush_flags(env, op);                       \
+    /* !Z is sticky.  */                                                \
+    env->cc_dest &= (old_flags | ~CCF_Z);                               \
+    return (op1 & ~((1UL << bits) - 1)) | res;                          \
+}
 
-    old_flags = env->cc_dest;
-    if (env->cc_x) {
-        env->cc_x = (op1 <= op2);
-        env->cc_op = CC_OP_SUBX;
-        res = op1 - (op2 + 1);
-    } else {
-        env->cc_x = (op1 < op2);
-        env->cc_op = CC_OP_SUB;
-        res = op1 - op2;
-    }
-    env->cc_dest = res;
-    env->cc_src = op2;
-    cpu_m68k_flush_flags(env, env->cc_op);
-    /* !Z is sticky.  */
-    env->cc_dest &= (old_flags | ~CCF_Z);
-    return res;
+HELPER_SUBX(uint8_t, 8, B)
+HELPER_SUBX(uint16_t, 16, W)
+HELPER_SUBX(uint32_t, 32, )
+
+#define HELPER_ADDX(type, bits, size) \
+uint32_t HELPER(glue(glue(addx, bits), _cc))(CPUM68KState *env,         \
+                                            uint32_t op1, uint32_t op2) \
+{                                                                       \
+    type res;                                                           \
+    uint32_t old_flags;                                                 \
+    int op;                                                             \
+    old_flags = env->cc_dest;                                           \
+    if (env->cc_x) {                                                    \
+        res = (type)op1 + (type)op2 + 1;                                \
+        env->cc_x = (res <= (type)op2);                                 \
+        op = glue(CC_OP_ADDX, size);                                    \
+    } else {                                                            \
+        res = (type)op1 + (type)op2;                                    \
+        env->cc_x = (res < (type)op2);                                  \
+        op = glue(CC_OP_ADD, size);                                     \
+    }                                                                   \
+    env->cc_dest = res;                                                 \
+    env->cc_src = (type)op2;                                            \
+    env->cc_dest = cpu_m68k_flush_flags(env, op);                       \
+    /* !Z is sticky.  */                                                \
+    env->cc_dest &= (old_flags | ~CCF_Z);                               \
+    return (op1 & ~((1UL << bits) - 1)) | res;                          \
 }
 
-uint32_t HELPER(addx_cc)(CPUM68KState *env, uint32_t op1, uint32_t op2)
+HELPER_ADDX(uint8_t, 8, B)
+HELPER_ADDX(uint16_t, 16, W)
+HELPER_ADDX(uint32_t, 32, )
+
+uint32_t HELPER(xflag_lt_i8)(uint32_t a, uint32_t b)
 {
-    uint32_t res;
-    uint32_t old_flags;
+    return (uint8_t)a < (uint8_t)b;
+}
 
-    old_flags = env->cc_dest;
-    if (env->cc_x) {
-        res = op1 + op2 + 1;
-        env->cc_x = (res <= op2);
-        env->cc_op = CC_OP_ADDX;
-    } else {
-        res = op1 + op2;
-        env->cc_x = (res < op2);
-        env->cc_op = CC_OP_ADD;
-    }
-    env->cc_dest = res;
-    env->cc_src = op2;
-    cpu_m68k_flush_flags(env, env->cc_op);
-    /* !Z is sticky.  */
-    env->cc_dest &= (old_flags | ~CCF_Z);
-    return res;
+uint32_t HELPER(xflag_lt_i16)(uint32_t a, uint32_t b)
+{
+    return (uint16_t)a < (uint16_t)b;
 }
 
-uint32_t HELPER(xflag_lt)(uint32_t a, uint32_t b)
+uint32_t HELPER(xflag_lt_i32)(uint32_t a, uint32_t b)
 {
     return a < b;
 }
@@ -417,78 +572,298 @@ void HELPER(set_sr)(CPUM68KState *env, uint32_t val)
     m68k_switch_sp(env);
 }
 
-uint32_t HELPER(shl_cc)(CPUM68KState *env, uint32_t val, uint32_t shift)
-{
-    uint32_t result;
-    uint32_t cf;
-
-    shift &= 63;
-    if (shift == 0) {
-        result = val;
-        cf = env->cc_src & CCF_C;
-    } else if (shift < 32) {
-        result = val << shift;
-        cf = (val >> (32 - shift)) & 1;
-    } else if (shift == 32) {
-        result = 0;
-        cf = val & 1;
-    } else /* shift > 32 */ {
-        result = 0;
-        cf = 0;
-    }
-    env->cc_src = cf;
-    env->cc_x = (cf != 0);
-    env->cc_dest = result;
-    return result;
+#define HELPER_SHL(type, bits) \
+uint32_t HELPER(glue(glue(shl, bits), _cc))(CPUM68KState *env, \
+                                            uint32_t val, uint32_t shift) \
+{ \
+    type result; \
+    uint32_t cf; \
+    shift &= 63; \
+    if (shift == 0) { \
+        result = (type)val; \
+        cf = 0; \
+    } else if (shift < bits) { \
+        result = (type)val << shift; \
+        cf = ((type)val >> (bits - shift)) & 1; \
+    } else if (shift == bits) { \
+        result = 0; \
+        cf = val & 1; \
+    } else { \
+        result = 0; \
+        cf = 0; \
+    } \
+    env->cc_src = cf ? CCF_C : 0; \
+    if (shift) { \
+        env->cc_x = (cf != 0); \
+    } \
+    env->cc_dest = result; \
+    return result; \
 }
 
-uint32_t HELPER(shr_cc)(CPUM68KState *env, uint32_t val, uint32_t shift)
-{
-    uint32_t result;
-    uint32_t cf;
-
-    shift &= 63;
-    if (shift == 0) {
-        result = val;
-        cf = env->cc_src & CCF_C;
-    } else if (shift < 32) {
-        result = val >> shift;
-        cf = (val >> (shift - 1)) & 1;
-    } else if (shift == 32) {
-        result = 0;
-        cf = val >> 31;
-    } else /* shift > 32 */ {
-        result = 0;
-        cf = 0;
-    }
-    env->cc_src = cf;
-    env->cc_x = (cf != 0);
-    env->cc_dest = result;
-    return result;
+HELPER_SHL(uint8_t, 8)
+HELPER_SHL(uint16_t, 16)
+HELPER_SHL(uint32_t, 32)
+
+#define HELPER_SHR(type, bits) \
+uint32_t HELPER(glue(glue(shr, bits), _cc))(CPUM68KState *env, \
+                                            uint32_t val, uint32_t shift) \
+{ \
+    type result; \
+    uint32_t cf; \
+    shift &= 63; \
+    if (shift == 0) { \
+        result = (type)val; \
+        cf = 0; \
+    } else if (shift < bits) { \
+        result = (type)val >> shift; \
+        cf = ((type)val >> (shift - 1)) & 1; \
+    } else if (shift == bits) { \
+        result = 0; \
+        cf = (type)val >> (bits - 1); \
+    } else { \
+        result = 0; \
+        cf = 0; \
+    } \
+    env->cc_src = cf ? CCF_C : 0; \
+    if (shift) { \
+        env->cc_x = (cf != 0); \
+    } \
+    env->cc_dest = result; \
+    return result; \
 }
 
-uint32_t HELPER(sar_cc)(CPUM68KState *env, uint32_t val, uint32_t shift)
-{
-    uint32_t result;
-    uint32_t cf;
-
-    shift &= 63;
-    if (shift == 0) {
-        result = val;
-        cf = (env->cc_src & CCF_C) != 0;
-    } else if (shift < 32) {
-        result = (int32_t)val >> shift;
-        cf = (val >> (shift - 1)) & 1;
-    } else /* shift >= 32 */ {
-        result = (int32_t)val >> 31;
-        cf = val >> 31;
-    }
-    env->cc_src = cf;
-    env->cc_x = cf;
-    env->cc_dest = result;
-    return result;
+HELPER_SHR(uint8_t, 8)
+HELPER_SHR(uint16_t, 16)
+HELPER_SHR(uint32_t, 32)
+
+#define HELPER_SAL(type, bits) \
+uint32_t HELPER(glue(glue(sal, bits), _cc))(CPUM68KState *env, \
+                                            uint32_t val, uint32_t shift) \
+{ \
+    type result; \
+    uint32_t cf; \
+    uint32_t vf; \
+    uint32_t m; \
+    shift &= 63; \
+    if (shift == 0) { \
+        vf = 0; \
+    } else if (shift < bits) { \
+        m = ((1llu << (shift + 1)) - 1) << (bits - shift - 1); \
+        vf = (val & m) != m && (val & m) != 0; \
+    } else { \
+        m = (1llu << bits) - 1; \
+        vf = (val & m) != 0; \
+    } \
+    if (shift == 0) { \
+        result = (type)val; \
+        cf = 0; \
+    } else if (shift < bits) { \
+        result = (type)val << shift; \
+        cf = ((type)val >> (bits - shift)) & 1; \
+    } else if (shift == bits) { \
+        result = 0; \
+        cf = val & 1; \
+    } else { \
+        result = 0; \
+        cf = 0; \
+    } \
+    env->cc_src = (cf ? CCF_C : 0) | (vf ? CCF_V : 0); \
+    if (shift) { \
+        env->cc_x = (cf != 0); \
+    } \
+    env->cc_dest = result; \
+    return result; \
+}
+
+HELPER_SAL(int8_t, 8)
+HELPER_SAL(int16_t, 16)
+HELPER_SAL(int32_t, 32)
+
+#define HELPER_SAR(type, bits) \
+uint32_t HELPER(glue(glue(sar, bits), _cc))(CPUM68KState *env, \
+                                            uint32_t val, uint32_t shift) \
+{ \
+    type result; \
+    uint32_t cf; \
+    shift &= 63; \
+    if (shift == 0) { \
+        result = (type)val; \
+        cf = 0; \
+    } else if (shift < bits) { \
+        result = (type)val >> shift; \
+        cf = ((type)val >> (shift - 1)) & 1; \
+    } else { \
+        result = (type)val >> (bits - 1); \
+        cf = (type)val >> (bits - 1); \
+    } \
+    env->cc_src = cf ? CCF_C : 0; \
+    if (shift) { \
+        env->cc_x = (cf != 0); \
+    } \
+    env->cc_dest = result; \
+    return result; \
 }
 
+HELPER_SAR(int8_t, 8)
+HELPER_SAR(int16_t, 16)
+HELPER_SAR(int32_t, 32)
+
+#define HELPER_ROL(type, bits) \
+uint32_t HELPER(glue(glue(rol, bits), _cc))(CPUM68KState *env, \
+                                            uint32_t val, uint32_t shift) \
+{ \
+    type result; \
+    uint32_t flags; \
+    int count = shift & (bits - 1); \
+    if (count) { \
+        result = ((type)val << count) | ((type)val >> (bits - count)); \
+    } else { \
+        result = (type)val; \
+    } \
+    flags = 0; \
+    if (result == 0) { \
+        flags |= CCF_Z; \
+    } \
+    if (result & (1 << (bits - 1))) { \
+        flags |= CCF_N; \
+    } \
+    if (shift && result & 1) { \
+        flags |= CCF_C; \
+    } \
+    env->cc_dest = flags; \
+    return result; \
+}
+
+HELPER_ROL(uint8_t, 8)
+HELPER_ROL(uint16_t, 16)
+HELPER_ROL(uint32_t, 32)
+
+#define HELPER_ROR(type, bits) \
+uint32_t HELPER(glue(glue(ror, bits), _cc))(CPUM68KState *env, \
+                                            uint32_t val, uint32_t shift) \
+{ \
+    type result; \
+    uint32_t flags; \
+    int count = shift & (bits - 1); \
+    if (count) { \
+        result = ((type)val >> count) | ((type)val << (bits - count)); \
+    } else { \
+        result = (type)val; \
+    } \
+    flags = 0; \
+    if (result == 0) { \
+        flags |= CCF_Z; \
+    } \
+    if (result & (1 << (bits - 1))) { \
+        flags |= CCF_N; \
+    } \
+    if (shift && result & (1 << (bits - 1))) { \
+        flags |= CCF_C; \
+    } \
+    env->cc_dest = flags; \
+    return result; \
+}
+
+HELPER_ROR(uint8_t, 8)
+HELPER_ROR(uint16_t, 16)
+HELPER_ROR(uint32_t, 32)
+
+#define HELPER_ROXR(type, bits) \
+uint32_t HELPER(glue(glue(roxr, bits), _cc))(CPUM68KState *env, \
+                                             uint32_t val, uint32_t shift) \
+{ \
+    type result; \
+    uint32_t flags; \
+    int count = shift; \
+    if (bits == 8) { \
+        count = rox8_table[count]; \
+    } \
+    if (bits == 16) { \
+        count = rox16_table[count]; \
+    } \
+    if (bits == 32) { \
+        count = rox32_table[count]; \
+    } \
+    if (count) { \
+        if (count == bits) { \
+            result = ((type)env->cc_x << (bits - count));\
+        } else { \
+            result = ((type)val >> count) | \
+                     ((type)env->cc_x << (bits - count));\
+        } \
+        if (count > 1) { \
+            result |= (type)val << (bits + 1 - count); \
+        } \
+        env->cc_x = ((type)val >> (count - 1)) & 1; \
+    } else { \
+        result = (type)val; \
+    } \
+    flags = 0; \
+    if (result == 0) { \
+        flags |= CCF_Z; \
+    } \
+    if (result & (1 << (bits - 1))) { \
+        flags |= CCF_N; \
+    } \
+    if (env->cc_x) { \
+        flags |= CCF_C; \
+    } \
+    env->cc_dest = flags; \
+    return result; \
+}
+
+HELPER_ROXR(uint8_t, 8)
+HELPER_ROXR(uint16_t, 16)
+HELPER_ROXR(uint32_t, 32)
+
+#define HELPER_ROXL(type, bits) \
+uint32_t HELPER(glue(glue(roxl, bits), _cc))(CPUM68KState *env, \
+                                             uint32_t val, uint32_t shift) \
+{ \
+    type result; \
+    uint32_t flags; \
+    int count; \
+    count = shift; \
+    if (bits == 8) { \
+        count = rox8_table[count]; \
+    } \
+    if (bits == 16) { \
+        count = rox16_table[count]; \
+    } \
+    if (bits == 32) { \
+        count = rox32_table[count]; \
+    } \
+    if (count) { \
+        if (count == bits) { \
+            result = ((type)env->cc_x << (count - 1)); \
+        } else { \
+            result = ((type)val << count) | ((type)env->cc_x << (count - 1)); \
+        } \
+        if (count > 1) { \
+            result |= (type)val >> (bits + 1 - count); \
+        } \
+        env->cc_x = ((type)val >> (bits - count)) & 1; \
+    } else { \
+        result = (type)val; \
+    } \
+    flags = 0; \
+    if (result == 0) { \
+        flags |= CCF_Z; \
+    } \
+    if (result & (1 << (bits - 1))) { \
+        flags |= CCF_N; \
+    } \
+    if (env->cc_x) { \
+        flags |= CCF_C; \
+    } \
+    env->cc_dest = flags; \
+    return result; \
+}
+
+HELPER_ROXL(uint8_t, 8)
+HELPER_ROXL(uint16_t, 16)
+HELPER_ROXL(uint32_t, 32)
+
 /* FPU helpers.  */
 uint32_t HELPER(f64_to_i32)(CPUM68KState *env, float64 val)
 {
@@ -737,9 +1112,9 @@ void HELPER(mac_set_flags)(CPUM68KState *env, uint32_t acc)
     }
 }
 
-void HELPER(flush_flags)(CPUM68KState *env, uint32_t cc_op)
+uint32_t HELPER(flush_flags)(CPUM68KState *env, uint32_t op)
 {
-    cpu_m68k_flush_flags(env, cc_op);
+    return cpu_m68k_flush_flags(env, op);
 }
 
 uint32_t HELPER(get_macf)(CPUM68KState *env, uint64_t val)
@@ -870,22 +1245,140 @@ void HELPER(set_mac_extu)(CPUM68KState *env, uint32_t val, uint32_t acc)
     env->macc[acc + 1] = res;
 }
 
-void m68k_cpu_exec_enter(CPUState *cs)
+/* load from a bitfield */
+
+uint64_t HELPER(bitfield_load)(uint32_t addr, uint32_t offset, uint32_t width)
 {
-    M68kCPU *cpu = M68K_CPU(cs);
-    CPUM68KState *env = &cpu->env;
+    uint8_t data[8];
+    uint64_t bitfield;
+    int size;
+    int i;
 
-    env->cc_op = CC_OP_FLAGS;
-    env->cc_dest = env->sr & 0xf;
-    env->cc_x = (env->sr >> 4) & 1;
+    size = (offset + width + 7) >> 3;
+#if defined(CONFIG_USER_ONLY)
+    cpu_memory_rw_debug(NULL, (target_ulong)addr, data, size, 0);
+#else
+    cpu_physical_memory_rw(addr, data, size, 0);
+#endif
+
+    bitfield = data[0];
+    for (i = 1; i < 8; i++) {
+        bitfield = (bitfield << 8) | data[i];
+    }
+
+    return bitfield;
 }
 
-void m68k_cpu_exec_exit(CPUState *cs)
+/* store to a bitfield */
+
+void HELPER(bitfield_store)(uint32_t addr, uint32_t offset, uint32_t width,
+                            uint64_t bitfield)
 {
-    M68kCPU *cpu = M68K_CPU(cs);
-    CPUM68KState *env = &cpu->env;
+    uint8_t data[8];
+    int size;
+    int i;
+
+    size = (offset + width + 7) >> 3;
+
+    for (i = 0; i < 8; i++) {
+        data[7 - i] = bitfield;
+        bitfield >>= 8;
+    }
+
+#if defined(CONFIG_USER_ONLY)
+    cpu_memory_rw_debug(NULL, (target_ulong)addr, data, size, 1);
+#else
+    cpu_physical_memory_rw(addr, data, size, 1);
+#endif
+}
+
+uint32_t HELPER(abcd_cc)(CPUM68KState *env, uint32_t src, uint32_t dest)
+{
+    uint16_t hi, lo;
+    uint16_t res;
+    uint32_t flags;
+    int extend = 0;
+
+    flags = env->cc_dest;
+    flags &= ~CCF_C;
+
+    lo = (src & 0x0f) + (dest & 0x0f);
+    if (env->cc_x) {
+        lo++;
+    }
+    hi = (src & 0xf0) + (dest & 0xf0);
+
+    res = hi + lo;
+    if (lo > 9) {
+        res += 0x06;
+    }
+
+    /* C and X flags: set if decimal carry, cleared otherwise */
+
+    if ((res & 0x3F0) > 0x90) {
+        res += 0x60;
+        flags |= CCF_C;
+        extend = 1;
+    }
+
+    /* Z flag: cleared if nonzero */
+
+    if (res & 0xff) {
+        flags &= ~CCF_Z;
+    }
+
+    dest = (dest & 0xffffff00) | (res & 0xff);
+
+    env->cc_x = extend;
+    env->cc_dest = flags;
+
+    return dest;
+}
+
+uint32_t HELPER(sbcd_cc)(CPUM68KState *env, uint32_t src, uint32_t dest)
+{
+    uint16_t hi, lo;
+    uint16_t res;
+    uint32_t flags;
+    int bcd = 0, carry = 0, extend = 0;
+
+    flags = env->cc_dest;
+    flags &= CCF_C;
+
+    if (env->cc_x) {
+        carry = 1;
+    }
+
+    lo = (dest & 0x0f) - (src & 0x0f) - carry;
+    hi = (dest & 0xf0) - (src & 0xf0);
+
+    res = hi + lo;
+    if (lo & 0xf0) {
+        res -= 0x06;
+        bcd = 0x06;
+    }
+
+    if ((((dest & 0xff) - (src & 0xff) - carry) & 0x100) > 0xff) {
+        res -= 0x60;
+    }
+
+    /* C and X flags: set if decimal carry, cleared otherwise */
+
+    if ((((dest & 0xff) - (src & 0xff) - (bcd + carry)) & 0x300) > 0xff) {
+        flags |= CCF_C;
+        extend = 1;
+    }
+
+    /* Z flag: cleared if nonzero */
+
+    if (res & 0xff) {
+        flags &= ~CCF_Z;
+    }
+
+    dest = (dest & 0xffffff00) | (res & 0xff);
+
+    env->cc_x = extend;
+    env->cc_dest = flags;
 
-    cpu_m68k_flush_flags(env, env->cc_op);
-    env->cc_op = CC_OP_FLAGS;
-    env->sr = (env->sr & 0xffe0) | env->cc_dest | (env->cc_x << 4);
+    return dest;
 }
diff --git a/target-m68k/helper.h b/target-m68k/helper.h
index f4e5fdf..5db4278 100644
--- a/target-m68k/helper.h
+++ b/target-m68k/helper.h
@@ -1,14 +1,50 @@
 DEF_HELPER_1(bitrev, i32, i32)
 DEF_HELPER_1(ff1, i32, i32)
+DEF_HELPER_2(bfffo, i32, i32, i32)
+DEF_HELPER_2(rol32, i32, i32, i32)
+DEF_HELPER_2(ror32, i32, i32, i32)
 DEF_HELPER_2(sats, i32, i32, i32)
 DEF_HELPER_2(divu, void, env, i32)
 DEF_HELPER_2(divs, void, env, i32)
-DEF_HELPER_3(addx_cc, i32, env, i32, i32)
-DEF_HELPER_3(subx_cc, i32, env, i32, i32)
-DEF_HELPER_3(shl_cc, i32, env, i32, i32)
-DEF_HELPER_3(shr_cc, i32, env, i32, i32)
-DEF_HELPER_3(sar_cc, i32, env, i32, i32)
-DEF_HELPER_2(xflag_lt, i32, i32, i32)
+DEF_HELPER_1(divu64, void, env)
+DEF_HELPER_1(divs64, void, env)
+DEF_HELPER_3(mulu32_cc, i32, env, i32, i32)
+DEF_HELPER_3(muls32_cc, i32, env, i32, i32)
+DEF_HELPER_3(mulu64, i32, env, i32, i32)
+DEF_HELPER_3(muls64, i32, env, i32, i32)
+DEF_HELPER_3(addx8_cc, i32, env, i32, i32)
+DEF_HELPER_3(addx16_cc, i32, env, i32, i32)
+DEF_HELPER_3(addx32_cc, i32, env, i32, i32)
+DEF_HELPER_3(subx8_cc, i32, env, i32, i32)
+DEF_HELPER_3(subx16_cc, i32, env, i32, i32)
+DEF_HELPER_3(subx32_cc, i32, env, i32, i32)
+DEF_HELPER_3(shl8_cc, i32, env, i32, i32)
+DEF_HELPER_3(shl16_cc, i32, env, i32, i32)
+DEF_HELPER_3(shl32_cc, i32, env, i32, i32)
+DEF_HELPER_3(shr8_cc, i32, env, i32, i32)
+DEF_HELPER_3(shr16_cc, i32, env, i32, i32)
+DEF_HELPER_3(shr32_cc, i32, env, i32, i32)
+DEF_HELPER_3(sal8_cc, i32, env, i32, i32)
+DEF_HELPER_3(sal16_cc, i32, env, i32, i32)
+DEF_HELPER_3(sal32_cc, i32, env, i32, i32)
+DEF_HELPER_3(sar8_cc, i32, env, i32, i32)
+DEF_HELPER_3(sar16_cc, i32, env, i32, i32)
+DEF_HELPER_3(sar32_cc, i32, env, i32, i32)
+DEF_HELPER_3(rol8_cc, i32, env, i32, i32)
+DEF_HELPER_3(rol16_cc, i32, env, i32, i32)
+DEF_HELPER_3(rol32_cc, i32, env, i32, i32)
+DEF_HELPER_3(ror8_cc, i32, env, i32, i32)
+DEF_HELPER_3(ror16_cc, i32, env, i32, i32)
+DEF_HELPER_3(ror32_cc, i32, env, i32, i32)
+DEF_HELPER_3(roxr8_cc, i32, env, i32, i32)
+DEF_HELPER_3(roxr16_cc, i32, env, i32, i32)
+DEF_HELPER_3(roxr32_cc, i32, env, i32, i32)
+DEF_HELPER_3(roxl8_cc, i32, env, i32, i32)
+DEF_HELPER_3(roxl16_cc, i32, env, i32, i32)
+DEF_HELPER_3(roxl32_cc, i32, env, i32, i32)
+DEF_HELPER_2(xflag_lt_i8, i32, i32, i32)
+DEF_HELPER_2(xflag_lt_i16, i32, i32, i32)
+DEF_HELPER_2(xflag_lt_i32, i32, i32, i32)
 DEF_HELPER_2(set_sr, void, env, i32)
 DEF_HELPER_3(movec, void, env, i32, i32)
 
@@ -46,5 +82,11 @@ DEF_HELPER_3(set_mac_extf, void, env, i32, i32)
 DEF_HELPER_3(set_mac_exts, void, env, i32, i32)
 DEF_HELPER_3(set_mac_extu, void, env, i32, i32)
 
-DEF_HELPER_2(flush_flags, void, env, i32)
+DEF_HELPER_2(flush_flags, i32, env, i32)
 DEF_HELPER_2(raise_exception, void, env, i32)
+
+DEF_HELPER_3(bitfield_load, i64, i32, i32, i32)
+DEF_HELPER_4(bitfield_store, void, i32, i32, i32, i64)
+
+DEF_HELPER_3(abcd_cc, i32, env, i32, i32)
+DEF_HELPER_3(sbcd_cc, i32, env, i32, i32)
diff --git a/target-m68k/m68k-qreg.h b/target-m68k/m68k-qreg.h
deleted file mode 100644
index c224d5e..0000000
--- a/target-m68k/m68k-qreg.h
+++ /dev/null
@@ -1,11 +0,0 @@
-enum {
-#define DEFO32(name, offset) QREG_##name,
-#define DEFR(name, reg, mode) QREG_##name,
-#define DEFF64(name, offset) QREG_##name,
-    QREG_NULL,
-#include "qregs.def"
-    TARGET_NUM_QREGS = 0x100
-#undef DEFO32
-#undef DEFR
-#undef DEFF64
-};
diff --git a/target-m68k/op_helper.c b/target-m68k/op_helper.c
index 1af0ca6..f30fa66 100644
--- a/target-m68k/op_helper.c
+++ b/target-m68k/op_helper.c
@@ -193,12 +193,22 @@ void HELPER(divu)(CPUM68KState *env, uint32_t word)
     quot = num / den;
     rem = num % den;
     flags = 0;
-    if (word && quot > 0xffff)
-        flags |= CCF_V;
-    if (quot == 0)
-        flags |= CCF_Z;
-    else if ((int32_t)quot < 0)
-        flags |= CCF_N;
+    /* Avoid using a PARAM1 of zero.  This breaks dyngen because it uses
+       the address of a symbol, and gcc knows symbols can't have address
+       zero.  */
+    if (word && quot > 0xffff) {
+        /* real 68040 keep Z and N on overflow,
+         * whereas documentation says "undefined"
+         */
+        flags |= CCF_V | (env->cc_dest & (CCF_Z|CCF_N));
+    } else {
+        if (quot == 0) {
+            flags |= CCF_Z;
+        } else if ((int16_t)quot < 0) {
+            flags |= CCF_N;
+        }
+    }
+
     env->div1 = quot;
     env->div2 = rem;
     env->cc_dest = flags;
@@ -220,13 +230,163 @@ void HELPER(divs)(CPUM68KState *env, uint32_t word)
     quot = num / den;
     rem = num % den;
     flags = 0;
-    if (word && quot != (int16_t)quot)
+    if (word && quot != (int16_t)quot) {
+        /* real 68040 keep Z and N on overflow,
+         * whereas documentation says "undefined"
+         */
+        flags |= CCF_V | (env->cc_dest & (CCF_Z|CCF_N));
+    } else {
+        if (quot == 0) {
+            flags |= CCF_Z;
+        } else if ((int16_t)quot < 0) {
+            flags |= CCF_N;
+        }
+    }
+
+    env->div1 = quot;
+    env->div2 = rem;
+    env->cc_dest = flags;
+}
+
+void HELPER(divu64)(CPUM68KState *env)
+{
+    uint32_t num;
+    uint32_t den;
+    uint64_t quot;
+    uint32_t rem;
+    uint32_t flags;
+    uint64_t quad;
+
+    num = env->div1;
+    den = env->div2;
+    /* ??? This needs to make sure the throwing location is accurate.  */
+    if (den == 0) {
+        raise_exception(env, EXCP_DIV0);
+    }
+    quad = num | ((uint64_t)env->quadh << 32);
+    quot = quad / den;
+    rem = quad % den;
+    if (quot > 0xffffffffULL) {
+        flags = (env->cc_dest & ~CCF_C) | CCF_V;
+    } else {
+        flags = 0;
+        if (quot == 0) {
+            flags |= CCF_Z;
+        } else if ((int32_t)quot < 0) {
+            flags |= CCF_N;
+        }
+        env->div1 = quot;
+        env->quadh = rem;
+    }
+    env->cc_dest = flags;
+}
+
+void HELPER(divs64)(CPUM68KState *env)
+{
+    uint32_t num;
+    int32_t den;
+    int64_t quot;
+    int32_t rem;
+    int32_t flags;
+    int64_t quad;
+
+    num = env->div1;
+    den = env->div2;
+    if (den == 0) {
+        raise_exception(env, EXCP_DIV0);
+    }
+    quad = num | ((int64_t)env->quadh << 32);
+    quot = quad / (int64_t)den;
+    rem = quad % (int64_t)den;
+
+    if ((quot & 0xffffffff80000000ULL) &&
+        (quot & 0xffffffff80000000ULL) != 0xffffffff80000000ULL) {
+        flags = (env->cc_dest & ~CCF_C) | CCF_V;
+    } else {
+        flags = 0;
+        if (quot == 0) {
+            flags |= CCF_Z;
+        } else if ((int32_t)quot < 0) {
+            flags |= CCF_N;
+        }
+        env->div1 = quot;
+        env->quadh = rem;
+    }
+    env->cc_dest = flags;
+}
+
+uint32_t HELPER(mulu32_cc)(CPUM68KState *env, uint32_t op1, uint32_t op2)
+{
+    uint64_t res = (uint32_t)op1 * op2;
+    uint32_t flags;
+
+    flags = 0;
+    if (res >> 32) {
         flags |= CCF_V;
-    if (quot == 0)
+    }
+    if ((uint32_t)res == 0) {
         flags |= CCF_Z;
-    else if (quot < 0)
+    }
+    if ((int32_t)res < 0) {
         flags |= CCF_N;
-    env->div1 = quot;
-    env->div2 = rem;
+    }
+    env->cc_dest = flags;
+
+    return res;
+}
+
+uint32_t HELPER(muls32_cc)(CPUM68KState *env, uint32_t op1, uint32_t op2)
+{
+    int64_t res = (int32_t)op1 * (int32_t)op2;
+    uint32_t flags;
+
+    flags = 0;
+    if (res != (int64_t)(int32_t)res) {
+        flags |= CCF_V;
+    }
+    if ((uint32_t)res == 0) {
+        flags |= CCF_Z;
+    }
+    if ((int32_t)res < 0) {
+        flags |= CCF_N;
+    }
+    env->cc_dest = flags;
+
+    return res;
+}
+
+uint32_t HELPER(mulu64)(CPUM68KState *env, uint32_t op1, uint32_t op2)
+{
+    uint64_t res = (uint64_t)op1 * op2;
+    uint32_t flags;
+
+    env->quadh = res >> 32;
+    flags = 0;
+    if (res == 0) {
+        flags |= CCF_Z;
+    }
+    if ((int64_t)res < 0) {
+        flags |= CCF_N;
+    }
+    env->cc_dest = flags;
+
+    return res;
+}
+
+uint32_t HELPER(muls64)(CPUM68KState *env, uint32_t op1, uint32_t op2)
+{
+    int64_t res = (uint64_t)(int32_t)op1 * (int32_t)op2;
+    uint32_t flags;
+
+    env->quadh = res >> 32;
+    flags = 0;
+    if (res == 0) {
+        flags |= CCF_Z;
+    }
+    if (res < 0) {
+        flags |= CCF_N;
+    }
     env->cc_dest = flags;
+
+    return res;
 }
diff --git a/target-m68k/qregs.def b/target-m68k/qregs.def
index 204663e..aba6c9a 100644
--- a/target-m68k/qregs.def
+++ b/target-m68k/qregs.def
@@ -7,5 +7,6 @@ DEFO32(CC_SRC, cc_src)
 DEFO32(CC_X, cc_x)
 DEFO32(DIV1, div1)
 DEFO32(DIV2, div2)
+DEFO32(QUADH, quadh)
 DEFO32(MACSR, macsr)
 DEFO32(MAC_MASK, mac_mask)
diff --git a/target-m68k/translate.c b/target-m68k/translate.c
index 96d75bf..a5e68cf 100644
--- a/target-m68k/translate.c
+++ b/target-m68k/translate.c
@@ -56,9 +56,10 @@ static TCGv cpu_aregs[8];
 static TCGv_i64 cpu_fregs[8];
 static TCGv_i64 cpu_macc[4];
 
-#define DREG(insn, pos) cpu_dregs[((insn) >> (pos)) & 7]
-#define AREG(insn, pos) cpu_aregs[((insn) >> (pos)) & 7]
-#define FREG(insn, pos) cpu_fregs[((insn) >> (pos)) & 7]
+#define REG(insn, pos) (((insn) >> (pos)) & 7)
+#define DREG(insn, pos) cpu_dregs[REG(insn, pos)]
+#define AREG(insn, pos) cpu_aregs[REG(insn, pos)]
+#define FREG(insn, pos) cpu_fregs[REG(insn, pos)]
 #define MACREG(acc) cpu_macc[acc]
 #define QREG_SP cpu_aregs[7]
 
@@ -67,6 +68,8 @@ static TCGv NULL_QREG;
 /* Used to distinguish stores from bad addressing modes.  */
 static TCGv store_dummy;
 
+static uint8_t gen_opc_cc_op[OPC_BUF_SIZE];
+
 #include "exec/gen-icount.h"
 
 void m68k_tcg_init(void)
@@ -124,7 +127,7 @@ typedef struct DisasContext {
     target_ulong insn_pc; /* Start of the current instruction.  */
     target_ulong pc;
     int is_jmp;
-    int cc_op;
+    CCOp cc_op; /* Current CC operation */
     int user;
     uint32_t fpcr;
     struct TranslationBlock *tb;
@@ -147,11 +150,13 @@ typedef struct DisasContext {
 static void *gen_throws_exception;
 #define gen_last_qop NULL
 
-#define OS_BYTE 0
-#define OS_WORD 1
-#define OS_LONG 2
-#define OS_SINGLE 4
-#define OS_DOUBLE 5
+#define OS_BYTE     1
+#define OS_WORD     2
+#define OS_LONG     3
+#define OS_SINGLE   4
+#define OS_DOUBLE   5
+#define OS_EXTENDED 6
+#define OS_PACKED   7
 
 typedef void (*disas_proc)(CPUM68KState *env, DisasContext *s, uint16_t insn);
 
@@ -173,6 +178,74 @@ typedef void (*disas_proc)(CPUM68KState *env, DisasContext *s, uint16_t insn);
                              uint16_t insn)
 #endif
 
+enum {
+    USES_CC_DST  = 1,
+    USES_CC_SRC  = 2,
+};
+
+static const uint8_t cc_op_live[CC_OP_NB] = {
+    [CC_OP_DYNAMIC] = USES_CC_DST | USES_CC_SRC,
+    [CC_OP_FLAGS] = USES_CC_DST,
+    [CC_OP_LOGICB ... CC_OP_LOGIC] = USES_CC_DST,
+    [CC_OP_ADDB ... CC_OP_ADD] = USES_CC_DST | USES_CC_SRC,
+    [CC_OP_SUBB ... CC_OP_SUB] = USES_CC_DST | USES_CC_SRC,
+    [CC_OP_ADDXB ... CC_OP_ADDX] = USES_CC_DST | USES_CC_SRC,
+    [CC_OP_SUBXB ... CC_OP_SUBX] = USES_CC_DST | USES_CC_SRC,
+    [CC_OP_SHIFTB ... CC_OP_SHIFT] = USES_CC_DST | USES_CC_SRC,
+};
+
+static void set_cc_op(DisasContext *s, CCOp op)
+{
+    int dead;
+
+    if (s->cc_op == op) {
+        return;
+    }
+
+    /* Discard CC computation that will no longer be used.  */
+
+    dead = cc_op_live[s->cc_op] & ~cc_op_live[op];
+    if (dead & USES_CC_DST) {
+        tcg_gen_discard_i32(QREG_CC_DEST);
+    }
+    if (dead & USES_CC_SRC) {
+        tcg_gen_discard_i32(QREG_CC_SRC);
+    }
+    if (s->cc_op == CC_OP_DYNAMIC) {
+        tcg_gen_discard_i32(QREG_CC_OP);
+    }
+    s->cc_op = op;
+}
+
+/* Update the CPU env CC_OP state.  */
+static inline void update_cc_op(DisasContext *s)
+{
+    if (s->cc_op != CC_OP_DYNAMIC) {
+        tcg_gen_movi_i32(QREG_CC_OP, s->cc_op);
+    }
+}
+
+
+/* Generate a jump to an immediate address.  */
+static void gen_jmp_im(DisasContext *s, uint32_t dest)
+{
+    update_cc_op(s);
+    tcg_gen_movi_i32(QREG_PC, dest);
+    s->is_jmp = DISAS_JUMP;
+}
+
+static void gen_exception(DisasContext *s, uint32_t where, int nr)
+{
+    update_cc_op(s);
+    gen_jmp_im(s, where);
+    gen_helper_raise_exception(cpu_env, tcg_const_i32(nr));
+}
+
+static inline void gen_addr_fault(DisasContext *s)
+{
+    gen_exception(s, s->insn_pc, EXCP_ADDRESS);
+}
+
 /* Generate a load from the specified address.  Narrow values are
    sign extended to full register width.  */
 static inline TCGv gen_load(DisasContext * s, int opsize, TCGv addr, int sign)
@@ -195,7 +268,6 @@ static inline TCGv gen_load(DisasContext * s, int opsize, TCGv addr, int sign)
             tcg_gen_qemu_ld16u(tmp, addr, index);
         break;
     case OS_LONG:
-    case OS_SINGLE:
         tcg_gen_qemu_ld32u(tmp, addr, index);
         break;
     default:
@@ -229,7 +301,6 @@ static inline void gen_store(DisasContext *s, int opsize, TCGv addr, TCGv val)
         tcg_gen_qemu_st16(val, addr, index);
         break;
     case OS_LONG:
-    case OS_SINGLE:
         tcg_gen_qemu_st32(val, addr, index);
         break;
     default:
@@ -265,16 +336,39 @@ static TCGv gen_ldst(DisasContext *s, int opsize, TCGv addr, TCGv val,
     }
 }
 
-/* Read a 32-bit immediate constant.  */
-static inline uint32_t read_im32(CPUM68KState *env, DisasContext *s)
+/* Read an 8-bit immediate constant */
+static inline uint32_t read_im8(CPUM68KState *env, DisasContext *s)
 {
     uint32_t im;
-    im = ((uint32_t)cpu_lduw_code(env, s->pc)) << 16;
+    im = cpu_ldsb_code(env, s->pc + 1);
     s->pc += 2;
-    im |= cpu_lduw_code(env, s->pc);
+    return im;
+}
+/* Read a 16-bit immediate constant */
+static inline uint32_t read_im16(CPUM68KState *env, DisasContext *s)
+{
+    uint32_t im;
+    im = cpu_ldsw_code(env, s->pc);
     s->pc += 2;
     return im;
 }
+/* Read a 32-bit immediate constant.  */
+static inline uint32_t read_im32(CPUM68KState *env, DisasContext *s)
+{
+    uint32_t im;
+    im = read_im16(env, s) << 16;
+    im |= 0xffff & read_im16(env, s);
+    return im;
+}
+
+/* Read a 64-bit immediate constant.  */
+static inline uint64_t read_im64(CPUM68KState *env, DisasContext *s)
+{
+    uint64_t im;
+    im = (uint64_t)read_im32(env, s) << 32;
+    im |= (uint64_t)read_im32(env, s);
+    return im;
+}
 
 /* Calculate and address index.  */
 static TCGv gen_addr_index(uint16_t ext, TCGv tmp)
@@ -297,8 +391,7 @@ static TCGv gen_addr_index(uint16_t ext, TCGv tmp)
 
 /* Handle a base + index + displacement effective addresss.
    A NULL_QREG base means pc-relative.  */
-static TCGv gen_lea_indexed(CPUM68KState *env, DisasContext *s, int opsize,
-                            TCGv base)
+static TCGv gen_lea_indexed(CPUM68KState *env, DisasContext *s, TCGv base)
 {
     uint32_t offset;
     uint16_t ext;
@@ -313,6 +406,11 @@ static TCGv gen_lea_indexed(CPUM68KState *env, DisasContext *s, int opsize,
     if ((ext & 0x800) == 0 && !m68k_feature(s->env, M68K_FEATURE_WORD_INDEX))
         return NULL_QREG;
 
+    if (m68k_feature(s->env, M68K_FEATURE_M68000) &&
+        !m68k_feature(s->env, M68K_FEATURE_SCALED_INDEX)) {
+        ext &= ~(3 << 9);
+    }
+
     if (ext & 0x100) {
         /* full extension word format */
         if (!m68k_feature(s->env, M68K_FEATURE_EXT_FULL))
@@ -321,7 +419,7 @@ static TCGv gen_lea_indexed(CPUM68KState *env, DisasContext *s, int opsize,
         if ((ext & 0x30) > 0x10) {
             /* base displacement */
             if ((ext & 0x30) == 0x20) {
-                bd = (int16_t)cpu_lduw_code(env, s->pc);
+                bd = cpu_ldsw_code(env, s->pc);
                 s->pc += 2;
             } else {
                 bd = read_im32(env, s);
@@ -370,7 +468,7 @@ static TCGv gen_lea_indexed(CPUM68KState *env, DisasContext *s, int opsize,
             if ((ext & 3) > 1) {
                 /* outer displacement */
                 if ((ext & 3) == 2) {
-                    od = (int16_t)cpu_lduw_code(env, s->pc);
+                    od = cpu_ldsw_code(env, s->pc);
                     s->pc += 2;
                 } else {
                     od = read_im32(env, s);
@@ -399,27 +497,50 @@ static TCGv gen_lea_indexed(CPUM68KState *env, DisasContext *s, int opsize,
     return add;
 }
 
-/* Update the CPU env CC_OP state.  */
-static inline void gen_flush_cc_op(DisasContext *s)
-{
-    if (s->cc_op != CC_OP_DYNAMIC)
-        tcg_gen_movi_i32(QREG_CC_OP, s->cc_op);
-}
-
 /* Evaluate all the CC flags.  */
+
 static inline void gen_flush_flags(DisasContext *s)
 {
     if (s->cc_op == CC_OP_FLAGS)
         return;
-    gen_flush_cc_op(s);
-    gen_helper_flush_flags(cpu_env, QREG_CC_OP);
-    s->cc_op = CC_OP_FLAGS;
+    if (s->cc_op == CC_OP_DYNAMIC) {
+        gen_helper_flush_flags(QREG_CC_DEST, cpu_env, QREG_CC_OP);
+    } else {
+        gen_helper_flush_flags(QREG_CC_DEST, cpu_env, tcg_const_i32(s->cc_op));
+    }
+    set_cc_op(s, CC_OP_FLAGS);
 }
 
-static void gen_logic_cc(DisasContext *s, TCGv val)
+#define SET_CC_OP(opsize, op) do { \
+    switch (opsize) { \
+    case OS_BYTE: \
+        set_cc_op(s, CC_OP_##op##B); break; \
+    case OS_WORD: \
+        set_cc_op(s, CC_OP_##op##W); break; \
+    case OS_LONG: \
+        set_cc_op(s, CC_OP_##op); break; \
+    default: \
+        abort(); \
+    } \
+} while (0)
+
+#define SET_X_FLAG(opsize, a, b) do { \
+    switch (opsize) { \
+    case OS_BYTE: \
+        gen_helper_xflag_lt_i8(QREG_CC_X, a, b); break; \
+    case OS_WORD: \
+        gen_helper_xflag_lt_i16(QREG_CC_X, a, b); break; \
+    case OS_LONG: \
+        gen_helper_xflag_lt_i32(QREG_CC_X, a, b); break; \
+    default: \
+        abort(); \
+    } \
+} while (0)
+
+static void gen_logic_cc(DisasContext *s, TCGv val, int opsize)
 {
     tcg_gen_mov_i32(QREG_CC_DEST, val);
-    s->cc_op = CC_OP_LOGIC;
+    SET_CC_OP(opsize, LOGIC);
 }
 
 static void gen_update_cc_add(TCGv dest, TCGv src)
@@ -436,11 +557,49 @@ static inline int opsize_bytes(int opsize)
     case OS_LONG: return 4;
     case OS_SINGLE: return 4;
     case OS_DOUBLE: return 8;
+    case OS_EXTENDED: return 12;
+    case OS_PACKED: return 12;
     default:
         g_assert_not_reached();
     }
 }
 
+static inline int insn_opsize(int insn, int pos)
+{
+    switch ((insn >> pos) & 3) {
+    case 0:
+        return OS_BYTE;
+    case 1:
+        return OS_WORD;
+    case 2:
+        return OS_LONG;
+    default:
+        abort();
+    }
+}
+
+static inline int ext_opsize(int ext, int pos)
+{
+    switch ((ext >> pos) & 7) {
+    case 0:
+        return OS_LONG;
+    case 1:
+        return OS_SINGLE;
+    case 2:
+        return OS_EXTENDED;
+    case 3:
+        return OS_PACKED;
+    case 4:
+        return OS_WORD;
+    case 5:
+        return OS_DOUBLE;
+    case 6:
+        return OS_BYTE;
+    default:
+        abort();
+    }
+}
+
 /* Assign value to a register.  If the width is less than the register width
    only the low part of the register is set.  */
 static void gen_partset_reg(int opsize, TCGv reg, TCGv val)
@@ -489,7 +648,6 @@ static inline TCGv gen_extend(TCGv val, int opsize, int sign)
             tcg_gen_ext16u_i32(tmp, val);
         break;
     case OS_LONG:
-    case OS_SINGLE:
         tmp = val;
         break;
     default:
@@ -523,13 +681,12 @@ static TCGv gen_lea(CPUM68KState *env, DisasContext *s, uint16_t insn,
     case 5: /* Indirect displacement.  */
         reg = AREG(insn, 0);
         tmp = tcg_temp_new();
-        ext = cpu_lduw_code(env, s->pc);
-        s->pc += 2;
+        ext = read_im16(env, s);
         tcg_gen_addi_i32(tmp, reg, (int16_t)ext);
         return tmp;
     case 6: /* Indirect index + displacement.  */
         reg = AREG(insn, 0);
-        return gen_lea_indexed(env, s, opsize, reg);
+        return gen_lea_indexed(env, s, reg);
     case 7: /* Other */
         switch (insn & 7) {
         case 0: /* Absolute short.  */
@@ -545,7 +702,7 @@ static TCGv gen_lea(CPUM68KState *env, DisasContext *s, uint16_t insn,
             s->pc += 2;
             return tcg_const_i32(offset);
         case 3: /* pc index+displacement.  */
-            return gen_lea_indexed(env, s, opsize, NULL_QREG);
+            return gen_lea_indexed(env, s, NULL_QREG);
         case 4: /* Immediate.  */
         default:
             return NULL_QREG;
@@ -802,46 +959,26 @@ DISAS_INSN(scc)
 /* Force a TB lookup after an instruction that changes the CPU state.  */
 static void gen_lookup_tb(DisasContext *s)
 {
-    gen_flush_cc_op(s);
+    update_cc_op(s);
     tcg_gen_movi_i32(QREG_PC, s->pc);
     s->is_jmp = DISAS_UPDATE;
 }
 
-/* Generate a jump to an immediate address.  */
-static void gen_jmp_im(DisasContext *s, uint32_t dest)
-{
-    gen_flush_cc_op(s);
-    tcg_gen_movi_i32(QREG_PC, dest);
-    s->is_jmp = DISAS_JUMP;
-}
-
 /* Generate a jump to the address in qreg DEST.  */
 static void gen_jmp(DisasContext *s, TCGv dest)
 {
-    gen_flush_cc_op(s);
+    update_cc_op(s);
     tcg_gen_mov_i32(QREG_PC, dest);
     s->is_jmp = DISAS_JUMP;
 }
 
-static void gen_exception(DisasContext *s, uint32_t where, int nr)
-{
-    gen_flush_cc_op(s);
-    gen_jmp_im(s, where);
-    gen_helper_raise_exception(cpu_env, tcg_const_i32(nr));
-}
-
-static inline void gen_addr_fault(DisasContext *s)
-{
-    gen_exception(s, s->insn_pc, EXCP_ADDRESS);
-}
-
-#define SRC_EA(env, result, opsize, op_sign, addrp) do {                \
-        result = gen_ea(env, s, insn, opsize, NULL_QREG, addrp,         \
-                        op_sign ? EA_LOADS : EA_LOADU);                 \
-        if (IS_NULL_QREG(result)) {                                     \
-            gen_addr_fault(s);                                          \
-            return;                                                     \
-        }                                                               \
+#define SRC_EA(env, result, opsize, op_sign, addrp) do { \
+    result = gen_ea(env, s, insn, opsize, NULL_QREG, addrp, \
+                    op_sign ? EA_LOADS : EA_LOADU); \
+    if (IS_NULL_QREG(result)) { \
+        gen_addr_fault(s); \
+        return; \
+    } \
     } while (0)
 
 #define DEST_EA(env, insn, opsize, val, addrp) do {                     \
@@ -872,6 +1009,50 @@ static void gen_jmp_tb(DisasContext *s, int n, uint32_t dest)
     s->is_jmp = DISAS_TB_JUMP;
 }
 
+DISAS_INSN(scc_mem)
+{
+    TCGLabel *l1;
+    int cond;
+    TCGv dest;
+
+    l1 = gen_new_label();
+    cond = (insn >> 8) & 0xf;
+    dest = tcg_temp_local_new();
+    tcg_gen_movi_i32(dest, 0);
+    gen_jmpcc(s, cond ^ 1, l1);
+    tcg_gen_movi_i32(dest, 0xff);
+    gen_set_label(l1);
+    DEST_EA(env, insn, OS_BYTE, dest, NULL);
+    tcg_temp_free(dest);
+}
+
+DISAS_INSN(dbcc)
+{
+    TCGLabel *l1;
+    TCGv reg;
+    TCGv tmp;
+    int16_t offset;
+    uint32_t base;
+
+    reg = DREG(insn, 0);
+    base = s->pc;
+    offset = cpu_ldsw_code(env, s->pc);
+    s->pc += 2;
+    l1 = gen_new_label();
+    gen_jmpcc(s, (insn >> 8) & 0xf, l1);
+
+    tmp = tcg_temp_new();
+    tcg_gen_ext16s_i32(tmp, reg);
+    tcg_gen_addi_i32(tmp, tmp, -1);
+    gen_partset_reg(OS_WORD, reg, tmp);
+    tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, -1, l1);
+    update_cc_op(s);
+    gen_jmp_tb(s, 1, base + offset);
+    gen_set_label(l1);
+    update_cc_op(s);
+    gen_jmp_tb(s, 0, s->pc);
+}
+
 DISAS_INSN(undef_mac)
 {
     gen_exception(s, s->pc - 2, EXCP_LINEA);
@@ -907,38 +1088,45 @@ DISAS_INSN(mulw)
     SRC_EA(env, src, OS_WORD, sign, NULL);
     tcg_gen_mul_i32(tmp, tmp, src);
     tcg_gen_mov_i32(reg, tmp);
-    /* Unlike m68k, coldfire always clears the overflow bit.  */
-    gen_logic_cc(s, tmp);
+    gen_logic_cc(s, tmp, OS_WORD);
 }
 
 DISAS_INSN(divw)
 {
-    TCGv reg;
+    TCGv dest;
     TCGv tmp;
     TCGv src;
     int sign;
+    TCGLabel *l1;
 
     sign = (insn & 0x100) != 0;
-    reg = DREG(insn, 9);
-    if (sign) {
-        tcg_gen_ext16s_i32(QREG_DIV1, reg);
-    } else {
-        tcg_gen_ext16u_i32(QREG_DIV1, reg);
-    }
+
+    /* dest.l / src.w */
+
+    dest = DREG(insn, 9);
+    tcg_gen_mov_i32(QREG_DIV1, dest);
+
     SRC_EA(env, src, OS_WORD, sign, NULL);
     tcg_gen_mov_i32(QREG_DIV2, src);
+
+    /* div1 / div2 */
+
     if (sign) {
         gen_helper_divs(cpu_env, tcg_const_i32(1));
     } else {
         gen_helper_divu(cpu_env, tcg_const_i32(1));
     }
 
+    set_cc_op(s, CC_OP_FLAGS);
+
+    l1 = gen_new_label();
+    gen_jmpcc(s, 9 /* V */, l1);
     tmp = tcg_temp_new();
     src = tcg_temp_new();
     tcg_gen_ext16u_i32(tmp, QREG_DIV1);
     tcg_gen_shli_i32(src, QREG_DIV2, 16);
-    tcg_gen_or_i32(reg, tmp, src);
-    s->cc_op = CC_OP_FLAGS;
+    tcg_gen_or_i32(dest, tmp, src);
+    gen_set_label(l1);
 }
 
 DISAS_INSN(divl)
@@ -948,10 +1136,28 @@ DISAS_INSN(divl)
     TCGv reg;
     uint16_t ext;
 
-    ext = cpu_lduw_code(env, s->pc);
-    s->pc += 2;
-    if (ext & 0x87f8) {
-        gen_exception(s, s->pc - 4, EXCP_UNSUPPORTED);
+    ext = read_im16(env, s);
+    if (ext & 0x400) {
+        if (!m68k_feature(s->env, M68K_FEATURE_QUAD_MULDIV)) {
+            gen_exception(s, s->pc - 4, EXCP_UNSUPPORTED);
+            return;
+        }
+        num = DREG(ext, 12);
+        reg = DREG(ext, 0);
+        tcg_gen_mov_i32(QREG_DIV1, num);
+        tcg_gen_mov_i32(QREG_QUADH, reg);
+        SRC_EA(env, den, OS_LONG, 0, NULL);
+        tcg_gen_mov_i32(QREG_DIV2, den);
+        if (ext & 0x0800) {
+            gen_helper_divs64(cpu_env);
+        } else {
+            gen_helper_divu64(cpu_env);
+        }
+        tcg_gen_mov_i32(num, QREG_DIV1);
+        if (!TCGV_EQUAL(num, reg)) {
+            tcg_gen_mov_i32(reg, QREG_QUADH);
+        }
+        set_cc_op(s, CC_OP_FLAGS);
         return;
     }
     num = DREG(ext, 12);
@@ -964,14 +1170,93 @@ DISAS_INSN(divl)
     } else {
         gen_helper_divu(cpu_env, tcg_const_i32(0));
     }
-    if ((ext & 7) == ((ext >> 12) & 7)) {
+    if (TCGV_EQUAL(num, reg) ||
+        m68k_feature(s->env, M68K_FEATURE_LONG_MULDIV)) {
         /* div */
-        tcg_gen_mov_i32 (reg, QREG_DIV1);
-    } else {
+        tcg_gen_mov_i32(num, QREG_DIV1);
+    }
+    if (!TCGV_EQUAL(num, reg)) {
         /* rem */
         tcg_gen_mov_i32 (reg, QREG_DIV2);
     }
-    s->cc_op = CC_OP_FLAGS;
+    set_cc_op(s, CC_OP_FLAGS);
+}
+
+DISAS_INSN(abcd_reg)
+{
+    TCGv src;
+    TCGv dest;
+
+    src = DREG(insn, 0);
+    dest = DREG(insn, 9);
+    gen_flush_flags(s);
+    gen_helper_abcd_cc(dest, cpu_env, src, dest);
+}
+
+DISAS_INSN(abcd_mem)
+{
+    TCGv src;
+    TCGv addr_src;
+    TCGv dest;
+    TCGv addr_dest;
+
+    addr_src = AREG(insn, 0);
+    tcg_gen_subi_i32(addr_src, addr_src, OS_BYTE);
+    src = gen_load(s, OS_BYTE, addr_src, 0);
+
+    addr_dest = AREG(insn, 9);
+    tcg_gen_subi_i32(addr_dest, addr_dest, OS_BYTE);
+    dest = gen_load(s, OS_BYTE, addr_dest, 0);
+
+    gen_flush_flags(s);
+    gen_helper_abcd_cc(dest, cpu_env, src, dest);
+
+    gen_store(s, OS_BYTE, addr_dest, dest);
+}
+
+DISAS_INSN(sbcd_reg)
+{
+    TCGv src;
+    TCGv dest;
+
+    src = DREG(insn, 0);
+    dest = DREG(insn, 9);
+    gen_flush_flags(s);
+    gen_helper_sbcd_cc(dest, cpu_env, src, dest);
+}
+
+DISAS_INSN(sbcd_mem)
+{
+    TCGv src;
+    TCGv addr_src;
+    TCGv dest;
+    TCGv addr_dest;
+
+    addr_src = AREG(insn, 0);
+    tcg_gen_subi_i32(addr_src, addr_src, OS_BYTE);
+    src = gen_load(s, OS_BYTE, addr_src, 0);
+
+    addr_dest = AREG(insn, 9);
+    tcg_gen_subi_i32(addr_dest, addr_dest, OS_BYTE);
+    dest = gen_load(s, OS_BYTE, addr_dest, 0);
+
+    gen_flush_flags(s);
+    gen_helper_sbcd_cc(dest, cpu_env, src, dest);
+
+    gen_store(s, OS_BYTE, addr_dest, dest);
+}
+
+DISAS_INSN(nbcd)
+{
+    TCGv dest;
+    TCGv addr;
+
+    SRC_EA(env, dest, OS_BYTE, -1, &addr);
+
+    gen_flush_flags(s);
+    gen_helper_sbcd_cc(dest, cpu_env, dest, tcg_const_i32(0));
+
+    DEST_EA(env, insn, OS_BYTE, dest, &addr);
 }
 
 DISAS_INSN(addsub)
@@ -982,31 +1267,33 @@ DISAS_INSN(addsub)
     TCGv tmp;
     TCGv addr;
     int add;
+    int opsize;
 
     add = (insn & 0x4000) != 0;
+    opsize = insn_opsize(insn, 6);
     reg = DREG(insn, 9);
     dest = tcg_temp_new();
     if (insn & 0x100) {
-        SRC_EA(env, tmp, OS_LONG, 0, &addr);
+        SRC_EA(env, tmp, opsize, -1, &addr);
         src = reg;
     } else {
         tmp = reg;
-        SRC_EA(env, src, OS_LONG, 0, NULL);
+        SRC_EA(env, src, opsize, -1, NULL);
     }
     if (add) {
         tcg_gen_add_i32(dest, tmp, src);
-        gen_helper_xflag_lt(QREG_CC_X, dest, src);
-        s->cc_op = CC_OP_ADD;
+        SET_X_FLAG(opsize, dest, src);
+        SET_CC_OP(opsize, ADD);
     } else {
-        gen_helper_xflag_lt(QREG_CC_X, tmp, src);
+        SET_X_FLAG(opsize, tmp, src);
         tcg_gen_sub_i32(dest, tmp, src);
-        s->cc_op = CC_OP_SUB;
+        SET_CC_OP(opsize, SUB);
     }
     gen_update_cc_add(dest, src);
     if (insn & 0x100) {
-        DEST_EA(env, insn, OS_LONG, dest, &addr);
+        DEST_EA(env, insn, opsize, dest, &addr);
     } else {
-        tcg_gen_mov_i32(reg, dest);
+        gen_partset_reg(opsize, reg, dest);
     }
 }
 
@@ -1078,7 +1365,7 @@ DISAS_INSN(sats)
     reg = DREG(insn, 0);
     gen_flush_flags(s);
     gen_helper_sats(reg, reg, QREG_CC_DEST);
-    gen_logic_cc(s, reg);
+    gen_logic_cc(s, reg, OS_LONG);
 }
 
 static void gen_push(DisasContext *s, TCGv val)
@@ -1089,6 +1376,7 @@ static void gen_push(DisasContext *s, TCGv val)
     tcg_gen_subi_i32(tmp, QREG_SP, 4);
     gen_store(s, OS_LONG, tmp, val);
     tcg_gen_mov_i32(QREG_SP, tmp);
+    tcg_temp_free(tmp);
 }
 
 DISAS_INSN(movem)
@@ -1099,9 +1387,10 @@ DISAS_INSN(movem)
     TCGv reg;
     TCGv tmp;
     int is_load;
+    int opsize;
+    int32_t incr;
 
-    mask = cpu_lduw_code(env, s->pc);
-    s->pc += 2;
+    mask = read_im16(env, s);
     tmp = gen_lea(env, s, insn, OS_LONG);
     if (IS_NULL_QREG(tmp)) {
         gen_addr_fault(s);
@@ -1110,20 +1399,44 @@ DISAS_INSN(movem)
     addr = tcg_temp_new();
     tcg_gen_mov_i32(addr, tmp);
     is_load = ((insn & 0x0400) != 0);
-    for (i = 0; i < 16; i++, mask >>= 1) {
-        if (mask & 1) {
-            if (i < 8)
-                reg = DREG(i, 0);
-            else
-                reg = AREG(i, 0);
-            if (is_load) {
-                tmp = gen_load(s, OS_LONG, addr, 0);
-                tcg_gen_mov_i32(reg, tmp);
-            } else {
-                gen_store(s, OS_LONG, addr, reg);
+    opsize = (insn & 0x40) != 0 ? OS_LONG : OS_WORD;
+    incr = opsize_bytes(opsize);
+    if (!is_load && (insn & 070) == 040) {
+        for (i = 15; i >= 0; i--, mask >>= 1) {
+            if (mask & 1) {
+                if (i < 8) {
+                    reg = DREG(i, 0);
+                } else {
+                    reg = AREG(i, 0);
+                }
+                gen_store(s, opsize, addr, reg);
+                if (mask != 1) {
+                    tcg_gen_subi_i32(addr, addr, incr);
+                }
+            }
+        }
+        tcg_gen_mov_i32(AREG(insn, 0), addr);
+    } else {
+        for (i = 0; i < 16; i++, mask >>= 1) {
+            if (mask & 1) {
+                if (i < 8) {
+                    reg = DREG(i, 0);
+                } else {
+                    reg = AREG(i, 0);
+                }
+                if (is_load) {
+                    tmp = gen_load(s, opsize, addr, 1);
+                    tcg_gen_mov_i32(reg, tmp);
+                } else {
+                    gen_store(s, opsize, addr, reg);
+                }
+                if (mask != 1 || (insn & 070) == 030) {
+                    tcg_gen_addi_i32(addr, addr, incr);
+                }
             }
-            if (mask != 1)
-                tcg_gen_addi_i32(addr, addr, 4);
+        }
+        if ((insn & 070) == 030) {
+            tcg_gen_mov_i32(AREG(insn, 0), addr);
         }
     }
 }
@@ -1144,8 +1457,7 @@ DISAS_INSN(bitop_im)
         opsize = OS_LONG;
     op = (insn >> 6) & 3;
 
-    bitnum = cpu_lduw_code(env, s->pc);
-    s->pc += 2;
+    bitnum = read_im16(env, s);
     if (bitnum & 0xff00) {
         disas_undef(env, s, insn);
         return;
@@ -1190,57 +1502,188 @@ DISAS_INSN(bitop_im)
     }
 }
 
-DISAS_INSN(arith_im)
+static TCGv gen_get_ccr(DisasContext *s)
 {
-    int op;
-    uint32_t im;
-    TCGv src1;
     TCGv dest;
-    TCGv addr;
 
-    op = (insn >> 9) & 7;
-    SRC_EA(env, src1, OS_LONG, 0, (op == 6) ? NULL : &addr);
-    im = read_im32(env, s);
+    gen_flush_flags(s);
     dest = tcg_temp_new();
-    switch (op) {
-    case 0: /* ori */
-        tcg_gen_ori_i32(dest, src1, im);
-        gen_logic_cc(s, dest);
-        break;
-    case 1: /* andi */
-        tcg_gen_andi_i32(dest, src1, im);
-        gen_logic_cc(s, dest);
-        break;
-    case 2: /* subi */
-        tcg_gen_mov_i32(dest, src1);
-        gen_helper_xflag_lt(QREG_CC_X, dest, tcg_const_i32(im));
-        tcg_gen_subi_i32(dest, dest, im);
-        gen_update_cc_add(dest, tcg_const_i32(im));
-        s->cc_op = CC_OP_SUB;
-        break;
-    case 3: /* addi */
-        tcg_gen_mov_i32(dest, src1);
-        tcg_gen_addi_i32(dest, dest, im);
-        gen_update_cc_add(dest, tcg_const_i32(im));
-        gen_helper_xflag_lt(QREG_CC_X, dest, tcg_const_i32(im));
-        s->cc_op = CC_OP_ADD;
-        break;
-    case 5: /* eori */
-        tcg_gen_xori_i32(dest, src1, im);
-        gen_logic_cc(s, dest);
-        break;
-    case 6: /* cmpi */
+    tcg_gen_shli_i32(dest, QREG_CC_X, 4);
+    tcg_gen_or_i32(dest, dest, QREG_CC_DEST);
+    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(DisasContext *s, TCGv val, int ccr_only)
+{
+    TCGv tmp;
+    tmp = tcg_temp_new();
+    tcg_gen_andi_i32(QREG_CC_DEST, val, 0xf);
+    tcg_gen_shri_i32(tmp, val, 4);
+    tcg_gen_andi_i32(QREG_CC_X, tmp, 1);
+    if (!ccr_only) {
+        gen_helper_set_sr(cpu_env, val);
+    }
+}
+
+DISAS_INSN(arith_im)
+{
+    int op;
+    uint32_t im;
+    TCGv src1;
+    TCGv dest;
+    TCGv addr;
+    int opsize;
+
+    op = (insn >> 9) & 7;
+    opsize = insn_opsize(insn, 6);
+    switch (opsize) {
+    case OS_BYTE:
+        im = read_im8(env, s);
+        break;
+    case OS_WORD:
+        im = read_im16(env, s);
+        break;
+    case OS_LONG:
+        im = read_im32(env, s);
+        break;
+    default:
+       abort();
+    }
+    if ((op == 0 || op == 1) &&
+        (insn & 0x3f) == 0x3c) {
+        if (opsize == OS_BYTE) {
+            src1 = gen_get_ccr(s);
+        } else {
+            if (IS_USER(s)) {
+                gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
+                return;
+            }
+            src1 = gen_get_sr(s);
+        }
+    } else {
+        SRC_EA(env, src1, opsize, -1, (op == 6) ? NULL : &addr);
+    }
+    dest = tcg_temp_new();
+    switch (op) {
+    case 0: /* ori */
+        tcg_gen_ori_i32(dest, src1, im);
+        gen_logic_cc(s, dest, opsize);
+        break;
+    case 1: /* andi */
+        tcg_gen_andi_i32(dest, src1, im);
+        gen_logic_cc(s, dest, opsize);
+        break;
+    case 2: /* subi */
+        tcg_gen_mov_i32(dest, src1);
+        SET_X_FLAG(opsize, dest, tcg_const_i32(im));
+        tcg_gen_subi_i32(dest, dest, im);
+        gen_update_cc_add(dest, tcg_const_i32(im));
+        SET_CC_OP(opsize, SUB);
+        break;
+    case 3: /* addi */
+        tcg_gen_mov_i32(dest, src1);
+        tcg_gen_addi_i32(dest, dest, im);
+        gen_update_cc_add(dest, tcg_const_i32(im));
+        SET_X_FLAG(opsize, dest, tcg_const_i32(im));
+        SET_CC_OP(opsize, ADD);
+        break;
+    case 5: /* eori */
+        tcg_gen_xori_i32(dest, src1, im);
+        gen_logic_cc(s, dest, opsize);
+        break;
+    case 6: /* cmpi */
         tcg_gen_mov_i32(dest, src1);
         tcg_gen_subi_i32(dest, dest, im);
         gen_update_cc_add(dest, tcg_const_i32(im));
-        s->cc_op = CC_OP_SUB;
+        SET_CC_OP(opsize, SUB);
         break;
     default:
         abort();
     }
     if (op != 6) {
-        DEST_EA(env, insn, OS_LONG, dest, &addr);
+        if ((op == 0 || op == 1) &&
+            (insn & 0x3f) == 0x3c) {
+            gen_set_sr(s, dest, opsize == OS_BYTE);
+        } else {
+            DEST_EA(env, insn, opsize, dest, &addr);
+        }
+    }
+}
+
+DISAS_INSN(cas)
+{
+    int opsize;
+    TCGv dest;
+    TCGv tmp;
+    TCGv cmp;
+    TCGv update;
+    TCGv taddr;
+    TCGv addr;
+    TCGv res;
+    uint16_t ext;
+    TCGLabel *l1, *l2;
+
+    if ((insn & 0x3f) == 0x3c) {
+        /* CAS2: Not yet implemented */
+        gen_exception(s, s->pc - 4, EXCP_UNSUPPORTED);
+    }
+
+    switch ((insn >> 9) & 3) {
+    case 1:
+        opsize = OS_BYTE;
+        break;
+    case 2:
+        opsize = OS_WORD;
+        break;
+    case 3:
+        opsize = OS_LONG;
+        break;
+    default:
+        abort();
+    }
+
+    ext = read_im16(env, s);
+    taddr = gen_lea(env, s, insn, opsize);
+    if (IS_NULL_QREG(taddr)) {
+        gen_addr_fault(s);
+        return;
     }
+
+    cmp = DREG(ext, 0);
+    update = DREG(ext, 6);
+    tmp = gen_load(s, opsize, taddr, 0);
+    dest = tcg_temp_local_new();
+    tcg_gen_mov_i32(dest, tmp);
+    addr = tcg_temp_local_new();
+    tcg_gen_mov_i32(addr, taddr);
+
+    res = tcg_temp_new();
+    tcg_gen_sub_i32(res, dest, cmp);
+    gen_logic_cc(s, res, opsize);
+
+    l1 = gen_new_label();
+    l2 = gen_new_label();
+
+    gen_jmpcc(s, 6 /* !Z */, l1);
+    gen_store(s, opsize, addr, update);
+    tcg_gen_br(l2);
+    gen_set_label(l1);
+    tcg_gen_mov_i32(cmp, dest);
+    gen_set_label(l2);
+    tcg_temp_free(dest);
+    tcg_temp_free(addr);
 }
 
 DISAS_INSN(byterev)
@@ -1284,17 +1727,34 @@ DISAS_INSN(move)
         dest_ea = ((insn >> 9) & 7) | (op << 3);
         DEST_EA(env, dest_ea, opsize, src, NULL);
         /* This will be correct because loads sign extend.  */
-        gen_logic_cc(s, src);
+        gen_logic_cc(s, src, opsize);
     }
 }
 
 DISAS_INSN(negx)
 {
-    TCGv reg;
+    TCGv src;
+    TCGv dest;
+    TCGv addr;
+    int opsize;
 
+    opsize = insn_opsize(insn, 6);
     gen_flush_flags(s);
-    reg = DREG(insn, 0);
-    gen_helper_subx_cc(reg, cpu_env, tcg_const_i32(0), reg);
+    SRC_EA(env, src, opsize, -1, &addr);
+    dest = tcg_temp_new();
+    switch (opsize) {
+    case OS_BYTE:
+        gen_helper_subx8_cc(dest, cpu_env, tcg_const_i32(0), src);
+        break;
+    case OS_WORD:
+        gen_helper_subx16_cc(dest, cpu_env, tcg_const_i32(0), src);
+        break;
+    case OS_LONG:
+        gen_helper_subx32_cc(dest, cpu_env, tcg_const_i32(0), src);
+        break;
+    }
+    DEST_EA(env, insn, opsize, dest, &addr);
+    set_cc_op(s, CC_OP_FLAGS);
 }
 
 DISAS_INSN(lea)
@@ -1314,33 +1774,13 @@ DISAS_INSN(lea)
 DISAS_INSN(clr)
 {
     int opsize;
+    TCGv zero;
 
-    switch ((insn >> 6) & 3) {
-    case 0: /* clr.b */
-        opsize = OS_BYTE;
-        break;
-    case 1: /* clr.w */
-        opsize = OS_WORD;
-        break;
-    case 2: /* clr.l */
-        opsize = OS_LONG;
-        break;
-    default:
-        abort();
-    }
-    DEST_EA(env, insn, opsize, tcg_const_i32(0), NULL);
-    gen_logic_cc(s, tcg_const_i32(0));
-}
-
-static TCGv gen_get_ccr(DisasContext *s)
-{
-    TCGv dest;
+    zero = tcg_const_i32(0);
 
-    gen_flush_flags(s);
-    dest = tcg_temp_new();
-    tcg_gen_shli_i32(dest, QREG_CC_X, 4);
-    tcg_gen_or_i32(dest, dest, QREG_CC_DEST);
-    return dest;
+    opsize = insn_opsize(insn, 6);
+    DEST_EA(env, insn, opsize, zero, NULL);
+    gen_logic_cc(s, zero, opsize);
 }
 
 DISAS_INSN(move_from_ccr)
@@ -1355,17 +1795,19 @@ DISAS_INSN(move_from_ccr)
 
 DISAS_INSN(neg)
 {
-    TCGv reg;
     TCGv src1;
+    TCGv dest;
+    TCGv addr;
+    int opsize;
 
-    reg = DREG(insn, 0);
-    src1 = tcg_temp_new();
-    tcg_gen_mov_i32(src1, reg);
-    tcg_gen_neg_i32(reg, src1);
-    s->cc_op = CC_OP_SUB;
-    gen_update_cc_add(reg, src1);
-    gen_helper_xflag_lt(QREG_CC_X, tcg_const_i32(0), src1);
-    s->cc_op = CC_OP_SUB;
+    opsize = insn_opsize(insn, 6);
+    SRC_EA(env, src1, opsize, -1, &addr);
+    dest = tcg_temp_new();
+    tcg_gen_neg_i32(dest, src1);
+    SET_CC_OP(opsize, SUB);
+    gen_update_cc_add(dest, src1);
+    SET_X_FLAG(opsize, tcg_const_i32(0), dest);
+    DEST_EA(env, insn, opsize, dest, &addr);
 }
 
 static void gen_set_sr_im(DisasContext *s, uint16_t val, int ccr_only)
@@ -1375,49 +1817,37 @@ static void gen_set_sr_im(DisasContext *s, uint16_t val, int ccr_only)
     if (!ccr_only) {
         gen_helper_set_sr(cpu_env, tcg_const_i32(val & 0xff00));
     }
+    set_cc_op(s, CC_OP_FLAGS);
 }
 
-static void gen_set_sr(CPUM68KState *env, DisasContext *s, uint16_t insn,
-                       int ccr_only)
+static void gen_move_to_sr(CPUM68KState *env, DisasContext *s, uint16_t insn,
+                           int ccr_only)
 {
-    TCGv tmp;
-    TCGv reg;
+    TCGv src;
 
     s->cc_op = CC_OP_FLAGS;
-    if ((insn & 0x38) == 0)
-      {
-        tmp = tcg_temp_new();
-        reg = DREG(insn, 0);
-        tcg_gen_andi_i32(QREG_CC_DEST, reg, 0xf);
-        tcg_gen_shri_i32(tmp, reg, 4);
-        tcg_gen_andi_i32(QREG_CC_X, tmp, 1);
-        if (!ccr_only) {
-            gen_helper_set_sr(cpu_env, reg);
-        }
-      }
-    else if ((insn & 0x3f) == 0x3c)
-      {
-        uint16_t val;
-        val = cpu_lduw_code(env, s->pc);
-        s->pc += 2;
-        gen_set_sr_im(s, val, ccr_only);
-      }
-    else
-        disas_undef(env, s, insn);
+    SRC_EA(env, src, OS_WORD, 0, NULL);
+    gen_set_sr(s, src, ccr_only);
 }
 
 DISAS_INSN(move_to_ccr)
 {
-    gen_set_sr(env, s, insn, 1);
+    gen_move_to_sr(env, s, insn, 1);
 }
 
 DISAS_INSN(not)
 {
-    TCGv reg;
+    TCGv src1;
+    TCGv dest;
+    TCGv addr;
+    int opsize;
 
-    reg = DREG(insn, 0);
-    tcg_gen_not_i32(reg, reg);
-    gen_logic_cc(s, reg);
+    opsize = insn_opsize(insn, 6);
+    SRC_EA(env, src1, opsize, -1, &addr);
+    dest = tcg_temp_new();
+    tcg_gen_not_i32(dest, src1);
+    DEST_EA(env, insn, opsize, dest, &addr);
+    gen_logic_cc(s, dest, opsize);
 }
 
 DISAS_INSN(swap)
@@ -1432,7 +1862,12 @@ DISAS_INSN(swap)
     tcg_gen_shli_i32(src1, reg, 16);
     tcg_gen_shri_i32(src2, reg, 16);
     tcg_gen_or_i32(reg, src1, src2);
-    gen_logic_cc(s, reg);
+    gen_logic_cc(s, reg, OS_LONG);
+}
+
+DISAS_INSN(bkpt)
+{
+    gen_exception(s, s->pc - 2, EXCP_DEBUG);
 }
 
 DISAS_INSN(pea)
@@ -1464,7 +1899,7 @@ DISAS_INSN(ext)
         gen_partset_reg(OS_WORD, reg, tmp);
     else
         tcg_gen_mov_i32(reg, tmp);
-    gen_logic_cc(s, tmp);
+    gen_logic_cc(s, tmp, OS_LONG);
 }
 
 DISAS_INSN(tst)
@@ -1472,21 +1907,9 @@ DISAS_INSN(tst)
     int opsize;
     TCGv tmp;
 
-    switch ((insn >> 6) & 3) {
-    case 0: /* tst.b */
-        opsize = OS_BYTE;
-        break;
-    case 1: /* tst.w */
-        opsize = OS_WORD;
-        break;
-    case 2: /* tst.l */
-        opsize = OS_LONG;
-        break;
-    default:
-        abort();
-    }
-    SRC_EA(env, tmp, opsize, 1, NULL);
-    gen_logic_cc(s, tmp);
+    opsize = insn_opsize(insn, 6);
+    SRC_EA(env, tmp, opsize, -1, NULL);
+    gen_logic_cc(s, tmp, opsize);
 }
 
 DISAS_INSN(pulse)
@@ -1508,7 +1931,7 @@ DISAS_INSN(tas)
 
     dest = tcg_temp_new();
     SRC_EA(env, src1, OS_BYTE, 1, &addr);
-    gen_logic_cc(s, src1);
+    gen_logic_cc(s, src1, OS_BYTE);
     tcg_gen_ori_i32(dest, src1, 0x80);
     DEST_EA(env, insn, OS_BYTE, dest, &addr);
 }
@@ -1519,22 +1942,45 @@ DISAS_INSN(mull)
     TCGv reg;
     TCGv src1;
     TCGv dest;
+    TCGv regh;
 
     /* The upper 32 bits of the product are discarded, so
        muls.l and mulu.l are functionally equivalent.  */
-    ext = cpu_lduw_code(env, s->pc);
-    s->pc += 2;
-    if (ext & 0x87ff) {
-        gen_exception(s, s->pc - 4, EXCP_UNSUPPORTED);
+    ext = read_im16(env, s);
+    if (ext & 0x400) {
+        if (!m68k_feature(s->env, M68K_FEATURE_QUAD_MULDIV)) {
+            gen_exception(s, s->pc - 4, EXCP_UNSUPPORTED);
+            return;
+        }
+        reg = DREG(ext, 12);
+        regh = DREG(ext, 0);
+        SRC_EA(env, src1, OS_LONG, 0, NULL);
+        dest = tcg_temp_new();
+        if (ext & 0x800) {
+            gen_helper_muls64(dest, cpu_env, src1, reg);
+        } else {
+            gen_helper_mulu64(dest, cpu_env, src1, reg);
+        }
+        tcg_gen_mov_i32(reg, dest);
+        tcg_gen_mov_i32(regh, QREG_QUADH);
+        set_cc_op(s, CC_OP_FLAGS);
         return;
     }
     reg = DREG(ext, 12);
     SRC_EA(env, src1, OS_LONG, 0, NULL);
     dest = tcg_temp_new();
-    tcg_gen_mul_i32(dest, src1, reg);
+    if (m68k_feature(s->env, M68K_FEATURE_M68000)) {
+        if (ext & 0x800) {
+            gen_helper_muls32_cc(dest, cpu_env, src1, reg);
+        } else {
+            gen_helper_mulu32_cc(dest, cpu_env, src1, reg);
+        }
+        set_cc_op(s, CC_OP_FLAGS);
+    } else {
+        tcg_gen_mul_i32(dest, src1, reg);
+    }
+    gen_logic_cc(s, dest, OS_LONG);
     tcg_gen_mov_i32(reg, dest);
-    /* Unlike m68k, coldfire always clears the overflow bit.  */
-    gen_logic_cc(s, dest);
 }
 
 DISAS_INSN(link)
@@ -1543,8 +1989,24 @@ DISAS_INSN(link)
     TCGv reg;
     TCGv tmp;
 
-    offset = cpu_ldsw_code(env, s->pc);
-    s->pc += 2;
+    offset = read_im16(env, s);
+    reg = AREG(insn, 0);
+    tmp = tcg_temp_new();
+    tcg_gen_subi_i32(tmp, QREG_SP, 4);
+    gen_store(s, OS_LONG, tmp, reg);
+    if ((insn & 7) != 7) {
+        tcg_gen_mov_i32(reg, tmp);
+    }
+    tcg_gen_addi_i32(QREG_SP, tmp, offset);
+}
+
+DISAS_INSN(linkl)
+{
+    int32_t offset;
+    TCGv reg;
+    TCGv tmp;
+
+    offset = read_im32(env, s);
     reg = AREG(insn, 0);
     tmp = tcg_temp_new();
     tcg_gen_subi_i32(tmp, QREG_SP, 4);
@@ -1601,40 +2063,48 @@ DISAS_INSN(jump)
 
 DISAS_INSN(addsubq)
 {
-    TCGv src1;
-    TCGv src2;
+    TCGv src;
     TCGv dest;
-    int val;
+    TCGv val;
+    int imm;
     TCGv addr;
+    int opsize;
 
-    SRC_EA(env, src1, OS_LONG, 0, &addr);
-    val = (insn >> 9) & 7;
-    if (val == 0)
-        val = 8;
+    if ((insn & 070) == 010) {
+        /* Operation on address register is always long.  */
+        opsize = OS_LONG;
+    } else {
+        opsize = insn_opsize(insn, 6);
+    }
+    SRC_EA(env, src, opsize, -1, &addr);
+    imm = (insn >> 9) & 7;
+    if (imm == 0) {
+        imm = 8;
+    }
+    val = tcg_const_i32(imm);
     dest = tcg_temp_new();
-    tcg_gen_mov_i32(dest, src1);
+    tcg_gen_mov_i32(dest, src);
     if ((insn & 0x38) == 0x08) {
         /* Don't update condition codes if the destination is an
            address register.  */
         if (insn & 0x0100) {
-            tcg_gen_subi_i32(dest, dest, val);
+            tcg_gen_sub_i32(dest, dest, val);
         } else {
-            tcg_gen_addi_i32(dest, dest, val);
+            tcg_gen_add_i32(dest, dest, val);
         }
     } else {
-        src2 = tcg_const_i32(val);
         if (insn & 0x0100) {
-            gen_helper_xflag_lt(QREG_CC_X, dest, src2);
-            tcg_gen_subi_i32(dest, dest, val);
-            s->cc_op = CC_OP_SUB;
+            SET_X_FLAG(opsize, dest, val);
+            tcg_gen_sub_i32(dest, dest, val);
+            SET_CC_OP(opsize, SUB);
         } else {
-            tcg_gen_addi_i32(dest, dest, val);
-            gen_helper_xflag_lt(QREG_CC_X, dest, src2);
-            s->cc_op = CC_OP_ADD;
+            tcg_gen_add_i32(dest, dest, val);
+            SET_X_FLAG(opsize, dest, val);
+            SET_CC_OP(opsize, ADD);
         }
-        gen_update_cc_add(dest, src2);
+        gen_update_cc_add(dest, val);
     }
-    DEST_EA(env, insn, OS_LONG, dest, &addr);
+    DEST_EA(env, insn, opsize, dest, &addr);
 }
 
 DISAS_INSN(tpf)
@@ -1673,27 +2143,29 @@ DISAS_INSN(branch)
         /* bsr */
         gen_push(s, tcg_const_i32(s->pc));
     }
-    gen_flush_cc_op(s);
     if (op > 1) {
         /* Bcc */
         l1 = gen_new_label();
         gen_jmpcc(s, ((insn >> 8) & 0xf) ^ 1, l1);
+        update_cc_op(s);
         gen_jmp_tb(s, 1, base + offset);
         gen_set_label(l1);
+        update_cc_op(s);
         gen_jmp_tb(s, 0, s->pc);
     } else {
         /* Unconditional branch.  */
+        update_cc_op(s);
         gen_jmp_tb(s, 0, base + offset);
     }
 }
 
 DISAS_INSN(moveq)
 {
-    uint32_t val;
+    TCGv val;
 
-    val = (int8_t)insn;
-    tcg_gen_movi_i32(DREG(insn, 9), val);
-    gen_logic_cc(s, tcg_const_i32(val));
+    val = tcg_const_i32((int8_t)insn);
+    tcg_gen_mov_i32(DREG(insn, 9), val);
+    gen_logic_cc(s, val, OS_LONG);
 }
 
 DISAS_INSN(mvzs)
@@ -1709,254 +2181,1068 @@ DISAS_INSN(mvzs)
     SRC_EA(env, src, opsize, (insn & 0x80) == 0, NULL);
     reg = DREG(insn, 9);
     tcg_gen_mov_i32(reg, src);
-    gen_logic_cc(s, src);
+    gen_logic_cc(s, src, opsize);
 }
 
 DISAS_INSN(or)
 {
-    TCGv reg;
-    TCGv dest;
+    TCGv reg;
+    TCGv dest;
+    TCGv src;
+    TCGv addr;
+    int opsize;
+
+    opsize = insn_opsize(insn, 6);
+    reg = DREG(insn, 9);
+    dest = tcg_temp_new();
+    if (insn & 0x100) {
+        SRC_EA(env, src, opsize, -1, &addr);
+        tcg_gen_or_i32(dest, src, reg);
+        DEST_EA(env, insn, opsize, dest, &addr);
+    } else {
+        SRC_EA(env, src, opsize, -1, NULL);
+        tcg_gen_or_i32(dest, src, reg);
+        gen_partset_reg(opsize, reg, dest);
+    }
+    gen_logic_cc(s, dest, opsize);
+}
+
+DISAS_INSN(suba)
+{
+    TCGv src;
+    TCGv reg;
+
+    SRC_EA(env, src, (insn & 0x100) ? OS_LONG : OS_WORD, -1, NULL);
+    reg = AREG(insn, 9);
+    tcg_gen_sub_i32(reg, reg, src);
+}
+
+DISAS_INSN(subx_reg)
+{
+    TCGv reg;
+    TCGv src;
+    int opsize;
+
+    opsize = insn_opsize(insn, 6);
+
+    gen_flush_flags(s);
+    reg = DREG(insn, 9);
+    src = DREG(insn, 0);
+    switch (opsize) {
+    case OS_BYTE:
+        gen_helper_subx8_cc(reg, cpu_env, reg, src);
+        break;
+    case OS_WORD:
+        gen_helper_subx16_cc(reg, cpu_env, reg, src);
+        break;
+    case OS_LONG:
+        gen_helper_subx32_cc(reg, cpu_env, reg, src);
+        break;
+    }
+    set_cc_op(s, CC_OP_FLAGS);
+}
+
+DISAS_INSN(subx_mem)
+{
+    TCGv src;
+    TCGv addr_src;
+    TCGv reg;
+    TCGv addr_reg;
+    int opsize;
+
+    opsize = insn_opsize(insn, 6);
+
+    gen_flush_flags(s);
+
+    addr_src = AREG(insn, 0);
+    tcg_gen_subi_i32(addr_src, addr_src, opsize);
+    src = gen_load(s, opsize, addr_src, 0);
+
+    addr_reg = AREG(insn, 9);
+    tcg_gen_subi_i32(addr_reg, addr_reg, opsize);
+    reg = gen_load(s, opsize, addr_reg, 0);
+
+    switch (opsize) {
+    case OS_BYTE:
+        gen_helper_subx8_cc(reg, cpu_env, reg, src);
+        break;
+    case OS_WORD:
+        gen_helper_subx16_cc(reg, cpu_env, reg, src);
+        break;
+    case OS_LONG:
+        gen_helper_subx32_cc(reg, cpu_env, reg, src);
+        break;
+    }
+    set_cc_op(s, CC_OP_FLAGS);
+
+    gen_store(s, opsize, addr_reg, reg);
+}
+
+DISAS_INSN(mov3q)
+{
+    TCGv src;
+    int val;
+
+    val = (insn >> 9) & 7;
+    if (val == 0) {
+        val = -1;
+    }
+    src = tcg_const_i32(val);
+    gen_logic_cc(s, src, OS_LONG);
+    DEST_EA(env, insn, OS_LONG, src, NULL);
+}
+
+DISAS_INSN(cmp)
+{
+    TCGv src;
+    TCGv reg;
+    TCGv dest;
+    int opsize;
+
+    opsize = insn_opsize(insn, 6);
+    SRC_EA(env, src, opsize, -1, NULL);
+    reg = DREG(insn, 9);
+    dest = tcg_temp_new();
+    tcg_gen_sub_i32(dest, reg, src);
+    gen_update_cc_add(dest, src);
+    SET_CC_OP(opsize, SUB);
+}
+
+DISAS_INSN(cmpa)
+{
+    int opsize;
+    TCGv src;
+    TCGv reg;
+    TCGv dest;
+
+    if (insn & 0x100) {
+        opsize = OS_LONG;
+    } else {
+        opsize = OS_WORD;
+    }
+    SRC_EA(env, src, opsize, 1, NULL);
+    reg = AREG(insn, 9);
+    dest = tcg_temp_new();
+    tcg_gen_sub_i32(dest, reg, src);
+    gen_update_cc_add(dest, src);
+    SET_CC_OP(OS_LONG, SUB);
+}
+
+DISAS_INSN(eor)
+{
+    TCGv src;
+    TCGv reg;
+    TCGv dest;
+    TCGv addr;
+    int opsize;
+
+    opsize = insn_opsize(insn, 6);
+
+    if (((insn >> 3) & 7) == 1) {
+        /* cmpm */
+        reg = AREG(insn, 0);
+        src = gen_load(s, opsize, reg, 1);
+        tcg_gen_addi_i32(reg, reg, opsize_bytes(opsize));
+
+        reg = AREG(insn, 9);
+        dest = gen_load(s, opsize, reg, 1);
+        tcg_gen_addi_i32(reg, reg, opsize_bytes(opsize));
+
+        reg = tcg_temp_new();
+        tcg_gen_sub_i32(reg, dest, src);
+        gen_update_cc_add(reg, src);
+        SET_CC_OP(opsize, SUB);
+
+        return;
+    }
+
+    SRC_EA(env, src, opsize, -1, &addr);
+    reg = DREG(insn, 9);
+    dest = tcg_temp_new();
+    tcg_gen_xor_i32(dest, src, reg);
+    gen_logic_cc(s, dest, opsize);
+    DEST_EA(env, insn, opsize, dest, &addr);
+}
+
+DISAS_INSN(and)
+{
+    TCGv src;
+    TCGv reg;
+    TCGv dest;
+    TCGv addr;
+    int opsize;
+    int exg_mode;
+
+    dest = tcg_temp_new();
+
+    /* exg */
+
+    exg_mode = insn & 0x1f8;
+    if (exg_mode == 0x140) {
+        /* exchange Dx and Dy */
+        src = DREG(insn, 9);
+        reg = DREG(insn, 0);
+        tcg_gen_mov_i32(dest, src);
+        tcg_gen_mov_i32(src, reg);
+        tcg_gen_mov_i32(reg, dest);
+        return;
+    } else if (exg_mode == 0x148) {
+        /* exchange Ax and Ay */
+        src = AREG(insn, 9);
+        reg = AREG(insn, 0);
+        tcg_gen_mov_i32(dest, src);
+        tcg_gen_mov_i32(src, reg);
+        tcg_gen_mov_i32(reg, dest);
+        return;
+    } else if (exg_mode == 0x188) {
+        /* exchange Dx and Ay */
+        src = DREG(insn, 9);
+        reg = AREG(insn, 0);
+        tcg_gen_mov_i32(dest, src);
+        tcg_gen_mov_i32(src, reg);
+        tcg_gen_mov_i32(reg, dest);
+        return;
+    }
+    /* and */
+    opsize = insn_opsize(insn, 6);
+    reg = DREG(insn, 9);
+    if (insn & 0x100) {
+        SRC_EA(env, src, opsize, -1, &addr);
+        tcg_gen_and_i32(dest, src, reg);
+        DEST_EA(env, insn, opsize, dest, &addr);
+    } else {
+        SRC_EA(env, src, opsize, -1, NULL);
+        tcg_gen_and_i32(dest, src, reg);
+        gen_partset_reg(opsize, reg, dest);
+    }
+    gen_logic_cc(s, dest, opsize);
+}
+
+DISAS_INSN(adda)
+{
+    TCGv src;
+    TCGv reg;
+
+    SRC_EA(env, src, (insn & 0x100) ? OS_LONG : OS_WORD, -1, NULL);
+    reg = AREG(insn, 9);
+    tcg_gen_add_i32(reg, reg, src);
+}
+
+DISAS_INSN(addx_reg)
+{
+    TCGv reg;
+    TCGv src;
+    int opsize;
+
+    opsize = insn_opsize(insn, 6);
+
+    gen_flush_flags(s);
+    reg = DREG(insn, 9);
+    src = DREG(insn, 0);
+    switch (opsize) {
+    case OS_BYTE:
+        gen_helper_addx8_cc(reg, cpu_env, reg, src);
+        break;
+    case OS_WORD:
+        gen_helper_addx16_cc(reg, cpu_env, reg, src);
+        break;
+    case OS_LONG:
+        gen_helper_addx32_cc(reg, cpu_env, reg, src);
+        break;
+    }
+    set_cc_op(s, CC_OP_FLAGS);
+}
+
+DISAS_INSN(addx_mem)
+{
+    TCGv src;
+    TCGv addr_src;
+    TCGv reg;
+    TCGv addr_reg;
+    int opsize;
+
+    opsize = insn_opsize(insn, 6);
+
+    gen_flush_flags(s);
+
+    addr_src = AREG(insn, 0);
+    tcg_gen_subi_i32(addr_src, addr_src, opsize);
+    src = gen_load(s, opsize, addr_src, 0);
+
+    addr_reg = AREG(insn, 9);
+    tcg_gen_subi_i32(addr_reg, addr_reg, opsize);
+    reg = gen_load(s, opsize, addr_reg, 0);
+
+    switch (opsize) {
+    case OS_BYTE:
+        gen_helper_addx8_cc(reg, cpu_env, reg, src);
+        break;
+    case OS_WORD:
+        gen_helper_addx16_cc(reg, cpu_env, reg, src);
+        break;
+    case OS_LONG:
+        gen_helper_addx32_cc(reg, cpu_env, reg, src);
+        break;
+    }
+
+    gen_store(s, opsize, addr_reg, reg);
+    set_cc_op(s, CC_OP_FLAGS);
+}
+
+/* TODO: This could be implemented without helper functions.  */
+DISAS_INSN(shift8_im)
+{
+    TCGv reg;
+    int tmp;
+    TCGv shift;
+    TCGv dest;
+
+    reg = DREG(insn, 0);
+    tmp = (insn >> 9) & 7;
+    if (tmp == 0) {
+        tmp = 8;
+    }
+    shift = tcg_const_i32(tmp);
+    dest = tcg_temp_new_i32();
+    /* No need to flush flags becuse we know we will set C flag.  */
+    if (insn & 0x100) {
+        if (insn & 8) {
+            gen_helper_shl8_cc(dest, cpu_env, reg, shift);
+        } else {
+            gen_helper_sal8_cc(dest, cpu_env, reg, shift);
+        }
+    } else {
+        if (insn & 8) {
+            gen_helper_shr8_cc(dest, cpu_env, reg, shift);
+        } else {
+            gen_helper_sar8_cc(dest, cpu_env, reg, shift);
+        }
+    }
+    gen_partset_reg(OS_BYTE, reg, dest);
+    set_cc_op(s, CC_OP_SHIFTB);
+}
+
+/* TODO: This could be implemented without helper functions.  */
+DISAS_INSN(shift16_im)
+{
+    TCGv reg;
+    int tmp;
+    TCGv shift;
+    TCGv dest;
+
+    reg = DREG(insn, 0);
+    tmp = (insn >> 9) & 7;
+    if (tmp == 0) {
+        tmp = 8;
+    }
+    shift = tcg_const_i32(tmp);
+    dest = tcg_temp_new_i32();
+    /* No need to flush flags becuse we know we will set C flag.  */
+    if (insn & 0x100) {
+        if (insn & 8) {
+            gen_helper_shl16_cc(dest, cpu_env, reg, shift);
+        } else {
+            gen_helper_sal16_cc(dest, cpu_env, reg, shift);
+        }
+    } else {
+        if (insn & 8) {
+            gen_helper_shr16_cc(dest, cpu_env, reg, shift);
+        } else {
+            gen_helper_sar16_cc(dest, cpu_env, reg, shift);
+        }
+    }
+    gen_partset_reg(OS_WORD, reg, dest);
+    set_cc_op(s, CC_OP_SHIFTW);
+}
+
+
+/* TODO: This could be implemented without helper functions.  */
+DISAS_INSN(shift_im)
+{
+    TCGv reg;
+    int tmp;
+    TCGv shift;
+
+    reg = DREG(insn, 0);
+    tmp = (insn >> 9) & 7;
+    if (tmp == 0) {
+        tmp = 8;
+    }
+    shift = tcg_const_i32(tmp);
+    /* No need to flush flags becuse we know we will set C flag.  */
+    if (insn & 0x100) {
+        if (insn & 8) {
+            gen_helper_shl32_cc(reg, cpu_env, reg, shift);
+        } else {
+            gen_helper_sal32_cc(reg, cpu_env, reg, shift);
+        }
+    } else {
+        if (insn & 8) {
+            gen_helper_shr32_cc(reg, cpu_env, reg, shift);
+        } else {
+            gen_helper_sar32_cc(reg, cpu_env, reg, shift);
+        }
+    }
+    set_cc_op(s, CC_OP_SHIFT);
+}
+
+DISAS_INSN(shift8_reg)
+{
+    TCGv reg;
+    TCGv shift;
+    TCGv dest;
+    TCGv tmp;
+
+    reg = DREG(insn, 0);
+    shift = DREG(insn, 9);
+    tmp = tcg_temp_new_i32();
+    tcg_gen_andi_i32(tmp, shift, 63);
+    dest = tcg_temp_new_i32();
+    /* Shift by zero leaves C flag unmodified.   */
+    if (insn & 0x100) {
+        if (insn & 8) {
+            gen_helper_shl8_cc(dest, cpu_env, reg, tmp);
+        } else {
+            gen_helper_sal8_cc(dest, cpu_env, reg, tmp);
+        }
+    } else {
+        if (insn & 8) {
+            gen_helper_shr8_cc(dest, cpu_env, reg, tmp);
+        } else {
+            gen_helper_sar8_cc(dest, cpu_env, reg, tmp);
+        }
+    }
+    gen_partset_reg(OS_BYTE, reg, dest);
+    set_cc_op(s, CC_OP_SHIFTB);
+}
+
+DISAS_INSN(shift16_reg)
+{
+    TCGv reg;
+    TCGv shift;
+    TCGv dest;
+    TCGv tmp;
+
+    reg = DREG(insn, 0);
+    shift = DREG(insn, 9);
+    tmp = tcg_temp_new_i32();
+    tcg_gen_andi_i32(tmp, shift, 63);
+    dest = tcg_temp_new_i32();
+    /* Shift by zero leaves C flag unmodified.   */
+    if (insn & 0x100) {
+        if (insn & 8) {
+            gen_helper_shl16_cc(dest, cpu_env, reg, tmp);
+        } else {
+            gen_helper_sal16_cc(dest, cpu_env, reg, tmp);
+        }
+    } else {
+        if (insn & 8) {
+            gen_helper_shr16_cc(dest, cpu_env, reg, tmp);
+        } else {
+            gen_helper_sar16_cc(dest, cpu_env, reg, tmp);
+        }
+    }
+    gen_partset_reg(OS_WORD, reg, dest);
+    set_cc_op(s, CC_OP_SHIFTW);
+}
+
+DISAS_INSN(shift_reg)
+{
+    TCGv reg;
+    TCGv shift;
+
+    reg = DREG(insn, 0);
+    shift = DREG(insn, 9);
+    /* Shift by zero leaves C flag unmodified.   */
+    if (insn & 0x100) {
+        if (insn & 8) {
+            gen_helper_shl32_cc(reg, cpu_env, reg, shift);
+        } else {
+            gen_helper_sal32_cc(reg, cpu_env, reg, shift);
+        }
+    } else {
+        if (insn & 8) {
+            gen_helper_shr32_cc(reg, cpu_env, reg, shift);
+        } else {
+            gen_helper_sar32_cc(reg, cpu_env, reg, shift);
+        }
+    }
+    set_cc_op(s, CC_OP_SHIFT);
+}
+
+DISAS_INSN(shift_mem)
+{
+    TCGv src;
+    TCGv dest;
+    TCGv addr;
+    TCGv shift;
+
+    SRC_EA(env, src, OS_WORD, 0, &addr);
+    dest = tcg_temp_new_i32();
+    shift = tcg_const_i32(1);
+    if (insn & 0x100) {
+        gen_helper_shl16_cc(dest, cpu_env, src, shift);
+    } else {
+        if (insn & 8) {
+            gen_helper_shr16_cc(dest, cpu_env, src, shift);
+        } else {
+            gen_helper_sar16_cc(dest, cpu_env, src, shift);
+        }
+    }
+    DEST_EA(env, insn, OS_WORD, dest, &addr);
+    set_cc_op(s, CC_OP_SHIFTW);
+}
+
+DISAS_INSN(rotate_im)
+{
+    TCGv reg;
+    TCGv shift;
+    int tmp;
+
+    reg = DREG(insn, 0);
+    tmp = (insn >> 9) & 7;
+    if (tmp == 0) {
+        tmp = 8;
+    }
+    shift = tcg_const_i32(tmp);
+    if (insn & 8) {
+        if (insn & 0x100) {
+            gen_helper_rol32_cc(reg, cpu_env, reg, shift);
+        } else {
+            gen_helper_ror32_cc(reg, cpu_env, reg, shift);
+        }
+    } else {
+        if (insn & 0x100) {
+            gen_helper_roxl32_cc(reg, cpu_env, reg, shift);
+        } else {
+            gen_helper_roxr32_cc(reg, cpu_env, reg, shift);
+        }
+    }
+    set_cc_op(s, CC_OP_FLAGS);
+}
+
+DISAS_INSN(rotate8_im)
+{
+    TCGv reg;
+    TCGv dest;
+    TCGv shift;
+    int tmp;
+
+    reg = DREG(insn, 0);
+    tmp = (insn >> 9) & 7;
+    if (tmp == 0) {
+        tmp = 8;
+    }
+    dest = tcg_temp_new_i32();
+    shift = tcg_const_i32(tmp);
+    if (insn & 8) {
+        if (insn & 0x100) {
+            gen_helper_rol8_cc(dest, cpu_env, reg, shift);
+        } else {
+            gen_helper_ror8_cc(dest, cpu_env, reg, shift);
+        }
+    } else {
+        if (insn & 0x100) {
+            gen_helper_roxl8_cc(dest, cpu_env, reg, shift);
+        } else {
+            gen_helper_roxr8_cc(dest, cpu_env, reg, shift);
+        }
+    }
+    gen_partset_reg(OS_BYTE, reg, dest);
+    set_cc_op(s, CC_OP_FLAGS);
+}
+
+DISAS_INSN(rotate16_im)
+{
+    TCGv reg;
+    TCGv dest;
+    TCGv shift;
+    int tmp;
+
+    reg = DREG(insn, 0);
+    tmp = (insn >> 9) & 7;
+    if (tmp == 0) {
+        tmp = 8;
+    }
+    dest = tcg_temp_new_i32();
+    shift = tcg_const_i32(tmp);
+    if (insn & 8) {
+        if (insn & 0x100) {
+            gen_helper_rol16_cc(dest, cpu_env, reg, shift);
+        } else {
+            gen_helper_ror16_cc(dest, cpu_env, reg, shift);
+        }
+    } else {
+        if (insn & 0x100) {
+            gen_helper_roxl16_cc(dest, cpu_env, reg, shift);
+        } else {
+            gen_helper_roxr16_cc(dest, cpu_env, reg, shift);
+        }
+    }
+    gen_partset_reg(OS_WORD, reg, dest);
+    set_cc_op(s, CC_OP_FLAGS);
+}
+
+DISAS_INSN(rotate_reg)
+{
+    TCGv reg;
+    TCGv src;
+    TCGv tmp;
+
+    reg = DREG(insn, 0);
+    src = DREG(insn, 9);
+    tmp = tcg_temp_new_i32();
+    tcg_gen_andi_i32(tmp, src, 63);
+    if (insn & 8) {
+        if (insn & 0x100) {
+            gen_helper_rol32_cc(reg, cpu_env, reg, tmp);
+        } else {
+            gen_helper_ror32_cc(reg, cpu_env, reg, tmp);
+        }
+    } else {
+        if (insn & 0x100) {
+            gen_helper_roxl32_cc(reg, cpu_env, reg, tmp);
+        } else {
+            gen_helper_roxr32_cc(reg, cpu_env, reg, tmp);
+        }
+    }
+    set_cc_op(s, CC_OP_FLAGS);
+}
+
+DISAS_INSN(rotate8_reg)
+{
+    TCGv reg;
+    TCGv src;
+    TCGv dest;
+    TCGv tmp;
+
+    reg = DREG(insn, 0);
+    src = DREG(insn, 9);
+    tmp = tcg_temp_new_i32();
+    tcg_gen_andi_i32(tmp, src, 63);
+    dest = tcg_temp_new_i32();
+    if (insn & 8) {
+        if (insn & 0x100) {
+            gen_helper_rol8_cc(dest, cpu_env, reg, tmp);
+        } else {
+            gen_helper_ror8_cc(dest, cpu_env, reg, tmp);
+        }
+    } else {
+        if (insn & 0x100) {
+            gen_helper_roxl8_cc(dest, cpu_env, reg, tmp);
+        } else {
+            gen_helper_roxr8_cc(dest, cpu_env, reg, tmp);
+        }
+    }
+    gen_partset_reg(OS_BYTE, reg, dest);
+    set_cc_op(s, CC_OP_FLAGS);
+}
+
+DISAS_INSN(rotate16_reg)
+{
+    TCGv reg;
+    TCGv src;
+    TCGv dest;
+    TCGv tmp;
+
+    reg = DREG(insn, 0);
+    src = DREG(insn, 9);
+    tmp = tcg_temp_new_i32();
+    tcg_gen_andi_i32(tmp, src, 63);
+    dest = tcg_temp_new_i32();
+    if (insn & 8) {
+        if (insn & 0x100) {
+            gen_helper_rol16_cc(dest, cpu_env, reg, tmp);
+        } else {
+            gen_helper_ror16_cc(dest, cpu_env, reg, tmp);
+        }
+    } else {
+        if (insn & 0x100) {
+            gen_helper_roxl16_cc(dest, cpu_env, reg, tmp);
+        } else {
+            gen_helper_roxr16_cc(dest, cpu_env, reg, tmp);
+        }
+    }
+    gen_partset_reg(OS_WORD, reg, dest);
+    set_cc_op(s, CC_OP_FLAGS);
+}
+
+DISAS_INSN(rotate_mem)
+{
     TCGv src;
+    TCGv dest;
     TCGv addr;
+    TCGv shift;
 
-    reg = DREG(insn, 9);
-    dest = tcg_temp_new();
-    if (insn & 0x100) {
-        SRC_EA(env, src, OS_LONG, 0, &addr);
-        tcg_gen_or_i32(dest, src, reg);
-        DEST_EA(env, insn, OS_LONG, dest, &addr);
+    SRC_EA(env, src, OS_WORD, 0, &addr);
+    dest = tcg_temp_new_i32();
+    shift = tcg_const_i32(1);
+    if (insn & 8) {
+        if (insn & 0x100) {
+            gen_helper_rol16_cc(dest, cpu_env, src, shift);
+        } else {
+            gen_helper_ror16_cc(dest, cpu_env, src, shift);
+        }
     } else {
-        SRC_EA(env, src, OS_LONG, 0, NULL);
-        tcg_gen_or_i32(dest, src, reg);
-        tcg_gen_mov_i32(reg, dest);
+        if (insn & 0x100) {
+            gen_helper_roxl16_cc(dest, cpu_env, src, shift);
+        } else {
+            gen_helper_roxr16_cc(dest, cpu_env, src, shift);
+        }
     }
-    gen_logic_cc(s, dest);
+    DEST_EA(env, insn, OS_WORD, dest, &addr);
+    set_cc_op(s, CC_OP_FLAGS);
 }
 
-DISAS_INSN(suba)
+static void bitfield_param(uint16_t ext, TCGv *offset, TCGv *width, TCGv *mask)
 {
-    TCGv src;
-    TCGv reg;
+    TCGv tmp;
 
-    SRC_EA(env, src, OS_LONG, 0, NULL);
-    reg = AREG(insn, 9);
-    tcg_gen_sub_i32(reg, reg, src);
-}
+    /* offset */
 
-DISAS_INSN(subx)
-{
-    TCGv reg;
-    TCGv src;
+    if (ext & 0x0800) {
+        *offset = tcg_temp_new_i32();
+        tcg_gen_mov_i32(*offset, DREG(ext, 6));
+    } else {
+        *offset = tcg_temp_new_i32();
+        tcg_gen_movi_i32(*offset, (ext >> 6) & 31);
+    }
 
-    gen_flush_flags(s);
-    reg = DREG(insn, 9);
-    src = DREG(insn, 0);
-    gen_helper_subx_cc(reg, cpu_env, reg, src);
-}
+    /* width */
 
-DISAS_INSN(mov3q)
-{
-    TCGv src;
-    int val;
+    if (ext & 0x0020) {
+        *width = tcg_temp_new_i32();
+        tcg_gen_subi_i32(*width, DREG(ext, 0), 1);
+        tcg_gen_andi_i32(*width, *width, 31);
+        tcg_gen_addi_i32(*width, *width, 1);
+    } else {
+        *width = tcg_temp_new_i32();
+        tcg_gen_movi_i32(*width, ((ext - 1) & 31) + 1);
+    }
 
-    val = (insn >> 9) & 7;
-    if (val == 0)
-        val = -1;
-    src = tcg_const_i32(val);
-    gen_logic_cc(s, src);
-    DEST_EA(env, insn, OS_LONG, src, NULL);
+    /* mask */
+
+    tmp = tcg_temp_new_i32();
+    tcg_gen_sub_i32(tmp, tcg_const_i32(32), *width);
+    *mask = tcg_temp_new_i32();
+    tcg_gen_shl_i32(*mask, tcg_const_i32(0xffffffff), tmp);
 }
 
-DISAS_INSN(cmp)
+DISAS_INSN(bitfield_reg)
 {
-    int op;
-    TCGv src;
+    uint16_t ext;
+    TCGv tmp;
+    TCGv tmp1;
     TCGv reg;
-    TCGv dest;
-    int opsize;
+    TCGv offset;
+    TCGv width;
+    int op;
+    TCGv reg2;
+    TCGv mask;
+
+    reg = DREG(insn, 0);
+    op = (insn >> 8) & 7;
+    ext = read_im16(env, s);
+
+    bitfield_param(ext, &offset, &width, &mask);
+
+    if (ext & 0x0800) {
+        tcg_gen_andi_i32(offset, offset, 31);
+    }
+    gen_helper_ror32(mask, mask, offset);
+
+    /* reg & mask */
+
+    tmp = tcg_temp_new_i32();
+    tcg_gen_and_i32(tmp, reg, mask);
+
+    tmp1 = tcg_temp_new_i32();
+    gen_helper_rol32(tmp1, tmp, offset);
+
+    reg2 = DREG(ext, 12);
+    if (op == 7) {
+        TCGv tmp2;
+
+        tmp2 = tcg_temp_new_i32();
+        tcg_gen_sub_i32(tmp2, tcg_const_i32(32), width);
+        tcg_gen_shl_i32(tmp2, reg2, tmp2);
+        tcg_gen_and_i32(tmp2, tmp2, mask);
+        gen_logic_cc(s, tmp2, OS_LONG);
+
+        tcg_temp_free_i32(tmp1);
+    } else {
+        gen_logic_cc(s, tmp1, OS_LONG);
+    }
 
-    op = (insn >> 6) & 3;
     switch (op) {
-    case 0: /* cmp.b */
-        opsize = OS_BYTE;
-        s->cc_op = CC_OP_CMPB;
+    case 0: /* bftst */
         break;
-    case 1: /* cmp.w */
-        opsize = OS_WORD;
-        s->cc_op = CC_OP_CMPW;
+    case 1: /* bfextu */
+        tcg_gen_add_i32(tmp1, offset, width);
+        tcg_gen_andi_i32(tmp1, tmp1, 31);
+        gen_helper_rol32(reg2, tmp, tmp1);
         break;
-    case 2: /* cmp.l */
-        opsize = OS_LONG;
-        s->cc_op = CC_OP_SUB;
+    case 2: /* bfchg */
+        tcg_gen_xor_i32(reg, reg, mask);
+        break;
+    case 3: /* bfexts */
+        gen_helper_rol32(reg2, tmp, offset);
+        tcg_gen_sub_i32(width, tcg_const_i32(32), width);
+        tcg_gen_sar_i32(reg2, reg2, width);
+        break;
+    case 4: /* bfclr */
+        tcg_gen_not_i32(mask, mask);
+        tcg_gen_and_i32(reg, reg, mask);
+        break;
+    case 5: /* bfffo */
+        gen_helper_rol32(reg2, tmp, offset);
+        gen_helper_bfffo(tmp, tmp, width);
+        tcg_gen_add_i32(reg2, tmp, offset);
+        break;
+    case 6: /* bfset */
+        tcg_gen_or_i32(reg, reg, mask);
+        break;
+    case 7: /* bfins */
+        tcg_gen_shl_i32(tmp1, tcg_const_i32(1), width);
+        tcg_gen_subi_i32(tmp1, tmp1, 1);
+        tcg_gen_and_i32(tmp, reg2, tmp1);
+        tcg_gen_add_i32(tmp1, offset, width);
+        tcg_gen_andi_i32(tmp1, tmp1, 31);
+        gen_helper_ror32(tmp, tmp, tmp1);
+        tcg_gen_not_i32(mask, mask);
+        tcg_gen_and_i32(reg, reg, mask);
+        tcg_gen_or_i32(reg, reg, tmp);
         break;
-    default:
-        abort();
     }
-    SRC_EA(env, src, opsize, 1, NULL);
-    reg = DREG(insn, 9);
-    dest = tcg_temp_new();
-    tcg_gen_sub_i32(dest, reg, src);
-    gen_update_cc_add(dest, src);
 }
 
-DISAS_INSN(cmpa)
+static TCGv gen_bitfield_cc(DisasContext *s,
+                            TCGv offset, TCGv mask_cc, TCGv_i64 bitfield)
 {
-    int opsize;
-    TCGv src;
-    TCGv reg;
     TCGv dest;
+    TCGv_i64 tmp64;
 
-    if (insn & 0x100) {
-        opsize = OS_LONG;
-    } else {
-        opsize = OS_WORD;
-    }
-    SRC_EA(env, src, opsize, 1, NULL);
-    reg = AREG(insn, 9);
-    dest = tcg_temp_new();
-    tcg_gen_sub_i32(dest, reg, src);
-    gen_update_cc_add(dest, src);
-    s->cc_op = CC_OP_SUB;
-}
+    /* move bitfield to a 32bit */
 
-DISAS_INSN(eor)
-{
-    TCGv src;
-    TCGv reg;
-    TCGv dest;
-    TCGv addr;
+    tmp64 = tcg_temp_new_i64();
 
-    SRC_EA(env, src, OS_LONG, 0, &addr);
-    reg = DREG(insn, 9);
-    dest = tcg_temp_new();
-    tcg_gen_xor_i32(dest, src, reg);
-    gen_logic_cc(s, dest);
-    DEST_EA(env, insn, OS_LONG, dest, &addr);
-}
+    tcg_gen_extu_i32_i64(tmp64, offset);
 
-DISAS_INSN(and)
-{
-    TCGv src;
-    TCGv reg;
-    TCGv dest;
-    TCGv addr;
+    /* tmp64 = bitfield << offset */
 
-    reg = DREG(insn, 9);
-    dest = tcg_temp_new();
-    if (insn & 0x100) {
-        SRC_EA(env, src, OS_LONG, 0, &addr);
-        tcg_gen_and_i32(dest, src, reg);
-        DEST_EA(env, insn, OS_LONG, dest, &addr);
-    } else {
-        SRC_EA(env, src, OS_LONG, 0, NULL);
-        tcg_gen_and_i32(dest, src, reg);
-        tcg_gen_mov_i32(reg, dest);
-    }
-    gen_logic_cc(s, dest);
+    tcg_gen_shl_i64(tmp64, bitfield, tmp64);
+
+    /* tmp = (bitfield << offset) >> 32 */
+
+    tcg_gen_shri_i64(tmp64, tmp64, 32ULL);
+    dest = tcg_temp_new_i32();
+    tcg_gen_trunc_i64_i32(dest, tmp64);
+    tcg_gen_and_i32(dest, dest, mask_cc);
+
+    return dest;
 }
 
-DISAS_INSN(adda)
+static TCGv_i64 gen_bitfield_mask(TCGv offset, TCGv width)
 {
-    TCGv src;
-    TCGv reg;
+    TCGv tmp;
+    TCGv_i64 mask;
+    TCGv_i64 shift;
 
-    SRC_EA(env, src, OS_LONG, 0, NULL);
-    reg = AREG(insn, 9);
-    tcg_gen_add_i32(reg, reg, src);
+    mask = tcg_temp_new_i64();
+
+    /* mask = (1u << width) - 1; */
+
+    tcg_gen_extu_i32_i64(mask, width);
+    tcg_gen_shl_i64(mask, tcg_const_i64(1), mask);
+    tcg_gen_subi_i64(mask, mask, 1);
+
+    /* shift = 64 - (width + offset); */
+
+    tmp = tcg_temp_new_i32();
+    tcg_gen_add_i32(tmp, offset, width);
+    tcg_gen_sub_i32(tmp, tcg_const_i32(64), tmp);
+    shift = tcg_temp_new_i64();
+    tcg_gen_extu_i32_i64(shift, tmp);
+
+    /* mask <<= shift */
+
+    tcg_gen_shl_i64(mask, mask, shift);
+
+    return mask;
 }
 
-DISAS_INSN(addx)
+static void gen_bitfield_ins(TCGv offset, TCGv width, TCGv src,
+                                 TCGv_i64 val)
 {
-    TCGv reg;
-    TCGv src;
+    TCGv_i64 insert;
+    TCGv_i64 shift;
+    TCGv tmp;
 
-    gen_flush_flags(s);
-    reg = DREG(insn, 9);
-    src = DREG(insn, 0);
-    gen_helper_addx_cc(reg, cpu_env, reg, src);
-    s->cc_op = CC_OP_FLAGS;
+    tmp = tcg_temp_new_i32();
+
+    /* tmp = (1u << width) - 1; */
+
+    /* width is between 1 and 32
+     * tcg_gen_shl_i32() cannot manage value 32
+     */
+    tcg_gen_subi_i32(tmp, width, 1);
+    tcg_gen_shl_i32(tmp, tcg_const_i32(2), tmp);
+    tcg_gen_subi_i32(tmp, tmp, 1);
+
+    /* tmp = tmp & src; */
+
+    tcg_gen_and_i32(tmp, tmp, src);
+
+    /* insert = (i64)tmp; */
+
+    insert = tcg_temp_new_i64();
+    tcg_gen_extu_i32_i64(insert, tmp);
+
+    /* tmp = 64 - (width + offset); */
+
+    tcg_gen_add_i32(tmp, offset, width);
+    tcg_gen_sub_i32(tmp, tcg_const_i32(64), tmp);
+    shift = tcg_temp_new_i64();
+    tcg_gen_extu_i32_i64(shift, tmp);
+
+    /* insert <<= shift */
+
+    tcg_gen_shl_i64(insert, insert, shift);
+
+    /* val |=  select */
+
+    tcg_gen_or_i64(val, val, insert);
 }
 
-/* TODO: This could be implemented without helper functions.  */
-DISAS_INSN(shift_im)
+DISAS_INSN(bitfield_mem)
 {
-    TCGv reg;
-    int tmp;
+    uint16_t ext;
+    int op;
+    TCGv_i64 bitfield;
+    TCGv_i64 mask_bitfield;
+    TCGv mask_cc;
     TCGv shift;
+    TCGv val;
+    TCGv src;
+    TCGv offset;
+    TCGv width;
+    TCGv reg;
+    TCGv tmp;
 
-    reg = DREG(insn, 0);
-    tmp = (insn >> 9) & 7;
-    if (tmp == 0)
-        tmp = 8;
-    shift = tcg_const_i32(tmp);
-    /* No need to flush flags becuse we know we will set C flag.  */
-    if (insn & 0x100) {
-        gen_helper_shl_cc(reg, cpu_env, reg, shift);
-    } else {
-        if (insn & 8) {
-            gen_helper_shr_cc(reg, cpu_env, reg, shift);
-        } else {
-            gen_helper_sar_cc(reg, cpu_env, reg, shift);
-        }
+    op = (insn >> 8) & 7;
+    ext = read_im16(env, s);
+    src = gen_lea(env, s, insn, OS_LONG);
+    if (IS_NULL_QREG(src)) {
+        gen_addr_fault(s);
+        return;
     }
-    s->cc_op = CC_OP_SHIFT;
-}
 
-DISAS_INSN(shift_reg)
-{
-    TCGv reg;
-    TCGv shift;
+    bitfield_param(ext, &offset, &width, &mask_cc);
 
-    reg = DREG(insn, 0);
-    shift = DREG(insn, 9);
-    /* Shift by zero leaves C flag unmodified.   */
-    gen_flush_flags(s);
-    if (insn & 0x100) {
-        gen_helper_shl_cc(reg, cpu_env, reg, shift);
+    /* adjust src and offset */
+
+    /* src += offset >> 3; */
+
+    tmp = tcg_temp_new_i32();
+    tcg_gen_shri_i32(tmp, offset, 3);
+    tcg_gen_add_i32(src, src, tmp);
+
+    /* offset &= 7; */
+
+    tcg_gen_andi_i32(offset, offset, 7);
+
+    /* load */
+
+    bitfield = tcg_temp_new_i64();
+    gen_helper_bitfield_load(bitfield, src, offset, width);
+
+    /* compute CC and move bitfield into a 32bit */
+
+    val = gen_bitfield_cc(s, offset, mask_cc, bitfield);
+
+    /* execute operation */
+
+    reg = DREG(ext, 12);
+
+    if (op == 7) {
+        TCGv tmp1;
+
+        tmp1 = tcg_temp_new_i32();
+        tcg_gen_sub_i32(tmp1, tcg_const_i32(32), width);
+        tcg_gen_shl_i32(tmp1, reg, tmp1);
+        tcg_gen_and_i32(tmp1, tmp1, mask_cc);
+        gen_logic_cc(s, tmp1, OS_LONG);
+
+        tcg_temp_free_i32(tmp1);
     } else {
-        if (insn & 8) {
-            gen_helper_shr_cc(reg, cpu_env, reg, shift);
-        } else {
-            gen_helper_sar_cc(reg, cpu_env, reg, shift);
-        }
+        gen_logic_cc(s, val, OS_LONG);
+    }
+
+    switch (op) {
+    case 0: /* bftst */
+        break;
+    case 1: /* bfextu */
+        shift = tcg_temp_new_i32();
+        tcg_gen_sub_i32(shift, tcg_const_i32(32), width);
+        tcg_gen_shr_i32(reg, val, shift);
+        break;
+    case 2: /* bfchg */
+        mask_bitfield = gen_bitfield_mask(offset, width);
+        tcg_gen_xor_i64(bitfield, bitfield, mask_bitfield);
+        gen_helper_bitfield_store(src, offset, width, bitfield);
+        break;
+    case 3: /* bfexts */
+        shift = tcg_temp_new_i32();
+        tcg_gen_sub_i32(shift, tcg_const_i32(32), width);
+        tcg_gen_sar_i32(reg, val, shift);
+        break;
+    case 4: /* bfclr */
+        mask_bitfield = gen_bitfield_mask(offset, width);
+        tcg_gen_not_i64(mask_bitfield, mask_bitfield);
+        tcg_gen_and_i64(bitfield, bitfield, mask_bitfield);
+        gen_helper_bitfield_store(src, offset, width, bitfield);
+        break;
+    case 5: /* bfffo */
+        gen_helper_bfffo(val, val, width);
+        tcg_gen_add_i32(reg, val, offset);
+        break;
+    case 6: /* bfset */
+        mask_bitfield = gen_bitfield_mask(offset, width);
+        tcg_gen_or_i64(bitfield, bitfield, mask_bitfield);
+        gen_helper_bitfield_store(src, offset, width, bitfield);
+        break;
+    case 7: /* bfins */
+        /* clear */
+        mask_bitfield = gen_bitfield_mask(offset, width);
+        tcg_gen_not_i64(mask_bitfield, mask_bitfield);
+        tcg_gen_and_i64(bitfield, bitfield, mask_bitfield);
+        /* insert */
+        gen_bitfield_ins(offset, width, reg, bitfield);
+        gen_helper_bitfield_store(src, offset, width, bitfield);
+        break;
     }
-    s->cc_op = CC_OP_SHIFT;
 }
 
 DISAS_INSN(ff1)
 {
     TCGv reg;
     reg = DREG(insn, 0);
-    gen_logic_cc(s, reg);
+    gen_logic_cc(s, reg, OS_LONG);
     gen_helper_ff1(reg, reg);
 }
 
-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;
-}
-
 DISAS_INSN(strldsr)
 {
     uint16_t ext;
     uint32_t addr;
 
     addr = s->pc - 2;
-    ext = cpu_lduw_code(env, s->pc);
-    s->pc += 2;
+    ext = read_im16(env, s);
     if (ext != 0x46FC) {
         gen_exception(s, addr, EXCP_UNSUPPORTED);
         return;
     }
-    ext = cpu_lduw_code(env, s->pc);
-    s->pc += 2;
+    ext = read_im16(env, s);
     if (IS_USER(s) || (ext & SR_S) == 0) {
         gen_exception(s, addr, EXCP_PRIVILEGE);
         return;
@@ -1967,16 +3253,14 @@ DISAS_INSN(strldsr)
 
 DISAS_INSN(move_from_sr)
 {
-    TCGv reg;
     TCGv sr;
 
-    if (IS_USER(s)) {
+    if (IS_USER(s)) {    /* FIXME: not privileged on 68000 */
         gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
         return;
     }
     sr = gen_get_sr(s);
-    reg = DREG(insn, 0);
-    gen_partset_reg(OS_WORD, reg, sr);
+    DEST_EA(env, insn, OS_WORD, sr, NULL);
 }
 
 DISAS_INSN(move_to_sr)
@@ -1985,7 +3269,7 @@ DISAS_INSN(move_to_sr)
         gen_exception(s, s->pc - 2, EXCP_PRIVILEGE);
         return;
     }
-    gen_set_sr(env, s, insn, 0);
+    gen_move_to_sr(env, s, insn, 0);
     gen_lookup_tb(s);
 }
 
@@ -2023,8 +3307,7 @@ DISAS_INSN(stop)
         return;
     }
 
-    ext = cpu_lduw_code(env, s->pc);
-    s->pc += 2;
+    ext = read_im16(env, s);
 
     gen_set_sr_im(s, ext, 0);
     tcg_gen_movi_i32(cpu_halted, 1);
@@ -2050,8 +3333,7 @@ DISAS_INSN(movec)
         return;
     }
 
-    ext = cpu_lduw_code(env, s->pc);
-    s->pc += 2;
+    ext = read_im16(env, s);
 
     if (ext & 0x8000) {
         reg = AREG(ext, 12);
@@ -2527,8 +3809,7 @@ DISAS_INSN(mac)
         s->done_mac = 1;
     }
 
-    ext = cpu_lduw_code(env, s->pc);
-    s->pc += 2;
+    ext = read_im16(env, s);
 
     acc = ((insn >> 7) & 1) | ((ext >> 3) & 2);
     dual = ((insn & 0x30) != 0 && (ext & 3) != 0);
@@ -2742,7 +4023,7 @@ DISAS_INSN(macsr_to_ccr)
 {
     tcg_gen_movi_i32(QREG_CC_X, 0);
     tcg_gen_andi_i32(QREG_CC_DEST, QREG_MACSR, 0xf);
-    s->cc_op = CC_OP_FLAGS;
+    set_cc_op(s, CC_OP_FLAGS);
 }
 
 DISAS_INSN(to_mac)
@@ -2839,85 +4120,162 @@ void register_m68k_insns (CPUM68KState *env)
         register_opcode(disas_##name, 0x##opcode, 0x##mask); \
     } while(0)
     INSN(undef,     0000, 0000, CF_ISA_A);
+    INSN(undef,     0000, 0000, M68000);
     INSN(arith_im,  0080, fff8, CF_ISA_A);
+    INSN(arith_im,  0000, ff00, M68000);
+    INSN(undef,     00c0, ffc0, M68000);
     INSN(bitrev,    00c0, fff8, CF_ISA_APLUSC);
     INSN(bitop_reg, 0100, f1c0, CF_ISA_A);
+    INSN(bitop_reg, 0100, f1c0, M68000);
     INSN(bitop_reg, 0140, f1c0, CF_ISA_A);
+    INSN(bitop_reg, 0140, f1c0, M68000);
     INSN(bitop_reg, 0180, f1c0, CF_ISA_A);
+    INSN(bitop_reg, 0180, f1c0, M68000);
     INSN(bitop_reg, 01c0, f1c0, CF_ISA_A);
+    INSN(bitop_reg, 01c0, f1c0, M68000);
     INSN(arith_im,  0280, fff8, CF_ISA_A);
+    INSN(arith_im,  0200, ff00, M68000);
+    INSN(undef,     02c0, ffc0, M68000);
     INSN(byterev,   02c0, fff8, CF_ISA_APLUSC);
     INSN(arith_im,  0480, fff8, CF_ISA_A);
+    INSN(arith_im,  0400, ff00, M68000);
+    INSN(undef,     04c0, ffc0, M68000);
+    INSN(arith_im,  0600, ff00, M68000);
+    INSN(undef,     06c0, ffc0, M68000);
     INSN(ff1,       04c0, fff8, CF_ISA_APLUSC);
     INSN(arith_im,  0680, fff8, CF_ISA_A);
     INSN(bitop_im,  0800, ffc0, CF_ISA_A);
+    INSN(bitop_im,  0800, ffc0, M68000);
     INSN(bitop_im,  0840, ffc0, CF_ISA_A);
+    INSN(bitop_im,  0840, ffc0, M68000);
     INSN(bitop_im,  0880, ffc0, CF_ISA_A);
+    INSN(bitop_im,  0880, ffc0, M68000);
+    INSN(cas,       08c0, f9c0, CAS);
     INSN(bitop_im,  08c0, ffc0, CF_ISA_A);
+    INSN(bitop_im,  08c0, ffc0, M68000);
     INSN(arith_im,  0a80, fff8, CF_ISA_A);
+    INSN(arith_im,  0a00, ff00, M68000);
+    INSN(undef,     0ac0, ffc0, M68000);
     INSN(arith_im,  0c00, ff38, CF_ISA_A);
+    INSN(arith_im,  0c00, ff00, M68000);
+    INSN(undef,     0cc0, ffc0, M68000);
     INSN(move,      1000, f000, CF_ISA_A);
+    INSN(move,      1000, f000, M68000);
     INSN(move,      2000, f000, CF_ISA_A);
+    INSN(move,      2000, f000, M68000);
     INSN(move,      3000, f000, CF_ISA_A);
+    INSN(move,      3000, f000, M68000);
     INSN(strldsr,   40e7, ffff, CF_ISA_APLUSC);
     INSN(negx,      4080, fff8, CF_ISA_A);
+    INSN(negx,      4000, ff00, M68000);
+    INSN(undef,     40c0, ffc0, M68000);
     INSN(move_from_sr, 40c0, fff8, CF_ISA_A);
+    INSN(move_from_sr, 40c0, ffc0, M68000);
     INSN(lea,       41c0, f1c0, CF_ISA_A);
+    INSN(lea,       41c0, f1c0, M68000);
     INSN(clr,       4200, ff00, CF_ISA_A);
+    INSN(clr,       4200, ff00, M68000);
     INSN(undef,     42c0, ffc0, CF_ISA_A);
+    INSN(undef,     42c0, ffc0, M68000);
     INSN(move_from_ccr, 42c0, fff8, CF_ISA_A);
     INSN(neg,       4480, fff8, CF_ISA_A);
+    INSN(neg,       4400, ff00, M68000);
+    INSN(undef,     44c0, ffc0, M68000);
     INSN(move_to_ccr, 44c0, ffc0, CF_ISA_A);
+    INSN(move_to_ccr, 44c0, ffc0, M68000);
     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);
+    INSN(nbcd,      4800, ffc0, M68000);
+    INSN(linkl,     4808, fff8, M68000);
     INSN(pea,       4840, ffc0, CF_ISA_A);
+    INSN(pea,       4840, ffc0, M68000);
     INSN(swap,      4840, fff8, CF_ISA_A);
+    INSN(swap,      4840, fff8, M68000);
+    INSN(bkpt,      4848, fff8, M68000);
     INSN(movem,     48c0, fbc0, CF_ISA_A);
+    INSN(movem,     48c0, fbc0, M68000);
     INSN(ext,       4880, fff8, CF_ISA_A);
+    INSN(ext,       4880, fff8, M68000);
     INSN(ext,       48c0, fff8, CF_ISA_A);
+    INSN(ext,       48c0, fff8, M68000);
     INSN(ext,       49c0, fff8, CF_ISA_A);
+    INSN(ext,       49c0, fff8, M68000);
     INSN(tst,       4a00, ff00, CF_ISA_A);
+    INSN(tst,       4a00, ff00, M68000);
     INSN(tas,       4ac0, ffc0, CF_ISA_B);
+    INSN(tas,       4ac0, ffc0, M68000);
     INSN(halt,      4ac8, ffff, CF_ISA_A);
     INSN(pulse,     4acc, ffff, CF_ISA_A);
     INSN(illegal,   4afc, ffff, CF_ISA_A);
+    INSN(illegal,   4afc, ffff, M68000);
     INSN(mull,      4c00, ffc0, CF_ISA_A);
+    INSN(mull,      4c00, ffc0, LONG_MULDIV);
     INSN(divl,      4c40, ffc0, CF_ISA_A);
+    INSN(divl,      4c40, ffc0, LONG_MULDIV);
     INSN(sats,      4c80, fff8, CF_ISA_B);
     INSN(trap,      4e40, fff0, CF_ISA_A);
+    INSN(trap,      4e40, fff0, M68000);
     INSN(link,      4e50, fff8, CF_ISA_A);
+    INSN(link,      4e50, fff8, M68000);
     INSN(unlk,      4e58, fff8, CF_ISA_A);
+    INSN(unlk,      4e58, fff8, M68000);
     INSN(move_to_usp, 4e60, fff8, USP);
     INSN(move_from_usp, 4e68, fff8, USP);
     INSN(nop,       4e71, ffff, CF_ISA_A);
+    INSN(nop,       4e71, ffff, M68000);
     INSN(stop,      4e72, ffff, CF_ISA_A);
+    INSN(stop,      4e72, ffff, M68000);
     INSN(rte,       4e73, ffff, CF_ISA_A);
+    INSN(rte,       4e73, ffff, M68000);
     INSN(rts,       4e75, ffff, CF_ISA_A);
+    INSN(rts,       4e75, ffff, M68000);
     INSN(movec,     4e7b, ffff, CF_ISA_A);
     INSN(jump,      4e80, ffc0, CF_ISA_A);
+    INSN(jump,      4e80, ffc0, M68000);
     INSN(jump,      4ec0, ffc0, CF_ISA_A);
     INSN(addsubq,   5180, f1c0, CF_ISA_A);
+    INSN(jump,      4ec0, ffc0, M68000);
+    INSN(addsubq,   5000, f080, M68000);
+    INSN(addsubq,   5080, f0c0, M68000);
     INSN(scc,       50c0, f0f8, CF_ISA_A);
     INSN(addsubq,   5080, f1c0, CF_ISA_A);
+    INSN(scc_mem,   50c0, f0c0, M68000);
+    INSN(scc,       50c0, f0f8, M68000);
+    INSN(dbcc,      50c8, f0f8, M68000);
     INSN(tpf,       51f8, fff8, CF_ISA_A);
 
     /* Branch instructions.  */
     INSN(branch,    6000, f000, CF_ISA_A);
+    INSN(branch,    6000, f000, M68000);
     /* Disable long branch instructions, then add back the ones we want.  */
     INSN(undef,     60ff, f0ff, CF_ISA_A); /* All long branches.  */
+    INSN(undef,     60ff, f0ff, M68000); /* All long branches.  */
     INSN(branch,    60ff, f0ff, CF_ISA_B);
     INSN(undef,     60ff, ffff, CF_ISA_B); /* bra.l */
     INSN(branch,    60ff, ffff, BRAL);
+    INSN(branch,    60ff, f0ff, BCCL);
 
     INSN(moveq,     7000, f100, CF_ISA_A);
+    INSN(moveq,     7000, f100, M68000);
     INSN(mvzs,      7100, f100, CF_ISA_B);
     INSN(or,        8000, f000, CF_ISA_A);
+    INSN(or,        8000, f000, M68000);
     INSN(divw,      80c0, f0c0, CF_ISA_A);
+    INSN(divw,      80c0, f0c0, M68000);
+    INSN(sbcd_reg,  8100, f1f8, M68000);
+    INSN(sbcd_mem,  8108, f1f8, M68000);
     INSN(addsub,    9000, f000, CF_ISA_A);
-    INSN(subx,      9180, f1f8, CF_ISA_A);
+    INSN(addsub,    9000, f000, M68000);
+    INSN(subx_reg,  9180, f1f8, CF_ISA_A);
+    INSN(subx_reg,  9100, f138, M68000);
+    INSN(subx_mem,  9108, f138, M68000);
     INSN(suba,      91c0, f1c0, CF_ISA_A);
+    INSN(suba,      90c0, f0c0, M68000);
 
     INSN(undef_mac, a000, f000, CF_ISA_A);
+    INSN(undef_mac, a000, f000, M68000);
     INSN(mac,       a000, f100, CF_EMAC);
     INSN(from_mac,  a180, f9b0, CF_EMAC);
     INSN(move_mac,  a110, f9fc, CF_EMAC);
@@ -2936,15 +4294,43 @@ void register_m68k_insns (CPUM68KState *env)
     INSN(cmpa,      b0c0, f1c0, CF_ISA_B); /* cmpa.w */
     INSN(cmp,       b080, f1c0, CF_ISA_A);
     INSN(cmpa,      b1c0, f1c0, CF_ISA_A);
+    INSN(cmp,       b000, f100, M68000);
+    INSN(eor,       b100, f100, M68000);
+    INSN(cmpa,      b0c0, f0c0, M68000);
     INSN(eor,       b180, f1c0, CF_ISA_A);
     INSN(and,       c000, f000, CF_ISA_A);
+    INSN(and,       c000, f000, M68000);
     INSN(mulw,      c0c0, f0c0, CF_ISA_A);
+    INSN(mulw,      c0c0, f0c0, M68000);
+    INSN(abcd_reg,  c100, f1f8, M68000);
+    INSN(abcd_mem,  c108, f1f8, M68000);
     INSN(addsub,    d000, f000, CF_ISA_A);
-    INSN(addx,      d180, f1f8, CF_ISA_A);
+    INSN(addsub,    d000, f000, M68000);
+    INSN(addx_reg,  d180, f1f8, CF_ISA_A);
+    INSN(addx_reg,  d100, f138, M68000);
+    INSN(addx_mem,  d108, f138, M68000);
     INSN(adda,      d1c0, f1c0, CF_ISA_A);
+    INSN(adda,      d0c0, f0c0, M68000);
     INSN(shift_im,  e080, f0f0, CF_ISA_A);
     INSN(shift_reg, e0a0, f0f0, CF_ISA_A);
+    INSN(shift8_im, e000, f0f0, M68000);
+    INSN(shift16_im, e040, f0f0, M68000);
+    INSN(shift_im,  e080, f0f0, M68000);
+    INSN(shift8_reg, e020, f0f0, M68000);
+    INSN(shift16_reg, e060, f0f0, M68000);
+    INSN(shift_reg, e0a0, f0f0, M68000);
+    INSN(shift_mem, e0c0, fcc0, M68000);
+    INSN(rotate_im, e090, f0f0, M68000);
+    INSN(rotate8_im, e010, f0f0, M68000);
+    INSN(rotate16_im, e050, f0f0, M68000);
+    INSN(rotate_reg, e0b0, f0f0, M68000);
+    INSN(rotate8_reg, e030, f0f0, M68000);
+    INSN(rotate16_reg, e070, f0f0, M68000);
+    INSN(rotate_mem, e4c0, fcc0, M68000);
+    INSN(bitfield_mem, e8c0, f8c0, BITFIELD);
+    INSN(bitfield_reg, e8c0, f8f8, BITFIELD);
     INSN(undef_fpu, f000, f000, CF_ISA_A);
+    INSN(undef_fpu, f000, f000, M68000);
     INSN(fpu,       f200, ffc0, CF_FPU);
     INSN(fbcc,      f280, ffc0, CF_FPU);
     INSN(frestore,  f340, ffc0, CF_FPU);
@@ -2966,8 +4352,7 @@ static void disas_m68k_insn(CPUM68KState * env, DisasContext *s)
         tcg_gen_debug_insn_start(s->pc);
     }
 
-    insn = cpu_lduw_code(env, s->pc);
-    s->pc += 2;
+    insn = read_im16(env, s);
 
     opcode_table[insn](env, s, insn);
 }
@@ -3030,6 +4415,7 @@ gen_intermediate_code_internal(M68kCPU *cpu, TranslationBlock *tb,
                     tcg_ctx.gen_opc_instr_start[lj++] = 0;
             }
             tcg_ctx.gen_opc_pc[lj] = dc->pc;
+            gen_opc_cc_op[lj] = dc->cc_op;
             tcg_ctx.gen_opc_instr_start[lj] = 1;
             tcg_ctx.gen_opc_icount[lj] = num_insns;
         }
@@ -3049,20 +4435,20 @@ gen_intermediate_code_internal(M68kCPU *cpu, TranslationBlock *tb,
     if (unlikely(cs->singlestep_enabled)) {
         /* Make sure the pc is updated, and raise a debug exception.  */
         if (!dc->is_jmp) {
-            gen_flush_cc_op(dc);
+            update_cc_op(dc);
             tcg_gen_movi_i32(QREG_PC, dc->pc);
         }
         gen_helper_raise_exception(cpu_env, tcg_const_i32(EXCP_DEBUG));
     } else {
         switch(dc->is_jmp) {
         case DISAS_NEXT:
-            gen_flush_cc_op(dc);
+            update_cc_op(dc);
             gen_jmp_tb(dc, 0, dc->pc);
             break;
         default:
         case DISAS_JUMP:
         case DISAS_UPDATE:
-            gen_flush_cc_op(dc);
+            update_cc_op(dc);
             /* indicate that the hash table must be used to find the next TB */
             tcg_gen_exit_tb(0);
             break;
@@ -3075,9 +4461,10 @@ gen_intermediate_code_internal(M68kCPU *cpu, TranslationBlock *tb,
 
 #ifdef DEBUG_DISAS
     if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
+        int flags = m68k_feature(env, M68K_FEATURE_M68000);
         qemu_log("----------------\n");
         qemu_log("IN: %s\n", lookup_symbol(pc_start));
-        log_target_disas(env, pc_start, dc->pc - pc_start, 0);
+        log_target_disas(env, pc_start, dc->pc - pc_start, flags);
         qemu_log("\n");
     }
 #endif
@@ -3090,9 +4477,6 @@ gen_intermediate_code_internal(M68kCPU *cpu, TranslationBlock *tb,
         tb->size = dc->pc - pc_start;
         tb->icount = num_insns;
     }
-
-    //optimize_flags();
-    //expand_target_qops();
 }
 
 void gen_intermediate_code(CPUM68KState *env, TranslationBlock *tb)
@@ -3130,5 +4514,25 @@ void m68k_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
 
 void restore_state_to_opc(CPUM68KState *env, TranslationBlock *tb, int pc_pos)
 {
+    int cc_op;
+#ifdef DEBUG_DISAS
+    if (qemu_loglevel_mask(CPU_LOG_TB_OP)) {
+        int i;
+        qemu_log("RESTORE:\n");
+        for (i = 0; i <= pc_pos; i++) {
+            if (tcg_ctx.gen_opc_instr_start[i]) {
+                qemu_log("0x%04x: " TARGET_FMT_lx "\n", i,
+                        tcg_ctx.gen_opc_pc[i]);
+            }
+        }
+        qemu_log("pc_pos=0x%x pc=" TARGET_FMT_lx " CC_OP %d\n",
+                pc_pos, tcg_ctx.gen_opc_pc[pc_pos],
+                gen_opc_cc_op[pc_pos]);
+    }
+#endif
     env->pc = tcg_ctx.gen_opc_pc[pc_pos];
+    cc_op = gen_opc_cc_op[pc_pos];
+    if (cc_op != CC_OP_DYNAMIC) {
+        env->cc_op = cc_op;
+    }
 }
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [Qemu-devel] [PATCH 2/2] m68k: Implement 680x0 processors family 96 bit FPU
  2015-06-21 22:35 [Qemu-devel] [PATCH 0/2] m68k: 680x0 processors family support Laurent Vivier
  2015-06-21 22:35 ` [Qemu-devel] [PATCH 1/2] m68k: Add compatibility with 680x0 processors family Laurent Vivier
@ 2015-06-21 22:35 ` Laurent Vivier
  2015-06-22  8:00   ` Andreas Schwab
  2015-06-22  8:33 ` [Qemu-devel] [PATCH 0/2] m68k: 680x0 processors family support Peter Maydell
  2015-06-22 12:06 ` Greg Ungerer
  3 siblings, 1 reply; 8+ messages in thread
From: Laurent Vivier @ 2015-06-21 22:35 UTC (permalink / raw)
  To: qemu-devel; +Cc: Andreas Schwab, Laurent Vivier, gerg

Signed-off-by: Laurent Vivier <laurent@vivier.eu>
---
 configure                  |   2 +-
 fpu/softfloat-specialize.h |  48 ++-
 fpu/softfloat.c            |  38 +-
 gdb-xml/m68k-fp.xml        |  21 +
 include/fpu/softfloat.h    |  11 +-
 target-m68k/cpu.c          |   9 +
 target-m68k/cpu.h          |  38 +-
 target-m68k/helper.c       | 868 +++++++++++++++++++++++++++++++++++++++++-
 target-m68k/helper.h       |  44 +++
 target-m68k/qregs.def      |   5 +-
 target-m68k/translate.c    | 930 ++++++++++++++++++++++++++++++++++++++++++++-
 11 files changed, 1967 insertions(+), 47 deletions(-)
 create mode 100644 gdb-xml/m68k-fp.xml

diff --git a/configure b/configure
index 4a66b2e..a282979 100755
--- a/configure
+++ b/configure
@@ -5211,7 +5211,7 @@ case "$target_name" in
   ;;
   m68k)
     bflt="yes"
-    gdb_xml_files="cf-core.xml cf-fp.xml"
+    gdb_xml_files="cf-core.xml cf-fp.xml m68k-fp.xml"
   ;;
   microblaze|microblazeel)
     TARGET_ARCH=microblaze
diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h
index 6dd41d8..1f8d5ea 100644
--- a/fpu/softfloat-specialize.h
+++ b/fpu/softfloat-specialize.h
@@ -113,7 +113,7 @@ const float16 float16_default_nan = const_float16(0xFE00);
 #if defined(TARGET_SPARC)
 const float32 float32_default_nan = const_float32(0x7FFFFFFF);
 #elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \
-      defined(TARGET_XTENSA) || defined(TARGET_S390X)
+      defined(TARGET_XTENSA) || defined(TARGET_S390X) || defined(TARGET_M68K)
 const float32 float32_default_nan = const_float32(0x7FC00000);
 #elif SNAN_BIT_IS_ONE
 const float32 float32_default_nan = const_float32(0x7FBFFFFF);
@@ -127,7 +127,7 @@ const float32 float32_default_nan = const_float32(0xFFC00000);
 #if defined(TARGET_SPARC)
 const float64 float64_default_nan = const_float64(LIT64( 0x7FFFFFFFFFFFFFFF ));
 #elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \
-      defined(TARGET_S390X)
+      defined(TARGET_S390X) || defined(TARGET_M68K)
 const float64 float64_default_nan = const_float64(LIT64( 0x7FF8000000000000 ));
 #elif SNAN_BIT_IS_ONE
 const float64 float64_default_nan = const_float64(LIT64(0x7FF7FFFFFFFFFFFF));
@@ -138,7 +138,10 @@ const float64 float64_default_nan = const_float64(LIT64( 0xFFF8000000000000 ));
 /*----------------------------------------------------------------------------
 | The pattern for a default generated extended double-precision NaN.
 *----------------------------------------------------------------------------*/
-#if SNAN_BIT_IS_ONE
+#if defined(TARGET_M68K)
+#define floatx80_default_nan_high 0x7FFF
+#define floatx80_default_nan_low  LIT64(0x4000000000000000)
+#elif SNAN_BIT_IS_ONE
 #define floatx80_default_nan_high 0x7FFF
 #define floatx80_default_nan_low  LIT64(0xBFFFFFFFFFFFFFFF)
 #else
@@ -150,6 +153,21 @@ const floatx80 floatx80_default_nan
     = make_floatx80_init(floatx80_default_nan_high, floatx80_default_nan_low);
 
 /*----------------------------------------------------------------------------
+| The pattern for a default generated extended double-precision inf.
+*----------------------------------------------------------------------------*/
+
+#if defined(TARGET_M68K)
+#define floatx80_default_inf_high 0x7FFF
+#define floatx80_default_inf_low  LIT64(0x0000000000000000)
+#else
+#define floatx80_default_inf_high 0x7FFF
+#define floatx80_default_inf_low  LIT64(0x8000000000000000)
+#endif
+
+const floatx80 floatx80_default_inf
+    = make_floatx80_init(floatx80_default_inf_high, floatx80_default_inf_low);
+
+/*----------------------------------------------------------------------------
 | The pattern for a default generated quadruple-precision NaN.  The `high' and
 | `low' values hold the most- and least-significant bits, respectively.
 *----------------------------------------------------------------------------*/
@@ -475,6 +493,26 @@ static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN,
         return 1;
     }
 }
+#elif defined(TARGET_M68K)
+static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN,
+                   flag aIsLargerSignificand)
+{
+    /* If either operand, but not both operands, of an operation is a
+     * nonsignaling NAN, then that NAN is returned as the result. If both
+     * operands are nonsignaling NANs, then the destination operand
+     * nonsignaling NAN is returned as the result.
+     */
+
+    if (aIsSNaN) {
+        return 0;
+    } else if (bIsSNaN) {
+        return 1;
+    } else if (bIsQNaN) {
+        return 1;
+    } else {
+        return 0;
+    }
+}
 #else
 static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN,
                     flag aIsLargerSignificand)
@@ -974,7 +1012,9 @@ int floatx80_is_signaling_nan( floatx80 a )
 floatx80 floatx80_maybe_silence_nan( floatx80 a )
 {
     if (floatx80_is_signaling_nan(a)) {
-#if SNAN_BIT_IS_ONE
+#if defined(TARGET_M68K)
+        a.low |= LIT64(0x4000000000000000);
+#elif SNAN_BIT_IS_ONE
 #  if defined(TARGET_MIPS) || defined(TARGET_SH4) || defined(TARGET_UNICORE32)
         a.low = floatx80_default_nan_low;
         a.high = floatx80_default_nan_high;
diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index f1170fe..19992a9 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -902,7 +902,9 @@ static floatx80 roundAndPackFloatx80(int8 roundingPrecision, flag zSign,
                ) {
                 return packFloatx80( zSign, 0x7FFE, ~ roundMask );
             }
-            return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+            return packFloatx80(zSign,
+                                floatx80_default_inf_high,
+                                floatx80_default_inf_low);
         }
         if ( zExp <= 0 ) {
             isTiny =
@@ -1861,7 +1863,9 @@ floatx80 float32_to_floatx80(float32 a, float_status *status)
         if (aSig) {
             return commonNaNToFloatx80(float32ToCommonNaN(a, status), status);
         }
-        return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+        return packFloatx80(aSign,
+                            floatx80_default_inf_high,
+                            floatx80_default_inf_low);
     }
     if ( aExp == 0 ) {
         if ( aSig == 0 ) return packFloatx80( aSign, 0, 0 );
@@ -3633,7 +3637,9 @@ floatx80 float64_to_floatx80(float64 a, float_status *status)
         if (aSig) {
             return commonNaNToFloatx80(float64ToCommonNaN(a, status), status);
         }
-        return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+        return packFloatx80(aSign,
+                            floatx80_default_inf_high,
+                            floatx80_default_inf_low);
     }
     if ( aExp == 0 ) {
         if ( aSig == 0 ) return packFloatx80( aSign, 0, 0 );
@@ -4878,8 +4884,8 @@ int64 floatx80_to_int64(floatx80 a, float_status *status)
         if ( shiftCount ) {
             float_raise(float_flag_invalid, status);
             if (    ! aSign
-                 || (    ( aExp == 0x7FFF )
-                      && ( aSig != LIT64( 0x8000000000000000 ) ) )
+                 || ((aExp == floatx80_default_inf_high)
+                     && (aSig != floatx80_default_inf_low))
                ) {
                 return LIT64( 0x7FFFFFFFFFFFFFFF );
             }
@@ -5150,7 +5156,9 @@ static floatx80 addFloatx80Sigs(floatx80 a, floatx80 b, flag zSign,
             if ((uint64_t)(bSig << 1)) {
                 return propagateFloatx80NaN(a, b, status);
             }
-            return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+            return packFloatx80(zSign,
+                                floatx80_default_inf_high,
+                                floatx80_default_inf_low);
         }
         if ( aExp == 0 ) ++expDiff;
         shift64ExtraRightJamming( aSig, 0, - expDiff, &aSig, &zSig1 );
@@ -5228,7 +5236,8 @@ static floatx80 subFloatx80Sigs(floatx80 a, floatx80 b, flag zSign,
         if ((uint64_t)(bSig << 1)) {
             return propagateFloatx80NaN(a, b, status);
         }
-        return packFloatx80( zSign ^ 1, 0x7FFF, LIT64( 0x8000000000000000 ) );
+        return packFloatx80(zSign ^ 1, floatx80_default_inf_high,
+                            floatx80_default_inf_low);
     }
     if ( aExp == 0 ) ++expDiff;
     shift128RightJamming( aSig, 0, - expDiff, &aSig, &zSig1 );
@@ -5322,7 +5331,8 @@ floatx80 floatx80_mul(floatx80 a, floatx80 b, float_status *status)
             return propagateFloatx80NaN(a, b, status);
         }
         if ( ( bExp | bSig ) == 0 ) goto invalid;
-        return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+        return packFloatx80(zSign, floatx80_default_inf_high,
+                                   floatx80_default_inf_low);
     }
     if ( bExp == 0x7FFF ) {
         if ((uint64_t)(bSig << 1)) {
@@ -5335,7 +5345,8 @@ floatx80 floatx80_mul(floatx80 a, floatx80 b, float_status *status)
             z.high = floatx80_default_nan_high;
             return z;
         }
-        return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+        return packFloatx80(zSign, floatx80_default_inf_high,
+                            floatx80_default_inf_low);
     }
     if ( aExp == 0 ) {
         if ( aSig == 0 ) return packFloatx80( zSign, 0, 0 );
@@ -5386,7 +5397,8 @@ floatx80 floatx80_div(floatx80 a, floatx80 b, float_status *status)
             }
             goto invalid;
         }
-        return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+        return packFloatx80(zSign, floatx80_default_inf_high,
+                                   floatx80_default_inf_low);
     }
     if ( bExp == 0x7FFF ) {
         if ((uint64_t)(bSig << 1)) {
@@ -5404,7 +5416,8 @@ floatx80 floatx80_div(floatx80 a, floatx80 b, float_status *status)
                 return z;
             }
             float_raise(float_flag_divbyzero, status);
-            return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+            return packFloatx80(zSign, floatx80_default_inf_high,
+                                       floatx80_default_inf_low);
         }
         normalizeFloatx80Subnormal( bSig, &bExp, &bSig );
     }
@@ -6118,7 +6131,8 @@ floatx80 float128_to_floatx80(float128 a, float_status *status)
         if ( aSig0 | aSig1 ) {
             return commonNaNToFloatx80(float128ToCommonNaN(a, status), status);
         }
-        return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+        return packFloatx80(aSign, floatx80_default_inf_high,
+                                   floatx80_default_inf_low);
     }
     if ( aExp == 0 ) {
         if ( ( aSig0 | aSig1 ) == 0 ) return packFloatx80( aSign, 0, 0 );
diff --git a/gdb-xml/m68k-fp.xml b/gdb-xml/m68k-fp.xml
new file mode 100644
index 0000000..64290d1
--- /dev/null
+++ b/gdb-xml/m68k-fp.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2008 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.coldfire.fp">
+  <reg name="fp0" bitsize="96" type="float" group="float"/>
+  <reg name="fp1" bitsize="96" type="float" group="float"/>
+  <reg name="fp2" bitsize="96" type="float" group="float"/>
+  <reg name="fp3" bitsize="96" type="float" group="float"/>
+  <reg name="fp4" bitsize="96" type="float" group="float"/>
+  <reg name="fp5" bitsize="96" type="float" group="float"/>
+  <reg name="fp6" bitsize="96" type="float" group="float"/>
+  <reg name="fp7" bitsize="96" type="float" group="float"/>
+
+  <reg name="fpcontrol" bitsize="32" group="float"/>
+  <reg name="fpstatus" bitsize="32" group="float"/>,
+  <reg name="fpiaddr" bitsize="32" type="code_ptr" group="float"/>
+</feature>
diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h
index ded34eb..ce6e3ed 100644
--- a/include/fpu/softfloat.h
+++ b/include/fpu/softfloat.h
@@ -605,6 +605,11 @@ float64 floatx80_to_float64(floatx80, float_status *status);
 float128 floatx80_to_float128(floatx80, float_status *status);
 
 /*----------------------------------------------------------------------------
+| The pattern for a default generated extended double-precision inf.
+*----------------------------------------------------------------------------*/
+extern const floatx80 floatx80_default_inf;
+
+/*----------------------------------------------------------------------------
 | Software IEC/IEEE extended double-precision operations.
 *----------------------------------------------------------------------------*/
 floatx80 floatx80_round_to_int(floatx80, float_status *status);
@@ -643,7 +648,8 @@ static inline floatx80 floatx80_chs(floatx80 a)
 
 static inline int floatx80_is_infinity(floatx80 a)
 {
-    return (a.high & 0x7fff) == 0x7fff && a.low == 0x8000000000000000LL;
+    return (a.high & 0x7fff) == floatx80_default_inf.high &&
+                       a.low == floatx80_default_inf.low;
 }
 
 static inline int floatx80_is_neg(floatx80 a)
@@ -672,6 +678,9 @@ static inline int floatx80_is_any_nan(floatx80 a)
 #define floatx80_pi make_floatx80(0x4000, 0xc90fdaa22168c235LL)
 #define floatx80_half make_floatx80(0x3ffe, 0x8000000000000000LL)
 #define floatx80_infinity make_floatx80(0x7fff, 0x8000000000000000LL)
+#define floatx80_e make_floatx80(0x4000, 0xadf85458a2bb4a9aULL)
+#define floatx80_log2e make_floatx80(0x3fff, 0xb8aa3b295c17f0bcULL)
+#define floatx80_10 make_floatx80(0x4002, 0xa000000000000000ULL)
 
 /*----------------------------------------------------------------------------
 | The pattern for a default generated extended double-precision NaN.
diff --git a/target-m68k/cpu.c b/target-m68k/cpu.c
index 4c6760b..c79d289 100644
--- a/target-m68k/cpu.c
+++ b/target-m68k/cpu.c
@@ -46,6 +46,7 @@ static void m68k_cpu_reset(CPUState *s)
     M68kCPU *cpu = M68K_CPU(s);
     M68kCPUClass *mcc = M68K_CPU_GET_CLASS(cpu);
     CPUM68KState *env = &cpu->env;
+    int i;
 
     mcc->parent_reset(s);
 
@@ -55,6 +56,14 @@ static void m68k_cpu_reset(CPUState *s)
 #endif
     m68k_switch_sp(env);
 
+    for (i = 0; i < 8; i++) {
+        env->m68k_fregs[i].d = floatx80_default_nan;
+    }
+    env->fp0h = floatx80_default_nan.high;
+    env->fp0l = floatx80_default_nan.low;
+    env->fp1h = floatx80_default_nan.high;
+    env->fp1l = floatx80_default_nan.low;
+
     env->cc_op = CC_OP_FLAGS;
     /* TODO: We should set PC from the interrupt vector.  */
     env->pc = 0;
diff --git a/target-m68k/cpu.h b/target-m68k/cpu.h
index 7a8eabf..ce2ad0d 100644
--- a/target-m68k/cpu.h
+++ b/target-m68k/cpu.h
@@ -56,6 +56,23 @@
 
 #define NB_MMU_MODES 2
 
+typedef uint32_t CPUM68K_SingleU;
+typedef uint64_t CPUM68K_DoubleU;
+
+typedef struct {
+    uint32_t high;
+    uint64_t low;
+} __attribute__((packed)) CPUM68K_XDoubleU;
+
+typedef struct {
+   uint8_t high[2];
+   uint8_t low[10];
+} __attribute__((packed)) CPUM68K_PDoubleU;
+
+typedef CPU_LDoubleU FPReg;
+#define PRIxFPH PRIx16
+#define PRIxFPL PRIx64
+
 typedef struct CPUM68KState {
     uint32_t dregs[8];
     uint32_t aregs[8];
@@ -72,8 +89,11 @@ typedef struct CPUM68KState {
     uint32_t cc_src;
     uint32_t cc_x;
 
-    float64 fregs[8];
-    float64 fp_result;
+    /* 680x0 */
+    FPReg m68k_fregs[8];
+    /* ColdFire */
+    float64 cf_fregs[8];
+    float64 cf_fp_result;
     uint32_t fpcr;
     uint32_t fpsr;
     float_status fp_status;
@@ -86,6 +106,13 @@ typedef struct CPUM68KState {
     uint32_t macsr;
     uint32_t mac_mask;
 
+    /* Temporary storage for FPU */
+
+    uint32_t fp0h;
+    uint64_t fp0l;
+    uint32_t fp1h;
+    uint64_t fp1l;
+
     /* Temporary storage for DIV helpers.  */
     uint32_t div1;
     uint32_t div2;
@@ -167,6 +194,13 @@ typedef enum {
 #define M68K_SSP    0
 #define M68K_USP    1
 
+#define FCCF_SHIFT 24
+#define FCCF_MASK  (0xff << FCCF_SHIFT)
+#define FCCF_A     (0x01 << FCCF_SHIFT) /* Not-A-Number */
+#define FCCF_I     (0x02 << FCCF_SHIFT) /* Infinity */
+#define FCCF_Z     (0x04 << FCCF_SHIFT) /* Zero */
+#define FCCF_N     (0x08 << FCCF_SHIFT) /* Negative */
+
 /* CACR fields are implementation defined, but some bits are common.  */
 #define M68K_CACR_EUSP  0x10
 
diff --git a/target-m68k/helper.c b/target-m68k/helper.c
index 1b92b50..208c06f 100644
--- a/target-m68k/helper.c
+++ b/target-m68k/helper.c
@@ -22,6 +22,24 @@
 #include "exec/gdbstub.h"
 
 #include "exec/helper-proto.h"
+#include <math.h>
+
+#if 0
+#define DBG_FPUH(...) do { fprintf(stderr, "0x%08x: ", env->pc); \
+                           fprintf(stderr, __VA_ARGS__); } while (0)
+#define DBG_FPU(...) do { fprintf(stderr, __VA_ARGS__); } while (0)
+#else
+#define DBG_FPUH(...)
+#define DBG_FPU(...)
+#endif
+static inline float FLOAT(float32 x)
+{
+    return *(float *)&x;
+}
+static inline double DOUBLE(float64 x)
+{
+    return *(double *)&x;
+}
 
 #define SIGNBIT (1u << 31)
 
@@ -116,10 +134,10 @@ void m68k_cpu_list(FILE *f, fprintf_function cpu_fprintf)
     g_slist_free(list);
 }
 
-static int fpu_gdb_get_reg(CPUM68KState *env, uint8_t *mem_buf, int n)
+static int cf_fpu_gdb_get_reg(CPUM68KState *env, uint8_t *mem_buf, int n)
 {
     if (n < 8) {
-        stfq_p(mem_buf, env->fregs[n]);
+        stfq_p(mem_buf, env->cf_fregs[n]);
         return 8;
     }
     if (n < 11) {
@@ -130,12 +148,54 @@ static int fpu_gdb_get_reg(CPUM68KState *env, uint8_t *mem_buf, int n)
     return 0;
 }
 
-static int fpu_gdb_set_reg(CPUM68KState *env, uint8_t *mem_buf, int n)
+static int cf_fpu_gdb_set_reg(CPUM68KState *env, uint8_t *mem_buf, int n)
 {
     if (n < 8) {
-        env->fregs[n] = ldfq_p(mem_buf);
+        env->cf_fregs[n] = ldfq_p(mem_buf);
         return 8;
     }
+    switch (n) {
+    case 8:
+        env->fpcr = ldl_be_p(mem_buf);
+        return 4;
+    case 9:
+        env->fpsr = ldl_be_p(mem_buf);
+        return 4;
+    case 10:
+        return 4;
+    }
+    return 0;
+}
+
+static int m68k_fpu_gdb_get_reg(CPUM68KState *env, uint8_t *mem_buf, int n)
+{
+    if (n < 8) {
+        stw_be_p(mem_buf, env->m68k_fregs[n].l.upper);
+        memset(mem_buf + 2, 0, 2);
+        stq_be_p(mem_buf + 4, env->m68k_fregs[n].l.lower);
+        return 12;
+    }
+    switch (n) {
+    case 8:
+        stl_be_p(mem_buf, env->fpcr);
+        return 4;
+    case 9:
+        stl_be_p(mem_buf, env->fpsr);
+        return 4;
+    case 10:
+        memset(mem_buf, 0, 4);
+        return 4;
+    }
+    return 0;
+}
+
+static int m68k_fpu_gdb_set_reg(CPUM68KState *env, uint8_t *mem_buf, int n)
+{
+    if (n < 8) {
+        env->m68k_fregs[n].l.upper = lduw_be_p(mem_buf);
+        env->m68k_fregs[n].l.lower = ldq_be_p(mem_buf + 4);
+        return 12;
+    }
     if (n < 11) {
         /* FP control registers (not implemented)  */
         return 4;
@@ -169,8 +229,11 @@ void m68k_cpu_init_gdb(M68kCPU *cpu)
     CPUM68KState *env = &cpu->env;
 
     if (m68k_feature(env, M68K_FEATURE_CF_FPU)) {
-        gdb_register_coprocessor(cs, fpu_gdb_get_reg, fpu_gdb_set_reg,
+        gdb_register_coprocessor(cs, cf_fpu_gdb_get_reg, cf_fpu_gdb_set_reg,
                                  11, "cf-fp.xml", 18);
+    } else if (m68k_feature(env, M68K_FEATURE_FPU)) {
+        gdb_register_coprocessor(cs, m68k_fpu_gdb_get_reg,
+                                 m68k_fpu_gdb_set_reg, 11, "m68k-fp.xml", 18);
     }
     /* TODO: Add [E]MAC registers.  */
 }
@@ -864,7 +927,7 @@ HELPER_ROXL(uint8_t, 8)
 HELPER_ROXL(uint16_t, 16)
 HELPER_ROXL(uint32_t, 32)
 
-/* FPU helpers.  */
+/* ColdFire FPU helpers.  */
 uint32_t HELPER(f64_to_i32)(CPUM68KState *env, float64 val)
 {
     return float64_to_int32(val, &env->fp_status);
@@ -953,6 +1016,799 @@ uint32_t HELPER(compare_f64)(CPUM68KState *env, float64 val)
     return float64_compare_quiet(val, float64_zero, &env->fp_status);
 }
 
+/* 680x0 FPU helpers.  */
+
+static const floatx80 fpu_rom[128] = {
+    [0x00] = floatx80_pi,                                       /* Pi */
+
+    [0x0b] = { .high = 0x3ffd, .low = 0x9a209a84fbcff798ULL },  /* Log10(2) */
+    [0x0c] = floatx80_e,                                        /* e        */
+    [0x0d] = floatx80_log2e,                                    /* Log2(e)  */
+    [0x0e] = { .high = 0x3ffd, .low = 0xde5bd8a937287195ULL },  /* Log10(e) */
+    [0x0f] = floatx80_zero,                                     /* Zero     */
+
+    [0x30] = floatx80_ln2,                                      /* ln(2)    */
+    [0x31] = { .high = 0x4000, .low = 0x935d8dddaaa8ac17ULL },  /* ln(10)   */
+    [0x32] = floatx80_one,                                      /* 10^0     */
+    [0x33] = floatx80_10,                                       /* 10^1     */
+    [0x34] = { .high = 0x4005, .low = 0xc800000000000000ULL },  /* 10^2     */
+    [0x35] = { .high = 0x400c, .low = 0x9c40000000000000ULL },  /* 10^4     */
+    [0x36] = { .high = 0x4019, .low = 0xbebc200000000000ULL },  /* 10^8     */
+    [0x37] = { .high = 0x4034, .low = 0x8e1bc9bf04000000ULL },  /* 10^16    */
+    [0x38] = { .high = 0x4069, .low = 0x9dc5ada82b70b59eULL },  /* 10^32    */
+    [0x39] = { .high = 0x40d3, .low = 0xc2781f49ffcfa6d5ULL },  /* 10^64    */
+    [0x3a] = { .high = 0x41a8, .low = 0x93ba47c980e98ce0ULL },  /* 10^128   */
+    [0x3b] = { .high = 0x4351, .low = 0xaa7eebfb9df9de8eULL },  /* 10^256   */
+    [0x3c] = { .high = 0x46a3, .low = 0xe319a0aea60e91c7ULL },  /* 10^512   */
+    [0x3d] = { .high = 0x4d48, .low = 0xc976758681750c17ULL },  /* 10^1024  */
+    [0x3e] = { .high = 0x5a92, .low = 0x9e8b3b5dc53d5de5ULL },  /* 10^2048  */
+    [0x3f] = { .high = 0x7525, .low = 0xc46052028a20979bULL },  /* 10^4096  */
+};
+
+static inline floatx80 FP0_to_floatx80(CPUM68KState *env)
+{
+    floatx80 res;
+
+    res.high = env->fp0h;
+    res.low = env->fp0l;
+
+    return res;
+}
+
+static inline void floatx80_to_FP0(CPUM68KState *env, floatx80 res)
+{
+    env->fp0h = res.high;
+    env->fp0l = res.low;
+}
+
+static inline void floatx80_to_FP1(CPUM68KState *env, floatx80 res)
+{
+    env->fp1h = res.high;
+    env->fp1l = res.low;
+}
+
+static inline int32_t FP0_to_int32(CPUM68KState *env)
+{
+    return env->fp0h;
+}
+
+static inline void int32_to_FP0(CPUM68KState *env, int32_t val)
+{
+    env->fp0h = val;
+}
+
+static inline float32 FP0_to_float32(CPUM68KState *env)
+{
+    return *(float32 *)&env->fp0h;
+}
+
+static inline void float32_to_FP0(CPUM68KState *env, float32 val)
+{
+
+    env->fp0h = *(uint32_t *)&val;
+}
+
+static inline float64 FP0_to_float64(CPUM68KState *env)
+{
+    return *(float64 *)&env->fp0l;
+}
+
+static inline void float64_to_FP0(CPUM68KState *env, float64 val)
+{
+    env->fp0l = *(uint64_t *)&val;
+}
+
+static inline floatx80 FP1_to_floatx80(CPUM68KState *env)
+{
+    floatx80 res;
+
+    res.high = env->fp1h;
+    res.low = env->fp1l;
+
+    return res;
+}
+
+static inline long double floatx80_to_ldouble(floatx80 val)
+{
+    if (floatx80_is_infinity(val)) {
+        if (floatx80_is_neg(val)) {
+            return -__builtin_infl();
+        }
+        return __builtin_infl();
+    }
+    if (floatx80_is_any_nan(val)) {
+        char low[20];
+        sprintf(low, "0x%016"PRIx64, val.low);
+
+        return nanl(low);
+    }
+
+    return *(long double *)&val;
+}
+
+static inline floatx80 ldouble_to_floatx80(long double val)
+{
+    floatx80 res;
+
+    if (isinf(val)) {
+        res.high = floatx80_default_nan.high;
+        res.low = 0;
+    }
+    if (isinf(val) < 0) {
+        res.high |= 0x8000;
+    }
+    if (isnan(val)) {
+        res.high = floatx80_default_nan.high;
+        res.low = *(uint64_t *)((char *)&val + 4);
+    }
+    return *(floatx80 *)&val;
+}
+
+void HELPER(const_FP0)(CPUM68KState *env, uint32_t offset)
+{
+    env->fp0h = fpu_rom[offset].high;
+    env->fp0l = fpu_rom[offset].low;
+    DBG_FPUH("ROM[0x%02x] %"PRIxFPH" %"PRIxFPL" %.17Lg\n",
+             offset, env->fp0h, env->fp0l,
+             floatx80_to_ldouble(FP0_to_floatx80(env)));
+}
+
+static inline void restore_precision_mode(CPUM68KState *env)
+{
+    int rounding_precision;
+
+    rounding_precision = (env->fpcr >> 6) & 0x03;
+
+    switch (rounding_precision) {
+    case 0: /* extended */
+        set_floatx80_rounding_precision(80, &env->fp_status);
+        break;
+    case 1: /* single */
+        set_floatx80_rounding_precision(32, &env->fp_status);
+        break;
+    case 2: /* double */
+        set_floatx80_rounding_precision(64, &env->fp_status);
+        break;
+    case 3: /* reserved */
+    default:
+        break;
+    }
+}
+
+static inline void restore_rounding_mode(CPUM68KState *env)
+{
+    int rounding_mode;
+
+    rounding_mode = (env->fpcr >> 4) & 0x03;
+
+    switch (rounding_mode) {
+    case 0: /* round to nearest */
+        set_float_rounding_mode(float_round_nearest_even, &env->fp_status);
+        break;
+    case 1: /* round to zero */
+        set_float_rounding_mode(float_round_to_zero, &env->fp_status);
+        break;
+    case 2: /* round toward minus infinity */
+        set_float_rounding_mode(float_round_down, &env->fp_status);
+        break;
+    case 3: /* round toward positive infinity */
+        set_float_rounding_mode(float_round_up, &env->fp_status);
+        break;
+    }
+}
+
+void HELPER(set_fpcr)(CPUM68KState *env, uint32_t val)
+{
+    DBG_FPUH("set_fpcr %04x\n", val);
+
+    env->fpcr = val & 0xffff;
+
+    restore_precision_mode(env);
+    restore_rounding_mode(env);
+}
+
+void HELPER(exts32_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    DBG_FPUH("exts32_FP0 %d", FP0_to_int32(env));
+
+    res = int32_to_floatx80(FP0_to_int32(env), &env->fp_status);
+
+    DBG_FPU(" = %Lg\n", floatx80_to_ldouble(res));
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(extf32_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    DBG_FPUH("extf32_FP0 %f %08x", FLOAT(FP0_to_float32(env)),
+                                   FP0_to_int32(env));
+    res = float32_to_floatx80(FP0_to_float32(env), &env->fp_status);
+    DBG_FPU(" = %Lg\n", floatx80_to_ldouble(res));
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(extf64_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+    uint64_t val;
+
+    val = FP0_to_float64(env);
+    DBG_FPUH("extf64_FP0 0x%016"PRIx64", %g", val, *(double *)&val);
+    res = float64_to_floatx80(val, &env->fp_status);
+    DBG_FPU(" = %Lg\n", floatx80_to_ldouble(res));
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(extp96_FP0)(CPUM68KState *env)
+{
+}
+
+void HELPER(reds32_FP0)(CPUM68KState *env)
+{
+    floatx80 val;
+    int32_t res;
+
+    val = FP0_to_floatx80(env);
+    DBG_FPUH("reds32_FP0 %Lg (%08x %016"PRIx64")",
+             floatx80_to_ldouble(val), env->fp0h, env->fp0l);
+    res = floatx80_to_int32(val, &env->fp_status);
+    DBG_FPU(" = %d\n", res);
+
+    int32_to_FP0(env, res);
+}
+
+void HELPER(redf32_FP0)(CPUM68KState *env)
+{
+    floatx80 val;
+    float32 res;
+
+    val = FP0_to_floatx80(env);
+    DBG_FPUH("redf32_FP0 %Lg", floatx80_to_ldouble(val));
+    res = floatx80_to_float32(val, &env->fp_status);
+    DBG_FPU(" = %f\n", FLOAT(res));
+
+    float32_to_FP0(env, res);
+}
+
+void HELPER(redf64_FP0)(CPUM68KState *env)
+{
+    floatx80 val;
+    float64 res;
+
+    val = FP0_to_floatx80(env);
+    DBG_FPUH("redf64_FP0 %Lg", floatx80_to_ldouble(val));
+    res = floatx80_to_float64(val, &env->fp_status);
+    DBG_FPU(" = %g\n", *(double *)&res);
+
+    float64_to_FP0(env, res);
+}
+
+void HELPER(redp96_FP0)(CPUM68KState *env)
+{
+    DBG_FPUH("redp96_FP0\n");
+}
+
+void HELPER(iround_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    res = FP0_to_floatx80(env);
+
+    DBG_FPUH("iround_FP0 %Lg", floatx80_to_ldouble(res));
+
+    res = floatx80_round_to_int(res, &env->fp_status);
+
+    DBG_FPU(" = %Lg\n", floatx80_to_ldouble(res));
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(sinh_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+    long double val;
+
+    res = FP0_to_floatx80(env);
+    val = floatx80_to_ldouble(res);
+
+    DBG_FPUH("sinh_FP0 %Lg", val);
+    val = sinhl(val);
+    DBG_FPU(" = %Lg", val);
+    res = ldouble_to_floatx80(val);
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(itrunc_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    res = FP0_to_floatx80(env);
+    DBG_FPUH("itrunc_FP0 %Lg", floatx80_to_ldouble(res));
+
+    set_float_rounding_mode(float_round_to_zero, &env->fp_status);
+    res = floatx80_round_to_int(res, &env->fp_status);
+    restore_rounding_mode(env);
+
+    DBG_FPU(" = %Lg\n", floatx80_to_ldouble(res));
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(sqrt_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    res = FP0_to_floatx80(env);
+    DBG_FPUH("sqrt_FP0 %Lg", floatx80_to_ldouble(res));
+    res = floatx80_sqrt(res, &env->fp_status);
+    DBG_FPU("  = %Lg\n", floatx80_to_ldouble(res));
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(lognp1_FP0)(CPUM68KState *env)
+{
+    floatx80 val;
+    long double res;
+
+    val = FP0_to_floatx80(env);
+    DBG_FPUH("lognp1_FP0 %Lg", floatx80_to_ldouble(val));
+    res = logl(floatx80_to_ldouble(val) + 1.0);
+    DBG_FPU(" = %Lg\n", res);
+
+    floatx80_to_FP0(env, ldouble_to_floatx80(res));
+}
+
+void HELPER(ln_FP0)(CPUM68KState *env)
+{
+    floatx80 val;
+    long double res;
+
+    val = FP0_to_floatx80(env);
+    DBG_FPUH("ln_FP0 %Lg", floatx80_to_ldouble(val));
+    res = logl(floatx80_to_ldouble(val));
+    DBG_FPU(" = %Lg\n", res);
+
+    floatx80_to_FP0(env, ldouble_to_floatx80(res));
+}
+
+void HELPER(log10_FP0)(CPUM68KState *env)
+{
+    floatx80 val;
+    long double res;
+
+    val = FP0_to_floatx80(env);
+    DBG_FPUH("log10_FP0 %Lg", floatx80_to_ldouble(val));
+    res = log10l(floatx80_to_ldouble(val));
+    DBG_FPU(" = %Lg\n", res);
+
+    floatx80_to_FP0(env, ldouble_to_floatx80(res));
+}
+
+void HELPER(atan_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+    long double val;
+
+    res = FP0_to_floatx80(env);
+    val = floatx80_to_ldouble(res);
+
+    DBG_FPUH("atan_FP0 %Lg", val);
+    val = atanl(val);
+    DBG_FPU(" = %Lg", val);
+    res = ldouble_to_floatx80(val);
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(asin_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+    long double val;
+
+    res = FP0_to_floatx80(env);
+    val = floatx80_to_ldouble(res);
+
+    DBG_FPUH("asin_FP0 %Lg", val);
+    val = asinl(val);
+    DBG_FPU(" = %Lg", val);
+    res = ldouble_to_floatx80(val);
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(atanh_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+    long double val;
+
+    res = FP0_to_floatx80(env);
+    val = floatx80_to_ldouble(res);
+
+    DBG_FPUH("atanh_FP0 %Lg", val);
+    val = atanhl(val);
+    DBG_FPU(" = %Lg", val);
+    res = ldouble_to_floatx80(val);
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(sin_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+    long double val;
+
+    res = FP0_to_floatx80(env);
+    val = floatx80_to_ldouble(res);
+
+    DBG_FPUH("sin_FP0 %Lg", val);
+    val = sinl(val);
+    DBG_FPU(" = %Lg", val);
+    res = ldouble_to_floatx80(val);
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(tanh_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+    long double val;
+
+    res = FP0_to_floatx80(env);
+    val = floatx80_to_ldouble(res);
+
+    DBG_FPUH("tanh_FP0 %Lg", val);
+    val = tanhl(val);
+    DBG_FPU(" = %Lg", val);
+    res = ldouble_to_floatx80(val);
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(tan_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+    long double val;
+
+    res = FP0_to_floatx80(env);
+    val = floatx80_to_ldouble(res);
+
+    DBG_FPUH("tan_FP0 %Lg", val);
+    val = tanl(val);
+    DBG_FPU(" = %Lg", val);
+    res = ldouble_to_floatx80(val);
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(exp_FP0)(CPUM68KState *env)
+{
+    floatx80 f;
+    long double res;
+
+    f = FP0_to_floatx80(env);
+
+    DBG_FPUH("exp_FP0 %Lg", floatx80_to_ldouble(f));
+
+    res = expl(floatx80_to_ldouble(f));
+
+    DBG_FPU(" = %Lg\n", res);
+    floatx80_to_FP0(env, ldouble_to_floatx80(res));
+}
+
+void HELPER(exp2_FP0)(CPUM68KState *env)
+{
+    floatx80 f;
+    long double res;
+
+    f = FP0_to_floatx80(env);
+
+    DBG_FPUH("exp2_FP0 %Lg", floatx80_to_ldouble(f));
+
+    res = exp2l(floatx80_to_ldouble(f));
+
+    DBG_FPU(" = %Lg\n", res);
+    floatx80_to_FP0(env, ldouble_to_floatx80(res));
+}
+
+void HELPER(exp10_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+    long double val;
+
+    res = FP0_to_floatx80(env);
+    val = floatx80_to_ldouble(res);
+
+    DBG_FPUH("exp2_FP0 %Lg", val);
+    val = exp10l(val);
+    DBG_FPU(" = %Lg", val);
+    res = ldouble_to_floatx80(val);
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(abs_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    res = FP0_to_floatx80(env);
+    DBG_FPUH("abs_FP0 %Lg", floatx80_to_ldouble(res));
+    res = floatx80_abs(res);
+    DBG_FPU(" = %Lg\n", floatx80_to_ldouble(res));
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(cosh_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+    long double val;
+
+    res = FP0_to_floatx80(env);
+    val = floatx80_to_ldouble(res);
+
+    DBG_FPUH("cosh_FP0 %Lg", val);
+    val = coshl(val);
+    DBG_FPU(" = %Lg", val);
+    res = ldouble_to_floatx80(val);
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(chs_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    res = FP0_to_floatx80(env);
+    DBG_FPUH("chs_FP0 %Lg", floatx80_to_ldouble(res));
+    res = floatx80_chs(res);
+    DBG_FPU(" = %Lg\n", floatx80_to_ldouble(res));
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(acos_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+    long double val;
+
+    res = FP0_to_floatx80(env);
+    val = floatx80_to_ldouble(res);
+
+    DBG_FPUH("acos_FP0 %Lg", val);
+    val = acosl(val);
+    DBG_FPU(" = %Lg", val);
+    res = ldouble_to_floatx80(val);
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(cos_FP0)(CPUM68KState *env)
+{
+    floatx80 res;
+    long double val;
+
+    res = FP0_to_floatx80(env);
+    val = floatx80_to_ldouble(res);
+
+    DBG_FPUH("cos_FP0 %Lg", val);
+    val = cosl(val);
+    DBG_FPU(" = %Lg", val);
+    res = ldouble_to_floatx80(val);
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(getexp_FP0)(CPUM68KState *env)
+{
+    int32_t exp;
+    floatx80 res;
+
+    DBG_FPUH("getexp_FP0 %Lg", floatx80_to_ldouble(FP0_to_floatx80(env)));
+
+    DBG_FPU(" fp0h 0x%08x fp0l 0x%016" PRIx64, env->fp0h, env->fp0l);
+
+    exp = (env->fp0h & 0x7fff) - 0x3fff;
+
+    res = int32_to_floatx80(exp, &env->fp_status);
+
+    DBG_FPU(" = %Lg", floatx80_to_ldouble(res));
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(scale_FP0_FP1)(CPUM68KState *env)
+{
+    int32_t scale;
+    int32_t exp;
+
+    DBG_FPUH("scale_FP0 %Lg", floatx80_to_ldouble(FP0_to_floatx80(env)));
+
+    DBG_FPU(" fp0h 0x%08x fp0l 0x%016" PRIx64, env->fp0h, env->fp0l);
+
+    scale = floatx80_to_int32(FP0_to_floatx80(env), &env->fp_status);
+
+    exp = (env->fp1h & 0x7fff) + scale;
+
+    env->fp0h = (env->fp1h & 0x8000) | (exp & 0x7fff);
+    env->fp0l = env->fp1l;
+    DBG_FPU(" = %Lg", floatx80_to_ldouble(FP0_to_floatx80(env)));
+}
+
+void HELPER(add_FP0_FP1)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    DBG_FPUH("add_FP0_FP1 %Lg %Lg", floatx80_to_ldouble(FP0_to_floatx80(env)),
+            floatx80_to_ldouble(FP1_to_floatx80(env)));
+    res = floatx80_add(FP0_to_floatx80(env), FP1_to_floatx80(env),
+                      &env->fp_status);
+    DBG_FPU(" = %Lg\n", floatx80_to_ldouble(res));
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(sub_FP0_FP1)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    DBG_FPUH("sub_FP0 %Lg %Lg", floatx80_to_ldouble(FP0_to_floatx80(env)),
+            floatx80_to_ldouble(FP1_to_floatx80(env)));
+    res = floatx80_sub(FP1_to_floatx80(env), FP0_to_floatx80(env),
+                       &env->fp_status);
+    DBG_FPU(" = %Lg\n", floatx80_to_ldouble(res));
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(mul_FP0_FP1)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    DBG_FPUH("mul_FP0_FP1 %Lg %Lg",
+            floatx80_to_ldouble(FP0_to_floatx80(env)),
+            floatx80_to_ldouble(FP1_to_floatx80(env)));
+    res = floatx80_mul(FP0_to_floatx80(env), FP1_to_floatx80(env),
+                       &env->fp_status);
+    DBG_FPU(" = %Lg\n", floatx80_to_ldouble(res));
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(div_FP0_FP1)(CPUM68KState *env)
+{
+    floatx80 res;
+
+    DBG_FPUH("div_FP0_FP1 %Lg %Lg",
+            floatx80_to_ldouble(FP0_to_floatx80(env)),
+            floatx80_to_ldouble(FP1_to_floatx80(env)));
+    res = floatx80_div(FP1_to_floatx80(env), FP0_to_floatx80(env),
+                       &env->fp_status);
+    DBG_FPU(" = %Lg\n", floatx80_to_ldouble(res));
+
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(mod_FP0_FP1)(CPUM68KState *env)
+{
+    floatx80 res;
+    long double src, dst;
+
+    src = floatx80_to_ldouble(FP0_to_floatx80(env));
+    dst = floatx80_to_ldouble(FP1_to_floatx80(env));
+
+    DBG_FPUH("mod_FP0_FP1 %Lg %Lg", src, dst);
+    dst = fmodl(dst, src);
+    DBG_FPU(" = %Lg\n", dst);
+
+    res = ldouble_to_floatx80(dst);
+    floatx80_to_FP0(env, res);
+}
+
+void HELPER(sincos_FP0_FP1)(CPUM68KState *env)
+{
+    floatx80 res;
+    long double val, valsin, valcos;
+
+    res = FP0_to_floatx80(env);
+    val = floatx80_to_ldouble(res);
+
+    DBG_FPUH("sincos_FP0 %Lg", val);
+    sincosl(val, &valsin, &valcos);
+    DBG_FPU(" = %Lg, %Lg", valsin, valcos);
+    res = ldouble_to_floatx80(valsin);
+    floatx80_to_FP0(env, res);
+    res = ldouble_to_floatx80(valcos);
+    floatx80_to_FP1(env, res);
+}
+
+static void set_fpcc(CPUM68KState *env, floatx80 val)
+{
+    uint32_t fpcc = 0;
+
+    if (floatx80_is_any_nan(val)) {
+        fpcc |= FCCF_A;
+    }
+    if (floatx80_is_infinity(val)) {
+        fpcc |= FCCF_I;
+    }
+    if (floatx80_is_neg(val)) {
+        fpcc |= FCCF_N;
+    }
+    if (floatx80_is_zero(val)) {
+        fpcc |= FCCF_Z;
+    }
+
+    DBG_FPU("FPCC 0x%02x %c%c%c%c\n", fpcc >> FCCF_SHIFT,
+            fpcc & FCCF_N ? 'N' : '-',
+            fpcc & FCCF_Z ? 'Z' : '-',
+            fpcc & FCCF_I ? 'I' : '-',
+            fpcc & FCCF_A ? 'A' : '-');
+    env->fpsr = (env->fpsr & ~FCCF_MASK) | fpcc;
+}
+
+void HELPER(fcmp_FP0_FP1)(CPUM68KState *env)
+{
+    /* ??? This may incorrectly raise exceptions.  */
+    /* ??? Should flush denormals to zero.  */
+    floatx80 res;
+    DBG_FPU("cmp_FP0_FP1 %Lg %Lg\n", floatx80_to_ldouble(FP0_to_floatx80(env)),
+            floatx80_to_ldouble(FP1_to_floatx80(env)));
+    res = floatx80_sub(FP1_to_floatx80(env), FP0_to_floatx80(env),
+                       &env->fp_status);
+    if (floatx80_is_any_nan(res)) {
+        /* +/-inf compares equal against itself, but sub returns nan.  */
+        if (!floatx80_is_any_nan(FP0_to_floatx80(env))
+            && !floatx80_is_any_nan(FP1_to_floatx80(env))) {
+            res = floatx80_zero;
+            if (floatx80_lt_quiet(FP1_to_floatx80(env), res, &env->fp_status)) {
+                res = floatx80_chs(res);
+            }
+        }
+    }
+
+    set_fpcc(env, res);
+}
+
+void HELPER(compare_FP0)(CPUM68KState *env)
+{
+    DBG_FPU("compare_FP0 %Lg\n", floatx80_to_ldouble(FP0_to_floatx80(env)));
+    set_fpcc(env, FP0_to_floatx80(env));
+}
+
+void HELPER(update_fpsr)(CPUM68KState *env)
+{
+    int eflags;
+
+    DBG_FPU("update_fpsr");
+
+    env->fpsr &= 0xff00;
+
+    eflags = get_float_exception_flags(&env->fp_status);
+
+    if (eflags & float_flag_invalid) {
+        env->fpsr |= 0x0080;
+        env->fpsr |= 0x2000;
+    }
+    if (eflags & float_flag_divbyzero) {
+        env->fpsr |= 0x0010;
+        env->fpsr |= 0x0400;
+    }
+    if (eflags & float_flag_overflow) {
+        env->fpsr |= 0x0040;
+        env->fpsr |= 0x1000;
+    }
+    if (eflags & float_flag_underflow) {
+        env->fpsr |= 0x0020;
+        env->fpsr |= 0x0800;
+    }
+    if (eflags & float_flag_inexact) {
+        env->fpsr |= 0x0008;
+        env->fpsr |= 0x0200;
+    }
+    set_float_exception_flags(0, &env->fp_status);
+}
+
+void HELPER(fmovem)(CPUM68KState *env, uint32_t opsize, uint32_t mode,
+                    uint32_t mask)
+{
+    fprintf(stderr, "MISSING HELPER fmovem\n");
+}
+
 /* MAC unit.  */
 /* FIXME: The MAC unit implementation is a bit of a mess.  Some helpers
    take values,  others take register numbers and manipulate the contents
diff --git a/target-m68k/helper.h b/target-m68k/helper.h
index 5db4278..eaa34c9 100644
--- a/target-m68k/helper.h
+++ b/target-m68k/helper.h
@@ -63,6 +63,50 @@ DEF_HELPER_3(mul_f64, f64, env, f64, f64)
 DEF_HELPER_3(div_f64, f64, env, f64, f64)
 DEF_HELPER_3(sub_cmp_f64, f64, env, f64, f64)
 DEF_HELPER_2(compare_f64, i32, env, f64)
+DEF_HELPER_1(exts32_FP0, void, env)
+DEF_HELPER_1(extf32_FP0, void, env)
+DEF_HELPER_1(extf64_FP0, void, env)
+DEF_HELPER_1(redf32_FP0, void, env)
+DEF_HELPER_1(redf64_FP0, void, env)
+DEF_HELPER_1(extp96_FP0, void, env)
+DEF_HELPER_1(reds32_FP0, void, env)
+DEF_HELPER_1(redp96_FP0, void, env)
+
+DEF_HELPER_4(fmovem, void, env, i32, i32, i32)
+DEF_HELPER_2(set_fpcr, void, env, i32)
+DEF_HELPER_2(const_FP0, void, env, i32)
+DEF_HELPER_1(iround_FP0, void, env)
+DEF_HELPER_1(sinh_FP0, void, env)
+DEF_HELPER_1(itrunc_FP0, void, env)
+DEF_HELPER_1(sqrt_FP0, void, env)
+DEF_HELPER_1(lognp1_FP0, void, env)
+DEF_HELPER_1(atan_FP0, void, env)
+DEF_HELPER_1(asin_FP0, void, env)
+DEF_HELPER_1(atanh_FP0, void, env)
+DEF_HELPER_1(sin_FP0, void, env)
+DEF_HELPER_1(tanh_FP0, void, env)
+DEF_HELPER_1(tan_FP0, void, env)
+DEF_HELPER_1(exp_FP0, void, env)
+DEF_HELPER_1(exp2_FP0, void, env)
+DEF_HELPER_1(exp10_FP0, void, env)
+DEF_HELPER_1(ln_FP0, void, env)
+DEF_HELPER_1(log10_FP0, void, env)
+DEF_HELPER_1(abs_FP0, void, env)
+DEF_HELPER_1(cosh_FP0, void, env)
+DEF_HELPER_1(chs_FP0, void, env)
+DEF_HELPER_1(acos_FP0, void, env)
+DEF_HELPER_1(cos_FP0, void, env)
+DEF_HELPER_1(getexp_FP0, void, env)
+DEF_HELPER_1(scale_FP0_FP1, void, env)
+DEF_HELPER_1(add_FP0_FP1, void, env)
+DEF_HELPER_1(sub_FP0_FP1, void, env)
+DEF_HELPER_1(mul_FP0_FP1, void, env)
+DEF_HELPER_1(div_FP0_FP1, void, env)
+DEF_HELPER_1(mod_FP0_FP1, void, env)
+DEF_HELPER_1(sincos_FP0_FP1, void, env)
+DEF_HELPER_1(fcmp_FP0_FP1, void, env)
+DEF_HELPER_1(compare_FP0, void, env)
+DEF_HELPER_1(update_fpsr, void, env)
 
 DEF_HELPER_3(mac_move, void, env, i32, i32)
 DEF_HELPER_3(macmulf, i64, env, i32, i32)
diff --git a/target-m68k/qregs.def b/target-m68k/qregs.def
index aba6c9a..af37602 100644
--- a/target-m68k/qregs.def
+++ b/target-m68k/qregs.def
@@ -1,4 +1,6 @@
-DEFF64(FP_RESULT, fp_result)
+DEFF64(FP_RESULT, cf_fp_result)
+DEFF96(FP0, fp0)
+DEFF96(FP1, fp1)
 DEFO32(PC, pc)
 DEFO32(SR, sr)
 DEFO32(CC_OP, cc_op)
@@ -10,3 +12,4 @@ DEFO32(DIV2, div2)
 DEFO32(QUADH, quadh)
 DEFO32(MACSR, macsr)
 DEFO32(MAC_MASK, mac_mask)
+DEFO32(FPSR, fpsr)
diff --git a/target-m68k/translate.c b/target-m68k/translate.c
index a5e68cf..05222fb 100644
--- a/target-m68k/translate.c
+++ b/target-m68k/translate.c
@@ -32,7 +32,7 @@
 
 //#define DEBUG_DISPATCH 1
 
-/* Fake floating point.  */
+/* ColdFire: Fake floating point.  */
 #define tcg_gen_mov_f64 tcg_gen_mov_i64
 #define tcg_gen_qemu_ldf64 tcg_gen_qemu_ld64
 #define tcg_gen_qemu_stf64 tcg_gen_qemu_st64
@@ -40,10 +40,14 @@
 #define DEFO32(name, offset) static TCGv QREG_##name;
 #define DEFO64(name, offset) static TCGv_i64 QREG_##name;
 #define DEFF64(name, offset) static TCGv_i64 QREG_##name;
+#define DEFF96(name, offset) \
+    static TCGv_i32 QREG_##name##H; \
+    static TCGv_i64 QREG_##name##L;
 #include "qregs.def"
 #undef DEFO32
 #undef DEFO64
 #undef DEFF64
+#undef DEFF96
 
 static TCGv_i32 cpu_halted;
 static TCGv_i32 cpu_exception_index;
@@ -55,6 +59,8 @@ static TCGv cpu_dregs[8];
 static TCGv cpu_aregs[8];
 static TCGv_i64 cpu_fregs[8];
 static TCGv_i64 cpu_macc[4];
+static TCGv QEMU_FPSR;
+static TCGv QEMU_FPCR;
 
 #define REG(insn, pos) (((insn) >> (pos)) & 7)
 #define DREG(insn, pos) cpu_dregs[REG(insn, pos)]
@@ -80,10 +86,17 @@ void m68k_tcg_init(void)
 #define DEFO32(name,  offset) QREG_##name = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUM68KState, offset), #name);
 #define DEFO64(name,  offset) QREG_##name = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUM68KState, offset), #name);
 #define DEFF64(name,  offset) DEFO64(name, offset)
+#define DEFF96(name,  offset) do { \
+QREG_##name##H = tcg_global_mem_new_i32(TCG_AREG0, \
+                                    offsetof(CPUM68KState, offset##h), #name); \
+QREG_##name##L = tcg_global_mem_new_i64(TCG_AREG0, \
+                                    offsetof(CPUM68KState, offset##l), #name); \
+} while (0);
 #include "qregs.def"
 #undef DEFO32
 #undef DEFO64
 #undef DEFF64
+#undef DEFF96
 
     cpu_halted = tcg_global_mem_new_i32(TCG_AREG0,
                                         -offsetof(M68kCPU, env) +
@@ -107,7 +120,7 @@ void m68k_tcg_init(void)
         p += 3;
         sprintf(p, "F%d", i);
         cpu_fregs[i] = tcg_global_mem_new_i64(TCG_AREG0,
-                                          offsetof(CPUM68KState, fregs[i]), p);
+                                       offsetof(CPUM68KState, cf_fregs[i]), p);
         p += 3;
     }
     for (i = 0; i < 4; i++) {
@@ -117,6 +130,11 @@ void m68k_tcg_init(void)
         p += 5;
     }
 
+    QEMU_FPSR = tcg_global_mem_new(TCG_AREG0, offsetof(CPUM68KState, fpsr),
+                                   "FPSR");
+    QEMU_FPCR = tcg_global_mem_new(TCG_AREG0, offsetof(CPUM68KState, fpcr),
+                                   "FPCR");
+
     NULL_QREG = tcg_global_mem_new(TCG_AREG0, -4, "NULL");
     store_dummy = tcg_global_mem_new(TCG_AREG0, -8, "NULL");
 }
@@ -246,6 +264,46 @@ static inline void gen_addr_fault(DisasContext *s)
     gen_exception(s, s->insn_pc, EXCP_ADDRESS);
 }
 
+static void gen_op_load_fpr_FP0(int freg)
+{
+    tcg_gen_ld16u_i32(QREG_FP0H, cpu_env,
+                      offsetof(CPUM68KState, m68k_fregs[freg]) +
+                      offsetof(FPReg, d.high));
+    tcg_gen_ld_i64(QREG_FP0L, cpu_env,
+                   offsetof(CPUM68KState, m68k_fregs[freg]) +
+                   offsetof(FPReg, d.low));
+}
+
+static void gen_op_store_fpr_FP0(int freg)
+{
+    tcg_gen_st16_i32(QREG_FP0H, cpu_env,
+                     offsetof(CPUM68KState, m68k_fregs[freg]) +
+                     offsetof(FPReg, d.high));
+    tcg_gen_st_i64(QREG_FP0L, cpu_env,
+                   offsetof(CPUM68KState, m68k_fregs[freg]) +
+                   offsetof(FPReg, d.low));
+}
+
+static void gen_op_store_fpr_FP1(int freg)
+{
+    tcg_gen_st16_i32(QREG_FP1H, cpu_env,
+                     offsetof(CPUM68KState, m68k_fregs[freg]) +
+                     offsetof(FPReg, d.high));
+    tcg_gen_st_i64(QREG_FP1L, cpu_env,
+                   offsetof(CPUM68KState, m68k_fregs[freg]) +
+                   offsetof(FPReg, d.low));
+}
+
+static void gen_op_load_fpr_FP1(int freg)
+{
+    tcg_gen_ld16u_i32(QREG_FP1H, cpu_env,
+                      offsetof(CPUM68KState, m68k_fregs[freg]) +
+                      offsetof(FPReg, d.high));
+    tcg_gen_ld_i64(QREG_FP1L, cpu_env,
+                   offsetof(CPUM68KState, m68k_fregs[freg]) +
+                   offsetof(FPReg, d.low));
+}
+
 /* Generate a load from the specified address.  Narrow values are
    sign extended to full register width.  */
 static inline TCGv gen_load(DisasContext * s, int opsize, TCGv addr, int sign)
@@ -835,6 +893,320 @@ static TCGv gen_ea(CPUM68KState *env, DisasContext *s, uint16_t insn,
     return NULL_QREG;
 }
 
+static inline void gen_extend_FP0(int opsize)
+{
+    switch (opsize) {
+    case OS_BYTE:
+        tcg_gen_ext8s_i32(QREG_FP0H, QREG_FP0H);
+        gen_helper_exts32_FP0(cpu_env);
+        break;
+    case OS_WORD:
+        tcg_gen_ext16s_i32(QREG_FP0H, QREG_FP0H);
+        gen_helper_exts32_FP0(cpu_env);
+        break;
+    case OS_LONG:
+        gen_helper_exts32_FP0(cpu_env);
+        break;
+    case OS_SINGLE:
+        gen_helper_extf32_FP0(cpu_env);
+        break;
+    case OS_DOUBLE:
+        gen_helper_extf64_FP0(cpu_env);
+        break;
+    case OS_EXTENDED:
+        tcg_gen_shri_i32(QREG_FP0H, QREG_FP0H, 16);
+        break;
+    case OS_PACKED:
+        gen_helper_extp96_FP0(cpu_env);
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static inline void gen_reduce_FP0(int opsize)
+{
+    switch (opsize) {
+    case OS_BYTE:
+    case OS_WORD:
+    case OS_LONG:
+        gen_helper_reds32_FP0(cpu_env);
+        break;
+    case OS_SINGLE:
+        gen_helper_redf32_FP0(cpu_env);
+        break;
+    case OS_DOUBLE:
+        gen_helper_redf64_FP0(cpu_env);
+        break;
+    case OS_EXTENDED:
+        tcg_gen_shli_i32(QREG_FP0H, QREG_FP0H, 16);
+        break;
+    case OS_PACKED:
+        gen_helper_redp96_FP0(cpu_env);
+        break;
+    default:
+        g_assert_not_reached();
+    }
+}
+
+static inline void gen_load_FP0(DisasContext *s, int opsize, TCGv addr)
+{
+    TCGv tmp;
+    int index = IS_USER(s);
+    s->is_mem = 1;
+    switch (opsize) {
+    case OS_BYTE:
+        tcg_gen_qemu_ld8s(QREG_FP0H, addr, index);
+        gen_helper_exts32_FP0(cpu_env);
+        break;
+    case OS_WORD:
+        tcg_gen_qemu_ld16s(QREG_FP0H, addr, index);
+        gen_helper_exts32_FP0(cpu_env);
+        break;
+    case OS_LONG:
+        tcg_gen_qemu_ld32u(QREG_FP0H, addr, index);
+        gen_helper_exts32_FP0(cpu_env);
+        break;
+    case OS_SINGLE:
+        tcg_gen_qemu_ld32u(QREG_FP0H, addr, index);
+        gen_helper_extf32_FP0(cpu_env);
+        break;
+    case OS_DOUBLE:
+        tcg_gen_qemu_ld64(QREG_FP0L, addr, index);
+        gen_helper_extf64_FP0(cpu_env);
+        break;
+    case OS_EXTENDED:
+        tcg_gen_qemu_ld32u(QREG_FP0H, addr, index);
+        tcg_gen_shri_i32(QREG_FP0H, QREG_FP0H, 16);
+        tmp = tcg_temp_new();
+        tcg_gen_addi_i32(tmp, addr, 4);
+        tcg_gen_qemu_ld64(QREG_FP0L, tmp, index);
+        tcg_temp_free(tmp);
+        break;
+    case OS_PACKED:
+        tcg_gen_qemu_ld32u(QREG_FP0H, addr, index);
+        tmp = tcg_temp_new();
+        tcg_gen_addi_i32(tmp, addr, 4);
+        tcg_gen_qemu_ld64(QREG_FP0L, tmp, index);
+        tcg_temp_free(tmp);
+        gen_helper_extp96_FP0(cpu_env);
+        break;
+    default:
+        g_assert_not_reached();
+    }
+    gen_throws_exception = gen_last_qop;
+}
+
+static inline void gen_store_FP0(DisasContext *s, int opsize, TCGv addr)
+{
+    TCGv tmp;
+    int index = IS_USER(s);
+    s->is_mem = 1;
+    switch (opsize) {
+    case OS_BYTE:
+        gen_helper_reds32_FP0(cpu_env);
+        tcg_gen_qemu_st8(QREG_FP0H, addr, index);
+        break;
+    case OS_WORD:
+        gen_helper_reds32_FP0(cpu_env);
+        tcg_gen_qemu_st16(QREG_FP0H, addr, index);
+        break;
+    case OS_LONG:
+        gen_helper_reds32_FP0(cpu_env);
+        tcg_gen_qemu_st32(QREG_FP0H, addr, index);
+        break;
+    case OS_SINGLE:
+        gen_helper_redf32_FP0(cpu_env);
+        tcg_gen_qemu_st32(QREG_FP0H, addr, index);
+        break;
+    case OS_DOUBLE:
+        gen_helper_redf64_FP0(cpu_env);
+        tcg_gen_qemu_st64(QREG_FP0L, addr, index);
+        break;
+    case OS_EXTENDED:
+        tcg_gen_shli_i32(QREG_FP0H, QREG_FP0H, 16);
+        tcg_gen_qemu_st32(QREG_FP0H, addr, index);
+        tmp = tcg_temp_new();
+        tcg_gen_addi_i32(tmp, addr, 4);
+        tcg_gen_qemu_st64(QREG_FP0L, tmp, index);
+        tcg_temp_free(tmp);
+        break;
+    case OS_PACKED:
+        gen_helper_redp96_FP0(cpu_env);
+        tcg_gen_qemu_st32(QREG_FP0H, addr, index);
+        tmp = tcg_temp_new();
+        tcg_gen_addi_i32(tmp, addr, 4);
+        tcg_gen_qemu_st64(QREG_FP0L, tmp, index);
+        tcg_temp_free(tmp);
+        break;
+    default:
+        g_assert_not_reached();
+    }
+    gen_throws_exception = gen_last_qop;
+}
+
+static void gen_op_load_ea_FP0(CPUM68KState *env, DisasContext *s,
+                               uint16_t insn, int opsize)
+{
+    TCGv reg;
+    TCGv addr;
+    uint64_t val;
+
+    switch ((insn >> 3) & 7) {
+    case 0: /* Data register direct.  */
+        tcg_gen_mov_i32(QREG_FP0H, DREG(insn, 0));
+        gen_extend_FP0(opsize);
+        break;
+    case 1:  /* Address register direct.  */
+        gen_addr_fault(s);
+        break;
+    case 2: /* Indirect register */
+        gen_load_FP0(s, opsize, AREG(insn, 0));
+        break;
+    case 3: /* Indirect postincrement.  */
+        reg = AREG(insn, 0);
+        gen_load_FP0(s, opsize, reg);
+        tcg_gen_addi_i32(reg, reg, opsize_bytes(opsize));
+        break;
+    case 4: /* Indirect predecrememnt.  */
+        addr = gen_lea(env, s, insn, opsize);
+        if (IS_NULL_QREG(addr)) {
+            gen_addr_fault(s);
+            return;
+        }
+        gen_load_FP0(s, opsize, addr);
+        tcg_gen_mov_i32(AREG(insn, 0), addr);
+        break;
+    case 5: /* Indirect displacement.  */
+    case 6: /* Indirect index + displacement.  */
+        addr = gen_lea(env, s, insn, opsize);
+        if (IS_NULL_QREG(addr)) {
+            gen_addr_fault(s);
+            return;
+        }
+        gen_load_FP0(s, opsize, addr);
+        break;
+    case 7: /* Other */
+        switch (insn & 7) {
+        case 0: /* Absolute short.  */
+        case 1: /* Absolute long.  */
+        case 2: /* pc displacement  */
+        case 3: /* pc index+displacement.  */
+            addr = gen_lea(env, s, insn, opsize);
+            if (IS_NULL_QREG(addr)) {
+                gen_addr_fault(s);
+                return;
+            }
+            gen_load_FP0(s, opsize, addr);
+            break;
+        case 4: /* Immediate.  */
+            switch (opsize) {
+            case OS_BYTE:
+                val = read_im8(env, s);
+                tcg_gen_movi_i32(QREG_FP0H, val);
+                break;
+            case OS_WORD:
+                val = read_im16(env, s);
+                tcg_gen_movi_i32(QREG_FP0H, val);
+                break;
+            case OS_LONG:
+                val = read_im32(env, s);
+                tcg_gen_movi_i32(QREG_FP0H, val);
+                break;
+            case OS_SINGLE:
+                val = read_im32(env, s);
+                tcg_gen_movi_i32(QREG_FP0H, val);
+                break;
+            case OS_DOUBLE:
+                val = read_im64(env, s);
+                tcg_gen_movi_i64(QREG_FP0L, val);
+                break;
+            case OS_EXTENDED:
+                val = read_im32(env, s);
+                tcg_gen_movi_i32(QREG_FP0H, val);
+                val = read_im64(env, s);
+                tcg_gen_movi_i64(QREG_FP0L, val);
+                break;
+            case OS_PACKED:
+                val = read_im32(env, s);
+                tcg_gen_movi_i32(QREG_FP0H, val);
+                val = read_im64(env, s);
+                tcg_gen_movi_i64(QREG_FP0L, val);
+                break;
+            default:
+                g_assert_not_reached();
+            }
+            gen_extend_FP0(opsize);
+            break;
+        default:
+            g_assert_not_reached();
+        }
+    }
+}
+
+static void gen_op_store_ea_FP0(CPUM68KState *env, DisasContext *s,
+                                uint16_t insn, int opsize)
+{
+    TCGv reg;
+    TCGv addr;
+
+    switch ((insn >> 3) & 7) {
+    case 0: /* Data register direct.  */
+        gen_reduce_FP0(opsize);
+        tcg_gen_mov_i32(DREG(insn, 0), QREG_FP0H);
+        break;
+    case 1:  /* Address register direct.  */
+        gen_addr_fault(s);
+        break;
+    case 2: /* Indirect register */
+        reg = AREG(insn, 0);
+        gen_store_FP0(s, opsize, reg);
+        break;
+    case 3: /* Indirect postincrement.  */
+        reg = AREG(insn, 0);
+        gen_store_FP0(s, opsize, reg);
+        tcg_gen_addi_i32(reg, reg, opsize_bytes(opsize));
+        break;
+    case 4: /* Indirect predecrememnt.  */
+        addr = gen_lea(env, s, insn, opsize);
+        if (IS_NULL_QREG(addr)) {
+            gen_addr_fault(s);
+            return;
+        }
+        gen_store_FP0(s, opsize, addr);
+        tcg_gen_mov_i32(AREG(insn, 0), addr);
+        break;
+    case 5: /* Indirect displacement.  */
+    case 6: /* Indirect index + displacement.  */
+        addr = gen_lea(env, s, insn, opsize);
+        if (IS_NULL_QREG(addr)) {
+            gen_addr_fault(s);
+            return;
+        }
+        gen_store_FP0(s, opsize, addr);
+        break;
+    case 7: /* Other */
+        switch (insn & 7) {
+        case 0: /* Absolute short.  */
+        case 1: /* Absolute long.  */
+            addr = gen_lea(env, s, insn, opsize);
+            if (IS_NULL_QREG(addr)) {
+                gen_addr_fault(s);
+                return;
+            }
+            gen_store_FP0(s, opsize, addr);
+            break;
+        case 2: /* pc displacement  */
+        case 3: /* pc index+displacement.  */
+        case 4: /* Immediate.  */
+            gen_addr_fault(s);
+            break;
+        default:
+            g_assert_not_reached();
+        }
+    }
+}
+
 /* This generates a conditional branch, clobbering all temporaries.  */
 static void gen_jmpcc(DisasContext *s, int cond, TCGLabel *l1)
 {
@@ -3386,7 +3758,7 @@ DISAS_INSN(trap)
 
 /* ??? FP exceptions are not implemented.  Most exceptions are deferred until
    immediately before the next FP instruction is executed.  */
-DISAS_INSN(fpu)
+DISAS_INSN(cf_fpu)
 {
     uint16_t ext;
     int32_t offset;
@@ -3672,7 +4044,7 @@ undef:
     disas_undef_fpu(env, s, insn);
 }
 
-DISAS_INSN(fbcc)
+DISAS_INSN(cf_fbcc)
 {
     uint32_t offset;
     uint32_t addr;
@@ -3748,7 +4120,494 @@ DISAS_INSN(fbcc)
     gen_jmp_tb(s, 1, addr + offset);
 }
 
-DISAS_INSN(frestore)
+static void gen_op_fmovem(CPUM68KState *env, DisasContext *s,
+                          uint32_t insn, uint32_t ext)
+{
+    int opsize;
+    uint16_t mask;
+    int i;
+    uint32_t mode;
+    int32_t incr;
+    TCGv addr, tmp;
+    int is_load;
+
+    if (m68k_feature(s->env, M68K_FEATURE_FPU)) {
+        opsize = OS_EXTENDED;
+    } else {
+        opsize = OS_DOUBLE;  /* FIXME */
+    }
+
+    mode = (ext >> 11) & 0x3;
+    if ((mode & 0x1) == 1) {
+        gen_helper_fmovem(cpu_env, tcg_const_i32(opsize),
+                          tcg_const_i32(mode), DREG(ext, 0));
+        return;
+    }
+
+    tmp = gen_lea(env, s, insn, opsize);
+    if (IS_NULL_QREG(tmp)) {
+        gen_addr_fault(s);
+        return;
+    }
+
+    addr = tcg_temp_new();
+    tcg_gen_mov_i32(addr, tmp);
+    is_load = ((ext & 0x2000) == 0);
+    incr = opsize_bytes(opsize);
+    mask = ext & 0x00FF;
+
+    if (!is_load && (mode & 2) == 0) {
+        for (i = 7; i >= 0; i--, mask <<= 1) {
+            if (mask & 0x80) {
+                gen_op_load_fpr_FP0(i);
+                gen_store_FP0(s, opsize, addr);
+                if ((mask & 0xff) != 0x80) {
+                    tcg_gen_subi_i32(addr, addr, incr);
+                }
+            }
+        }
+        tcg_gen_mov_i32(AREG(insn, 0), addr);
+    } else {
+        for (i = 0; i < 8; i++, mask <<= 1) {
+            if (mask & 0x80) {
+                if (is_load) {
+                    gen_load_FP0(s, opsize, addr);
+                    gen_op_store_fpr_FP0(i);
+                } else {
+                    gen_op_load_fpr_FP0(i);
+                    gen_store_FP0(s, opsize, addr);
+                }
+                tcg_gen_addi_i32(addr, addr, incr);
+            }
+        }
+        if ((insn & 070) == 030) {
+            tcg_gen_mov_i32(AREG(insn, 0), addr);
+        }
+    }
+    tcg_temp_free_i32(addr);
+}
+
+DISAS_INSN(m68k_fpu)
+{
+    int ctrl;
+    uint16_t ext;
+    uint8_t rom_offset;
+    int opmode;
+    int round;
+    int set_dest;
+    int opsize;
+    TCGv val;
+
+    ext = read_im16(env, s);
+    opmode = ext & 0x7f;
+    switch ((ext >> 13) & 7) {
+    case 0:
+        break;
+    case 1:
+        goto undef;
+    case 2:
+        if (insn == 0xf200 && (ext & 0xfc00) == 0x5c00) {
+            /* fmovecr */
+            rom_offset = ext & 0x7f;
+            gen_helper_const_FP0(cpu_env, tcg_const_i32(rom_offset));
+            gen_op_store_fpr_FP0(REG(ext, 7));
+            return;
+        }
+        break;
+    case 3: /* fmove out */
+        opsize = ext_opsize(ext, 10);
+        gen_op_load_fpr_FP0(REG(ext, 7));
+        gen_helper_compare_FP0(cpu_env);
+        gen_op_store_ea_FP0(env, s, insn, opsize);
+        return;
+    case 4: /* fmove to control register.  */
+        ctrl = (ext >> 10) & 7;
+        if (ctrl & 4) { /* FPCR */
+            SRC_EA(env, val, OS_LONG, 0, NULL);
+            gen_helper_set_fpcr(cpu_env, val);
+        }
+        if (ctrl & 2) { /* FPSR */
+            SRC_EA(env, QEMU_FPSR, OS_LONG, 0, NULL);
+        }
+        if (ctrl & 1) { /* FPIAR */
+            SRC_EA(env, val, OS_LONG, 0, NULL);
+        }
+        return;
+    case 5: /* fmove from control register.  */
+        ctrl = (ext >> 10) & 7;
+        if (ctrl & 4) { /* FPCR */
+            DEST_EA(env, insn, OS_LONG, QEMU_FPCR, NULL);
+        }
+        if (ctrl & 2) { /* FPSR */
+            gen_helper_update_fpsr(cpu_env);
+            DEST_EA(env, insn, OS_LONG, QEMU_FPSR, NULL);
+        }
+        if (ctrl & 1) { /* FPIAR */
+            TCGv tmp = tcg_temp_new_i32();
+            DEST_EA(env, insn, OS_LONG, tmp, NULL);
+            tcg_temp_free_i32(tmp);
+        }
+        return;
+    case 6: /* fmovem */
+    case 7:
+        if ((ext & 0xf00) != 0 || (ext & 0xff) == 0) {
+            goto undef;
+        }
+        if ((ext & 0x1000) == 0 && !m68k_feature(s->env, M68K_FEATURE_FPU)) {
+            goto undef;
+        }
+        gen_op_fmovem(env, s, insn, ext);
+        return;
+    }
+    if (ext & (1 << 14)) {
+        opsize = ext_opsize(ext, 10);
+        gen_op_load_ea_FP0(env, s, insn, opsize);
+    } else {
+        /* Source register.  */
+        opsize = OS_EXTENDED;
+        gen_op_load_fpr_FP0(REG(ext, 10));
+    }
+    round = 1;
+    set_dest = 1;
+    switch (opmode) {
+    case 0: case 0x40: case 0x44: /* fmove */
+        break;
+    case 1: /* fint */
+        gen_helper_iround_FP0(cpu_env);
+        round = 0;
+        break;
+    case 2: /* fsinh */
+        gen_helper_sinh_FP0(cpu_env);
+        break;
+    case 3: /* fintrz */
+        gen_helper_itrunc_FP0(cpu_env);
+        round = 0;
+        break;
+    case 4: case 0x41: case 0x45: /* fsqrt */
+        gen_helper_sqrt_FP0(cpu_env);
+        break;
+    case 6:                          /* flognp1 */
+        gen_helper_lognp1_FP0(cpu_env);
+        break;
+    case 0x09:                       /* ftanh */
+        gen_helper_tanh_FP0(cpu_env);
+        break;
+    case 0x0a:                       /* fatan */
+        gen_helper_atan_FP0(cpu_env);
+        break;
+    case 0x0c:                       /* fasin */
+        gen_helper_asin_FP0(cpu_env);
+        break;
+    case 0x0d:                       /* fatanh */
+        gen_helper_atanh_FP0(cpu_env);
+        break;
+    case 0x0e:                       /* fsin */
+        gen_helper_sin_FP0(cpu_env);
+        break;
+    case 0x0f:                       /* ftan */
+        gen_helper_tan_FP0(cpu_env);
+        break;
+    case 0x10:                       /* fetox */
+        gen_helper_exp_FP0(cpu_env);
+        break;
+    case 0x11:                       /* ftwotox */
+        gen_helper_exp2_FP0(cpu_env);
+        break;
+    case 0x12:                       /* ftentox */
+        gen_helper_exp10_FP0(cpu_env);
+        break;
+    case 0x14:                       /* flogn */
+        gen_helper_ln_FP0(cpu_env);
+        break;
+    case 0x15:                       /* flog10 */
+        gen_helper_log10_FP0(cpu_env);
+        break;
+    case 0x18: case 0x58: case 0x5c: /* fabs */
+        gen_helper_abs_FP0(cpu_env);
+        break;
+    case 0x19:
+        gen_helper_cosh_FP0(cpu_env);
+        break;
+    case 0x1a: case 0x5a: case 0x5e: /* fneg */
+        gen_helper_chs_FP0(cpu_env);
+        break;
+    case 0x1c:                       /* facos */
+        gen_helper_acos_FP0(cpu_env);
+        break;
+    case 0x1d:                       /* fcos */
+        gen_helper_cos_FP0(cpu_env);
+        break;
+    case 0x1e:                       /* fgetexp */
+        gen_helper_getexp_FP0(cpu_env);
+        break;
+    case 0x20: case 0x60: case 0x64: /* fdiv */
+        gen_op_load_fpr_FP1(REG(ext, 7));
+        gen_helper_div_FP0_FP1(cpu_env);
+        break;
+    case 0x21:                       /* fmod */
+        gen_op_load_fpr_FP1(REG(ext, 7));
+        gen_helper_mod_FP0_FP1(cpu_env);
+        break;
+    case 0x22: case 0x62: case 0x66: /* fadd */
+        gen_op_load_fpr_FP1(REG(ext, 7));
+        gen_helper_add_FP0_FP1(cpu_env);
+        break;
+    case 0x23: case 0x63: case 0x67: /* fmul */
+        gen_op_load_fpr_FP1(REG(ext, 7));
+        gen_helper_mul_FP0_FP1(cpu_env);
+        break;
+    case 0x24:                      /* fsgldiv */
+        gen_op_load_fpr_FP1(REG(ext, 7));
+        gen_helper_div_FP0_FP1(cpu_env);
+        break;
+    case 0x26:                       /* fscale */
+        gen_op_load_fpr_FP1(REG(ext, 7));
+        gen_helper_scale_FP0_FP1(cpu_env);
+        break;
+    case 0x27:                      /* fsglmul */
+        gen_op_load_fpr_FP1(REG(ext, 7));
+        gen_helper_mul_FP0_FP1(cpu_env);
+        break;
+    case 0x28: case 0x68: case 0x6c: /* fsub */
+        gen_op_load_fpr_FP1(REG(ext, 7));
+        gen_helper_sub_FP0_FP1(cpu_env);
+        break;
+    case 0x30: case 0x31: case 0x32:
+    case 0x33: case 0x34: case 0x35:
+    case 0x36: case 0x37:
+        gen_helper_sincos_FP0_FP1(cpu_env);
+        gen_op_store_fpr_FP0(REG(ext, 7)); /* sin */
+        gen_op_store_fpr_FP1(REG(ext, 0)); /* cos */
+        break;
+    case 0x38: /* fcmp */
+        gen_op_load_fpr_FP1(REG(ext, 7));
+        gen_helper_fcmp_FP0_FP1(cpu_env);
+        return;
+    case 0x3a: /* ftst */
+        set_dest = 0;
+        round = 0;
+        break;
+    default:
+        goto undef;
+    }
+    gen_helper_compare_FP0(cpu_env);
+    if (round) {
+        if (opmode & 0x40) {
+            if ((opmode & 0x4) != 0) {
+                round = 0;
+            }
+        } else if ((s->fpcr & M68K_FPCR_PREC) == 0) {
+            round = 0;
+        }
+    }
+    if (round) {
+#if 0
+        TCGv tmp = tcg_temp_new_i32();
+        gen_helper_f64_to_f32(tmp, cpu_env, res);
+        gen_helper_f32_to_f64(res, cpu_env, tmp);
+        tcg_temp_free_i32(tmp);
+#endif
+    }
+    if (set_dest) {
+        gen_op_store_fpr_FP0(REG(ext, 7));
+    }
+    return;
+undef:
+    /* FIXME: Is this right for offset addressing modes?  */
+    s->pc -= 2;
+    disas_undef_fpu(env, s, insn);
+}
+
+static void gen_fjmpcc(DisasContext *s, int cond, TCGLabel *l1)
+{
+    TCGv tmp;
+
+    /* TODO: Raise BSUN exception.  */
+
+    /* Jump to l1 if condition is true.  */
+    switch (cond) {
+    case 0:  /* False */
+    case 16: /* Signaling false */
+        break;
+    case 1:  /* Equal Z */
+    case 17: /* Signaling Equal Z */
+        tmp = tcg_temp_new();
+        tcg_gen_andi_i32(tmp, QREG_FPSR, FCCF_Z);
+        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+        break;
+    case 2: /* Ordered Greater Than !(A || Z || N) */
+    case 18:
+        tmp = tcg_temp_new();
+        tcg_gen_andi_i32(tmp, QREG_FPSR,
+                         FCCF_A | FCCF_Z | FCCF_N);
+        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
+        break;
+    case 3: /* Ordered Greater Than or Equal Z || !(A || N) */
+    case 19:
+        assert(FCCF_A == (FCCF_N >> 3));
+        tmp = tcg_temp_new();
+        tcg_gen_shli_i32(tmp, QREG_FPSR, 3);
+        tcg_gen_or_i32(tmp, tmp, QREG_FPSR);
+        tcg_gen_xori_i32(tmp, tmp, FCCF_N);
+        tcg_gen_andi_i32(tmp, tmp, FCCF_N | FCCF_Z);
+        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+        break;
+    case 4: /* Ordered Less Than !(!N || A || Z); */
+    case 20:
+        tmp = tcg_temp_new();
+        tcg_gen_xori_i32(tmp, QREG_FPSR, FCCF_N);
+        tcg_gen_andi_i32(tmp, tmp, FCCF_N | FCCF_A | FCCF_Z);
+        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
+        break;
+    case 5: /* Ordered Less Than or Equal Z || (N && !A) */
+    case 21:
+        assert(FCCF_A == (FCCF_N >> 3));
+        tmp = tcg_temp_new();
+        tcg_gen_xori_i32(tmp, QREG_FPSR, FCCF_A);
+        tcg_gen_shli_i32(tmp, tmp, 3);
+        tcg_gen_ori_i32(tmp, tmp, FCCF_Z);
+        tcg_gen_and_i32(tmp, tmp, QREG_FPSR);
+        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+        break;
+    case 6: /* Ordered Greater or Less Than !(A || Z) */
+    case 22:
+        tmp = tcg_temp_new();
+        tcg_gen_andi_i32(tmp, QREG_FPSR, FCCF_A | FCCF_Z);
+        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
+        break;
+    case 7: /* Ordered !A */
+    case 23:
+        tmp = tcg_temp_new();
+        tcg_gen_andi_i32(tmp, QREG_FPSR, FCCF_A);
+        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
+        break;
+    case 8: /* Unordered A */
+    case 24:
+        tmp = tcg_temp_new();
+        tcg_gen_andi_i32(tmp, QREG_FPSR, FCCF_A);
+        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+        break;
+    case 9: /* Unordered or Equal A || Z */
+    case 25:
+        tmp = tcg_temp_new();
+        tcg_gen_andi_i32(tmp, QREG_FPSR, FCCF_A | FCCF_Z);
+        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+        break;
+    case 10: /* Unordered or Greater Than A || !(N || Z)) */
+    case 26:
+        assert(FCCF_Z == (FCCF_N >> 1));
+        tmp = tcg_temp_new();
+        tcg_gen_shli_i32(tmp, QREG_FPSR, 1);
+        tcg_gen_or_i32(tmp, tmp, QREG_FPSR);
+        tcg_gen_xori_i32(tmp, tmp, FCCF_N);
+        tcg_gen_andi_i32(tmp, tmp, FCCF_N | FCCF_A);
+        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+        break;
+    case 11: /* Unordered or Greater or Equal A || Z || N */
+    case 13: /* Unordered or Less or Equal A || Z || N */
+    case 29:
+        tmp = tcg_temp_new();
+        tcg_gen_andi_i32(tmp, QREG_FPSR, FCCF_A | FCCF_Z | FCCF_N);
+        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+        break;
+    case 27: /* Not Less Than A || Z || !N */
+        tmp = tcg_temp_new();
+        tcg_gen_andi_i32(tmp, QREG_FPSR, FCCF_A | FCCF_Z | FCCF_N);
+        tcg_gen_xori_i32(tmp, tmp, FCCF_N);
+        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+        break;
+    case 12: /* Unordered or Less Than A || (N && !Z) */
+    case 28:
+        assert(FCCF_Z == (FCCF_N >> 1));
+        tmp = tcg_temp_new();
+        tcg_gen_xori_i32(tmp, QREG_FPSR, FCCF_Z);
+        tcg_gen_shli_i32(tmp, tmp, 1);
+        tcg_gen_ori_i32(tmp, tmp, FCCF_A);
+        tcg_gen_and_i32(tmp, tmp, QREG_FPSR);
+        tcg_gen_andi_i32(tmp, tmp, FCCF_A | FCCF_N);
+        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1);
+        break;
+    case 14: /* Not Equal !Z */
+    case 30: /* Signaling Not Equal !Z */
+        tmp = tcg_temp_new();
+        tcg_gen_andi_i32(tmp, QREG_FPSR, FCCF_Z);
+        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1);
+        break;
+    case 15: /* True */
+    case 31: /* Signaling True */
+        tcg_gen_br(l1);
+        break;
+    }
+}
+
+DISAS_INSN(m68k_fbcc)
+{
+    uint32_t offset;
+    uint32_t addr;
+    TCGLabel *l1;
+
+    addr = s->pc;
+    offset = cpu_ldsw_code(env, s->pc);
+    s->pc += 2;
+    if (insn & (1 << 6)) {
+        offset = (offset << 16) | cpu_lduw_code(env, s->pc);
+        s->pc += 2;
+    }
+
+    l1 = gen_new_label();
+    gen_fjmpcc(s, insn & 0x3f, l1);
+    update_cc_op(s);
+    gen_jmp_tb(s, 0, s->pc);
+    gen_set_label(l1);
+    update_cc_op(s);
+    gen_jmp_tb(s, 1, addr + offset);
+}
+
+DISAS_INSN(fscc_mem)
+{
+    TCGLabel *l1, *l2;
+    TCGv taddr;
+    TCGv addr;
+    uint16_t ext;
+
+    ext = read_im16(env, s);
+
+    taddr = gen_lea(env, s, insn, OS_BYTE);
+    if (IS_NULL_QREG(taddr)) {
+        gen_addr_fault(s);
+        return;
+    }
+    addr = tcg_temp_local_new();
+    tcg_gen_mov_i32(addr, taddr);
+    l1 = gen_new_label();
+    l2 = gen_new_label();
+    gen_fjmpcc(s, ext & 0x3f, l1);
+    gen_store(s, OS_BYTE, addr, tcg_const_i32(0x00));
+    tcg_gen_br(l2);
+    gen_set_label(l1);
+    gen_store(s, OS_BYTE, addr, tcg_const_i32(0xff));
+    gen_set_label(l2);
+    tcg_temp_free(addr);
+}
+
+DISAS_INSN(fscc_reg)
+{
+    TCGLabel *l1;
+    TCGv reg;
+    uint16_t ext;
+
+    ext = read_im16(env, s);
+
+    reg = DREG(insn, 0);
+
+    l1 = gen_new_label();
+    tcg_gen_ori_i32(reg, reg, 0x000000ff);
+    gen_fjmpcc(s, ext & 0x3f, l1);
+    tcg_gen_andi_i32(reg, reg, 0xffffff00);
+    gen_set_label(l1);
+}
+
+DISAS_INSN(cf_frestore)
 {
     M68kCPU *cpu = m68k_env_get_cpu(env);
 
@@ -3756,7 +4615,7 @@ DISAS_INSN(frestore)
     cpu_abort(CPU(cpu), "FRESTORE not implemented");
 }
 
-DISAS_INSN(fsave)
+DISAS_INSN(cf_fsave)
 {
     M68kCPU *cpu = m68k_env_get_cpu(env);
 
@@ -3764,6 +4623,21 @@ DISAS_INSN(fsave)
     cpu_abort(CPU(cpu), "FSAVE not implemented");
 }
 
+DISAS_INSN(m68k_frestore)
+{
+    TCGv addr;
+
+    SRC_EA(env, addr, OS_LONG, 0, NULL);
+    /* FIXME: check the state frame */
+}
+
+DISAS_INSN(m68k_fsave)
+{
+    /* always write IDLE */
+    /* FIXME: 68040 only */
+    DEST_EA(env, insn, OS_LONG, tcg_const_i32(0x41000000), NULL);
+}
+
 static inline TCGv gen_mac_extract_word(DisasContext *s, TCGv val, int upper)
 {
     TCGv tmp = tcg_temp_new();
@@ -4331,10 +5205,16 @@ void register_m68k_insns (CPUM68KState *env)
     INSN(bitfield_reg, e8c0, f8f8, BITFIELD);
     INSN(undef_fpu, f000, f000, CF_ISA_A);
     INSN(undef_fpu, f000, f000, M68000);
-    INSN(fpu,       f200, ffc0, CF_FPU);
-    INSN(fbcc,      f280, ffc0, CF_FPU);
-    INSN(frestore,  f340, ffc0, CF_FPU);
-    INSN(fsave,     f340, ffc0, CF_FPU);
+    INSN(cf_fpu,    f200, ffc0, CF_FPU);
+    INSN(cf_fbcc,   f280, ffc0, CF_FPU);
+    INSN(cf_frestore, f340, ffc0, CF_FPU);
+    INSN(cf_fsave,  f340, ffc0, CF_FPU);
+    INSN(m68k_fpu,  f200, ffc0, FPU);
+    INSN(fscc_mem,  f240, ffc0, FPU);
+    INSN(fscc_reg,  f240, fff8, FPU);
+    INSN(m68k_fbcc, f280, ffc0, FPU);
+    INSN(m68k_frestore, f140, f1c0, FPU);
+    INSN(m68k_fsave, f100, f1c0, FPU);
     INSN(intouch,   f340, ffc0, CF_ISA_A);
     INSN(cpushl,    f428, ff38, CF_ISA_A);
     INSN(wddata,    fb00, ff00, CF_ISA_A);
@@ -4496,20 +5376,30 @@ void m68k_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
     CPUM68KState *env = &cpu->env;
     int i;
     uint16_t sr;
-    CPU_DoubleU u;
     for (i = 0; i < 8; i++)
       {
-        u.d = env->fregs[i];
-        cpu_fprintf (f, "D%d = %08x   A%d = %08x   F%d = %08x%08x (%12g)\n",
-                     i, env->dregs[i], i, env->aregs[i],
-                     i, u.l.upper, u.l.lower, *(double *)&u.d);
+        cpu_fprintf(f, "D%d = %08x   A%d = %08x   ",
+                    i, env->dregs[i], i, env->aregs[i]);
+        if (m68k_feature(env, M68K_FEATURE_CF_FPU)) {
+            CPU_DoubleU u;
+            u.d = env->cf_fregs[i];
+            cpu_fprintf(f, "F%d = %08x%08x (%12g)\n",
+                        i, u.l.upper, u.l.lower, *(double *)&u.d);
+        }
+        if (m68k_feature(env, M68K_FEATURE_FPU)) {
+            cpu_fprintf(f, "F%d = %" PRIxFPH " %" PRIxFPL "\n",
+                        i, env->m68k_fregs[i].d.high,
+                           env->m68k_fregs[i].d.low);
+        }
       }
-    cpu_fprintf (f, "PC = %08x   ", env->pc);
+    cpu_fprintf(f, "PC = %08x   ", env->pc);
     sr = env->sr;
-    cpu_fprintf (f, "SR = %04x %c%c%c%c%c ", sr, (sr & 0x10) ? 'X' : '-',
-                 (sr & CCF_N) ? 'N' : '-', (sr & CCF_Z) ? 'Z' : '-',
-                 (sr & CCF_V) ? 'V' : '-', (sr & CCF_C) ? 'C' : '-');
-    cpu_fprintf (f, "FPRESULT = %12g\n", *(double *)&env->fp_result);
+    cpu_fprintf(f, "SR = %04x %c%c%c%c%c ", sr, (sr & 0x10) ? 'X' : '-',
+                (sr & CCF_N) ? 'N' : '-', (sr & CCF_Z) ? 'Z' : '-',
+                (sr & CCF_V) ? 'V' : '-', (sr & CCF_C) ? 'C' : '-');
+    if (m68k_feature(env, M68K_FEATURE_CF_FPU)) {
+        cpu_fprintf(f, "FPRESULT = %12g\n", *(double *)&env->cf_fp_result);
+    }
 }
 
 void restore_state_to_opc(CPUM68KState *env, TranslationBlock *tb, int pc_pos)
-- 
2.4.3

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [Qemu-devel] [PATCH 2/2] m68k: Implement 680x0 processors family 96 bit FPU
  2015-06-21 22:35 ` [Qemu-devel] [PATCH 2/2] m68k: Implement 680x0 processors family 96 bit FPU Laurent Vivier
@ 2015-06-22  8:00   ` Andreas Schwab
  0 siblings, 0 replies; 8+ messages in thread
From: Andreas Schwab @ 2015-06-22  8:00 UTC (permalink / raw)
  To: Laurent Vivier; +Cc: qemu-devel, gerg

Laurent Vivier <laurent@vivier.eu> writes:

> +#if defined(TARGET_M68K)
> +#define floatx80_default_nan_high 0x7FFF
> +#define floatx80_default_nan_low  LIT64(0x4000000000000000)

The default NaN generated by the m68k FPU is all-bits-one (this is true
for all formats).

Andreas.

-- 
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5
"And now for something completely different."

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Qemu-devel] [PATCH 0/2] m68k: 680x0 processors family support
  2015-06-21 22:35 [Qemu-devel] [PATCH 0/2] m68k: 680x0 processors family support Laurent Vivier
  2015-06-21 22:35 ` [Qemu-devel] [PATCH 1/2] m68k: Add compatibility with 680x0 processors family Laurent Vivier
  2015-06-21 22:35 ` [Qemu-devel] [PATCH 2/2] m68k: Implement 680x0 processors family 96 bit FPU Laurent Vivier
@ 2015-06-22  8:33 ` Peter Maydell
  2015-06-22  8:55   ` Laurent Vivier
  2015-06-22 12:06 ` Greg Ungerer
  3 siblings, 1 reply; 8+ messages in thread
From: Peter Maydell @ 2015-06-22  8:33 UTC (permalink / raw)
  To: Laurent Vivier; +Cc: Andreas Schwab, QEMU Developers, Greg Ungerer

On 21 June 2015 at 23:35, Laurent Vivier <laurent@vivier.eu> wrote:
> I carry this series for several years now.
>
> I was reluctant to merge it with the mainstream because
> I was sure it was breaking Coldfire support.
>
> But with the kernel provided by Greg Ungerer I was able
> to check and correct the support of ColdFire family
> in my tree.
>
> This series allows:
> - to start a ColdFire semi-hosted kernel (m68k-softmmu)
> - chroot or start a container of debian etch-m68k (m68k-linux-user)
>
> I have another series providing privileged instructions and
> allowing to boot a 680x0 debian kernel (Macintosh Quadra 800),
> but this one needs more work (and love). Based on this work,
> we will also be able to boot a NextStation (Bryce Lanham, GSoC)
>
> This work is based on the work of Andreas Schwab.
>
> Laurent Vivier (2):
>   m68k: Add compatibility with 680x0 processors family
>   m68k: Implement 680x0 processors family 96 bit FPU
>
>  configure                  |    2 +-
>  cpu-exec.c                 |    6 -
>  disas.c                    |    4 +
>  fpu/softfloat-specialize.h |   48 +-
>  fpu/softfloat.c            |   38 +-
>  gdb-xml/m68k-fp.xml        |   21 +
>  include/fpu/softfloat.h    |   11 +-
>  target-m68k/cpu.c          |   67 +-
>  target-m68k/cpu.h          |   76 +-
>  target-m68k/helper.c       | 1691 ++++++++++++++++++--
>  target-m68k/helper.h       |  100 +-
>  target-m68k/m68k-qreg.h    |   11 -
>  target-m68k/op_helper.c    |  182 ++-
>  target-m68k/qregs.def      |    6 +-
>  target-m68k/translate.c    | 3682 +++++++++++++++++++++++++++++++++++---------
>  15 files changed, 5013 insertions(+), 932 deletions(-)
>  create mode 100644 gdb-xml/m68k-fp.xml
>  delete mode 100644 target-m68k/m68k-qreg.h

Thanks for sending this. My initial feeling is that this would
really benefit from being split up into more patches to make
it easier to review.

-- PMM

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Qemu-devel] [PATCH 0/2] m68k: 680x0 processors family support
  2015-06-22  8:33 ` [Qemu-devel] [PATCH 0/2] m68k: 680x0 processors family support Peter Maydell
@ 2015-06-22  8:55   ` Laurent Vivier
  2015-06-22 10:05     ` Peter Maydell
  0 siblings, 1 reply; 8+ messages in thread
From: Laurent Vivier @ 2015-06-22  8:55 UTC (permalink / raw)
  To: Peter Maydell; +Cc: Andreas Schwab, QEMU Developers, Greg Ungerer



Le 22/06/2015 10:33, Peter Maydell a écrit :
> On 21 June 2015 at 23:35, Laurent Vivier <laurent@vivier.eu> wrote:
>> I carry this series for several years now.
>>
>> I was reluctant to merge it with the mainstream because
>> I was sure it was breaking Coldfire support.
>>
>> But with the kernel provided by Greg Ungerer I was able
>> to check and correct the support of ColdFire family
>> in my tree.
>>
>> This series allows:
>> - to start a ColdFire semi-hosted kernel (m68k-softmmu)
>> - chroot or start a container of debian etch-m68k (m68k-linux-user)
>>
>> I have another series providing privileged instructions and
>> allowing to boot a 680x0 debian kernel (Macintosh Quadra 800),
>> but this one needs more work (and love). Based on this work,
>> we will also be able to boot a NextStation (Bryce Lanham, GSoC)
>>
>> This work is based on the work of Andreas Schwab.
>>
>> Laurent Vivier (2):
>>   m68k: Add compatibility with 680x0 processors family
>>   m68k: Implement 680x0 processors family 96 bit FPU
>>
>>  configure                  |    2 +-
>>  cpu-exec.c                 |    6 -
>>  disas.c                    |    4 +
>>  fpu/softfloat-specialize.h |   48 +-
>>  fpu/softfloat.c            |   38 +-
>>  gdb-xml/m68k-fp.xml        |   21 +
>>  include/fpu/softfloat.h    |   11 +-
>>  target-m68k/cpu.c          |   67 +-
>>  target-m68k/cpu.h          |   76 +-
>>  target-m68k/helper.c       | 1691 ++++++++++++++++++--
>>  target-m68k/helper.h       |  100 +-
>>  target-m68k/m68k-qreg.h    |   11 -
>>  target-m68k/op_helper.c    |  182 ++-
>>  target-m68k/qregs.def      |    6 +-
>>  target-m68k/translate.c    | 3682 +++++++++++++++++++++++++++++++++++---------
>>  15 files changed, 5013 insertions(+), 932 deletions(-)
>>  create mode 100644 gdb-xml/m68k-fp.xml
>>  delete mode 100644 target-m68k/m68k-qreg.h
> 
> Thanks for sending this. My initial feeling is that this would
> really benefit from being split up into more patches to make
> it easier to review.

Thank you Peter, I understand.

In fact, in my tree, I have 160 commits I have merged in two big ones
;-) (some are new features, other bug fixes).

So, no problem, just define the granularity, I will create the patches
accordingly.

Is it possible to integrate them little by little ?
I really want to see my patches stack to decrease.

Laurent

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Qemu-devel] [PATCH 0/2] m68k: 680x0 processors family support
  2015-06-22  8:55   ` Laurent Vivier
@ 2015-06-22 10:05     ` Peter Maydell
  0 siblings, 0 replies; 8+ messages in thread
From: Peter Maydell @ 2015-06-22 10:05 UTC (permalink / raw)
  To: Laurent Vivier; +Cc: Andreas Schwab, QEMU Developers, Greg Ungerer

On 22 June 2015 at 09:55, Laurent Vivier <laurent@vivier.eu> wrote:
> Le 22/06/2015 10:33, Peter Maydell a écrit :
>> Thanks for sending this. My initial feeling is that this would
>> really benefit from being split up into more patches to make
>> it easier to review.
>
> Thank you Peter, I understand.
>
> In fact, in my tree, I have 160 commits I have merged in two big ones
> ;-) (some are new features, other bug fixes).
>
> So, no problem, just define the granularity, I will create the patches
> accordingly.
>
> Is it possible to integrate them little by little ?
> I really want to see my patches stack to decrease.

(this is summarising/repeating my remarks on IRC)

Yes, and in fact little by little is my recommendation.
My rule of thumb is that individual patches should be ideally less
than 250 lines in the diffstat. (But they should also be coherent
lumps of functionality, so prefer an occasional larger patch
rather than a completely artificial patch split. It's pretty
rare that something really doesn't have a sensible split point,
though, unless it's a purely mechanical change like a renaming.)

If a patch series gets over 15 or so patches it's pretty oppressive to
review all at once, though. I find the easiest thing to do for this
kind of thing is extract a coherent thematic subset of the total lump
of code, split that into individual patches, and send that as a series.
Once that's reviewed and on its way into the tree, then repeat. This is
how we did the ARM 64-bit support. It means that if there are general
issues (code needing updates to new APIs or best practices, for
example) then they'll be caught in review of the first series, and
you can then get subsequent series right from the start.

As a random example pulled from the archives, this was about
the sixth patchseries for A64 Neon:
https://lists.gnu.org/archive/html/qemu-devel/2014-03/msg01615.html
Each patch implements one insn or a few related insns.

thanks
-- PMM

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Qemu-devel] [PATCH 0/2] m68k: 680x0 processors family support
  2015-06-21 22:35 [Qemu-devel] [PATCH 0/2] m68k: 680x0 processors family support Laurent Vivier
                   ` (2 preceding siblings ...)
  2015-06-22  8:33 ` [Qemu-devel] [PATCH 0/2] m68k: 680x0 processors family support Peter Maydell
@ 2015-06-22 12:06 ` Greg Ungerer
  3 siblings, 0 replies; 8+ messages in thread
From: Greg Ungerer @ 2015-06-22 12:06 UTC (permalink / raw)
  To: Laurent Vivier, qemu-devel; +Cc: Andreas Schwab

Hi Laurent,

On 22/06/15 08:35, Laurent Vivier wrote:
> I carry this series for several years now.
>
> I was reluctant to merge it with the mainstream because
> I was sure it was breaking Coldfire support.
>
> But with the kernel provided by Greg Ungerer I was able
> to check and correct the support of ColdFire family
> in my tree.
>
> This series allows:
> - to start a ColdFire semi-hosted kernel (m68k-softmmu)
> - chroot or start a container of debian etch-m68k (m68k-linux-user)
>
> I have another series providing privileged instructions and
> allowing to boot a 680x0 debian kernel (Macintosh Quadra 800),
> but this one needs more work (and love). Based on this work,
> we will also be able to boot a NextStation (Bryce Lanham, GSoC)
>
> This work is based on the work of Andreas Schwab.

All together that is impressive. Would be nice if you can
work to get it all in.

Regards
Greg



> Laurent Vivier (2):
>    m68k: Add compatibility with 680x0 processors family
>    m68k: Implement 680x0 processors family 96 bit FPU
>
>   configure                  |    2 +-
>   cpu-exec.c                 |    6 -
>   disas.c                    |    4 +
>   fpu/softfloat-specialize.h |   48 +-
>   fpu/softfloat.c            |   38 +-
>   gdb-xml/m68k-fp.xml        |   21 +
>   include/fpu/softfloat.h    |   11 +-
>   target-m68k/cpu.c          |   67 +-
>   target-m68k/cpu.h          |   76 +-
>   target-m68k/helper.c       | 1691 ++++++++++++++++++--
>   target-m68k/helper.h       |  100 +-
>   target-m68k/m68k-qreg.h    |   11 -
>   target-m68k/op_helper.c    |  182 ++-
>   target-m68k/qregs.def      |    6 +-
>   target-m68k/translate.c    | 3682 +++++++++++++++++++++++++++++++++++---------
>   15 files changed, 5013 insertions(+), 932 deletions(-)
>   create mode 100644 gdb-xml/m68k-fp.xml
>   delete mode 100644 target-m68k/m68k-qreg.h
>

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2015-06-22 12:07 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-06-21 22:35 [Qemu-devel] [PATCH 0/2] m68k: 680x0 processors family support Laurent Vivier
2015-06-21 22:35 ` [Qemu-devel] [PATCH 1/2] m68k: Add compatibility with 680x0 processors family Laurent Vivier
2015-06-21 22:35 ` [Qemu-devel] [PATCH 2/2] m68k: Implement 680x0 processors family 96 bit FPU Laurent Vivier
2015-06-22  8:00   ` Andreas Schwab
2015-06-22  8:33 ` [Qemu-devel] [PATCH 0/2] m68k: 680x0 processors family support Peter Maydell
2015-06-22  8:55   ` Laurent Vivier
2015-06-22 10:05     ` Peter Maydell
2015-06-22 12:06 ` Greg Ungerer

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.