Patchwork [v6,1/4] Add moxie target code

login
register
mail settings
Submitter Anthony Green
Date March 2, 2013, 12:24 p.m.
Message ID <1362227072-7084-2-git-send-email-green@moxielogic.com>
Download mbox | patch
Permalink /patch/224485/
State New
Headers show

Comments

Anthony Green - March 2, 2013, 12:24 p.m.
Signed-off-by: Anthony Green <green@moxielogic.com>
---
 target-moxie/Makefile.objs |    2 +
 target-moxie/cpu.c         |  172 ++++++++
 target-moxie/cpu.h         |  170 ++++++++
 target-moxie/helper.c      |   97 +++++
 target-moxie/helper.h      |    9 +
 target-moxie/machine.c     |   27 ++
 target-moxie/machine.h     |    2 +
 target-moxie/mmu.c         |   36 ++
 target-moxie/mmu.h         |   19 +
 target-moxie/op_helper.c   |   90 ++++
 target-moxie/translate.c   | 1003 ++++++++++++++++++++++++++++++++++++++++++++
 11 files changed, 1627 insertions(+)
 create mode 100644 target-moxie/Makefile.objs
 create mode 100644 target-moxie/cpu.c
 create mode 100644 target-moxie/cpu.h
 create mode 100644 target-moxie/helper.c
 create mode 100644 target-moxie/helper.h
 create mode 100644 target-moxie/machine.c
 create mode 100644 target-moxie/machine.h
 create mode 100644 target-moxie/mmu.c
 create mode 100644 target-moxie/mmu.h
 create mode 100644 target-moxie/op_helper.c
 create mode 100644 target-moxie/translate.c
Peter Maydell - March 2, 2013, 1:45 p.m.
On 2 March 2013 12:24, Anthony Green <green@moxielogic.com> wrote:
> +uint32_t helper_div(CPUMoxieState *env, uint32_t pc, uint32_t a, uint32_t b)
> +{
> +    if (unlikely(b == 0)) {
> +        helper_raise_exception(env, pc, MOXIE_EX_DIV0);

This never returns, so the following 'return' is unreachable.

> +        return 0;
> +    }
> +    return (int32_t)a / (int32_t)b;
> +}

You need to handle MININT / -1 too, because MININT idiv -1 is
undefined behaviour in C, and we mustn't allow the guest to
cause us to do that. (Does the Moxie architecture spec specify
the result here or does it say it's an unknown value, or does
it cause an exception, or is it genuinely unpredictable?)

Do you have a coherent rationale for what code you are putting
into op_helper.c and what into helper.c? (In most of QEMU's
existing targets the split is purely for historical reasons
since these files used to be compiled differently.)

-- PMM

Patch

diff --git a/target-moxie/Makefile.objs b/target-moxie/Makefile.objs
new file mode 100644
index 0000000..130db4c
--- /dev/null
+++ b/target-moxie/Makefile.objs
@@ -0,0 +1,2 @@ 
+obj-y += translate.o op_helper.o helper.o machine.o cpu.o machine.o
+obj-$(CONFIG_SOFTMMU) += mmu.o
diff --git a/target-moxie/cpu.c b/target-moxie/cpu.c
new file mode 100644
index 0000000..c17d3f0
--- /dev/null
+++ b/target-moxie/cpu.c
@@ -0,0 +1,172 @@ 
+/*
+ * QEMU Moxie CPU
+ *
+ * Copyright (c) 2013 Anthony Green
+ *
+ * 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 General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "cpu.h"
+#include "qemu-common.h"
+#include "migration/vmstate.h"
+#include "machine.h"
+
+static void moxie_cpu_reset(CPUState *s)
+{
+    MoxieCPU *cpu = MOXIE_CPU(s);
+    MoxieCPUClass *mcc = MOXIE_CPU_GET_CLASS(cpu);
+    CPUMoxieState *env = &cpu->env;
+
+    if (qemu_loglevel_mask(CPU_LOG_RESET)) {
+        qemu_log("CPU Reset (CPU %d)\n", s->cpu_index);
+        log_cpu_state(env, 0);
+    }
+
+    mcc->parent_reset(s);
+
+    memset(env, 0, offsetof(CPUMoxieState, breakpoints));
+    env->pc = 0x1000;
+
+    tlb_flush(env, 1);
+}
+
+static void moxie_cpu_realizefn(DeviceState *dev, Error **errp)
+{
+    MoxieCPU *cpu = MOXIE_CPU(dev);
+    MoxieCPUClass *occ = MOXIE_CPU_GET_CLASS(dev);
+
+    qemu_init_vcpu(&cpu->env);
+    cpu_reset(CPU(cpu));
+
+    occ->parent_realize(dev, errp);
+}
+
+static void moxie_cpu_initfn(Object *obj)
+{
+    CPUState *cs = CPU(obj);
+    MoxieCPU *cpu = MOXIE_CPU(obj);
+    static int inited;
+
+    cs->env_ptr = &cpu->env;
+    cpu_exec_init(&cpu->env);
+
+    if (tcg_enabled() && !inited) {
+        inited = 1;
+        moxie_translate_init();
+    }
+}
+
+static ObjectClass *moxie_cpu_class_by_name(const char *cpu_model)
+{
+    ObjectClass *oc;
+
+    if (cpu_model == NULL) {
+        return NULL;
+    }
+
+    oc = object_class_by_name(cpu_model);
+    if (oc != NULL && (!object_class_dynamic_cast(oc, TYPE_MOXIE_CPU) ||
+                       object_class_is_abstract(oc))) {
+        return NULL;
+    }
+    return oc;
+}
+
+static void moxie_cpu_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    CPUClass *cc = CPU_CLASS(oc);
+    MoxieCPUClass *mcc = MOXIE_CPU_CLASS(oc);
+
+    mcc->parent_realize = dc->realize;
+    dc->realize = moxie_cpu_realizefn;
+
+    mcc->parent_reset = cc->reset;
+    cc->reset = moxie_cpu_reset;
+
+    cc->class_by_name = moxie_cpu_class_by_name;
+
+    dc->vmsd = &vmstate_moxie_cpu;
+}
+
+static void moxielite_initfn(Object *obj)
+{
+    /* Set cpu feature flags */
+}
+
+static void moxie_any_initfn(Object *obj)
+{
+    /* Set cpu feature flags */
+}
+
+typedef struct MoxieCPUInfo {
+    const char *name;
+    void (*initfn)(Object *obj);
+} MoxieCPUInfo;
+
+static const MoxieCPUInfo moxie_cpus[] = {
+    { .name = "MoxieLite",      .initfn = moxielite_initfn },
+    { .name = "any",            .initfn = moxie_any_initfn },
+};
+
+MoxieCPU *cpu_moxie_init(const char *cpu_model)
+{
+    MoxieCPU *cpu;
+    ObjectClass *oc;
+
+    oc = moxie_cpu_class_by_name(cpu_model);
+    if (oc == NULL) {
+        return NULL;
+    }
+    cpu = MOXIE_CPU(object_new(object_class_get_name(oc)));
+    cpu->env.cpu_model_str = cpu_model;
+
+    object_property_set_bool(OBJECT(cpu), true, "realized", NULL);
+
+    return cpu;
+}
+
+static void cpu_register(const MoxieCPUInfo *info)
+{
+    TypeInfo type_info = {
+        .parent = TYPE_MOXIE_CPU,
+        .instance_size = sizeof(MoxieCPU),
+        .instance_init = info->initfn,
+        .class_size = sizeof(MoxieCPUClass),
+    };
+
+    type_info.name = g_strdup_printf("%s-" TYPE_MOXIE_CPU, info->name);
+    type_register(&type_info);
+    g_free((void *)type_info.name);
+}
+
+static const TypeInfo moxie_cpu_type_info = {
+    .name = TYPE_MOXIE_CPU,
+    .parent = TYPE_CPU,
+    .instance_size = sizeof(MoxieCPU),
+    .instance_init = moxie_cpu_initfn,
+    .class_size = sizeof(MoxieCPUClass),
+    .class_init = moxie_cpu_class_init,
+};
+
+static void moxie_cpu_register_types(void)
+{
+    int i;
+    type_register_static(&moxie_cpu_type_info);
+    for (i = 0; i < ARRAY_SIZE(moxie_cpus); i++) {
+        cpu_register(&moxie_cpus[i]);
+    }
+}
+
+type_init(moxie_cpu_register_types)
diff --git a/target-moxie/cpu.h b/target-moxie/cpu.h
new file mode 100644
index 0000000..eb7354f
--- /dev/null
+++ b/target-moxie/cpu.h
@@ -0,0 +1,170 @@ 
+/*
+ *  Moxie emulation
+ *
+ *  Copyright (c) 2008, 2010, 2013 Anthony Green
+ *
+ * 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 General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _CPU_MOXIE_H
+#define _CPU_MOXIE_H
+
+#include "config.h"
+#include "qemu-common.h"
+
+#define TARGET_LONG_BITS 32
+
+#define CPUArchState struct CPUMoxieState
+
+#define TARGET_HAS_ICE 1
+
+#define CPU_SAVE_VERSION 1
+
+#define ELF_MACHINE     0xFEED /* EM_MOXIE */
+
+#define MOXIE_EX_DIV0        0
+#define MOXIE_EX_BAD         1
+#define MOXIE_EX_IRQ         2
+#define MOXIE_EX_SWI         3
+#define MOXIE_EX_MMU_MISS    4
+#define MOXIE_EX_BREAK      16
+
+#include "exec/cpu-defs.h"
+#include "fpu/softfloat.h"
+
+#define TARGET_PAGE_BITS 12     /* 4k */
+
+#define TARGET_PHYS_ADDR_SPACE_BITS 32
+#define TARGET_VIRT_ADDR_SPACE_BITS 32
+
+#define NB_MMU_MODES 1
+
+#define CC_GT  (1<<0)
+#define CC_LT  (1<<1)
+#define CC_EQ  (1<<2)
+#define CC_GTU (1<<3)
+#define CC_LTU (1<<4)
+
+typedef struct CPUMoxieState {
+
+    uint32_t flags;               /* general execution flags */
+    uint32_t gregs[16];           /* general registers */
+    uint32_t sregs[256];          /* special registers */
+    uint32_t pc;                  /* program counter */
+    uint32_t cc;                  /* condition codes */
+
+    void *irq[8];
+
+    CPU_COMMON
+
+} CPUMoxieState;
+
+#include "qom/cpu.h"
+
+#define TYPE_MOXIE_CPU "moxie-cpu"
+
+#define MOXIE_CPU_CLASS(klass) \
+    OBJECT_CLASS_CHECK(MoxieCPUClass, (klass), TYPE_MOXIE_CPU)
+#define MOXIE_CPU(obj) \
+    OBJECT_CHECK(MoxieCPU, (obj), TYPE_MOXIE_CPU)
+#define MOXIE_CPU_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(MoxieCPUClass, (obj), TYPE_MOXIE_CPU)
+
+/**
+ * MoxieCPUClass:
+ * @parent_reset: The parent class' reset handler.
+ *
+ * A Moxie CPU model.
+ */
+typedef struct MoxieCPUClass {
+    /*< private >*/
+    CPUClass parent_class;
+    /*< public >*/
+
+    DeviceRealize parent_realize;
+    void (*parent_reset)(CPUState *cpu);
+} MoxieCPUClass;
+
+/**
+ * MoxieCPU:
+ * @env: #CPUMoxieState
+ *
+ * A Moxie CPU.
+ */
+typedef struct MoxieCPU {
+    /*< private >*/
+    CPUState parent_obj;
+    /*< public >*/
+
+    CPUMoxieState env;
+} MoxieCPU;
+
+static inline MoxieCPU *moxie_env_get_cpu(CPUMoxieState *env)
+{
+    return MOXIE_CPU(container_of(env, MoxieCPU, env));
+}
+
+#define ENV_GET_CPU(e) CPU(moxie_env_get_cpu(e))
+
+MoxieCPU *cpu_moxie_init(const char *cpu_model);
+int cpu_moxie_exec(CPUMoxieState *s);
+void do_interrupt(CPUMoxieState *env);
+void moxie_translate_init(void);
+int cpu_moxie_signal_handler(int host_signum, void *pinfo,
+                             void *puc);
+
+static inline CPUMoxieState *cpu_init(const char *cpu_model)
+{
+    MoxieCPU *cpu = cpu_moxie_init(cpu_model);
+    if (cpu == NULL) {
+        return NULL;
+    }
+    return &cpu->env;
+}
+
+#define cpu_exec cpu_moxie_exec
+#define cpu_gen_code cpu_moxie_gen_code
+#define cpu_signal_handler cpu_moxie_signal_handler
+
+static inline int cpu_mmu_index(CPUMoxieState *env)
+{
+    return 0;
+}
+
+#include "exec/cpu-all.h"
+#include "exec/exec-all.h"
+
+static inline void cpu_pc_from_tb(CPUMoxieState *env, TranslationBlock *tb)
+{
+    env->pc = tb->pc;
+}
+
+static inline void cpu_get_tb_cpu_state(CPUMoxieState *env, target_ulong *pc,
+                                        target_ulong *cs_base, int *flags)
+{
+    *pc = env->pc;
+    *cs_base = 0;
+    *flags = 0;
+}
+
+static inline int cpu_has_work(CPUState *cpu)
+{
+    CPUMoxieState *env = &MOXIE_CPU(cpu)->env;
+
+    return env->interrupt_request & CPU_INTERRUPT_HARD;
+}
+
+int cpu_moxie_handle_mmu_fault(CPUMoxieState *env, target_ulong address,
+                               int rw, int mmu_idx);
+
+#endif /* _CPU_MOXIE_H */
diff --git a/target-moxie/helper.c b/target-moxie/helper.c
new file mode 100644
index 0000000..db6deb9
--- /dev/null
+++ b/target-moxie/helper.c
@@ -0,0 +1,97 @@ 
+/*
+ *  Moxie helper routines.
+ *
+ *  Copyright (c) 2008, 2009, 2010, 2013 Anthony Green
+ *
+ * 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 General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "config.h"
+#include "cpu.h"
+#include "mmu.h"
+#include "exec/exec-all.h"
+#include "qemu/host-utils.h"
+
+#if defined(CONFIG_USER_ONLY)
+
+void do_interrupt(CPUState *env)
+{
+    env->exception_index = -1;
+}
+
+int cpu_moxie_handle_mmu_fault(CPUMoxieState *env, target_ulong address,
+                               int rw, int mmu_idx)
+{
+    env->exception_index = 0xaa;
+    env->debug1 = address;
+    cpu_dump_state(env, stderr, fprintf, 0);
+    return 1;
+}
+
+target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+    return addr;
+}
+
+#else /* !CONFIG_USER_ONLY */
+
+int cpu_moxie_handle_mmu_fault(CPUMoxieState *env, target_ulong address,
+                               int rw, int mmu_idx)
+{
+    struct moxie_mmu_result_t res;
+    int prot, miss;
+    target_ulong phy;
+    int r = 1;
+
+    address &= TARGET_PAGE_MASK;
+    prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+    miss = moxie_mmu_translate(&res, env, address, rw, mmu_idx);
+    if (miss) {
+        /* handle the miss.  */
+        phy = 0;
+        env->exception_index = MOXIE_EX_MMU_MISS;
+    } else {
+        phy = res.phy;
+        r = 0;
+    }
+    tlb_set_page(env, address, phy, prot, mmu_idx, TARGET_PAGE_SIZE);
+    return r;
+}
+
+
+void do_interrupt(CPUMoxieState *env)
+{
+    switch (env->exception_index) {
+    case MOXIE_EX_BREAK:
+        break;
+    default:
+        break;
+    }
+}
+
+hwaddr cpu_get_phys_page_debug(CPUMoxieState *env, target_ulong addr)
+{
+    uint32_t phy = addr;
+    struct moxie_mmu_result_t res;
+    int miss;
+    miss = moxie_mmu_translate(&res, env, addr, 0, 0);
+    if (!miss) {
+        phy = res.phy;
+    }
+    return phy;
+}
+#endif
diff --git a/target-moxie/helper.h b/target-moxie/helper.h
new file mode 100644
index 0000000..36ce126
--- /dev/null
+++ b/target-moxie/helper.h
@@ -0,0 +1,9 @@ 
+#include "exec/def-helper.h"
+
+DEF_HELPER_3(raise_exception, void, env, i32, int)
+DEF_HELPER_1(debug, void, env)
+DEF_HELPER_4(div, i32, env, i32, i32, i32)
+DEF_HELPER_4(udiv, i32, env, i32, i32, i32)
+
+#include "exec/def-helper.h"
+
diff --git a/target-moxie/machine.c b/target-moxie/machine.c
new file mode 100644
index 0000000..b8ed253
--- /dev/null
+++ b/target-moxie/machine.c
@@ -0,0 +1,27 @@ 
+#include "hw/hw.h"
+#include "hw/boards.h"
+
+const VMStateDescription vmstate_moxie_cpu = {
+    .name = "cpu",
+    .version_id = CPU_SAVE_VERSION,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField[]) {
+        VMSTATE_UINT32(flags, CPUMoxieState),
+        VMSTATE_UINT32_ARRAY(gregs, CPUMoxieState, 16),
+        VMSTATE_UINT32_ARRAY(sregs, CPUMoxieState, 256),
+        VMSTATE_UINT32(pc, CPUMoxieState),
+        VMSTATE_UINT32(cc, CPUMoxieState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+void cpu_save(QEMUFile *f, void *opaque)
+{
+    vmstate_save_state(f, &vmstate_moxie_cpu, opaque);
+}
+
+int cpu_load(QEMUFile *f, void *opaque, int version_id)
+{
+    return vmstate_load_state(f, &vmstate_moxie_cpu, opaque, version_id);
+}
diff --git a/target-moxie/machine.h b/target-moxie/machine.h
new file mode 100644
index 0000000..eeb1b73
--- /dev/null
+++ b/target-moxie/machine.h
@@ -0,0 +1,2 @@ 
+extern const VMStateDescription vmstate_moxie_cpu;
+
diff --git a/target-moxie/mmu.c b/target-moxie/mmu.c
new file mode 100644
index 0000000..5217eed
--- /dev/null
+++ b/target-moxie/mmu.c
@@ -0,0 +1,36 @@ 
+/*
+ *  Moxie mmu emulation.
+ *
+ *  Copyright (c) 2008, 2013 Anthony Green
+ *
+ * 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 General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "config.h"
+#include "cpu.h"
+#include "mmu.h"
+#include "exec/exec-all.h"
+
+int moxie_mmu_translate(MoxieMMUResult *res,
+                       CPUMoxieState *env, uint32_t vaddr,
+                       int rw, int mmu_idx)
+{
+    /* Perform no translation yet.  */
+    res->phy = vaddr;
+    return 0;
+}
diff --git a/target-moxie/mmu.h b/target-moxie/mmu.h
new file mode 100644
index 0000000..25a7609
--- /dev/null
+++ b/target-moxie/mmu.h
@@ -0,0 +1,19 @@ 
+#define MOXIE_MMU_ERR_EXEC  0
+#define MOXIE_MMU_ERR_READ  1
+#define MOXIE_MMU_ERR_WRITE 2
+#define MOXIE_MMU_ERR_FLUSH 3
+
+typedef struct moxie_mmu_result_t {
+    uint32_t phy;
+    uint32_t pfn;
+    int g:1;
+    int v:1;
+    int k:1;
+    int w:1;
+    int e:1;
+    int cause_op;
+} MoxieMMUResult;
+
+int moxie_mmu_translate(MoxieMMUResult *res,
+                        CPUMoxieState *env, uint32_t vaddr,
+                        int rw, int mmu_idx);
diff --git a/target-moxie/op_helper.c b/target-moxie/op_helper.c
new file mode 100644
index 0000000..e157f4f
--- /dev/null
+++ b/target-moxie/op_helper.c
@@ -0,0 +1,90 @@ 
+/*
+ *  Moxie helper routines
+ *
+ *  Copyright (c) 2008, 2009, 2013 Anthony Green
+ *
+ * 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 General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include "cpu.h"
+#include "helper.h"
+
+#define MMUSUFFIX _mmu
+
+#define SHIFT 0
+#include "exec/softmmu_template.h"
+
+#define SHIFT 1
+#include "exec/softmmu_template.h"
+
+#define SHIFT 2
+#include "exec/softmmu_template.h"
+
+#define SHIFT 3
+#include "exec/softmmu_template.h"
+
+/* Try to fill the TLB and return an exception if error. If retaddr is
+   NULL, it means that the function was called in C code (i.e. not
+   from generated code or from helper.c) */
+void tlb_fill(CPUMoxieState *env, target_ulong addr, int is_write, int mmu_idx,
+              uintptr_t retaddr)
+{
+    int ret;
+
+    ret = cpu_moxie_handle_mmu_fault(env, addr, is_write, mmu_idx);
+    if (unlikely(ret)) {
+        if (retaddr) {
+            cpu_restore_state(env, retaddr);
+        }
+    }
+    cpu_loop_exit(env);
+}
+
+void helper_raise_exception(CPUMoxieState *env, uint32_t pc, int ex)
+{
+    env->exception_index = ex;
+    /* Stash the exception type.  */
+    env->sregs[2] = ex;
+    /* Stash the address where the exception occurred.  */
+    env->sregs[5] = pc;
+    /* Jump the the exception handline routine.  */
+    env->pc = env->sregs[1];
+    cpu_loop_exit(env);
+}
+
+uint32_t helper_div(CPUMoxieState *env, uint32_t pc, uint32_t a, uint32_t b)
+{
+    if (unlikely(b == 0)) {
+        helper_raise_exception(env, pc, MOXIE_EX_DIV0);
+        return 0;
+    }
+    return (int32_t)a / (int32_t)b;
+}
+
+uint32_t helper_udiv(CPUMoxieState *env, uint32_t pc, uint32_t a, uint32_t b)
+{
+    if (unlikely(b == 0)) {
+        helper_raise_exception(env, pc, MOXIE_EX_DIV0);
+        return 0;
+    }
+    return a / b;
+}
+
+void helper_debug(CPUMoxieState *env)
+{
+    env->exception_index = EXCP_DEBUG;
+    cpu_loop_exit(env);
+}
+
diff --git a/target-moxie/translate.c b/target-moxie/translate.c
new file mode 100644
index 0000000..7ed4c0b
--- /dev/null
+++ b/target-moxie/translate.c
@@ -0,0 +1,1003 @@ 
+/*
+ *  Moxie emulation for qemu: main translation routines.
+ *
+ *  Copyright (c) 2009, 2013 Anthony Green
+ *
+ * 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 General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* For information on the Moxie architecture, see
+ *    http://moxielogic.org/wiki
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "cpu.h"
+#include "exec/exec-all.h"
+#include "disas/disas.h"
+#include "tcg-op.h"
+
+#include "helper.h"
+#define GEN_HELPER 1
+#include "helper.h"
+
+/* This is the state at translation time.  */
+typedef struct DisasContext {
+    struct TranslationBlock *tb;
+    target_ulong pc, saved_pc;
+    uint32_t opcode;
+    uint32_t fp_status;
+    /* Routine used to access memory */
+    int memidx;
+    int bstate;
+    target_ulong btarget;
+    int singlestep_enabled;
+} DisasContext;
+
+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 */
+};
+
+static TCGv cpu_pc;
+static TCGv cpu_gregs[16];
+static TCGv_ptr cpu_env;
+static TCGv cc;
+
+#define REG(x) (cpu_gregs[x])
+
+/* Extract the signed 10-bit offset from a 16-bit branch
+   instruction.  */
+#define INST2OFFSET(o) ((((signed short)((o & ((1<<10)-1))<<6))>>6)<<1)
+
+void cpu_dump_state(CPUArchState *env, FILE *f, fprintf_function cpu_fprintf,
+                    int flags)
+{
+    int i;
+    cpu_fprintf(f, "pc=0x%08x\n", env->pc);
+    cpu_fprintf(f, "$fp=0x%08x $sp=0x%08x $r0=0x%08x $r1=0x%08x\n",
+                env->gregs[0], env->gregs[1], env->gregs[2], env->gregs[3]);
+    for (i = 4; i < 16; i += 4) {
+        cpu_fprintf(f, "$r%d=0x%08x $r%d=0x%08x $r%d=0x%08x $r%d=0x%08x\n",
+                    i-2, env->gregs[i], i-1, env->gregs[i + 1],
+                    i, env->gregs[i + 2], i+1, env->gregs[i + 3]);
+    }
+    cpu_fprintf(f, "sr0=0x%08x $r1=0x%08x\n",
+                env->sregs[0], env->sregs[1]);
+}
+
+void moxie_translate_init(void)
+{
+    int i;
+    static int done_init;
+    static const char * const gregnames[16] = {
+        "$fp", "$sp", "$r0", "$r1",
+        "$r2", "$r3", "$r4", "$r5",
+        "$r6", "$r7", "$r8", "$r9",
+        "$r10", "$r11", "$r12", "$r13"
+    };
+
+    if (done_init) {
+        return;
+    }
+    cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
+    cpu_pc = tcg_global_mem_new_i32(TCG_AREG0,
+                                    offsetof(CPUMoxieState, pc), "$pc");
+    for (i = 0; i < 24; i++)
+        cpu_gregs[i] = tcg_global_mem_new_i32(TCG_AREG0,
+                                              offsetof(CPUMoxieState, gregs[i]),
+                                              gregnames[i]);
+
+    cc = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUMoxieState, cc), "cc");
+
+    done_init = 1;
+}
+
+static inline void gen_goto_tb(CPUMoxieState *env, DisasContext *ctx,
+                               int n, target_ulong dest)
+{
+    TranslationBlock *tb;
+    tb = ctx->tb;
+
+    if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK) &&
+        !ctx->singlestep_enabled) {
+        tcg_gen_goto_tb(n);
+        tcg_gen_movi_i32(cpu_pc, dest);
+        tcg_gen_exit_tb((long) 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 int decode_opc(MoxieCPU *cpu, DisasContext *ctx)
+{
+    CPUMoxieState *env = &cpu->env;
+
+    /* Local cache for the instruction opcode.  */
+    int opcode;
+    /* Set the default instruction length.  */
+    int length = 2;
+
+    if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) {
+        tcg_gen_debug_insn_start(ctx->pc);
+    }
+
+    /* Examine the 16-bit opcode.  */
+    opcode = ctx->opcode;
+
+    /* Decode instruction.  */
+    if (opcode & (1 << 15)) {
+        if (opcode & (1 << 14)) {
+            /* This is a Form 3 instruction.  */
+            int inst = (opcode >> 10 & 0xf);
+
+            switch (inst) {
+            case 0x00: /* beq */
+                {
+                    int l1 = gen_new_label();
+                    tcg_gen_brcondi_i32(TCG_COND_EQ, cc, CC_EQ, l1);
+                    gen_goto_tb(env, ctx, 1, ctx->pc + 2);
+                    gen_set_label(l1);
+                    gen_goto_tb(env, ctx, 0, INST2OFFSET(opcode) + ctx->pc + 2);
+                    ctx->bstate = BS_BRANCH;
+                }
+                break;
+            case 0x01: /* bne */
+                {
+                    int l1 = gen_new_label();
+                    tcg_gen_brcondi_i32(TCG_COND_NE, cc, CC_EQ, l1);
+                    gen_goto_tb(env, ctx, 1, ctx->pc + 2);
+                    gen_set_label(l1);
+                    gen_goto_tb(env, ctx, 0, INST2OFFSET(opcode) + ctx->pc + 2);
+                    ctx->bstate = BS_BRANCH;
+                }
+                break;
+            case 0x02: /* blt */
+                {
+                    int l1 = gen_new_label();
+                    TCGv t1 = tcg_temp_new_i32();
+                    tcg_gen_andi_i32(t1, cc, CC_LT);
+                    tcg_gen_brcondi_i32(TCG_COND_EQ, t1, CC_LT, l1);
+                    tcg_temp_free_i32(t1);
+                    gen_goto_tb(env, ctx, 1, ctx->pc + 2);
+                    gen_set_label(l1);
+                    gen_goto_tb(env, ctx, 0, INST2OFFSET(opcode) + ctx->pc + 2);
+                    ctx->bstate = BS_BRANCH;
+                }
+                break;
+            case 0x03: /* bgt */
+                {
+                    int l1 = gen_new_label();
+                    TCGv t1 = tcg_temp_new_i32();
+                    tcg_gen_andi_i32(t1, cc, CC_GT);
+                    tcg_gen_brcondi_i32(TCG_COND_EQ, t1, CC_GT, l1);
+                    tcg_temp_free_i32(t1);
+                    gen_goto_tb(env, ctx, 1, ctx->pc + 2);
+                    gen_set_label(l1);
+                    gen_goto_tb(env, ctx, 0, INST2OFFSET(opcode) + ctx->pc + 2);
+                    ctx->bstate = BS_BRANCH;
+                }
+                break;
+            case 0x04: /* bltu */
+                {
+                    int l1 = gen_new_label();
+                    TCGv t1 = tcg_temp_new_i32();
+                    tcg_gen_andi_i32(t1, cc, CC_LTU);
+                    tcg_gen_brcondi_i32(TCG_COND_EQ, t1, CC_LTU, l1);
+                    tcg_temp_free_i32(t1);
+                    gen_goto_tb(env, ctx, 1, ctx->pc + 2);
+                    gen_set_label(l1);
+                    gen_goto_tb(env, ctx, 0, INST2OFFSET(opcode) + ctx->pc + 2);
+                    ctx->bstate = BS_BRANCH;
+                }
+                break;
+            case 0x05: /* bgtu */
+                {
+                    int l1 = gen_new_label();
+                    TCGv t1 = tcg_temp_new_i32();
+                    tcg_gen_andi_i32(t1, cc, CC_GTU);
+                    tcg_gen_brcondi_i32(TCG_COND_EQ, t1, CC_GTU, l1);
+                    tcg_temp_free_i32(t1);
+                    gen_goto_tb(env, ctx, 1, ctx->pc + 2);
+                    gen_set_label(l1);
+                    gen_goto_tb(env, ctx, 0, INST2OFFSET(opcode) + ctx->pc + 2);
+                    ctx->bstate = BS_BRANCH;
+                }
+                break;
+            case 0x06: /* bge */
+                {
+                    int l1 = gen_new_label();
+                    TCGv t1 = tcg_temp_new_i32();
+                    tcg_gen_andi_i32(t1, cc, CC_GT|CC_EQ);
+                    tcg_gen_brcondi_i32(TCG_COND_GT, t1, 0, l1);
+                    tcg_temp_free_i32(t1);
+                    gen_goto_tb(env, ctx, 1, ctx->pc + 2);
+                    gen_set_label(l1);
+                    gen_goto_tb(env, ctx, 0, INST2OFFSET(opcode) + ctx->pc + 2);
+                    ctx->bstate = BS_BRANCH;
+                }
+                break;
+            case 0x07: /* ble */
+                {
+                    int l1 = gen_new_label();
+                    TCGv t1 = tcg_temp_new_i32();
+                    tcg_gen_andi_i32(t1, cc, CC_LT|CC_EQ);
+                    tcg_gen_brcondi_i32(TCG_COND_GT, t1, 0, l1);
+                    tcg_temp_free_i32(t1);
+                    gen_goto_tb(env, ctx, 1, ctx->pc + 2);
+                    gen_set_label(l1);
+                    gen_goto_tb(env, ctx, 0, INST2OFFSET(opcode) + ctx->pc + 2);
+                    ctx->bstate = BS_BRANCH;
+                }
+                break;
+            case 0x08: /* bgeu */
+                {
+                    int l1 = gen_new_label();
+                    TCGv t1 = tcg_temp_new_i32();
+                    tcg_gen_andi_i32(t1, cc, CC_GTU|CC_EQ);
+                    tcg_gen_brcondi_i32(TCG_COND_GT, t1, 0, l1);
+                    tcg_temp_free_i32(t1);
+                    gen_goto_tb(env, ctx, 1, ctx->pc + 2);
+                    gen_set_label(l1);
+                    gen_goto_tb(env, ctx, 0, INST2OFFSET(opcode) + ctx->pc + 2);
+                    ctx->bstate = BS_BRANCH;
+                }
+                break;
+            case 0x09: /* bleu */
+                {
+                    int l1 = gen_new_label();
+                    TCGv t1 = tcg_temp_new_i32();
+                    tcg_gen_andi_i32(t1, cc, CC_LTU|CC_EQ);
+                    tcg_gen_brcondi_i32(TCG_COND_GT, t1, 0, l1);
+                    tcg_temp_free_i32(t1);
+                    gen_goto_tb(env, ctx, 1, ctx->pc + 2);
+                    gen_set_label(l1);
+                    gen_goto_tb(env, ctx, 0, INST2OFFSET(opcode) + ctx->pc + 2);
+                    ctx->bstate = BS_BRANCH;
+                }
+                break;
+            default:
+                {
+                    TCGv temp = tcg_temp_new_i32();
+                    tcg_gen_movi_i32(cpu_pc, ctx->pc);
+                    tcg_gen_movi_i32(temp, MOXIE_EX_BAD);
+                    gen_helper_raise_exception(cpu_env, cpu_pc, temp);
+                    tcg_temp_free_i32(temp);
+                }
+                break;
+            }
+        } else {
+            /* This is a Form 2 instruction.  */
+            int inst = (opcode >> 12 & 0x3);
+            switch (inst) {
+            case 0x00: /* inc */
+                {
+                    int a = (opcode >> 8) & 0xf;
+                    unsigned int v = (opcode & 0xff);
+                    tcg_gen_addi_i32(REG(a), REG(a), v);
+                }
+                break;
+            case 0x01: /* dec */
+                {
+                    int a = (opcode >> 8) & 0xf;
+                    unsigned int v = (opcode & 0xff);
+                    tcg_gen_subi_i32(REG(a), REG(a), v);
+                }
+                break;
+            case 0x02: /* gsr */
+                {
+                    int a = (opcode >> 8) & 0xf;
+                    unsigned v = (opcode & 0xff);
+                    tcg_gen_ld_i32(REG(a), cpu_env,
+                                   offsetof(CPUMoxieState, sregs[v]));
+                }
+                break;
+            case 0x03: /* ssr */
+                {
+                    int a = (opcode >> 8) & 0xf;
+                    unsigned v = (opcode & 0xff);
+                    tcg_gen_st_i32(REG(a), cpu_env,
+                                   offsetof(CPUMoxieState, sregs[v]));
+                }
+                break;
+            default:
+                {
+                    TCGv temp = tcg_temp_new_i32();
+                    tcg_gen_movi_i32(cpu_pc, ctx->pc);
+                    tcg_gen_movi_i32(temp, MOXIE_EX_BAD);
+                    gen_helper_raise_exception(cpu_env, cpu_pc, temp);
+                    tcg_temp_free_i32(temp);
+                }
+                break;
+            }
+        }
+    } else {
+        /* This is a Form 1 instruction.  */
+        int inst = opcode >> 8;
+        switch (inst) {
+        case 0x00: /* nop */
+            break;
+        case 0x01: /* ldi.l (immediate) */
+            {
+                int reg = (opcode >> 4) & 0xf;
+                int val = cpu_ldl_code(env, ctx->pc + 2);
+                tcg_gen_movi_i32(REG(reg), val);
+                length = 6;
+            }
+            break;
+        case 0x02: /* mov (register-to-register) */
+            {
+                int dest  = (opcode >> 4) & 0xf;
+                int src = opcode & 0xf;
+                tcg_gen_mov_i32(REG(dest), REG(src));
+            }
+            break;
+        case 0x03: /* jsra */
+            {
+                TCGv t1 = tcg_temp_new_i32();
+                TCGv t2 = tcg_temp_new_i32();
+
+                tcg_gen_movi_i32(t1, ctx->pc + 6);
+
+                /* Make space for the static chain and return address.  */
+                tcg_gen_subi_i32(t2, REG(1), 8);
+                tcg_gen_mov_i32(REG(1), t2);
+                tcg_gen_qemu_st32(t1, REG(1), ctx->memidx);
+
+                /* Push the current frame pointer.  */
+                tcg_gen_subi_i32(t2, REG(1), 4);
+                tcg_gen_mov_i32(REG(1), t2);
+                tcg_gen_qemu_st32(REG(0), REG(1), ctx->memidx);
+
+                /* Set the pc and $fp.  */
+                tcg_gen_mov_i32(REG(0), REG(1));
+
+                gen_goto_tb(env, ctx, 0, cpu_ldl_code(env, ctx->pc + 2));
+
+                tcg_temp_free_i32(t1);
+                tcg_temp_free_i32(t2);
+
+                ctx->bstate = BS_BRANCH;
+                length = 6;
+            }
+            break;
+        case 0x04: /* ret */
+            {
+                TCGv t1 = tcg_temp_new_i32();
+
+                /* The new $sp is the old $fp.  */
+                tcg_gen_mov_i32(REG(1), REG(0));
+
+                /* Pop the frame pointer.  */
+                tcg_gen_qemu_ld32u(REG(0), REG(1), ctx->memidx);
+                tcg_gen_addi_i32(t1, REG(1), 4);
+                tcg_gen_mov_i32(REG(1), t1);
+
+
+                /* Pop the return address and skip over the static chain
+                   slot.  */
+                tcg_gen_qemu_ld32u(cpu_pc, REG(1), ctx->memidx);
+                tcg_gen_addi_i32(t1, REG(1), 8);
+                tcg_gen_mov_i32(REG(1), t1);
+
+                tcg_temp_free_i32(t1);
+
+                /* Jump... */
+                tcg_gen_exit_tb(0);
+
+                ctx->bstate = BS_BRANCH;
+            }
+            break;
+        case 0x05: /* add.l */
+            {
+                int a = (opcode >> 4) & 0xf;
+                int b = opcode & 0xf;
+
+                tcg_gen_add_i32(REG(a), REG(a), REG(b));
+            }
+            break;
+        case 0x06: /* push */
+            {
+                int a = (opcode >> 4) & 0xf;
+                int b = opcode & 0xf;
+
+                TCGv t1 = tcg_temp_new_i32();
+                tcg_gen_subi_i32(t1, REG(a), 4);
+                tcg_gen_mov_i32(REG(a), t1);
+                tcg_gen_qemu_st32(REG(b), REG(a), ctx->memidx);
+                tcg_temp_free_i32(t1);
+            }
+            break;
+        case 0x07: /* pop */
+            {
+                int a = (opcode >> 4) & 0xf;
+                int b = opcode & 0xf;
+                TCGv t1 = tcg_temp_new_i32();
+
+                tcg_gen_qemu_ld32u(REG(b), REG(a), ctx->memidx);
+                tcg_gen_addi_i32(t1, REG(a), 4);
+                tcg_gen_mov_i32(REG(a), t1);
+                tcg_temp_free_i32(t1);
+            }
+            break;
+        case 0x08: /* lda.l */
+            {
+                int reg = (opcode >> 4) & 0xf;
+
+                TCGv ptr = tcg_temp_new_i32();
+                tcg_gen_movi_i32(ptr, cpu_ldl_code(env, ctx->pc + 2));
+                tcg_gen_qemu_ld32u(REG(reg), ptr, ctx->memidx);
+                tcg_temp_free_i32(ptr);
+
+                length = 6;
+            }
+            break;
+        case 0x09: /* sta.l */
+            {
+                int val = (opcode >> 4) & 0xf;
+
+                TCGv ptr = tcg_temp_new_i32();
+                tcg_gen_movi_i32(ptr, cpu_ldl_code(env, ctx->pc + 2));
+                tcg_gen_qemu_st32(REG(val), ptr, ctx->memidx);
+                tcg_temp_free_i32(ptr);
+
+                length = 6;
+            }
+            break;
+        case 0x0a: /* ld.l (register indirect) */
+            {
+                int src  = opcode & 0xf;
+                int dest = (opcode >> 4) & 0xf;
+
+                tcg_gen_qemu_ld32u(REG(dest), REG(src), ctx->memidx);
+            }
+            break;
+        case 0x0b: /* st.l */
+            {
+                int dest = (opcode >> 4) & 0xf;
+                int val  = opcode & 0xf;
+
+                tcg_gen_qemu_st32(REG(val), REG(dest), ctx->memidx);
+            }
+            break;
+        case 0x0c: /* ldo.l */
+            {
+                int a = (opcode >> 4) & 0xf;
+                int b = opcode & 0xf;
+
+                TCGv t1 = tcg_temp_new_i32();
+                TCGv t2 = tcg_temp_new_i32();
+                tcg_gen_addi_i32(t1, REG(b), cpu_ldl_code(env, ctx->pc + 2));
+                tcg_gen_qemu_ld32u(t2, t1, ctx->memidx);
+                tcg_gen_mov_i32(REG(a), t2);
+
+                tcg_temp_free_i32(t1);
+                tcg_temp_free_i32(t2);
+
+                length = 6;
+            }
+            break;
+        case 0x0d: /* sto.l */
+            {
+                int a = (opcode >> 4) & 0xf;
+                int b = opcode & 0xf;
+
+                TCGv t1 = tcg_temp_new_i32();
+                TCGv t2 = tcg_temp_new_i32();
+                tcg_gen_addi_i32(t1, REG(a), cpu_ldl_code(env, ctx->pc + 2));
+                tcg_gen_qemu_st32(REG(b), t1, ctx->memidx);
+
+                tcg_temp_free_i32(t1);
+                tcg_temp_free_i32(t2);
+
+                length = 6;
+            }
+            break;
+        case 0x0e: /* cmp */
+            {
+                int a  = (opcode >> 4) & 0xf;
+                int b  = opcode & 0xf;
+
+                int label_equal = gen_new_label();
+                int label_done = gen_new_label();
+
+                /* Clear CC */
+                tcg_gen_movi_i32(cc, 0);
+
+                tcg_gen_brcond_i32(TCG_COND_EQ, REG(a), REG(b), label_equal);
+
+#define CMPTEST(t, CODE)                                            \
+                {                                                   \
+                    int lyes = gen_new_label();                     \
+                    int lno = gen_new_label();                      \
+                    tcg_gen_brcond_i32(t, REG(a), REG(b), lyes);    \
+                    tcg_gen_br(lno);                                \
+                    gen_set_label(lyes);                            \
+                    tcg_gen_ori_i32(cc, cc, CODE);                  \
+                    gen_set_label(lno);                             \
+                }
+                CMPTEST(TCG_COND_LT, CC_LT);
+                CMPTEST(TCG_COND_GT, CC_GT);
+                CMPTEST(TCG_COND_LTU, CC_LTU);
+                CMPTEST(TCG_COND_GTU, CC_GTU);
+                tcg_gen_br(label_done);
+                gen_set_label(label_equal);
+                tcg_gen_movi_i32(cc, CC_EQ);
+                gen_set_label(label_done);
+            }
+            break;
+        case 0x19: /* jsr */
+            {
+                int fnreg = (opcode >> 4) & 0xf;
+
+                /* Load the stack pointer into T0.  */
+                TCGv t1 = tcg_temp_new_i32();
+                TCGv t2 = tcg_temp_new_i32();
+
+                tcg_gen_movi_i32(t1, ctx->pc + 2);
+
+                /* Make space for the static chain and return address.  */
+                tcg_gen_subi_i32(t2, REG(1), 8);
+                tcg_gen_mov_i32(REG(1), t2);
+                tcg_gen_qemu_st32(t1, REG(1), ctx->memidx);
+
+                /* Push the current frame pointer.  */
+                tcg_gen_subi_i32(t2, REG(1), 4);
+                tcg_gen_mov_i32(REG(1), t2);
+                tcg_gen_qemu_st32(REG(0), REG(1), ctx->memidx);
+
+                /* Set the pc and $fp.  */
+                tcg_gen_mov_i32(REG(0), REG(1));
+                tcg_gen_mov_i32(cpu_pc, REG(fnreg));
+                tcg_temp_free_i32(t1);
+                tcg_temp_free_i32(t2);
+                tcg_gen_exit_tb(0);
+                ctx->bstate = BS_BRANCH;
+            }
+            break;
+        case 0x1a: /* jmpa */
+            {
+                tcg_gen_movi_i32(cpu_pc, cpu_ldl_code(env, ctx->pc + 2));
+                tcg_gen_exit_tb(0);
+                ctx->bstate = BS_BRANCH;
+                length = 6;
+            }
+            break;
+        case 0x1b: /* ldi.b (immediate) */
+            {
+                int reg = (opcode >> 4) & 0xf;
+                int val = cpu_ldl_code(env, ctx->pc + 2);
+                tcg_gen_movi_i32(REG(reg), val);
+                length = 6;
+            }
+            break;
+        case 0x1c: /* ld.b (register indirect) */
+            {
+                int src  = opcode & 0xf;
+                int dest = (opcode >> 4) & 0xf;
+
+                tcg_gen_qemu_ld8u(REG(dest), REG(src), ctx->memidx);
+            }
+            break;
+        case 0x1d: /* lda.b */
+            {
+                int reg = (opcode >> 4) & 0xf;
+
+                TCGv ptr = tcg_temp_new_i32();
+                tcg_gen_movi_i32(ptr, cpu_ldl_code(env, ctx->pc + 2));
+                tcg_gen_qemu_ld8u(REG(reg), ptr, ctx->memidx);
+                tcg_temp_free_i32(ptr);
+
+                length = 6;
+            }
+            break;
+        case 0x1e: /* st.b */
+            {
+                int dest = (opcode >> 4) & 0xf;
+                int val  = opcode & 0xf;
+
+                tcg_gen_qemu_st8(REG(val), REG(dest), ctx->memidx);
+            }
+            break;
+        case 0x1f: /* sta.b */
+            {
+                int val = (opcode >> 4) & 0xf;
+
+                TCGv ptr = tcg_temp_new_i32();
+                tcg_gen_movi_i32(ptr, cpu_ldl_code(env, ctx->pc + 2));
+                tcg_gen_qemu_st8(REG(val), ptr, ctx->memidx);
+                tcg_temp_free_i32(ptr);
+
+                length = 6;
+            }
+            break;
+        case 0x20: /* ldi.s (immediate) */
+            {
+                int reg = (opcode >> 4) & 0xf;
+                int val = cpu_ldl_code(env, ctx->pc + 2);
+                tcg_gen_movi_i32(REG(reg), val);
+                length = 6;
+            }
+            break;
+        case 0x21: /* ld.s (register indirect) */
+            {
+                int src  = opcode & 0xf;
+                int dest = (opcode >> 4) & 0xf;
+
+                tcg_gen_qemu_ld16u(REG(dest), REG(src), ctx->memidx);
+            }
+            break;
+        case 0x22: /* lda.s */
+            {
+                int reg = (opcode >> 4) & 0xf;
+
+                TCGv ptr = tcg_temp_new_i32();
+                tcg_gen_movi_i32(ptr, cpu_ldl_code(env, ctx->pc + 2));
+                tcg_gen_qemu_ld16u(REG(reg), ptr, ctx->memidx);
+                tcg_temp_free_i32(ptr);
+
+                length = 6;
+            }
+            break;
+        case 0x23: /* st.s */
+            {
+                int dest = (opcode >> 4) & 0xf;
+                int val  = opcode & 0xf;
+
+                tcg_gen_qemu_st16(REG(val), REG(dest), ctx->memidx);
+            }
+            break;
+        case 0x24: /* sta.s */
+            {
+                int val = (opcode >> 4) & 0xf;
+
+                TCGv ptr = tcg_temp_new_i32();
+                tcg_gen_movi_i32(ptr, cpu_ldl_code(env, ctx->pc + 2));
+                tcg_gen_qemu_st16(REG(val), ptr, ctx->memidx);
+                tcg_temp_free_i32(ptr);
+
+                length = 6;
+            }
+            break;
+        case 0x25: /* jmp */
+            {
+                int reg = (opcode >> 4) & 0xf;
+                tcg_gen_mov_i32(cpu_pc, REG(reg));
+                tcg_gen_exit_tb(0);
+                ctx->bstate = BS_BRANCH;
+            }
+            break;
+        case 0x26: /* and */
+            {
+                int a = (opcode >> 4) & 0xf;
+                int b = opcode & 0xf;
+
+                tcg_gen_and_i32(REG(a), REG(a), REG(b));
+            }
+            break;
+        case 0x27: /* lshr */
+            {
+                int a = (opcode >> 4) & 0xf;
+                int b = opcode & 0xf;
+
+                tcg_gen_shr_i32(REG(a), REG(a), REG(b));
+            }
+            break;
+        case 0x28: /* ashl */
+            {
+                int a = (opcode >> 4) & 0xf;
+                int b = opcode & 0xf;
+
+                tcg_gen_shl_i32(REG(a), REG(a), REG(b));
+            }
+            break;
+        case 0x29: /* sub.l */
+            {
+                int a = (opcode >> 4) & 0xf;
+                int b = opcode & 0xf;
+
+                tcg_gen_sub_i32(REG(a), REG(a), REG(b));
+            }
+            break;
+        case 0x2a: /* neg */
+            {
+                int a = (opcode >> 4) & 0xf;
+                int b = opcode & 0xf;
+
+                tcg_gen_neg_i32(REG(a), REG(b));
+            }
+            break;
+        case 0x2b: /* or */
+            {
+                int a = (opcode >> 4) & 0xf;
+                int b = opcode & 0xf;
+
+                tcg_gen_or_i32(REG(a), REG(a), REG(b));
+            }
+            break;
+        case 0x2c: /* not */
+            {
+                int a = (opcode >> 4) & 0xf;
+                int b = opcode & 0xf;
+
+                tcg_gen_not_i32(REG(a), REG(b));
+            }
+            break;
+        case 0x2d: /* ashr */
+            {
+                int a = (opcode >> 4) & 0xf;
+                int b = opcode & 0xf;
+
+                tcg_gen_sar_i32(REG(a), REG(a), REG(b));
+            }
+            break;
+        case 0x2e: /* xor */
+            {
+                int a = (opcode >> 4) & 0xf;
+                int b = opcode & 0xf;
+
+                tcg_gen_xor_i32(REG(a), REG(a), REG(b));
+            }
+            break;
+        case 0x2f: /* mul.l */
+            {
+                int a = (opcode >> 4) & 0xf;
+                int b = opcode & 0xf;
+
+                tcg_gen_mul_i32(REG(a), REG(a), REG(b));
+            }
+            break;
+        case 0x30: /* swi */
+            {
+                int val = cpu_ldl_code(env, ctx->pc + 2);
+
+                TCGv temp = tcg_temp_new_i32();
+                tcg_gen_movi_i32(temp, val);
+                tcg_gen_st_i32(temp, cpu_env,
+                               offsetof(CPUMoxieState, sregs[3]));
+                tcg_gen_movi_i32(cpu_pc, ctx->pc);
+                tcg_gen_movi_i32(temp, MOXIE_EX_SWI);
+                gen_helper_raise_exception(cpu_env, cpu_pc, temp);
+                tcg_temp_free_i32(temp);
+
+                length = 6;
+            }
+            break;
+        case 0x31: /* div.l */
+            {
+                int a = (opcode >> 4) & 0xf;
+                int b = opcode & 0xf;
+                tcg_gen_movi_i32(cpu_pc, ctx->pc);
+                gen_helper_div(REG(a), cpu_env, cpu_pc, REG(a), REG(b));
+            }
+            break;
+        case 0x32: /* udiv.l */
+            {
+                int a = (opcode >> 4) & 0xf;
+                int b = opcode & 0xf;
+                tcg_gen_movi_i32(cpu_pc, ctx->pc);
+                gen_helper_udiv(REG(a), cpu_env, cpu_pc, REG(a), REG(b));
+            }
+            break;
+        case 0x33: /* mod.l */
+            {
+                int a = (opcode >> 4) & 0xf;
+                int b = opcode & 0xf;
+                tcg_gen_rem_i32(REG(a), REG(a), REG(b));
+            }
+            break;
+        case 0x34: /* umod.l */
+            {
+                int a = (opcode >> 4) & 0xf;
+                int b = opcode & 0xf;
+                tcg_gen_remu_i32(REG(a), REG(a), REG(b));
+            }
+            break;
+        case 0x35: /* brk */
+            {
+                 gen_helper_debug(cpu_env);
+            }
+            break;
+        case 0x36: /* ldo.b */
+            {
+                int a = (opcode >> 4) & 0xf;
+                int b = opcode & 0xf;
+
+                TCGv t1 = tcg_temp_new_i32();
+                TCGv t2 = tcg_temp_new_i32();
+                tcg_gen_addi_i32(t1, REG(b), cpu_ldl_code(env, ctx->pc + 2));
+                tcg_gen_qemu_ld8u(t2, t1, ctx->memidx);
+                tcg_gen_mov_i32(REG(a), t2);
+
+                tcg_temp_free_i32(t1);
+                tcg_temp_free_i32(t2);
+
+                length = 6;
+            }
+            break;
+        case 0x37: /* sto.b */
+            {
+                int a = (opcode >> 4) & 0xf;
+                int b = opcode & 0xf;
+
+                TCGv t1 = tcg_temp_new_i32();
+                TCGv t2 = tcg_temp_new_i32();
+                tcg_gen_addi_i32(t1, REG(a), cpu_ldl_code(env, ctx->pc + 2));
+                tcg_gen_qemu_st8(REG(b), t1, ctx->memidx);
+
+                tcg_temp_free_i32(t1);
+                tcg_temp_free_i32(t2);
+
+                length = 6;
+            }
+            break;
+        case 0x38: /* ldo.s */
+            {
+                int a = (opcode >> 4) & 0xf;
+                int b = opcode & 0xf;
+
+                TCGv t1 = tcg_temp_new_i32();
+                TCGv t2 = tcg_temp_new_i32();
+                tcg_gen_addi_i32(t1, REG(b), cpu_ldl_code(env, ctx->pc + 2));
+                tcg_gen_qemu_ld16u(t2, t1, ctx->memidx);
+                tcg_gen_mov_i32(REG(a), t2);
+
+                tcg_temp_free_i32(t1);
+                tcg_temp_free_i32(t2);
+
+                length = 6;
+            }
+            break;
+        case 0x39: /* sto.s */
+            {
+                int a = (opcode >> 4) & 0xf;
+                int b = opcode & 0xf;
+
+                TCGv t1 = tcg_temp_new_i32();
+                TCGv t2 = tcg_temp_new_i32();
+                tcg_gen_addi_i32(t1, REG(a), cpu_ldl_code(env, ctx->pc + 2));
+                tcg_gen_qemu_st16(REG(b), t1, ctx->memidx);
+                tcg_temp_free_i32(t1);
+                tcg_temp_free_i32(t2);
+
+                length = 6;
+            }
+            break;
+        default:
+            {
+                TCGv temp = tcg_temp_new_i32();
+                tcg_gen_movi_i32(cpu_pc, ctx->pc);
+                tcg_gen_movi_i32(temp, MOXIE_EX_BAD);
+                gen_helper_raise_exception(cpu_env, cpu_pc, temp);
+                tcg_temp_free_i32(temp);
+             }
+            break;
+        }
+    }
+
+    return length;
+}
+
+/* generate intermediate code for basic block 'tb'.  */
+static void
+gen_intermediate_code_internal(MoxieCPU *cpu, TranslationBlock *tb,
+                               bool search_pc)
+{
+    DisasContext ctx;
+    target_ulong pc_start;
+    uint16_t *gen_opc_end;
+    CPUBreakpoint *bp;
+    int j, lj = -1;
+    CPUMoxieState *env = &cpu->env;
+
+    pc_start = tb->pc;
+    gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE;
+    ctx.pc = pc_start;
+    ctx.saved_pc = -1;
+    ctx.tb = tb;
+    ctx.memidx = 0;
+    ctx.singlestep_enabled = 0;
+    ctx.bstate = BS_NONE;
+
+    do {
+        if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) {
+            QTAILQ_FOREACH(bp, &env->breakpoints, entry) {
+                if (ctx.pc == bp->pc) {
+                    tcg_gen_movi_i32(cpu_pc, ctx.pc);
+                    gen_helper_debug(cpu_env);
+                    ctx.bstate = BS_EXCP;
+                    goto done_generating;
+                }
+            }
+        }
+
+        if (search_pc) {
+            j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf;
+            if (lj < j) {
+                lj++;
+                while (lj < j) {
+                    tcg_ctx.gen_opc_instr_start[lj++] = 0;
+                }
+            }
+            tcg_ctx.gen_opc_pc[lj] = ctx.pc;
+            tcg_ctx.gen_opc_instr_start[lj] = 1;
+        }
+        ctx.opcode = cpu_lduw_code(env, ctx.pc);
+        ctx.pc += decode_opc(cpu, &ctx);
+
+        if (env->singlestep_enabled) {
+            break;
+        }
+
+        if ((ctx.pc & (TARGET_PAGE_SIZE - 1)) == 0) {
+            break;
+        }
+    } while (ctx.bstate == BS_NONE && tcg_ctx.gen_opc_ptr < gen_opc_end);
+
+    if (env->singlestep_enabled) {
+        tcg_gen_movi_tl(cpu_pc, ctx.pc);
+        gen_helper_debug(cpu_env);
+    } else {
+        switch (ctx.bstate) {
+        case BS_STOP:
+        case BS_NONE:
+            gen_goto_tb(env, &ctx, 0, ctx.pc);
+            break;
+        case BS_EXCP:
+            tcg_gen_exit_tb(0);
+            break;
+        case BS_BRANCH:
+        default:
+            break;
+        }
+    }
+ done_generating:
+    *tcg_ctx.gen_opc_ptr = INDEX_op_end;
+    if (search_pc) {
+        j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf;
+        lj++;
+        while (lj <= j) {
+            tcg_ctx.gen_opc_instr_start[lj++] = 0;
+        }
+    } else {
+        tb->size = ctx.pc - pc_start;
+    }
+}
+
+void gen_intermediate_code(CPUMoxieState *env, struct TranslationBlock *tb)
+{
+    gen_intermediate_code_internal(moxie_env_get_cpu(env), tb, false);
+}
+
+void gen_intermediate_code_pc(CPUMoxieState *env, struct TranslationBlock *tb)
+{
+    gen_intermediate_code_internal(moxie_env_get_cpu(env), tb, true);
+}
+
+void restore_state_to_opc(CPUMoxieState *env, TranslationBlock *tb, int pc_pos)
+{
+    env->pc = tcg_ctx.gen_opc_pc[pc_pos];
+}