diff mbox

[2/7] nios2: Add architecture emulation support

Message ID 20160927233049.5429-2-marex@denx.de
State New
Headers show

Commit Message

Marek Vasut Sept. 27, 2016, 11:30 p.m. UTC
From: Chris Wulff <crwulff@gmail.com>

Add support for emulating Altera NiosII R1 architecture into qemu.
This patch is based on previous work by Chris Wulff from 2012 and
updated to latest mainline QEMU.

Signed-off-by: Marek Vasut <marex@denx.de>
Cc: Chris Wulff <crwulff@gmail.com>
Cc: Jeff Da Silva <jdasilva@altera.com>
Cc: Ley Foon Tan <lftan@altera.com>
Cc: Sandra Loosemore <sandra@codesourcery.com>
Cc: Yves Vandervennet <yvanderv@altera.com>
---
 hw/nios2/cpu_pic.c         |   70 +++
 target-nios2/Makefile.objs |    4 +
 target-nios2/cpu.c         |  229 +++++++
 target-nios2/cpu.h         |  267 +++++++++
 target-nios2/helper.c      |  304 ++++++++++
 target-nios2/helper.h      |   41 ++
 target-nios2/instruction.c | 1427 ++++++++++++++++++++++++++++++++++++++++++++
 target-nios2/instruction.h |  279 +++++++++
 target-nios2/machine.c     |   38 ++
 target-nios2/mmu.c         |  292 +++++++++
 target-nios2/mmu.h         |   54 ++
 target-nios2/monitor.c     |   35 ++
 target-nios2/op_helper.c   |   86 +++
 target-nios2/translate.c   |  242 ++++++++
 14 files changed, 3368 insertions(+)
 create mode 100644 hw/nios2/cpu_pic.c
 create mode 100644 target-nios2/Makefile.objs
 create mode 100644 target-nios2/cpu.c
 create mode 100644 target-nios2/cpu.h
 create mode 100644 target-nios2/helper.c
 create mode 100644 target-nios2/helper.h
 create mode 100644 target-nios2/instruction.c
 create mode 100644 target-nios2/instruction.h
 create mode 100644 target-nios2/machine.c
 create mode 100644 target-nios2/mmu.c
 create mode 100644 target-nios2/mmu.h
 create mode 100644 target-nios2/monitor.c
 create mode 100644 target-nios2/op_helper.c
 create mode 100644 target-nios2/translate.c

Comments

Richard Henderson Sept. 29, 2016, 1:05 a.m. UTC | #1
>  hw/nios2/cpu_pic.c         |   70 +++

Why is this in this patch?

>  target-nios2/instruction.c | 1427 ++++++++++++++++++++++++++++++++++++++++++++
>  target-nios2/instruction.h |  279 +++++++++
>  target-nios2/translate.c   |  242 ++++++++

Why are these files separate?

> +    if (n < 32)		/* GP regs */
> +        return gdb_get_reg32(mem_buf, env->regs[n]);
> +    else if (n == 32)	/* PC */
> +        return gdb_get_reg32(mem_buf, env->regs[R_PC]);
> +    else if (n < 49)	/* Status regs */
> +        return gdb_get_reg32(mem_buf, env->regs[n - 1]);

Use checkpatch.pl; the formatting is wrong.

There's no particular reason why R_PC needs to be 64; if you change it to 32, 
you can simplify this.

> +struct CPUNios2State {
> +    uint32_t regs[NUM_CORE_REGS];
> +
> +    /* Addresses that are hard-coded in the FPGA build settings */
> +    uint32_t reset_addr;
> +    uint32_t exception_addr;
> +    uint32_t fast_tlb_miss_addr;

These three should go after ...

> +
> +#if !defined(CONFIG_USER_ONLY)
> +    Nios2MMU mmu;
> +
> +    uint32_t irq_pending;
> +#endif
> +
> +    CPU_COMMON

... here, or even into ...

> +};
> +
> +/**
> + * Nios2CPU:
> + * @env: #CPUNios2State
> + *
> + * A Nios2 CPU.
> + */
> +typedef struct Nios2CPU {
> +    /*< private >*/
> +    CPUState parent_obj;
> +    /*< public >*/
> +
> +    CPUNios2State env;
> +    bool mmu_present;

... here.

> +static inline void t_gen_helper_raise_exception(DisasContext *dc,
> +                                                uint32_t index)

Remove all of the inline markers and let the compiler decide.

> +static void gen_check_supervisor(DisasContext *dc, TCGLabel *label)
> +{
> +    TCGLabel *l1 = gen_new_label();
> +
> +    TCGv_i32 tmp = tcg_temp_new();
> +    tcg_gen_andi_tl(tmp, dc->cpu_R[CR_STATUS], CR_STATUS_U);
> +    tcg_gen_brcond_tl(TCG_COND_EQ, dc->cpu_R[R_ZERO], tmp, l1);
> +    t_gen_helper_raise_exception(dc, EXCP_SUPERI);
> +    tcg_gen_br(label);

The supervisor check should be done at translate time by checking dc->tb->flags 
& CR_STATUS_U.

> +#ifdef CALL_TRACING
> +    TCGv_i32 tmp = tcg_const_i32(dc->pc);
> +    TCGv_i32 tmp2 = tcg_const_i32((dc->pc & 0xF0000000) | (instr->imm26 * 4));
> +    gen_helper_call_status(tmp, tmp2);
> +    tcg_temp_free_i32(tmp);
> +    tcg_temp_free_i32(tmp2);
> +#endif

What's the point of this?

> +/*
> + * I-Type instructions
> + */
> +
> +/* rB <- 0x000000 : Mem8[rA + @(IMM16)] */
> +static void ldbu(DisasContext *dc, uint32_t code)
> +{
> +    I_TYPE(instr, code);
> +
> +    TCGv addr = tcg_temp_new();
> +    tcg_gen_addi_tl(addr, dc->cpu_R[instr->a],
> +                    (int32_t)((int16_t)instr->imm16));
> +
> +    tcg_gen_qemu_ld8u(dc->cpu_R[instr->b], addr, dc->mem_idx);
> +
> +    tcg_temp_free(addr);
> +}

You should have helper functions so that you don't have to replicate 12 line 
functions.  Use the tcg_gen_qemu_ld_tl function, and the TCGMemOp flags to help 
merge all load instructions, and all store instructions.

> +/* rB <- rA + IMM16 */
> +static void addi(DisasContext *dc, uint32_t code)
> +{
> +    I_TYPE(instr, code);
> +
> +    TCGv imm = tcg_temp_new();
> +    tcg_gen_addi_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a],
> +                    (int32_t)((int16_t)instr->imm16));

The double cast is pointless, as are the extra parenthesis.

You're not doing anything to make sure that r0 reads as 0, and ignores writes. 
You need to look at target-alpha, target-mips, or target-sparc to see various 
ways in which a zero register may be handled.

You should handle the special case of movi, documented as addi rb, r0, imm, so 
that the common method of loading a small value does not require extra tcg opcodes.

Similarly for the other (common) aliases, like mov and nop, which are both 
aliased to add.

> +/* Initializes the data cache line currently caching address rA + @(IMM16) */
> +static void initda(DisasContext *dc __attribute__((unused)),
> +                   uint32_t code __attribute__((unused)))
> +{
> +    /* TODO */
> +}

This will always be a nop for qemu.

> +static void blt(DisasContext *dc, uint32_t code)
> +{
> +    I_TYPE(instr, code);
> +
> +    TCGLabel *l1 = gen_new_label();
> +
> +    tcg_gen_brcond_tl(TCG_COND_LT, dc->cpu_R[instr->a],
> +                      dc->cpu_R[instr->b], l1);
> +
> +    gen_goto_tb(dc, 0, dc->pc + 4);
> +
> +    gen_set_label(l1);
> +
> +    gen_goto_tb(dc, 1, dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> +
> +    dc->is_jmp = DISAS_TB_JUMP;
> +}

These really require a helper function.

> +/* rB <- 0x000000 : Mem8[rA + @(IMM16)] (bypassing cache) */
> +static void ldbuio(DisasContext *dc, uint32_t code)

Reuse ldbu; qemu will never implement caches.

> +/* rB <- (rA * @(IMM16))(31..0) */
> +static void muli(DisasContext *dc, uint32_t code)
> +{
> +    I_TYPE(instr, code);
> +
> +    TCGv imm = tcg_temp_new();
> +    tcg_gen_muli_i32(dc->cpu_R[instr->b], dc->cpu_R[instr->a],
> +                     (int32_t)((int16_t)instr->imm16));
> +    tcg_temp_free(imm);

imm is unused.

> +static const Nios2Instruction i_type_instructions[I_TYPE_COUNT] = {
> +    [CALL]    = INSTRUCTION(call),
> +    [JMPI]    = INSTRUCTION(jmpi),
> +    [0x02]    = INSTRUCTION_ILLEGAL(),
> +    [LDBU]    = INSTRUCTION(ldbu),
> +    [ADDI]    = INSTRUCTION(addi),
> +    [STB]     = INSTRUCTION(stb),
> +    [BR]      = INSTRUCTION(br),
> +    [LDB]     = INSTRUCTION(ldb),
> +    [CMPGEI]  = INSTRUCTION(cmpgei),
> +    [0x09]    = INSTRUCTION_ILLEGAL(),
> +    [0x0a]    = INSTRUCTION_ILLEGAL(),
> +    [LDHU]    = INSTRUCTION(ldhu),
> +    [ANDI]    = INSTRUCTION(andi),
> +    [STH]     = INSTRUCTION(sth),
> +    [BGE]     = INSTRUCTION(bge),
> +    [LDH]     = INSTRUCTION(ldh),
> +    [CMPLTI]  = INSTRUCTION(cmplti),
> +    [0x11]    = INSTRUCTION_ILLEGAL(),
> +    [0x12]    = INSTRUCTION_ILLEGAL(),
> +    [INITDA]  = INSTRUCTION(initda),
> +    [ORI]     = INSTRUCTION(ori),
> +    [STW]     = INSTRUCTION(stw),
> +    [BLT]     = INSTRUCTION(blt),
> +    [LDW]     = INSTRUCTION(ldw),
> +    [CMPNEI]  = INSTRUCTION(cmpnei),
> +    [0x19]    = INSTRUCTION_ILLEGAL(),
> +    [0x1a]    = INSTRUCTION_ILLEGAL(),
> +    [FLUSHDA] = INSTRUCTION_NOP(flushda),
> +    [XORI]    = INSTRUCTION(xori),
> +    [0x1d]    = INSTRUCTION_ILLEGAL(),
> +    [BNE]     = INSTRUCTION(bne),
> +    [0x1f]    = INSTRUCTION_ILLEGAL(),
> +    [CMPEQI]  = INSTRUCTION(cmpeqi),
> +    [0x21]    = INSTRUCTION_ILLEGAL(),
> +    [0x22]    = INSTRUCTION_ILLEGAL(),
> +    [LDBUIO]  = INSTRUCTION(ldbuio),
> +    [MULI]    = INSTRUCTION(muli),
> +    [STBIO]   = INSTRUCTION(stbio),
> +    [BEQ]     = INSTRUCTION(beq),
> +    [LDBIO]   = INSTRUCTION(ldbio),
> +    [CMPGEUI] = INSTRUCTION(cmpgeui),
> +    [0x29]    = INSTRUCTION_ILLEGAL(),
> +    [0x2a]    = INSTRUCTION_ILLEGAL(),
> +    [LDHUIO]  = INSTRUCTION(ldhuio),
> +    [ANDHI]   = INSTRUCTION(andhi),
> +    [STHIO]   = INSTRUCTION(sthio),
> +    [BGEU]    = INSTRUCTION(bgeu),
> +    [LDHIO]   = INSTRUCTION(ldhio),
> +    [CMPLTUI] = INSTRUCTION(cmpltui),
> +    [0x31]    = INSTRUCTION_ILLEGAL(),
> +    [CUSTOM]  = INSTRUCTION_UNIMPLEMENTED(custom),
> +    [INITD]   = INSTRUCTION(initd),
> +    [ORHI]    = INSTRUCTION(orhi),
> +    [STWIO]   = INSTRUCTION(stwio),
> +    [BLTU]    = INSTRUCTION(bltu),
> +    [LDWIO]   = INSTRUCTION(ldwio),
> +    [RDPRS]   = INSTRUCTION_UNIMPLEMENTED(rdprs),
> +    [0x39]    = INSTRUCTION_ILLEGAL(),
> +    [R_TYPE]  = { "<R-type instruction>", handle_r_type_instr },
> +    [FLUSHD]  = INSTRUCTION_NOP(flushd),
> +    [XORHI]   = INSTRUCTION(xorhi),
> +    [0x3d]    = INSTRUCTION_ILLEGAL(),
> +    [0x3e]    = INSTRUCTION_ILLEGAL(),
> +    [0x3f]    = INSTRUCTION_ILLEGAL(),

Is there really any point to using a table, as opposed to a switch statement? 
At least with a switch you can use default to catch all of the illegals at once.

> +/* rC <- ((unsigned)rA * (unsigned)rB))(31..0) */
> +static void mulxuu(DisasContext *dc, uint32_t code)
> +{
> +    R_TYPE(instr, code);
> +
> +    TCGv_i64 t0, t1;
> +
> +    t0 = tcg_temp_new_i64();
> +    t1 = tcg_temp_new_i64();
> +
> +    tcg_gen_extu_i32_i64(t0, dc->cpu_R[instr->a]);
> +    tcg_gen_extu_i32_i64(t1, dc->cpu_R[instr->b]);
> +    tcg_gen_mul_i64(t0, t0, t1);
> +
> +    tcg_gen_shri_i64(t0, t0, 32);
> +    tcg_gen_extrl_i64_i32(dc->cpu_R[instr->c], t0);

   TCGv t0 = tcg_temp_new();
   tcg_gen_mulu2_tl(t0, dc->cpu_R[instr->c],
                    dc->cpu_R[instr->a],
                    dc->cpu_R[instr->b]);
   tcg_temp_free(t0);

> +/* rC <- ((signed)rA * (unsigned)rB))(31..0) */
> +static void mulxsu(DisasContext *dc, uint32_t code)
> +{
> +    R_TYPE(instr, code);
> +
> +    TCGv_i64 t0, t1;
> +
> +    t0 = tcg_temp_new_i64();
> +    t1 = tcg_temp_new_i64();
> +
> +    tcg_gen_ext_i32_i64(t0, dc->cpu_R[instr->a]);
> +    tcg_gen_extu_i32_i64(t1, dc->cpu_R[instr->b]);
> +    tcg_gen_mul_i64(t0, t0, t1);
> +
> +    tcg_gen_shri_i64(t0, t0, 32);
> +    tcg_gen_extrl_i64_i32(dc->cpu_R[instr->c], t0);

Similarly, using tcg_gen_mulsu2_tl.  To be fair, a patch for this was posted 
yesterday, and will be included in a tcg pull soon.  See

   https://patchwork.ozlabs.org/patch/675859/

> +static void callr(DisasContext *dc, uint32_t code)
> +{
> +    R_TYPE(instr, code);
> +
> +#ifdef CALL_TRACING
> +    TCGv_i32 tmp = tcg_const_i32(dc->pc);
> +    gen_helper_call_status(tmp, dc->cpu_R[instr->a]);
> +    tcg_temp_free_i32(tmp);
> +#endif
> +
> +    tcg_gen_movi_tl(dc->cpu_R[R_RA], dc->pc + 4);
> +    tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[instr->a]);

Does the hardware actually prevent rA == RA like this?  The documentation is 
unclear.  If the hardware actually performs the described actions in parallel, 
then you need to swap these two lines.

> +/* rC <- ((signed)rA * (signed)rB))(31..0) */
> +static void mulxss(DisasContext *dc, uint32_t code)
> +{
> +    R_TYPE(instr, code);
> +
> +    TCGv_i64 t0, t1;
> +
> +    t0 = tcg_temp_new_i64();
> +    t1 = tcg_temp_new_i64();
> +
> +    tcg_gen_ext_i32_i64(t0, dc->cpu_R[instr->a]);
> +    tcg_gen_ext_i32_i64(t1, dc->cpu_R[instr->b]);
> +    tcg_gen_mul_i64(t0, t0, t1);

Similarly, tcg_gen_muls2_tl.

> +/* rC <- rA / rB */
> +static void divu(DisasContext *dc, uint32_t code)
> +{
> +    R_TYPE(instr, code);
> +
> +    gen_helper_divu(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> +                    dc->cpu_R[instr->b]);
> +}

You can perform this inline.  C.f. target-mips

Since divide-by-zero is undefined, this can be done with

   TCGv t0 = tcg_const_tl(0);
   tcg_gen_setcond_tl(TCG_COND_EQ, t0, dc->cpu_R[instr->b], t0);
   tcg_gen_or_tl(t0, t0, dc->cpu_R[instr->b]);
   tcg_gen_divu_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
   tcg_temp_free_tl(t0);

> +    gen_helper_divs(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> +                    dc->cpu_R[instr->b]);

Similarly.

> +/*
> + * bstatus ← status
> + * PIE <- 0
> + * U <- 0
> + * ba <- PC + 4
> + * PC <- break handler address
> + */
> +static void __break(DisasContext *dc, uint32_t code __attribute__((unused)))

This symbol is in the system namespace.  Just use break_insn or something.

> +static const Nios2Instruction r_type_instructions[R_TYPE_COUNT] = {
> +    [0x00]   = INSTRUCTION_ILLEGAL(),
> +    [ERET]   = INSTRUCTION(eret),
> +    [ROLI]   = INSTRUCTION(roli),
> +    [ROL]    = INSTRUCTION(rol),
> +    [FLUSHP] = INSTRUCTION(flushp),
> +    [RET]    = INSTRUCTION(ret),
> +    [NOR]    = INSTRUCTION(nor),
> +    [MULXUU] = INSTRUCTION(mulxuu),
> +    [CMPGE]  = INSTRUCTION(cmpge),
> +    [BRET]   = INSTRUCTION(bret),
> +    [0x0a]   = INSTRUCTION_ILLEGAL(),
> +    [ROR]    = INSTRUCTION(ror),
> +    [FLUSHI] = INSTRUCTION(flushi),
> +    [JMP]    = INSTRUCTION(jmp),
> +    [AND]    = INSTRUCTION(and),
> +    [0x0f]   = INSTRUCTION_ILLEGAL(),
> +    [CMPLT]  = INSTRUCTION(cmplt),
> +    [0x11]   = INSTRUCTION_ILLEGAL(),
> +    [SLLI]   = INSTRUCTION(slli),
> +    [SLL]    = INSTRUCTION(sll),
> +    [WRPRS]  = INSTRUCTION_UNIMPLEMENTED(wrprs),
> +    [0x15]   = INSTRUCTION_ILLEGAL(),
> +    [OR]     = INSTRUCTION(or),
> +    [MULXSU] = INSTRUCTION(mulxsu),
> +    [CMPNE]  = INSTRUCTION(cmpne),
> +    [0x19]   = INSTRUCTION_ILLEGAL(),
> +    [SRLI]   = INSTRUCTION(srli),
> +    [SRL]    = INSTRUCTION(srl),
> +    [NEXTPC] = INSTRUCTION(nextpc),
> +    [CALLR]  = INSTRUCTION(callr),
> +    [XOR]    = INSTRUCTION(xor),
> +    [MULXSS] = INSTRUCTION(mulxss),
> +    [CMPEQ]  = INSTRUCTION(cmpeq),
> +    [0x21]   = INSTRUCTION_ILLEGAL(),
> +    [0x22]   = INSTRUCTION_ILLEGAL(),
> +    [0x23]   = INSTRUCTION_ILLEGAL(),
> +    [DIVU]   = INSTRUCTION(divu),
> +    [DIV]    = { "div", _div },
> +    [RDCTL]  = INSTRUCTION(rdctl),
> +    [MUL]    = INSTRUCTION(mul),
> +    [CMPGEU] = INSTRUCTION(cmpgeu),
> +    [INITI]  = INSTRUCTION(initi),
> +    [0x2a]   = INSTRUCTION_ILLEGAL(),
> +    [0x2b]   = INSTRUCTION_ILLEGAL(),
> +    [0x2c]   = INSTRUCTION_ILLEGAL(),
> +    [TRAP]   = INSTRUCTION(trap),
> +    [WRCTL]  = INSTRUCTION(wrctl),
> +    [0x2f]   = INSTRUCTION_ILLEGAL(),
> +    [CMPLTU] = INSTRUCTION(cmpltu),
> +    [ADD]    = INSTRUCTION(add),
> +    [0x32]   = INSTRUCTION_ILLEGAL(),
> +    [0x33]   = INSTRUCTION_ILLEGAL(),
> +    [BREAK]  = { "break", __break },
> +    [0x35]   = INSTRUCTION_ILLEGAL(),
> +    [SYNC]   = INSTRUCTION(nop),
> +    [0x37]   = INSTRUCTION_ILLEGAL(),
> +    [0x38]   = INSTRUCTION_ILLEGAL(),
> +    [SUB]    = INSTRUCTION(sub),
> +    [SRAI]   = INSTRUCTION(srai),
> +    [SRA]    = INSTRUCTION(sra),
> +    [0x3c]   = INSTRUCTION_ILLEGAL(),
> +    [0x3d]   = INSTRUCTION_ILLEGAL(),
> +    [0x3e]   = INSTRUCTION_ILLEGAL(),
> +    [0x3f]   = INSTRUCTION_ILLEGAL(),
> +};

Similar comment wrt the I-type table.

> +const char *instruction_get_string(uint32_t code)
> +{
> +    uint32_t op = get_opcode(code);
> +
> +    if (unlikely(op >= I_TYPE_COUNT)) {
> +        return "";
> +    } else if (op == R_TYPE) {
> +        uint32_t opx = get_opxcode(code);
> +        if (unlikely(opx >= R_TYPE_COUNT)) {
> +            return "";
> +        }
> +        return r_type_instructions[opx].name;
> +    } else {
> +        return i_type_instructions[op].name;
> +    }
> +}

What is this function used for?

> +/* I-Type instruction */
> +typedef struct Nios2IType {
> +    uint32_t op:6;
> +    uint32_t imm16:16;
> +    uint32_t b:5;
> +    uint32_t a:5;
> +} QEMU_PACKED Nios2IType;

These bitfields are a non-starter.  Layout of them is non-portable in more ways 
than is worth going into here.  You must use extract32 and sextract32 to 
extract the fields.

> +
> +union i_type_u {
> +    uint32_t      v;
> +    Nios2IType i;
> +};
> +
> +#define I_TYPE(instr, op) \
> +    union i_type_u instr_u = { .v = op }; \
> +    Nios2IType *instr = &instr_u.i

You could probably still hide everything behind this macro, with an inline 
function returning a structure (defined *without* bitfields).

> +/*
> + * Return values for instruction handlers
> + */
> +#define INSTR_UNIMPL     -2  /* Unimplemented instruction */
> +#define INSTR_ERR        -1  /* Error in instruction */
> +#define PC_INC_NORMAL     0  /* Normal PC increment after instruction */
> +#define PC_INC_BY_INSTR   1  /* PC got incremented by instruction */
> +#define INSTR_BREAK       2  /* Break encountered */
> +#define INSTR_EXCEPTION 255  /* Instruction generated an exception
> +                                (the exception cause will be stored
> +                                in struct nios2 */

What's the gain over using a simple enum with sequential values?

> +/*
> + * FIXME: Convert to VMstate
> + */
> +
> +#include "qemu/osdep.h"
> +#include "hw/hw.h"
> +#include "hw/boards.h"
> +
> +void cpu_save(QEMUFile *f, void *opaque)
> +{
> +    /* TODO */
> +}
> +
> +int cpu_load(QEMUFile *f, void *opaque, int version_id)
> +{
> +    /* TODO */
> +    return 0;
> +}

Fixing this is no longer optional.

> +void helper_memalign(CPUNios2State *env, uint32_t addr, uint32_t dr, uint32_t wr, uint32_t mask)
> +{
> +    if (addr & mask) {
> +        qemu_log("unaligned access addr=%x mask=%x, wr=%d dr=r%d\n",
> +                 addr, mask, wr, dr);
> +        env->regs[CR_BADADDR] = addr;
> +        env->regs[CR_EXCEPTION] = EXCP_UNALIGN << 2;
> +        helper_raise_exception(env, EXCP_UNALIGN);
> +    }
> +}

What is this doing that cc->do_unaligned_access doesn't?

> +    /* Initialize DC */
> +    dc->cpu_env = cpu_env;
> +    dc->cpu_R   = cpu_R;

What is this assignment for?  Are you planning to implement shadow registers at 
some point?


r~
Marek Vasut Oct. 18, 2016, 3:58 a.m. UTC | #2
On 09/29/2016 03:05 AM, Richard Henderson wrote:
>>  hw/nios2/cpu_pic.c         |   70 +++
> 
> Why is this in this patch?

Ah, good catch, moved to 10m50 support patch.

>>  target-nios2/instruction.c | 1427
>> ++++++++++++++++++++++++++++++++++++++++++++
>>  target-nios2/instruction.h |  279 +++++++++
>>  target-nios2/translate.c   |  242 ++++++++
> 
> Why are these files separate?

Remnant of the old code, will merge them.

>> +    if (n < 32)        /* GP regs */
>> +        return gdb_get_reg32(mem_buf, env->regs[n]);
>> +    else if (n == 32)    /* PC */
>> +        return gdb_get_reg32(mem_buf, env->regs[R_PC]);
>> +    else if (n < 49)    /* Status regs */
>> +        return gdb_get_reg32(mem_buf, env->regs[n - 1]);
> 
> Use checkpatch.pl; the formatting is wrong.

Fixed across the entire series, thanks.

> There's no particular reason why R_PC needs to be 64; if you change it
> to 32, you can simplify this.

I believe this is in fact needed, see [1] page 18 (section 2, Register
file), quote:

"
The Nios II architecture supports a flat register file, consisting of
thirty-two 32-bit general-purpose integer
registers, and up to thirty-two 32-bit control registers. The
architecture supports supervisor and user
modes that allow system code to protect the control registers from
errant applications.
"

So the CPU supports 32 general purpose regs (R_ZERO..R_RA), then up-to
32 Control registers (CR_STATUS..CR_MPUACC) and then the PC .


[1]
https://www.altera.com/content/dam/altera-www/global/en_US/pdfs/literature/hb/nios2/n2cpu_nii5v1.pdf

>> +struct CPUNios2State {
>> +    uint32_t regs[NUM_CORE_REGS];
>> +
>> +    /* Addresses that are hard-coded in the FPGA build settings */
>> +    uint32_t reset_addr;
>> +    uint32_t exception_addr;
>> +    uint32_t fast_tlb_miss_addr;
> 
> These three should go after ...
> 
>> +
>> +#if !defined(CONFIG_USER_ONLY)
>> +    Nios2MMU mmu;
>> +
>> +    uint32_t irq_pending;
>> +#endif
>> +
>> +    CPU_COMMON
> 
> ... here, or even into ...
> 
>> +};
>> +
>> +/**
>> + * Nios2CPU:
>> + * @env: #CPUNios2State
>> + *
>> + * A Nios2 CPU.
>> + */
>> +typedef struct Nios2CPU {
>> +    /*< private >*/
>> +    CPUState parent_obj;
>> +    /*< public >*/
>> +
>> +    CPUNios2State env;
>> +    bool mmu_present;
> 
> ... here.

Moved here, it looks more sensible as it's not something one can change
at runtime.

>> +static inline void t_gen_helper_raise_exception(DisasContext *dc,
>> +                                                uint32_t index)
> 
> Remove all of the inline markers and let the compiler decide.

Done globally, thanks.

>> +static void gen_check_supervisor(DisasContext *dc, TCGLabel *label)
>> +{
>> +    TCGLabel *l1 = gen_new_label();
>> +
>> +    TCGv_i32 tmp = tcg_temp_new();
>> +    tcg_gen_andi_tl(tmp, dc->cpu_R[CR_STATUS], CR_STATUS_U);
>> +    tcg_gen_brcond_tl(TCG_COND_EQ, dc->cpu_R[R_ZERO], tmp, l1);
>> +    t_gen_helper_raise_exception(dc, EXCP_SUPERI);
>> +    tcg_gen_br(label);
> 
> The supervisor check should be done at translate time by checking
> dc->tb->flags & CR_STATUS_U.

Fixed.

>> +#ifdef CALL_TRACING
>> +    TCGv_i32 tmp = tcg_const_i32(dc->pc);
>> +    TCGv_i32 tmp2 = tcg_const_i32((dc->pc & 0xF0000000) |
>> (instr->imm26 * 4));
>> +    gen_helper_call_status(tmp, tmp2);
>> +    tcg_temp_free_i32(tmp);
>> +    tcg_temp_free_i32(tmp2);
>> +#endif
> 
> What's the point of this?

Seems like some stale debug helper, removed, thanks.

>> +/*
>> + * I-Type instructions
>> + */
>> +
>> +/* rB <- 0x000000 : Mem8[rA + @(IMM16)] */
>> +static void ldbu(DisasContext *dc, uint32_t code)
>> +{
>> +    I_TYPE(instr, code);
>> +
>> +    TCGv addr = tcg_temp_new();
>> +    tcg_gen_addi_tl(addr, dc->cpu_R[instr->a],
>> +                    (int32_t)((int16_t)instr->imm16));
>> +
>> +    tcg_gen_qemu_ld8u(dc->cpu_R[instr->b], addr, dc->mem_idx);
>> +
>> +    tcg_temp_free(addr);
>> +}
> 
> You should have helper functions so that you don't have to replicate 12
> line functions.  Use the tcg_gen_qemu_ld_tl function, and the TCGMemOp
> flags to help merge all load instructions, and all store instructions.

Oh nice, thanks.

>> +/* rB <- rA + IMM16 */
>> +static void addi(DisasContext *dc, uint32_t code)
>> +{
>> +    I_TYPE(instr, code);
>> +
>> +    TCGv imm = tcg_temp_new();
>> +    tcg_gen_addi_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a],
>> +                    (int32_t)((int16_t)instr->imm16));
> 
> The double cast is pointless, as are the extra parenthesis.

The int16_t cast is needed as the imm16 is signed value. The int32()
cast is indeed pointless, so removed. Thanks.

> You're not doing anything to make sure that r0 reads as 0, and ignores
> writes. You need to look at target-alpha, target-mips, or target-sparc
> to see various ways in which a zero register may be handled.

Any hint on this one ?

> You should handle the special case of movi, documented as addi rb, r0,
> imm, so that the common method of loading a small value does not require
> extra tcg opcodes.
> 
> Similarly for the other (common) aliases, like mov and nop, which are
> both aliased to add.

Fixed

>> +/* Initializes the data cache line currently caching address rA +
>> @(IMM16) */
>> +static void initda(DisasContext *dc __attribute__((unused)),
>> +                   uint32_t code __attribute__((unused)))
>> +{
>> +    /* TODO */
>> +}
> 
> This will always be a nop for qemu.

Replaced with NOP instructions.

>> +static void blt(DisasContext *dc, uint32_t code)
>> +{
>> +    I_TYPE(instr, code);
>> +
>> +    TCGLabel *l1 = gen_new_label();
>> +
>> +    tcg_gen_brcond_tl(TCG_COND_LT, dc->cpu_R[instr->a],
>> +                      dc->cpu_R[instr->b], l1);
>> +
>> +    gen_goto_tb(dc, 0, dc->pc + 4);
>> +
>> +    gen_set_label(l1);
>> +
>> +    gen_goto_tb(dc, 1, dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
>> +
>> +    dc->is_jmp = DISAS_TB_JUMP;
>> +}
> 
> These really require a helper function.

Yes, fixed.

>> +/* rB <- 0x000000 : Mem8[rA + @(IMM16)] (bypassing cache) */
>> +static void ldbuio(DisasContext *dc, uint32_t code)
> 
> Reuse ldbu; qemu will never implement caches.
> 
>> +/* rB <- (rA * @(IMM16))(31..0) */
>> +static void muli(DisasContext *dc, uint32_t code)
>> +{
>> +    I_TYPE(instr, code);
>> +
>> +    TCGv imm = tcg_temp_new();
>> +    tcg_gen_muli_i32(dc->cpu_R[instr->b], dc->cpu_R[instr->a],
>> +                     (int32_t)((int16_t)instr->imm16));
>> +    tcg_temp_free(imm);
> 
> imm is unused.

I reworked this some more, so fixed.

>> +static const Nios2Instruction i_type_instructions[I_TYPE_COUNT] = {
>> +    [CALL]    = INSTRUCTION(call),
>> +    [JMPI]    = INSTRUCTION(jmpi),
>> +    [0x02]    = INSTRUCTION_ILLEGAL(),
>> +    [LDBU]    = INSTRUCTION(ldbu),
>> +    [ADDI]    = INSTRUCTION(addi),
>> +    [STB]     = INSTRUCTION(stb),
>> +    [BR]      = INSTRUCTION(br),
>> +    [LDB]     = INSTRUCTION(ldb),
>> +    [CMPGEI]  = INSTRUCTION(cmpgei),
>> +    [0x09]    = INSTRUCTION_ILLEGAL(),
>> +    [0x0a]    = INSTRUCTION_ILLEGAL(),
>> +    [LDHU]    = INSTRUCTION(ldhu),
>> +    [ANDI]    = INSTRUCTION(andi),
>> +    [STH]     = INSTRUCTION(sth),
>> +    [BGE]     = INSTRUCTION(bge),
>> +    [LDH]     = INSTRUCTION(ldh),
>> +    [CMPLTI]  = INSTRUCTION(cmplti),
>> +    [0x11]    = INSTRUCTION_ILLEGAL(),
>> +    [0x12]    = INSTRUCTION_ILLEGAL(),
>> +    [INITDA]  = INSTRUCTION(initda),
>> +    [ORI]     = INSTRUCTION(ori),
>> +    [STW]     = INSTRUCTION(stw),
>> +    [BLT]     = INSTRUCTION(blt),
>> +    [LDW]     = INSTRUCTION(ldw),
>> +    [CMPNEI]  = INSTRUCTION(cmpnei),
>> +    [0x19]    = INSTRUCTION_ILLEGAL(),
>> +    [0x1a]    = INSTRUCTION_ILLEGAL(),
>> +    [FLUSHDA] = INSTRUCTION_NOP(flushda),
>> +    [XORI]    = INSTRUCTION(xori),
>> +    [0x1d]    = INSTRUCTION_ILLEGAL(),
>> +    [BNE]     = INSTRUCTION(bne),
>> +    [0x1f]    = INSTRUCTION_ILLEGAL(),
>> +    [CMPEQI]  = INSTRUCTION(cmpeqi),
>> +    [0x21]    = INSTRUCTION_ILLEGAL(),
>> +    [0x22]    = INSTRUCTION_ILLEGAL(),
>> +    [LDBUIO]  = INSTRUCTION(ldbuio),
>> +    [MULI]    = INSTRUCTION(muli),
>> +    [STBIO]   = INSTRUCTION(stbio),
>> +    [BEQ]     = INSTRUCTION(beq),
>> +    [LDBIO]   = INSTRUCTION(ldbio),
>> +    [CMPGEUI] = INSTRUCTION(cmpgeui),
>> +    [0x29]    = INSTRUCTION_ILLEGAL(),
>> +    [0x2a]    = INSTRUCTION_ILLEGAL(),
>> +    [LDHUIO]  = INSTRUCTION(ldhuio),
>> +    [ANDHI]   = INSTRUCTION(andhi),
>> +    [STHIO]   = INSTRUCTION(sthio),
>> +    [BGEU]    = INSTRUCTION(bgeu),
>> +    [LDHIO]   = INSTRUCTION(ldhio),
>> +    [CMPLTUI] = INSTRUCTION(cmpltui),
>> +    [0x31]    = INSTRUCTION_ILLEGAL(),
>> +    [CUSTOM]  = INSTRUCTION_UNIMPLEMENTED(custom),
>> +    [INITD]   = INSTRUCTION(initd),
>> +    [ORHI]    = INSTRUCTION(orhi),
>> +    [STWIO]   = INSTRUCTION(stwio),
>> +    [BLTU]    = INSTRUCTION(bltu),
>> +    [LDWIO]   = INSTRUCTION(ldwio),
>> +    [RDPRS]   = INSTRUCTION_UNIMPLEMENTED(rdprs),
>> +    [0x39]    = INSTRUCTION_ILLEGAL(),
>> +    [R_TYPE]  = { "<R-type instruction>", handle_r_type_instr },
>> +    [FLUSHD]  = INSTRUCTION_NOP(flushd),
>> +    [XORHI]   = INSTRUCTION(xorhi),
>> +    [0x3d]    = INSTRUCTION_ILLEGAL(),
>> +    [0x3e]    = INSTRUCTION_ILLEGAL(),
>> +    [0x3f]    = INSTRUCTION_ILLEGAL(),
> 
> Is there really any point to using a table, as opposed to a switch
> statement? At least with a switch you can use default to catch all of
> the illegals at once.

I don't need to track the instructions encodings separately, since the
NIOS2 only supports 40 I-/R-instructions .

>> +/* rC <- ((unsigned)rA * (unsigned)rB))(31..0) */
>> +static void mulxuu(DisasContext *dc, uint32_t code)
>> +{
>> +    R_TYPE(instr, code);
>> +
>> +    TCGv_i64 t0, t1;
>> +
>> +    t0 = tcg_temp_new_i64();
>> +    t1 = tcg_temp_new_i64();
>> +
>> +    tcg_gen_extu_i32_i64(t0, dc->cpu_R[instr->a]);
>> +    tcg_gen_extu_i32_i64(t1, dc->cpu_R[instr->b]);
>> +    tcg_gen_mul_i64(t0, t0, t1);
>> +
>> +    tcg_gen_shri_i64(t0, t0, 32);
>> +    tcg_gen_extrl_i64_i32(dc->cpu_R[instr->c], t0);
> 
>   TCGv t0 = tcg_temp_new();
>   tcg_gen_mulu2_tl(t0, dc->cpu_R[instr->c],
>                    dc->cpu_R[instr->a],
>                    dc->cpu_R[instr->b]);
>   tcg_temp_free(t0);
> 
>> +/* rC <- ((signed)rA * (unsigned)rB))(31..0) */
>> +static void mulxsu(DisasContext *dc, uint32_t code)
>> +{
>> +    R_TYPE(instr, code);
>> +
>> +    TCGv_i64 t0, t1;
>> +
>> +    t0 = tcg_temp_new_i64();
>> +    t1 = tcg_temp_new_i64();
>> +
>> +    tcg_gen_ext_i32_i64(t0, dc->cpu_R[instr->a]);
>> +    tcg_gen_extu_i32_i64(t1, dc->cpu_R[instr->b]);
>> +    tcg_gen_mul_i64(t0, t0, t1);
>> +
>> +    tcg_gen_shri_i64(t0, t0, 32);
>> +    tcg_gen_extrl_i64_i32(dc->cpu_R[instr->c], t0);
> 
> Similarly, using tcg_gen_mulsu2_tl.  To be fair, a patch for this was
> posted yesterday, and will be included in a tcg pull soon.  See
> 
>   https://patchwork.ozlabs.org/patch/675859/

Oh, nice, I picked this patch and replaced all those mulx*() functions.

>> +static void callr(DisasContext *dc, uint32_t code)
>> +{
>> +    R_TYPE(instr, code);
>> +
>> +#ifdef CALL_TRACING
>> +    TCGv_i32 tmp = tcg_const_i32(dc->pc);
>> +    gen_helper_call_status(tmp, dc->cpu_R[instr->a]);
>> +    tcg_temp_free_i32(tmp);
>> +#endif
>> +
>> +    tcg_gen_movi_tl(dc->cpu_R[R_RA], dc->pc + 4);
>> +    tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[instr->a]);
> 
> Does the hardware actually prevent rA == RA like this?  The
> documentation is unclear.  If the hardware actually performs the
> described actions in parallel, then you need to swap these two lines.

That would only make sense, so swapped.

>> +/* rC <- ((signed)rA * (signed)rB))(31..0) */
>> +static void mulxss(DisasContext *dc, uint32_t code)
>> +{
>> +    R_TYPE(instr, code);
>> +
>> +    TCGv_i64 t0, t1;
>> +
>> +    t0 = tcg_temp_new_i64();
>> +    t1 = tcg_temp_new_i64();
>> +
>> +    tcg_gen_ext_i32_i64(t0, dc->cpu_R[instr->a]);
>> +    tcg_gen_ext_i32_i64(t1, dc->cpu_R[instr->b]);
>> +    tcg_gen_mul_i64(t0, t0, t1);
> 
> Similarly, tcg_gen_muls2_tl.

Fixed.

>> +/* rC <- rA / rB */
>> +static void divu(DisasContext *dc, uint32_t code)
>> +{
>> +    R_TYPE(instr, code);
>> +
>> +    gen_helper_divu(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
>> +                    dc->cpu_R[instr->b]);
>> +}
> 
> You can perform this inline.  C.f. target-mips
> 
> Since divide-by-zero is undefined, this can be done with
> 
>   TCGv t0 = tcg_const_tl(0);
>   tcg_gen_setcond_tl(TCG_COND_EQ, t0, dc->cpu_R[instr->b], t0);
>   tcg_gen_or_tl(t0, t0, dc->cpu_R[instr->b]);
>   tcg_gen_divu_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
>   tcg_temp_free_tl(t0);

Just so I get it completely, isn't this handled somehow by the
tcg_gen_divu_tl() itself ?

>> +    gen_helper_divs(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
>> +                    dc->cpu_R[instr->b]);
> 
> Similarly.

Fixed

>> +/*
>> + * bstatus ← status
>> + * PIE <- 0
>> + * U <- 0
>> + * ba <- PC + 4
>> + * PC <- break handler address
>> + */
>> +static void __break(DisasContext *dc, uint32_t code
>> __attribute__((unused)))
> 
> This symbol is in the system namespace.  Just use break_insn or something.

Fixed

>> +static const Nios2Instruction r_type_instructions[R_TYPE_COUNT] = {
>> +    [0x00]   = INSTRUCTION_ILLEGAL(),
>> +    [ERET]   = INSTRUCTION(eret),
>> +    [ROLI]   = INSTRUCTION(roli),
>> +    [ROL]    = INSTRUCTION(rol),
>> +    [FLUSHP] = INSTRUCTION(flushp),
>> +    [RET]    = INSTRUCTION(ret),
>> +    [NOR]    = INSTRUCTION(nor),
>> +    [MULXUU] = INSTRUCTION(mulxuu),
>> +    [CMPGE]  = INSTRUCTION(cmpge),
>> +    [BRET]   = INSTRUCTION(bret),
>> +    [0x0a]   = INSTRUCTION_ILLEGAL(),
>> +    [ROR]    = INSTRUCTION(ror),
>> +    [FLUSHI] = INSTRUCTION(flushi),
>> +    [JMP]    = INSTRUCTION(jmp),
>> +    [AND]    = INSTRUCTION(and),
>> +    [0x0f]   = INSTRUCTION_ILLEGAL(),
>> +    [CMPLT]  = INSTRUCTION(cmplt),
>> +    [0x11]   = INSTRUCTION_ILLEGAL(),
>> +    [SLLI]   = INSTRUCTION(slli),
>> +    [SLL]    = INSTRUCTION(sll),
>> +    [WRPRS]  = INSTRUCTION_UNIMPLEMENTED(wrprs),
>> +    [0x15]   = INSTRUCTION_ILLEGAL(),
>> +    [OR]     = INSTRUCTION(or),
>> +    [MULXSU] = INSTRUCTION(mulxsu),
>> +    [CMPNE]  = INSTRUCTION(cmpne),
>> +    [0x19]   = INSTRUCTION_ILLEGAL(),
>> +    [SRLI]   = INSTRUCTION(srli),
>> +    [SRL]    = INSTRUCTION(srl),
>> +    [NEXTPC] = INSTRUCTION(nextpc),
>> +    [CALLR]  = INSTRUCTION(callr),
>> +    [XOR]    = INSTRUCTION(xor),
>> +    [MULXSS] = INSTRUCTION(mulxss),
>> +    [CMPEQ]  = INSTRUCTION(cmpeq),
>> +    [0x21]   = INSTRUCTION_ILLEGAL(),
>> +    [0x22]   = INSTRUCTION_ILLEGAL(),
>> +    [0x23]   = INSTRUCTION_ILLEGAL(),
>> +    [DIVU]   = INSTRUCTION(divu),
>> +    [DIV]    = { "div", _div },
>> +    [RDCTL]  = INSTRUCTION(rdctl),
>> +    [MUL]    = INSTRUCTION(mul),
>> +    [CMPGEU] = INSTRUCTION(cmpgeu),
>> +    [INITI]  = INSTRUCTION(initi),
>> +    [0x2a]   = INSTRUCTION_ILLEGAL(),
>> +    [0x2b]   = INSTRUCTION_ILLEGAL(),
>> +    [0x2c]   = INSTRUCTION_ILLEGAL(),
>> +    [TRAP]   = INSTRUCTION(trap),
>> +    [WRCTL]  = INSTRUCTION(wrctl),
>> +    [0x2f]   = INSTRUCTION_ILLEGAL(),
>> +    [CMPLTU] = INSTRUCTION(cmpltu),
>> +    [ADD]    = INSTRUCTION(add),
>> +    [0x32]   = INSTRUCTION_ILLEGAL(),
>> +    [0x33]   = INSTRUCTION_ILLEGAL(),
>> +    [BREAK]  = { "break", __break },
>> +    [0x35]   = INSTRUCTION_ILLEGAL(),
>> +    [SYNC]   = INSTRUCTION(nop),
>> +    [0x37]   = INSTRUCTION_ILLEGAL(),
>> +    [0x38]   = INSTRUCTION_ILLEGAL(),
>> +    [SUB]    = INSTRUCTION(sub),
>> +    [SRAI]   = INSTRUCTION(srai),
>> +    [SRA]    = INSTRUCTION(sra),
>> +    [0x3c]   = INSTRUCTION_ILLEGAL(),
>> +    [0x3d]   = INSTRUCTION_ILLEGAL(),
>> +    [0x3e]   = INSTRUCTION_ILLEGAL(),
>> +    [0x3f]   = INSTRUCTION_ILLEGAL(),
>> +};
> 
> Similar comment wrt the I-type table.
> 
>> +const char *instruction_get_string(uint32_t code)
>> +{
>> +    uint32_t op = get_opcode(code);
>> +
>> +    if (unlikely(op >= I_TYPE_COUNT)) {
>> +        return "";
>> +    } else if (op == R_TYPE) {
>> +        uint32_t opx = get_opxcode(code);
>> +        if (unlikely(opx >= R_TYPE_COUNT)) {
>> +            return "";
>> +        }
>> +        return r_type_instructions[opx].name;
>> +    } else {
>> +        return i_type_instructions[op].name;
>> +    }
>> +}
> 
> What is this function used for?

Nothing, removed.

>> +/* I-Type instruction */
>> +typedef struct Nios2IType {
>> +    uint32_t op:6;
>> +    uint32_t imm16:16;
>> +    uint32_t b:5;
>> +    uint32_t a:5;
>> +} QEMU_PACKED Nios2IType;
> 
> These bitfields are a non-starter.  Layout of them is non-portable in
> more ways than is worth going into here.  You must use extract32 and
> sextract32 to extract the fields.

Fixed

>> +
>> +union i_type_u {
>> +    uint32_t      v;
>> +    Nios2IType i;
>> +};
>> +
>> +#define I_TYPE(instr, op) \
>> +    union i_type_u instr_u = { .v = op }; \
>> +    Nios2IType *instr = &instr_u.i
> 
> You could probably still hide everything behind this macro, with an
> inline function returning a structure (defined *without* bitfields).

Yes, fixed.

>> +/*
>> + * Return values for instruction handlers
>> + */
>> +#define INSTR_UNIMPL     -2  /* Unimplemented instruction */
>> +#define INSTR_ERR        -1  /* Error in instruction */
>> +#define PC_INC_NORMAL     0  /* Normal PC increment after instruction */
>> +#define PC_INC_BY_INSTR   1  /* PC got incremented by instruction */
>> +#define INSTR_BREAK       2  /* Break encountered */
>> +#define INSTR_EXCEPTION 255  /* Instruction generated an exception
>> +                                (the exception cause will be stored
>> +                                in struct nios2 */
> 
> What's the gain over using a simple enum with sequential values?

These are unused, so removed.

>> +/*
>> + * FIXME: Convert to VMstate
>> + */
>> +
>> +#include "qemu/osdep.h"
>> +#include "hw/hw.h"
>> +#include "hw/boards.h"
>> +
>> +void cpu_save(QEMUFile *f, void *opaque)
>> +{
>> +    /* TODO */
>> +}
>> +
>> +int cpu_load(QEMUFile *f, void *opaque, int version_id)
>> +{
>> +    /* TODO */
>> +    return 0;
>> +}
> 
> Fixing this is no longer optional.

This file wasn't even compiled, so removed.

>> +void helper_memalign(CPUNios2State *env, uint32_t addr, uint32_t dr,
>> uint32_t wr, uint32_t mask)
>> +{
>> +    if (addr & mask) {
>> +        qemu_log("unaligned access addr=%x mask=%x, wr=%d dr=r%d\n",
>> +                 addr, mask, wr, dr);
>> +        env->regs[CR_BADADDR] = addr;
>> +        env->regs[CR_EXCEPTION] = EXCP_UNALIGN << 2;
>> +        helper_raise_exception(env, EXCP_UNALIGN);
>> +    }
>> +}
> 
> What is this doing that cc->do_unaligned_access doesn't?

Switched to do_unaligned_access, thanks.

>> +    /* Initialize DC */
>> +    dc->cpu_env = cpu_env;
>> +    dc->cpu_R   = cpu_R;
> 
> What is this assignment for?  Are you planning to implement shadow
> registers at some point?

Eventually yes, but so far I haven't seen that used at all.

> r~

Thanks for the review!
Richard Henderson Oct. 18, 2016, 3:31 p.m. UTC | #3
On 10/17/2016 08:58 PM, Marek Vasut wrote:
>> There's no particular reason why R_PC needs to be 64; if you change it
>> to 32, you can simplify this.
>
> I believe this is in fact needed, see [1] page 18 (section 2, Register
> file), quote:
>
> "
> The Nios II architecture supports a flat register file, consisting of
> thirty-two 32-bit general-purpose integer
> registers, and up to thirty-two 32-bit control registers. The
> architecture supports supervisor and user
> modes that allow system code to protect the control registers from
> errant applications.
> "
>
> So the CPU supports 32 general purpose regs (R_ZERO..R_RA), then up-to
> 32 Control registers (CR_STATUS..CR_MPUACC) and then the PC .

The architecture manual doesn't imply anything about the numbering of these 
registers.

>> You're not doing anything to make sure that r0 reads as 0, and ignores
>> writes. You need to look at target-alpha, target-mips, or target-sparc
>> to see various ways in which a zero register may be handled.
>
> Any hint on this one ?

Well, there's three hints right there.  But look at target-alpha, and the 
functions load_gpr and dest_gpr (note that for alpha, 31 is the zero register).

>> Since divide-by-zero is undefined, this can be done with
>>
>>   TCGv t0 = tcg_const_tl(0);
>>   tcg_gen_setcond_tl(TCG_COND_EQ, t0, dc->cpu_R[instr->b], t0);
>>   tcg_gen_or_tl(t0, t0, dc->cpu_R[instr->b]);
>>   tcg_gen_divu_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
>>   tcg_temp_free_tl(t0);
>
> Just so I get it completely, isn't this handled somehow by the
> tcg_gen_divu_tl() itself ?

No, tcg_gen_divu_tl is a plain host divide instruction.

For guests that require exceptions to be raised, we handle those manually 
beforehand.  At which point extra checks in tcg_gen_divu_tl are wasted.



r~
Marek Vasut Oct. 18, 2016, 6:32 p.m. UTC | #4
On 10/18/2016 05:31 PM, Richard Henderson wrote:
> On 10/17/2016 08:58 PM, Marek Vasut wrote:
>>> There's no particular reason why R_PC needs to be 64; if you change it
>>> to 32, you can simplify this.
>>
>> I believe this is in fact needed, see [1] page 18 (section 2, Register
>> file), quote:
>>
>> "
>> The Nios II architecture supports a flat register file, consisting of
>> thirty-two 32-bit general-purpose integer
>> registers, and up to thirty-two 32-bit control registers. The
>> architecture supports supervisor and user
>> modes that allow system code to protect the control registers from
>> errant applications.
>> "
>>
>> So the CPU supports 32 general purpose regs (R_ZERO..R_RA), then up-to
>> 32 Control registers (CR_STATUS..CR_MPUACC) and then the PC .
> 
> The architecture manual doesn't imply anything about the numbering of
> these registers.

But the instruction encoding does, so I can use the field from the
instruction to directly index the register array.

>>> You're not doing anything to make sure that r0 reads as 0, and ignores
>>> writes. You need to look at target-alpha, target-mips, or target-sparc
>>> to see various ways in which a zero register may be handled.
>>
>> Any hint on this one ?
> 
> Well, there's three hints right there.  But look at target-alpha, and
> the functions load_gpr and dest_gpr (note that for alpha, 31 is the zero
> register).

Thanks, I hope this is fixed now, although I mostly special-case the
R_ZERO handling throughout the code. Any writes to R_ZERO are now
ignored and any usage is converted to mov/movi instructions where
applicable.

>>> Since divide-by-zero is undefined, this can be done with
>>>
>>>   TCGv t0 = tcg_const_tl(0);
>>>   tcg_gen_setcond_tl(TCG_COND_EQ, t0, dc->cpu_R[instr->b], t0);
>>>   tcg_gen_or_tl(t0, t0, dc->cpu_R[instr->b]);
>>>   tcg_gen_divu_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
>>>   tcg_temp_free_tl(t0);
>>
>> Just so I get it completely, isn't this handled somehow by the
>> tcg_gen_divu_tl() itself ?
> 
> No, tcg_gen_divu_tl is a plain host divide instruction.
> 
> For guests that require exceptions to be raised, we handle those
> manually beforehand.  At which point extra checks in tcg_gen_divu_tl are
> wasted.

OK, got it, thanks!
Richard Henderson Oct. 18, 2016, 8:44 p.m. UTC | #5
On 10/18/2016 11:32 AM, Marek Vasut wrote:
> But the instruction encoding does, so I can use the field from the
> instruction to directly index the register array.

Well, no, you can't.

In fact, it would be cleaner to have multiple arrays -- one for general 
registers and one for control registers -- that you *could* actually directly 
index.  There are only two instructions that touch the control registers, rdctl 
and wrctl.  In those instructions you're having to explicitly add 32.

> Thanks, I hope this is fixed now, although I mostly special-case the
> R_ZERO handling throughout the code. Any writes to R_ZERO are now
> ignored and any usage is converted to mov/movi instructions where
> applicable.

We've done that in the past, but in the end it is much cleaner to minimize the 
number of places in which you have to check for R_ZERO.


r~
Marek Vasut Oct. 18, 2016, 10:05 p.m. UTC | #6
On 10/18/2016 10:44 PM, Richard Henderson wrote:
> On 10/18/2016 11:32 AM, Marek Vasut wrote:
>> But the instruction encoding does, so I can use the field from the
>> instruction to directly index the register array.
> 
> Well, no, you can't.
> 
> In fact, it would be cleaner to have multiple arrays -- one for general
> registers and one for control registers -- that you *could* actually
> directly index.  There are only two instructions that touch the control
> registers, rdctl and wrctl.  In those instructions you're having to
> explicitly add 32.

Well yes, that's true.

>> Thanks, I hope this is fixed now, although I mostly special-case the
>> R_ZERO handling throughout the code. Any writes to R_ZERO are now
>> ignored and any usage is converted to mov/movi instructions where
>> applicable.
> 
> We've done that in the past, but in the end it is much cleaner to
> minimize the number of places in which you have to check for R_ZERO.

Isn't it a bit more performant if you generate as little TCG
instructions as possible ?

> r~
Richard Henderson Oct. 19, 2016, 1:24 a.m. UTC | #7
On 10/18/2016 03:05 PM, Marek Vasut wrote:
>>> Thanks, I hope this is fixed now, although I mostly special-case the
>>> R_ZERO handling throughout the code. Any writes to R_ZERO are now
>>> ignored and any usage is converted to mov/movi instructions where
>>> applicable.
>>
>> We've done that in the past, but in the end it is much cleaner to
>> minimize the number of places in which you have to check for R_ZERO.
>
> Isn't it a bit more performant if you generate as little TCG
> instructions as possible ?

Well, yes and no.

We're always going to run the tcg optimizers, so the resulting code should be 
the same either way.

Processing a little more data can be preferable to fewer branch prediction 
failures.  And the best way to avoid those is to not have the branch at all. 
Especially when it's unlikely that the data will be created in the first place.


r~
Marek Vasut Oct. 19, 2016, 2:31 a.m. UTC | #8
On 10/19/2016 03:24 AM, Richard Henderson wrote:
> On 10/18/2016 03:05 PM, Marek Vasut wrote:
>>>> Thanks, I hope this is fixed now, although I mostly special-case the
>>>> R_ZERO handling throughout the code. Any writes to R_ZERO are now
>>>> ignored and any usage is converted to mov/movi instructions where
>>>> applicable.
>>>
>>> We've done that in the past, but in the end it is much cleaner to
>>> minimize the number of places in which you have to check for R_ZERO.
>>
>> Isn't it a bit more performant if you generate as little TCG
>> instructions as possible ?
> 
> Well, yes and no.
> 
> We're always going to run the tcg optimizers, so the resulting code
> should be the same either way.

I see, I'd have to dig deeper into the TCG.

> Processing a little more data can be preferable to fewer branch
> prediction failures.  And the best way to avoid those is to not have the
> branch at all. Especially when it's unlikely that the data will be
> created in the first place.

OK, but I need to make sure stores to ZERO register are ignored, right ?
Richard Henderson Oct. 19, 2016, 4:18 p.m. UTC | #9
On 10/18/2016 07:31 PM, Marek Vasut wrote:
>> Processing a little more data can be preferable to fewer branch
>> prediction failures.  And the best way to avoid those is to not have the
>> branch at all. Especially when it's unlikely that the data will be
>> created in the first place.
>
> OK, but I need to make sure stores to ZERO register are ignored, right ?

Yes.

Just remember that since nios2 doesn't have condition codes, where side effects 
of an instruction can be useful alone, the *only* likely case of ZERO as a 
destination register is in the (designated) NOP psuedo-op.


r~
Marek Vasut Oct. 20, 2016, 3:01 a.m. UTC | #10
On 10/19/2016 06:18 PM, Richard Henderson wrote:
> On 10/18/2016 07:31 PM, Marek Vasut wrote:
>>> Processing a little more data can be preferable to fewer branch
>>> prediction failures.  And the best way to avoid those is to not have the
>>> branch at all. Especially when it's unlikely that the data will be
>>> created in the first place.
>>
>> OK, but I need to make sure stores to ZERO register are ignored, right ?
> 
> Yes.
> 
> Just remember that since nios2 doesn't have condition codes, where side
> effects of an instruction can be useful alone, the *only* likely case of
> ZERO as a destination register is in the (designated) NOP psuedo-op.

Got it, thanks.
diff mbox

Patch

diff --git a/hw/nios2/cpu_pic.c b/hw/nios2/cpu_pic.c
new file mode 100644
index 0000000..0f95987
--- /dev/null
+++ b/hw/nios2/cpu_pic.c
@@ -0,0 +1,70 @@ 
+/*
+ * Altera Nios2 CPU PIC
+ *
+ * Copyright (c) 2016 Marek Vasut <marek.vasut@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
+
+#include "qemu/config-file.h"
+
+#include "boot.h"
+
+static void nios2_pic_cpu_handler(void *opaque, int irq, int level)
+{
+    Nios2CPU *cpu = opaque;
+    CPUNios2State *env = &cpu->env;
+    CPUState *cs = CPU(cpu);
+    int type = irq ? CPU_INTERRUPT_NMI : CPU_INTERRUPT_HARD;
+
+    if (type == CPU_INTERRUPT_HARD) {
+        env->irq_pending = level;
+
+        if (level && (env->regs[CR_STATUS] & CR_STATUS_PIE)) {
+            env->irq_pending = 0;
+            cpu_interrupt(cs, type);
+        } else if (!level) {
+            env->irq_pending = 0;
+            cpu_reset_interrupt(cs, type);
+        }
+    } else {
+        if (level) {
+            cpu_interrupt(cs, type);
+        } else {
+            cpu_reset_interrupt(cs, type);
+        }
+    }
+}
+
+void nios2_check_interrupts(CPUNios2State *env)
+{
+    Nios2CPU *cpu = nios2_env_get_cpu(env);
+    CPUState *cs = CPU(cpu);
+
+    if (env->irq_pending) {
+        env->irq_pending = 0;
+        cpu_interrupt(cs, CPU_INTERRUPT_HARD);
+    }
+}
+
+qemu_irq *nios2_cpu_pic_init(Nios2CPU *cpu)
+{
+    return qemu_allocate_irqs(nios2_pic_cpu_handler, cpu, 2);
+}
diff --git a/target-nios2/Makefile.objs b/target-nios2/Makefile.objs
new file mode 100644
index 0000000..ea6a528
--- /dev/null
+++ b/target-nios2/Makefile.objs
@@ -0,0 +1,4 @@ 
+obj-y += translate.o op_helper.o helper.o cpu.o mmu.o instruction.o
+obj-$(CONFIG_SOFTMMU) += monitor.o
+
+$(obj)/op_helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
diff --git a/target-nios2/cpu.c b/target-nios2/cpu.c
new file mode 100644
index 0000000..5b4a2f6
--- /dev/null
+++ b/target-nios2/cpu.c
@@ -0,0 +1,229 @@ 
+/*
+ * QEMU Nios II CPU
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qapi/error.h"
+#include "cpu.h"
+#include "exec/log.h"
+#include "exec/gdbstub.h"
+#include "hw/qdev-properties.h"
+
+static void nios2_cpu_set_pc(CPUState *cs, vaddr value)
+{
+    Nios2CPU *cpu = NIOS2_CPU(cs);
+    CPUNios2State *env = &cpu->env;
+
+    env->regs[R_PC] = value;
+}
+
+static bool nios2_cpu_has_work(CPUState *cs)
+{
+    return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI);
+}
+
+/* CPUClass::reset() */
+static void nios2_cpu_reset(CPUState *cs)
+{
+    Nios2CPU *cpu = NIOS2_CPU(cs);
+    Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(cpu);
+    CPUNios2State *env = &cpu->env;
+
+    if (qemu_loglevel_mask(CPU_LOG_RESET)) {
+        qemu_log("CPU Reset (CPU %d)\n", cs->cpu_index);
+        log_cpu_state(cs, 0);
+    }
+
+    ncc->parent_reset(cs);
+
+    tlb_flush(cs, 1);
+
+    memset(env->regs, 0, sizeof(uint32_t) * NUM_CORE_REGS);
+    env->regs[R_PC] = env->reset_addr;
+
+#if defined(CONFIG_USER_ONLY)
+    /* Start in user mode with interrupts enabled. */
+    env->regs[CR_STATUS] = CR_STATUS_U | CR_STATUS_PIE;
+#endif
+}
+
+static void nios2_cpu_initfn(Object *obj)
+{
+    CPUState *cs = CPU(obj);
+    Nios2CPU *cpu = NIOS2_CPU(obj);
+    CPUNios2State *env = &cpu->env;
+    static bool tcg_initialized;
+
+    cpu->mmu_present = true;
+    cs->env_ptr = env;
+    cpu_exec_init(cs, &error_abort);
+
+#if !defined(CONFIG_USER_ONLY)
+    mmu_init(&env->mmu);
+#endif
+
+    if (tcg_enabled() && !tcg_initialized) {
+        tcg_initialized = true;
+        nios2_tcg_init();
+    }
+}
+
+Nios2CPU *cpu_nios2_init(const char *cpu_model)
+{
+    Nios2CPU *cpu = NIOS2_CPU(object_new(TYPE_NIOS2_CPU));
+
+    object_property_set_bool(OBJECT(cpu), true, "realized", NULL);
+
+    return cpu;
+}
+
+static void nios2_cpu_realizefn(DeviceState *dev, Error **errp)
+{
+    CPUState *cs = CPU(dev);
+    Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(dev);
+
+    qemu_init_vcpu(cs);
+    cpu_reset(cs);
+
+    ncc->parent_realize(dev, errp);
+}
+
+static bool nios2_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
+{
+    Nios2CPU *cpu = NIOS2_CPU(cs);
+    CPUNios2State *env = &cpu->env;
+
+    if ((interrupt_request & CPU_INTERRUPT_HARD) &&
+        (env->regs[CR_STATUS] & CR_STATUS_PIE)) {
+        cs->exception_index = EXCP_IRQ;
+        nios2_cpu_do_interrupt(cs);
+        return true;
+    }
+    return false;
+}
+
+
+static void nios2_cpu_disas_set_info(CPUState *cpu, disassemble_info *info)
+{
+    /* NOTE: NiosII R2 is not supported yet. */
+    info->mach = bfd_arch_nios2;
+#ifdef TARGET_WORDS_BIGENDIAN
+    info->print_insn = print_insn_big_nios2;
+#else
+    info->print_insn = print_insn_little_nios2;
+#endif
+}
+
+static int nios2_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n)
+{
+    Nios2CPU *cpu = NIOS2_CPU(cs);
+    CPUClass *cc = CPU_GET_CLASS(cs);
+    CPUNios2State *env = &cpu->env;
+
+    if (n > cc->gdb_num_core_regs)
+        return 0;
+
+    if (n < 32)		/* GP regs */
+        return gdb_get_reg32(mem_buf, env->regs[n]);
+    else if (n == 32)	/* PC */
+        return gdb_get_reg32(mem_buf, env->regs[R_PC]);
+    else if (n < 49)	/* Status regs */
+        return gdb_get_reg32(mem_buf, env->regs[n - 1]);
+    /* Invalid regs */
+    return 0;
+}
+
+static int nios2_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
+{
+    Nios2CPU *cpu = NIOS2_CPU(cs);
+    CPUClass *cc = CPU_GET_CLASS(cs);
+    CPUNios2State *env = &cpu->env;
+
+    if (n > cc->gdb_num_core_regs)
+        return 0;
+
+    if (n < 32)		/* GP regs */
+        env->regs[n] = ldl_p(mem_buf);
+    else if (n == 32)	/* PC */
+        env->regs[R_PC] = ldl_p(mem_buf);
+    else if (n < 49)	/* Status regs */
+        env->regs[n - 1] = ldl_p(mem_buf);
+
+    return 4;
+}
+
+static Property nios2_properties[] = {
+    DEFINE_PROP_BOOL("mmu_present", Nios2CPU, mmu_present, true),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+
+static void nios2_cpu_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    CPUClass *cc = CPU_CLASS(oc);
+    Nios2CPUClass *ncc = NIOS2_CPU_CLASS(oc);
+
+    ncc->parent_realize = dc->realize;
+    dc->realize = nios2_cpu_realizefn;
+    dc->props = nios2_properties;
+    ncc->parent_reset = cc->reset;
+    cc->reset = nios2_cpu_reset;
+
+//FIXME    cc->class_by_name = nios2_cpu_class_by_name;
+    cc->has_work = nios2_cpu_has_work;
+    cc->do_interrupt = nios2_cpu_do_interrupt;
+    cc->cpu_exec_interrupt = nios2_cpu_exec_interrupt;
+    cc->dump_state = nios2_cpu_dump_state;
+    cc->set_pc = nios2_cpu_set_pc;
+    cc->disas_set_info = nios2_cpu_disas_set_info;
+#ifdef CONFIG_USER_ONLY
+    cc->handle_mmu_fault = nios2_cpu_handle_mmu_fault;
+#else
+    cc->get_phys_page_debug = nios2_cpu_get_phys_page_debug;
+/* FIXME *///    cc->do_unassigned_access = nios2_cpu_unassigned_access;
+#endif
+    cc->gdb_read_register = nios2_cpu_gdb_read_register;
+    cc->gdb_write_register = nios2_cpu_gdb_write_register;
+    cc->gdb_num_core_regs = 49;
+
+    /*
+     * Reason: nios2_cpu_initfn() calls cpu_exec_init(), which saves
+     * the object in cpus -> dangling pointer after final
+     * object_unref().
+     */
+    dc->cannot_destroy_with_object_finalize_yet = true;
+}
+
+static const TypeInfo nios2_cpu_type_info = {
+    .name = TYPE_NIOS2_CPU,
+    .parent = TYPE_CPU,
+    .instance_size = sizeof(Nios2CPU),
+    .instance_init = nios2_cpu_initfn,
+    .class_size = sizeof(Nios2CPUClass),
+    .class_init = nios2_cpu_class_init,
+};
+
+static void nios2_cpu_register_types(void)
+{
+    type_register_static(&nios2_cpu_type_info);
+}
+
+type_init(nios2_cpu_register_types)
diff --git a/target-nios2/cpu.h b/target-nios2/cpu.h
new file mode 100644
index 0000000..feac4fb
--- /dev/null
+++ b/target-nios2/cpu.h
@@ -0,0 +1,267 @@ 
+/*
+ * Altera Nios II virtual CPU header
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * 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 CPU_NIOS2_H
+#define CPU_NIOS2_H
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+
+#define TARGET_LONG_BITS 32
+
+#define CPUArchState struct CPUNios2State
+
+#include "exec/cpu-defs.h"
+#include "fpu/softfloat.h"
+#include "qom/cpu.h"
+struct CPUNios2State;
+typedef struct CPUNios2State CPUNios2State;
+#if !defined(CONFIG_USER_ONLY)
+#include "mmu.h"
+#endif
+
+#define TYPE_NIOS2_CPU "nios2-cpu"
+
+#define NIOS2_CPU_CLASS(klass) \
+    OBJECT_CLASS_CHECK(Nios2CPUClass, (klass), TYPE_NIOS2_CPU)
+#define NIOS2_CPU(obj) \
+    OBJECT_CHECK(Nios2CPU, (obj), TYPE_NIOS2_CPU)
+#define NIOS2_CPU_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(Nios2CPUClass, (obj), TYPE_NIOS2_CPU)
+
+/**
+ * Nios2CPUClass:
+ * @parent_reset: The parent class' reset handler.
+ *
+ * A Nios2 CPU model.
+ */
+typedef struct Nios2CPUClass {
+    /*< private >*/
+    CPUClass parent_class;
+    /*< public >*/
+
+    DeviceRealize parent_realize;
+    void (*parent_reset)(CPUState *cpu);
+} Nios2CPUClass;
+
+#define TARGET_HAS_ICE 1
+
+/* Configuration options for Nios II */
+#define RESET_ADDRESS         0x00000000
+#define EXCEPTION_ADDRESS     0x00000004
+#define FAST_TLB_MISS_ADDRESS 0x00000008
+
+
+/* GP regs + CR regs + PC */
+#define NUM_CORE_REGS (32 + 32 + 1)
+
+/* General purpose register aliases */
+#define R_ZERO   0
+#define R_AT     1
+#define R_RET0   2
+#define R_RET1   3
+#define R_ARG0   4
+#define R_ARG1   5
+#define R_ARG2   6
+#define R_ARG3   7
+#define R_ET     24
+#define R_BT     25
+#define R_GP     26
+#define R_SP     27
+#define R_FP     28
+#define R_EA     29
+#define R_BA     30
+#define R_RA     31
+
+/* Control register aliases */
+#define CR_BASE  32
+#define CR_STATUS    (CR_BASE + 0)
+#define   CR_STATUS_PIE  (1<<0)
+#define   CR_STATUS_U    (1<<1)
+#define   CR_STATUS_EH   (1<<2)
+#define   CR_STATUS_IH   (1<<3)
+#define   CR_STATUS_IL   (63<<4)
+#define   CR_STATUS_CRS  (63<<10)
+#define   CR_STATUS_PRS  (63<<16)
+#define   CR_STATUS_NMI  (1<<22)
+#define   CR_STATUS_RSIE (1<<23)
+#define CR_ESTATUS   (CR_BASE + 1)
+#define CR_BSTATUS   (CR_BASE + 2)
+#define CR_IENABLE   (CR_BASE + 3)
+#define CR_IPENDING  (CR_BASE + 4)
+#define CR_CPUID     (CR_BASE + 5)
+#define CR_CTL6      (CR_BASE + 6)
+#define CR_EXCEPTION (CR_BASE + 7)
+#define CR_PTEADDR   (CR_BASE + 8)
+#define   CR_PTEADDR_PTBASE_SHIFT 22
+#define   CR_PTEADDR_PTBASE_MASK  (0x3FF << CR_PTEADDR_PTBASE_SHIFT)
+#define   CR_PTEADDR_VPN_SHIFT    2
+#define   CR_PTEADDR_VPN_MASK     (0xFFFFF << CR_PTEADDR_VPN_SHIFT)
+#define CR_TLBACC    (CR_BASE + 9)
+#define   CR_TLBACC_IGN_SHIFT 25
+#define   CR_TLBACC_IGN_MASK  (0x7F << CR_TLBACC_IGN_SHIFT)
+#define   CR_TLBACC_C         (1<<24)
+#define   CR_TLBACC_R         (1<<23)
+#define   CR_TLBACC_W         (1<<22)
+#define   CR_TLBACC_X         (1<<21)
+#define   CR_TLBACC_G         (1<<20)
+#define   CR_TLBACC_PFN_MASK  0x000FFFFF
+#define CR_TLBMISC   (CR_BASE + 10)
+#define   CR_TLBMISC_WAY_SHIFT 20
+#define   CR_TLBMISC_WAY_MASK  (0xF << CR_TLBMISC_WAY_SHIFT)
+#define   CR_TLBMISC_RD        (1<<19)
+#define   CR_TLBMISC_WR        (1<<18)
+#define   CR_TLBMISC_PID_SHIFT 4
+#define   CR_TLBMISC_PID_MASK  (0x3FFF << CR_TLBMISC_PID_SHIFT)
+#define   CR_TLBMISC_DBL       (1<<3)
+#define   CR_TLBMISC_BAD       (1<<2)
+#define   CR_TLBMISC_PERM      (1<<1)
+#define   CR_TLBMISC_D         (1<<0)
+#define CR_ENCINJ    (CR_BASE + 11)
+#define CR_BADADDR   (CR_BASE + 12)
+#define CR_CONFIG    (CR_BASE + 13)
+#define CR_MPUBASE   (CR_BASE + 14)
+#define CR_MPUACC    (CR_BASE + 15)
+
+/* Other registers */
+#define R_PC         64
+
+/* Exceptions */
+#define EXCP_BREAK    -1
+#define EXCP_RESET    0
+#define EXCP_PRESET   1
+#define EXCP_IRQ      2
+#define EXCP_TRAP     3
+#define EXCP_UNIMPL   4
+#define EXCP_ILLEGAL  5
+#define EXCP_UNALIGN  6
+#define EXCP_UNALIGND 7
+#define EXCP_DIV      8
+#define EXCP_SUPERA   9
+#define EXCP_SUPERI   10
+#define EXCP_SUPERD   11
+#define EXCP_TLBD     12
+#define EXCP_TLBX     13
+#define EXCP_TLBR     14
+#define EXCP_TLBW     15
+#define EXCP_MPUI     16
+#define EXCP_MPUD     17
+
+#define CPU_INTERRUPT_NMI       CPU_INTERRUPT_TGT_EXT_3
+
+#define NB_MMU_MODES 2
+
+struct CPUNios2State {
+    uint32_t regs[NUM_CORE_REGS];
+
+    /* Addresses that are hard-coded in the FPGA build settings */
+    uint32_t reset_addr;
+    uint32_t exception_addr;
+    uint32_t fast_tlb_miss_addr;
+
+#if !defined(CONFIG_USER_ONLY)
+    Nios2MMU mmu;
+
+    uint32_t irq_pending;
+#endif
+
+    CPU_COMMON
+};
+
+/**
+ * Nios2CPU:
+ * @env: #CPUNios2State
+ *
+ * A Nios2 CPU.
+ */
+typedef struct Nios2CPU {
+    /*< private >*/
+    CPUState parent_obj;
+    /*< public >*/
+
+    CPUNios2State env;
+    bool mmu_present;
+} Nios2CPU;
+
+static inline Nios2CPU *nios2_env_get_cpu(CPUNios2State *env)
+{
+    return NIOS2_CPU(container_of(env, Nios2CPU, env));
+}
+
+#define ENV_GET_CPU(e) CPU(nios2_env_get_cpu(e))
+
+#define ENV_OFFSET offsetof(Nios2CPU, env)
+
+void nios2_tcg_init(void);
+Nios2CPU *cpu_nios2_init(const char *cpu_model);
+void nios2_cpu_do_interrupt(CPUState *cs);
+int cpu_nios2_signal_handler(int host_signum, void *pinfo, void *puc);
+void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUNios2State *env);
+void nios2_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf,
+                          int flags);
+hwaddr nios2_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
+
+qemu_irq *nios2_cpu_pic_init(Nios2CPU *cpu);
+void nios2_check_interrupts(CPUNios2State *env);
+
+#define TARGET_PHYS_ADDR_SPACE_BITS 32
+#define TARGET_VIRT_ADDR_SPACE_BITS 32
+
+#define cpu_init(cpu_model) CPU(cpu_nios2_init(cpu_model))
+
+#define cpu_gen_code cpu_nios2_gen_code
+#define cpu_signal_handler cpu_nios2_signal_handler
+
+#define CPU_SAVE_VERSION 1
+
+#define TARGET_PAGE_BITS 12
+
+/* MMU modes definitions */
+#define MMU_MODE0_SUFFIX _kernel
+#define MMU_MODE1_SUFFIX _user
+#define MMU_SUPERVISOR_IDX  0
+#define MMU_USER_IDX        1
+
+static inline int cpu_mmu_index(CPUNios2State *env, bool ifetch)
+{
+    return (env->regs[CR_STATUS] & CR_STATUS_U) ? MMU_USER_IDX :
+                                                  MMU_SUPERVISOR_IDX;
+}
+
+int nios2_cpu_handle_mmu_fault(CPUState *env, vaddr address,
+                               int rw, int mmu_idx);
+
+static inline int cpu_interrupts_enabled(CPUNios2State *env)
+{
+    return env->regs[CR_STATUS] & CR_STATUS_PIE;
+}
+
+#include "exec/cpu-all.h"
+#include "exec/exec-all.h"
+
+static inline void cpu_get_tb_cpu_state(CPUNios2State *env, target_ulong *pc,
+                                        target_ulong *cs_base, uint32_t *flags)
+{
+    *pc = env->regs[R_PC];
+    *cs_base = 0;
+    *flags = (env->regs[CR_STATUS] & (CR_STATUS_EH | CR_STATUS_U));
+}
+
+#endif /* CPU_NIOS2_H */
+
diff --git a/target-nios2/helper.c b/target-nios2/helper.c
new file mode 100644
index 0000000..66fcd30
--- /dev/null
+++ b/target-nios2/helper.c
@@ -0,0 +1,304 @@ 
+/*
+ * Altera Nios II helper routines.
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qapi/error.h"
+#include "cpu.h"
+#include "exec/exec-all.h"
+#include "exec/log.h"
+#include "qemu/host-utils.h"
+
+#if defined(CONFIG_USER_ONLY)
+
+void nios2_cpu_do_interrupt(CPUState *cs)
+{
+    Nios2CPU *cpu = NIOS2_CPU(cs);
+    CPUNios2State *env = &cpu->env;
+    cs->exception_index = -1;
+    env->regs[R_EA] = env->regs[R_PC] + 4;
+}
+
+int nios2_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, int mmu_idx)
+{
+    cs->exception_index = 0xaa;
+    /* Page 0x1000 is kuser helper */
+    if (address < 0x1000 || address >= 0x2000) {
+        cpu_dump_state(cs, stderr, fprintf, 0);
+    }
+    return 1;
+}
+
+#else /* !CONFIG_USER_ONLY */
+
+
+
+void nios2_cpu_do_interrupt(CPUState *cs)
+{
+    Nios2CPU *cpu = NIOS2_CPU(cs);
+    CPUNios2State *env = &cpu->env;
+
+    switch (cs->exception_index) {
+    case EXCP_IRQ:
+        assert(env->regs[CR_STATUS] & CR_STATUS_PIE);
+
+        qemu_log_mask(CPU_LOG_INT, "interrupt at pc=%x\n", env->regs[R_PC]);
+
+        env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
+        env->regs[CR_STATUS] |= CR_STATUS_IH;
+        env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
+
+        env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
+        env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
+
+        env->regs[R_EA] = env->regs[R_PC] + 4;
+        env->regs[R_PC] = env->exception_addr;
+        break;
+
+    case EXCP_TLBD:
+        if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
+            qemu_log_mask(CPU_LOG_INT, "TLB MISS (fast) at pc=%x\n",
+                          env->regs[R_PC]);
+
+            /* Fast TLB miss */
+            /* Variation from the spec. Table 3-35 of the cpu reference shows
+             * estatus not being changed for TLB miss but this appears to
+             * be incorrect. */
+            env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
+            env->regs[CR_STATUS] |= CR_STATUS_EH;
+            env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
+
+            env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
+            env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
+
+            env->regs[CR_TLBMISC] &= ~CR_TLBMISC_DBL;
+            env->regs[CR_TLBMISC] |= CR_TLBMISC_WR;
+
+            env->regs[R_EA] = env->regs[R_PC] + 4;
+            env->regs[R_PC] = env->fast_tlb_miss_addr;
+        } else {
+            qemu_log_mask(CPU_LOG_INT, "TLB MISS (double) at pc=%x\n",
+                          env->regs[R_PC]);
+
+            /* Double TLB miss */
+            env->regs[CR_STATUS] |= CR_STATUS_EH;
+            env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
+
+            env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
+            env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
+
+            env->regs[CR_TLBMISC] |= CR_TLBMISC_DBL;
+
+            env->regs[R_PC] = env->exception_addr;
+        }
+        break;
+
+    case EXCP_TLBR:
+    case EXCP_TLBW:
+    case EXCP_TLBX:
+        qemu_log_mask(CPU_LOG_INT, "TLB PERM at pc=%x\n", env->regs[R_PC]);
+
+        env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
+        env->regs[CR_STATUS] |= CR_STATUS_EH;
+        env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
+
+        env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
+        env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
+
+        if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
+            env->regs[CR_TLBMISC] |= CR_TLBMISC_WR;
+        }
+
+        env->regs[R_EA] = env->regs[R_PC] + 4;
+        env->regs[R_PC] = env->exception_addr;
+        break;
+
+    case EXCP_SUPERA:
+    case EXCP_SUPERI:
+    case EXCP_SUPERD:
+        qemu_log_mask(CPU_LOG_INT, "SUPERVISOR exception at pc=%x\n",
+                      env->regs[R_PC]);
+
+        if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
+            env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
+            env->regs[R_EA] = env->regs[R_PC] + 4;
+        }
+
+        env->regs[CR_STATUS] |= CR_STATUS_EH;
+        env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
+
+        env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
+        env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
+
+        env->regs[R_PC] = env->exception_addr;
+        break;
+
+    case EXCP_ILLEGAL:
+    case EXCP_TRAP:
+        qemu_log_mask(CPU_LOG_INT, "TRAP exception at pc=%x\n",
+                      env->regs[R_PC]);
+
+        if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
+            env->regs[CR_ESTATUS] = env->regs[CR_STATUS];
+            env->regs[R_EA] = env->regs[R_PC] + 4;
+        }
+
+        env->regs[CR_STATUS] |= CR_STATUS_EH;
+        env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
+
+        env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
+        env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
+
+        env->regs[R_PC] = env->exception_addr;
+        break;
+
+    case EXCP_BREAK:
+        if ((env->regs[CR_STATUS] & CR_STATUS_EH) == 0) {
+            env->regs[CR_BSTATUS] = env->regs[CR_STATUS];
+            env->regs[R_BA] = env->regs[R_PC] + 4;
+        }
+
+        env->regs[CR_STATUS] |= CR_STATUS_EH;
+        env->regs[CR_STATUS] &= ~(CR_STATUS_PIE|CR_STATUS_U);
+
+        env->regs[CR_EXCEPTION] &= ~(0x1F << 2);
+        env->regs[CR_EXCEPTION] |= (cs->exception_index & 0x1F) << 2;
+
+        env->regs[R_PC] = env->exception_addr;
+        break;
+
+    default:
+        cpu_abort(cs, "unhandled exception type=%d\n",
+                  cs->exception_index);
+        break;
+    }
+}
+
+static int cpu_nios2_handle_virtual_page(
+    CPUState *cs, target_ulong address, int rw, int mmu_idx)
+{
+    Nios2CPU *cpu = NIOS2_CPU(cs);
+    CPUNios2State *env = &cpu->env;
+    target_ulong vaddr, paddr;
+    Nios2MMULookup lu;
+    unsigned int hit;
+    hit = mmu_translate(env, &lu, address, rw, mmu_idx);
+    if (hit) {
+        vaddr = address & TARGET_PAGE_MASK;
+        paddr = lu.paddr + vaddr - lu.vaddr;
+
+        if (((rw == 0) && (lu.prot & PAGE_READ)) ||
+            ((rw == 1) && (lu.prot & PAGE_WRITE)) ||
+            ((rw == 2) && (lu.prot & PAGE_EXEC))) {
+
+            tlb_set_page(cs, vaddr, paddr, lu.prot,
+                         mmu_idx, TARGET_PAGE_SIZE);
+            return 0;
+        } else {
+            /* Permission violation */
+            cs->exception_index = (rw == 0) ? EXCP_TLBR :
+                                               ((rw == 1) ? EXCP_TLBW :
+                                                            EXCP_TLBX);
+        }
+    } else {
+        cs->exception_index = EXCP_TLBD;
+    }
+
+    if (rw == 2) {
+        env->regs[CR_TLBMISC] &= ~CR_TLBMISC_D;
+    } else {
+        env->regs[CR_TLBMISC] |= CR_TLBMISC_D;
+    }
+    env->regs[CR_PTEADDR] &= CR_PTEADDR_PTBASE_MASK;
+    env->regs[CR_PTEADDR] |= (address >> 10) & CR_PTEADDR_VPN_MASK;
+    env->mmu.pteaddr_wr = env->regs[CR_PTEADDR];
+    env->regs[CR_BADADDR] = address;
+    return 1;
+}
+
+int nios2_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, int mmu_idx)
+{
+    Nios2CPU *cpu = NIOS2_CPU(cs);
+    CPUNios2State *env = &cpu->env;
+
+    if (cpu->mmu_present) {
+        if (MMU_SUPERVISOR_IDX == mmu_idx) {
+            if (address >= 0xC0000000) {
+                /* Kernel physical page - TLB bypassed */
+                address &= TARGET_PAGE_MASK;
+                tlb_set_page(cs, address, address, PAGE_BITS,
+                             mmu_idx, TARGET_PAGE_SIZE);
+            } else if (address >= 0x80000000) {
+                /* Kernel virtual page */
+                return cpu_nios2_handle_virtual_page(cs, address, rw, mmu_idx);
+            } else {
+                /* User virtual page */
+                return cpu_nios2_handle_virtual_page(cs, address, rw, mmu_idx);
+            }
+        } else {
+            if (address >= 0x80000000) {
+                /* Illegal access from user mode */
+                cs->exception_index = EXCP_SUPERA;
+                env->regs[CR_BADADDR] = address;
+                return 1;
+            } else {
+                /* User virtual page */
+                return cpu_nios2_handle_virtual_page(cs, address, rw, mmu_idx);
+            }
+        }
+    } else {
+        /* No MMU */
+        address &= TARGET_PAGE_MASK;
+        tlb_set_page(cs, address, address, PAGE_BITS,
+                     mmu_idx, TARGET_PAGE_SIZE);
+    }
+
+    return 0;
+}
+
+hwaddr nios2_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
+{
+    Nios2CPU *cpu = NIOS2_CPU(cs);
+    CPUNios2State *env = &cpu->env;
+    target_ulong vaddr, paddr = 0;
+    Nios2MMULookup lu;
+    unsigned int hit;
+
+    if (cpu->mmu_present && (addr < 0xC0000000)) {
+        hit = mmu_translate(env, &lu, addr, 0, 0);
+        if (hit) {
+            vaddr = addr & TARGET_PAGE_MASK;
+            paddr = lu.paddr + vaddr - lu.vaddr;
+        } else {
+            paddr = -1;
+            qemu_log("cpu_get_phys_page debug MISS: %08lX\n", addr);
+        }
+    } else {
+        paddr = addr & TARGET_PAGE_MASK;
+    }
+
+    return paddr;
+}
+
+#endif /* !CONFIG_USER_ONLY */
diff --git a/target-nios2/helper.h b/target-nios2/helper.h
new file mode 100644
index 0000000..7656a5e
--- /dev/null
+++ b/target-nios2/helper.h
@@ -0,0 +1,41 @@ 
+/*
+ * Altera Nios II helper routines header.
+ *
+ * Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * 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>
+ */
+
+/* Define this to enable tracing calls/returns */
+/* #define CALL_TRACING */
+
+#ifdef CALL_TRACING
+DEF_HELPER_2(call_status, void, i32, i32)
+DEF_HELPER_1(eret_status, void, i32)
+DEF_HELPER_1(ret_status, void, i32)
+#endif
+
+DEF_HELPER_2(raise_exception, void, env, i32)
+
+#if !defined(CONFIG_USER_ONLY)
+DEF_HELPER_2(mmu_read, i32, env, i32)
+DEF_HELPER_3(mmu_write, void, env, i32, i32)
+DEF_HELPER_1(check_interrupts, void, env)
+#endif
+
+DEF_HELPER_2(divs, i32, i32, i32)
+DEF_HELPER_2(divu, i32, i32, i32)
+
+DEF_HELPER_5(memalign, void, env, i32, i32, i32, i32)
diff --git a/target-nios2/instruction.c b/target-nios2/instruction.c
new file mode 100644
index 0000000..2639cff
--- /dev/null
+++ b/target-nios2/instruction.c
@@ -0,0 +1,1427 @@ 
+/*
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ *  (Portions of this file that were originally from nios2sim-ng.)
+ *
+ * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * 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 <stdio.h>
+
+#include "instruction.h"
+#include "exec/exec-all.h"
+#include "disas/disas.h"
+#include "exec/helper-proto.h"
+#include "exec/helper-gen.h"
+
+static inline uint32_t get_opcode(uint32_t code)
+{
+    I_TYPE(instr, code);
+    return instr->op;
+}
+
+static inline uint32_t get_opxcode(uint32_t code)
+{
+    R_TYPE(instr, code);
+    return instr->opx6;
+}
+
+static inline void t_gen_helper_raise_exception(DisasContext *dc,
+                                                uint32_t index)
+{
+    TCGv_i32 tmp = tcg_const_i32(index);
+
+    tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc);
+    gen_helper_raise_exception(dc->cpu_env, tmp);
+    tcg_temp_free_i32(tmp);
+    dc->is_jmp = DISAS_UPDATE;
+}
+
+static inline void gen_goto_tb(DisasContext *dc, int n, uint32_t dest)
+{
+    TranslationBlock *tb = dc->tb;
+
+    if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK)) {
+        tcg_gen_goto_tb(n);
+        tcg_gen_movi_tl(dc->cpu_R[R_PC], dest);
+        tcg_gen_exit_tb((tcg_target_long)tb + n);
+    } else {
+        tcg_gen_movi_tl(dc->cpu_R[R_PC], dest);
+        tcg_gen_exit_tb(0);
+    }
+}
+
+/*
+ * Instructions not implemented by the simulator.
+ */
+static void unimplemented(DisasContext *dc, uint32_t code)
+{
+    t_gen_helper_raise_exception(dc, EXCP_UNIMPL);
+}
+
+/*
+ * Illegal instruction
+ */
+static void illegal_instruction(DisasContext *dc,
+                                uint32_t code __attribute__((unused)))
+{
+    t_gen_helper_raise_exception(dc, EXCP_ILLEGAL);
+}
+
+static void gen_check_supervisor(DisasContext *dc, TCGLabel *label)
+{
+    TCGLabel *l1 = gen_new_label();
+
+    TCGv_i32 tmp = tcg_temp_new();
+    tcg_gen_andi_tl(tmp, dc->cpu_R[CR_STATUS], CR_STATUS_U);
+    tcg_gen_brcond_tl(TCG_COND_EQ, dc->cpu_R[R_ZERO], tmp, l1);
+    t_gen_helper_raise_exception(dc, EXCP_SUPERI);
+    tcg_gen_br(label);
+
+    gen_set_label(l1);
+    tcg_temp_free_i32(tmp);
+
+    /* If we aren't taking the exception, update the PC to the
+     * next instruction */
+    tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc+4);
+}
+
+/*
+ * Used as a placeholder for all instructions which do not have an effect on the
+ * simulator (e.g. flush, sync)
+ */
+static void nop(DisasContext *dc __attribute__((unused)),
+                uint32_t code __attribute__((unused)))
+{
+    /* Nothing to do here */
+}
+
+/*
+ * J-Type instructions
+ */
+
+/*
+ * ra <- PC + 4
+ * PC <- (PC(31..28) : IMM26 * 4)
+ */
+static void call(DisasContext *dc, uint32_t code)
+{
+    J_TYPE(instr, code);
+
+#ifdef CALL_TRACING
+    TCGv_i32 tmp = tcg_const_i32(dc->pc);
+    TCGv_i32 tmp2 = tcg_const_i32((dc->pc & 0xF0000000) | (instr->imm26 * 4));
+    gen_helper_call_status(tmp, tmp2);
+    tcg_temp_free_i32(tmp);
+    tcg_temp_free_i32(tmp2);
+#endif
+
+    tcg_gen_movi_tl(dc->cpu_R[R_RA], dc->pc + 4);
+
+    gen_goto_tb(dc, 0, (dc->pc & 0xF0000000) | (instr->imm26 * 4));
+
+    dc->is_jmp = DISAS_TB_JUMP;
+}
+
+/* PC <- (PC(31..28) : IMM26 * 4) */
+static void jmpi(DisasContext *dc, uint32_t code)
+{
+    J_TYPE(instr, code);
+
+    gen_goto_tb(dc, 0, (dc->pc & 0xF0000000) | (instr->imm26 * 4));
+
+    dc->is_jmp = DISAS_TB_JUMP;
+}
+
+/*
+ * I-Type instructions
+ */
+
+/* rB <- 0x000000 : Mem8[rA + @(IMM16)] */
+static void ldbu(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    TCGv addr = tcg_temp_new();
+    tcg_gen_addi_tl(addr, dc->cpu_R[instr->a],
+                    (int32_t)((int16_t)instr->imm16));
+
+    tcg_gen_qemu_ld8u(dc->cpu_R[instr->b], addr, dc->mem_idx);
+
+    tcg_temp_free(addr);
+}
+
+/* rB <- rA + IMM16 */
+static void addi(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    TCGv imm = tcg_temp_new();
+    tcg_gen_addi_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a],
+                    (int32_t)((int16_t)instr->imm16));
+    tcg_temp_free(imm);
+}
+
+/* Mem8[rA + @(IMM16)] <- rB(7..0) */
+static void stb(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    TCGv addr = tcg_temp_new();
+    tcg_gen_addi_tl(addr, dc->cpu_R[instr->a],
+                    (int32_t)((int16_t)instr->imm16));
+
+    tcg_gen_qemu_st8(dc->cpu_R[instr->b], addr, dc->mem_idx);
+
+    tcg_temp_free(addr);
+}
+
+/* PC <- PC + 4 + IMM16 */
+static void br(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    gen_goto_tb(dc, 0, dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
+
+    dc->is_jmp = DISAS_TB_JUMP;
+}
+
+/* rB <- @(Mem8[rA + @(IMM16)]) */
+static void ldb(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    TCGv addr = tcg_temp_new();
+    tcg_gen_addi_tl(addr, dc->cpu_R[instr->a],
+                    (int32_t)((int16_t)instr->imm16));
+
+    tcg_gen_qemu_ld8s(dc->cpu_R[instr->b], addr, dc->mem_idx);
+
+    tcg_temp_free(addr);
+}
+
+/*
+ * if ((signed) rA >= (signed) @(IMM16))
+ *   rB <- 1
+ * else
+ *   rB <- 0
+ */
+static void cmpgei(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    tcg_gen_setcondi_tl(TCG_COND_GE, dc->cpu_R[instr->b], dc->cpu_R[instr->a],
+                        (int32_t)((int16_t)instr->imm16));
+}
+
+/* rB <- 0x0000 : Mem16[rA + @IMM16)] */
+static void ldhu(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    TCGv addr = tcg_temp_new();
+    tcg_gen_addi_tl(addr, dc->cpu_R[instr->a],
+                    (int32_t)((int16_t)instr->imm16));
+
+    tcg_gen_qemu_ld16u(dc->cpu_R[instr->b], addr, dc->mem_idx);
+
+    tcg_temp_free(addr);
+}
+
+/* rB <- rA & IMM16 */
+static void andi(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    tcg_gen_andi_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a], instr->imm16);
+}
+
+/* Mem16[rA + @(IMM16)] <- rB(15..0) */
+static void sth(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    TCGv addr = tcg_temp_new();
+    tcg_gen_addi_tl(addr, dc->cpu_R[instr->a],
+                    (int32_t)((int16_t)instr->imm16));
+
+    tcg_gen_qemu_st16(dc->cpu_R[instr->b], addr, dc->mem_idx);
+
+    tcg_temp_free(addr);
+}
+
+/*
+ * if ((signed) rA >= (signed) rB)
+ *   PC <- PC + 4 + @(IMM16)
+ * else
+ *   PC <- PC + 4
+ */
+static void bge(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    TCGLabel *l1 = gen_new_label();
+
+    tcg_gen_brcond_tl(TCG_COND_GE, dc->cpu_R[instr->a],
+                      dc->cpu_R[instr->b], l1);
+
+    gen_goto_tb(dc, 0, dc->pc + 4);
+
+    gen_set_label(l1);
+
+    gen_goto_tb(dc, 1, dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
+
+    dc->is_jmp = DISAS_TB_JUMP;
+}
+
+/* rB <- Mem16[rA + @IMM16)] */
+static void ldh(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    TCGv addr = tcg_temp_new();
+    tcg_gen_addi_tl(addr, dc->cpu_R[instr->a],
+                    (int32_t)((int16_t)instr->imm16));
+
+    tcg_gen_qemu_ld16s(dc->cpu_R[instr->b], addr, dc->mem_idx);
+
+    tcg_temp_free(addr);
+}
+
+/*
+ * if ((signed) rA < (signed) @(IMM16))
+ *   rB <- 1
+ * else
+ *   rB <- 0
+ */
+static void cmplti(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    tcg_gen_setcondi_tl(TCG_COND_LT, dc->cpu_R[instr->b], dc->cpu_R[instr->a],
+                        (int32_t)((int16_t)instr->imm16));
+}
+
+/* Initializes the data cache line currently caching address rA + @(IMM16) */
+static void initda(DisasContext *dc __attribute__((unused)),
+                   uint32_t code __attribute__((unused)))
+{
+    /* TODO */
+}
+
+/* rB <- rA | IMM16 */
+static void ori(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    tcg_gen_ori_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a], instr->imm16);
+}
+
+/* Mem32[rA + @(IMM16)] <- rB */
+static void stw(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    TCGv addr = tcg_temp_new();
+    tcg_gen_addi_tl(addr, dc->cpu_R[instr->a],
+                    (int32_t)((int16_t)instr->imm16));
+
+    tcg_gen_qemu_st32(dc->cpu_R[instr->b], addr, dc->mem_idx);
+
+    tcg_temp_free(addr);
+}
+
+/*
+ * if ((signed) rA < (signed) rB)
+ *   PC <- PC + 4 + @(IMM16)
+ * else
+ *   PC <- PC + 4
+ */
+static void blt(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    TCGLabel *l1 = gen_new_label();
+
+    tcg_gen_brcond_tl(TCG_COND_LT, dc->cpu_R[instr->a],
+                      dc->cpu_R[instr->b], l1);
+
+    gen_goto_tb(dc, 0, dc->pc + 4);
+
+    gen_set_label(l1);
+
+    gen_goto_tb(dc, 1, dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
+
+    dc->is_jmp = DISAS_TB_JUMP;
+}
+
+/* rB <- @(Mem32[rA + @(IMM16)]) */
+static void ldw(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    TCGv addr = tcg_temp_new();
+    tcg_gen_addi_tl(addr, dc->cpu_R[instr->a],
+                    (int32_t)((int16_t)instr->imm16));
+
+    tcg_gen_qemu_ld32u(dc->cpu_R[instr->b], addr, dc->mem_idx);
+
+    tcg_temp_free(addr);
+}
+
+/*
+ * if ((signed) rA != (signed) @(IMM16))
+ *   rB <- 1
+ * else
+ *   rB <- 0
+ */
+static void cmpnei(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    tcg_gen_setcondi_tl(TCG_COND_NE, dc->cpu_R[instr->b], dc->cpu_R[instr->a],
+                        (int32_t)((int16_t)instr->imm16));
+}
+
+/* rB <- rA ^ IMM16 */
+static void xori(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    tcg_gen_xori_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a], instr->imm16);
+}
+
+/*
+ * if (rA != rB)
+ *   PC <- PC + 4 + @(IMM16)
+ * else
+ *   PC <- PC + 4
+ */
+static void bne(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    TCGLabel *l1 = gen_new_label();
+
+    tcg_gen_brcond_tl(TCG_COND_NE, dc->cpu_R[instr->a],
+                      dc->cpu_R[instr->b], l1);
+
+    gen_goto_tb(dc, 0, dc->pc + 4);
+
+    gen_set_label(l1);
+
+    gen_goto_tb(dc, 1, dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
+
+    dc->is_jmp = DISAS_TB_JUMP;
+}
+
+/*
+ * if ((signed) rA == (signed) @(IMM16))
+ *   rB <- 1
+ * else
+ *   rB <- 0
+ */
+static void cmpeqi(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    tcg_gen_setcondi_tl(TCG_COND_EQ, dc->cpu_R[instr->b], dc->cpu_R[instr->a],
+                        (int32_t)((int16_t)instr->imm16));
+}
+
+/* rB <- 0x000000 : Mem8[rA + @(IMM16)] (bypassing cache) */
+static void ldbuio(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    TCGv addr = tcg_temp_new();
+    tcg_gen_addi_tl(addr, dc->cpu_R[instr->a],
+                    (int32_t)((int16_t)instr->imm16));
+
+    tcg_gen_qemu_ld8u(dc->cpu_R[instr->b], addr, dc->mem_idx);
+
+    tcg_temp_free(addr);
+}
+
+/* rB <- (rA * @(IMM16))(31..0) */
+static void muli(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    TCGv imm = tcg_temp_new();
+    tcg_gen_muli_i32(dc->cpu_R[instr->b], dc->cpu_R[instr->a],
+                     (int32_t)((int16_t)instr->imm16));
+    tcg_temp_free(imm);
+}
+
+/* Mem8[rA + @(IMM16)] <- rB(7..0) (bypassing cache) */
+static void stbio(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    TCGv addr = tcg_temp_new();
+    tcg_gen_addi_tl(addr, dc->cpu_R[instr->a],
+                    (int32_t)((int16_t)instr->imm16));
+
+    tcg_gen_qemu_st8(dc->cpu_R[instr->b], addr, dc->mem_idx);
+
+    tcg_temp_free(addr);
+}
+
+/*
+ * if (rA == rB)
+ *   PC <- PC + 4 + @(IMM16)
+ * else
+ *   PC <- PC + 4
+ */
+static void beq(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    TCGLabel *l1 = gen_new_label();
+
+    tcg_gen_brcond_tl(TCG_COND_EQ, dc->cpu_R[instr->a],
+                      dc->cpu_R[instr->b], l1);
+
+    gen_goto_tb(dc, 0, dc->pc + 4);
+
+    gen_set_label(l1);
+
+    gen_goto_tb(dc, 1, dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
+
+    dc->is_jmp = DISAS_TB_JUMP;
+}
+
+/* rB <- @(Mem8[rA + @(IMM16)]) (bypassing cache) */
+static void ldbio(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    TCGv addr = tcg_temp_new();
+    tcg_gen_addi_tl(addr, dc->cpu_R[instr->a],
+                    (int32_t)((int16_t)instr->imm16));
+
+    tcg_gen_qemu_ld8s(dc->cpu_R[instr->b], addr, dc->mem_idx);
+
+    tcg_temp_free(addr);
+}
+
+/*
+ * if (rA >= 0x0000 : @(IMM16))
+ *   rB <- 1
+ * else
+ *   rB <- 0
+ */
+static void cmpgeui(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    tcg_gen_setcondi_tl(TCG_COND_GEU, dc->cpu_R[instr->b], dc->cpu_R[instr->a],
+                        instr->imm16);
+}
+
+/* rB <- 0x0000 : Mem16[rA + @IMM16)] (bypassing cache) */
+static void ldhuio(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    TCGv addr = tcg_temp_new();
+    tcg_gen_addi_tl(addr, dc->cpu_R[instr->a],
+                    (int32_t)((int16_t)instr->imm16));
+
+    tcg_gen_qemu_ld16u(dc->cpu_R[instr->b], addr, dc->mem_idx);
+
+    tcg_temp_free(addr);
+}
+
+/* rB <- rA & (IMM16 : 0x0000) */
+static void andhi(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    tcg_gen_andi_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a],
+                    instr->imm16 << 16);
+}
+
+/* Mem16[rA + @(IMM16)] <- rB(15..0) (bypassing cache) */
+static void sthio(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    TCGv addr = tcg_temp_new();
+    tcg_gen_addi_tl(addr, dc->cpu_R[instr->a],
+                    (int32_t)((int16_t)instr->imm16));
+
+    tcg_gen_qemu_st16(dc->cpu_R[instr->b], addr, dc->mem_idx);
+
+    tcg_temp_free(addr);
+}
+
+/*
+ * if (rA >= rB)
+ *   PC <- PC + 4 + @(IMM16)
+ * else
+ *   PC <- PC + 4
+ */
+static void bgeu(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    TCGLabel *l1 = gen_new_label();
+
+    tcg_gen_brcond_tl(TCG_COND_GEU, dc->cpu_R[instr->a],
+                      dc->cpu_R[instr->b], l1);
+
+    gen_goto_tb(dc, 0, dc->pc + 4);
+
+    gen_set_label(l1);
+
+    gen_goto_tb(dc, 1, dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
+
+    dc->is_jmp = DISAS_TB_JUMP;
+}
+
+/* rB <- Mem16[rA + @IMM16)] (bypassing cache) */
+static void ldhio(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    TCGv addr = tcg_temp_new();
+    tcg_gen_addi_tl(addr, dc->cpu_R[instr->a],
+                    (int32_t)((int16_t)instr->imm16));
+
+    tcg_gen_qemu_ld16s(dc->cpu_R[instr->b], addr, dc->mem_idx);
+
+    tcg_temp_free(addr);
+}
+
+/*
+ * if (rA < 0x0000 : @(IMM16))
+ *   rB <- 1
+ * else
+ *   rB <- 0
+ */
+static void cmpltui(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    tcg_gen_setcondi_tl(TCG_COND_LTU, dc->cpu_R[instr->b], dc->cpu_R[instr->a],
+                        instr->imm16);
+}
+
+/* */
+static void initd(DisasContext *dc __attribute__((unused)),
+                  uint32_t code __attribute((unused)))
+{
+    /* TODO */
+}
+
+/* rB <- rA | (IMM16 : 0x0000) */
+static void orhi(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    tcg_gen_ori_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a],
+                   instr->imm16 << 16);
+}
+
+/* Mem32[rA + @(IMM16)] <- rB (bypassing cache) */
+static void stwio(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    TCGv addr = tcg_temp_new();
+    tcg_gen_addi_tl(addr, dc->cpu_R[instr->a],
+                    (int32_t)((int16_t)instr->imm16));
+
+    tcg_gen_qemu_st32(dc->cpu_R[instr->b], addr, dc->mem_idx);
+
+    tcg_temp_free(addr);
+}
+
+/*
+ * if ((unsigned) rA < (unsigned) rB)
+ *   PC <- PC + 4 + @(IMM16)
+ * else
+ *   PC <- PC + 4
+ */
+static void bltu(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    TCGLabel *l1 = gen_new_label();
+
+    tcg_gen_brcond_tl(TCG_COND_LTU, dc->cpu_R[instr->a],
+                      dc->cpu_R[instr->b], l1);
+
+    gen_goto_tb(dc, 0, dc->pc + 4);
+
+    gen_set_label(l1);
+
+    gen_goto_tb(dc, 1, dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
+
+    dc->is_jmp = DISAS_TB_JUMP;
+}
+
+/* rB <- @(Mem32[rA + @(IMM16)]) (bypassing cache) */
+static void ldwio(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    TCGv addr = tcg_temp_new();
+    tcg_gen_addi_tl(addr, dc->cpu_R[instr->a],
+                    (int32_t)((int16_t)instr->imm16));
+
+    tcg_gen_qemu_ld32u(dc->cpu_R[instr->b], addr, dc->mem_idx);
+
+    tcg_temp_free(addr);
+}
+
+/* Prototype only, defined below */
+static void handle_r_type_instr(DisasContext *dc, uint32_t code);
+
+/* rB <- rA ^ (IMM16 : 0x0000) */
+static void xorhi(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    tcg_gen_xori_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a],
+                    instr->imm16 << 16);
+}
+
+static const Nios2Instruction i_type_instructions[I_TYPE_COUNT] = {
+    [CALL]    = INSTRUCTION(call),
+    [JMPI]    = INSTRUCTION(jmpi),
+    [0x02]    = INSTRUCTION_ILLEGAL(),
+    [LDBU]    = INSTRUCTION(ldbu),
+    [ADDI]    = INSTRUCTION(addi),
+    [STB]     = INSTRUCTION(stb),
+    [BR]      = INSTRUCTION(br),
+    [LDB]     = INSTRUCTION(ldb),
+    [CMPGEI]  = INSTRUCTION(cmpgei),
+    [0x09]    = INSTRUCTION_ILLEGAL(),
+    [0x0a]    = INSTRUCTION_ILLEGAL(),
+    [LDHU]    = INSTRUCTION(ldhu),
+    [ANDI]    = INSTRUCTION(andi),
+    [STH]     = INSTRUCTION(sth),
+    [BGE]     = INSTRUCTION(bge),
+    [LDH]     = INSTRUCTION(ldh),
+    [CMPLTI]  = INSTRUCTION(cmplti),
+    [0x11]    = INSTRUCTION_ILLEGAL(),
+    [0x12]    = INSTRUCTION_ILLEGAL(),
+    [INITDA]  = INSTRUCTION(initda),
+    [ORI]     = INSTRUCTION(ori),
+    [STW]     = INSTRUCTION(stw),
+    [BLT]     = INSTRUCTION(blt),
+    [LDW]     = INSTRUCTION(ldw),
+    [CMPNEI]  = INSTRUCTION(cmpnei),
+    [0x19]    = INSTRUCTION_ILLEGAL(),
+    [0x1a]    = INSTRUCTION_ILLEGAL(),
+    [FLUSHDA] = INSTRUCTION_NOP(flushda),
+    [XORI]    = INSTRUCTION(xori),
+    [0x1d]    = INSTRUCTION_ILLEGAL(),
+    [BNE]     = INSTRUCTION(bne),
+    [0x1f]    = INSTRUCTION_ILLEGAL(),
+    [CMPEQI]  = INSTRUCTION(cmpeqi),
+    [0x21]    = INSTRUCTION_ILLEGAL(),
+    [0x22]    = INSTRUCTION_ILLEGAL(),
+    [LDBUIO]  = INSTRUCTION(ldbuio),
+    [MULI]    = INSTRUCTION(muli),
+    [STBIO]   = INSTRUCTION(stbio),
+    [BEQ]     = INSTRUCTION(beq),
+    [LDBIO]   = INSTRUCTION(ldbio),
+    [CMPGEUI] = INSTRUCTION(cmpgeui),
+    [0x29]    = INSTRUCTION_ILLEGAL(),
+    [0x2a]    = INSTRUCTION_ILLEGAL(),
+    [LDHUIO]  = INSTRUCTION(ldhuio),
+    [ANDHI]   = INSTRUCTION(andhi),
+    [STHIO]   = INSTRUCTION(sthio),
+    [BGEU]    = INSTRUCTION(bgeu),
+    [LDHIO]   = INSTRUCTION(ldhio),
+    [CMPLTUI] = INSTRUCTION(cmpltui),
+    [0x31]    = INSTRUCTION_ILLEGAL(),
+    [CUSTOM]  = INSTRUCTION_UNIMPLEMENTED(custom),
+    [INITD]   = INSTRUCTION(initd),
+    [ORHI]    = INSTRUCTION(orhi),
+    [STWIO]   = INSTRUCTION(stwio),
+    [BLTU]    = INSTRUCTION(bltu),
+    [LDWIO]   = INSTRUCTION(ldwio),
+    [RDPRS]   = INSTRUCTION_UNIMPLEMENTED(rdprs),
+    [0x39]    = INSTRUCTION_ILLEGAL(),
+    [R_TYPE]  = { "<R-type instruction>", handle_r_type_instr },
+    [FLUSHD]  = INSTRUCTION_NOP(flushd),
+    [XORHI]   = INSTRUCTION(xorhi),
+    [0x3d]    = INSTRUCTION_ILLEGAL(),
+    [0x3e]    = INSTRUCTION_ILLEGAL(),
+    [0x3f]    = INSTRUCTION_ILLEGAL(),
+};
+
+/*
+ * R-Type instructions
+ */
+
+/*
+ * status <- estatus
+ * PC <- ea
+ */
+static void eret(DisasContext *dc, uint32_t code __attribute__((unused)))
+{
+#ifdef CALL_TRACING
+    TCGv_i32 tmp = tcg_const_i32(dc->pc);
+    gen_helper_eret_status(tmp);
+    tcg_temp_free_i32(tmp);
+#endif
+
+    tcg_gen_mov_tl(dc->cpu_R[CR_STATUS], dc->cpu_R[CR_ESTATUS]);
+    tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_EA]);
+
+    dc->is_jmp = DISAS_JUMP;
+}
+
+/* rC <- rA rotated left IMM5 bit positions */
+static void roli(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    tcg_gen_rotli_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], instr->imm5);
+}
+
+/* rC <- rA rotated left rB(4..0) bit positions */
+static void rol(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    TCGv t0 = tcg_temp_new();
+
+    tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
+    tcg_gen_rotl_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
+
+    tcg_temp_free(t0);
+}
+
+/* */
+static void flushp(DisasContext *dc __attribute__((unused)),
+                   uint32_t code __attribute__((unused)))
+{
+    /* TODO */
+}
+
+/* PC <- ra */
+static void ret(DisasContext *dc, uint32_t code __attribute__((unused)))
+{
+#ifdef CALL_TRACING
+    TCGv_i32 tmp = tcg_const_i32(dc->pc);
+    gen_helper_ret_status(tmp);
+    tcg_temp_free_i32(tmp);
+#endif
+
+    tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_RA]);
+
+    dc->is_jmp = DISAS_JUMP;
+}
+
+/* rC <- ~(A | rB) */
+static void nor(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    tcg_gen_nor_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+                   dc->cpu_R[instr->b]);
+}
+
+/* rC <- ((unsigned)rA * (unsigned)rB))(31..0) */
+static void mulxuu(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    TCGv_i64 t0, t1;
+
+    t0 = tcg_temp_new_i64();
+    t1 = tcg_temp_new_i64();
+
+    tcg_gen_extu_i32_i64(t0, dc->cpu_R[instr->a]);
+    tcg_gen_extu_i32_i64(t1, dc->cpu_R[instr->b]);
+    tcg_gen_mul_i64(t0, t0, t1);
+
+    tcg_gen_shri_i64(t0, t0, 32);
+    tcg_gen_extrl_i64_i32(dc->cpu_R[instr->c], t0);
+
+    tcg_temp_free_i64(t0);
+    tcg_temp_free_i64(t1);
+}
+
+/*
+ * if (rA >= rB)
+ *   rC <- 1
+ * else
+ *   rC <- 0
+ */
+static void cmpge(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    tcg_gen_setcond_tl(TCG_COND_GE, dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+                       dc->cpu_R[instr->b]);
+}
+
+/* PC <- ba */
+static void bret(DisasContext *dc, uint32_t code __attribute__((unused)))
+{
+    tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_BA]);
+
+    dc->is_jmp = DISAS_JUMP;
+}
+
+/*  rC <- rA rotated right rb(4..0) bit positions */
+static void ror(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    TCGv t0 = tcg_temp_new();
+
+    tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
+    tcg_gen_rotr_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
+
+    tcg_temp_free(t0);
+}
+
+/* */
+static void flushi(DisasContext *dc __attribute__((unused)),
+                   uint32_t code __attribute__((unused)))
+{
+    /* TODO */
+}
+
+/* PC <- rA */
+static void jmp(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[instr->a]);
+
+    dc->is_jmp = DISAS_JUMP;
+}
+
+/* rC <- rA & rB */
+static void and(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    tcg_gen_and_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+                   dc->cpu_R[instr->b]);
+}
+
+/*
+ * if ((signed) rA < (signed) rB)
+ *   rC <- 1
+ * else
+ *   rC <- 0
+ */
+static void cmplt(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    tcg_gen_setcond_tl(TCG_COND_LT, dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+                       dc->cpu_R[instr->b]);
+}
+
+/* rC <- rA << IMM5 */
+static void slli(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    tcg_gen_shli_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], instr->imm5);
+}
+
+/* rC <- rA << rB(4..0) */
+static void sll(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    TCGv t0 = tcg_temp_new();
+
+    tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
+    tcg_gen_shl_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
+
+    tcg_temp_free(t0);
+}
+
+/* rC <- rA | rB */
+static void or(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+                  dc->cpu_R[instr->b]);
+}
+
+/* rC <- ((signed)rA * (unsigned)rB))(31..0) */
+static void mulxsu(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    TCGv_i64 t0, t1;
+
+    t0 = tcg_temp_new_i64();
+    t1 = tcg_temp_new_i64();
+
+    tcg_gen_ext_i32_i64(t0, dc->cpu_R[instr->a]);
+    tcg_gen_extu_i32_i64(t1, dc->cpu_R[instr->b]);
+    tcg_gen_mul_i64(t0, t0, t1);
+
+    tcg_gen_shri_i64(t0, t0, 32);
+    tcg_gen_extrl_i64_i32(dc->cpu_R[instr->c], t0);
+
+    tcg_temp_free_i64(t0);
+    tcg_temp_free_i64(t1);
+}
+
+/*
+ * if (rA != rB)
+ *   rC <- 1
+ * else
+ *   rC <- 0
+ */
+static void cmpne(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    tcg_gen_setcond_tl(TCG_COND_NE, dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+                       dc->cpu_R[instr->b]);
+}
+
+/* rC <- (unsigned) rA >> ((unsigned) IMM5)*/
+static void srli(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    tcg_gen_shri_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], instr->imm5);
+}
+
+/* rC <- (unsigned) rA >> ((unsigned) rB(4..0))*/
+static void srl(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    TCGv t0 = tcg_temp_new();
+
+    tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
+    tcg_gen_shr_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
+
+    tcg_temp_free(t0);
+}
+
+/* rC <- PC + 4 */
+static void nextpc(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    tcg_gen_movi_tl(dc->cpu_R[instr->c], dc->pc + 4);
+}
+
+/*
+ * ra <- PC + 4
+ * PC <- rA
+ */
+static void callr(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+#ifdef CALL_TRACING
+    TCGv_i32 tmp = tcg_const_i32(dc->pc);
+    gen_helper_call_status(tmp, dc->cpu_R[instr->a]);
+    tcg_temp_free_i32(tmp);
+#endif
+
+    tcg_gen_movi_tl(dc->cpu_R[R_RA], dc->pc + 4);
+    tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[instr->a]);
+
+    dc->is_jmp = DISAS_JUMP;
+}
+
+/* rC <- rA ^ rB */
+static void xor(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    tcg_gen_xor_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+                   dc->cpu_R[instr->b]);
+}
+
+/* rC <- ((signed)rA * (signed)rB))(31..0) */
+static void mulxss(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    TCGv_i64 t0, t1;
+
+    t0 = tcg_temp_new_i64();
+    t1 = tcg_temp_new_i64();
+
+    tcg_gen_ext_i32_i64(t0, dc->cpu_R[instr->a]);
+    tcg_gen_ext_i32_i64(t1, dc->cpu_R[instr->b]);
+    tcg_gen_mul_i64(t0, t0, t1);
+
+    tcg_gen_shri_i64(t0, t0, 32);
+    tcg_gen_extrl_i64_i32(dc->cpu_R[instr->c], t0);
+
+    tcg_temp_free_i64(t0);
+    tcg_temp_free_i64(t1);
+}
+
+/*
+ * if (rA == rB)
+ *   rC <- 1
+ * else
+ *   rC <- 0
+ */
+static void cmpeq(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    tcg_gen_setcond_tl(TCG_COND_EQ, dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+                       dc->cpu_R[instr->b]);
+}
+
+/* rC <- rA / rB */
+static void divu(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    gen_helper_divu(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+                    dc->cpu_R[instr->b]);
+}
+
+/* rC <- rA / rB */
+static void _div(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    gen_helper_divs(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+                    dc->cpu_R[instr->b]);
+}
+
+/* rC <- ctlN */
+static void rdctl(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    TCGLabel *l1 = gen_new_label();
+    gen_check_supervisor(dc, l1);
+
+    switch (instr->imm5 + 32) {
+    case CR_PTEADDR:
+    case CR_TLBACC:
+    case CR_TLBMISC:
+    {
+#if !defined(CONFIG_USER_ONLY)
+        TCGv_i32 tmp = tcg_const_i32(instr->imm5 + 32);
+        gen_helper_mmu_read(dc->cpu_R[instr->c], dc->cpu_env, tmp);
+        tcg_temp_free_i32(tmp);
+#endif
+        break;
+    }
+
+    default:
+        tcg_gen_mov_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->imm5 + 32]);
+        break;
+    }
+
+    gen_set_label(l1);
+}
+
+/* rC <- (rA * rB))(31..0) */
+static void mul(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    tcg_gen_mul_i32(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+                    dc->cpu_R[instr->b]);
+}
+
+/*
+ * if (rA >= rB)
+ *   rC <- 1
+ * else
+ *   rC <- 0
+ */
+static void cmpgeu(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    tcg_gen_setcond_tl(TCG_COND_GEU, dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+                       dc->cpu_R[instr->b]);
+}
+
+/* */
+static void initi(DisasContext *dc __attribute__((unused)),
+                  uint32_t code __attribute__((unused)))
+{
+    /* TODO */
+}
+
+/*
+ * estatus <- status
+ * PIE <- 0
+ * U <- 0
+ * ea <- PC + 4
+ * PC <- exception handler address
+ */
+static void trap(DisasContext *dc, uint32_t code __attribute__((unused)))
+{
+    t_gen_helper_raise_exception(dc, EXCP_TRAP);
+}
+
+/* ctlN <- rA */
+static void wrctl(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    TCGLabel *l1 = gen_new_label();
+    gen_check_supervisor(dc, l1);
+
+    switch (instr->imm5 + 32) {
+    case CR_PTEADDR:
+    case CR_TLBACC:
+    case CR_TLBMISC:
+    {
+#if !defined(CONFIG_USER_ONLY)
+        TCGv_i32 tmp = tcg_const_i32(instr->imm5 + 32);
+        gen_helper_mmu_write(dc->cpu_env, tmp, dc->cpu_R[instr->a]);
+        tcg_temp_free_i32(tmp);
+#endif
+        break;
+    }
+
+    default:
+        tcg_gen_mov_tl(dc->cpu_R[instr->imm5 + 32], dc->cpu_R[instr->a]);
+        break;
+    }
+
+    /* If interrupts were enabled using WRCTL, trigger them. */
+#if !defined(CONFIG_USER_ONLY)
+    if ((instr->imm5 + 32) == CR_STATUS) {
+        gen_helper_check_interrupts(dc->cpu_env);
+    }
+#endif
+
+    gen_set_label(l1);
+}
+
+/*
+ * if (rA < rB)
+ *   rC <- 1
+ * else
+ *   rC <- 0
+ */
+static void cmpltu(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    tcg_gen_setcond_tl(TCG_COND_LTU, dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+                       dc->cpu_R[instr->b]);
+}
+
+/* rC <- rA + rB */
+static void add(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    tcg_gen_add_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+                   dc->cpu_R[instr->b]);
+}
+
+/*
+ * bstatus ← status
+ * PIE <- 0
+ * U <- 0
+ * ba <- PC + 4
+ * PC <- break handler address
+ */
+static void __break(DisasContext *dc, uint32_t code __attribute__((unused)))
+{
+    t_gen_helper_raise_exception(dc, EXCP_BREAK);
+}
+
+/* rC <- rA - rB */
+static void sub(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    tcg_gen_sub_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
+                   dc->cpu_R[instr->b]);
+}
+
+/* rC <- (signed) rA >> ((unsigned) IMM5) */
+static void srai(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    tcg_gen_sari_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], instr->imm5);
+}
+
+/* rC <- (signed) rA >> ((unsigned) rB(4..0)) */
+static void sra(DisasContext *dc, uint32_t code)
+{
+    R_TYPE(instr, code);
+
+    TCGv t0 = tcg_temp_new();
+
+    tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
+    tcg_gen_sar_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
+
+    tcg_temp_free(t0);
+}
+
+static const Nios2Instruction r_type_instructions[R_TYPE_COUNT] = {
+    [0x00]   = INSTRUCTION_ILLEGAL(),
+    [ERET]   = INSTRUCTION(eret),
+    [ROLI]   = INSTRUCTION(roli),
+    [ROL]    = INSTRUCTION(rol),
+    [FLUSHP] = INSTRUCTION(flushp),
+    [RET]    = INSTRUCTION(ret),
+    [NOR]    = INSTRUCTION(nor),
+    [MULXUU] = INSTRUCTION(mulxuu),
+    [CMPGE]  = INSTRUCTION(cmpge),
+    [BRET]   = INSTRUCTION(bret),
+    [0x0a]   = INSTRUCTION_ILLEGAL(),
+    [ROR]    = INSTRUCTION(ror),
+    [FLUSHI] = INSTRUCTION(flushi),
+    [JMP]    = INSTRUCTION(jmp),
+    [AND]    = INSTRUCTION(and),
+    [0x0f]   = INSTRUCTION_ILLEGAL(),
+    [CMPLT]  = INSTRUCTION(cmplt),
+    [0x11]   = INSTRUCTION_ILLEGAL(),
+    [SLLI]   = INSTRUCTION(slli),
+    [SLL]    = INSTRUCTION(sll),
+    [WRPRS]  = INSTRUCTION_UNIMPLEMENTED(wrprs),
+    [0x15]   = INSTRUCTION_ILLEGAL(),
+    [OR]     = INSTRUCTION(or),
+    [MULXSU] = INSTRUCTION(mulxsu),
+    [CMPNE]  = INSTRUCTION(cmpne),
+    [0x19]   = INSTRUCTION_ILLEGAL(),
+    [SRLI]   = INSTRUCTION(srli),
+    [SRL]    = INSTRUCTION(srl),
+    [NEXTPC] = INSTRUCTION(nextpc),
+    [CALLR]  = INSTRUCTION(callr),
+    [XOR]    = INSTRUCTION(xor),
+    [MULXSS] = INSTRUCTION(mulxss),
+    [CMPEQ]  = INSTRUCTION(cmpeq),
+    [0x21]   = INSTRUCTION_ILLEGAL(),
+    [0x22]   = INSTRUCTION_ILLEGAL(),
+    [0x23]   = INSTRUCTION_ILLEGAL(),
+    [DIVU]   = INSTRUCTION(divu),
+    [DIV]    = { "div", _div },
+    [RDCTL]  = INSTRUCTION(rdctl),
+    [MUL]    = INSTRUCTION(mul),
+    [CMPGEU] = INSTRUCTION(cmpgeu),
+    [INITI]  = INSTRUCTION(initi),
+    [0x2a]   = INSTRUCTION_ILLEGAL(),
+    [0x2b]   = INSTRUCTION_ILLEGAL(),
+    [0x2c]   = INSTRUCTION_ILLEGAL(),
+    [TRAP]   = INSTRUCTION(trap),
+    [WRCTL]  = INSTRUCTION(wrctl),
+    [0x2f]   = INSTRUCTION_ILLEGAL(),
+    [CMPLTU] = INSTRUCTION(cmpltu),
+    [ADD]    = INSTRUCTION(add),
+    [0x32]   = INSTRUCTION_ILLEGAL(),
+    [0x33]   = INSTRUCTION_ILLEGAL(),
+    [BREAK]  = { "break", __break },
+    [0x35]   = INSTRUCTION_ILLEGAL(),
+    [SYNC]   = INSTRUCTION(nop),
+    [0x37]   = INSTRUCTION_ILLEGAL(),
+    [0x38]   = INSTRUCTION_ILLEGAL(),
+    [SUB]    = INSTRUCTION(sub),
+    [SRAI]   = INSTRUCTION(srai),
+    [SRA]    = INSTRUCTION(sra),
+    [0x3c]   = INSTRUCTION_ILLEGAL(),
+    [0x3d]   = INSTRUCTION_ILLEGAL(),
+    [0x3e]   = INSTRUCTION_ILLEGAL(),
+    [0x3f]   = INSTRUCTION_ILLEGAL(),
+};
+
+static void handle_r_type_instr(DisasContext *dc, uint32_t code)
+{
+    uint32_t opx;
+    instruction_handler handle_instr;
+
+    opx = get_opxcode(code);
+    if (unlikely(opx >= R_TYPE_COUNT)) {
+        goto illegal_op;
+    }
+
+    LOG_DIS("R: %s (%08x)\n", r_type_instructions[opx].name, code);
+    handle_instr = r_type_instructions[opx].handler;
+
+    handle_instr(dc, code);
+
+    return;
+
+illegal_op:
+    t_gen_helper_raise_exception(dc, EXCP_ILLEGAL);
+}
+
+#include "exec/cpu_ldst.h"
+
+void handle_instruction(DisasContext *dc, CPUNios2State *env)
+{
+    uint32_t insn;
+    uint32_t op;
+#if defined(CONFIG_USER_ONLY)
+    /* FIXME: Is this needed ? */
+    if (dc->pc >= 0x1000 && dc->pc < 0x2000) {
+        env->regs[R_PC] = dc->pc;
+        t_gen_helper_raise_exception(dc, 0xaa);
+       return;
+    }
+#endif
+    insn = cpu_ldl_code(env, dc->pc);
+    op = get_opcode(insn);
+
+    LOG_DIS("%8.8x\t", insn);
+
+    if (unlikely(op >= I_TYPE_COUNT)) {
+        goto illegal_op;
+    }
+
+    if (op != R_TYPE) {
+        LOG_DIS("I: %s (%08x)\n", i_type_instructions[op].name, insn);
+    }
+    i_type_instructions[op].handler(dc, insn);
+
+    return;
+
+illegal_op:
+    t_gen_helper_raise_exception(dc, EXCP_ILLEGAL);
+}
+
+const char *instruction_get_string(uint32_t code)
+{
+    uint32_t op = get_opcode(code);
+
+    if (unlikely(op >= I_TYPE_COUNT)) {
+        return "";
+    } else if (op == R_TYPE) {
+        uint32_t opx = get_opxcode(code);
+        if (unlikely(opx >= R_TYPE_COUNT)) {
+            return "";
+        }
+        return r_type_instructions[opx].name;
+    } else {
+        return i_type_instructions[op].name;
+    }
+}
+
diff --git a/target-nios2/instruction.h b/target-nios2/instruction.h
new file mode 100644
index 0000000..3758899
--- /dev/null
+++ b/target-nios2/instruction.h
@@ -0,0 +1,279 @@ 
+/*
+ * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2010 chysun2000@gmail.com
+ *  (Portions of this file that were originally from nios2sim-ng.)
+ *
+ * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * 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 _INSTRUCTION_H_
+#define _INSTRUCTION_H_
+
+#include <stdint.h>
+#include "cpu.h"
+#include "tcg-op.h"
+
+/*
+ * Instruction Word Formats
+ */
+
+/* I-Type instruction */
+typedef struct Nios2IType {
+    uint32_t op:6;
+    uint32_t imm16:16;
+    uint32_t b:5;
+    uint32_t a:5;
+} QEMU_PACKED Nios2IType;
+
+union i_type_u {
+    uint32_t      v;
+    Nios2IType i;
+};
+
+#define I_TYPE(instr, op) \
+    union i_type_u instr_u = { .v = op }; \
+    Nios2IType *instr = &instr_u.i
+
+/* R-Type instruction */
+typedef struct Nios2RType {
+    uint32_t op:6;
+    /*
+     * Some R-Type instructions embed a small immediate value in the
+     * low-order bits of OPX.
+     */
+    uint32_t imm5:5;
+    uint32_t opx6:6;
+    uint32_t c:5;
+    uint32_t b:5;
+    uint32_t a:5;
+} QEMU_PACKED Nios2RType;
+
+union r_type_u {
+    uint32_t      v;
+    Nios2RType i;
+};
+
+#define R_TYPE(instr, op) \
+    union r_type_u instr_u = { .v = op }; \
+    Nios2RType *instr = &instr_u.i
+
+/* J-Type instruction */
+typedef struct Nios2JType {
+    uint32_t op:6;
+    uint32_t imm26:26;
+} QEMU_PACKED Nios2JType;
+
+#define J_TYPE(instr, op) \
+    Nios2JType *instr = (Nios2JType *) &op
+
+/*
+ * Instruction Opcodes
+ */
+
+/*
+ * OP Encodings for I-Type instructions (except for CALL and JMPI, which are
+ * J-type instructions)
+ */
+enum {
+    CALL    = 0x00,  /* J-type */
+    JMPI    = 0x01,  /* J-type */
+           /* 0x02 */
+    LDBU    = 0x03,
+    ADDI    = 0x04,
+    STB     = 0x05,
+    BR      = 0x06,
+    LDB     = 0x07,
+    CMPGEI  = 0x08,
+           /* 0x09 */
+           /* 0x0A */
+    LDHU    = 0x0B,
+    ANDI    = 0x0C,
+    STH     = 0x0D,
+    BGE     = 0x0E,
+    LDH     = 0x0F,
+    CMPLTI  = 0x10,
+           /* 0x11 */
+           /* 0x12 */
+    INITDA  = 0x13,
+    ORI     = 0x14,
+    STW     = 0x15,
+    BLT     = 0x16,
+    LDW     = 0x17,
+    CMPNEI  = 0x18,
+           /* 0x19 */
+           /* 0x1A */
+    FLUSHDA = 0x1B,
+    XORI    = 0x1C,
+           /* 0x1D */
+    BNE     = 0x1E,
+           /* 0x1F */
+    CMPEQI  = 0x20,
+           /* 0x21 */
+           /* 0x22 */
+    LDBUIO  = 0x23,
+    MULI    = 0x24,
+    STBIO   = 0x25,
+    BEQ     = 0x26,
+    LDBIO   = 0x27,
+    CMPGEUI = 0x28,
+           /* 0x29 */
+           /* 0x2A */
+    LDHUIO  = 0x2B,
+    ANDHI   = 0x2C,
+    STHIO   = 0x2D,
+    BGEU    = 0x2E,
+    LDHIO   = 0x2F,
+    CMPLTUI = 0x30,
+           /* 0x31 */
+    CUSTOM  = 0x32,
+    INITD   = 0x33,
+    ORHI    = 0x34,
+    STWIO   = 0x35,
+    BLTU    = 0x36,
+    LDWIO   = 0x37,
+    RDPRS   = 0x38,
+           /* 0x39 */
+    R_TYPE  = 0x3A,
+    FLUSHD  = 0x3B,
+    XORHI   = 0x3C,
+           /* 0x3D */
+           /* 0x3E */
+           /* 0x3F */
+};
+#define I_TYPE_COUNT  0x40
+
+/* OPX Encodings for R-Type instructions */
+enum {
+          /* 0x00 */
+    ERET   = 0x01,
+    ROLI   = 0x02,
+    ROL    = 0x03,
+    FLUSHP = 0x04,
+    RET    = 0x05,
+    NOR    = 0x06,
+    MULXUU = 0x07,
+    CMPGE  = 0x08,
+    BRET   = 0x09,
+          /* 0x0A */
+    ROR    = 0x0B,
+    FLUSHI = 0x0C,
+    JMP    = 0x0D,
+    AND    = 0x0E,
+          /* 0x0F */
+    CMPLT  = 0x10,
+          /* 0x11 */
+    SLLI   = 0x12,
+    SLL    = 0x13,
+    WRPRS  = 0x14,
+          /* 0x15 */
+    OR     = 0x16,
+    MULXSU = 0x17,
+    CMPNE  = 0x18,
+          /* 0x19 */
+    SRLI   = 0x1A,
+    SRL    = 0x1B,
+    NEXTPC = 0x1C,
+    CALLR  = 0x1D,
+    XOR    = 0x1E,
+    MULXSS = 0x1F,
+    CMPEQ  = 0x20,
+          /* 0x21 */
+          /* 0x22 */
+          /* 0x23 */
+    DIVU   = 0x24,
+    DIV    = 0x25,
+    RDCTL  = 0x26,
+    MUL    = 0x27,
+    CMPGEU = 0x28,
+    INITI  = 0x29,
+          /* 0x2A */
+          /* 0x2B */
+          /* 0x2C */
+    TRAP   = 0x2D,
+    WRCTL  = 0x2E,
+          /* 0x2F */
+    CMPLTU = 0x30,
+    ADD    = 0x31,
+          /* 0x32 */
+          /* 0x33 */
+    BREAK  = 0x34,
+          /* 0x35 */
+    SYNC   = 0x36,
+          /* 0x37 */
+          /* 0x38 */
+    SUB    = 0x39,
+    SRAI   = 0x3A,
+    SRA    = 0x3B,
+          /* 0x3C */
+          /* 0x3D */
+          /* 0x3E */
+          /* 0x3F */
+};
+#define R_TYPE_COUNT  0x40
+
+/*
+ * Return values for instruction handlers
+ */
+#define INSTR_UNIMPL     -2  /* Unimplemented instruction */
+#define INSTR_ERR        -1  /* Error in instruction */
+#define PC_INC_NORMAL     0  /* Normal PC increment after instruction */
+#define PC_INC_BY_INSTR   1  /* PC got incremented by instruction */
+#define INSTR_BREAK       2  /* Break encountered */
+#define INSTR_EXCEPTION 255  /* Instruction generated an exception
+                                (the exception cause will be stored
+                                in struct nios2 */
+
+#define EXCEPTION(cpu, cause)           \
+    ({                                  \
+        (cpu)->exception_cause = cause; \
+        INSTR_EXCEPTION;                \
+    })
+
+typedef struct DisasContext {
+    TCGv_ptr          cpu_env;
+    TCGv             *cpu_R;
+    int               is_jmp;
+    target_ulong      pc;
+    TranslationBlock *tb;
+    int               mem_idx;
+} DisasContext;
+
+typedef void (*instruction_handler)(DisasContext *dc, uint32_t opcode);
+
+typedef struct Nios2Instruction {
+    const char         *name;
+    instruction_handler handler;
+} Nios2Instruction;
+
+#define INSTRUCTION(name)    { stringify(name), name }
+#define INSTRUCTION_NOP(name)    { stringify(name), nop }
+#define INSTRUCTION_UNIMPLEMENTED(name)  { stringify(name), unimplemented }
+#define INSTRUCTION_ILLEGAL()  { "", illegal_instruction }
+
+extern void handle_instruction(DisasContext *dc, CPUNios2State *env);
+extern const char *instruction_get_string(uint32_t code);
+
+#define SIM_COMPAT 0
+#define DISAS_GNU 1   /* Disassembly via GNU gdb derived routines */
+#define DISAS_NIOS2 0 /* Disassembly via routines in instruction.c */
+#if DISAS_NIOS2 && !SIM_COMPAT
+#  define LOG_DIS(...) qemu_log_mask(CPU_LOG_TB_IN_ASM, ## __VA_ARGS__)
+#else
+#  define LOG_DIS(...) do { } while (0)
+#endif
+
+#endif /* _INSTRUCTION_H_ */
diff --git a/target-nios2/machine.c b/target-nios2/machine.c
new file mode 100644
index 0000000..da2cb04
--- /dev/null
+++ b/target-nios2/machine.c
@@ -0,0 +1,38 @@ 
+/*
+ * Altera Nios II MMU emulation for qemu.
+ *
+ * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * 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>
+ */
+
+/*
+ * FIXME: Convert to VMstate
+ */
+
+#include "qemu/osdep.h"
+#include "hw/hw.h"
+#include "hw/boards.h"
+
+void cpu_save(QEMUFile *f, void *opaque)
+{
+    /* TODO */
+}
+
+int cpu_load(QEMUFile *f, void *opaque, int version_id)
+{
+    /* TODO */
+    return 0;
+}
diff --git a/target-nios2/mmu.c b/target-nios2/mmu.c
new file mode 100644
index 0000000..fbb846a
--- /dev/null
+++ b/target-nios2/mmu.c
@@ -0,0 +1,292 @@ 
+/*
+ * Altera Nios II MMU emulation for qemu.
+ *
+ * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "cpu.h"
+#include "exec/exec-all.h"
+#include "mmu.h"
+
+#if !defined(CONFIG_USER_ONLY)
+
+/* Define this to enable MMU debug messages */
+/* #define DEBUG_MMU */
+
+#ifdef DEBUG_MMU
+#define MMU_LOG(x) x
+#else
+#define MMU_LOG(x)
+#endif
+
+void tlb_fill(CPUState *cs, target_ulong addr, MMUAccessType access_type,
+	      int mmu_idx, uintptr_t retaddr)
+{
+    int ret;
+
+    ret = nios2_cpu_handle_mmu_fault(cs, addr, access_type, mmu_idx);
+    if (unlikely(ret)) {
+        if (retaddr) {
+            /* now we have a real cpu fault */
+            cpu_restore_state(cs, retaddr);
+        }
+        cpu_loop_exit(cs);
+    }
+}
+
+uint32_t mmu_read(CPUNios2State *env, uint32_t rn)
+{
+    switch (rn) {
+    case CR_TLBACC:
+        MMU_LOG(qemu_log("TLBACC READ %08X\n", env->regs[rn]));
+        break;
+
+    case CR_TLBMISC:
+        MMU_LOG(qemu_log("TLBMISC READ %08X\n", env->regs[rn]));
+        break;
+
+    case CR_PTEADDR:
+        MMU_LOG(qemu_log("PTEADDR READ %08X\n", env->regs[rn]));
+        break;
+
+    default:
+        break;
+    }
+    return env->regs[rn];
+}
+
+/* rw - 0 = read, 1 = write, 2 = fetch.  */
+unsigned int mmu_translate(CPUNios2State *env,
+                           Nios2MMULookup *lu,
+                           target_ulong vaddr, int rw, int mmu_idx)
+{
+    int pid = (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4;
+    int vpn = vaddr >> 12;
+
+    MMU_LOG(qemu_log("mmu_translate vaddr %08X, pid %08X, vpn %08X\n",
+                     vaddr, pid, vpn));
+
+    int way;
+    for (way = 0; way < env->mmu.tlb_num_ways; way++) {
+
+        Nios2TLBEntry *entry =
+            &env->mmu.tlb[(way * env->mmu.tlb_num_ways) +
+                          (vpn & env->mmu.tlb_entry_mask)];
+
+        MMU_LOG(qemu_log("TLB[%d] TAG %08X, VPN %08X\n",
+                         (way * env->mmu.tlb_num_ways) +
+                         (vpn & env->mmu.tlb_entry_mask),
+                         entry->tag, (entry->tag >> 12)));
+
+        if (((entry->tag >> 12) != vpn) ||
+            (((entry->tag & (1<<11)) == 0) &&
+            ((entry->tag & ((1<<env->mmu.pid_bits)-1)) != pid))) {
+            continue;
+        }
+        lu->vaddr = vaddr & TARGET_PAGE_MASK;
+        lu->paddr = (entry->data & CR_TLBACC_PFN_MASK) << TARGET_PAGE_BITS;
+        lu->prot = ((entry->data & CR_TLBACC_R) ? PAGE_READ : 0) |
+                   ((entry->data & CR_TLBACC_W) ? PAGE_WRITE : 0) |
+                   ((entry->data & CR_TLBACC_X) ? PAGE_EXEC : 0);
+
+        MMU_LOG(qemu_log("HIT TLB[%d] %08X %08X %08X\n",
+                         (way * env->mmu.tlb_num_ways) +
+                         (vpn & env->mmu.tlb_entry_mask),
+                         lu->vaddr, lu->paddr, lu->prot));
+        return 1;
+    }
+    return 0;
+}
+
+static void mmu_flush_pid(CPUNios2State *env, uint32_t pid)
+{
+    CPUState *cs = ENV_GET_CPU(env);
+    int idx;
+    MMU_LOG(qemu_log("TLB Flush PID %d\n", pid));
+
+    for (idx = 0; idx < env->mmu.tlb_num_entries; idx++) {
+        Nios2TLBEntry *entry = &env->mmu.tlb[idx];
+
+        MMU_LOG(qemu_log("TLB[%d] => %08X %08X\n",
+                         idx, entry->tag, entry->data));
+
+        if ((entry->tag & (1<<10)) && (!(entry->tag & (1<<11))) &&
+            ((entry->tag & ((1<<env->mmu.pid_bits)-1)) == pid)) {
+            uint32_t vaddr = entry->tag & TARGET_PAGE_MASK;
+
+            MMU_LOG(qemu_log("TLB Flush Page %08X\n", vaddr));
+
+            tlb_flush_page(cs, vaddr);
+        }
+    }
+}
+
+void mmu_write(CPUNios2State *env, uint32_t rn, uint32_t v)
+{
+    CPUState *cs = ENV_GET_CPU(env);
+
+    MMU_LOG(qemu_log("mmu_write %08X = %08X\n", rn, v));
+
+    switch (rn) {
+    case CR_TLBACC:
+        MMU_LOG(qemu_log("TLBACC: IG %02X, FLAGS %c%c%c%c%c, PFN %05X\n",
+                         v >> CR_TLBACC_IGN_SHIFT,
+                         (v & CR_TLBACC_C) ? 'C' : '.',
+                         (v & CR_TLBACC_R) ? 'R' : '.',
+                         (v & CR_TLBACC_W) ? 'W' : '.',
+                         (v & CR_TLBACC_X) ? 'X' : '.',
+                         (v & CR_TLBACC_G) ? 'G' : '.',
+                         v & CR_TLBACC_PFN_MASK));
+
+        /* if tlbmisc.WE == 1 then trigger a TLB write on writes to TLBACC */
+        if (env->regs[CR_TLBMISC] & CR_TLBMISC_WR) {
+            int way = (env->regs[CR_TLBMISC] >> CR_TLBMISC_WAY_SHIFT);
+            int vpn = (env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK) >> 2;
+            int pid = (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4;
+            int g = (v & CR_TLBACC_G) ? 1 : 0;
+            int valid = ((vpn & CR_TLBACC_PFN_MASK) < 0xC0000) ? 1 : 0;
+            Nios2TLBEntry *entry =
+                &env->mmu.tlb[(way * env->mmu.tlb_num_ways) +
+                              (vpn & env->mmu.tlb_entry_mask)];
+            uint32_t newTag = (vpn << 12) | (g << 11) | (valid << 10) | pid;
+            uint32_t newData = v & (CR_TLBACC_C | CR_TLBACC_R | CR_TLBACC_W |
+                                    CR_TLBACC_X | CR_TLBACC_PFN_MASK);
+
+            if ((entry->tag != newTag) || (entry->data != newData)) {
+                if (entry->tag & (1<<10)) {
+                    /* Flush existing entry */
+                    MMU_LOG(qemu_log("TLB Flush Page (OLD) %08X\n",
+                                     entry->tag & TARGET_PAGE_MASK));
+                    tlb_flush_page(cs, entry->tag & TARGET_PAGE_MASK);
+                }
+                entry->tag = newTag;
+                entry->data = newData;
+                MMU_LOG(qemu_log("TLB[%d] = %08X %08X\n",
+                                 (way * env->mmu.tlb_num_ways) +
+                                 (vpn & env->mmu.tlb_entry_mask),
+                                 entry->tag, entry->data));
+            }
+            /* Auto-increment tlbmisc.WAY */
+            env->regs[CR_TLBMISC] =
+                (env->regs[CR_TLBMISC] & ~CR_TLBMISC_WAY_MASK) |
+                (((way+1) & (env->mmu.tlb_num_ways-1)) << CR_TLBMISC_WAY_SHIFT);
+        }
+
+        /* Writes to TLBACC don't change the read-back value */
+        env->mmu.tlbacc_wr = v;
+        break;
+
+    case CR_TLBMISC:
+        MMU_LOG(qemu_log("TLBMISC: WAY %X, FLAGS %c%c%c%c%c%c, PID %04X\n",
+                         v >> CR_TLBMISC_WAY_SHIFT,
+                         (v & CR_TLBMISC_RD) ? 'R' : '.',
+                         (v & CR_TLBMISC_WR) ? 'W' : '.',
+                         (v & CR_TLBMISC_DBL) ? '2' : '.',
+                         (v & CR_TLBMISC_BAD) ? 'B' : '.',
+                         (v & CR_TLBMISC_PERM) ? 'P' : '.',
+                         (v & CR_TLBMISC_D) ? 'D' : '.',
+                         (v & CR_TLBMISC_PID_MASK) >> 4));
+
+        if ((v & CR_TLBMISC_PID_MASK) !=
+            (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK)) {
+            mmu_flush_pid(env, (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >>
+                               CR_TLBMISC_PID_SHIFT);
+        }
+        /* if tlbmisc.RD == 1 then trigger a TLB read on writes to TLBMISC */
+        if (v & CR_TLBMISC_RD) {
+            int way = (v >> CR_TLBMISC_WAY_SHIFT);
+            int vpn = (env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK) >> 2;
+            Nios2TLBEntry *entry =
+                &env->mmu.tlb[(way * env->mmu.tlb_num_ways) +
+                              (vpn & env->mmu.tlb_entry_mask)];
+
+            env->regs[CR_TLBACC] &= CR_TLBACC_IGN_MASK;
+            env->regs[CR_TLBACC] |= entry->data |
+                                    ((entry->tag & (1<<11)) ? CR_TLBACC_G : 0);
+            env->regs[CR_TLBMISC] =
+                (v & ~CR_TLBMISC_PID_MASK) |
+                ((entry->tag & ((1<<env->mmu.pid_bits)-1)) <<
+                 CR_TLBMISC_PID_SHIFT);
+            env->regs[CR_PTEADDR] &= ~CR_PTEADDR_VPN_MASK;
+            env->regs[CR_PTEADDR] |= (entry->tag >> 12) << CR_PTEADDR_VPN_SHIFT;
+            MMU_LOG(qemu_log("TLB READ way %d, vpn %05X, tag %08X, data %08X, "
+                             "tlbacc %08X, tlbmisc %08X, pteaddr %08X\n",
+                             way, vpn, entry->tag, entry->data,
+                             env->regs[CR_TLBACC], env->regs[CR_TLBMISC],
+                             env->regs[CR_PTEADDR]));
+        } else {
+            env->regs[CR_TLBMISC] = v;
+        }
+
+        env->mmu.tlbmisc_wr = v;
+        break;
+
+    case CR_PTEADDR:
+        MMU_LOG(qemu_log("PTEADDR: PTBASE %03X, VPN %05X\n",
+                         v >> CR_PTEADDR_PTBASE_SHIFT,
+                         (v & CR_PTEADDR_VPN_MASK) >> CR_PTEADDR_VPN_SHIFT));
+
+        /* Writes to PTEADDR don't change the read-back VPN value */
+        env->regs[CR_PTEADDR] = (v & ~CR_PTEADDR_VPN_MASK) |
+                                (env->regs[CR_PTEADDR] & CR_PTEADDR_VPN_MASK);
+        env->mmu.pteaddr_wr = v;
+        break;
+
+    default:
+        break;
+    }
+}
+
+void mmu_init(Nios2MMU *mmu)
+{
+    MMU_LOG(qemu_log("mmu_init\n"));
+
+    mmu->pid_bits = 8;          /* TODO: get this from ALTR,pid-num-bits */
+    mmu->tlb_num_ways = 16;     /* TODO: get this from ALTR,tlb-num-ways */
+    mmu->tlb_num_entries = 256; /* TODO: get this from ALTR,tlb-num-entries */
+    mmu->tlb_entry_mask = (mmu->tlb_num_entries/mmu->tlb_num_ways) - 1;
+
+    mmu->tlb = (Nios2TLBEntry *)g_malloc0(
+        sizeof(Nios2TLBEntry) * mmu->tlb_num_entries);
+}
+
+void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUNios2State *env)
+{
+    int i;
+    cpu_fprintf(f, "MMU: ways %d, entries %d, pid bits %d\n",
+                env->mmu.tlb_num_ways, env->mmu.tlb_num_entries,
+                env->mmu.pid_bits);
+
+    for (i = 0; i < env->mmu.tlb_num_entries; i++) {
+        Nios2TLBEntry *entry = &env->mmu.tlb[i];
+        cpu_fprintf(f, "TLB[%d] = %08X %08X %c VPN %05X "
+                    "PID %02X %c PFN %05X %c%c%c%c\n",
+                    i, entry->tag, entry->data,
+                    (entry->tag & (1<<10)) ? 'V' : '-',
+                    entry->tag >> 12, entry->tag & ((1<<env->mmu.pid_bits)-1),
+                    (entry->tag & (1<<11)) ? 'G' : '-',
+                    entry->data & CR_TLBACC_PFN_MASK,
+                    (entry->data & CR_TLBACC_C) ? 'C' : '-',
+                    (entry->data & CR_TLBACC_R) ? 'R' : '-',
+                    (entry->data & CR_TLBACC_W) ? 'W' : '-',
+                    (entry->data & CR_TLBACC_X) ? 'X' : '-');
+    }
+}
+
+#endif /* !CONFIG_USER_ONLY */
diff --git a/target-nios2/mmu.h b/target-nios2/mmu.h
new file mode 100644
index 0000000..797db67
--- /dev/null
+++ b/target-nios2/mmu.h
@@ -0,0 +1,54 @@ 
+/*
+ * Altera Nios II MMU emulation for qemu.
+ *
+ * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * 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 MMU_NIOS2_H
+#define MMU_NIOS2_H
+
+
+typedef struct Nios2TLBEntry {
+    target_ulong tag;
+    target_ulong data;
+} Nios2TLBEntry;
+
+typedef struct Nios2MMU {
+    int pid_bits;
+    int tlb_num_ways;
+    int tlb_num_entries;
+    int tlb_entry_mask;
+    uint32_t pteaddr_wr;
+    uint32_t tlbacc_wr;
+    uint32_t tlbmisc_wr;
+    Nios2TLBEntry *tlb;
+} Nios2MMU;
+
+typedef struct Nios2MMULookup {
+    target_ulong vaddr;
+    target_ulong paddr;
+    int prot;
+} Nios2MMULookup;
+
+void mmu_flip_um(CPUNios2State *env, unsigned int um);
+unsigned int mmu_translate(CPUNios2State *env,
+                           Nios2MMULookup *lu,
+                           target_ulong vaddr, int rw, int mmu_idx);
+uint32_t mmu_read(CPUNios2State *env, uint32_t rn);
+void mmu_write(CPUNios2State *env, uint32_t rn, uint32_t v);
+void mmu_init(Nios2MMU *mmu);
+
+#endif /* MMU_NIOS2_H */
diff --git a/target-nios2/monitor.c b/target-nios2/monitor.c
new file mode 100644
index 0000000..f3fa4cd
--- /dev/null
+++ b/target-nios2/monitor.c
@@ -0,0 +1,35 @@ 
+/*
+ * QEMU monitor
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "monitor/monitor.h"
+#include "monitor/hmp-target.h"
+#include "hmp.h"
+
+void hmp_info_tlb(Monitor *mon, const QDict *qdict)
+{
+    CPUArchState *env1 = mon_get_cpu_env();
+
+    dump_mmu((FILE*)mon, (fprintf_function)monitor_printf, env1);
+}
diff --git a/target-nios2/op_helper.c b/target-nios2/op_helper.c
new file mode 100644
index 0000000..b8ef396
--- /dev/null
+++ b/target-nios2/op_helper.c
@@ -0,0 +1,86 @@ 
+/*
+ * Altera Nios II helper routines.
+ *
+ * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * 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 "exec/helper-proto.h"
+#include "exec/cpu_ldst.h"
+
+#if !defined(CONFIG_USER_ONLY)
+uint32_t helper_mmu_read(CPUNios2State *env, uint32_t rn)
+{
+    return mmu_read(env, rn);
+}
+
+void helper_mmu_write(CPUNios2State *env, uint32_t rn, uint32_t v)
+{
+    mmu_write(env, rn, v);
+}
+
+void helper_check_interrupts(CPUNios2State *env)
+{
+    nios2_check_interrupts(env);
+}
+#endif /* !CONFIG_USER_ONLY */
+
+void helper_raise_exception(CPUNios2State *env, uint32_t index)
+{
+    CPUState *cs = ENV_GET_CPU(env);
+    cs->exception_index = index;
+    cpu_loop_exit(cs);
+}
+
+void helper_memalign(CPUNios2State *env, uint32_t addr, uint32_t dr, uint32_t wr, uint32_t mask)
+{
+    if (addr & mask) {
+        qemu_log("unaligned access addr=%x mask=%x, wr=%d dr=r%d\n",
+                 addr, mask, wr, dr);
+        env->regs[CR_BADADDR] = addr;
+        env->regs[CR_EXCEPTION] = EXCP_UNALIGN << 2;
+        helper_raise_exception(env, EXCP_UNALIGN);
+    }
+}
+
+uint32_t helper_divs(uint32_t a, uint32_t b)
+{
+    return (int32_t)a / (int32_t)b;
+}
+
+uint32_t helper_divu(uint32_t a, uint32_t b)
+{
+    return a / b;
+}
+
+#ifdef CALL_TRACING
+void helper_call_status(uint32_t pc, uint32_t target)
+{
+    qemu_log("%08X: CALL %08X %s\n", pc, target, lookup_symbol(target));
+}
+
+void helper_eret_status(uint32_t pc)
+{
+    qemu_log("%08X: ERET STATUS %08X, ESTATUS %08X, EA %08X\n",
+             pc, env->regs[CR_STATUS], env->regs[CR_ESTATUS], env->regs[R_EA]);
+}
+
+void helper_ret_status(uint32_t pc)
+{
+    qemu_log("%08X: RET RA %08X\n", pc, env->regs[R_RA]);
+}
+#endif
diff --git a/target-nios2/translate.c b/target-nios2/translate.c
new file mode 100644
index 0000000..16d1df8
--- /dev/null
+++ b/target-nios2/translate.c
@@ -0,0 +1,242 @@ 
+/*
+ * Altera Nios II emulation for qemu: main translation routines.
+ *
+ * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com>
+ *
+ * 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 <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 "exec/helper-proto.h"
+#include "qemu-common.h"
+
+#include "instruction.h"
+
+#include "exec/cpu_ldst.h"
+#include "exec/helper-gen.h"
+#include "exec/log.h"
+
+static const char *regnames[] = {
+    "zero",     "at",       "r2",       "r3",
+    "r4",       "r5",       "r6",       "r7",
+    "r8",       "r9",       "r10",      "r11",
+    "r12",      "r13",      "r14",      "r15",
+    "r16",      "r17",      "r18",      "r19",
+    "r20",      "r21",      "r22",      "r23",
+    "et",       "bt",       "gp",       "sp",
+    "fp",       "ea",       "ba",       "ra",
+    "status",   "estatus",  "bstatus",  "ienable",
+    "ipending", "cpuid",    "reserved", "exception",
+    "pteaddr",  "tlbacc",   "tlbmisc",  "reserved",
+    "badaddr",  "config",   "mpubase",  "mpuacc",
+    "reserved", "reserved", "reserved", "reserved",
+    "reserved", "reserved", "reserved", "reserved",
+    "reserved", "reserved", "reserved", "reserved",
+    "reserved", "reserved", "reserved", "reserved",
+    "rpc"
+};
+
+static TCGv_ptr cpu_env;
+static TCGv cpu_R[NUM_CORE_REGS];
+
+#include "exec/gen-icount.h"
+
+static void gen_exception(DisasContext *dc, uint32_t excp)
+{
+    TCGv_i32 tmp = tcg_const_i32(excp);
+
+    tcg_gen_movi_tl(cpu_R[R_PC], dc->pc);
+    gen_helper_raise_exception(cpu_env, tmp);
+    tcg_temp_free_i32(tmp);
+    dc->is_jmp = DISAS_UPDATE;
+}
+
+/* generate intermediate code for basic block 'tb'.  */
+void gen_intermediate_code(CPUNios2State *env, TranslationBlock *tb)
+{
+    Nios2CPU *cpu = nios2_env_get_cpu(env);
+    CPUState *cs = CPU(cpu);
+    DisasContext dc1, *dc = &dc1;
+    int num_insns;
+    int max_insns;
+    uint32_t next_page_start;
+
+    /* Initialize DC */
+    dc->cpu_env = cpu_env;
+    dc->cpu_R   = cpu_R;
+    dc->is_jmp  = DISAS_NEXT;
+    dc->pc      = tb->pc;
+    dc->tb      = tb;
+    dc->mem_idx = cpu_mmu_index(env, false);
+
+    /* Dump the CPU state to the log */
+    if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
+        qemu_log("--------------\n");
+        log_cpu_state(cs, 0);
+    }
+
+    /* Set up instruction counts */
+    num_insns = 0;
+    max_insns = tb->cflags & CF_COUNT_MASK;
+    if (max_insns == 0) {
+        max_insns = CF_COUNT_MASK;
+    }
+    if (max_insns > TCG_MAX_INSNS) {
+        max_insns = TCG_MAX_INSNS;
+    }
+    next_page_start = (tb->pc & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
+
+    gen_tb_start(tb);
+    do {
+        LOG_DIS("%8.8x:\t", dc->pc);
+
+        tcg_gen_insn_start(dc->pc);
+        num_insns++;
+
+#if SIM_COMPAT
+        if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
+            tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc);
+            gen_helper_debug();
+        }
+#endif
+
+        if (unlikely(cpu_breakpoint_test(cs, dc->pc, BP_ANY))) {
+            gen_exception(dc, EXCP_DEBUG);
+            /* The address covered by the breakpoint must be included in
+               [tb->pc, tb->pc + tb->size) in order to for it to be
+               properly cleared -- thus we increment the PC here so that
+               the logic setting tb->size below does the right thing.  */
+            dc->pc += 4;
+            break;
+        }
+
+        if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) {
+            gen_io_start();
+        }
+
+        /* Decode an instruction */
+        handle_instruction(dc, env);
+
+        dc->pc += 4;
+
+        /* Translation stops when a conditional branch is encountered.
+         * Otherwise the subsequent code could get translated several times.
+         * Also stop translation when a page boundary is reached.  This
+         * ensures prefetch aborts occur at the right place.  */
+    } while (!dc->is_jmp &&
+             !tcg_op_buf_full() &&
+             !cs->singlestep_enabled &&
+             !singlestep &&
+             dc->pc < next_page_start &&
+             num_insns < max_insns);
+
+    if (tb->cflags & CF_LAST_IO) {
+        gen_io_end();
+    }
+
+    /* Indicate where the next block should start */
+    switch (dc->is_jmp) {
+    case DISAS_NEXT:
+        /* Save the current PC back into the CPU register */
+        tcg_gen_movi_tl(cpu_R[R_PC], dc->pc);
+        tcg_gen_exit_tb(0);
+        break;
+
+    default:
+    case DISAS_JUMP:
+    case DISAS_UPDATE:
+        /* The jump will already have updated the PC register */
+        tcg_gen_exit_tb(0);
+        break;
+
+    case DISAS_TB_JUMP:
+        /* nothing more to generate */
+        break;
+    }
+
+    /* End off the block */
+    gen_tb_end(tb, num_insns);
+
+    /* Mark instruction starts for the final generated instruction */
+    tb->size = dc->pc - tb->pc;
+    tb->icount = num_insns;
+
+#ifdef DEBUG_DISAS
+    if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
+        qemu_log("----------------\n");
+        qemu_log("IN: %s\n", lookup_symbol(tb->pc));
+        log_target_disas(cs, tb->pc, dc->pc - tb->pc, 0);
+        qemu_log("\nisize=%d osize=%d\n",
+                 dc->pc - tb->pc, tcg_op_buf_count());
+    }
+#endif
+}
+
+void nios2_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
+                          int flags)
+{
+    Nios2CPU *cpu = NIOS2_CPU(cs);
+    CPUNios2State *env = &cpu->env;
+    int i;
+
+    if (!env || !f) {
+        return;
+    }
+
+    cpu_fprintf(f, "IN: PC=%x %s\n",
+                env->regs[R_PC], lookup_symbol(env->regs[R_PC]));
+
+    for (i = 0; i < NUM_CORE_REGS; i++) {
+        cpu_fprintf(f, "%9s=%8.8x ", regnames[i], env->regs[i]);
+        if ((i + 1) % 4 == 0) {
+            cpu_fprintf(f, "\n");
+        }
+    }
+#if !defined(CONFIG_USER_ONLY)
+    cpu_fprintf(f, " mmu write: VPN=%05X PID %02X TLBACC %08X\n",
+                env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK,
+                (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4,
+                env->mmu.tlbacc_wr);
+#endif
+    cpu_fprintf(f, "\n\n");
+}
+
+void nios2_tcg_init(void)
+{
+    int i;
+
+    cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
+
+    for (i = 0; i < NUM_CORE_REGS; i++) {
+        cpu_R[i] = tcg_global_mem_new(cpu_env,
+                                      offsetof(CPUNios2State, regs[i]),
+                                      regnames[i]);
+    }
+}
+
+void restore_state_to_opc(CPUNios2State *env, TranslationBlock *tb,
+                          target_ulong *data)
+{
+    env->regs[R_PC] = data[0];
+}