summary refs log tree commit diff stats
path: root/target/sh4
diff options
context:
space:
mode:
Diffstat (limited to 'target/sh4')
-rw-r--r--target/sh4/Makefile.objs3
-rw-r--r--target/sh4/README.sh4150
-rw-r--r--target/sh4/cpu-qom.h65
-rw-r--r--target/sh4/cpu.c332
-rw-r--r--target/sh4/cpu.h391
-rw-r--r--target/sh4/gdbstub.c147
-rw-r--r--target/sh4/helper.c872
-rw-r--r--target/sh4/helper.h45
-rw-r--r--target/sh4/monitor.c53
-rw-r--r--target/sh4/op_helper.c498
-rw-r--r--target/sh4/translate.c1944
11 files changed, 4500 insertions, 0 deletions
diff --git a/target/sh4/Makefile.objs b/target/sh4/Makefile.objs
new file mode 100644
index 0000000000..2c25d96e65
--- /dev/null
+++ b/target/sh4/Makefile.objs
@@ -0,0 +1,3 @@
+obj-y += translate.o op_helper.o helper.o cpu.o
+obj-$(CONFIG_SOFTMMU) += monitor.o
+obj-y += gdbstub.o
diff --git a/target/sh4/README.sh4 b/target/sh4/README.sh4
new file mode 100644
index 0000000000..a192ca7540
--- /dev/null
+++ b/target/sh4/README.sh4
@@ -0,0 +1,150 @@
+qemu target:   sh4
+author:        Samuel Tardieu <sam@rfc1149.net>
+last modified: Tue Dec  6 07:22:44 CET 2005
+
+The sh4 target is not ready at all yet for integration in qemu. This
+file describes the current state of implementation.
+
+Most places requiring attention and/or modification can be detected by
+looking for "XXXXX" or "abort()".
+
+The sh4 core is located in target/sh4/*, while the 7750 peripheral
+features (IO ports for example) are located in hw/sh7750.[ch]. The
+main board description is in hw/shix.c, and the NAND flash in
+hw/tc58128.[ch].
+
+All the shortcomings indicated here will eventually be resolved. This
+is a work in progress. Features are added in a semi-random order: if a
+point is blocking to progress on booting the Linux kernel for the shix
+board, it is addressed first; if feedback is necessary and no progress
+can be made on blocking points until it is received, a random feature
+is worked on.
+
+Goals
+-----
+
+The primary model being worked on is the soft MMU target to be able to
+emulate the Shix 2.0 board by Alexis Polti, described at
+https://web.archive.org/web/20070917001736/http://perso.enst.fr/~polti/realisations/shix20/
+
+Ultimately, qemu will be coupled with a system C or a verilog
+simulator to simulate the whole board functionalities.
+
+A sh4 user-mode has also somewhat started but will be worked on
+afterwards. The goal is to automate tests for GNAT (GNU Ada) compiler
+that I ported recently to the sh4-linux target.
+
+Registers
+---------
+
+16 general purpose registers are available at any time. The first 8
+registers are banked and the non-directly visible ones can be accessed
+by privileged instructions. In qemu, we define 24 general purpose
+registers and the code generation use either [0-7]+[8-15] or
+[16-23]+[8-15] depending on the MD and RB flags in the sr
+configuration register.
+
+Instructions
+------------
+
+Most sh4 instructions have been implemented. The missing ones at this
+time are:
+  - FPU related instructions
+  - LDTLB to load a new MMU entry
+  - SLEEP to put the processor in sleep mode
+
+Most instructions could be optimized a lot. This will be worked on
+after the current model is fully functional unless debugging
+convenience requires that it is done early.
+
+Many instructions did not have a chance to be tested yet. The plan is
+to implement unit and regression testing of those in the future.
+
+MMU
+---
+
+The MMU is implemented in the sh4 core. MMU management has not been
+tested at all yet. In the sh7750, it can be manipulated through memory
+mapped registers and this part has not yet been implemented.
+
+Exceptions
+----------
+
+Exceptions are implemented as described in the sh4 reference manual
+but have not been tested yet. They do not use qemu EXCP_ features
+yet.
+
+IRQ
+---
+
+IRQ are not implemented yet.
+
+Peripheral features
+-------------------
+
+  + Serial ports
+
+Configuration and use of the first serial port (SCI) without
+interrupts is supported. Input has not yet been tested.
+
+Configuration of the second serial port (SCIF) is supported. FIFO
+handling infrastructure has been started but is not completed yet.
+
+  + GPIO ports
+
+GPIO ports have been implemented. A registration function allows
+external modules to register interest in some port changes (see
+hw/tc58128.[ch] for an example) and will be called back. Interrupt
+generation is not yet supported but some infrastructure is in place
+for this purpose. Note that in the current model a peripheral module
+cannot directly simulate a H->L->H input port transition and have an
+interrupt generated on the low level.
+
+  + TC58128 NAND flash
+
+TC58128 NAND flash is partially implemented through GPIO ports. It
+supports reading from flash.
+
+GDB
+---
+
+GDB remote target support has been implemented and lightly tested.
+
+Files
+-----
+
+File names are hardcoded at this time. The bootloader must be stored in
+shix_bios.bin in the current directory. The initial Linux image must
+be stored in shix_linux_nand.bin in the current directory in NAND
+format. Test files can be obtained from
+http://perso.enst.fr/~polti/robot/ as well as the various datasheets I
+use.
+
+qemu disk parameter on the command line is unused. You can supply any
+existing image and it will be ignored. As the goal is to simulate an
+embedded target, it is not clear how this parameter will be handled in
+the future.
+
+To build an ELF kernel image from the NAND image, 16 bytes have to be
+stripped off the end of every 528 bytes, keeping only 512 of them. The
+following Python code snippet does it:
+
+#! /usr/bin/python
+
+def denand (infd, outfd):
+    while True:
+        d = infd.read (528)
+        if not d: return
+        outfd.write (d[:512])
+
+if __name__ == '__main__':
+    import sys
+    denand (open (sys.argv[1], 'rb'),
+            open (sys.argv[2], 'wb'))
+
+Style isssues
+-------------
+
+There is currently a mix between my style (space before opening
+parenthesis) and qemu style. This will be resolved before final
+integration is proposed.
diff --git a/target/sh4/cpu-qom.h b/target/sh4/cpu-qom.h
new file mode 100644
index 0000000000..01abb206e4
--- /dev/null
+++ b/target/sh4/cpu-qom.h
@@ -0,0 +1,65 @@
+/*
+ * QEMU SuperH CPU
+ *
+ * Copyright (c) 2012 SUSE LINUX Products GmbH
+ *
+ * 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.1 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/lgpl-2.1.html>
+ */
+#ifndef QEMU_SUPERH_CPU_QOM_H
+#define QEMU_SUPERH_CPU_QOM_H
+
+#include "qom/cpu.h"
+
+#define TYPE_SUPERH_CPU "superh-cpu"
+
+#define TYPE_SH7750R_CPU "sh7750r-" TYPE_SUPERH_CPU
+#define TYPE_SH7751R_CPU "sh7751r-" TYPE_SUPERH_CPU
+#define TYPE_SH7785_CPU "sh7785-" TYPE_SUPERH_CPU
+
+#define SUPERH_CPU_CLASS(klass) \
+    OBJECT_CLASS_CHECK(SuperHCPUClass, (klass), TYPE_SUPERH_CPU)
+#define SUPERH_CPU(obj) \
+    OBJECT_CHECK(SuperHCPU, (obj), TYPE_SUPERH_CPU)
+#define SUPERH_CPU_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(SuperHCPUClass, (obj), TYPE_SUPERH_CPU)
+
+/**
+ * SuperHCPUClass:
+ * @parent_realize: The parent class' realize handler.
+ * @parent_reset: The parent class' reset handler.
+ * @name: The name.
+ * @pvr: Processor Version Register
+ * @prr: Processor Revision Register
+ * @cvr: Cache Version Register
+ *
+ * A SuperH CPU model.
+ */
+typedef struct SuperHCPUClass {
+    /*< private >*/
+    CPUClass parent_class;
+    /*< public >*/
+
+    DeviceRealize parent_realize;
+    void (*parent_reset)(CPUState *cpu);
+
+    const char *name;
+    uint32_t pvr;
+    uint32_t prr;
+    uint32_t cvr;
+} SuperHCPUClass;
+
+typedef struct SuperHCPU SuperHCPU;
+
+#endif
diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c
new file mode 100644
index 0000000000..a38f6a6ded
--- /dev/null
+++ b/target/sh4/cpu.c
@@ -0,0 +1,332 @@
+/*
+ * QEMU SuperH CPU
+ *
+ * Copyright (c) 2005 Samuel Tardieu
+ * Copyright (c) 2012 SUSE LINUX Products GmbH
+ *
+ * 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.1 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/lgpl-2.1.html>
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "cpu.h"
+#include "qemu-common.h"
+#include "migration/vmstate.h"
+#include "exec/exec-all.h"
+
+
+static void superh_cpu_set_pc(CPUState *cs, vaddr value)
+{
+    SuperHCPU *cpu = SUPERH_CPU(cs);
+
+    cpu->env.pc = value;
+}
+
+static void superh_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb)
+{
+    SuperHCPU *cpu = SUPERH_CPU(cs);
+
+    cpu->env.pc = tb->pc;
+    cpu->env.flags = tb->flags;
+}
+
+static bool superh_cpu_has_work(CPUState *cs)
+{
+    return cs->interrupt_request & CPU_INTERRUPT_HARD;
+}
+
+/* CPUClass::reset() */
+static void superh_cpu_reset(CPUState *s)
+{
+    SuperHCPU *cpu = SUPERH_CPU(s);
+    SuperHCPUClass *scc = SUPERH_CPU_GET_CLASS(cpu);
+    CPUSH4State *env = &cpu->env;
+
+    scc->parent_reset(s);
+
+    memset(env, 0, offsetof(CPUSH4State, id));
+    tlb_flush(s, 1);
+
+    env->pc = 0xA0000000;
+#if defined(CONFIG_USER_ONLY)
+    env->fpscr = FPSCR_PR; /* value for userspace according to the kernel */
+    set_float_rounding_mode(float_round_nearest_even, &env->fp_status); /* ?! */
+#else
+    env->sr = (1u << SR_MD) | (1u << SR_RB) | (1u << SR_BL) |
+              (1u << SR_I3) | (1u << SR_I2) | (1u << SR_I1) | (1u << SR_I0);
+    env->fpscr = FPSCR_DN | FPSCR_RM_ZERO; /* CPU reset value according to SH4 manual */
+    set_float_rounding_mode(float_round_to_zero, &env->fp_status);
+    set_flush_to_zero(1, &env->fp_status);
+#endif
+    set_default_nan_mode(1, &env->fp_status);
+    set_snan_bit_is_one(1, &env->fp_status);
+}
+
+static void superh_cpu_disas_set_info(CPUState *cpu, disassemble_info *info)
+{
+    info->mach = bfd_mach_sh4;
+    info->print_insn = print_insn_sh;
+}
+
+typedef struct SuperHCPUListState {
+    fprintf_function cpu_fprintf;
+    FILE *file;
+} SuperHCPUListState;
+
+/* Sort alphabetically by type name. */
+static gint superh_cpu_list_compare(gconstpointer a, gconstpointer b)
+{
+    ObjectClass *class_a = (ObjectClass *)a;
+    ObjectClass *class_b = (ObjectClass *)b;
+    const char *name_a, *name_b;
+
+    name_a = object_class_get_name(class_a);
+    name_b = object_class_get_name(class_b);
+    return strcmp(name_a, name_b);
+}
+
+static void superh_cpu_list_entry(gpointer data, gpointer user_data)
+{
+    ObjectClass *oc = data;
+    SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc);
+    SuperHCPUListState *s = user_data;
+
+    (*s->cpu_fprintf)(s->file, "%s\n",
+                      scc->name);
+}
+
+void sh4_cpu_list(FILE *f, fprintf_function cpu_fprintf)
+{
+    SuperHCPUListState s = {
+        .cpu_fprintf = cpu_fprintf,
+        .file = f,
+    };
+    GSList *list;
+
+    list = object_class_get_list(TYPE_SUPERH_CPU, false);
+    list = g_slist_sort(list, superh_cpu_list_compare);
+    g_slist_foreach(list, superh_cpu_list_entry, &s);
+    g_slist_free(list);
+}
+
+static gint superh_cpu_name_compare(gconstpointer a, gconstpointer b)
+{
+    const SuperHCPUClass *scc = SUPERH_CPU_CLASS(a);
+    const char *name = b;
+
+    return strcasecmp(scc->name, name);
+}
+
+static ObjectClass *superh_cpu_class_by_name(const char *cpu_model)
+{
+    ObjectClass *oc;
+    GSList *list, *item;
+
+    if (cpu_model == NULL) {
+        return NULL;
+    }
+    if (strcasecmp(cpu_model, "any") == 0) {
+        return object_class_by_name(TYPE_SH7750R_CPU);
+    }
+
+    oc = object_class_by_name(cpu_model);
+    if (oc != NULL && object_class_dynamic_cast(oc, TYPE_SUPERH_CPU) != NULL
+        && !object_class_is_abstract(oc)) {
+        return oc;
+    }
+
+    oc = NULL;
+    list = object_class_get_list(TYPE_SUPERH_CPU, false);
+    item = g_slist_find_custom(list, cpu_model, superh_cpu_name_compare);
+    if (item != NULL) {
+        oc = item->data;
+    }
+    g_slist_free(list);
+    return oc;
+}
+
+SuperHCPU *cpu_sh4_init(const char *cpu_model)
+{
+    return SUPERH_CPU(cpu_generic_init(TYPE_SUPERH_CPU, cpu_model));
+}
+
+static void sh7750r_cpu_initfn(Object *obj)
+{
+    SuperHCPU *cpu = SUPERH_CPU(obj);
+    CPUSH4State *env = &cpu->env;
+
+    env->id = SH_CPU_SH7750R;
+    env->features = SH_FEATURE_BCR3_AND_BCR4;
+}
+
+static void sh7750r_class_init(ObjectClass *oc, void *data)
+{
+    SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc);
+
+    scc->name = "SH7750R";
+    scc->pvr = 0x00050000;
+    scc->prr = 0x00000100;
+    scc->cvr = 0x00110000;
+}
+
+static const TypeInfo sh7750r_type_info = {
+    .name = TYPE_SH7750R_CPU,
+    .parent = TYPE_SUPERH_CPU,
+    .class_init = sh7750r_class_init,
+    .instance_init = sh7750r_cpu_initfn,
+};
+
+static void sh7751r_cpu_initfn(Object *obj)
+{
+    SuperHCPU *cpu = SUPERH_CPU(obj);
+    CPUSH4State *env = &cpu->env;
+
+    env->id = SH_CPU_SH7751R;
+    env->features = SH_FEATURE_BCR3_AND_BCR4;
+}
+
+static void sh7751r_class_init(ObjectClass *oc, void *data)
+{
+    SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc);
+
+    scc->name = "SH7751R";
+    scc->pvr = 0x04050005;
+    scc->prr = 0x00000113;
+    scc->cvr = 0x00110000; /* Neutered caches, should be 0x20480000 */
+}
+
+static const TypeInfo sh7751r_type_info = {
+    .name = TYPE_SH7751R_CPU,
+    .parent = TYPE_SUPERH_CPU,
+    .class_init = sh7751r_class_init,
+    .instance_init = sh7751r_cpu_initfn,
+};
+
+static void sh7785_cpu_initfn(Object *obj)
+{
+    SuperHCPU *cpu = SUPERH_CPU(obj);
+    CPUSH4State *env = &cpu->env;
+
+    env->id = SH_CPU_SH7785;
+    env->features = SH_FEATURE_SH4A;
+}
+
+static void sh7785_class_init(ObjectClass *oc, void *data)
+{
+    SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc);
+
+    scc->name = "SH7785";
+    scc->pvr = 0x10300700;
+    scc->prr = 0x00000200;
+    scc->cvr = 0x71440211;
+}
+
+static const TypeInfo sh7785_type_info = {
+    .name = TYPE_SH7785_CPU,
+    .parent = TYPE_SUPERH_CPU,
+    .class_init = sh7785_class_init,
+    .instance_init = sh7785_cpu_initfn,
+};
+
+static void superh_cpu_realizefn(DeviceState *dev, Error **errp)
+{
+    CPUState *cs = CPU(dev);
+    SuperHCPUClass *scc = SUPERH_CPU_GET_CLASS(dev);
+    Error *local_err = NULL;
+
+    cpu_exec_realizefn(cs, &local_err);
+    if (local_err != NULL) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    cpu_reset(cs);
+    qemu_init_vcpu(cs);
+
+    scc->parent_realize(dev, errp);
+}
+
+static void superh_cpu_initfn(Object *obj)
+{
+    CPUState *cs = CPU(obj);
+    SuperHCPU *cpu = SUPERH_CPU(obj);
+    CPUSH4State *env = &cpu->env;
+
+    cs->env_ptr = env;
+
+    env->movcal_backup_tail = &(env->movcal_backup);
+
+    if (tcg_enabled()) {
+        sh4_translate_init();
+    }
+}
+
+static const VMStateDescription vmstate_sh_cpu = {
+    .name = "cpu",
+    .unmigratable = 1,
+};
+
+static void superh_cpu_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    CPUClass *cc = CPU_CLASS(oc);
+    SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc);
+
+    scc->parent_realize = dc->realize;
+    dc->realize = superh_cpu_realizefn;
+
+    scc->parent_reset = cc->reset;
+    cc->reset = superh_cpu_reset;
+
+    cc->class_by_name = superh_cpu_class_by_name;
+    cc->has_work = superh_cpu_has_work;
+    cc->do_interrupt = superh_cpu_do_interrupt;
+    cc->cpu_exec_interrupt = superh_cpu_exec_interrupt;
+    cc->dump_state = superh_cpu_dump_state;
+    cc->set_pc = superh_cpu_set_pc;
+    cc->synchronize_from_tb = superh_cpu_synchronize_from_tb;
+    cc->gdb_read_register = superh_cpu_gdb_read_register;
+    cc->gdb_write_register = superh_cpu_gdb_write_register;
+#ifdef CONFIG_USER_ONLY
+    cc->handle_mmu_fault = superh_cpu_handle_mmu_fault;
+#else
+    cc->get_phys_page_debug = superh_cpu_get_phys_page_debug;
+#endif
+    cc->disas_set_info = superh_cpu_disas_set_info;
+
+    cc->gdb_num_core_regs = 59;
+
+    dc->vmsd = &vmstate_sh_cpu;
+}
+
+static const TypeInfo superh_cpu_type_info = {
+    .name = TYPE_SUPERH_CPU,
+    .parent = TYPE_CPU,
+    .instance_size = sizeof(SuperHCPU),
+    .instance_init = superh_cpu_initfn,
+    .abstract = true,
+    .class_size = sizeof(SuperHCPUClass),
+    .class_init = superh_cpu_class_init,
+};
+
+static void superh_cpu_register_types(void)
+{
+    type_register_static(&superh_cpu_type_info);
+    type_register_static(&sh7750r_type_info);
+    type_register_static(&sh7751r_type_info);
+    type_register_static(&sh7785_type_info);
+}
+
+type_init(superh_cpu_register_types)
diff --git a/target/sh4/cpu.h b/target/sh4/cpu.h
new file mode 100644
index 0000000000..478ab55868
--- /dev/null
+++ b/target/sh4/cpu.h
@@ -0,0 +1,391 @@
+/*
+ *  SH4 emulation
+ *
+ *  Copyright (c) 2005 Samuel Tardieu
+ *
+ * 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/>.
+ */
+
+#ifndef SH4_CPU_H
+#define SH4_CPU_H
+
+#include "qemu-common.h"
+#include "cpu-qom.h"
+
+#define TARGET_LONG_BITS 32
+
+/* CPU Subtypes */
+#define SH_CPU_SH7750  (1 << 0)
+#define SH_CPU_SH7750S (1 << 1)
+#define SH_CPU_SH7750R (1 << 2)
+#define SH_CPU_SH7751  (1 << 3)
+#define SH_CPU_SH7751R (1 << 4)
+#define SH_CPU_SH7785  (1 << 5)
+#define SH_CPU_SH7750_ALL (SH_CPU_SH7750 | SH_CPU_SH7750S | SH_CPU_SH7750R)
+#define SH_CPU_SH7751_ALL (SH_CPU_SH7751 | SH_CPU_SH7751R)
+
+#define CPUArchState struct CPUSH4State
+
+#include "exec/cpu-defs.h"
+
+#include "fpu/softfloat.h"
+
+#define TARGET_PAGE_BITS 12	/* 4k XXXXX */
+
+#define TARGET_PHYS_ADDR_SPACE_BITS 32
+#define TARGET_VIRT_ADDR_SPACE_BITS 32
+
+#define SR_MD 30
+#define SR_RB 29
+#define SR_BL 28
+#define SR_FD 15
+#define SR_M  9
+#define SR_Q  8
+#define SR_I3 7
+#define SR_I2 6
+#define SR_I1 5
+#define SR_I0 4
+#define SR_S  1
+#define SR_T  0
+
+#define FPSCR_MASK             (0x003fffff)
+#define FPSCR_FR               (1 << 21)
+#define FPSCR_SZ               (1 << 20)
+#define FPSCR_PR               (1 << 19)
+#define FPSCR_DN               (1 << 18)
+#define FPSCR_CAUSE_MASK       (0x3f << 12)
+#define FPSCR_CAUSE_SHIFT      (12)
+#define FPSCR_CAUSE_E          (1 << 17)
+#define FPSCR_CAUSE_V          (1 << 16)
+#define FPSCR_CAUSE_Z          (1 << 15)
+#define FPSCR_CAUSE_O          (1 << 14)
+#define FPSCR_CAUSE_U          (1 << 13)
+#define FPSCR_CAUSE_I          (1 << 12)
+#define FPSCR_ENABLE_MASK      (0x1f << 7)
+#define FPSCR_ENABLE_SHIFT     (7)
+#define FPSCR_ENABLE_V         (1 << 11)
+#define FPSCR_ENABLE_Z         (1 << 10)
+#define FPSCR_ENABLE_O         (1 << 9)
+#define FPSCR_ENABLE_U         (1 << 8)
+#define FPSCR_ENABLE_I         (1 << 7)
+#define FPSCR_FLAG_MASK        (0x1f << 2)
+#define FPSCR_FLAG_SHIFT       (2)
+#define FPSCR_FLAG_V           (1 << 6)
+#define FPSCR_FLAG_Z           (1 << 5)
+#define FPSCR_FLAG_O           (1 << 4)
+#define FPSCR_FLAG_U           (1 << 3)
+#define FPSCR_FLAG_I           (1 << 2)
+#define FPSCR_RM_MASK          (0x03 << 0)
+#define FPSCR_RM_NEAREST       (0 << 0)
+#define FPSCR_RM_ZERO          (1 << 0)
+
+#define DELAY_SLOT             (1 << 0)
+#define DELAY_SLOT_CONDITIONAL (1 << 1)
+#define DELAY_SLOT_TRUE        (1 << 2)
+#define DELAY_SLOT_CLEARME     (1 << 3)
+/* The dynamic value of the DELAY_SLOT_TRUE flag determines whether the jump
+ * after the delay slot should be taken or not. It is calculated from SR_T.
+ *
+ * It is unclear if it is permitted to modify the SR_T flag in a delay slot.
+ * The use of DELAY_SLOT_TRUE flag makes us accept such SR_T modification.
+ */
+
+typedef struct tlb_t {
+    uint32_t vpn;		/* virtual page number */
+    uint32_t ppn;		/* physical page number */
+    uint32_t size;		/* mapped page size in bytes */
+    uint8_t asid;		/* address space identifier */
+    uint8_t v:1;		/* validity */
+    uint8_t sz:2;		/* page size */
+    uint8_t sh:1;		/* share status */
+    uint8_t c:1;		/* cacheability */
+    uint8_t pr:2;		/* protection key */
+    uint8_t d:1;		/* dirty */
+    uint8_t wt:1;		/* write through */
+    uint8_t sa:3;		/* space attribute (PCMCIA) */
+    uint8_t tc:1;		/* timing control */
+} tlb_t;
+
+#define UTLB_SIZE 64
+#define ITLB_SIZE 4
+
+#define NB_MMU_MODES 2
+#define TARGET_INSN_START_EXTRA_WORDS 1
+
+enum sh_features {
+    SH_FEATURE_SH4A = 1,
+    SH_FEATURE_BCR3_AND_BCR4 = 2,
+};
+
+typedef struct memory_content {
+    uint32_t address;
+    uint32_t value;
+    struct memory_content *next;
+} memory_content;
+
+typedef struct CPUSH4State {
+    uint32_t flags;		/* general execution flags */
+    uint32_t gregs[24];		/* general registers */
+    float32 fregs[32];		/* floating point registers */
+    uint32_t sr;                /* status register (with T split out) */
+    uint32_t sr_m;              /* M bit of status register */
+    uint32_t sr_q;              /* Q bit of status register */
+    uint32_t sr_t;              /* T bit of status register */
+    uint32_t ssr;		/* saved status register */
+    uint32_t spc;		/* saved program counter */
+    uint32_t gbr;		/* global base register */
+    uint32_t vbr;		/* vector base register */
+    uint32_t sgr;		/* saved global register 15 */
+    uint32_t dbr;		/* debug base register */
+    uint32_t pc;		/* program counter */
+    uint32_t delayed_pc;	/* target of delayed jump */
+    uint32_t mach;		/* multiply and accumulate high */
+    uint32_t macl;		/* multiply and accumulate low */
+    uint32_t pr;		/* procedure register */
+    uint32_t fpscr;		/* floating point status/control register */
+    uint32_t fpul;		/* floating point communication register */
+
+    /* float point status register */
+    float_status fp_status;
+
+    /* Those belong to the specific unit (SH7750) but are handled here */
+    uint32_t mmucr;		/* MMU control register */
+    uint32_t pteh;		/* page table entry high register */
+    uint32_t ptel;		/* page table entry low register */
+    uint32_t ptea;		/* page table entry assistance register */
+    uint32_t ttb;		/* tranlation table base register */
+    uint32_t tea;		/* TLB exception address register */
+    uint32_t tra;		/* TRAPA exception register */
+    uint32_t expevt;		/* exception event register */
+    uint32_t intevt;		/* interrupt event register */
+
+    tlb_t itlb[ITLB_SIZE];	/* instruction translation table */
+    tlb_t utlb[UTLB_SIZE];	/* unified translation table */
+
+    uint32_t ldst;
+
+    CPU_COMMON
+
+    /* Fields from here on are preserved over CPU reset. */
+    int id;			/* CPU model */
+
+    /* The features that we should emulate. See sh_features above.  */
+    uint32_t features;
+
+    void *intc_handle;
+    int in_sleep;		/* SR_BL ignored during sleep */
+    memory_content *movcal_backup;
+    memory_content **movcal_backup_tail;
+} CPUSH4State;
+
+/**
+ * SuperHCPU:
+ * @env: #CPUSH4State
+ *
+ * A SuperH CPU.
+ */
+struct SuperHCPU {
+    /*< private >*/
+    CPUState parent_obj;
+    /*< public >*/
+
+    CPUSH4State env;
+};
+
+static inline SuperHCPU *sh_env_get_cpu(CPUSH4State *env)
+{
+    return container_of(env, SuperHCPU, env);
+}
+
+#define ENV_GET_CPU(e) CPU(sh_env_get_cpu(e))
+
+#define ENV_OFFSET offsetof(SuperHCPU, env)
+
+void superh_cpu_do_interrupt(CPUState *cpu);
+bool superh_cpu_exec_interrupt(CPUState *cpu, int int_req);
+void superh_cpu_dump_state(CPUState *cpu, FILE *f,
+                           fprintf_function cpu_fprintf, int flags);
+hwaddr superh_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
+int superh_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg);
+int superh_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
+
+void sh4_translate_init(void);
+SuperHCPU *cpu_sh4_init(const char *cpu_model);
+int cpu_sh4_signal_handler(int host_signum, void *pinfo,
+                           void *puc);
+int superh_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int rw,
+                                int mmu_idx);
+
+void sh4_cpu_list(FILE *f, fprintf_function cpu_fprintf);
+#if !defined(CONFIG_USER_ONLY)
+void cpu_sh4_invalidate_tlb(CPUSH4State *s);
+uint32_t cpu_sh4_read_mmaped_itlb_addr(CPUSH4State *s,
+                                       hwaddr addr);
+void cpu_sh4_write_mmaped_itlb_addr(CPUSH4State *s, hwaddr addr,
+                                    uint32_t mem_value);
+uint32_t cpu_sh4_read_mmaped_itlb_data(CPUSH4State *s,
+                                       hwaddr addr);
+void cpu_sh4_write_mmaped_itlb_data(CPUSH4State *s, hwaddr addr,
+                                    uint32_t mem_value);
+uint32_t cpu_sh4_read_mmaped_utlb_addr(CPUSH4State *s,
+                                       hwaddr addr);
+void cpu_sh4_write_mmaped_utlb_addr(CPUSH4State *s, hwaddr addr,
+                                    uint32_t mem_value);
+uint32_t cpu_sh4_read_mmaped_utlb_data(CPUSH4State *s,
+                                       hwaddr addr);
+void cpu_sh4_write_mmaped_utlb_data(CPUSH4State *s, hwaddr addr,
+                                    uint32_t mem_value);
+#endif
+
+int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr);
+
+void cpu_load_tlb(CPUSH4State * env);
+
+#define cpu_init(cpu_model) CPU(cpu_sh4_init(cpu_model))
+
+#define cpu_signal_handler cpu_sh4_signal_handler
+#define cpu_list sh4_cpu_list
+
+/* MMU modes definitions */
+#define MMU_MODE0_SUFFIX _kernel
+#define MMU_MODE1_SUFFIX _user
+#define MMU_USER_IDX 1
+static inline int cpu_mmu_index (CPUSH4State *env, bool ifetch)
+{
+    return (env->sr & (1u << SR_MD)) == 0 ? 1 : 0;
+}
+
+#include "exec/cpu-all.h"
+
+/* Memory access type */
+enum {
+    /* Privilege */
+    ACCESS_PRIV = 0x01,
+    /* Direction */
+    ACCESS_WRITE = 0x02,
+    /* Type of instruction */
+    ACCESS_CODE = 0x10,
+    ACCESS_INT = 0x20
+};
+
+/* MMU control register */
+#define MMUCR    0x1F000010
+#define MMUCR_AT (1<<0)
+#define MMUCR_TI (1<<2)
+#define MMUCR_SV (1<<8)
+#define MMUCR_URC_BITS (6)
+#define MMUCR_URC_OFFSET (10)
+#define MMUCR_URC_SIZE (1 << MMUCR_URC_BITS)
+#define MMUCR_URC_MASK (((MMUCR_URC_SIZE) - 1) << MMUCR_URC_OFFSET)
+static inline int cpu_mmucr_urc (uint32_t mmucr)
+{
+    return ((mmucr & MMUCR_URC_MASK) >> MMUCR_URC_OFFSET);
+}
+
+/* PTEH : Page Translation Entry High register */
+#define PTEH_ASID_BITS (8)
+#define PTEH_ASID_SIZE (1 << PTEH_ASID_BITS)
+#define PTEH_ASID_MASK (PTEH_ASID_SIZE - 1)
+#define cpu_pteh_asid(pteh) ((pteh) & PTEH_ASID_MASK)
+#define PTEH_VPN_BITS (22)
+#define PTEH_VPN_OFFSET (10)
+#define PTEH_VPN_SIZE (1 << PTEH_VPN_BITS)
+#define PTEH_VPN_MASK (((PTEH_VPN_SIZE) - 1) << PTEH_VPN_OFFSET)
+static inline int cpu_pteh_vpn (uint32_t pteh)
+{
+    return ((pteh & PTEH_VPN_MASK) >> PTEH_VPN_OFFSET);
+}
+
+/* PTEL : Page Translation Entry Low register */
+#define PTEL_V        (1 << 8)
+#define cpu_ptel_v(ptel) (((ptel) & PTEL_V) >> 8)
+#define PTEL_C        (1 << 3)
+#define cpu_ptel_c(ptel) (((ptel) & PTEL_C) >> 3)
+#define PTEL_D        (1 << 2)
+#define cpu_ptel_d(ptel) (((ptel) & PTEL_D) >> 2)
+#define PTEL_SH       (1 << 1)
+#define cpu_ptel_sh(ptel)(((ptel) & PTEL_SH) >> 1)
+#define PTEL_WT       (1 << 0)
+#define cpu_ptel_wt(ptel) ((ptel) & PTEL_WT)
+
+#define PTEL_SZ_HIGH_OFFSET  (7)
+#define PTEL_SZ_HIGH  (1 << PTEL_SZ_HIGH_OFFSET)
+#define PTEL_SZ_LOW_OFFSET   (4)
+#define PTEL_SZ_LOW   (1 << PTEL_SZ_LOW_OFFSET)
+static inline int cpu_ptel_sz (uint32_t ptel)
+{
+    int sz;
+    sz = (ptel & PTEL_SZ_HIGH) >> PTEL_SZ_HIGH_OFFSET;
+    sz <<= 1;
+    sz |= (ptel & PTEL_SZ_LOW) >> PTEL_SZ_LOW_OFFSET;
+    return sz;
+}
+
+#define PTEL_PPN_BITS (19)
+#define PTEL_PPN_OFFSET (10)
+#define PTEL_PPN_SIZE (1 << PTEL_PPN_BITS)
+#define PTEL_PPN_MASK (((PTEL_PPN_SIZE) - 1) << PTEL_PPN_OFFSET)
+static inline int cpu_ptel_ppn (uint32_t ptel)
+{
+    return ((ptel & PTEL_PPN_MASK) >> PTEL_PPN_OFFSET);
+}
+
+#define PTEL_PR_BITS   (2)
+#define PTEL_PR_OFFSET (5)
+#define PTEL_PR_SIZE (1 << PTEL_PR_BITS)
+#define PTEL_PR_MASK (((PTEL_PR_SIZE) - 1) << PTEL_PR_OFFSET)
+static inline int cpu_ptel_pr (uint32_t ptel)
+{
+    return ((ptel & PTEL_PR_MASK) >> PTEL_PR_OFFSET);
+}
+
+/* PTEA : Page Translation Entry Assistance register */
+#define PTEA_SA_BITS (3)
+#define PTEA_SA_SIZE (1 << PTEA_SA_BITS)
+#define PTEA_SA_MASK (PTEA_SA_SIZE - 1)
+#define cpu_ptea_sa(ptea) ((ptea) & PTEA_SA_MASK)
+#define PTEA_TC        (1 << 3)
+#define cpu_ptea_tc(ptea) (((ptea) & PTEA_TC) >> 3)
+
+#define TB_FLAG_PENDING_MOVCA  (1 << 4)
+
+static inline target_ulong cpu_read_sr(CPUSH4State *env)
+{
+    return env->sr | (env->sr_m << SR_M) |
+                     (env->sr_q << SR_Q) |
+                     (env->sr_t << SR_T);
+}
+
+static inline void cpu_write_sr(CPUSH4State *env, target_ulong sr)
+{
+    env->sr_m = (sr >> SR_M) & 1;
+    env->sr_q = (sr >> SR_Q) & 1;
+    env->sr_t = (sr >> SR_T) & 1;
+    env->sr = sr & ~((1u << SR_M) | (1u << SR_Q) | (1u << SR_T));
+}
+
+static inline void cpu_get_tb_cpu_state(CPUSH4State *env, target_ulong *pc,
+                                        target_ulong *cs_base, uint32_t *flags)
+{
+    *pc = env->pc;
+    *cs_base = 0;
+    *flags = (env->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL
+                    | DELAY_SLOT_TRUE | DELAY_SLOT_CLEARME))   /* Bits  0- 3 */
+            | (env->fpscr & (FPSCR_FR | FPSCR_SZ | FPSCR_PR))  /* Bits 19-21 */
+            | (env->sr & ((1u << SR_MD) | (1u << SR_RB)))      /* Bits 29-30 */
+            | (env->sr & (1u << SR_FD))                        /* Bit 15 */
+            | (env->movcal_backup ? TB_FLAG_PENDING_MOVCA : 0); /* Bit 4 */
+}
+
+#endif /* SH4_CPU_H */
diff --git a/target/sh4/gdbstub.c b/target/sh4/gdbstub.c
new file mode 100644
index 0000000000..13bea00d7d
--- /dev/null
+++ b/target/sh4/gdbstub.c
@@ -0,0 +1,147 @@
+/*
+ * SuperH gdb server stub
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2013 SUSE LINUX Products GmbH
+ *
+ * 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/>.
+ */
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
+#include "exec/gdbstub.h"
+
+/* Hint: Use "set architecture sh4" in GDB to see fpu registers */
+/* FIXME: We should use XML for this.  */
+
+int superh_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n)
+{
+    SuperHCPU *cpu = SUPERH_CPU(cs);
+    CPUSH4State *env = &cpu->env;
+
+    switch (n) {
+    case 0 ... 7:
+        if ((env->sr & (1u << SR_MD)) && (env->sr & (1u << SR_RB))) {
+            return gdb_get_regl(mem_buf, env->gregs[n + 16]);
+        } else {
+            return gdb_get_regl(mem_buf, env->gregs[n]);
+        }
+    case 8 ... 15:
+        return gdb_get_regl(mem_buf, env->gregs[n]);
+    case 16:
+        return gdb_get_regl(mem_buf, env->pc);
+    case 17:
+        return gdb_get_regl(mem_buf, env->pr);
+    case 18:
+        return gdb_get_regl(mem_buf, env->gbr);
+    case 19:
+        return gdb_get_regl(mem_buf, env->vbr);
+    case 20:
+        return gdb_get_regl(mem_buf, env->mach);
+    case 21:
+        return gdb_get_regl(mem_buf, env->macl);
+    case 22:
+        return gdb_get_regl(mem_buf, cpu_read_sr(env));
+    case 23:
+        return gdb_get_regl(mem_buf, env->fpul);
+    case 24:
+        return gdb_get_regl(mem_buf, env->fpscr);
+    case 25 ... 40:
+        if (env->fpscr & FPSCR_FR) {
+            stfl_p(mem_buf, env->fregs[n - 9]);
+        } else {
+            stfl_p(mem_buf, env->fregs[n - 25]);
+        }
+        return 4;
+    case 41:
+        return gdb_get_regl(mem_buf, env->ssr);
+    case 42:
+        return gdb_get_regl(mem_buf, env->spc);
+    case 43 ... 50:
+        return gdb_get_regl(mem_buf, env->gregs[n - 43]);
+    case 51 ... 58:
+        return gdb_get_regl(mem_buf, env->gregs[n - (51 - 16)]);
+    }
+
+    return 0;
+}
+
+int superh_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
+{
+    SuperHCPU *cpu = SUPERH_CPU(cs);
+    CPUSH4State *env = &cpu->env;
+
+    switch (n) {
+    case 0 ... 7:
+        if ((env->sr & (1u << SR_MD)) && (env->sr & (1u << SR_RB))) {
+            env->gregs[n + 16] = ldl_p(mem_buf);
+        } else {
+            env->gregs[n] = ldl_p(mem_buf);
+        }
+        break;
+    case 8 ... 15:
+        env->gregs[n] = ldl_p(mem_buf);
+        break;
+    case 16:
+        env->pc = ldl_p(mem_buf);
+        break;
+    case 17:
+        env->pr = ldl_p(mem_buf);
+        break;
+    case 18:
+        env->gbr = ldl_p(mem_buf);
+        break;
+    case 19:
+        env->vbr = ldl_p(mem_buf);
+        break;
+    case 20:
+        env->mach = ldl_p(mem_buf);
+        break;
+    case 21:
+        env->macl = ldl_p(mem_buf);
+        break;
+    case 22:
+        cpu_write_sr(env, ldl_p(mem_buf));
+        break;
+    case 23:
+        env->fpul = ldl_p(mem_buf);
+        break;
+    case 24:
+        env->fpscr = ldl_p(mem_buf);
+        break;
+    case 25 ... 40:
+        if (env->fpscr & FPSCR_FR) {
+            env->fregs[n - 9] = ldfl_p(mem_buf);
+        } else {
+            env->fregs[n - 25] = ldfl_p(mem_buf);
+        }
+        break;
+    case 41:
+        env->ssr = ldl_p(mem_buf);
+        break;
+    case 42:
+        env->spc = ldl_p(mem_buf);
+        break;
+    case 43 ... 50:
+        env->gregs[n - 43] = ldl_p(mem_buf);
+        break;
+    case 51 ... 58:
+        env->gregs[n - (51 - 16)] = ldl_p(mem_buf);
+        break;
+    default:
+        return 0;
+    }
+
+    return 4;
+}
diff --git a/target/sh4/helper.c b/target/sh4/helper.c
new file mode 100644
index 0000000000..a33ac697c5
--- /dev/null
+++ b/target/sh4/helper.c
@@ -0,0 +1,872 @@
+/*
+ *  SH4 emulation
+ *
+ *  Copyright (c) 2005 Samuel Tardieu
+ *
+ * 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/>.
+ */
+#include "qemu/osdep.h"
+
+#include "cpu.h"
+#include "exec/exec-all.h"
+#include "exec/log.h"
+
+#if !defined(CONFIG_USER_ONLY)
+#include "hw/sh4/sh_intc.h"
+#endif
+
+#if defined(CONFIG_USER_ONLY)
+
+void superh_cpu_do_interrupt(CPUState *cs)
+{
+    cs->exception_index = -1;
+}
+
+int superh_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw,
+                                int mmu_idx)
+{
+    SuperHCPU *cpu = SUPERH_CPU(cs);
+    CPUSH4State *env = &cpu->env;
+
+    env->tea = address;
+    cs->exception_index = -1;
+    switch (rw) {
+    case 0:
+        cs->exception_index = 0x0a0;
+        break;
+    case 1:
+        cs->exception_index = 0x0c0;
+        break;
+    case 2:
+        cs->exception_index = 0x0a0;
+        break;
+    }
+    return 1;
+}
+
+int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr)
+{
+    /* For user mode, only U0 area is cacheable. */
+    return !(addr & 0x80000000);
+}
+
+#else /* !CONFIG_USER_ONLY */
+
+#define MMU_OK                   0
+#define MMU_ITLB_MISS            (-1)
+#define MMU_ITLB_MULTIPLE        (-2)
+#define MMU_ITLB_VIOLATION       (-3)
+#define MMU_DTLB_MISS_READ       (-4)
+#define MMU_DTLB_MISS_WRITE      (-5)
+#define MMU_DTLB_INITIAL_WRITE   (-6)
+#define MMU_DTLB_VIOLATION_READ  (-7)
+#define MMU_DTLB_VIOLATION_WRITE (-8)
+#define MMU_DTLB_MULTIPLE        (-9)
+#define MMU_DTLB_MISS            (-10)
+#define MMU_IADDR_ERROR          (-11)
+#define MMU_DADDR_ERROR_READ     (-12)
+#define MMU_DADDR_ERROR_WRITE    (-13)
+
+void superh_cpu_do_interrupt(CPUState *cs)
+{
+    SuperHCPU *cpu = SUPERH_CPU(cs);
+    CPUSH4State *env = &cpu->env;
+    int do_irq = cs->interrupt_request & CPU_INTERRUPT_HARD;
+    int do_exp, irq_vector = cs->exception_index;
+
+    /* prioritize exceptions over interrupts */
+
+    do_exp = cs->exception_index != -1;
+    do_irq = do_irq && (cs->exception_index == -1);
+
+    if (env->sr & (1u << SR_BL)) {
+        if (do_exp && cs->exception_index != 0x1e0) {
+            cs->exception_index = 0x000; /* masked exception -> reset */
+        }
+        if (do_irq && !env->in_sleep) {
+            return; /* masked */
+        }
+    }
+    env->in_sleep = 0;
+
+    if (do_irq) {
+        irq_vector = sh_intc_get_pending_vector(env->intc_handle,
+						(env->sr >> 4) & 0xf);
+        if (irq_vector == -1) {
+            return; /* masked */
+	}
+    }
+
+    if (qemu_loglevel_mask(CPU_LOG_INT)) {
+	const char *expname;
+        switch (cs->exception_index) {
+	case 0x0e0:
+	    expname = "addr_error";
+	    break;
+	case 0x040:
+	    expname = "tlb_miss";
+	    break;
+	case 0x0a0:
+	    expname = "tlb_violation";
+	    break;
+	case 0x180:
+	    expname = "illegal_instruction";
+	    break;
+	case 0x1a0:
+	    expname = "slot_illegal_instruction";
+	    break;
+	case 0x800:
+	    expname = "fpu_disable";
+	    break;
+	case 0x820:
+	    expname = "slot_fpu";
+	    break;
+	case 0x100:
+	    expname = "data_write";
+	    break;
+	case 0x060:
+	    expname = "dtlb_miss_write";
+	    break;
+	case 0x0c0:
+	    expname = "dtlb_violation_write";
+	    break;
+	case 0x120:
+	    expname = "fpu_exception";
+	    break;
+	case 0x080:
+	    expname = "initial_page_write";
+	    break;
+	case 0x160:
+	    expname = "trapa";
+	    break;
+	default:
+            expname = do_irq ? "interrupt" : "???";
+            break;
+	}
+	qemu_log("exception 0x%03x [%s] raised\n",
+		  irq_vector, expname);
+        log_cpu_state(cs, 0);
+    }
+
+    env->ssr = cpu_read_sr(env);
+    env->spc = env->pc;
+    env->sgr = env->gregs[15];
+    env->sr |= (1u << SR_BL) | (1u << SR_MD) | (1u << SR_RB);
+
+    if (env->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) {
+        /* Branch instruction should be executed again before delay slot. */
+	env->spc -= 2;
+	/* Clear flags for exception/interrupt routine. */
+	env->flags &= ~(DELAY_SLOT | DELAY_SLOT_CONDITIONAL | DELAY_SLOT_TRUE);
+    }
+    if (env->flags & DELAY_SLOT_CLEARME)
+        env->flags = 0;
+
+    if (do_exp) {
+        env->expevt = cs->exception_index;
+        switch (cs->exception_index) {
+        case 0x000:
+        case 0x020:
+        case 0x140:
+            env->sr &= ~(1u << SR_FD);
+            env->sr |= 0xf << 4; /* IMASK */
+            env->pc = 0xa0000000;
+            break;
+        case 0x040:
+        case 0x060:
+            env->pc = env->vbr + 0x400;
+            break;
+        case 0x160:
+            env->spc += 2; /* special case for TRAPA */
+            /* fall through */
+        default:
+            env->pc = env->vbr + 0x100;
+            break;
+        }
+        return;
+    }
+
+    if (do_irq) {
+        env->intevt = irq_vector;
+        env->pc = env->vbr + 0x600;
+        return;
+    }
+}
+
+static void update_itlb_use(CPUSH4State * env, int itlbnb)
+{
+    uint8_t or_mask = 0, and_mask = (uint8_t) - 1;
+
+    switch (itlbnb) {
+    case 0:
+	and_mask = 0x1f;
+	break;
+    case 1:
+	and_mask = 0xe7;
+	or_mask = 0x80;
+	break;
+    case 2:
+	and_mask = 0xfb;
+	or_mask = 0x50;
+	break;
+    case 3:
+	or_mask = 0x2c;
+	break;
+    }
+
+    env->mmucr &= (and_mask << 24) | 0x00ffffff;
+    env->mmucr |= (or_mask << 24);
+}
+
+static int itlb_replacement(CPUSH4State * env)
+{
+    SuperHCPU *cpu = sh_env_get_cpu(env);
+
+    if ((env->mmucr & 0xe0000000) == 0xe0000000) {
+	return 0;
+    }
+    if ((env->mmucr & 0x98000000) == 0x18000000) {
+	return 1;
+    }
+    if ((env->mmucr & 0x54000000) == 0x04000000) {
+	return 2;
+    }
+    if ((env->mmucr & 0x2c000000) == 0x00000000) {
+	return 3;
+    }
+    cpu_abort(CPU(cpu), "Unhandled itlb_replacement");
+}
+
+/* Find the corresponding entry in the right TLB
+   Return entry, MMU_DTLB_MISS or MMU_DTLB_MULTIPLE
+*/
+static int find_tlb_entry(CPUSH4State * env, target_ulong address,
+			  tlb_t * entries, uint8_t nbtlb, int use_asid)
+{
+    int match = MMU_DTLB_MISS;
+    uint32_t start, end;
+    uint8_t asid;
+    int i;
+
+    asid = env->pteh & 0xff;
+
+    for (i = 0; i < nbtlb; i++) {
+	if (!entries[i].v)
+	    continue;		/* Invalid entry */
+	if (!entries[i].sh && use_asid && entries[i].asid != asid)
+	    continue;		/* Bad ASID */
+	start = (entries[i].vpn << 10) & ~(entries[i].size - 1);
+	end = start + entries[i].size - 1;
+	if (address >= start && address <= end) {	/* Match */
+	    if (match != MMU_DTLB_MISS)
+		return MMU_DTLB_MULTIPLE;	/* Multiple match */
+	    match = i;
+	}
+    }
+    return match;
+}
+
+static void increment_urc(CPUSH4State * env)
+{
+    uint8_t urb, urc;
+
+    /* Increment URC */
+    urb = ((env->mmucr) >> 18) & 0x3f;
+    urc = ((env->mmucr) >> 10) & 0x3f;
+    urc++;
+    if ((urb > 0 && urc > urb) || urc > (UTLB_SIZE - 1))
+	urc = 0;
+    env->mmucr = (env->mmucr & 0xffff03ff) | (urc << 10);
+}
+
+/* Copy and utlb entry into itlb
+   Return entry
+*/
+static int copy_utlb_entry_itlb(CPUSH4State *env, int utlb)
+{
+    int itlb;
+
+    tlb_t * ientry;
+    itlb = itlb_replacement(env);
+    ientry = &env->itlb[itlb];
+    if (ientry->v) {
+        tlb_flush_page(CPU(sh_env_get_cpu(env)), ientry->vpn << 10);
+    }
+    *ientry = env->utlb[utlb];
+    update_itlb_use(env, itlb);
+    return itlb;
+}
+
+/* Find itlb entry
+   Return entry, MMU_ITLB_MISS, MMU_ITLB_MULTIPLE or MMU_DTLB_MULTIPLE
+*/
+static int find_itlb_entry(CPUSH4State * env, target_ulong address,
+                           int use_asid)
+{
+    int e;
+
+    e = find_tlb_entry(env, address, env->itlb, ITLB_SIZE, use_asid);
+    if (e == MMU_DTLB_MULTIPLE) {
+	e = MMU_ITLB_MULTIPLE;
+    } else if (e == MMU_DTLB_MISS) {
+	e = MMU_ITLB_MISS;
+    } else if (e >= 0) {
+	update_itlb_use(env, e);
+    }
+    return e;
+}
+
+/* Find utlb entry
+   Return entry, MMU_DTLB_MISS, MMU_DTLB_MULTIPLE */
+static int find_utlb_entry(CPUSH4State * env, target_ulong address, int use_asid)
+{
+    /* per utlb access */
+    increment_urc(env);
+
+    /* Return entry */
+    return find_tlb_entry(env, address, env->utlb, UTLB_SIZE, use_asid);
+}
+
+/* Match address against MMU
+   Return MMU_OK, MMU_DTLB_MISS_READ, MMU_DTLB_MISS_WRITE,
+   MMU_DTLB_INITIAL_WRITE, MMU_DTLB_VIOLATION_READ,
+   MMU_DTLB_VIOLATION_WRITE, MMU_ITLB_MISS,
+   MMU_ITLB_MULTIPLE, MMU_ITLB_VIOLATION,
+   MMU_IADDR_ERROR, MMU_DADDR_ERROR_READ, MMU_DADDR_ERROR_WRITE.
+*/
+static int get_mmu_address(CPUSH4State * env, target_ulong * physical,
+			   int *prot, target_ulong address,
+			   int rw, int access_type)
+{
+    int use_asid, n;
+    tlb_t *matching = NULL;
+
+    use_asid = !(env->mmucr & MMUCR_SV) || !(env->sr & (1u << SR_MD));
+
+    if (rw == 2) {
+        n = find_itlb_entry(env, address, use_asid);
+	if (n >= 0) {
+	    matching = &env->itlb[n];
+            if (!(env->sr & (1u << SR_MD)) && !(matching->pr & 2)) {
+		n = MMU_ITLB_VIOLATION;
+            } else {
+		*prot = PAGE_EXEC;
+            }
+        } else {
+            n = find_utlb_entry(env, address, use_asid);
+            if (n >= 0) {
+                n = copy_utlb_entry_itlb(env, n);
+                matching = &env->itlb[n];
+                if (!(env->sr & (1u << SR_MD)) && !(matching->pr & 2)) {
+                    n = MMU_ITLB_VIOLATION;
+                } else {
+                    *prot = PAGE_READ | PAGE_EXEC;
+                    if ((matching->pr & 1) && matching->d) {
+                        *prot |= PAGE_WRITE;
+                    }
+                }
+            } else if (n == MMU_DTLB_MULTIPLE) {
+                n = MMU_ITLB_MULTIPLE;
+            } else if (n == MMU_DTLB_MISS) {
+                n = MMU_ITLB_MISS;
+            }
+	}
+    } else {
+	n = find_utlb_entry(env, address, use_asid);
+	if (n >= 0) {
+	    matching = &env->utlb[n];
+            if (!(env->sr & (1u << SR_MD)) && !(matching->pr & 2)) {
+                n = (rw == 1) ? MMU_DTLB_VIOLATION_WRITE :
+                    MMU_DTLB_VIOLATION_READ;
+            } else if ((rw == 1) && !(matching->pr & 1)) {
+                n = MMU_DTLB_VIOLATION_WRITE;
+            } else if ((rw == 1) && !matching->d) {
+                n = MMU_DTLB_INITIAL_WRITE;
+            } else {
+                *prot = PAGE_READ;
+                if ((matching->pr & 1) && matching->d) {
+                    *prot |= PAGE_WRITE;
+                }
+            }
+	} else if (n == MMU_DTLB_MISS) {
+	    n = (rw == 1) ? MMU_DTLB_MISS_WRITE :
+		MMU_DTLB_MISS_READ;
+	}
+    }
+    if (n >= 0) {
+	n = MMU_OK;
+	*physical = ((matching->ppn << 10) & ~(matching->size - 1)) |
+	    (address & (matching->size - 1));
+    }
+    return n;
+}
+
+static int get_physical_address(CPUSH4State * env, target_ulong * physical,
+                                int *prot, target_ulong address,
+                                int rw, int access_type)
+{
+    /* P1, P2 and P4 areas do not use translation */
+    if ((address >= 0x80000000 && address < 0xc0000000) ||
+	address >= 0xe0000000) {
+        if (!(env->sr & (1u << SR_MD))
+	    && (address < 0xe0000000 || address >= 0xe4000000)) {
+	    /* Unauthorized access in user mode (only store queues are available) */
+	    fprintf(stderr, "Unauthorized access\n");
+	    if (rw == 0)
+		return MMU_DADDR_ERROR_READ;
+	    else if (rw == 1)
+		return MMU_DADDR_ERROR_WRITE;
+	    else
+		return MMU_IADDR_ERROR;
+	}
+	if (address >= 0x80000000 && address < 0xc0000000) {
+	    /* Mask upper 3 bits for P1 and P2 areas */
+	    *physical = address & 0x1fffffff;
+	} else {
+	    *physical = address;
+	}
+	*prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+	return MMU_OK;
+    }
+
+    /* If MMU is disabled, return the corresponding physical page */
+    if (!(env->mmucr & MMUCR_AT)) {
+	*physical = address & 0x1FFFFFFF;
+	*prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+	return MMU_OK;
+    }
+
+    /* We need to resort to the MMU */
+    return get_mmu_address(env, physical, prot, address, rw, access_type);
+}
+
+int superh_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw,
+                                int mmu_idx)
+{
+    SuperHCPU *cpu = SUPERH_CPU(cs);
+    CPUSH4State *env = &cpu->env;
+    target_ulong physical;
+    int prot, ret, access_type;
+
+    access_type = ACCESS_INT;
+    ret =
+	get_physical_address(env, &physical, &prot, address, rw,
+			     access_type);
+
+    if (ret != MMU_OK) {
+	env->tea = address;
+	if (ret != MMU_DTLB_MULTIPLE && ret != MMU_ITLB_MULTIPLE) {
+	    env->pteh = (env->pteh & PTEH_ASID_MASK) |
+		    (address & PTEH_VPN_MASK);
+	}
+	switch (ret) {
+	case MMU_ITLB_MISS:
+	case MMU_DTLB_MISS_READ:
+            cs->exception_index = 0x040;
+	    break;
+	case MMU_DTLB_MULTIPLE:
+	case MMU_ITLB_MULTIPLE:
+            cs->exception_index = 0x140;
+	    break;
+	case MMU_ITLB_VIOLATION:
+            cs->exception_index = 0x0a0;
+	    break;
+	case MMU_DTLB_MISS_WRITE:
+            cs->exception_index = 0x060;
+	    break;
+	case MMU_DTLB_INITIAL_WRITE:
+            cs->exception_index = 0x080;
+	    break;
+	case MMU_DTLB_VIOLATION_READ:
+            cs->exception_index = 0x0a0;
+	    break;
+	case MMU_DTLB_VIOLATION_WRITE:
+            cs->exception_index = 0x0c0;
+	    break;
+	case MMU_IADDR_ERROR:
+	case MMU_DADDR_ERROR_READ:
+            cs->exception_index = 0x0e0;
+	    break;
+	case MMU_DADDR_ERROR_WRITE:
+            cs->exception_index = 0x100;
+	    break;
+	default:
+            cpu_abort(cs, "Unhandled MMU fault");
+	}
+	return 1;
+    }
+
+    address &= TARGET_PAGE_MASK;
+    physical &= TARGET_PAGE_MASK;
+
+    tlb_set_page(cs, address, physical, prot, mmu_idx, TARGET_PAGE_SIZE);
+    return 0;
+}
+
+hwaddr superh_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
+{
+    SuperHCPU *cpu = SUPERH_CPU(cs);
+    target_ulong physical;
+    int prot;
+
+    get_physical_address(&cpu->env, &physical, &prot, addr, 0, 0);
+    return physical;
+}
+
+void cpu_load_tlb(CPUSH4State * env)
+{
+    SuperHCPU *cpu = sh_env_get_cpu(env);
+    int n = cpu_mmucr_urc(env->mmucr);
+    tlb_t * entry = &env->utlb[n];
+
+    if (entry->v) {
+        /* Overwriting valid entry in utlb. */
+        target_ulong address = entry->vpn << 10;
+        tlb_flush_page(CPU(cpu), address);
+    }
+
+    /* Take values into cpu status from registers. */
+    entry->asid = (uint8_t)cpu_pteh_asid(env->pteh);
+    entry->vpn  = cpu_pteh_vpn(env->pteh);
+    entry->v    = (uint8_t)cpu_ptel_v(env->ptel);
+    entry->ppn  = cpu_ptel_ppn(env->ptel);
+    entry->sz   = (uint8_t)cpu_ptel_sz(env->ptel);
+    switch (entry->sz) {
+    case 0: /* 00 */
+        entry->size = 1024; /* 1K */
+        break;
+    case 1: /* 01 */
+        entry->size = 1024 * 4; /* 4K */
+        break;
+    case 2: /* 10 */
+        entry->size = 1024 * 64; /* 64K */
+        break;
+    case 3: /* 11 */
+        entry->size = 1024 * 1024; /* 1M */
+        break;
+    default:
+        cpu_abort(CPU(cpu), "Unhandled load_tlb");
+        break;
+    }
+    entry->sh   = (uint8_t)cpu_ptel_sh(env->ptel);
+    entry->c    = (uint8_t)cpu_ptel_c(env->ptel);
+    entry->pr   = (uint8_t)cpu_ptel_pr(env->ptel);
+    entry->d    = (uint8_t)cpu_ptel_d(env->ptel);
+    entry->wt   = (uint8_t)cpu_ptel_wt(env->ptel);
+    entry->sa   = (uint8_t)cpu_ptea_sa(env->ptea);
+    entry->tc   = (uint8_t)cpu_ptea_tc(env->ptea);
+}
+
+ void cpu_sh4_invalidate_tlb(CPUSH4State *s)
+{
+    int i;
+
+    /* UTLB */
+    for (i = 0; i < UTLB_SIZE; i++) {
+        tlb_t * entry = &s->utlb[i];
+        entry->v = 0;
+    }
+    /* ITLB */
+    for (i = 0; i < ITLB_SIZE; i++) {
+        tlb_t * entry = &s->itlb[i];
+        entry->v = 0;
+    }
+
+    tlb_flush(CPU(sh_env_get_cpu(s)), 1);
+}
+
+uint32_t cpu_sh4_read_mmaped_itlb_addr(CPUSH4State *s,
+                                       hwaddr addr)
+{
+    int index = (addr & 0x00000300) >> 8;
+    tlb_t * entry = &s->itlb[index];
+
+    return (entry->vpn  << 10) |
+           (entry->v    <<  8) |
+           (entry->asid);
+}
+
+void cpu_sh4_write_mmaped_itlb_addr(CPUSH4State *s, hwaddr addr,
+				    uint32_t mem_value)
+{
+    uint32_t vpn = (mem_value & 0xfffffc00) >> 10;
+    uint8_t v = (uint8_t)((mem_value & 0x00000100) >> 8);
+    uint8_t asid = (uint8_t)(mem_value & 0x000000ff);
+
+    int index = (addr & 0x00000300) >> 8;
+    tlb_t * entry = &s->itlb[index];
+    if (entry->v) {
+        /* Overwriting valid entry in itlb. */
+        target_ulong address = entry->vpn << 10;
+        tlb_flush_page(CPU(sh_env_get_cpu(s)), address);
+    }
+    entry->asid = asid;
+    entry->vpn = vpn;
+    entry->v = v;
+}
+
+uint32_t cpu_sh4_read_mmaped_itlb_data(CPUSH4State *s,
+                                       hwaddr addr)
+{
+    int array = (addr & 0x00800000) >> 23;
+    int index = (addr & 0x00000300) >> 8;
+    tlb_t * entry = &s->itlb[index];
+
+    if (array == 0) {
+        /* ITLB Data Array 1 */
+        return (entry->ppn << 10) |
+               (entry->v   <<  8) |
+               (entry->pr  <<  5) |
+               ((entry->sz & 1) <<  6) |
+               ((entry->sz & 2) <<  4) |
+               (entry->c   <<  3) |
+               (entry->sh  <<  1);
+    } else {
+        /* ITLB Data Array 2 */
+        return (entry->tc << 1) |
+               (entry->sa);
+    }
+}
+
+void cpu_sh4_write_mmaped_itlb_data(CPUSH4State *s, hwaddr addr,
+                                    uint32_t mem_value)
+{
+    int array = (addr & 0x00800000) >> 23;
+    int index = (addr & 0x00000300) >> 8;
+    tlb_t * entry = &s->itlb[index];
+
+    if (array == 0) {
+        /* ITLB Data Array 1 */
+        if (entry->v) {
+            /* Overwriting valid entry in utlb. */
+            target_ulong address = entry->vpn << 10;
+            tlb_flush_page(CPU(sh_env_get_cpu(s)), address);
+        }
+        entry->ppn = (mem_value & 0x1ffffc00) >> 10;
+        entry->v   = (mem_value & 0x00000100) >> 8;
+        entry->sz  = (mem_value & 0x00000080) >> 6 |
+                     (mem_value & 0x00000010) >> 4;
+        entry->pr  = (mem_value & 0x00000040) >> 5;
+        entry->c   = (mem_value & 0x00000008) >> 3;
+        entry->sh  = (mem_value & 0x00000002) >> 1;
+    } else {
+        /* ITLB Data Array 2 */
+        entry->tc  = (mem_value & 0x00000008) >> 3;
+        entry->sa  = (mem_value & 0x00000007);
+    }
+}
+
+uint32_t cpu_sh4_read_mmaped_utlb_addr(CPUSH4State *s,
+                                       hwaddr addr)
+{
+    int index = (addr & 0x00003f00) >> 8;
+    tlb_t * entry = &s->utlb[index];
+
+    increment_urc(s); /* per utlb access */
+
+    return (entry->vpn  << 10) |
+           (entry->v    <<  8) |
+           (entry->asid);
+}
+
+void cpu_sh4_write_mmaped_utlb_addr(CPUSH4State *s, hwaddr addr,
+				    uint32_t mem_value)
+{
+    int associate = addr & 0x0000080;
+    uint32_t vpn = (mem_value & 0xfffffc00) >> 10;
+    uint8_t d = (uint8_t)((mem_value & 0x00000200) >> 9);
+    uint8_t v = (uint8_t)((mem_value & 0x00000100) >> 8);
+    uint8_t asid = (uint8_t)(mem_value & 0x000000ff);
+    int use_asid = !(s->mmucr & MMUCR_SV) || !(s->sr & (1u << SR_MD));
+
+    if (associate) {
+        int i;
+	tlb_t * utlb_match_entry = NULL;
+	int needs_tlb_flush = 0;
+
+	/* search UTLB */
+	for (i = 0; i < UTLB_SIZE; i++) {
+            tlb_t * entry = &s->utlb[i];
+            if (!entry->v)
+	        continue;
+
+            if (entry->vpn == vpn
+                && (!use_asid || entry->asid == asid || entry->sh)) {
+	        if (utlb_match_entry) {
+                    CPUState *cs = CPU(sh_env_get_cpu(s));
+
+		    /* Multiple TLB Exception */
+                    cs->exception_index = 0x140;
+		    s->tea = addr;
+		    break;
+	        }
+		if (entry->v && !v)
+		    needs_tlb_flush = 1;
+		entry->v = v;
+		entry->d = d;
+	        utlb_match_entry = entry;
+	    }
+	    increment_urc(s); /* per utlb access */
+	}
+
+	/* search ITLB */
+	for (i = 0; i < ITLB_SIZE; i++) {
+            tlb_t * entry = &s->itlb[i];
+            if (entry->vpn == vpn
+                && (!use_asid || entry->asid == asid || entry->sh)) {
+	        if (entry->v && !v)
+		    needs_tlb_flush = 1;
+	        if (utlb_match_entry)
+		    *entry = *utlb_match_entry;
+	        else
+		    entry->v = v;
+		break;
+	    }
+	}
+
+        if (needs_tlb_flush) {
+            tlb_flush_page(CPU(sh_env_get_cpu(s)), vpn << 10);
+        }
+        
+    } else {
+        int index = (addr & 0x00003f00) >> 8;
+        tlb_t * entry = &s->utlb[index];
+	if (entry->v) {
+            CPUState *cs = CPU(sh_env_get_cpu(s));
+
+	    /* Overwriting valid entry in utlb. */
+            target_ulong address = entry->vpn << 10;
+            tlb_flush_page(cs, address);
+	}
+	entry->asid = asid;
+	entry->vpn = vpn;
+	entry->d = d;
+	entry->v = v;
+	increment_urc(s);
+    }
+}
+
+uint32_t cpu_sh4_read_mmaped_utlb_data(CPUSH4State *s,
+                                       hwaddr addr)
+{
+    int array = (addr & 0x00800000) >> 23;
+    int index = (addr & 0x00003f00) >> 8;
+    tlb_t * entry = &s->utlb[index];
+
+    increment_urc(s); /* per utlb access */
+
+    if (array == 0) {
+        /* ITLB Data Array 1 */
+        return (entry->ppn << 10) |
+               (entry->v   <<  8) |
+               (entry->pr  <<  5) |
+               ((entry->sz & 1) <<  6) |
+               ((entry->sz & 2) <<  4) |
+               (entry->c   <<  3) |
+               (entry->d   <<  2) |
+               (entry->sh  <<  1) |
+               (entry->wt);
+    } else {
+        /* ITLB Data Array 2 */
+        return (entry->tc << 1) |
+               (entry->sa);
+    }
+}
+
+void cpu_sh4_write_mmaped_utlb_data(CPUSH4State *s, hwaddr addr,
+                                    uint32_t mem_value)
+{
+    int array = (addr & 0x00800000) >> 23;
+    int index = (addr & 0x00003f00) >> 8;
+    tlb_t * entry = &s->utlb[index];
+
+    increment_urc(s); /* per utlb access */
+
+    if (array == 0) {
+        /* UTLB Data Array 1 */
+        if (entry->v) {
+            /* Overwriting valid entry in utlb. */
+            target_ulong address = entry->vpn << 10;
+            tlb_flush_page(CPU(sh_env_get_cpu(s)), address);
+        }
+        entry->ppn = (mem_value & 0x1ffffc00) >> 10;
+        entry->v   = (mem_value & 0x00000100) >> 8;
+        entry->sz  = (mem_value & 0x00000080) >> 6 |
+                     (mem_value & 0x00000010) >> 4;
+        entry->pr  = (mem_value & 0x00000060) >> 5;
+        entry->c   = (mem_value & 0x00000008) >> 3;
+        entry->d   = (mem_value & 0x00000004) >> 2;
+        entry->sh  = (mem_value & 0x00000002) >> 1;
+        entry->wt  = (mem_value & 0x00000001);
+    } else {
+        /* UTLB Data Array 2 */
+        entry->tc = (mem_value & 0x00000008) >> 3;
+        entry->sa = (mem_value & 0x00000007);
+    }
+}
+
+int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr)
+{
+    int n;
+    int use_asid = !(env->mmucr & MMUCR_SV) || !(env->sr & (1u << SR_MD));
+
+    /* check area */
+    if (env->sr & (1u << SR_MD)) {
+        /* For privileged mode, P2 and P4 area is not cacheable. */
+        if ((0xA0000000 <= addr && addr < 0xC0000000) || 0xE0000000 <= addr)
+            return 0;
+    } else {
+        /* For user mode, only U0 area is cacheable. */
+        if (0x80000000 <= addr)
+            return 0;
+    }
+
+    /*
+     * TODO : Evaluate CCR and check if the cache is on or off.
+     *        Now CCR is not in CPUSH4State, but in SH7750State.
+     *        When you move the ccr into CPUSH4State, the code will be
+     *        as follows.
+     */
+#if 0
+    /* check if operand cache is enabled or not. */
+    if (!(env->ccr & 1))
+        return 0;
+#endif
+
+    /* if MMU is off, no check for TLB. */
+    if (env->mmucr & MMUCR_AT)
+        return 1;
+
+    /* check TLB */
+    n = find_tlb_entry(env, addr, env->itlb, ITLB_SIZE, use_asid);
+    if (n >= 0)
+        return env->itlb[n].c;
+
+    n = find_tlb_entry(env, addr, env->utlb, UTLB_SIZE, use_asid);
+    if (n >= 0)
+        return env->utlb[n].c;
+
+    return 0;
+}
+
+#endif
+
+bool superh_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
+{
+    if (interrupt_request & CPU_INTERRUPT_HARD) {
+        superh_cpu_do_interrupt(cs);
+        return true;
+    }
+    return false;
+}
diff --git a/target/sh4/helper.h b/target/sh4/helper.h
new file mode 100644
index 0000000000..dce859caea
--- /dev/null
+++ b/target/sh4/helper.h
@@ -0,0 +1,45 @@
+DEF_HELPER_1(ldtlb, void, env)
+DEF_HELPER_1(raise_illegal_instruction, noreturn, env)
+DEF_HELPER_1(raise_slot_illegal_instruction, noreturn, env)
+DEF_HELPER_1(raise_fpu_disable, noreturn, env)
+DEF_HELPER_1(raise_slot_fpu_disable, noreturn, env)
+DEF_HELPER_1(debug, noreturn, env)
+DEF_HELPER_1(sleep, noreturn, env)
+DEF_HELPER_2(trapa, noreturn, env, i32)
+
+DEF_HELPER_3(movcal, void, env, i32, i32)
+DEF_HELPER_1(discard_movcal_backup, void, env)
+DEF_HELPER_2(ocbi, void, env, i32)
+
+DEF_HELPER_3(macl, void, env, i32, i32)
+DEF_HELPER_3(macw, void, env, i32, i32)
+
+DEF_HELPER_2(ld_fpscr, void, env, i32)
+
+DEF_HELPER_FLAGS_1(fabs_FT, TCG_CALL_NO_RWG_SE, f32, f32)
+DEF_HELPER_FLAGS_1(fabs_DT, TCG_CALL_NO_RWG_SE, f64, f64)
+DEF_HELPER_FLAGS_3(fadd_FT, TCG_CALL_NO_WG, f32, env, f32, f32)
+DEF_HELPER_FLAGS_3(fadd_DT, TCG_CALL_NO_WG, f64, env, f64, f64)
+DEF_HELPER_FLAGS_2(fcnvsd_FT_DT, TCG_CALL_NO_WG, f64, env, f32)
+DEF_HELPER_FLAGS_2(fcnvds_DT_FT, TCG_CALL_NO_WG, f32, env, f64)
+
+DEF_HELPER_3(fcmp_eq_FT, void, env, f32, f32)
+DEF_HELPER_3(fcmp_eq_DT, void, env, f64, f64)
+DEF_HELPER_3(fcmp_gt_FT, void, env, f32, f32)
+DEF_HELPER_3(fcmp_gt_DT, void, env, f64, f64)
+DEF_HELPER_FLAGS_3(fdiv_FT, TCG_CALL_NO_WG, f32, env, f32, f32)
+DEF_HELPER_FLAGS_3(fdiv_DT, TCG_CALL_NO_WG, f64, env, f64, f64)
+DEF_HELPER_FLAGS_2(float_FT, TCG_CALL_NO_WG, f32, env, i32)
+DEF_HELPER_FLAGS_2(float_DT, TCG_CALL_NO_WG, f64, env, i32)
+DEF_HELPER_FLAGS_4(fmac_FT, TCG_CALL_NO_WG, f32, env, f32, f32, f32)
+DEF_HELPER_FLAGS_3(fmul_FT, TCG_CALL_NO_WG, f32, env, f32, f32)
+DEF_HELPER_FLAGS_3(fmul_DT, TCG_CALL_NO_WG, f64, env, f64, f64)
+DEF_HELPER_FLAGS_1(fneg_T, TCG_CALL_NO_RWG_SE, f32, f32)
+DEF_HELPER_FLAGS_3(fsub_FT, TCG_CALL_NO_WG, f32, env, f32, f32)
+DEF_HELPER_FLAGS_3(fsub_DT, TCG_CALL_NO_WG, f64, env, f64, f64)
+DEF_HELPER_FLAGS_2(fsqrt_FT, TCG_CALL_NO_WG, f32, env, f32)
+DEF_HELPER_FLAGS_2(fsqrt_DT, TCG_CALL_NO_WG, f64, env, f64)
+DEF_HELPER_FLAGS_2(ftrc_FT, TCG_CALL_NO_WG, i32, env, f32)
+DEF_HELPER_FLAGS_2(ftrc_DT, TCG_CALL_NO_WG, i32, env, f64)
+DEF_HELPER_3(fipr, void, env, i32, i32)
+DEF_HELPER_2(ftrv, void, env, i32)
diff --git a/target/sh4/monitor.c b/target/sh4/monitor.c
new file mode 100644
index 0000000000..426e5d4914
--- /dev/null
+++ b/target/sh4/monitor.c
@@ -0,0 +1,53 @@
+/*
+ * QEMU monitor
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "monitor/monitor.h"
+#include "monitor/hmp-target.h"
+#include "hmp.h"
+
+static void print_tlb(Monitor *mon, int idx, tlb_t *tlb)
+{
+    monitor_printf(mon, " tlb%i:\t"
+                   "asid=%hhu vpn=%x\tppn=%x\tsz=%hhu size=%u\t"
+                   "v=%hhu shared=%hhu cached=%hhu prot=%hhu "
+                   "dirty=%hhu writethrough=%hhu\n",
+                   idx,
+                   tlb->asid, tlb->vpn, tlb->ppn, tlb->sz, tlb->size,
+                   tlb->v, tlb->sh, tlb->c, tlb->pr,
+                   tlb->d, tlb->wt);
+}
+
+void hmp_info_tlb(Monitor *mon, const QDict *qdict)
+{
+    CPUArchState *env = mon_get_cpu_env();
+    int i;
+
+    monitor_printf (mon, "ITLB:\n");
+    for (i = 0 ; i < ITLB_SIZE ; i++)
+        print_tlb (mon, i, &env->itlb[i]);
+    monitor_printf (mon, "UTLB:\n");
+    for (i = 0 ; i < UTLB_SIZE ; i++)
+        print_tlb (mon, i, &env->utlb[i]);
+}
diff --git a/target/sh4/op_helper.c b/target/sh4/op_helper.c
new file mode 100644
index 0000000000..684d3f3758
--- /dev/null
+++ b/target/sh4/op_helper.c
@@ -0,0 +1,498 @@
+/*
+ *  SH4 emulation
+ *
+ *  Copyright (c) 2005 Samuel Tardieu
+ *
+ * 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/>.
+ */
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "exec/helper-proto.h"
+#include "exec/exec-all.h"
+#include "exec/cpu_ldst.h"
+
+#ifndef CONFIG_USER_ONLY
+
+void tlb_fill(CPUState *cs, target_ulong addr, MMUAccessType access_type,
+              int mmu_idx, uintptr_t retaddr)
+{
+    int ret;
+
+    ret = superh_cpu_handle_mmu_fault(cs, addr, access_type, mmu_idx);
+    if (ret) {
+        /* now we have a real cpu fault */
+        if (retaddr) {
+            cpu_restore_state(cs, retaddr);
+        }
+        cpu_loop_exit(cs);
+    }
+}
+
+#endif
+
+void helper_ldtlb(CPUSH4State *env)
+{
+#ifdef CONFIG_USER_ONLY
+    SuperHCPU *cpu = sh_env_get_cpu(env);
+
+    /* XXXXX */
+    cpu_abort(CPU(cpu), "Unhandled ldtlb");
+#else
+    cpu_load_tlb(env);
+#endif
+}
+
+static inline void QEMU_NORETURN raise_exception(CPUSH4State *env, int index,
+                                                 uintptr_t retaddr)
+{
+    CPUState *cs = CPU(sh_env_get_cpu(env));
+
+    cs->exception_index = index;
+    if (retaddr) {
+        cpu_restore_state(cs, retaddr);
+    }
+    cpu_loop_exit(cs);
+}
+
+void helper_raise_illegal_instruction(CPUSH4State *env)
+{
+    raise_exception(env, 0x180, 0);
+}
+
+void helper_raise_slot_illegal_instruction(CPUSH4State *env)
+{
+    raise_exception(env, 0x1a0, 0);
+}
+
+void helper_raise_fpu_disable(CPUSH4State *env)
+{
+    raise_exception(env, 0x800, 0);
+}
+
+void helper_raise_slot_fpu_disable(CPUSH4State *env)
+{
+    raise_exception(env, 0x820, 0);
+}
+
+void helper_debug(CPUSH4State *env)
+{
+    raise_exception(env, EXCP_DEBUG, 0);
+}
+
+void helper_sleep(CPUSH4State *env)
+{
+    CPUState *cs = CPU(sh_env_get_cpu(env));
+
+    cs->halted = 1;
+    env->in_sleep = 1;
+    raise_exception(env, EXCP_HLT, 0);
+}
+
+void helper_trapa(CPUSH4State *env, uint32_t tra)
+{
+    env->tra = tra << 2;
+    raise_exception(env, 0x160, 0);
+}
+
+void helper_movcal(CPUSH4State *env, uint32_t address, uint32_t value)
+{
+    if (cpu_sh4_is_cached (env, address))
+    {
+        memory_content *r = g_new(memory_content, 1);
+
+	r->address = address;
+	r->value = value;
+	r->next = NULL;
+
+	*(env->movcal_backup_tail) = r;
+	env->movcal_backup_tail = &(r->next);
+    }
+}
+
+void helper_discard_movcal_backup(CPUSH4State *env)
+{
+    memory_content *current = env->movcal_backup;
+
+    while(current)
+    {
+	memory_content *next = current->next;
+        g_free(current);
+	env->movcal_backup = current = next;
+	if (current == NULL)
+	    env->movcal_backup_tail = &(env->movcal_backup);
+    } 
+}
+
+void helper_ocbi(CPUSH4State *env, uint32_t address)
+{
+    memory_content **current = &(env->movcal_backup);
+    while (*current)
+    {
+	uint32_t a = (*current)->address;
+	if ((a & ~0x1F) == (address & ~0x1F))
+	{
+	    memory_content *next = (*current)->next;
+            cpu_stl_data(env, a, (*current)->value);
+	    
+	    if (next == NULL)
+	    {
+		env->movcal_backup_tail = current;
+	    }
+
+            g_free(*current);
+	    *current = next;
+	    break;
+	}
+    }
+}
+
+void helper_macl(CPUSH4State *env, uint32_t arg0, uint32_t arg1)
+{
+    int64_t res;
+
+    res = ((uint64_t) env->mach << 32) | env->macl;
+    res += (int64_t) (int32_t) arg0 *(int64_t) (int32_t) arg1;
+    env->mach = (res >> 32) & 0xffffffff;
+    env->macl = res & 0xffffffff;
+    if (env->sr & (1u << SR_S)) {
+	if (res < 0)
+	    env->mach |= 0xffff0000;
+	else
+	    env->mach &= 0x00007fff;
+    }
+}
+
+void helper_macw(CPUSH4State *env, uint32_t arg0, uint32_t arg1)
+{
+    int64_t res;
+
+    res = ((uint64_t) env->mach << 32) | env->macl;
+    res += (int64_t) (int16_t) arg0 *(int64_t) (int16_t) arg1;
+    env->mach = (res >> 32) & 0xffffffff;
+    env->macl = res & 0xffffffff;
+    if (env->sr & (1u << SR_S)) {
+	if (res < -0x80000000) {
+	    env->mach = 1;
+	    env->macl = 0x80000000;
+	} else if (res > 0x000000007fffffff) {
+	    env->mach = 1;
+	    env->macl = 0x7fffffff;
+	}
+    }
+}
+
+void helper_ld_fpscr(CPUSH4State *env, uint32_t val)
+{
+    env->fpscr = val & FPSCR_MASK;
+    if ((val & FPSCR_RM_MASK) == FPSCR_RM_ZERO) {
+	set_float_rounding_mode(float_round_to_zero, &env->fp_status);
+    } else {
+	set_float_rounding_mode(float_round_nearest_even, &env->fp_status);
+    }
+    set_flush_to_zero((val & FPSCR_DN) != 0, &env->fp_status);
+}
+
+static void update_fpscr(CPUSH4State *env, uintptr_t retaddr)
+{
+    int xcpt, cause, enable;
+
+    xcpt = get_float_exception_flags(&env->fp_status);
+
+    /* Clear the flag entries */
+    env->fpscr &= ~FPSCR_FLAG_MASK;
+
+    if (unlikely(xcpt)) {
+        if (xcpt & float_flag_invalid) {
+            env->fpscr |= FPSCR_FLAG_V;
+        }
+        if (xcpt & float_flag_divbyzero) {
+            env->fpscr |= FPSCR_FLAG_Z;
+        }
+        if (xcpt & float_flag_overflow) {
+            env->fpscr |= FPSCR_FLAG_O;
+        }
+        if (xcpt & float_flag_underflow) {
+            env->fpscr |= FPSCR_FLAG_U;
+        }
+        if (xcpt & float_flag_inexact) {
+            env->fpscr |= FPSCR_FLAG_I;
+        }
+
+        /* Accumulate in cause entries */
+        env->fpscr |= (env->fpscr & FPSCR_FLAG_MASK)
+                      << (FPSCR_CAUSE_SHIFT - FPSCR_FLAG_SHIFT);
+
+        /* Generate an exception if enabled */
+        cause = (env->fpscr & FPSCR_CAUSE_MASK) >> FPSCR_CAUSE_SHIFT;
+        enable = (env->fpscr & FPSCR_ENABLE_MASK) >> FPSCR_ENABLE_SHIFT;
+        if (cause & enable) {
+            raise_exception(env, 0x120, retaddr);
+        }
+    }
+}
+
+float32 helper_fabs_FT(float32 t0)
+{
+    return float32_abs(t0);
+}
+
+float64 helper_fabs_DT(float64 t0)
+{
+    return float64_abs(t0);
+}
+
+float32 helper_fadd_FT(CPUSH4State *env, float32 t0, float32 t1)
+{
+    set_float_exception_flags(0, &env->fp_status);
+    t0 = float32_add(t0, t1, &env->fp_status);
+    update_fpscr(env, GETPC());
+    return t0;
+}
+
+float64 helper_fadd_DT(CPUSH4State *env, float64 t0, float64 t1)
+{
+    set_float_exception_flags(0, &env->fp_status);
+    t0 = float64_add(t0, t1, &env->fp_status);
+    update_fpscr(env, GETPC());
+    return t0;
+}
+
+void helper_fcmp_eq_FT(CPUSH4State *env, float32 t0, float32 t1)
+{
+    int relation;
+
+    set_float_exception_flags(0, &env->fp_status);
+    relation = float32_compare(t0, t1, &env->fp_status);
+    if (unlikely(relation == float_relation_unordered)) {
+        update_fpscr(env, GETPC());
+    } else {
+        env->sr_t = (relation == float_relation_equal);
+    }
+}
+
+void helper_fcmp_eq_DT(CPUSH4State *env, float64 t0, float64 t1)
+{
+    int relation;
+
+    set_float_exception_flags(0, &env->fp_status);
+    relation = float64_compare(t0, t1, &env->fp_status);
+    if (unlikely(relation == float_relation_unordered)) {
+        update_fpscr(env, GETPC());
+    } else {
+        env->sr_t = (relation == float_relation_equal);
+    }
+}
+
+void helper_fcmp_gt_FT(CPUSH4State *env, float32 t0, float32 t1)
+{
+    int relation;
+
+    set_float_exception_flags(0, &env->fp_status);
+    relation = float32_compare(t0, t1, &env->fp_status);
+    if (unlikely(relation == float_relation_unordered)) {
+        update_fpscr(env, GETPC());
+    } else {
+        env->sr_t = (relation == float_relation_greater);
+    }
+}
+
+void helper_fcmp_gt_DT(CPUSH4State *env, float64 t0, float64 t1)
+{
+    int relation;
+
+    set_float_exception_flags(0, &env->fp_status);
+    relation = float64_compare(t0, t1, &env->fp_status);
+    if (unlikely(relation == float_relation_unordered)) {
+        update_fpscr(env, GETPC());
+    } else {
+        env->sr_t = (relation == float_relation_greater);
+    }
+}
+
+float64 helper_fcnvsd_FT_DT(CPUSH4State *env, float32 t0)
+{
+    float64 ret;
+    set_float_exception_flags(0, &env->fp_status);
+    ret = float32_to_float64(t0, &env->fp_status);
+    update_fpscr(env, GETPC());
+    return ret;
+}
+
+float32 helper_fcnvds_DT_FT(CPUSH4State *env, float64 t0)
+{
+    float32 ret;
+    set_float_exception_flags(0, &env->fp_status);
+    ret = float64_to_float32(t0, &env->fp_status);
+    update_fpscr(env, GETPC());
+    return ret;
+}
+
+float32 helper_fdiv_FT(CPUSH4State *env, float32 t0, float32 t1)
+{
+    set_float_exception_flags(0, &env->fp_status);
+    t0 = float32_div(t0, t1, &env->fp_status);
+    update_fpscr(env, GETPC());
+    return t0;
+}
+
+float64 helper_fdiv_DT(CPUSH4State *env, float64 t0, float64 t1)
+{
+    set_float_exception_flags(0, &env->fp_status);
+    t0 = float64_div(t0, t1, &env->fp_status);
+    update_fpscr(env, GETPC());
+    return t0;
+}
+
+float32 helper_float_FT(CPUSH4State *env, uint32_t t0)
+{
+    float32 ret;
+    set_float_exception_flags(0, &env->fp_status);
+    ret = int32_to_float32(t0, &env->fp_status);
+    update_fpscr(env, GETPC());
+    return ret;
+}
+
+float64 helper_float_DT(CPUSH4State *env, uint32_t t0)
+{
+    float64 ret;
+    set_float_exception_flags(0, &env->fp_status);
+    ret = int32_to_float64(t0, &env->fp_status);
+    update_fpscr(env, GETPC());
+    return ret;
+}
+
+float32 helper_fmac_FT(CPUSH4State *env, float32 t0, float32 t1, float32 t2)
+{
+    set_float_exception_flags(0, &env->fp_status);
+    t0 = float32_muladd(t0, t1, t2, 0, &env->fp_status);
+    update_fpscr(env, GETPC());
+    return t0;
+}
+
+float32 helper_fmul_FT(CPUSH4State *env, float32 t0, float32 t1)
+{
+    set_float_exception_flags(0, &env->fp_status);
+    t0 = float32_mul(t0, t1, &env->fp_status);
+    update_fpscr(env, GETPC());
+    return t0;
+}
+
+float64 helper_fmul_DT(CPUSH4State *env, float64 t0, float64 t1)
+{
+    set_float_exception_flags(0, &env->fp_status);
+    t0 = float64_mul(t0, t1, &env->fp_status);
+    update_fpscr(env, GETPC());
+    return t0;
+}
+
+float32 helper_fneg_T(float32 t0)
+{
+    return float32_chs(t0);
+}
+
+float32 helper_fsqrt_FT(CPUSH4State *env, float32 t0)
+{
+    set_float_exception_flags(0, &env->fp_status);
+    t0 = float32_sqrt(t0, &env->fp_status);
+    update_fpscr(env, GETPC());
+    return t0;
+}
+
+float64 helper_fsqrt_DT(CPUSH4State *env, float64 t0)
+{
+    set_float_exception_flags(0, &env->fp_status);
+    t0 = float64_sqrt(t0, &env->fp_status);
+    update_fpscr(env, GETPC());
+    return t0;
+}
+
+float32 helper_fsub_FT(CPUSH4State *env, float32 t0, float32 t1)
+{
+    set_float_exception_flags(0, &env->fp_status);
+    t0 = float32_sub(t0, t1, &env->fp_status);
+    update_fpscr(env, GETPC());
+    return t0;
+}
+
+float64 helper_fsub_DT(CPUSH4State *env, float64 t0, float64 t1)
+{
+    set_float_exception_flags(0, &env->fp_status);
+    t0 = float64_sub(t0, t1, &env->fp_status);
+    update_fpscr(env, GETPC());
+    return t0;
+}
+
+uint32_t helper_ftrc_FT(CPUSH4State *env, float32 t0)
+{
+    uint32_t ret;
+    set_float_exception_flags(0, &env->fp_status);
+    ret = float32_to_int32_round_to_zero(t0, &env->fp_status);
+    update_fpscr(env, GETPC());
+    return ret;
+}
+
+uint32_t helper_ftrc_DT(CPUSH4State *env, float64 t0)
+{
+    uint32_t ret;
+    set_float_exception_flags(0, &env->fp_status);
+    ret = float64_to_int32_round_to_zero(t0, &env->fp_status);
+    update_fpscr(env, GETPC());
+    return ret;
+}
+
+void helper_fipr(CPUSH4State *env, uint32_t m, uint32_t n)
+{
+    int bank, i;
+    float32 r, p;
+
+    bank = (env->sr & FPSCR_FR) ? 16 : 0;
+    r = float32_zero;
+    set_float_exception_flags(0, &env->fp_status);
+
+    for (i = 0 ; i < 4 ; i++) {
+        p = float32_mul(env->fregs[bank + m + i],
+                        env->fregs[bank + n + i],
+                        &env->fp_status);
+        r = float32_add(r, p, &env->fp_status);
+    }
+    update_fpscr(env, GETPC());
+
+    env->fregs[bank + n + 3] = r;
+}
+
+void helper_ftrv(CPUSH4State *env, uint32_t n)
+{
+    int bank_matrix, bank_vector;
+    int i, j;
+    float32 r[4];
+    float32 p;
+
+    bank_matrix = (env->sr & FPSCR_FR) ? 0 : 16;
+    bank_vector = (env->sr & FPSCR_FR) ? 16 : 0;
+    set_float_exception_flags(0, &env->fp_status);
+    for (i = 0 ; i < 4 ; i++) {
+        r[i] = float32_zero;
+        for (j = 0 ; j < 4 ; j++) {
+            p = float32_mul(env->fregs[bank_matrix + 4 * j + i],
+                            env->fregs[bank_vector + j],
+                            &env->fp_status);
+            r[i] = float32_add(r[i], p, &env->fp_status);
+        }
+    }
+    update_fpscr(env, GETPC());
+
+    for (i = 0 ; i < 4 ; i++) {
+        env->fregs[bank_vector + i] = r[i];
+    }
+}
diff --git a/target/sh4/translate.c b/target/sh4/translate.c
new file mode 100644
index 0000000000..c89a14733f
--- /dev/null
+++ b/target/sh4/translate.c
@@ -0,0 +1,1944 @@
+/*
+ *  SH4 translation
+ *
+ *  Copyright (c) 2005 Samuel Tardieu
+ *
+ * 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/>.
+ */
+
+#define DEBUG_DISAS
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "disas/disas.h"
+#include "exec/exec-all.h"
+#include "tcg-op.h"
+#include "exec/cpu_ldst.h"
+
+#include "exec/helper-proto.h"
+#include "exec/helper-gen.h"
+
+#include "trace-tcg.h"
+#include "exec/log.h"
+
+
+typedef struct DisasContext {
+    struct TranslationBlock *tb;
+    target_ulong pc;
+    uint16_t opcode;
+    uint32_t flags;
+    int bstate;
+    int memidx;
+    uint32_t delayed_pc;
+    int singlestep_enabled;
+    uint32_t features;
+    int has_movcal;
+} DisasContext;
+
+#if defined(CONFIG_USER_ONLY)
+#define IS_USER(ctx) 1
+#else
+#define IS_USER(ctx) (!(ctx->flags & (1u << SR_MD)))
+#endif
+
+enum {
+    BS_NONE     = 0, /* We go out of the TB without reaching a branch or an
+                      * exception condition
+                      */
+    BS_STOP     = 1, /* We want to stop translation for any reason */
+    BS_BRANCH   = 2, /* We reached a branch condition     */
+    BS_EXCP     = 3, /* We reached an exception condition */
+};
+
+/* global register indexes */
+static TCGv_env cpu_env;
+static TCGv cpu_gregs[24];
+static TCGv cpu_sr, cpu_sr_m, cpu_sr_q, cpu_sr_t;
+static TCGv cpu_pc, cpu_ssr, cpu_spc, cpu_gbr;
+static TCGv cpu_vbr, cpu_sgr, cpu_dbr, cpu_mach, cpu_macl;
+static TCGv cpu_pr, cpu_fpscr, cpu_fpul, cpu_ldst;
+static TCGv cpu_fregs[32];
+
+/* internal register indexes */
+static TCGv cpu_flags, cpu_delayed_pc;
+
+#include "exec/gen-icount.h"
+
+void sh4_translate_init(void)
+{
+    int i;
+    static int done_init = 0;
+    static const char * const gregnames[24] = {
+        "R0_BANK0", "R1_BANK0", "R2_BANK0", "R3_BANK0",
+        "R4_BANK0", "R5_BANK0", "R6_BANK0", "R7_BANK0",
+        "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15",
+        "R0_BANK1", "R1_BANK1", "R2_BANK1", "R3_BANK1",
+        "R4_BANK1", "R5_BANK1", "R6_BANK1", "R7_BANK1"
+    };
+    static const char * const fregnames[32] = {
+         "FPR0_BANK0",  "FPR1_BANK0",  "FPR2_BANK0",  "FPR3_BANK0",
+         "FPR4_BANK0",  "FPR5_BANK0",  "FPR6_BANK0",  "FPR7_BANK0",
+         "FPR8_BANK0",  "FPR9_BANK0", "FPR10_BANK0", "FPR11_BANK0",
+        "FPR12_BANK0", "FPR13_BANK0", "FPR14_BANK0", "FPR15_BANK0",
+         "FPR0_BANK1",  "FPR1_BANK1",  "FPR2_BANK1",  "FPR3_BANK1",
+         "FPR4_BANK1",  "FPR5_BANK1",  "FPR6_BANK1",  "FPR7_BANK1",
+         "FPR8_BANK1",  "FPR9_BANK1", "FPR10_BANK1", "FPR11_BANK1",
+        "FPR12_BANK1", "FPR13_BANK1", "FPR14_BANK1", "FPR15_BANK1",
+    };
+
+    if (done_init)
+        return;
+
+    cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
+    tcg_ctx.tcg_env = cpu_env;
+
+    for (i = 0; i < 24; i++)
+        cpu_gregs[i] = tcg_global_mem_new_i32(cpu_env,
+                                              offsetof(CPUSH4State, gregs[i]),
+                                              gregnames[i]);
+
+    cpu_pc = tcg_global_mem_new_i32(cpu_env,
+                                    offsetof(CPUSH4State, pc), "PC");
+    cpu_sr = tcg_global_mem_new_i32(cpu_env,
+                                    offsetof(CPUSH4State, sr), "SR");
+    cpu_sr_m = tcg_global_mem_new_i32(cpu_env,
+                                      offsetof(CPUSH4State, sr_m), "SR_M");
+    cpu_sr_q = tcg_global_mem_new_i32(cpu_env,
+                                      offsetof(CPUSH4State, sr_q), "SR_Q");
+    cpu_sr_t = tcg_global_mem_new_i32(cpu_env,
+                                      offsetof(CPUSH4State, sr_t), "SR_T");
+    cpu_ssr = tcg_global_mem_new_i32(cpu_env,
+                                     offsetof(CPUSH4State, ssr), "SSR");
+    cpu_spc = tcg_global_mem_new_i32(cpu_env,
+                                     offsetof(CPUSH4State, spc), "SPC");
+    cpu_gbr = tcg_global_mem_new_i32(cpu_env,
+                                     offsetof(CPUSH4State, gbr), "GBR");
+    cpu_vbr = tcg_global_mem_new_i32(cpu_env,
+                                     offsetof(CPUSH4State, vbr), "VBR");
+    cpu_sgr = tcg_global_mem_new_i32(cpu_env,
+                                     offsetof(CPUSH4State, sgr), "SGR");
+    cpu_dbr = tcg_global_mem_new_i32(cpu_env,
+                                     offsetof(CPUSH4State, dbr), "DBR");
+    cpu_mach = tcg_global_mem_new_i32(cpu_env,
+                                      offsetof(CPUSH4State, mach), "MACH");
+    cpu_macl = tcg_global_mem_new_i32(cpu_env,
+                                      offsetof(CPUSH4State, macl), "MACL");
+    cpu_pr = tcg_global_mem_new_i32(cpu_env,
+                                    offsetof(CPUSH4State, pr), "PR");
+    cpu_fpscr = tcg_global_mem_new_i32(cpu_env,
+                                       offsetof(CPUSH4State, fpscr), "FPSCR");
+    cpu_fpul = tcg_global_mem_new_i32(cpu_env,
+                                      offsetof(CPUSH4State, fpul), "FPUL");
+
+    cpu_flags = tcg_global_mem_new_i32(cpu_env,
+				       offsetof(CPUSH4State, flags), "_flags_");
+    cpu_delayed_pc = tcg_global_mem_new_i32(cpu_env,
+					    offsetof(CPUSH4State, delayed_pc),
+					    "_delayed_pc_");
+    cpu_ldst = tcg_global_mem_new_i32(cpu_env,
+				      offsetof(CPUSH4State, ldst), "_ldst_");
+
+    for (i = 0; i < 32; i++)
+        cpu_fregs[i] = tcg_global_mem_new_i32(cpu_env,
+                                              offsetof(CPUSH4State, fregs[i]),
+                                              fregnames[i]);
+
+    done_init = 1;
+}
+
+void superh_cpu_dump_state(CPUState *cs, FILE *f,
+                           fprintf_function cpu_fprintf, int flags)
+{
+    SuperHCPU *cpu = SUPERH_CPU(cs);
+    CPUSH4State *env = &cpu->env;
+    int i;
+    cpu_fprintf(f, "pc=0x%08x sr=0x%08x pr=0x%08x fpscr=0x%08x\n",
+                env->pc, cpu_read_sr(env), env->pr, env->fpscr);
+    cpu_fprintf(f, "spc=0x%08x ssr=0x%08x gbr=0x%08x vbr=0x%08x\n",
+		env->spc, env->ssr, env->gbr, env->vbr);
+    cpu_fprintf(f, "sgr=0x%08x dbr=0x%08x delayed_pc=0x%08x fpul=0x%08x\n",
+		env->sgr, env->dbr, env->delayed_pc, env->fpul);
+    for (i = 0; i < 24; i += 4) {
+	cpu_fprintf(f, "r%d=0x%08x r%d=0x%08x r%d=0x%08x r%d=0x%08x\n",
+		    i, env->gregs[i], i + 1, env->gregs[i + 1],
+		    i + 2, env->gregs[i + 2], i + 3, env->gregs[i + 3]);
+    }
+    if (env->flags & DELAY_SLOT) {
+	cpu_fprintf(f, "in delay slot (delayed_pc=0x%08x)\n",
+		    env->delayed_pc);
+    } else if (env->flags & DELAY_SLOT_CONDITIONAL) {
+	cpu_fprintf(f, "in conditional delay slot (delayed_pc=0x%08x)\n",
+		    env->delayed_pc);
+    }
+}
+
+static void gen_read_sr(TCGv dst)
+{
+    TCGv t0 = tcg_temp_new();
+    tcg_gen_shli_i32(t0, cpu_sr_q, SR_Q);
+    tcg_gen_or_i32(dst, dst, t0);
+    tcg_gen_shli_i32(t0, cpu_sr_m, SR_M);
+    tcg_gen_or_i32(dst, dst, t0);
+    tcg_gen_shli_i32(t0, cpu_sr_t, SR_T);
+    tcg_gen_or_i32(dst, cpu_sr, t0);
+    tcg_temp_free_i32(t0);
+}
+
+static void gen_write_sr(TCGv src)
+{
+    tcg_gen_andi_i32(cpu_sr, src,
+                     ~((1u << SR_Q) | (1u << SR_M) | (1u << SR_T)));
+    tcg_gen_shri_i32(cpu_sr_q, src, SR_Q);
+    tcg_gen_andi_i32(cpu_sr_q, cpu_sr_q, 1);
+    tcg_gen_shri_i32(cpu_sr_m, src, SR_M);
+    tcg_gen_andi_i32(cpu_sr_m, cpu_sr_m, 1);
+    tcg_gen_shri_i32(cpu_sr_t, src, SR_T);
+    tcg_gen_andi_i32(cpu_sr_t, cpu_sr_t, 1);
+}
+
+static inline bool use_goto_tb(DisasContext *ctx, target_ulong dest)
+{
+    if (unlikely(ctx->singlestep_enabled)) {
+        return false;
+    }
+
+#ifndef CONFIG_USER_ONLY
+    return (ctx->tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK);
+#else
+    return true;
+#endif
+}
+
+static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
+{
+    if (use_goto_tb(ctx, dest)) {
+	/* Use a direct jump if in same page and singlestep not enabled */
+        tcg_gen_goto_tb(n);
+        tcg_gen_movi_i32(cpu_pc, dest);
+        tcg_gen_exit_tb((uintptr_t)ctx->tb + n);
+    } else {
+        tcg_gen_movi_i32(cpu_pc, dest);
+        if (ctx->singlestep_enabled)
+            gen_helper_debug(cpu_env);
+        tcg_gen_exit_tb(0);
+    }
+}
+
+static void gen_jump(DisasContext * ctx)
+{
+    if (ctx->delayed_pc == (uint32_t) - 1) {
+	/* Target is not statically known, it comes necessarily from a
+	   delayed jump as immediate jump are conditinal jumps */
+	tcg_gen_mov_i32(cpu_pc, cpu_delayed_pc);
+	if (ctx->singlestep_enabled)
+            gen_helper_debug(cpu_env);
+	tcg_gen_exit_tb(0);
+    } else {
+	gen_goto_tb(ctx, 0, ctx->delayed_pc);
+    }
+}
+
+static inline void gen_branch_slot(uint32_t delayed_pc, int t)
+{
+    TCGLabel *label = gen_new_label();
+    tcg_gen_movi_i32(cpu_delayed_pc, delayed_pc);
+    tcg_gen_brcondi_i32(t ? TCG_COND_EQ : TCG_COND_NE, cpu_sr_t, 0, label);
+    tcg_gen_ori_i32(cpu_flags, cpu_flags, DELAY_SLOT_TRUE);
+    gen_set_label(label);
+}
+
+/* Immediate conditional jump (bt or bf) */
+static void gen_conditional_jump(DisasContext * ctx,
+				 target_ulong ift, target_ulong ifnott)
+{
+    TCGLabel *l1 = gen_new_label();
+    tcg_gen_brcondi_i32(TCG_COND_NE, cpu_sr_t, 0, l1);
+    gen_goto_tb(ctx, 0, ifnott);
+    gen_set_label(l1);
+    gen_goto_tb(ctx, 1, ift);
+}
+
+/* Delayed conditional jump (bt or bf) */
+static void gen_delayed_conditional_jump(DisasContext * ctx)
+{
+    TCGLabel *l1;
+    TCGv ds;
+
+    l1 = gen_new_label();
+    ds = tcg_temp_new();
+    tcg_gen_andi_i32(ds, cpu_flags, DELAY_SLOT_TRUE);
+    tcg_gen_brcondi_i32(TCG_COND_NE, ds, 0, l1);
+    gen_goto_tb(ctx, 1, ctx->pc + 2);
+    gen_set_label(l1);
+    tcg_gen_andi_i32(cpu_flags, cpu_flags, ~DELAY_SLOT_TRUE);
+    gen_jump(ctx);
+}
+
+static inline void gen_store_flags(uint32_t flags)
+{
+    tcg_gen_andi_i32(cpu_flags, cpu_flags, DELAY_SLOT_TRUE);
+    tcg_gen_ori_i32(cpu_flags, cpu_flags, flags);
+}
+
+static inline void gen_load_fpr64(TCGv_i64 t, int reg)
+{
+    tcg_gen_concat_i32_i64(t, cpu_fregs[reg + 1], cpu_fregs[reg]);
+}
+
+static inline void gen_store_fpr64 (TCGv_i64 t, int reg)
+{
+    TCGv_i32 tmp = tcg_temp_new_i32();
+    tcg_gen_extrl_i64_i32(tmp, t);
+    tcg_gen_mov_i32(cpu_fregs[reg + 1], tmp);
+    tcg_gen_shri_i64(t, t, 32);
+    tcg_gen_extrl_i64_i32(tmp, t);
+    tcg_gen_mov_i32(cpu_fregs[reg], tmp);
+    tcg_temp_free_i32(tmp);
+}
+
+#define B3_0 (ctx->opcode & 0xf)
+#define B6_4 ((ctx->opcode >> 4) & 0x7)
+#define B7_4 ((ctx->opcode >> 4) & 0xf)
+#define B7_0 (ctx->opcode & 0xff)
+#define B7_0s ((int32_t) (int8_t) (ctx->opcode & 0xff))
+#define B11_0s (ctx->opcode & 0x800 ? 0xfffff000 | (ctx->opcode & 0xfff) : \
+  (ctx->opcode & 0xfff))
+#define B11_8 ((ctx->opcode >> 8) & 0xf)
+#define B15_12 ((ctx->opcode >> 12) & 0xf)
+
+#define REG(x) ((x) < 8 && (ctx->flags & (1u << SR_MD))\
+                        && (ctx->flags & (1u << SR_RB))\
+                ? (cpu_gregs[x + 16]) : (cpu_gregs[x]))
+
+#define ALTREG(x) ((x) < 8 && (!(ctx->flags & (1u << SR_MD))\
+                               || !(ctx->flags & (1u << SR_RB)))\
+		? (cpu_gregs[x + 16]) : (cpu_gregs[x]))
+
+#define FREG(x) (ctx->flags & FPSCR_FR ? (x) ^ 0x10 : (x))
+#define XHACK(x) ((((x) & 1 ) << 4) | ((x) & 0xe))
+#define XREG(x) (ctx->flags & FPSCR_FR ? XHACK(x) ^ 0x10 : XHACK(x))
+#define DREG(x) FREG(x) /* Assumes lsb of (x) is always 0 */
+
+#define CHECK_NOT_DELAY_SLOT \
+  if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL))     \
+  {                                                           \
+      tcg_gen_movi_i32(cpu_pc, ctx->pc);                      \
+      gen_helper_raise_slot_illegal_instruction(cpu_env);     \
+      ctx->bstate = BS_BRANCH;                                \
+      return;                                                 \
+  }
+
+#define CHECK_PRIVILEGED                                        \
+  if (IS_USER(ctx)) {                                           \
+      tcg_gen_movi_i32(cpu_pc, ctx->pc);                        \
+      if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) { \
+          gen_helper_raise_slot_illegal_instruction(cpu_env);   \
+      } else {                                                  \
+          gen_helper_raise_illegal_instruction(cpu_env);        \
+      }                                                         \
+      ctx->bstate = BS_BRANCH;                                  \
+      return;                                                   \
+  }
+
+#define CHECK_FPU_ENABLED                                       \
+  if (ctx->flags & (1u << SR_FD)) {                             \
+      tcg_gen_movi_i32(cpu_pc, ctx->pc);                        \
+      if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) { \
+          gen_helper_raise_slot_fpu_disable(cpu_env);           \
+      } else {                                                  \
+          gen_helper_raise_fpu_disable(cpu_env);                \
+      }                                                         \
+      ctx->bstate = BS_BRANCH;                                  \
+      return;                                                   \
+  }
+
+static void _decode_opc(DisasContext * ctx)
+{
+    /* This code tries to make movcal emulation sufficiently
+       accurate for Linux purposes.  This instruction writes
+       memory, and prior to that, always allocates a cache line.
+       It is used in two contexts:
+       - in memcpy, where data is copied in blocks, the first write
+       of to a block uses movca.l for performance.
+       - in arch/sh/mm/cache-sh4.c, movcal.l + ocbi combination is used
+       to flush the cache. Here, the data written by movcal.l is never
+       written to memory, and the data written is just bogus.
+
+       To simulate this, we simulate movcal.l, we store the value to memory,
+       but we also remember the previous content. If we see ocbi, we check
+       if movcal.l for that address was done previously. If so, the write should
+       not have hit the memory, so we restore the previous content.
+       When we see an instruction that is neither movca.l
+       nor ocbi, the previous content is discarded.
+
+       To optimize, we only try to flush stores when we're at the start of
+       TB, or if we already saw movca.l in this TB and did not flush stores
+       yet.  */
+    if (ctx->has_movcal)
+	{
+	  int opcode = ctx->opcode & 0xf0ff;
+	  if (opcode != 0x0093 /* ocbi */
+	      && opcode != 0x00c3 /* movca.l */)
+	      {
+                  gen_helper_discard_movcal_backup(cpu_env);
+		  ctx->has_movcal = 0;
+	      }
+	}
+
+#if 0
+    fprintf(stderr, "Translating opcode 0x%04x\n", ctx->opcode);
+#endif
+
+    switch (ctx->opcode) {
+    case 0x0019:		/* div0u */
+        tcg_gen_movi_i32(cpu_sr_m, 0);
+        tcg_gen_movi_i32(cpu_sr_q, 0);
+        tcg_gen_movi_i32(cpu_sr_t, 0);
+	return;
+    case 0x000b:		/* rts */
+	CHECK_NOT_DELAY_SLOT
+	tcg_gen_mov_i32(cpu_delayed_pc, cpu_pr);
+	ctx->flags |= DELAY_SLOT;
+	ctx->delayed_pc = (uint32_t) - 1;
+	return;
+    case 0x0028:		/* clrmac */
+	tcg_gen_movi_i32(cpu_mach, 0);
+	tcg_gen_movi_i32(cpu_macl, 0);
+	return;
+    case 0x0048:		/* clrs */
+        tcg_gen_andi_i32(cpu_sr, cpu_sr, ~(1u << SR_S));
+	return;
+    case 0x0008:		/* clrt */
+        tcg_gen_movi_i32(cpu_sr_t, 0);
+	return;
+    case 0x0038:		/* ldtlb */
+	CHECK_PRIVILEGED
+        gen_helper_ldtlb(cpu_env);
+	return;
+    case 0x002b:		/* rte */
+	CHECK_PRIVILEGED
+	CHECK_NOT_DELAY_SLOT
+        gen_write_sr(cpu_ssr);
+	tcg_gen_mov_i32(cpu_delayed_pc, cpu_spc);
+	ctx->flags |= DELAY_SLOT;
+	ctx->delayed_pc = (uint32_t) - 1;
+	return;
+    case 0x0058:		/* sets */
+        tcg_gen_ori_i32(cpu_sr, cpu_sr, (1u << SR_S));
+	return;
+    case 0x0018:		/* sett */
+        tcg_gen_movi_i32(cpu_sr_t, 1);
+	return;
+    case 0xfbfd:		/* frchg */
+	tcg_gen_xori_i32(cpu_fpscr, cpu_fpscr, FPSCR_FR);
+	ctx->bstate = BS_STOP;
+	return;
+    case 0xf3fd:		/* fschg */
+        tcg_gen_xori_i32(cpu_fpscr, cpu_fpscr, FPSCR_SZ);
+	ctx->bstate = BS_STOP;
+	return;
+    case 0x0009:		/* nop */
+	return;
+    case 0x001b:		/* sleep */
+	CHECK_PRIVILEGED
+        tcg_gen_movi_i32(cpu_pc, ctx->pc + 2);
+        gen_helper_sleep(cpu_env);
+	return;
+    }
+
+    switch (ctx->opcode & 0xf000) {
+    case 0x1000:		/* mov.l Rm,@(disp,Rn) */
+	{
+	    TCGv addr = tcg_temp_new();
+	    tcg_gen_addi_i32(addr, REG(B11_8), B3_0 * 4);
+            tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_TEUL);
+	    tcg_temp_free(addr);
+	}
+	return;
+    case 0x5000:		/* mov.l @(disp,Rm),Rn */
+	{
+	    TCGv addr = tcg_temp_new();
+	    tcg_gen_addi_i32(addr, REG(B7_4), B3_0 * 4);
+            tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESL);
+	    tcg_temp_free(addr);
+	}
+	return;
+    case 0xe000:		/* mov #imm,Rn */
+	tcg_gen_movi_i32(REG(B11_8), B7_0s);
+	return;
+    case 0x9000:		/* mov.w @(disp,PC),Rn */
+	{
+	    TCGv addr = tcg_const_i32(ctx->pc + 4 + B7_0 * 2);
+            tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESW);
+	    tcg_temp_free(addr);
+	}
+	return;
+    case 0xd000:		/* mov.l @(disp,PC),Rn */
+	{
+	    TCGv addr = tcg_const_i32((ctx->pc + 4 + B7_0 * 4) & ~3);
+            tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESL);
+	    tcg_temp_free(addr);
+	}
+	return;
+    case 0x7000:		/* add #imm,Rn */
+	tcg_gen_addi_i32(REG(B11_8), REG(B11_8), B7_0s);
+	return;
+    case 0xa000:		/* bra disp */
+	CHECK_NOT_DELAY_SLOT
+	ctx->delayed_pc = ctx->pc + 4 + B11_0s * 2;
+	tcg_gen_movi_i32(cpu_delayed_pc, ctx->delayed_pc);
+	ctx->flags |= DELAY_SLOT;
+	return;
+    case 0xb000:		/* bsr disp */
+	CHECK_NOT_DELAY_SLOT
+	tcg_gen_movi_i32(cpu_pr, ctx->pc + 4);
+	ctx->delayed_pc = ctx->pc + 4 + B11_0s * 2;
+	tcg_gen_movi_i32(cpu_delayed_pc, ctx->delayed_pc);
+	ctx->flags |= DELAY_SLOT;
+	return;
+    }
+
+    switch (ctx->opcode & 0xf00f) {
+    case 0x6003:		/* mov Rm,Rn */
+	tcg_gen_mov_i32(REG(B11_8), REG(B7_4));
+	return;
+    case 0x2000:		/* mov.b Rm,@Rn */
+        tcg_gen_qemu_st_i32(REG(B7_4), REG(B11_8), ctx->memidx, MO_UB);
+	return;
+    case 0x2001:		/* mov.w Rm,@Rn */
+        tcg_gen_qemu_st_i32(REG(B7_4), REG(B11_8), ctx->memidx, MO_TEUW);
+	return;
+    case 0x2002:		/* mov.l Rm,@Rn */
+        tcg_gen_qemu_st_i32(REG(B7_4), REG(B11_8), ctx->memidx, MO_TEUL);
+	return;
+    case 0x6000:		/* mov.b @Rm,Rn */
+        tcg_gen_qemu_ld_i32(REG(B11_8), REG(B7_4), ctx->memidx, MO_SB);
+	return;
+    case 0x6001:		/* mov.w @Rm,Rn */
+        tcg_gen_qemu_ld_i32(REG(B11_8), REG(B7_4), ctx->memidx, MO_TESW);
+	return;
+    case 0x6002:		/* mov.l @Rm,Rn */
+        tcg_gen_qemu_ld_i32(REG(B11_8), REG(B7_4), ctx->memidx, MO_TESL);
+	return;
+    case 0x2004:		/* mov.b Rm,@-Rn */
+	{
+	    TCGv addr = tcg_temp_new();
+	    tcg_gen_subi_i32(addr, REG(B11_8), 1);
+            /* might cause re-execution */
+            tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_UB);
+	    tcg_gen_mov_i32(REG(B11_8), addr);			/* modify register status */
+	    tcg_temp_free(addr);
+	}
+	return;
+    case 0x2005:		/* mov.w Rm,@-Rn */
+	{
+	    TCGv addr = tcg_temp_new();
+	    tcg_gen_subi_i32(addr, REG(B11_8), 2);
+            tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_TEUW);
+	    tcg_gen_mov_i32(REG(B11_8), addr);
+	    tcg_temp_free(addr);
+	}
+	return;
+    case 0x2006:		/* mov.l Rm,@-Rn */
+	{
+	    TCGv addr = tcg_temp_new();
+	    tcg_gen_subi_i32(addr, REG(B11_8), 4);
+            tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_TEUL);
+	    tcg_gen_mov_i32(REG(B11_8), addr);
+	}
+	return;
+    case 0x6004:		/* mov.b @Rm+,Rn */
+        tcg_gen_qemu_ld_i32(REG(B11_8), REG(B7_4), ctx->memidx, MO_SB);
+	if ( B11_8 != B7_4 )
+		tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 1);
+	return;
+    case 0x6005:		/* mov.w @Rm+,Rn */
+        tcg_gen_qemu_ld_i32(REG(B11_8), REG(B7_4), ctx->memidx, MO_TESW);
+	if ( B11_8 != B7_4 )
+		tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 2);
+	return;
+    case 0x6006:		/* mov.l @Rm+,Rn */
+        tcg_gen_qemu_ld_i32(REG(B11_8), REG(B7_4), ctx->memidx, MO_TESL);
+	if ( B11_8 != B7_4 )
+		tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 4);
+	return;
+    case 0x0004:		/* mov.b Rm,@(R0,Rn) */
+	{
+	    TCGv addr = tcg_temp_new();
+	    tcg_gen_add_i32(addr, REG(B11_8), REG(0));
+            tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_UB);
+	    tcg_temp_free(addr);
+	}
+	return;
+    case 0x0005:		/* mov.w Rm,@(R0,Rn) */
+	{
+	    TCGv addr = tcg_temp_new();
+	    tcg_gen_add_i32(addr, REG(B11_8), REG(0));
+            tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_TEUW);
+	    tcg_temp_free(addr);
+	}
+	return;
+    case 0x0006:		/* mov.l Rm,@(R0,Rn) */
+	{
+	    TCGv addr = tcg_temp_new();
+	    tcg_gen_add_i32(addr, REG(B11_8), REG(0));
+            tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_TEUL);
+	    tcg_temp_free(addr);
+	}
+	return;
+    case 0x000c:		/* mov.b @(R0,Rm),Rn */
+	{
+	    TCGv addr = tcg_temp_new();
+	    tcg_gen_add_i32(addr, REG(B7_4), REG(0));
+            tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_SB);
+	    tcg_temp_free(addr);
+	}
+	return;
+    case 0x000d:		/* mov.w @(R0,Rm),Rn */
+	{
+	    TCGv addr = tcg_temp_new();
+	    tcg_gen_add_i32(addr, REG(B7_4), REG(0));
+            tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESW);
+	    tcg_temp_free(addr);
+	}
+	return;
+    case 0x000e:		/* mov.l @(R0,Rm),Rn */
+	{
+	    TCGv addr = tcg_temp_new();
+	    tcg_gen_add_i32(addr, REG(B7_4), REG(0));
+            tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESL);
+	    tcg_temp_free(addr);
+	}
+	return;
+    case 0x6008:		/* swap.b Rm,Rn */
+	{
+            TCGv low = tcg_temp_new();;
+	    tcg_gen_ext16u_i32(low, REG(B7_4));
+	    tcg_gen_bswap16_i32(low, low);
+            tcg_gen_deposit_i32(REG(B11_8), REG(B7_4), low, 0, 16);
+	    tcg_temp_free(low);
+	}
+	return;
+    case 0x6009:		/* swap.w Rm,Rn */
+        tcg_gen_rotli_i32(REG(B11_8), REG(B7_4), 16);
+	return;
+    case 0x200d:		/* xtrct Rm,Rn */
+	{
+	    TCGv high, low;
+	    high = tcg_temp_new();
+	    tcg_gen_shli_i32(high, REG(B7_4), 16);
+	    low = tcg_temp_new();
+	    tcg_gen_shri_i32(low, REG(B11_8), 16);
+	    tcg_gen_or_i32(REG(B11_8), high, low);
+	    tcg_temp_free(low);
+	    tcg_temp_free(high);
+	}
+	return;
+    case 0x300c:		/* add Rm,Rn */
+	tcg_gen_add_i32(REG(B11_8), REG(B11_8), REG(B7_4));
+	return;
+    case 0x300e:		/* addc Rm,Rn */
+        {
+            TCGv t0, t1;
+            t0 = tcg_const_tl(0);
+            t1 = tcg_temp_new();
+            tcg_gen_add2_i32(t1, cpu_sr_t, cpu_sr_t, t0, REG(B7_4), t0);
+            tcg_gen_add2_i32(REG(B11_8), cpu_sr_t,
+                             REG(B11_8), t0, t1, cpu_sr_t);
+            tcg_temp_free(t0);
+            tcg_temp_free(t1);
+        }
+	return;
+    case 0x300f:		/* addv Rm,Rn */
+        {
+            TCGv t0, t1, t2;
+            t0 = tcg_temp_new();
+            tcg_gen_add_i32(t0, REG(B7_4), REG(B11_8));
+            t1 = tcg_temp_new();
+            tcg_gen_xor_i32(t1, t0, REG(B11_8));
+            t2 = tcg_temp_new();
+            tcg_gen_xor_i32(t2, REG(B7_4), REG(B11_8));
+            tcg_gen_andc_i32(cpu_sr_t, t1, t2);
+            tcg_temp_free(t2);
+            tcg_gen_shri_i32(cpu_sr_t, cpu_sr_t, 31);
+            tcg_temp_free(t1);
+            tcg_gen_mov_i32(REG(B7_4), t0);
+            tcg_temp_free(t0);
+        }
+	return;
+    case 0x2009:		/* and Rm,Rn */
+	tcg_gen_and_i32(REG(B11_8), REG(B11_8), REG(B7_4));
+	return;
+    case 0x3000:		/* cmp/eq Rm,Rn */
+        tcg_gen_setcond_i32(TCG_COND_EQ, cpu_sr_t, REG(B11_8), REG(B7_4));
+	return;
+    case 0x3003:		/* cmp/ge Rm,Rn */
+        tcg_gen_setcond_i32(TCG_COND_GE, cpu_sr_t, REG(B11_8), REG(B7_4));
+	return;
+    case 0x3007:		/* cmp/gt Rm,Rn */
+        tcg_gen_setcond_i32(TCG_COND_GT, cpu_sr_t, REG(B11_8), REG(B7_4));
+	return;
+    case 0x3006:		/* cmp/hi Rm,Rn */
+        tcg_gen_setcond_i32(TCG_COND_GTU, cpu_sr_t, REG(B11_8), REG(B7_4));
+	return;
+    case 0x3002:		/* cmp/hs Rm,Rn */
+        tcg_gen_setcond_i32(TCG_COND_GEU, cpu_sr_t, REG(B11_8), REG(B7_4));
+	return;
+    case 0x200c:		/* cmp/str Rm,Rn */
+	{
+	    TCGv cmp1 = tcg_temp_new();
+	    TCGv cmp2 = tcg_temp_new();
+            tcg_gen_xor_i32(cmp2, REG(B7_4), REG(B11_8));
+            tcg_gen_subi_i32(cmp1, cmp2, 0x01010101);
+            tcg_gen_andc_i32(cmp1, cmp1, cmp2);
+            tcg_gen_andi_i32(cmp1, cmp1, 0x80808080);
+            tcg_gen_setcondi_i32(TCG_COND_NE, cpu_sr_t, cmp1, 0);
+	    tcg_temp_free(cmp2);
+	    tcg_temp_free(cmp1);
+	}
+	return;
+    case 0x2007:		/* div0s Rm,Rn */
+        tcg_gen_shri_i32(cpu_sr_q, REG(B11_8), 31);         /* SR_Q */
+        tcg_gen_shri_i32(cpu_sr_m, REG(B7_4), 31);          /* SR_M */
+        tcg_gen_xor_i32(cpu_sr_t, cpu_sr_q, cpu_sr_m);      /* SR_T */
+	return;
+    case 0x3004:		/* div1 Rm,Rn */
+        {
+            TCGv t0 = tcg_temp_new();
+            TCGv t1 = tcg_temp_new();
+            TCGv t2 = tcg_temp_new();
+            TCGv zero = tcg_const_i32(0);
+
+            /* shift left arg1, saving the bit being pushed out and inserting
+               T on the right */
+            tcg_gen_shri_i32(t0, REG(B11_8), 31);
+            tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 1);
+            tcg_gen_or_i32(REG(B11_8), REG(B11_8), cpu_sr_t);
+
+            /* Add or subtract arg0 from arg1 depending if Q == M. To avoid
+               using 64-bit temps, we compute arg0's high part from q ^ m, so
+               that it is 0x00000000 when adding the value or 0xffffffff when
+               subtracting it. */
+            tcg_gen_xor_i32(t1, cpu_sr_q, cpu_sr_m);
+            tcg_gen_subi_i32(t1, t1, 1);
+            tcg_gen_neg_i32(t2, REG(B7_4));
+            tcg_gen_movcond_i32(TCG_COND_EQ, t2, t1, zero, REG(B7_4), t2);
+            tcg_gen_add2_i32(REG(B11_8), t1, REG(B11_8), zero, t2, t1);
+
+            /* compute T and Q depending on carry */
+            tcg_gen_andi_i32(t1, t1, 1);
+            tcg_gen_xor_i32(t1, t1, t0);
+            tcg_gen_xori_i32(cpu_sr_t, t1, 1);
+            tcg_gen_xor_i32(cpu_sr_q, cpu_sr_m, t1);
+
+            tcg_temp_free(zero);
+            tcg_temp_free(t2);
+            tcg_temp_free(t1);
+            tcg_temp_free(t0);
+        }
+	return;
+    case 0x300d:		/* dmuls.l Rm,Rn */
+        tcg_gen_muls2_i32(cpu_macl, cpu_mach, REG(B7_4), REG(B11_8));
+	return;
+    case 0x3005:		/* dmulu.l Rm,Rn */
+        tcg_gen_mulu2_i32(cpu_macl, cpu_mach, REG(B7_4), REG(B11_8));
+	return;
+    case 0x600e:		/* exts.b Rm,Rn */
+	tcg_gen_ext8s_i32(REG(B11_8), REG(B7_4));
+	return;
+    case 0x600f:		/* exts.w Rm,Rn */
+	tcg_gen_ext16s_i32(REG(B11_8), REG(B7_4));
+	return;
+    case 0x600c:		/* extu.b Rm,Rn */
+	tcg_gen_ext8u_i32(REG(B11_8), REG(B7_4));
+	return;
+    case 0x600d:		/* extu.w Rm,Rn */
+	tcg_gen_ext16u_i32(REG(B11_8), REG(B7_4));
+	return;
+    case 0x000f:		/* mac.l @Rm+,@Rn+ */
+	{
+	    TCGv arg0, arg1;
+	    arg0 = tcg_temp_new();
+            tcg_gen_qemu_ld_i32(arg0, REG(B7_4), ctx->memidx, MO_TESL);
+	    arg1 = tcg_temp_new();
+            tcg_gen_qemu_ld_i32(arg1, REG(B11_8), ctx->memidx, MO_TESL);
+            gen_helper_macl(cpu_env, arg0, arg1);
+	    tcg_temp_free(arg1);
+	    tcg_temp_free(arg0);
+	    tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 4);
+	    tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4);
+	}
+	return;
+    case 0x400f:		/* mac.w @Rm+,@Rn+ */
+	{
+	    TCGv arg0, arg1;
+	    arg0 = tcg_temp_new();
+            tcg_gen_qemu_ld_i32(arg0, REG(B7_4), ctx->memidx, MO_TESL);
+	    arg1 = tcg_temp_new();
+            tcg_gen_qemu_ld_i32(arg1, REG(B11_8), ctx->memidx, MO_TESL);
+            gen_helper_macw(cpu_env, arg0, arg1);
+	    tcg_temp_free(arg1);
+	    tcg_temp_free(arg0);
+	    tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 2);
+	    tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 2);
+	}
+	return;
+    case 0x0007:		/* mul.l Rm,Rn */
+	tcg_gen_mul_i32(cpu_macl, REG(B7_4), REG(B11_8));
+	return;
+    case 0x200f:		/* muls.w Rm,Rn */
+	{
+	    TCGv arg0, arg1;
+	    arg0 = tcg_temp_new();
+	    tcg_gen_ext16s_i32(arg0, REG(B7_4));
+	    arg1 = tcg_temp_new();
+	    tcg_gen_ext16s_i32(arg1, REG(B11_8));
+	    tcg_gen_mul_i32(cpu_macl, arg0, arg1);
+	    tcg_temp_free(arg1);
+	    tcg_temp_free(arg0);
+	}
+	return;
+    case 0x200e:		/* mulu.w Rm,Rn */
+	{
+	    TCGv arg0, arg1;
+	    arg0 = tcg_temp_new();
+	    tcg_gen_ext16u_i32(arg0, REG(B7_4));
+	    arg1 = tcg_temp_new();
+	    tcg_gen_ext16u_i32(arg1, REG(B11_8));
+	    tcg_gen_mul_i32(cpu_macl, arg0, arg1);
+	    tcg_temp_free(arg1);
+	    tcg_temp_free(arg0);
+	}
+	return;
+    case 0x600b:		/* neg Rm,Rn */
+	tcg_gen_neg_i32(REG(B11_8), REG(B7_4));
+	return;
+    case 0x600a:		/* negc Rm,Rn */
+        {
+            TCGv t0 = tcg_const_i32(0);
+            tcg_gen_add2_i32(REG(B11_8), cpu_sr_t,
+                             REG(B7_4), t0, cpu_sr_t, t0);
+            tcg_gen_sub2_i32(REG(B11_8), cpu_sr_t,
+                             t0, t0, REG(B11_8), cpu_sr_t);
+            tcg_gen_andi_i32(cpu_sr_t, cpu_sr_t, 1);
+            tcg_temp_free(t0);
+        }
+	return;
+    case 0x6007:		/* not Rm,Rn */
+	tcg_gen_not_i32(REG(B11_8), REG(B7_4));
+	return;
+    case 0x200b:		/* or Rm,Rn */
+	tcg_gen_or_i32(REG(B11_8), REG(B11_8), REG(B7_4));
+	return;
+    case 0x400c:		/* shad Rm,Rn */
+	{
+            TCGv t0 = tcg_temp_new();
+            TCGv t1 = tcg_temp_new();
+            TCGv t2 = tcg_temp_new();
+
+            tcg_gen_andi_i32(t0, REG(B7_4), 0x1f);
+
+            /* positive case: shift to the left */
+            tcg_gen_shl_i32(t1, REG(B11_8), t0);
+
+            /* negative case: shift to the right in two steps to
+               correctly handle the -32 case */
+            tcg_gen_xori_i32(t0, t0, 0x1f);
+            tcg_gen_sar_i32(t2, REG(B11_8), t0);
+            tcg_gen_sari_i32(t2, t2, 1);
+
+            /* select between the two cases */
+            tcg_gen_movi_i32(t0, 0);
+            tcg_gen_movcond_i32(TCG_COND_GE, REG(B11_8), REG(B7_4), t0, t1, t2);
+
+            tcg_temp_free(t0);
+            tcg_temp_free(t1);
+            tcg_temp_free(t2);
+	}
+	return;
+    case 0x400d:		/* shld Rm,Rn */
+	{
+            TCGv t0 = tcg_temp_new();
+            TCGv t1 = tcg_temp_new();
+            TCGv t2 = tcg_temp_new();
+
+            tcg_gen_andi_i32(t0, REG(B7_4), 0x1f);
+
+            /* positive case: shift to the left */
+            tcg_gen_shl_i32(t1, REG(B11_8), t0);
+
+            /* negative case: shift to the right in two steps to
+               correctly handle the -32 case */
+            tcg_gen_xori_i32(t0, t0, 0x1f);
+            tcg_gen_shr_i32(t2, REG(B11_8), t0);
+            tcg_gen_shri_i32(t2, t2, 1);
+
+            /* select between the two cases */
+            tcg_gen_movi_i32(t0, 0);
+            tcg_gen_movcond_i32(TCG_COND_GE, REG(B11_8), REG(B7_4), t0, t1, t2);
+
+            tcg_temp_free(t0);
+            tcg_temp_free(t1);
+            tcg_temp_free(t2);
+	}
+	return;
+    case 0x3008:		/* sub Rm,Rn */
+	tcg_gen_sub_i32(REG(B11_8), REG(B11_8), REG(B7_4));
+	return;
+    case 0x300a:		/* subc Rm,Rn */
+        {
+            TCGv t0, t1;
+            t0 = tcg_const_tl(0);
+            t1 = tcg_temp_new();
+            tcg_gen_add2_i32(t1, cpu_sr_t, cpu_sr_t, t0, REG(B7_4), t0);
+            tcg_gen_sub2_i32(REG(B11_8), cpu_sr_t,
+                             REG(B11_8), t0, t1, cpu_sr_t);
+            tcg_gen_andi_i32(cpu_sr_t, cpu_sr_t, 1);
+            tcg_temp_free(t0);
+            tcg_temp_free(t1);
+        }
+	return;
+    case 0x300b:		/* subv Rm,Rn */
+        {
+            TCGv t0, t1, t2;
+            t0 = tcg_temp_new();
+            tcg_gen_sub_i32(t0, REG(B11_8), REG(B7_4));
+            t1 = tcg_temp_new();
+            tcg_gen_xor_i32(t1, t0, REG(B7_4));
+            t2 = tcg_temp_new();
+            tcg_gen_xor_i32(t2, REG(B11_8), REG(B7_4));
+            tcg_gen_and_i32(t1, t1, t2);
+            tcg_temp_free(t2);
+            tcg_gen_shri_i32(cpu_sr_t, t1, 31);
+            tcg_temp_free(t1);
+            tcg_gen_mov_i32(REG(B11_8), t0);
+            tcg_temp_free(t0);
+        }
+	return;
+    case 0x2008:		/* tst Rm,Rn */
+	{
+	    TCGv val = tcg_temp_new();
+	    tcg_gen_and_i32(val, REG(B7_4), REG(B11_8));
+            tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, val, 0);
+	    tcg_temp_free(val);
+	}
+	return;
+    case 0x200a:		/* xor Rm,Rn */
+	tcg_gen_xor_i32(REG(B11_8), REG(B11_8), REG(B7_4));
+	return;
+    case 0xf00c: /* fmov {F,D,X}Rm,{F,D,X}Rn - FPSCR: Nothing */
+	CHECK_FPU_ENABLED
+        if (ctx->flags & FPSCR_SZ) {
+	    TCGv_i64 fp = tcg_temp_new_i64();
+	    gen_load_fpr64(fp, XREG(B7_4));
+	    gen_store_fpr64(fp, XREG(B11_8));
+	    tcg_temp_free_i64(fp);
+	} else {
+	    tcg_gen_mov_i32(cpu_fregs[FREG(B11_8)], cpu_fregs[FREG(B7_4)]);
+	}
+	return;
+    case 0xf00a: /* fmov {F,D,X}Rm,@Rn - FPSCR: Nothing */
+	CHECK_FPU_ENABLED
+        if (ctx->flags & FPSCR_SZ) {
+	    TCGv addr_hi = tcg_temp_new();
+	    int fr = XREG(B7_4);
+	    tcg_gen_addi_i32(addr_hi, REG(B11_8), 4);
+            tcg_gen_qemu_st_i32(cpu_fregs[fr], REG(B11_8),
+                                ctx->memidx, MO_TEUL);
+            tcg_gen_qemu_st_i32(cpu_fregs[fr+1], addr_hi,
+                                ctx->memidx, MO_TEUL);
+	    tcg_temp_free(addr_hi);
+	} else {
+            tcg_gen_qemu_st_i32(cpu_fregs[FREG(B7_4)], REG(B11_8),
+                                ctx->memidx, MO_TEUL);
+	}
+	return;
+    case 0xf008: /* fmov @Rm,{F,D,X}Rn - FPSCR: Nothing */
+	CHECK_FPU_ENABLED
+        if (ctx->flags & FPSCR_SZ) {
+	    TCGv addr_hi = tcg_temp_new();
+	    int fr = XREG(B11_8);
+	    tcg_gen_addi_i32(addr_hi, REG(B7_4), 4);
+            tcg_gen_qemu_ld_i32(cpu_fregs[fr], REG(B7_4), ctx->memidx, MO_TEUL);
+            tcg_gen_qemu_ld_i32(cpu_fregs[fr+1], addr_hi, ctx->memidx, MO_TEUL);
+	    tcg_temp_free(addr_hi);
+	} else {
+            tcg_gen_qemu_ld_i32(cpu_fregs[FREG(B11_8)], REG(B7_4),
+                                ctx->memidx, MO_TEUL);
+	}
+	return;
+    case 0xf009: /* fmov @Rm+,{F,D,X}Rn - FPSCR: Nothing */
+	CHECK_FPU_ENABLED
+        if (ctx->flags & FPSCR_SZ) {
+	    TCGv addr_hi = tcg_temp_new();
+	    int fr = XREG(B11_8);
+	    tcg_gen_addi_i32(addr_hi, REG(B7_4), 4);
+            tcg_gen_qemu_ld_i32(cpu_fregs[fr], REG(B7_4), ctx->memidx, MO_TEUL);
+            tcg_gen_qemu_ld_i32(cpu_fregs[fr+1], addr_hi, ctx->memidx, MO_TEUL);
+	    tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 8);
+	    tcg_temp_free(addr_hi);
+	} else {
+            tcg_gen_qemu_ld_i32(cpu_fregs[FREG(B11_8)], REG(B7_4),
+                                ctx->memidx, MO_TEUL);
+	    tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 4);
+	}
+	return;
+    case 0xf00b: /* fmov {F,D,X}Rm,@-Rn - FPSCR: Nothing */
+	CHECK_FPU_ENABLED
+        TCGv addr = tcg_temp_new_i32();
+        tcg_gen_subi_i32(addr, REG(B11_8), 4);
+        if (ctx->flags & FPSCR_SZ) {
+	    int fr = XREG(B7_4);
+            tcg_gen_qemu_st_i32(cpu_fregs[fr+1], addr, ctx->memidx, MO_TEUL);
+	    tcg_gen_subi_i32(addr, addr, 4);
+            tcg_gen_qemu_st_i32(cpu_fregs[fr], addr, ctx->memidx, MO_TEUL);
+	} else {
+            tcg_gen_qemu_st_i32(cpu_fregs[FREG(B7_4)], addr,
+                                ctx->memidx, MO_TEUL);
+	}
+        tcg_gen_mov_i32(REG(B11_8), addr);
+        tcg_temp_free(addr);
+	return;
+    case 0xf006: /* fmov @(R0,Rm),{F,D,X}Rm - FPSCR: Nothing */
+	CHECK_FPU_ENABLED
+	{
+	    TCGv addr = tcg_temp_new_i32();
+	    tcg_gen_add_i32(addr, REG(B7_4), REG(0));
+            if (ctx->flags & FPSCR_SZ) {
+		int fr = XREG(B11_8);
+                tcg_gen_qemu_ld_i32(cpu_fregs[fr], addr,
+                                    ctx->memidx, MO_TEUL);
+		tcg_gen_addi_i32(addr, addr, 4);
+                tcg_gen_qemu_ld_i32(cpu_fregs[fr+1], addr,
+                                    ctx->memidx, MO_TEUL);
+	    } else {
+                tcg_gen_qemu_ld_i32(cpu_fregs[FREG(B11_8)], addr,
+                                    ctx->memidx, MO_TEUL);
+	    }
+	    tcg_temp_free(addr);
+	}
+	return;
+    case 0xf007: /* fmov {F,D,X}Rn,@(R0,Rn) - FPSCR: Nothing */
+	CHECK_FPU_ENABLED
+	{
+	    TCGv addr = tcg_temp_new();
+	    tcg_gen_add_i32(addr, REG(B11_8), REG(0));
+            if (ctx->flags & FPSCR_SZ) {
+		int fr = XREG(B7_4);
+                tcg_gen_qemu_ld_i32(cpu_fregs[fr], addr,
+                                    ctx->memidx, MO_TEUL);
+		tcg_gen_addi_i32(addr, addr, 4);
+                tcg_gen_qemu_ld_i32(cpu_fregs[fr+1], addr,
+                                    ctx->memidx, MO_TEUL);
+	    } else {
+                tcg_gen_qemu_st_i32(cpu_fregs[FREG(B7_4)], addr,
+                                    ctx->memidx, MO_TEUL);
+	    }
+	    tcg_temp_free(addr);
+	}
+	return;
+    case 0xf000: /* fadd Rm,Rn - FPSCR: R[PR,Enable.O/U/I]/W[Cause,Flag] */
+    case 0xf001: /* fsub Rm,Rn - FPSCR: R[PR,Enable.O/U/I]/W[Cause,Flag] */
+    case 0xf002: /* fmul Rm,Rn - FPSCR: R[PR,Enable.O/U/I]/W[Cause,Flag] */
+    case 0xf003: /* fdiv Rm,Rn - FPSCR: R[PR,Enable.O/U/I]/W[Cause,Flag] */
+    case 0xf004: /* fcmp/eq Rm,Rn - FPSCR: R[PR,Enable.V]/W[Cause,Flag] */
+    case 0xf005: /* fcmp/gt Rm,Rn - FPSCR: R[PR,Enable.V]/W[Cause,Flag] */
+	{
+	    CHECK_FPU_ENABLED
+            if (ctx->flags & FPSCR_PR) {
+                TCGv_i64 fp0, fp1;
+
+		if (ctx->opcode & 0x0110)
+		    break; /* illegal instruction */
+		fp0 = tcg_temp_new_i64();
+		fp1 = tcg_temp_new_i64();
+		gen_load_fpr64(fp0, DREG(B11_8));
+		gen_load_fpr64(fp1, DREG(B7_4));
+                switch (ctx->opcode & 0xf00f) {
+                case 0xf000:		/* fadd Rm,Rn */
+                    gen_helper_fadd_DT(fp0, cpu_env, fp0, fp1);
+                    break;
+                case 0xf001:		/* fsub Rm,Rn */
+                    gen_helper_fsub_DT(fp0, cpu_env, fp0, fp1);
+                    break;
+                case 0xf002:		/* fmul Rm,Rn */
+                    gen_helper_fmul_DT(fp0, cpu_env, fp0, fp1);
+                    break;
+                case 0xf003:		/* fdiv Rm,Rn */
+                    gen_helper_fdiv_DT(fp0, cpu_env, fp0, fp1);
+                    break;
+                case 0xf004:		/* fcmp/eq Rm,Rn */
+                    gen_helper_fcmp_eq_DT(cpu_env, fp0, fp1);
+                    return;
+                case 0xf005:		/* fcmp/gt Rm,Rn */
+                    gen_helper_fcmp_gt_DT(cpu_env, fp0, fp1);
+                    return;
+                }
+		gen_store_fpr64(fp0, DREG(B11_8));
+                tcg_temp_free_i64(fp0);
+                tcg_temp_free_i64(fp1);
+	    } else {
+                switch (ctx->opcode & 0xf00f) {
+                case 0xf000:		/* fadd Rm,Rn */
+                    gen_helper_fadd_FT(cpu_fregs[FREG(B11_8)], cpu_env,
+                                       cpu_fregs[FREG(B11_8)],
+                                       cpu_fregs[FREG(B7_4)]);
+                    break;
+                case 0xf001:		/* fsub Rm,Rn */
+                    gen_helper_fsub_FT(cpu_fregs[FREG(B11_8)], cpu_env,
+                                       cpu_fregs[FREG(B11_8)],
+                                       cpu_fregs[FREG(B7_4)]);
+                    break;
+                case 0xf002:		/* fmul Rm,Rn */
+                    gen_helper_fmul_FT(cpu_fregs[FREG(B11_8)], cpu_env,
+                                       cpu_fregs[FREG(B11_8)],
+                                       cpu_fregs[FREG(B7_4)]);
+                    break;
+                case 0xf003:		/* fdiv Rm,Rn */
+                    gen_helper_fdiv_FT(cpu_fregs[FREG(B11_8)], cpu_env,
+                                       cpu_fregs[FREG(B11_8)],
+                                       cpu_fregs[FREG(B7_4)]);
+                    break;
+                case 0xf004:		/* fcmp/eq Rm,Rn */
+                    gen_helper_fcmp_eq_FT(cpu_env, cpu_fregs[FREG(B11_8)],
+                                          cpu_fregs[FREG(B7_4)]);
+                    return;
+                case 0xf005:		/* fcmp/gt Rm,Rn */
+                    gen_helper_fcmp_gt_FT(cpu_env, cpu_fregs[FREG(B11_8)],
+                                          cpu_fregs[FREG(B7_4)]);
+                    return;
+                }
+	    }
+	}
+	return;
+    case 0xf00e: /* fmac FR0,RM,Rn */
+        {
+            CHECK_FPU_ENABLED
+            if (ctx->flags & FPSCR_PR) {
+                break; /* illegal instruction */
+            } else {
+                gen_helper_fmac_FT(cpu_fregs[FREG(B11_8)], cpu_env,
+                                   cpu_fregs[FREG(0)], cpu_fregs[FREG(B7_4)],
+                                   cpu_fregs[FREG(B11_8)]);
+                return;
+            }
+        }
+    }
+
+    switch (ctx->opcode & 0xff00) {
+    case 0xc900:		/* and #imm,R0 */
+	tcg_gen_andi_i32(REG(0), REG(0), B7_0);
+	return;
+    case 0xcd00:		/* and.b #imm,@(R0,GBR) */
+	{
+	    TCGv addr, val;
+	    addr = tcg_temp_new();
+	    tcg_gen_add_i32(addr, REG(0), cpu_gbr);
+	    val = tcg_temp_new();
+            tcg_gen_qemu_ld_i32(val, addr, ctx->memidx, MO_UB);
+	    tcg_gen_andi_i32(val, val, B7_0);
+            tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_UB);
+	    tcg_temp_free(val);
+	    tcg_temp_free(addr);
+	}
+	return;
+    case 0x8b00:		/* bf label */
+	CHECK_NOT_DELAY_SLOT
+	    gen_conditional_jump(ctx, ctx->pc + 2,
+				 ctx->pc + 4 + B7_0s * 2);
+	ctx->bstate = BS_BRANCH;
+	return;
+    case 0x8f00:		/* bf/s label */
+	CHECK_NOT_DELAY_SLOT
+	gen_branch_slot(ctx->delayed_pc = ctx->pc + 4 + B7_0s * 2, 0);
+	ctx->flags |= DELAY_SLOT_CONDITIONAL;
+	return;
+    case 0x8900:		/* bt label */
+	CHECK_NOT_DELAY_SLOT
+	    gen_conditional_jump(ctx, ctx->pc + 4 + B7_0s * 2,
+				 ctx->pc + 2);
+	ctx->bstate = BS_BRANCH;
+	return;
+    case 0x8d00:		/* bt/s label */
+	CHECK_NOT_DELAY_SLOT
+	gen_branch_slot(ctx->delayed_pc = ctx->pc + 4 + B7_0s * 2, 1);
+	ctx->flags |= DELAY_SLOT_CONDITIONAL;
+	return;
+    case 0x8800:		/* cmp/eq #imm,R0 */
+        tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, REG(0), B7_0s);
+	return;
+    case 0xc400:		/* mov.b @(disp,GBR),R0 */
+	{
+	    TCGv addr = tcg_temp_new();
+	    tcg_gen_addi_i32(addr, cpu_gbr, B7_0);
+            tcg_gen_qemu_ld_i32(REG(0), addr, ctx->memidx, MO_SB);
+	    tcg_temp_free(addr);
+	}
+	return;
+    case 0xc500:		/* mov.w @(disp,GBR),R0 */
+	{
+	    TCGv addr = tcg_temp_new();
+	    tcg_gen_addi_i32(addr, cpu_gbr, B7_0 * 2);
+            tcg_gen_qemu_ld_i32(REG(0), addr, ctx->memidx, MO_TESW);
+	    tcg_temp_free(addr);
+	}
+	return;
+    case 0xc600:		/* mov.l @(disp,GBR),R0 */
+	{
+	    TCGv addr = tcg_temp_new();
+	    tcg_gen_addi_i32(addr, cpu_gbr, B7_0 * 4);
+            tcg_gen_qemu_ld_i32(REG(0), addr, ctx->memidx, MO_TESL);
+	    tcg_temp_free(addr);
+	}
+	return;
+    case 0xc000:		/* mov.b R0,@(disp,GBR) */
+	{
+	    TCGv addr = tcg_temp_new();
+	    tcg_gen_addi_i32(addr, cpu_gbr, B7_0);
+            tcg_gen_qemu_st_i32(REG(0), addr, ctx->memidx, MO_UB);
+	    tcg_temp_free(addr);
+	}
+	return;
+    case 0xc100:		/* mov.w R0,@(disp,GBR) */
+	{
+	    TCGv addr = tcg_temp_new();
+	    tcg_gen_addi_i32(addr, cpu_gbr, B7_0 * 2);
+            tcg_gen_qemu_st_i32(REG(0), addr, ctx->memidx, MO_TEUW);
+	    tcg_temp_free(addr);
+	}
+	return;
+    case 0xc200:		/* mov.l R0,@(disp,GBR) */
+	{
+	    TCGv addr = tcg_temp_new();
+	    tcg_gen_addi_i32(addr, cpu_gbr, B7_0 * 4);
+            tcg_gen_qemu_st_i32(REG(0), addr, ctx->memidx, MO_TEUL);
+	    tcg_temp_free(addr);
+	}
+	return;
+    case 0x8000:		/* mov.b R0,@(disp,Rn) */
+	{
+	    TCGv addr = tcg_temp_new();
+	    tcg_gen_addi_i32(addr, REG(B7_4), B3_0);
+            tcg_gen_qemu_st_i32(REG(0), addr, ctx->memidx, MO_UB);
+	    tcg_temp_free(addr);
+	}
+	return;
+    case 0x8100:		/* mov.w R0,@(disp,Rn) */
+	{
+	    TCGv addr = tcg_temp_new();
+	    tcg_gen_addi_i32(addr, REG(B7_4), B3_0 * 2);
+            tcg_gen_qemu_st_i32(REG(0), addr, ctx->memidx, MO_TEUW);
+	    tcg_temp_free(addr);
+	}
+	return;
+    case 0x8400:		/* mov.b @(disp,Rn),R0 */
+	{
+	    TCGv addr = tcg_temp_new();
+	    tcg_gen_addi_i32(addr, REG(B7_4), B3_0);
+            tcg_gen_qemu_ld_i32(REG(0), addr, ctx->memidx, MO_SB);
+	    tcg_temp_free(addr);
+	}
+	return;
+    case 0x8500:		/* mov.w @(disp,Rn),R0 */
+	{
+	    TCGv addr = tcg_temp_new();
+	    tcg_gen_addi_i32(addr, REG(B7_4), B3_0 * 2);
+            tcg_gen_qemu_ld_i32(REG(0), addr, ctx->memidx, MO_TESW);
+	    tcg_temp_free(addr);
+	}
+	return;
+    case 0xc700:		/* mova @(disp,PC),R0 */
+	tcg_gen_movi_i32(REG(0), ((ctx->pc & 0xfffffffc) + 4 + B7_0 * 4) & ~3);
+	return;
+    case 0xcb00:		/* or #imm,R0 */
+	tcg_gen_ori_i32(REG(0), REG(0), B7_0);
+	return;
+    case 0xcf00:		/* or.b #imm,@(R0,GBR) */
+	{
+	    TCGv addr, val;
+	    addr = tcg_temp_new();
+	    tcg_gen_add_i32(addr, REG(0), cpu_gbr);
+	    val = tcg_temp_new();
+            tcg_gen_qemu_ld_i32(val, addr, ctx->memidx, MO_UB);
+	    tcg_gen_ori_i32(val, val, B7_0);
+            tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_UB);
+	    tcg_temp_free(val);
+	    tcg_temp_free(addr);
+	}
+	return;
+    case 0xc300:		/* trapa #imm */
+	{
+	    TCGv imm;
+	    CHECK_NOT_DELAY_SLOT
+            tcg_gen_movi_i32(cpu_pc, ctx->pc);
+	    imm = tcg_const_i32(B7_0);
+            gen_helper_trapa(cpu_env, imm);
+	    tcg_temp_free(imm);
+	    ctx->bstate = BS_BRANCH;
+	}
+	return;
+    case 0xc800:		/* tst #imm,R0 */
+	{
+	    TCGv val = tcg_temp_new();
+	    tcg_gen_andi_i32(val, REG(0), B7_0);
+            tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, val, 0);
+	    tcg_temp_free(val);
+	}
+	return;
+    case 0xcc00:		/* tst.b #imm,@(R0,GBR) */
+	{
+	    TCGv val = tcg_temp_new();
+	    tcg_gen_add_i32(val, REG(0), cpu_gbr);
+            tcg_gen_qemu_ld_i32(val, val, ctx->memidx, MO_UB);
+	    tcg_gen_andi_i32(val, val, B7_0);
+            tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, val, 0);
+	    tcg_temp_free(val);
+	}
+	return;
+    case 0xca00:		/* xor #imm,R0 */
+	tcg_gen_xori_i32(REG(0), REG(0), B7_0);
+	return;
+    case 0xce00:		/* xor.b #imm,@(R0,GBR) */
+	{
+	    TCGv addr, val;
+	    addr = tcg_temp_new();
+	    tcg_gen_add_i32(addr, REG(0), cpu_gbr);
+	    val = tcg_temp_new();
+            tcg_gen_qemu_ld_i32(val, addr, ctx->memidx, MO_UB);
+	    tcg_gen_xori_i32(val, val, B7_0);
+            tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_UB);
+	    tcg_temp_free(val);
+	    tcg_temp_free(addr);
+	}
+	return;
+    }
+
+    switch (ctx->opcode & 0xf08f) {
+    case 0x408e:		/* ldc Rm,Rn_BANK */
+	CHECK_PRIVILEGED
+	tcg_gen_mov_i32(ALTREG(B6_4), REG(B11_8));
+	return;
+    case 0x4087:		/* ldc.l @Rm+,Rn_BANK */
+	CHECK_PRIVILEGED
+        tcg_gen_qemu_ld_i32(ALTREG(B6_4), REG(B11_8), ctx->memidx, MO_TESL);
+	tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4);
+	return;
+    case 0x0082:		/* stc Rm_BANK,Rn */
+	CHECK_PRIVILEGED
+	tcg_gen_mov_i32(REG(B11_8), ALTREG(B6_4));
+	return;
+    case 0x4083:		/* stc.l Rm_BANK,@-Rn */
+	CHECK_PRIVILEGED
+	{
+	    TCGv addr = tcg_temp_new();
+	    tcg_gen_subi_i32(addr, REG(B11_8), 4);
+            tcg_gen_qemu_st_i32(ALTREG(B6_4), addr, ctx->memidx, MO_TEUL);
+	    tcg_gen_mov_i32(REG(B11_8), addr);
+	    tcg_temp_free(addr);
+	}
+	return;
+    }
+
+    switch (ctx->opcode & 0xf0ff) {
+    case 0x0023:		/* braf Rn */
+	CHECK_NOT_DELAY_SLOT
+	tcg_gen_addi_i32(cpu_delayed_pc, REG(B11_8), ctx->pc + 4);
+	ctx->flags |= DELAY_SLOT;
+	ctx->delayed_pc = (uint32_t) - 1;
+	return;
+    case 0x0003:		/* bsrf Rn */
+	CHECK_NOT_DELAY_SLOT
+	tcg_gen_movi_i32(cpu_pr, ctx->pc + 4);
+	tcg_gen_add_i32(cpu_delayed_pc, REG(B11_8), cpu_pr);
+	ctx->flags |= DELAY_SLOT;
+	ctx->delayed_pc = (uint32_t) - 1;
+	return;
+    case 0x4015:		/* cmp/pl Rn */
+        tcg_gen_setcondi_i32(TCG_COND_GT, cpu_sr_t, REG(B11_8), 0);
+	return;
+    case 0x4011:		/* cmp/pz Rn */
+        tcg_gen_setcondi_i32(TCG_COND_GE, cpu_sr_t, REG(B11_8), 0);
+	return;
+    case 0x4010:		/* dt Rn */
+	tcg_gen_subi_i32(REG(B11_8), REG(B11_8), 1);
+        tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, REG(B11_8), 0);
+	return;
+    case 0x402b:		/* jmp @Rn */
+	CHECK_NOT_DELAY_SLOT
+	tcg_gen_mov_i32(cpu_delayed_pc, REG(B11_8));
+	ctx->flags |= DELAY_SLOT;
+	ctx->delayed_pc = (uint32_t) - 1;
+	return;
+    case 0x400b:		/* jsr @Rn */
+	CHECK_NOT_DELAY_SLOT
+	tcg_gen_movi_i32(cpu_pr, ctx->pc + 4);
+	tcg_gen_mov_i32(cpu_delayed_pc, REG(B11_8));
+	ctx->flags |= DELAY_SLOT;
+	ctx->delayed_pc = (uint32_t) - 1;
+	return;
+    case 0x400e:		/* ldc Rm,SR */
+	CHECK_PRIVILEGED
+        {
+            TCGv val = tcg_temp_new();
+            tcg_gen_andi_i32(val, REG(B11_8), 0x700083f3);
+            gen_write_sr(val);
+            tcg_temp_free(val);
+            ctx->bstate = BS_STOP;
+        }
+	return;
+    case 0x4007:		/* ldc.l @Rm+,SR */
+	CHECK_PRIVILEGED
+	{
+	    TCGv val = tcg_temp_new();
+            tcg_gen_qemu_ld_i32(val, REG(B11_8), ctx->memidx, MO_TESL);
+            tcg_gen_andi_i32(val, val, 0x700083f3);
+            gen_write_sr(val);
+	    tcg_temp_free(val);
+	    tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4);
+	    ctx->bstate = BS_STOP;
+	}
+	return;
+    case 0x0002:		/* stc SR,Rn */
+	CHECK_PRIVILEGED
+        gen_read_sr(REG(B11_8));
+	return;
+    case 0x4003:		/* stc SR,@-Rn */
+	CHECK_PRIVILEGED
+	{
+	    TCGv addr = tcg_temp_new();
+            TCGv val = tcg_temp_new();
+	    tcg_gen_subi_i32(addr, REG(B11_8), 4);
+            gen_read_sr(val);
+            tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_TEUL);
+	    tcg_gen_mov_i32(REG(B11_8), addr);
+            tcg_temp_free(val);
+	    tcg_temp_free(addr);
+	}
+	return;
+#define LD(reg,ldnum,ldpnum,prechk)		\
+  case ldnum:							\
+    prechk    							\
+    tcg_gen_mov_i32 (cpu_##reg, REG(B11_8));			\
+    return;							\
+  case ldpnum:							\
+    prechk    							\
+    tcg_gen_qemu_ld_i32(cpu_##reg, REG(B11_8), ctx->memidx, MO_TESL); \
+    tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4);		\
+    return;
+#define ST(reg,stnum,stpnum,prechk)		\
+  case stnum:							\
+    prechk    							\
+    tcg_gen_mov_i32 (REG(B11_8), cpu_##reg);			\
+    return;							\
+  case stpnum:							\
+    prechk    							\
+    {								\
+	TCGv addr = tcg_temp_new();				\
+	tcg_gen_subi_i32(addr, REG(B11_8), 4);			\
+        tcg_gen_qemu_st_i32(cpu_##reg, addr, ctx->memidx, MO_TEUL); \
+	tcg_gen_mov_i32(REG(B11_8), addr);			\
+	tcg_temp_free(addr);					\
+    }								\
+    return;
+#define LDST(reg,ldnum,ldpnum,stnum,stpnum,prechk)		\
+	LD(reg,ldnum,ldpnum,prechk)				\
+	ST(reg,stnum,stpnum,prechk)
+	LDST(gbr,  0x401e, 0x4017, 0x0012, 0x4013, {})
+	LDST(vbr,  0x402e, 0x4027, 0x0022, 0x4023, CHECK_PRIVILEGED)
+	LDST(ssr,  0x403e, 0x4037, 0x0032, 0x4033, CHECK_PRIVILEGED)
+	LDST(spc,  0x404e, 0x4047, 0x0042, 0x4043, CHECK_PRIVILEGED)
+	ST(sgr,  0x003a, 0x4032, CHECK_PRIVILEGED)
+	LD(sgr,  0x403a, 0x4036, CHECK_PRIVILEGED if (!(ctx->features & SH_FEATURE_SH4A)) break;)
+	LDST(dbr,  0x40fa, 0x40f6, 0x00fa, 0x40f2, CHECK_PRIVILEGED)
+	LDST(mach, 0x400a, 0x4006, 0x000a, 0x4002, {})
+	LDST(macl, 0x401a, 0x4016, 0x001a, 0x4012, {})
+	LDST(pr,   0x402a, 0x4026, 0x002a, 0x4022, {})
+	LDST(fpul, 0x405a, 0x4056, 0x005a, 0x4052, {CHECK_FPU_ENABLED})
+    case 0x406a:		/* lds Rm,FPSCR */
+	CHECK_FPU_ENABLED
+        gen_helper_ld_fpscr(cpu_env, REG(B11_8));
+	ctx->bstate = BS_STOP;
+	return;
+    case 0x4066:		/* lds.l @Rm+,FPSCR */
+	CHECK_FPU_ENABLED
+	{
+	    TCGv addr = tcg_temp_new();
+            tcg_gen_qemu_ld_i32(addr, REG(B11_8), ctx->memidx, MO_TESL);
+	    tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4);
+            gen_helper_ld_fpscr(cpu_env, addr);
+	    tcg_temp_free(addr);
+	    ctx->bstate = BS_STOP;
+	}
+	return;
+    case 0x006a:		/* sts FPSCR,Rn */
+	CHECK_FPU_ENABLED
+	tcg_gen_andi_i32(REG(B11_8), cpu_fpscr, 0x003fffff);
+	return;
+    case 0x4062:		/* sts FPSCR,@-Rn */
+	CHECK_FPU_ENABLED
+	{
+	    TCGv addr, val;
+	    val = tcg_temp_new();
+	    tcg_gen_andi_i32(val, cpu_fpscr, 0x003fffff);
+	    addr = tcg_temp_new();
+	    tcg_gen_subi_i32(addr, REG(B11_8), 4);
+            tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_TEUL);
+	    tcg_gen_mov_i32(REG(B11_8), addr);
+	    tcg_temp_free(addr);
+	    tcg_temp_free(val);
+	}
+	return;
+    case 0x00c3:		/* movca.l R0,@Rm */
+        {
+            TCGv val = tcg_temp_new();
+            tcg_gen_qemu_ld_i32(val, REG(B11_8), ctx->memidx, MO_TEUL);
+            gen_helper_movcal(cpu_env, REG(B11_8), val);
+            tcg_gen_qemu_st_i32(REG(0), REG(B11_8), ctx->memidx, MO_TEUL);
+        }
+        ctx->has_movcal = 1;
+	return;
+    case 0x40a9:
+	/* MOVUA.L @Rm,R0 (Rm) -> R0
+	   Load non-boundary-aligned data */
+        tcg_gen_qemu_ld_i32(REG(0), REG(B11_8), ctx->memidx, MO_TEUL);
+	return;
+    case 0x40e9:
+	/* MOVUA.L @Rm+,R0   (Rm) -> R0, Rm + 4 -> Rm
+	   Load non-boundary-aligned data */
+        tcg_gen_qemu_ld_i32(REG(0), REG(B11_8), ctx->memidx, MO_TEUL);
+	tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4);
+	return;
+    case 0x0029:		/* movt Rn */
+        tcg_gen_mov_i32(REG(B11_8), cpu_sr_t);
+	return;
+    case 0x0073:
+        /* MOVCO.L
+	       LDST -> T
+               If (T == 1) R0 -> (Rn)
+               0 -> LDST
+        */
+        if (ctx->features & SH_FEATURE_SH4A) {
+            TCGLabel *label = gen_new_label();
+            tcg_gen_mov_i32(cpu_sr_t, cpu_ldst);
+	    tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_ldst, 0, label);
+            tcg_gen_qemu_st_i32(REG(0), REG(B11_8), ctx->memidx, MO_TEUL);
+	    gen_set_label(label);
+	    tcg_gen_movi_i32(cpu_ldst, 0);
+	    return;
+	} else
+	    break;
+    case 0x0063:
+        /* MOVLI.L @Rm,R0
+               1 -> LDST
+               (Rm) -> R0
+               When interrupt/exception
+               occurred 0 -> LDST
+        */
+	if (ctx->features & SH_FEATURE_SH4A) {
+	    tcg_gen_movi_i32(cpu_ldst, 0);
+            tcg_gen_qemu_ld_i32(REG(0), REG(B11_8), ctx->memidx, MO_TESL);
+	    tcg_gen_movi_i32(cpu_ldst, 1);
+	    return;
+	} else
+	    break;
+    case 0x0093:		/* ocbi @Rn */
+	{
+            gen_helper_ocbi(cpu_env, REG(B11_8));
+	}
+	return;
+    case 0x00a3:		/* ocbp @Rn */
+    case 0x00b3:		/* ocbwb @Rn */
+        /* These instructions are supposed to do nothing in case of
+           a cache miss. Given that we only partially emulate caches
+           it is safe to simply ignore them. */
+	return;
+    case 0x0083:		/* pref @Rn */
+	return;
+    case 0x00d3:		/* prefi @Rn */
+	if (ctx->features & SH_FEATURE_SH4A)
+	    return;
+	else
+	    break;
+    case 0x00e3:		/* icbi @Rn */
+	if (ctx->features & SH_FEATURE_SH4A)
+	    return;
+	else
+	    break;
+    case 0x00ab:		/* synco */
+	if (ctx->features & SH_FEATURE_SH4A)
+	    return;
+	else
+	    break;
+    case 0x4024:		/* rotcl Rn */
+	{
+	    TCGv tmp = tcg_temp_new();
+            tcg_gen_mov_i32(tmp, cpu_sr_t);
+            tcg_gen_shri_i32(cpu_sr_t, REG(B11_8), 31);
+	    tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 1);
+            tcg_gen_or_i32(REG(B11_8), REG(B11_8), tmp);
+	    tcg_temp_free(tmp);
+	}
+	return;
+    case 0x4025:		/* rotcr Rn */
+	{
+	    TCGv tmp = tcg_temp_new();
+            tcg_gen_shli_i32(tmp, cpu_sr_t, 31);
+            tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 1);
+	    tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 1);
+            tcg_gen_or_i32(REG(B11_8), REG(B11_8), tmp);
+	    tcg_temp_free(tmp);
+	}
+	return;
+    case 0x4004:		/* rotl Rn */
+	tcg_gen_rotli_i32(REG(B11_8), REG(B11_8), 1);
+        tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 0);
+	return;
+    case 0x4005:		/* rotr Rn */
+        tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 0);
+	tcg_gen_rotri_i32(REG(B11_8), REG(B11_8), 1);
+	return;
+    case 0x4000:		/* shll Rn */
+    case 0x4020:		/* shal Rn */
+        tcg_gen_shri_i32(cpu_sr_t, REG(B11_8), 31);
+	tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 1);
+	return;
+    case 0x4021:		/* shar Rn */
+        tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 1);
+	tcg_gen_sari_i32(REG(B11_8), REG(B11_8), 1);
+	return;
+    case 0x4001:		/* shlr Rn */
+        tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 1);
+	tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 1);
+	return;
+    case 0x4008:		/* shll2 Rn */
+	tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 2);
+	return;
+    case 0x4018:		/* shll8 Rn */
+	tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 8);
+	return;
+    case 0x4028:		/* shll16 Rn */
+	tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 16);
+	return;
+    case 0x4009:		/* shlr2 Rn */
+	tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 2);
+	return;
+    case 0x4019:		/* shlr8 Rn */
+	tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 8);
+	return;
+    case 0x4029:		/* shlr16 Rn */
+	tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 16);
+	return;
+    case 0x401b:		/* tas.b @Rn */
+	{
+	    TCGv addr, val;
+	    addr = tcg_temp_local_new();
+	    tcg_gen_mov_i32(addr, REG(B11_8));
+	    val = tcg_temp_local_new();
+            tcg_gen_qemu_ld_i32(val, addr, ctx->memidx, MO_UB);
+            tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, val, 0);
+	    tcg_gen_ori_i32(val, val, 0x80);
+            tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_UB);
+	    tcg_temp_free(val);
+	    tcg_temp_free(addr);
+	}
+	return;
+    case 0xf00d: /* fsts FPUL,FRn - FPSCR: Nothing */
+	CHECK_FPU_ENABLED
+	tcg_gen_mov_i32(cpu_fregs[FREG(B11_8)], cpu_fpul);
+	return;
+    case 0xf01d: /* flds FRm,FPUL - FPSCR: Nothing */
+	CHECK_FPU_ENABLED
+	tcg_gen_mov_i32(cpu_fpul, cpu_fregs[FREG(B11_8)]);
+	return;
+    case 0xf02d: /* float FPUL,FRn/DRn - FPSCR: R[PR,Enable.I]/W[Cause,Flag] */
+	CHECK_FPU_ENABLED
+        if (ctx->flags & FPSCR_PR) {
+	    TCGv_i64 fp;
+	    if (ctx->opcode & 0x0100)
+		break; /* illegal instruction */
+	    fp = tcg_temp_new_i64();
+            gen_helper_float_DT(fp, cpu_env, cpu_fpul);
+	    gen_store_fpr64(fp, DREG(B11_8));
+	    tcg_temp_free_i64(fp);
+	}
+	else {
+            gen_helper_float_FT(cpu_fregs[FREG(B11_8)], cpu_env, cpu_fpul);
+	}
+	return;
+    case 0xf03d: /* ftrc FRm/DRm,FPUL - FPSCR: R[PR,Enable.V]/W[Cause,Flag] */
+	CHECK_FPU_ENABLED
+        if (ctx->flags & FPSCR_PR) {
+	    TCGv_i64 fp;
+	    if (ctx->opcode & 0x0100)
+		break; /* illegal instruction */
+	    fp = tcg_temp_new_i64();
+	    gen_load_fpr64(fp, DREG(B11_8));
+            gen_helper_ftrc_DT(cpu_fpul, cpu_env, fp);
+	    tcg_temp_free_i64(fp);
+	}
+	else {
+            gen_helper_ftrc_FT(cpu_fpul, cpu_env, cpu_fregs[FREG(B11_8)]);
+	}
+	return;
+    case 0xf04d: /* fneg FRn/DRn - FPSCR: Nothing */
+	CHECK_FPU_ENABLED
+	{
+	    gen_helper_fneg_T(cpu_fregs[FREG(B11_8)], cpu_fregs[FREG(B11_8)]);
+	}
+	return;
+    case 0xf05d: /* fabs FRn/DRn */
+	CHECK_FPU_ENABLED
+        if (ctx->flags & FPSCR_PR) {
+	    if (ctx->opcode & 0x0100)
+		break; /* illegal instruction */
+	    TCGv_i64 fp = tcg_temp_new_i64();
+	    gen_load_fpr64(fp, DREG(B11_8));
+	    gen_helper_fabs_DT(fp, fp);
+	    gen_store_fpr64(fp, DREG(B11_8));
+	    tcg_temp_free_i64(fp);
+	} else {
+	    gen_helper_fabs_FT(cpu_fregs[FREG(B11_8)], cpu_fregs[FREG(B11_8)]);
+	}
+	return;
+    case 0xf06d: /* fsqrt FRn */
+	CHECK_FPU_ENABLED
+        if (ctx->flags & FPSCR_PR) {
+	    if (ctx->opcode & 0x0100)
+		break; /* illegal instruction */
+	    TCGv_i64 fp = tcg_temp_new_i64();
+	    gen_load_fpr64(fp, DREG(B11_8));
+            gen_helper_fsqrt_DT(fp, cpu_env, fp);
+	    gen_store_fpr64(fp, DREG(B11_8));
+	    tcg_temp_free_i64(fp);
+	} else {
+            gen_helper_fsqrt_FT(cpu_fregs[FREG(B11_8)], cpu_env,
+                                cpu_fregs[FREG(B11_8)]);
+	}
+	return;
+    case 0xf07d: /* fsrra FRn */
+	CHECK_FPU_ENABLED
+	break;
+    case 0xf08d: /* fldi0 FRn - FPSCR: R[PR] */
+	CHECK_FPU_ENABLED
+        if (!(ctx->flags & FPSCR_PR)) {
+	    tcg_gen_movi_i32(cpu_fregs[FREG(B11_8)], 0);
+	}
+	return;
+    case 0xf09d: /* fldi1 FRn - FPSCR: R[PR] */
+	CHECK_FPU_ENABLED
+        if (!(ctx->flags & FPSCR_PR)) {
+	    tcg_gen_movi_i32(cpu_fregs[FREG(B11_8)], 0x3f800000);
+	}
+	return;
+    case 0xf0ad: /* fcnvsd FPUL,DRn */
+	CHECK_FPU_ENABLED
+	{
+	    TCGv_i64 fp = tcg_temp_new_i64();
+            gen_helper_fcnvsd_FT_DT(fp, cpu_env, cpu_fpul);
+	    gen_store_fpr64(fp, DREG(B11_8));
+	    tcg_temp_free_i64(fp);
+	}
+	return;
+    case 0xf0bd: /* fcnvds DRn,FPUL */
+	CHECK_FPU_ENABLED
+	{
+	    TCGv_i64 fp = tcg_temp_new_i64();
+	    gen_load_fpr64(fp, DREG(B11_8));
+            gen_helper_fcnvds_DT_FT(cpu_fpul, cpu_env, fp);
+	    tcg_temp_free_i64(fp);
+	}
+	return;
+    case 0xf0ed: /* fipr FVm,FVn */
+        CHECK_FPU_ENABLED
+        if ((ctx->flags & FPSCR_PR) == 0) {
+            TCGv m, n;
+            m = tcg_const_i32((ctx->opcode >> 8) & 3);
+            n = tcg_const_i32((ctx->opcode >> 10) & 3);
+            gen_helper_fipr(cpu_env, m, n);
+            tcg_temp_free(m);
+            tcg_temp_free(n);
+            return;
+        }
+        break;
+    case 0xf0fd: /* ftrv XMTRX,FVn */
+        CHECK_FPU_ENABLED
+        if ((ctx->opcode & 0x0300) == 0x0100 &&
+            (ctx->flags & FPSCR_PR) == 0) {
+            TCGv n;
+            n = tcg_const_i32((ctx->opcode >> 10) & 3);
+            gen_helper_ftrv(cpu_env, n);
+            tcg_temp_free(n);
+            return;
+        }
+        break;
+    }
+#if 0
+    fprintf(stderr, "unknown instruction 0x%04x at pc 0x%08x\n",
+	    ctx->opcode, ctx->pc);
+    fflush(stderr);
+#endif
+    tcg_gen_movi_i32(cpu_pc, ctx->pc);
+    if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) {
+        gen_helper_raise_slot_illegal_instruction(cpu_env);
+    } else {
+        gen_helper_raise_illegal_instruction(cpu_env);
+    }
+    ctx->bstate = BS_BRANCH;
+}
+
+static void decode_opc(DisasContext * ctx)
+{
+    uint32_t old_flags = ctx->flags;
+
+    _decode_opc(ctx);
+
+    if (old_flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) {
+        if (ctx->flags & DELAY_SLOT_CLEARME) {
+            gen_store_flags(0);
+        } else {
+	    /* go out of the delay slot */
+	    uint32_t new_flags = ctx->flags;
+	    new_flags &= ~(DELAY_SLOT | DELAY_SLOT_CONDITIONAL);
+	    gen_store_flags(new_flags);
+        }
+        ctx->flags = 0;
+        ctx->bstate = BS_BRANCH;
+        if (old_flags & DELAY_SLOT_CONDITIONAL) {
+	    gen_delayed_conditional_jump(ctx);
+        } else if (old_flags & DELAY_SLOT) {
+            gen_jump(ctx);
+	}
+
+    }
+
+    /* go into a delay slot */
+    if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL))
+        gen_store_flags(ctx->flags);
+}
+
+void gen_intermediate_code(CPUSH4State * env, struct TranslationBlock *tb)
+{
+    SuperHCPU *cpu = sh_env_get_cpu(env);
+    CPUState *cs = CPU(cpu);
+    DisasContext ctx;
+    target_ulong pc_start;
+    int num_insns;
+    int max_insns;
+
+    pc_start = tb->pc;
+    ctx.pc = pc_start;
+    ctx.flags = (uint32_t)tb->flags;
+    ctx.bstate = BS_NONE;
+    ctx.memidx = (ctx.flags & (1u << SR_MD)) == 0 ? 1 : 0;
+    /* We don't know if the delayed pc came from a dynamic or static branch,
+       so assume it is a dynamic branch.  */
+    ctx.delayed_pc = -1; /* use delayed pc from env pointer */
+    ctx.tb = tb;
+    ctx.singlestep_enabled = cs->singlestep_enabled;
+    ctx.features = env->features;
+    ctx.has_movcal = (ctx.flags & TB_FLAG_PENDING_MOVCA);
+
+    num_insns = 0;
+    max_insns = tb->cflags & CF_COUNT_MASK;
+    if (max_insns == 0) {
+        max_insns = CF_COUNT_MASK;
+    }
+    if (max_insns > TCG_MAX_INSNS) {
+        max_insns = TCG_MAX_INSNS;
+    }
+
+    gen_tb_start(tb);
+    while (ctx.bstate == BS_NONE && !tcg_op_buf_full()) {
+        tcg_gen_insn_start(ctx.pc, ctx.flags);
+        num_insns++;
+
+        if (unlikely(cpu_breakpoint_test(cs, ctx.pc, BP_ANY))) {
+            /* We have hit a breakpoint - make sure PC is up-to-date */
+            tcg_gen_movi_i32(cpu_pc, ctx.pc);
+            gen_helper_debug(cpu_env);
+            ctx.bstate = BS_BRANCH;
+            /* The address covered by the breakpoint must be included in
+               [tb->pc, tb->pc + tb->size) in order to for it to be
+               properly cleared -- thus we increment the PC here so that
+               the logic setting tb->size below does the right thing.  */
+            ctx.pc += 2;
+            break;
+        }
+
+        if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) {
+            gen_io_start();
+        }
+
+        ctx.opcode = cpu_lduw_code(env, ctx.pc);
+	decode_opc(&ctx);
+	ctx.pc += 2;
+	if ((ctx.pc & (TARGET_PAGE_SIZE - 1)) == 0)
+	    break;
+        if (cs->singlestep_enabled) {
+	    break;
+        }
+        if (num_insns >= max_insns)
+            break;
+        if (singlestep)
+            break;
+    }
+    if (tb->cflags & CF_LAST_IO)
+        gen_io_end();
+    if (cs->singlestep_enabled) {
+        tcg_gen_movi_i32(cpu_pc, ctx.pc);
+        gen_helper_debug(cpu_env);
+    } else {
+	switch (ctx.bstate) {
+        case BS_STOP:
+            /* gen_op_interrupt_restart(); */
+            /* fall through */
+        case BS_NONE:
+            if (ctx.flags) {
+                gen_store_flags(ctx.flags | DELAY_SLOT_CLEARME);
+	    }
+            gen_goto_tb(&ctx, 0, ctx.pc);
+            break;
+        case BS_EXCP:
+            /* gen_op_interrupt_restart(); */
+            tcg_gen_exit_tb(0);
+            break;
+        case BS_BRANCH:
+        default:
+            break;
+	}
+    }
+
+    gen_tb_end(tb, num_insns);
+
+    tb->size = ctx.pc - pc_start;
+    tb->icount = num_insns;
+
+#ifdef DEBUG_DISAS
+    if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
+        && qemu_log_in_addr_range(pc_start)) {
+        qemu_log_lock();
+	qemu_log("IN:\n");	/* , lookup_symbol(pc_start)); */
+        log_target_disas(cs, pc_start, ctx.pc - pc_start, 0);
+	qemu_log("\n");
+        qemu_log_unlock();
+    }
+#endif
+}
+
+void restore_state_to_opc(CPUSH4State *env, TranslationBlock *tb,
+                          target_ulong *data)
+{
+    env->pc = data[0];
+    env->flags = data[1];
+}