diff mbox

[moxie,3/5] Moxie target code

Message ID CACxje58b_10gT7F5AY5j=AJLHTLOLDnM9MeZYOseBQO5bbozPw@mail.gmail.com
State New
Headers show

Commit Message

Anthony Green Feb. 13, 2013, 10:26 p.m. UTC
This patch adds all of the target-moxie code for the moxie port.

From 6515851ec77b7ab139033057dbf1c93aba68fa26 Mon Sep 17 00:00:00 2001
From: Anthony Green <green@moxielogic.com>
Date: Wed, 13 Feb 2013 17:01:01 -0500
Subject: [PATCH 3/5] Add moxie target code


Signed-off-by: Anthony Green <green@moxielogic.com>
---
 target-moxie/Makefile.objs |    2 +
 target-moxie/cpu-qom.h     |   70 +++
 target-moxie/cpu.c         |   84 +++
 target-moxie/cpu.h         |  181 +++++++
 target-moxie/exec.h        |   28 +
 target-moxie/helper.c      |  136 +++++
 target-moxie/helper.h      |    6 +
 target-moxie/machine.c     |   15 +
 target-moxie/mmu.c         |   41 ++
 target-moxie/mmu.h         |   20 +
 target-moxie/op_helper.c   |   81 +++
 target-moxie/translate.c   | 1222 ++++++++++++++++++++++++++++++++++++++++++++
 12 files changed, 1886 insertions(+)
 create mode 100644 target-moxie/Makefile.objs
 create mode 100644 target-moxie/cpu-qom.h
 create mode 100644 target-moxie/cpu.c
 create mode 100644 target-moxie/cpu.h
 create mode 100644 target-moxie/exec.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

+  if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
+    qemu_log("IN: %s\n", lookup_symbol(pc_start));
+    log_target_disas(env, pc_start, ctx.pc - pc_start, 0);
+    qemu_log("\n");
+  }
+  if (qemu_loglevel_mask(CPU_LOG_TB_CPU)) {
+    qemu_log("---------------- %d %08x\n", ctx.bstate, ctx.hflags);
+  }
+#endif
+}
+
+void gen_intermediate_code(CPUMoxieState * env, struct TranslationBlock *tb)
+{
+  gen_intermediate_code_internal(env, tb, 0);
+}
+
+void gen_intermediate_code_pc(CPUMoxieState * env, struct TranslationBlock *tb)
+{
+  gen_intermediate_code_internal(env, tb, 1);
+}
+
+void restore_state_to_opc(CPUMoxieState *env, TranslationBlock *tb, int pc_pos)
+{
+    env->pc = tcg_ctx.gen_opc_pc[pc_pos];
+}

Comments

Richard Henderson Feb. 14, 2013, 11:19 p.m. UTC | #1
On 02/13/2013 02:26 PM, Anthony Green wrote:
> +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 */
> +
> +  uint32_t t0;
> +  uint32_t t1;
> +  uint32_t t2;

Are these t[0-2] used anywhere?  I couldn't immediately see.
Such named temporaries were legacy on a couple of targets,
so depending on when you copied this from, e.g. sparc, you
might have gotten these unnecessarily.

> +/* 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 cpu_sregs[256];

You're slowing down the compiler by allocating registers for all of
these SREGS, where they're only used in move to/from insns, and in the
SWI insn.  You'll be better off issuing explicit tcg_gen_ld/st_i32 in
those cases.

> +/* The code generator doesn't like lots of temporaries, so maintain our own
> +   cache for reuse within a function.  */
> +#define MAX_TEMPS 8
> +static int num_temps;
> +static TCGv temps[MAX_TEMPS];

The code generator does fine with quite a few temporaries.  The biggest
problem being that you're never freeing them.  You don't need a local
cache, just use tcg_temp_free_i32 in dead_tmp.

FYI, there are some debugging hooks that might help:
tcg_clear_temp_count, tcg_check_temp_count.  Placing these around e.g.
each individual insn can make sure that you're not leaking.

> +/* Create a new temporary and set it to the value of a CPU register.  */
> +static inline TCGv load_reg(DisasContext *s, int reg)
> +{
> +  TCGv tmp = new_tmp();
> +  tcg_gen_ld_i32(tmp, cpu_env, offsetof(CPUMoxieState, gregs[reg]));
> +  return tmp;
> +}

This function won't work, since there's no synchronization between
cpu_env memory and tcg global registers.  On the good side, it's
actually unused.

Please delete all the dead code.  E.g. by not marking any functions
inline, and letting the compiler work out what should be inlined, and
warning about unused.

> +            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 = new_tmp();
> +                tcg_gen_andi_i32(t1, cc, CC_LT);
> +                tcg_gen_brcondi_i32 (TCG_COND_EQ, t1, CC_LT, l1);
> +                dead_tmp(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 = new_tmp();
> +                tcg_gen_andi_i32(t1, cc, CC_GT);
> +                tcg_gen_brcondi_i32 (TCG_COND_EQ, t1, CC_GT, l1);
> +                dead_tmp(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 = new_tmp();
> +                tcg_gen_andi_i32(t1, cc, CC_LTU);
> +                tcg_gen_brcondi_i32 (TCG_COND_EQ, t1, CC_LTU, l1);
> +                dead_tmp(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 = new_tmp();
> +                tcg_gen_andi_i32(t1, cc, CC_GTU);
> +                tcg_gen_brcondi_i32 (TCG_COND_EQ, t1, CC_GTU, l1);
> +                dead_tmp(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 = new_tmp();
> +                tcg_gen_andi_i32(t1, cc, CC_GT|CC_EQ);
> +                tcg_gen_brcondi_i32 (TCG_COND_GT, t1, 0, l1);
> +                dead_tmp(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 = new_tmp();
> +                tcg_gen_andi_i32(t1, cc, CC_LT|CC_EQ);
> +                tcg_gen_brcondi_i32 (TCG_COND_GT, t1, 0, l1);
> +                dead_tmp(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 = new_tmp();
> +                tcg_gen_andi_i32(t1, cc, CC_GTU|CC_EQ);
> +                tcg_gen_brcondi_i32 (TCG_COND_GT, t1, 0, l1);
> +                dead_tmp(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 = new_tmp();
> +                tcg_gen_andi_i32(t1, cc, CC_LTU|CC_EQ);
> +                tcg_gen_brcondi_i32 (TCG_COND_GT, t1, 0, l1);
> +                dead_tmp(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;

Consider a subroutine for all this?

> +            default:
> +              printf("* 0x%x\tForm 3 ********** 0x%x\n", ctx->pc, inst);

No raw printfs please.

> +            case 0x00: /* inc */
> +              {
> +                int a = (opcode >> 8) & 0xf;
> +                unsigned int v = (opcode & 0xff);
> +
> +                TCGv t1 = new_tmp();
> +                tcg_gen_addi_i32(t1, REG(a), v);
> +                tcg_gen_mov_i32(REG(a), t1);
> +                dead_tmp(t1);
> +              }

Note that there's no need for temporary allocation here.
Just perform the entire operation with tcg_gen_addi_i32.
Same with dec.

> +        case 0x03: /* jsra */
> +          {
> +            /* Load the stack pointer into T0.  */
> +            TCGv t1 = new_tmp();
> +            TCGv t2 = new_tmp();
> +
> +            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);

There are two exceptions that can be taken here, for the two stores.
Are you certain that REG(1) should be updated before both are handled?
Should the write to REG(1) be delayed until after the second store?

> +        case 0x04: /* ret */
> +          {
> +            TCGv t1 = new_tmp();
> +
> +            /* 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);

Similarly, should any global variable be updated before the second load?
Same comments apply to JSR and SWI.

> +        case 0x05: /* add.l */
> +          {
> +            int a = (opcode >> 4) & 0xf;
> +            int b = opcode & 0xf;
> +
> +            TCGv result = new_tmp();
> +            tcg_gen_add_i32(result, REG(a), REG(b));
> +            tcg_gen_mov_i32(REG(a), result);
> +            dead_tmp(result);

I'll quit mentioning useless temporaries.  Suffice that there are lots.

> +        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 = new_tmp();                              \
> +              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);                               \
> +              dead_tmp(t1);                                     \

Consider making use of tcg_setcond_i32 here.  It's probably new since
you wrote all this in the first place.

> +        case 0x31: /* div.l */
> +          {
> +            int a = (opcode >> 4) & 0xf;
> +            int b = opcode & 0xf;
> +
> +            TCGv result = new_tmp();
> +            tcg_gen_div_i32(result, REG(a), REG(b));
> +            tcg_gen_mov_i32(REG(a), result);
> +            dead_tmp(result);
> +          }
> +          break;

No divide by zero check?  If not generated by moxie, you'll still want
to care for it being generated by the host.  And sometimes working
around the extra INT_MIN / -1 exception you'll get from an i386 host.
Given all those, it's sometimes easier to do this in a helper.

> +#ifdef DEBUG_DISAS
> +  if (qemu_loglevel_mask(CPU_LOG_TB_CPU)) {
> +    qemu_log("------------------------------------------------\n");
> +    /* FIXME: This may print out stale hflags from env... */
> +    /* cpu_dump_state(env, logfile, fprintf, 0); */
> +  }
> +#endif

Remove this.  Proper TB_CPU logging is done in generic code.

Consider placing a tcg_gen_debug_insn_start at the beginning of decode_opc.


r~
Richard Henderson Feb. 15, 2013, 12:37 a.m. UTC | #2
On 02/14/2013 03:19 PM, Richard Henderson wrote:
>> > +            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 = new_tmp();                              \
>> > +              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);                               \
>> > +              dead_tmp(t1);                                     \
> Consider making use of tcg_setcond_i32 here.  It's probably new since
> you wrote all this in the first place.
> 

That said, I wonder if the amount of code generated here even with
setcond would be too big.

The quick and easy option is to use a helper function.  If we mark it
as-if gcc's pure, aka TCG_CALL_NO_RWG_SE, it should be relatively
efficient, as far as TCG goes:

target_long helper_cmp(target_long x, target_long y)
{
    target_ulong ux = x, uy = y;
    if (x == y) {
        return CC_EQ;
    }
    return (x < y ? CC_LT : CC_GT) | (ux < uy ? CC_LTU : CC_GTU);
}

Optimizes pretty well inside gcc:

	cmpl	%esi, %edi
	movl	$4, %eax
	je	.L11
	setl	%dl
	movzbl	%dl, %edx
	addl	$1, %edx
	cmpl	%esi, %edi
	sbbl	%eax, %eax
	andl	$8, %eax
	addl	$8, %eax
	orl	%edx, %eax
.L11:
	rep
	ret

Now, given that CC is only settable via CMP, and apparently only
readable via branches. means that we could optimize things a bit.

What if the CMP insn merely copies its inputs to TCG globals CMP1 and
CMP2?  Then the actual implementation of BEQ (et al) is

	tcg_gen_brcond_i32(TCG_COND_EQ, cpu_cmp1, cpu_cmp2, lab_true);

Since these are globals, a subsequent BGT in a different TB will still
read the same values and perform the appropriate comparison in
performing its branch.

What I don't know is how this affects the moxie exception model, of
which I can find no information.  There doesn't seem to be any
implementation of such a model in the patches sent here.

When saving/restoring the CC value, one could presumably translate the
two variables into a canonical CC code as above, and for the reverse
transformation chose two values that create any valid comparison.

			cmp1	cmp2
	EQ		0	0
	LT & LTU	0	1
	LT & GTU	-1	1
	GT & LTU	1	-1
	GT & GTU	1	0

as everything else is logically impossible.


r~
Anthony Green Feb. 27, 2013, 9:07 p.m. UTC | #3
On Thu, Feb 14, 2013 at 6:19 PM, Richard Henderson <rth@twiddle.net> wrote:
> On 02/13/2013 02:26 PM, Anthony Green wrote:
>> +        case 0x03: /* jsra */
>> +          {
>> +            /* Load the stack pointer into T0.  */
>> +            TCGv t1 = new_tmp();
>> +            TCGv t2 = new_tmp();
>> +
>> +            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);
>
> There are two exceptions that can be taken here, for the two stores.
> Are you certain that REG(1) should be updated before both are handled?
> Should the write to REG(1) be delayed until after the second store?

You raise a good point, but that's approximately how the hardware works today.
We write to $sp midway through the multi-cycle jsr instruction (before
the second write).
https://github.com/atgreen/moxie-cores/blob/master/cores/MoxieLite/moxielite.vhd#L932
Something to put on the TODO list for both hw and qemu.

>> +        case 0x04: /* ret */
>> +          {
>> +            TCGv t1 = new_tmp();
>> +
>> +            /* 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);
>
> Similarly, should any global variable be updated before the second load?
> Same comments apply to JSR and SWI.

Same answer applies.

Thanks,

AG
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-qom.h b/target-moxie/cpu-qom.h
new file mode 100644
index 0000000..f09ef75
--- /dev/null
+++ b/target-moxie/cpu-qom.h
@@ -0,0 +1,70 @@ 
+/*
+ * 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>
+ */
+#ifndef QEMU_MOXIE_CPU_QOM_H
+#define QEMU_MOXIE_CPU_QOM_H
+
+#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 >*/
+
+    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))
+
+
+#endif
diff --git a/target-moxie/cpu.c b/target-moxie/cpu.c
new file mode 100644
index 0000000..5eb2875
--- /dev/null
+++ b/target-moxie/cpu.c
@@ -0,0 +1,84 @@ 
+/*
+ * 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_initfn(Object *obj)
+{
+    MoxieCPU *cpu = MOXIE_CPU(obj);
+    CPUMoxieState *env = &cpu->env;
+
+    cpu_exec_init(env);
+}
+
+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_reset = cc->reset;
+    cc->reset = moxie_cpu_reset;
+
+    dc->vmsd = &vmstate_moxie_cpu;
+}
+
+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)
+{
+    type_register_static(&moxie_cpu_type_info);
+}
+
+type_init(moxie_cpu_register_types)
diff --git a/target-moxie/cpu.h b/target-moxie/cpu.h
new file mode 100644
index 0000000..f04833f
--- /dev/null
+++ b/target-moxie/cpu.h
@@ -0,0 +1,181 @@ 
+/*
+ *  moxie emulation
+ *
+ *  Copyright (c) 2008, 2010 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 */
+
+  uint32_t t0;
+  uint32_t t1;
+  uint32_t t2;
+
+  void *irq[8];
+
+  uint32_t nb_breakpoints;
+
+  int user_mode_only;
+  void *intc_handle;
+
+  CPU_COMMON
+
+} CPUMoxieState;
+
+#include "cpu-qom.h"
+
+MoxieCPU *cpu_moxie_init(const char *cpu_model);
+int cpu_moxie_exec(CPUMoxieState * s);
+void do_interrupt (CPUMoxieState *env);
+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/exec.h b/target-moxie/exec.h
new file mode 100644
index 0000000..e150aaf
--- /dev/null
+++ b/target-moxie/exec.h
@@ -0,0 +1,28 @@ 
+#ifndef EXEC_MOXIE_H
+#define EXEC_MOXIE_H 1
+#include "config.h"
+
+register struct CPUMoxieState *env asm(AREG0);
+
+#include "cpu.h"
+#include "exec/exec-all.h"
+
+#if !defined(CONFIG_USER_ONLY)
+#include "exec/softmmu_exec.h"
+#endif /* !defined(CONFIG_USER_ONLY) */
+
+static inline void env_to_regs(void)
+{
+}
+
+static inline void regs_to_env(void)
+{
+}
+
+static inline int cpu_halted(CPUState *env) {
+    if (!env->halted)
+        return 0;
+    return EXCP_HALTED;
+}
+
+#endif
diff --git a/target-moxie/helper.c b/target-moxie/helper.c
new file mode 100644
index 0000000..bad3f1e
--- /dev/null
+++ b/target-moxie/helper.c
@@ -0,0 +1,136 @@ 
+/*
+ *  Moxie helper routines.
+ *
+ *  Copyright (c) 2008, 2009, 2010 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);
+    printf("%s addr=%x env->pc=%x\n", __func__, address, env->pc);
+    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);
+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;
+        //      printf ("%s pc=%x %x w=%d smmu=%d\n", __func__,
env->pc, address, rw, is_softmmu);
+        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)
+{
+#if 0
+        uint32_t ebp, isr;
+        int irqnum;
+#endif
+
+        fflush(NULL);
+
+#if 0
+        printf ("exception index=%d interrupt_req=%d\n",
+                env->exception_index,
+                env->interrupt_request);
+#endif
+
+        switch (env->exception_index)
+        {
+                case EXCP_BREAK:
+                  break;
+#if 0
+                case EXCP_MMU_MISS:
+                  irqnum = 4;
+                  isr = ldl_code(ebp + irqnum * 4);
+                  env->pc = isr;
+                  break;
+
+                default:
+                {
+                  printf ("%s ebp=%x %x isr=%x %d"
+                          " ir=%x\n",
+                          __func__,
+                          ebp, ebp + irqnum * 4,
+                          isr, env->exception_index,
+                          env->interrupt_request);
+                }
+#else
+        default:
+          break;
+        }
+#endif
+}
+
+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..2d4ac7a
--- /dev/null
+++ b/target-moxie/machine.c
@@ -0,0 +1,15 @@ 
+#include "hw/hw.h"
+#include "hw/boards.h"
+
+extern QEMUMachine moxiesim_machine;
+
+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..e016544
--- /dev/null
+++ b/target-moxie/mmu.c
@@ -0,0 +1,41 @@ 
+/*
+ *  Moxie mmu emulation.
+ *
+ *  Copyright (c) 2008 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)
+{
+  if (vaddr >= 0x1000)
+    res->phy = vaddr; /* + 0x80000000; */
+  else
+    {
+      res->phy = vaddr;
+    }
+  return 0;
+}
diff --git a/target-moxie/mmu.h b/target-moxie/mmu.h
new file mode 100644
index 0000000..273a37b
--- /dev/null
+++ b/target-moxie/mmu.h
@@ -0,0 +1,20 @@ 
+#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..4ad1c3b
--- /dev/null
+++ b/target-moxie/op_helper.c
@@ -0,0 +1,81 @@ 
+/*
+ *  Moxie helper routines
+ *
+ *  Copyright (c) 2008, 2009 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..0861635
--- /dev/null
+++ b/target-moxie/translate.c
@@ -0,0 +1,1222 @@ 
+/*
+ *  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>
+
+#if 0
+#define DEBUG_DISAS
+#define DISAS_MOXIE 1
+#if DISAS_MOXIE
+#define DIS(x) x
+#else
+#define DIS(x)
+#endif
+#endif
+
+#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 cpu_sregs[256];
+static TCGv_ptr cpu_env;
+static TCGv cc;
+
+#define REG(x) (cpu_gregs[x])
+#define SREG(x) (cpu_sregs[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)
+
+/* The code generator doesn't like lots of temporaries, so maintain our own
+   cache for reuse within a function.  */
+#define MAX_TEMPS 8
+static int num_temps;
+static TCGv temps[MAX_TEMPS];
+
+/* Allocate a temporary variable.  */
+static TCGv_i32 new_tmp(void)
+{
+  TCGv tmp;
+  if (num_temps == MAX_TEMPS)
+    abort();
+
+  if (GET_TCGV_I32(temps[num_temps]))
+    return temps[num_temps++];
+
+  tmp = tcg_temp_new_i32();
+  temps[num_temps++] = tmp;
+  return tmp;
+}
+
+/* Release a temporary variable.  */
+static void dead_tmp(TCGv tmp)
+{
+  int i;
+  num_temps--;
+  i = num_temps;
+  if (TCGV_EQUAL(temps[i], tmp))
+    return;
+
+  /* Shuffle this temp to the last slot.  */
+  while (!TCGV_EQUAL(temps[i], tmp))
+    i--;
+  while (i < num_temps) {
+    temps[i] = temps[i + 1];
+    i++;
+  }
+  temps[i] = tmp;
+}
+
+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]);
+
+}
+
+static void moxie_translate_init(void)
+{
+  int i;
+  static int done_init = 0;
+  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"
+  };
+
+  static const char * const sregnames[256] = {
+    "$sw", "$ea", "$sr2", "$sr3", "$s4", "$sr5", "$sr6", "$sr7",
"$sr8", "$sr9",
+    "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX",
"$srX", "$srX",
+    "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX",
"$srX", "$srX",
+    "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX",
"$srX", "$srX",
+    "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX",
"$srX", "$srX",
+    "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX",
"$srX", "$srX",
+    "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX",
"$srX", "$srX",
+    "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX",
"$srX", "$srX",
+    "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX",
"$srX", "$srX",
+    "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX",
"$srX", "$srX",
+    "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX",
"$srX", "$srX",
+    "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX",
"$srX", "$srX",
+    "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX",
"$srX", "$srX",
+    "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX",
"$srX", "$srX",
+    "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX",
"$srX", "$srX",
+    "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX",
"$srX", "$srX",
+    "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX",
"$srX", "$srX",
+    "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX",
"$srX", "$srX",
+    "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX",
"$srX", "$srX",
+    "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX",
"$srX", "$srX",
+    "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX",
"$srX", "$srX",
+    "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX",
"$srX", "$srX",
+    "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX",
"$srX", "$srX",
+    "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX",
"$srX", "$srX",
+    "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX", "$srX",
"$srX", "$srX",
+    "$srX", "$srX", "$srX", "$srX", "$srX", "$srX"
+  };
+
+  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]);
+
+  for (i = 0; i < 256; i++)
+    cpu_sregs[i] = tcg_global_mem_new_i32(TCG_AREG0,
+                                          offsetof(CPUMoxieState, sregs[i]),
+                                          sregnames[i]);
+
+  cc = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUMoxieState, cc), "cc");
+
+  done_init = 1;
+}
+
+MoxieCPU *cpu_moxie_init (const char *cpu_model)
+{
+  MoxieCPU *cpu;
+
+  cpu = MOXIE_CPU(object_new(TYPE_MOXIE_CPU));
+  cpu_reset(CPU(cpu));
+  qemu_init_vcpu(&cpu->env);
+
+  moxie_translate_init();
+
+  return cpu;
+}
+
+#if 0
+void cpu_reset(CPUState *cpu)
+{
+  memset(env, 0, offsetof(CPUMoxieState, breakpoints));
+  tlb_flush(env, 1);
+}
+#endif
+
+static inline void restore_cpu_state (CPUMoxieState *env, 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);
+  }
+}
+
+/* Create a new temporary and set it to the value of a CPU register.  */
+static inline TCGv load_reg(DisasContext *s, int reg)
+{
+  TCGv tmp = new_tmp();
+  tcg_gen_ld_i32(tmp, cpu_env, offsetof(CPUMoxieState, gregs[reg]));
+  return tmp;
+}
+
+static int decode_opc (CPUMoxieState *env, DisasContext *ctx)
+{
+  /* Local cache for the instruction opcode.  */
+  int opcode;
+  /* Set the default instruction length.  */
+  int length = 2;
+
+  /* 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 = new_tmp();
+                tcg_gen_andi_i32(t1, cc, CC_LT);
+                tcg_gen_brcondi_i32 (TCG_COND_EQ, t1, CC_LT, l1);
+                dead_tmp(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 = new_tmp();
+                tcg_gen_andi_i32(t1, cc, CC_GT);
+                tcg_gen_brcondi_i32 (TCG_COND_EQ, t1, CC_GT, l1);
+                dead_tmp(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 = new_tmp();
+                tcg_gen_andi_i32(t1, cc, CC_LTU);
+                tcg_gen_brcondi_i32 (TCG_COND_EQ, t1, CC_LTU, l1);
+                dead_tmp(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 = new_tmp();
+                tcg_gen_andi_i32(t1, cc, CC_GTU);
+                tcg_gen_brcondi_i32 (TCG_COND_EQ, t1, CC_GTU, l1);
+                dead_tmp(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 = new_tmp();
+                tcg_gen_andi_i32(t1, cc, CC_GT|CC_EQ);
+                tcg_gen_brcondi_i32 (TCG_COND_GT, t1, 0, l1);
+                dead_tmp(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 = new_tmp();
+                tcg_gen_andi_i32(t1, cc, CC_LT|CC_EQ);
+                tcg_gen_brcondi_i32 (TCG_COND_GT, t1, 0, l1);
+                dead_tmp(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 = new_tmp();
+                tcg_gen_andi_i32(t1, cc, CC_GTU|CC_EQ);
+                tcg_gen_brcondi_i32 (TCG_COND_GT, t1, 0, l1);
+                dead_tmp(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 = new_tmp();
+                tcg_gen_andi_i32(t1, cc, CC_LTU|CC_EQ);
+                tcg_gen_brcondi_i32 (TCG_COND_GT, t1, 0, l1);
+                dead_tmp(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:
+              printf("* 0x%x\tForm 3 ********** 0x%x\n", ctx->pc, inst);
+              /* FIXME cpu_dump_state(env, logfile, fprintf, 0); */
+              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);
+
+                TCGv t1 = new_tmp();
+                tcg_gen_addi_i32(t1, REG(a), v);
+                tcg_gen_mov_i32(REG(a), t1);
+                dead_tmp(t1);
+              }
+              break;
+            case 0x01: /* dec */
+              {
+                int a = (opcode >> 8) & 0xf;
+                unsigned int v = (opcode & 0xff);
+
+                TCGv t1 = new_tmp();
+                tcg_gen_subi_i32(t1, REG(a), v);
+                tcg_gen_mov_i32(REG(a), t1);
+                dead_tmp(t1);
+              }
+              break;
+            case 0x02: /* gsr */
+              {
+                int a = (opcode >> 8) & 0xf;
+                unsigned v = (opcode & 0xff);
+                tcg_gen_mov_i32(REG(a), SREG(v));
+              }
+              break;
+            case 0x03: /* ssr */
+              {
+                int a = (opcode >> 8) & 0xf;
+                unsigned v = (opcode & 0xff);
+                tcg_gen_mov_i32(SREG(v), REG(a));
+              }
+              break;
+            default:
+              printf("* 0x%x\tForm 2 ********** 0x%x\n", ctx->pc, inst);
+              /* FIXME cpu_dump_state(env, logfile, fprintf, 0); */
+              abort();
+              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 */
+          {
+            /* Load the stack pointer into T0.  */
+            TCGv t1 = new_tmp();
+            TCGv t2 = new_tmp();
+
+            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));
+
+            dead_tmp(t1);
+            dead_tmp(t2);
+
+            ctx->bstate = BS_BRANCH;
+            length = 6;
+          }
+          break;
+        case 0x04: /* ret */
+          {
+            TCGv t1 = new_tmp();
+
+            /* 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);
+
+            dead_tmp(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;
+
+            TCGv result = new_tmp();
+            tcg_gen_add_i32(result, REG(a), REG(b));
+            tcg_gen_mov_i32(REG(a), result);
+            dead_tmp(result);
+          }
+          break;
+        case 0x06: /* push */
+          {
+            int a = (opcode >> 4) & 0xf;
+            int b = opcode & 0xf;
+
+            TCGv t1 = new_tmp();
+            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);
+            dead_tmp(t1);
+          }
+          break;
+        case 0x07: /* pop */
+          {
+            int a = (opcode >> 4) & 0xf;
+            int b = opcode & 0xf;
+            TCGv t1 = new_tmp();
+
+            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);
+            dead_tmp(t1);
+          }
+          break;
+        case 0x08: /* lda.l */
+          {
+            int reg = (opcode >> 4) & 0xf;
+
+            TCGv ptr = new_tmp();
+            tcg_gen_movi_i32(ptr, cpu_ldl_code(env, ctx->pc+2));
+            tcg_gen_qemu_ld32u(REG(reg), ptr, ctx->memidx);
+            dead_tmp(ptr);
+
+            length = 6;
+          }
+          break;
+        case 0x09: /* sta.l */
+          {
+            int val = (opcode >> 4) & 0xf;
+
+            TCGv ptr = new_tmp();
+            tcg_gen_movi_i32(ptr, cpu_ldl_code(env, ctx->pc+2));
+            tcg_gen_qemu_st32(REG(val), ptr, ctx->memidx);
+            dead_tmp(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 = new_tmp();
+            TCGv t2 = new_tmp();
+            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);
+
+            dead_tmp(t1);
+            dead_tmp(t2);
+
+            length = 6;
+          }
+          break;
+        case 0x0d: /* sto.l */
+          {
+            int a = (opcode >> 4) & 0xf;
+            int b = opcode & 0xf;
+
+            TCGv t1 = new_tmp();
+            TCGv t2 = new_tmp();
+            tcg_gen_addi_i32(t1, REG(a), cpu_ldl_code(env, ctx->pc+2));
+            tcg_gen_qemu_st32(REG(b), t1, ctx->memidx);
+
+            dead_tmp(t1);
+            dead_tmp(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 = new_tmp();                              \
+              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);                               \
+              dead_tmp(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 = new_tmp();
+            TCGv t2 = new_tmp();
+
+            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));
+            dead_tmp(t1);
+            dead_tmp(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 = new_tmp();
+            tcg_gen_movi_i32(ptr, cpu_ldl_code(env, ctx->pc+2));
+            tcg_gen_qemu_ld8u(REG(reg), ptr, ctx->memidx);
+            dead_tmp(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 = new_tmp();
+            tcg_gen_movi_i32(ptr, cpu_ldl_code(env, ctx->pc+2));
+            tcg_gen_qemu_st8(REG(val), ptr, ctx->memidx);
+            dead_tmp(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 = new_tmp();
+            tcg_gen_movi_i32(ptr, cpu_ldl_code(env, ctx->pc+2));
+            tcg_gen_qemu_ld16u(REG(reg), ptr, ctx->memidx);
+            dead_tmp(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 = new_tmp();
+            tcg_gen_movi_i32(ptr, cpu_ldl_code(env, ctx->pc+2));
+            tcg_gen_qemu_st16(REG(val), ptr, ctx->memidx);
+            dead_tmp(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;
+            TCGv t1 = new_tmp();
+            tcg_gen_and_i32(t1, REG(a), REG(b));
+            tcg_gen_mov_i32(REG(a), t1);
+            dead_tmp(t1);
+          }
+          break;
+        case 0x27: /* lshr */
+          {
+            int a = (opcode >> 4) & 0xf;
+            int b = opcode & 0xf;
+
+            TCGv result = new_tmp();
+            tcg_gen_shr_i32(result, REG(a), REG(b));
+            tcg_gen_mov_i32(REG(a), result);
+            dead_tmp(result);
+          }
+          break;
+        case 0x28: /* ashl */
+          {
+            int a = (opcode >> 4) & 0xf;
+            int b = opcode & 0xf;
+
+            TCGv result = new_tmp();
+            tcg_gen_shl_i32(result, REG(a), REG(b));
+            tcg_gen_mov_i32(REG(a), result);
+            dead_tmp(result);
+          }
+          break;
+        case 0x29: /* sub.l */
+          {
+            int a = (opcode >> 4) & 0xf;
+            int b = opcode & 0xf;
+
+            TCGv result = new_tmp();
+            tcg_gen_sub_i32(result, REG(a), REG(b));
+            tcg_gen_mov_i32(REG(a), result);
+            dead_tmp(result);
+          }
+          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;
+            TCGv t1 = new_tmp();
+            tcg_gen_or_i32(t1, REG(a), REG(b));
+            tcg_gen_mov_i32(REG(a), t1);
+            dead_tmp(t1);
+          }
+          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;
+
+            TCGv result = new_tmp();
+            tcg_gen_sar_i32(result, REG(a), REG(b));
+            tcg_gen_mov_i32(REG(a), result);
+            dead_tmp(result);
+          }
+          break;
+        case 0x2e: /* xor */
+          {
+            int a = (opcode >> 4) & 0xf;
+            int b = opcode & 0xf;
+            TCGv t1 = new_tmp();
+            tcg_gen_xor_i32(t1, REG(a), REG(b));
+            tcg_gen_mov_i32(REG(a), t1);
+            dead_tmp(t1);
+          }
+          break;
+        case 0x2f: /* mul.l */
+          {
+            int a = (opcode >> 4) & 0xf;
+            int b = opcode & 0xf;
+
+            TCGv result = new_tmp();
+            tcg_gen_mul_i32(result, REG(a), REG(b));
+            tcg_gen_mov_i32(REG(a), result);
+            dead_tmp(result);
+          }
+          break;
+        case 0x30: /* swi */
+          {
+            int val = cpu_ldl_code(env, ctx->pc+2);
+            TCGv code = new_tmp();
+
+            /* Load the stack pointer into T0.  */
+            TCGv t1 = new_tmp();
+            TCGv t2 = new_tmp();
+
+            tcg_gen_movi_i32(SREG(2), MOXIE_EX_SWI);
+            tcg_gen_movi_i32(code, val);
+            tcg_gen_mov_i32(SREG(3), code);
+
+            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_mov_i32(cpu_pc, SREG(1));
+            dead_tmp(t1);
+            dead_tmp(t2);
+            dead_tmp(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;
+
+            TCGv result = new_tmp();
+            tcg_gen_div_i32(result, REG(a), REG(b));
+            tcg_gen_mov_i32(REG(a), result);
+            dead_tmp(result);
+          }
+          break;
+        case 0x32: /* udiv.l */
+          {
+            int a = (opcode >> 4) & 0xf;
+            int b = opcode & 0xf;
+
+            TCGv result = new_tmp();
+            tcg_gen_divu_i32(result, REG(a), REG(b));
+            tcg_gen_mov_i32(REG(a), result);
+            dead_tmp(result);
+          }
+          break;
+        case 0x33: /* mod.l */
+          {
+            int a = (opcode >> 4) & 0xf;
+            int b = opcode & 0xf;
+
+            TCGv result = new_tmp();
+            tcg_gen_rem_i32(result, REG(a), REG(b));
+            tcg_gen_mov_i32(REG(a), result);
+            dead_tmp(result);
+          }
+          break;
+        case 0x34: /* umod.l */
+          {
+            int a = (opcode >> 4) & 0xf;
+            int b = opcode & 0xf;
+
+            TCGv result = new_tmp();
+            tcg_gen_remu_i32(result, REG(a), REG(b));
+            tcg_gen_mov_i32(REG(a), result);
+            dead_tmp(result);
+          }
+          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 = new_tmp();
+            TCGv t2 = new_tmp();
+            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);
+
+            dead_tmp(t1);
+            dead_tmp(t2);
+
+            length = 6;
+          }
+          break;
+        case 0x37: /* sto.b */
+          {
+            int a = (opcode >> 4) & 0xf;
+            int b = opcode & 0xf;
+
+            TCGv t1 = new_tmp();
+            TCGv t2 = new_tmp();
+            tcg_gen_addi_i32(t1, REG(a), cpu_ldl_code(env, ctx->pc+2));
+            tcg_gen_qemu_st8(REG(b), t1, ctx->memidx);
+
+            dead_tmp(t1);
+            dead_tmp(t2);
+
+            length = 6;
+          }
+          break;
+        case 0x38: /* ldo.s */
+          {
+            int a = (opcode >> 4) & 0xf;
+            int b = opcode & 0xf;
+
+            TCGv t1 = new_tmp();
+            TCGv t2 = new_tmp();
+            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);
+
+            dead_tmp(t1);
+            dead_tmp(t2);
+
+            length = 6;
+          }
+          break;
+        case 0x39: /* sto.s */
+          {
+            int a = (opcode >> 4) & 0xf;
+            int b = opcode & 0xf;
+
+            TCGv t1 = new_tmp();
+            TCGv t2 = new_tmp();
+            tcg_gen_addi_i32(t1, REG(a), cpu_ldl_code(env, ctx->pc+2));
+            tcg_gen_qemu_st16(REG(b), t1, ctx->memidx);
+
+            dead_tmp(t1);
+            dead_tmp(t2);
+
+            length = 6;
+          }
+          break;
+        default:
+          printf("* 0x%x\tForm 1 ********** 0x%x\n", ctx->pc, inst);
+          /* FIXME cpu_dump_state(env, logfile, fprintf, 0); */
+          abort();
+          break;
+        }
+    }
+
+  return length;
+}
+
+/* generate intermediate code for basic block 'tb'.  */
+struct DisasContext ctx;
+static void
+gen_intermediate_code_internal(CPUMoxieState *env, TranslationBlock *tb,
+                               int search_pc)
+{
+  DisasContext ctx;
+  target_ulong pc_start;
+  uint16_t *gen_opc_end;
+  CPUBreakpoint *bp;
+  int j, lj = -1;
+
+  /* generate intermediate code */
+  num_temps = 0;
+  memset(temps, 0, sizeof(temps));
+
+  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(env, &ctx);
+#ifdef DEBUG_DISAS
+  if (qemu_loglevel_mask(CPU_LOG_TB_CPU)) {
+    qemu_log("------------------------------------------------\n");
+    /* FIXME: This may print out stale hflags from env... */
+    /* cpu_dump_state(env, logfile, fprintf, 0); */
+  }
+#endif
+
+  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(env, &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;
+  }
+#ifdef DEBUG_DISAS