]> Git Repo - qemu.git/blobdiff - target/hppa/translate.c
Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging
[qemu.git] / target / hppa / translate.c
index 822d93fb2352ebaac667fac5ed27ca0dce3846a9..188fe688cbeca2179e81bd6ff8c0d84f76572355 100644 (file)
@@ -279,7 +279,7 @@ typedef struct DisasContext {
 } DisasContext;
 
 /* Note that ssm/rsm instructions number PSW_W and PSW_E differently.  */
-static int expand_sm_imm(int val)
+static int expand_sm_imm(DisasContext *ctx, int val)
 {
     if (val & PSW_SM_E) {
         val = (val & ~PSW_SM_E) | PSW_E;
@@ -291,18 +291,48 @@ static int expand_sm_imm(int val)
 }
 
 /* Inverted space register indicates 0 means sr0 not inferred from base.  */
-static int expand_sr3x(int val)
+static int expand_sr3x(DisasContext *ctx, int val)
 {
     return ~val;
 }
 
 /* Convert the M:A bits within a memory insn to the tri-state value
    we use for the final M.  */
-static int ma_to_m(int val)
+static int ma_to_m(DisasContext *ctx, int val)
 {
     return val & 2 ? (val & 1 ? -1 : 1) : 0;
 }
 
+/* Convert the sign of the displacement to a pre or post-modify.  */
+static int pos_to_m(DisasContext *ctx, int val)
+{
+    return val ? 1 : -1;
+}
+
+static int neg_to_m(DisasContext *ctx, int val)
+{
+    return val ? -1 : 1;
+}
+
+/* Used for branch targets and fp memory ops.  */
+static int expand_shl2(DisasContext *ctx, int val)
+{
+    return val << 2;
+}
+
+/* Used for fp memory ops.  */
+static int expand_shl3(DisasContext *ctx, int val)
+{
+    return val << 3;
+}
+
+/* Used for assemble_21.  */
+static int expand_shl11(DisasContext *ctx, int val)
+{
+    return val << 11;
+}
+
+
 /* Include the auto-generated decoder.  */
 #include "decode.inc.c"
 
@@ -317,21 +347,7 @@ static int ma_to_m(int val)
 /* Similarly, but we want to return to the main loop immediately
    to recognize unmasked interrupts.  */
 #define DISAS_IAQ_N_STALE_EXIT      DISAS_TARGET_2
-
-typedef struct DisasInsn {
-    uint32_t insn, mask;
-    bool (*trans)(DisasContext *ctx, uint32_t insn,
-                  const struct DisasInsn *f);
-    union {
-        void (*ttt)(TCGv_reg, TCGv_reg, TCGv_reg);
-        void (*weww)(TCGv_i32, TCGv_env, TCGv_i32, TCGv_i32);
-        void (*dedd)(TCGv_i64, TCGv_env, TCGv_i64, TCGv_i64);
-        void (*wew)(TCGv_i32, TCGv_env, TCGv_i32);
-        void (*ded)(TCGv_i64, TCGv_env, TCGv_i64);
-        void (*wed)(TCGv_i32, TCGv_env, TCGv_i64);
-        void (*dew)(TCGv_i64, TCGv_env, TCGv_i32);
-    } f;
-} DisasInsn;
+#define DISAS_EXIT                  DISAS_TARGET_3
 
 /* global register indexes */
 static TCGv_reg cpu_gr[32];
@@ -417,6 +433,15 @@ static DisasCond cond_make_f(void)
     };
 }
 
+static DisasCond cond_make_t(void)
+{
+    return (DisasCond){
+        .c = TCG_COND_ALWAYS,
+        .a0 = NULL,
+        .a1 = NULL,
+    };
+}
+
 static DisasCond cond_make_n(void)
 {
     return (DisasCond){
@@ -428,15 +453,19 @@ static DisasCond cond_make_n(void)
     };
 }
 
-static DisasCond cond_make_0(TCGCond c, TCGv_reg a0)
+static DisasCond cond_make_0_tmp(TCGCond c, TCGv_reg a0)
 {
-    DisasCond r = { .c = c, .a1 = NULL, .a1_is_0 = true };
-
     assert (c != TCG_COND_NEVER && c != TCG_COND_ALWAYS);
-    r.a0 = tcg_temp_new();
-    tcg_gen_mov_reg(r.a0, a0);
+    return (DisasCond){
+        .c = c, .a0 = a0, .a1_is_0 = true
+    };
+}
 
-    return r;
+static DisasCond cond_make_0(TCGCond c, TCGv_reg a0)
+{
+    TCGv_reg tmp = tcg_temp_new();
+    tcg_gen_mov_reg(tmp, a0);
+    return cond_make_0_tmp(c, tmp);
 }
 
 static DisasCond cond_make(TCGCond c, TCGv_reg a0, TCGv_reg a1)
@@ -788,12 +817,10 @@ static bool gen_illegal(DisasContext *ctx)
 
 static bool use_goto_tb(DisasContext *ctx, target_ureg dest)
 {
-    /* Suppress goto_tb in the case of single-steping and IO.  */
-    if ((tb_cflags(ctx->base.tb) & CF_LAST_IO)
-        || ctx->base.singlestep_enabled) {
-        return false;
-    }
-    return true;
+    /* Suppress goto_tb for page crossing, IO, or single-steping.  */
+    return !(((ctx->base.pc_first ^ dest) & TARGET_PAGE_MASK)
+             || (tb_cflags(ctx->base.tb) & CF_LAST_IO)
+             || ctx->base.singlestep_enabled);
 }
 
 /* If the next insn is to be nullified, and it's on the same page,
@@ -825,111 +852,20 @@ static void gen_goto_tb(DisasContext *ctx, int which,
     }
 }
 
-/* PA has a habit of taking the LSB of a field and using that as the sign,
-   with the rest of the field becoming the least significant bits.  */
-static target_sreg low_sextract(uint32_t val, int pos, int len)
-{
-    target_ureg x = -(target_ureg)extract32(val, pos, 1);
-    x = (x << (len - 1)) | extract32(val, pos + 1, len - 1);
-    return x;
-}
-
-static unsigned assemble_rt64(uint32_t insn)
-{
-    unsigned r1 = extract32(insn, 6, 1);
-    unsigned r0 = extract32(insn, 0, 5);
-    return r1 * 32 + r0;
-}
-
-static unsigned assemble_ra64(uint32_t insn)
-{
-    unsigned r1 = extract32(insn, 7, 1);
-    unsigned r0 = extract32(insn, 21, 5);
-    return r1 * 32 + r0;
-}
-
-static unsigned assemble_rb64(uint32_t insn)
-{
-    unsigned r1 = extract32(insn, 12, 1);
-    unsigned r0 = extract32(insn, 16, 5);
-    return r1 * 32 + r0;
-}
-
-static unsigned assemble_rc64(uint32_t insn)
-{
-    unsigned r2 = extract32(insn, 8, 1);
-    unsigned r1 = extract32(insn, 13, 3);
-    unsigned r0 = extract32(insn, 9, 2);
-    return r2 * 32 + r1 * 4 + r0;
-}
-
-static inline unsigned assemble_sr3(uint32_t insn)
-{
-    unsigned s2 = extract32(insn, 13, 1);
-    unsigned s0 = extract32(insn, 14, 2);
-    return s2 * 4 + s0;
-}
-
-static target_sreg assemble_12(uint32_t insn)
-{
-    target_ureg x = -(target_ureg)(insn & 1);
-    x = (x <<  1) | extract32(insn, 2, 1);
-    x = (x << 10) | extract32(insn, 3, 10);
-    return x;
-}
-
-static target_sreg assemble_16(uint32_t insn)
-{
-    /* Take the name from PA2.0, which produces a 16-bit number
-       only with wide mode; otherwise a 14-bit number.  Since we don't
-       implement wide mode, this is always the 14-bit number.  */
-    return low_sextract(insn, 0, 14);
-}
-
-static target_sreg assemble_16a(uint32_t insn)
-{
-    /* Take the name from PA2.0, which produces a 14-bit shifted number
-       only with wide mode; otherwise a 12-bit shifted number.  Since we
-       don't implement wide mode, this is always the 12-bit number.  */
-    target_ureg x = -(target_ureg)(insn & 1);
-    x = (x << 11) | extract32(insn, 2, 11);
-    return x << 2;
-}
-
-static target_sreg assemble_17(uint32_t insn)
+static bool cond_need_sv(int c)
 {
-    target_ureg x = -(target_ureg)(insn & 1);
-    x = (x <<  5) | extract32(insn, 16, 5);
-    x = (x <<  1) | extract32(insn, 2, 1);
-    x = (x << 10) | extract32(insn, 3, 10);
-    return x << 2;
+    return c == 2 || c == 3 || c == 6;
 }
 
-static target_sreg assemble_21(uint32_t insn)
+static bool cond_need_cb(int c)
 {
-    target_ureg x = -(target_ureg)(insn & 1);
-    x = (x << 11) | extract32(insn, 1, 11);
-    x = (x <<  2) | extract32(insn, 14, 2);
-    x = (x <<  5) | extract32(insn, 16, 5);
-    x = (x <<  2) | extract32(insn, 12, 2);
-    return x << 11;
+    return c == 4 || c == 5;
 }
 
-static target_sreg assemble_22(uint32_t insn)
-{
-    target_ureg x = -(target_ureg)(insn & 1);
-    x = (x << 10) | extract32(insn, 16, 10);
-    x = (x <<  1) | extract32(insn, 2, 1);
-    x = (x << 10) | extract32(insn, 3, 10);
-    return x << 2;
-}
-
-/* The parisc documentation describes only the general interpretation of
-   the conditions, without describing their exact implementation.  The
-   interpretations do not stand up well when considering ADD,C and SUB,B.
-   However, considering the Addition, Subtraction and Logical conditions
-   as a whole it would appear that these relations are similar to what
-   a traditional NZCV set of flags would produce.  */
+/*
+ * Compute conditional for arithmetic.  See Page 5-3, Table 5-1, of
+ * the Parisc 1.1 Architecture Reference Manual for details.
+ */
 
 static DisasCond do_cond(unsigned cf, TCGv_reg res,
                          TCGv_reg cb_msb, TCGv_reg sv)
@@ -938,17 +874,32 @@ static DisasCond do_cond(unsigned cf, TCGv_reg res,
     TCGv_reg tmp;
 
     switch (cf >> 1) {
-    case 0: /* Never / TR */
+    case 0: /* Never / TR    (0 / 1) */
         cond = cond_make_f();
         break;
     case 1: /* = / <>        (Z / !Z) */
         cond = cond_make_0(TCG_COND_EQ, res);
         break;
-    case 2: /* < / >=        (N / !N) */
-        cond = cond_make_0(TCG_COND_LT, res);
+    case 2: /* < / >=        (N ^ V / !(N ^ V) */
+        tmp = tcg_temp_new();
+        tcg_gen_xor_reg(tmp, res, sv);
+        cond = cond_make_0_tmp(TCG_COND_LT, tmp);
         break;
-    case 3: /* <= / >        (N | Z / !N & !Z) */
-        cond = cond_make_0(TCG_COND_LE, res);
+    case 3: /* <= / >        (N ^ V) | Z / !((N ^ V) | Z) */
+        /*
+         * Simplify:
+         *   (N ^ V) | Z
+         *   ((res < 0) ^ (sv < 0)) | !res
+         *   ((res ^ sv) < 0) | !res
+         *   (~(res ^ sv) >= 0) | !res
+         *   !(~(res ^ sv) >> 31) | !res
+         *   !(~(res ^ sv) >> 31 & res)
+         */
+        tmp = tcg_temp_new();
+        tcg_gen_eqv_reg(tmp, res, sv);
+        tcg_gen_sari_reg(tmp, tmp, TARGET_REGISTER_BITS - 1);
+        tcg_gen_and_reg(tmp, tmp, res);
+        cond = cond_make_0_tmp(TCG_COND_EQ, tmp);
         break;
     case 4: /* NUV / UV      (!C / C) */
         cond = cond_make_0(TCG_COND_EQ, cb_msb);
@@ -957,8 +908,7 @@ static DisasCond do_cond(unsigned cf, TCGv_reg res,
         tmp = tcg_temp_new();
         tcg_gen_neg_reg(tmp, cb_msb);
         tcg_gen_and_reg(tmp, tmp, res);
-        cond = cond_make_0(TCG_COND_EQ, tmp);
-        tcg_temp_free(tmp);
+        cond = cond_make_0_tmp(TCG_COND_EQ, tmp);
         break;
     case 6: /* SV / NSV      (V / !V) */
         cond = cond_make_0(TCG_COND_LT, sv);
@@ -966,8 +916,7 @@ static DisasCond do_cond(unsigned cf, TCGv_reg res,
     case 7: /* OD / EV */
         tmp = tcg_temp_new();
         tcg_gen_andi_reg(tmp, res, 1);
-        cond = cond_make_0(TCG_COND_NE, tmp);
-        tcg_temp_free(tmp);
+        cond = cond_make_0_tmp(TCG_COND_NE, tmp);
         break;
     default:
         g_assert_not_reached();
@@ -1005,7 +954,7 @@ static DisasCond do_sub_cond(unsigned cf, TCGv_reg res,
         cond = cond_make(TCG_COND_LEU, in1, in2);
         break;
     default:
-        return do_cond(cf, res, sv, sv);
+        return do_cond(cf, res, NULL, sv);
     }
     if (cf & 1) {
         cond.c = tcg_invert_cond(cond.c);
@@ -1014,17 +963,50 @@ static DisasCond do_sub_cond(unsigned cf, TCGv_reg res,
     return cond;
 }
 
-/* Similar, but for logicals, where the carry and overflow bits are not
-   computed, and use of them is undefined.  */
+/*
+ * Similar, but for logicals, where the carry and overflow bits are not
+ * computed, and use of them is undefined.
+ *
+ * Undefined or not, hardware does not trap.  It seems reasonable to
+ * assume hardware treats cases c={4,5,6} as if C=0 & V=0, since that's
+ * how cases c={2,3} are treated.
+ */
 
 static DisasCond do_log_cond(unsigned cf, TCGv_reg res)
 {
-    switch (cf >> 1) {
-    case 4: case 5: case 6:
-        cf &= 1;
-        break;
+    switch (cf) {
+    case 0:  /* never */
+    case 9:  /* undef, C */
+    case 11: /* undef, C & !Z */
+    case 12: /* undef, V */
+        return cond_make_f();
+
+    case 1:  /* true */
+    case 8:  /* undef, !C */
+    case 10: /* undef, !C | Z */
+    case 13: /* undef, !V */
+        return cond_make_t();
+
+    case 2:  /* == */
+        return cond_make_0(TCG_COND_EQ, res);
+    case 3:  /* <> */
+        return cond_make_0(TCG_COND_NE, res);
+    case 4:  /* < */
+        return cond_make_0(TCG_COND_LT, res);
+    case 5:  /* >= */
+        return cond_make_0(TCG_COND_GE, res);
+    case 6:  /* <= */
+        return cond_make_0(TCG_COND_LE, res);
+    case 7:  /* > */
+        return cond_make_0(TCG_COND_GT, res);
+
+    case 14: /* OD */
+    case 15: /* EV */
+        return do_cond(cf, res, NULL, NULL);
+
+    default:
+        g_assert_not_reached();
     }
-    return do_cond(cf, res, res, res);
 }
 
 /* Similar, but for shift/extract/deposit conditions.  */
@@ -1171,7 +1153,7 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1,
         in1 = tmp;
     }
 
-    if (!is_l || c == 4 || c == 5) {
+    if (!is_l || cond_need_cb(c)) {
         TCGv_reg zero = tcg_const_reg(0);
         cb_msb = get_temp(ctx);
         tcg_gen_add2_reg(dest, cb_msb, in1, zero, in2, zero);
@@ -1193,7 +1175,7 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1,
 
     /* Compute signed overflow if required.  */
     sv = NULL;
-    if (is_tsv || c == 6) {
+    if (is_tsv || cond_need_sv(c)) {
         sv = do_add_sv(ctx, dest, in1, in2);
         if (is_tsv) {
             /* ??? Need to include overflow from shift.  */
@@ -1238,6 +1220,20 @@ static bool do_add_reg(DisasContext *ctx, arg_rrr_cf_sh *a,
     return nullify_end(ctx);
 }
 
+static bool do_add_imm(DisasContext *ctx, arg_rri_cf *a,
+                       bool is_tsv, bool is_tc)
+{
+    TCGv_reg tcg_im, tcg_r2;
+
+    if (a->cf) {
+        nullify_over(ctx);
+    }
+    tcg_im = load_const(ctx, a->i);
+    tcg_r2 = load_gpr(ctx, a->r);
+    do_add(ctx, a->t, tcg_im, tcg_r2, 0, 0, is_tsv, is_tc, 0, a->cf);
+    return nullify_end(ctx);
+}
+
 static void do_sub(DisasContext *ctx, unsigned rt, TCGv_reg in1,
                    TCGv_reg in2, bool is_tsv, bool is_b,
                    bool is_tc, unsigned cf)
@@ -1270,7 +1266,7 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_reg in1,
 
     /* Compute signed overflow if required.  */
     sv = NULL;
-    if (is_tsv || c == 6) {
+    if (is_tsv || cond_need_sv(c)) {
         sv = do_sub_sv(ctx, dest, in1, in2);
         if (is_tsv) {
             gen_helper_tsv(cpu_env, sv);
@@ -1318,6 +1314,19 @@ static bool do_sub_reg(DisasContext *ctx, arg_rrr_cf *a,
     return nullify_end(ctx);
 }
 
+static bool do_sub_imm(DisasContext *ctx, arg_rri_cf *a, bool is_tsv)
+{
+    TCGv_reg tcg_im, tcg_r2;
+
+    if (a->cf) {
+        nullify_over(ctx);
+    }
+    tcg_im = load_const(ctx, a->i);
+    tcg_r2 = load_gpr(ctx, a->r);
+    do_sub(ctx, a->t, tcg_im, tcg_r2, is_tsv, 0, 0, a->cf);
+    return nullify_end(ctx);
+}
+
 static void do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_reg in1,
                       TCGv_reg in2, unsigned cf)
 {
@@ -1329,7 +1338,7 @@ static void do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_reg in1,
 
     /* Compute signed overflow if required.  */
     sv = NULL;
-    if ((cf >> 1) == 6) {
+    if (cond_need_sv(cf >> 1)) {
         sv = do_sub_sv(ctx, dest, in1, in2);
     }
 
@@ -1590,7 +1599,7 @@ static bool do_load(DisasContext *ctx, unsigned rt, unsigned rb,
     return nullify_end(ctx);
 }
 
-static void do_floadw(DisasContext *ctx, unsigned rt, unsigned rb,
+static bool do_floadw(DisasContext *ctx, unsigned rt, unsigned rb,
                       unsigned rx, int scale, target_sreg disp,
                       unsigned sp, int modify)
 {
@@ -1607,10 +1616,16 @@ static void do_floadw(DisasContext *ctx, unsigned rt, unsigned rb,
         gen_helper_loaded_fr0(cpu_env);
     }
 
-    nullify_end(ctx);
+    return nullify_end(ctx);
+}
+
+static bool trans_fldw(DisasContext *ctx, arg_ldst *a)
+{
+    return do_floadw(ctx, a->t, a->b, a->x, a->scale ? 2 : 0,
+                     a->disp, a->sp, a->m);
 }
 
-static void do_floadd(DisasContext *ctx, unsigned rt, unsigned rb,
+static bool do_floadd(DisasContext *ctx, unsigned rt, unsigned rb,
                       unsigned rx, int scale, target_sreg disp,
                       unsigned sp, int modify)
 {
@@ -1627,7 +1642,13 @@ static void do_floadd(DisasContext *ctx, unsigned rt, unsigned rb,
         gen_helper_loaded_fr0(cpu_env);
     }
 
-    nullify_end(ctx);
+    return nullify_end(ctx);
+}
+
+static bool trans_fldd(DisasContext *ctx, arg_ldst *a)
+{
+    return do_floadd(ctx, a->t, a->b, a->x, a->scale ? 3 : 0,
+                     a->disp, a->sp, a->m);
 }
 
 static bool do_store(DisasContext *ctx, unsigned rt, unsigned rb,
@@ -1639,7 +1660,7 @@ static bool do_store(DisasContext *ctx, unsigned rt, unsigned rb,
     return nullify_end(ctx);
 }
 
-static void do_fstorew(DisasContext *ctx, unsigned rt, unsigned rb,
+static bool do_fstorew(DisasContext *ctx, unsigned rt, unsigned rb,
                        unsigned rx, int scale, target_sreg disp,
                        unsigned sp, int modify)
 {
@@ -1651,10 +1672,16 @@ static void do_fstorew(DisasContext *ctx, unsigned rt, unsigned rb,
     do_store_32(ctx, tmp, rb, rx, scale, disp, sp, modify, MO_TEUL);
     tcg_temp_free_i32(tmp);
 
-    nullify_end(ctx);
+    return nullify_end(ctx);
+}
+
+static bool trans_fstw(DisasContext *ctx, arg_ldst *a)
+{
+    return do_fstorew(ctx, a->t, a->b, a->x, a->scale ? 2 : 0,
+                      a->disp, a->sp, a->m);
 }
 
-static void do_fstored(DisasContext *ctx, unsigned rt, unsigned rb,
+static bool do_fstored(DisasContext *ctx, unsigned rt, unsigned rb,
                        unsigned rx, int scale, target_sreg disp,
                        unsigned sp, int modify)
 {
@@ -1666,10 +1693,16 @@ static void do_fstored(DisasContext *ctx, unsigned rt, unsigned rb,
     do_store_64(ctx, tmp, rb, rx, scale, disp, sp, modify, MO_TEQ);
     tcg_temp_free_i64(tmp);
 
-    nullify_end(ctx);
+    return nullify_end(ctx);
+}
+
+static bool trans_fstd(DisasContext *ctx, arg_ldst *a)
+{
+    return do_fstored(ctx, a->t, a->b, a->x, a->scale ? 3 : 0,
+                      a->disp, a->sp, a->m);
 }
 
-static void do_fop_wew(DisasContext *ctx, unsigned rt, unsigned ra,
+static bool do_fop_wew(DisasContext *ctx, unsigned rt, unsigned ra,
                        void (*func)(TCGv_i32, TCGv_env, TCGv_i32))
 {
     TCGv_i32 tmp;
@@ -1681,10 +1714,10 @@ static void do_fop_wew(DisasContext *ctx, unsigned rt, unsigned ra,
 
     save_frw_i32(rt, tmp);
     tcg_temp_free_i32(tmp);
-    nullify_end(ctx);
+    return nullify_end(ctx);
 }
 
-static void do_fop_wed(DisasContext *ctx, unsigned rt, unsigned ra,
+static bool do_fop_wed(DisasContext *ctx, unsigned rt, unsigned ra,
                        void (*func)(TCGv_i32, TCGv_env, TCGv_i64))
 {
     TCGv_i32 dst;
@@ -1699,10 +1732,10 @@ static void do_fop_wed(DisasContext *ctx, unsigned rt, unsigned ra,
     tcg_temp_free_i64(src);
     save_frw_i32(rt, dst);
     tcg_temp_free_i32(dst);
-    nullify_end(ctx);
+    return nullify_end(ctx);
 }
 
-static void do_fop_ded(DisasContext *ctx, unsigned rt, unsigned ra,
+static bool do_fop_ded(DisasContext *ctx, unsigned rt, unsigned ra,
                        void (*func)(TCGv_i64, TCGv_env, TCGv_i64))
 {
     TCGv_i64 tmp;
@@ -1714,10 +1747,10 @@ static void do_fop_ded(DisasContext *ctx, unsigned rt, unsigned ra,
 
     save_frd(rt, tmp);
     tcg_temp_free_i64(tmp);
-    nullify_end(ctx);
+    return nullify_end(ctx);
 }
 
-static void do_fop_dew(DisasContext *ctx, unsigned rt, unsigned ra,
+static bool do_fop_dew(DisasContext *ctx, unsigned rt, unsigned ra,
                        void (*func)(TCGv_i64, TCGv_env, TCGv_i32))
 {
     TCGv_i32 src;
@@ -1732,10 +1765,10 @@ static void do_fop_dew(DisasContext *ctx, unsigned rt, unsigned ra,
     tcg_temp_free_i32(src);
     save_frd(rt, dst);
     tcg_temp_free_i64(dst);
-    nullify_end(ctx);
+    return nullify_end(ctx);
 }
 
-static void do_fop_weww(DisasContext *ctx, unsigned rt,
+static bool do_fop_weww(DisasContext *ctx, unsigned rt,
                         unsigned ra, unsigned rb,
                         void (*func)(TCGv_i32, TCGv_env, TCGv_i32, TCGv_i32))
 {
@@ -1750,10 +1783,10 @@ static void do_fop_weww(DisasContext *ctx, unsigned rt,
     tcg_temp_free_i32(b);
     save_frw_i32(rt, a);
     tcg_temp_free_i32(a);
-    nullify_end(ctx);
+    return nullify_end(ctx);
 }
 
-static void do_fop_dedd(DisasContext *ctx, unsigned rt,
+static bool do_fop_dedd(DisasContext *ctx, unsigned rt,
                         unsigned ra, unsigned rb,
                         void (*func)(TCGv_i64, TCGv_env, TCGv_i64, TCGv_i64))
 {
@@ -1768,12 +1801,12 @@ static void do_fop_dedd(DisasContext *ctx, unsigned rt,
     tcg_temp_free_i64(b);
     save_frd(rt, a);
     tcg_temp_free_i64(a);
-    nullify_end(ctx);
+    return nullify_end(ctx);
 }
 
 /* Emit an unconditional branch to a direct target, which may or may not
    have already had nullification handled.  */
-static void do_dbranch(DisasContext *ctx, target_ureg dest,
+static bool do_dbranch(DisasContext *ctx, target_ureg dest,
                        unsigned link, bool is_n)
 {
     if (ctx->null_cond.c == TCG_COND_NEVER && ctx->null_lab == NULL) {
@@ -1805,11 +1838,12 @@ static void do_dbranch(DisasContext *ctx, target_ureg dest,
         gen_goto_tb(ctx, 1, ctx->iaoq_b, ctx->iaoq_n);
         ctx->base.is_jmp = DISAS_NORETURN;
     }
+    return true;
 }
 
 /* Emit a conditional branch to a direct target.  If the branch itself
    is nullified, we should have already used nullify_over.  */
-static void do_cbranch(DisasContext *ctx, target_sreg disp, bool is_n,
+static bool do_cbranch(DisasContext *ctx, target_sreg disp, bool is_n,
                        DisasCond *cond)
 {
     target_ureg dest = iaoq_dest(ctx, disp);
@@ -1821,12 +1855,10 @@ static void do_cbranch(DisasContext *ctx, target_sreg disp, bool is_n,
 
     /* Handle TRUE and NEVER as direct branches.  */
     if (c == TCG_COND_ALWAYS) {
-        do_dbranch(ctx, dest, 0, is_n && disp >= 0);
-        return;
+        return do_dbranch(ctx, dest, 0, is_n && disp >= 0);
     }
     if (c == TCG_COND_NEVER) {
-        do_dbranch(ctx, ctx->iaoq_n, 0, is_n && disp < 0);
-        return;
+        return do_dbranch(ctx, ctx->iaoq_n, 0, is_n && disp < 0);
     }
 
     taken = gen_new_label();
@@ -1873,11 +1905,12 @@ static void do_cbranch(DisasContext *ctx, target_sreg disp, bool is_n,
     } else {
         ctx->base.is_jmp = DISAS_NORETURN;
     }
+    return true;
 }
 
 /* Emit an unconditional branch to an indirect target.  This handles
    nullification of the branch itself.  */
-static void do_ibranch(DisasContext *ctx, TCGv_reg dest,
+static bool do_ibranch(DisasContext *ctx, TCGv_reg dest,
                        unsigned link, bool is_n)
 {
     TCGv_reg a0, a1, next, tmp;
@@ -1897,7 +1930,7 @@ static void do_ibranch(DisasContext *ctx, TCGv_reg dest,
                 tcg_gen_addi_reg(cpu_iaoq_b, next, 4);
                 nullify_set(ctx, 0);
                 ctx->base.is_jmp = DISAS_IAQ_N_UPDATED;
-                return;
+                return true;
             }
             ctx->null_cond.c = TCG_COND_ALWAYS;
         }
@@ -1924,7 +1957,7 @@ static void do_ibranch(DisasContext *ctx, TCGv_reg dest,
             tcg_gen_movi_reg(cpu_gr[link], ctx->iaoq_n);
         }
         tcg_gen_lookup_and_goto_ptr();
-        nullify_end(ctx);
+        return nullify_end(ctx);
     } else {
         cond_prep(&ctx->null_cond);
         c = ctx->null_cond.c;
@@ -1955,6 +1988,7 @@ static void do_ibranch(DisasContext *ctx, TCGv_reg dest,
             cond_free(&ctx->null_cond);
         }
     }
+    return true;
 }
 
 /* Implement
@@ -1972,16 +2006,15 @@ static TCGv_reg do_ibranch_priv(DisasContext *ctx, TCGv_reg offset)
         /* Privilege 0 is maximum and is allowed to decrease.  */
         return offset;
     case 3:
-        /* Privilege 3 is minimum and is never allowed increase.  */
+        /* Privilege 3 is minimum and is never allowed to increase.  */
         dest = get_temp(ctx);
         tcg_gen_ori_reg(dest, offset, 3);
         break;
     default:
-        dest = tcg_temp_new();
+        dest = get_temp(ctx);
         tcg_gen_andi_reg(dest, offset, -4);
         tcg_gen_ori_reg(dest, dest, ctx->privilege);
         tcg_gen_movcond_reg(TCG_COND_GTU, dest, dest, offset, dest, offset);
-        tcg_temp_free(dest);
         break;
     }
     return dest;
@@ -2224,6 +2257,16 @@ static bool trans_mtctl(DisasContext *ctx, arg_mtctl *a)
                        offsetof(CPUHPPAState, cr_back[ctl - CR_IIASQ]));
         break;
 
+    case CR_PID1:
+    case CR_PID2:
+    case CR_PID3:
+    case CR_PID4:
+        tcg_gen_st_reg(reg, cpu_env, offsetof(CPUHPPAState, cr[ctl]));
+#ifndef CONFIG_USER_ONLY
+        gen_helper_change_prot_id(cpu_env);
+#endif
+        break;
+
     default:
         tcg_gen_st_reg(reg, cpu_env, offsetof(CPUHPPAState, cr[ctl]));
         break;
@@ -2357,20 +2400,27 @@ static bool trans_rfi_r(DisasContext *ctx, arg_rfi_r *a)
     return do_rfi(ctx, true);
 }
 
+static bool trans_halt(DisasContext *ctx, arg_halt *a)
+{
+    CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
 #ifndef CONFIG_USER_ONLY
-static bool gen_hlt(DisasContext *ctx, int reset)
+    nullify_over(ctx);
+    gen_helper_halt(cpu_env);
+    ctx->base.is_jmp = DISAS_NORETURN;
+    return nullify_end(ctx);
+#endif
+}
+
+static bool trans_reset(DisasContext *ctx, arg_reset *a)
 {
     CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
+#ifndef CONFIG_USER_ONLY
     nullify_over(ctx);
-    if (reset) {
-        gen_helper_reset(cpu_env);
-    } else {
-        gen_helper_halt(cpu_env);
-    }
+    gen_helper_reset(cpu_env);
     ctx->base.is_jmp = DISAS_NORETURN;
     return nullify_end(ctx);
+#endif
 }
-#endif /* !CONFIG_USER_ONLY */
 
 static bool trans_nop_addrx(DisasContext *ctx, arg_ldst *a)
 {
@@ -2433,9 +2483,8 @@ static bool trans_ixtlbx(DisasContext *ctx, arg_ixtlbx *a)
         gen_helper_itlbp(cpu_env, addr, reg);
     }
 
-    /* Exit TB for ITLB change if mmu is enabled.  This *should* not be
-       the case, since the OS TLB fill handler runs with mmu disabled.  */
-    if (!a->data && (ctx->tb_flags & PSW_C)) {
+    /* Exit TB for TLB change if mmu is enabled.  */
+    if (ctx->tb_flags & PSW_C) {
         ctx->base.is_jmp = DISAS_IAQ_N_STALE;
     }
     return nullify_end(ctx);
@@ -2462,7 +2511,61 @@ static bool trans_pxtlbx(DisasContext *ctx, arg_pxtlbx *a)
     }
 
     /* Exit TB for TLB change if mmu is enabled.  */
-    if (!a->data && (ctx->tb_flags & PSW_C)) {
+    if (ctx->tb_flags & PSW_C) {
+        ctx->base.is_jmp = DISAS_IAQ_N_STALE;
+    }
+    return nullify_end(ctx);
+#endif
+}
+
+/*
+ * Implement the pcxl and pcxl2 Fast TLB Insert instructions.
+ * See
+ *     https://parisc.wiki.kernel.org/images-parisc/a/a9/Pcxl2_ers.pdf
+ *     page 13-9 (195/206)
+ */
+static bool trans_ixtlbxf(DisasContext *ctx, arg_ixtlbxf *a)
+{
+    CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
+#ifndef CONFIG_USER_ONLY
+    TCGv_tl addr, atl, stl;
+    TCGv_reg reg;
+
+    nullify_over(ctx);
+
+    /*
+     * FIXME:
+     *  if (not (pcxl or pcxl2))
+     *    return gen_illegal(ctx);
+     *
+     * Note for future: these are 32-bit systems; no hppa64.
+     */
+
+    atl = tcg_temp_new_tl();
+    stl = tcg_temp_new_tl();
+    addr = tcg_temp_new_tl();
+
+    tcg_gen_ld32u_i64(stl, cpu_env,
+                      a->data ? offsetof(CPUHPPAState, cr[CR_ISR])
+                      : offsetof(CPUHPPAState, cr[CR_IIASQ]));
+    tcg_gen_ld32u_i64(atl, cpu_env,
+                      a->data ? offsetof(CPUHPPAState, cr[CR_IOR])
+                      : offsetof(CPUHPPAState, cr[CR_IIAOQ]));
+    tcg_gen_shli_i64(stl, stl, 32);
+    tcg_gen_or_tl(addr, atl, stl);
+    tcg_temp_free_tl(atl);
+    tcg_temp_free_tl(stl);
+
+    reg = load_gpr(ctx, a->r);
+    if (a->addr) {
+        gen_helper_itlba(cpu_env, addr, reg);
+    } else {
+        gen_helper_itlbp(cpu_env, addr, reg);
+    }
+    tcg_temp_free_tl(addr);
+
+    /* Exit TB for TLB change if mmu is enabled.  */
+    if (ctx->tb_flags & PSW_C) {
         ctx->base.is_jmp = DISAS_IAQ_N_STALE;
     }
     return nullify_end(ctx);
@@ -2702,7 +2805,7 @@ static bool do_dcor(DisasContext *ctx, arg_rr_cf *a, bool is_i)
     }
     tcg_gen_andi_reg(tmp, tmp, 0x11111111);
     tcg_gen_muli_reg(tmp, tmp, 6);
-    do_unit(ctx, a->t, tmp, load_gpr(ctx, a->r), a->cf, false,
+    do_unit(ctx, a->t, load_gpr(ctx, a->r), tmp, a->cf, false,
             is_i ? tcg_gen_add_reg : tcg_gen_sub_reg);
     return nullify_end(ctx);
 }
@@ -2764,7 +2867,7 @@ static bool trans_ds(DisasContext *ctx, arg_rrr_cf *a)
     /* Install the new nullification.  */
     if (a->cf) {
         TCGv_reg sv = NULL;
-        if (a->cf >> 1 == 6) {
+        if (cond_need_sv(a->cf >> 1)) {
             /* ??? The lshift is supposed to contribute to overflow.  */
             sv = do_add_sv(ctx, dest, add1, add2);
         }
@@ -2778,62 +2881,47 @@ static bool trans_ds(DisasContext *ctx, arg_rrr_cf *a)
     return nullify_end(ctx);
 }
 
-static bool trans_addi(DisasContext *ctx, uint32_t insn)
+static bool trans_addi(DisasContext *ctx, arg_rri_cf *a)
 {
-    target_sreg im = low_sextract(insn, 0, 11);
-    unsigned e1 = extract32(insn, 11, 1);
-    unsigned cf = extract32(insn, 12, 4);
-    unsigned rt = extract32(insn, 16, 5);
-    unsigned r2 = extract32(insn, 21, 5);
-    unsigned o1 = extract32(insn, 26, 1);
-    TCGv_reg tcg_im, tcg_r2;
-
-    if (cf) {
-        nullify_over(ctx);
-    }
-
-    tcg_im = load_const(ctx, im);
-    tcg_r2 = load_gpr(ctx, r2);
-    do_add(ctx, rt, tcg_im, tcg_r2, 0, false, e1, !o1, false, cf);
+    return do_add_imm(ctx, a, false, false);
+}
 
-    return nullify_end(ctx);
+static bool trans_addi_tsv(DisasContext *ctx, arg_rri_cf *a)
+{
+    return do_add_imm(ctx, a, true, false);
 }
 
-static bool trans_subi(DisasContext *ctx, uint32_t insn)
+static bool trans_addi_tc(DisasContext *ctx, arg_rri_cf *a)
 {
-    target_sreg im = low_sextract(insn, 0, 11);
-    unsigned e1 = extract32(insn, 11, 1);
-    unsigned cf = extract32(insn, 12, 4);
-    unsigned rt = extract32(insn, 16, 5);
-    unsigned r2 = extract32(insn, 21, 5);
-    TCGv_reg tcg_im, tcg_r2;
+    return do_add_imm(ctx, a, false, true);
+}
 
-    if (cf) {
-        nullify_over(ctx);
-    }
+static bool trans_addi_tc_tsv(DisasContext *ctx, arg_rri_cf *a)
+{
+    return do_add_imm(ctx, a, true, true);
+}
 
-    tcg_im = load_const(ctx, im);
-    tcg_r2 = load_gpr(ctx, r2);
-    do_sub(ctx, rt, tcg_im, tcg_r2, e1, false, false, cf);
+static bool trans_subi(DisasContext *ctx, arg_rri_cf *a)
+{
+    return do_sub_imm(ctx, a, false);
+}
 
-    return nullify_end(ctx);
+static bool trans_subi_tsv(DisasContext *ctx, arg_rri_cf *a)
+{
+    return do_sub_imm(ctx, a, true);
 }
 
-static bool trans_cmpiclr(DisasContext *ctx, uint32_t insn)
+static bool trans_cmpiclr(DisasContext *ctx, arg_rri_cf *a)
 {
-    target_sreg im = low_sextract(insn, 0, 11);
-    unsigned cf = extract32(insn, 12, 4);
-    unsigned rt = extract32(insn, 16, 5);
-    unsigned r2 = extract32(insn, 21, 5);
     TCGv_reg tcg_im, tcg_r2;
 
-    if (cf) {
+    if (a->cf) {
         nullify_over(ctx);
     }
 
-    tcg_im = load_const(ctx, im);
-    tcg_r2 = load_gpr(ctx, r2);
-    do_cmpclr(ctx, rt, tcg_im, tcg_r2, cf);
+    tcg_im = load_const(ctx, a->i);
+    tcg_r2 = load_gpr(ctx, a->r);
+    do_cmpclr(ctx, a->t, tcg_im, tcg_r2, a->cf);
 
     return nullify_end(ctx);
 }
@@ -2931,378 +3019,194 @@ static bool trans_sta(DisasContext *ctx, arg_ldst *a)
     return true;
 }
 
-static bool trans_ldil(DisasContext *ctx, uint32_t insn)
+static bool trans_ldil(DisasContext *ctx, arg_ldil *a)
 {
-    unsigned rt = extract32(insn, 21, 5);
-    target_sreg i = assemble_21(insn);
-    TCGv_reg tcg_rt = dest_gpr(ctx, rt);
+    TCGv_reg tcg_rt = dest_gpr(ctx, a->t);
 
-    tcg_gen_movi_reg(tcg_rt, i);
-    save_gpr(ctx, rt, tcg_rt);
+    tcg_gen_movi_reg(tcg_rt, a->i);
+    save_gpr(ctx, a->t, tcg_rt);
     cond_free(&ctx->null_cond);
     return true;
 }
 
-static bool trans_addil(DisasContext *ctx, uint32_t insn)
+static bool trans_addil(DisasContext *ctx, arg_addil *a)
 {
-    unsigned rt = extract32(insn, 21, 5);
-    target_sreg i = assemble_21(insn);
-    TCGv_reg tcg_rt = load_gpr(ctx, rt);
+    TCGv_reg tcg_rt = load_gpr(ctx, a->r);
     TCGv_reg tcg_r1 = dest_gpr(ctx, 1);
 
-    tcg_gen_addi_reg(tcg_r1, tcg_rt, i);
+    tcg_gen_addi_reg(tcg_r1, tcg_rt, a->i);
     save_gpr(ctx, 1, tcg_r1);
     cond_free(&ctx->null_cond);
     return true;
 }
 
-static bool trans_ldo(DisasContext *ctx, uint32_t insn)
+static bool trans_ldo(DisasContext *ctx, arg_ldo *a)
 {
-    unsigned rb = extract32(insn, 21, 5);
-    unsigned rt = extract32(insn, 16, 5);
-    target_sreg i = assemble_16(insn);
-    TCGv_reg tcg_rt = dest_gpr(ctx, rt);
+    TCGv_reg tcg_rt = dest_gpr(ctx, a->t);
 
     /* Special case rb == 0, for the LDI pseudo-op.
        The COPY pseudo-op is handled for free within tcg_gen_addi_tl.  */
-    if (rb == 0) {
-        tcg_gen_movi_reg(tcg_rt, i);
+    if (a->b == 0) {
+        tcg_gen_movi_reg(tcg_rt, a->i);
     } else {
-        tcg_gen_addi_reg(tcg_rt, cpu_gr[rb], i);
+        tcg_gen_addi_reg(tcg_rt, cpu_gr[a->b], a->i);
     }
-    save_gpr(ctx, rt, tcg_rt);
+    save_gpr(ctx, a->t, tcg_rt);
     cond_free(&ctx->null_cond);
     return true;
 }
 
-static bool trans_load(DisasContext *ctx, uint32_t insn,
-                       bool is_mod, TCGMemOp mop)
+static bool do_cmpb(DisasContext *ctx, unsigned r, TCGv_reg in1,
+                    unsigned c, unsigned f, unsigned n, int disp)
 {
-    unsigned rb = extract32(insn, 21, 5);
-    unsigned rt = extract32(insn, 16, 5);
-    unsigned sp = extract32(insn, 14, 2);
-    target_sreg i = assemble_16(insn);
+    TCGv_reg dest, in2, sv;
+    DisasCond cond;
 
-    do_load(ctx, rt, rb, 0, 0, i, sp, is_mod ? (i < 0 ? -1 : 1) : 0, mop);
-    return true;
-}
+    in2 = load_gpr(ctx, r);
+    dest = get_temp(ctx);
 
-static bool trans_load_w(DisasContext *ctx, uint32_t insn)
-{
-    unsigned rb = extract32(insn, 21, 5);
-    unsigned rt = extract32(insn, 16, 5);
-    unsigned sp = extract32(insn, 14, 2);
-    target_sreg i = assemble_16a(insn);
-    unsigned ext2 = extract32(insn, 1, 2);
+    tcg_gen_sub_reg(dest, in1, in2);
 
-    switch (ext2) {
-    case 0:
-    case 1:
-        /* FLDW without modification.  */
-        do_floadw(ctx, ext2 * 32 + rt, rb, 0, 0, i, sp, 0);
-        break;
-    case 2:
-        /* LDW with modification.  Note that the sign of I selects
-           post-dec vs pre-inc.  */
-        do_load(ctx, rt, rb, 0, 0, i, sp, (i < 0 ? 1 : -1), MO_TEUL);
-        break;
-    default:
-        return gen_illegal(ctx);
+    sv = NULL;
+    if (cond_need_sv(c)) {
+        sv = do_sub_sv(ctx, dest, in1, in2);
     }
-    return true;
+
+    cond = do_sub_cond(c * 2 + f, dest, in1, in2, sv);
+    return do_cbranch(ctx, disp, n, &cond);
 }
 
-static bool trans_fload_mod(DisasContext *ctx, uint32_t insn)
+static bool trans_cmpb(DisasContext *ctx, arg_cmpb *a)
 {
-    target_sreg i = assemble_16a(insn);
-    unsigned t1 = extract32(insn, 1, 1);
-    unsigned a = extract32(insn, 2, 1);
-    unsigned sp = extract32(insn, 14, 2);
-    unsigned t0 = extract32(insn, 16, 5);
-    unsigned rb = extract32(insn, 21, 5);
-
-    /* FLDW with modification.  */
-    do_floadw(ctx, t1 * 32 + t0, rb, 0, 0, i, sp, (a ? -1 : 1));
-    return true;
+    nullify_over(ctx);
+    return do_cmpb(ctx, a->r2, load_gpr(ctx, a->r1), a->c, a->f, a->n, a->disp);
 }
 
-static bool trans_store(DisasContext *ctx, uint32_t insn,
-                        bool is_mod, TCGMemOp mop)
+static bool trans_cmpbi(DisasContext *ctx, arg_cmpbi *a)
 {
-    unsigned rb = extract32(insn, 21, 5);
-    unsigned rt = extract32(insn, 16, 5);
-    unsigned sp = extract32(insn, 14, 2);
-    target_sreg i = assemble_16(insn);
-
-    do_store(ctx, rt, rb, i, sp, is_mod ? (i < 0 ? -1 : 1) : 0, mop);
-    return true;
+    nullify_over(ctx);
+    return do_cmpb(ctx, a->r, load_const(ctx, a->i), a->c, a->f, a->n, a->disp);
 }
 
-static bool trans_store_w(DisasContext *ctx, uint32_t insn)
+static bool do_addb(DisasContext *ctx, unsigned r, TCGv_reg in1,
+                    unsigned c, unsigned f, unsigned n, int disp)
 {
-    unsigned rb = extract32(insn, 21, 5);
-    unsigned rt = extract32(insn, 16, 5);
-    unsigned sp = extract32(insn, 14, 2);
-    target_sreg i = assemble_16a(insn);
-    unsigned ext2 = extract32(insn, 1, 2);
+    TCGv_reg dest, in2, sv, cb_msb;
+    DisasCond cond;
 
-    switch (ext2) {
-    case 0:
-    case 1:
-        /* FSTW without modification.  */
-        do_fstorew(ctx, ext2 * 32 + rt, rb, 0, 0, i, sp, 0);
-        break;
-    case 2:
-        /* STW with modification.  */
-        do_store(ctx, rt, rb, i, sp, (i < 0 ? 1 : -1), MO_TEUL);
-        break;
-    default:
-        return gen_illegal(ctx);
+    in2 = load_gpr(ctx, r);
+    dest = tcg_temp_new();
+    sv = NULL;
+    cb_msb = NULL;
+
+    if (cond_need_cb(c)) {
+        cb_msb = get_temp(ctx);
+        tcg_gen_movi_reg(cb_msb, 0);
+        tcg_gen_add2_reg(dest, cb_msb, in1, cb_msb, in2, cb_msb);
+    } else {
+        tcg_gen_add_reg(dest, in1, in2);
     }
-    return true;
+    if (cond_need_sv(c)) {
+        sv = do_add_sv(ctx, dest, in1, in2);
+    }
+
+    cond = do_cond(c * 2 + f, dest, cb_msb, sv);
+    save_gpr(ctx, r, dest);
+    tcg_temp_free(dest);
+    return do_cbranch(ctx, disp, n, &cond);
 }
 
-static bool trans_fstore_mod(DisasContext *ctx, uint32_t insn)
+static bool trans_addb(DisasContext *ctx, arg_addb *a)
 {
-    target_sreg i = assemble_16a(insn);
-    unsigned t1 = extract32(insn, 1, 1);
-    unsigned a = extract32(insn, 2, 1);
-    unsigned sp = extract32(insn, 14, 2);
-    unsigned t0 = extract32(insn, 16, 5);
-    unsigned rb = extract32(insn, 21, 5);
+    nullify_over(ctx);
+    return do_addb(ctx, a->r2, load_gpr(ctx, a->r1), a->c, a->f, a->n, a->disp);
+}
 
-    /* FSTW with modification.  */
-    do_fstorew(ctx, t1 * 32 + t0, rb, 0, 0, i, sp, (a ? -1 : 1));
-    return true;
+static bool trans_addbi(DisasContext *ctx, arg_addbi *a)
+{
+    nullify_over(ctx);
+    return do_addb(ctx, a->r, load_const(ctx, a->i), a->c, a->f, a->n, a->disp);
 }
 
-static bool trans_copr_w(DisasContext *ctx, uint32_t insn)
-{
-    unsigned t0 = extract32(insn, 0, 5);
-    unsigned m = extract32(insn, 5, 1);
-    unsigned t1 = extract32(insn, 6, 1);
-    unsigned ext3 = extract32(insn, 7, 3);
-    /* unsigned cc = extract32(insn, 10, 2); */
-    unsigned i = extract32(insn, 12, 1);
-    unsigned ua = extract32(insn, 13, 1);
-    unsigned sp = extract32(insn, 14, 2);
-    unsigned rx = extract32(insn, 16, 5);
-    unsigned rb = extract32(insn, 21, 5);
-    unsigned rt = t1 * 32 + t0;
-    int modify = (m ? (ua ? -1 : 1) : 0);
-    int disp, scale;
-
-    if (i == 0) {
-        scale = (ua ? 2 : 0);
-        disp = 0;
-        modify = m;
-    } else {
-        disp = low_sextract(rx, 0, 5);
-        scale = 0;
-        rx = 0;
-        modify = (m ? (ua ? -1 : 1) : 0);
-    }
-
-    switch (ext3) {
-    case 0: /* FLDW */
-        do_floadw(ctx, rt, rb, rx, scale, disp, sp, modify);
-        break;
-    case 4: /* FSTW */
-        do_fstorew(ctx, rt, rb, rx, scale, disp, sp, modify);
-        break;
-    default:
-        return gen_illegal(ctx);
-    }
-    return true;
-}
-
-static bool trans_copr_dw(DisasContext *ctx, uint32_t insn)
-{
-    unsigned rt = extract32(insn, 0, 5);
-    unsigned m = extract32(insn, 5, 1);
-    unsigned ext4 = extract32(insn, 6, 4);
-    /* unsigned cc = extract32(insn, 10, 2); */
-    unsigned i = extract32(insn, 12, 1);
-    unsigned ua = extract32(insn, 13, 1);
-    unsigned sp = extract32(insn, 14, 2);
-    unsigned rx = extract32(insn, 16, 5);
-    unsigned rb = extract32(insn, 21, 5);
-    int modify = (m ? (ua ? -1 : 1) : 0);
-    int disp, scale;
-
-    if (i == 0) {
-        scale = (ua ? 3 : 0);
-        disp = 0;
-        modify = m;
-    } else {
-        disp = low_sextract(rx, 0, 5);
-        scale = 0;
-        rx = 0;
-        modify = (m ? (ua ? -1 : 1) : 0);
-    }
-
-    switch (ext4) {
-    case 0: /* FLDD */
-        do_floadd(ctx, rt, rb, rx, scale, disp, sp, modify);
-        break;
-    case 8: /* FSTD */
-        do_fstored(ctx, rt, rb, rx, scale, disp, sp, modify);
-        break;
-    default:
-        return gen_illegal(ctx);
-    }
-    return true;
-}
-
-static bool trans_cmpb(DisasContext *ctx, uint32_t insn,
-                       bool is_true, bool is_imm, bool is_dw)
-{
-    target_sreg disp = assemble_12(insn) * 4;
-    unsigned n = extract32(insn, 1, 1);
-    unsigned c = extract32(insn, 13, 3);
-    unsigned r = extract32(insn, 21, 5);
-    unsigned cf = c * 2 + !is_true;
-    TCGv_reg dest, in1, in2, sv;
-    DisasCond cond;
+static bool trans_bb_sar(DisasContext *ctx, arg_bb_sar *a)
+{
+    TCGv_reg tmp, tcg_r;
+    DisasCond cond;
 
     nullify_over(ctx);
 
-    if (is_imm) {
-        in1 = load_const(ctx, low_sextract(insn, 16, 5));
-    } else {
-        in1 = load_gpr(ctx, extract32(insn, 16, 5));
-    }
-    in2 = load_gpr(ctx, r);
-    dest = get_temp(ctx);
-
-    tcg_gen_sub_reg(dest, in1, in2);
-
-    sv = NULL;
-    if (c == 6) {
-        sv = do_sub_sv(ctx, dest, in1, in2);
-    }
+    tmp = tcg_temp_new();
+    tcg_r = load_gpr(ctx, a->r);
+    tcg_gen_shl_reg(tmp, tcg_r, cpu_sar);
 
-    cond = do_sub_cond(cf, dest, in1, in2, sv);
-    do_cbranch(ctx, disp, n, &cond);
-    return true;
+    cond = cond_make_0(a->c ? TCG_COND_GE : TCG_COND_LT, tmp);
+    tcg_temp_free(tmp);
+    return do_cbranch(ctx, a->disp, a->n, &cond);
 }
 
-static bool trans_addb(DisasContext *ctx, uint32_t insn,
-                       bool is_true, bool is_imm)
+static bool trans_bb_imm(DisasContext *ctx, arg_bb_imm *a)
 {
-    target_sreg disp = assemble_12(insn) * 4;
-    unsigned n = extract32(insn, 1, 1);
-    unsigned c = extract32(insn, 13, 3);
-    unsigned r = extract32(insn, 21, 5);
-    unsigned cf = c * 2 + !is_true;
-    TCGv_reg dest, in1, in2, sv, cb_msb;
+    TCGv_reg tmp, tcg_r;
     DisasCond cond;
 
     nullify_over(ctx);
 
-    if (is_imm) {
-        in1 = load_const(ctx, low_sextract(insn, 16, 5));
-    } else {
-        in1 = load_gpr(ctx, extract32(insn, 16, 5));
-    }
-    in2 = load_gpr(ctx, r);
-    dest = dest_gpr(ctx, r);
-    sv = NULL;
-    cb_msb = NULL;
-
-    switch (c) {
-    default:
-        tcg_gen_add_reg(dest, in1, in2);
-        break;
-    case 4: case 5:
-        cb_msb = get_temp(ctx);
-        tcg_gen_movi_reg(cb_msb, 0);
-        tcg_gen_add2_reg(dest, cb_msb, in1, cb_msb, in2, cb_msb);
-        break;
-    case 6:
-        tcg_gen_add_reg(dest, in1, in2);
-        sv = do_add_sv(ctx, dest, in1, in2);
-        break;
-    }
+    tmp = tcg_temp_new();
+    tcg_r = load_gpr(ctx, a->r);
+    tcg_gen_shli_reg(tmp, tcg_r, a->p);
 
-    cond = do_cond(cf, dest, cb_msb, sv);
-    do_cbranch(ctx, disp, n, &cond);
-    return true;
+    cond = cond_make_0(a->c ? TCG_COND_GE : TCG_COND_LT, tmp);
+    tcg_temp_free(tmp);
+    return do_cbranch(ctx, a->disp, a->n, &cond);
 }
 
-static bool trans_bb(DisasContext *ctx, uint32_t insn)
+static bool trans_movb(DisasContext *ctx, arg_movb *a)
 {
-    target_sreg disp = assemble_12(insn) * 4;
-    unsigned n = extract32(insn, 1, 1);
-    unsigned c = extract32(insn, 15, 1);
-    unsigned r = extract32(insn, 16, 5);
-    unsigned p = extract32(insn, 21, 5);
-    unsigned i = extract32(insn, 26, 1);
-    TCGv_reg tmp, tcg_r;
+    TCGv_reg dest;
     DisasCond cond;
 
     nullify_over(ctx);
 
-    tmp = tcg_temp_new();
-    tcg_r = load_gpr(ctx, r);
-    if (i) {
-        tcg_gen_shli_reg(tmp, tcg_r, p);
+    dest = dest_gpr(ctx, a->r2);
+    if (a->r1 == 0) {
+        tcg_gen_movi_reg(dest, 0);
     } else {
-        tcg_gen_shl_reg(tmp, tcg_r, cpu_sar);
+        tcg_gen_mov_reg(dest, cpu_gr[a->r1]);
     }
 
-    cond = cond_make_0(c ? TCG_COND_GE : TCG_COND_LT, tmp);
-    tcg_temp_free(tmp);
-    do_cbranch(ctx, disp, n, &cond);
-    return true;
+    cond = do_sed_cond(a->c, dest);
+    return do_cbranch(ctx, a->disp, a->n, &cond);
 }
 
-static bool trans_movb(DisasContext *ctx, uint32_t insn, bool is_imm)
+static bool trans_movbi(DisasContext *ctx, arg_movbi *a)
 {
-    target_sreg disp = assemble_12(insn) * 4;
-    unsigned n = extract32(insn, 1, 1);
-    unsigned c = extract32(insn, 13, 3);
-    unsigned t = extract32(insn, 16, 5);
-    unsigned r = extract32(insn, 21, 5);
     TCGv_reg dest;
     DisasCond cond;
 
     nullify_over(ctx);
 
-    dest = dest_gpr(ctx, r);
-    if (is_imm) {
-        tcg_gen_movi_reg(dest, low_sextract(t, 0, 5));
-    } else if (t == 0) {
-        tcg_gen_movi_reg(dest, 0);
-    } else {
-        tcg_gen_mov_reg(dest, cpu_gr[t]);
-    }
+    dest = dest_gpr(ctx, a->r);
+    tcg_gen_movi_reg(dest, a->i);
 
-    cond = do_sed_cond(c, dest);
-    do_cbranch(ctx, disp, n, &cond);
-    return true;
+    cond = do_sed_cond(a->c, dest);
+    return do_cbranch(ctx, a->disp, a->n, &cond);
 }
 
-static bool trans_shrpw_sar(DisasContext *ctx, uint32_t insn,
-                            const DisasInsn *di)
+static bool trans_shrpw_sar(DisasContext *ctx, arg_shrpw_sar *a)
 {
-    unsigned rt = extract32(insn, 0, 5);
-    unsigned c = extract32(insn, 13, 3);
-    unsigned r1 = extract32(insn, 16, 5);
-    unsigned r2 = extract32(insn, 21, 5);
     TCGv_reg dest;
 
-    if (c) {
+    if (a->c) {
         nullify_over(ctx);
     }
 
-    dest = dest_gpr(ctx, rt);
-    if (r1 == 0) {
-        tcg_gen_ext32u_reg(dest, load_gpr(ctx, r2));
+    dest = dest_gpr(ctx, a->t);
+    if (a->r1 == 0) {
+        tcg_gen_ext32u_reg(dest, load_gpr(ctx, a->r2));
         tcg_gen_shr_reg(dest, dest, cpu_sar);
-    } else if (r1 == r2) {
+    } else if (a->r1 == a->r2) {
         TCGv_i32 t32 = tcg_temp_new_i32();
-        tcg_gen_trunc_reg_i32(t32, load_gpr(ctx, r2));
+        tcg_gen_trunc_reg_i32(t32, load_gpr(ctx, a->r2));
         tcg_gen_rotr_i32(t32, t32, cpu_sar);
         tcg_gen_extu_i32_reg(dest, t32);
         tcg_temp_free_i32(t32);
@@ -3310,7 +3214,7 @@ static bool trans_shrpw_sar(DisasContext *ctx, uint32_t insn,
         TCGv_i64 t = tcg_temp_new_i64();
         TCGv_i64 s = tcg_temp_new_i64();
 
-        tcg_gen_concat_reg_i64(t, load_gpr(ctx, r2), load_gpr(ctx, r1));
+        tcg_gen_concat_reg_i64(t, load_gpr(ctx, a->r2), load_gpr(ctx, a->r1));
         tcg_gen_extu_reg_i64(s, cpu_sar);
         tcg_gen_shr_i64(t, t, s);
         tcg_gen_trunc_i64_reg(dest, t);
@@ -3318,79 +3222,67 @@ static bool trans_shrpw_sar(DisasContext *ctx, uint32_t insn,
         tcg_temp_free_i64(t);
         tcg_temp_free_i64(s);
     }
-    save_gpr(ctx, rt, dest);
+    save_gpr(ctx, a->t, dest);
 
     /* Install the new nullification.  */
     cond_free(&ctx->null_cond);
-    if (c) {
-        ctx->null_cond = do_sed_cond(c, dest);
+    if (a->c) {
+        ctx->null_cond = do_sed_cond(a->c, dest);
     }
     return nullify_end(ctx);
 }
 
-static bool trans_shrpw_imm(DisasContext *ctx, uint32_t insn,
-                            const DisasInsn *di)
+static bool trans_shrpw_imm(DisasContext *ctx, arg_shrpw_imm *a)
 {
-    unsigned rt = extract32(insn, 0, 5);
-    unsigned cpos = extract32(insn, 5, 5);
-    unsigned c = extract32(insn, 13, 3);
-    unsigned r1 = extract32(insn, 16, 5);
-    unsigned r2 = extract32(insn, 21, 5);
-    unsigned sa = 31 - cpos;
+    unsigned sa = 31 - a->cpos;
     TCGv_reg dest, t2;
 
-    if (c) {
+    if (a->c) {
         nullify_over(ctx);
     }
 
-    dest = dest_gpr(ctx, rt);
-    t2 = load_gpr(ctx, r2);
-    if (r1 == r2) {
+    dest = dest_gpr(ctx, a->t);
+    t2 = load_gpr(ctx, a->r2);
+    if (a->r1 == a->r2) {
         TCGv_i32 t32 = tcg_temp_new_i32();
         tcg_gen_trunc_reg_i32(t32, t2);
         tcg_gen_rotri_i32(t32, t32, sa);
         tcg_gen_extu_i32_reg(dest, t32);
         tcg_temp_free_i32(t32);
-    } else if (r1 == 0) {
+    } else if (a->r1 == 0) {
         tcg_gen_extract_reg(dest, t2, sa, 32 - sa);
     } else {
         TCGv_reg t0 = tcg_temp_new();
         tcg_gen_extract_reg(t0, t2, sa, 32 - sa);
-        tcg_gen_deposit_reg(dest, t0, cpu_gr[r1], 32 - sa, sa);
+        tcg_gen_deposit_reg(dest, t0, cpu_gr[a->r1], 32 - sa, sa);
         tcg_temp_free(t0);
     }
-    save_gpr(ctx, rt, dest);
+    save_gpr(ctx, a->t, dest);
 
     /* Install the new nullification.  */
     cond_free(&ctx->null_cond);
-    if (c) {
-        ctx->null_cond = do_sed_cond(c, dest);
+    if (a->c) {
+        ctx->null_cond = do_sed_cond(a->c, dest);
     }
     return nullify_end(ctx);
 }
 
-static bool trans_extrw_sar(DisasContext *ctx, uint32_t insn,
-                            const DisasInsn *di)
+static bool trans_extrw_sar(DisasContext *ctx, arg_extrw_sar *a)
 {
-    unsigned clen = extract32(insn, 0, 5);
-    unsigned is_se = extract32(insn, 10, 1);
-    unsigned c = extract32(insn, 13, 3);
-    unsigned rt = extract32(insn, 16, 5);
-    unsigned rr = extract32(insn, 21, 5);
-    unsigned len = 32 - clen;
+    unsigned len = 32 - a->clen;
     TCGv_reg dest, src, tmp;
 
-    if (c) {
+    if (a->c) {
         nullify_over(ctx);
     }
 
-    dest = dest_gpr(ctx, rt);
-    src = load_gpr(ctx, rr);
+    dest = dest_gpr(ctx, a->t);
+    src = load_gpr(ctx, a->r);
     tmp = tcg_temp_new();
 
     /* Recall that SAR is using big-endian bit numbering.  */
     tcg_gen_xori_reg(tmp, cpu_sar, TARGET_REGISTER_BITS - 1);
-    if (is_se) {
+    if (a->se) {
         tcg_gen_sar_reg(dest, src, tmp);
         tcg_gen_sextract_reg(dest, dest, 0, len);
     } else {
@@ -3398,83 +3290,62 @@ static bool trans_extrw_sar(DisasContext *ctx, uint32_t insn,
         tcg_gen_extract_reg(dest, dest, 0, len);
     }
     tcg_temp_free(tmp);
-    save_gpr(ctx, rt, dest);
+    save_gpr(ctx, a->t, dest);
 
     /* Install the new nullification.  */
     cond_free(&ctx->null_cond);
-    if (c) {
-        ctx->null_cond = do_sed_cond(c, dest);
+    if (a->c) {
+        ctx->null_cond = do_sed_cond(a->c, dest);
     }
     return nullify_end(ctx);
 }
 
-static bool trans_extrw_imm(DisasContext *ctx, uint32_t insn,
-                            const DisasInsn *di)
+static bool trans_extrw_imm(DisasContext *ctx, arg_extrw_imm *a)
 {
-    unsigned clen = extract32(insn, 0, 5);
-    unsigned pos = extract32(insn, 5, 5);
-    unsigned is_se = extract32(insn, 10, 1);
-    unsigned c = extract32(insn, 13, 3);
-    unsigned rt = extract32(insn, 16, 5);
-    unsigned rr = extract32(insn, 21, 5);
-    unsigned len = 32 - clen;
-    unsigned cpos = 31 - pos;
+    unsigned len = 32 - a->clen;
+    unsigned cpos = 31 - a->pos;
     TCGv_reg dest, src;
 
-    if (c) {
+    if (a->c) {
         nullify_over(ctx);
     }
 
-    dest = dest_gpr(ctx, rt);
-    src = load_gpr(ctx, rr);
-    if (is_se) {
+    dest = dest_gpr(ctx, a->t);
+    src = load_gpr(ctx, a->r);
+    if (a->se) {
         tcg_gen_sextract_reg(dest, src, cpos, len);
     } else {
         tcg_gen_extract_reg(dest, src, cpos, len);
     }
-    save_gpr(ctx, rt, dest);
+    save_gpr(ctx, a->t, dest);
 
     /* Install the new nullification.  */
     cond_free(&ctx->null_cond);
-    if (c) {
-        ctx->null_cond = do_sed_cond(c, dest);
+    if (a->c) {
+        ctx->null_cond = do_sed_cond(a->c, dest);
     }
     return nullify_end(ctx);
 }
 
-static const DisasInsn table_sh_ex[] = {
-    { 0xd0000000u, 0xfc001fe0u, trans_shrpw_sar },
-    { 0xd0000800u, 0xfc001c00u, trans_shrpw_imm },
-    { 0xd0001000u, 0xfc001be0u, trans_extrw_sar },
-    { 0xd0001800u, 0xfc001800u, trans_extrw_imm },
-};
-
-static bool trans_depw_imm_c(DisasContext *ctx, uint32_t insn,
-                             const DisasInsn *di)
+static bool trans_depwi_imm(DisasContext *ctx, arg_depwi_imm *a)
 {
-    unsigned clen = extract32(insn, 0, 5);
-    unsigned cpos = extract32(insn, 5, 5);
-    unsigned nz = extract32(insn, 10, 1);
-    unsigned c = extract32(insn, 13, 3);
-    target_sreg val = low_sextract(insn, 16, 5);
-    unsigned rt = extract32(insn, 21, 5);
-    unsigned len = 32 - clen;
+    unsigned len = 32 - a->clen;
     target_sreg mask0, mask1;
     TCGv_reg dest;
 
-    if (c) {
+    if (a->c) {
         nullify_over(ctx);
     }
-    if (cpos + len > 32) {
-        len = 32 - cpos;
+    if (a->cpos + len > 32) {
+        len = 32 - a->cpos;
     }
 
-    dest = dest_gpr(ctx, rt);
-    mask0 = deposit64(0, cpos, len, val);
-    mask1 = deposit64(-1, cpos, len, val);
+    dest = dest_gpr(ctx, a->t);
+    mask0 = deposit64(0, a->cpos, len, a->i);
+    mask1 = deposit64(-1, a->cpos, len, a->i);
 
-    if (nz) {
-        TCGv_reg src = load_gpr(ctx, rt);
+    if (a->nz) {
+        TCGv_reg src = load_gpr(ctx, a->t);
         if (mask1 != -1) {
             tcg_gen_andi_reg(dest, src, mask1);
             src = dest;
@@ -3483,75 +3354,58 @@ static bool trans_depw_imm_c(DisasContext *ctx, uint32_t insn,
     } else {
         tcg_gen_movi_reg(dest, mask0);
     }
-    save_gpr(ctx, rt, dest);
+    save_gpr(ctx, a->t, dest);
 
     /* Install the new nullification.  */
     cond_free(&ctx->null_cond);
-    if (c) {
-        ctx->null_cond = do_sed_cond(c, dest);
+    if (a->c) {
+        ctx->null_cond = do_sed_cond(a->c, dest);
     }
     return nullify_end(ctx);
 }
 
-static bool trans_depw_imm(DisasContext *ctx, uint32_t insn,
-                           const DisasInsn *di)
+static bool trans_depw_imm(DisasContext *ctx, arg_depw_imm *a)
 {
-    unsigned clen = extract32(insn, 0, 5);
-    unsigned cpos = extract32(insn, 5, 5);
-    unsigned nz = extract32(insn, 10, 1);
-    unsigned c = extract32(insn, 13, 3);
-    unsigned rr = extract32(insn, 16, 5);
-    unsigned rt = extract32(insn, 21, 5);
-    unsigned rs = nz ? rt : 0;
-    unsigned len = 32 - clen;
+    unsigned rs = a->nz ? a->t : 0;
+    unsigned len = 32 - a->clen;
     TCGv_reg dest, val;
 
-    if (c) {
+    if (a->c) {
         nullify_over(ctx);
     }
-    if (cpos + len > 32) {
-        len = 32 - cpos;
+    if (a->cpos + len > 32) {
+        len = 32 - a->cpos;
     }
 
-    dest = dest_gpr(ctx, rt);
-    val = load_gpr(ctx, rr);
+    dest = dest_gpr(ctx, a->t);
+    val = load_gpr(ctx, a->r);
     if (rs == 0) {
-        tcg_gen_deposit_z_reg(dest, val, cpos, len);
+        tcg_gen_deposit_z_reg(dest, val, a->cpos, len);
     } else {
-        tcg_gen_deposit_reg(dest, cpu_gr[rs], val, cpos, len);
+        tcg_gen_deposit_reg(dest, cpu_gr[rs], val, a->cpos, len);
     }
-    save_gpr(ctx, rt, dest);
+    save_gpr(ctx, a->t, dest);
 
     /* Install the new nullification.  */
     cond_free(&ctx->null_cond);
-    if (c) {
-        ctx->null_cond = do_sed_cond(c, dest);
+    if (a->c) {
+        ctx->null_cond = do_sed_cond(a->c, dest);
     }
     return nullify_end(ctx);
 }
 
-static bool trans_depw_sar(DisasContext *ctx, uint32_t insn,
-                           const DisasInsn *di)
+static bool do_depw_sar(DisasContext *ctx, unsigned rt, unsigned c,
+                        unsigned nz, unsigned clen, TCGv_reg val)
 {
-    unsigned clen = extract32(insn, 0, 5);
-    unsigned nz = extract32(insn, 10, 1);
-    unsigned i = extract32(insn, 12, 1);
-    unsigned c = extract32(insn, 13, 3);
-    unsigned rt = extract32(insn, 21, 5);
     unsigned rs = nz ? rt : 0;
     unsigned len = 32 - clen;
-    TCGv_reg val, mask, tmp, shift, dest;
+    TCGv_reg mask, tmp, shift, dest;
     unsigned msb = 1U << (len - 1);
 
     if (c) {
         nullify_over(ctx);
     }
 
-    if (i) {
-        val = load_const(ctx, low_sextract(insn, 16, 5));
-    } else {
-        val = load_gpr(ctx, extract32(insn, 16, 5));
-    }
     dest = dest_gpr(ctx, rt);
     shift = tcg_temp_new();
     tmp = tcg_temp_new();
@@ -3582,17 +3436,18 @@ static bool trans_depw_sar(DisasContext *ctx, uint32_t insn,
     return nullify_end(ctx);
 }
 
-static const DisasInsn table_depw[] = {
-    { 0xd4000000u, 0xfc000be0u, trans_depw_sar },
-    { 0xd4000800u, 0xfc001800u, trans_depw_imm },
-    { 0xd4001800u, 0xfc001800u, trans_depw_imm_c },
-};
+static bool trans_depw_sar(DisasContext *ctx, arg_depw_sar *a)
+{
+    return do_depw_sar(ctx, a->t, a->c, a->nz, a->clen, load_gpr(ctx, a->r));
+}
+
+static bool trans_depwi_sar(DisasContext *ctx, arg_depwi_sar *a)
+{
+    return do_depw_sar(ctx, a->t, a->c, a->nz, a->clen, load_const(ctx, a->i));
+}
 
-static bool trans_be(DisasContext *ctx, uint32_t insn, bool is_l)
+static bool trans_be(DisasContext *ctx, arg_be *a)
 {
-    unsigned n = extract32(insn, 1, 1);
-    unsigned b = extract32(insn, 21, 5);
-    target_sreg disp = assemble_17(insn);
     TCGv_reg tmp;
 
 #ifdef CONFIG_USER_ONLY
@@ -3604,30 +3459,28 @@ static bool trans_be(DisasContext *ctx, uint32_t insn, bool is_l)
     /* Since we don't implement spaces, just branch.  Do notice the special
        case of "be disp(*,r0)" using a direct branch to disp, so that we can
        goto_tb to the TB containing the syscall.  */
-    if (b == 0) {
-        do_dbranch(ctx, disp, is_l ? 31 : 0, n);
-        return true;
+    if (a->b == 0) {
+        return do_dbranch(ctx, a->disp, a->l, a->n);
     }
 #else
-    int sp = assemble_sr3(insn);
     nullify_over(ctx);
 #endif
 
     tmp = get_temp(ctx);
-    tcg_gen_addi_reg(tmp, load_gpr(ctx, b), disp);
+    tcg_gen_addi_reg(tmp, load_gpr(ctx, a->b), a->disp);
     tmp = do_ibranch_priv(ctx, tmp);
 
 #ifdef CONFIG_USER_ONLY
-    do_ibranch(ctx, tmp, is_l ? 31 : 0, n);
+    return do_ibranch(ctx, tmp, a->l, a->n);
 #else
     TCGv_i64 new_spc = tcg_temp_new_i64();
 
-    load_spr(ctx, new_spc, sp);
-    if (is_l) {
+    load_spr(ctx, new_spc, a->sp);
+    if (a->l) {
         copy_iaoq_entry(cpu_gr[31], ctx->iaoq_n, ctx->iaoq_n_var);
         tcg_gen_mov_i64(cpu_sr[0], cpu_iasq_f);
     }
-    if (n && use_nullify_skip(ctx)) {
+    if (a->n && use_nullify_skip(ctx)) {
         tcg_gen_mov_reg(cpu_iaoq_f, tmp);
         tcg_gen_addi_reg(cpu_iaoq_b, cpu_iaoq_f, 4);
         tcg_gen_mov_i64(cpu_iasq_f, new_spc);
@@ -3639,32 +3492,25 @@ static bool trans_be(DisasContext *ctx, uint32_t insn, bool is_l)
         }
         tcg_gen_mov_reg(cpu_iaoq_b, tmp);
         tcg_gen_mov_i64(cpu_iasq_b, new_spc);
-        nullify_set(ctx, n);
+        nullify_set(ctx, a->n);
     }
     tcg_temp_free_i64(new_spc);
     tcg_gen_lookup_and_goto_ptr();
     ctx->base.is_jmp = DISAS_NORETURN;
     return nullify_end(ctx);
 #endif
-    return true;
 }
 
-static bool trans_bl(DisasContext *ctx, uint32_t insn, const DisasInsn *di)
+static bool trans_bl(DisasContext *ctx, arg_bl *a)
 {
-    unsigned n = extract32(insn, 1, 1);
-    unsigned link = extract32(insn, 21, 5);
-    target_sreg disp = assemble_17(insn);
-
-    do_dbranch(ctx, iaoq_dest(ctx, disp), link, n);
-    return true;
+    return do_dbranch(ctx, iaoq_dest(ctx, a->disp), a->l, a->n);
 }
 
-static bool trans_b_gate(DisasContext *ctx, uint32_t insn, const DisasInsn *di)
+static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a)
 {
-    unsigned n = extract32(insn, 1, 1);
-    unsigned link = extract32(insn, 21, 5);
-    target_sreg disp = assemble_17(insn);
-    target_ureg dest = iaoq_dest(ctx, disp);
+    target_ureg dest = iaoq_dest(ctx, a->disp);
+
+    nullify_over(ctx);
 
     /* Make sure the caller hasn't done something weird with the queue.
      * ??? This is not quite the same as the PSW[B] bit, which would be
@@ -3703,65 +3549,57 @@ static bool trans_b_gate(DisasContext *ctx, uint32_t insn, const DisasInsn *di)
     }
 #endif
 
-    do_dbranch(ctx, dest, link, n);
-    return true;
-}
-
-static bool trans_bl_long(DisasContext *ctx, uint32_t insn, const DisasInsn *di)
-{
-    unsigned n = extract32(insn, 1, 1);
-    target_sreg disp = assemble_22(insn);
+    if (a->l) {
+        TCGv_reg tmp = dest_gpr(ctx, a->l);
+        if (ctx->privilege < 3) {
+            tcg_gen_andi_reg(tmp, tmp, -4);
+        }
+        tcg_gen_ori_reg(tmp, tmp, ctx->privilege);
+        save_gpr(ctx, a->l, tmp);
+    }
 
-    do_dbranch(ctx, iaoq_dest(ctx, disp), 2, n);
-    return true;
+    return do_dbranch(ctx, dest, 0, a->n);
 }
 
-static bool trans_blr(DisasContext *ctx, uint32_t insn, const DisasInsn *di)
+static bool trans_blr(DisasContext *ctx, arg_blr *a)
 {
-    unsigned n = extract32(insn, 1, 1);
-    unsigned rx = extract32(insn, 16, 5);
-    unsigned link = extract32(insn, 21, 5);
-    TCGv_reg tmp = get_temp(ctx);
-
-    tcg_gen_shli_reg(tmp, load_gpr(ctx, rx), 3);
-    tcg_gen_addi_reg(tmp, tmp, ctx->iaoq_f + 8);
-    /* The computation here never changes privilege level.  */
-    do_ibranch(ctx, tmp, link, n);
-    return true;
+    if (a->x) {
+        TCGv_reg tmp = get_temp(ctx);
+        tcg_gen_shli_reg(tmp, load_gpr(ctx, a->x), 3);
+        tcg_gen_addi_reg(tmp, tmp, ctx->iaoq_f + 8);
+        /* The computation here never changes privilege level.  */
+        return do_ibranch(ctx, tmp, a->l, a->n);
+    } else {
+        /* BLR R0,RX is a good way to load PC+8 into RX.  */
+        return do_dbranch(ctx, ctx->iaoq_f + 8, a->l, a->n);
+    }
 }
 
-static bool trans_bv(DisasContext *ctx, uint32_t insn, const DisasInsn *di)
+static bool trans_bv(DisasContext *ctx, arg_bv *a)
 {
-    unsigned n = extract32(insn, 1, 1);
-    unsigned rx = extract32(insn, 16, 5);
-    unsigned rb = extract32(insn, 21, 5);
     TCGv_reg dest;
 
-    if (rx == 0) {
-        dest = load_gpr(ctx, rb);
+    if (a->x == 0) {
+        dest = load_gpr(ctx, a->b);
     } else {
         dest = get_temp(ctx);
-        tcg_gen_shli_reg(dest, load_gpr(ctx, rx), 3);
-        tcg_gen_add_reg(dest, dest, load_gpr(ctx, rb));
+        tcg_gen_shli_reg(dest, load_gpr(ctx, a->x), 3);
+        tcg_gen_add_reg(dest, dest, load_gpr(ctx, a->b));
     }
     dest = do_ibranch_priv(ctx, dest);
-    do_ibranch(ctx, dest, 0, n);
-    return true;
+    return do_ibranch(ctx, dest, 0, a->n);
 }
 
-static bool trans_bve(DisasContext *ctx, uint32_t insn, const DisasInsn *di)
+static bool trans_bve(DisasContext *ctx, arg_bve *a)
 {
-    unsigned n = extract32(insn, 1, 1);
-    unsigned rb = extract32(insn, 21, 5);
-    unsigned link = extract32(insn, 13, 1) ? 2 : 0;
     TCGv_reg dest;
 
 #ifdef CONFIG_USER_ONLY
-    dest = do_ibranch_priv(ctx, load_gpr(ctx, rb));
-    do_ibranch(ctx, dest, link, n);
+    dest = do_ibranch_priv(ctx, load_gpr(ctx, a->b));
+    return do_ibranch(ctx, dest, a->l, a->n);
 #else
     nullify_over(ctx);
-    dest = do_ibranch_priv(ctx, load_gpr(ctx, rb));
+    dest = do_ibranch_priv(ctx, load_gpr(ctx, a->b));
 
     copy_iaoq_entry(cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b);
     if (ctx->iaoq_b == -1) {
@@ -3769,775 +3607,527 @@ static bool trans_bve(DisasContext *ctx, uint32_t insn, const DisasInsn *di)
     }
     copy_iaoq_entry(cpu_iaoq_b, -1, dest);
     tcg_gen_mov_i64(cpu_iasq_b, space_select(ctx, 0, dest));
-    if (link) {
-        copy_iaoq_entry(cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var);
+    if (a->l) {
+        copy_iaoq_entry(cpu_gr[a->l], ctx->iaoq_n, ctx->iaoq_n_var);
     }
-    nullify_set(ctx, n);
+    nullify_set(ctx, a->n);
     tcg_gen_lookup_and_goto_ptr();
     ctx->base.is_jmp = DISAS_NORETURN;
     return nullify_end(ctx);
 #endif
-    return true;
 }
 
-static const DisasInsn table_branch[] = {
-    { 0xe8000000u, 0xfc006000u, trans_bl }, /* B,L and B,L,PUSH */
-    { 0xe800a000u, 0xfc00e000u, trans_bl_long },
-    { 0xe8004000u, 0xfc00fffdu, trans_blr },
-    { 0xe800c000u, 0xfc00fffdu, trans_bv },
-    { 0xe800d000u, 0xfc00dffcu, trans_bve },
-    { 0xe8002000u, 0xfc00e000u, trans_b_gate },
-};
+/*
+ * Float class 0
+ */
 
-static bool trans_fop_wew_0c(DisasContext *ctx, uint32_t insn,
-                             const DisasInsn *di)
+static void gen_fcpy_f(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src)
 {
-    unsigned rt = extract32(insn, 0, 5);
-    unsigned ra = extract32(insn, 21, 5);
-    do_fop_wew(ctx, rt, ra, di->f.wew);
-    return true;
+    tcg_gen_mov_i32(dst, src);
 }
 
-static bool trans_fop_wew_0e(DisasContext *ctx, uint32_t insn,
-                             const DisasInsn *di)
+static bool trans_fcpy_f(DisasContext *ctx, arg_fclass01 *a)
 {
-    unsigned rt = assemble_rt64(insn);
-    unsigned ra = assemble_ra64(insn);
-    do_fop_wew(ctx, rt, ra, di->f.wew);
-    return true;
+    return do_fop_wew(ctx, a->t, a->r, gen_fcpy_f);
 }
 
-static bool trans_fop_ded(DisasContext *ctx, uint32_t insn,
-                          const DisasInsn *di)
+static void gen_fcpy_d(TCGv_i64 dst, TCGv_env unused, TCGv_i64 src)
 {
-    unsigned rt = extract32(insn, 0, 5);
-    unsigned ra = extract32(insn, 21, 5);
-    do_fop_ded(ctx, rt, ra, di->f.ded);
-    return true;
+    tcg_gen_mov_i64(dst, src);
 }
 
-static bool trans_fop_wed_0c(DisasContext *ctx, uint32_t insn,
-                             const DisasInsn *di)
+static bool trans_fcpy_d(DisasContext *ctx, arg_fclass01 *a)
 {
-    unsigned rt = extract32(insn, 0, 5);
-    unsigned ra = extract32(insn, 21, 5);
-    do_fop_wed(ctx, rt, ra, di->f.wed);
-    return true;
+    return do_fop_ded(ctx, a->t, a->r, gen_fcpy_d);
 }
 
-static bool trans_fop_wed_0e(DisasContext *ctx, uint32_t insn,
-                             const DisasInsn *di)
+static void gen_fabs_f(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src)
 {
-    unsigned rt = assemble_rt64(insn);
-    unsigned ra = extract32(insn, 21, 5);
-    do_fop_wed(ctx, rt, ra, di->f.wed);
-    return true;
+    tcg_gen_andi_i32(dst, src, INT32_MAX);
 }
 
-static bool trans_fop_dew_0c(DisasContext *ctx, uint32_t insn,
-                             const DisasInsn *di)
+static bool trans_fabs_f(DisasContext *ctx, arg_fclass01 *a)
 {
-    unsigned rt = extract32(insn, 0, 5);
-    unsigned ra = extract32(insn, 21, 5);
-    do_fop_dew(ctx, rt, ra, di->f.dew);
-    return true;
+    return do_fop_wew(ctx, a->t, a->r, gen_fabs_f);
 }
 
-static bool trans_fop_dew_0e(DisasContext *ctx, uint32_t insn,
-                             const DisasInsn *di)
+static void gen_fabs_d(TCGv_i64 dst, TCGv_env unused, TCGv_i64 src)
 {
-    unsigned rt = extract32(insn, 0, 5);
-    unsigned ra = assemble_ra64(insn);
-    do_fop_dew(ctx, rt, ra, di->f.dew);
-    return true;
+    tcg_gen_andi_i64(dst, src, INT64_MAX);
 }
 
-static bool trans_fop_weww_0c(DisasContext *ctx, uint32_t insn,
-                              const DisasInsn *di)
+static bool trans_fabs_d(DisasContext *ctx, arg_fclass01 *a)
 {
-    unsigned rt = extract32(insn, 0, 5);
-    unsigned rb = extract32(insn, 16, 5);
-    unsigned ra = extract32(insn, 21, 5);
-    do_fop_weww(ctx, rt, ra, rb, di->f.weww);
-    return true;
+    return do_fop_ded(ctx, a->t, a->r, gen_fabs_d);
 }
 
-static bool trans_fop_weww_0e(DisasContext *ctx, uint32_t insn,
-                              const DisasInsn *di)
+static bool trans_fsqrt_f(DisasContext *ctx, arg_fclass01 *a)
 {
-    unsigned rt = assemble_rt64(insn);
-    unsigned rb = assemble_rb64(insn);
-    unsigned ra = assemble_ra64(insn);
-    do_fop_weww(ctx, rt, ra, rb, di->f.weww);
-    return true;
+    return do_fop_wew(ctx, a->t, a->r, gen_helper_fsqrt_s);
 }
 
-static bool trans_fop_dedd(DisasContext *ctx, uint32_t insn,
-                           const DisasInsn *di)
+static bool trans_fsqrt_d(DisasContext *ctx, arg_fclass01 *a)
 {
-    unsigned rt = extract32(insn, 0, 5);
-    unsigned rb = extract32(insn, 16, 5);
-    unsigned ra = extract32(insn, 21, 5);
-    do_fop_dedd(ctx, rt, ra, rb, di->f.dedd);
-    return true;
+    return do_fop_ded(ctx, a->t, a->r, gen_helper_fsqrt_d);
 }
 
-static void gen_fcpy_s(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src)
+static bool trans_frnd_f(DisasContext *ctx, arg_fclass01 *a)
 {
-    tcg_gen_mov_i32(dst, src);
+    return do_fop_wew(ctx, a->t, a->r, gen_helper_frnd_s);
 }
 
-static void gen_fcpy_d(TCGv_i64 dst, TCGv_env unused, TCGv_i64 src)
+static bool trans_frnd_d(DisasContext *ctx, arg_fclass01 *a)
 {
-    tcg_gen_mov_i64(dst, src);
+    return do_fop_ded(ctx, a->t, a->r, gen_helper_frnd_d);
 }
 
-static void gen_fabs_s(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src)
+static void gen_fneg_f(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src)
 {
-    tcg_gen_andi_i32(dst, src, INT32_MAX);
+    tcg_gen_xori_i32(dst, src, INT32_MIN);
 }
 
-static void gen_fabs_d(TCGv_i64 dst, TCGv_env unused, TCGv_i64 src)
+static bool trans_fneg_f(DisasContext *ctx, arg_fclass01 *a)
 {
-    tcg_gen_andi_i64(dst, src, INT64_MAX);
+    return do_fop_wew(ctx, a->t, a->r, gen_fneg_f);
 }
 
-static void gen_fneg_s(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src)
+static void gen_fneg_d(TCGv_i64 dst, TCGv_env unused, TCGv_i64 src)
 {
-    tcg_gen_xori_i32(dst, src, INT32_MIN);
+    tcg_gen_xori_i64(dst, src, INT64_MIN);
 }
 
-static void gen_fneg_d(TCGv_i64 dst, TCGv_env unused, TCGv_i64 src)
+static bool trans_fneg_d(DisasContext *ctx, arg_fclass01 *a)
 {
-    tcg_gen_xori_i64(dst, src, INT64_MIN);
+    return do_fop_ded(ctx, a->t, a->r, gen_fneg_d);
 }
 
-static void gen_fnegabs_s(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src)
+static void gen_fnegabs_f(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src)
 {
     tcg_gen_ori_i32(dst, src, INT32_MIN);
 }
 
+static bool trans_fnegabs_f(DisasContext *ctx, arg_fclass01 *a)
+{
+    return do_fop_wew(ctx, a->t, a->r, gen_fnegabs_f);
+}
+
 static void gen_fnegabs_d(TCGv_i64 dst, TCGv_env unused, TCGv_i64 src)
 {
     tcg_gen_ori_i64(dst, src, INT64_MIN);
 }
 
-static void do_fcmp_s(DisasContext *ctx, unsigned ra, unsigned rb,
-                      unsigned y, unsigned c)
+static bool trans_fnegabs_d(DisasContext *ctx, arg_fclass01 *a)
 {
-    TCGv_i32 ta, tb, tc, ty;
+    return do_fop_ded(ctx, a->t, a->r, gen_fnegabs_d);
+}
 
-    nullify_over(ctx);
+/*
+ * Float class 1
+ */
 
-    ta = load_frw0_i32(ra);
-    tb = load_frw0_i32(rb);
-    ty = tcg_const_i32(y);
-    tc = tcg_const_i32(c);
+static bool trans_fcnv_d_f(DisasContext *ctx, arg_fclass01 *a)
+{
+    return do_fop_wed(ctx, a->t, a->r, gen_helper_fcnv_d_s);
+}
 
-    gen_helper_fcmp_s(cpu_env, ta, tb, ty, tc);
+static bool trans_fcnv_f_d(DisasContext *ctx, arg_fclass01 *a)
+{
+    return do_fop_dew(ctx, a->t, a->r, gen_helper_fcnv_s_d);
+}
 
-    tcg_temp_free_i32(ta);
-    tcg_temp_free_i32(tb);
-    tcg_temp_free_i32(ty);
-    tcg_temp_free_i32(tc);
+static bool trans_fcnv_w_f(DisasContext *ctx, arg_fclass01 *a)
+{
+    return do_fop_wew(ctx, a->t, a->r, gen_helper_fcnv_w_s);
+}
+
+static bool trans_fcnv_q_f(DisasContext *ctx, arg_fclass01 *a)
+{
+    return do_fop_wed(ctx, a->t, a->r, gen_helper_fcnv_dw_s);
+}
 
-    nullify_end(ctx);
+static bool trans_fcnv_w_d(DisasContext *ctx, arg_fclass01 *a)
+{
+    return do_fop_dew(ctx, a->t, a->r, gen_helper_fcnv_w_d);
 }
 
-static bool trans_fcmp_s_0c(DisasContext *ctx, uint32_t insn,
-                            const DisasInsn *di)
+static bool trans_fcnv_q_d(DisasContext *ctx, arg_fclass01 *a)
 {
-    unsigned c = extract32(insn, 0, 5);
-    unsigned y = extract32(insn, 13, 3);
-    unsigned rb = extract32(insn, 16, 5);
-    unsigned ra = extract32(insn, 21, 5);
-    do_fcmp_s(ctx, ra, rb, y, c);
-    return true;
+    return do_fop_ded(ctx, a->t, a->r, gen_helper_fcnv_dw_d);
 }
 
-static bool trans_fcmp_s_0e(DisasContext *ctx, uint32_t insn,
-                            const DisasInsn *di)
+static bool trans_fcnv_f_w(DisasContext *ctx, arg_fclass01 *a)
 {
-    unsigned c = extract32(insn, 0, 5);
-    unsigned y = extract32(insn, 13, 3);
-    unsigned rb = assemble_rb64(insn);
-    unsigned ra = assemble_ra64(insn);
-    do_fcmp_s(ctx, ra, rb, y, c);
-    return true;
+    return do_fop_wew(ctx, a->t, a->r, gen_helper_fcnv_s_w);
 }
 
-static bool trans_fcmp_d(DisasContext *ctx, uint32_t insn, const DisasInsn *di)
+static bool trans_fcnv_d_w(DisasContext *ctx, arg_fclass01 *a)
 {
-    unsigned c = extract32(insn, 0, 5);
-    unsigned y = extract32(insn, 13, 3);
-    unsigned rb = extract32(insn, 16, 5);
-    unsigned ra = extract32(insn, 21, 5);
-    TCGv_i64 ta, tb;
-    TCGv_i32 tc, ty;
+    return do_fop_wed(ctx, a->t, a->r, gen_helper_fcnv_d_w);
+}
+
+static bool trans_fcnv_f_q(DisasContext *ctx, arg_fclass01 *a)
+{
+    return do_fop_dew(ctx, a->t, a->r, gen_helper_fcnv_s_dw);
+}
+
+static bool trans_fcnv_d_q(DisasContext *ctx, arg_fclass01 *a)
+{
+    return do_fop_ded(ctx, a->t, a->r, gen_helper_fcnv_d_dw);
+}
+
+static bool trans_fcnv_t_f_w(DisasContext *ctx, arg_fclass01 *a)
+{
+    return do_fop_wew(ctx, a->t, a->r, gen_helper_fcnv_t_s_w);
+}
+
+static bool trans_fcnv_t_d_w(DisasContext *ctx, arg_fclass01 *a)
+{
+    return do_fop_wed(ctx, a->t, a->r, gen_helper_fcnv_t_d_w);
+}
+
+static bool trans_fcnv_t_f_q(DisasContext *ctx, arg_fclass01 *a)
+{
+    return do_fop_dew(ctx, a->t, a->r, gen_helper_fcnv_t_s_dw);
+}
+
+static bool trans_fcnv_t_d_q(DisasContext *ctx, arg_fclass01 *a)
+{
+    return do_fop_ded(ctx, a->t, a->r, gen_helper_fcnv_t_d_dw);
+}
+
+static bool trans_fcnv_uw_f(DisasContext *ctx, arg_fclass01 *a)
+{
+    return do_fop_wew(ctx, a->t, a->r, gen_helper_fcnv_uw_s);
+}
+
+static bool trans_fcnv_uq_f(DisasContext *ctx, arg_fclass01 *a)
+{
+    return do_fop_wed(ctx, a->t, a->r, gen_helper_fcnv_udw_s);
+}
+
+static bool trans_fcnv_uw_d(DisasContext *ctx, arg_fclass01 *a)
+{
+    return do_fop_dew(ctx, a->t, a->r, gen_helper_fcnv_uw_d);
+}
+
+static bool trans_fcnv_uq_d(DisasContext *ctx, arg_fclass01 *a)
+{
+    return do_fop_ded(ctx, a->t, a->r, gen_helper_fcnv_udw_d);
+}
+
+static bool trans_fcnv_f_uw(DisasContext *ctx, arg_fclass01 *a)
+{
+    return do_fop_wew(ctx, a->t, a->r, gen_helper_fcnv_s_uw);
+}
+
+static bool trans_fcnv_d_uw(DisasContext *ctx, arg_fclass01 *a)
+{
+    return do_fop_wed(ctx, a->t, a->r, gen_helper_fcnv_d_uw);
+}
+
+static bool trans_fcnv_f_uq(DisasContext *ctx, arg_fclass01 *a)
+{
+    return do_fop_dew(ctx, a->t, a->r, gen_helper_fcnv_s_udw);
+}
+
+static bool trans_fcnv_d_uq(DisasContext *ctx, arg_fclass01 *a)
+{
+    return do_fop_ded(ctx, a->t, a->r, gen_helper_fcnv_d_udw);
+}
+
+static bool trans_fcnv_t_f_uw(DisasContext *ctx, arg_fclass01 *a)
+{
+    return do_fop_wew(ctx, a->t, a->r, gen_helper_fcnv_t_s_uw);
+}
+
+static bool trans_fcnv_t_d_uw(DisasContext *ctx, arg_fclass01 *a)
+{
+    return do_fop_wed(ctx, a->t, a->r, gen_helper_fcnv_t_d_uw);
+}
+
+static bool trans_fcnv_t_f_uq(DisasContext *ctx, arg_fclass01 *a)
+{
+    return do_fop_dew(ctx, a->t, a->r, gen_helper_fcnv_t_s_udw);
+}
+
+static bool trans_fcnv_t_d_uq(DisasContext *ctx, arg_fclass01 *a)
+{
+    return do_fop_ded(ctx, a->t, a->r, gen_helper_fcnv_t_d_udw);
+}
+
+/*
+ * Float class 2
+ */
+
+static bool trans_fcmp_f(DisasContext *ctx, arg_fclass2 *a)
+{
+    TCGv_i32 ta, tb, tc, ty;
 
     nullify_over(ctx);
 
-    ta = load_frd0(ra);
-    tb = load_frd0(rb);
-    ty = tcg_const_i32(y);
-    tc = tcg_const_i32(c);
+    ta = load_frw0_i32(a->r1);
+    tb = load_frw0_i32(a->r2);
+    ty = tcg_const_i32(a->y);
+    tc = tcg_const_i32(a->c);
 
-    gen_helper_fcmp_d(cpu_env, ta, tb, ty, tc);
+    gen_helper_fcmp_s(cpu_env, ta, tb, ty, tc);
 
-    tcg_temp_free_i64(ta);
-    tcg_temp_free_i64(tb);
+    tcg_temp_free_i32(ta);
+    tcg_temp_free_i32(tb);
     tcg_temp_free_i32(ty);
     tcg_temp_free_i32(tc);
 
     return nullify_end(ctx);
 }
 
-static bool trans_ftest_t(DisasContext *ctx, uint32_t insn,
-                          const DisasInsn *di)
+static bool trans_fcmp_d(DisasContext *ctx, arg_fclass2 *a)
 {
-    unsigned y = extract32(insn, 13, 3);
-    unsigned cbit = (y ^ 1) - 1;
-    TCGv_reg t;
+    TCGv_i64 ta, tb;
+    TCGv_i32 tc, ty;
 
     nullify_over(ctx);
 
-    t = tcg_temp_new();
-    tcg_gen_ld32u_reg(t, cpu_env, offsetof(CPUHPPAState, fr0_shadow));
-    tcg_gen_extract_reg(t, t, 21 - cbit, 1);
-    ctx->null_cond = cond_make_0(TCG_COND_NE, t);
-    tcg_temp_free(t);
+    ta = load_frd0(a->r1);
+    tb = load_frd0(a->r2);
+    ty = tcg_const_i32(a->y);
+    tc = tcg_const_i32(a->c);
+
+    gen_helper_fcmp_d(cpu_env, ta, tb, ty, tc);
+
+    tcg_temp_free_i64(ta);
+    tcg_temp_free_i64(tb);
+    tcg_temp_free_i32(ty);
+    tcg_temp_free_i32(tc);
 
     return nullify_end(ctx);
 }
 
-static bool trans_ftest_q(DisasContext *ctx, uint32_t insn,
-                          const DisasInsn *di)
+static bool trans_ftest(DisasContext *ctx, arg_ftest *a)
 {
-    unsigned c = extract32(insn, 0, 5);
-    int mask;
-    bool inv = false;
     TCGv_reg t;
 
     nullify_over(ctx);
 
-    t = tcg_temp_new();
+    t = get_temp(ctx);
     tcg_gen_ld32u_reg(t, cpu_env, offsetof(CPUHPPAState, fr0_shadow));
 
-    switch (c) {
-    case 0: /* simple */
-        tcg_gen_andi_reg(t, t, 0x4000000);
-        ctx->null_cond = cond_make_0(TCG_COND_NE, t);
-        goto done;
-    case 2: /* rej */
-        inv = true;
-        /* fallthru */
-    case 1: /* acc */
-        mask = 0x43ff800;
-        break;
-    case 6: /* rej8 */
-        inv = true;
-        /* fallthru */
-    case 5: /* acc8 */
-        mask = 0x43f8000;
-        break;
-    case 9: /* acc6 */
-        mask = 0x43e0000;
-        break;
-    case 13: /* acc4 */
-        mask = 0x4380000;
-        break;
-    case 17: /* acc2 */
-        mask = 0x4200000;
-        break;
-    default:
-        return gen_illegal(ctx);
-    }
-    if (inv) {
-        TCGv_reg c = load_const(ctx, mask);
-        tcg_gen_or_reg(t, t, c);
-        ctx->null_cond = cond_make(TCG_COND_EQ, t, c);
+    if (a->y == 1) {
+        int mask;
+        bool inv = false;
+
+        switch (a->c) {
+        case 0: /* simple */
+            tcg_gen_andi_reg(t, t, 0x4000000);
+            ctx->null_cond = cond_make_0(TCG_COND_NE, t);
+            goto done;
+        case 2: /* rej */
+            inv = true;
+            /* fallthru */
+        case 1: /* acc */
+            mask = 0x43ff800;
+            break;
+        case 6: /* rej8 */
+            inv = true;
+            /* fallthru */
+        case 5: /* acc8 */
+            mask = 0x43f8000;
+            break;
+        case 9: /* acc6 */
+            mask = 0x43e0000;
+            break;
+        case 13: /* acc4 */
+            mask = 0x4380000;
+            break;
+        case 17: /* acc2 */
+            mask = 0x4200000;
+            break;
+        default:
+            gen_illegal(ctx);
+            return true;
+        }
+        if (inv) {
+            TCGv_reg c = load_const(ctx, mask);
+            tcg_gen_or_reg(t, t, c);
+            ctx->null_cond = cond_make(TCG_COND_EQ, t, c);
+        } else {
+            tcg_gen_andi_reg(t, t, mask);
+            ctx->null_cond = cond_make_0(TCG_COND_EQ, t);
+        }
     } else {
-        tcg_gen_andi_reg(t, t, mask);
-        ctx->null_cond = cond_make_0(TCG_COND_EQ, t);
+        unsigned cbit = (a->y ^ 1) - 1;
+
+        tcg_gen_extract_reg(t, t, 21 - cbit, 1);
+        ctx->null_cond = cond_make_0(TCG_COND_NE, t);
+        tcg_temp_free(t);
     }
+
  done:
     return nullify_end(ctx);
 }
 
-static bool trans_xmpyu(DisasContext *ctx, uint32_t insn, const DisasInsn *di)
+/*
+ * Float class 2
+ */
+
+static bool trans_fadd_f(DisasContext *ctx, arg_fclass3 *a)
 {
-    unsigned rt = extract32(insn, 0, 5);
-    unsigned rb = assemble_rb64(insn);
-    unsigned ra = assemble_ra64(insn);
-    TCGv_i64 a, b;
+    return do_fop_weww(ctx, a->t, a->r1, a->r2, gen_helper_fadd_s);
+}
 
-    nullify_over(ctx);
+static bool trans_fadd_d(DisasContext *ctx, arg_fclass3 *a)
+{
+    return do_fop_dedd(ctx, a->t, a->r1, a->r2, gen_helper_fadd_d);
+}
 
-    a = load_frw0_i64(ra);
-    b = load_frw0_i64(rb);
-    tcg_gen_mul_i64(a, a, b);
-    save_frd(rt, a);
-    tcg_temp_free_i64(a);
-    tcg_temp_free_i64(b);
+static bool trans_fsub_f(DisasContext *ctx, arg_fclass3 *a)
+{
+    return do_fop_weww(ctx, a->t, a->r1, a->r2, gen_helper_fsub_s);
+}
 
-    return nullify_end(ctx);
+static bool trans_fsub_d(DisasContext *ctx, arg_fclass3 *a)
+{
+    return do_fop_dedd(ctx, a->t, a->r1, a->r2, gen_helper_fsub_d);
 }
 
-#define FOP_DED  trans_fop_ded, .f.ded
-#define FOP_DEDD trans_fop_dedd, .f.dedd
-
-#define FOP_WEW  trans_fop_wew_0c, .f.wew
-#define FOP_DEW  trans_fop_dew_0c, .f.dew
-#define FOP_WED  trans_fop_wed_0c, .f.wed
-#define FOP_WEWW trans_fop_weww_0c, .f.weww
-
-static const DisasInsn table_float_0c[] = {
-    /* floating point class zero */
-    { 0x30004000, 0xfc1fffe0, FOP_WEW = gen_fcpy_s },
-    { 0x30006000, 0xfc1fffe0, FOP_WEW = gen_fabs_s },
-    { 0x30008000, 0xfc1fffe0, FOP_WEW = gen_helper_fsqrt_s },
-    { 0x3000a000, 0xfc1fffe0, FOP_WEW = gen_helper_frnd_s },
-    { 0x3000c000, 0xfc1fffe0, FOP_WEW = gen_fneg_s },
-    { 0x3000e000, 0xfc1fffe0, FOP_WEW = gen_fnegabs_s },
-
-    { 0x30004800, 0xfc1fffe0, FOP_DED = gen_fcpy_d },
-    { 0x30006800, 0xfc1fffe0, FOP_DED = gen_fabs_d },
-    { 0x30008800, 0xfc1fffe0, FOP_DED = gen_helper_fsqrt_d },
-    { 0x3000a800, 0xfc1fffe0, FOP_DED = gen_helper_frnd_d },
-    { 0x3000c800, 0xfc1fffe0, FOP_DED = gen_fneg_d },
-    { 0x3000e800, 0xfc1fffe0, FOP_DED = gen_fnegabs_d },
-
-    /* floating point class three */
-    { 0x30000600, 0xfc00ffe0, FOP_WEWW = gen_helper_fadd_s },
-    { 0x30002600, 0xfc00ffe0, FOP_WEWW = gen_helper_fsub_s },
-    { 0x30004600, 0xfc00ffe0, FOP_WEWW = gen_helper_fmpy_s },
-    { 0x30006600, 0xfc00ffe0, FOP_WEWW = gen_helper_fdiv_s },
-
-    { 0x30000e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fadd_d },
-    { 0x30002e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fsub_d },
-    { 0x30004e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fmpy_d },
-    { 0x30006e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fdiv_d },
-
-    /* floating point class one */
-    /* float/float */
-    { 0x30000a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_d_s },
-    { 0x30002200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_s_d },
-    /* int/float */
-    { 0x30008200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_w_s },
-    { 0x30008a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_dw_s },
-    { 0x3000a200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_w_d },
-    { 0x3000aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_dw_d },
-    /* float/int */
-    { 0x30010200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_s_w },
-    { 0x30010a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_d_w },
-    { 0x30012200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_s_dw },
-    { 0x30012a00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_d_dw },
-    /* float/int truncate */
-    { 0x30018200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_t_s_w },
-    { 0x30018a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_t_d_w },
-    { 0x3001a200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_t_s_dw },
-    { 0x3001aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_t_d_dw },
-    /* uint/float */
-    { 0x30028200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_uw_s },
-    { 0x30028a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_udw_s },
-    { 0x3002a200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_uw_d },
-    { 0x3002aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_udw_d },
-    /* float/uint */
-    { 0x30030200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_s_uw },
-    { 0x30030a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_d_uw },
-    { 0x30032200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_s_udw },
-    { 0x30032a00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_d_udw },
-    /* float/uint truncate */
-    { 0x30038200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_t_s_uw },
-    { 0x30038a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_t_d_uw },
-    { 0x3003a200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_t_s_udw },
-    { 0x3003aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_t_d_udw },
-
-    /* floating point class two */
-    { 0x30000400, 0xfc001fe0, trans_fcmp_s_0c },
-    { 0x30000c00, 0xfc001fe0, trans_fcmp_d },
-    { 0x30002420, 0xffffffe0, trans_ftest_q },
-    { 0x30000420, 0xffff1fff, trans_ftest_t },
-
-    /* FID.  Note that ra == rt == 0, which via fcpy puts 0 into fr0.
-       This is machine/revision == 0, which is reserved for simulator.  */
-    { 0x30000000, 0xffffffff, FOP_WEW = gen_fcpy_s },
-};
+static bool trans_fmpy_f(DisasContext *ctx, arg_fclass3 *a)
+{
+    return do_fop_weww(ctx, a->t, a->r1, a->r2, gen_helper_fmpy_s);
+}
 
-#undef FOP_WEW
-#undef FOP_DEW
-#undef FOP_WED
-#undef FOP_WEWW
-#define FOP_WEW  trans_fop_wew_0e, .f.wew
-#define FOP_DEW  trans_fop_dew_0e, .f.dew
-#define FOP_WED  trans_fop_wed_0e, .f.wed
-#define FOP_WEWW trans_fop_weww_0e, .f.weww
-
-static const DisasInsn table_float_0e[] = {
-    /* floating point class zero */
-    { 0x38004000, 0xfc1fff20, FOP_WEW = gen_fcpy_s },
-    { 0x38006000, 0xfc1fff20, FOP_WEW = gen_fabs_s },
-    { 0x38008000, 0xfc1fff20, FOP_WEW = gen_helper_fsqrt_s },
-    { 0x3800a000, 0xfc1fff20, FOP_WEW = gen_helper_frnd_s },
-    { 0x3800c000, 0xfc1fff20, FOP_WEW = gen_fneg_s },
-    { 0x3800e000, 0xfc1fff20, FOP_WEW = gen_fnegabs_s },
-
-    { 0x38004800, 0xfc1fffe0, FOP_DED = gen_fcpy_d },
-    { 0x38006800, 0xfc1fffe0, FOP_DED = gen_fabs_d },
-    { 0x38008800, 0xfc1fffe0, FOP_DED = gen_helper_fsqrt_d },
-    { 0x3800a800, 0xfc1fffe0, FOP_DED = gen_helper_frnd_d },
-    { 0x3800c800, 0xfc1fffe0, FOP_DED = gen_fneg_d },
-    { 0x3800e800, 0xfc1fffe0, FOP_DED = gen_fnegabs_d },
-
-    /* floating point class three */
-    { 0x38000600, 0xfc00ef20, FOP_WEWW = gen_helper_fadd_s },
-    { 0x38002600, 0xfc00ef20, FOP_WEWW = gen_helper_fsub_s },
-    { 0x38004600, 0xfc00ef20, FOP_WEWW = gen_helper_fmpy_s },
-    { 0x38006600, 0xfc00ef20, FOP_WEWW = gen_helper_fdiv_s },
-
-    { 0x38000e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fadd_d },
-    { 0x38002e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fsub_d },
-    { 0x38004e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fmpy_d },
-    { 0x38006e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fdiv_d },
-
-    { 0x38004700, 0xfc00ef60, trans_xmpyu },
-
-    /* floating point class one */
-    /* float/float */
-    { 0x38000a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_d_s },
-    { 0x38002200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_s_d },
-    /* int/float */
-    { 0x38008200, 0xfc1ffe20, FOP_WEW = gen_helper_fcnv_w_s },
-    { 0x38008a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_dw_s },
-    { 0x3800a200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_w_d },
-    { 0x3800aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_dw_d },
-    /* float/int */
-    { 0x38010200, 0xfc1ffe20, FOP_WEW = gen_helper_fcnv_s_w },
-    { 0x38010a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_d_w },
-    { 0x38012200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_s_dw },
-    { 0x38012a00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_d_dw },
-    /* float/int truncate */
-    { 0x38018200, 0xfc1ffe20, FOP_WEW = gen_helper_fcnv_t_s_w },
-    { 0x38018a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_t_d_w },
-    { 0x3801a200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_t_s_dw },
-    { 0x3801aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_t_d_dw },
-    /* uint/float */
-    { 0x38028200, 0xfc1ffe20, FOP_WEW = gen_helper_fcnv_uw_s },
-    { 0x38028a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_udw_s },
-    { 0x3802a200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_uw_d },
-    { 0x3802aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_udw_d },
-    /* float/uint */
-    { 0x38030200, 0xfc1ffe20, FOP_WEW = gen_helper_fcnv_s_uw },
-    { 0x38030a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_d_uw },
-    { 0x38032200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_s_udw },
-    { 0x38032a00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_d_udw },
-    /* float/uint truncate */
-    { 0x38038200, 0xfc1ffe20, FOP_WEW = gen_helper_fcnv_t_s_uw },
-    { 0x38038a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_t_d_uw },
-    { 0x3803a200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_t_s_udw },
-    { 0x3803aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_t_d_udw },
-
-    /* floating point class two */
-    { 0x38000400, 0xfc000f60, trans_fcmp_s_0e },
-    { 0x38000c00, 0xfc001fe0, trans_fcmp_d },
-};
+static bool trans_fmpy_d(DisasContext *ctx, arg_fclass3 *a)
+{
+    return do_fop_dedd(ctx, a->t, a->r1, a->r2, gen_helper_fmpy_d);
+}
 
-#undef FOP_WEW
-#undef FOP_DEW
-#undef FOP_WED
-#undef FOP_WEWW
-#undef FOP_DED
-#undef FOP_DEDD
+static bool trans_fdiv_f(DisasContext *ctx, arg_fclass3 *a)
+{
+    return do_fop_weww(ctx, a->t, a->r1, a->r2, gen_helper_fdiv_s);
+}
 
-/* Convert the fmpyadd single-precision register encodings to standard.  */
-static inline int fmpyadd_s_reg(unsigned r)
+static bool trans_fdiv_d(DisasContext *ctx, arg_fclass3 *a)
 {
-    return (r & 16) * 2 + 16 + (r & 15);
+    return do_fop_dedd(ctx, a->t, a->r1, a->r2, gen_helper_fdiv_d);
 }
 
-static bool trans_fmpyadd(DisasContext *ctx, uint32_t insn, bool is_sub)
+static bool trans_xmpyu(DisasContext *ctx, arg_xmpyu *a)
 {
-    unsigned tm = extract32(insn, 0, 5);
-    unsigned f = extract32(insn, 5, 1);
-    unsigned ra = extract32(insn, 6, 5);
-    unsigned ta = extract32(insn, 11, 5);
-    unsigned rm2 = extract32(insn, 16, 5);
-    unsigned rm1 = extract32(insn, 21, 5);
+    TCGv_i64 x, y;
 
     nullify_over(ctx);
 
-    /* Independent multiply & add/sub, with undefined behaviour
-       if outputs overlap inputs.  */
-    if (f == 0) {
-        tm = fmpyadd_s_reg(tm);
-        ra = fmpyadd_s_reg(ra);
-        ta = fmpyadd_s_reg(ta);
-        rm2 = fmpyadd_s_reg(rm2);
-        rm1 = fmpyadd_s_reg(rm1);
-        do_fop_weww(ctx, tm, rm1, rm2, gen_helper_fmpy_s);
-        do_fop_weww(ctx, ta, ta, ra,
-                    is_sub ? gen_helper_fsub_s : gen_helper_fadd_s);
-    } else {
-        do_fop_dedd(ctx, tm, rm1, rm2, gen_helper_fmpy_d);
-        do_fop_dedd(ctx, ta, ta, ra,
-                    is_sub ? gen_helper_fsub_d : gen_helper_fadd_d);
-    }
+    x = load_frw0_i64(a->r1);
+    y = load_frw0_i64(a->r2);
+    tcg_gen_mul_i64(x, x, y);
+    save_frd(a->t, x);
+    tcg_temp_free_i64(x);
+    tcg_temp_free_i64(y);
 
     return nullify_end(ctx);
 }
 
-static bool trans_fmpyfadd_s(DisasContext *ctx, uint32_t insn,
-                             const DisasInsn *di)
+/* Convert the fmpyadd single-precision register encodings to standard.  */
+static inline int fmpyadd_s_reg(unsigned r)
+{
+    return (r & 16) * 2 + 16 + (r & 15);
+}
+
+static bool do_fmpyadd_s(DisasContext *ctx, arg_mpyadd *a, bool is_sub)
 {
-    unsigned rt = assemble_rt64(insn);
-    unsigned neg = extract32(insn, 5, 1);
-    unsigned rm1 = assemble_ra64(insn);
-    unsigned rm2 = assemble_rb64(insn);
-    unsigned ra3 = assemble_rc64(insn);
-    TCGv_i32 a, b, c;
+    int tm = fmpyadd_s_reg(a->tm);
+    int ra = fmpyadd_s_reg(a->ra);
+    int ta = fmpyadd_s_reg(a->ta);
+    int rm2 = fmpyadd_s_reg(a->rm2);
+    int rm1 = fmpyadd_s_reg(a->rm1);
 
     nullify_over(ctx);
-    a = load_frw0_i32(rm1);
-    b = load_frw0_i32(rm2);
-    c = load_frw0_i32(ra3);
 
-    if (neg) {
-        gen_helper_fmpynfadd_s(a, cpu_env, a, b, c);
-    } else {
-        gen_helper_fmpyfadd_s(a, cpu_env, a, b, c);
-    }
+    do_fop_weww(ctx, tm, rm1, rm2, gen_helper_fmpy_s);
+    do_fop_weww(ctx, ta, ta, ra,
+                is_sub ? gen_helper_fsub_s : gen_helper_fadd_s);
 
-    tcg_temp_free_i32(b);
-    tcg_temp_free_i32(c);
-    save_frw_i32(rt, a);
-    tcg_temp_free_i32(a);
     return nullify_end(ctx);
 }
 
-static bool trans_fmpyfadd_d(DisasContext *ctx, uint32_t insn,
-                             const DisasInsn *di)
+static bool trans_fmpyadd_f(DisasContext *ctx, arg_mpyadd *a)
+{
+    return do_fmpyadd_s(ctx, a, false);
+}
+
+static bool trans_fmpysub_f(DisasContext *ctx, arg_mpyadd *a)
 {
-    unsigned rt = extract32(insn, 0, 5);
-    unsigned neg = extract32(insn, 5, 1);
-    unsigned rm1 = extract32(insn, 21, 5);
-    unsigned rm2 = extract32(insn, 16, 5);
-    unsigned ra3 = assemble_rc64(insn);
-    TCGv_i64 a, b, c;
+    return do_fmpyadd_s(ctx, a, true);
+}
 
+static bool do_fmpyadd_d(DisasContext *ctx, arg_mpyadd *a, bool is_sub)
+{
     nullify_over(ctx);
-    a = load_frd0(rm1);
-    b = load_frd0(rm2);
-    c = load_frd0(ra3);
 
-    if (neg) {
-        gen_helper_fmpynfadd_d(a, cpu_env, a, b, c);
-    } else {
-        gen_helper_fmpyfadd_d(a, cpu_env, a, b, c);
-    }
+    do_fop_dedd(ctx, a->tm, a->rm1, a->rm2, gen_helper_fmpy_d);
+    do_fop_dedd(ctx, a->ta, a->ta, a->ra,
+                is_sub ? gen_helper_fsub_d : gen_helper_fadd_d);
 
-    tcg_temp_free_i64(b);
-    tcg_temp_free_i64(c);
-    save_frd(rt, a);
-    tcg_temp_free_i64(a);
     return nullify_end(ctx);
 }
 
-static const DisasInsn table_fp_fused[] = {
-    { 0xb8000000u, 0xfc000800u, trans_fmpyfadd_s },
-    { 0xb8000800u, 0xfc0019c0u, trans_fmpyfadd_d }
-};
-
-static void translate_table_int(DisasContext *ctx, uint32_t insn,
-                                const DisasInsn table[], size_t n)
+static bool trans_fmpyadd_d(DisasContext *ctx, arg_mpyadd *a)
 {
-    size_t i;
-    for (i = 0; i < n; ++i) {
-        if ((insn & table[i].mask) == table[i].insn) {
-            table[i].trans(ctx, insn, &table[i]);
-            return;
-        }
-    }
-    qemu_log_mask(LOG_UNIMP, "UNIMP insn %08x @ " TARGET_FMT_lx "\n",
-                  insn, ctx->base.pc_next);
-    gen_illegal(ctx);
+    return do_fmpyadd_d(ctx, a, false);
 }
 
-#define translate_table(ctx, insn, table) \
-    translate_table_int(ctx, insn, table, ARRAY_SIZE(table))
+static bool trans_fmpysub_d(DisasContext *ctx, arg_mpyadd *a)
+{
+    return do_fmpyadd_d(ctx, a, true);
+}
 
-static void translate_one(DisasContext *ctx, uint32_t insn)
+static bool trans_fmpyfadd_f(DisasContext *ctx, arg_fmpyfadd_f *a)
 {
-    uint32_t opc;
+    TCGv_i32 x, y, z;
 
-    /* Transition to the auto-generated decoder.  */
-    if (decode(ctx, insn)) {
-        return;
+    nullify_over(ctx);
+    x = load_frw0_i32(a->rm1);
+    y = load_frw0_i32(a->rm2);
+    z = load_frw0_i32(a->ra3);
+
+    if (a->neg) {
+        gen_helper_fmpynfadd_s(x, cpu_env, x, y, z);
+    } else {
+        gen_helper_fmpyfadd_s(x, cpu_env, x, y, z);
     }
 
-    opc = extract32(insn, 26, 6);
-    switch (opc) {
-    case 0x06:
-        trans_fmpyadd(ctx, insn, false);
-        return;
-    case 0x08:
-        trans_ldil(ctx, insn);
-        return;
-    case 0x09:
-        trans_copr_w(ctx, insn);
-        return;
-    case 0x0A:
-        trans_addil(ctx, insn);
-        return;
-    case 0x0B:
-        trans_copr_dw(ctx, insn);
-        return;
-    case 0x0C:
-        translate_table(ctx, insn, table_float_0c);
-        return;
-    case 0x0D:
-        trans_ldo(ctx, insn);
-        return;
-    case 0x0E:
-        translate_table(ctx, insn, table_float_0e);
-        return;
+    tcg_temp_free_i32(y);
+    tcg_temp_free_i32(z);
+    save_frw_i32(a->t, x);
+    tcg_temp_free_i32(x);
+    return nullify_end(ctx);
+}
 
-    case 0x10:
-        trans_load(ctx, insn, false, MO_UB);
-        return;
-    case 0x11:
-        trans_load(ctx, insn, false, MO_TEUW);
-        return;
-    case 0x12:
-        trans_load(ctx, insn, false, MO_TEUL);
-        return;
-    case 0x13:
-        trans_load(ctx, insn, true, MO_TEUL);
-        return;
-    case 0x16:
-        trans_fload_mod(ctx, insn);
-        return;
-    case 0x17:
-        trans_load_w(ctx, insn);
-        return;
-    case 0x18:
-        trans_store(ctx, insn, false, MO_UB);
-        return;
-    case 0x19:
-        trans_store(ctx, insn, false, MO_TEUW);
-        return;
-    case 0x1A:
-        trans_store(ctx, insn, false, MO_TEUL);
-        return;
-    case 0x1B:
-        trans_store(ctx, insn, true, MO_TEUL);
-        return;
-    case 0x1E:
-        trans_fstore_mod(ctx, insn);
-        return;
-    case 0x1F:
-        trans_store_w(ctx, insn);
-        return;
+static bool trans_fmpyfadd_d(DisasContext *ctx, arg_fmpyfadd_d *a)
+{
+    TCGv_i64 x, y, z;
 
-    case 0x20:
-        trans_cmpb(ctx, insn, true, false, false);
-        return;
-    case 0x21:
-        trans_cmpb(ctx, insn, true, true, false);
-        return;
-    case 0x22:
-        trans_cmpb(ctx, insn, false, false, false);
-        return;
-    case 0x23:
-        trans_cmpb(ctx, insn, false, true, false);
-        return;
-    case 0x24:
-        trans_cmpiclr(ctx, insn);
-        return;
-    case 0x25:
-        trans_subi(ctx, insn);
-        return;
-    case 0x26:
-        trans_fmpyadd(ctx, insn, true);
-        return;
-    case 0x27:
-        trans_cmpb(ctx, insn, true, false, true);
-        return;
-    case 0x28:
-        trans_addb(ctx, insn, true, false);
-        return;
-    case 0x29:
-        trans_addb(ctx, insn, true, true);
-        return;
-    case 0x2A:
-        trans_addb(ctx, insn, false, false);
-        return;
-    case 0x2B:
-        trans_addb(ctx, insn, false, true);
-        return;
-    case 0x2C:
-    case 0x2D:
-        trans_addi(ctx, insn);
-        return;
-    case 0x2E:
-        translate_table(ctx, insn, table_fp_fused);
-        return;
-    case 0x2F:
-        trans_cmpb(ctx, insn, false, false, true);
-        return;
+    nullify_over(ctx);
+    x = load_frd0(a->rm1);
+    y = load_frd0(a->rm2);
+    z = load_frd0(a->ra3);
 
-    case 0x30:
-    case 0x31:
-        trans_bb(ctx, insn);
-        return;
-    case 0x32:
-        trans_movb(ctx, insn, false);
-        return;
-    case 0x33:
-        trans_movb(ctx, insn, true);
-        return;
-    case 0x34:
-        translate_table(ctx, insn, table_sh_ex);
-        return;
-    case 0x35:
-        translate_table(ctx, insn, table_depw);
-        return;
-    case 0x38:
-        trans_be(ctx, insn, false);
-        return;
-    case 0x39:
-        trans_be(ctx, insn, true);
-        return;
-    case 0x3A:
-        translate_table(ctx, insn, table_branch);
-        return;
+    if (a->neg) {
+        gen_helper_fmpynfadd_d(x, cpu_env, x, y, z);
+    } else {
+        gen_helper_fmpyfadd_d(x, cpu_env, x, y, z);
+    }
 
-    case 0x04: /* spopn */
-    case 0x05: /* diag */
-    case 0x0F: /* product specific */
-        break;
+    tcg_temp_free_i64(y);
+    tcg_temp_free_i64(z);
+    save_frd(a->t, x);
+    tcg_temp_free_i64(x);
+    return nullify_end(ctx);
+}
 
-    case 0x07: /* unassigned */
-    case 0x15: /* unassigned */
-    case 0x1D: /* unassigned */
-    case 0x37: /* unassigned */
-        break;
-    case 0x3F:
-#ifndef CONFIG_USER_ONLY
-        /* Unassigned, but use as system-halt.  */
-        if (insn == 0xfffdead0) {
-            gen_hlt(ctx, 0); /* halt system */
-            return;
-        }
-        if (insn == 0xfffdead1) {
-            gen_hlt(ctx, 1); /* reset system */
-            return;
-        }
-#endif
-        break;
-    default:
-        break;
-    }
-    gen_illegal(ctx);
+static bool trans_diag(DisasContext *ctx, arg_diag *a)
+{
+    qemu_log_mask(LOG_UNIMP, "DIAG opcode ignored\n");
+    cond_free(&ctx->null_cond);
+    return true;
 }
 
 static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
@@ -4645,7 +4235,9 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
             ret = DISAS_NEXT;
         } else {
             ctx->insn = insn;
-            translate_one(ctx, insn);
+            if (!decode(ctx, insn)) {
+                gen_illegal(ctx);
+            }
             ret = ctx->base.is_jmp;
             assert(ctx->null_lab == NULL);
         }
@@ -4681,19 +4273,31 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
     ctx->iaoq_b = ctx->iaoq_n;
     ctx->base.pc_next += 4;
 
-    if (ret == DISAS_NORETURN || ret == DISAS_IAQ_N_UPDATED) {
-        return;
-    }
-    if (ctx->iaoq_f == -1) {
-        tcg_gen_mov_reg(cpu_iaoq_f, cpu_iaoq_b);
-        copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var);
+    switch (ret) {
+    case DISAS_NORETURN:
+    case DISAS_IAQ_N_UPDATED:
+        break;
+
+    case DISAS_NEXT:
+    case DISAS_IAQ_N_STALE:
+    case DISAS_IAQ_N_STALE_EXIT:
+        if (ctx->iaoq_f == -1) {
+            tcg_gen_mov_reg(cpu_iaoq_f, cpu_iaoq_b);
+            copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var);
 #ifndef CONFIG_USER_ONLY
-        tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b);
+            tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b);
 #endif
-        nullify_save(ctx);
-        ctx->base.is_jmp = DISAS_IAQ_N_UPDATED;
-    } else if (ctx->iaoq_b == -1) {
-        tcg_gen_mov_reg(cpu_iaoq_b, ctx->iaoq_n_var);
+            nullify_save(ctx);
+            ctx->base.is_jmp = (ret == DISAS_IAQ_N_STALE_EXIT
+                                ? DISAS_EXIT
+                                : DISAS_IAQ_N_UPDATED);
+        } else if (ctx->iaoq_b == -1) {
+            tcg_gen_mov_reg(cpu_iaoq_b, ctx->iaoq_n_var);
+        }
+        break;
+
+    default:
+        g_assert_not_reached();
     }
 }
 
@@ -4715,11 +4319,12 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
     case DISAS_IAQ_N_UPDATED:
         if (ctx->base.singlestep_enabled) {
             gen_excp_1(EXCP_DEBUG);
-        } else if (is_jmp == DISAS_IAQ_N_STALE_EXIT) {
-            tcg_gen_exit_tb(NULL, 0);
-        } else {
+        } else if (is_jmp != DISAS_IAQ_N_STALE_EXIT) {
             tcg_gen_lookup_and_goto_ptr();
         }
+        /* FALLTHRU */
+    case DISAS_EXIT:
+        tcg_gen_exit_tb(NULL, 0);
         break;
     default:
         g_assert_not_reached();
@@ -4761,11 +4366,10 @@ static const TranslatorOps hppa_tr_ops = {
     .disas_log          = hppa_tr_disas_log,
 };
 
-void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb)
-
+void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns)
 {
     DisasContext ctx;
-    translator_loop(&hppa_tr_ops, &ctx.base, cs, tb);
+    translator_loop(&hppa_tr_ops, &ctx.base, cs, tb, max_insns);
 }
 
 void restore_state_to_opc(CPUHPPAState *env, TranslationBlock *tb,
This page took 0.124237 seconds and 4 git commands to generate.