Patchwork [1/9] NiosII: Add support for the Altera NiosII soft-core CPU.

login
register
mail settings
Submitter crwulff@gmail.com
Date Sept. 10, 2012, 12:19 a.m.
Message ID <1347236407-10465-2-git-send-email-crwulff@gmail.com>
Download mbox | patch
Permalink /patch/182768/
State New
Headers show

Comments

crwulff@gmail.com - Sept. 10, 2012, 12:19 a.m.
From: Chris Wulff <crwulff@gmail.com>

Signed-off-by: Chris Wulff <crwulff@gmail.com>
---
 target-nios2/Makefile.objs |    5 +
 target-nios2/altera_iic.c  |  100 +++
 target-nios2/cpu-qom.h     |   69 +++
 target-nios2/cpu.c         |   83 +++
 target-nios2/cpu.h         |  259 ++++++++
 target-nios2/exec.h        |   60 ++
 target-nios2/helper.c      |  291 +++++++++
 target-nios2/helper.h      |   45 ++
 target-nios2/instruction.c | 1463 ++++++++++++++++++++++++++++++++++++++++++++
 target-nios2/instruction.h |  290 +++++++++
 target-nios2/machine.c     |   33 +
 target-nios2/mmu.c         |  273 +++++++++
 target-nios2/mmu.h         |   49 ++
 target-nios2/op_helper.c   |  125 ++++
 target-nios2/translate.c   |  252 ++++++++
 15 files changed, 3397 insertions(+)
 create mode 100644 target-nios2/Makefile.objs
 create mode 100644 target-nios2/altera_iic.c
 create mode 100644 target-nios2/cpu-qom.h
 create mode 100644 target-nios2/cpu.c
 create mode 100644 target-nios2/cpu.h
 create mode 100644 target-nios2/exec.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/op_helper.c
 create mode 100644 target-nios2/translate.c
Blue Swirl - Sept. 11, 2012, 8:19 p.m.
On Mon, Sep 10, 2012 at 12:19 AM,  <crwulff@gmail.com> wrote:
> From: Chris Wulff <crwulff@gmail.com>
>
> Signed-off-by: Chris Wulff <crwulff@gmail.com>
> ---
>  target-nios2/Makefile.objs |    5 +
>  target-nios2/altera_iic.c  |  100 +++
>  target-nios2/cpu-qom.h     |   69 +++
>  target-nios2/cpu.c         |   83 +++
>  target-nios2/cpu.h         |  259 ++++++++
>  target-nios2/exec.h        |   60 ++
>  target-nios2/helper.c      |  291 +++++++++
>  target-nios2/helper.h      |   45 ++
>  target-nios2/instruction.c | 1463 ++++++++++++++++++++++++++++++++++++++++++++
>  target-nios2/instruction.h |  290 +++++++++
>  target-nios2/machine.c     |   33 +
>  target-nios2/mmu.c         |  273 +++++++++
>  target-nios2/mmu.h         |   49 ++
>  target-nios2/op_helper.c   |  125 ++++
>  target-nios2/translate.c   |  252 ++++++++
>  15 files changed, 3397 insertions(+)
>  create mode 100644 target-nios2/Makefile.objs
>  create mode 100644 target-nios2/altera_iic.c
>  create mode 100644 target-nios2/cpu-qom.h
>  create mode 100644 target-nios2/cpu.c
>  create mode 100644 target-nios2/cpu.h
>  create mode 100644 target-nios2/exec.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/op_helper.c
>  create mode 100644 target-nios2/translate.c
>
> diff --git a/target-nios2/Makefile.objs b/target-nios2/Makefile.objs
> new file mode 100644
> index 0000000..d072795
> --- /dev/null
> +++ b/target-nios2/Makefile.objs
> @@ -0,0 +1,5 @@
> +obj-y += translate.o op_helper.o helper.o cpu.o instruction.o
> +obj-$(CONFIG_SOFTMMU) += mmu.o machine.o
> +obj-y += altera_iic.o
> +
> +$(obj)/op_helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
> diff --git a/target-nios2/altera_iic.c b/target-nios2/altera_iic.c
> new file mode 100644
> index 0000000..d45d6fa
> --- /dev/null
> +++ b/target-nios2/altera_iic.c
> @@ -0,0 +1,100 @@
> +/*
> + * QEMU Altera Internal Interrupt Controller.
> + *
> + * 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 "hw/sysbus.h"
> +#include "dyngen-exec.h"
> +#include "cpu.h"
> +
> +struct altera_iic {

CamelCase

> +    SysBusDevice busdev;
> +    Nios2CPU *cpu;
> +    qemu_irq parent_irq;
> +};
> +
> +static void update_irq(struct altera_iic *pv)
> +{
> +    uint32_t i;
> +
> +    if ((pv->cpu->env.regs[CR_STATUS] & CR_STATUS_PIE) == 0) {
> +        qemu_irq_lower(pv->parent_irq);
> +        return;
> +    }
> +
> +    for (i = 0; i < 32; i++) {
> +        if (pv->cpu->env.regs[CR_IPENDING] &
> +            pv->cpu->env.regs[CR_IENABLE] & (1 << i)) {
> +            break;
> +        }
> +    }
> +    if (i == 32) {
> +        qemu_irq_lower(pv->parent_irq);
> +    } else {
> +        qemu_irq_raise(pv->parent_irq);
> +    }
> +}
> +
> +static void irq_handler(void *opaque, int irq, int level)
> +{
> +    struct altera_iic *pv = opaque;
> +
> +    pv->cpu->env.regs[CR_IPENDING] &= ~(1 << irq);
> +    pv->cpu->env.regs[CR_IPENDING] |= level << irq;
> +
> +    update_irq(pv);
> +}
> +
> +static int altera_iic_init(SysBusDevice *dev)
> +{
> +    struct altera_iic *pv = FROM_SYSBUS(typeof(*pv), dev);
> +    pv->cpu = g_cpu; /* TODO: Get attached CPU instead somehow... */
> +
> +    qdev_init_gpio_in(&dev->qdev, irq_handler, 32);
> +    sysbus_init_irq(dev, &pv->parent_irq);
> +
> +    return 0;
> +}
> +
> +static Property altera_iic_properties[] = {
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void altera_iic_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +
> +    k->init = altera_iic_init;
> +    dc->props = altera_iic_properties;
> +}
> +
> +static TypeInfo altera_iic_info = {
> +    .name          = "altera,iic",
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(struct altera_iic),
> +    .class_init    = altera_iic_class_init,
> +};
> +
> +static void altera_iic_register(void)
> +{
> +    type_register_static(&altera_iic_info);
> +}
> +
> +type_init(altera_iic_register)
> +
> diff --git a/target-nios2/cpu-qom.h b/target-nios2/cpu-qom.h
> new file mode 100644
> index 0000000..be6fa65
> --- /dev/null
> +++ b/target-nios2/cpu-qom.h
> @@ -0,0 +1,69 @@
> +/*
> + * 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>
> + */
> +#ifndef QEMU_NIOS2_CPU_QOM_H
> +#define QEMU_NIOS2_CPU_QOM_H
> +
> +#include "qemu/cpu.h"
> +
> +#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 >*/
> +
> +    void (*parent_reset)(CPUState *cpu);
> +} Nios2CPUClass;
> +
> +/**
> + * Nios2CPU:
> + * @env: #CPUNios2State
> + *
> + * A Nios2 CPU.
> + */
> +typedef struct Nios2CPU {
> +    /*< private >*/
> +    CPUState parent_obj;
> +    /*< public >*/
> +
> +    CPUNios2State env;
> +} 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))
> +
> +#endif /* QEMU_NIOS2_CPU_QOM_H */
> diff --git a/target-nios2/cpu.c b/target-nios2/cpu.c
> new file mode 100644
> index 0000000..bd819c4
> --- /dev/null
> +++ b/target-nios2/cpu.c
> @@ -0,0 +1,83 @@
> +/*
> + * 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 "cpu.h"
> +#include "qemu-common.h"
> +
> +
> +/* CPUClass::reset() */
> +static void nios2_cpu_reset(CPUState *s)
> +{
> +    Nios2CPU *cpu = NIOS2_CPU(s);
> +    Nios2CPUClass *mcc = NIOS2_CPU_GET_CLASS(cpu);
> +    CPUNios2State *env = &cpu->env;
> +
> +    if (qemu_loglevel_mask(CPU_LOG_RESET)) {
> +        qemu_log("CPU Reset (CPU %d)\n", env->cpu_index);
> +        log_cpu_state(env, 0);
> +    }
> +
> +    mcc->parent_reset(s);
> +
> +    tlb_flush(env, 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;
> +#else
> +    mmu_init(&env->mmu);
> +#endif
> +}
> +
> +static void nios2_cpu_initfn(Object *obj)
> +{
> +    Nios2CPU *cpu = NIOS2_CPU(obj);
> +    CPUNios2State *env = &cpu->env;
> +
> +    cpu_exec_init(env);
> +}
> +
> +static void nios2_cpu_class_init(ObjectClass *oc, void *data)
> +{
> +    CPUClass *cc = CPU_CLASS(oc);
> +    Nios2CPUClass *mcc = NIOS2_CPU_CLASS(oc);
> +
> +    mcc->parent_reset = cc->reset;
> +    cc->reset = nios2_cpu_reset;
> +}
> +
> +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..0ee22e6
> --- /dev/null
> +++ b/target-nios2/cpu.h
> @@ -0,0 +1,259 @@
> +/*
> + * 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 "config.h"
> +#include "qemu-common.h"
> +
> +#define TARGET_LONG_BITS 32
> +
> +#define CPUArchState struct CPUNios2State
> +
> +#include "cpu-defs.h"
> +#include "softfloat.h"
> +struct CPUNios2State;
> +typedef struct CPUNios2State CPUNios2State;
> +#if !defined(CONFIG_USER_ONLY)
> +#include "mmu.h"
> +#endif
> +
> +#define TARGET_HAS_ICE 1
> +
> +/* Configuration options for Nios II */
> +#define RESET_ADDRESS         0x00000000
> +#define EXCEPTION_ADDRESS     0x00000004
> +#define FAST_TLB_MISS_ADDRESS 0x00000008
> +
> +
> +#define ELF_MACHINE EM_ALTERA_NIOS2
> +
> +/* GP regs + CR regs + PC */
> +#define NUM_CORE_REGS (32 + 32 + 1)
> +
> +/* General purpose egister 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_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_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
> +
> +typedef 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)
> +    struct nios2_mmu mmu;
> +#endif
> +
> +    CPU_COMMON
> +} CPUNios2State;
> +
> +#include "cpu-qom.h"
> +
> +extern Nios2CPU *cpu_nios2_init(const char *cpu_model);

'extern' is useless for functions.

> +extern int cpu_nios2_exec(CPUNios2State *s);
> +extern void cpu_nios2_close(CPUNios2State *s);
> +extern void do_interrupt(CPUNios2State *env);
> +extern int cpu_nios2_signal_handler(int host_signum, void *pinfo, void *puc);
> +extern void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUNios2State *env);
> +
> +#define TARGET_PHYS_ADDR_SPACE_BITS 32
> +#define TARGET_VIRT_ADDR_SPACE_BITS 32
> +
> +extern Nios2CPU *g_cpu;

Just say no to globals.

> +
> +static inline CPUNios2State *cpu_init(const char *cpu_model)
> +{
> +    Nios2CPU *cpu = cpu_nios2_init(cpu_model);
> +    if (cpu == NULL) {
> +        return NULL;
> +    }
> +    return &cpu->env;
> +}
> +
> +#define cpu_exec cpu_nios2_exec
> +#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)
> +{
> +    return (env->regs[CR_STATUS] & CR_STATUS_U) ? MMU_USER_IDX :
> +                                                  MMU_SUPERVISOR_IDX;
> +}
> +
> +int cpu_nios2_handle_mmu_fault(CPUNios2State *env, target_ulong address,
> +                               int rw, int mmu_idx, int is_softmmu);
> +#define cpu_handle_mmu_fault cpu_nios2_handle_mmu_fault
> +
> +#if defined(CONFIG_USER_ONLY)
> +static inline void cpu_clone_regs(CPUNios2State *env, target_ulong newsp)
> +{
> +    if (newsp) {
> +        env->regs[R_SP] = newsp;
> +    }
> +    env->regs[R_RET0] = 0;
> +}
> +#endif
> +
> +static inline void cpu_set_tls(CPUNios2State *env, target_ulong newtls)
> +{
> +}
> +
> +static inline int cpu_interrupts_enabled(CPUNios2State *env)
> +{
> +    return env->regs[CR_STATUS] & CR_STATUS_PIE;
> +}
> +
> +#include "cpu-all.h"
> +
> +static inline target_ulong cpu_get_pc(CPUNios2State *env)
> +{
> +    return env->regs[R_PC];
> +}
> +
> +static inline void cpu_get_tb_cpu_state(CPUNios2State *env, target_ulong *pc,
> +                                        target_ulong *cs_base, int *flags)
> +{
> +    *pc = env->regs[R_PC];
> +    *cs_base = 0;
> +    *flags = (env->regs[CR_STATUS] & (CR_STATUS_EH | CR_STATUS_U));
> +}
> +
> +#if !defined(CONFIG_USER_ONLY)
> +void cpu_unassigned_access(CPUNios2State *env1, target_phys_addr_t addr,
> +                           int is_write, int is_exec, int is_asi, int size);
> +#endif
> +
> +static inline bool cpu_has_work(CPUNios2State *env)
> +{
> +    return env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI);
> +}
> +
> +#include "exec-all.h"
> +
> +static inline void cpu_pc_from_tb(CPUNios2State *env, TranslationBlock *tb)
> +{
> +    env->regs[R_PC] = tb->pc;
> +}
> +
> +#endif /* CPU_NIOS2_H */
> +
> diff --git a/target-nios2/exec.h b/target-nios2/exec.h
> new file mode 100644
> index 0000000..cd4446b
> --- /dev/null
> +++ b/target-nios2/exec.h
> @@ -0,0 +1,60 @@
> +/*
> + * Altera Nios II execution defines
> + *
> + * 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 NIOS2_EXEC_H
> +#define NIOS2_EXEC_H
> +
> +#include "dyngen-exec.h"

Dyngen model is being obsoleted in coming days. Please make all
helpers which need access to CPU state take the state as parameter.

> +
> +register struct CPUNios2State *env asm(AREG0);

This is already defined in dyngen-exec.h, but see above. Maybe you are
not using Git HEAD? No target uses exec.h anymore.

> +
> +#include "cpu.h"
> +#include "exec-all.h"
> +
> +#if !defined(CONFIG_USER_ONLY)
> +#include "softmmu_exec.h"
> +#endif
> +

The functions below should go to cpu.h.

> +static inline int cpu_has_work(CPUState *env)
> +{
> +    return env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI);
> +}
> +
> +static inline int cpu_halted(CPUState *env)
> +{
> +    if (!env->halted) {
> +        return 0;
> +    }
> +
> +    if (cpu_has_work(env)) {
> +        env->halted = 0;
> +        return 0;
> +    }
> +
> +    return EXCP_HALTED;
> +}
> +
> +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb)
> +{
> +    env->regs[R_PC] = tb->pc;
> +}
> +
> +#endif /* NIOS2_EXEC_H */
> +
> diff --git a/target-nios2/helper.c b/target-nios2/helper.c
> new file mode 100644
> index 0000000..9b200c9
> --- /dev/null
> +++ b/target-nios2/helper.c
> @@ -0,0 +1,291 @@
> +/*
> + * 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 "config.h"
> +#include "cpu.h"
> +#include "exec-all.h"
> +#include "host-utils.h"
> +
> +#if defined(CONFIG_USER_ONLY)
> +
> +void do_interrupt(CPUNios2State *env)
> +{
> +    env->exception_index = -1;
> +    env->regs[R_EA] = env->regs[R_PC] + 4;
> +}
> +
> +int cpu_nios2_handle_mmu_fault(CPUNios2State *env, target_ulong address,
> +                               int rw, int mmu_idx, int is_softmmu)
> +{
> +    env->exception_index = 0xaa;
> +    cpu_dump_state(env, stderr, fprintf, 0);
> +    return 1;
> +}
> +
> +#else /* !CONFIG_USER_ONLY */
> +
> +bool has_mmu = true;
> +
> +void do_interrupt(CPUNios2State *env)
> +{
> +    switch (env->exception_index) {
> +    case EXCP_IRQ:
> +        assert(env->regs[CR_STATUS] & CR_STATUS_PIE);

I hope this is not a guest triggerable assert.

> +
> +        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] |= (env->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]);
> +            log_cpu_state(env, 0);
> +
> +            /* 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] |= (env->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] |= (env->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]);
> +        log_cpu_state(env, 0);
> +
> +        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] |= (env->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] |= (env->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] |= (env->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] |= (env->exception_index & 0x1F) << 2;
> +
> +        env->regs[R_PC] = env->exception_addr;
> +        break;
> +
> +    default:
> +        cpu_abort(env, "unhandled exception type=%d\n",
> +                  env->exception_index);
> +        break;
> +    }
> +}
> +
> +static int cpu_nios2_handle_virtual_page(
> +    CPUNios2State *env, target_ulong address, int rw, int mmu_idx)
> +{
> +    target_ulong vaddr, paddr;
> +    struct nios2_mmu_lookup 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(env, vaddr, paddr, lu.prot,
> +                         mmu_idx, TARGET_PAGE_SIZE);
> +            return 0;
> +        } else {
> +            /* Permission violation */
> +            env->exception_index = (rw == 0) ? EXCP_TLBR :
> +                                               ((rw == 1) ? EXCP_TLBW :
> +                                                            EXCP_TLBX);
> +        }
> +    } else {
> +        env->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 cpu_nios2_handle_mmu_fault(CPUNios2State *env, target_ulong address,
> +                               int rw, int mmu_idx, int is_softmmu)
> +{
> +    if (has_mmu) {
> +        if (MMU_SUPERVISOR_IDX == mmu_idx) {
> +            if (address >= 0xC0000000) {
> +                /* Kernel physical page - TLB bypassed */
> +                address &= TARGET_PAGE_MASK;
> +                tlb_set_page(env, address, address, PAGE_BITS,
> +                             mmu_idx, TARGET_PAGE_SIZE);
> +            } else if (address >= 0x80000000) {
> +                /* Kernel virtual page */
> +                return cpu_nios2_handle_virtual_page(env, address, rw, mmu_idx);
> +            } else {
> +                /* User virtual page */
> +                return cpu_nios2_handle_virtual_page(env, address, rw, mmu_idx);
> +            }
> +        } else {
> +            if (address >= 0x80000000) {
> +                /* Illegal access from user mode */
> +                env->exception_index = EXCP_SUPERA;
> +                env->regs[CR_BADADDR] = address;
> +                return 1;
> +            } else {
> +                /* User virtual page */
> +                return cpu_nios2_handle_virtual_page(env, address, rw, mmu_idx);
> +            }
> +        }
> +    } else {
> +        /* No MMU */
> +        address &= TARGET_PAGE_MASK;
> +        tlb_set_page(env, address, address, PAGE_BITS,
> +                     mmu_idx, TARGET_PAGE_SIZE);
> +    }
> +
> +    return 0;
> +}
> +
> +target_phys_addr_t cpu_get_phys_page_debug(CPUNios2State *env,
> +                                           target_ulong addr)
> +{
> +    target_ulong vaddr, paddr = 0;
> +    struct nios2_mmu_lookup lu;
> +    unsigned int hit;
> +
> +    if (has_mmu && (addr < 0xC0000000)) {
> +        hit = mmu_translate(env, &lu, addr, 0, 0);
> +        if (hit) {
> +            vaddr = addr & TARGET_PAGE_MASK;
> +            paddr = lu.paddr + vaddr - lu.vaddr;
> +        } else {
> +            cpu_abort(env, "cpu_get_phys_page debug MISS: %08X\n", addr);

cpu_get_phys_page_debug is used for memory access from monitor.
Aborting because user specified a bad address is rather draconian.

> +        }
> +    } 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..69b6684
> --- /dev/null
> +++ b/target-nios2/helper.h
> @@ -0,0 +1,45 @@
> +/*
> + * 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>
> + */
> +
> +#include "def-helper.h"
> +
> +/* 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_1(raise_exception, void, i32)
> +
> +#if !defined(CONFIG_USER_ONLY)
> +DEF_HELPER_1(mmu_read, i32, i32)
> +DEF_HELPER_2(mmu_write, void, i32, i32)
> +#endif
> +
> +DEF_HELPER_2(divs, i32, i32, i32)
> +DEF_HELPER_2(divu, i32, i32, i32)
> +
> +DEF_HELPER_4(memalign, void, i32, i32, i32, i32)
> +
> +#include "def-helper.h"
> +
> diff --git a/target-nios2/instruction.c b/target-nios2/instruction.c
> new file mode 100644
> index 0000000..4dc06cd
> --- /dev/null
> +++ b/target-nios2/instruction.c
> @@ -0,0 +1,1463 @@
> +/*
> + * 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-all.h"
> +#include "helper.h"
> +
> +#define GEN_HELPER 1
> +#include "helper.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(tmp);
> +    tcg_temp_free_i32(tmp);
> +    dc->is_jmp = DISAS_UPDATE;
> +}
> +
> +/*
> + * 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, int label)
> +{
> +    int 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);
> +}
> +
> +static inline void gen_load_u(DisasContext *dc, TCGv dst, TCGv addr,
> +                              unsigned int size)
> +{
> +    int mem_index = cpu_mmu_index(dc->env);
> +
> +    switch (size) {
> +    case 1:
> +        tcg_gen_qemu_ld8u(dst, addr, mem_index);
> +        break;
> +    case 2:
> +        tcg_gen_qemu_ld16u(dst, addr, mem_index);
> +        break;
> +    case 4:
> +        tcg_gen_qemu_ld32u(dst, addr, mem_index);
> +        break;
> +    default:
> +        cpu_abort(dc->env, "Incorrect load size %d\n", size);

Maybe assert instead.

> +        break;
> +    }
> +}
> +
> +static inline void gen_load_s(DisasContext *dc, TCGv dst, TCGv addr,
> +                              unsigned int size)
> +{
> +    int mem_index = cpu_mmu_index(dc->env);
> +
> +    switch (size) {
> +    case 1:
> +        tcg_gen_qemu_ld8s(dst, addr, mem_index);
> +        break;
> +    case 2:
> +        tcg_gen_qemu_ld16s(dst, addr, mem_index);
> +        break;
> +    case 4:
> +        tcg_gen_qemu_ld32s(dst, addr, mem_index);
> +        break;
> +    default:
> +        cpu_abort(dc->env, "Incorrect load size %d\n", size);
> +        break;
> +    }
> +}
> +
> +static inline void gen_store(DisasContext *dc, TCGv val, TCGv addr,
> +                             unsigned int size)
> +{
> +    int mem_index = cpu_mmu_index(dc->env);
> +
> +    switch (size) {
> +    case 1:
> +        tcg_gen_qemu_st8(val, addr, mem_index);
> +        break;
> +    case 2:
> +        tcg_gen_qemu_st16(val, addr, mem_index);
> +        break;
> +    case 4:
> +        tcg_gen_qemu_st32(val, addr, mem_index);
> +        break;
> +    default:
> +        cpu_abort(dc->env, "Incorrect load size %d\n", size);
> +        break;
> +    }
> +}
> +
> +/*
> + * 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);
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC],
> +                    (dc->pc & 0xF0000000) | (instr->imm26 * 4));
> +
> +    dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* PC <- (PC(31..28) : IMM26 * 4) */
> +static void jmpi(DisasContext *dc, uint32_t code)
> +{
> +    J_TYPE(instr, code);
> +
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC],
> +                    (dc->pc & 0xF0000000) | (instr->imm26 * 4));
> +
> +    dc->is_jmp = DISAS_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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_load_u(dc, dc->cpu_R[instr->b], addr, 1);
> +
> +    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_movi_tl(imm, (int32_t)((int16_t)instr->imm16));
> +
> +    tcg_gen_add_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a], imm);
> +
> +    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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_store(dc, dc->cpu_R[instr->b], addr, 1);
> +
> +    tcg_temp_free(addr);
> +}
> +
> +/* PC <- PC + 4 + IMM16 */
> +static void br(DisasContext *dc, uint32_t code)
> +{
> +    I_TYPE(instr, code);
> +
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC],
> +                    dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> +    dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rB <- @(Mem8[rA + @(IMM16)]) */
> +static void ldb(DisasContext *dc, uint32_t code)
> +{
> +    I_TYPE(instr, code);
> +
> +    TCGv addr = tcg_temp_new();
> +    tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_load_s(dc, dc->cpu_R[instr->b], addr, 1);
> +
> +    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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_load_u(dc, dc->cpu_R[instr->b], addr, 2);
> +
> +    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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_store(dc, dc->cpu_R[instr->b], addr, 2);
> +
> +    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);
> +
> +    int l1 = gen_new_label();
> +
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC],
> +                    dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> +    tcg_gen_brcond_tl(TCG_COND_GE, dc->cpu_R[instr->a],
> +                      dc->cpu_R[instr->b], l1);
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
> +    gen_set_label(l1);
> +
> +    dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rB <- Mem16[rA + @IMM16)] */
> +static void ldh(DisasContext *dc, uint32_t code)
> +{
> +    I_TYPE(instr, code);
> +
> +    TCGv addr = tcg_temp_new();
> +    tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_load_s(dc, dc->cpu_R[instr->b], addr, 2);
> +
> +    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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_store(dc, dc->cpu_R[instr->b], addr, 4);
> +
> +    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);
> +
> +    int l1 = gen_new_label();
> +
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC],
> +                    dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> +    tcg_gen_brcond_tl(TCG_COND_LT, dc->cpu_R[instr->a],
> +                      dc->cpu_R[instr->b], l1);
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
> +    gen_set_label(l1);
> +
> +    dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rB <- @(Mem32[rA + @(IMM16)]) */
> +static void ldw(DisasContext *dc, uint32_t code)
> +{
> +    I_TYPE(instr, code);
> +
> +    TCGv addr = tcg_temp_new();
> +    tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_load_u(dc, dc->cpu_R[instr->b], addr, 4);
> +
> +    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);
> +
> +    int l1 = gen_new_label();
> +
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC],
> +                    dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> +    tcg_gen_brcond_tl(TCG_COND_NE, dc->cpu_R[instr->a],
> +                      dc->cpu_R[instr->b], l1);
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
> +    gen_set_label(l1);
> +
> +    dc->is_jmp = DISAS_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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_load_u(dc, dc->cpu_R[instr->b], addr, 1);
> +
> +    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_movi_tl(imm, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_mul_i32(dc->cpu_R[instr->b], dc->cpu_R[instr->a], imm);
> +    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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_store(dc, dc->cpu_R[instr->b], addr, 1);
> +
> +    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);
> +
> +    int l1 = gen_new_label();
> +
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC],
> +                    dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> +    tcg_gen_brcond_tl(TCG_COND_EQ, dc->cpu_R[instr->a],
> +                      dc->cpu_R[instr->b], l1);
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
> +    gen_set_label(l1);
> +
> +    dc->is_jmp = DISAS_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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_load_s(dc, dc->cpu_R[instr->b], addr, 1);
> +
> +    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],
> +                        (int32_t)((int16_t)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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_load_u(dc, dc->cpu_R[instr->b], addr, 2);
> +
> +    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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_store(dc, dc->cpu_R[instr->b], addr, 2);
> +
> +    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);
> +
> +    int l1 = gen_new_label();
> +
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC],
> +                    dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> +    tcg_gen_brcond_tl(TCG_COND_GEU, dc->cpu_R[instr->a],
> +                      dc->cpu_R[instr->b], l1);
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
> +    gen_set_label(l1);
> +
> +    dc->is_jmp = DISAS_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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_load_s(dc, dc->cpu_R[instr->b], addr, 2);
> +
> +    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],
> +                        (int32_t)((int16_t)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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_store(dc, dc->cpu_R[instr->b], addr, 4);
> +
> +    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);
> +
> +    int l1 = gen_new_label();
> +
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC],
> +                    dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> +    tcg_gen_brcond_tl(TCG_COND_LTU, dc->cpu_R[instr->a],
> +                      dc->cpu_R[instr->b], l1);
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
> +    gen_set_label(l1);
> +
> +    dc->is_jmp = DISAS_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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_load_u(dc, dc->cpu_R[instr->b], addr, 4);
> +
> +    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 struct instruction i_type_instructions[I_TYPE_COUNT] = {
'const'?

> +    [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);
> +
> +    TCGv t0 = tcg_temp_new();
> +
> +    tcg_gen_shri_tl(t0, dc->cpu_R[instr->a], 32 - instr->imm5);
> +    tcg_gen_shli_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], instr->imm5);
> +    tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->c], t0);
> +
> +    tcg_temp_free(t0);
> +}
> +
> +/* 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();
> +    TCGv t1 = tcg_temp_new();
> +
> +    tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
> +    tcg_gen_movi_tl(t1, 32);
> +    tcg_gen_sub_tl(t1, t1, t0);
> +    tcg_gen_shri_tl(t1, dc->cpu_R[instr->a], t1);
> +    tcg_gen_shli_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
> +    tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->c], t1);
> +
> +    tcg_temp_free(t0);
> +    tcg_temp_free(t1);
> +}
> +
> +/* */
> +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_trunc_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();
> +    TCGv t1 = tcg_temp_new();
> +
> +    tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
> +    tcg_gen_movi_tl(t1, 32);
> +    tcg_gen_sub_tl(t1, t1, t0);
> +    tcg_gen_shli_tl(t1, dc->cpu_R[instr->a], t1);
> +    tcg_gen_shri_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
> +    tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->c], t1);
> +
> +    tcg_temp_free(t0);
> +    tcg_temp_free(t1);
> +}
> +
> +/* */
> +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_trunc_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_trunc_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);
> +
> +    int l1 = gen_new_label();
> +    gen_check_supervisor(dc, l1);
> +
> +    switch (instr->imm5 + 32) {
> +    case CR_PTEADDR:
> +    case CR_TLBACC:
> +    case CR_TLBMISC:
> +    {
> +        TCGv_i32 tmp = tcg_const_i32(instr->imm5 + 32);
> +        gen_helper_mmu_read(dc->cpu_R[instr->c], tmp);
> +        tcg_temp_free_i32(tmp);
> +        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);
> +
> +    int l1 = gen_new_label();
> +    gen_check_supervisor(dc, l1);
> +
> +    switch (instr->imm5 + 32) {
> +    case CR_PTEADDR:
> +    case CR_TLBACC:
> +    case CR_TLBMISC:
> +    {
> +        TCGv_i32 tmp = tcg_const_i32(instr->imm5 + 32);
> +        gen_helper_mmu_write(tmp, dc->cpu_R[instr->a]);
> +        tcg_temp_free_i32(tmp);
> +        break;
> +    }
> +
> +    default:
> +        tcg_gen_mov_tl(dc->cpu_R[instr->imm5 + 32], dc->cpu_R[instr->a]);
> +        break;
> +    }
> +
> +    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 struct instruction 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);
> +}
> +
> +void handle_instruction(DisasContext *dc)
> +{
> +    uint32_t insn = ldl_code(dc->pc);
> +    uint32_t 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..fe29933
> --- /dev/null
> +++ b/target-nios2/instruction.h
> @@ -0,0 +1,290 @@
> +/*
> + * 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 */
> +struct i_type {

IType

> +    uint32_t op:6;
> +    uint32_t imm16:16;
> +    uint32_t b:5;
> +    uint32_t a:5;
> +} __attribute__((packed));

QEMU_PACKED

> +
> +union i_type_u {
> +    uint32_t      v;
> +    struct i_type i;
> +};
> +
> +#define I_TYPE(instr, op) \
> +    union i_type_u instr_u = { .v = op }; \
> +    struct i_type *instr = &instr_u.i
> +
> +/* R-Type instruction */
> +struct r_type {
> +    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;
> +} __attribute__((packed));
> +
> +union r_type_u {
> +    uint32_t      v;
> +    struct r_type i;
> +};
> +
> +#define R_TYPE(instr, op) \
> +    union r_type_u instr_u = { .v = op }; \
> +    struct r_type *instr = &instr_u.i
> +
> +/* J-Type instruction */
> +struct j_type {
> +    uint32_t op:6;
> +    uint32_t imm26:26;
> +} __attribute__((packed));
> +
> +#define J_TYPE(instr, op) \
> +    struct j_type *instr = (struct j_type *) &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 {
> +    CPUNios2State           *env;
> +    TCGv                    *cpu_R;
> +    int                      is_jmp;
> +    target_ulong             pc;
> +    struct TranslationBlock *tb;
> +} DisasContext;
> +
> +typedef void (*instruction_handler)(DisasContext *dc, uint32_t opcode);
> +
> +struct instruction {
> +    const char *name;
> +    instruction_handler handler;
> +};
> +
> +/*
> + * Stringification macro (taken from Linux Kernel source code)
> + */
> +
> +/* Indirect stringification.  Doing two levels allows the parameter to be a
> + * macro itself.  For example, compile with -DFOO=bar, __stringify(FOO)
> + * converts to "bar".
> + */
> +
> +#define __stringify_1(x...)     #x
> +#define __stringify(x...)       __stringify_1(x)

We already have glue and stringify, no need to add another.

> +
> +#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);
> +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..09198db
> --- /dev/null
> +++ b/target-nios2/machine.c
> @@ -0,0 +1,33 @@
> +/*
> + * 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 "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..a52fa1b
> --- /dev/null
> +++ b/target-nios2/mmu.c
> @@ -0,0 +1,273 @@
> +/*
> + * 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 <stdio.h>
> +#include <stdlib.h>
> +#include <assert.h>
> +
> +#include "config.h"
> +#include "cpu.h"
> +#include "exec-all.h"
> +
> +/* Define this to enable MMU debug messages */
> +/* #define DEBUG_MMU */
> +
> +#ifdef DEBUG_MMU
> +#define MMU_LOG(x) x
> +#else
> +#define MMU_LOG(x)
> +#endif
> +
> +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,
> +                           struct nios2_mmu_lookup *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++) {
> +
> +        struct nios2_tlb_entry *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)
> +{
> +    int idx;
> +    MMU_LOG(qemu_log("TLB Flush PID %d\n", pid));
> +
> +    for (idx = 0; idx < env->mmu.tlb_num_entries; idx++) {
> +        struct nios2_tlb_entry *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(env, vaddr);
> +        }
> +    }
> +}
> +
> +void mmu_write(CPUNios2State *env, uint32_t rn, uint32_t v)
> +{
> +    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;
> +            struct nios2_tlb_entry *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(env, 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;
> +            struct nios2_tlb_entry *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(struct nios2_mmu *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 = (struct nios2_tlb_entry *)g_malloc0(
> +        sizeof(struct nios2_tlb_entry) * 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++) {
> +        struct nios2_tlb_entry *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' : '-');
> +    }
> +}
> +
> diff --git a/target-nios2/mmu.h b/target-nios2/mmu.h
> new file mode 100644
> index 0000000..01d67cf
> --- /dev/null
> +++ b/target-nios2/mmu.h
> @@ -0,0 +1,49 @@
> +/*
> + * 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>
> + */
> +
> +struct nios2_tlb_entry {
> +    target_ulong tag;
> +    target_ulong data;
> +};
> +
> +struct nios2_mmu {
> +    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;
> +    struct nios2_tlb_entry *tlb;
> +};
> +
> +struct nios2_mmu_lookup {
> +    target_ulong vaddr;
> +    target_ulong paddr;
> +    int prot;
> +};
> +
> +void mmu_flip_um(CPUNios2State *env, unsigned int um);
> +unsigned int mmu_translate(CPUNios2State *env,
> +                           struct nios2_mmu_lookup *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(struct nios2_mmu *mmu);
> diff --git a/target-nios2/op_helper.c b/target-nios2/op_helper.c
> new file mode 100644
> index 0000000..37bac66
> --- /dev/null
> +++ b/target-nios2/op_helper.c
> @@ -0,0 +1,125 @@
> +/*
> + * 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 "dyngen-exec.h"

Remove.

> +#include "helper.h"
> +#include "host-utils.h"
> +
> +#if !defined(CONFIG_USER_ONLY)
> +#define MMUSUFFIX _mmu
> +#define SHIFT 0
> +#include "softmmu_template.h"
> +#define SHIFT 1
> +#include "softmmu_template.h"
> +#define SHIFT 2
> +#include "softmmu_template.h"
> +#define SHIFT 3
> +#include "softmmu_template.h"
> +
> +void tlb_fill(CPUNios2State *env1, target_ulong addr, int is_write, int mmu_idx,
> +              uintptr_t retaddr)
> +{
> +    TranslationBlock *tb;
> +    CPUNios2State *saved_env;
> +    int ret;
> +
> +    saved_env = env;
> +    env = env1;

References to global env should be removed, the CPU state should be
passed around instead. Please check for example Sparc or recent
commits to s390x.

> +
> +    ret = cpu_nios2_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
> +    if (unlikely(ret)) {
> +        if (retaddr) {
> +            /* now we have a real cpu fault */
> +            tb = tb_find_pc(retaddr);
> +            if (tb) {
> +                /* The PC is inside the translated code. It means that we have
> +                   a virtual CPU fault */
> +                cpu_restore_state(tb, env, retaddr);
> +            }
> +        }
> +        cpu_loop_exit(env);
> +    }
> +    env = saved_env;
> +}
> +
> +void helper_raise_exception(uint32_t index)
> +{
> +    env->exception_index = index;
> +    cpu_loop_exit(env);
> +}
> +
> +uint32_t helper_mmu_read(uint32_t rn)
> +{
> +    return mmu_read(env, rn);
> +}
> +
> +void helper_mmu_write(uint32_t rn, uint32_t v)
> +{
> +    mmu_write(env, rn, v);
> +}
> +
> +void helper_memalign(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(EXCP_UNALIGN);
> +    }
> +}
> +
> +void cpu_unassigned_access(CPUNios2State *env1, target_phys_addr_t addr,
> +                           int is_write, int is_exec, int is_asi, int size)
> +{
> +    qemu_log("unassigned access to %08X\n", addr);
> +}
> +
> +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
> +
> +#endif /* !CONFIG_USER_ONLY */
> +
> diff --git a/target-nios2/translate.c b/target-nios2/translate.c
> new file mode 100644
> index 0000000..5d0979f
> --- /dev/null
> +++ b/target-nios2/translate.c
> @@ -0,0 +1,252 @@
> +/*
> + * 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-all.h"
> +#include "disas.h"
> +#include "helper.h"
> +#include "qemu-common.h"
> +
> +#include "instruction.h"
> +
> +#define GEN_HELPER 1
> +#include "helper.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 "gen-icount.h"
> +
> +/* generate intermediate code for basic block 'tb'.  */
> +static void gen_intermediate_code_internal(
> +    CPUNios2State *env, TranslationBlock *tb, int search_pc)
> +{
> +    DisasContext dc1, *dc = &dc1;
> +    int num_insns;
> +    int max_insns;
> +    uint32_t next_page_start;
> +    int j, lj = -1;
> +    uint16_t *gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
> +
> +    /* Initialize DC */
> +    dc->env    = env;
> +    dc->cpu_R  = cpu_R;
> +    dc->is_jmp = DISAS_NEXT;
> +    dc->pc     = tb->pc;
> +    dc->tb     = tb;
> +
> +    /* Dump the CPU state to the log */
> +    if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
> +        qemu_log("--------------\n");
> +        log_cpu_state(env, 0);
> +    }
> +
> +    /* Set up instruction counts */
> +    num_insns = 0;
> +    max_insns = tb->cflags & CF_COUNT_MASK;
> +    if (max_insns == 0) {
> +        max_insns = CF_COUNT_MASK;
> +    }
> +    next_page_start = (tb->pc & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
> +
> +    gen_icount_start();
> +    do {
> +        /* Mark instruction start with associated PC */
> +        if (search_pc) {
> +            j = gen_opc_ptr - gen_opc_buf;
> +            if (lj < j) {
> +                lj++;
> +                while (lj < j) {
> +                    gen_opc_instr_start[lj++] = 0;
> +                }
> +            }
> +            gen_opc_pc[lj] = dc->pc;
> +            gen_opc_instr_start[lj] = 1;
> +            gen_opc_icount[lj] = num_insns;
> +        }
> +
> +        LOG_DIS("%8.8x:\t", dc->pc);
> +
> +        if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) {
> +            gen_io_start();
> +        }
> +
> +        /* Decode an instruction */
> +        handle_instruction(dc);
> +
> +        dc->pc += 4;
> +        num_insns++;
> +
> +        /* 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 && gen_opc_ptr < gen_opc_end &&
> +             !env->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_icount_end(tb, num_insns);
> +    *gen_opc_ptr = INDEX_op_end;
> +
> +    /* Mark instruction starts for the final generated instruction */
> +    if (search_pc) {
> +        j = gen_opc_ptr - gen_opc_buf;
> +        lj++;
> +        while (lj <= j) {
> +            gen_opc_instr_start[lj++] = 0;
> +        }
> +    } else {
> +        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(tb->pc, dc->pc - tb->pc, 0);
> +        qemu_log("\nisize=%d osize=%td\n",
> +                 dc->pc - tb->pc, gen_opc_ptr - gen_opc_buf);
> +    }
> +#endif
> +}
> +
> +void gen_intermediate_code(CPUNios2State *env, struct TranslationBlock *tb)
> +{
> +    gen_intermediate_code_internal(env, tb, 0);
> +}
> +
> +void gen_intermediate_code_pc(CPUNios2State *env, struct TranslationBlock *tb)
> +{
> +    gen_intermediate_code_internal(env, tb, 1);
> +}
> +
> +void cpu_dump_state(CPUNios2State *env, FILE *f, fprintf_function cpu_fprintf,
> +                    int flags)
> +{
> +    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");
> +        }
> +    }
> +    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);
> +    cpu_fprintf(f, "\n\n");
> +}
> +
> +Nios2CPU *cpu_nios2_init(const char *cpu_model)
> +{
> +    Nios2CPU *cpu;
> +    int i;
> +
> +    cpu = NIOS2_CPU(object_new(TYPE_NIOS2_CPU));
> +
> +    cpu->env.reset_addr = RESET_ADDRESS;
> +    cpu->env.exception_addr = EXCEPTION_ADDRESS;
> +    cpu->env.fast_tlb_miss_addr = FAST_TLB_MISS_ADDRESS;
> +
> +    cpu_reset(CPU(cpu));
> +    qemu_init_vcpu(&cpu->env);
> +
> +    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(TCG_AREG0,
> +                                      offsetof(CPUNios2State, regs[i]),
> +                                      regnames[i]);
> +    }
> +
> +#define GEN_HELPER 2
> +#include "helper.h"
> +
> +  return cpu;
> +}
> +
> +void restore_state_to_opc(CPUNios2State *env, TranslationBlock *tb, int pc_pos)
> +{
> +    env->regs[R_PC] = gen_opc_pc[pc_pos];
> +}
> +
> --
> 1.7.9.5
>
>
Aurelien Jarno - Sept. 11, 2012, 9:34 p.m.
On Sun, Sep 09, 2012 at 08:19:59PM -0400, crwulff@gmail.com wrote:
> From: Chris Wulff <crwulff@gmail.com>
> 
> Signed-off-by: Chris Wulff <crwulff@gmail.com>
> ---
>  target-nios2/Makefile.objs |    5 +
>  target-nios2/altera_iic.c  |  100 +++
>  target-nios2/cpu-qom.h     |   69 +++
>  target-nios2/cpu.c         |   83 +++
>  target-nios2/cpu.h         |  259 ++++++++
>  target-nios2/exec.h        |   60 ++
>  target-nios2/helper.c      |  291 +++++++++
>  target-nios2/helper.h      |   45 ++
>  target-nios2/instruction.c | 1463 ++++++++++++++++++++++++++++++++++++++++++++
>  target-nios2/instruction.h |  290 +++++++++
>  target-nios2/machine.c     |   33 +
>  target-nios2/mmu.c         |  273 +++++++++
>  target-nios2/mmu.h         |   49 ++
>  target-nios2/op_helper.c   |  125 ++++
>  target-nios2/translate.c   |  252 ++++++++
>  15 files changed, 3397 insertions(+)
>  create mode 100644 target-nios2/Makefile.objs
>  create mode 100644 target-nios2/altera_iic.c
>  create mode 100644 target-nios2/cpu-qom.h
>  create mode 100644 target-nios2/cpu.c
>  create mode 100644 target-nios2/cpu.h
>  create mode 100644 target-nios2/exec.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/op_helper.c
>  create mode 100644 target-nios2/translate.c
> 
> diff --git a/target-nios2/Makefile.objs b/target-nios2/Makefile.objs
> new file mode 100644
> index 0000000..d072795
> --- /dev/null
> +++ b/target-nios2/Makefile.objs
> @@ -0,0 +1,5 @@
> +obj-y += translate.o op_helper.o helper.o cpu.o instruction.o
> +obj-$(CONFIG_SOFTMMU) += mmu.o machine.o
> +obj-y += altera_iic.o
> +
> +$(obj)/op_helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
> diff --git a/target-nios2/altera_iic.c b/target-nios2/altera_iic.c
> new file mode 100644
> index 0000000..d45d6fa
> --- /dev/null
> +++ b/target-nios2/altera_iic.c
> @@ -0,0 +1,100 @@
> +/*
> + * QEMU Altera Internal Interrupt Controller.
> + *
> + * 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 "hw/sysbus.h"
> +#include "dyngen-exec.h"
> +#include "cpu.h"
> +
> +struct altera_iic {
> +    SysBusDevice busdev;
> +    Nios2CPU *cpu;
> +    qemu_irq parent_irq;
> +};
> +
> +static void update_irq(struct altera_iic *pv)
> +{
> +    uint32_t i;
> +
> +    if ((pv->cpu->env.regs[CR_STATUS] & CR_STATUS_PIE) == 0) {
> +        qemu_irq_lower(pv->parent_irq);
> +        return;
> +    }
> +
> +    for (i = 0; i < 32; i++) {
> +        if (pv->cpu->env.regs[CR_IPENDING] &
> +            pv->cpu->env.regs[CR_IENABLE] & (1 << i)) {
> +            break;
> +        }
> +    }
> +    if (i == 32) {
> +        qemu_irq_lower(pv->parent_irq);
> +    } else {
> +        qemu_irq_raise(pv->parent_irq);
> +    }
> +}
> +
> +static void irq_handler(void *opaque, int irq, int level)
> +{
> +    struct altera_iic *pv = opaque;
> +
> +    pv->cpu->env.regs[CR_IPENDING] &= ~(1 << irq);
> +    pv->cpu->env.regs[CR_IPENDING] |= level << irq;
> +
> +    update_irq(pv);
> +}
> +
> +static int altera_iic_init(SysBusDevice *dev)
> +{
> +    struct altera_iic *pv = FROM_SYSBUS(typeof(*pv), dev);
> +    pv->cpu = g_cpu; /* TODO: Get attached CPU instead somehow... */
> +
> +    qdev_init_gpio_in(&dev->qdev, irq_handler, 32);
> +    sysbus_init_irq(dev, &pv->parent_irq);
> +
> +    return 0;
> +}
> +
> +static Property altera_iic_properties[] = {
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void altera_iic_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +
> +    k->init = altera_iic_init;
> +    dc->props = altera_iic_properties;
> +}
> +
> +static TypeInfo altera_iic_info = {
> +    .name          = "altera,iic",
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(struct altera_iic),
> +    .class_init    = altera_iic_class_init,
> +};
> +
> +static void altera_iic_register(void)
> +{
> +    type_register_static(&altera_iic_info);
> +}
> +
> +type_init(altera_iic_register)
> +
> diff --git a/target-nios2/cpu-qom.h b/target-nios2/cpu-qom.h
> new file mode 100644
> index 0000000..be6fa65
> --- /dev/null
> +++ b/target-nios2/cpu-qom.h
> @@ -0,0 +1,69 @@
> +/*
> + * 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>
> + */
> +#ifndef QEMU_NIOS2_CPU_QOM_H
> +#define QEMU_NIOS2_CPU_QOM_H
> +
> +#include "qemu/cpu.h"
> +
> +#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 >*/
> +
> +    void (*parent_reset)(CPUState *cpu);
> +} Nios2CPUClass;
> +
> +/**
> + * Nios2CPU:
> + * @env: #CPUNios2State
> + *
> + * A Nios2 CPU.
> + */
> +typedef struct Nios2CPU {
> +    /*< private >*/
> +    CPUState parent_obj;
> +    /*< public >*/
> +
> +    CPUNios2State env;
> +} 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))
> +
> +#endif /* QEMU_NIOS2_CPU_QOM_H */
> diff --git a/target-nios2/cpu.c b/target-nios2/cpu.c
> new file mode 100644
> index 0000000..bd819c4
> --- /dev/null
> +++ b/target-nios2/cpu.c
> @@ -0,0 +1,83 @@
> +/*
> + * 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 "cpu.h"
> +#include "qemu-common.h"
> +
> +
> +/* CPUClass::reset() */
> +static void nios2_cpu_reset(CPUState *s)
> +{
> +    Nios2CPU *cpu = NIOS2_CPU(s);
> +    Nios2CPUClass *mcc = NIOS2_CPU_GET_CLASS(cpu);
> +    CPUNios2State *env = &cpu->env;
> +
> +    if (qemu_loglevel_mask(CPU_LOG_RESET)) {
> +        qemu_log("CPU Reset (CPU %d)\n", env->cpu_index);
> +        log_cpu_state(env, 0);
> +    }
> +
> +    mcc->parent_reset(s);
> +
> +    tlb_flush(env, 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;
> +#else
> +    mmu_init(&env->mmu);
> +#endif
> +}
> +
> +static void nios2_cpu_initfn(Object *obj)
> +{
> +    Nios2CPU *cpu = NIOS2_CPU(obj);
> +    CPUNios2State *env = &cpu->env;
> +
> +    cpu_exec_init(env);
> +}
> +
> +static void nios2_cpu_class_init(ObjectClass *oc, void *data)
> +{
> +    CPUClass *cc = CPU_CLASS(oc);
> +    Nios2CPUClass *mcc = NIOS2_CPU_CLASS(oc);
> +
> +    mcc->parent_reset = cc->reset;
> +    cc->reset = nios2_cpu_reset;
> +}
> +
> +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..0ee22e6
> --- /dev/null
> +++ b/target-nios2/cpu.h
> @@ -0,0 +1,259 @@
> +/*
> + * 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 "config.h"
> +#include "qemu-common.h"
> +
> +#define TARGET_LONG_BITS 32
> +
> +#define CPUArchState struct CPUNios2State
> +
> +#include "cpu-defs.h"
> +#include "softfloat.h"
> +struct CPUNios2State;
> +typedef struct CPUNios2State CPUNios2State;
> +#if !defined(CONFIG_USER_ONLY)
> +#include "mmu.h"
> +#endif
> +
> +#define TARGET_HAS_ICE 1
> +
> +/* Configuration options for Nios II */
> +#define RESET_ADDRESS         0x00000000
> +#define EXCEPTION_ADDRESS     0x00000004
> +#define FAST_TLB_MISS_ADDRESS 0x00000008
> +
> +
> +#define ELF_MACHINE EM_ALTERA_NIOS2
> +
> +/* GP regs + CR regs + PC */
> +#define NUM_CORE_REGS (32 + 32 + 1)
> +
> +/* General purpose egister 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_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_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
> +
> +typedef 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)
> +    struct nios2_mmu mmu;
> +#endif
> +
> +    CPU_COMMON
> +} CPUNios2State;
> +
> +#include "cpu-qom.h"
> +
> +extern Nios2CPU *cpu_nios2_init(const char *cpu_model);
> +extern int cpu_nios2_exec(CPUNios2State *s);
> +extern void cpu_nios2_close(CPUNios2State *s);
> +extern void do_interrupt(CPUNios2State *env);
> +extern int cpu_nios2_signal_handler(int host_signum, void *pinfo, void *puc);
> +extern void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUNios2State *env);
> +
> +#define TARGET_PHYS_ADDR_SPACE_BITS 32
> +#define TARGET_VIRT_ADDR_SPACE_BITS 32
> +
> +extern Nios2CPU *g_cpu;
> +
> +static inline CPUNios2State *cpu_init(const char *cpu_model)
> +{
> +    Nios2CPU *cpu = cpu_nios2_init(cpu_model);
> +    if (cpu == NULL) {
> +        return NULL;
> +    }
> +    return &cpu->env;
> +}
> +
> +#define cpu_exec cpu_nios2_exec
> +#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)
> +{
> +    return (env->regs[CR_STATUS] & CR_STATUS_U) ? MMU_USER_IDX :
> +                                                  MMU_SUPERVISOR_IDX;
> +}
> +
> +int cpu_nios2_handle_mmu_fault(CPUNios2State *env, target_ulong address,
> +                               int rw, int mmu_idx, int is_softmmu);
> +#define cpu_handle_mmu_fault cpu_nios2_handle_mmu_fault
> +
> +#if defined(CONFIG_USER_ONLY)
> +static inline void cpu_clone_regs(CPUNios2State *env, target_ulong newsp)
> +{
> +    if (newsp) {
> +        env->regs[R_SP] = newsp;
> +    }
> +    env->regs[R_RET0] = 0;
> +}
> +#endif
> +
> +static inline void cpu_set_tls(CPUNios2State *env, target_ulong newtls)
> +{
> +}
> +
> +static inline int cpu_interrupts_enabled(CPUNios2State *env)
> +{
> +    return env->regs[CR_STATUS] & CR_STATUS_PIE;
> +}
> +
> +#include "cpu-all.h"
> +
> +static inline target_ulong cpu_get_pc(CPUNios2State *env)
> +{
> +    return env->regs[R_PC];
> +}
> +
> +static inline void cpu_get_tb_cpu_state(CPUNios2State *env, target_ulong *pc,
> +                                        target_ulong *cs_base, int *flags)
> +{
> +    *pc = env->regs[R_PC];
> +    *cs_base = 0;
> +    *flags = (env->regs[CR_STATUS] & (CR_STATUS_EH | CR_STATUS_U));
> +}
> +
> +#if !defined(CONFIG_USER_ONLY)
> +void cpu_unassigned_access(CPUNios2State *env1, target_phys_addr_t addr,
> +                           int is_write, int is_exec, int is_asi, int size);
> +#endif
> +
> +static inline bool cpu_has_work(CPUNios2State *env)
> +{
> +    return env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI);
> +}
> +
> +#include "exec-all.h"
> +
> +static inline void cpu_pc_from_tb(CPUNios2State *env, TranslationBlock *tb)
> +{
> +    env->regs[R_PC] = tb->pc;
> +}
> +
> +#endif /* CPU_NIOS2_H */
> +
> diff --git a/target-nios2/exec.h b/target-nios2/exec.h
> new file mode 100644
> index 0000000..cd4446b
> --- /dev/null
> +++ b/target-nios2/exec.h
> @@ -0,0 +1,60 @@
> +/*
> + * Altera Nios II execution defines
> + *
> + * 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 NIOS2_EXEC_H
> +#define NIOS2_EXEC_H
> +
> +#include "dyngen-exec.h"
> +
> +register struct CPUNios2State *env asm(AREG0);
> +
> +#include "cpu.h"
> +#include "exec-all.h"
> +
> +#if !defined(CONFIG_USER_ONLY)
> +#include "softmmu_exec.h"
> +#endif
> +
> +static inline int cpu_has_work(CPUState *env)
> +{
> +    return env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI);
> +}
> +
> +static inline int cpu_halted(CPUState *env)
> +{
> +    if (!env->halted) {
> +        return 0;
> +    }
> +
> +    if (cpu_has_work(env)) {
> +        env->halted = 0;
> +        return 0;
> +    }
> +
> +    return EXCP_HALTED;
> +}
> +
> +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb)
> +{
> +    env->regs[R_PC] = tb->pc;
> +}
> +
> +#endif /* NIOS2_EXEC_H */
> +
> diff --git a/target-nios2/helper.c b/target-nios2/helper.c
> new file mode 100644
> index 0000000..9b200c9
> --- /dev/null
> +++ b/target-nios2/helper.c
> @@ -0,0 +1,291 @@
> +/*
> + * 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 "config.h"
> +#include "cpu.h"
> +#include "exec-all.h"
> +#include "host-utils.h"
> +
> +#if defined(CONFIG_USER_ONLY)
> +
> +void do_interrupt(CPUNios2State *env)
> +{
> +    env->exception_index = -1;
> +    env->regs[R_EA] = env->regs[R_PC] + 4;
> +}
> +
> +int cpu_nios2_handle_mmu_fault(CPUNios2State *env, target_ulong address,
> +                               int rw, int mmu_idx, int is_softmmu)
> +{
> +    env->exception_index = 0xaa;
> +    cpu_dump_state(env, stderr, fprintf, 0);
> +    return 1;
> +}
> +
> +#else /* !CONFIG_USER_ONLY */
> +
> +bool has_mmu = true;
> +
> +void do_interrupt(CPUNios2State *env)
> +{
> +    switch (env->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] |= (env->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]);
> +            log_cpu_state(env, 0);
> +
> +            /* 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] |= (env->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] |= (env->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]);
> +        log_cpu_state(env, 0);
> +
> +        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] |= (env->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] |= (env->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] |= (env->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] |= (env->exception_index & 0x1F) << 2;
> +
> +        env->regs[R_PC] = env->exception_addr;
> +        break;
> +
> +    default:
> +        cpu_abort(env, "unhandled exception type=%d\n",
> +                  env->exception_index);
> +        break;
> +    }
> +}
> +
> +static int cpu_nios2_handle_virtual_page(
> +    CPUNios2State *env, target_ulong address, int rw, int mmu_idx)
> +{
> +    target_ulong vaddr, paddr;
> +    struct nios2_mmu_lookup 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(env, vaddr, paddr, lu.prot,
> +                         mmu_idx, TARGET_PAGE_SIZE);
> +            return 0;
> +        } else {
> +            /* Permission violation */
> +            env->exception_index = (rw == 0) ? EXCP_TLBR :
> +                                               ((rw == 1) ? EXCP_TLBW :
> +                                                            EXCP_TLBX);
> +        }
> +    } else {
> +        env->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 cpu_nios2_handle_mmu_fault(CPUNios2State *env, target_ulong address,
> +                               int rw, int mmu_idx, int is_softmmu)
> +{
> +    if (has_mmu) {
> +        if (MMU_SUPERVISOR_IDX == mmu_idx) {
> +            if (address >= 0xC0000000) {
> +                /* Kernel physical page - TLB bypassed */
> +                address &= TARGET_PAGE_MASK;
> +                tlb_set_page(env, address, address, PAGE_BITS,
> +                             mmu_idx, TARGET_PAGE_SIZE);
> +            } else if (address >= 0x80000000) {
> +                /* Kernel virtual page */
> +                return cpu_nios2_handle_virtual_page(env, address, rw, mmu_idx);
> +            } else {
> +                /* User virtual page */
> +                return cpu_nios2_handle_virtual_page(env, address, rw, mmu_idx);
> +            }
> +        } else {
> +            if (address >= 0x80000000) {
> +                /* Illegal access from user mode */
> +                env->exception_index = EXCP_SUPERA;
> +                env->regs[CR_BADADDR] = address;
> +                return 1;
> +            } else {
> +                /* User virtual page */
> +                return cpu_nios2_handle_virtual_page(env, address, rw, mmu_idx);
> +            }
> +        }
> +    } else {
> +        /* No MMU */
> +        address &= TARGET_PAGE_MASK;
> +        tlb_set_page(env, address, address, PAGE_BITS,
> +                     mmu_idx, TARGET_PAGE_SIZE);
> +    }
> +
> +    return 0;
> +}
> +
> +target_phys_addr_t cpu_get_phys_page_debug(CPUNios2State *env,
> +                                           target_ulong addr)
> +{
> +    target_ulong vaddr, paddr = 0;
> +    struct nios2_mmu_lookup lu;
> +    unsigned int hit;
> +
> +    if (has_mmu && (addr < 0xC0000000)) {
> +        hit = mmu_translate(env, &lu, addr, 0, 0);
> +        if (hit) {
> +            vaddr = addr & TARGET_PAGE_MASK;
> +            paddr = lu.paddr + vaddr - lu.vaddr;
> +        } else {
> +            cpu_abort(env, "cpu_get_phys_page debug MISS: %08X\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..69b6684
> --- /dev/null
> +++ b/target-nios2/helper.h
> @@ -0,0 +1,45 @@
> +/*
> + * 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>
> + */
> +
> +#include "def-helper.h"
> +
> +/* 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_1(raise_exception, void, i32)
> +
> +#if !defined(CONFIG_USER_ONLY)
> +DEF_HELPER_1(mmu_read, i32, i32)
> +DEF_HELPER_2(mmu_write, void, i32, i32)
> +#endif
> +
> +DEF_HELPER_2(divs, i32, i32, i32)
> +DEF_HELPER_2(divu, i32, i32, i32)
> +
> +DEF_HELPER_4(memalign, void, i32, i32, i32, i32)
> +
> +#include "def-helper.h"
> +
> diff --git a/target-nios2/instruction.c b/target-nios2/instruction.c
> new file mode 100644
> index 0000000..4dc06cd
> --- /dev/null
> +++ b/target-nios2/instruction.c
> @@ -0,0 +1,1463 @@
> +/*
> + * 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-all.h"
> +#include "helper.h"
> +
> +#define GEN_HELPER 1
> +#include "helper.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(tmp);
> +    tcg_temp_free_i32(tmp);
> +    dc->is_jmp = DISAS_UPDATE;
> +}
> +
> +/*
> + * 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, int label)
> +{
> +    int 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);

You probably don't want to do that. It's quite expensive to have a
branch in every priviledged instruction.

Given that CR_STATUS_U is use for the flags in cpu_get_tb_cpu_state(),
for a given value of CR_STATUS_U, only TB with a corresponding 
CR_STATUS_U will be reused.

For that you should copy tb->flags in DisasContext and use that instead.

> +}
> +
> +static inline void gen_load_u(DisasContext *dc, TCGv dst, TCGv addr,
> +                              unsigned int size)
> +{
> +    int mem_index = cpu_mmu_index(dc->env);

In general you should not use env in instructions.c, unless it is for
accessing a value that doesn't change during the execution (for example
CPU features/capabilities), as there is no guarantee that the value will
be the same when the TB is actually executed.

In this case it should work correctly because cpu_mmu_index() only
access CR_STATUS_U which is in the TB flags, but it is technically
wrong. Either make cpu_mmu_index check the tb flags (see above to add
them in DisasContext), or add a mem_index flag in DisasContext, computed
from TB flags at the beginning of gen_intermediate_code_internal.

In the latter case, you might even remove these functions and directly
call tcg_gen_qemu_ldXXu(dst, addr, ctx->mem_index) when needed.


> +
> +    switch (size) {
> +    case 1:
> +        tcg_gen_qemu_ld8u(dst, addr, mem_index);
> +        break;
> +    case 2:
> +        tcg_gen_qemu_ld16u(dst, addr, mem_index);
> +        break;
> +    case 4:
> +        tcg_gen_qemu_ld32u(dst, addr, mem_index);
> +        break;
> +    default:
> +        cpu_abort(dc->env, "Incorrect load size %d\n", size);
> +        break;
> +    }
> +}
> +
> +static inline void gen_load_s(DisasContext *dc, TCGv dst, TCGv addr,
> +                              unsigned int size)
> +{
> +    int mem_index = cpu_mmu_index(dc->env);
> +

Ditto

> +    switch (size) {
> +    case 1:
> +        tcg_gen_qemu_ld8s(dst, addr, mem_index);
> +        break;
> +    case 2:
> +        tcg_gen_qemu_ld16s(dst, addr, mem_index);
> +        break;
> +    case 4:
> +        tcg_gen_qemu_ld32s(dst, addr, mem_index);
> +        break;
> +    default:
> +        cpu_abort(dc->env, "Incorrect load size %d\n", size);
> +        break;
> +    }
> +}
> +
> +static inline void gen_store(DisasContext *dc, TCGv val, TCGv addr,
> +                             unsigned int size)
> +{
> +    int mem_index = cpu_mmu_index(dc->env);
> +

Ditto

> +    switch (size) {
> +    case 1:
> +        tcg_gen_qemu_st8(val, addr, mem_index);
> +        break;
> +    case 2:
> +        tcg_gen_qemu_st16(val, addr, mem_index);
> +        break;
> +    case 4:
> +        tcg_gen_qemu_st32(val, addr, mem_index);
> +        break;
> +    default:
> +        cpu_abort(dc->env, "Incorrect load size %d\n", size);
> +        break;
> +    }
> +}
> +
> +/*
> + * 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);
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC],
> +                    (dc->pc & 0xF0000000) | (instr->imm26 * 4));
> +
> +    dc->is_jmp = DISAS_JUMP;
> +}
> +

You probably want to add some tcg_gen_goto_tb() for static jumps, so
that TB linking is possible. It greatly improves the speed of the
emulation.


> +/* PC <- (PC(31..28) : IMM26 * 4) */
> +static void jmpi(DisasContext *dc, uint32_t code)
> +{
> +    J_TYPE(instr, code);
> +
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC],
> +                    (dc->pc & 0xF0000000) | (instr->imm26 * 4));
> +
> +    dc->is_jmp = DISAS_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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);

Minor nitpick: it's better to write:

       tcg_gen_add_tl(addr, addr, dc->cpu_R[instr->a]);

as the code generator on non-RISC hosts usually generate a slightly tiny
better code.

> +
> +    gen_load_u(dc, dc->cpu_R[instr->b], addr, 1);
> +
> +    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_movi_tl(imm, (int32_t)((int16_t)instr->imm16));
> +
> +    tcg_gen_add_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a], imm);
> +

You can also call tcg_gen_addi_tl() instead, so that you don't need
to load the constant by yourself. As a bonus the immediate = 0 case
is optimized earlier in the code generation.

> +    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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);

Ditto

> +
> +    gen_store(dc, dc->cpu_R[instr->b], addr, 1);
> +
> +    tcg_temp_free(addr);
> +}
> +
> +/* PC <- PC + 4 + IMM16 */
> +static void br(DisasContext *dc, uint32_t code)
> +{
> +    I_TYPE(instr, code);
> +
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC],
> +                    dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> +    dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rB <- @(Mem8[rA + @(IMM16)]) */
> +static void ldb(DisasContext *dc, uint32_t code)
> +{
> +    I_TYPE(instr, code);
> +
> +    TCGv addr = tcg_temp_new();
> +    tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_load_s(dc, dc->cpu_R[instr->b], addr, 1);
> +
> +    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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_load_u(dc, dc->cpu_R[instr->b], addr, 2);
> +
> +    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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_store(dc, dc->cpu_R[instr->b], addr, 2);
> +
> +    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);
> +
> +    int l1 = gen_new_label();
> +
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC],
> +                    dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> +    tcg_gen_brcond_tl(TCG_COND_GE, dc->cpu_R[instr->a],
> +                      dc->cpu_R[instr->b], l1);
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
> +    gen_set_label(l1);
> +
> +    dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rB <- Mem16[rA + @IMM16)] */
> +static void ldh(DisasContext *dc, uint32_t code)
> +{
> +    I_TYPE(instr, code);
> +
> +    TCGv addr = tcg_temp_new();
> +    tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_load_s(dc, dc->cpu_R[instr->b], addr, 2);
> +
> +    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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_store(dc, dc->cpu_R[instr->b], addr, 4);
> +
> +    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);
> +
> +    int l1 = gen_new_label();
> +
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC],
> +                    dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> +    tcg_gen_brcond_tl(TCG_COND_LT, dc->cpu_R[instr->a],
> +                      dc->cpu_R[instr->b], l1);
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
> +    gen_set_label(l1);
> +
> +    dc->is_jmp = DISAS_JUMP;
> +}
> +
> +/* rB <- @(Mem32[rA + @(IMM16)]) */
> +static void ldw(DisasContext *dc, uint32_t code)
> +{
> +    I_TYPE(instr, code);
> +
> +    TCGv addr = tcg_temp_new();
> +    tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_load_u(dc, dc->cpu_R[instr->b], addr, 4);
> +
> +    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);
> +
> +    int l1 = gen_new_label();
> +
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC],
> +                    dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> +    tcg_gen_brcond_tl(TCG_COND_NE, dc->cpu_R[instr->a],
> +                      dc->cpu_R[instr->b], l1);
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
> +    gen_set_label(l1);
> +
> +    dc->is_jmp = DISAS_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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_load_u(dc, dc->cpu_R[instr->b], addr, 1);
> +
> +    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_movi_tl(imm, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_mul_i32(dc->cpu_R[instr->b], dc->cpu_R[instr->a], imm);

tcg_gen_muli_tl() also exists.

> +    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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_store(dc, dc->cpu_R[instr->b], addr, 1);
> +
> +    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);
> +
> +    int l1 = gen_new_label();
> +
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC],
> +                    dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> +    tcg_gen_brcond_tl(TCG_COND_EQ, dc->cpu_R[instr->a],
> +                      dc->cpu_R[instr->b], l1);
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
> +    gen_set_label(l1);
> +
> +    dc->is_jmp = DISAS_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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_load_s(dc, dc->cpu_R[instr->b], addr, 1);
> +
> +    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],
> +                        (int32_t)((int16_t)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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_load_u(dc, dc->cpu_R[instr->b], addr, 2);
> +
> +    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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_store(dc, dc->cpu_R[instr->b], addr, 2);
> +
> +    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);
> +
> +    int l1 = gen_new_label();
> +
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC],
> +                    dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> +    tcg_gen_brcond_tl(TCG_COND_GEU, dc->cpu_R[instr->a],
> +                      dc->cpu_R[instr->b], l1);
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
> +    gen_set_label(l1);
> +
> +    dc->is_jmp = DISAS_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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_load_s(dc, dc->cpu_R[instr->b], addr, 2);
> +
> +    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],
> +                        (int32_t)((int16_t)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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_store(dc, dc->cpu_R[instr->b], addr, 4);
> +
> +    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);
> +
> +    int l1 = gen_new_label();
> +
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC],
> +                    dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
> +    tcg_gen_brcond_tl(TCG_COND_LTU, dc->cpu_R[instr->a],
> +                      dc->cpu_R[instr->b], l1);
> +    tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
> +    gen_set_label(l1);
> +
> +    dc->is_jmp = DISAS_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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
> +
> +    gen_load_u(dc, dc->cpu_R[instr->b], addr, 4);
> +
> +    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 struct instruction 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);
> +
> +    TCGv t0 = tcg_temp_new();
> +
> +    tcg_gen_shri_tl(t0, dc->cpu_R[instr->a], 32 - instr->imm5);
> +    tcg_gen_shli_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], instr->imm5);
> +    tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->c], t0);
> +

Looks like you want to use tcg_gen_rotli, which is optimized on some
hosts (like x86).

> +    tcg_temp_free(t0);
> +}
> +
> +/* 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();
> +    TCGv t1 = tcg_temp_new();
> +
> +    tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
> +    tcg_gen_movi_tl(t1, 32);
> +    tcg_gen_sub_tl(t1, t1, t0);
> +    tcg_gen_shri_tl(t1, dc->cpu_R[instr->a], t1);
> +    tcg_gen_shli_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
> +    tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->c], t1);
> +

Same, tcg_gen_rotl exists.

> +    tcg_temp_free(t0);
> +    tcg_temp_free(t1);
> +}
> +
> +/* */
> +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_trunc_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();
> +    TCGv t1 = tcg_temp_new();
> +
> +    tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
> +    tcg_gen_movi_tl(t1, 32);
> +    tcg_gen_sub_tl(t1, t1, t0);
> +    tcg_gen_shli_tl(t1, dc->cpu_R[instr->a], t1);
> +    tcg_gen_shri_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
> +    tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->c], t1);
> +
> +    tcg_temp_free(t0);
> +    tcg_temp_free(t1);
> +}
> +
> +/* */
> +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_trunc_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_trunc_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);
> +
> +    int l1 = gen_new_label();
> +    gen_check_supervisor(dc, l1);
> +
> +    switch (instr->imm5 + 32) {
> +    case CR_PTEADDR:
> +    case CR_TLBACC:
> +    case CR_TLBMISC:
> +    {
> +        TCGv_i32 tmp = tcg_const_i32(instr->imm5 + 32);
> +        gen_helper_mmu_read(dc->cpu_R[instr->c], tmp);
> +        tcg_temp_free_i32(tmp);
> +        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);
> +
> +    int l1 = gen_new_label();
> +    gen_check_supervisor(dc, l1);
> +
> +    switch (instr->imm5 + 32) {
> +    case CR_PTEADDR:
> +    case CR_TLBACC:
> +    case CR_TLBMISC:
> +    {
> +        TCGv_i32 tmp = tcg_const_i32(instr->imm5 + 32);
> +        gen_helper_mmu_write(tmp, dc->cpu_R[instr->a]);
> +        tcg_temp_free_i32(tmp);
> +        break;
> +    }
> +
> +    default:
> +        tcg_gen_mov_tl(dc->cpu_R[instr->imm5 + 32], dc->cpu_R[instr->a]);
> +        break;
> +    }
> +
> +    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 struct instruction 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);
> +}
> +
> +void handle_instruction(DisasContext *dc)
> +{
> +    uint32_t insn = ldl_code(dc->pc);
> +    uint32_t 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..fe29933
> --- /dev/null
> +++ b/target-nios2/instruction.h
> @@ -0,0 +1,290 @@
> +/*
> + * 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 */
> +struct i_type {
> +    uint32_t op:6;
> +    uint32_t imm16:16;
> +    uint32_t b:5;
> +    uint32_t a:5;
> +} __attribute__((packed));
> +
> +union i_type_u {
> +    uint32_t      v;
> +    struct i_type i;
> +};
> +
> +#define I_TYPE(instr, op) \
> +    union i_type_u instr_u = { .v = op }; \
> +    struct i_type *instr = &instr_u.i
> +
> +/* R-Type instruction */
> +struct r_type {
> +    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;
> +} __attribute__((packed));
> +
> +union r_type_u {
> +    uint32_t      v;
> +    struct r_type i;
> +};
> +
> +#define R_TYPE(instr, op) \
> +    union r_type_u instr_u = { .v = op }; \
> +    struct r_type *instr = &instr_u.i
> +
> +/* J-Type instruction */
> +struct j_type {
> +    uint32_t op:6;
> +    uint32_t imm26:26;
> +} __attribute__((packed));
> +
> +#define J_TYPE(instr, op) \
> +    struct j_type *instr = (struct j_type *) &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 {
> +    CPUNios2State           *env;
> +    TCGv                    *cpu_R;
> +    int                      is_jmp;
> +    target_ulong             pc;
> +    struct TranslationBlock *tb;
> +} DisasContext;
> +
> +typedef void (*instruction_handler)(DisasContext *dc, uint32_t opcode);
> +
> +struct instruction {
> +    const char *name;
> +    instruction_handler handler;
> +};
> +
> +/*
> + * Stringification macro (taken from Linux Kernel source code)
> + */
> +
> +/* Indirect stringification.  Doing two levels allows the parameter to be a
> + * macro itself.  For example, compile with -DFOO=bar, __stringify(FOO)
> + * converts to "bar".
> + */
> +
> +#define __stringify_1(x...)     #x
> +#define __stringify(x...)       __stringify_1(x)
> +
> +#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);
> +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..09198db
> --- /dev/null
> +++ b/target-nios2/machine.c
> @@ -0,0 +1,33 @@
> +/*
> + * 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 "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..a52fa1b
> --- /dev/null
> +++ b/target-nios2/mmu.c
> @@ -0,0 +1,273 @@
> +/*
> + * 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 <stdio.h>
> +#include <stdlib.h>
> +#include <assert.h>
> +
> +#include "config.h"
> +#include "cpu.h"
> +#include "exec-all.h"
> +
> +/* Define this to enable MMU debug messages */
> +/* #define DEBUG_MMU */
> +
> +#ifdef DEBUG_MMU
> +#define MMU_LOG(x) x
> +#else
> +#define MMU_LOG(x)
> +#endif
> +
> +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,
> +                           struct nios2_mmu_lookup *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++) {
> +
> +        struct nios2_tlb_entry *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)
> +{
> +    int idx;
> +    MMU_LOG(qemu_log("TLB Flush PID %d\n", pid));
> +
> +    for (idx = 0; idx < env->mmu.tlb_num_entries; idx++) {
> +        struct nios2_tlb_entry *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(env, vaddr);
> +        }
> +    }
> +}
> +
> +void mmu_write(CPUNios2State *env, uint32_t rn, uint32_t v)
> +{
> +    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;
> +            struct nios2_tlb_entry *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(env, 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;
> +            struct nios2_tlb_entry *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(struct nios2_mmu *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 = (struct nios2_tlb_entry *)g_malloc0(
> +        sizeof(struct nios2_tlb_entry) * 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++) {
> +        struct nios2_tlb_entry *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' : '-');
> +    }
> +}
> +
> diff --git a/target-nios2/mmu.h b/target-nios2/mmu.h
> new file mode 100644
> index 0000000..01d67cf
> --- /dev/null
> +++ b/target-nios2/mmu.h
> @@ -0,0 +1,49 @@
> +/*
> + * 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>
> + */
> +
> +struct nios2_tlb_entry {
> +    target_ulong tag;
> +    target_ulong data;
> +};
> +
> +struct nios2_mmu {
> +    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;
> +    struct nios2_tlb_entry *tlb;
> +};
> +
> +struct nios2_mmu_lookup {
> +    target_ulong vaddr;
> +    target_ulong paddr;
> +    int prot;
> +};
> +
> +void mmu_flip_um(CPUNios2State *env, unsigned int um);
> +unsigned int mmu_translate(CPUNios2State *env,
> +                           struct nios2_mmu_lookup *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(struct nios2_mmu *mmu);
> diff --git a/target-nios2/op_helper.c b/target-nios2/op_helper.c
> new file mode 100644
> index 0000000..37bac66
> --- /dev/null
> +++ b/target-nios2/op_helper.c
> @@ -0,0 +1,125 @@
> +/*
> + * 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 "dyngen-exec.h"
> +#include "helper.h"
> +#include "host-utils.h"
> +
> +#if !defined(CONFIG_USER_ONLY)
> +#define MMUSUFFIX _mmu
> +#define SHIFT 0
> +#include "softmmu_template.h"
> +#define SHIFT 1
> +#include "softmmu_template.h"
> +#define SHIFT 2
> +#include "softmmu_template.h"
> +#define SHIFT 3
> +#include "softmmu_template.h"
> +
> +void tlb_fill(CPUNios2State *env1, target_ulong addr, int is_write, int mmu_idx,
> +              uintptr_t retaddr)
> +{
> +    TranslationBlock *tb;
> +    CPUNios2State *saved_env;
> +    int ret;
> +
> +    saved_env = env;
> +    env = env1;
> +
> +    ret = cpu_nios2_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
> +    if (unlikely(ret)) {
> +        if (retaddr) {
> +            /* now we have a real cpu fault */
> +            tb = tb_find_pc(retaddr);
> +            if (tb) {
> +                /* The PC is inside the translated code. It means that we have
> +                   a virtual CPU fault */
> +                cpu_restore_state(tb, env, retaddr);
> +            }
> +        }
> +        cpu_loop_exit(env);
> +    }
> +    env = saved_env;
> +}
> +
> +void helper_raise_exception(uint32_t index)
> +{
> +    env->exception_index = index;
> +    cpu_loop_exit(env);
> +}
> +
> +uint32_t helper_mmu_read(uint32_t rn)
> +{
> +    return mmu_read(env, rn);
> +}
> +
> +void helper_mmu_write(uint32_t rn, uint32_t v)
> +{
> +    mmu_write(env, rn, v);
> +}
> +
> +void helper_memalign(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(EXCP_UNALIGN);
> +    }
> +}
> +
> +void cpu_unassigned_access(CPUNios2State *env1, target_phys_addr_t addr,
> +                           int is_write, int is_exec, int is_asi, int size)
> +{
> +    qemu_log("unassigned access to %08X\n", addr);
> +}
> +
> +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
> +
> +#endif /* !CONFIG_USER_ONLY */
> +
> diff --git a/target-nios2/translate.c b/target-nios2/translate.c
> new file mode 100644
> index 0000000..5d0979f
> --- /dev/null
> +++ b/target-nios2/translate.c
> @@ -0,0 +1,252 @@
> +/*
> + * 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-all.h"
> +#include "disas.h"
> +#include "helper.h"
> +#include "qemu-common.h"
> +
> +#include "instruction.h"
> +
> +#define GEN_HELPER 1
> +#include "helper.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 "gen-icount.h"
> +
> +/* generate intermediate code for basic block 'tb'.  */
> +static void gen_intermediate_code_internal(
> +    CPUNios2State *env, TranslationBlock *tb, int search_pc)
> +{
> +    DisasContext dc1, *dc = &dc1;
> +    int num_insns;
> +    int max_insns;
> +    uint32_t next_page_start;
> +    int j, lj = -1;
> +    uint16_t *gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
> +
> +    /* Initialize DC */
> +    dc->env    = env;
> +    dc->cpu_R  = cpu_R;
> +    dc->is_jmp = DISAS_NEXT;
> +    dc->pc     = tb->pc;
> +    dc->tb     = tb;
> +
> +    /* Dump the CPU state to the log */
> +    if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
> +        qemu_log("--------------\n");
> +        log_cpu_state(env, 0);
> +    }
> +
> +    /* Set up instruction counts */
> +    num_insns = 0;
> +    max_insns = tb->cflags & CF_COUNT_MASK;
> +    if (max_insns == 0) {
> +        max_insns = CF_COUNT_MASK;
> +    }
> +    next_page_start = (tb->pc & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
> +
> +    gen_icount_start();
> +    do {
> +        /* Mark instruction start with associated PC */
> +        if (search_pc) {
> +            j = gen_opc_ptr - gen_opc_buf;
> +            if (lj < j) {
> +                lj++;
> +                while (lj < j) {
> +                    gen_opc_instr_start[lj++] = 0;
> +                }
> +            }
> +            gen_opc_pc[lj] = dc->pc;
> +            gen_opc_instr_start[lj] = 1;
> +            gen_opc_icount[lj] = num_insns;
> +        }
> +
> +        LOG_DIS("%8.8x:\t", dc->pc);
> +
> +        if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) {
> +            gen_io_start();
> +        }
> +
> +        /* Decode an instruction */
> +        handle_instruction(dc);
> +
> +        dc->pc += 4;
> +        num_insns++;
> +
> +        /* 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 && gen_opc_ptr < gen_opc_end &&
> +             !env->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_icount_end(tb, num_insns);
> +    *gen_opc_ptr = INDEX_op_end;
> +
> +    /* Mark instruction starts for the final generated instruction */
> +    if (search_pc) {
> +        j = gen_opc_ptr - gen_opc_buf;
> +        lj++;
> +        while (lj <= j) {
> +            gen_opc_instr_start[lj++] = 0;
> +        }
> +    } else {
> +        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(tb->pc, dc->pc - tb->pc, 0);
> +        qemu_log("\nisize=%d osize=%td\n",
> +                 dc->pc - tb->pc, gen_opc_ptr - gen_opc_buf);
> +    }
> +#endif
> +}
> +
> +void gen_intermediate_code(CPUNios2State *env, struct TranslationBlock *tb)
> +{
> +    gen_intermediate_code_internal(env, tb, 0);
> +}
> +
> +void gen_intermediate_code_pc(CPUNios2State *env, struct TranslationBlock *tb)
> +{
> +    gen_intermediate_code_internal(env, tb, 1);
> +}
> +
> +void cpu_dump_state(CPUNios2State *env, FILE *f, fprintf_function cpu_fprintf,
> +                    int flags)
> +{
> +    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");
> +        }
> +    }
> +    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);
> +    cpu_fprintf(f, "\n\n");
> +}
> +
> +Nios2CPU *cpu_nios2_init(const char *cpu_model)
> +{
> +    Nios2CPU *cpu;
> +    int i;
> +
> +    cpu = NIOS2_CPU(object_new(TYPE_NIOS2_CPU));
> +
> +    cpu->env.reset_addr = RESET_ADDRESS;
> +    cpu->env.exception_addr = EXCEPTION_ADDRESS;
> +    cpu->env.fast_tlb_miss_addr = FAST_TLB_MISS_ADDRESS;
> +
> +    cpu_reset(CPU(cpu));
> +    qemu_init_vcpu(&cpu->env);
> +
> +    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(TCG_AREG0,
> +                                      offsetof(CPUNios2State, regs[i]),
> +                                      regnames[i]);
> +    }
> +
> +#define GEN_HELPER 2
> +#include "helper.h"
> +
> +  return cpu;
> +}
> +
> +void restore_state_to_opc(CPUNios2State *env, TranslationBlock *tb, int pc_pos)
> +{
> +    env->regs[R_PC] = gen_opc_pc[pc_pos];
> +}
> +
> -- 
> 1.7.9.5
> 
> 
>
Richard Henderson - Sept. 11, 2012, 10:30 p.m.
On 09/11/2012 02:34 PM, Aurelien Jarno wrote:
> Minor nitpick: it's better to write:
> 
>        tcg_gen_add_tl(addr, addr, dc->cpu_R[instr->a]);
> 
> as the code generator on non-RISC hosts usually generate a slightly tiny
> better code.

You know... we already swap operands for constants.  It would be trivial
have tcg_constant_folding swap for matching operands at the same time.


r~
Richard Henderson - Sept. 11, 2012, 11:18 p.m.
Somehow the original patch set never arrived here.  Replying indirectly...

> On Sun, Sep 09, 2012 at 08:19:59PM -0400, crwulff@gmail.com wrote:
>> diff --git a/target-nios2/exec.h b/target-nios2/exec.h
...
>> +static inline int cpu_has_work(CPUState *env)
>> +{
>> +    return env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI);
>> +}
>> +
>> +static inline int cpu_halted(CPUState *env)
>> +{
>> +    if (!env->halted) {
>> +        return 0;
>> +    }
>> +
>> +    if (cpu_has_work(env)) {
>> +        env->halted = 0;
>> +        return 0;
>> +    }
>> +
>> +    return EXCP_HALTED;
>> +}
>> +
>> +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb)
>> +{
>> +    env->regs[R_PC] = tb->pc;
>> +}

Do my eyes deceive me or do you have duplicates of these from cpu.h?

>> +++ b/target-nios2/instruction.c

Any particular reason you split this file out from translate.c?

>> +static void __break(DisasContext *dc, uint32_t code __attribute__((unused)))

Leading un is reserved to the compiler.  break1?  break_?

>> +/* I-Type instruction */
>> +struct i_type {
>> +    uint32_t op:6;
>> +    uint32_t imm16:16;
>> +    uint32_t b:5;
>> +    uint32_t a:5;
>> +} __attribute__((packed));
>> +
>> +union i_type_u {
>> +    uint32_t      v;
>> +    struct i_type i;
>> +};
>> +
>> +#define I_TYPE(instr, op) \
>> +    union i_type_u instr_u = { .v = op }; \
>> +    struct i_type *instr = &instr_u.i

This is an extremely unportable idea.

Bit field layout differs from big-endian to little-endian, and between
compiler abis.  The only reliable method of picking out a specific set
of bits is to shift and mask by hand.

Which you can still do with your I_TYPE/R_TYPE macros (which I do like),
but instead with different structure definitions and initialization.

>> +typedef struct DisasContext {
>> +    CPUNios2State           *env;
>> +    TCGv                    *cpu_R;
>> +    int                      is_jmp;
>> +    target_ulong             pc;
>> +    struct TranslationBlock *tb;
>> +} DisasContext;

Why are you copying cpu_R here, and using s->cpu_R everywhere?
Why not directly use the global variable cpu_R like everyone else?
I suppose it's related to translate.c vs instruction.c, but I've
already expressed an opinion there...

>> +/* Indirect stringification.  Doing two levels allows the parameter to be a
>> + * macro itself.  For example, compile with -DFOO=bar, __stringify(FOO)
>> + * converts to "bar".
>> + */

Is there any reason you'd want to do that for the instruction tables?

>> +#define __stringify_1(x...)     #x
>> +#define __stringify(x...)       __stringify_1(x)

... because there's that leading underscore again, and honestly you don't need
anything but

#define INSTRUCTION(N)  { #N, N }

>> +#include "dyngen-exec.h"

This is going away.

>> +void helper_memalign(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(EXCP_UNALIGN);
>> +    }
>> +}

This should be done with 

#define ALIGNED_ONLY

directly in the softmmu_template.h helpers.  C.f. target-sparc/ldst_helper.c.

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

(1) Missing divide by zero check.  This will generally put qemu into a loop.

(2) You could (and probably should) use tcg_gen_div{,u}_tl.
    I would only suggest external helper functions if you have to check for
    additional exceptions apart from X / 0, like -INT_MIN / -1.

>> +    /* Dump the CPU state to the log */
>> +    if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
>> +        qemu_log("--------------\n");
>> +        log_cpu_state(env, 0);
>> +    }

Don't log cpu state for in_asm.  That's a common bug across translators,
and all it does is cause double logging for "-d cpu,in_asm".

>> +        LOG_DIS("%8.8x:\t", dc->pc);

Use tcg_gen_debug_insn_start, which makes the tcg opcodes dumps pretty too.


r~
crwulff@gmail.com - Sept. 14, 2012, 3:30 a.m.
Thanks for the feedback. Nothing jumps out at me that I disagree with. I'll
get it updated and another patch out once I get the chance to incorporate
the info.

 > +#include "hw/sysbus.h"
> +#include "dyngen-exec.h"
> +#include "cpu.h"
> +
> +struct altera_iic {

CamelCase



Yep. I missed that one in reviewing the coding standard and the checkpatch
script apparently doesn't flag it. I'll clean up that one an the others
like it.

 > +
> +#if !defined(CONFIG_USER_ONLY)
> +    struct nios2_mmu mmu;
> +#endif
> +
> +    CPU_COMMON
> +} CPUNios2State;
> +
> +#include "cpu-qom.h"
> +
> +extern Nios2CPU *cpu_nios2_init(const char *cpu_model);

'extern' is useless for functions.



True. I will remove for consistency with other code.

 > +extern int cpu_nios2_exec(CPUNios2State *s);
> +extern void cpu_nios2_close(CPUNios2State *s);
> +extern void do_interrupt(CPUNios2State *env);
> +extern int cpu_nios2_signal_handler(int host_signum, void *pinfo, void *puc);
> +extern void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUNios2State *env);
> +
> +#define TARGET_PHYS_ADDR_SPACE_BITS 32
> +#define TARGET_VIRT_ADDR_SPACE_BITS 32
> +
> +extern Nios2CPU *g_cpu;

Just say no to globals.



 > +#ifndef NIOS2_EXEC_H
> +#define NIOS2_EXEC_H
> +
> +#include "dyngen-exec.h"

Dyngen model is being obsoleted in coming days. Please make all
helpers which need access to CPU state take the state as parameter.

> +
> +register struct CPUNios2State *env asm(AREG0);

This is already defined in dyngen-exec.h, but see above. Maybe you are
not using Git HEAD? No target uses exec.h anymore.

> +
> +#include "cpu.h"
> +#include "exec-all.h"
> +
> +#if !defined(CONFIG_USER_ONLY)
> +#include "softmmu_exec.h"
> +#endif
> +

The functions below should go to cpu.h.


I've been working on this on and off for months now so it is very well
possible that I referenced older code that still did use some of this
stuff. I'll review the latest to see how this is being done now. I agree
that globals aren't good. I'll see if I can find how to get the information
passed along to the appropriate places that need it.

 > +void do_interrupt(CPUNios2State *env)
> +{
> +    switch (env->exception_index) {
> +    case EXCP_IRQ:
> +        assert(env->regs[CR_STATUS] & CR_STATUS_PIE);

I hope this is not a guest triggerable assert.



It shouldn't be. But maybe this should just be a debug message and not
trigger an exception if this is the case. We should never be trying to send
an IRQ exception to the guest if interrupts are globally disabled.

 > +    if (has_mmu && (addr < 0xC0000000)) {
> +        hit = mmu_translate(env, &lu, addr, 0, 0);
> +        if (hit) {
> +            vaddr = addr & TARGET_PAGE_MASK;
> +            paddr = lu.paddr + vaddr - lu.vaddr;
> +        } else {
> +            cpu_abort(env, "cpu_get_phys_page debug MISS: %08X\n", addr);

cpu_get_phys_page_debug is used for memory access from monitor.
Aborting because user specified a bad address is rather draconian.



I was seeing this come from the disassembly code when I had a mismatch
between the TLB in the processor emulation and the one that QEMU knows
about (due to improper removal of QEMU tlb entries when they were
overwritten in the NiosII TLB.) I can make this into a debug message though
instead. I'm not seeing this come out at all now that I've fixed my TLB
implementation.

 > +    default:
> +        cpu_abort(dc->env, "Incorrect load size %d\n", size);

Maybe assert instead.

> +        break;
> +    }
> +}



 > + */
> +
> +/* I-Type instruction */
> +struct i_type {

IType

> +    uint32_t op:6;
> +    uint32_t imm16:16;
> +    uint32_t b:5;
> +    uint32_t a:5;
> +} __attribute__((packed));

QEMU_PACKED



Yep. I will change this and any others.

 > +#define __stringify_1(x...)     #x
> +#define __stringify(x...)       __stringify_1(x)

We already have glue and stringify, no need to add another.



Ok, didn't see those. This was some of the bits I originally got from
nios2sim-ng, but I can easily clean that up.


 > +#include "cpu.h"
> +#include "dyngen-exec.h"

Remove.



 > +
> +void tlb_fill(CPUNios2State *env1, target_ulong addr, int is_write, int mmu_idx,
> +              uintptr_t retaddr)
> +{
> +    TranslationBlock *tb;
> +    CPUNios2State *saved_env;
> +    int ret;
> +
> +    saved_env = env;
> +    env = env1;

References to global env should be removed, the CPU state should be
passed around instead. Please check for example Sparc or recent
commits to s390x.



Ok, I'll take a look at that example. Definitely good to get rid of globals.


-- Chris Wulff
crwulff@gmail.com - Sept. 14, 2012, 3:42 a.m.
Thanks for the feedback. I'll see about getting things updated once I have
the chance. I wasn't too terribly concerned with performance as the target
processor being emulated is much slower than the host (about 100MHz). Even
the way things are now, Linux runs several times faster in emulation that
it does on the actual target CPU.

Many of the optimizations you point out look pretty simple to incorporate
so I will definitely see about doing that. I'll review the places you've
pointed out where the translation vs. execution time CPU state is being
mis-handled. It seems to be working, but those are definitely the types of
things that will introduce hard to find bugs.

  -- Chris Wulff


> > +static void gen_check_supervisor(DisasContext *dc, int label)
> > +{
> > +    int 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);
>
> You probably don't want to do that. It's quite expensive to have a
> branch in every priviledged instruction.
>
> Given that CR_STATUS_U is use for the flags in cpu_get_tb_cpu_state(),
> for a given value of CR_STATUS_U, only TB with a corresponding
> CR_STATUS_U will be reused.
>
> For that you should copy tb->flags in DisasContext and use that instead.
>
> > +}
> > +
> > +static inline void gen_load_u(DisasContext *dc, TCGv dst, TCGv addr,
> > +                              unsigned int size)
> > +{
> > +    int mem_index = cpu_mmu_index(dc->env);
>
> In general you should not use env in instructions.c, unless it is for
> accessing a value that doesn't change during the execution (for example
> CPU features/capabilities), as there is no guarantee that the value will
> be the same when the TB is actually executed.
>
> In this case it should work correctly because cpu_mmu_index() only
> access CR_STATUS_U which is in the TB flags, but it is technically
> wrong. Either make cpu_mmu_index check the tb flags (see above to add
> them in DisasContext), or add a mem_index flag in DisasContext, computed
> from TB flags at the beginning of gen_intermediate_code_internal.
>
> In the latter case, you might even remove these functions and directly
> call tcg_gen_qemu_ldXXu(dst, addr, ctx->mem_index) when needed.
>
>
> > +
> > +    switch (size) {
> > +    case 1:
> > +        tcg_gen_qemu_ld8u(dst, addr, mem_index);
> > +        break;
> > +    case 2:
> > +        tcg_gen_qemu_ld16u(dst, addr, mem_index);
> > +        break;
> > +    case 4:
> > +        tcg_gen_qemu_ld32u(dst, addr, mem_index);
> > +        break;
> > +    default:
> > +        cpu_abort(dc->env, "Incorrect load size %d\n", size);
> > +        break;
> > +    }
> > +}
> > +
> > +static inline void gen_load_s(DisasContext *dc, TCGv dst, TCGv addr,
> > +                              unsigned int size)
> > +{
> > +    int mem_index = cpu_mmu_index(dc->env);
> > +
>
> Ditto
>
> > +    switch (size) {
> > +    case 1:
> > +        tcg_gen_qemu_ld8s(dst, addr, mem_index);
> > +        break;
> > +    case 2:
> > +        tcg_gen_qemu_ld16s(dst, addr, mem_index);
> > +        break;
> > +    case 4:
> > +        tcg_gen_qemu_ld32s(dst, addr, mem_index);
> > +        break;
> > +    default:
> > +        cpu_abort(dc->env, "Incorrect load size %d\n", size);
> > +        break;
> > +    }
> > +}
> > +
> > +static inline void gen_store(DisasContext *dc, TCGv val, TCGv addr,
> > +                             unsigned int size)
> > +{
> > +    int mem_index = cpu_mmu_index(dc->env);
> > +
>
> Ditto
>
> > +    switch (size) {
> > +    case 1:
> > +        tcg_gen_qemu_st8(val, addr, mem_index);
> > +        break;
> > +    case 2:
> > +        tcg_gen_qemu_st16(val, addr, mem_index);
> > +        break;
> > +    case 4:
> > +        tcg_gen_qemu_st32(val, addr, mem_index);
> > +        break;
> > +    default:
> > +        cpu_abort(dc->env, "Incorrect load size %d\n", size);
> > +        break;
> > +    }
> > +}
> > +
> > +/*
> > + * 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);
> > +    tcg_gen_movi_tl(dc->cpu_R[R_PC],
> > +                    (dc->pc & 0xF0000000) | (instr->imm26 * 4));
> > +
> > +    dc->is_jmp = DISAS_JUMP;
> > +}
> > +
>
> You probably want to add some tcg_gen_goto_tb() for static jumps, so
> that TB linking is possible. It greatly improves the speed of the
> emulation.
>
>
> > +/* PC <- (PC(31..28) : IMM26 * 4) */
> > +static void jmpi(DisasContext *dc, uint32_t code)
> > +{
> > +    J_TYPE(instr, code);
> > +
> > +    tcg_gen_movi_tl(dc->cpu_R[R_PC],
> > +                    (dc->pc & 0xF0000000) | (instr->imm26 * 4));
> > +
> > +    dc->is_jmp = DISAS_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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> > +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
>
> Minor nitpick: it's better to write:
>
>        tcg_gen_add_tl(addr, addr, dc->cpu_R[instr->a]);
>
> as the code generator on non-RISC hosts usually generate a slightly tiny
> better code.
>
> > +
> > +    gen_load_u(dc, dc->cpu_R[instr->b], addr, 1);
> > +
> > +    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_movi_tl(imm, (int32_t)((int16_t)instr->imm16));
> > +
> > +    tcg_gen_add_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a], imm);
> > +
>
> You can also call tcg_gen_addi_tl() instead, so that you don't need
> to load the constant by yourself. As a bonus the immediate = 0 case
> is optimized earlier in the code generation.
>
> > +    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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
> > +    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
>
> Ditto
>
>
> > +/* rC <- rA rotated left IMM5 bit positions */
> > +static void roli(DisasContext *dc, uint32_t code)
> > +{
> > +    R_TYPE(instr, code);
> > +
> > +    TCGv t0 = tcg_temp_new();
> > +
> > +    tcg_gen_shri_tl(t0, dc->cpu_R[instr->a], 32 - instr->imm5);
> > +    tcg_gen_shli_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a],
> instr->imm5);
> > +    tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->c], t0);
> > +
>
> Looks like you want to use tcg_gen_rotli, which is optimized on some
> hosts (like x86).
>
> > +    tcg_temp_free(t0);
> > +}
> > +
> > +/* 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();
> > +    TCGv t1 = tcg_temp_new();
> > +
> > +    tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
> > +    tcg_gen_movi_tl(t1, 32);
> > +    tcg_gen_sub_tl(t1, t1, t0);
> > +    tcg_gen_shri_tl(t1, dc->cpu_R[instr->a], t1);
> > +    tcg_gen_shli_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
> > +    tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->c], t1);
> > +
>
> Same, tcg_gen_rotl exists.
>
>
> --
> Aurelien Jarno                          GPG: 1024D/F1BCDB73
> aurelien@aurel32.net                 http://www.aurel32.net
>
Andreas Färber - Sept. 15, 2012, 2:55 p.m.
Am 10.09.2012 02:19, schrieb crwulff@gmail.com:
> From: Chris Wulff <crwulff@gmail.com>
> 
> Signed-off-by: Chris Wulff <crwulff@gmail.com>
> ---
>  target-nios2/Makefile.objs |    5 +
>  target-nios2/altera_iic.c  |  100 +++
>  target-nios2/cpu-qom.h     |   69 +++
>  target-nios2/cpu.c         |   83 +++
>  target-nios2/cpu.h         |  259 ++++++++
>  target-nios2/exec.h        |   60 ++
>  target-nios2/helper.c      |  291 +++++++++
>  target-nios2/helper.h      |   45 ++
>  target-nios2/instruction.c | 1463 ++++++++++++++++++++++++++++++++++++++++++++
>  target-nios2/instruction.h |  290 +++++++++
>  target-nios2/machine.c     |   33 +
>  target-nios2/mmu.c         |  273 +++++++++
>  target-nios2/mmu.h         |   49 ++
>  target-nios2/op_helper.c   |  125 ++++
>  target-nios2/translate.c   |  252 ++++++++
>  15 files changed, 3397 insertions(+)

Some general comments: You're introducing a new target here, so if you
design your API cleanly (using Nios2CPU where possible) you don't really
need a separate cpu-qom.h file in addition to cpu.h, cf. target-or32.

Please prefer passing Nios2CPU as opaque rather than CPUNios2State
(e.g., 3/9; cf. target-arm). Reason is that fields are being moved from
CPUxxxState to CPUState and this will simplify the migration.

Thanks,
Andreas
crwulff@gmail.com - Sept. 15, 2012, 3:33 p.m.
On Tue, Sep 11, 2012 at 5:34 PM, Aurelien Jarno <aurelien@aurel32.net>wrote:

> On Sun, Sep 09, 2012 at 08:19:59PM -0400, crwulff@gmail.com wrote:
> > From: Chris Wulff <crwulff@gmail.com>
> >
> > Signed-off-by: Chris Wulff <crwulff@gmail.com>
>
> > +    tcg_gen_movi_tl(dc->cpu_R[R_RA], dc->pc + 4);
> > +    tcg_gen_movi_tl(dc->cpu_R[R_PC],
> > +                    (dc->pc & 0xF0000000) | (instr->imm26 * 4));
> > +
> > +    dc->is_jmp = DISAS_JUMP;
> > +}
> > +
>
> You probably want to add some tcg_gen_goto_tb() for static jumps, so
> that TB linking is possible. It greatly improves the speed of the
> emulation.
>
>
>
Doing this actually made quite a big difference. The reported bogomips from
linux went up 15x and the observable speed by about 2x. The rest of your
suggested changes didn't have much noticeable effect but were at least good
things to clean up anyway.

  -- Chris Wulff

Patch

diff --git a/target-nios2/Makefile.objs b/target-nios2/Makefile.objs
new file mode 100644
index 0000000..d072795
--- /dev/null
+++ b/target-nios2/Makefile.objs
@@ -0,0 +1,5 @@ 
+obj-y += translate.o op_helper.o helper.o cpu.o instruction.o
+obj-$(CONFIG_SOFTMMU) += mmu.o machine.o
+obj-y += altera_iic.o
+
+$(obj)/op_helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
diff --git a/target-nios2/altera_iic.c b/target-nios2/altera_iic.c
new file mode 100644
index 0000000..d45d6fa
--- /dev/null
+++ b/target-nios2/altera_iic.c
@@ -0,0 +1,100 @@ 
+/*
+ * QEMU Altera Internal Interrupt Controller.
+ *
+ * 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 "hw/sysbus.h"
+#include "dyngen-exec.h"
+#include "cpu.h"
+
+struct altera_iic {
+    SysBusDevice busdev;
+    Nios2CPU *cpu;
+    qemu_irq parent_irq;
+};
+
+static void update_irq(struct altera_iic *pv)
+{
+    uint32_t i;
+
+    if ((pv->cpu->env.regs[CR_STATUS] & CR_STATUS_PIE) == 0) {
+        qemu_irq_lower(pv->parent_irq);
+        return;
+    }
+
+    for (i = 0; i < 32; i++) {
+        if (pv->cpu->env.regs[CR_IPENDING] &
+            pv->cpu->env.regs[CR_IENABLE] & (1 << i)) {
+            break;
+        }
+    }
+    if (i == 32) {
+        qemu_irq_lower(pv->parent_irq);
+    } else {
+        qemu_irq_raise(pv->parent_irq);
+    }
+}
+
+static void irq_handler(void *opaque, int irq, int level)
+{
+    struct altera_iic *pv = opaque;
+
+    pv->cpu->env.regs[CR_IPENDING] &= ~(1 << irq);
+    pv->cpu->env.regs[CR_IPENDING] |= level << irq;
+
+    update_irq(pv);
+}
+
+static int altera_iic_init(SysBusDevice *dev)
+{
+    struct altera_iic *pv = FROM_SYSBUS(typeof(*pv), dev);
+    pv->cpu = g_cpu; /* TODO: Get attached CPU instead somehow... */
+
+    qdev_init_gpio_in(&dev->qdev, irq_handler, 32);
+    sysbus_init_irq(dev, &pv->parent_irq);
+
+    return 0;
+}
+
+static Property altera_iic_properties[] = {
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void altera_iic_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = altera_iic_init;
+    dc->props = altera_iic_properties;
+}
+
+static TypeInfo altera_iic_info = {
+    .name          = "altera,iic",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(struct altera_iic),
+    .class_init    = altera_iic_class_init,
+};
+
+static void altera_iic_register(void)
+{
+    type_register_static(&altera_iic_info);
+}
+
+type_init(altera_iic_register)
+
diff --git a/target-nios2/cpu-qom.h b/target-nios2/cpu-qom.h
new file mode 100644
index 0000000..be6fa65
--- /dev/null
+++ b/target-nios2/cpu-qom.h
@@ -0,0 +1,69 @@ 
+/*
+ * 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>
+ */
+#ifndef QEMU_NIOS2_CPU_QOM_H
+#define QEMU_NIOS2_CPU_QOM_H
+
+#include "qemu/cpu.h"
+
+#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 >*/
+
+    void (*parent_reset)(CPUState *cpu);
+} Nios2CPUClass;
+
+/**
+ * Nios2CPU:
+ * @env: #CPUNios2State
+ *
+ * A Nios2 CPU.
+ */
+typedef struct Nios2CPU {
+    /*< private >*/
+    CPUState parent_obj;
+    /*< public >*/
+
+    CPUNios2State env;
+} 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))
+
+#endif /* QEMU_NIOS2_CPU_QOM_H */
diff --git a/target-nios2/cpu.c b/target-nios2/cpu.c
new file mode 100644
index 0000000..bd819c4
--- /dev/null
+++ b/target-nios2/cpu.c
@@ -0,0 +1,83 @@ 
+/*
+ * 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 "cpu.h"
+#include "qemu-common.h"
+
+
+/* CPUClass::reset() */
+static void nios2_cpu_reset(CPUState *s)
+{
+    Nios2CPU *cpu = NIOS2_CPU(s);
+    Nios2CPUClass *mcc = NIOS2_CPU_GET_CLASS(cpu);
+    CPUNios2State *env = &cpu->env;
+
+    if (qemu_loglevel_mask(CPU_LOG_RESET)) {
+        qemu_log("CPU Reset (CPU %d)\n", env->cpu_index);
+        log_cpu_state(env, 0);
+    }
+
+    mcc->parent_reset(s);
+
+    tlb_flush(env, 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;
+#else
+    mmu_init(&env->mmu);
+#endif
+}
+
+static void nios2_cpu_initfn(Object *obj)
+{
+    Nios2CPU *cpu = NIOS2_CPU(obj);
+    CPUNios2State *env = &cpu->env;
+
+    cpu_exec_init(env);
+}
+
+static void nios2_cpu_class_init(ObjectClass *oc, void *data)
+{
+    CPUClass *cc = CPU_CLASS(oc);
+    Nios2CPUClass *mcc = NIOS2_CPU_CLASS(oc);
+
+    mcc->parent_reset = cc->reset;
+    cc->reset = nios2_cpu_reset;
+}
+
+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..0ee22e6
--- /dev/null
+++ b/target-nios2/cpu.h
@@ -0,0 +1,259 @@ 
+/*
+ * 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 "config.h"
+#include "qemu-common.h"
+
+#define TARGET_LONG_BITS 32
+
+#define CPUArchState struct CPUNios2State
+
+#include "cpu-defs.h"
+#include "softfloat.h"
+struct CPUNios2State;
+typedef struct CPUNios2State CPUNios2State;
+#if !defined(CONFIG_USER_ONLY)
+#include "mmu.h"
+#endif
+
+#define TARGET_HAS_ICE 1
+
+/* Configuration options for Nios II */
+#define RESET_ADDRESS         0x00000000
+#define EXCEPTION_ADDRESS     0x00000004
+#define FAST_TLB_MISS_ADDRESS 0x00000008
+
+
+#define ELF_MACHINE EM_ALTERA_NIOS2
+
+/* GP regs + CR regs + PC */
+#define NUM_CORE_REGS (32 + 32 + 1)
+
+/* General purpose egister 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_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_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
+
+typedef 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)
+    struct nios2_mmu mmu;
+#endif
+
+    CPU_COMMON
+} CPUNios2State;
+
+#include "cpu-qom.h"
+
+extern Nios2CPU *cpu_nios2_init(const char *cpu_model);
+extern int cpu_nios2_exec(CPUNios2State *s);
+extern void cpu_nios2_close(CPUNios2State *s);
+extern void do_interrupt(CPUNios2State *env);
+extern int cpu_nios2_signal_handler(int host_signum, void *pinfo, void *puc);
+extern void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUNios2State *env);
+
+#define TARGET_PHYS_ADDR_SPACE_BITS 32
+#define TARGET_VIRT_ADDR_SPACE_BITS 32
+
+extern Nios2CPU *g_cpu;
+
+static inline CPUNios2State *cpu_init(const char *cpu_model)
+{
+    Nios2CPU *cpu = cpu_nios2_init(cpu_model);
+    if (cpu == NULL) {
+        return NULL;
+    }
+    return &cpu->env;
+}
+
+#define cpu_exec cpu_nios2_exec
+#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)
+{
+    return (env->regs[CR_STATUS] & CR_STATUS_U) ? MMU_USER_IDX :
+                                                  MMU_SUPERVISOR_IDX;
+}
+
+int cpu_nios2_handle_mmu_fault(CPUNios2State *env, target_ulong address,
+                               int rw, int mmu_idx, int is_softmmu);
+#define cpu_handle_mmu_fault cpu_nios2_handle_mmu_fault
+
+#if defined(CONFIG_USER_ONLY)
+static inline void cpu_clone_regs(CPUNios2State *env, target_ulong newsp)
+{
+    if (newsp) {
+        env->regs[R_SP] = newsp;
+    }
+    env->regs[R_RET0] = 0;
+}
+#endif
+
+static inline void cpu_set_tls(CPUNios2State *env, target_ulong newtls)
+{
+}
+
+static inline int cpu_interrupts_enabled(CPUNios2State *env)
+{
+    return env->regs[CR_STATUS] & CR_STATUS_PIE;
+}
+
+#include "cpu-all.h"
+
+static inline target_ulong cpu_get_pc(CPUNios2State *env)
+{
+    return env->regs[R_PC];
+}
+
+static inline void cpu_get_tb_cpu_state(CPUNios2State *env, target_ulong *pc,
+                                        target_ulong *cs_base, int *flags)
+{
+    *pc = env->regs[R_PC];
+    *cs_base = 0;
+    *flags = (env->regs[CR_STATUS] & (CR_STATUS_EH | CR_STATUS_U));
+}
+
+#if !defined(CONFIG_USER_ONLY)
+void cpu_unassigned_access(CPUNios2State *env1, target_phys_addr_t addr,
+                           int is_write, int is_exec, int is_asi, int size);
+#endif
+
+static inline bool cpu_has_work(CPUNios2State *env)
+{
+    return env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI);
+}
+
+#include "exec-all.h"
+
+static inline void cpu_pc_from_tb(CPUNios2State *env, TranslationBlock *tb)
+{
+    env->regs[R_PC] = tb->pc;
+}
+
+#endif /* CPU_NIOS2_H */
+
diff --git a/target-nios2/exec.h b/target-nios2/exec.h
new file mode 100644
index 0000000..cd4446b
--- /dev/null
+++ b/target-nios2/exec.h
@@ -0,0 +1,60 @@ 
+/*
+ * Altera Nios II execution defines
+ *
+ * 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 NIOS2_EXEC_H
+#define NIOS2_EXEC_H
+
+#include "dyngen-exec.h"
+
+register struct CPUNios2State *env asm(AREG0);
+
+#include "cpu.h"
+#include "exec-all.h"
+
+#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
+#endif
+
+static inline int cpu_has_work(CPUState *env)
+{
+    return env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI);
+}
+
+static inline int cpu_halted(CPUState *env)
+{
+    if (!env->halted) {
+        return 0;
+    }
+
+    if (cpu_has_work(env)) {
+        env->halted = 0;
+        return 0;
+    }
+
+    return EXCP_HALTED;
+}
+
+static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb)
+{
+    env->regs[R_PC] = tb->pc;
+}
+
+#endif /* NIOS2_EXEC_H */
+
diff --git a/target-nios2/helper.c b/target-nios2/helper.c
new file mode 100644
index 0000000..9b200c9
--- /dev/null
+++ b/target-nios2/helper.c
@@ -0,0 +1,291 @@ 
+/*
+ * 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 "config.h"
+#include "cpu.h"
+#include "exec-all.h"
+#include "host-utils.h"
+
+#if defined(CONFIG_USER_ONLY)
+
+void do_interrupt(CPUNios2State *env)
+{
+    env->exception_index = -1;
+    env->regs[R_EA] = env->regs[R_PC] + 4;
+}
+
+int cpu_nios2_handle_mmu_fault(CPUNios2State *env, target_ulong address,
+                               int rw, int mmu_idx, int is_softmmu)
+{
+    env->exception_index = 0xaa;
+    cpu_dump_state(env, stderr, fprintf, 0);
+    return 1;
+}
+
+#else /* !CONFIG_USER_ONLY */
+
+bool has_mmu = true;
+
+void do_interrupt(CPUNios2State *env)
+{
+    switch (env->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] |= (env->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]);
+            log_cpu_state(env, 0);
+
+            /* 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] |= (env->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] |= (env->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]);
+        log_cpu_state(env, 0);
+
+        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] |= (env->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] |= (env->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] |= (env->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] |= (env->exception_index & 0x1F) << 2;
+
+        env->regs[R_PC] = env->exception_addr;
+        break;
+
+    default:
+        cpu_abort(env, "unhandled exception type=%d\n",
+                  env->exception_index);
+        break;
+    }
+}
+
+static int cpu_nios2_handle_virtual_page(
+    CPUNios2State *env, target_ulong address, int rw, int mmu_idx)
+{
+    target_ulong vaddr, paddr;
+    struct nios2_mmu_lookup 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(env, vaddr, paddr, lu.prot,
+                         mmu_idx, TARGET_PAGE_SIZE);
+            return 0;
+        } else {
+            /* Permission violation */
+            env->exception_index = (rw == 0) ? EXCP_TLBR :
+                                               ((rw == 1) ? EXCP_TLBW :
+                                                            EXCP_TLBX);
+        }
+    } else {
+        env->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 cpu_nios2_handle_mmu_fault(CPUNios2State *env, target_ulong address,
+                               int rw, int mmu_idx, int is_softmmu)
+{
+    if (has_mmu) {
+        if (MMU_SUPERVISOR_IDX == mmu_idx) {
+            if (address >= 0xC0000000) {
+                /* Kernel physical page - TLB bypassed */
+                address &= TARGET_PAGE_MASK;
+                tlb_set_page(env, address, address, PAGE_BITS,
+                             mmu_idx, TARGET_PAGE_SIZE);
+            } else if (address >= 0x80000000) {
+                /* Kernel virtual page */
+                return cpu_nios2_handle_virtual_page(env, address, rw, mmu_idx);
+            } else {
+                /* User virtual page */
+                return cpu_nios2_handle_virtual_page(env, address, rw, mmu_idx);
+            }
+        } else {
+            if (address >= 0x80000000) {
+                /* Illegal access from user mode */
+                env->exception_index = EXCP_SUPERA;
+                env->regs[CR_BADADDR] = address;
+                return 1;
+            } else {
+                /* User virtual page */
+                return cpu_nios2_handle_virtual_page(env, address, rw, mmu_idx);
+            }
+        }
+    } else {
+        /* No MMU */
+        address &= TARGET_PAGE_MASK;
+        tlb_set_page(env, address, address, PAGE_BITS,
+                     mmu_idx, TARGET_PAGE_SIZE);
+    }
+
+    return 0;
+}
+
+target_phys_addr_t cpu_get_phys_page_debug(CPUNios2State *env,
+                                           target_ulong addr)
+{
+    target_ulong vaddr, paddr = 0;
+    struct nios2_mmu_lookup lu;
+    unsigned int hit;
+
+    if (has_mmu && (addr < 0xC0000000)) {
+        hit = mmu_translate(env, &lu, addr, 0, 0);
+        if (hit) {
+            vaddr = addr & TARGET_PAGE_MASK;
+            paddr = lu.paddr + vaddr - lu.vaddr;
+        } else {
+            cpu_abort(env, "cpu_get_phys_page debug MISS: %08X\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..69b6684
--- /dev/null
+++ b/target-nios2/helper.h
@@ -0,0 +1,45 @@ 
+/*
+ * 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>
+ */
+
+#include "def-helper.h"
+
+/* 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_1(raise_exception, void, i32)
+
+#if !defined(CONFIG_USER_ONLY)
+DEF_HELPER_1(mmu_read, i32, i32)
+DEF_HELPER_2(mmu_write, void, i32, i32)
+#endif
+
+DEF_HELPER_2(divs, i32, i32, i32)
+DEF_HELPER_2(divu, i32, i32, i32)
+
+DEF_HELPER_4(memalign, void, i32, i32, i32, i32)
+
+#include "def-helper.h"
+
diff --git a/target-nios2/instruction.c b/target-nios2/instruction.c
new file mode 100644
index 0000000..4dc06cd
--- /dev/null
+++ b/target-nios2/instruction.c
@@ -0,0 +1,1463 @@ 
+/*
+ * 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-all.h"
+#include "helper.h"
+
+#define GEN_HELPER 1
+#include "helper.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(tmp);
+    tcg_temp_free_i32(tmp);
+    dc->is_jmp = DISAS_UPDATE;
+}
+
+/*
+ * 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, int label)
+{
+    int 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);
+}
+
+static inline void gen_load_u(DisasContext *dc, TCGv dst, TCGv addr,
+                              unsigned int size)
+{
+    int mem_index = cpu_mmu_index(dc->env);
+
+    switch (size) {
+    case 1:
+        tcg_gen_qemu_ld8u(dst, addr, mem_index);
+        break;
+    case 2:
+        tcg_gen_qemu_ld16u(dst, addr, mem_index);
+        break;
+    case 4:
+        tcg_gen_qemu_ld32u(dst, addr, mem_index);
+        break;
+    default:
+        cpu_abort(dc->env, "Incorrect load size %d\n", size);
+        break;
+    }
+}
+
+static inline void gen_load_s(DisasContext *dc, TCGv dst, TCGv addr,
+                              unsigned int size)
+{
+    int mem_index = cpu_mmu_index(dc->env);
+
+    switch (size) {
+    case 1:
+        tcg_gen_qemu_ld8s(dst, addr, mem_index);
+        break;
+    case 2:
+        tcg_gen_qemu_ld16s(dst, addr, mem_index);
+        break;
+    case 4:
+        tcg_gen_qemu_ld32s(dst, addr, mem_index);
+        break;
+    default:
+        cpu_abort(dc->env, "Incorrect load size %d\n", size);
+        break;
+    }
+}
+
+static inline void gen_store(DisasContext *dc, TCGv val, TCGv addr,
+                             unsigned int size)
+{
+    int mem_index = cpu_mmu_index(dc->env);
+
+    switch (size) {
+    case 1:
+        tcg_gen_qemu_st8(val, addr, mem_index);
+        break;
+    case 2:
+        tcg_gen_qemu_st16(val, addr, mem_index);
+        break;
+    case 4:
+        tcg_gen_qemu_st32(val, addr, mem_index);
+        break;
+    default:
+        cpu_abort(dc->env, "Incorrect load size %d\n", size);
+        break;
+    }
+}
+
+/*
+ * 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);
+    tcg_gen_movi_tl(dc->cpu_R[R_PC],
+                    (dc->pc & 0xF0000000) | (instr->imm26 * 4));
+
+    dc->is_jmp = DISAS_JUMP;
+}
+
+/* PC <- (PC(31..28) : IMM26 * 4) */
+static void jmpi(DisasContext *dc, uint32_t code)
+{
+    J_TYPE(instr, code);
+
+    tcg_gen_movi_tl(dc->cpu_R[R_PC],
+                    (dc->pc & 0xF0000000) | (instr->imm26 * 4));
+
+    dc->is_jmp = DISAS_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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+    gen_load_u(dc, dc->cpu_R[instr->b], addr, 1);
+
+    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_movi_tl(imm, (int32_t)((int16_t)instr->imm16));
+
+    tcg_gen_add_tl(dc->cpu_R[instr->b], dc->cpu_R[instr->a], imm);
+
+    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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+    gen_store(dc, dc->cpu_R[instr->b], addr, 1);
+
+    tcg_temp_free(addr);
+}
+
+/* PC <- PC + 4 + IMM16 */
+static void br(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    tcg_gen_movi_tl(dc->cpu_R[R_PC],
+                    dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
+    dc->is_jmp = DISAS_JUMP;
+}
+
+/* rB <- @(Mem8[rA + @(IMM16)]) */
+static void ldb(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    TCGv addr = tcg_temp_new();
+    tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+    gen_load_s(dc, dc->cpu_R[instr->b], addr, 1);
+
+    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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+    gen_load_u(dc, dc->cpu_R[instr->b], addr, 2);
+
+    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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+    gen_store(dc, dc->cpu_R[instr->b], addr, 2);
+
+    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);
+
+    int l1 = gen_new_label();
+
+    tcg_gen_movi_tl(dc->cpu_R[R_PC],
+                    dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
+    tcg_gen_brcond_tl(TCG_COND_GE, dc->cpu_R[instr->a],
+                      dc->cpu_R[instr->b], l1);
+    tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
+    gen_set_label(l1);
+
+    dc->is_jmp = DISAS_JUMP;
+}
+
+/* rB <- Mem16[rA + @IMM16)] */
+static void ldh(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    TCGv addr = tcg_temp_new();
+    tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+    gen_load_s(dc, dc->cpu_R[instr->b], addr, 2);
+
+    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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+    gen_store(dc, dc->cpu_R[instr->b], addr, 4);
+
+    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);
+
+    int l1 = gen_new_label();
+
+    tcg_gen_movi_tl(dc->cpu_R[R_PC],
+                    dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
+    tcg_gen_brcond_tl(TCG_COND_LT, dc->cpu_R[instr->a],
+                      dc->cpu_R[instr->b], l1);
+    tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
+    gen_set_label(l1);
+
+    dc->is_jmp = DISAS_JUMP;
+}
+
+/* rB <- @(Mem32[rA + @(IMM16)]) */
+static void ldw(DisasContext *dc, uint32_t code)
+{
+    I_TYPE(instr, code);
+
+    TCGv addr = tcg_temp_new();
+    tcg_gen_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+    gen_load_u(dc, dc->cpu_R[instr->b], addr, 4);
+
+    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);
+
+    int l1 = gen_new_label();
+
+    tcg_gen_movi_tl(dc->cpu_R[R_PC],
+                    dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
+    tcg_gen_brcond_tl(TCG_COND_NE, dc->cpu_R[instr->a],
+                      dc->cpu_R[instr->b], l1);
+    tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
+    gen_set_label(l1);
+
+    dc->is_jmp = DISAS_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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+    gen_load_u(dc, dc->cpu_R[instr->b], addr, 1);
+
+    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_movi_tl(imm, (int32_t)((int16_t)instr->imm16));
+    tcg_gen_mul_i32(dc->cpu_R[instr->b], dc->cpu_R[instr->a], imm);
+    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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+    gen_store(dc, dc->cpu_R[instr->b], addr, 1);
+
+    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);
+
+    int l1 = gen_new_label();
+
+    tcg_gen_movi_tl(dc->cpu_R[R_PC],
+                    dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
+    tcg_gen_brcond_tl(TCG_COND_EQ, dc->cpu_R[instr->a],
+                      dc->cpu_R[instr->b], l1);
+    tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
+    gen_set_label(l1);
+
+    dc->is_jmp = DISAS_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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+    gen_load_s(dc, dc->cpu_R[instr->b], addr, 1);
+
+    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],
+                        (int32_t)((int16_t)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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+    gen_load_u(dc, dc->cpu_R[instr->b], addr, 2);
+
+    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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+    gen_store(dc, dc->cpu_R[instr->b], addr, 2);
+
+    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);
+
+    int l1 = gen_new_label();
+
+    tcg_gen_movi_tl(dc->cpu_R[R_PC],
+                    dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
+    tcg_gen_brcond_tl(TCG_COND_GEU, dc->cpu_R[instr->a],
+                      dc->cpu_R[instr->b], l1);
+    tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
+    gen_set_label(l1);
+
+    dc->is_jmp = DISAS_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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+    gen_load_s(dc, dc->cpu_R[instr->b], addr, 2);
+
+    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],
+                        (int32_t)((int16_t)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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+    gen_store(dc, dc->cpu_R[instr->b], addr, 4);
+
+    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);
+
+    int l1 = gen_new_label();
+
+    tcg_gen_movi_tl(dc->cpu_R[R_PC],
+                    dc->pc + 4 + (int16_t)(instr->imm16 & 0xFFFC));
+    tcg_gen_brcond_tl(TCG_COND_LTU, dc->cpu_R[instr->a],
+                      dc->cpu_R[instr->b], l1);
+    tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc + 4);
+    gen_set_label(l1);
+
+    dc->is_jmp = DISAS_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_movi_tl(addr, (int32_t)((int16_t)instr->imm16));
+    tcg_gen_add_tl(addr, dc->cpu_R[instr->a], addr);
+
+    gen_load_u(dc, dc->cpu_R[instr->b], addr, 4);
+
+    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 struct instruction 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);
+
+    TCGv t0 = tcg_temp_new();
+
+    tcg_gen_shri_tl(t0, dc->cpu_R[instr->a], 32 - instr->imm5);
+    tcg_gen_shli_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], instr->imm5);
+    tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->c], t0);
+
+    tcg_temp_free(t0);
+}
+
+/* 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();
+    TCGv t1 = tcg_temp_new();
+
+    tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
+    tcg_gen_movi_tl(t1, 32);
+    tcg_gen_sub_tl(t1, t1, t0);
+    tcg_gen_shri_tl(t1, dc->cpu_R[instr->a], t1);
+    tcg_gen_shli_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
+    tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->c], t1);
+
+    tcg_temp_free(t0);
+    tcg_temp_free(t1);
+}
+
+/* */
+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_trunc_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();
+    TCGv t1 = tcg_temp_new();
+
+    tcg_gen_andi_tl(t0, dc->cpu_R[instr->b], 31);
+    tcg_gen_movi_tl(t1, 32);
+    tcg_gen_sub_tl(t1, t1, t0);
+    tcg_gen_shli_tl(t1, dc->cpu_R[instr->a], t1);
+    tcg_gen_shri_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->a], t0);
+    tcg_gen_or_tl(dc->cpu_R[instr->c], dc->cpu_R[instr->c], t1);
+
+    tcg_temp_free(t0);
+    tcg_temp_free(t1);
+}
+
+/* */
+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_trunc_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_trunc_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);
+
+    int l1 = gen_new_label();
+    gen_check_supervisor(dc, l1);
+
+    switch (instr->imm5 + 32) {
+    case CR_PTEADDR:
+    case CR_TLBACC:
+    case CR_TLBMISC:
+    {
+        TCGv_i32 tmp = tcg_const_i32(instr->imm5 + 32);
+        gen_helper_mmu_read(dc->cpu_R[instr->c], tmp);
+        tcg_temp_free_i32(tmp);
+        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);
+
+    int l1 = gen_new_label();
+    gen_check_supervisor(dc, l1);
+
+    switch (instr->imm5 + 32) {
+    case CR_PTEADDR:
+    case CR_TLBACC:
+    case CR_TLBMISC:
+    {
+        TCGv_i32 tmp = tcg_const_i32(instr->imm5 + 32);
+        gen_helper_mmu_write(tmp, dc->cpu_R[instr->a]);
+        tcg_temp_free_i32(tmp);
+        break;
+    }
+
+    default:
+        tcg_gen_mov_tl(dc->cpu_R[instr->imm5 + 32], dc->cpu_R[instr->a]);
+        break;
+    }
+
+    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 struct instruction 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);
+}
+
+void handle_instruction(DisasContext *dc)
+{
+    uint32_t insn = ldl_code(dc->pc);
+    uint32_t 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..fe29933
--- /dev/null
+++ b/target-nios2/instruction.h
@@ -0,0 +1,290 @@ 
+/*
+ * 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 */
+struct i_type {
+    uint32_t op:6;
+    uint32_t imm16:16;
+    uint32_t b:5;
+    uint32_t a:5;
+} __attribute__((packed));
+
+union i_type_u {
+    uint32_t      v;
+    struct i_type i;
+};
+
+#define I_TYPE(instr, op) \
+    union i_type_u instr_u = { .v = op }; \
+    struct i_type *instr = &instr_u.i
+
+/* R-Type instruction */
+struct r_type {
+    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;
+} __attribute__((packed));
+
+union r_type_u {
+    uint32_t      v;
+    struct r_type i;
+};
+
+#define R_TYPE(instr, op) \
+    union r_type_u instr_u = { .v = op }; \
+    struct r_type *instr = &instr_u.i
+
+/* J-Type instruction */
+struct j_type {
+    uint32_t op:6;
+    uint32_t imm26:26;
+} __attribute__((packed));
+
+#define J_TYPE(instr, op) \
+    struct j_type *instr = (struct j_type *) &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 {
+    CPUNios2State           *env;
+    TCGv                    *cpu_R;
+    int                      is_jmp;
+    target_ulong             pc;
+    struct TranslationBlock *tb;
+} DisasContext;
+
+typedef void (*instruction_handler)(DisasContext *dc, uint32_t opcode);
+
+struct instruction {
+    const char *name;
+    instruction_handler handler;
+};
+
+/*
+ * Stringification macro (taken from Linux Kernel source code)
+ */
+
+/* Indirect stringification.  Doing two levels allows the parameter to be a
+ * macro itself.  For example, compile with -DFOO=bar, __stringify(FOO)
+ * converts to "bar".
+ */
+
+#define __stringify_1(x...)     #x
+#define __stringify(x...)       __stringify_1(x)
+
+#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);
+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..09198db
--- /dev/null
+++ b/target-nios2/machine.c
@@ -0,0 +1,33 @@ 
+/*
+ * 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 "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..a52fa1b
--- /dev/null
+++ b/target-nios2/mmu.c
@@ -0,0 +1,273 @@ 
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "config.h"
+#include "cpu.h"
+#include "exec-all.h"
+
+/* Define this to enable MMU debug messages */
+/* #define DEBUG_MMU */
+
+#ifdef DEBUG_MMU
+#define MMU_LOG(x) x
+#else
+#define MMU_LOG(x)
+#endif
+
+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,
+                           struct nios2_mmu_lookup *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++) {
+
+        struct nios2_tlb_entry *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)
+{
+    int idx;
+    MMU_LOG(qemu_log("TLB Flush PID %d\n", pid));
+
+    for (idx = 0; idx < env->mmu.tlb_num_entries; idx++) {
+        struct nios2_tlb_entry *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(env, vaddr);
+        }
+    }
+}
+
+void mmu_write(CPUNios2State *env, uint32_t rn, uint32_t v)
+{
+    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;
+            struct nios2_tlb_entry *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(env, 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;
+            struct nios2_tlb_entry *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(struct nios2_mmu *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 = (struct nios2_tlb_entry *)g_malloc0(
+        sizeof(struct nios2_tlb_entry) * 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++) {
+        struct nios2_tlb_entry *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' : '-');
+    }
+}
+
diff --git a/target-nios2/mmu.h b/target-nios2/mmu.h
new file mode 100644
index 0000000..01d67cf
--- /dev/null
+++ b/target-nios2/mmu.h
@@ -0,0 +1,49 @@ 
+/*
+ * 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>
+ */
+
+struct nios2_tlb_entry {
+    target_ulong tag;
+    target_ulong data;
+};
+
+struct nios2_mmu {
+    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;
+    struct nios2_tlb_entry *tlb;
+};
+
+struct nios2_mmu_lookup {
+    target_ulong vaddr;
+    target_ulong paddr;
+    int prot;
+};
+
+void mmu_flip_um(CPUNios2State *env, unsigned int um);
+unsigned int mmu_translate(CPUNios2State *env,
+                           struct nios2_mmu_lookup *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(struct nios2_mmu *mmu);
diff --git a/target-nios2/op_helper.c b/target-nios2/op_helper.c
new file mode 100644
index 0000000..37bac66
--- /dev/null
+++ b/target-nios2/op_helper.c
@@ -0,0 +1,125 @@ 
+/*
+ * 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 "dyngen-exec.h"
+#include "helper.h"
+#include "host-utils.h"
+
+#if !defined(CONFIG_USER_ONLY)
+#define MMUSUFFIX _mmu
+#define SHIFT 0
+#include "softmmu_template.h"
+#define SHIFT 1
+#include "softmmu_template.h"
+#define SHIFT 2
+#include "softmmu_template.h"
+#define SHIFT 3
+#include "softmmu_template.h"
+
+void tlb_fill(CPUNios2State *env1, target_ulong addr, int is_write, int mmu_idx,
+              uintptr_t retaddr)
+{
+    TranslationBlock *tb;
+    CPUNios2State *saved_env;
+    int ret;
+
+    saved_env = env;
+    env = env1;
+
+    ret = cpu_nios2_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
+    if (unlikely(ret)) {
+        if (retaddr) {
+            /* now we have a real cpu fault */
+            tb = tb_find_pc(retaddr);
+            if (tb) {
+                /* The PC is inside the translated code. It means that we have
+                   a virtual CPU fault */
+                cpu_restore_state(tb, env, retaddr);
+            }
+        }
+        cpu_loop_exit(env);
+    }
+    env = saved_env;
+}
+
+void helper_raise_exception(uint32_t index)
+{
+    env->exception_index = index;
+    cpu_loop_exit(env);
+}
+
+uint32_t helper_mmu_read(uint32_t rn)
+{
+    return mmu_read(env, rn);
+}
+
+void helper_mmu_write(uint32_t rn, uint32_t v)
+{
+    mmu_write(env, rn, v);
+}
+
+void helper_memalign(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(EXCP_UNALIGN);
+    }
+}
+
+void cpu_unassigned_access(CPUNios2State *env1, target_phys_addr_t addr,
+                           int is_write, int is_exec, int is_asi, int size)
+{
+    qemu_log("unassigned access to %08X\n", addr);
+}
+
+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
+
+#endif /* !CONFIG_USER_ONLY */
+
diff --git a/target-nios2/translate.c b/target-nios2/translate.c
new file mode 100644
index 0000000..5d0979f
--- /dev/null
+++ b/target-nios2/translate.c
@@ -0,0 +1,252 @@ 
+/*
+ * 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-all.h"
+#include "disas.h"
+#include "helper.h"
+#include "qemu-common.h"
+
+#include "instruction.h"
+
+#define GEN_HELPER 1
+#include "helper.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 "gen-icount.h"
+
+/* generate intermediate code for basic block 'tb'.  */
+static void gen_intermediate_code_internal(
+    CPUNios2State *env, TranslationBlock *tb, int search_pc)
+{
+    DisasContext dc1, *dc = &dc1;
+    int num_insns;
+    int max_insns;
+    uint32_t next_page_start;
+    int j, lj = -1;
+    uint16_t *gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
+
+    /* Initialize DC */
+    dc->env    = env;
+    dc->cpu_R  = cpu_R;
+    dc->is_jmp = DISAS_NEXT;
+    dc->pc     = tb->pc;
+    dc->tb     = tb;
+
+    /* Dump the CPU state to the log */
+    if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
+        qemu_log("--------------\n");
+        log_cpu_state(env, 0);
+    }
+
+    /* Set up instruction counts */
+    num_insns = 0;
+    max_insns = tb->cflags & CF_COUNT_MASK;
+    if (max_insns == 0) {
+        max_insns = CF_COUNT_MASK;
+    }
+    next_page_start = (tb->pc & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
+
+    gen_icount_start();
+    do {
+        /* Mark instruction start with associated PC */
+        if (search_pc) {
+            j = gen_opc_ptr - gen_opc_buf;
+            if (lj < j) {
+                lj++;
+                while (lj < j) {
+                    gen_opc_instr_start[lj++] = 0;
+                }
+            }
+            gen_opc_pc[lj] = dc->pc;
+            gen_opc_instr_start[lj] = 1;
+            gen_opc_icount[lj] = num_insns;
+        }
+
+        LOG_DIS("%8.8x:\t", dc->pc);
+
+        if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) {
+            gen_io_start();
+        }
+
+        /* Decode an instruction */
+        handle_instruction(dc);
+
+        dc->pc += 4;
+        num_insns++;
+
+        /* 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 && gen_opc_ptr < gen_opc_end &&
+             !env->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_icount_end(tb, num_insns);
+    *gen_opc_ptr = INDEX_op_end;
+
+    /* Mark instruction starts for the final generated instruction */
+    if (search_pc) {
+        j = gen_opc_ptr - gen_opc_buf;
+        lj++;
+        while (lj <= j) {
+            gen_opc_instr_start[lj++] = 0;
+        }
+    } else {
+        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(tb->pc, dc->pc - tb->pc, 0);
+        qemu_log("\nisize=%d osize=%td\n",
+                 dc->pc - tb->pc, gen_opc_ptr - gen_opc_buf);
+    }
+#endif
+}
+
+void gen_intermediate_code(CPUNios2State *env, struct TranslationBlock *tb)
+{
+    gen_intermediate_code_internal(env, tb, 0);
+}
+
+void gen_intermediate_code_pc(CPUNios2State *env, struct TranslationBlock *tb)
+{
+    gen_intermediate_code_internal(env, tb, 1);
+}
+
+void cpu_dump_state(CPUNios2State *env, FILE *f, fprintf_function cpu_fprintf,
+                    int flags)
+{
+    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");
+        }
+    }
+    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);
+    cpu_fprintf(f, "\n\n");
+}
+
+Nios2CPU *cpu_nios2_init(const char *cpu_model)
+{
+    Nios2CPU *cpu;
+    int i;
+
+    cpu = NIOS2_CPU(object_new(TYPE_NIOS2_CPU));
+
+    cpu->env.reset_addr = RESET_ADDRESS;
+    cpu->env.exception_addr = EXCEPTION_ADDRESS;
+    cpu->env.fast_tlb_miss_addr = FAST_TLB_MISS_ADDRESS;
+
+    cpu_reset(CPU(cpu));
+    qemu_init_vcpu(&cpu->env);
+
+    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(TCG_AREG0,
+                                      offsetof(CPUNios2State, regs[i]),
+                                      regnames[i]);
+    }
+
+#define GEN_HELPER 2
+#include "helper.h"
+
+  return cpu;
+}
+
+void restore_state_to_opc(CPUNios2State *env, TranslationBlock *tb, int pc_pos)
+{
+    env->regs[R_PC] = gen_opc_pc[pc_pos];
+}
+