diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c
index f312663110..0783b1e7ee 100644
--- a/target/i386/tcg/translate.c
+++ b/target/i386/tcg/translate.c
@@ -2918,7 +2918,6 @@ static bool first = true; static unsigned long limit;
 #define SSE_OPF_CMP       (1 << 1) /* does not write for first operand */
 #define SSE_OPF_BLENDV    (1 << 2) /* blendv* instruction */
 #define SSE_OPF_SPECIAL   (1 << 3) /* magic */
-#define SSE_OPF_3DNOW     (1 << 4) /* 3DNow! instruction */
 #define SSE_OPF_MMX       (1 << 5) /* MMX/integer/AVX2 instruction */
 #define SSE_OPF_SCALAR    (1 << 6) /* Has SSE scalar variants */
 #define SSE_OPF_SHUF      (1 << 9) /* pshufx/shufpx */
@@ -2952,13 +2951,9 @@ struct SSEOpHelper_table1 {
     SSEFuncs fn[4];
 };
 
-#define SSE_3DNOW { SSE_OPF_3DNOW }
 #define SSE_SPECIAL { SSE_OPF_SPECIAL }
 
 static const struct SSEOpHelper_table1 sse_op_table1[256] = {
-    /* 3DNow! extensions */
-    [0x0e] = SSE_SPECIAL, /* femms */
-    [0x0f] = SSE_3DNOW, /* pf... (sse_op_table5) */
     /* pure SSE operations */
     [0x10] = SSE_SPECIAL, /* movups, movupd, movss, movsd */
     [0x11] = SSE_SPECIAL, /* movups, movupd, movss, movsd */
@@ -3172,7 +3167,7 @@ static void gen_helper_pavgusb(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b)
     gen_helper_pavgb_mmx(env, reg_a, reg_a, reg_b);
 }
 
-static const SSEFunc_0_epp sse_op_table5[256] = {
+static const SSEFunc_0_epp op_3dnow[256] = {
     [0x0c] = gen_helper_pi2fw,
     [0x0d] = gen_helper_pi2fd,
     [0x1c] = gen_helper_pf2iw,
@@ -3351,7 +3346,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
         b1 = 0;
     sse_op_flags = sse_op_table1[b].flags;
     sse_op_fn = sse_op_table1[b].fn[b1];
-    if ((sse_op_flags & (SSE_OPF_SPECIAL | SSE_OPF_3DNOW)) == 0
+    if ((sse_op_flags & SSE_OPF_SPECIAL) == 0
             && !sse_op_fn.op1) {
         goto unknown_op;
     }
@@ -3365,11 +3360,6 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
             is_xmm = 1;
         }
     }
-    if (sse_op_flags & SSE_OPF_3DNOW) {
-        if (!(s->cpuid_ext2_features & CPUID_EXT2_3DNOW)) {
-            goto illegal_op;
-        }
-    }
     /* simple MMX/SSE operation */
     if (s->flags & HF_TS_MASK) {
         gen_exception(s, EXCP07_PREX, pc_start - s->cs_base);
@@ -3385,15 +3375,6 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
         && (b != 0x38 && b != 0x3a)) {
         goto unknown_op;
     }
-    if (b == 0x0e) {
-        if (!(s->cpuid_ext2_features & CPUID_EXT2_3DNOW)) {
-            /* If we were fully decoding this we might use illegal_op.  */
-            goto unknown_op;
-        }
-        /* femms */
-        gen_helper_emms(cpu_env);
-        return;
-    }
     if (b == 0x77) {
         /* emms */
         gen_helper_emms(cpu_env);
@@ -4536,18 +4517,6 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
                 rm = (modrm & 7);
                 op2_offset = offsetof(CPUX86State,fpregs[rm].mmx);
             }
-            if (sse_op_flags & SSE_OPF_3DNOW) {
-                /* 3DNow! data insns */
-                val = x86_ldub_code(env, s);
-                SSEFunc_0_epp op_3dnow = sse_op_table5[val];
-                if (!op_3dnow) {
-                    goto unknown_op;
-                }
-                tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset);
-                tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset);
-                op_3dnow(cpu_env, s->ptr0, s->ptr1);
-                return;
-            }
         }
 
 
@@ -4598,6 +4567,70 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b,
     }
 }
 
+static void gen_3dnow(CPUX86State *env, DisasContext *s, int b,
+                      target_ulong pc_start)
+{
+    int op1_offset, op2_offset, val;
+    int modrm, mod, rm, reg;
+    SSEFunc_0_epp fn;
+
+    /* simple MMX/SSE operation */
+    if (s->flags & HF_TS_MASK) {
+        gen_exception(s, EXCP07_PREX, pc_start - s->cs_base);
+        return;
+    }
+    if (s->flags & HF_EM_MASK) {
+        goto illegal_op;
+        return;
+    }
+    if (b == 0x10e) {
+        if (!(s->cpuid_ext2_features & CPUID_EXT2_3DNOW)) {
+            /* If we were fully decoding this we might use illegal_op.  */
+            goto unknown_op;
+        }
+        /* femms */
+        gen_helper_emms(cpu_env);
+        return;
+    }
+
+    if (!(s->cpuid_ext2_features & CPUID_EXT2_3DNOW)) {
+        goto illegal_op;
+    }
+
+    gen_helper_enter_mmx(cpu_env);
+
+    modrm = x86_ldub_code(env, s);
+    reg = ((modrm >> 3) & 7);
+    mod = (modrm >> 6) & 3;
+
+    op1_offset = offsetof(CPUX86State,fpregs[reg].mmx);
+    if (mod != 3) {
+        gen_lea_modrm(env, s, modrm);
+        op2_offset = offsetof(CPUX86State,mmx_t0);
+        gen_ldq_env_A0(s, op2_offset);
+    } else {
+        rm = (modrm & 7);
+        op2_offset = offsetof(CPUX86State,fpregs[rm].mmx);
+    }
+
+    val = x86_ldub_code(env, s);
+    fn = op_3dnow[val];
+    if (!fn) {
+        goto unknown_op;
+    }
+    tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset);
+    tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset);
+    fn(cpu_env, s->ptr0, s->ptr1);
+    return;
+
+illegal_op:
+    gen_illegal_opcode(s);
+    return;
+
+unknown_op:
+    gen_unknown_opcode(env, s);
+}
+
 /* convert one instruction. s->base.is_jmp is set if the translation must
    be stopped. Return the next pc value */
 static target_ulong disas_insn(DisasContext *s, CPUState *cpu)
@@ -8505,9 +8538,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu)
         set_cc_op(s, CC_OP_POPCNT);
         break;
     case 0x10e ... 0x10f:
-        /* 3DNow! instructions, ignore prefixes */
-        s->prefix &= ~(PREFIX_REPZ | PREFIX_REPNZ | PREFIX_DATA);
-        /* fall through */
+        gen_3dnow(env, s, b, pc_start);
+        break;
     case 0x110 ... 0x117:
     case 0x128 ... 0x12f:
     case 0x138 ... 0x13a:
