diff --git a/target-xtensa/cpu.h b/target-xtensa/cpu.h
index 176d352..d8cd923 100644
--- a/target-xtensa/cpu.h
+++ b/target-xtensa/cpu.h
@@ -45,7 +45,67 @@
 #define TARGET_VIRT_ADDR_SPACE_BITS 32
 #define TARGET_PAGE_BITS 12
 
+enum {
+    /* Additional instructions */
+    XTENSA_OPTION_CODE_DENSITY,
+    XTENSA_OPTION_LOOP,
+    XTENSA_OPTION_EXTENDED_L32R,
+    XTENSA_OPTION_16_BIT_IMUL,
+    XTENSA_OPTION_32_BIT_IMUL,
+    XTENSA_OPTION_32_BIT_IDIV,
+    XTENSA_OPTION_MAC16,
+    XTENSA_OPTION_MISC_OP,
+    XTENSA_OPTION_COPROCESSOR,
+    XTENSA_OPTION_BOOLEAN,
+    XTENSA_OPTION_FP_COPROCESSOR,
+    XTENSA_OPTION_MP_SYNCHRO,
+    XTENSA_OPTION_CONDITIONAL_STORE,
+
+    /* Interrupts and exceptions */
+    XTENSA_OPTION_EXCEPTION,
+    XTENSA_OPTION_RELOCATABLE_VECTOR,
+    XTENSA_OPTION_UNALIGNED_EXCEPTION,
+    XTENSA_OPTION_INTERRUPT,
+    XTENSA_OPTION_HIGH_PRIORITY_INTERRUPT,
+    XTENSA_OPTION_TIMER_INTERRUPT,
+
+    /* Local memory */
+    XTENSA_OPTION_ICACHE,
+    XTENSA_OPTION_ICACHE_TEST,
+    XTENSA_OPTION_ICACHE_INDEX_LOCK,
+    XTENSA_OPTION_DCACHE,
+    XTENSA_OPTION_DCACHE_TEST,
+    XTENSA_OPTION_DCACHE_INDEX_LOCK,
+    XTENSA_OPTION_IRAM,
+    XTENSA_OPTION_IROM,
+    XTENSA_OPTION_DRAM,
+    XTENSA_OPTION_DROM,
+    XTENSA_OPTION_XLMI,
+    XTENSA_OPTION_HW_ALIGNMENT,
+    XTENSA_OPTION_MEMORY_ECC_PARITY,
+
+    /* Memory protection and translation */
+    XTENSA_OPTION_REGION_PROTECTION,
+    XTENSA_OPTION_REGION_TRANSLATION,
+    XTENSA_OPTION_MMU,
+
+    /* Other */
+    XTENSA_OPTION_WINDOWED_REGISTER,
+    XTENSA_OPTION_PROCESSOR_INTERFACE,
+    XTENSA_OPTION_MISC_SR,
+    XTENSA_OPTION_THREAD_POINTER,
+    XTENSA_OPTION_PROCESSOR_ID,
+    XTENSA_OPTION_DEBUG,
+    XTENSA_OPTIN_TRACE_PORT,
+};
+
+typedef struct XtensaConfig {
+    const char *name;
+    uint64_t options;
+} XtensaConfig;
+
 typedef struct CPUXtensaState {
+    const XtensaConfig *config;
     uint32_t regs[16];
     uint32_t pc;
     uint32_t sregs[256];
@@ -66,6 +126,11 @@ void do_interrupt(CPUXtensaState *s);
 int cpu_xtensa_signal_handler(int host_signum, void *pinfo, void *puc);
 void xtensa_cpu_list(FILE *f, fprintf_function cpu_fprintf);
 
+static inline int xtensa_option_enabled(const XtensaConfig *config, int opt)
+{
+    return (config->options & (((uint64_t)1) << (opt))) != 0;
+}
+
 static inline int cpu_mmu_index(CPUState *env)
 {
     return 0;
diff --git a/target-xtensa/helper.c b/target-xtensa/helper.c
index 7003259..c6ffcc9 100644
--- a/target-xtensa/helper.c
+++ b/target-xtensa/helper.c
@@ -39,12 +39,32 @@ void cpu_reset(CPUXtensaState *env)
     env->pc = 0;
 }
 
+static const XtensaConfig core_config[] = {
+    {
+        .name = "sample-xtensa-core",
+        .options = -1,
+    },
+};
+
 CPUXtensaState *cpu_xtensa_init(const char *cpu_model)
 {
     static int tcg_inited;
     CPUXtensaState *env;
+    const XtensaConfig *config = NULL;
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(core_config); ++i)
+        if (strcmp(core_config[i].name, cpu_model) == 0) {
+            config = core_config + i;
+            break;
+        }
+
+    if (config == NULL) {
+        return NULL;
+    }
 
     env = qemu_mallocz(sizeof(*env));
+    env->config = config;
     cpu_exec_init(env);
 
     if (!tcg_inited) {
@@ -59,8 +79,11 @@ CPUXtensaState *cpu_xtensa_init(const char *cpu_model)
 
 void xtensa_cpu_list(FILE *f, fprintf_function cpu_fprintf)
 {
-    cpu_fprintf(f, "Available CPUs:\n"
-            "  Xtensa core\n");
+    int i;
+    cpu_fprintf(f, "Available CPUs:\n");
+    for (i = 0; i < ARRAY_SIZE(core_config); ++i) {
+        cpu_fprintf(f, "  %s\n", core_config[i].name);
+    }
 }
 
 target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
diff --git a/target-xtensa/helpers.h b/target-xtensa/helpers.h
new file mode 100644
index 0000000..c298d74
--- /dev/null
+++ b/target-xtensa/helpers.h
@@ -0,0 +1,5 @@
+#include "def-helper.h"
+
+DEF_HELPER_1(exception, void, i32)
+
+#include "def-helper.h"
diff --git a/target-xtensa/op_helper.c b/target-xtensa/op_helper.c
index 43f7772..ecc2f6e 100644
--- a/target-xtensa/op_helper.c
+++ b/target-xtensa/op_helper.c
@@ -26,6 +26,7 @@
  */
 
 #include "exec.h"
+#include "helpers.h"
 
 #define MMUSUFFIX _mmu
 
@@ -49,3 +50,9 @@ void tlb_fill(target_ulong addr, int is_write, int mmu_idx, void *retaddr)
             PAGE_READ | PAGE_WRITE | PAGE_EXEC,
             mmu_idx, TARGET_PAGE_SIZE);
 }
+
+void HELPER(exception)(uint32_t excp)
+{
+    env->exception_index = excp;
+    cpu_loop_exit();
+}
diff --git a/target-xtensa/translate.c b/target-xtensa/translate.c
index e91d8a5..59a28fc 100644
--- a/target-xtensa/translate.c
+++ b/target-xtensa/translate.c
@@ -36,17 +36,462 @@
 #include "tcg-op.h"
 #include "qemu-log.h"
 
+#include "helpers.h"
+#define GEN_HELPER 1
+#include "helpers.h"
+
+typedef struct DisasContext {
+    const XtensaConfig *config;
+    TranslationBlock *tb;
+    uint32_t pc;
+    uint32_t next_pc;
+    int is_jmp;
+    int singlestep_enabled;
+} DisasContext;
+
+static TCGv_ptr cpu_env;
+static TCGv_i32 cpu_pc;
+static TCGv_i32 cpu_R[16];
+
+#include "gen-icount.h"
 
 void xtensa_translate_init(void)
 {
+    static const char * const regnames[] = {
+        "ar0", "ar1", "ar2", "ar3",
+        "ar4", "ar5", "ar6", "ar7",
+        "ar8", "ar9", "ar10", "ar11",
+        "ar12", "ar13", "ar14", "ar15",
+    };
+    int i;
+
+    cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
+    cpu_pc = tcg_global_mem_new_i32(TCG_AREG0,
+            offsetof(CPUState, pc), "pc");
+
+    for (i = 0; i < 16; i++) {
+        cpu_R[i] = tcg_global_mem_new_i32(TCG_AREG0,
+                offsetof(CPUState, regs[i]),
+                regnames[i]);
+    }
+}
+
+static inline int option_enabled(DisasContext *dc, int opt)
+{
+    return xtensa_option_enabled(dc->config, opt);
+}
+
+static void gen_exception(int excp)
+{
+    TCGv_i32 tmp = tcg_const_i32(excp);
+    gen_helper_exception(tmp);
+    tcg_temp_free(tmp);
+}
+
+static void gen_jump_slot(DisasContext *dc, TCGv dest, int slot)
+{
+    tcg_gen_mov_i32(cpu_pc, dest);
+    if (dc->singlestep_enabled) {
+        gen_exception(EXCP_DEBUG);
+    } else {
+        if (slot >= 0) {
+            tcg_gen_goto_tb(slot);
+            tcg_gen_exit_tb((tcg_target_long)dc->tb + slot);
+        } else {
+            tcg_gen_exit_tb(0);
+        }
+    }
+    dc->is_jmp = DISAS_UPDATE;
+}
+
+static void gen_jumpi(DisasContext *dc, uint32_t dest, int slot)
+{
+    TCGv_i32 tmp = tcg_const_i32(dest);
+    if (((dc->pc ^ dest) & TARGET_PAGE_MASK) != 0) {
+        slot = -1;
+    }
+    gen_jump_slot(dc, tmp, slot);
+    tcg_temp_free(tmp);
+}
+
+static void disas_xtensa_insn(DisasContext *dc)
+{
+#define HAS_OPTION(opt) do { \
+        if (!option_enabled(dc, opt)) { \
+            qemu_log("Option %d is not enabled %s:%d\n", \
+                    (opt), __FILE__, __LINE__); \
+            goto invalid_opcode; \
+        } \
+    } while (0)
+
+#ifdef TARGET_WORDS_BIGENDIAN
+#define _OP0 (((_b0) & 0xf0) >> 4)
+#define _OP1 (((_b2) & 0xf0) >> 4)
+#define _OP2 ((_b2) & 0xf)
+#define RRR_R ((_b1) & 0xf)
+#define RRR_S (((_b1) & 0xf0) >> 4)
+#define RRR_T ((_b0) & 0xf)
+#else
+#define _OP0 (((_b0) & 0xf))
+#define _OP1 (((_b2) & 0xf))
+#define _OP2 (((_b2) & 0xf0) >> 4)
+#define RRR_R (((_b1) & 0xf0) >> 4)
+#define RRR_S (((_b1) & 0xf))
+#define RRR_T (((_b0) & 0xf0) >> 4)
+#endif
+
+#define RRRN_R RRR_R
+#define RRRN_S RRR_S
+#define RRRN_T RRR_T
+
+#define RRI8_R RRR_R
+#define RRI8_S RRR_S
+#define RRI8_T RRR_T
+#define RRI8_IMM8 (_b2)
+#define RRI8_IMM8_SE ((((_b2) & 0x80) ? 0xffffff00 : 0) | RRI8_IMM8)
+
+#ifdef TARGET_WORDS_BIGENDIAN
+#define RI16_IMM16 (((_b1) << 8) | (_b2))
+#else
+#define RI16_IMM16 (((_b2) << 8) | (_b1))
+#endif
+
+#ifdef TARGET_WORDS_BIGENDIAN
+#define CALL_N (((_b0) & 0xc) >> 2)
+#define CALL_OFFSET ((((_b0) & 0x3) << 16) | ((_b1) << 8) | (_b2))
+#else
+#define CALL_N (((_b0) & 0x30) >> 4)
+#define CALL_OFFSET ((((_b0) & 0xc0) >> 6) | ((_b1) << 2) | ((_b2) << 10))
+#endif
+#define CALL_OFFSET_SE \
+    (((CALL_OFFSET & 0x20000) ? 0xfffc0000 : 0) | CALL_OFFSET)
+
+#define CALLX_N CALL_N
+#ifdef TARGET_WORDS_BIGENDIAN
+#define CALLX_M ((_b0) & 0x3)
+#else
+#define CALLX_M (((_b0) & 0xc0) >> 6)
+#endif
+#define CALLX_S RRR_S
+
+#define BRI12_M CALLX_M
+#define BRI12_S RRR_S
+#ifdef TARGET_WORDS_BIGENDIAN
+#define BRI12_IMM12 ((((_b1) & 0xf) << 8) | (_b2))
+#else
+#define BRI12_IMM12 ((((_b1) & 0xf0) >> 4) | ((_b2) << 4))
+#endif
+#define BRI12_IMM12_SE (((BRI12_IMM12 & 0x800) ? 0xfffff000 : 0) | BRI12_IMM12)
+
+#define BRI8_M BRI12_M
+#define BRI8_R RRI8_R
+#define BRI8_S RRI8_S
+#define BRI8_IMM8 RRI8_IMM8
+#define BRI8_IMM8_SE RRI8_IMM8_SE
+
+#define RSR_SR (_b1)
+
+    uint8_t _b0 = ldub_code(dc->pc);
+    uint8_t _b1 = ldub_code(dc->pc + 1);
+    uint8_t _b2 = ldub_code(dc->pc + 2);
+
+    if (_OP0 >= 8) {
+        dc->next_pc = dc->pc + 2;
+        HAS_OPTION(XTENSA_OPTION_CODE_DENSITY);
+    } else {
+        dc->next_pc = dc->pc + 3;
+    }
+
+    switch (_OP0) {
+    case 0: /*QRST*/
+        switch (_OP1) {
+        case 0: /*RST0*/
+            switch (_OP2) {
+            case 0: /*ST0*/
+                if ((RRR_R & 0xc) == 0x8) {
+                    HAS_OPTION(XTENSA_OPTION_BOOLEAN);
+                }
+
+                switch (RRR_R) {
+                case 0: /*SNM0*/
+                    break;
+
+                case 1: /*MOVSPw*/
+                    HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER);
+                    break;
+
+                case 2: /*SYNC*/
+                    break;
+
+                case 3:
+                    break;
+
+                }
+                break;
+
+            case 1: /*AND*/
+                tcg_gen_and_i32(cpu_R[RRR_R], cpu_R[RRR_S], cpu_R[RRR_T]);
+                break;
+
+            case 2: /*OR*/
+                tcg_gen_or_i32(cpu_R[RRR_R], cpu_R[RRR_S], cpu_R[RRR_T]);
+                break;
+
+            case 3: /*XOR*/
+                tcg_gen_xor_i32(cpu_R[RRR_R], cpu_R[RRR_S], cpu_R[RRR_T]);
+                break;
+
+            case 4: /*ST1*/
+                break;
+
+            case 5: /*TLB*/
+                break;
+
+            case 6: /*RT0*/
+                break;
+
+            case 7: /*reserved*/
+                break;
+
+            case 8: /*ADD*/
+                tcg_gen_add_i32(cpu_R[RRR_R], cpu_R[RRR_S], cpu_R[RRR_T]);
+                break;
+
+            case 9: /*ADD**/
+            case 10:
+            case 11:
+                {
+                    TCGv_i32 tmp = tcg_temp_new_i32();
+                    tcg_gen_shli_i32(tmp, cpu_R[RRR_S], _OP2 - 8);
+                    tcg_gen_add_i32(cpu_R[RRR_R], tmp, cpu_R[RRR_T]);
+                    tcg_temp_free(tmp);
+                }
+                break;
+
+            case 12: /*SUB*/
+                tcg_gen_sub_i32(cpu_R[RRR_R], cpu_R[RRR_S], cpu_R[RRR_T]);
+                break;
+
+            case 13: /*SUB**/
+            case 14:
+            case 15:
+                {
+                    TCGv_i32 tmp = tcg_temp_new_i32();
+                    tcg_gen_shli_i32(tmp, cpu_R[RRR_S], _OP2 - 12);
+                    tcg_gen_sub_i32(cpu_R[RRR_R], tmp, cpu_R[RRR_T]);
+                    tcg_temp_free(tmp);
+                }
+                break;
+            }
+            break;
+
+        case 1: /*RST1*/
+            break;
+
+        case 2: /*RST2*/
+            break;
+
+        case 3: /*RST3*/
+            break;
+
+        case 4: /*EXTUI*/
+        case 5:
+            break;
+
+        case 6: /*CUST0*/
+            break;
+
+        case 7: /*CUST1*/
+            break;
+
+        case 8: /*LSCXp*/
+            HAS_OPTION(XTENSA_OPTION_COPROCESSOR);
+            break;
+
+        case 9: /*LSC4*/
+            break;
+
+        case 10: /*FP0*/
+            HAS_OPTION(XTENSA_OPTION_FP_COPROCESSOR);
+            break;
+
+        case 11: /*FP1*/
+            HAS_OPTION(XTENSA_OPTION_FP_COPROCESSOR);
+            break;
+
+        default: /*reserved*/
+            break;
+        }
+        break;
+
+    case 1: /*L32R*/
+        {
+            TCGv_i32 tmp = tcg_const_i32(
+                    (0xfffc0000 | (RI16_IMM16 << 2)) +
+                    ((dc->pc + 3) & ~3));
+
+            /* no ext L32R */
+
+            tcg_gen_qemu_ld32u(cpu_R[RRR_T], tmp, 0);
+            tcg_temp_free(tmp);
+        }
+        break;
+
+    case 2: /*LSAI*/
+        break;
+
+    case 3: /*LSCIp*/
+        HAS_OPTION(XTENSA_OPTION_COPROCESSOR);
+        break;
+
+    case 4: /*MAC16d*/
+        HAS_OPTION(XTENSA_OPTION_MAC16);
+        break;
+
+    case 5: /*CALLN*/
+        switch (CALL_N) {
+        case 0: /*CALL0*/
+            tcg_gen_movi_i32(cpu_R[0], dc->next_pc);
+            gen_jumpi(dc, (dc->pc & ~3) + (CALL_OFFSET_SE << 2) + 4, 0);
+            break;
+
+        case 1: /*CALL4w*/
+        case 2: /*CALL8w*/
+        case 3: /*CALL12w*/
+            HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER);
+            break;
+        }
+        break;
+
+    case 6: /*SI*/
+        switch (CALL_N) {
+        case 0: /*J*/
+            gen_jumpi(dc, dc->pc + 4 + CALL_OFFSET_SE, 0);
+            break;
+
+        }
+        break;
+
+    case 7: /*B*/
+        break;
+
+    case 8: /*L32I.Nn*/
+        break;
+
+    case 9: /*S32I.Nn*/
+        break;
+
+    case 10: /*ADD.Nn*/
+        break;
+
+    case 11: /*ADDI.Nn*/
+        break;
+
+    case 12: /*ST2n*/
+        break;
+
+    case 13: /*ST3n*/
+        break;
+
+    default: /*reserved*/
+        break;
+    }
+
+    dc->pc = dc->next_pc;
+    return;
+
+invalid_opcode:
+    qemu_log("INVALID(pc = %08x)\n", dc->pc);
+    dc->pc = dc->next_pc;
+#undef HAS_OPTION
+}
+
+static void check_breakpoint(CPUState *env, DisasContext *dc)
+{
+    CPUBreakpoint *bp;
+
+    if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) {
+        QTAILQ_FOREACH(bp, &env->breakpoints, entry) {
+            if (bp->pc == dc->pc) {
+                tcg_gen_movi_i32(cpu_pc, dc->pc);
+                gen_exception(EXCP_DEBUG);
+                dc->is_jmp = DISAS_UPDATE;
+             }
+        }
+    }
+}
+
+static void gen_intermediate_code_internal(
+        CPUState *env, TranslationBlock *tb, int search_pc)
+{
+    DisasContext dc;
+    int insn_count = 0;
+    int j, lj = -1;
+    uint16_t *gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
+    int max_insns = tb->cflags & CF_COUNT_MASK;
+    uint32_t next_page_start =
+        (env->pc & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
+
+    if (max_insns == 0) {
+        max_insns = CF_COUNT_MASK;
+    }
+
+    dc.config = env->config;
+    dc.singlestep_enabled = env->singlestep_enabled;
+    dc.tb = tb;
+    dc.pc = env->pc;
+    dc.is_jmp = DISAS_NEXT;
+
+    gen_icount_start();
+
+    do {
+        check_breakpoint(env, &dc);
+
+        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] = insn_count;
+        }
+
+        if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) {
+            tcg_gen_debug_insn_start(dc.pc);
+        }
+
+        disas_xtensa_insn(&dc);
+        ++insn_count;
+        if (env->singlestep_enabled) {
+            tcg_gen_movi_i32(cpu_pc, dc.pc);
+            gen_exception(EXCP_DEBUG);
+            break;
+        }
+    } while (dc.is_jmp == DISAS_NEXT &&
+            insn_count < max_insns &&
+            dc.pc < next_page_start &&
+            gen_opc_ptr < gen_opc_end);
+
+    if (dc.is_jmp == DISAS_NEXT) {
+        gen_jumpi(&dc, dc.pc, 0);
+    }
+    gen_icount_end(tb, insn_count);
+    *gen_opc_ptr = INDEX_op_end;
+
 }
 
 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);
 }
 
 void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf,
