summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS1
-rw-r--r--hw/hppa/dino.c8
-rw-r--r--hw/riscv/sifive_e.c2
-rw-r--r--hw/riscv/sifive_u.c2
-rw-r--r--hw/riscv/spike.c2
-rw-r--r--hw/riscv/virt.c2
-rw-r--r--linux-user/riscv/signal.c4
-rw-r--r--target/hppa/Makefile.objs8
-rw-r--r--target/hppa/insns.decode527
-rw-r--r--target/hppa/op_helper.c16
-rw-r--r--target/hppa/translate.c3225
-rw-r--r--target/riscv/cpu.c2
-rw-r--r--target/riscv/cpu.h31
-rw-r--r--target/riscv/cpu_bits.h11
-rw-r--r--target/riscv/cpu_helper.c10
-rw-r--r--target/riscv/csr.c103
-rw-r--r--target/riscv/fpu_helper.c6
-rw-r--r--target/riscv/op_helper.c47
-rw-r--r--target/riscv/translate.c290
19 files changed, 2250 insertions, 2047 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index a2da141a92..e170a4c733 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -259,7 +259,6 @@ F: include/hw/ppc/
 F: disas/ppc.c
 
 RISC-V
-M: Michael Clark <mjc@sifive.com>
 M: Palmer Dabbelt <palmer@sifive.com>
 M: Alistair Francis <Alistair.Francis@wdc.com>
 M: Sagar Karandikar <sagark@eecs.berkeley.edu>
diff --git a/hw/hppa/dino.c b/hw/hppa/dino.c
index 31e09942b5..360716de57 100644
--- a/hw/hppa/dino.c
+++ b/hw/hppa/dino.c
@@ -105,6 +105,7 @@ typedef struct DinoState {
     MemoryRegion bm;
     MemoryRegion bm_ram_alias;
     MemoryRegion bm_pci_alias;
+    MemoryRegion bm_cpu_alias;
 
     MemoryRegion cpu0_eir_mem;
 } DinoState;
@@ -473,12 +474,17 @@ PCIBus *dino_init(MemoryRegion *addr_space,
     memory_region_init_alias(&s->bm_pci_alias, OBJECT(s),
                              "bm-pci", &s->pci_mem,
                              0xf0000000 + DINO_MEM_CHUNK_SIZE,
-                             31 * DINO_MEM_CHUNK_SIZE);
+                             30 * DINO_MEM_CHUNK_SIZE);
+    memory_region_init_alias(&s->bm_cpu_alias, OBJECT(s),
+                             "bm-cpu", addr_space, 0xfff00000,
+                             0xfffff);
     memory_region_add_subregion(&s->bm, 0,
                                 &s->bm_ram_alias);
     memory_region_add_subregion(&s->bm,
                                 0xf0000000 + DINO_MEM_CHUNK_SIZE,
                                 &s->bm_pci_alias);
+    memory_region_add_subregion(&s->bm, 0xfff00000,
+                                &s->bm_cpu_alias);
     address_space_init(&s->bm_as, &s->bm, "pci-bm");
     pci_setup_iommu(b, dino_pcihost_set_iommu, s);
 
diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c
index bfc086609c..b1cd11363c 100644
--- a/hw/riscv/sifive_e.c
+++ b/hw/riscv/sifive_e.c
@@ -74,7 +74,7 @@ static const struct MemmapEntry {
     [SIFIVE_E_DTIM] =     { 0x80000000,     0x4000 }
 };
 
-static uint64_t load_kernel(const char *kernel_filename)
+static target_ulong load_kernel(const char *kernel_filename)
 {
     uint64_t kernel_entry, kernel_high;
 
diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c
index 2730b25b60..7bc25820fe 100644
--- a/hw/riscv/sifive_u.c
+++ b/hw/riscv/sifive_u.c
@@ -65,7 +65,7 @@ static const struct MemmapEntry {
 
 #define GEM_REVISION        0x10070109
 
-static uint64_t load_kernel(const char *kernel_filename)
+static target_ulong load_kernel(const char *kernel_filename)
 {
     uint64_t kernel_entry, kernel_high;
 
diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c
index c66ffc50cc..2a000a5800 100644
--- a/hw/riscv/spike.c
+++ b/hw/riscv/spike.c
@@ -53,7 +53,7 @@ static const struct MemmapEntry {
     [SPIKE_DRAM] =     { 0x80000000,        0x0 },
 };
 
-static uint64_t load_kernel(const char *kernel_filename)
+static target_ulong load_kernel(const char *kernel_filename)
 {
     uint64_t kernel_entry, kernel_high;
 
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index 3e8b19c668..fc4c6b306e 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -62,7 +62,7 @@ static const struct MemmapEntry {
     [VIRT_PCIE_ECAM] =   { 0x30000000,    0x10000000 },
 };
 
-static uint64_t load_kernel(const char *kernel_filename)
+static target_ulong load_kernel(const char *kernel_filename)
 {
     uint64_t kernel_entry, kernel_high;
 
diff --git a/linux-user/riscv/signal.c b/linux-user/riscv/signal.c
index f598d41891..83ecc6f799 100644
--- a/linux-user/riscv/signal.c
+++ b/linux-user/riscv/signal.c
@@ -83,7 +83,7 @@ static void setup_sigcontext(struct target_sigcontext *sc, CPURISCVState *env)
         __put_user(env->fpr[i], &sc->fpr[i]);
     }
 
-    uint32_t fcsr = csr_read_helper(env, CSR_FCSR); /*riscv_get_fcsr(env);*/
+    uint32_t fcsr = riscv_csr_read(env, CSR_FCSR);
     __put_user(fcsr, &sc->fcsr);
 }
 
@@ -159,7 +159,7 @@ static void restore_sigcontext(CPURISCVState *env, struct target_sigcontext *sc)
 
     uint32_t fcsr;
     __get_user(fcsr, &sc->fcsr);
-    csr_write_helper(env, fcsr, CSR_FCSR);
+    riscv_csr_write(env, CSR_FCSR, fcsr);
 }
 
 static void restore_ucontext(CPURISCVState *env, struct target_ucontext *uc)
diff --git a/target/hppa/Makefile.objs b/target/hppa/Makefile.objs
index 3359da5341..174f50a96c 100644
--- a/target/hppa/Makefile.objs
+++ b/target/hppa/Makefile.objs
@@ -1,3 +1,11 @@
 obj-y += translate.o helper.o cpu.o op_helper.o gdbstub.o mem_helper.o
 obj-y += int_helper.o
 obj-$(CONFIG_SOFTMMU) += machine.o
+
+DECODETREE = $(SRC_PATH)/scripts/decodetree.py
+
+target/hppa/decode.inc.c: $(SRC_PATH)/target/hppa/insns.decode $(DECODETREE)
+	$(call quiet-command,\
+	  $(PYTHON) $(DECODETREE) -o $@ $<, "GEN", $(TARGET_DIR)$@)
+
+target/hppa/translate.o: target/hppa/decode.inc.c
diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode
new file mode 100644
index 0000000000..55ff39dd05
--- /dev/null
+++ b/target/hppa/insns.decode
@@ -0,0 +1,527 @@
+#
+# HPPA instruction decode definitions.
+#
+# Copyright (c) 2018 Richard Henderson <rth@twiddle.net>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, see <http://www.gnu.org/licenses/>.
+#
+
+####
+# Field definitions
+####
+
+%assemble_sr3   13:1 14:2
+%assemble_sr3x  13:1 14:2 !function=expand_sr3x
+
+%assemble_11a   0:s1 4:10            !function=expand_shl3
+%assemble_12    0:s1 2:1 3:10        !function=expand_shl2
+%assemble_12a   0:s1 3:11            !function=expand_shl2
+%assemble_17    0:s1 16:5 2:1 3:10   !function=expand_shl2
+%assemble_22    0:s1 16:10 2:1 3:10  !function=expand_shl2
+
+%assemble_21    0:s1 1:11 14:2 16:5 12:2  !function=expand_shl11
+
+%lowsign_11     0:s1 1:10
+%lowsign_14     0:s1 1:13
+
+%sm_imm         16:10 !function=expand_sm_imm
+
+%rm64           1:1 16:5
+%rt64           6:1 0:5
+%ra64           7:1 21:5
+%rb64           12:1 16:5
+%rc64           8:1 13:3 9:2
+%rc32           13:3 9:2
+
+%im5_0          0:s1 1:4
+%im5_16         16:s1 17:4
+%ma_to_m        5:1 13:1 !function=ma_to_m
+%ma2_to_m       2:2      !function=ma_to_m
+%pos_to_m       0:1      !function=pos_to_m
+%neg_to_m       0:1      !function=neg_to_m
+%a_to_m         2:1      !function=neg_to_m
+
+####
+# Argument set definitions
+####
+
+# All insns that need to form a virtual address should use this set.
+&ldst           t b x disp sp m scale size
+
+&rr_cf          t r cf
+&rrr_cf         t r1 r2 cf
+&rrr_cf_sh      t r1 r2 cf sh
+&rri_cf         t r i cf
+
+&rrb_c_f        disp n c f r1 r2
+&rib_c_f        disp n c f r i
+
+####
+# Format definitions
+####
+
+@rr_cf          ...... r:5 ..... cf:4 ....... t:5       &rr_cf
+@rrr_cf         ...... r2:5 r1:5 cf:4 ....... t:5       &rrr_cf
+@rrr_cf_sh      ...... r2:5 r1:5 cf:4 .... sh:2 . t:5   &rrr_cf_sh
+@rrr_cf_sh0     ...... r2:5 r1:5 cf:4 ....... t:5       &rrr_cf_sh sh=0
+@rri_cf         ...... r:5  t:5  cf:4 . ...........     &rri_cf i=%lowsign_11
+
+@rrb_cf         ...... r2:5 r1:5 c:3 ........... n:1 .  \
+                &rrb_c_f disp=%assemble_12
+@rib_cf         ...... r:5 ..... c:3 ........... n:1 .  \
+                &rib_c_f disp=%assemble_12 i=%im5_16
+
+####
+# System
+####
+
+break           000000 ----- ----- --- 00000000 -----
+
+mtsp            000000 ----- r:5   ... 11000001 00000   sp=%assemble_sr3
+mtctl           000000 t:5   r:5   --- 11000010 00000
+mtsarcm         000000 01011 r:5   --- 11000110 00000
+mtsm            000000 00000 r:5   000 11000011 00000
+
+mfia            000000 ----- 00000 ---   10100101 t:5
+mfsp            000000 ----- 00000 ...   00100101 t:5   sp=%assemble_sr3
+mfctl           000000 r:5   00000- e:1 -01000101 t:5
+
+sync            000000 ----- ----- 000 00100000 00000   # sync, syncdma
+
+ldsid           000000 b:5   ----- sp:2 0 10000101 t:5
+
+rsm             000000 ..........  000 01110011 t:5     i=%sm_imm
+ssm             000000 ..........  000 01101011 t:5     i=%sm_imm
+
+rfi             000000 ----- ----- --- 01100000 00000
+rfi_r           000000 ----- ----- --- 01100101 00000
+
+# These are artificial instructions used by QEMU firmware.
+# They are allocated from the unassigned instruction space.
+halt            1111 1111 1111 1101 1110 1010 1101 0000
+reset           1111 1111 1111 1101 1110 1010 1101 0001
+
+####
+# Memory Management
+####
+
+@addrx          ...... b:5 x:5 .. ........ m:1 .....    \
+                &ldst disp=0 scale=0 t=0 sp=0 size=0
+
+nop             000001 ----- ----- -- 11001010 0 -----         # fdc, disp
+nop_addrx       000001 ..... ..... -- 01001010 . -----  @addrx # fdc, index
+nop_addrx       000001 ..... ..... -- 01001011 . -----  @addrx # fdce
+nop_addrx       000001 ..... ..... --- 0001010 . -----  @addrx # fic 0x0a
+nop_addrx       000001 ..... ..... -- 01001111 . 00000  @addrx # fic 0x4f
+nop_addrx       000001 ..... ..... --- 0001011 . -----  @addrx # fice
+nop_addrx       000001 ..... ..... -- 01001110 . 00000  @addrx # pdc
+
+probe           000001 b:5 ri:5 sp:2 imm:1 100011 write:1 0 t:5
+
+ixtlbx          000001 b:5 r:5 sp:2 0100000 addr:1 0 00000      data=1
+ixtlbx          000001 b:5 r:5 ... 000000 addr:1 0 00000        \
+                sp=%assemble_sr3x data=0
+
+pxtlbx          000001 b:5 x:5 sp:2 0100100 local:1 m:1 -----   data=1
+pxtlbx          000001 b:5 x:5 ... 000100 local:1 m:1 -----     \
+                sp=%assemble_sr3x data=0
+
+lpa             000001 b:5 x:5 sp:2 01001101 m:1 t:5    \
+                &ldst disp=0 scale=0 size=0
+
+lci             000001 ----- ----- -- 01001100 0 t:5
+
+####
+# Arith/Log
+####
+
+andcm           000010 ..... ..... .... 000000 0 .....  @rrr_cf
+and             000010 ..... ..... .... 001000 0 .....  @rrr_cf
+or              000010 ..... ..... .... 001001 0 .....  @rrr_cf
+xor             000010 ..... ..... .... 001010 0 .....  @rrr_cf
+uxor            000010 ..... ..... .... 001110 0 .....  @rrr_cf
+ds              000010 ..... ..... .... 010001 0 .....  @rrr_cf
+cmpclr          000010 ..... ..... .... 100010 0 .....  @rrr_cf
+uaddcm          000010 ..... ..... .... 100110 0 .....  @rrr_cf
+uaddcm_tc       000010 ..... ..... .... 100111 0 .....  @rrr_cf
+dcor            000010 ..... 00000 .... 101110 0 .....  @rr_cf
+dcor_i          000010 ..... 00000 .... 101111 0 .....  @rr_cf
+
+add             000010 ..... ..... .... 0110.. 0 .....  @rrr_cf_sh
+add_l           000010 ..... ..... .... 1010.. 0 .....  @rrr_cf_sh
+add_tsv         000010 ..... ..... .... 1110.. 0 .....  @rrr_cf_sh
+add_c           000010 ..... ..... .... 011100 0 .....  @rrr_cf_sh0
+add_c_tsv       000010 ..... ..... .... 111100 0 .....  @rrr_cf_sh0
+
+sub             000010 ..... ..... .... 010000 0 .....  @rrr_cf
+sub_tsv         000010 ..... ..... .... 110000 0 .....  @rrr_cf
+sub_tc          000010 ..... ..... .... 010011 0 .....  @rrr_cf
+sub_tsv_tc      000010 ..... ..... .... 110011 0 .....  @rrr_cf
+sub_b           000010 ..... ..... .... 010100 0 .....  @rrr_cf
+sub_b_tsv       000010 ..... ..... .... 110100 0 .....  @rrr_cf
+
+ldil            001000 t:5 .....................        i=%assemble_21
+addil           001010 r:5 .....................        i=%assemble_21
+ldo             001101 b:5 t:5 -- ..............        i=%lowsign_14
+
+addi            101101 ..... ..... .... 0 ...........   @rri_cf
+addi_tsv        101101 ..... ..... .... 1 ...........   @rri_cf
+addi_tc         101100 ..... ..... .... 0 ...........   @rri_cf
+addi_tc_tsv     101100 ..... ..... .... 1 ...........   @rri_cf
+
+subi            100101 ..... ..... .... 0 ...........   @rri_cf
+subi_tsv        100101 ..... ..... .... 1 ...........   @rri_cf
+
+cmpiclr         100100 ..... ..... .... 0 ...........   @rri_cf
+
+####
+# Index Mem
+####
+
+@ldstx          ...... b:5 x:5 sp:2 scale:1 ....... m:1 t:5     &ldst disp=0
+@ldim5          ...... b:5 ..... sp:2 ......... t:5     \
+                &ldst disp=%im5_16 x=0 scale=0 m=%ma_to_m
+@stim5          ...... b:5 t:5 sp:2 ......... .....     \
+                &ldst disp=%im5_0 x=0 scale=0 m=%ma_to_m
+
+ld              000011 ..... ..... .. . 1 -- 00 size:2 ......   @ldim5
+ld              000011 ..... ..... .. . 0 -- 00 size:2 ......   @ldstx
+st              000011 ..... ..... .. . 1 -- 10 size:2 ......   @stim5
+ldc             000011 ..... ..... .. . 1 -- 0111      ......   @ldim5 size=2
+ldc             000011 ..... ..... .. . 0 -- 0111      ......   @ldstx size=2
+lda             000011 ..... ..... .. . 1 -- 0110      ......   @ldim5 size=2
+lda             000011 ..... ..... .. . 0 -- 0110      ......   @ldstx size=2
+sta             000011 ..... ..... .. . 1 -- 1110      ......   @stim5 size=2
+stby            000011 b:5 r:5 sp:2 a:1 1 -- 1100 m:1   .....   disp=%im5_0
+
+@fldstwx        ...... b:5 x:5   sp:2 scale:1 ....... m:1 ..... \
+                &ldst t=%rt64 disp=0 size=2
+@fldstwi        ...... b:5 ..... sp:2 .       ....... .   ..... \
+                &ldst t=%rt64 disp=%im5_16 m=%ma_to_m x=0 scale=0 size=2
+
+fldw            001001 ..... ..... .. . 0 -- 000 . . .....      @fldstwx
+fldw            001001 ..... ..... .. . 1 -- 000 . . .....      @fldstwi
+fstw            001001 ..... ..... .. . 0 -- 100 . . .....      @fldstwx
+fstw            001001 ..... ..... .. . 1 -- 100 . . .....      @fldstwi
+
+@fldstdx        ...... b:5 x:5   sp:2 scale:1 ....... m:1 t:5 \
+                &ldst disp=0 size=3
+@fldstdi        ...... b:5 ..... sp:2 .       ....... .   t:5 \
+                &ldst disp=%im5_16 m=%ma_to_m x=0 scale=0 size=3
+
+fldd            001011 ..... ..... .. . 0 -- 000 0 . .....      @fldstdx
+fldd            001011 ..... ..... .. . 1 -- 000 0 . .....      @fldstdi
+fstd            001011 ..... ..... .. . 0 -- 100 0 . .....      @fldstdx
+fstd            001011 ..... ..... .. . 1 -- 100 0 . .....      @fldstdi
+
+####
+# Offset Mem
+####
+
+@ldstim14       ...... b:5 t:5 sp:2 ..............      \
+                &ldst disp=%lowsign_14 x=0 scale=0 m=0
+@ldstim14m      ...... b:5 t:5 sp:2 ..............      \
+                &ldst disp=%lowsign_14 x=0 scale=0 m=%neg_to_m
+@ldstim12m      ...... b:5 t:5 sp:2 ..............      \
+                &ldst disp=%assemble_12a x=0 scale=0 m=%pos_to_m
+
+# LDB, LDH, LDW, LDWM
+ld              010000 ..... ..... .. ..............    @ldstim14  size=0
+ld              010001 ..... ..... .. ..............    @ldstim14  size=1
+ld              010010 ..... ..... .. ..............    @ldstim14  size=2
+ld              010011 ..... ..... .. ..............    @ldstim14m size=2
+ld              010111 ..... ..... .. ...........10.    @ldstim12m size=2
+
+# STB, STH, STW, STWM
+st              011000 ..... ..... .. ..............    @ldstim14  size=0
+st              011001 ..... ..... .. ..............    @ldstim14  size=1
+st              011010 ..... ..... .. ..............    @ldstim14  size=2
+st              011011 ..... ..... .. ..............    @ldstim14m size=2
+st              011111 ..... ..... .. ...........10.    @ldstim12m size=2
+
+fldw            010110 b:5 ..... sp:2 ..............    \
+                &ldst disp=%assemble_12a t=%rm64 m=%a_to_m x=0 scale=0 size=2
+fldw            010111 b:5 ..... sp:2 ...........0..    \
+                &ldst disp=%assemble_12a t=%rm64 m=0 x=0 scale=0 size=2
+
+fstw            011110 b:5 ..... sp:2 ..............    \
+                &ldst disp=%assemble_12a t=%rm64 m=%a_to_m x=0 scale=0 size=2
+fstw            011111 b:5 ..... sp:2 ...........0..    \
+                &ldst disp=%assemble_12a t=%rm64 m=0 x=0 scale=0 size=2
+
+fldd            010100 b:5 t:5   sp:2 .......... .. 1 . \
+                &ldst disp=%assemble_11a m=%ma2_to_m x=0 scale=0 size=3
+
+fstd            011100 b:5 t:5   sp:2 .......... .. 1 . \
+                &ldst disp=%assemble_11a m=%ma2_to_m x=0 scale=0 size=3
+
+####
+# Floating-point Multiply Add
+####
+
+&mpyadd         rm1 rm2 ta ra tm
+@mpyadd         ...... rm1:5 rm2:5 ta:5 ra:5 . tm:5     &mpyadd
+
+fmpyadd_f       000110 ..... ..... ..... ..... 0 .....  @mpyadd
+fmpyadd_d       000110 ..... ..... ..... ..... 1 .....  @mpyadd
+fmpysub_f       100110 ..... ..... ..... ..... 0 .....  @mpyadd
+fmpysub_d       100110 ..... ..... ..... ..... 1 .....  @mpyadd
+
+####
+# Conditional Branches
+####
+
+bb_sar          110000 00000 r:5 c:1 10 ........... n:1 .  disp=%assemble_12
+bb_imm          110001 p:5   r:5 c:1 10 ........... n:1 .  disp=%assemble_12
+
+movb            110010 ..... ..... ... ........... . .  @rrb_cf f=0
+movbi           110011 ..... ..... ... ........... . .  @rib_cf f=0
+
+cmpb            100000 ..... ..... ... ........... . .  @rrb_cf f=0
+cmpb            100010 ..... ..... ... ........... . .  @rrb_cf f=1
+cmpbi           100001 ..... ..... ... ........... . .  @rib_cf f=0
+cmpbi           100011 ..... ..... ... ........... . .  @rib_cf f=1
+
+addb            101000 ..... ..... ... ........... . .  @rrb_cf f=0
+addb            101010 ..... ..... ... ........... . .  @rrb_cf f=1
+addbi           101001 ..... ..... ... ........... . .  @rib_cf f=0
+addbi           101011 ..... ..... ... ........... . .  @rib_cf f=1
+
+####
+# Shift, Extract, Deposit
+####
+
+shrpw_sar       110100 r2:5 r1:5 c:3 00 0    00000  t:5
+shrpw_imm       110100 r2:5 r1:5 c:3 01 0    cpos:5 t:5
+
+extrw_sar       110100 r:5  t:5  c:3 10 se:1 00000  clen:5
+extrw_imm       110100 r:5  t:5  c:3 11 se:1 pos:5  clen:5
+
+depw_sar        110101 t:5 r:5   c:3 00 nz:1 00000  clen:5
+depw_imm        110101 t:5 r:5   c:3 01 nz:1 cpos:5 clen:5
+depwi_sar       110101 t:5 ..... c:3 10 nz:1 00000  clen:5      i=%im5_16
+depwi_imm       110101 t:5 ..... c:3 11 nz:1 cpos:5 clen:5      i=%im5_16
+
+####
+# Branch External
+####
+
+&BE             b l n disp sp
+@be             ...... b:5 ..... ... ........... n:1 .  \
+                &BE disp=%assemble_17 sp=%assemble_sr3
+
+be              111000 ..... ..... ... ........... . .  @be l=0
+be              111001 ..... ..... ... ........... . .  @be l=31
+
+####
+# Branch
+####
+
+&BL             l n disp
+@bl             ...... l:5 ..... ... ........... n:1 .  &BL disp=%assemble_17
+
+# B,L and B,L,PUSH
+bl              111010 ..... ..... 000 ........... .   .        @bl
+bl              111010 ..... ..... 100 ........... .   .        @bl
+# B,L (long displacement)
+bl              111010 ..... ..... 101 ........... n:1 .        &BL l=2 \
+                disp=%assemble_22
+b_gate          111010 ..... ..... 001 ........... .   .        @bl
+blr             111010 l:5   x:5   010 00000000000 n:1 0
+bv              111010 b:5   x:5   110 00000000000 n:1 0
+bve             111010 b:5   00000 110 10000000000 n:1 -        l=0
+bve             111010 b:5   00000 111 10000000000 n:1 -        l=2
+
+####
+# FP Fused Multiple-Add
+####
+
+fmpyfadd_f      101110 ..... ..... ... . 0 ... . . neg:1 ..... \
+                rm1=%ra64 rm2=%rb64 ra3=%rc64 t=%rt64
+fmpyfadd_d      101110 rm1:5 rm2:5 ... 0 1 ..0 0 0 neg:1 t:5    ra3=%rc32
+
+####
+# FP operations
+####
+
+&fclass01       r t
+&fclass2        r1 r2 c y
+&fclass3        r1 r2 t
+
+@f0c_0          ...... r:5  00000 ..... 00 000 0 t:5    &fclass01
+@f0c_1          ...... r:5  000.. ..... 01 000 0 t:5    &fclass01
+@f0c_2          ...... r1:5 r2:5 y:3 .. 10 000 . c:5    &fclass2
+@f0c_3          ...... r1:5 r2:5  ..... 11 000 0 t:5    &fclass3
+
+@f0e_f_0        ...... ..... 00000 ... 0 0 000 .. 0 .....  \
+                &fclass01 r=%ra64 t=%rt64
+@f0e_d_0        ...... r:5   00000 ... 0 1 000 00 0 t:5    &fclass01
+
+@f0e_ff_1       ...... ..... 000  ... 0000 010 .. 0 .....  \
+                &fclass01 r=%ra64 t=%rt64
+@f0e_fd_1       ...... ..... 000  ... 0100 010 .0 0 t:5    &fclass01 r=%ra64
+@f0e_df_1       ...... r:5   000  ... 0001 010 0. 0 .....  &fclass01 t=%rt64
+@f0e_dd_1       ...... r:5   000  ... 0101 010 00 0 t:5    &fclass01
+
+@f0e_f_2        ...... ..... ..... y:3 .0 100 .00 c:5      \
+                &fclass2 r1=%ra64 r2=%rb64
+@f0e_d_2        ...... r1:5  r2:5  y:3 01 100 000 c:5      &fclass2
+
+@f0e_f_3        ...... ..... ..... ... .0 110 ..0 .....    \
+                &fclass3 r1=%ra64 r2=%rb64 t=%rt64
+@f0e_d_3        ...... r1:5  r2:5  ... 01 110 000 t:5
+
+# Floating point class 0
+
+# FID.  With r = t = 0, which via fcpy puts 0 into fr0.
+# This is machine/revision = 0, which is reserved for simulator.
+fcpy_f          001100 00000 00000 00000 000000 00000   \
+                &fclass01 r=0 t=0
+
+fcpy_f          001100 ..... ..... 010 00 ...... .....  @f0c_0
+fabs_f          001100 ..... ..... 011 00 ...... .....  @f0c_0
+fsqrt_f         001100 ..... ..... 100 00 ...... .....  @f0c_0
+frnd_f          001100 ..... ..... 101 00 ...... .....  @f0c_0
+fneg_f          001100 ..... ..... 110 00 ...... .....  @f0c_0
+fnegabs_f       001100 ..... ..... 111 00 ...... .....  @f0c_0
+
+fcpy_d          001100 ..... ..... 010 01 ...... .....  @f0c_0
+fabs_d          001100 ..... ..... 011 01 ...... .....  @f0c_0
+fsqrt_d         001100 ..... ..... 100 01 ...... .....  @f0c_0
+frnd_d          001100 ..... ..... 101 01 ...... .....  @f0c_0
+fneg_d          001100 ..... ..... 110 01 ...... .....  @f0c_0
+fnegabs_d       001100 ..... ..... 111 01 ...... .....  @f0c_0
+
+fcpy_f          001110 ..... ..... 010 ........ .....   @f0e_f_0
+fabs_f          001110 ..... ..... 011 ........ .....   @f0e_f_0
+fsqrt_f         001110 ..... ..... 100 ........ .....   @f0e_f_0
+frnd_f          001110 ..... ..... 101 ........ .....   @f0e_f_0
+fneg_f          001110 ..... ..... 110 ........ .....   @f0e_f_0
+fnegabs_f       001110 ..... ..... 111 ........ .....   @f0e_f_0
+
+fcpy_d          001110 ..... ..... 010 ........ .....   @f0e_d_0
+fabs_d          001110 ..... ..... 011 ........ .....   @f0e_d_0
+fsqrt_d         001110 ..... ..... 100 ........ .....   @f0e_d_0
+frnd_d          001110 ..... ..... 101 ........ .....   @f0e_d_0
+fneg_d          001110 ..... ..... 110 ........ .....   @f0e_d_0
+fnegabs_d       001110 ..... ..... 111 ........ .....   @f0e_d_0
+
+# Floating point class 1
+
+# float/float
+fcnv_d_f        001100 ..... ... 000 00 01 ...... ..... @f0c_1
+fcnv_f_d        001100 ..... ... 000 01 00 ...... ..... @f0c_1
+
+fcnv_d_f        001110 ..... ... 000 .......... .....   @f0e_df_1
+fcnv_f_d        001110 ..... ... 000 .......... .....   @f0e_fd_1
+
+# int/float
+fcnv_w_f        001100 ..... ... 001 00 00 ...... ..... @f0c_1
+fcnv_q_f        001100 ..... ... 001 00 01 ...... ..... @f0c_1
+fcnv_w_d        001100 ..... ... 001 01 00 ...... ..... @f0c_1
+fcnv_q_d        001100 ..... ... 001 01 01 ...... ..... @f0c_1
+
+fcnv_w_f        001110 ..... ... 001 .......... .....   @f0e_ff_1
+fcnv_q_f        001110 ..... ... 001 .......... .....   @f0e_df_1
+fcnv_w_d        001110 ..... ... 001 .......... .....   @f0e_fd_1
+fcnv_q_d        001110 ..... ... 001 .......... .....   @f0e_dd_1
+
+# float/int
+fcnv_f_w        001100 ..... ... 010 00 00 ...... ..... @f0c_1
+fcnv_d_w        001100 ..... ... 010 00 01 ...... ..... @f0c_1
+fcnv_f_q        001100 ..... ... 010 01 00 ...... ..... @f0c_1
+fcnv_d_q        001100 ..... ... 010 01 01 ...... ..... @f0c_1
+
+fcnv_f_w        001110 ..... ... 010 .......... .....   @f0e_ff_1
+fcnv_d_w        001110 ..... ... 010 .......... .....   @f0e_df_1
+fcnv_f_q        001110 ..... ... 010 .......... .....   @f0e_fd_1
+fcnv_d_q        001110 ..... ... 010 .......... .....   @f0e_dd_1
+
+# float/int truncate
+fcnv_t_f_w      001100 ..... ... 011 00 00 ...... ..... @f0c_1
+fcnv_t_d_w      001100 ..... ... 011 00 01 ...... ..... @f0c_1
+fcnv_t_f_q      001100 ..... ... 011 01 00 ...... ..... @f0c_1
+fcnv_t_d_q      001100 ..... ... 011 01 01 ...... ..... @f0c_1
+
+fcnv_t_f_w      001110 ..... ... 011 .......... .....   @f0e_ff_1
+fcnv_t_d_w      001110 ..... ... 011 .......... .....   @f0e_df_1
+fcnv_t_f_q      001110 ..... ... 011 .......... .....   @f0e_fd_1
+fcnv_t_d_q      001110 ..... ... 011 .......... .....   @f0e_dd_1
+
+# uint/float
+fcnv_uw_f       001100 ..... ... 101 00 00 ...... ..... @f0c_1
+fcnv_uq_f       001100 ..... ... 101 00 01 ...... ..... @f0c_1
+fcnv_uw_d       001100 ..... ... 101 01 00 ...... ..... @f0c_1
+fcnv_uq_d       001100 ..... ... 101 01 01 ...... ..... @f0c_1
+
+fcnv_uw_f       001110 ..... ... 101 .......... .....   @f0e_ff_1
+fcnv_uq_f       001110 ..... ... 101 .......... .....   @f0e_df_1
+fcnv_uw_d       001110 ..... ... 101 .......... .....   @f0e_fd_1
+fcnv_uq_d       001110 ..... ... 101 .......... .....   @f0e_dd_1
+
+# float/int
+fcnv_f_uw       001100 ..... ... 110 00 00 ...... ..... @f0c_1
+fcnv_d_uw       001100 ..... ... 110 00 01 ...... ..... @f0c_1
+fcnv_f_uq       001100 ..... ... 110 01 00 ...... ..... @f0c_1
+fcnv_d_uq       001100 ..... ... 110 01 01 ...... ..... @f0c_1
+
+fcnv_f_uw       001110 ..... ... 110 .......... .....   @f0e_ff_1
+fcnv_d_uw       001110 ..... ... 110 .......... .....   @f0e_df_1
+fcnv_f_uq       001110 ..... ... 110 .......... .....   @f0e_fd_1
+fcnv_d_uq       001110 ..... ... 110 .......... .....   @f0e_dd_1
+
+# float/int truncate
+fcnv_t_f_uw     001100 ..... ... 111 00 00 ...... ..... @f0c_1
+fcnv_t_d_uw     001100 ..... ... 111 00 01 ...... ..... @f0c_1
+fcnv_t_f_uq     001100 ..... ... 111 01 00 ...... ..... @f0c_1
+fcnv_t_d_uq     001100 ..... ... 111 01 01 ...... ..... @f0c_1
+
+fcnv_t_f_uw     001110 ..... ... 111 .......... .....   @f0e_ff_1
+fcnv_t_d_uw     001110 ..... ... 111 .......... .....   @f0e_df_1
+fcnv_t_f_uq     001110 ..... ... 111 .......... .....   @f0e_fd_1
+fcnv_t_d_uq     001110 ..... ... 111 .......... .....   @f0e_dd_1
+
+# Floating point class 2
+
+ftest           001100 00000 00000 y:3 00 10000 1 c:5
+
+fcmp_f          001100 ..... ..... ... 00 ..... 0 ..... @f0c_2
+fcmp_d          001100 ..... ..... ... 01 ..... 0 ..... @f0c_2
+
+fcmp_f          001110 ..... ..... ... ..... ... .....  @f0e_f_2
+fcmp_d          001110 ..... ..... ... ..... ... .....  @f0e_d_2
+
+# Floating point class 3
+
+fadd_f          001100 ..... ..... 000 00 ...... .....  @f0c_3
+fsub_f          001100 ..... ..... 001 00 ...... .....  @f0c_3
+fmpy_f          001100 ..... ..... 010 00 ...... .....  @f0c_3
+fdiv_f          001100 ..... ..... 011 00 ...... .....  @f0c_3
+
+fadd_d          001100 ..... ..... 000 01 ...... .....  @f0c_3
+fsub_d          001100 ..... ..... 001 01 ...... .....  @f0c_3
+fmpy_d          001100 ..... ..... 010 01 ...... .....  @f0c_3
+fdiv_d          001100 ..... ..... 011 01 ...... .....  @f0c_3
+
+fadd_f          001110 ..... ..... 000 ..... ... .....  @f0e_f_3
+fsub_f          001110 ..... ..... 001 ..... ... .....  @f0e_f_3
+fmpy_f          001110 ..... ..... 010 ..... ... .....  @f0e_f_3
+fdiv_f          001110 ..... ..... 011 ..... ... .....  @f0e_f_3
+
+fadd_d          001110 ..... ..... 000 ..... ... .....  @f0e_d_3
+fsub_d          001110 ..... ..... 001 ..... ... .....  @f0e_d_3
+fmpy_d          001110 ..... ..... 010 ..... ... .....  @f0e_d_3
+fdiv_d          001110 ..... ..... 011 ..... ... .....  @f0e_d_3
+
+xmpyu           001110 ..... ..... 010 .0111 .00 t:5    r1=%ra64 r2=%rb64
diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c
index 6bf478e7b0..268caaaa20 100644
--- a/target/hppa/op_helper.c
+++ b/target/hppa/op_helper.c
@@ -81,10 +81,8 @@ static void atomic_store_3(CPUHPPAState *env, target_ulong addr, uint32_t val,
 }
 
 static void do_stby_b(CPUHPPAState *env, target_ulong addr, target_ureg val,
-                      bool parallel)
+                      bool parallel, uintptr_t ra)
 {
-    uintptr_t ra = GETPC();
-
     switch (addr & 3) {
     case 3:
         cpu_stb_data_ra(env, addr, val, ra);
@@ -109,20 +107,18 @@ static void do_stby_b(CPUHPPAState *env, target_ulong addr, target_ureg val,
 
 void HELPER(stby_b)(CPUHPPAState *env, target_ulong addr, target_ureg val)
 {
-    do_stby_b(env, addr, val, false);
+    do_stby_b(env, addr, val, false, GETPC());
 }
 
 void HELPER(stby_b_parallel)(CPUHPPAState *env, target_ulong addr,
                              target_ureg val)
 {
-    do_stby_b(env, addr, val, true);
+    do_stby_b(env, addr, val, true, GETPC());
 }
 
 static void do_stby_e(CPUHPPAState *env, target_ulong addr, target_ureg val,
-                      bool parallel)
+                      bool parallel, uintptr_t ra)
 {
-    uintptr_t ra = GETPC();
-
     switch (addr & 3) {
     case 3:
         /* The 3 byte store must appear atomic.  */
@@ -151,13 +147,13 @@ static void do_stby_e(CPUHPPAState *env, target_ulong addr, target_ureg val,
 
 void HELPER(stby_e)(CPUHPPAState *env, target_ulong addr, target_ureg val)
 {
-    do_stby_e(env, addr, val, false);
+    do_stby_e(env, addr, val, false, GETPC());
 }
 
 void HELPER(stby_e_parallel)(CPUHPPAState *env, target_ulong addr,
                              target_ureg val)
 {
-    do_stby_e(env, addr, val, true);
+    do_stby_e(env, addr, val, true, GETPC());
 }
 
 target_ureg HELPER(probe)(CPUHPPAState *env, target_ulong addr,
diff --git a/target/hppa/translate.c b/target/hppa/translate.c
index 51bfd9849d..b4fd307b77 100644
--- a/target/hppa/translate.c
+++ b/target/hppa/translate.c
@@ -278,9 +278,63 @@ typedef struct DisasContext {
     bool psw_n_nonzero;
 } DisasContext;
 
-/* Target-specific return values from translate_one, indicating the
-   state of the TB.  Note that DISAS_NEXT indicates that we are not
-   exiting the TB.  */
+/* Note that ssm/rsm instructions number PSW_W and PSW_E differently.  */
+static int expand_sm_imm(int val)
+{
+    if (val & PSW_SM_E) {
+        val = (val & ~PSW_SM_E) | PSW_E;
+    }
+    if (val & PSW_SM_W) {
+        val = (val & ~PSW_SM_W) | PSW_W;
+    }
+    return val;
+}
+
+/* Inverted space register indicates 0 means sr0 not inferred from base.  */
+static int expand_sr3x(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)
+{
+    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(int val)
+{
+    return val ? 1 : -1;
+}
+
+static int neg_to_m(int val)
+{
+    return val ? -1 : 1;
+}
+
+/* Used for branch targets and fp memory ops.  */
+static int expand_shl2(int val)
+{
+    return val << 2;
+}
+
+/* Used for fp memory ops.  */
+static int expand_shl3(int val)
+{
+    return val << 3;
+}
+
+/* Used for assemble_21.  */
+static int expand_shl11(int val)
+{
+    return val << 11;
+}
+
+
+/* Include the auto-generated decoder.  */
+#include "decode.inc.c"
 
 /* We are not using a goto_tb (for whatever reason), but have updated
    the iaq (for whatever reason), so don't do it again on exit.  */
@@ -294,21 +348,6 @@ typedef struct DisasContext {
    to recognize unmasked interrupts.  */
 #define DISAS_IAQ_N_STALE_EXIT      DISAS_TARGET_2
 
-typedef struct DisasInsn {
-    uint32_t insn, mask;
-    DisasJumpType (*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;
-
 /* global register indexes */
 static TCGv_reg cpu_gr[32];
 static TCGv_i64 cpu_sr[4];
@@ -393,6 +432,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){
@@ -404,15 +452,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)
@@ -665,10 +717,12 @@ static void nullify_set(DisasContext *ctx, bool x)
 }
 
 /* Mark the end of an instruction that may have been nullified.
-   This is the pair to nullify_over.  */
-static DisasJumpType nullify_end(DisasContext *ctx, DisasJumpType status)
+   This is the pair to nullify_over.  Always returns true so that
+   it may be tail-called from a translate function.  */
+static bool nullify_end(DisasContext *ctx)
 {
     TCGLabel *null_lab = ctx->null_lab;
+    DisasJumpType status = ctx->base.is_jmp;
 
     /* For NEXT, NORETURN, STALE, we can easily continue (or exit).
        For UPDATED, we cannot update on the nullified path.  */
@@ -678,7 +732,7 @@ static DisasJumpType nullify_end(DisasContext *ctx, DisasJumpType status)
         /* The current insn wasn't conditional or handled the condition
            applied to it without a branch, so the (new) setting of
            NULL_COND can be applied directly to the next insn.  */
-        return status;
+        return true;
     }
     ctx->null_lab = NULL;
 
@@ -696,9 +750,9 @@ static DisasJumpType nullify_end(DisasContext *ctx, DisasJumpType status)
         ctx->null_cond = cond_make_n();
     }
     if (status == DISAS_NORETURN) {
-        status = DISAS_NEXT;
+        ctx->base.is_jmp = DISAS_NEXT;
     }
-    return status;
+    return true;
 }
 
 static void copy_iaoq_entry(TCGv_reg dest, target_ureg ival, TCGv_reg vval)
@@ -722,41 +776,49 @@ static void gen_excp_1(int exception)
     tcg_temp_free_i32(t);
 }
 
-static DisasJumpType gen_excp(DisasContext *ctx, int exception)
+static void gen_excp(DisasContext *ctx, int exception)
 {
     copy_iaoq_entry(cpu_iaoq_f, ctx->iaoq_f, cpu_iaoq_f);
     copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_b, cpu_iaoq_b);
     nullify_save(ctx);
     gen_excp_1(exception);
-    return DISAS_NORETURN;
+    ctx->base.is_jmp = DISAS_NORETURN;
 }
 
-static DisasJumpType gen_excp_iir(DisasContext *ctx, int exc)
+static bool gen_excp_iir(DisasContext *ctx, int exc)
 {
-    TCGv_reg tmp = tcg_const_reg(ctx->insn);
+    TCGv_reg tmp;
+
+    nullify_over(ctx);
+    tmp = tcg_const_reg(ctx->insn);
     tcg_gen_st_reg(tmp, cpu_env, offsetof(CPUHPPAState, cr[CR_IIR]));
     tcg_temp_free(tmp);
-    return gen_excp(ctx, exc);
+    gen_excp(ctx, exc);
+    return nullify_end(ctx);
 }
 
-static DisasJumpType gen_illegal(DisasContext *ctx)
+static bool gen_illegal(DisasContext *ctx)
 {
-    nullify_over(ctx);
-    return nullify_end(ctx, gen_excp_iir(ctx, EXCP_ILL));
+    return gen_excp_iir(ctx, EXCP_ILL);
 }
 
-#define CHECK_MOST_PRIVILEGED(EXCP)                               \
-    do {                                                          \
-        if (ctx->privilege != 0) {                                \
-            nullify_over(ctx);                                    \
-            return nullify_end(ctx, gen_excp_iir(ctx, EXCP));     \
-        }                                                         \
+#ifdef CONFIG_USER_ONLY
+#define CHECK_MOST_PRIVILEGED(EXCP) \
+    return gen_excp_iir(ctx, EXCP)
+#else
+#define CHECK_MOST_PRIVILEGED(EXCP) \
+    do {                                     \
+        if (ctx->privilege != 0) {           \
+            return gen_excp_iir(ctx, EXCP);  \
+        }                                    \
     } while (0)
+#endif
 
 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) {
+    if ((tb_cflags(ctx->base.tb) & CF_LAST_IO)
+        || ctx->base.singlestep_enabled) {
         return false;
     }
     return true;
@@ -791,111 +853,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)
+static bool cond_need_sv(int c)
 {
-    unsigned r1 = extract32(insn, 7, 1);
-    unsigned r0 = extract32(insn, 21, 5);
-    return r1 * 32 + r0;
+    return c == 2 || c == 3 || c == 6;
 }
 
-static unsigned assemble_rb64(uint32_t insn)
+static bool cond_need_cb(int c)
 {
-    unsigned r1 = extract32(insn, 12, 1);
-    unsigned r0 = extract32(insn, 16, 5);
-    return r1 * 32 + r0;
+    return c == 4 || c == 5;
 }
 
-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 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)
-{
-    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;
-}
-
-static target_sreg assemble_21(uint32_t insn)
-{
-    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;
-}
-
-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)
@@ -904,17 +875,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);
@@ -923,8 +909,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);
@@ -932,8 +917,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();
@@ -971,7 +955,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);
@@ -980,17 +964,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.  */
@@ -1119,9 +1136,9 @@ static TCGv_reg do_sub_sv(DisasContext *ctx, TCGv_reg res,
     return sv;
 }
 
-static DisasJumpType do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1,
-                            TCGv_reg in2, unsigned shift, bool is_l,
-                            bool is_tsv, bool is_tc, bool is_c, unsigned cf)
+static void do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1,
+                   TCGv_reg in2, unsigned shift, bool is_l,
+                   bool is_tsv, bool is_tc, bool is_c, unsigned cf)
 {
     TCGv_reg dest, cb, cb_msb, sv, tmp;
     unsigned c = cf >> 1;
@@ -1137,7 +1154,7 @@ static DisasJumpType 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);
@@ -1159,7 +1176,7 @@ static DisasJumpType 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.  */
@@ -1188,12 +1205,39 @@ static DisasJumpType do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1,
     /* Install the new nullification.  */
     cond_free(&ctx->null_cond);
     ctx->null_cond = cond;
-    return DISAS_NEXT;
 }
 
-static DisasJumpType do_sub(DisasContext *ctx, unsigned rt, TCGv_reg in1,
-                            TCGv_reg in2, bool is_tsv, bool is_b,
-                            bool is_tc, unsigned cf)
+static bool do_add_reg(DisasContext *ctx, arg_rrr_cf_sh *a,
+                       bool is_l, bool is_tsv, bool is_tc, bool is_c)
+{
+    TCGv_reg tcg_r1, tcg_r2;
+
+    if (a->cf) {
+        nullify_over(ctx);
+    }
+    tcg_r1 = load_gpr(ctx, a->r1);
+    tcg_r2 = load_gpr(ctx, a->r2);
+    do_add(ctx, a->t, tcg_r1, tcg_r2, a->sh, is_l, is_tsv, is_tc, is_c, a->cf);
+    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)
 {
     TCGv_reg dest, sv, cb, cb_msb, zero, tmp;
     unsigned c = cf >> 1;
@@ -1223,7 +1267,7 @@ static DisasJumpType 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);
@@ -1255,11 +1299,37 @@ static DisasJumpType do_sub(DisasContext *ctx, unsigned rt, TCGv_reg in1,
     /* Install the new nullification.  */
     cond_free(&ctx->null_cond);
     ctx->null_cond = cond;
-    return DISAS_NEXT;
 }
 
-static DisasJumpType do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_reg in1,
-                               TCGv_reg in2, unsigned cf)
+static bool do_sub_reg(DisasContext *ctx, arg_rrr_cf *a,
+                       bool is_tsv, bool is_b, bool is_tc)
+{
+    TCGv_reg tcg_r1, tcg_r2;
+
+    if (a->cf) {
+        nullify_over(ctx);
+    }
+    tcg_r1 = load_gpr(ctx, a->r1);
+    tcg_r2 = load_gpr(ctx, a->r2);
+    do_sub(ctx, a->t, tcg_r1, tcg_r2, is_tsv, is_b, is_tc, a->cf);
+    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)
 {
     TCGv_reg dest, sv;
     DisasCond cond;
@@ -1269,7 +1339,7 @@ static DisasJumpType 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);
     }
 
@@ -1284,12 +1354,11 @@ static DisasJumpType do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_reg in1,
     /* Install the new nullification.  */
     cond_free(&ctx->null_cond);
     ctx->null_cond = cond;
-    return DISAS_NEXT;
 }
 
-static DisasJumpType do_log(DisasContext *ctx, unsigned rt, TCGv_reg in1,
-                            TCGv_reg in2, unsigned cf,
-                            void (*fn)(TCGv_reg, TCGv_reg, TCGv_reg))
+static void do_log(DisasContext *ctx, unsigned rt, TCGv_reg in1,
+                   TCGv_reg in2, unsigned cf,
+                   void (*fn)(TCGv_reg, TCGv_reg, TCGv_reg))
 {
     TCGv_reg dest = dest_gpr(ctx, rt);
 
@@ -1302,12 +1371,25 @@ static DisasJumpType do_log(DisasContext *ctx, unsigned rt, TCGv_reg in1,
     if (cf) {
         ctx->null_cond = do_log_cond(cf, dest);
     }
-    return DISAS_NEXT;
 }
 
-static DisasJumpType do_unit(DisasContext *ctx, unsigned rt, TCGv_reg in1,
-                             TCGv_reg in2, unsigned cf, bool is_tc,
-                             void (*fn)(TCGv_reg, TCGv_reg, TCGv_reg))
+static bool do_log_reg(DisasContext *ctx, arg_rrr_cf *a,
+                       void (*fn)(TCGv_reg, TCGv_reg, TCGv_reg))
+{
+    TCGv_reg tcg_r1, tcg_r2;
+
+    if (a->cf) {
+        nullify_over(ctx);
+    }
+    tcg_r1 = load_gpr(ctx, a->r1);
+    tcg_r2 = load_gpr(ctx, a->r2);
+    do_log(ctx, a->t, tcg_r1, tcg_r2, a->cf, fn);
+    return nullify_end(ctx);
+}
+
+static void do_unit(DisasContext *ctx, unsigned rt, TCGv_reg in1,
+                    TCGv_reg in2, unsigned cf, bool is_tc,
+                    void (*fn)(TCGv_reg, TCGv_reg, TCGv_reg))
 {
     TCGv_reg dest;
     DisasCond cond;
@@ -1335,7 +1417,6 @@ static DisasJumpType do_unit(DisasContext *ctx, unsigned rt, TCGv_reg in1,
         cond_free(&ctx->null_cond);
         ctx->null_cond = cond;
     }
-    return DISAS_NEXT;
 }
 
 #ifndef CONFIG_USER_ONLY
@@ -1498,9 +1579,9 @@ static void do_store_64(DisasContext *ctx, TCGv_i64 src, unsigned rb,
 #define do_store_reg  do_store_32
 #endif
 
-static DisasJumpType do_load(DisasContext *ctx, unsigned rt, unsigned rb,
-                             unsigned rx, int scale, target_sreg disp,
-                             unsigned sp, int modify, TCGMemOp mop)
+static bool do_load(DisasContext *ctx, unsigned rt, unsigned rb,
+                    unsigned rx, int scale, target_sreg disp,
+                    unsigned sp, int modify, TCGMemOp mop)
 {
     TCGv_reg dest;
 
@@ -1516,12 +1597,12 @@ static DisasJumpType do_load(DisasContext *ctx, unsigned rt, unsigned rb,
     do_load_reg(ctx, dest, rb, rx, scale, disp, sp, modify, mop);
     save_gpr(ctx, rt, dest);
 
-    return nullify_end(ctx, DISAS_NEXT);
+    return nullify_end(ctx);
 }
 
-static DisasJumpType do_floadw(DisasContext *ctx, unsigned rt, unsigned rb,
-                               unsigned rx, int scale, target_sreg disp,
-                               unsigned sp, int modify)
+static bool do_floadw(DisasContext *ctx, unsigned rt, unsigned rb,
+                      unsigned rx, int scale, target_sreg disp,
+                      unsigned sp, int modify)
 {
     TCGv_i32 tmp;
 
@@ -1536,12 +1617,18 @@ static DisasJumpType do_floadw(DisasContext *ctx, unsigned rt, unsigned rb,
         gen_helper_loaded_fr0(cpu_env);
     }
 
-    return nullify_end(ctx, DISAS_NEXT);
+    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 DisasJumpType do_floadd(DisasContext *ctx, unsigned rt, unsigned rb,
-                               unsigned rx, int scale, target_sreg disp,
-                               unsigned sp, int modify)
+static bool do_floadd(DisasContext *ctx, unsigned rt, unsigned rb,
+                      unsigned rx, int scale, target_sreg disp,
+                      unsigned sp, int modify)
 {
     TCGv_i64 tmp;
 
@@ -1556,21 +1643,27 @@ static DisasJumpType do_floadd(DisasContext *ctx, unsigned rt, unsigned rb,
         gen_helper_loaded_fr0(cpu_env);
     }
 
-    return nullify_end(ctx, DISAS_NEXT);
+    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 DisasJumpType do_store(DisasContext *ctx, unsigned rt, unsigned rb,
-                              target_sreg disp, unsigned sp,
-                              int modify, TCGMemOp mop)
+static bool do_store(DisasContext *ctx, unsigned rt, unsigned rb,
+                     target_sreg disp, unsigned sp,
+                     int modify, TCGMemOp mop)
 {
     nullify_over(ctx);
     do_store_reg(ctx, load_gpr(ctx, rt), rb, 0, 0, disp, sp, modify, mop);
-    return nullify_end(ctx, DISAS_NEXT);
+    return nullify_end(ctx);
 }
 
-static DisasJumpType do_fstorew(DisasContext *ctx, unsigned rt, unsigned rb,
-                                unsigned rx, int scale, target_sreg disp,
-                                unsigned sp, int modify)
+static bool do_fstorew(DisasContext *ctx, unsigned rt, unsigned rb,
+                       unsigned rx, int scale, target_sreg disp,
+                       unsigned sp, int modify)
 {
     TCGv_i32 tmp;
 
@@ -1580,12 +1673,18 @@ static DisasJumpType 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);
 
-    return nullify_end(ctx, DISAS_NEXT);
+    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 DisasJumpType do_fstored(DisasContext *ctx, unsigned rt, unsigned rb,
-                                unsigned rx, int scale, target_sreg disp,
-                                unsigned sp, int modify)
+static bool do_fstored(DisasContext *ctx, unsigned rt, unsigned rb,
+                       unsigned rx, int scale, target_sreg disp,
+                       unsigned sp, int modify)
 {
     TCGv_i64 tmp;
 
@@ -1595,11 +1694,17 @@ static DisasJumpType 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);
 
-    return nullify_end(ctx, DISAS_NEXT);
+    return nullify_end(ctx);
 }
 
-static DisasJumpType do_fop_wew(DisasContext *ctx, unsigned rt, unsigned ra,
-                                void (*func)(TCGv_i32, TCGv_env, TCGv_i32))
+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 bool do_fop_wew(DisasContext *ctx, unsigned rt, unsigned ra,
+                       void (*func)(TCGv_i32, TCGv_env, TCGv_i32))
 {
     TCGv_i32 tmp;
 
@@ -1610,11 +1715,11 @@ static DisasJumpType do_fop_wew(DisasContext *ctx, unsigned rt, unsigned ra,
 
     save_frw_i32(rt, tmp);
     tcg_temp_free_i32(tmp);
-    return nullify_end(ctx, DISAS_NEXT);
+    return nullify_end(ctx);
 }
 
-static DisasJumpType do_fop_wed(DisasContext *ctx, unsigned rt, unsigned ra,
-                                void (*func)(TCGv_i32, TCGv_env, TCGv_i64))
+static bool do_fop_wed(DisasContext *ctx, unsigned rt, unsigned ra,
+                       void (*func)(TCGv_i32, TCGv_env, TCGv_i64))
 {
     TCGv_i32 dst;
     TCGv_i64 src;
@@ -1628,11 +1733,11 @@ static DisasJumpType do_fop_wed(DisasContext *ctx, unsigned rt, unsigned ra,
     tcg_temp_free_i64(src);
     save_frw_i32(rt, dst);
     tcg_temp_free_i32(dst);
-    return nullify_end(ctx, DISAS_NEXT);
+    return nullify_end(ctx);
 }
 
-static DisasJumpType do_fop_ded(DisasContext *ctx, unsigned rt, unsigned ra,
-                                void (*func)(TCGv_i64, TCGv_env, TCGv_i64))
+static bool do_fop_ded(DisasContext *ctx, unsigned rt, unsigned ra,
+                       void (*func)(TCGv_i64, TCGv_env, TCGv_i64))
 {
     TCGv_i64 tmp;
 
@@ -1643,11 +1748,11 @@ static DisasJumpType do_fop_ded(DisasContext *ctx, unsigned rt, unsigned ra,
 
     save_frd(rt, tmp);
     tcg_temp_free_i64(tmp);
-    return nullify_end(ctx, DISAS_NEXT);
+    return nullify_end(ctx);
 }
 
-static DisasJumpType do_fop_dew(DisasContext *ctx, unsigned rt, unsigned ra,
-                                void (*func)(TCGv_i64, TCGv_env, TCGv_i32))
+static bool do_fop_dew(DisasContext *ctx, unsigned rt, unsigned ra,
+                       void (*func)(TCGv_i64, TCGv_env, TCGv_i32))
 {
     TCGv_i32 src;
     TCGv_i64 dst;
@@ -1661,13 +1766,12 @@ static DisasJumpType do_fop_dew(DisasContext *ctx, unsigned rt, unsigned ra,
     tcg_temp_free_i32(src);
     save_frd(rt, dst);
     tcg_temp_free_i64(dst);
-    return nullify_end(ctx, DISAS_NEXT);
+    return nullify_end(ctx);
 }
 
-static DisasJumpType do_fop_weww(DisasContext *ctx, unsigned rt,
-                                 unsigned ra, unsigned rb,
-                                 void (*func)(TCGv_i32, TCGv_env,
-                                              TCGv_i32, TCGv_i32))
+static bool do_fop_weww(DisasContext *ctx, unsigned rt,
+                        unsigned ra, unsigned rb,
+                        void (*func)(TCGv_i32, TCGv_env, TCGv_i32, TCGv_i32))
 {
     TCGv_i32 a, b;
 
@@ -1680,13 +1784,12 @@ static DisasJumpType do_fop_weww(DisasContext *ctx, unsigned rt,
     tcg_temp_free_i32(b);
     save_frw_i32(rt, a);
     tcg_temp_free_i32(a);
-    return nullify_end(ctx, DISAS_NEXT);
+    return nullify_end(ctx);
 }
 
-static DisasJumpType do_fop_dedd(DisasContext *ctx, unsigned rt,
-                                 unsigned ra, unsigned rb,
-                                 void (*func)(TCGv_i64, TCGv_env,
-                                              TCGv_i64, TCGv_i64))
+static bool do_fop_dedd(DisasContext *ctx, unsigned rt,
+                        unsigned ra, unsigned rb,
+                        void (*func)(TCGv_i64, TCGv_env, TCGv_i64, TCGv_i64))
 {
     TCGv_i64 a, b;
 
@@ -1699,13 +1802,13 @@ static DisasJumpType do_fop_dedd(DisasContext *ctx, unsigned rt,
     tcg_temp_free_i64(b);
     save_frd(rt, a);
     tcg_temp_free_i64(a);
-    return nullify_end(ctx, DISAS_NEXT);
+    return nullify_end(ctx);
 }
 
 /* Emit an unconditional branch to a direct target, which may or may not
    have already had nullification handled.  */
-static DisasJumpType do_dbranch(DisasContext *ctx, target_ureg dest,
-                                unsigned link, bool is_n)
+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) {
         if (link != 0) {
@@ -1715,7 +1818,6 @@ static DisasJumpType do_dbranch(DisasContext *ctx, target_ureg dest,
         if (is_n) {
             ctx->null_cond.c = TCG_COND_ALWAYS;
         }
-        return DISAS_NEXT;
     } else {
         nullify_over(ctx);
 
@@ -1731,18 +1833,19 @@ static DisasJumpType do_dbranch(DisasContext *ctx, target_ureg dest,
             gen_goto_tb(ctx, 0, ctx->iaoq_b, dest);
         }
 
-        nullify_end(ctx, DISAS_NEXT);
+        nullify_end(ctx);
 
         nullify_set(ctx, 0);
         gen_goto_tb(ctx, 1, ctx->iaoq_b, ctx->iaoq_n);
-        return DISAS_NORETURN;
+        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 DisasJumpType do_cbranch(DisasContext *ctx, target_sreg disp, bool is_n,
-                                DisasCond *cond)
+static bool do_cbranch(DisasContext *ctx, target_sreg disp, bool is_n,
+                       DisasCond *cond)
 {
     target_ureg dest = iaoq_dest(ctx, disp);
     TCGLabel *taken = NULL;
@@ -1799,16 +1902,17 @@ static DisasJumpType do_cbranch(DisasContext *ctx, target_sreg disp, bool is_n,
     if (ctx->null_lab) {
         gen_set_label(ctx->null_lab);
         ctx->null_lab = NULL;
-        return DISAS_IAQ_N_STALE;
+        ctx->base.is_jmp = DISAS_IAQ_N_STALE;
     } else {
-        return DISAS_NORETURN;
+        ctx->base.is_jmp = DISAS_NORETURN;
     }
+    return true;
 }
 
 /* Emit an unconditional branch to an indirect target.  This handles
    nullification of the branch itself.  */
-static DisasJumpType do_ibranch(DisasContext *ctx, TCGv_reg dest,
-                                unsigned link, bool is_n)
+static bool do_ibranch(DisasContext *ctx, TCGv_reg dest,
+                       unsigned link, bool is_n)
 {
     TCGv_reg a0, a1, next, tmp;
     TCGCond c;
@@ -1826,7 +1930,8 @@ static DisasJumpType do_ibranch(DisasContext *ctx, TCGv_reg dest,
                 tcg_gen_mov_reg(cpu_iaoq_f, next);
                 tcg_gen_addi_reg(cpu_iaoq_b, next, 4);
                 nullify_set(ctx, 0);
-                return DISAS_IAQ_N_UPDATED;
+                ctx->base.is_jmp = DISAS_IAQ_N_UPDATED;
+                return true;
             }
             ctx->null_cond.c = TCG_COND_ALWAYS;
         }
@@ -1853,7 +1958,7 @@ static DisasJumpType do_ibranch(DisasContext *ctx, TCGv_reg dest,
             tcg_gen_movi_reg(cpu_gr[link], ctx->iaoq_n);
         }
         tcg_gen_lookup_and_goto_ptr();
-        return nullify_end(ctx, DISAS_NEXT);
+        return nullify_end(ctx);
     } else {
         cond_prep(&ctx->null_cond);
         c = ctx->null_cond.c;
@@ -1884,8 +1989,7 @@ static DisasJumpType do_ibranch(DisasContext *ctx, TCGv_reg dest,
             cond_free(&ctx->null_cond);
         }
     }
-
-    return DISAS_NEXT;
+    return true;
 }
 
 /* Implement
@@ -1926,7 +2030,7 @@ static TCGv_reg do_ibranch_priv(DisasContext *ctx, TCGv_reg offset)
    in than the "be disp(sr2,r0)" instruction that probably sent us
    here, is the easiest way to handle the branch delay slot on the
    aforementioned BE.  */
-static DisasJumpType do_page_zero(DisasContext *ctx)
+static void do_page_zero(DisasContext *ctx)
 {
     /* If by some means we get here with PSW[N]=1, that implies that
        the B,GATE instruction would be skipped, and we'd fault on the
@@ -1954,71 +2058,70 @@ static DisasJumpType do_page_zero(DisasContext *ctx)
     switch (ctx->iaoq_f & -4) {
     case 0x00: /* Null pointer call */
         gen_excp_1(EXCP_IMP);
-        return DISAS_NORETURN;
+        ctx->base.is_jmp = DISAS_NORETURN;
+        break;
 
     case 0xb0: /* LWS */
         gen_excp_1(EXCP_SYSCALL_LWS);
-        return DISAS_NORETURN;
+        ctx->base.is_jmp = DISAS_NORETURN;
+        break;
 
     case 0xe0: /* SET_THREAD_POINTER */
         tcg_gen_st_reg(cpu_gr[26], cpu_env, offsetof(CPUHPPAState, cr[27]));
         tcg_gen_ori_reg(cpu_iaoq_f, cpu_gr[31], 3);
         tcg_gen_addi_reg(cpu_iaoq_b, cpu_iaoq_f, 4);
-        return DISAS_IAQ_N_UPDATED;
+        ctx->base.is_jmp = DISAS_IAQ_N_UPDATED;
+        break;
 
     case 0x100: /* SYSCALL */
         gen_excp_1(EXCP_SYSCALL);
-        return DISAS_NORETURN;
+        ctx->base.is_jmp = DISAS_NORETURN;
+        break;
 
     default:
     do_sigill:
         gen_excp_1(EXCP_ILL);
-        return DISAS_NORETURN;
+        ctx->base.is_jmp = DISAS_NORETURN;
+        break;
     }
 }
 #endif
 
-static DisasJumpType trans_nop(DisasContext *ctx, uint32_t insn,
-                               const DisasInsn *di)
+static bool trans_nop(DisasContext *ctx, arg_nop *a)
 {
     cond_free(&ctx->null_cond);
-    return DISAS_NEXT;
+    return true;
 }
 
-static DisasJumpType trans_break(DisasContext *ctx, uint32_t insn,
-                                 const DisasInsn *di)
+static bool trans_break(DisasContext *ctx, arg_break *a)
 {
-    nullify_over(ctx);
-    return nullify_end(ctx, gen_excp_iir(ctx, EXCP_BREAK));
+    return gen_excp_iir(ctx, EXCP_BREAK);
 }
 
-static DisasJumpType trans_sync(DisasContext *ctx, uint32_t insn,
-                                const DisasInsn *di)
+static bool trans_sync(DisasContext *ctx, arg_sync *a)
 {
     /* No point in nullifying the memory barrier.  */
     tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL);
 
     cond_free(&ctx->null_cond);
-    return DISAS_NEXT;
+    return true;
 }
 
-static DisasJumpType trans_mfia(DisasContext *ctx, uint32_t insn,
-                                const DisasInsn *di)
+static bool trans_mfia(DisasContext *ctx, arg_mfia *a)
 {
-    unsigned rt = extract32(insn, 0, 5);
+    unsigned rt = a->t;
     TCGv_reg tmp = dest_gpr(ctx, rt);
     tcg_gen_movi_reg(tmp, ctx->iaoq_f);
     save_gpr(ctx, rt, tmp);
 
     cond_free(&ctx->null_cond);
-    return DISAS_NEXT;
+    return true;
 }
 
-static DisasJumpType trans_mfsp(DisasContext *ctx, uint32_t insn,
-                                const DisasInsn *di)
+static bool trans_mfsp(DisasContext *ctx, arg_mfsp *a)
 {
-    unsigned rt = extract32(insn, 0, 5);
-    unsigned rs = assemble_sr3(insn);
+    unsigned rt = a->t;
+    unsigned rs = a->sp;
     TCGv_i64 t0 = tcg_temp_new_i64();
     TCGv_reg t1 = tcg_temp_new();
 
@@ -2031,21 +2134,19 @@ static DisasJumpType trans_mfsp(DisasContext *ctx, uint32_t insn,
     tcg_temp_free_i64(t0);
 
     cond_free(&ctx->null_cond);
-    return DISAS_NEXT;
+    return true;
 }
 
-static DisasJumpType trans_mfctl(DisasContext *ctx, uint32_t insn,
-                                 const DisasInsn *di)
+static bool trans_mfctl(DisasContext *ctx, arg_mfctl *a)
 {
-    unsigned rt = extract32(insn, 0, 5);
-    unsigned ctl = extract32(insn, 21, 5);
+    unsigned rt = a->t;
+    unsigned ctl = a->r;
     TCGv_reg tmp;
-    DisasJumpType ret;
 
     switch (ctl) {
     case CR_SAR:
 #ifdef TARGET_HPPA64
-        if (extract32(insn, 14, 1) == 0) {
+        if (a->e == 0) {
             /* MFSAR without ,W masks low 5 bits.  */
             tmp = dest_gpr(ctx, rt);
             tcg_gen_andi_reg(tmp, cpu_sar, 31);
@@ -2063,13 +2164,12 @@ static DisasJumpType trans_mfctl(DisasContext *ctx, uint32_t insn,
             gen_io_start();
             gen_helper_read_interval_timer(tmp);
             gen_io_end();
-            ret = DISAS_IAQ_N_STALE;
+            ctx->base.is_jmp = DISAS_IAQ_N_STALE;
         } else {
             gen_helper_read_interval_timer(tmp);
-            ret = DISAS_NEXT;
         }
         save_gpr(ctx, rt, tmp);
-        return nullify_end(ctx, ret);
+        return nullify_end(ctx);
     case 26:
     case 27:
         break;
@@ -2085,14 +2185,13 @@ static DisasJumpType trans_mfctl(DisasContext *ctx, uint32_t insn,
 
  done:
     cond_free(&ctx->null_cond);
-    return DISAS_NEXT;
+    return true;
 }
 
-static DisasJumpType trans_mtsp(DisasContext *ctx, uint32_t insn,
-                                const DisasInsn *di)
+static bool trans_mtsp(DisasContext *ctx, arg_mtsp *a)
 {
-    unsigned rr = extract32(insn, 16, 5);
-    unsigned rs = assemble_sr3(insn);
+    unsigned rr = a->r;
+    unsigned rs = a->sp;
     TCGv_i64 t64;
 
     if (rs >= 5) {
@@ -2112,15 +2211,13 @@ static DisasJumpType trans_mtsp(DisasContext *ctx, uint32_t insn,
     }
     tcg_temp_free_i64(t64);
 
-    return nullify_end(ctx, DISAS_NEXT);
+    return nullify_end(ctx);
 }
 
-static DisasJumpType trans_mtctl(DisasContext *ctx, uint32_t insn,
-                                 const DisasInsn *di)
+static bool trans_mtctl(DisasContext *ctx, arg_mtctl *a)
 {
-    unsigned rin = extract32(insn, 16, 5);
-    unsigned ctl = extract32(insn, 21, 5);
-    TCGv_reg reg = load_gpr(ctx, rin);
+    unsigned ctl = a->t;
+    TCGv_reg reg = load_gpr(ctx, a->r);
     TCGv_reg tmp;
 
     if (ctl == CR_SAR) {
@@ -2130,17 +2227,13 @@ static DisasJumpType trans_mtctl(DisasContext *ctx, uint32_t insn,
         tcg_temp_free(tmp);
 
         cond_free(&ctx->null_cond);
-        return DISAS_NEXT;
+        return true;
     }
 
     /* All other control registers are privileged or read-only.  */
     CHECK_MOST_PRIVILEGED(EXCP_PRIV_REG);
 
-#ifdef CONFIG_USER_ONLY
-    g_assert_not_reached();
-#else
-    DisasJumpType ret = DISAS_NEXT;
-
+#ifndef CONFIG_USER_ONLY
     nullify_over(ctx);
     switch (ctl) {
     case CR_IT:
@@ -2151,7 +2244,7 @@ static DisasJumpType trans_mtctl(DisasContext *ctx, uint32_t insn,
         break;
     case CR_EIEM:
         gen_helper_write_eiem(cpu_env, reg);
-        ret = DISAS_IAQ_N_STALE_EXIT;
+        ctx->base.is_jmp = DISAS_IAQ_N_STALE_EXIT;
         break;
 
     case CR_IIASQ:
@@ -2170,255 +2263,213 @@ static DisasJumpType trans_mtctl(DisasContext *ctx, uint32_t insn,
         tcg_gen_st_reg(reg, cpu_env, offsetof(CPUHPPAState, cr[ctl]));
         break;
     }
-    return nullify_end(ctx, ret);
+    return nullify_end(ctx);
 #endif
 }
 
-static DisasJumpType trans_mtsarcm(DisasContext *ctx, uint32_t insn,
-                                   const DisasInsn *di)
+static bool trans_mtsarcm(DisasContext *ctx, arg_mtsarcm *a)
 {
-    unsigned rin = extract32(insn, 16, 5);
     TCGv_reg tmp = tcg_temp_new();
 
-    tcg_gen_not_reg(tmp, load_gpr(ctx, rin));
+    tcg_gen_not_reg(tmp, load_gpr(ctx, a->r));
     tcg_gen_andi_reg(tmp, tmp, TARGET_REGISTER_BITS - 1);
     save_or_nullify(ctx, cpu_sar, tmp);
     tcg_temp_free(tmp);
 
     cond_free(&ctx->null_cond);
-    return DISAS_NEXT;
+    return true;
 }
 
-static DisasJumpType trans_ldsid(DisasContext *ctx, uint32_t insn,
-                                 const DisasInsn *di)
+static bool trans_ldsid(DisasContext *ctx, arg_ldsid *a)
 {
-    unsigned rt = extract32(insn, 0, 5);
-    TCGv_reg dest = dest_gpr(ctx, rt);
+    TCGv_reg dest = dest_gpr(ctx, a->t);
 
 #ifdef CONFIG_USER_ONLY
     /* We don't implement space registers in user mode. */
     tcg_gen_movi_reg(dest, 0);
 #else
-    unsigned rb = extract32(insn, 21, 5);
-    unsigned sp = extract32(insn, 14, 2);
     TCGv_i64 t0 = tcg_temp_new_i64();
 
-    tcg_gen_mov_i64(t0, space_select(ctx, sp, load_gpr(ctx, rb)));
+    tcg_gen_mov_i64(t0, space_select(ctx, a->sp, load_gpr(ctx, a->b)));
     tcg_gen_shri_i64(t0, t0, 32);
     tcg_gen_trunc_i64_reg(dest, t0);
 
     tcg_temp_free_i64(t0);
 #endif
-    save_gpr(ctx, rt, dest);
+    save_gpr(ctx, a->t, dest);
 
     cond_free(&ctx->null_cond);
-    return DISAS_NEXT;
-}
-
-#ifndef CONFIG_USER_ONLY
-/* Note that ssm/rsm instructions number PSW_W and PSW_E differently.  */
-static target_ureg extract_sm_imm(uint32_t insn)
-{
-    target_ureg val = extract32(insn, 16, 10);
-
-    if (val & PSW_SM_E) {
-        val = (val & ~PSW_SM_E) | PSW_E;
-    }
-    if (val & PSW_SM_W) {
-        val = (val & ~PSW_SM_W) | PSW_W;
-    }
-    return val;
+    return true;
 }
 
-static DisasJumpType trans_rsm(DisasContext *ctx, uint32_t insn,
-                               const DisasInsn *di)
+static bool trans_rsm(DisasContext *ctx, arg_rsm *a)
 {
-    unsigned rt = extract32(insn, 0, 5);
-    target_ureg sm = extract_sm_imm(insn);
+    CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
+#ifndef CONFIG_USER_ONLY
     TCGv_reg tmp;
 
-    CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
     nullify_over(ctx);
 
     tmp = get_temp(ctx);
     tcg_gen_ld_reg(tmp, cpu_env, offsetof(CPUHPPAState, psw));
-    tcg_gen_andi_reg(tmp, tmp, ~sm);
+    tcg_gen_andi_reg(tmp, tmp, ~a->i);
     gen_helper_swap_system_mask(tmp, cpu_env, tmp);
-    save_gpr(ctx, rt, tmp);
+    save_gpr(ctx, a->t, tmp);
 
     /* Exit the TB to recognize new interrupts, e.g. PSW_M.  */
-    return nullify_end(ctx, DISAS_IAQ_N_STALE_EXIT);
+    ctx->base.is_jmp = DISAS_IAQ_N_STALE_EXIT;
+    return nullify_end(ctx);
+#endif
 }
 
-static DisasJumpType trans_ssm(DisasContext *ctx, uint32_t insn,
-                               const DisasInsn *di)
+static bool trans_ssm(DisasContext *ctx, arg_ssm *a)
 {
-    unsigned rt = extract32(insn, 0, 5);
-    target_ureg sm = extract_sm_imm(insn);
+    CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
+#ifndef CONFIG_USER_ONLY
     TCGv_reg tmp;
 
-    CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
     nullify_over(ctx);
 
     tmp = get_temp(ctx);
     tcg_gen_ld_reg(tmp, cpu_env, offsetof(CPUHPPAState, psw));
-    tcg_gen_ori_reg(tmp, tmp, sm);
+    tcg_gen_ori_reg(tmp, tmp, a->i);
     gen_helper_swap_system_mask(tmp, cpu_env, tmp);
-    save_gpr(ctx, rt, tmp);
+    save_gpr(ctx, a->t, tmp);
 
     /* Exit the TB to recognize new interrupts, e.g. PSW_I.  */
-    return nullify_end(ctx, DISAS_IAQ_N_STALE_EXIT);
+    ctx->base.is_jmp = DISAS_IAQ_N_STALE_EXIT;
+    return nullify_end(ctx);
+#endif
 }
 
-static DisasJumpType trans_mtsm(DisasContext *ctx, uint32_t insn,
-                                const DisasInsn *di)
+static bool trans_mtsm(DisasContext *ctx, arg_mtsm *a)
 {
-    unsigned rr = extract32(insn, 16, 5);
-    TCGv_reg tmp, reg;
-
     CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
+#ifndef CONFIG_USER_ONLY
+    TCGv_reg tmp, reg;
     nullify_over(ctx);
 
-    reg = load_gpr(ctx, rr);
+    reg = load_gpr(ctx, a->r);
     tmp = get_temp(ctx);
     gen_helper_swap_system_mask(tmp, cpu_env, reg);
 
     /* Exit the TB to recognize new interrupts.  */
-    return nullify_end(ctx, DISAS_IAQ_N_STALE_EXIT);
+    ctx->base.is_jmp = DISAS_IAQ_N_STALE_EXIT;
+    return nullify_end(ctx);
+#endif
 }
 
-static DisasJumpType trans_rfi(DisasContext *ctx, uint32_t insn,
-                               const DisasInsn *di)
+static bool do_rfi(DisasContext *ctx, bool rfi_r)
 {
-    unsigned comp = extract32(insn, 5, 4);
-
     CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
+#ifndef CONFIG_USER_ONLY
     nullify_over(ctx);
 
-    if (comp == 5) {
+    if (rfi_r) {
         gen_helper_rfi_r(cpu_env);
     } else {
         gen_helper_rfi(cpu_env);
     }
+    /* Exit the TB to recognize new interrupts.  */
     if (ctx->base.singlestep_enabled) {
         gen_excp_1(EXCP_DEBUG);
     } else {
         tcg_gen_exit_tb(NULL, 0);
     }
+    ctx->base.is_jmp = DISAS_NORETURN;
 
-    /* Exit the TB to recognize new interrupts.  */
-    return nullify_end(ctx, DISAS_NORETURN);
+    return nullify_end(ctx);
+#endif
+}
+
+static bool trans_rfi(DisasContext *ctx, arg_rfi *a)
+{
+    return do_rfi(ctx, false);
 }
 
-static DisasJumpType gen_hlt(DisasContext *ctx, int reset)
+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);
-    nullify_over(ctx);
-    if (reset) {
-        gen_helper_reset(cpu_env);
-    } else {
-        gen_helper_halt(cpu_env);
-    }
-    return nullify_end(ctx, DISAS_NORETURN);
-}
-#endif /* !CONFIG_USER_ONLY */
-
-static const DisasInsn table_system[] = {
-    { 0x00000000u, 0xfc001fe0u, trans_break },
-    { 0x00001820u, 0xffe01fffu, trans_mtsp },
-    { 0x00001840u, 0xfc00ffffu, trans_mtctl },
-    { 0x016018c0u, 0xffe0ffffu, trans_mtsarcm },
-    { 0x000014a0u, 0xffffffe0u, trans_mfia },
-    { 0x000004a0u, 0xffff1fe0u, trans_mfsp },
-    { 0x000008a0u, 0xfc1fbfe0u, trans_mfctl },
-    { 0x00000400u, 0xffffffffu, trans_sync },  /* sync */
-    { 0x00100400u, 0xffffffffu, trans_sync },  /* syncdma */
-    { 0x000010a0u, 0xfc1f3fe0u, trans_ldsid },
 #ifndef CONFIG_USER_ONLY
-    { 0x00000e60u, 0xfc00ffe0u, trans_rsm },
-    { 0x00000d60u, 0xfc00ffe0u, trans_ssm },
-    { 0x00001860u, 0xffe0ffffu, trans_mtsm },
-    { 0x00000c00u, 0xfffffe1fu, trans_rfi },
+    nullify_over(ctx);
+    gen_helper_halt(cpu_env);
+    ctx->base.is_jmp = DISAS_NORETURN;
+    return nullify_end(ctx);
 #endif
-};
+}
 
-static DisasJumpType trans_base_idx_mod(DisasContext *ctx, uint32_t insn,
-                                        const DisasInsn *di)
+static bool trans_reset(DisasContext *ctx, arg_reset *a)
 {
-    unsigned rb = extract32(insn, 21, 5);
-    unsigned rx = extract32(insn, 16, 5);
-    TCGv_reg dest = dest_gpr(ctx, rb);
-    TCGv_reg src1 = load_gpr(ctx, rb);
-    TCGv_reg src2 = load_gpr(ctx, rx);
+    CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
+#ifndef CONFIG_USER_ONLY
+    nullify_over(ctx);
+    gen_helper_reset(cpu_env);
+    ctx->base.is_jmp = DISAS_NORETURN;
+    return nullify_end(ctx);
+#endif
+}
 
-    /* The only thing we need to do is the base register modification.  */
-    tcg_gen_add_reg(dest, src1, src2);
-    save_gpr(ctx, rb, dest);
+static bool trans_nop_addrx(DisasContext *ctx, arg_ldst *a)
+{
+    if (a->m) {
+        TCGv_reg dest = dest_gpr(ctx, a->b);
+        TCGv_reg src1 = load_gpr(ctx, a->b);
+        TCGv_reg src2 = load_gpr(ctx, a->x);
 
+        /* The only thing we need to do is the base register modification.  */
+        tcg_gen_add_reg(dest, src1, src2);
+        save_gpr(ctx, a->b, dest);
+    }
     cond_free(&ctx->null_cond);
-    return DISAS_NEXT;
+    return true;
 }
 
-static DisasJumpType trans_probe(DisasContext *ctx, uint32_t insn,
-                                 const DisasInsn *di)
+static bool trans_probe(DisasContext *ctx, arg_probe *a)
 {
-    unsigned rt = extract32(insn, 0, 5);
-    unsigned sp = extract32(insn, 14, 2);
-    unsigned rr = extract32(insn, 16, 5);
-    unsigned rb = extract32(insn, 21, 5);
-    unsigned is_write = extract32(insn, 6, 1);
-    unsigned is_imm = extract32(insn, 13, 1);
     TCGv_reg dest, ofs;
     TCGv_i32 level, want;
     TCGv_tl addr;
 
     nullify_over(ctx);
 
-    dest = dest_gpr(ctx, rt);
-    form_gva(ctx, &addr, &ofs, rb, 0, 0, 0, sp, 0, false);
+    dest = dest_gpr(ctx, a->t);
+    form_gva(ctx, &addr, &ofs, a->b, 0, 0, 0, a->sp, 0, false);
 
-    if (is_imm) {
-        level = tcg_const_i32(extract32(insn, 16, 2));
+    if (a->imm) {
+        level = tcg_const_i32(a->ri);
     } else {
         level = tcg_temp_new_i32();
-        tcg_gen_trunc_reg_i32(level, load_gpr(ctx, rr));
+        tcg_gen_trunc_reg_i32(level, load_gpr(ctx, a->ri));
         tcg_gen_andi_i32(level, level, 3);
     }
-    want = tcg_const_i32(is_write ? PAGE_WRITE : PAGE_READ);
+    want = tcg_const_i32(a->write ? PAGE_WRITE : PAGE_READ);
 
     gen_helper_probe(dest, cpu_env, addr, level, want);
 
     tcg_temp_free_i32(want);
     tcg_temp_free_i32(level);
 
-    save_gpr(ctx, rt, dest);
-    return nullify_end(ctx, DISAS_NEXT);
+    save_gpr(ctx, a->t, dest);
+    return nullify_end(ctx);
 }
 
+static bool trans_ixtlbx(DisasContext *ctx, arg_ixtlbx *a)
+{
+    CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
 #ifndef CONFIG_USER_ONLY
-static DisasJumpType trans_ixtlbx(DisasContext *ctx, uint32_t insn,
-                                  const DisasInsn *di)
-{
-    unsigned sp;
-    unsigned rr = extract32(insn, 16, 5);
-    unsigned rb = extract32(insn, 21, 5);
-    unsigned is_data = insn & 0x1000;
-    unsigned is_addr = insn & 0x40;
     TCGv_tl addr;
     TCGv_reg ofs, reg;
 
-    if (is_data) {
-        sp = extract32(insn, 14, 2);
-    } else {
-        sp = ~assemble_sr3(insn);
-    }
-
-    CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
     nullify_over(ctx);
 
-    form_gva(ctx, &addr, &ofs, rb, 0, 0, 0, sp, 0, false);
-    reg = load_gpr(ctx, rr);
-    if (is_addr) {
+    form_gva(ctx, &addr, &ofs, a->b, 0, 0, 0, a->sp, 0, false);
+    reg = load_gpr(ctx, a->r);
+    if (a->addr) {
         gen_helper_itlba(cpu_env, addr, reg);
     } else {
         gen_helper_itlbp(cpu_env, addr, reg);
@@ -2426,79 +2477,67 @@ static DisasJumpType trans_ixtlbx(DisasContext *ctx, uint32_t insn,
 
     /* 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.  */
-    return nullify_end(ctx, !is_data && (ctx->tb_flags & PSW_C)
-                       ? DISAS_IAQ_N_STALE : DISAS_NEXT);
+    if (!a->data && (ctx->tb_flags & PSW_C)) {
+        ctx->base.is_jmp = DISAS_IAQ_N_STALE;
+    }
+    return nullify_end(ctx);
+#endif
 }
 
-static DisasJumpType trans_pxtlbx(DisasContext *ctx, uint32_t insn,
-                                  const DisasInsn *di)
+static bool trans_pxtlbx(DisasContext *ctx, arg_pxtlbx *a)
 {
-    unsigned m = extract32(insn, 5, 1);
-    unsigned sp;
-    unsigned rx = extract32(insn, 16, 5);
-    unsigned rb = extract32(insn, 21, 5);
-    unsigned is_data = insn & 0x1000;
-    unsigned is_local = insn & 0x40;
+    CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
+#ifndef CONFIG_USER_ONLY
     TCGv_tl addr;
     TCGv_reg ofs;
 
-    if (is_data) {
-        sp = extract32(insn, 14, 2);
-    } else {
-        sp = ~assemble_sr3(insn);
-    }
-
-    CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
     nullify_over(ctx);
 
-    form_gva(ctx, &addr, &ofs, rb, rx, 0, 0, sp, m, false);
-    if (m) {
-        save_gpr(ctx, rb, ofs);
+    form_gva(ctx, &addr, &ofs, a->b, a->x, 0, 0, a->sp, a->m, false);
+    if (a->m) {
+        save_gpr(ctx, a->b, ofs);
     }
-    if (is_local) {
+    if (a->local) {
         gen_helper_ptlbe(cpu_env);
     } else {
         gen_helper_ptlb(cpu_env, addr);
     }
 
     /* Exit TB for TLB change if mmu is enabled.  */
-    return nullify_end(ctx, !is_data && (ctx->tb_flags & PSW_C)
-                       ? DISAS_IAQ_N_STALE : DISAS_NEXT);
+    if (!a->data && (ctx->tb_flags & PSW_C)) {
+        ctx->base.is_jmp = DISAS_IAQ_N_STALE;
+    }
+    return nullify_end(ctx);
+#endif
 }
 
-static DisasJumpType trans_lpa(DisasContext *ctx, uint32_t insn,
-                               const DisasInsn *di)
+static bool trans_lpa(DisasContext *ctx, arg_ldst *a)
 {
-    unsigned rt = extract32(insn, 0, 5);
-    unsigned m = extract32(insn, 5, 1);
-    unsigned sp = extract32(insn, 14, 2);
-    unsigned rx = extract32(insn, 16, 5);
-    unsigned rb = extract32(insn, 21, 5);
+    CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
+#ifndef CONFIG_USER_ONLY
     TCGv_tl vaddr;
     TCGv_reg ofs, paddr;
 
-    CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
     nullify_over(ctx);
 
-    form_gva(ctx, &vaddr, &ofs, rb, rx, 0, 0, sp, m, false);
+    form_gva(ctx, &vaddr, &ofs, a->b, a->x, 0, 0, a->sp, a->m, false);
 
     paddr = tcg_temp_new();
     gen_helper_lpa(paddr, cpu_env, vaddr);
 
     /* Note that physical address result overrides base modification.  */
-    if (m) {
-        save_gpr(ctx, rb, ofs);
+    if (a->m) {
+        save_gpr(ctx, a->b, ofs);
     }
-    save_gpr(ctx, rt, paddr);
+    save_gpr(ctx, a->t, paddr);
     tcg_temp_free(paddr);
 
-    return nullify_end(ctx, DISAS_NEXT);
+    return nullify_end(ctx);
+#endif
 }
 
-static DisasJumpType trans_lci(DisasContext *ctx, uint32_t insn,
-                               const DisasInsn *di)
+static bool trans_lci(DisasContext *ctx, arg_lci *a)
 {
-    unsigned rt = extract32(insn, 0, 5);
     TCGv_reg ci;
 
     CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
@@ -2508,238 +2547,193 @@ static DisasJumpType trans_lci(DisasContext *ctx, uint32_t insn,
        view of the cache.  Our implementation is to return 0 for all,
        since the entire address space is coherent.  */
     ci = tcg_const_reg(0);
-    save_gpr(ctx, rt, ci);
+    save_gpr(ctx, a->t, ci);
     tcg_temp_free(ci);
 
-    return DISAS_NEXT;
-}
-#endif /* !CONFIG_USER_ONLY */
-
-static const DisasInsn table_mem_mgmt[] = {
-    { 0x04003280u, 0xfc003fffu, trans_nop },          /* fdc, disp */
-    { 0x04001280u, 0xfc003fffu, trans_nop },          /* fdc, index */
-    { 0x040012a0u, 0xfc003fffu, trans_base_idx_mod }, /* fdc, index, base mod */
-    { 0x040012c0u, 0xfc003fffu, trans_nop },          /* fdce */
-    { 0x040012e0u, 0xfc003fffu, trans_base_idx_mod }, /* fdce, base mod */
-    { 0x04000280u, 0xfc001fffu, trans_nop },          /* fic 0a */
-    { 0x040002a0u, 0xfc001fffu, trans_base_idx_mod }, /* fic 0a, base mod */
-    { 0x040013c0u, 0xfc003fffu, trans_nop },          /* fic 4f */
-    { 0x040013e0u, 0xfc003fffu, trans_base_idx_mod }, /* fic 4f, base mod */
-    { 0x040002c0u, 0xfc001fffu, trans_nop },          /* fice */
-    { 0x040002e0u, 0xfc001fffu, trans_base_idx_mod }, /* fice, base mod */
-    { 0x04002700u, 0xfc003fffu, trans_nop },          /* pdc */
-    { 0x04002720u, 0xfc003fffu, trans_base_idx_mod }, /* pdc, base mod */
-    { 0x04001180u, 0xfc003fa0u, trans_probe },        /* probe */
-    { 0x04003180u, 0xfc003fa0u, trans_probe },        /* probei */
-#ifndef CONFIG_USER_ONLY
-    { 0x04000000u, 0xfc001fffu, trans_ixtlbx },       /* iitlbp */
-    { 0x04000040u, 0xfc001fffu, trans_ixtlbx },       /* iitlba */
-    { 0x04001000u, 0xfc001fffu, trans_ixtlbx },       /* idtlbp */
-    { 0x04001040u, 0xfc001fffu, trans_ixtlbx },       /* idtlba */
-    { 0x04000200u, 0xfc001fdfu, trans_pxtlbx },       /* pitlb */
-    { 0x04000240u, 0xfc001fdfu, trans_pxtlbx },       /* pitlbe */
-    { 0x04001200u, 0xfc001fdfu, trans_pxtlbx },       /* pdtlb */
-    { 0x04001240u, 0xfc001fdfu, trans_pxtlbx },       /* pdtlbe */
-    { 0x04001340u, 0xfc003fc0u, trans_lpa },
-    { 0x04001300u, 0xfc003fe0u, trans_lci },
-#endif
-};
+    cond_free(&ctx->null_cond);
+    return true;
+}
 
-static DisasJumpType trans_add(DisasContext *ctx, uint32_t insn,
-                               const DisasInsn *di)
+static bool trans_add(DisasContext *ctx, arg_rrr_cf_sh *a)
 {
-    unsigned r2 = extract32(insn, 21, 5);
-    unsigned r1 = extract32(insn, 16, 5);
-    unsigned cf = extract32(insn, 12, 4);
-    unsigned ext = extract32(insn, 8, 4);
-    unsigned shift = extract32(insn, 6, 2);
-    unsigned rt = extract32(insn,  0, 5);
-    TCGv_reg tcg_r1, tcg_r2;
-    bool is_c = false;
-    bool is_l = false;
-    bool is_tc = false;
-    bool is_tsv = false;
-    DisasJumpType ret;
+    return do_add_reg(ctx, a, false, false, false, false);
+}
 
-    switch (ext) {
-    case 0x6: /* ADD, SHLADD */
-        break;
-    case 0xa: /* ADD,L, SHLADD,L */
-        is_l = true;
-        break;
-    case 0xe: /* ADD,TSV, SHLADD,TSV (1) */
-        is_tsv = true;
-        break;
-    case 0x7: /* ADD,C */
-        is_c = true;
-        break;
-    case 0xf: /* ADD,C,TSV */
-        is_c = is_tsv = true;
-        break;
-    default:
-        return gen_illegal(ctx);
-    }
+static bool trans_add_l(DisasContext *ctx, arg_rrr_cf_sh *a)
+{
+    return do_add_reg(ctx, a, true, false, false, false);
+}
 
-    if (cf) {
-        nullify_over(ctx);
-    }
-    tcg_r1 = load_gpr(ctx, r1);
-    tcg_r2 = load_gpr(ctx, r2);
-    ret = do_add(ctx, rt, tcg_r1, tcg_r2, shift, is_l, is_tsv, is_tc, is_c, cf);
-    return nullify_end(ctx, ret);
+static bool trans_add_tsv(DisasContext *ctx, arg_rrr_cf_sh *a)
+{
+    return do_add_reg(ctx, a, false, true, false, false);
 }
 
-static DisasJumpType trans_sub(DisasContext *ctx, uint32_t insn,
-                               const DisasInsn *di)
+static bool trans_add_c(DisasContext *ctx, arg_rrr_cf_sh *a)
 {
-    unsigned r2 = extract32(insn, 21, 5);
-    unsigned r1 = extract32(insn, 16, 5);
-    unsigned cf = extract32(insn, 12, 4);
-    unsigned ext = extract32(insn, 6, 6);
-    unsigned rt = extract32(insn,  0, 5);
-    TCGv_reg tcg_r1, tcg_r2;
-    bool is_b = false;
-    bool is_tc = false;
-    bool is_tsv = false;
-    DisasJumpType ret;
+    return do_add_reg(ctx, a, false, false, false, true);
+}
 
-    switch (ext) {
-    case 0x10: /* SUB */
-        break;
-    case 0x30: /* SUB,TSV */
-        is_tsv = true;
-        break;
-    case 0x14: /* SUB,B */
-        is_b = true;
-        break;
-    case 0x34: /* SUB,B,TSV */
-        is_b = is_tsv = true;
-        break;
-    case 0x13: /* SUB,TC */
-        is_tc = true;
-        break;
-    case 0x33: /* SUB,TSV,TC */
-        is_tc = is_tsv = true;
-        break;
-    default:
-        return gen_illegal(ctx);
-    }
+static bool trans_add_c_tsv(DisasContext *ctx, arg_rrr_cf_sh *a)
+{
+    return do_add_reg(ctx, a, false, true, false, true);
+}
 
-    if (cf) {
-        nullify_over(ctx);
-    }
-    tcg_r1 = load_gpr(ctx, r1);
-    tcg_r2 = load_gpr(ctx, r2);
-    ret = do_sub(ctx, rt, tcg_r1, tcg_r2, is_tsv, is_b, is_tc, cf);
-    return nullify_end(ctx, ret);
+static bool trans_sub(DisasContext *ctx, arg_rrr_cf *a)
+{
+    return do_sub_reg(ctx, a, false, false, false);
 }
 
-static DisasJumpType trans_log(DisasContext *ctx, uint32_t insn,
-                               const DisasInsn *di)
+static bool trans_sub_tsv(DisasContext *ctx, arg_rrr_cf *a)
 {
-    unsigned r2 = extract32(insn, 21, 5);
-    unsigned r1 = extract32(insn, 16, 5);
-    unsigned cf = extract32(insn, 12, 4);
-    unsigned rt = extract32(insn,  0, 5);
-    TCGv_reg tcg_r1, tcg_r2;
-    DisasJumpType ret;
+    return do_sub_reg(ctx, a, true, false, false);
+}
 
-    if (cf) {
-        nullify_over(ctx);
-    }
-    tcg_r1 = load_gpr(ctx, r1);
-    tcg_r2 = load_gpr(ctx, r2);
-    ret = do_log(ctx, rt, tcg_r1, tcg_r2, cf, di->f.ttt);
-    return nullify_end(ctx, ret);
+static bool trans_sub_tc(DisasContext *ctx, arg_rrr_cf *a)
+{
+    return do_sub_reg(ctx, a, false, false, true);
 }
 
-/* OR r,0,t -> COPY (according to gas) */
-static DisasJumpType trans_copy(DisasContext *ctx, uint32_t insn,
-                                const DisasInsn *di)
+static bool trans_sub_tsv_tc(DisasContext *ctx, arg_rrr_cf *a)
 {
-    unsigned r1 = extract32(insn, 16, 5);
-    unsigned rt = extract32(insn,  0, 5);
+    return do_sub_reg(ctx, a, true, false, true);
+}
 
-    if (r1 == 0) {
-        TCGv_reg dest = dest_gpr(ctx, rt);
-        tcg_gen_movi_reg(dest, 0);
-        save_gpr(ctx, rt, dest);
-    } else {
-        save_gpr(ctx, rt, cpu_gr[r1]);
+static bool trans_sub_b(DisasContext *ctx, arg_rrr_cf *a)
+{
+    return do_sub_reg(ctx, a, false, true, false);
+}
+
+static bool trans_sub_b_tsv(DisasContext *ctx, arg_rrr_cf *a)
+{
+    return do_sub_reg(ctx, a, true, true, false);
+}
+
+static bool trans_andcm(DisasContext *ctx, arg_rrr_cf *a)
+{
+    return do_log_reg(ctx, a, tcg_gen_andc_reg);
+}
+
+static bool trans_and(DisasContext *ctx, arg_rrr_cf *a)
+{
+    return do_log_reg(ctx, a, tcg_gen_and_reg);
+}
+
+static bool trans_or(DisasContext *ctx, arg_rrr_cf *a)
+{
+    if (a->cf == 0) {
+        unsigned r2 = a->r2;
+        unsigned r1 = a->r1;
+        unsigned rt = a->t;
+
+        if (rt == 0) { /* NOP */
+            cond_free(&ctx->null_cond);
+            return true;
+        }
+        if (r2 == 0) { /* COPY */
+            if (r1 == 0) {
+                TCGv_reg dest = dest_gpr(ctx, rt);
+                tcg_gen_movi_reg(dest, 0);
+                save_gpr(ctx, rt, dest);
+            } else {
+                save_gpr(ctx, rt, cpu_gr[r1]);
+            }
+            cond_free(&ctx->null_cond);
+            return true;
+        }
+#ifndef CONFIG_USER_ONLY
+        /* These are QEMU extensions and are nops in the real architecture:
+         *
+         * or %r10,%r10,%r10 -- idle loop; wait for interrupt
+         * or %r31,%r31,%r31 -- death loop; offline cpu
+         *                      currently implemented as idle.
+         */
+        if ((rt == 10 || rt == 31) && r1 == rt && r2 == rt) { /* PAUSE */
+            TCGv_i32 tmp;
+
+            /* No need to check for supervisor, as userland can only pause
+               until the next timer interrupt.  */
+            nullify_over(ctx);
+
+            /* Advance the instruction queue.  */
+            copy_iaoq_entry(cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b);
+            copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var);
+            nullify_set(ctx, 0);
+
+            /* Tell the qemu main loop to halt until this cpu has work.  */
+            tmp = tcg_const_i32(1);
+            tcg_gen_st_i32(tmp, cpu_env, -offsetof(HPPACPU, env) +
+                                         offsetof(CPUState, halted));
+            tcg_temp_free_i32(tmp);
+            gen_excp_1(EXCP_HALTED);
+            ctx->base.is_jmp = DISAS_NORETURN;
+
+            return nullify_end(ctx);
+        }
+#endif
     }
-    cond_free(&ctx->null_cond);
-    return DISAS_NEXT;
+    return do_log_reg(ctx, a, tcg_gen_or_reg);
+}
+
+static bool trans_xor(DisasContext *ctx, arg_rrr_cf *a)
+{
+    return do_log_reg(ctx, a, tcg_gen_xor_reg);
 }
 
-static DisasJumpType trans_cmpclr(DisasContext *ctx, uint32_t insn,
-                                  const DisasInsn *di)
+static bool trans_cmpclr(DisasContext *ctx, arg_rrr_cf *a)
 {
-    unsigned r2 = extract32(insn, 21, 5);
-    unsigned r1 = extract32(insn, 16, 5);
-    unsigned cf = extract32(insn, 12, 4);
-    unsigned rt = extract32(insn,  0, 5);
     TCGv_reg tcg_r1, tcg_r2;
-    DisasJumpType ret;
 
-    if (cf) {
+    if (a->cf) {
         nullify_over(ctx);
     }
-    tcg_r1 = load_gpr(ctx, r1);
-    tcg_r2 = load_gpr(ctx, r2);
-    ret = do_cmpclr(ctx, rt, tcg_r1, tcg_r2, cf);
-    return nullify_end(ctx, ret);
+    tcg_r1 = load_gpr(ctx, a->r1);
+    tcg_r2 = load_gpr(ctx, a->r2);
+    do_cmpclr(ctx, a->t, tcg_r1, tcg_r2, a->cf);
+    return nullify_end(ctx);
 }
 
-static DisasJumpType trans_uxor(DisasContext *ctx, uint32_t insn,
-                                const DisasInsn *di)
+static bool trans_uxor(DisasContext *ctx, arg_rrr_cf *a)
 {
-    unsigned r2 = extract32(insn, 21, 5);
-    unsigned r1 = extract32(insn, 16, 5);
-    unsigned cf = extract32(insn, 12, 4);
-    unsigned rt = extract32(insn,  0, 5);
     TCGv_reg tcg_r1, tcg_r2;
-    DisasJumpType ret;
 
-    if (cf) {
+    if (a->cf) {
         nullify_over(ctx);
     }
-    tcg_r1 = load_gpr(ctx, r1);
-    tcg_r2 = load_gpr(ctx, r2);
-    ret = do_unit(ctx, rt, tcg_r1, tcg_r2, cf, false, tcg_gen_xor_reg);
-    return nullify_end(ctx, ret);
+    tcg_r1 = load_gpr(ctx, a->r1);
+    tcg_r2 = load_gpr(ctx, a->r2);
+    do_unit(ctx, a->t, tcg_r1, tcg_r2, a->cf, false, tcg_gen_xor_reg);
+    return nullify_end(ctx);
 }
 
-static DisasJumpType trans_uaddcm(DisasContext *ctx, uint32_t insn,
-                                  const DisasInsn *di)
+static bool do_uaddcm(DisasContext *ctx, arg_rrr_cf *a, bool is_tc)
 {
-    unsigned r2 = extract32(insn, 21, 5);
-    unsigned r1 = extract32(insn, 16, 5);
-    unsigned cf = extract32(insn, 12, 4);
-    unsigned is_tc = extract32(insn, 6, 1);
-    unsigned rt = extract32(insn,  0, 5);
     TCGv_reg tcg_r1, tcg_r2, tmp;
-    DisasJumpType ret;
 
-    if (cf) {
+    if (a->cf) {
         nullify_over(ctx);
     }
-    tcg_r1 = load_gpr(ctx, r1);
-    tcg_r2 = load_gpr(ctx, r2);
+    tcg_r1 = load_gpr(ctx, a->r1);
+    tcg_r2 = load_gpr(ctx, a->r2);
     tmp = get_temp(ctx);
     tcg_gen_not_reg(tmp, tcg_r2);
-    ret = do_unit(ctx, rt, tcg_r1, tmp, cf, is_tc, tcg_gen_add_reg);
-    return nullify_end(ctx, ret);
+    do_unit(ctx, a->t, tcg_r1, tmp, a->cf, is_tc, tcg_gen_add_reg);
+    return nullify_end(ctx);
+}
+
+static bool trans_uaddcm(DisasContext *ctx, arg_rrr_cf *a)
+{
+    return do_uaddcm(ctx, a, false);
+}
+
+static bool trans_uaddcm_tc(DisasContext *ctx, arg_rrr_cf *a)
+{
+    return do_uaddcm(ctx, a, true);
 }
 
-static DisasJumpType trans_dcor(DisasContext *ctx, uint32_t insn,
-                                const DisasInsn *di)
+static bool do_dcor(DisasContext *ctx, arg_rr_cf *a, bool is_i)
 {
-    unsigned r2 = extract32(insn, 21, 5);
-    unsigned cf = extract32(insn, 12, 4);
-    unsigned is_i = extract32(insn, 6, 1);
-    unsigned rt = extract32(insn,  0, 5);
     TCGv_reg tmp;
-    DisasJumpType ret;
 
     nullify_over(ctx);
 
@@ -2750,25 +2744,29 @@ static DisasJumpType trans_dcor(DisasContext *ctx, uint32_t insn,
     }
     tcg_gen_andi_reg(tmp, tmp, 0x11111111);
     tcg_gen_muli_reg(tmp, tmp, 6);
-    ret = do_unit(ctx, rt, tmp, load_gpr(ctx, r2), cf, false,
-                  is_i ? tcg_gen_add_reg : tcg_gen_sub_reg);
+    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);
+}
 
-    return nullify_end(ctx, ret);
+static bool trans_dcor(DisasContext *ctx, arg_rr_cf *a)
+{
+    return do_dcor(ctx, a, false);
 }
 
-static DisasJumpType trans_ds(DisasContext *ctx, uint32_t insn,
-                              const DisasInsn *di)
+static bool trans_dcor_i(DisasContext *ctx, arg_rr_cf *a)
+{
+    return do_dcor(ctx, a, true);
+}
+
+static bool trans_ds(DisasContext *ctx, arg_rrr_cf *a)
 {
-    unsigned r2 = extract32(insn, 21, 5);
-    unsigned r1 = extract32(insn, 16, 5);
-    unsigned cf = extract32(insn, 12, 4);
-    unsigned rt = extract32(insn,  0, 5);
     TCGv_reg dest, add1, add2, addc, zero, in1, in2;
 
     nullify_over(ctx);
 
-    in1 = load_gpr(ctx, r1);
-    in2 = load_gpr(ctx, r2);
+    in1 = load_gpr(ctx, a->r1);
+    in2 = load_gpr(ctx, a->r2);
 
     add1 = tcg_temp_new();
     add2 = tcg_temp_new();
@@ -2795,7 +2793,7 @@ static DisasJumpType trans_ds(DisasContext *ctx, uint32_t insn,
     tcg_temp_free(zero);
 
     /* Write back the result register.  */
-    save_gpr(ctx, rt, dest);
+    save_gpr(ctx, a->t, dest);
 
     /* Write back PSW[CB].  */
     tcg_gen_xor_reg(cpu_psw_cb, add1, add2);
@@ -2806,251 +2804,118 @@ static DisasJumpType trans_ds(DisasContext *ctx, uint32_t insn,
     tcg_gen_xor_reg(cpu_psw_v, cpu_psw_v, in2);
 
     /* Install the new nullification.  */
-    if (cf) {
+    if (a->cf) {
         TCGv_reg sv = NULL;
-        if (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);
         }
-        ctx->null_cond = do_cond(cf, dest, cpu_psw_cb_msb, sv);
+        ctx->null_cond = do_cond(a->cf, dest, cpu_psw_cb_msb, sv);
     }
 
     tcg_temp_free(add1);
     tcg_temp_free(add2);
     tcg_temp_free(dest);
 
-    return nullify_end(ctx, DISAS_NEXT);
+    return nullify_end(ctx);
 }
 
-#ifndef CONFIG_USER_ONLY
-/* These are QEMU extensions and are nops in the real architecture:
- *
- * or %r10,%r10,%r10 -- idle loop; wait for interrupt
- * or %r31,%r31,%r31 -- death loop; offline cpu
- *                      currently implemented as idle.
- */
-static DisasJumpType trans_pause(DisasContext *ctx, uint32_t insn,
-                                 const DisasInsn *di)
+static bool trans_addi(DisasContext *ctx, arg_rri_cf *a)
 {
-    TCGv_i32 tmp;
-
-    /* No need to check for supervisor, as userland can only pause
-       until the next timer interrupt.  */
-    nullify_over(ctx);
-
-    /* Advance the instruction queue.  */
-    copy_iaoq_entry(cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b);
-    copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var);
-    nullify_set(ctx, 0);
-
-    /* Tell the qemu main loop to halt until this cpu has work.  */
-    tmp = tcg_const_i32(1);
-    tcg_gen_st_i32(tmp, cpu_env, -offsetof(HPPACPU, env) +
-                                 offsetof(CPUState, halted));
-    tcg_temp_free_i32(tmp);
-    gen_excp_1(EXCP_HALTED);
-
-    return nullify_end(ctx, DISAS_NORETURN);
+    return do_add_imm(ctx, a, false, false);
 }
-#endif
 
-static const DisasInsn table_arith_log[] = {
-    { 0x08000240u, 0xfc00ffffu, trans_nop },  /* or x,y,0 */
-    { 0x08000240u, 0xffe0ffe0u, trans_copy }, /* or x,0,t */
-#ifndef CONFIG_USER_ONLY
-    { 0x094a024au, 0xffffffffu, trans_pause }, /* or r10,r10,r10 */
-    { 0x0bff025fu, 0xffffffffu, trans_pause }, /* or r31,r31,r31 */
-#endif
-    { 0x08000000u, 0xfc000fe0u, trans_log, .f.ttt = tcg_gen_andc_reg },
-    { 0x08000200u, 0xfc000fe0u, trans_log, .f.ttt = tcg_gen_and_reg },
-    { 0x08000240u, 0xfc000fe0u, trans_log, .f.ttt = tcg_gen_or_reg },
-    { 0x08000280u, 0xfc000fe0u, trans_log, .f.ttt = tcg_gen_xor_reg },
-    { 0x08000880u, 0xfc000fe0u, trans_cmpclr },
-    { 0x08000380u, 0xfc000fe0u, trans_uxor },
-    { 0x08000980u, 0xfc000fa0u, trans_uaddcm },
-    { 0x08000b80u, 0xfc1f0fa0u, trans_dcor },
-    { 0x08000440u, 0xfc000fe0u, trans_ds },
-    { 0x08000700u, 0xfc0007e0u, trans_add }, /* add */
-    { 0x08000400u, 0xfc0006e0u, trans_sub }, /* sub; sub,b; sub,tsv */
-    { 0x080004c0u, 0xfc0007e0u, trans_sub }, /* sub,tc; sub,tsv,tc */
-    { 0x08000200u, 0xfc000320u, trans_add }, /* shladd */
-};
-
-static DisasJumpType trans_addi(DisasContext *ctx, uint32_t insn)
+static bool trans_addi_tsv(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;
-    DisasJumpType ret;
-
-    if (cf) {
-        nullify_over(ctx);
-    }
-
-    tcg_im = load_const(ctx, im);
-    tcg_r2 = load_gpr(ctx, r2);
-    ret = do_add(ctx, rt, tcg_im, tcg_r2, 0, false, e1, !o1, false, cf);
-
-    return nullify_end(ctx, ret);
+    return do_add_imm(ctx, a, true, false);
 }
 
-static DisasJumpType 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;
-    DisasJumpType ret;
+    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);
-    ret = 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, ret);
+static bool trans_subi_tsv(DisasContext *ctx, arg_rri_cf *a)
+{
+    return do_sub_imm(ctx, a, true);
 }
 
-static DisasJumpType 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;
-    DisasJumpType ret;
 
-    if (cf) {
+    if (a->cf) {
         nullify_over(ctx);
     }
 
-    tcg_im = load_const(ctx, im);
-    tcg_r2 = load_gpr(ctx, r2);
-    ret = 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, ret);
+    return nullify_end(ctx);
 }
 
-static DisasJumpType trans_ld_idx_i(DisasContext *ctx, uint32_t insn,
-                                    const DisasInsn *di)
+static bool trans_ld(DisasContext *ctx, arg_ldst *a)
 {
-    unsigned rt = extract32(insn, 0, 5);
-    unsigned m = extract32(insn, 5, 1);
-    unsigned sz = extract32(insn, 6, 2);
-    unsigned a = extract32(insn, 13, 1);
-    unsigned sp = extract32(insn, 14, 2);
-    int disp = low_sextract(insn, 16, 5);
-    unsigned rb = extract32(insn, 21, 5);
-    int modify = (m ? (a ? -1 : 1) : 0);
-    TCGMemOp mop = MO_TE | sz;
-
-    return do_load(ctx, rt, rb, 0, 0, disp, sp, modify, mop);
-}
-
-static DisasJumpType trans_ld_idx_x(DisasContext *ctx, uint32_t insn,
-                                    const DisasInsn *di)
-{
-    unsigned rt = extract32(insn, 0, 5);
-    unsigned m = extract32(insn, 5, 1);
-    unsigned sz = extract32(insn, 6, 2);
-    unsigned u = extract32(insn, 13, 1);
-    unsigned sp = extract32(insn, 14, 2);
-    unsigned rx = extract32(insn, 16, 5);
-    unsigned rb = extract32(insn, 21, 5);
-    TCGMemOp mop = MO_TE | sz;
-
-    return do_load(ctx, rt, rb, rx, u ? sz : 0, 0, sp, m, mop);
+    return do_load(ctx, a->t, a->b, a->x, a->scale ? a->size : 0,
+                   a->disp, a->sp, a->m, a->size | MO_TE);
 }
 
-static DisasJumpType trans_st_idx_i(DisasContext *ctx, uint32_t insn,
-                                    const DisasInsn *di)
+static bool trans_st(DisasContext *ctx, arg_ldst *a)
 {
-    int disp = low_sextract(insn, 0, 5);
-    unsigned m = extract32(insn, 5, 1);
-    unsigned sz = extract32(insn, 6, 2);
-    unsigned a = extract32(insn, 13, 1);
-    unsigned sp = extract32(insn, 14, 2);
-    unsigned rr = extract32(insn, 16, 5);
-    unsigned rb = extract32(insn, 21, 5);
-    int modify = (m ? (a ? -1 : 1) : 0);
-    TCGMemOp mop = MO_TE | sz;
-
-    return do_store(ctx, rr, rb, disp, sp, modify, mop);
+    assert(a->x == 0 && a->scale == 0);
+    return do_store(ctx, a->t, a->b, a->disp, a->sp, a->m, a->size | MO_TE);
 }
 
-static DisasJumpType trans_ldcw(DisasContext *ctx, uint32_t insn,
-                                const DisasInsn *di)
+static bool trans_ldc(DisasContext *ctx, arg_ldst *a)
 {
-    unsigned rt = extract32(insn, 0, 5);
-    unsigned m = extract32(insn, 5, 1);
-    unsigned i = extract32(insn, 12, 1);
-    unsigned au = extract32(insn, 13, 1);
-    unsigned sp = extract32(insn, 14, 2);
-    unsigned rx = extract32(insn, 16, 5);
-    unsigned rb = extract32(insn, 21, 5);
-    TCGMemOp mop = MO_TEUL | MO_ALIGN_16;
+    TCGMemOp mop = MO_TEUL | MO_ALIGN_16 | a->size;
     TCGv_reg zero, dest, ofs;
     TCGv_tl addr;
-    int modify, disp = 0, scale = 0;
 
     nullify_over(ctx);
 
-    if (i) {
-        modify = (m ? (au ? -1 : 1) : 0);
-        disp = low_sextract(rx, 0, 5);
-        rx = 0;
-    } else {
-        modify = m;
-        if (au) {
-            scale = mop & MO_SIZE;
-        }
-    }
-    if (modify) {
+    if (a->m) {
         /* Base register modification.  Make sure if RT == RB,
            we see the result of the load.  */
         dest = get_temp(ctx);
     } else {
-        dest = dest_gpr(ctx, rt);
+        dest = dest_gpr(ctx, a->t);
     }
 
-    form_gva(ctx, &addr, &ofs, rb, rx, scale, disp, sp, modify,
-             ctx->mmu_idx == MMU_PHYS_IDX);
+    form_gva(ctx, &addr, &ofs, a->b, a->x, a->scale ? a->size : 0,
+             a->disp, a->sp, a->m, ctx->mmu_idx == MMU_PHYS_IDX);
     zero = tcg_const_reg(0);
     tcg_gen_atomic_xchg_reg(dest, addr, zero, ctx->mmu_idx, mop);
-    if (modify) {
-        save_gpr(ctx, rb, ofs);
+    if (a->m) {
+        save_gpr(ctx, a->b, ofs);
     }
-    save_gpr(ctx, rt, dest);
+    save_gpr(ctx, a->t, dest);
 
-    return nullify_end(ctx, DISAS_NEXT);
+    return nullify_end(ctx);
 }
 
-static DisasJumpType trans_stby(DisasContext *ctx, uint32_t insn,
-                                const DisasInsn *di)
+static bool trans_stby(DisasContext *ctx, arg_stby *a)
 {
-    target_sreg disp = low_sextract(insn, 0, 5);
-    unsigned m = extract32(insn, 5, 1);
-    unsigned a = extract32(insn, 13, 1);
-    unsigned sp = extract32(insn, 14, 2);
-    unsigned rt = extract32(insn, 16, 5);
-    unsigned rb = extract32(insn, 21, 5);
     TCGv_reg ofs, val;
     TCGv_tl addr;
 
     nullify_over(ctx);
 
-    form_gva(ctx, &addr, &ofs, rb, 0, 0, disp, sp, m,
+    form_gva(ctx, &addr, &ofs, a->b, 0, 0, a->disp, a->sp, a->m,
              ctx->mmu_idx == MMU_PHYS_IDX);
-    val = load_gpr(ctx, rt);
-    if (a) {
+    val = load_gpr(ctx, a->r);
+    if (a->a) {
         if (tb_cflags(ctx->base.tb) & CF_PARALLEL) {
             gen_helper_stby_e_parallel(cpu_env, addr, val);
         } else {
@@ -3063,433 +2928,222 @@ static DisasJumpType trans_stby(DisasContext *ctx, uint32_t insn,
             gen_helper_stby_b(cpu_env, addr, val);
         }
     }
-
-    if (m) {
+    if (a->m) {
         tcg_gen_andi_reg(ofs, ofs, ~3);
-        save_gpr(ctx, rb, ofs);
+        save_gpr(ctx, a->b, ofs);
     }
 
-    return nullify_end(ctx, DISAS_NEXT);
-}
-
-#ifndef CONFIG_USER_ONLY
-static DisasJumpType trans_ldwa_idx_i(DisasContext *ctx, uint32_t insn,
-                                      const DisasInsn *di)
-{
-    int hold_mmu_idx = ctx->mmu_idx;
-    DisasJumpType ret;
-
-    CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
-
-    /* ??? needs fixing for hppa64 -- ldda does not follow the same
-       format wrt the sub-opcode in bits 6:9.  */
-    ctx->mmu_idx = MMU_PHYS_IDX;
-    ret = trans_ld_idx_i(ctx, insn, di);
-    ctx->mmu_idx = hold_mmu_idx;
-    return ret;
+    return nullify_end(ctx);
 }
 
-static DisasJumpType trans_ldwa_idx_x(DisasContext *ctx, uint32_t insn,
-                                      const DisasInsn *di)
+static bool trans_lda(DisasContext *ctx, arg_ldst *a)
 {
     int hold_mmu_idx = ctx->mmu_idx;
-    DisasJumpType ret;
 
     CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
-
-    /* ??? needs fixing for hppa64 -- ldda does not follow the same
-       format wrt the sub-opcode in bits 6:9.  */
     ctx->mmu_idx = MMU_PHYS_IDX;
-    ret = trans_ld_idx_x(ctx, insn, di);
+    trans_ld(ctx, a);
     ctx->mmu_idx = hold_mmu_idx;
-    return ret;
+    return true;
 }
 
-static DisasJumpType trans_stwa_idx_i(DisasContext *ctx, uint32_t insn,
-                                      const DisasInsn *di)
+static bool trans_sta(DisasContext *ctx, arg_ldst *a)
 {
     int hold_mmu_idx = ctx->mmu_idx;
-    DisasJumpType ret;
 
     CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
-
-    /* ??? needs fixing for hppa64 -- ldda does not follow the same
-       format wrt the sub-opcode in bits 6:9.  */
     ctx->mmu_idx = MMU_PHYS_IDX;
-    ret = trans_st_idx_i(ctx, insn, di);
+    trans_st(ctx, a);
     ctx->mmu_idx = hold_mmu_idx;
-    return ret;
+    return true;
 }
-#endif
 
-static const DisasInsn table_index_mem[] = {
-    { 0x0c001000u, 0xfc001300, trans_ld_idx_i }, /* LD[BHWD], im */
-    { 0x0c000000u, 0xfc001300, trans_ld_idx_x }, /* LD[BHWD], rx */
-    { 0x0c001200u, 0xfc001300, trans_st_idx_i }, /* ST[BHWD] */
-    { 0x0c0001c0u, 0xfc0003c0, trans_ldcw },
-    { 0x0c001300u, 0xfc0013c0, trans_stby },
-#ifndef CONFIG_USER_ONLY
-    { 0x0c000180u, 0xfc00d3c0, trans_ldwa_idx_x }, /* LDWA, rx */
-    { 0x0c001180u, 0xfc00d3c0, trans_ldwa_idx_i }, /* LDWA, im */
-    { 0x0c001380u, 0xfc00d3c0, trans_stwa_idx_i }, /* STWA, im */
-#endif
-};
-
-static DisasJumpType 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 DISAS_NEXT;
+    return true;
 }
 
-static DisasJumpType 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 DISAS_NEXT;
+    return true;
 }
 
-static DisasJumpType 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 DISAS_NEXT;
+    return true;
 }
 
-static DisasJumpType 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;
 
-    return do_load(ctx, rt, rb, 0, 0, i, sp,
-                   is_mod ? (i < 0 ? -1 : 1) : 0, mop);
-}
+    in2 = load_gpr(ctx, r);
+    dest = get_temp(ctx);
 
-static DisasJumpType 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.  */
-        return do_floadw(ctx, ext2 * 32 + rt, rb, 0, 0, i, sp, 0);
-    case 2:
-        /* LDW with modification.  Note that the sign of I selects
-           post-dec vs pre-inc.  */
-        return do_load(ctx, rt, rb, 0, 0, i, sp, (i < 0 ? 1 : -1), MO_TEUL);
-    default:
-        return gen_illegal(ctx);
+    sv = NULL;
+    if (cond_need_sv(c)) {
+        sv = do_sub_sv(ctx, dest, in1, in2);
     }
+
+    cond = do_sub_cond(c * 2 + f, dest, in1, in2, sv);
+    return do_cbranch(ctx, disp, n, &cond);
 }
 
-static DisasJumpType 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.  */
-    return do_floadw(ctx, t1 * 32 + t0, rb, 0, 0, i, sp, (a ? -1 : 1));
+    nullify_over(ctx);
+    return do_cmpb(ctx, a->r2, load_gpr(ctx, a->r1), a->c, a->f, a->n, a->disp);
 }
 
-static DisasJumpType 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);
-
-    return do_store(ctx, rt, rb, i, sp, is_mod ? (i < 0 ? -1 : 1) : 0, mop);
+    nullify_over(ctx);
+    return do_cmpb(ctx, a->r, load_const(ctx, a->i), a->c, a->f, a->n, a->disp);
 }
 
-static DisasJumpType 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.  */
-        return do_fstorew(ctx, ext2 * 32 + rt, rb, 0, 0, i, sp, 0);
-    case 2:
-        /* STW with modification.  */
-        return do_store(ctx, rt, rb, i, sp, (i < 0 ? 1 : -1), MO_TEUL);
-    default:
-        return gen_illegal(ctx);
+    in2 = load_gpr(ctx, r);
+    dest = dest_gpr(ctx, r);
+    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);
+    }
+    if (cond_need_sv(c)) {
+        sv = do_add_sv(ctx, dest, in1, in2);
     }
+
+    cond = do_cond(c * 2 + f, dest, cb_msb, sv);
+    return do_cbranch(ctx, disp, n, &cond);
 }
 
-static DisasJumpType 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);
-
-    /* FSTW with modification.  */
-    return do_fstorew(ctx, t1 * 32 + t0, rb, 0, 0, i, sp, (a ? -1 : 1));
+    nullify_over(ctx);
+    return do_addb(ctx, a->r2, load_gpr(ctx, a->r1), a->c, a->f, a->n, a->disp);
 }
 
-static DisasJumpType trans_copr_w(DisasContext *ctx, uint32_t insn)
+static bool trans_addbi(DisasContext *ctx, arg_addbi *a)
 {
-    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 */
-        return do_floadw(ctx, rt, rb, rx, scale, disp, sp, modify);
-    case 4: /* FSTW */
-        return do_fstorew(ctx, rt, rb, rx, scale, disp, sp, modify);
-    }
-    return gen_illegal(ctx);
-}
-
-static DisasJumpType 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 */
-        return do_floadd(ctx, rt, rb, rx, scale, disp, sp, modify);
-    case 8: /* FSTD */
-        return do_fstored(ctx, rt, rb, rx, scale, disp, sp, modify);
-    default:
-        return gen_illegal(ctx);
-    }
+    nullify_over(ctx);
+    return do_addb(ctx, a->r, load_const(ctx, a->i), a->c, a->f, a->n, a->disp);
 }
 
-static DisasJumpType trans_cmpb(DisasContext *ctx, uint32_t insn,
-                                bool is_true, bool is_imm, bool is_dw)
+static bool trans_bb_sar(DisasContext *ctx, arg_bb_sar *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;
+    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);
-    return do_cbranch(ctx, disp, n, &cond);
+    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 DisasJumpType 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);
-    return do_cbranch(ctx, disp, n, &cond);
+    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 DisasJumpType 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);
-    return do_cbranch(ctx, disp, n, &cond);
+    cond = do_sed_cond(a->c, dest);
+    return do_cbranch(ctx, a->disp, a->n, &cond);
 }
 
-static DisasJumpType 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);
-    return do_cbranch(ctx, disp, n, &cond);
+    cond = do_sed_cond(a->c, dest);
+    return do_cbranch(ctx, a->disp, a->n, &cond);
 }
 
-static DisasJumpType 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);
@@ -3497,7 +3151,7 @@ static DisasJumpType 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);
@@ -3505,79 +3159,67 @@ static DisasJumpType 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, DISAS_NEXT);
+    return nullify_end(ctx);
 }
 
-static DisasJumpType 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, DISAS_NEXT);
+    return nullify_end(ctx);
 }
 
-static DisasJumpType 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 {
@@ -3585,83 +3227,62 @@ static DisasJumpType 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, DISAS_NEXT);
+    return nullify_end(ctx);
 }
 
-static DisasJumpType 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, DISAS_NEXT);
+    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 DisasJumpType 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;
@@ -3670,75 +3291,58 @@ static DisasJumpType 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, DISAS_NEXT);
+    return nullify_end(ctx);
 }
 
-static DisasJumpType 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, DISAS_NEXT);
+    return nullify_end(ctx);
 }
 
-static DisasJumpType 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();
@@ -3766,20 +3370,21 @@ static DisasJumpType trans_depw_sar(DisasContext *ctx, uint32_t insn,
     if (c) {
         ctx->null_cond = do_sed_cond(c, dest);
     }
-    return nullify_end(ctx, DISAS_NEXT);
+    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 DisasJumpType trans_be(DisasContext *ctx, uint32_t insn, bool is_l)
+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, 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
@@ -3791,29 +3396,28 @@ static DisasJumpType 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) {
-        return do_dbranch(ctx, disp, is_l ? 31 : 0, n);
+    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
-    return 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);
@@ -3825,31 +3429,23 @@ static DisasJumpType 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();
-    return nullify_end(ctx, DISAS_NORETURN);
+    ctx->base.is_jmp = DISAS_NORETURN;
+    return nullify_end(ctx);
 #endif
 }
 
-static DisasJumpType 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);
-
-    return do_dbranch(ctx, iaoq_dest(ctx, disp), link, n);
+    return do_dbranch(ctx, iaoq_dest(ctx, a->disp), a->l, a->n);
 }
 
-static DisasJumpType 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);
 
     /* 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
@@ -3876,7 +3472,8 @@ static DisasJumpType trans_b_gate(DisasContext *ctx, uint32_t insn,
            we will re-translate, at which point we *will* be able to find
            the TLB entry and determine if this is in fact a gateway page.  */
         if (type < 0) {
-            return gen_excp(ctx, EXCP_ITLB_MISS);
+            gen_excp(ctx, EXCP_ITLB_MISS);
+            return true;
         }
         /* No change for non-gateway pages or for priv decrease.  */
         if (type >= 4 && type - 4 < ctx->privilege) {
@@ -3887,65 +3484,44 @@ static DisasJumpType trans_b_gate(DisasContext *ctx, uint32_t insn,
     }
 #endif
 
-    return do_dbranch(ctx, dest, link, n);
+    return do_dbranch(ctx, dest, a->l, a->n);
 }
 
-static DisasJumpType trans_bl_long(DisasContext *ctx, uint32_t insn,
-                                   const DisasInsn *di)
+static bool trans_blr(DisasContext *ctx, arg_blr *a)
 {
-    unsigned n = extract32(insn, 1, 1);
-    target_sreg disp = assemble_22(insn);
-
-    return do_dbranch(ctx, iaoq_dest(ctx, disp), 2, n);
-}
-
-static DisasJumpType trans_blr(DisasContext *ctx, uint32_t insn,
-                               const DisasInsn *di)
-{
-    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_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, link, n);
+    return do_ibranch(ctx, tmp, a->l, a->n);
 }
 
-static DisasJumpType 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);
-    return do_ibranch(ctx, dest, 0, n);
+    return do_ibranch(ctx, dest, 0, a->n);
 }
 
-static DisasJumpType 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));
-    return 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) {
@@ -3953,158 +3529,268 @@ static DisasJumpType trans_bve(DisasContext *ctx, uint32_t insn,
     }
     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();
-    return nullify_end(ctx, DISAS_NORETURN);
+    ctx->base.is_jmp = DISAS_NORETURN;
+    return nullify_end(ctx);
 #endif
 }
 
-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 DisasJumpType 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);
-    return do_fop_wew(ctx, rt, ra, di->f.wew);
+    tcg_gen_mov_i32(dst, src);
 }
 
-static DisasJumpType 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);
-    return do_fop_wew(ctx, rt, ra, di->f.wew);
+    return do_fop_wew(ctx, a->t, a->r, gen_fcpy_f);
 }
 
-static DisasJumpType 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);
-    return do_fop_ded(ctx, rt, ra, di->f.ded);
+    tcg_gen_mov_i64(dst, src);
 }
 
-static DisasJumpType 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);
-    return do_fop_wed(ctx, rt, ra, di->f.wed);
+    return do_fop_ded(ctx, a->t, a->r, gen_fcpy_d);
 }
 
-static DisasJumpType 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);
-    return do_fop_wed(ctx, rt, ra, di->f.wed);
+    tcg_gen_andi_i32(dst, src, INT32_MAX);
 }
 
-static DisasJumpType 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);
-    return do_fop_dew(ctx, rt, ra, di->f.dew);
+    return do_fop_wew(ctx, a->t, a->r, gen_fabs_f);
 }
 
-static DisasJumpType 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);
-    return do_fop_dew(ctx, rt, ra, di->f.dew);
+    tcg_gen_andi_i64(dst, src, INT64_MAX);
 }
 
-static DisasJumpType 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);
-    return do_fop_weww(ctx, rt, ra, rb, di->f.weww);
+    return do_fop_ded(ctx, a->t, a->r, gen_fabs_d);
 }
 
-static DisasJumpType 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);
-    return do_fop_weww(ctx, rt, ra, rb, di->f.weww);
+    return do_fop_wew(ctx, a->t, a->r, gen_helper_fsqrt_s);
 }
 
-static DisasJumpType 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);
-    return do_fop_dedd(ctx, rt, ra, rb, di->f.dedd);
+    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 DisasJumpType do_fcmp_s(DisasContext *ctx, unsigned ra, unsigned rb,
-                               unsigned y, unsigned c)
+static bool trans_fnegabs_d(DisasContext *ctx, arg_fclass01 *a)
+{
+    return do_fop_ded(ctx, a->t, a->r, gen_fnegabs_d);
+}
+
+/*
+ * Float class 1
+ */
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+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_fcnv_q_d(DisasContext *ctx, arg_fclass01 *a)
+{
+    return do_fop_ded(ctx, a->t, a->r, gen_helper_fcnv_dw_d);
+}
+
+static bool trans_fcnv_f_w(DisasContext *ctx, arg_fclass01 *a)
+{
+    return do_fop_wew(ctx, a->t, a->r, gen_helper_fcnv_s_w);
+}
+
+static bool trans_fcnv_d_w(DisasContext *ctx, arg_fclass01 *a)
+{
+    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_frw0_i32(ra);
-    tb = load_frw0_i32(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_s(cpu_env, ta, tb, ty, tc);
 
@@ -4113,45 +3799,20 @@ static DisasJumpType do_fcmp_s(DisasContext *ctx, unsigned ra, unsigned rb,
     tcg_temp_free_i32(ty);
     tcg_temp_free_i32(tc);
 
-    return nullify_end(ctx, DISAS_NEXT);
-}
-
-static DisasJumpType trans_fcmp_s_0c(DisasContext *ctx, uint32_t insn,
-                                     const DisasInsn *di)
-{
-    unsigned c = extract32(insn, 0, 5);
-    unsigned y = extract32(insn, 13, 3);
-    unsigned rb = extract32(insn, 16, 5);
-    unsigned ra = extract32(insn, 21, 5);
-    return do_fcmp_s(ctx, ra, rb, y, c);
+    return nullify_end(ctx);
 }
 
-static DisasJumpType trans_fcmp_s_0e(DisasContext *ctx, uint32_t insn,
-                                     const DisasInsn *di)
+static bool trans_fcmp_d(DisasContext *ctx, arg_fclass2 *a)
 {
-    unsigned c = extract32(insn, 0, 5);
-    unsigned y = extract32(insn, 13, 3);
-    unsigned rb = assemble_rb64(insn);
-    unsigned ra = assemble_ra64(insn);
-    return do_fcmp_s(ctx, ra, rb, y, c);
-}
-
-static DisasJumpType trans_fcmp_d(DisasContext *ctx, uint32_t insn,
-                                  const DisasInsn *di)
-{
-    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;
 
     nullify_over(ctx);
 
-    ta = load_frd0(ra);
-    tb = load_frd0(rb);
-    ty = tcg_const_i32(y);
-    tc = tcg_const_i32(c);
+    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);
 
@@ -4160,266 +3821,131 @@ static DisasJumpType trans_fcmp_d(DisasContext *ctx, uint32_t insn,
     tcg_temp_free_i32(ty);
     tcg_temp_free_i32(tc);
 
-    return nullify_end(ctx, DISAS_NEXT);
+    return nullify_end(ctx);
 }
 
-static DisasJumpType trans_ftest_t(DisasContext *ctx, uint32_t insn,
-                                   const DisasInsn *di)
+static bool trans_ftest(DisasContext *ctx, arg_ftest *a)
 {
-    unsigned y = extract32(insn, 13, 3);
-    unsigned cbit = (y ^ 1) - 1;
     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));
-    tcg_gen_extract_reg(t, t, 21 - cbit, 1);
-    ctx->null_cond = cond_make_0(TCG_COND_NE, t);
-    tcg_temp_free(t);
 
-    return nullify_end(ctx, DISAS_NEXT);
+    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 {
+        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 DisasJumpType trans_ftest_q(DisasContext *ctx, uint32_t insn,
-                                   const DisasInsn *di)
+/*
+ * Float class 2
+ */
+
+static bool trans_fadd_f(DisasContext *ctx, arg_fclass3 *a)
 {
-    unsigned c = extract32(insn, 0, 5);
-    int mask;
-    bool inv = false;
-    TCGv_reg t;
+    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);
+}
 
-    t = tcg_temp_new();
-    tcg_gen_ld32u_reg(t, cpu_env, offsetof(CPUHPPAState, fr0_shadow));
+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);
+}
 
-    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);
-    } else {
-        tcg_gen_andi_reg(t, t, mask);
-        ctx->null_cond = cond_make_0(TCG_COND_EQ, t);
-    }
- done:
-    return nullify_end(ctx, DISAS_NEXT);
+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);
 }
 
-static DisasJumpType trans_xmpyu(DisasContext *ctx, uint32_t insn,
-                                 const DisasInsn *di)
+static bool trans_fmpy_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_fmpy_s);
+}
 
-    nullify_over(ctx);
+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);
+}
 
-    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_fdiv_f(DisasContext *ctx, arg_fclass3 *a)
+{
+    return do_fop_weww(ctx, a->t, a->r1, a->r2, gen_helper_fdiv_s);
+}
 
-    return nullify_end(ctx, DISAS_NEXT);
-}
-
-#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_fdiv_d(DisasContext *ctx, arg_fclass3 *a)
+{
+    return do_fop_dedd(ctx, a->t, a->r1, a->r2, gen_helper_fdiv_d);
+}
 
-#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_xmpyu(DisasContext *ctx, arg_xmpyu *a)
+{
+    TCGv_i64 x, y;
 
-#undef FOP_WEW
-#undef FOP_DEW
-#undef FOP_WED
-#undef FOP_WEWW
-#undef FOP_DED
-#undef FOP_DEDD
+    nullify_over(ctx);
+
+    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);
+}
 
 /* Convert the fmpyadd single-precision register encodings to standard.  */
 static inline int fmpyadd_s_reg(unsigned r)
@@ -4427,246 +3953,96 @@ static inline int fmpyadd_s_reg(unsigned r)
     return (r & 16) * 2 + 16 + (r & 15);
 }
 
-static DisasJumpType trans_fmpyadd(DisasContext *ctx,
-                                   uint32_t insn, bool is_sub)
+static bool do_fmpyadd_s(DisasContext *ctx, arg_mpyadd *a, bool is_sub)
 {
-    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);
+    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);
 
-    /* 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);
-    }
+    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);
 
-    return nullify_end(ctx, DISAS_NEXT);
+    return nullify_end(ctx);
 }
 
-static DisasJumpType trans_fmpyfadd_s(DisasContext *ctx, uint32_t insn,
-                                      const DisasInsn *di)
+static bool trans_fmpyadd_f(DisasContext *ctx, arg_mpyadd *a)
 {
-    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;
+    return do_fmpyadd_s(ctx, a, false);
+}
+
+static bool trans_fmpysub_f(DisasContext *ctx, arg_mpyadd *a)
+{
+    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_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_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_i32(b);
-    tcg_temp_free_i32(c);
-    save_frw_i32(rt, a);
-    tcg_temp_free_i32(a);
-    return nullify_end(ctx, DISAS_NEXT);
+    return nullify_end(ctx);
+}
+
+static bool trans_fmpyadd_d(DisasContext *ctx, arg_mpyadd *a)
+{
+    return do_fmpyadd_d(ctx, a, false);
+}
+
+static bool trans_fmpysub_d(DisasContext *ctx, arg_mpyadd *a)
+{
+    return do_fmpyadd_d(ctx, a, true);
 }
 
-static DisasJumpType trans_fmpyfadd_d(DisasContext *ctx, uint32_t insn,
-                                      const DisasInsn *di)
+static bool trans_fmpyfadd_f(DisasContext *ctx, arg_fmpyfadd_f *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;
+    TCGv_i32 x, y, z;
 
     nullify_over(ctx);
-    a = load_frd0(rm1);
-    b = load_frd0(rm2);
-    c = load_frd0(ra3);
+    x = load_frw0_i32(a->rm1);
+    y = load_frw0_i32(a->rm2);
+    z = load_frw0_i32(a->ra3);
 
-    if (neg) {
-        gen_helper_fmpynfadd_d(a, cpu_env, a, b, c);
+    if (a->neg) {
+        gen_helper_fmpynfadd_s(x, cpu_env, x, y, z);
     } else {
-        gen_helper_fmpyfadd_d(a, cpu_env, a, b, c);
+        gen_helper_fmpyfadd_s(x, cpu_env, x, y, z);
     }
 
-    tcg_temp_free_i64(b);
-    tcg_temp_free_i64(c);
-    save_frd(rt, a);
-    tcg_temp_free_i64(a);
-    return nullify_end(ctx, DISAS_NEXT);
+    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);
 }
 
-static const DisasInsn table_fp_fused[] = {
-    { 0xb8000000u, 0xfc000800u, trans_fmpyfadd_s },
-    { 0xb8000800u, 0xfc0019c0u, trans_fmpyfadd_d }
-};
-
-static DisasJumpType translate_table_int(DisasContext *ctx, uint32_t insn,
-                                         const DisasInsn table[], size_t n)
+static bool trans_fmpyfadd_d(DisasContext *ctx, arg_fmpyfadd_d *a)
 {
-    size_t i;
-    for (i = 0; i < n; ++i) {
-        if ((insn & table[i].mask) == table[i].insn) {
-            return table[i].trans(ctx, insn, &table[i]);
-        }
-    }
-    qemu_log_mask(LOG_UNIMP, "UNIMP insn %08x @ " TARGET_FMT_lx "\n",
-                  insn, ctx->base.pc_next);
-    return gen_illegal(ctx);
-}
-
-#define translate_table(ctx, insn, table) \
-    translate_table_int(ctx, insn, table, ARRAY_SIZE(table))
-
-static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn)
-{
-    uint32_t opc = extract32(insn, 26, 6);
-
-    switch (opc) {
-    case 0x00: /* system op */
-        return translate_table(ctx, insn, table_system);
-    case 0x01:
-        return translate_table(ctx, insn, table_mem_mgmt);
-    case 0x02:
-        return translate_table(ctx, insn, table_arith_log);
-    case 0x03:
-        return translate_table(ctx, insn, table_index_mem);
-    case 0x06:
-        return trans_fmpyadd(ctx, insn, false);
-    case 0x08:
-        return trans_ldil(ctx, insn);
-    case 0x09:
-        return trans_copr_w(ctx, insn);
-    case 0x0A:
-        return trans_addil(ctx, insn);
-    case 0x0B:
-        return trans_copr_dw(ctx, insn);
-    case 0x0C:
-        return translate_table(ctx, insn, table_float_0c);
-    case 0x0D:
-        return trans_ldo(ctx, insn);
-    case 0x0E:
-        return translate_table(ctx, insn, table_float_0e);
-
-    case 0x10:
-        return trans_load(ctx, insn, false, MO_UB);
-    case 0x11:
-        return trans_load(ctx, insn, false, MO_TEUW);
-    case 0x12:
-        return trans_load(ctx, insn, false, MO_TEUL);
-    case 0x13:
-        return trans_load(ctx, insn, true, MO_TEUL);
-    case 0x16:
-        return trans_fload_mod(ctx, insn);
-    case 0x17:
-        return trans_load_w(ctx, insn);
-    case 0x18:
-        return trans_store(ctx, insn, false, MO_UB);
-    case 0x19:
-        return trans_store(ctx, insn, false, MO_TEUW);
-    case 0x1A:
-        return trans_store(ctx, insn, false, MO_TEUL);
-    case 0x1B:
-        return trans_store(ctx, insn, true, MO_TEUL);
-    case 0x1E:
-        return trans_fstore_mod(ctx, insn);
-    case 0x1F:
-        return trans_store_w(ctx, insn);
-
-    case 0x20:
-        return trans_cmpb(ctx, insn, true, false, false);
-    case 0x21:
-        return trans_cmpb(ctx, insn, true, true, false);
-    case 0x22:
-        return trans_cmpb(ctx, insn, false, false, false);
-    case 0x23:
-        return trans_cmpb(ctx, insn, false, true, false);
-    case 0x24:
-        return trans_cmpiclr(ctx, insn);
-    case 0x25:
-        return trans_subi(ctx, insn);
-    case 0x26:
-        return trans_fmpyadd(ctx, insn, true);
-    case 0x27:
-        return trans_cmpb(ctx, insn, true, false, true);
-    case 0x28:
-        return trans_addb(ctx, insn, true, false);
-    case 0x29:
-        return trans_addb(ctx, insn, true, true);
-    case 0x2A:
-        return trans_addb(ctx, insn, false, false);
-    case 0x2B:
-        return trans_addb(ctx, insn, false, true);
-    case 0x2C:
-    case 0x2D:
-        return trans_addi(ctx, insn);
-    case 0x2E:
-        return translate_table(ctx, insn, table_fp_fused);
-    case 0x2F:
-        return trans_cmpb(ctx, insn, false, false, true);
-
-    case 0x30:
-    case 0x31:
-        return trans_bb(ctx, insn);
-    case 0x32:
-        return trans_movb(ctx, insn, false);
-    case 0x33:
-        return trans_movb(ctx, insn, true);
-    case 0x34:
-        return translate_table(ctx, insn, table_sh_ex);
-    case 0x35:
-        return translate_table(ctx, insn, table_depw);
-    case 0x38:
-        return trans_be(ctx, insn, false);
-    case 0x39:
-        return trans_be(ctx, insn, true);
-    case 0x3A:
-        return translate_table(ctx, insn, table_branch);
-
-    case 0x04: /* spopn */
-    case 0x05: /* diag */
-    case 0x0F: /* product specific */
-        break;
+    TCGv_i64 x, y, z;
 
-    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) {
-            return gen_hlt(ctx, 0); /* halt system */
-        }
-        if (insn == 0xfffdead1) {
-            return gen_hlt(ctx, 1); /* reset system */
-        }
-#endif
-        break;
-    default:
-        break;
+    nullify_over(ctx);
+    x = load_frd0(a->rm1);
+    y = load_frd0(a->rm2);
+    z = load_frd0(a->ra3);
+
+    if (a->neg) {
+        gen_helper_fmpynfadd_d(x, cpu_env, x, y, z);
+    } else {
+        gen_helper_fmpyfadd_d(x, cpu_env, x, y, z);
     }
-    return gen_illegal(ctx);
+
+    tcg_temp_free_i64(y);
+    tcg_temp_free_i64(z);
+    save_frd(a->t, x);
+    tcg_temp_free_i64(x);
+    return nullify_end(ctx);
 }
 
 static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
@@ -4733,7 +4109,7 @@ static bool hppa_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs,
 {
     DisasContext *ctx = container_of(dcbase, DisasContext, base);
 
-    ctx->base.is_jmp = gen_excp(ctx, EXCP_DEBUG);
+    gen_excp(ctx, EXCP_DEBUG);
     ctx->base.pc_next += 4;
     return true;
 }
@@ -4748,7 +4124,8 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
     /* Execute one insn.  */
 #ifdef CONFIG_USER_ONLY
     if (ctx->base.pc_next < TARGET_PAGE_SIZE) {
-        ret = do_page_zero(ctx);
+        do_page_zero(ctx);
+        ret = ctx->base.is_jmp;
         assert(ret != DISAS_NEXT);
     } else
 #endif
@@ -4773,7 +4150,10 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
             ret = DISAS_NEXT;
         } else {
             ctx->insn = insn;
-            ret = translate_one(ctx, insn);
+            if (!decode(ctx, insn)) {
+                gen_illegal(ctx);
+            }
+            ret = ctx->base.is_jmp;
             assert(ctx->null_lab == NULL);
         }
     }
@@ -4799,14 +4179,13 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
                 || ctx->null_cond.c == TCG_COND_ALWAYS)) {
             nullify_set(ctx, ctx->null_cond.c == TCG_COND_ALWAYS);
             gen_goto_tb(ctx, 0, ctx->iaoq_b, ctx->iaoq_n);
-            ret = DISAS_NORETURN;
+            ctx->base.is_jmp = ret = DISAS_NORETURN;
         } else {
-            ret = DISAS_IAQ_N_STALE;
+            ctx->base.is_jmp = ret = DISAS_IAQ_N_STALE;
         }
     }
     ctx->iaoq_f = ctx->iaoq_b;
     ctx->iaoq_b = ctx->iaoq_n;
-    ctx->base.is_jmp = ret;
     ctx->base.pc_next += 4;
 
     if (ret == DISAS_NORETURN || ret == DISAS_IAQ_N_UPDATED) {
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 28d7e5302f..cc3ddc0ae4 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -88,7 +88,7 @@ typedef struct RISCVCPUInfo {
 
 static void set_misa(CPURISCVState *env, target_ulong misa)
 {
-    env->misa = misa;
+    env->misa_mask = env->misa = misa;
 }
 
 static void set_versions(CPURISCVState *env, int user_ver, int priv_ver)
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 743f02c8b9..5c2aebf132 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -86,7 +86,8 @@
    so a cpu features bitfield is required, likewise for optional PMP support */
 enum {
     RISCV_FEATURE_MMU,
-    RISCV_FEATURE_PMP
+    RISCV_FEATURE_PMP,
+    RISCV_FEATURE_MISA
 };
 
 #define USER_VERSION_2_02_0 0x00020200
@@ -118,6 +119,7 @@ struct CPURISCVState {
     target_ulong user_ver;
     target_ulong priv_ver;
     target_ulong misa;
+    target_ulong misa_mask;
 
     uint32_t features;
 
@@ -256,7 +258,7 @@ int riscv_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size,
 char *riscv_isa_string(RISCVCPU *cpu);
 void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf);
 
-#define cpu_signal_handler cpu_riscv_signal_handler
+#define cpu_signal_handler riscv_cpu_signal_handler
 #define cpu_list riscv_cpu_list
 #define cpu_mmu_index riscv_cpu_mmu_index
 
@@ -264,19 +266,18 @@ void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf);
 uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value);
 #define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */
 #endif
-void riscv_set_mode(CPURISCVState *env, target_ulong newpriv);
+void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv);
 
 void riscv_translate_init(void);
-RISCVCPU *cpu_riscv_init(const char *cpu_model);
-int cpu_riscv_signal_handler(int host_signum, void *pinfo, void *puc);
-void QEMU_NORETURN do_raise_exception_err(CPURISCVState *env,
-                                          uint32_t exception, uintptr_t pc);
+int riscv_cpu_signal_handler(int host_signum, void *pinfo, void *puc);
+void QEMU_NORETURN riscv_raise_exception(CPURISCVState *env,
+                                         uint32_t exception, uintptr_t pc);
 
-target_ulong cpu_riscv_get_fflags(CPURISCVState *env);
-void cpu_riscv_set_fflags(CPURISCVState *env, target_ulong);
+target_ulong riscv_cpu_get_fflags(CPURISCVState *env);
+void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong);
 
-#define TB_FLAGS_MMU_MASK  3
-#define TB_FLAGS_FP_ENABLE MSTATUS_FS
+#define TB_FLAGS_MMU_MASK   3
+#define TB_FLAGS_MSTATUS_FS MSTATUS_FS
 
 static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc,
                                         target_ulong *cs_base, uint32_t *flags)
@@ -284,7 +285,7 @@ static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc,
     *pc = env->pc;
     *cs_base = 0;
 #ifdef CONFIG_USER_ONLY
-    *flags = TB_FLAGS_FP_ENABLE;
+    *flags = TB_FLAGS_MSTATUS_FS;
 #else
     *flags = cpu_mmu_index(env, 0) | (env->mstatus & MSTATUS_FS);
 #endif
@@ -293,13 +294,13 @@ static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc,
 int riscv_csrrw(CPURISCVState *env, int csrno, target_ulong *ret_value,
                 target_ulong new_value, target_ulong write_mask);
 
-static inline void csr_write_helper(CPURISCVState *env, target_ulong val,
-                                    int csrno)
+static inline void riscv_csr_write(CPURISCVState *env, int csrno,
+                                   target_ulong val)
 {
     riscv_csrrw(env, csrno, NULL, val, MAKE_64BIT_MASK(0, TARGET_LONG_BITS));
 }
 
-static inline target_ulong csr_read_helper(CPURISCVState *env, int csrno)
+static inline target_ulong riscv_csr_read(CPURISCVState *env, int csrno)
 {
     target_ulong val = 0;
     riscv_csrrw(env, csrno, &val, 0, 0);
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index 5439f4719e..7afcb2468d 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -311,10 +311,21 @@
 #define MSTATUS32_SD        0x80000000
 #define MSTATUS64_SD        0x8000000000000000ULL
 
+#define MISA32_MXL          0xC0000000
+#define MISA64_MXL          0xC000000000000000ULL
+
+#define MXL_RV32            1
+#define MXL_RV64            2
+#define MXL_RV128           3
+
 #if defined(TARGET_RISCV32)
 #define MSTATUS_SD MSTATUS32_SD
+#define MISA_MXL MISA32_MXL
+#define MXL_VAL MXL_RV32
 #elif defined(TARGET_RISCV64)
 #define MSTATUS_SD MSTATUS64_SD
+#define MISA_MXL MISA64_MXL
+#define MXL_VAL MXL_RV64
 #endif
 
 /* sstatus CSR bits */
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index f257050f12..f49e98ed59 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -93,7 +93,7 @@ uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value)
     return old;
 }
 
-void riscv_set_mode(CPURISCVState *env, target_ulong newpriv)
+void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv)
 {
     if (newpriv > PRV_M) {
         g_assert_not_reached();
@@ -366,7 +366,7 @@ void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
         g_assert_not_reached();
     }
     env->badaddr = addr;
-    do_raise_exception_err(env, cs->exception_index, retaddr);
+    riscv_raise_exception(env, cs->exception_index, retaddr);
 }
 
 /* called by qemu's softmmu to fill the qemu tlb */
@@ -378,7 +378,7 @@ void tlb_fill(CPUState *cs, target_ulong addr, int size,
     if (ret == TRANSLATE_FAIL) {
         RISCVCPU *cpu = RISCV_CPU(cs);
         CPURISCVState *env = &cpu->env;
-        do_raise_exception_err(env, cs->exception_index, retaddr);
+        riscv_raise_exception(env, cs->exception_index, retaddr);
     }
 }
 
@@ -530,7 +530,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
         s = set_field(s, MSTATUS_SPP, env->priv);
         s = set_field(s, MSTATUS_SIE, 0);
         env->mstatus = s;
-        riscv_set_mode(env, PRV_S);
+        riscv_cpu_set_mode(env, PRV_S);
     } else {
         /* No need to check MTVEC for misaligned - lower 2 bits cannot be set */
         env->pc = env->mtvec;
@@ -555,7 +555,7 @@ void riscv_cpu_do_interrupt(CPUState *cs)
         s = set_field(s, MSTATUS_MPP, env->priv);
         s = set_field(s, MSTATUS_MIE, 0);
         env->mstatus = s;
-        riscv_set_mode(env, PRV_M);
+        riscv_cpu_set_mode(env, PRV_M);
     }
     /* TODO yield load reservation  */
 #endif
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 5e7e7d16b8..960d2b0aa9 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -56,9 +56,15 @@ static int fs(CPURISCVState *env, int csrno)
 static int ctr(CPURISCVState *env, int csrno)
 {
 #if !defined(CONFIG_USER_ONLY)
-    target_ulong ctr_en = env->priv == PRV_U ? env->scounteren :
-                          env->priv == PRV_S ? env->mcounteren : -1U;
-    if (!(ctr_en & (1 << (csrno & 31)))) {
+    uint32_t ctr_en = ~0u;
+
+    if (env->priv < PRV_M) {
+        ctr_en &= env->mcounteren;
+    }
+    if (env->priv < PRV_S) {
+        ctr_en &= env->scounteren;
+    }
+    if (!(ctr_en & (1u << (csrno & 31)))) {
         return -1;
     }
 #endif
@@ -90,7 +96,7 @@ static int read_fflags(CPURISCVState *env, int csrno, target_ulong *val)
         return -1;
     }
 #endif
-    *val = cpu_riscv_get_fflags(env);
+    *val = riscv_cpu_get_fflags(env);
     return 0;
 }
 
@@ -102,7 +108,7 @@ static int write_fflags(CPURISCVState *env, int csrno, target_ulong val)
     }
     env->mstatus |= MSTATUS_FS;
 #endif
-    cpu_riscv_set_fflags(env, val & (FSR_AEXC >> FSR_AEXC_SHIFT));
+    riscv_cpu_set_fflags(env, val & (FSR_AEXC >> FSR_AEXC_SHIFT));
     return 0;
 }
 
@@ -136,7 +142,7 @@ static int read_fcsr(CPURISCVState *env, int csrno, target_ulong *val)
         return -1;
     }
 #endif
-    *val = (cpu_riscv_get_fflags(env) << FSR_AEXC_SHIFT)
+    *val = (riscv_cpu_get_fflags(env) << FSR_AEXC_SHIFT)
         | (env->frm << FSR_RD_SHIFT);
     return 0;
 }
@@ -150,7 +156,7 @@ static int write_fcsr(CPURISCVState *env, int csrno, target_ulong val)
     env->mstatus |= MSTATUS_FS;
 #endif
     env->frm = (val & FSR_RD) >> FSR_RD_SHIFT;
-    cpu_riscv_set_fflags(env, (val & FSR_AEXC) >> FSR_AEXC_SHIFT);
+    riscv_cpu_set_fflags(env, (val & FSR_AEXC) >> FSR_AEXC_SHIFT);
     return 0;
 }
 
@@ -305,7 +311,8 @@ static int write_mstatus(CPURISCVState *env, int csrno, target_ulong val)
         }
         mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE |
             MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM |
-            MSTATUS_MPP | MSTATUS_MXR;
+            MSTATUS_MPP | MSTATUS_MXR | MSTATUS_TVM | MSTATUS_TSR |
+            MSTATUS_TW;
     }
 
     /* silenty discard mstatus.mpp writes for unsupported modes */
@@ -317,18 +324,6 @@ static int write_mstatus(CPURISCVState *env, int csrno, target_ulong val)
 
     mstatus = (mstatus & ~mask) | (val & mask);
 
-    /* Note: this is a workaround for an issue where mstatus.FS
-       does not report dirty after floating point operations
-       that modify floating point state. This workaround is
-       technically compliant with the RISC-V Privileged
-       specification as it is legal to return only off, or dirty.
-       at the expense of extra floating point save/restore. */
-
-    /* FP is always dirty or off */
-    if (mstatus & MSTATUS_FS) {
-        mstatus |= MSTATUS_FS;
-    }
-
     int dirty = ((mstatus & MSTATUS_FS) == MSTATUS_FS) |
                 ((mstatus & MSTATUS_XS) == MSTATUS_XS);
     mstatus = set_field(mstatus, MSTATUS_SD, dirty);
@@ -343,6 +338,58 @@ static int read_misa(CPURISCVState *env, int csrno, target_ulong *val)
     return 0;
 }
 
+static int write_misa(CPURISCVState *env, int csrno, target_ulong val)
+{
+    if (!riscv_feature(env, RISCV_FEATURE_MISA)) {
+        /* drop write to misa */
+        return 0;
+    }
+
+    /* 'I' or 'E' must be present */
+    if (!(val & (RVI | RVE))) {
+        /* It is not, drop write to misa */
+        return 0;
+    }
+
+    /* 'E' excludes all other extensions */
+    if (val & RVE) {
+        /* when we support 'E' we can do "val = RVE;" however
+         * for now we just drop writes if 'E' is present.
+         */
+        return 0;
+    }
+
+    /* Mask extensions that are not supported by this hart */
+    val &= env->misa_mask;
+
+    /* Mask extensions that are not supported by QEMU */
+    val &= (RVI | RVE | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
+
+    /* 'D' depends on 'F', so clear 'D' if 'F' is not present */
+    if ((val & RVD) && !(val & RVF)) {
+        val &= ~RVD;
+    }
+
+    /* Suppress 'C' if next instruction is not aligned
+     * TODO: this should check next_pc
+     */
+    if ((val & RVC) && (GETPC() & ~3) != 0) {
+        val &= ~RVC;
+    }
+
+    /* misa.MXL writes are not supported by QEMU */
+    val = (env->misa & MISA_MXL) | (val & ~MISA_MXL);
+
+    /* flush translation cache */
+    if (val != env->misa) {
+        tb_flush(CPU(riscv_env_get_cpu(env)));
+    }
+
+    env->misa = val;
+
+    return 0;
+}
+
 static int read_medeleg(CPURISCVState *env, int csrno, target_ulong *val)
 {
     *val = env->medeleg;
@@ -654,7 +701,11 @@ static int read_satp(CPURISCVState *env, int csrno, target_ulong *val)
     if (!riscv_feature(env, RISCV_FEATURE_MMU)) {
         *val = 0;
     } else if (env->priv_ver >= PRIV_VERSION_1_10_0) {
-        *val = env->satp;
+        if (env->priv == PRV_S && get_field(env->mstatus, MSTATUS_TVM)) {
+            return -1;
+        } else {
+            *val = env->satp;
+        }
     } else {
         *val = env->sptbr;
     }
@@ -675,8 +726,12 @@ static int write_satp(CPURISCVState *env, int csrno, target_ulong val)
         validate_vm(env, get_field(val, SATP_MODE)) &&
         ((val ^ env->satp) & (SATP_MODE | SATP_ASID | SATP_PPN)))
     {
-        tlb_flush(CPU(riscv_env_get_cpu(env)));
-        env->satp = val;
+        if (env->priv == PRV_S && get_field(env->mstatus, MSTATUS_TVM)) {
+            return -1;
+        } else {
+            tlb_flush(CPU(riscv_env_get_cpu(env)));
+            env->satp = val;
+        }
     }
     return 0;
 }
@@ -813,7 +868,7 @@ static riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = {
 
     /* Machine Trap Setup */
     [CSR_MSTATUS] =             { any,  read_mstatus,     write_mstatus     },
-    [CSR_MISA] =                { any,  read_misa                           },
+    [CSR_MISA] =                { any,  read_misa,        write_misa        },
     [CSR_MIDELEG] =             { any,  read_mideleg,     write_mideleg     },
     [CSR_MEDELEG] =             { any,  read_medeleg,     write_medeleg     },
     [CSR_MIE] =                 { any,  read_mie,         write_mie         },
diff --git a/target/riscv/fpu_helper.c b/target/riscv/fpu_helper.c
index 01b45ca0ae..b4f818a646 100644
--- a/target/riscv/fpu_helper.c
+++ b/target/riscv/fpu_helper.c
@@ -22,7 +22,7 @@
 #include "exec/exec-all.h"
 #include "exec/helper-proto.h"
 
-target_ulong cpu_riscv_get_fflags(CPURISCVState *env)
+target_ulong riscv_cpu_get_fflags(CPURISCVState *env)
 {
     int soft = get_float_exception_flags(&env->fp_status);
     target_ulong hard = 0;
@@ -36,7 +36,7 @@ target_ulong cpu_riscv_get_fflags(CPURISCVState *env)
     return hard;
 }
 
-void cpu_riscv_set_fflags(CPURISCVState *env, target_ulong hard)
+void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong hard)
 {
     int soft = 0;
 
@@ -73,7 +73,7 @@ void helper_set_rounding_mode(CPURISCVState *env, uint32_t rm)
         softrm = float_round_ties_away;
         break;
     default:
-        do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
     }
 
     set_float_rounding_mode(softrm, &env->fp_status);
diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
index 81bd1a77ea..b7dc18a41e 100644
--- a/target/riscv/op_helper.c
+++ b/target/riscv/op_helper.c
@@ -25,7 +25,7 @@
 #include "exec/helper-proto.h"
 
 /* Exceptions processing helpers */
-void QEMU_NORETURN do_raise_exception_err(CPURISCVState *env,
+void QEMU_NORETURN riscv_raise_exception(CPURISCVState *env,
                                           uint32_t exception, uintptr_t pc)
 {
     CPUState *cs = CPU(riscv_env_get_cpu(env));
@@ -36,7 +36,7 @@ void QEMU_NORETURN do_raise_exception_err(CPURISCVState *env,
 
 void helper_raise_exception(CPURISCVState *env, uint32_t exception)
 {
-    do_raise_exception_err(env, exception, 0);
+    riscv_raise_exception(env, exception, 0);
 }
 
 target_ulong helper_csrrw(CPURISCVState *env, target_ulong src,
@@ -44,7 +44,7 @@ target_ulong helper_csrrw(CPURISCVState *env, target_ulong src,
 {
     target_ulong val = 0;
     if (riscv_csrrw(env, csr, &val, src, -1) < 0) {
-        do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
     }
     return val;
 }
@@ -54,7 +54,7 @@ target_ulong helper_csrrs(CPURISCVState *env, target_ulong src,
 {
     target_ulong val = 0;
     if (riscv_csrrw(env, csr, &val, -1, rs1_pass ? src : 0) < 0) {
-        do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
     }
     return val;
 }
@@ -64,7 +64,7 @@ target_ulong helper_csrrc(CPURISCVState *env, target_ulong src,
 {
     target_ulong val = 0;
     if (riscv_csrrw(env, csr, &val, 0, rs1_pass ? src : 0) < 0) {
-        do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
     }
     return val;
 }
@@ -74,12 +74,17 @@ target_ulong helper_csrrc(CPURISCVState *env, target_ulong src,
 target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb)
 {
     if (!(env->priv >= PRV_S)) {
-        do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
     }
 
     target_ulong retpc = env->sepc;
     if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) {
-        do_raise_exception_err(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
+        riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
+    }
+
+    if (env->priv_ver >= PRIV_VERSION_1_10_0 &&
+        get_field(env->mstatus, MSTATUS_TSR)) {
+        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
     }
 
     target_ulong mstatus = env->mstatus;
@@ -90,7 +95,7 @@ target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb)
         get_field(mstatus, MSTATUS_SPIE));
     mstatus = set_field(mstatus, MSTATUS_SPIE, 0);
     mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U);
-    riscv_set_mode(env, prev_priv);
+    riscv_cpu_set_mode(env, prev_priv);
     env->mstatus = mstatus;
 
     return retpc;
@@ -99,12 +104,12 @@ target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb)
 target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb)
 {
     if (!(env->priv >= PRV_M)) {
-        do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
     }
 
     target_ulong retpc = env->mepc;
     if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) {
-        do_raise_exception_err(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
+        riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
     }
 
     target_ulong mstatus = env->mstatus;
@@ -115,7 +120,7 @@ target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb)
         get_field(mstatus, MSTATUS_MPIE));
     mstatus = set_field(mstatus, MSTATUS_MPIE, 0);
     mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U);
-    riscv_set_mode(env, prev_priv);
+    riscv_cpu_set_mode(env, prev_priv);
     env->mstatus = mstatus;
 
     return retpc;
@@ -125,16 +130,28 @@ void helper_wfi(CPURISCVState *env)
 {
     CPUState *cs = CPU(riscv_env_get_cpu(env));
 
-    cs->halted = 1;
-    cs->exception_index = EXCP_HLT;
-    cpu_loop_exit(cs);
+    if (env->priv == PRV_S &&
+        env->priv_ver >= PRIV_VERSION_1_10_0 &&
+        get_field(env->mstatus, MSTATUS_TW)) {
+        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+    } else {
+        cs->halted = 1;
+        cs->exception_index = EXCP_HLT;
+        cpu_loop_exit(cs);
+    }
 }
 
 void helper_tlb_flush(CPURISCVState *env)
 {
     RISCVCPU *cpu = riscv_env_get_cpu(env);
     CPUState *cs = CPU(cpu);
-    tlb_flush(cs);
+    if (env->priv == PRV_S &&
+        env->priv_ver >= PRIV_VERSION_1_10_0 &&
+        get_field(env->mstatus, MSTATUS_TVM)) {
+        riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+    } else {
+        tlb_flush(cs);
+    }
 }
 
 #endif /* !CONFIG_USER_ONLY */
diff --git a/target/riscv/translate.c b/target/riscv/translate.c
index 312bf298b3..b7176cbf98 100644
--- a/target/riscv/translate.c
+++ b/target/riscv/translate.c
@@ -43,8 +43,10 @@ typedef struct DisasContext {
     DisasContextBase base;
     /* pc_succ_insn points to the instruction following base.pc_next */
     target_ulong pc_succ_insn;
+    target_ulong priv_ver;
     uint32_t opcode;
-    uint32_t flags;
+    uint32_t mstatus_fs;
+    uint32_t misa;
     uint32_t mem_idx;
     /* Remember the rounding mode encoded in the previous fp instruction,
        which we have already installed into env->fp_status.  Or -1 for
@@ -74,6 +76,11 @@ static const int tcg_memop_lookup[8] = {
 #define CASE_OP_32_64(X) case X
 #endif
 
+static inline bool has_ext(DisasContext *ctx, uint32_t ext)
+{
+    return ctx->misa & ext;
+}
+
 static void generate_exception(DisasContext *ctx, int excp)
 {
     tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
@@ -284,24 +291,42 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1,
         tcg_gen_and_tl(source1, source1, source2);
         break;
     CASE_OP_32_64(OPC_RISC_MUL):
+        if (!has_ext(ctx, RVM)) {
+            goto do_illegal;
+        }
         tcg_gen_mul_tl(source1, source1, source2);
         break;
     case OPC_RISC_MULH:
+        if (!has_ext(ctx, RVM)) {
+            goto do_illegal;
+        }
         tcg_gen_muls2_tl(source2, source1, source1, source2);
         break;
     case OPC_RISC_MULHSU:
+        if (!has_ext(ctx, RVM)) {
+            goto do_illegal;
+        }
         gen_mulhsu(source1, source1, source2);
         break;
     case OPC_RISC_MULHU:
+        if (!has_ext(ctx, RVM)) {
+            goto do_illegal;
+        }
         tcg_gen_mulu2_tl(source2, source1, source1, source2);
         break;
 #if defined(TARGET_RISCV64)
     case OPC_RISC_DIVW:
+        if (!has_ext(ctx, RVM)) {
+            goto do_illegal;
+        }
         tcg_gen_ext32s_tl(source1, source1);
         tcg_gen_ext32s_tl(source2, source2);
         /* fall through to DIV */
 #endif
     case OPC_RISC_DIV:
+        if (!has_ext(ctx, RVM)) {
+            goto do_illegal;
+        }
         /* Handle by altering args to tcg_gen_div to produce req'd results:
          * For overflow: want source1 in source1 and 1 in source2
          * For div by zero: want -1 in source1 and 1 in source2 -> -1 result */
@@ -333,11 +358,17 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1,
         break;
 #if defined(TARGET_RISCV64)
     case OPC_RISC_DIVUW:
+        if (!has_ext(ctx, RVM)) {
+            goto do_illegal;
+        }
         tcg_gen_ext32u_tl(source1, source1);
         tcg_gen_ext32u_tl(source2, source2);
         /* fall through to DIVU */
 #endif
     case OPC_RISC_DIVU:
+        if (!has_ext(ctx, RVM)) {
+            goto do_illegal;
+        }
         cond1 = tcg_temp_new();
         zeroreg = tcg_const_tl(0);
         resultopt1 = tcg_temp_new();
@@ -357,11 +388,17 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1,
         break;
 #if defined(TARGET_RISCV64)
     case OPC_RISC_REMW:
+        if (!has_ext(ctx, RVM)) {
+            goto do_illegal;
+        }
         tcg_gen_ext32s_tl(source1, source1);
         tcg_gen_ext32s_tl(source2, source2);
         /* fall through to REM */
 #endif
     case OPC_RISC_REM:
+        if (!has_ext(ctx, RVM)) {
+            goto do_illegal;
+        }
         cond1 = tcg_temp_new();
         cond2 = tcg_temp_new();
         zeroreg = tcg_const_tl(0);
@@ -389,11 +426,17 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1,
         break;
 #if defined(TARGET_RISCV64)
     case OPC_RISC_REMUW:
+        if (!has_ext(ctx, RVM)) {
+            goto do_illegal;
+        }
         tcg_gen_ext32u_tl(source1, source1);
         tcg_gen_ext32u_tl(source2, source2);
         /* fall through to REMU */
 #endif
     case OPC_RISC_REMU:
+        if (!has_ext(ctx, RVM)) {
+            goto do_illegal;
+        }
         cond1 = tcg_temp_new();
         zeroreg = tcg_const_tl(0);
         resultopt1 = tcg_temp_new();
@@ -411,6 +454,7 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1,
         tcg_temp_free(zeroreg);
         tcg_temp_free(resultopt1);
         break;
+    do_illegal:
     default:
         gen_exception_illegal(ctx);
         return;
@@ -505,14 +549,13 @@ static void gen_arith_imm(DisasContext *ctx, uint32_t opc, int rd,
     tcg_temp_free(source1);
 }
 
-static void gen_jal(CPURISCVState *env, DisasContext *ctx, int rd,
-                    target_ulong imm)
+static void gen_jal(DisasContext *ctx, int rd, target_ulong imm)
 {
     target_ulong next_pc;
 
     /* check misaligned: */
     next_pc = ctx->base.pc_next + imm;
-    if (!riscv_has_ext(env, RVC)) {
+    if (!has_ext(ctx, RVC)) {
         if ((next_pc & 0x3) != 0) {
             gen_exception_inst_addr_mis(ctx);
             return;
@@ -526,8 +569,8 @@ static void gen_jal(CPURISCVState *env, DisasContext *ctx, int rd,
     ctx->base.is_jmp = DISAS_NORETURN;
 }
 
-static void gen_jalr(CPURISCVState *env, DisasContext *ctx, uint32_t opc,
-                     int rd, int rs1, target_long imm)
+static void gen_jalr(DisasContext *ctx, uint32_t opc, int rd, int rs1,
+                     target_long imm)
 {
     /* no chaining with JALR */
     TCGLabel *misaligned = NULL;
@@ -539,7 +582,7 @@ static void gen_jalr(CPURISCVState *env, DisasContext *ctx, uint32_t opc,
         tcg_gen_addi_tl(cpu_pc, cpu_pc, imm);
         tcg_gen_andi_tl(cpu_pc, cpu_pc, (target_ulong)-2);
 
-        if (!riscv_has_ext(env, RVC)) {
+        if (!has_ext(ctx, RVC)) {
             misaligned = gen_new_label();
             tcg_gen_andi_tl(t0, cpu_pc, 0x2);
             tcg_gen_brcondi_tl(TCG_COND_NE, t0, 0x0, misaligned);
@@ -564,8 +607,8 @@ static void gen_jalr(CPURISCVState *env, DisasContext *ctx, uint32_t opc,
     tcg_temp_free(t0);
 }
 
-static void gen_branch(CPURISCVState *env, DisasContext *ctx, uint32_t opc,
-                       int rs1, int rs2, target_long bimm)
+static void gen_branch(DisasContext *ctx, uint32_t opc, int rs1, int rs2,
+                       target_long bimm)
 {
     TCGLabel *l = gen_new_label();
     TCGv source1, source2;
@@ -602,7 +645,7 @@ static void gen_branch(CPURISCVState *env, DisasContext *ctx, uint32_t opc,
 
     gen_goto_tb(ctx, 1, ctx->pc_succ_insn);
     gen_set_label(l); /* branch taken */
-    if (!riscv_has_ext(env, RVC) && ((ctx->base.pc_next + bimm) & 0x3)) {
+    if (!has_ext(ctx, RVC) && ((ctx->base.pc_next + bimm) & 0x3)) {
         /* misaligned */
         gen_exception_inst_addr_mis(ctx);
     } else {
@@ -651,12 +694,37 @@ static void gen_store(DisasContext *ctx, uint32_t opc, int rs1, int rs2,
     tcg_temp_free(dat);
 }
 
+#ifndef CONFIG_USER_ONLY
+/* The states of mstatus_fs are:
+ * 0 = disabled, 1 = initial, 2 = clean, 3 = dirty
+ * We will have already diagnosed disabled state,
+ * and need to turn initial/clean into dirty.
+ */
+static void mark_fs_dirty(DisasContext *ctx)
+{
+    TCGv tmp;
+    if (ctx->mstatus_fs == MSTATUS_FS) {
+        return;
+    }
+    /* Remember the state change for the rest of the TB.  */
+    ctx->mstatus_fs = MSTATUS_FS;
+
+    tmp = tcg_temp_new();
+    tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus));
+    tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS);
+    tcg_gen_st_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus));
+    tcg_temp_free(tmp);
+}
+#else
+static inline void mark_fs_dirty(DisasContext *ctx) { }
+#endif
+
 static void gen_fp_load(DisasContext *ctx, uint32_t opc, int rd,
         int rs1, target_long imm)
 {
     TCGv t0;
 
-    if (!(ctx->flags & TB_FLAGS_FP_ENABLE)) {
+    if (ctx->mstatus_fs == 0) {
         gen_exception_illegal(ctx);
         return;
     }
@@ -667,18 +735,27 @@ static void gen_fp_load(DisasContext *ctx, uint32_t opc, int rd,
 
     switch (opc) {
     case OPC_RISC_FLW:
+        if (!has_ext(ctx, RVF)) {
+            goto do_illegal;
+        }
         tcg_gen_qemu_ld_i64(cpu_fpr[rd], t0, ctx->mem_idx, MO_TEUL);
         /* RISC-V requires NaN-boxing of narrower width floating point values */
         tcg_gen_ori_i64(cpu_fpr[rd], cpu_fpr[rd], 0xffffffff00000000ULL);
         break;
     case OPC_RISC_FLD:
+        if (!has_ext(ctx, RVD)) {
+            goto do_illegal;
+        }
         tcg_gen_qemu_ld_i64(cpu_fpr[rd], t0, ctx->mem_idx, MO_TEQ);
         break;
+    do_illegal:
     default:
         gen_exception_illegal(ctx);
         break;
     }
     tcg_temp_free(t0);
+
+    mark_fs_dirty(ctx);
 }
 
 static void gen_fp_store(DisasContext *ctx, uint32_t opc, int rs1,
@@ -686,7 +763,7 @@ static void gen_fp_store(DisasContext *ctx, uint32_t opc, int rs1,
 {
     TCGv t0;
 
-    if (!(ctx->flags & TB_FLAGS_FP_ENABLE)) {
+    if (ctx->mstatus_fs == 0) {
         gen_exception_illegal(ctx);
         return;
     }
@@ -697,11 +774,18 @@ static void gen_fp_store(DisasContext *ctx, uint32_t opc, int rs1,
 
     switch (opc) {
     case OPC_RISC_FSW:
+        if (!has_ext(ctx, RVF)) {
+            goto do_illegal;
+        }
         tcg_gen_qemu_st_i64(cpu_fpr[rs2], t0, ctx->mem_idx, MO_TEUL);
         break;
     case OPC_RISC_FSD:
+        if (!has_ext(ctx, RVD)) {
+            goto do_illegal;
+        }
         tcg_gen_qemu_st_i64(cpu_fpr[rs2], t0, ctx->mem_idx, MO_TEQ);
         break;
+    do_illegal:
     default:
         gen_exception_illegal(ctx);
         break;
@@ -865,15 +949,22 @@ static void gen_fp_fmadd(DisasContext *ctx, uint32_t opc, int rd,
 {
     switch (opc) {
     case OPC_RISC_FMADD_S:
+        if (!has_ext(ctx, RVF)) {
+            goto do_illegal;
+        }
         gen_set_rm(ctx, rm);
         gen_helper_fmadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
                            cpu_fpr[rs2], cpu_fpr[rs3]);
         break;
     case OPC_RISC_FMADD_D:
+        if (!has_ext(ctx, RVD)) {
+            goto do_illegal;
+        }
         gen_set_rm(ctx, rm);
         gen_helper_fmadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
                            cpu_fpr[rs2], cpu_fpr[rs3]);
         break;
+    do_illegal:
     default:
         gen_exception_illegal(ctx);
         break;
@@ -885,15 +976,22 @@ static void gen_fp_fmsub(DisasContext *ctx, uint32_t opc, int rd,
 {
     switch (opc) {
     case OPC_RISC_FMSUB_S:
+        if (!has_ext(ctx, RVF)) {
+            goto do_illegal;
+        }
         gen_set_rm(ctx, rm);
         gen_helper_fmsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
                            cpu_fpr[rs2], cpu_fpr[rs3]);
         break;
     case OPC_RISC_FMSUB_D:
+        if (!has_ext(ctx, RVD)) {
+            goto do_illegal;
+        }
         gen_set_rm(ctx, rm);
         gen_helper_fmsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
                            cpu_fpr[rs2], cpu_fpr[rs3]);
         break;
+    do_illegal:
     default:
         gen_exception_illegal(ctx);
         break;
@@ -905,15 +1003,22 @@ static void gen_fp_fnmsub(DisasContext *ctx, uint32_t opc, int rd,
 {
     switch (opc) {
     case OPC_RISC_FNMSUB_S:
+        if (!has_ext(ctx, RVF)) {
+            goto do_illegal;
+        }
         gen_set_rm(ctx, rm);
         gen_helper_fnmsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
                             cpu_fpr[rs2], cpu_fpr[rs3]);
         break;
     case OPC_RISC_FNMSUB_D:
+        if (!has_ext(ctx, RVD)) {
+            goto do_illegal;
+        }
         gen_set_rm(ctx, rm);
         gen_helper_fnmsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
                             cpu_fpr[rs2], cpu_fpr[rs3]);
         break;
+    do_illegal:
     default:
         gen_exception_illegal(ctx);
         break;
@@ -925,15 +1030,22 @@ static void gen_fp_fnmadd(DisasContext *ctx, uint32_t opc, int rd,
 {
     switch (opc) {
     case OPC_RISC_FNMADD_S:
+        if (!has_ext(ctx, RVF)) {
+            goto do_illegal;
+        }
         gen_set_rm(ctx, rm);
         gen_helper_fnmadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
                             cpu_fpr[rs2], cpu_fpr[rs3]);
         break;
     case OPC_RISC_FNMADD_D:
+        if (!has_ext(ctx, RVD)) {
+            goto do_illegal;
+        }
         gen_set_rm(ctx, rm);
         gen_helper_fnmadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
                             cpu_fpr[rs2], cpu_fpr[rs3]);
         break;
+    do_illegal:
     default:
         gen_exception_illegal(ctx);
         break;
@@ -944,37 +1056,59 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
                          int rs1, int rs2, int rm)
 {
     TCGv t0 = NULL;
+    bool fp_output = true;
 
-    if (!(ctx->flags & TB_FLAGS_FP_ENABLE)) {
+    if (ctx->mstatus_fs == 0) {
         goto do_illegal;
     }
 
     switch (opc) {
     case OPC_RISC_FADD_S:
+        if (!has_ext(ctx, RVF)) {
+            goto do_illegal;
+        }
         gen_set_rm(ctx, rm);
         gen_helper_fadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
         break;
     case OPC_RISC_FSUB_S:
+        if (!has_ext(ctx, RVF)) {
+            goto do_illegal;
+        }
         gen_set_rm(ctx, rm);
         gen_helper_fsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
         break;
     case OPC_RISC_FMUL_S:
+        if (!has_ext(ctx, RVF)) {
+            goto do_illegal;
+        }
         gen_set_rm(ctx, rm);
         gen_helper_fmul_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
         break;
     case OPC_RISC_FDIV_S:
+        if (!has_ext(ctx, RVF)) {
+            goto do_illegal;
+        }
         gen_set_rm(ctx, rm);
         gen_helper_fdiv_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
         break;
     case OPC_RISC_FSQRT_S:
+        if (!has_ext(ctx, RVF)) {
+            goto do_illegal;
+        }
         gen_set_rm(ctx, rm);
         gen_helper_fsqrt_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]);
         break;
     case OPC_RISC_FSGNJ_S:
+        if (!has_ext(ctx, RVF)) {
+            goto do_illegal;
+        }
         gen_fsgnj(ctx, rd, rs1, rs2, rm, INT32_MIN);
         break;
 
     case OPC_RISC_FMIN_S:
+        if (!has_ext(ctx, RVF)) {
+            goto do_illegal;
+        }
         /* also handles: OPC_RISC_FMAX_S */
         switch (rm) {
         case 0x0:
@@ -990,6 +1124,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
 
     case OPC_RISC_FEQ_S:
         /* also handles: OPC_RISC_FLT_S, OPC_RISC_FLE_S */
+        if (!has_ext(ctx, RVF)) {
+            goto do_illegal;
+        }
         t0 = tcg_temp_new();
         switch (rm) {
         case 0x0:
@@ -1006,10 +1143,14 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
         }
         gen_set_gpr(rd, t0);
         tcg_temp_free(t0);
+        fp_output = false;
         break;
 
     case OPC_RISC_FCVT_W_S:
         /* also OPC_RISC_FCVT_WU_S, OPC_RISC_FCVT_L_S, OPC_RISC_FCVT_LU_S */
+        if (!has_ext(ctx, RVF)) {
+            goto do_illegal;
+        }
         t0 = tcg_temp_new();
         switch (rs2) {
         case 0: /* FCVT_W_S */
@@ -1035,10 +1176,14 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
         }
         gen_set_gpr(rd, t0);
         tcg_temp_free(t0);
+        fp_output = false;
         break;
 
     case OPC_RISC_FCVT_S_W:
         /* also OPC_RISC_FCVT_S_WU, OPC_RISC_FCVT_S_L, OPC_RISC_FCVT_S_LU */
+        if (!has_ext(ctx, RVF)) {
+            goto do_illegal;
+        }
         t0 = tcg_temp_new();
         gen_get_gpr(t0, rs1);
         switch (rs2) {
@@ -1068,6 +1213,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
 
     case OPC_RISC_FMV_X_S:
         /* also OPC_RISC_FCLASS_S */
+        if (!has_ext(ctx, RVF)) {
+            goto do_illegal;
+        }
         t0 = tcg_temp_new();
         switch (rm) {
         case 0: /* FMV */
@@ -1085,9 +1233,13 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
         }
         gen_set_gpr(rd, t0);
         tcg_temp_free(t0);
+        fp_output = false;
         break;
 
     case OPC_RISC_FMV_S_X:
+        if (!has_ext(ctx, RVF)) {
+            goto do_illegal;
+        }
         t0 = tcg_temp_new();
         gen_get_gpr(t0, rs1);
 #if defined(TARGET_RISCV64)
@@ -1100,22 +1252,37 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
 
     /* double */
     case OPC_RISC_FADD_D:
+        if (!has_ext(ctx, RVD)) {
+            goto do_illegal;
+        }
         gen_set_rm(ctx, rm);
         gen_helper_fadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
         break;
     case OPC_RISC_FSUB_D:
+        if (!has_ext(ctx, RVD)) {
+            goto do_illegal;
+        }
         gen_set_rm(ctx, rm);
         gen_helper_fsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
         break;
     case OPC_RISC_FMUL_D:
+        if (!has_ext(ctx, RVD)) {
+            goto do_illegal;
+        }
         gen_set_rm(ctx, rm);
         gen_helper_fmul_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
         break;
     case OPC_RISC_FDIV_D:
+        if (!has_ext(ctx, RVD)) {
+            goto do_illegal;
+        }
         gen_set_rm(ctx, rm);
         gen_helper_fdiv_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
         break;
     case OPC_RISC_FSQRT_D:
+        if (!has_ext(ctx, RVD)) {
+            goto do_illegal;
+        }
         gen_set_rm(ctx, rm);
         gen_helper_fsqrt_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]);
         break;
@@ -1125,6 +1292,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
 
     case OPC_RISC_FMIN_D:
         /* also OPC_RISC_FMAX_D */
+        if (!has_ext(ctx, RVD)) {
+            goto do_illegal;
+        }
         switch (rm) {
         case 0:
             gen_helper_fmin_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
@@ -1138,6 +1308,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
         break;
 
     case OPC_RISC_FCVT_S_D:
+        if (!has_ext(ctx, RVD)) {
+            goto do_illegal;
+        }
         switch (rs2) {
         case 1:
             gen_set_rm(ctx, rm);
@@ -1149,6 +1322,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
         break;
 
     case OPC_RISC_FCVT_D_S:
+        if (!has_ext(ctx, RVD)) {
+            goto do_illegal;
+        }
         switch (rs2) {
         case 0:
             gen_set_rm(ctx, rm);
@@ -1161,6 +1337,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
 
     case OPC_RISC_FEQ_D:
         /* also OPC_RISC_FLT_D, OPC_RISC_FLE_D */
+        if (!has_ext(ctx, RVD)) {
+            goto do_illegal;
+        }
         t0 = tcg_temp_new();
         switch (rm) {
         case 0:
@@ -1177,10 +1356,14 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
         }
         gen_set_gpr(rd, t0);
         tcg_temp_free(t0);
+        fp_output = false;
         break;
 
     case OPC_RISC_FCVT_W_D:
         /* also OPC_RISC_FCVT_WU_D, OPC_RISC_FCVT_L_D, OPC_RISC_FCVT_LU_D */
+        if (!has_ext(ctx, RVD)) {
+            goto do_illegal;
+        }
         t0 = tcg_temp_new();
         switch (rs2) {
         case 0:
@@ -1206,10 +1389,14 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
         }
         gen_set_gpr(rd, t0);
         tcg_temp_free(t0);
+        fp_output = false;
         break;
 
     case OPC_RISC_FCVT_D_W:
         /* also OPC_RISC_FCVT_D_WU, OPC_RISC_FCVT_D_L, OPC_RISC_FCVT_D_LU */
+        if (!has_ext(ctx, RVD)) {
+            goto do_illegal;
+        }
         t0 = tcg_temp_new();
         gen_get_gpr(t0, rs1);
         switch (rs2) {
@@ -1239,6 +1426,9 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
 
     case OPC_RISC_FMV_X_D:
         /* also OPC_RISC_FCLASS_D */
+        if (!has_ext(ctx, RVD)) {
+            goto do_illegal;
+        }
         switch (rm) {
 #if defined(TARGET_RISCV64)
         case 0: /* FMV */
@@ -1254,10 +1444,14 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
         default:
             goto do_illegal;
         }
+        fp_output = false;
         break;
 
 #if defined(TARGET_RISCV64)
     case OPC_RISC_FMV_D_X:
+        if (!has_ext(ctx, RVD)) {
+            goto do_illegal;
+        }
         t0 = tcg_temp_new();
         gen_get_gpr(t0, rs1);
         tcg_gen_mov_tl(cpu_fpr[rd], t0);
@@ -1271,12 +1465,16 @@ static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
             tcg_temp_free(t0);
         }
         gen_exception_illegal(ctx);
-        break;
+        return;
+    }
+
+    if (fp_output) {
+        mark_fs_dirty(ctx);
     }
 }
 
-static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc,
-                      int rd, int rs1, int csr)
+static void gen_system(DisasContext *ctx, uint32_t opc, int rd, int rs1,
+                       int csr)
 {
     TCGv source1, csr_store, dest, rs1_pass, imm_rs1;
     source1 = tcg_temp_new();
@@ -1292,7 +1490,7 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc,
 #ifndef CONFIG_USER_ONLY
     /* Extract funct7 value and check whether it matches SFENCE.VMA */
     if ((opc == OPC_RISC_ECALL) && ((csr >> 5) == 9)) {
-        if (env->priv_ver == PRIV_VERSION_1_10_0) {
+        if (ctx->priv_ver == PRIV_VERSION_1_10_0) {
             /* sfence.vma */
             /* TODO: handle ASID specific fences */
             gen_helper_tlb_flush(cpu_env);
@@ -1322,7 +1520,7 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc,
             gen_exception_illegal(ctx);
             break;
         case 0x102: /* SRET */
-            if (riscv_has_ext(env, RVS)) {
+            if (has_ext(ctx, RVS)) {
                 gen_helper_sret(cpu_pc, cpu_env, cpu_pc);
                 tcg_gen_exit_tb(NULL, 0); /* no chaining */
                 ctx->base.is_jmp = DISAS_NORETURN;
@@ -1346,7 +1544,7 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc,
             gen_helper_wfi(cpu_env);
             break;
         case 0x104: /* SFENCE.VM */
-            if (env->priv_ver <= PRIV_VERSION_1_09_1) {
+            if (ctx->priv_ver <= PRIV_VERSION_1_09_1) {
                 gen_helper_tlb_flush(cpu_env);
             } else {
                 gen_exception_illegal(ctx);
@@ -1467,7 +1665,7 @@ static void decode_RV32_64C0(DisasContext *ctx)
     }
 }
 
-static void decode_RV32_64C1(CPURISCVState *env, DisasContext *ctx)
+static void decode_RV32_64C1(DisasContext *ctx)
 {
     uint8_t funct3 = extract32(ctx->opcode, 13, 3);
     uint8_t rd_rs1 = GET_C_RS1(ctx->opcode);
@@ -1487,7 +1685,7 @@ static void decode_RV32_64C1(CPURISCVState *env, DisasContext *ctx)
                       GET_C_IMM(ctx->opcode));
 #else
         /* C.JAL(RV32) -> jal x1, offset[11:1] */
-        gen_jal(env, ctx, 1, GET_C_J_IMM(ctx->opcode));
+        gen_jal(ctx, 1, GET_C_J_IMM(ctx->opcode));
 #endif
         break;
     case 2:
@@ -1566,22 +1764,22 @@ static void decode_RV32_64C1(CPURISCVState *env, DisasContext *ctx)
         break;
     case 5:
         /* C.J -> jal x0, offset[11:1]*/
-        gen_jal(env, ctx, 0, GET_C_J_IMM(ctx->opcode));
+        gen_jal(ctx, 0, GET_C_J_IMM(ctx->opcode));
         break;
     case 6:
         /* C.BEQZ -> beq rs1', x0, offset[8:1]*/
         rs1s = GET_C_RS1S(ctx->opcode);
-        gen_branch(env, ctx, OPC_RISC_BEQ, rs1s, 0, GET_C_B_IMM(ctx->opcode));
+        gen_branch(ctx, OPC_RISC_BEQ, rs1s, 0, GET_C_B_IMM(ctx->opcode));
         break;
     case 7:
         /* C.BNEZ -> bne rs1', x0, offset[8:1]*/
         rs1s = GET_C_RS1S(ctx->opcode);
-        gen_branch(env, ctx, OPC_RISC_BNE, rs1s, 0, GET_C_B_IMM(ctx->opcode));
+        gen_branch(ctx, OPC_RISC_BNE, rs1s, 0, GET_C_B_IMM(ctx->opcode));
         break;
     }
 }
 
-static void decode_RV32_64C2(CPURISCVState *env, DisasContext *ctx)
+static void decode_RV32_64C2(DisasContext *ctx)
 {
     uint8_t rd, rs2;
     uint8_t funct3 = extract32(ctx->opcode, 13, 3);
@@ -1615,7 +1813,7 @@ static void decode_RV32_64C2(CPURISCVState *env, DisasContext *ctx)
         if (extract32(ctx->opcode, 12, 1) == 0) {
             if (rs2 == 0) {
                 /* C.JR -> jalr x0, rs1, 0*/
-                gen_jalr(env, ctx, OPC_RISC_JALR, 0, rd, 0);
+                gen_jalr(ctx, OPC_RISC_JALR, 0, rd, 0);
             } else {
                 /* C.MV -> add rd, x0, rs2 */
                 gen_arith(ctx, OPC_RISC_ADD, rd, 0, rs2);
@@ -1623,11 +1821,11 @@ static void decode_RV32_64C2(CPURISCVState *env, DisasContext *ctx)
         } else {
             if (rd == 0) {
                 /* C.EBREAK -> ebreak*/
-                gen_system(env, ctx, OPC_RISC_ECALL, 0, 0, 0x1);
+                gen_system(ctx, OPC_RISC_ECALL, 0, 0, 0x1);
             } else {
                 if (rs2 == 0) {
                     /* C.JALR -> jalr x1, rs1, 0*/
-                    gen_jalr(env, ctx, OPC_RISC_JALR, 1, rd, 0);
+                    gen_jalr(ctx, OPC_RISC_JALR, 1, rd, 0);
                 } else {
                     /* C.ADD -> add rd, rd, rs2 */
                     gen_arith(ctx, OPC_RISC_ADD, rd, rd, rs2);
@@ -1659,7 +1857,7 @@ static void decode_RV32_64C2(CPURISCVState *env, DisasContext *ctx)
     }
 }
 
-static void decode_RV32_64C(CPURISCVState *env, DisasContext *ctx)
+static void decode_RV32_64C(DisasContext *ctx)
 {
     uint8_t op = extract32(ctx->opcode, 0, 2);
 
@@ -1668,15 +1866,15 @@ static void decode_RV32_64C(CPURISCVState *env, DisasContext *ctx)
         decode_RV32_64C0(ctx);
         break;
     case 1:
-        decode_RV32_64C1(env, ctx);
+        decode_RV32_64C1(ctx);
         break;
     case 2:
-        decode_RV32_64C2(env, ctx);
+        decode_RV32_64C2(ctx);
         break;
     }
 }
 
-static void decode_RV32_64G(CPURISCVState *env, DisasContext *ctx)
+static void decode_RV32_64G(DisasContext *ctx)
 {
     int rs1;
     int rs2;
@@ -1711,13 +1909,13 @@ static void decode_RV32_64G(CPURISCVState *env, DisasContext *ctx)
         break;
     case OPC_RISC_JAL:
         imm = GET_JAL_IMM(ctx->opcode);
-        gen_jal(env, ctx, rd, imm);
+        gen_jal(ctx, rd, imm);
         break;
     case OPC_RISC_JALR:
-        gen_jalr(env, ctx, MASK_OP_JALR(ctx->opcode), rd, rs1, imm);
+        gen_jalr(ctx, MASK_OP_JALR(ctx->opcode), rd, rs1, imm);
         break;
     case OPC_RISC_BRANCH:
-        gen_branch(env, ctx, MASK_OP_BRANCH(ctx->opcode), rs1, rs2,
+        gen_branch(ctx, MASK_OP_BRANCH(ctx->opcode), rs1, rs2,
                    GET_B_IMM(ctx->opcode));
         break;
     case OPC_RISC_LOAD:
@@ -1753,6 +1951,9 @@ static void decode_RV32_64G(CPURISCVState *env, DisasContext *ctx)
                      GET_STORE_IMM(ctx->opcode));
         break;
     case OPC_RISC_ATOMIC:
+        if (!has_ext(ctx, RVA)) {
+            goto do_illegal;
+        }
         gen_atomic(ctx, MASK_OP_ATOMIC(ctx->opcode), rd, rs1, rs2);
         break;
     case OPC_RISC_FMADD:
@@ -1788,38 +1989,42 @@ static void decode_RV32_64G(CPURISCVState *env, DisasContext *ctx)
         }
         break;
     case OPC_RISC_SYSTEM:
-        gen_system(env, ctx, MASK_OP_SYSTEM(ctx->opcode), rd, rs1,
+        gen_system(ctx, MASK_OP_SYSTEM(ctx->opcode), rd, rs1,
                    (ctx->opcode & 0xFFF00000) >> 20);
         break;
+    do_illegal:
     default:
         gen_exception_illegal(ctx);
         break;
     }
 }
 
-static void decode_opc(CPURISCVState *env, DisasContext *ctx)
+static void decode_opc(DisasContext *ctx)
 {
     /* check for compressed insn */
     if (extract32(ctx->opcode, 0, 2) != 3) {
-        if (!riscv_has_ext(env, RVC)) {
+        if (!has_ext(ctx, RVC)) {
             gen_exception_illegal(ctx);
         } else {
             ctx->pc_succ_insn = ctx->base.pc_next + 2;
-            decode_RV32_64C(env, ctx);
+            decode_RV32_64C(ctx);
         }
     } else {
         ctx->pc_succ_insn = ctx->base.pc_next + 4;
-        decode_RV32_64G(env, ctx);
+        decode_RV32_64G(ctx);
     }
 }
 
 static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
 {
     DisasContext *ctx = container_of(dcbase, DisasContext, base);
+    CPURISCVState *env = cs->env_ptr;
 
     ctx->pc_succ_insn = ctx->base.pc_first;
-    ctx->flags = ctx->base.tb->flags;
     ctx->mem_idx = ctx->base.tb->flags & TB_FLAGS_MMU_MASK;
+    ctx->mstatus_fs = ctx->base.tb->flags & TB_FLAGS_MSTATUS_FS;
+    ctx->priv_ver = env->priv_ver;
+    ctx->misa = env->misa;
     ctx->frm = -1;  /* unknown rounding mode */
 }
 
@@ -1850,14 +2055,13 @@ static bool riscv_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu,
     return true;
 }
 
-
 static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
 {
     DisasContext *ctx = container_of(dcbase, DisasContext, base);
     CPURISCVState *env = cpu->env_ptr;
 
     ctx->opcode = cpu_ldl_code(env, ctx->base.pc_next);
-    decode_opc(env, ctx);
+    decode_opc(ctx);
     ctx->base.pc_next = ctx->pc_succ_insn;
 
     if (ctx->base.is_jmp == DISAS_NEXT) {