diff mbox

[v4,1/4] Add moxie target code

Message ID 1362002958-1441-2-git-send-email-green@moxielogic.com
State New
Headers show

Commit Message

Anthony Green Feb. 27, 2013, 10:09 p.m. UTC
Signed-off-by: Anthony Green <green@moxielogic.com>
---
 target-moxie/Makefile.objs |    2 +
 target-moxie/cpu.c         |  177 ++++++++
 target-moxie/cpu.h         |  222 ++++++++++
 target-moxie/helper.c      |  100 +++++
 target-moxie/helper.h      |    6 +
 target-moxie/machine.c     |   11 +
 target-moxie/mmu.c         |   37 ++
 target-moxie/mmu.h         |   19 +
 target-moxie/op_helper.c   |   80 ++++
 target-moxie/translate.c   | 1042 ++++++++++++++++++++++++++++++++++++++++++++
 10 files changed, 1696 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/mmu.c
 create mode 100644 target-moxie/mmu.h
 create mode 100644 target-moxie/op_helper.c
 create mode 100644 target-moxie/translate.c

Comments

Richard Henderson Feb. 28, 2013, 8:30 a.m. UTC | #1
On 2013-02-27 14:09, Anthony Green wrote:
> + done_generating:
> +    ctx.last_T0_store = NULL;
> +    *tcg_ctx.gen_opc_ptr = INDEX_op_end;

Looks good now.  Except for the start/end TB thing, which is
lacking the icount calls, which will shortly be the hooks to
exit the TB due to interrupt as well.

But I'll still give it the

Reviewed-by: Richard Henderson <rth@twiddle.net>

since PMM's patch isn't committed yet.  But watch out...


r~
Peter Maydell Feb. 28, 2013, 9:45 a.m. UTC | #2
On 28 February 2013 08:30, Richard Henderson <rth@twiddle.net> wrote:
> Looks good now.  Except for the start/end TB thing, which is
> lacking the icount calls, which will shortly be the hooks to
> exit the TB due to interrupt as well.
>
> But I'll still give it the
>
> Reviewed-by: Richard Henderson <rth@twiddle.net>
>
> since PMM's patch isn't committed yet.  But watch out...

Good catch. It would be better to get that int-flag patchset
committed first I think (but then I would).

-- PMM
Peter Maydell Feb. 28, 2013, 11:06 a.m. UTC | #3
On 27 February 2013 22:09, Anthony Green <green@moxielogic.com> wrote:
> +static const VMStateDescription vmstate_moxie_cpu = {
> +    .name = "cpu",
> +    .unmigratable = 1,
> +};

Since this is a new CPU it should just go ahead and implement
migration support.

> +/* 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.
> + */

Is the Moxie really an SH-4 clone which has cloned this particular
SH-4 issue, or have you just copied this comment incorrectly
from the target-sh4 code? Do all of the copied constants really
also apply?

> +/* XXXXX The structure could be made more compact */

Either:
 * you should just move the 32 bit fields to the front
 * we don't actually care about the tiny wasted space (how many
   copies of this struct exist at once anyway) and should
   drop the XXXXX comment
 * it's matching an in-memory hardware struct layout, in which
   case it's not our bug to fix [and probably needs some kind
   of packed attribute]

> +static inline void cpu_pc_from_tb(CPUMoxieState *env, TranslationBlock *tb)
> +{
> +    env->pc = tb->pc;
> +    env->flags = tb->flags;
> +}
> +
> +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 = env->flags;

This looks bogus. The TB flags should generally not be an exact
copy of your CPU's flags register -- it needs to track bits of
state which the generated code makes assumptions about, which
often includes some flags but also some other CPU state.

> +int cpu_moxie_handle_mmu_fault(CPUMoxieState *env, target_ulong address,
> +                               int rw, int mmu_idx, int is_softmmu)
> +{
> +        struct moxie_mmu_result_t res;
> +        int prot, miss;
> +        target_ulong phy;
> +        int r = 1;

Indentation here is wrong -- please conform to the coding style
(which specifies 4-space indent).

> +void do_interrupt(CPUMoxieState *env)
> +{
> +    fflush(NULL);

Huh? Accidentally included debug code?

> +
> +    switch (env->exception_index) {
> +    case EXCP_BREAK:
> +      break;
> +    default:
> +      break;
> +    }
> +}

> +int cpu_moxie_handle_mmu_fault(CPUMoxieState *env, target_ulong address,
> +                               int rw, int mmu_idx);
> +
> +/* 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) */
> +/* XXX: fix it to restore all registers */

This XXX comment suggests confusion -- cpu_restore_state()
(assisted by your code in cpu.h or translate.c) should indeed
restore all the CPU registers. If it doesn't the XXX comment
should be somewhere else, not here.

> +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);
> +      }
> +  }

Duff indentation again.

> +  cpu_loop_exit(env);
> +}
> +
> +void helper_debug(CPUMoxieState *env)
> +{
> +    env->exception_index = EXCP_DEBUG;
> +    cpu_loop_exit(env);
> +}
> +
> +#if 0
> +void helper_raise_exception(uint32_t index)
> +{
> +  env->exception_index = index;
> +  cpu_loop_exit();
> +}
> +#endif

Don't include #if-0'd out code, please.

> +
> +#if 0
> +void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
> +                          int is_asi)
> +{
> +}
> +#endif

> +#define REG(x) (cpu_gregs[x])

Is this really a particularly useful abstraction?

> +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)

It's better style to write your decoder so that it takes only
the DisasContext*, and is not passed either a MoxieCPU* or a
CPUMoxieState*. The rationale for this is that mostly your
generated code shouldn't depend on the values in the CPUState
(it is not guaranteed to be "the CPU state at the start of
the TB", which is perhaps not obvious; for more on that see
http://lists.gnu.org/archive/html/qemu-devel/2011-06/msg02404.html)
So if the functions which generate code aren't passed a
CPUState they can't accidentally rely on the values in it.
Instead you copy the values from the CPUState (or from
the tb_flags) into the DisasContext at the top level
gen_intermediate_code_internal() function, and then it's
very clear what the list of CPUState fields your decoder
relies on is.

Many of our existing targets don't do things this way,
but it's a nicer way to write a new port. I won't insist
on this change, but you should consider it.

In this case it's a bit complicated by the requirement
to call cpu_ldl_code() to read immediate values from
the instruction stream; I guess you could stick a pointer
to the env into the DisasContext, though.

In any case you should definitely go through and satisfy
yourself about which of the various valid ways to handle
CPU state each of your uses of a CPUState field in the
decoder falls into.

> +{
> +    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();

This is a weird way to indent switch statements. You can save
some horizontal space by moving the braces left so they line up
with the 'c' in 'case'.

> +                    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);

A lot of your calls to gen_goto_tb() are passing offset + ctx->pc+2.
Consider abstracting this out as a gen_goto_rel() that does the
addition of ctx->pc+2 for you.

> +                    ctx->bstate = BS_BRANCH;
> +                }
> +                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);

Most (all?) of your calls to cpu_ldl_code() use an
argument of ctx->pc+2. Consider abstracting this out
to load_insn_imm32(DisasContext *ctx).

> +                tcg_gen_movi_i32(REG(reg), val);
> +                length = 6;

Tracking the insn length separately like this is a recipe
for bugs -- it would be better to arrange to automatically
update the length when the decoder calls the "read an
imm32 from the instruction stream".

> +        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;

This handling of the basic data processing insns is rather
repetitive -- consider whether it can be table driven or
otherwise refactored?


> +        case 0x31: /* div.l */
> +            {
> +                int a = (opcode >> 4) & 0xf;
> +                int b = opcode & 0xf;
> +                tcg_gen_div_i32(REG(a), REG(a), REG(b));

Didn't Richard mention the problem of exceptions on division
in review of an earlier version of this patch?


> +/* generate intermediate code for basic block 'tb'.  */
> +struct DisasContext ctx;
> +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;
> +
> +    if (search_pc) {
> +        qemu_log("search pc %d\n", search_pc);
> +    }
> +
> +    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;
> +    /* Restore delay slot state from the tb context.  */

So, this comment about delay slots seems to be copied from the MIPS
target; earlier you had some constants and comments about delay
slots copied from the SH4 target. Does Moxie actually have delay slots?

> +    ctx.hflags = (uint32_t)tb->flags; /* FIXME: maybe use 64 bits here? */
> +    restore_cpu_state(cpu, &ctx);
> +
> +    do {
> +        if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) {
> +            QTAILQ_FOREACH(bp, &env->breakpoints, entry) {
> +                if (ctx.pc == bp->pc) {
> +                    save_cpu_state(&ctx, 1);
> +                    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_ldsw_code(env, ctx.pc);

Are you sure you want to sign extend the opcode ??
(ldsw is a signed load, ctx.opcode is 32 bit. cpu_lduw_code()
would be the unsigned load.)

> +        ctx.pc += decode_opc(cpu, &ctx);

thanks
-- PMM
Anthony Green Feb. 28, 2013, 5:30 p.m. UTC | #4
Peter - thanks for reviewing this.

On Thu, Feb 28, 2013 at 6:06 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 27 February 2013 22:09, Anthony Green <green@moxielogic.com> wrote:
>> +static const VMStateDescription vmstate_moxie_cpu = {
>> +    .name = "cpu",
>> +    .unmigratable = 1,
>> +};
>
> Since this is a new CPU it should just go ahead and implement
> migration support.

I think I did this.  See next patch.

>> +/* 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.
>> + */
>
> Is the Moxie really an SH-4 clone which has cloned this particular
> SH-4 issue, or have you just copied this comment incorrectly
> from the target-sh4 code? Do all of the copied constants really
> also apply?

A fair bit of this was cut-n-paste.  It's all cleaned up now.  Moxie
has no delay slots.

>> +        case 0x31: /* div.l */
>> +            {
>> +                int a = (opcode >> 4) & 0xf;
>> +                int b = opcode & 0xf;
>> +                tcg_gen_div_i32(REG(a), REG(a), REG(b));
>
> Didn't Richard mention the problem of exceptions on division
> in review of an earlier version of this patch?

Current hardware doesn't trap on divide by zero, so I don't mind this
failing now.  The exception processing mechanism will be implemented
soon at which time I'll clean this up.

I'll post the new series in a few minutes....

Thanks again,

AG
Peter Maydell Feb. 28, 2013, 5:35 p.m. UTC | #5
On 28 February 2013 17:30, Anthony Green <green@moxielogic.com> wrote:
> On Thu, Feb 28, 2013 at 6:06 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
>> On 27 February 2013 22:09, Anthony Green <green@moxielogic.com> wrote:
>>> +        case 0x31: /* div.l */
>>> +            {
>>> +                int a = (opcode >> 4) & 0xf;
>>> +                int b = opcode & 0xf;
>>> +                tcg_gen_div_i32(REG(a), REG(a), REG(b));
>>
>> Didn't Richard mention the problem of exceptions on division
>> in review of an earlier version of this patch?
>
> Current hardware doesn't trap on divide by zero, so I don't mind this
> failing now.  The exception processing mechanism will be implemented
> soon at which time I'll clean this up.

No, you must avoid generating TCG code which can do undefined
things, even if your CPU's divide instruction does not throw
an exception. In fact, especially if your CPU's divide insn
doesn't throw an exception. Otherwise QEMU could just blow up
when trying to execute buggy or malicious guest code.

-- PMM
diff mbox

Patch

diff --git a/target-moxie/Makefile.objs b/target-moxie/Makefile.objs
new file mode 100644
index 0000000..1c8c921
--- /dev/null
+++ b/target-moxie/Makefile.objs
@@ -0,0 +1,2 @@ 
+obj-y += translate.o op_helper.o helper.o machine.o cpu.o
+obj-$(CONFIG_SOFTMMU) += mmu.o
diff --git a/target-moxie/cpu.c b/target-moxie/cpu.c
new file mode 100644
index 0000000..2a4a4f3
--- /dev/null
+++ b/target-moxie/cpu.c
@@ -0,0 +1,177 @@ 
+/*
+ * 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 Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "cpu.h"
+#include "qemu-common.h"
+#include "migration/vmstate.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 const VMStateDescription vmstate_moxie_cpu = {
+    .name = "cpu",
+    .unmigratable = 1,
+};
+
+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..e86d0ae
--- /dev/null
+++ b/target-moxie/cpu.h
@@ -0,0 +1,222 @@ 
+/*
+ *  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 Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#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 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 EXCP_MMU_EXEC    0
+#define EXCP_MMU_READ    1
+#define EXCP_MMU_WRITE   2
+#define EXCP_MMU_FLUSH   3
+#define EXCP_MMU_MISS    4
+#define EXCP_BREAK      16 /* trap.  */
+
+#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 (1 << 30)
+#define SR_RB (1 << 29)
+#define SR_BL (1 << 28)
+#define SR_FD (1 << 15)
+#define SR_M  (1 << 9)
+#define SR_Q  (1 << 8)
+#define SR_S  (1 << 1)
+#define SR_T  (1 << 0)
+
+#define FPSCR_FR (1 << 21)
+#define FPSCR_SZ (1 << 20)
+#define FPSCR_PR (1 << 19)
+#define FPSCR_DN (1 << 18)
+#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.
+ */
+
+/* XXXXX The structure could be made more compact */
+typedef struct tlb_t {
+    uint8_t asid;               /* address space identifier */
+    uint32_t vpn;               /* virtual page number */
+    uint8_t v;                  /* validity */
+    uint32_t ppn;               /* physical page number */
+    uint8_t sz;                 /* page size */
+    uint32_t size;              /* cached page size in bytes */
+    uint8_t sh;                 /* share status */
+    uint8_t c;                  /* cacheability */
+    uint8_t pr;                 /* protection key */
+    uint8_t d;                  /* dirty */
+    uint8_t wt;                 /* write through */
+    uint8_t sa;                 /* space attribute (PCMCIA) */
+    uint8_t tc;                 /* timing control */
+} tlb_t;
+
+#define UTLB_SIZE 64
+#define ITLB_SIZE 4
+
+#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;
+  uint32_t cc;                /* condition codes */
+
+  void *irq[8];
+
+  uint32_t nb_breakpoints;
+
+  int user_mode_only;
+  void *intc_handle;
+
+  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;
+    env->flags = tb->flags;
+}
+
+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 = env->flags;
+}
+
+static inline int cpu_has_work(CPUState *cpu)
+{
+  CPUMoxieState *env = &MOXIE_CPU(cpu)->env;
+
+  return env->interrupt_request & CPU_INTERRUPT_HARD;
+}
+
+
+#endif /* _CPU_MOXIE_H */
diff --git a/target-moxie/helper.c b/target-moxie/helper.c
new file mode 100644
index 0000000..6c92b98
--- /dev/null
+++ b/target-moxie/helper.c
@@ -0,0 +1,100 @@ 
+/*
+ *  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 Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#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, int is_softmmu)
+{
+        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 = EXCP_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)
+{
+    fflush(NULL);
+
+    switch (env->exception_index) {
+    case EXCP_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..4289dbf
--- /dev/null
+++ b/target-moxie/helper.h
@@ -0,0 +1,6 @@ 
+#include "exec/def-helper.h"
+
+DEF_HELPER_1(debug, void, env)
+
+#include "exec/def-helper.h"
+
diff --git a/target-moxie/machine.c b/target-moxie/machine.c
new file mode 100644
index 0000000..1be1c35
--- /dev/null
+++ b/target-moxie/machine.c
@@ -0,0 +1,11 @@ 
+#include "hw/hw.h"
+#include "hw/boards.h"
+
+void cpu_save(QEMUFile *f, void *opaque)
+{
+}
+
+int cpu_load(QEMUFile *f, void *opaque, int version_id)
+{
+    return 0;
+}
diff --git a/target-moxie/mmu.c b/target-moxie/mmu.c
new file mode 100644
index 0000000..9f5e0b0
--- /dev/null
+++ b/target-moxie/mmu.c
@@ -0,0 +1,37 @@ 
+/*
+ *  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 Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#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(struct moxie_mmu_result_t *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..e67ed23
--- /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
+
+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;
+};
+
+int moxie_mmu_translate(struct moxie_mmu_result_t *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..60fd0f6
--- /dev/null
+++ b/target-moxie/op_helper.c
@@ -0,0 +1,80 @@ 
+/*
+ *  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 Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#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"
+
+int cpu_moxie_handle_mmu_fault(CPUMoxieState *env, target_ulong address,
+                               int rw, int mmu_idx);
+
+/* 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) */
+/* XXX: fix it to restore all registers */
+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_debug(CPUMoxieState *env)
+{
+    env->exception_index = EXCP_DEBUG;
+    cpu_loop_exit(env);
+}
+
+#if 0
+void helper_raise_exception(uint32_t index)
+{
+  env->exception_index = index;
+  cpu_loop_exit();
+}
+#endif
+
+#if 0
+void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
+                          int is_asi)
+{
+}
+#endif
diff --git a/target-moxie/translate.c b/target-moxie/translate.c
new file mode 100644
index 0000000..1797fd6
--- /dev/null
+++ b/target-moxie/translate.c
@@ -0,0 +1,1042 @@ 
+/*
+ *  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 Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA
+ * 02110-1301 USA
+ */
+
+#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"
+
+#ifdef USE_DIRECT_JUMP
+#define TBPARAM(x)
+#else
+#define TBPARAM(x) (long)(x)
+#endif
+
+/* 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;
+    uint32_t hflags, saved_hflags;
+    int bstate;
+    target_ulong btarget;
+    void *last_T0_store;
+    int last_T0_gpr;
+    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;
+    cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
+    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_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 restore_cpu_state(MoxieCPU *cpu, DisasContext *ctx)
+{
+    ctx->saved_hflags = ctx->hflags;
+}
+
+static inline void save_cpu_state(DisasContext *ctx, int do_save_pc)
+{
+
+}
+
+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:
+                abort();
+            }
+        } 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:
+                abort();
+            }
+        }
+    } 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 */
+            {
+                /* 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+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();                      \
+                    TCGv t1 = tcg_temp_new_i32();                            \
+                    tcg_gen_brcond_i32(t, REG(a), REG(b), lyes);    \
+                    tcg_gen_br(lno);                                \
+                    gen_set_label(lyes);                            \
+                    tcg_gen_ori_i32(t1, cc, CODE);                  \
+                    tcg_gen_mov_i32(cc, t1);                        \
+                    gen_set_label(lno);                             \
+                    tcg_temp_free_i32(t1);                                   \
+                }
+                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 code = tcg_temp_new_i32();
+
+                /* Load the stack pointer into T0.  */
+                TCGv t1 = tcg_temp_new_i32();
+                TCGv t2 = tcg_temp_new_i32();
+
+                tcg_gen_movi_i32(t1, MOXIE_EX_SWI);
+                tcg_gen_st_i32(t1, cpu_env, offsetof(CPUMoxieState, sregs[2]));
+
+                tcg_gen_movi_i32(code, val);
+                tcg_gen_st_i32(code, cpu_env,
+                               offsetof(CPUMoxieState, sregs[3]));
+
+                tcg_gen_movi_i32(t1, ctx->pc+6);
+
+                /* Make space for the static chain and return address.  */
+                tcg_gen_subi_i32(t2, REG(1), 4);
+                tcg_gen_qemu_st32(code, t2, ctx->memidx);
+
+                tcg_gen_subi_i32(REG(1), t2, 4);
+                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_ld_i32(cpu_pc, cpu_env,
+                               offsetof(CPUMoxieState, sregs[1]));
+                tcg_temp_free_i32(t1);
+                tcg_temp_free_i32(t2);
+                tcg_temp_free_i32(code);
+                tcg_gen_exit_tb(0);
+                ctx->bstate = BS_BRANCH;
+
+                length = 6;
+            }
+            break;
+        case 0x31: /* div.l */
+            {
+                int a = (opcode >> 4) & 0xf;
+                int b = opcode & 0xf;
+                tcg_gen_div_i32(REG(a), REG(a), REG(b));
+            }
+            break;
+        case 0x32: /* udiv.l */
+            {
+                int a = (opcode >> 4) & 0xf;
+                int b = opcode & 0xf;
+                tcg_gen_divu_i32(REG(a), 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:
+            abort();
+        }
+    }
+
+    return length;
+}
+
+/* generate intermediate code for basic block 'tb'.  */
+struct DisasContext ctx;
+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;
+
+    if (search_pc) {
+        qemu_log("search pc %d\n", search_pc);
+    }
+
+    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;
+    /* Restore delay slot state from the tb context.  */
+    ctx.hflags = (uint32_t)tb->flags; /* FIXME: maybe use 64 bits here? */
+    restore_cpu_state(cpu, &ctx);
+
+    do {
+        if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) {
+            QTAILQ_FOREACH(bp, &env->breakpoints, entry) {
+                if (ctx.pc == bp->pc) {
+                    save_cpu_state(&ctx, 1);
+                    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_ldsw_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) {
+        save_cpu_state(&ctx, ctx.bstate == BS_NONE);
+        tcg_gen_movi_tl(cpu_pc, ctx.pc);
+        gen_helper_debug(cpu_env);
+    } else {
+        switch (ctx.bstate) {
+        case BS_STOP:
+        case BS_NONE:
+            save_cpu_state(&ctx, 0);
+            gen_goto_tb(env, &ctx, 0, ctx.pc);
+            break;
+        case BS_EXCP:
+            /* AG FIXME gen_op_interrupt_restart();
+               gen_op_reset_T0();
+               gen_op_exit_tb();*/
+            break;
+        case BS_BRANCH:
+        default:
+            break;
+        }
+    }
+ done_generating:
+    ctx.last_T0_store = NULL;
+    *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];
+}