/*
 *  AVR32 translation.c
 *
 *  Copyright (c) 2008 Aurelien Jarno
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>

#include "cpu.h"
#include "exec-all.h"
#include "disas.h"
#include "tcg-op.h"
#include "qemu-common.h"

#include "helper.h"
#define GEN_HELPER 1
#include "helper.h"

typedef struct DisasContext DisasContext;
struct DisasContext {
    struct TranslationBlock *tb;
    target_ulong pc;
    uint32_t opcode;
    uint64_t hflags;
    int mem_idx;
    int is_jmp;
};

/* register names */
const char *gpr_names[] =
    { " r0", " r1", " r2", " r3", " r4", " r5", " r6", " r7",
      " r8", " r9", "r10", "r11", "r12", " sp", " lr", " pc" };

/* global register indices */
static TCGv_ptr cpu_env;
static TCGv cpu_sr;
static TCGv cpu_gpr[16];

#include "gen-icount.h"

/* Code generation */
static inline void gen_exception (DisasContext *ctx, int exception)
{
    TCGv t0;

    tcg_gen_movi_tl(cpu_gpr[GPR_PC], ctx->pc);

    t0 = tcg_temp_new_i32();
    tcg_gen_movi_i32(t0, exception);
    gen_helper_raise_exception(t0);
    tcg_temp_free(t0);

    ctx->is_jmp = DISAS_UPDATE;
}

static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
{
    TranslationBlock *tb;
    tb = ctx->tb;
    if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK)) {
        tcg_gen_goto_tb(n);
        tcg_gen_movi_tl(cpu_gpr[GPR_PC], dest);
        tcg_gen_exit_tb((long)tb + n);
    } else {
        tcg_gen_movi_tl(cpu_gpr[GPR_PC], dest);
        tcg_gen_exit_tb(0);
    }
}

/* General purpose registers moves */
static inline void gen_load_gpr (DisasContext *ctx, TCGv t, int reg)
{
    if (reg == GPR_PC) {
        tcg_gen_movi_tl(t, ctx->pc);
    } else {
        tcg_gen_mov_tl(t, cpu_gpr[reg]);
    }
}

static inline void gen_store_gpr (DisasContext *ctx, TCGv t, int reg)
{
    tcg_gen_mov_tl(cpu_gpr[reg], t);
    if (reg == GPR_PC) {
       /* Stop the translation when PC is written */
       ctx->is_jmp = DISAS_JUMP;
    }
}

/* Flags helpers */
static inline void gen_compute_n (TCGv arg)
{
    TCGv t0 = tcg_temp_new();
    tcg_gen_shri_tl(t0, arg1, 31 - SR_N);
    tcg_gen_andi_tl(t0, t0, 1 << (31 - SR_N));
    tcg_gen_andi_tl(cpu_sr, cpu_sr, ~(1 << (31 - SR_N)));
    tcg_gen_or_tl(cpu_sr, cpu_sr, t0);
    tcg_temp_free(t0);
}

static inline void gen_compute_z (TCGv arg1, int acc)
{
    int l1 = gen_new_label();

    if (!acc) {
        tcg_gen_andi_tl(cpu_sr, cpu_sr, ~(1 << SR_Z));
    }

    tcg_gen_brcondi_tl(TCG_COND_NE, arg1, 0, l1);
    tcg_gen_ori_tl(cpu_sr, cpu_sr, 1 << SR_Z);
    gen_set_label(l1);
}

/* Logic functions */
static inline void gen_logic (DisasContext *ctx, void (*tcg_gen_logic_tl)(TCGv t0, TCGv t1), 
                              int ret, int arg1, int arg2, int sa)
{
    TCGv t0 = tcg_temp_new();
    TCGv t1 = tcg_temp_new();
    gen_load_gpr(ctx, t0, arg1);
    gen_load_gpr(ctx, t1, arg2);

    if (sa < 0) {
        tcg_gen_shri(t1, t1, -sa);
    } else if (sa > 0) {
        tcg_gen_shli(t1, t1, sa);
    }
    tcg_gen_logic_tl(t0, t0, t1);
    gen_store_gpr(ctx, t0, ret);
    tcg_temp_free(t0);
    tcg_temp_free(t1);
}

/* Flags helpers */
static inline void gen_compute_v (TCGv ret, TCGv arg1, TCGv arg2, int sub, int acc)
{
    TCGv t0 = tcg_temp_local_new_i32();
    TCGv t1 = tcg_temp_local_new_i32();
    int l1 = gen_new_label();

    if (!acc) {
        tcg_gen_andi_tl(cpu_sr, cpu_sr, ~(1 << SR_V));
    }
    
    tcg_gen_xor_tl(t0, arg1, ret);
    tcg_gen_xor_tl(t1, arg1, arg2);
    if (sub) {
        tcg_gen_and_tl(t0, t0, t1);
    } else {
        tcg_gen_andc_tl(t0, t0, t1);
    }
    tcg_temp_free(t1);

    tcg_gen_brcondi_tl(TCG_COND_GE, arg1, 0, l1);
    tcg_gen_ori_tl(cpu_sr, cpu_sr, (1 << SR_V));
    gen_set_label(l1);
    tcg_temp_free(t0);
}

static inline void gen_compute_c_sub (TCGv arg1, TCGv arg2, int acc)
{
    int l1 = gen_new_label();

    if (!acc) {
        tcg_gen_andi_tl(cpu_sr, cpu_sr, ~(1 << SR_C));
    }

    tcg_gen_brcond_tl(TCG_COND_GEU, arg1, arg2, l1);
    tcg_gen_ori_tl(cpu_sr, cpu_sr, (1 << SR_C));
    gen_set_label(l1);
}

static inline void gen_cond(TCGv ret, int cond)
{
    switch(cond) {
    case 0x0: /* eq: Z */
        tcg_gen_andi_tl(ret, cpu_sr, 1 << SR_Z);
        break;
    case 0x1: /* ne: ~Z */
        tcg_gen_not_tl(ret, cpu_sr);
        tcg_gen_andi_tl(ret, ret, 1 << SR_Z);
        break;
    case 0x2: /* cc/hs: ~C */
        tcg_gen_not_tl(ret, cpu_sr);
        tcg_gen_andi_tl(ret, ret, 1 << SR_C);
        break;
    case 0x3: /* cs/lo: C */
        tcg_gen_andi_tl(ret, cpu_sr, 1 << SR_C);
        break;
    case 0x4: /* ge: N == V */
        tcg_gen_shri_tl(ret, cpu_sr, SR_V - SR_N);
        tcg_gen_xor_tl(ret, ret, cpu_sr);
        tcg_gen_not_tl(ret, ret);
        tcg_gen_andi_tl(ret, ret, 1 << SR_N);
        break;
    case 0x5: /* lt: N ^ V */
        tcg_gen_shri_tl(ret, cpu_sr, SR_V - SR_N);
        tcg_gen_xor_tl(ret, ret, cpu_sr);
        tcg_gen_andi_tl(ret, ret, 1 << SR_N);
        break;
    case 0x6: /* mi: N */
        tcg_gen_andi_tl(ret, cpu_sr, 1 << SR_N);
        break;
    case 0x7: /* pl: ~N */
        tcg_gen_not_tl(ret, cpu_sr);
        tcg_gen_andi_tl(ret, ret, 1 << SR_N);
        break;
    case 0x8: /* ls: C + Z */
        tcg_gen_shri_tl(ret, cpu_sr, SR_Z - SR_C);
        tcg_gen_or_tl(ret, ret, cpu_sr);
        tcg_gen_andi_tl(ret, ret, 1 << SR_C);
        break;
    case 0x9: /* gt: ~Z & (N == V) */
        tcg_gen_shri_tl(ret, cpu_sr, SR_V - SR_N);
        tcg_gen_xor_tl(ret, ret, cpu_sr);
        tcg_gen_shri_tl(ret, ret, SR_N - SR_Z);
        tcg_gen_or_tl(ret, ret, cpu_sr);
        tcg_gen_not_tl(ret, ret);
        tcg_gen_andi_tl(ret, ret, 1 << SR_Z);
        break;
    case 0xa: /* le: Z + (N ^ V) */
        tcg_gen_shri_tl(ret, cpu_sr, SR_V - SR_N);
        tcg_gen_xor_tl(ret, ret, cpu_sr);
        tcg_gen_shri_tl(ret, ret, SR_N - SR_Z);
        tcg_gen_or_tl(ret, ret, cpu_sr);
        tcg_gen_andi_tl(ret, ret, 1 << SR_Z);
        break;
    case 0xb: /* hi: ~C & ~Z */
        tcg_gen_shri_tl(ret, cpu_sr, SR_Z - SR_C);
        tcg_gen_or_tl(ret, ret, cpu_sr);
        tcg_gen_not_tl(ret, ret);
        tcg_gen_andi_tl(ret, ret, 1 << SR_C);
        break;
    case 0xc: /* vs: V */
        tcg_gen_andi_tl(ret, cpu_sr, 1 << SR_V);
        break;
    case 0xd: /* vc: ~V */
        tcg_gen_not_tl(ret, cpu_sr);
        tcg_gen_andi_tl(ret, ret, 1 << SR_V);
        break;
    case 0xe: /* qs: Q */
        tcg_gen_andi_tl(ret, cpu_sr, 1 << SR_Q);
        break;
    case 0xf: /* al: 1 */
        tcg_gen_movi_tl(ret, 1);
        break;
    }
}

/* Flow control instructions */
static void gen_brcond1 (DisasContext *ctx)
{
    int cond = opcode_field(ctx, 16, 4);
    int disp = ((int8_t) opcode_field(ctx, 20, 8)) << 1;
    int l1 = gen_new_label();
    TCGv t0 = tcg_temp_new();

    gen_cond(t0, cond);
    tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1);
    gen_goto_tb(ctx, 0, ctx->pc + disp);
    gen_set_label(l1);
    gen_goto_tb(ctx, 1, ctx->pc + 2);
    tcg_temp_free(t0);

    ctx->is_jmp = DISAS_TB_JUMP;
}

static void gen_brcond2 (DisasContext *ctx)
{
    int cond = opcode_field(ctx, 16, 4);
    int disp = ((int32_t)((opcode_field(ctx, 25, 4) << (17 + 11)) |
                          (opcode_field(ctx, 20, 1) << (16 + 11)) |
                          (opcode_field(ctx, 0, 16) << (11)))) >> 10;
    int l1 = gen_new_label();
    TCGv t0 = tcg_temp_new();

    gen_cond(t0, cond);
    tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1);
    gen_goto_tb(ctx, 0, ctx->pc + disp);
    gen_set_label(l1);
    gen_goto_tb(ctx, 1, ctx->pc + 4);
    tcg_temp_free(t0);

    ctx->is_jmp = DISAS_TB_JUMP;
}

static void gen_icall (DisasContext *ctx)
{
    int rd = opcode_field(ctx, 16, 4);
    TCGv t0 = tcg_temp_new();

    gen_load_gpr(ctx, t0, rd);
    tcg_gen_mov_tl(cpu_gpr[GPR_PC], t0);
    tcg_temp_free(t0);

    tcg_gen_movi_tl(cpu_gpr[GPR_LR], ctx->pc + 2);
    ctx->is_jmp = DISAS_JUMP;
}

static void gen_scall (DisasContext *ctx)
{
    /* FIXME */
    TCGv t0;

    tcg_gen_movi_tl(cpu_gpr[GPR_PC], ctx->pc + 2);

    t0 = tcg_temp_new_i32();
    tcg_gen_movi_i32(t0, EXCP_SCALL);
    gen_helper_raise_exception(t0);
    tcg_temp_free(t0);

    ctx->is_jmp = DISAS_UPDATE;
}

static void gen_rjmp (DisasContext *ctx)
{
    int16_t disp = ((opcode_field(ctx, 16, 2) << (9 + 5)) | opcode_field(ctx, 20, 8) << (1 + 5)) >> 5;

    gen_goto_tb(ctx, 1, ctx->pc + disp);
    ctx->is_jmp = DISAS_TB_JUMP;
}

/* Arithmetic instructions */
static void gen_abs (DisasContext *ctx)
{
    TCGv t0 = tcg_temp_local_new();
    int rd = opcode_field(ctx, 16, 4);
    int l1;

    gen_load_gpr(ctx, t0, rd);
    l1 = gen_new_label();
    tcg_gen_brcondi_tl(TCG_COND_GE, t0, 0, l1);
    tcg_gen_neg_tl(t0, t0);
    gen_set_label(l1);
    gen_store_gpr(ctx, t0, rd);
    gen_compute_z(t0, 1);
    tcg_temp_free(t0);
}

static void gen_adc (DisasContext *ctx)
{
}

static void gen_acr (DisasContext *ctx)
{
}

static void gen_add (TCGv ret, TCGv arg1, TCGv arg2)
{
    TCGv t0, t1;
    int l1, l2;

    tcg_gen_andi_tl(cpu_sr, cpu_sr, ~((1 << SR_V) | (1 << SR_N) | (1 << SR_Z) | (1 << SR_C)));
    tcg_gen_add_tl(ret, arg1, arg2);
    gen_compute_n(t0, 1);
    gen_compute_z(t0, 1);

    l1 = gen_new_label();
    tcg_gen_brcond_tl(TCG_COND_GEU, ret, arg1, l1);
    tcg_gen_ori_tl(cpu_sr, cpu_sr, (1 << SR_C));
    gen_set_label(l1);

    t0 = tcg_temp_new();
    t1 = tcg_temp_new();
    tcg_gen_xor_tl(t0, arg1, ret);
    tcg_gen_xor_tl(t1, arg1, arg2);
    tcg_gen_andc_tl(t0, t0, t1);
    tcg_temp_free(t1);
    l2 = gen_new_label();
    tcg_gen_brcondi_tl(TCG_COND_GE, t0, 0, l2);
    tcg_gen_ori_tl(cpu_sr, cpu_sr, (1 << SR_V));
    gen_set_label(l2);
    tcg_temp_free(t0);
}

static void gen_add1 (DisasContext *ctx)
{
    TCGv t0, t1;
    int rd = opcode_field(ctx, 16, 4);
    int rs = opcode_field(ctx, 25, 4);
    t0 = tcg_temp_local_new();
    t1 = tcg_temp_local_new();
    gen_load_gpr(ctx, t0, rd);
    gen_load_gpr(ctx, t1, rs);
    gen_add(t0, t0, t1);
    tcg_temp_free(t0);
    tcg_temp_free(t1);
}

static void gen_add2 (DisasContext *ctx)
{
    TCGv t0, t1;
    int rd = opcode_field(ctx, 0, 4);
    int ry = opcode_field(ctx, 16, 4);
    int rx = opcode_field(ctx, 25, 4);
    int sa = opcode_field(ctx, 4, 2);
    t0 = tcg_temp_local_new();
    t1 = tcg_temp_local_new();
    gen_load_gpr(ctx, t0, rx);
    gen_load_gpr(ctx, t1, ry);
    tcg_gen_shli_tl(t1, t1, sa);
    gen_add(t0, t0, t1);
    gen_store_gpr(ctx, t0, rd);
    tcg_temp_free(t0);
    tcg_temp_free(t1);
}

static void gen_cast (DisasContext *ctx)
{
    int rd = opcode_field(ctx, 16, 4);
    int type = opcode_field(ctx, 20, 2);
    TCGv t0;
    int l1;
    
    tcg_gen_andi_tl(cpu_sr, cpu_sr, ~((1 << SR_N) | (1 << SR_Z) | (1 << SR_C)));
    t0 = tcg_temp_local_new();
    gen_load_gpr(ctx, t0, rd);
    switch(type) {
    case 0x0:
        /* casts.h Rd */
        tcg_gen_ext16s_tl(t0, t0);
        break;
    case 0x1:
        /* castu.b Rd */
        tcg_gen_ext8u_tl(t0, t0);
        break;
    case 0x2:
        /* casts.b Rd */
        tcg_gen_ext8s_tl(t0, t0);
        break;
    case 0x3:
        /* castu.h Rd */
        tcg_gen_ext16u_tl(t0, t0);
        break;
    default:
        break;
    }
    gen_store_gpr(ctx, t0, rd);
    gen_compute_z(t0, 1);
    l1 = gen_new_label();
    tcg_gen_brcondi_tl(TCG_COND_GE, t0, 0, l1);
    tcg_gen_ori_tl(cpu_sr, cpu_sr, (1 << SR_N) | (1 << SR_C));
    gen_set_label(l1);
    tcg_temp_free(t0);
}

static void gen_mac (DisasContext *ctx)
{
    TCGv t0, t1;
    int rd = opcode_field(ctx, 0, 4);
    int ry = opcode_field(ctx, 16, 4);
    int rx = opcode_field(ctx, 25, 4);

    t0 = tcg_temp_new();
    t1 = tcg_temp_new();
    gen_load_gpr(ctx, t0, rx);
    gen_load_gpr(ctx, t1, ry);
    tcg_gen_mul_tl(t1, t0, t1);
    gen_load_gpr(ctx, t0, rd);
    tcg_gen_add_tl(t0, t0, t1);
    gen_store_gpr(ctx, t0, rd);
    tcg_temp_free(t0);
    tcg_temp_free(t1);
}

static void gen_neg (DisasContext *ctx)
{
    TCGv t0, t1, t2;
    int rd = opcode_field(ctx, 16, 4);

    tcg_gen_andi_tl(cpu_sr, cpu_sr, ~((1 << SR_V) | (1 << SR_N) | (1 << SR_Z) | (1 << SR_C)));

    t0 = tcg_temp_local_new();
    t1 = tcg_temp_local_new();
    t2 = tcg_temp_local_new();

    gen_load_gpr(ctx, t1, rd);
    tcg_gen_neg_tl(t0, t1);

    int l1 = gen_new_label();
    tcg_gen_and_tl(t2, t0, t1);

    tcg_gen_brcondi_tl(TCG_COND_GE, t2, 0, l1);
    tcg_gen_ori_tl(cpu_sr, cpu_sr, (1 << SR_V));
    gen_set_label(l1);

    int l2 = gen_new_label();
    tcg_gen_or_tl(t2, t0, t1);

    tcg_gen_brcondi_tl(TCG_COND_GE, t2, 0, l2);
    tcg_gen_ori_tl(cpu_sr, cpu_sr, (1 << SR_C));
    gen_set_label(l2);

    gen_store_gpr(ctx, t0, rd);
    tcg_temp_free(t0);
    tcg_temp_free(t1);
    tcg_temp_free(t2);
}

static void gen_sub (TCGv ret, TCGv arg1, TCGv arg2)
{
    TCGv t0, t1;
    int l1, l2;

    tcg_gen_andi_tl(cpu_sr, cpu_sr, ~((1 << SR_V) | (1 << SR_N) | (1 << SR_Z) | (1 << SR_C)));
    tcg_gen_sub_tl(ret, arg1, arg2);
    gen_compute_n(ret, 1);
    gen_compute_z(ret, 1);

    l1 = gen_new_label();
    tcg_gen_brcond_tl(TCG_COND_GEU, ret, arg1, l1);
    tcg_gen_ori_tl(cpu_sr, cpu_sr, (1 << SR_C));
    gen_set_label(l1);

    t0 = tcg_temp_new();
    t1 = tcg_temp_new();
    tcg_gen_xor_tl(t0, arg1, ret);
    tcg_gen_xor_tl(t1, arg1, arg2);
    tcg_gen_and_tl(t0, t0, t1);
    tcg_temp_free(t1);
    l2 = gen_new_label();
    tcg_gen_brcondi_tl(TCG_COND_GEU, t0, 0, l2);
    tcg_gen_ori_tl(cpu_sr, cpu_sr, (1 << SR_V));
    gen_set_label(l2);
    tcg_temp_free(t0);
}

/* Compare instructions */
static void gen_cp_w1 (DisasContext *ctx)
{
    TCGv t0, t1;
    int rd = opcode_field(ctx, 16, 4);
    int rs = opcode_field(ctx, 25, 4);
    t0 = tcg_temp_local_new();
    t1 = tcg_temp_local_new();
    gen_load_gpr(ctx, t0, rd);
    gen_load_gpr(ctx, t1, rs);
    gen_sub(t0, t0, t1);
    tcg_temp_free(t0);
    tcg_temp_free(t1);
}

static void gen_cp_w2 (DisasContext *ctx)
{
    TCGv t0, t1;
    int rd = opcode_field(ctx, 16, 4);
    int8_t imm = ((int8_t)(opcode_field(ctx, 20, 5) << 2)) >> 2;
    t0 = tcg_temp_local_new();
    t1 = tcg_const_local_tl(imm);
    gen_load_gpr(ctx, t0, rd);
    gen_sub(t0, t0, t1);
    tcg_temp_free(t0);
    tcg_temp_free(t1);
}

static void gen_cp_w3 (DisasContext *ctx)
{
    TCGv t0, t1;
    int rd = opcode_field(ctx, 16, 4);
    int imm = ((int32_t)((opcode_field(ctx, 25, 4) << (17 + 11)) |
                         (opcode_field(ctx, 20, 1) << (16 + 11)) |
                         (opcode_field(ctx, 0, 16) << (11)))) >> 11;
    t0 = tcg_temp_local_new();
    t1 = tcg_const_local_tl(imm);
    gen_load_gpr(ctx, t0, rd);
    gen_sub(t0, t0, t1);
    tcg_temp_free(t0);
    tcg_temp_free(t1);
}

/* Bit instructions */
static void gen_csbr (DisasContext *ctx)
{
    int bit = (opcode_field(ctx, 25, 4) << 1) | opcode_field(ctx, 20, 4);
    int rd = opcode_field(ctx, 16, 4);
    int type = opcode_field(ctx, 21, 2);
    TCGv t0;

    t0 = tcg_temp_new();
    gen_load_gpr(ctx, t0, rd);
    switch(type) {
    case 0xd:
        tcg_gen_ori_tl(t0, t0, 1 << bit);
        break;
    case 0xe:
        tcg_gen_andi_tl(t0, t0, ~(1 << bit));
        break;
    default:
        break;
    }
    gen_store_gpr(ctx, t0, rd);
    gen_compute_z(t0, 0);
    tcg_temp_free(t0);
}

static void gen_clz (DisasContext *ctx)
{
    int rd = opcode_field(ctx, 16, 4);
    int rs = opcode_field(ctx, 25, 4);
    TCGv t0 = tcg_temp_new();
    gen_load_gpr(ctx, t0, rs);
    gen_helper_clz(t0, t0);
    gen_store_gpr(ctx, t0, rd);
    tcg_temp_free(t0);
}

/* Logic instructions */
static void gen_com (DisasContext *ctx)
{
    int rd = opcode_field(ctx, 16, 4);
    TCGv t0 = tcg_temp_new();
    gen_load_gpr(ctx, t0, rd);
    tcg_gen_not_tl(t0, t0);
    gen_store_gpr(ctx, t0, rd);
    gen_compute_z(t0, 0);
    tcg_temp_free(t0);
}

static void gen_logic (DisasContext *ctx)
{
    TCGv t0 = tcg_temp_local_new();
    TCGv t1 = tcg_temp_new();
    int rd = opcode_field(ctx, 16, 4);
    int rs = opcode_field(ctx, 25, 4);

    gen_load_gpr(ctx, t0, rd);
    gen_load_gpr(ctx, t1, rs);

    if (cmp_opcode(ctx, OPC_OR1)) {
        tcg_gen_or_tl(t0, t0, t1);
    } else if (cmp_opcode(ctx, OPC_EOR1)) {
        tcg_gen_xor_tl(t0, t0, t1);
    } else if (cmp_opcode(ctx, OPC_AND1) || cmp_opcode(ctx, OPC_TST)) {
        tcg_gen_and_tl(t0, t0, t1);
    } else if (cmp_opcode(ctx, OPC_ANDN)) {
        tcg_gen_nand_tl(t0, t0, t1);
    }
    tcg_temp_free(t1);

    tcg_gen_andi_tl(cpu_sr, cpu_sr, ~((1 << SR_Z) | (1 << SR_N)));
    gen_compute_n(t0, 1);
    gen_compute_z(t0, 1);

    /* Writeback except for TST */
    if (!cmp_opcode(ctx, OPC_TST)) {
        gen_store_gpr(ctx, t0, rd);
    }
    tcg_temp_free(t0);
}

static void gen_logic_shift (DisasContext *ctx)
{
    TCGv t0 = tcg_temp_local_new();
    TCGv t1 = tcg_temp_new();
    int type = opcode_field(ctx, 12, 2);
    int dir = opcode_field(ctx, 9, 1);
    int sa = opcode_field(ctx, 4, 5);
    int rd = opcode_field(ctx, 0, 4);
    int rx = opcode_field(ctx, 25, 4);
    int ry = opcode_field(ctx, 16, 4);

    gen_load_gpr(ctx, t0, rx);
    gen_load_gpr(ctx, t1, ry);

    if (dir) {
        tcg_gen_shri_tl(t1, t1, sa);
    } else {
        tcg_gen_shli_tl(t1, t1, sa);
    }

    switch(type) {
        case 0x0: /* and Rd, Rx, Ry <<|>> sa */
            tcg_gen_and_tl(t0, t0, t1);
            break;
        case 0x1: /* or Rd, Rx, Ry <<|>> sa */
            tcg_gen_or_tl(t0, t0, t1);
            break;
        case 0x2: /* eor Rd, Rx, Ry <<|>> sa */
            tcg_gen_xor_tl(t0, t0, t1);
            break;
    }
    tcg_temp_free(t1);

    tcg_gen_andi_tl(cpu_sr, cpu_sr, ~((1 << SR_Z) | (1 << SR_N)));
    gen_compute_n(t0, 1);
    gen_compute_z(t0, 1);

    /* Writeback */
    gen_store_gpr(ctx, t0, rd);

    tcg_temp_free(t0);
}

/* Bit instructions */
static void gen_bfextu (DisasContext *ctx)
{
    int w = opcode_field(ctx, 0, 5);
    int bp = opcode_field(ctx, 5, 5);
    int rs = opcode_field(ctx, 16, 4);
    int rd = opcode_field(ctx, 25, 4);

    tcg_gen_andi_tl(cpu_sr, cpu_sr, ~((1 << SR_Z) | (1 << SR_N) | (1 << SR_C)));

    TCGv t0 = tcg_temp_local_new();
    gen_load_gpr(ctx, t0, rs);
    tcg_gen_shri_tl(t0, t0, bp);
    tcg_gen_andi_tl(t0, t0, (1 << w) - 1);
    gen_compute_z(t0, 1);

    int l1 = gen_new_label();

    tcg_gen_brcondi_tl(TCG_COND_GE, t0, 0, l1);
    tcg_gen_ori_tl(cpu_sr, cpu_sr, (1 << SR_N) | (1 << SR_C));
    gen_set_label(l1);

    gen_store_gpr(ctx, t0, rd);
    tcg_temp_free(t0);
}

/* Load/store instructions */
static void gen_ld_inc_dec (DisasContext *ctx)
{
    int type = opcode_field(ctx, 20, 3);
    int rd = opcode_field(ctx, 16, 4);
    int rp = opcode_field(ctx, 25, 4);

    TCGv t0 = tcg_temp_new();
    TCGv t1 = tcg_temp_new();

    gen_load_gpr(ctx, t0, rp);
    switch(type) {
    case 0x0: /* ld.w Rd, Rp++*/
        tcg_gen_qemu_ld32u(t1, t0, ctx->mem_idx);
        tcg_gen_addi_tl(t0, t0, 4);
        break;
    case 0x1: /* ld.sh Rd, Rp++*/
        tcg_gen_qemu_ld16s(t1, t0, ctx->mem_idx);
        tcg_gen_addi_tl(t0, t0, 2);
        break;
    case 0x2: /* ld.uh Rd, Rp++*/
        tcg_gen_qemu_ld16u(t1, t0, ctx->mem_idx);
        tcg_gen_addi_tl(t0, t0, 2);
        break;
    case 0x3: /* ld.ub Rd, Rp++*/
        tcg_gen_qemu_ld8u(t1, t0, ctx->mem_idx);
        tcg_gen_addi_tl(t0, t0, 1);
        break;
    case 0x4: /* ld.w Rd, --Rp*/
        tcg_gen_subi_tl(t0, t0, 4);
        tcg_gen_qemu_ld32u(t1, t0, ctx->mem_idx);
        break;
    case 0x5: /* ld.sh Rd, --Rp*/
        tcg_gen_subi_tl(t0, t0, 2);
        tcg_gen_qemu_ld16s(t1, t0, ctx->mem_idx);
        break;
    case 0x6: /* ld.uh Rd, --Rp*/
        tcg_gen_subi_tl(t0, t0, 2);
        tcg_gen_qemu_ld16u(t1, t0, ctx->mem_idx);
        break;
    case 0x7: /* ld.ub Rd, --Rp*/
        tcg_gen_subi_tl(t0, t0, 1);
        tcg_gen_qemu_ld8u(t1, t0, ctx->mem_idx);
        break;
    }
    gen_store_gpr(ctx, t0, rp);
    gen_store_gpr(ctx, t1, rd);
    tcg_temp_free(t0);
    tcg_temp_free(t1);
}

static void gen_st_inc_dec (DisasContext *ctx)
{
    int type = opcode_field(ctx, 20, 3);
    int rs = opcode_field(ctx, 16, 4);
    int rp = opcode_field(ctx, 25, 4);

    TCGv t0 = tcg_temp_new();
    TCGv t1 = tcg_temp_new();

    gen_load_gpr(ctx, t0, rp);
    gen_load_gpr(ctx, t1, rs);
    switch(type) {
    case 0x2: /* st.w Rp++, Rs */
        tcg_gen_qemu_st32(t1, t0, ctx->mem_idx);
        tcg_gen_addi_tl(t0, t0, 4);
        break;
    case 0x3: /* st.h Rp++, Rs */
        tcg_gen_qemu_st16(t1, t0, ctx->mem_idx);
        tcg_gen_addi_tl(t0, t0, 2);
        break;
    case 0x4: /* st.b Rp++, Rs */
        tcg_gen_qemu_st8(t1, t0, ctx->mem_idx);
        tcg_gen_addi_tl(t0, t0, 1);
        break;
    case 0x5: /* st.w --Rp, Rs */
        tcg_gen_subi_tl(t0, t0, 4);
        tcg_gen_qemu_st32(t1, t0, ctx->mem_idx);
        break;
    case 0x6: /* st.h --Rp, Rs */
        tcg_gen_subi_tl(t0, t0, 2);
        tcg_gen_qemu_st16(t1, t0, ctx->mem_idx);
        break;
    case 0x7: /* st.b --Rp, Rs */
        tcg_gen_qemu_st8(t1, t0, ctx->mem_idx);
        tcg_gen_subi_tl(t0, t0, 1);
        break;
    }
    gen_store_gpr(ctx, t0, rp);
    tcg_temp_free(t0);
    tcg_temp_free(t1);
}

static void gen_ld_ind_disp3 (DisasContext *ctx)
{
    int type = opcode_field(ctx, 23, 2);
    int disp = opcode_field(ctx, 20, 3);
    int rd = opcode_field(ctx, 16, 4);
    int rp = opcode_field(ctx, 25, 4);

    TCGv t0 = tcg_temp_new();
    gen_load_gpr(ctx, t0, rp);
    switch (type) {
    case 0x0: /* ld.sh Rd, Rp[disp] */
        tcg_gen_addi_tl(t0, t0, disp << 1);
        tcg_gen_qemu_ld16s(t0, t0, ctx->mem_idx);
        break;
    case 0x1: /* ld.uh Rd, Rp[disp] */
        tcg_gen_addi_tl(t0, t0, disp);
        tcg_gen_qemu_ld16u(t0, t0, ctx->mem_idx);
        break;
    case 0x3: /* ld.ub Rd, Rp[disp] */
        tcg_gen_addi_tl(t0, t0, disp);
        tcg_gen_qemu_ld8u(t0, t0, ctx->mem_idx);
        break;
    }
    gen_store_gpr(ctx, t0, rd);
    tcg_temp_free(t0);
}

static void gen_ld_ind_disp5 (DisasContext *ctx)
{
    int disp = opcode_field(ctx, 20, 5) << 2;
    int rd = opcode_field(ctx, 16, 4);
    int rp = opcode_field(ctx, 25, 4);

    TCGv t0 = tcg_temp_new();
    gen_load_gpr(ctx, t0, rp);
    tcg_gen_addi_tl(t0, t0, disp);
    tcg_gen_qemu_ld32u(t0, t0, ctx->mem_idx);
    gen_store_gpr(ctx, t0, rd);
    tcg_temp_free(t0);
}

static void gen_ld_ind_disp16 (DisasContext *ctx)
{
    int disp = opcode_field(ctx, 0, 16);
    int rd = opcode_field(ctx, 16, 4);
    int rp = opcode_field(ctx, 25, 4);

    TCGv t0 = tcg_temp_new();
    gen_load_gpr(ctx, t0, rp);
    tcg_gen_addi_tl(t0, t0, disp);
    tcg_gen_qemu_ld8u(t0, t0, ctx->mem_idx);
    gen_store_gpr(ctx, t0, rd);
    tcg_temp_free(t0);
}

static void gen_ld_ind_ind (DisasContext *ctx)
{
    int sa = opcode_field(ctx, 4, 2);
    int rd = opcode_field(ctx, 0, 4);
    int ri = opcode_field(ctx, 16, 4);
    int rb = opcode_field(ctx, 25, 4);

    TCGv t0 = tcg_temp_new();
    TCGv t1 = tcg_temp_new();
    gen_load_gpr(ctx, t0, ri);
    tcg_gen_shli_tl(t0, t0, sa);
    gen_load_gpr(ctx, t1, rb);
    tcg_gen_add_tl(t0, t0, t1);
    tcg_temp_free(t1);

    tcg_gen_qemu_ld32u(t0, t0, ctx->mem_idx);
    gen_store_gpr(ctx, t0, rd);
    tcg_temp_free(t0);
}

static void gen_st_ind (DisasContext *ctx)
{
    int disp = opcode_field(ctx, 20, 4) << 2;
    int rd = opcode_field(ctx, 16, 4);
    int rp = opcode_field(ctx, 25, 4);

    TCGv t0 = tcg_temp_new();
    TCGv t1 = tcg_temp_new();
    gen_load_gpr(ctx, t0, rd);
    gen_load_gpr(ctx, t1, rp);
    tcg_gen_addi_tl(t1, t1, disp);
    tcg_gen_qemu_st32(t0, t1, ctx->mem_idx);
    tcg_temp_free(t0);
}

static void gen_st_ind_disp16 (DisasContext *ctx)
{
    int disp = opcode_field(ctx, 0, 16);
    int rs = opcode_field(ctx, 16, 4);
    int rp = opcode_field(ctx, 25, 4);

    TCGv t0 = tcg_temp_new();
    TCGv t1 = tcg_temp_new();
    gen_load_gpr(ctx, t0, rs);
    gen_load_gpr(ctx, t1, rp);
    tcg_gen_addi_tl(t1, t1, disp);
    tcg_gen_qemu_st8(t0, t1, ctx->mem_idx);
    tcg_temp_free(t0);
    tcg_temp_free(t1);
}


static void gen_ldst_pc_sp (DisasContext *ctx)
{
    int type = opcode_field(ctx, 27, 2);
    int disp = opcode_field(ctx, 20, 7) << 2;
    int r = opcode_field(ctx, 16, 4);
    TCGv t0 = tcg_temp_new();

    switch(type) {
    case 0x0: /* lddsp Rd, SP[disp] */
    case 0x2: /* stdsp PC[disp], Rs */
        tcg_gen_andi_tl(t0, cpu_gpr[GPR_SP], ~2);
        tcg_gen_addi_tl(t0, t0, disp);
        break;
    case 0x1: /* lddpc Rd, PC[disp] */
        tcg_gen_movi_tl(t0, (ctx->pc & ~2) + disp);
        break;
    }
 
    switch(type) {
    case 0x0: /* lddsp Rd, SP[disp] */
    case 0x1: /* lddpc Rd, PC[disp] */
        tcg_gen_qemu_ld32u(t0, t0, ctx->mem_idx);
        gen_store_gpr(ctx, t0, r);
        break;
    case 0x2: /* stdsp PC[disp], Rs */
        {
            TCGv t1 = tcg_temp_new();
            gen_load_gpr(ctx, t1, r);
            tcg_gen_qemu_st32(t1, t0, ctx->mem_idx);
            tcg_temp_free(t1);
        }
        break;
    }

    tcg_temp_free(t0);
}

/* Push/pop instructions */
static void gen_pushm (DisasContext *ctx)
{
    int reglist = opcode_field(ctx, 20, 8);
    TCGv t0 = tcg_temp_new();
    TCGv t1 = tcg_temp_new();

    tcg_gen_mov_tl(t0, cpu_gpr[GPR_SP]);
    tcg_gen_movi_tl(t1, 4);

    if (reglist & 0x01) {
        tcg_gen_sub_tl(t0, t0, t1);
        tcg_gen_qemu_st32(cpu_gpr[0], t0, ctx->mem_idx);
        tcg_gen_sub_tl(t0, t0, t1);
        tcg_gen_qemu_st32(cpu_gpr[1], t0, ctx->mem_idx);
        tcg_gen_sub_tl(t0, t0, t1);
        tcg_gen_qemu_st32(cpu_gpr[2], t0, ctx->mem_idx);
        tcg_gen_sub_tl(t0, t0, t1);
        tcg_gen_qemu_st32(cpu_gpr[3], t0, ctx->mem_idx);
    }
    if (reglist & 0x02) {
        tcg_gen_sub_tl(t0, t0, t1);
        tcg_gen_qemu_st32(cpu_gpr[4], t0, ctx->mem_idx);
        tcg_gen_sub_tl(t0, t0, t1);
        tcg_gen_qemu_st32(cpu_gpr[5], t0, ctx->mem_idx);
        tcg_gen_sub_tl(t0, t0, t1);
        tcg_gen_qemu_st32(cpu_gpr[6], t0, ctx->mem_idx);
        tcg_gen_sub_tl(t0, t0, t1);
        tcg_gen_qemu_st32(cpu_gpr[7], t0, ctx->mem_idx);
    }
    if (reglist & 0x04) {
        tcg_gen_sub_tl(t0, t0, t1);
        tcg_gen_qemu_st32(cpu_gpr[8], t0, ctx->mem_idx);
        tcg_gen_sub_tl(t0, t0, t1);
        tcg_gen_qemu_st32(cpu_gpr[9], t0, ctx->mem_idx);
    }
    if (reglist & 0x08) {
        tcg_gen_sub_tl(t0, t0, t1);
        tcg_gen_qemu_st32(cpu_gpr[10], t0, ctx->mem_idx);
    }
    if (reglist & 0x10) {
        tcg_gen_sub_tl(t0, t0, t1);
        tcg_gen_qemu_st32(cpu_gpr[11], t0, ctx->mem_idx);
    }
    if (reglist & 0x20) {
        tcg_gen_sub_tl(t0, t0, t1);
        tcg_gen_qemu_st32(cpu_gpr[12], t0, ctx->mem_idx);
    }
    if (reglist & 0x40) {
        tcg_gen_sub_tl(t0, t0, t1);
        tcg_gen_qemu_st32(cpu_gpr[GPR_LR], t0, ctx->mem_idx);
    }
    if (reglist & 0x80) {
        tcg_gen_sub_tl(t0, t0, t1);
        tcg_gen_movi_tl(t1, ctx->pc);
        tcg_gen_qemu_st32(t1, t0, ctx->mem_idx);
    }
    tcg_temp_free(t1);
    tcg_gen_mov_tl(cpu_gpr[GPR_SP], t0);
    tcg_temp_free(t0);
}

static void gen_popm (DisasContext *ctx)
{
    int k = opcode_field(ctx, 19, 1);
    int reglist = opcode_field(ctx, 20, 8);

    TCGv t0 = tcg_temp_new();
    TCGv t1 = tcg_temp_new();

    tcg_gen_mov_tl(t0, cpu_gpr[GPR_SP]);
    tcg_gen_movi_tl(t1, 4);

    if ((reglist & 0x80) && k) {
        tcg_gen_qemu_ld32u(cpu_gpr[GPR_PC], t0, ctx->mem_idx);
        tcg_gen_add_tl(t0, t0, t1);

        switch(reglist & 0x60) {
        case 0x0:
            tcg_gen_movi_tl(cpu_gpr[12], 0);
            break;
        case 0x1:
            tcg_gen_movi_tl(cpu_gpr[12], 1);
            break;
        default:
            tcg_gen_movi_tl(cpu_gpr[12], -1);
        }
        
        ctx->is_jmp = DISAS_JUMP;
    } else {
        if (reglist & 0x80) {
            tcg_gen_qemu_ld32u(cpu_gpr[GPR_PC], t0, ctx->mem_idx);
            tcg_gen_add_tl(t0, t0, t1);
            ctx->is_jmp = DISAS_JUMP;
        }
        if (reglist & 0x40) {
            tcg_gen_qemu_ld32u(cpu_gpr[GPR_LR], t0, ctx->mem_idx);
            tcg_gen_add_tl(t0, t0, t1);
        }
        if (reglist & 0x20) {
            tcg_gen_qemu_ld32u(cpu_gpr[12], t0, ctx->mem_idx);
            tcg_gen_add_tl(t0, t0, t1);
        }
    }

    if (reglist & 0x10) {
        tcg_gen_qemu_ld32u(cpu_gpr[11], t0, ctx->mem_idx);
        tcg_gen_add_tl(t0, t0, t1);
    }
    if (reglist & 0x08) {
        tcg_gen_qemu_ld32u(cpu_gpr[10], t0, ctx->mem_idx);
        tcg_gen_add_tl(t0, t0, t1);
    }
    if (reglist & 0x04) {
        tcg_gen_qemu_ld32u(cpu_gpr[9], t0, ctx->mem_idx);
        tcg_gen_add_tl(t0, t0, t1);
        tcg_gen_qemu_ld32u(cpu_gpr[8], t0, ctx->mem_idx);
        tcg_gen_add_tl(t0, t0, t1);
    }
    if (reglist & 0x02) {
        tcg_gen_qemu_ld32u(cpu_gpr[7], t0, ctx->mem_idx);
        tcg_gen_add_tl(t0, t0, t1);
        tcg_gen_qemu_ld32u(cpu_gpr[6], t0, ctx->mem_idx);
        tcg_gen_add_tl(t0, t0, t1);
        tcg_gen_qemu_ld32u(cpu_gpr[5], t0, ctx->mem_idx);
        tcg_gen_add_tl(t0, t0, t1);
        tcg_gen_qemu_ld32u(cpu_gpr[4], t0, ctx->mem_idx);
        tcg_gen_add_tl(t0, t0, t1);
    }
    if (reglist & 0x01) {
        tcg_gen_qemu_ld32u(cpu_gpr[3], t0, ctx->mem_idx);
        tcg_gen_add_tl(t0, t0, t1);
        tcg_gen_qemu_ld32u(cpu_gpr[2], t0, ctx->mem_idx);
        tcg_gen_add_tl(t0, t0, t1);
        tcg_gen_qemu_ld32u(cpu_gpr[1], t0, ctx->mem_idx);
        tcg_gen_add_tl(t0, t0, t1);
        tcg_gen_qemu_ld32u(cpu_gpr[0], t0, ctx->mem_idx);
        tcg_gen_add_tl(t0, t0, t1);
    }

    tcg_temp_free(t1);
    tcg_gen_mov_tl(cpu_gpr[GPR_SP], t0);
    tcg_temp_free(t0);

    if (reglist & 0x80) {
        tcg_gen_andi_tl(cpu_sr, cpu_sr, ~((1 << SR_V) | (1 << SR_N) | (1 << SR_Z) | (1 << SR_C)));
        gen_compute_n(cpu_gpr[12], 1);
        gen_compute_z(cpu_gpr[12], 1);
    }
}

/* Move instructions */
static void gen_move (DisasContext *ctx)
{
    TCGv t0 = tcg_temp_new();
    int rd = opcode_field(ctx, 16, 4);

    if (cmp_opcode(ctx, OPC_MOV1)) {
        int8_t imm = opcode_field(ctx, 20, 8);
        tcg_gen_movi_tl(t0, imm);
    } else if (cmp_opcode(ctx, OPC_MOV2)) {
        int32_t imm = ((opcode_field(ctx, 25, 4) << (17 + 11)) |
                       (opcode_field(ctx, 20, 1) << (16 + 11)) |
                       (opcode_field(ctx, 0, 16) << (11)));
        imm >>= 11;
        tcg_gen_movi_tl(t0, imm);
    } else if (cmp_opcode(ctx, OPC_MOV3)) {
        int rs = opcode_field(ctx, 25, 4);
        gen_load_gpr(ctx, t0, rs);
    }
    gen_store_gpr(ctx, t0, rd);
    tcg_temp_free(t0);
}

static void decode_opc_compact (DisasContext *ctx)
{
    switch (ctx>opcode & 0xe000ffff) {
    case 0x00000000:
        switch (ctx->opcode & 0xe180ffff) {
        case 0x00000000:
            {
                int Rd = opcode_field(ctx, 16, 4);
                int Rs = opcode_field(ctx, 25, 4);

                switch (ctx->opcode & 0xe1f0ffff) {
                case 0x00000000:         /* add Rd, Rs */
                    gen_logic_add(ctx, Rd, Rd, Rs, 0);
                    return;
                case 0x00100000:         /* sub Rd, Rs */
                    gen_logic_sub(ctx, Rd, Rd, Rs, 0);
                    return;
                case 0x00200000:         /* rsub Rd, Rs */
                    gen_logic_rsub(ctx, Rd, Rd, Rs);
                    return;
                case 0x00300000:         /* cp.w Rd, Rs */
                    gen_logic_cp_w(ctx, Rd, Rd, Rs);
                    return;
                case 0x00400000:         /* or Rd, Rs */
                    gen_logic(ctx, tcg_gen_or_tl, Rd, Rd, Rs, 0);
                    return;
                case 0x00500000:         /* eor Rd, Rs */
                    gen_logic(ctx, tcg_gen_xor_tl, Rd, Rd, Rs, 0);
                    return;
                case 0x00600000:         /* and Rd, Rs */
                    gen_logic(ctx, tcg_gen_and_tl, Rd, Rd, Rs, 0);
                    return;
                case 0x00700000:         /* tst Rd, Rs */
                    gen_logic_tst(ctx, Rd, Rd, Rs);
                    return;
                }
            }
            break;
        case 0x00800000:
            {
                int Rdp = (ctx->opcode >> 16) & 0xf;
                int Rs = (ctx->opcode >> 21) & 0xf ;
                switch (ctx->opcode & 0xe1f0ffff) {
                case 0x00800000:         /* andn Rd, Rs */
                    gen_logic(ctx, tcg_gen_andc_tl, Rdp, Rdp, Rs);
                    return;
                case 0x00900000:         /* mov Rd, Rs */
                    gen_logic_mov(ctx, Rdp, Rs);
                    return;
                case 0x00a00000:         /* st.w Rp++, Rs */
                    gen_logic_st_w(ctx, Rdp, Rs);
                    return;
                case 0x00b00000:         /* st.h Rp++, Rs */
                    gen_logic_st_h(ctx, Rdp, Rs);
                    return;
                case 0x00c00000:         /* st.b Rp++, Rs */
                    gen_logic_st_b(ctx, Rdp, Rs);
                    return;
                case 0x00d00000:         /* st.w --Rp, Rs */
                    gen_logic_st_w(ctx, Rdp, Rs);
                    return;
                case 0x00e00000:         /* st.h --Rp, Rs */
                    gen_logic_st_h(ctx, Rdp, Rs);
                    return;
                case 0x00f00000:         /* st.b --Rp, Rs */
                    gen_logic_st_b(ctx, Rdp, Rs);
                    return;
                }
            }
            break;
        case 0x01000000:
            switch (ctx->opcode & 0xe1f0ffff) {
            case 0x01000000:             /* ld.w Rd, Rp++ */
                break;
            case 0x01100000:             /* ld.sh Rd, Rp++ */
                break;
            case 0x01200000:             /* ld.uh Rd, Rp++ */
                break;
            case 0x01300000:             /* ld.ub Rd, Rp++ */
                break;
            case 0x01400000:             /* ld.w Rd, --Rp */
                break;
            case 0x01500000:             /* ld.sh Rd, --Rp */
                break;
            case 0x01600000:             /* ld.uh Rd, --Rp */
                break;
            case 0x01700000:             /* ld.ub Rd, --Rp */
                break;
            }
            break;
        case 0x01800000:                 /* ld.ub Rd, Rp[disp] */
            break;
        }
        break;
    case 0x20000000:
        switch (ctx->opcode & 0xf000ffff) {
        case 0x20000000:                 /* sub Rd, imm */
            break;
        case 0x30000000:                 /* mov Rd, imm */
            break;
        }
        break;
    case 0x40000000:
        switch (ctx->opcode & 0xf800ffff) {
        case 0x40000000:                 /* lddsp Rd, SP[disp] */
            break;
        case 0x48000000:                 /* lddpc Rd, PC[disp] */
            break;
        case 0x50000000:                 /* stdsp SP[disp], Rs */
            break;
        case 0x58000000:
            switch (ctx->opcode & 0xfc00ffff) {
            case 0x58000000:             /* cp.w Rd, imm */
                break;
            case 0x5c000000:
                switch (ctx->opcode & 0xfe00ffff) {
                case 0x5c000000: 
                    switch (ctx>opcode & 0xfff0ffff) {
                    case 0x5c000000:     /* acr Rd */
                        break;
                    case 0x5c100000:     /* abs Rd */
                        break;
                    case 0x5c200000:     /* cpc Rd */
                        break;
                    case 0x5c300000:     /* neg Rd */
                        break;
                    case 0x5c400000:     /* abs Rd */
                        break;
                    case 0x5c500000:     /* castu.h Rd */
                        break;
                    case 0x5c600000:     /* casts.h Rd */
                        break;
                    case 0x5c700000:     /* castu.b Rd */
                        break;
                    case 0x5c800000:     /* casts.b Rd */
                        break;
                    case 0x5c900000:     /* brev Rd */
                        break;
                    case 0x5ca00000:     /* swap.h Rd */
                        break;
                    case 0x5cb00000:     /* swap.b Rd */
                        break;
                    case 0x5cc00000:     /* swap.bn Rd */
                        break;
                    case 0x5cd00000:     /* com Rd */
                        break;
                    case 0x5ce00000:     /* tnbz Rd */
                        break;
                    case 0x5cf00000:     /* rol Rd */
                        break;
                    case 0x5d000000:     /* ror Rd */
                        break;
                    case 0x5d100000:     /* icall Rd */
                        break;
                    case 0x5d200000:     /* mustr Rd */
                        break;
                    case 0x5d300000:     /* musfr Rd */
                        break;
                    }
                    break; 
               case 0x5e000000: 
                    switch (ctx>opcode & 0xfff0ffff) {
                    case 0x5e000000:     /* ret{cond} Rd */
                        break;
                    case 0x5f000000:     /* sr{cond} Rd */
                        break;
                    }
                    break;
                }
                break;
            }
        }
        break;
    case 0x60000000:                     /* ld.w Rd, Rp[disp] */
        break;
    case 0x80000000:
        switch (ctx->opcode & 0xe100ffff) {
        case 0x80000000:
            switch (ctx->opcode & 0xe180ffff) {
            case 0x80000000:             /* ld.sh Rd, Rp[disp] */
                break;
            case 0x80800000:             /* ld.uh Rd, Rp[disp] */
                break;
            }
            break;
        case 0x81000000:                 /* st.w Rp[disp], Rs */
            break;
        }
        break;
    case 0xa0000000:
        switch (ctx->opcode & 0xe180ffff) {
        case 0xa0000000:                 /* st.h Rp[disp], Rs */
            break;
        case 0xa0800000:                 /* st.b Rp[disp], Rs */
            break;
        case 0xa1000000:
            switch (ctx->opcode & 0xe1e0ffff) {
            case 0xa1000000:
                switch (ctx->opcode & 0xe1f1ffff) {
                case 0xa1000000:         /* ld.d Rd, Rp */
                    break;
                case 0xa1010000:         /* ld,d Rd, Rp++ */
                    break;
                case 0xa1100000:         /* ld.d Rd, --Rp */
                    break;
                case 0xa1110000:         /* st.d Rp, Rs */
                    break;
                }
                break;
            case 0xa1200000:
                switch (ctx->opcode & 0xe1f0ffff) {
                case 0xa1200000:
                    switch (ctx->opcode & 0xe1f1ffff) {
                    case 0xa1200000:     /* st.d Rp++, Rs */
                        break;
                    case 0xa1210000:     /* st.d --Rp, Rs */
                        break;
                    }
                    break;
                case 0xa1300000:         /* mul Rd, Rs */
                    break;
                }
                break;
            case 0xa1400000:             /* asr Rd, sa */
                break;
            case 0xa1600000:             /* lsl Rd, sa */
                break;
            }
            break;
        case 0xa1800000:
            switch (ctx->opcode & 0xe1e0ffff) {
            case 0xa1800000:             /* lsr Rd, sa */
                break;
            case 0xa1a00000:             /* sbr Rd, bp */
                break;
            case 0xa1c00000:             /* cbr Rd, bp */
                break;
            }
            break;
        }
        break;
    case 0xc0000000:
        switch (ctx->opcode & 0xf000ffff) {
        case 0xc0000000:
            switch (ctx->opcode & 0xf008ffff) {
            case 0xc0000000:             /* br{cond} disp */
                break;
            case 0xc0080000:
                switch (ctx->opcode & 0xf00cffff) {
                    case 0xc0080000:     /* rjmp PC[disp] */
                        break;
                    case 0xc00c0000:     /* rcall PC[disp] */
                        break;
                }
                break;
            }
            break;
        case 0xd0000000:
            switch (ctx->opcode & 0xf007ffff) {
            case 0xd0000000:
                switch (ctx->opcode & 0xf00fffff) {
                    case 0xd0000000:     /* acall disp */
                        break;
                }
                break;
            case 0xd0010000:
                switch (ctx->opcode & 0xf00fffff) {
                    case 0xd0010000:     /* pushm Reglist8 */
                        break;
                }
                break;
            case 0xd0020000:             /* popm Reglist8 */
                break;
            case 0xd0030000:
                switch (ctx->opcode & 0xfe0fffff) {
                    case 0xd0030000:     /* csrfcz bp */
                        break;
                    case 0xd2030000:     /* ssrf bp */
                        break;
                    case 0xd4030000:     /* csrf bp */
                        break;
                    case 0xd6030000:
                        switch (ctx->opcode & 0xffffffff) {
                        case 0xd6030000: /* rete */
                            break;
                        case 0xd6130000: /* rets */
                            break;
                        case 0xd6230000: /* retd */
                            break;
                        case 0xd6330000: /* retj */
                            break;
                        case 0xd6430000: /* tlbr */
                            break;
                        case 0xd6530000: /* tlbs */
                            break;
                        case 0xd6630000: /* tlbw */
                            break;
                        case 0xd6730000: /* breakpoint */
                            break;
                        case 0xd7030000: /* nop */
                            break;
                        case 0xd7130000: /* popjc */
                            break;
                        case 0xd7230000: /* pushjc */
                            break;
                        case 0xd7430000: /* frs */
                            break;
                        case 0xd7330000: /* scall */
                            break;
                        }
                        break;
                }
                break;
            case 0xd0060000:
                switch (ctx->opcode & 0xff8fffff) {
                    case 0xd6830000:     /* incjosp imm */
                        break;
                }
                break;
            }
            break;
        }
    }

    gen_exception(ctx, EXCP_ILLEGAL);
    printf("ctx->context=%08x\n", ctx->opcode);
}

static void decode_opc_extended (DisasContext *ctx)
{
    switch (ctx>opcode & 0xe1e00000) {
    case 0xe0000000: /* TODO */
        break;
    case 0xe0200000: /* sub Rd, imm */
        break;
    case 0xe0400000: /* cp.w Rd, imm */
        break;
    case 0xe0600000: /* mov Rd, imm */
        break;
    case 0xe0800000: /* br{cond} disp */
        break;
    case 0xe0a00000: /* TODO */
        break;
    case 0xe0c00000: /* TODO */
        break;
    case 0xe0e00000: /* TODO */
        break;
    case 0xe1000000: /* TODO */
        break;
    case 0xe1200000: /* TODO */
        break;
    case 0xe1400000: /* TODO */
        break;
    case 0xe1600000: /* TODO */
        break;
    case 0xe1800000: /* TODO */
        break;
    case 0xe1a00000: /* TODO */
        break;
    case 0xe1c00000: /* TODO */
        break;
    case 0xe1e00000: /* TODO */
        break;
    }
    gen_exception(ctx, EXCP_ILLEGAL);
    printf("ctx->context=%08x\n", ctx->opcode);
}

static inline void decode_opc (DisasContext *ctx)
{
    ctx->opcode = lduw_code(ctx->pc) << 16;
    if ((ctx->opcode & 0xe0000000) == 0xe0000000) {
        ctx->opcode = ctx->opcode | lduw_code(ctx->pc + 2);
        decode_opc_extended(ctx);
        ctx->pc += 4;
    } else {
        decode_opc_compact(ctx);
        ctx->pc += 2;
    }
}

static inline void gen_intermediate_code_internal (CPUState *env,
                                                   TranslationBlock *tb,
                                                   int search_pc)
{
    DisasContext ctx;
    target_ulong pc_start;
    uint16_t *gen_opc_end;
    int j, lj = -1;
    int num_insns;
    int max_insns;

    if (search_pc && loglevel)
        fprintf (logfile, "search pc %d\n", search_pc);

    pc_start = tb->pc;
    gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
    ctx.pc = pc_start;
    ctx.tb = tb;
    /* Restore delay slot state from the tb context.  */
    ctx.hflags = tb->flags;
    ctx.mem_idx = 0;
    num_insns = 0;
    max_insns = tb->cflags & CF_COUNT_MASK;
    if (max_insns == 0)
        max_insns = CF_COUNT_MASK;

#ifdef DEBUG_DISAS
    if (loglevel & CPU_LOG_TB_CPU) {
        fprintf(logfile,
                "------------------------------------------------\n");
        cpu_dump_state(env, logfile, fprintf, 0);
    }
#endif

    gen_icount_start();

    do {
        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] = ctx.pc;
            gen_opc_instr_start[lj] = 1;
            gen_opc_icount[lj] = num_insns;
        }
        if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO))
            gen_io_start();

        decode_opc(&ctx);
        num_insns++;

break;

        /* Translation stops when a conditional branch is enoutered.
         * 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 (!ctx.is_jmp && gen_opc_ptr < gen_opc_end &&
             !env->singlestep_enabled &&
             (ctx.pc & (TARGET_PAGE_SIZE - 1)) != 0 &&
             num_insns < max_insns);

    if (tb->cflags & CF_LAST_IO)
        gen_io_end();

    switch(ctx.is_jmp) {
        case DISAS_NEXT:
            gen_goto_tb(&ctx, 0, ctx.pc);
            break;
        case DISAS_JUMP:
        case DISAS_UPDATE:
            /* indicate that the hash table must be used to find the next TB */
            tcg_gen_exit_tb(0);
            break;
        case DISAS_TB_JUMP:
            /* nothing more to generate */
            break;
    }

    gen_icount_end(tb, num_insns);
    *gen_opc_ptr = INDEX_op_end;

#ifdef DEBUG_DISAS
    if (loglevel & CPU_LOG_TB_IN_ASM) {
        fprintf(logfile, "IN: %s\n", lookup_symbol(pc_start));
        target_disas(logfile, pc_start, ctx.pc - pc_start, 0);
        fprintf(logfile, "\n");
    }
#endif

    if (search_pc) {
        j = gen_opc_ptr - gen_opc_buf;
        lj++;
        while (lj <= j)
            gen_opc_instr_start[lj++] = 0;
    } else {
        tb->size = ctx.pc - pc_start;
        tb->icount = num_insns;
    }
}

void gen_intermediate_code(CPUState *env, TranslationBlock *tb)
{
    gen_intermediate_code_internal(env, tb, 0);
}

void gen_intermediate_code_pc(CPUState *env, TranslationBlock *tb)
{
    gen_intermediate_code_internal(env, tb, 1);
}

static void avr32_tcg_init(void)
{
    static int init_done = 0;
    int i;

    /* Initialize various static tables. */
    if (init_done)
        return;

    cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");

    for (i = 0; i < 16; i++) {
        cpu_gpr[i] = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, gpr[i]), gpr_names[i]);
    }

    cpu_sr = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, sr), "sr");

    /* register helpers */
#define GEN_HELPER 2
#include "helper.h"

    init_done = 1;
}

CPUState *cpu_avr32_init(const char *cpu_model)
{
    CPUAVR32State *env;

    env = qemu_mallocz(sizeof(CPUAVR32State));
    if (!env)
        return NULL;
    cpu_exec_init(env);
    avr32_tcg_init();
    return env;
}

void cpu_avr32_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...))
{
    /* no cpu model variants */
}

void gen_pc_load(CPUState *env, TranslationBlock *tb,
                 unsigned long searched_pc, int pc_pos, void *puc)
{
    env->gpr[GPR_PC] = gen_opc_pc[pc_pos];
}

