diff --git a/target-s390x/insn-data.def b/target-s390x/insn-data.def
new file mode 100644
index 0000000..7d81928
--- /dev/null
+++ b/target-s390x/insn-data.def
@@ -0,0 +1,54 @@
+/* ADD */
+    C(0x1a00, AR,      RR_a,  Z,   r1, r2, new, r1_32, add, adds32)
+    C(0xb9f8, ARK,     RRF_a, DO,  r2, r3, new, r1_32, add, adds32)
+    C(0x5a00, A,       RX_a,  Z,   r1, m2_32s, new, r1_32, add, adds32)
+    C(0xe35a, AY,      RXY_a, LD,  r1, m2_32s, new, r1_32, add, adds32)
+    C(0xb908, AGR,     RRE,   Z,   r1, r2, r1, 0, add, adds64)
+    C(0xb918, AGFR,    RRE,   Z,   r1, r2_32s, r1, 0, add, adds64)
+    C(0xb9e8, AGRK,    RRF_a, DO,  r2, r3, r1, 0, add, adds64)
+    C(0xe308, AG,      RXY_a, Z,   r1, m2_64, r1, 0, add, adds64)
+    C(0xe318, AGF,     RXY_a, Z,   r1, m2_32s, r1, 0, add, adds64)
+/* ADD IMMEDIATE */
+    C(0xc209, AFI,     RIL_a, EI,  r1, i2, new, r1_32, add, adds32)
+    C(0xeb6a, ASI,     SIY,   GIE, m1_32s, i2, new, m1_32, add, adds32)
+    C(0xecd8, AHIK,    RIE_d, DO,  r3, i2, new, r1_32, add, adds32)
+    C(0xc208, AGFI,    RIL_a, EI,  r1, i2, r1, 0, add, adds64)
+    C(0xeb7a, AGSI,    SIY,   GIE, m1_64, i2, new, m1_64, add, adds64)
+    C(0xecd9, AGHIK,   RIE_d, DO,  r3, i2, r1, 0, add, adds64)
+/* ADD LOGICAL */
+    C(0x1e00, ALR,     RR_a,  Z,   r1, r2, new, r1_32, add, addu32)
+    C(0xb9fa, ALRK,    RRF_a, DO,  r2, r3, new, r1_32, add, addu32)
+    C(0x5e00, AL,      RX_a,  Z,   r1, m2_32u, new, r1_32, add, addu32)
+    C(0xe35e, ALY,     RXY_a, LD,  r1, m2_32u, new, r1_32, add, addu32)
+    C(0xb90a, ALGR,    RRE,   Z,   r1, r2, r1, 0, add, addu64)
+    C(0xb91a, ALGFR,   RRE,   Z,   r1, r2_32u, r1, 0, add, addu64)
+    C(0xb9ea, ALGRK,   RRF_a, DO,  r2, r3, r1, 0, add, addu64)
+    C(0xe30a, ALG,     RXY_a, Z,   r1, m2_64, r1, 0, add, addu64)
+    C(0xe31a, ALGF,    RXY_a, Z,   r1, m2_32u, r1, 0, add, addu64)
+/* ADD LOGICAL IMMEDIATE */
+    C(0xc20b, ALFI,    RIL_a, EI,  r1, i2_32u, new, r1_32, add, addu32)
+    C(0xc20a, ALGFI,   RIL_a, EI,  r1, i2_32u, r1, 0, add, addu64)
+
+/* SUBTRACT */
+    C(0x1b00, SR,      RR_a,  Z,   r1, r2, new, r1_32, sub, subs32)
+    C(0xb9f9, SRK,     RRF_a, DO,  r2, r3, new, r1_32, sub, subs32)
+    C(0x5b00, S,       RX_a,  Z,   r1, m2_32s, new, r1_32, sub, subs32)
+    C(0xe35b, SY,      RXY_a, LD,  r1, m2_32s, new, r1_32, sub, subs32)
+    C(0xb909, SGR,     RRE,   Z,   r1, r2, r1, 0, sub, subs64)
+    C(0xb919, SGFR,    RRE,   Z,   r1, r2_32s, r1, 0, sub, subs64)
+    C(0xb9e9, SGRK,    RRF_a, DO,  r2, r3, r1, 0, sub, subs64)
+    C(0xe309, SG,      RXY_a, Z,   r1, m2_64, r1, 0, sub, subs64)
+    C(0xe319, SGF,     RXY_a, Z,   r1, m2_32s, r1, 0, sub, subs64)
+/* SUBTRACT LOGICAL */
+    C(0x1f00, SLR,     RR_a,  Z,   r1, r2, new, r1_32, sub, subu32)
+    C(0xb9fb, SLRK,    RRF_a, DO,  r2, r3, new, r1_32, sub, subu32)
+    C(0x5f00, SL,      RX_a,  Z,   r1, m2_32u, new, r1_32, sub, subu32)
+    C(0xe35f, SLY,     RXY_a, LD,  r1, m2_32u, new, r1_32, sub, subu32)
+    C(0xb90b, SLGR,    RRE,   Z,   r1, r2, r1, 0, sub, subu64)
+    C(0xb91b, SLGFR,   RRE,   Z,   r1, r2_32u, r1, 0, sub, subu64)
+    C(0xb9eb, SLGRK,   RRF_a, DO,  r2, r3, r1, 0, sub, subu64)
+    C(0xe30b, SLG,     RXY_a, Z,   r1, m2_64, r1, 0, sub, subu64)
+    C(0xe31b, SLGF,    RXY_a, Z,   r1, m2_32u, r1, 0, sub, subu64)
+/* SUBTRACT LOGICAL IMMEDIATE */
+    C(0xc205, SLFI,    RIL_a, EI,  r1, i2_32u, new, r1_32, sub, subu32)
+    C(0xc204, SLGFI,   RIL_a, EI,  r1, i2_32u, r1, 0, sub, subu64)
diff --git a/target-s390x/insn-format.def b/target-s390x/insn-format.def
new file mode 100644
index 0000000..0e898b9
--- /dev/null
+++ b/target-s390x/insn-format.def
@@ -0,0 +1,55 @@
+/* Description of s390 insn formats.  */
+/* NAME   F1,          F2... */
+F0(E)
+F1(I,     I(1, 8, 8))
+F2(RI_a,  R(1, 8),     I(2,16,16))
+F2(RI_b,  R(1, 8),     I(2,16,16))
+F2(RI_c,  M(1, 8),     I(2,16,16))
+F3(RIE_a, R(1, 8),     I(2,16,16),  M(3,32))
+F4(RIE_b, R(1, 8),     R(2,12),     M(3,32),   I(4,16,16))
+F4(RIE_c, R(1, 8),     I(2,32, 8),  M(3,12),   I(4,16,16))
+F3(RIE_d, R(1, 8),     I(2,16,16),  R(3,12))
+F3(RIE_e, R(1, 8),     I(2,16,16),  R(3,12))
+F5(RIE_f, R(1, 8),     R(2,12),     I(3,16,8), I(4,24,8),  I(5,32,8))
+F2(RIL_a, R(1, 8),     I(2,16,32))
+F2(RIL_b, R(1, 8),     I(2,16,32))
+F2(RIL_c, M(1, 8),     I(2,16,32))
+F4(RIS,   R(1, 8),     I(2,32, 8),  M(3,12),   BD(4,16,20))
+/* ??? The PoO does not call out subtypes _a and _b for RR, as it does
+   for e.g. RX.  Our checking requires this for e.g. BCR.  */
+F2(RR_a,  R(1, 8),     R(2,12))
+F2(RR_b,  M(1, 8),     R(2,12))
+F2(RRE,   R(1,24),     R(2,28))
+F3(RRD,   R(1,16),     R(2,28),     R(3,24))
+F4(RRF_a, R(1,24),     R(2,28),     R(3,16),   M(4,20))
+F4(RRF_b, R(1,24),     R(2,28),     R(3,16),   M(4,20))
+F4(RRF_c, R(1,24),     R(2,28),     M(3,16),   M(4,20))
+F4(RRF_d, R(1,24),     R(2,28),     M(3,16),   M(4,20))
+F4(RRF_e, R(1,24),     R(2,28),     M(3,16),   M(4,20))
+F4(RRS,   R(1, 8),     R(2,12),     M(3,32),   BD(4,16,20))
+F3(RS_a,  R(1, 8),     BD(2,16,20), R(3,12))
+F3(RS_b,  R(1, 8),     BD(2,16,20), M(3,12))
+F3(RSI,   R(1, 8),     I(2,16,16),  R(3,12))
+F2(RSL,   L(1, 8, 4),  BD(1,16,20))
+F3(RSY_a, R(1, 8),     BDL(2),      R(3,12))
+F3(RSY_b, R(1, 8),     BDL(2),      M(3,12))
+F2(RX_a,  R(1, 8),     BXD(2))
+F2(RX_b,  M(1, 8),     BXD(2))
+F2(RXE,   R(1, 8),     BXD(2))
+F3(RXF,   R(1,32),     BXD(2),      R(3, 8))
+F2(RXY_a, R(1, 8),     BXDL(2))
+F2(RXY_b, M(1, 8),     BXDL(2))
+F1(S,     BD(2,16,20))
+F2(SI,    BD(1,16,20), I(2,8,8))
+F2(SIL,   BD(1,16,20), I(2,32,16))
+F2(SIY,   BDL(1),      I(2, 8, 8))
+F3(SS_a,  L(1, 8, 8),  BD(1,16,20), BD(2,32,36))
+F4(SS_b,  L(1, 8, 4),  BD(1,16,20), L(2,12,4),   BD(2,32,36))
+F4(SS_c,  L(1, 8, 4),  BD(1,16,20), BD(2,32,36), I(3,12, 4))
+/* ??? Odd man out.  The L1 field here is really a register, but the
+   easy way to compress the fields has R1 and B1 overlap.  */
+F4(SS_d,  L(1, 8, 4),  BD(1,16,20), BD(2,32,36), R(3,12))
+F4(SS_e,  R(1, 8),     BD(2,16,20), R(3,12),     BD(4,32,36))
+F3(SS_f,  BD(1,16,20), L(2,8,8),    BD(2,32,36))
+F2(SSE,   BD(1,16,20), BD(2,32,36))
+F3(SSF,   BD(1,16,20), BD(2,32,36), R(3,8))
diff --git a/target-s390x/translate.c b/target-s390x/translate.c
index e140c79..02736e3 100644
--- a/target-s390x/translate.c
+++ b/target-s390x/translate.c
@@ -42,12 +42,20 @@ static TCGv_ptr cpu_env;
 #define GEN_HELPER 1
 #include "helper.h"
 
+
+/* Information that (most) every instruction needs to manipulate.  */
 typedef struct DisasContext DisasContext;
+typedef struct DisasInsn DisasInsn;
+typedef struct DisasFields DisasFields;
+
 struct DisasContext {
-    uint64_t pc;
-    int is_jmp;
-    enum cc_op cc_op;
     struct TranslationBlock *tb;
+    const DisasInsn *insn;
+    DisasFields *fields;
+    uint64_t pc, next_pc;
+    enum cc_op cc_op;
+    bool singlestep_enabled;
+    int is_jmp;
 };
 
 #define DISAS_EXCP 4
@@ -295,15 +303,12 @@ static inline uint64_t ld_code2(CPUS390XState *env, uint64_t pc)
 
 static inline uint64_t ld_code4(CPUS390XState *env, uint64_t pc)
 {
-    return (uint64_t)cpu_ldl_code(env, pc);
+    return (uint64_t)(uint32_t)cpu_ldl_code(env, pc);
 }
 
 static inline uint64_t ld_code6(CPUS390XState *env, uint64_t pc)
 {
-    uint64_t opc;
-    opc = (uint64_t)cpu_lduw_code(env, pc) << 32;
-    opc |= (uint64_t)(uint32_t)cpu_ldl_code(env, pc + 2);
-    return opc;
+    return (ld_code2(env, pc) << 32) | ld_code4(env, pc + 2);
 }
 
 static inline int get_mem_index(DisasContext *s)
@@ -607,17 +612,6 @@ static void set_cc_addu64(DisasContext *s, TCGv_i64 v1, TCGv_i64 v2,
     gen_op_update3_cc_i64(s, CC_OP_ADDU_64, v1, v2, vr);
 }
 
-static void set_cc_sub64(DisasContext *s, TCGv_i64 v1, TCGv_i64 v2, TCGv_i64 vr)
-{
-    gen_op_update3_cc_i64(s, CC_OP_SUB_64, v1, v2, vr);
-}
-
-static void set_cc_subu64(DisasContext *s, TCGv_i64 v1, TCGv_i64 v2,
-                          TCGv_i64 vr)
-{
-    gen_op_update3_cc_i64(s, CC_OP_SUBU_64, v1, v2, vr);
-}
-
 static void set_cc_abs64(DisasContext *s, TCGv_i64 v1)
 {
     gen_op_update1_cc_i64(s, CC_OP_ABS_64, v1);
@@ -644,12 +638,6 @@ static void set_cc_sub32(DisasContext *s, TCGv_i32 v1, TCGv_i32 v2, TCGv_i32 vr)
     gen_op_update3_cc_i32(s, CC_OP_SUB_32, v1, v2, vr);
 }
 
-static void set_cc_subu32(DisasContext *s, TCGv_i32 v1, TCGv_i32 v2,
-                          TCGv_i32 vr)
-{
-    gen_op_update3_cc_i32(s, CC_OP_SUBU_32, v1, v2, vr);
-}
-
 static void set_cc_abs32(DisasContext *s, TCGv_i32 v1)
 {
     gen_op_update1_cc_i32(s, CC_OP_ABS_32, v1);
@@ -1535,72 +1523,6 @@ static void disas_e3(CPUS390XState *env, DisasContext* s, int op, int r1,
         tcg_temp_free_i64(tmp3);
         tcg_temp_free_i64(tmp4);
         break;
-    case 0x8: /* AG      R1,D2(X2,B2)     [RXY] */
-    case 0xa: /* ALG      R1,D2(X2,B2)     [RXY] */
-    case 0x18: /* AGF       R1,D2(X2,B2)     [RXY] */
-    case 0x1a: /* ALGF      R1,D2(X2,B2)     [RXY] */
-        if (op == 0x1a) {
-            tmp2 = tcg_temp_new_i64();
-            tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s));
-        } else if (op == 0x18) {
-            tmp2 = tcg_temp_new_i64();
-            tcg_gen_qemu_ld32s(tmp2, addr, get_mem_index(s));
-        } else {
-            tmp2 = tcg_temp_new_i64();
-            tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s));
-        }
-        tmp4 = load_reg(r1);
-        tmp3 = tcg_temp_new_i64();
-        tcg_gen_add_i64(tmp3, tmp4, tmp2);
-        store_reg(r1, tmp3);
-        switch (op) {
-        case 0x8:
-        case 0x18:
-            set_cc_add64(s, tmp4, tmp2, tmp3);
-            break;
-        case 0xa:
-        case 0x1a:
-            set_cc_addu64(s, tmp4, tmp2, tmp3);
-            break;
-        default:
-            tcg_abort();
-        }
-        tcg_temp_free_i64(tmp2);
-        tcg_temp_free_i64(tmp3);
-        tcg_temp_free_i64(tmp4);
-        break;
-    case 0x9: /* SG      R1,D2(X2,B2)     [RXY] */
-    case 0xb: /* SLG      R1,D2(X2,B2)     [RXY] */
-    case 0x19: /* SGF      R1,D2(X2,B2)     [RXY] */
-    case 0x1b: /* SLGF     R1,D2(X2,B2)     [RXY] */
-        tmp2 = tcg_temp_new_i64();
-        if (op == 0x19) {
-            tcg_gen_qemu_ld32s(tmp2, addr, get_mem_index(s));
-        } else if (op == 0x1b) {
-            tcg_gen_qemu_ld32u(tmp2, addr, get_mem_index(s));
-        } else {
-            tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s));
-        }
-        tmp4 = load_reg(r1);
-        tmp3 = tcg_temp_new_i64();
-        tcg_gen_sub_i64(tmp3, tmp4, tmp2);
-        store_reg(r1, tmp3);
-        switch (op) {
-        case 0x9:
-        case 0x19:
-            set_cc_sub64(s, tmp4, tmp2, tmp3);
-            break;
-        case 0xb:
-        case 0x1b:
-            set_cc_subu64(s, tmp4, tmp2, tmp3);
-            break;
-        default:
-            tcg_abort();
-        }
-        tcg_temp_free_i64(tmp2);
-        tcg_temp_free_i64(tmp3);
-        tcg_temp_free_i64(tmp4);
-        break;
     case 0xf: /* LRVG     R1,D2(X2,B2)     [RXE] */
         tmp2 = tcg_temp_new_i64();
         tcg_gen_qemu_ld64(tmp2, addr, get_mem_index(s));
@@ -1723,40 +1645,6 @@ static void disas_e3(CPUS390XState *env, DisasContext* s, int op, int r1,
         store_reg32_i64(r1, tmp3);
         tcg_temp_free_i64(tmp3);
         break;
-    case 0x5a: /* AY R1,D2(X2,B2) [RXY] */
-    case 0x5b: /* SY R1,D2(X2,B2) [RXY] */
-        tmp32_1 = load_reg32(r1);
-        tmp32_2 = tcg_temp_new_i32();
-        tmp32_3 = tcg_temp_new_i32();
-        tmp2 = tcg_temp_new_i64();
-        tcg_gen_qemu_ld32s(tmp2, addr, get_mem_index(s));
-        tcg_gen_trunc_i64_i32(tmp32_2, tmp2);
-        tcg_temp_free_i64(tmp2);
-        switch (op) {
-        case 0x5a:
-            tcg_gen_add_i32(tmp32_3, tmp32_1, tmp32_2);
-            break;
-        case 0x5b:
-            tcg_gen_sub_i32(tmp32_3, tmp32_1, tmp32_2);
-            break;
-        default:
-            tcg_abort();
-        }
-        store_reg32(r1, tmp32_3);
-        switch (op) {
-        case 0x5a:
-            set_cc_add32(s, tmp32_1, tmp32_2, tmp32_3);
-            break;
-        case 0x5b:
-            set_cc_sub32(s, tmp32_1, tmp32_2, tmp32_3);
-            break;
-        default:
-            tcg_abort();
-        }
-        tcg_temp_free_i32(tmp32_1);
-        tcg_temp_free_i32(tmp32_2);
-        tcg_temp_free_i32(tmp32_3);
-        break;
     case 0x71: /* LAY R1,D2(X2,B2) [RXY] */
         store_reg(r1, addr);
         break;
@@ -3350,68 +3238,6 @@ static void disas_b9(CPUS390XState *env, DisasContext *s, int op, int r1,
         store_reg(r1, tmp2);
         tcg_temp_free_i64(tmp2);
         break;
-    case 0x8: /* AGR     R1,R2     [RRE] */
-    case 0xa: /* ALGR     R1,R2     [RRE] */
-        tmp = load_reg(r1);
-        tmp2 = load_reg(r2);
-        tmp3 = tcg_temp_new_i64();
-        tcg_gen_add_i64(tmp3, tmp, tmp2);
-        store_reg(r1, tmp3);
-        switch (op) {
-        case 0x8:
-            set_cc_add64(s, tmp, tmp2, tmp3);
-            break;
-        case 0xa:
-            set_cc_addu64(s, tmp, tmp2, tmp3);
-            break;
-        default:
-            tcg_abort();
-        }
-        tcg_temp_free_i64(tmp);
-        tcg_temp_free_i64(tmp2);
-        tcg_temp_free_i64(tmp3);
-        break;
-    case 0x9: /* SGR     R1,R2     [RRE] */
-    case 0xb: /* SLGR     R1,R2     [RRE] */
-    case 0x1b: /* SLGFR     R1,R2     [RRE] */
-    case 0x19: /* SGFR     R1,R2     [RRE] */
-        tmp = load_reg(r1);
-        switch (op) {
-        case 0x1b:
-            tmp32_1 = load_reg32(r2);
-            tmp2 = tcg_temp_new_i64();
-            tcg_gen_extu_i32_i64(tmp2, tmp32_1);
-            tcg_temp_free_i32(tmp32_1);
-            break;
-        case 0x19:
-            tmp32_1 = load_reg32(r2);
-            tmp2 = tcg_temp_new_i64();
-            tcg_gen_ext_i32_i64(tmp2, tmp32_1);
-            tcg_temp_free_i32(tmp32_1);
-            break;
-        default:
-            tmp2 = load_reg(r2);
-            break;
-        }
-        tmp3 = tcg_temp_new_i64();
-        tcg_gen_sub_i64(tmp3, tmp, tmp2);
-        store_reg(r1, tmp3);
-        switch (op) {
-        case 0x9:
-        case 0x19:
-            set_cc_sub64(s, tmp, tmp2, tmp3);
-            break;
-        case 0xb:
-        case 0x1b:
-            set_cc_subu64(s, tmp, tmp2, tmp3);
-            break;
-        default:
-            tcg_abort();
-        }
-        tcg_temp_free_i64(tmp);
-        tcg_temp_free_i64(tmp2);
-        tcg_temp_free_i64(tmp3);
-        break;
     case 0xc: /* MSGR      R1,R2     [RRE] */
     case 0x1c: /* MSGFR      R1,R2     [RRE] */
         tmp = load_reg(r1);
@@ -3469,29 +3295,6 @@ static void disas_b9(CPUS390XState *env, DisasContext *s, int op, int r1,
         tcg_temp_free_i32(tmp32_1);
         tcg_temp_free_i64(tmp);
         break;
-    case 0x18: /* AGFR     R1,R2     [RRE] */
-    case 0x1a: /* ALGFR     R1,R2     [RRE] */
-        tmp32_1 = load_reg32(r2);
-        tmp2 = tcg_temp_new_i64();
-        if (op == 0x18) {
-            tcg_gen_ext_i32_i64(tmp2, tmp32_1);
-        } else {
-            tcg_gen_extu_i32_i64(tmp2, tmp32_1);
-        }
-        tcg_temp_free_i32(tmp32_1);
-        tmp = load_reg(r1);
-        tmp3 = tcg_temp_new_i64();
-        tcg_gen_add_i64(tmp3, tmp, tmp2);
-        store_reg(r1, tmp3);
-        if (op == 0x18) {
-            set_cc_add64(s, tmp, tmp2, tmp3);
-        } else {
-            set_cc_addu64(s, tmp, tmp2, tmp3);
-        }
-        tcg_temp_free_i64(tmp);
-        tcg_temp_free_i64(tmp2);
-        tcg_temp_free_i64(tmp3);
-        break;
     case 0x0f: /* LRVGR    R1,R2     [RRE] */
         tcg_gen_bswap64_i64(regs[r1], regs[r2]);
         break;
@@ -3794,54 +3597,10 @@ static void disas_c0(CPUS390XState *env, DisasContext *s, int op, int r1, int i2
 static void disas_c2(CPUS390XState *env, DisasContext *s, int op, int r1,
                      int i2)
 {
-    TCGv_i64 tmp, tmp2, tmp3;
-    TCGv_i32 tmp32_1, tmp32_2, tmp32_3;
+    TCGv_i64 tmp;
+    TCGv_i32 tmp32_1;
 
     switch (op) {
-    case 0x4: /* SLGFI R1,I2 [RIL] */
-    case 0xa: /* ALGFI R1,I2 [RIL] */
-        tmp = load_reg(r1);
-        tmp2 = tcg_const_i64((uint64_t)(uint32_t)i2);
-        tmp3 = tcg_temp_new_i64();
-        switch (op) {
-        case 0x4:
-            tcg_gen_sub_i64(tmp3, tmp, tmp2);
-            set_cc_subu64(s, tmp, tmp2, tmp3);
-            break;
-        case 0xa:
-            tcg_gen_add_i64(tmp3, tmp, tmp2);
-            set_cc_addu64(s, tmp, tmp2, tmp3);
-            break;
-        default:
-            tcg_abort();
-        }
-        store_reg(r1, tmp3);
-        tcg_temp_free_i64(tmp);
-        tcg_temp_free_i64(tmp2);
-        tcg_temp_free_i64(tmp3);
-        break;
-    case 0x5: /* SLFI R1,I2 [RIL] */
-    case 0xb: /* ALFI R1,I2 [RIL] */
-        tmp32_1 = load_reg32(r1);
-        tmp32_2 = tcg_const_i32(i2);
-        tmp32_3 = tcg_temp_new_i32();
-        switch (op) {
-        case 0x5:
-            tcg_gen_sub_i32(tmp32_3, tmp32_1, tmp32_2);
-            set_cc_subu32(s, tmp32_1, tmp32_2, tmp32_3);
-            break;
-        case 0xb:
-            tcg_gen_add_i32(tmp32_3, tmp32_1, tmp32_2);
-            set_cc_addu32(s, tmp32_1, tmp32_2, tmp32_3);
-            break;
-        default:
-            tcg_abort();
-        }
-        store_reg32(r1, tmp32_3);
-        tcg_temp_free_i32(tmp32_1);
-        tcg_temp_free_i32(tmp32_2);
-        tcg_temp_free_i32(tmp32_3);
-        break;
     case 0xc: /* CGFI R1,I2 [RIL] */
         tmp = load_reg(r1);
         cmp_s64c(s, tmp, (int64_t)i2);
@@ -4059,42 +3818,6 @@ static void disas_s390_insn(CPUS390XState *env, DisasContext *s)
         tcg_temp_free_i32(tmp32_1);
         tcg_temp_free_i32(tmp32_2);
         break;
-    case 0x1a: /* AR     R1,R2     [RR] */
-    case 0x1e: /* ALR    R1,R2     [RR] */
-        insn = ld_code2(env, s->pc);
-        decode_rr(s, insn, &r1, &r2);
-        tmp32_1 = load_reg32(r1);
-        tmp32_2 = load_reg32(r2);
-        tmp32_3 = tcg_temp_new_i32();
-        tcg_gen_add_i32(tmp32_3, tmp32_1, tmp32_2);
-        store_reg32(r1, tmp32_3);
-        if (opc == 0x1a) {
-            set_cc_add32(s, tmp32_1, tmp32_2, tmp32_3);
-        } else {
-            set_cc_addu32(s, tmp32_1, tmp32_2, tmp32_3);
-        }
-        tcg_temp_free_i32(tmp32_1);
-        tcg_temp_free_i32(tmp32_2);
-        tcg_temp_free_i32(tmp32_3);
-        break;
-    case 0x1b: /* SR     R1,R2     [RR] */
-    case 0x1f: /* SLR    R1,R2     [RR] */
-        insn = ld_code2(env, s->pc);
-        decode_rr(s, insn, &r1, &r2);
-        tmp32_1 = load_reg32(r1);
-        tmp32_2 = load_reg32(r2);
-        tmp32_3 = tcg_temp_new_i32();
-        tcg_gen_sub_i32(tmp32_3, tmp32_1, tmp32_2);
-        store_reg32(r1, tmp32_3);
-        if (opc == 0x1b) {
-            set_cc_sub32(s, tmp32_1, tmp32_2, tmp32_3);
-        } else {
-            set_cc_subu32(s, tmp32_1, tmp32_2, tmp32_3);
-        }
-        tcg_temp_free_i32(tmp32_1);
-        tcg_temp_free_i32(tmp32_2);
-        tcg_temp_free_i32(tmp32_3);
-        break;
     case 0x1c: /* MR     R1,R2     [RR] */
         /* reg(r1, r1+1) = reg(r1+1) * reg(r2) */
         insn = ld_code2(env, s->pc);
@@ -4380,51 +4103,6 @@ static void disas_s390_insn(CPUS390XState *env, DisasContext *s)
         tcg_temp_free_i32(tmp32_1);
         tcg_temp_free_i32(tmp32_2);
         break;
-    case 0x5a: /* A      R1,D2(X2,B2)     [RX] */
-    case 0x5b: /* S      R1,D2(X2,B2)     [RX] */
-    case 0x5e: /* AL     R1,D2(X2,B2)     [RX] */
-    case 0x5f: /* SL     R1,D2(X2,B2)     [RX] */
-        insn = ld_code4(env, s->pc);
-        tmp = decode_rx(s, insn, &r1, &x2, &b2, &d2);
-        tmp32_1 = load_reg32(r1);
-        tmp32_2 = tcg_temp_new_i32();
-        tmp32_3 = tcg_temp_new_i32();
-        tcg_gen_qemu_ld32s(tmp, tmp, get_mem_index(s));
-        tcg_gen_trunc_i64_i32(tmp32_2, tmp);
-        switch (opc) {
-        case 0x5a:
-        case 0x5e:
-            tcg_gen_add_i32(tmp32_3, tmp32_1, tmp32_2);
-            break;
-        case 0x5b:
-        case 0x5f:
-            tcg_gen_sub_i32(tmp32_3, tmp32_1, tmp32_2);
-            break;
-        default:
-            tcg_abort();
-        }
-        store_reg32(r1, tmp32_3);
-        switch (opc) {
-        case 0x5a:
-            set_cc_add32(s, tmp32_1, tmp32_2, tmp32_3);
-            break;
-        case 0x5e:
-            set_cc_addu32(s, tmp32_1, tmp32_2, tmp32_3);
-            break;
-        case 0x5b:
-            set_cc_sub32(s, tmp32_1, tmp32_2, tmp32_3);
-            break;
-        case 0x5f:
-            set_cc_subu32(s, tmp32_1, tmp32_2, tmp32_3);
-            break;
-        default:
-            tcg_abort();
-        }
-        tcg_temp_free_i64(tmp);
-        tcg_temp_free_i32(tmp32_1);
-        tcg_temp_free_i32(tmp32_2);
-        tcg_temp_free_i32(tmp32_3);
-        break;
     case 0x5c: /* M      R1,D2(X2,B2)        [RX] */
         /* reg(r1, r1+1) = reg(r1+1) * *(s32*)addr */
         insn = ld_code4(env, s->pc);
@@ -5131,9 +4809,699 @@ static void disas_s390_insn(CPUS390XState *env, DisasContext *s)
         gen_illegal_opcode(env, s, ilc);
         break;
     }
+}
+
+/* ====================================================================== */
+/* Define the insn format enumeration.  */
+#define F0(N)                         FMT_##N,
+#define F1(N, X1)                     F0(N)
+#define F2(N, X1, X2)                 F0(N)
+#define F3(N, X1, X2, X3)             F0(N)
+#define F4(N, X1, X2, X3, X4)         F0(N)
+#define F5(N, X1, X2, X3, X4, X5)     F0(N)
+
+typedef enum {
+#include "insn-format.def"
+} DisasFormat;
+
+#undef F0
+#undef F1
+#undef F2
+#undef F3
+#undef F4
+#undef F5
+
+/* Define a structure to hold the decoded fields.  We'll store each inside
+   an array indexed by an enum.  In order to conserve memory, we'll arrange
+   for fields that do not exist at the same time to overlap, thus the "C"
+   for compact.  For checking purposes there is an "O" for original index
+   as well that will be applied to availability bitmaps.  */
+
+enum DisasFieldIndexO {
+    FLD_O_r1,
+    FLD_O_r2,
+    FLD_O_r3,
+    FLD_O_m1,
+    FLD_O_m3,
+    FLD_O_m4,
+    FLD_O_b1,
+    FLD_O_b2,
+    FLD_O_b4,
+    FLD_O_d1,
+    FLD_O_d2,
+    FLD_O_d4,
+    FLD_O_x2,
+    FLD_O_l1,
+    FLD_O_l2,
+    FLD_O_i1,
+    FLD_O_i2,
+    FLD_O_i3,
+    FLD_O_i4,
+    FLD_O_i5
+};
+
+enum DisasFieldIndexC {
+    FLD_C_r1 = 0,
+    FLD_C_m1 = 0,
+    FLD_C_b1 = 0,
+    FLD_C_i1 = 0,
+
+    FLD_C_r2 = 1,
+    FLD_C_b2 = 1,
+    FLD_C_i2 = 1,
+
+    FLD_C_r3 = 2,
+    FLD_C_m3 = 2,
+    FLD_C_i3 = 2,
+
+    FLD_C_m4 = 3,
+    FLD_C_b4 = 3,
+    FLD_C_i4 = 3,
+    FLD_C_l1 = 3,
+
+    FLD_C_i5 = 4,
+    FLD_C_d1 = 4,
+
+    FLD_C_d2 = 5,
+
+    FLD_C_d4 = 6,
+    FLD_C_x2 = 6,
+    FLD_C_l2 = 6,
+
+    NUM_C_FIELD = 7
+};
+
+struct DisasFields {
+    unsigned op:8;
+    unsigned op2:8;
+    unsigned presentC:16;
+    unsigned int presentO;
+    int c[NUM_C_FIELD];
+};
+
+/* This is the way fields are to be accessed out of DisasFields.  */
+#define have_field(S, F)  have_field1((S), FLD_O_##F)
+#define get_field(S, F)   get_field1((S), FLD_O_##F, FLD_C_##F)
+
+static bool have_field1(const DisasFields *f, enum DisasFieldIndexO c)
+{
+    return (f->presentO >> c) & 1;
+}
+
+static int get_field1(const DisasFields *f, enum DisasFieldIndexO o,
+                      enum DisasFieldIndexC c)
+{
+    assert(have_field1(f, o));
+    return f->c[c];
+}
+
+/* Describe the layout of each field in each format.  */
+typedef struct DisasField {
+    unsigned int beg:8;
+    unsigned int size:8;
+    unsigned int type:2;
+    unsigned int indexC:6;
+    enum DisasFieldIndexO indexO:8;
+} DisasField;
+
+typedef struct DisasFormatInfo {
+    DisasField op[NUM_C_FIELD];
+} DisasFormatInfo;
+
+#define R(N, B)       {  B,  4, 0, FLD_C_r##N, FLD_O_r##N }
+#define M(N, B)       {  B,  4, 0, FLD_C_m##N, FLD_O_m##N }
+#define BD(N, BB, BD) { BB,  4, 0, FLD_C_b##N, FLD_O_b##N }, \
+                      { BD, 12, 0, FLD_C_d##N, FLD_O_d##N }
+#define BXD(N)        { 16,  4, 0, FLD_C_b##N, FLD_O_b##N }, \
+                      { 12,  4, 0, FLD_C_x##N, FLD_O_x##N }, \
+                      { 20, 12, 0, FLD_C_d##N, FLD_O_d##N }
+#define BDL(N)        { 16,  4, 0, FLD_C_b##N, FLD_O_b##N }, \
+                      { 20, 20, 2, FLD_C_d##N, FLD_O_d##N }
+#define BXDL(N)       { 16,  4, 0, FLD_C_b##N, FLD_O_b##N }, \
+                      { 12,  4, 0, FLD_C_x##N, FLD_O_x##N }, \
+                      { 20, 20, 2, FLD_C_d##N, FLD_O_d##N }
+#define I(N, B, S)    {  B,  S, 1, FLD_C_i##N, FLD_O_i##N }
+#define L(N, B, S)    {  B,  S, 0, FLD_C_l##N, FLD_O_l##N }
+
+#define F0(N)                     { { } },
+#define F1(N, X1)                 { { X1 } },
+#define F2(N, X1, X2)             { { X1, X2 } },
+#define F3(N, X1, X2, X3)         { { X1, X2, X3 } },
+#define F4(N, X1, X2, X3, X4)     { { X1, X2, X3, X4 } },
+#define F5(N, X1, X2, X3, X4, X5) { { X1, X2, X3, X4, X5 } },
+
+static const DisasFormatInfo format_info[] = {
+#include "insn-format.def"
+};
+
+#undef F0
+#undef F1
+#undef F2
+#undef F3
+#undef F4
+#undef F5
+#undef R
+#undef M
+#undef BD
+#undef BXD
+#undef BDL
+#undef BXDL
+#undef I
+#undef L
+
+/* Generally, we'll extract operands into this structures, operate upon
+   them, and store them back.  See the "in1", "in2", "prep", "wout" sets
+   of routines below for more details.  */
+typedef struct {
+    bool g_out, g_out2, g_in1, g_in2;
+    TCGv_i64 out, out2, in1, in2;
+    TCGv_i64 addr1;
+} DisasOps;
+
+/* Return values from translate_one, indicating the state of the TB.  */
+typedef enum {
+    /* Continue the TB.  */
+    NO_EXIT,
+    /* We have emitted one or more goto_tb.  No fixup required.  */
+    EXIT_GOTO_TB,
+    /* We are not using a goto_tb (for whatever reason), but have updated
+       the PC (for whatever reason), so there's no need to do it again on
+       exiting the TB.  */
+    EXIT_PC_UPDATED,
+    /* We are exiting the TB, but have neither emitted a goto_tb, nor
+       updated the PC for the next instruction to be executed.  */
+    EXIT_PC_STALE,
+    /* We are ending the TB with a noreturn function call, e.g. longjmp.
+       No following code will be executed.  */
+    EXIT_NORETURN,
+} ExitStatus;
+
+typedef enum DisasFacility {
+    FAC_Z,                  /* zarch (default) */
+    FAC_CASS,               /* compare and swap and store */
+    FAC_CASS2,              /* compare and swap and store 2*/
+    FAC_DFP,                /* decimal floating point */
+    FAC_DFPR,               /* decimal floating point rounding */
+    FAC_DO,                 /* distinct operands */
+    FAC_EE,                 /* execute extensions */
+    FAC_EI,                 /* extended immediate */
+    FAC_FPE,                /* floating point extension */
+    FAC_FPSSH,              /* floating point support sign handling */
+    FAC_FPRGR,              /* FPR-GR transfer */
+    FAC_GIE,                /* general instructions extension */
+    FAC_HFP_MA,             /* HFP multiply-and-add/subtract */
+    FAC_HW,                 /* high-word */
+    FAC_IEEEE_SIM,          /* IEEE exception sumilation */
+    FAC_LOC,                /* load/store on condition */
+    FAC_LD,                 /* long displacement */
+    FAC_PC,                 /* population count */
+    FAC_SCF,                /* store clock fast */
+    FAC_SFLE,               /* store facility list extended */
+} DisasFacility;
+
+struct DisasInsn {
+    unsigned opc:16;
+    DisasFormat fmt:6;
+    DisasFacility fac:6;
+
+    const char *name;
+
+    void (*help_in1)(DisasContext *, DisasFields *, DisasOps *);
+    void (*help_in2)(DisasContext *, DisasFields *, DisasOps *);
+    void (*help_prep)(DisasContext *, DisasFields *, DisasOps *);
+    void (*help_wout)(DisasContext *, DisasFields *, DisasOps *);
+    void (*help_cout)(DisasContext *, DisasOps *);
+    ExitStatus (*help_op)(DisasContext *, DisasOps *);
+
+    uint64_t data;
+};
+
+/* ====================================================================== */
+/* The operations.  These perform the bulk of the work for any insn,
+   usually after the operands have been loaded and output initialized.  */
+
+static ExitStatus op_add(DisasContext *s, DisasOps *o)
+{
+    tcg_gen_add_i64(o->out, o->in1, o->in2);
+    return NO_EXIT;
+}
+
+static ExitStatus op_sub(DisasContext *s, DisasOps *o)
+{
+    tcg_gen_sub_i64(o->out, o->in1, o->in2);
+    return NO_EXIT;
+}
+
+/* ====================================================================== */
+/* The "Cc OUTput" generators.  Given the generated output (and in some cases
+   the original inputs), update the various cc data structures in order to
+   be able to compute the new condition code.  */
+
+static void cout_adds32(DisasContext *s, DisasOps *o)
+{
+    gen_op_update3_cc_i64(s, CC_OP_ADD_32, o->in1, o->in2, o->out);
+}
+
+static void cout_adds64(DisasContext *s, DisasOps *o)
+{
+    gen_op_update3_cc_i64(s, CC_OP_ADD_64, o->in1, o->in2, o->out);
+}
+
+static void cout_addu32(DisasContext *s, DisasOps *o)
+{
+    gen_op_update3_cc_i64(s, CC_OP_ADDU_32, o->in1, o->in2, o->out);
+}
+
+static void cout_addu64(DisasContext *s, DisasOps *o)
+{
+    gen_op_update3_cc_i64(s, CC_OP_ADDU_64, o->in1, o->in2, o->out);
+}
+
+static void cout_subs32(DisasContext *s, DisasOps *o)
+{
+    gen_op_update3_cc_i64(s, CC_OP_SUB_32, o->in1, o->in2, o->out);
+}
+
+static void cout_subs64(DisasContext *s, DisasOps *o)
+{
+    gen_op_update3_cc_i64(s, CC_OP_SUB_64, o->in1, o->in2, o->out);
+}
+
+static void cout_subu32(DisasContext *s, DisasOps *o)
+{
+    gen_op_update3_cc_i64(s, CC_OP_SUBU_32, o->in1, o->in2, o->out);
+}
+
+static void cout_subu64(DisasContext *s, DisasOps *o)
+{
+    gen_op_update3_cc_i64(s, CC_OP_SUBU_64, o->in1, o->in2, o->out);
+}
+
+/* ====================================================================== */
+/* The "PREPeration" generators.  These initialize the DisasOps.OUT fields
+   with the TCG register to which we will write.  Used in combination with
+   the "wout" generators, in some cases we need a new temporary, and in
+   some cases we can write to a TCG global.  */
+
+static void prep_new(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+    o->out = tcg_temp_new_i64();
+}
+
+static void prep_r1(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+    o->out = regs[get_field(f, r1)];
+    o->g_out = true;
+}
+
+/* ====================================================================== */
+/* The "Write OUTput" generators.  These generally perform some non-trivial
+   copy of data to TCG globals, or to main memory.  The trivial cases are
+   generally handled by having a "prep" generator install the TCG global
+   as the destination of the operation.  */
+
+static void wout_r1_32(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+    store_reg32_i64(get_field(f, r1), o->out);
+}
+
+static void wout_m1_32(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+    tcg_gen_qemu_st32(o->out, o->addr1, get_mem_index(s));
+}
+
+static void wout_m1_64(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+    tcg_gen_qemu_st64(o->out, o->addr1, get_mem_index(s));
+}
+
+/* ====================================================================== */
+/* The "INput 1" generators.  These load the first operand to an insn.  */
+
+static void in1_r1(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+    o->in1 = load_reg(get_field(f, r1));
+}
+
+static void in1_r2(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+    o->in1 = load_reg(get_field(f, r2));
+}
+
+static void in1_r3(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+    o->in1 = load_reg(get_field(f, r3));
+}
+
+static void in1_la1(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+    o->addr1 = get_address(s, 0, get_field(f, b1), get_field(f, d1));
+}
+
+static void in1_m1_32s(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+    in1_la1(s, f, o);
+    o->in1 = tcg_temp_new_i64();
+    tcg_gen_qemu_ld32s(o->in1, o->addr1, get_mem_index(s));
+}
+
+static void in1_m1_64(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+    in1_la1(s, f, o);
+    o->in1 = tcg_temp_new_i64();
+    tcg_gen_qemu_ld64(o->in1, o->addr1, get_mem_index(s));
+}
+
+/* ====================================================================== */
+/* The "INput 2" generators.  These load the second operand to an insn.  */
+
+static void in2_r2(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+    o->in2 = load_reg(get_field(f, r2));
+}
+
+static void in2_r3(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+    o->in2 = load_reg(get_field(f, r3));
+}
+
+static void in2_r2_32s(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+    o->in2 = tcg_temp_new_i64();
+    tcg_gen_ext32s_i64(o->in2, regs[get_field(f, r2)]);
+}
+
+static void in2_r2_32u(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+    o->in2 = tcg_temp_new_i64();
+    tcg_gen_ext32u_i64(o->in2, regs[get_field(f, r2)]);
+}
+
+static void in2_a2(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+    int x2 = have_field(f, x2) ? get_field(f, x2) : 0;
+    o->in2 = get_address(s, x2, get_field(f, b2), get_field(f, d2));
+}
+
+static void in2_m2_32s(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+    in2_a2(s, f, o);
+    tcg_gen_qemu_ld32s(o->in2, o->in2, get_mem_index(s));
+}
+
+static void in2_m2_32u(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+    in2_a2(s, f, o);
+    tcg_gen_qemu_ld32u(o->in2, o->in2, get_mem_index(s));
+}
+
+static void in2_m2_64(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+    in2_a2(s, f, o);
+    tcg_gen_qemu_ld64(o->in2, o->in2, get_mem_index(s));
+}
+
+static void in2_i2(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+    o->in2 = tcg_const_i64(get_field(f, i2));
+}
+
+static void in2_i2_32u(DisasContext *s, DisasFields *f, DisasOps *o)
+{
+    o->in2 = tcg_const_i64((uint32_t)get_field(f, i2));
+}
+
+/* ====================================================================== */
+
+/* Find opc within the table of insns.  This is formulated as a switch
+   statement so that (1) we get compile-time notice of cut-paste errors
+   for duplicated opcodes, and (2) the compiler generates the binary
+   search tree, rather than us having to post-process the table.  */
+
+#define C(OPC, NM, FT, FC, I1, I2, P, W, OP, CC) \
+    D(OPC, NM, FT, FC, I1, I2, P, W, OP, CC, 0)
+
+#define D(OPC, NM, FT, FC, I1, I2, P, W, OP, CC, D) insn_ ## NM,
+
+enum DisasInsnEnum {
+#include "insn-data.def"
+};
+
+#undef D
+#define D(OPC, NM, FT, FC, I1, I2, P, W, OP, CC, D) { \
+    .opc = OPC,                           \
+    .fmt = FMT_##FT,                      \
+    .fac = FAC_##FC,                      \
+    .name = #NM,                          \
+    .help_in1 = in1_##I1,                 \
+    .help_in2 = in2_##I2,                 \
+    .help_prep = prep_##P,                \
+    .help_wout = wout_##W,                \
+    .help_cout = cout_##CC,               \
+    .help_op = op_##OP,                   \
+    .data = D                             \
+ },
+
+/* Allow 0 to be used for NULL in the table below.  */
+#define in1_0  NULL
+#define in2_0  NULL
+#define prep_0  NULL
+#define wout_0  NULL
+#define cout_0  NULL
+#define op_0  NULL
+
+static const DisasInsn insn_info[] = {
+#include "insn-data.def"
+};
+
+#undef D
+#define D(OPC, NM, FT, FC, I1, I2, P, W, OP, CC, D) \
+    case OPC: return &insn_info[insn_ ## NM];
+
+static const DisasInsn *lookup_opc(uint16_t opc)
+{
+    switch (opc) {
+#include "insn-data.def"
+    default:
+        return NULL;
+    }
+}
+
+#undef D
+#undef C
+
+/* Extract a field from the insn.  The INSN should be left-aligned in
+   the uint64_t so that we can more easily utilize the big-bit-endian
+   definitions we extract from the Principals of Operation.  */
+
+static void extract_field(DisasFields *o, const DisasField *f, uint64_t insn)
+{
+    uint32_t r, m;
+
+    if (f->size == 0) {
+        return;
+    }
+
+    /* Zero extract the field from the insn.  */
+    r = (insn << f->beg) >> (64 - f->size);
+
+    /* Sign-extend, or un-swap the field as necessary.  */
+    switch (f->type) {
+    case 0: /* unsigned */
+        break;
+    case 1: /* signed */
+        assert(f->size <= 32);
+        m = 1u << (f->size - 1);
+        r = (r ^ m) - m;
+        break;
+    case 2: /* dl+dh split, signed 20 bit. */
+        r = ((int8_t)r << 12) | (r >> 8);
+        break;
+    default:
+        abort();
+    }
+
+    /* Validate that the "compressed" encoding we selected above is valid.
+       I.e. we havn't make two different original fields overlap.  */
+    assert(((o->presentC >> f->indexC) & 1) == 0);
+    o->presentC |= 1 << f->indexC;
+    o->presentO |= 1 << f->indexO;
+
+    o->c[f->indexC] = r;
+}
+
+/* Lookup the insn at the current PC, extracting the operands into O and
+   returning the info struct for the insn.  Returns NULL for invalid insn.  */
+
+static const DisasInsn *extract_insn(CPUS390XState *env, DisasContext *s,
+                                     DisasFields *f)
+{
+    uint64_t insn, pc = s->pc;
+    int op, op2;
+    const DisasInsn *info;
+
+    insn = ld_code2(env, pc);
+    op = (insn >> 8) & 0xff;
+    switch (get_ilc(op)) {
+    case 1:
+        insn = insn << 48;
+        break;
+    case 2:
+        insn = ld_code4(env, pc) << 32;
+        break;
+    case 3:
+        insn = (insn << 48) | (ld_code4(env, pc + 2) << 16);
+        break;
+    default:
+        abort();
+    }
+
+    /* We can't actually determine the insn format until we've looked up
+       the full insn opcode.  Which we can't do without locating the
+       secondary opcode.  Assume by default that OP2 is at bit 40; for
+       those smaller insns that don't actually have a secondary opcode
+       this will correctly result in OP2 = 0. */
+    switch (op) {
+    case 0x01: /* E */
+    case 0x80: /* S */
+    case 0x82: /* S */
+    case 0x93: /* S */
+    case 0xb2: /* S, RRF, RRE */
+    case 0xb3: /* RRE, RRD, RRF */
+    case 0xb9: /* RRE, RRF */
+    case 0xe5: /* SSE, SIL */
+        op2 = (insn << 8) >> 56;
+        break;
+    case 0xa5: /* RI */
+    case 0xa7: /* RI */
+    case 0xc0: /* RIL */
+    case 0xc2: /* RIL */
+    case 0xc4: /* RIL */
+    case 0xc6: /* RIL */
+    case 0xc8: /* SSF */
+    case 0xcc: /* RIL */
+        op2 = (insn << 12) >> 60;
+        break;
+    case 0xd0 ... 0xdf: /* SS */
+    case 0xe1: /* SS */
+    case 0xe2: /* SS */
+    case 0xe8: /* SS */
+    case 0xe9: /* SS */
+    case 0xea: /* SS */
+    case 0xee ... 0xf3: /* SS */
+    case 0xf8 ... 0xfd: /* SS */
+        op2 = 0;
+        break;
+    default:
+        op2 = (insn << 40) >> 56;
+        break;
+    }
+
+    memset(f, 0, sizeof(*f));
+    f->op = op;
+    f->op2 = op2;
+
+    /* Lookup the instruction.  */
+    info = lookup_opc(op << 8 | op2);
+
+    /* If we found it, extract the operands.  */
+    if (info != NULL) {
+        DisasFormat fmt = info->fmt;
+        int i;
+
+        for (i = 0; i < NUM_C_FIELD; ++i) {
+            extract_field(f, &format_info[fmt].op[i], insn);
+        }
+    }
+    return info;
+}
+
+static ExitStatus translate_one(CPUS390XState *env, DisasContext *s)
+{
+    const DisasInsn *insn;
+    ExitStatus ret = NO_EXIT;
+    DisasFields f;
+    DisasOps o;
+
+    insn = extract_insn(env, s, &f);
 
     /* Instruction length is encoded in the opcode */
-    s->pc += (ilc * 2);
+    s->next_pc = s->pc + get_ilc(f.op) * 2;
+
+    /* If not found, try the old interpreter.  This includes ILLOPC.  */
+    if (insn == NULL) {
+        disas_s390_insn(env, s);
+        switch (s->is_jmp) {
+        case DISAS_NEXT:
+            ret = NO_EXIT;
+            break;
+        case DISAS_TB_JUMP:
+            ret = EXIT_GOTO_TB;
+            break;
+        case DISAS_JUMP:
+            ret = EXIT_PC_UPDATED;
+            break;
+        case DISAS_EXCP:
+            ret = EXIT_NORETURN;
+            break;
+        default:
+            abort();
+        }
+
+        s->pc = s->next_pc;
+        return ret;
+    }
+
+    /* Set up the strutures we use to communicate with the helpers. */
+    s->insn = insn;
+    s->fields = &f;
+    o.g_out = o.g_out2 = o.g_in1 = o.g_in2 = false;
+    TCGV_UNUSED_I64(o.out);
+    TCGV_UNUSED_I64(o.out2);
+    TCGV_UNUSED_I64(o.in1);
+    TCGV_UNUSED_I64(o.in2);
+    TCGV_UNUSED_I64(o.addr1);
+
+    /* Implement the instruction.  */
+    if (insn->help_in1) {
+        insn->help_in1(s, &f, &o);
+    }
+    if (insn->help_in2) {
+        insn->help_in2(s, &f, &o);
+    }
+    if (insn->help_prep) {
+        insn->help_prep(s, &f, &o);
+    }
+    if (insn->help_op) {
+        ret = insn->help_op(s, &o);
+    }
+    if (insn->help_wout) {
+        insn->help_wout(s, &f, &o);
+    }
+    if (insn->help_cout) {
+        insn->help_cout(s, &o);
+    }
+
+    /* Free any temporaries created by the helpers.  */
+    if (!TCGV_IS_UNUSED_I64(o.out) && !o.g_out) {
+        tcg_temp_free_i64(o.out);
+    }
+    if (!TCGV_IS_UNUSED_I64(o.out2) && !o.g_out2) {
+        tcg_temp_free_i64(o.out2);
+    }
+    if (!TCGV_IS_UNUSED_I64(o.in1) && !o.g_in1) {
+        tcg_temp_free_i64(o.in1);
+    }
+    if (!TCGV_IS_UNUSED_I64(o.in2) && !o.g_in2) {
+        tcg_temp_free_i64(o.in2);
+    }
+    if (!TCGV_IS_UNUSED_I64(o.addr1)) {
+        tcg_temp_free_i64(o.addr1);
+    }
+
+    /* Advance to the next instruction.  */
+    s->pc = s->next_pc;
+    return ret;
 }
 
 static inline void gen_intermediate_code_internal(CPUS390XState *env,
@@ -5147,6 +5515,7 @@ static inline void gen_intermediate_code_internal(CPUS390XState *env,
     int j, lj = -1;
     int num_insns, max_insns;
     CPUBreakpoint *bp;
+    ExitStatus status;
 
     pc_start = tb->pc;
 
@@ -5155,10 +5524,11 @@ static inline void gen_intermediate_code_internal(CPUS390XState *env,
         pc_start &= 0x7fffffff;
     }
 
-    dc.pc = pc_start;
-    dc.is_jmp = DISAS_NEXT;
     dc.tb = tb;
+    dc.pc = pc_start;
     dc.cc_op = CC_OP_DYNAMIC;
+    dc.singlestep_enabled = env->singlestep_enabled;
+    dc.is_jmp = DISAS_NEXT;
 
     gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
 
@@ -5194,7 +5564,7 @@ static inline void gen_intermediate_code_internal(CPUS390XState *env,
             gen_opc_instr_start[lj] = 1;
             gen_opc_icount[lj] = num_insns;
         }
-        if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) {
+        if (++num_insns == max_insns && (tb->cflags & CF_LAST_IO)) {
             gen_io_start();
         }
 
@@ -5202,35 +5572,50 @@ static inline void gen_intermediate_code_internal(CPUS390XState *env,
             tcg_gen_debug_insn_start(dc.pc);
         }
 
-        disas_s390_insn(env, &dc);
-
-        num_insns++;
-        if (env->singlestep_enabled) {
-            gen_debug(&dc);
+        status = translate_one(env, &dc);
+
+        /* If we reach a page boundary, are single stepping,
+           or exhaust instruction count, stop generation.  */
+        if (status == NO_EXIT
+            && (dc.pc >= next_page_start
+                || gen_opc_ptr >= gen_opc_end
+                || num_insns >= max_insns
+                || singlestep
+                || env->singlestep_enabled)) {
+            status = EXIT_PC_STALE;
         }
-    } while (!dc.is_jmp && gen_opc_ptr < gen_opc_end && dc.pc < next_page_start
-             && num_insns < max_insns && !env->singlestep_enabled
-             && !singlestep);
-
-    if (!dc.is_jmp) {
-        update_psw_addr(&dc);
-    }
-
-    if (singlestep && dc.cc_op != CC_OP_DYNAMIC) {
-        gen_op_calc_cc(&dc);
-    } else {
-        /* next TB starts off with CC_OP_DYNAMIC, so make sure the cc op type
-           is in env */
-        gen_op_set_cc_op(&dc);
-    }
+    } while (status == NO_EXIT);
 
     if (tb->cflags & CF_LAST_IO) {
         gen_io_end();
     }
-    /* Generate the return instruction */
-    if (dc.is_jmp != DISAS_TB_JUMP) {
-        tcg_gen_exit_tb(0);
+
+    switch (status) {
+    case EXIT_GOTO_TB:
+    case EXIT_NORETURN:
+        break;
+    case EXIT_PC_STALE:
+        update_psw_addr(&dc);
+        /* FALLTHRU */
+    case EXIT_PC_UPDATED:
+        if (singlestep && dc.cc_op != CC_OP_DYNAMIC) {
+            gen_op_calc_cc(&dc);
+        } else {
+            /* Next TB starts off with CC_OP_DYNAMIC,
+               so make sure the cc op type is in env */
+            gen_op_set_cc_op(&dc);
+        }
+        if (env->singlestep_enabled) {
+            gen_debug(&dc);
+        } else {
+            /* Generate the return instruction */
+            tcg_gen_exit_tb(0);
+        }
+        break;
+    default:
+        abort();
     }
+
     gen_icount_end(tb, num_insns);
     *gen_opc_ptr = INDEX_op_end;
     if (search_pc) {
@@ -5243,6 +5628,7 @@ static inline void gen_intermediate_code_internal(CPUS390XState *env,
         tb->size = dc.pc - pc_start;
         tb->icount = num_insns;
     }
+
 #if defined(S390X_DEBUG_DISAS)
     if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
         qemu_log("IN: %s\n", lookup_symbol(pc_start));
