]> git.proxmox.com Git - qemu.git/blobdiff - target-xtensa/translate.c
target-xtensa: implement extended L32R
[qemu.git] / target-xtensa / translate.c
index 9a19ca80e179a7082dcf2bac7ad8955427642416..07a737a90c793b2815bf06776a0f649ffbd4444e 100644 (file)
@@ -45,8 +45,18 @@ typedef struct DisasContext {
     TranslationBlock *tb;
     uint32_t pc;
     uint32_t next_pc;
+    int cring;
+    int ring;
+    uint32_t lbeg;
+    uint32_t lend;
+    TCGv_i32 litbase;
     int is_jmp;
     int singlestep_enabled;
+
+    bool sar_5bit;
+    bool sar_m32_5bit;
+    bool sar_m32_allocated;
+    TCGv_i32 sar_m32;
 } DisasContext;
 
 static TCGv_ptr cpu_env;
@@ -58,6 +68,20 @@ static TCGv_i32 cpu_UR[256];
 #include "gen-icount.h"
 
 static const char * const sregnames[256] = {
+    [LBEG] = "LBEG",
+    [LEND] = "LEND",
+    [LCOUNT] = "LCOUNT",
+    [SAR] = "SAR",
+    [LITBASE] = "LITBASE",
+    [SCOMPARE1] = "SCOMPARE1",
+    [WINDOW_BASE] = "WINDOW_BASE",
+    [WINDOW_START] = "WINDOW_START",
+    [EPC1] = "EPC1",
+    [DEPC] = "DEPC",
+    [EXCSAVE1] = "EXCSAVE1",
+    [PS] = "PS",
+    [EXCCAUSE] = "EXCCAUSE",
+    [EXCVADDR] = "EXCVADDR",
 };
 
 static const char * const uregnames[256] = {
@@ -110,6 +134,59 @@ static inline bool option_enabled(DisasContext *dc, int opt)
     return xtensa_option_enabled(dc->config, opt);
 }
 
+static void init_litbase(DisasContext *dc)
+{
+    if (dc->tb->flags & XTENSA_TBFLAG_LITBASE) {
+        dc->litbase = tcg_temp_local_new_i32();
+        tcg_gen_andi_i32(dc->litbase, cpu_SR[LITBASE], 0xfffff000);
+    }
+}
+
+static void reset_litbase(DisasContext *dc)
+{
+    if (dc->tb->flags & XTENSA_TBFLAG_LITBASE) {
+        tcg_temp_free(dc->litbase);
+    }
+}
+
+static void init_sar_tracker(DisasContext *dc)
+{
+    dc->sar_5bit = false;
+    dc->sar_m32_5bit = false;
+    dc->sar_m32_allocated = false;
+}
+
+static void reset_sar_tracker(DisasContext *dc)
+{
+    if (dc->sar_m32_allocated) {
+        tcg_temp_free(dc->sar_m32);
+    }
+}
+
+static void gen_right_shift_sar(DisasContext *dc, TCGv_i32 sa)
+{
+    tcg_gen_andi_i32(cpu_SR[SAR], sa, 0x1f);
+    if (dc->sar_m32_5bit) {
+        tcg_gen_discard_i32(dc->sar_m32);
+    }
+    dc->sar_5bit = true;
+    dc->sar_m32_5bit = false;
+}
+
+static void gen_left_shift_sar(DisasContext *dc, TCGv_i32 sa)
+{
+    TCGv_i32 tmp = tcg_const_i32(32);
+    if (!dc->sar_m32_allocated) {
+        dc->sar_m32 = tcg_temp_local_new_i32();
+        dc->sar_m32_allocated = true;
+    }
+    tcg_gen_andi_i32(dc->sar_m32, sa, 0x1f);
+    tcg_gen_sub_i32(cpu_SR[SAR], tmp, dc->sar_m32);
+    dc->sar_5bit = false;
+    dc->sar_m32_5bit = true;
+    tcg_temp_free(tmp);
+}
+
 static void gen_exception(int excp)
 {
     TCGv_i32 tmp = tcg_const_i32(excp);
@@ -117,6 +194,22 @@ static void gen_exception(int excp)
     tcg_temp_free(tmp);
 }
 
+static void gen_exception_cause(DisasContext *dc, uint32_t cause)
+{
+    TCGv_i32 tpc = tcg_const_i32(dc->pc);
+    TCGv_i32 tcause = tcg_const_i32(cause);
+    gen_helper_exception_cause(tpc, tcause);
+    tcg_temp_free(tpc);
+    tcg_temp_free(tcause);
+}
+
+static void gen_check_privilege(DisasContext *dc)
+{
+    if (dc->cring) {
+        gen_exception_cause(dc, PRIVILEGED_CAUSE);
+    }
+}
+
 static void gen_jump_slot(DisasContext *dc, TCGv dest, int slot)
 {
     tcg_gen_mov_i32(cpu_pc, dest);
@@ -148,13 +241,65 @@ static void gen_jumpi(DisasContext *dc, uint32_t dest, int slot)
     tcg_temp_free(tmp);
 }
 
+static void gen_callw_slot(DisasContext *dc, int callinc, TCGv_i32 dest,
+        int slot)
+{
+    TCGv_i32 tcallinc = tcg_const_i32(callinc);
+
+    tcg_gen_deposit_i32(cpu_SR[PS], cpu_SR[PS],
+            tcallinc, PS_CALLINC_SHIFT, PS_CALLINC_LEN);
+    tcg_temp_free(tcallinc);
+    tcg_gen_movi_i32(cpu_R[callinc << 2],
+            (callinc << 30) | (dc->next_pc & 0x3fffffff));
+    gen_jump_slot(dc, dest, slot);
+}
+
+static void gen_callw(DisasContext *dc, int callinc, TCGv_i32 dest)
+{
+    gen_callw_slot(dc, callinc, dest, -1);
+}
+
+static void gen_callwi(DisasContext *dc, int callinc, uint32_t dest, int slot)
+{
+    TCGv_i32 tmp = tcg_const_i32(dest);
+    if (((dc->pc ^ dest) & TARGET_PAGE_MASK) != 0) {
+        slot = -1;
+    }
+    gen_callw_slot(dc, callinc, tmp, slot);
+    tcg_temp_free(tmp);
+}
+
+static bool gen_check_loop_end(DisasContext *dc, int slot)
+{
+    if (option_enabled(dc, XTENSA_OPTION_LOOP) &&
+            !(dc->tb->flags & XTENSA_TBFLAG_EXCM) &&
+            dc->next_pc == dc->lend) {
+        int label = gen_new_label();
+
+        tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_SR[LCOUNT], 0, label);
+        tcg_gen_subi_i32(cpu_SR[LCOUNT], cpu_SR[LCOUNT], 1);
+        gen_jumpi(dc, dc->lbeg, slot);
+        gen_set_label(label);
+        gen_jumpi(dc, dc->next_pc, -1);
+        return true;
+    }
+    return false;
+}
+
+static void gen_jumpi_check_loop_end(DisasContext *dc, int slot)
+{
+    if (!gen_check_loop_end(dc, slot)) {
+        gen_jumpi(dc, dc->next_pc, slot);
+    }
+}
+
 static void gen_brcond(DisasContext *dc, TCGCond cond,
         TCGv_i32 t0, TCGv_i32 t1, uint32_t offset)
 {
     int label = gen_new_label();
 
     tcg_gen_brcond_i32(cond, t0, t1, label);
-    gen_jumpi(dc, dc->next_pc, 0);
+    gen_jumpi_check_loop_end(dc, 0);
     gen_set_label(label);
     gen_jumpi(dc, dc->pc + offset, 1);
 }
@@ -184,10 +329,61 @@ static void gen_rsr(DisasContext *dc, TCGv_i32 d, uint32_t sr)
     }
 }
 
+static void gen_wsr_lbeg(DisasContext *dc, uint32_t sr, TCGv_i32 s)
+{
+    gen_helper_wsr_lbeg(s);
+}
+
+static void gen_wsr_lend(DisasContext *dc, uint32_t sr, TCGv_i32 s)
+{
+    gen_helper_wsr_lend(s);
+}
+
+static void gen_wsr_sar(DisasContext *dc, uint32_t sr, TCGv_i32 s)
+{
+    tcg_gen_andi_i32(cpu_SR[sr], s, 0x3f);
+    if (dc->sar_m32_5bit) {
+        tcg_gen_discard_i32(dc->sar_m32);
+    }
+    dc->sar_5bit = false;
+    dc->sar_m32_5bit = false;
+}
+
+static void gen_wsr_litbase(DisasContext *dc, uint32_t sr, TCGv_i32 s)
+{
+    tcg_gen_andi_i32(cpu_SR[sr], s, 0xfffff001);
+    /* This can change tb->flags, so exit tb */
+    gen_jumpi_check_loop_end(dc, -1);
+}
+
+static void gen_wsr_windowbase(DisasContext *dc, uint32_t sr, TCGv_i32 v)
+{
+    gen_helper_wsr_windowbase(v);
+}
+
+static void gen_wsr_ps(DisasContext *dc, uint32_t sr, TCGv_i32 v)
+{
+    uint32_t mask = PS_WOE | PS_CALLINC | PS_OWB |
+        PS_UM | PS_EXCM | PS_INTLEVEL;
+
+    if (option_enabled(dc, XTENSA_OPTION_MMU)) {
+        mask |= PS_RING;
+    }
+    tcg_gen_andi_i32(cpu_SR[sr], v, mask);
+    /* This can change mmu index, so exit tb */
+    gen_jumpi_check_loop_end(dc, -1);
+}
+
 static void gen_wsr(DisasContext *dc, uint32_t sr, TCGv_i32 s)
 {
     static void (* const wsr_handler[256])(DisasContext *dc,
             uint32_t sr, TCGv_i32 v) = {
+        [LBEG] = gen_wsr_lbeg,
+        [LEND] = gen_wsr_lend,
+        [SAR] = gen_wsr_sar,
+        [LITBASE] = gen_wsr_litbase,
+        [WINDOW_BASE] = gen_wsr_windowbase,
+        [PS] = gen_wsr_ps,
     };
 
     if (sregnames[sr]) {
@@ -211,6 +407,14 @@ static void disas_xtensa_insn(DisasContext *dc)
         } \
     } while (0)
 
+#define TBD() qemu_log("TBD(pc = %08x): %s:%d\n", dc->pc, __FILE__, __LINE__)
+#define RESERVED() do { \
+        qemu_log("RESERVED(pc = %08x, %02x%02x%02x): %s:%d\n", \
+                dc->pc, b0, b1, b2, __FILE__, __LINE__); \
+        goto invalid_opcode; \
+    } while (0)
+
+
 #ifdef TARGET_WORDS_BIGENDIAN
 #define OP0 (((b0) & 0xf0) >> 4)
 #define OP1 (((b2) & 0xf0) >> 4)
@@ -311,9 +515,11 @@ static void disas_xtensa_insn(DisasContext *dc)
                 case 0: /*SNM0*/
                     switch (CALLX_M) {
                     case 0: /*ILL*/
+                        gen_exception_cause(dc, ILLEGAL_INSTRUCTION_CAUSE);
                         break;
 
                     case 1: /*reserved*/
+                        RESERVED();
                         break;
 
                     case 2: /*JR*/
@@ -325,9 +531,16 @@ static void disas_xtensa_insn(DisasContext *dc)
 
                         case 1: /*RETWw*/
                             HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER);
+                            {
+                                TCGv_i32 tmp = tcg_const_i32(dc->pc);
+                                gen_helper_retw(tmp, tmp);
+                                gen_jump(dc, tmp);
+                                tcg_temp_free(tmp);
+                            }
                             break;
 
                         case 3: /*reserved*/
+                            RESERVED();
                             break;
                         }
                         break;
@@ -348,6 +561,13 @@ static void disas_xtensa_insn(DisasContext *dc)
                         case 2: /*CALLX8w*/
                         case 3: /*CALLX12w*/
                             HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER);
+                            {
+                                TCGv_i32 tmp = tcg_temp_new_i32();
+
+                                tcg_gen_mov_i32(tmp, cpu_R[CALLX_S]);
+                                gen_callw(dc, CALLX_N, tmp);
+                                tcg_temp_free(tmp);
+                            }
                             break;
                         }
                         break;
@@ -356,12 +576,174 @@ static void disas_xtensa_insn(DisasContext *dc)
 
                 case 1: /*MOVSPw*/
                     HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER);
+                    {
+                        TCGv_i32 pc = tcg_const_i32(dc->pc);
+                        gen_helper_movsp(pc);
+                        tcg_gen_mov_i32(cpu_R[RRR_T], cpu_R[RRR_S]);
+                        tcg_temp_free(pc);
+                    }
                     break;
 
                 case 2: /*SYNC*/
+                    switch (RRR_T) {
+                    case 0: /*ISYNC*/
+                        break;
+
+                    case 1: /*RSYNC*/
+                        break;
+
+                    case 2: /*ESYNC*/
+                        break;
+
+                    case 3: /*DSYNC*/
+                        break;
+
+                    case 8: /*EXCW*/
+                        HAS_OPTION(XTENSA_OPTION_EXCEPTION);
+                        break;
+
+                    case 12: /*MEMW*/
+                        break;
+
+                    case 13: /*EXTW*/
+                        break;
+
+                    case 15: /*NOP*/
+                        break;
+
+                    default: /*reserved*/
+                        RESERVED();
+                        break;
+                    }
+                    break;
+
+                case 3: /*RFEIx*/
+                    switch (RRR_T) {
+                    case 0: /*RFETx*/
+                        HAS_OPTION(XTENSA_OPTION_EXCEPTION);
+                        switch (RRR_S) {
+                        case 0: /*RFEx*/
+                            gen_check_privilege(dc);
+                            tcg_gen_andi_i32(cpu_SR[PS], cpu_SR[PS], ~PS_EXCM);
+                            gen_jump(dc, cpu_SR[EPC1]);
+                            break;
+
+                        case 1: /*RFUEx*/
+                            RESERVED();
+                            break;
+
+                        case 2: /*RFDEx*/
+                            gen_check_privilege(dc);
+                            gen_jump(dc, cpu_SR[
+                                    dc->config->ndepc ? DEPC : EPC1]);
+                            break;
+
+                        case 4: /*RFWOw*/
+                        case 5: /*RFWUw*/
+                            HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER);
+                            gen_check_privilege(dc);
+                            {
+                                TCGv_i32 tmp = tcg_const_i32(1);
+
+                                tcg_gen_andi_i32(
+                                        cpu_SR[PS], cpu_SR[PS], ~PS_EXCM);
+                                tcg_gen_shl_i32(tmp, tmp, cpu_SR[WINDOW_BASE]);
+
+                                if (RRR_S == 4) {
+                                    tcg_gen_andc_i32(cpu_SR[WINDOW_START],
+                                            cpu_SR[WINDOW_START], tmp);
+                                } else {
+                                    tcg_gen_or_i32(cpu_SR[WINDOW_START],
+                                            cpu_SR[WINDOW_START], tmp);
+                                }
+
+                                gen_helper_restore_owb();
+                                gen_jump(dc, cpu_SR[EPC1]);
+
+                                tcg_temp_free(tmp);
+                            }
+                            break;
+
+                        default: /*reserved*/
+                            RESERVED();
+                            break;
+                        }
+                        break;
+
+                    case 1: /*RFIx*/
+                        HAS_OPTION(XTENSA_OPTION_HIGH_PRIORITY_INTERRUPT);
+                        TBD();
+                        break;
+
+                    case 2: /*RFME*/
+                        TBD();
+                        break;
+
+                    default: /*reserved*/
+                        RESERVED();
+                        break;
+
+                    }
+                    break;
+
+                case 4: /*BREAKx*/
+                    HAS_OPTION(XTENSA_OPTION_EXCEPTION);
+                    TBD();
+                    break;
+
+                case 5: /*SYSCALLx*/
+                    HAS_OPTION(XTENSA_OPTION_EXCEPTION);
+                    switch (RRR_S) {
+                    case 0: /*SYSCALLx*/
+                        gen_exception_cause(dc, SYSCALL_CAUSE);
+                        break;
+
+                    case 1: /*SIMCALL*/
+                        TBD();
+                        break;
+
+                    default:
+                        RESERVED();
+                        break;
+                    }
+                    break;
+
+                case 6: /*RSILx*/
+                    HAS_OPTION(XTENSA_OPTION_INTERRUPT);
+                    gen_check_privilege(dc);
+                    tcg_gen_mov_i32(cpu_R[RRR_T], cpu_SR[PS]);
+                    tcg_gen_ori_i32(cpu_SR[PS], cpu_SR[PS], RRR_S);
+                    tcg_gen_andi_i32(cpu_SR[PS], cpu_SR[PS],
+                            RRR_S | ~PS_INTLEVEL);
                     break;
 
-                case 3:
+                case 7: /*WAITIx*/
+                    HAS_OPTION(XTENSA_OPTION_INTERRUPT);
+                    TBD();
+                    break;
+
+                case 8: /*ANY4p*/
+                    HAS_OPTION(XTENSA_OPTION_BOOLEAN);
+                    TBD();
+                    break;
+
+                case 9: /*ALL4p*/
+                    HAS_OPTION(XTENSA_OPTION_BOOLEAN);
+                    TBD();
+                    break;
+
+                case 10: /*ANY8p*/
+                    HAS_OPTION(XTENSA_OPTION_BOOLEAN);
+                    TBD();
+                    break;
+
+                case 11: /*ALL8p*/
+                    HAS_OPTION(XTENSA_OPTION_BOOLEAN);
+                    TBD();
+                    break;
+
+                default: /*reserved*/
+                    RESERVED();
                     break;
 
                 }
@@ -380,9 +762,79 @@ static void disas_xtensa_insn(DisasContext *dc)
                 break;
 
             case 4: /*ST1*/
+                switch (RRR_R) {
+                case 0: /*SSR*/
+                    gen_right_shift_sar(dc, cpu_R[RRR_S]);
+                    break;
+
+                case 1: /*SSL*/
+                    gen_left_shift_sar(dc, cpu_R[RRR_S]);
+                    break;
+
+                case 2: /*SSA8L*/
+                    {
+                        TCGv_i32 tmp = tcg_temp_new_i32();
+                        tcg_gen_shli_i32(tmp, cpu_R[RRR_S], 3);
+                        gen_right_shift_sar(dc, tmp);
+                        tcg_temp_free(tmp);
+                    }
+                    break;
+
+                case 3: /*SSA8B*/
+                    {
+                        TCGv_i32 tmp = tcg_temp_new_i32();
+                        tcg_gen_shli_i32(tmp, cpu_R[RRR_S], 3);
+                        gen_left_shift_sar(dc, tmp);
+                        tcg_temp_free(tmp);
+                    }
+                    break;
+
+                case 4: /*SSAI*/
+                    {
+                        TCGv_i32 tmp = tcg_const_i32(
+                                RRR_S | ((RRR_T & 1) << 4));
+                        gen_right_shift_sar(dc, tmp);
+                        tcg_temp_free(tmp);
+                    }
+                    break;
+
+                case 6: /*RER*/
+                    TBD();
+                    break;
+
+                case 7: /*WER*/
+                    TBD();
+                    break;
+
+                case 8: /*ROTWw*/
+                    HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER);
+                    gen_check_privilege(dc);
+                    {
+                        TCGv_i32 tmp = tcg_const_i32(
+                                RRR_T | ((RRR_T & 8) ? 0xfffffff0 : 0));
+                        gen_helper_rotw(tmp);
+                        tcg_temp_free(tmp);
+                    }
+                    break;
+
+                case 14: /*NSAu*/
+                    HAS_OPTION(XTENSA_OPTION_MISC_OP);
+                    gen_helper_nsa(cpu_R[RRR_T], cpu_R[RRR_S]);
+                    break;
+
+                case 15: /*NSAUu*/
+                    HAS_OPTION(XTENSA_OPTION_MISC_OP);
+                    gen_helper_nsau(cpu_R[RRR_T], cpu_R[RRR_S]);
+                    break;
+
+                default: /*reserved*/
+                    RESERVED();
+                    break;
+                }
                 break;
 
             case 5: /*TLB*/
+                TBD();
                 break;
 
             case 6: /*RT0*/
@@ -403,11 +855,13 @@ static void disas_xtensa_insn(DisasContext *dc)
                     break;
 
                 default: /*reserved*/
+                    RESERVED();
                     break;
                 }
                 break;
 
             case 7: /*reserved*/
+                RESERVED();
                 break;
 
             case 8: /*ADD*/
@@ -443,19 +897,229 @@ static void disas_xtensa_insn(DisasContext *dc)
             break;
 
         case 1: /*RST1*/
+            switch (OP2) {
+            case 0: /*SLLI*/
+            case 1:
+                tcg_gen_shli_i32(cpu_R[RRR_R], cpu_R[RRR_S],
+                        32 - (RRR_T | ((OP2 & 1) << 4)));
+                break;
+
+            case 2: /*SRAI*/
+            case 3:
+                tcg_gen_sari_i32(cpu_R[RRR_R], cpu_R[RRR_T],
+                        RRR_S | ((OP2 & 1) << 4));
+                break;
+
+            case 4: /*SRLI*/
+                tcg_gen_shri_i32(cpu_R[RRR_R], cpu_R[RRR_T], RRR_S);
+                break;
+
+            case 6: /*XSR*/
+                {
+                    TCGv_i32 tmp = tcg_temp_new_i32();
+                    if (RSR_SR >= 64) {
+                        gen_check_privilege(dc);
+                    }
+                    tcg_gen_mov_i32(tmp, cpu_R[RRR_T]);
+                    gen_rsr(dc, cpu_R[RRR_T], RSR_SR);
+                    gen_wsr(dc, RSR_SR, tmp);
+                    tcg_temp_free(tmp);
+                    if (!sregnames[RSR_SR]) {
+                        TBD();
+                    }
+                }
+                break;
+
+                /*
+                 * Note: 64 bit ops are used here solely because SAR values
+                 * have range 0..63
+                 */
+#define gen_shift_reg(cmd, reg) do { \
+                    TCGv_i64 tmp = tcg_temp_new_i64(); \
+                    tcg_gen_extu_i32_i64(tmp, reg); \
+                    tcg_gen_##cmd##_i64(v, v, tmp); \
+                    tcg_gen_trunc_i64_i32(cpu_R[RRR_R], v); \
+                    tcg_temp_free_i64(v); \
+                    tcg_temp_free_i64(tmp); \
+                } while (0)
+
+#define gen_shift(cmd) gen_shift_reg(cmd, cpu_SR[SAR])
+
+            case 8: /*SRC*/
+                {
+                    TCGv_i64 v = tcg_temp_new_i64();
+                    tcg_gen_concat_i32_i64(v, cpu_R[RRR_T], cpu_R[RRR_S]);
+                    gen_shift(shr);
+                }
+                break;
+
+            case 9: /*SRL*/
+                if (dc->sar_5bit) {
+                    tcg_gen_shr_i32(cpu_R[RRR_R], cpu_R[RRR_T], cpu_SR[SAR]);
+                } else {
+                    TCGv_i64 v = tcg_temp_new_i64();
+                    tcg_gen_extu_i32_i64(v, cpu_R[RRR_T]);
+                    gen_shift(shr);
+                }
+                break;
+
+            case 10: /*SLL*/
+                if (dc->sar_m32_5bit) {
+                    tcg_gen_shl_i32(cpu_R[RRR_R], cpu_R[RRR_S], dc->sar_m32);
+                } else {
+                    TCGv_i64 v = tcg_temp_new_i64();
+                    TCGv_i32 s = tcg_const_i32(32);
+                    tcg_gen_sub_i32(s, s, cpu_SR[SAR]);
+                    tcg_gen_andi_i32(s, s, 0x3f);
+                    tcg_gen_extu_i32_i64(v, cpu_R[RRR_S]);
+                    gen_shift_reg(shl, s);
+                    tcg_temp_free(s);
+                }
+                break;
+
+            case 11: /*SRA*/
+                if (dc->sar_5bit) {
+                    tcg_gen_sar_i32(cpu_R[RRR_R], cpu_R[RRR_T], cpu_SR[SAR]);
+                } else {
+                    TCGv_i64 v = tcg_temp_new_i64();
+                    tcg_gen_ext_i32_i64(v, cpu_R[RRR_T]);
+                    gen_shift(sar);
+                }
+                break;
+#undef gen_shift
+#undef gen_shift_reg
+
+            case 12: /*MUL16U*/
+                HAS_OPTION(XTENSA_OPTION_16_BIT_IMUL);
+                {
+                    TCGv_i32 v1 = tcg_temp_new_i32();
+                    TCGv_i32 v2 = tcg_temp_new_i32();
+                    tcg_gen_ext16u_i32(v1, cpu_R[RRR_S]);
+                    tcg_gen_ext16u_i32(v2, cpu_R[RRR_T]);
+                    tcg_gen_mul_i32(cpu_R[RRR_R], v1, v2);
+                    tcg_temp_free(v2);
+                    tcg_temp_free(v1);
+                }
+                break;
+
+            case 13: /*MUL16S*/
+                HAS_OPTION(XTENSA_OPTION_16_BIT_IMUL);
+                {
+                    TCGv_i32 v1 = tcg_temp_new_i32();
+                    TCGv_i32 v2 = tcg_temp_new_i32();
+                    tcg_gen_ext16s_i32(v1, cpu_R[RRR_S]);
+                    tcg_gen_ext16s_i32(v2, cpu_R[RRR_T]);
+                    tcg_gen_mul_i32(cpu_R[RRR_R], v1, v2);
+                    tcg_temp_free(v2);
+                    tcg_temp_free(v1);
+                }
+                break;
+
+            default: /*reserved*/
+                RESERVED();
+                break;
+            }
             break;
 
         case 2: /*RST2*/
+            if (OP2 >= 12) {
+                HAS_OPTION(XTENSA_OPTION_32_BIT_IDIV);
+                int label = gen_new_label();
+                tcg_gen_brcondi_i32(TCG_COND_NE, cpu_R[RRR_T], 0, label);
+                gen_exception_cause(dc, INTEGER_DIVIDE_BY_ZERO_CAUSE);
+                gen_set_label(label);
+            }
+
+            switch (OP2) {
+            case 8: /*MULLi*/
+                HAS_OPTION(XTENSA_OPTION_32_BIT_IMUL);
+                tcg_gen_mul_i32(cpu_R[RRR_R], cpu_R[RRR_S], cpu_R[RRR_T]);
+                break;
+
+            case 10: /*MULUHi*/
+            case 11: /*MULSHi*/
+                HAS_OPTION(XTENSA_OPTION_32_BIT_IMUL);
+                {
+                    TCGv_i64 r = tcg_temp_new_i64();
+                    TCGv_i64 s = tcg_temp_new_i64();
+                    TCGv_i64 t = tcg_temp_new_i64();
+
+                    if (OP2 == 10) {
+                        tcg_gen_extu_i32_i64(s, cpu_R[RRR_S]);
+                        tcg_gen_extu_i32_i64(t, cpu_R[RRR_T]);
+                    } else {
+                        tcg_gen_ext_i32_i64(s, cpu_R[RRR_S]);
+                        tcg_gen_ext_i32_i64(t, cpu_R[RRR_T]);
+                    }
+                    tcg_gen_mul_i64(r, s, t);
+                    tcg_gen_shri_i64(r, r, 32);
+                    tcg_gen_trunc_i64_i32(cpu_R[RRR_R], r);
+
+                    tcg_temp_free_i64(r);
+                    tcg_temp_free_i64(s);
+                    tcg_temp_free_i64(t);
+                }
+                break;
+
+            case 12: /*QUOUi*/
+                tcg_gen_divu_i32(cpu_R[RRR_R], cpu_R[RRR_S], cpu_R[RRR_T]);
+                break;
+
+            case 13: /*QUOSi*/
+            case 15: /*REMSi*/
+                {
+                    int label1 = gen_new_label();
+                    int label2 = gen_new_label();
+
+                    tcg_gen_brcondi_i32(TCG_COND_NE, cpu_R[RRR_S], 0x80000000,
+                            label1);
+                    tcg_gen_brcondi_i32(TCG_COND_NE, cpu_R[RRR_T], 0xffffffff,
+                            label1);
+                    tcg_gen_movi_i32(cpu_R[RRR_R],
+                            OP2 == 13 ? 0x80000000 : 0);
+                    tcg_gen_br(label2);
+                    gen_set_label(label1);
+                    if (OP2 == 13) {
+                        tcg_gen_div_i32(cpu_R[RRR_R],
+                                cpu_R[RRR_S], cpu_R[RRR_T]);
+                    } else {
+                        tcg_gen_rem_i32(cpu_R[RRR_R],
+                                cpu_R[RRR_S], cpu_R[RRR_T]);
+                    }
+                    gen_set_label(label2);
+                }
+                break;
+
+            case 14: /*REMUi*/
+                tcg_gen_remu_i32(cpu_R[RRR_R], cpu_R[RRR_S], cpu_R[RRR_T]);
+                break;
+
+            default: /*reserved*/
+                RESERVED();
+                break;
+            }
             break;
 
         case 3: /*RST3*/
             switch (OP2) {
             case 0: /*RSR*/
+                if (RSR_SR >= 64) {
+                    gen_check_privilege(dc);
+                }
                 gen_rsr(dc, cpu_R[RRR_T], RSR_SR);
+                if (!sregnames[RSR_SR]) {
+                    TBD();
+                }
                 break;
 
             case 1: /*WSR*/
+                if (RSR_SR >= 64) {
+                    gen_check_privilege(dc);
+                }
                 gen_wsr(dc, RSR_SR, cpu_R[RRR_T]);
+                if (!sregnames[RSR_SR]) {
+                    TBD();
+                }
                 break;
 
             case 2: /*SEXTu*/
@@ -548,10 +1212,12 @@ static void disas_xtensa_insn(DisasContext *dc)
 
             case 12: /*MOVFp*/
                 HAS_OPTION(XTENSA_OPTION_BOOLEAN);
+                TBD();
                 break;
 
             case 13: /*MOVTp*/
                 HAS_OPTION(XTENSA_OPTION_BOOLEAN);
+                TBD();
                 break;
 
             case 14: /*RUR*/
@@ -561,6 +1227,7 @@ static void disas_xtensa_insn(DisasContext *dc)
                         tcg_gen_mov_i32(cpu_R[RRR_R], cpu_UR[st]);
                     } else {
                         qemu_log("RUR %d not implemented, ", st);
+                        TBD();
                     }
                 }
                 break;
@@ -571,6 +1238,7 @@ static void disas_xtensa_insn(DisasContext *dc)
                         tcg_gen_mov_i32(cpu_UR[RSR_SR], cpu_R[RRR_T]);
                     } else {
                         qemu_log("WUR %d not implemented, ", RSR_SR);
+                        TBD();
                     }
                 }
                 break;
@@ -580,30 +1248,74 @@ static void disas_xtensa_insn(DisasContext *dc)
 
         case 4: /*EXTUI*/
         case 5:
+            {
+                int shiftimm = RRR_S | (OP1 << 4);
+                int maskimm = (1 << (OP2 + 1)) - 1;
+
+                TCGv_i32 tmp = tcg_temp_new_i32();
+                tcg_gen_shri_i32(tmp, cpu_R[RRR_T], shiftimm);
+                tcg_gen_andi_i32(cpu_R[RRR_R], tmp, maskimm);
+                tcg_temp_free(tmp);
+            }
             break;
 
         case 6: /*CUST0*/
+            RESERVED();
             break;
 
         case 7: /*CUST1*/
+            RESERVED();
             break;
 
         case 8: /*LSCXp*/
             HAS_OPTION(XTENSA_OPTION_COPROCESSOR);
+            TBD();
             break;
 
         case 9: /*LSC4*/
+            switch (OP2) {
+            case 0: /*L32E*/
+                HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER);
+                gen_check_privilege(dc);
+                {
+                    TCGv_i32 addr = tcg_temp_new_i32();
+                    tcg_gen_addi_i32(addr, cpu_R[RRR_S],
+                            (0xffffffc0 | (RRR_R << 2)));
+                    tcg_gen_qemu_ld32u(cpu_R[RRR_T], addr, dc->ring);
+                    tcg_temp_free(addr);
+                }
+                break;
+
+            case 4: /*S32E*/
+                HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER);
+                gen_check_privilege(dc);
+                {
+                    TCGv_i32 addr = tcg_temp_new_i32();
+                    tcg_gen_addi_i32(addr, cpu_R[RRR_S],
+                            (0xffffffc0 | (RRR_R << 2)));
+                    tcg_gen_qemu_st32(cpu_R[RRR_T], addr, dc->ring);
+                    tcg_temp_free(addr);
+                }
+                break;
+
+            default:
+                RESERVED();
+                break;
+            }
             break;
 
         case 10: /*FP0*/
             HAS_OPTION(XTENSA_OPTION_FP_COPROCESSOR);
+            TBD();
             break;
 
         case 11: /*FP1*/
             HAS_OPTION(XTENSA_OPTION_FP_COPROCESSOR);
+            TBD();
             break;
 
         default: /*reserved*/
+            RESERVED();
             break;
         }
         break;
@@ -611,25 +1323,212 @@ static void disas_xtensa_insn(DisasContext *dc)
     case 1: /*L32R*/
         {
             TCGv_i32 tmp = tcg_const_i32(
-                    (0xfffc0000 | (RI16_IMM16 << 2)) +
-                    ((dc->pc + 3) & ~3));
+                    ((dc->tb->flags & XTENSA_TBFLAG_LITBASE) ?
+                     0 : ((dc->pc + 3) & ~3)) +
+                    (0xfffc0000 | (RI16_IMM16 << 2)));
 
-            /* no ext L32R */
-
-            tcg_gen_qemu_ld32u(cpu_R[RRR_T], tmp, 0);
+            if (dc->tb->flags & XTENSA_TBFLAG_LITBASE) {
+                tcg_gen_add_i32(tmp, tmp, dc->litbase);
+            }
+            tcg_gen_qemu_ld32u(cpu_R[RRR_T], tmp, dc->cring);
             tcg_temp_free(tmp);
         }
         break;
 
     case 2: /*LSAI*/
+#define gen_load_store(type, shift) do { \
+            TCGv_i32 addr = tcg_temp_new_i32(); \
+            tcg_gen_addi_i32(addr, cpu_R[RRI8_S], RRI8_IMM8 << shift); \
+            tcg_gen_qemu_##type(cpu_R[RRI8_T], addr, dc->cring); \
+            tcg_temp_free(addr); \
+        } while (0)
+
+        switch (RRI8_R) {
+        case 0: /*L8UI*/
+            gen_load_store(ld8u, 0);
+            break;
+
+        case 1: /*L16UI*/
+            gen_load_store(ld16u, 1);
+            break;
+
+        case 2: /*L32I*/
+            gen_load_store(ld32u, 2);
+            break;
+
+        case 4: /*S8I*/
+            gen_load_store(st8, 0);
+            break;
+
+        case 5: /*S16I*/
+            gen_load_store(st16, 1);
+            break;
+
+        case 6: /*S32I*/
+            gen_load_store(st32, 2);
+            break;
+
+        case 7: /*CACHEc*/
+            if (RRI8_T < 8) {
+                HAS_OPTION(XTENSA_OPTION_DCACHE);
+            }
+
+            switch (RRI8_T) {
+            case 0: /*DPFRc*/
+                break;
+
+            case 1: /*DPFWc*/
+                break;
+
+            case 2: /*DPFROc*/
+                break;
+
+            case 3: /*DPFWOc*/
+                break;
+
+            case 4: /*DHWBc*/
+                break;
+
+            case 5: /*DHWBIc*/
+                break;
+
+            case 6: /*DHIc*/
+                break;
+
+            case 7: /*DIIc*/
+                break;
+
+            case 8: /*DCEc*/
+                switch (OP1) {
+                case 0: /*DPFLl*/
+                    HAS_OPTION(XTENSA_OPTION_DCACHE_INDEX_LOCK);
+                    break;
+
+                case 2: /*DHUl*/
+                    HAS_OPTION(XTENSA_OPTION_DCACHE_INDEX_LOCK);
+                    break;
+
+                case 3: /*DIUl*/
+                    HAS_OPTION(XTENSA_OPTION_DCACHE_INDEX_LOCK);
+                    break;
+
+                case 4: /*DIWBc*/
+                    HAS_OPTION(XTENSA_OPTION_DCACHE);
+                    break;
+
+                case 5: /*DIWBIc*/
+                    HAS_OPTION(XTENSA_OPTION_DCACHE);
+                    break;
+
+                default: /*reserved*/
+                    RESERVED();
+                    break;
+
+                }
+                break;
+
+            case 12: /*IPFc*/
+                HAS_OPTION(XTENSA_OPTION_ICACHE);
+                break;
+
+            case 13: /*ICEc*/
+                switch (OP1) {
+                case 0: /*IPFLl*/
+                    HAS_OPTION(XTENSA_OPTION_ICACHE_INDEX_LOCK);
+                    break;
+
+                case 2: /*IHUl*/
+                    HAS_OPTION(XTENSA_OPTION_ICACHE_INDEX_LOCK);
+                    break;
+
+                case 3: /*IIUl*/
+                    HAS_OPTION(XTENSA_OPTION_ICACHE_INDEX_LOCK);
+                    break;
+
+                default: /*reserved*/
+                    RESERVED();
+                    break;
+                }
+                break;
+
+            case 14: /*IHIc*/
+                HAS_OPTION(XTENSA_OPTION_ICACHE);
+                break;
+
+            case 15: /*IIIc*/
+                HAS_OPTION(XTENSA_OPTION_ICACHE);
+                break;
+
+            default: /*reserved*/
+                RESERVED();
+                break;
+            }
+            break;
+
+        case 9: /*L16SI*/
+            gen_load_store(ld16s, 1);
+            break;
+
+        case 10: /*MOVI*/
+            tcg_gen_movi_i32(cpu_R[RRI8_T],
+                    RRI8_IMM8 | (RRI8_S << 8) |
+                    ((RRI8_S & 0x8) ? 0xfffff000 : 0));
+            break;
+
+        case 11: /*L32AIy*/
+            HAS_OPTION(XTENSA_OPTION_MP_SYNCHRO);
+            gen_load_store(ld32u, 2); /*TODO acquire?*/
+            break;
+
+        case 12: /*ADDI*/
+            tcg_gen_addi_i32(cpu_R[RRI8_T], cpu_R[RRI8_S], RRI8_IMM8_SE);
+            break;
+
+        case 13: /*ADDMI*/
+            tcg_gen_addi_i32(cpu_R[RRI8_T], cpu_R[RRI8_S], RRI8_IMM8_SE << 8);
+            break;
+
+        case 14: /*S32C1Iy*/
+            HAS_OPTION(XTENSA_OPTION_MP_SYNCHRO);
+            {
+                int label = gen_new_label();
+                TCGv_i32 tmp = tcg_temp_local_new_i32();
+                TCGv_i32 addr = tcg_temp_local_new_i32();
+
+                tcg_gen_mov_i32(tmp, cpu_R[RRI8_T]);
+                tcg_gen_addi_i32(addr, cpu_R[RRI8_S], RRI8_IMM8 << 2);
+                tcg_gen_qemu_ld32u(cpu_R[RRI8_T], addr, dc->cring);
+                tcg_gen_brcond_i32(TCG_COND_NE, cpu_R[RRI8_T],
+                        cpu_SR[SCOMPARE1], label);
+
+                tcg_gen_qemu_st32(tmp, addr, dc->cring);
+
+                gen_set_label(label);
+                tcg_temp_free(addr);
+                tcg_temp_free(tmp);
+            }
+            break;
+
+        case 15: /*S32RIy*/
+            HAS_OPTION(XTENSA_OPTION_MP_SYNCHRO);
+            gen_load_store(st32, 2); /*TODO release?*/
+            break;
+
+        default: /*reserved*/
+            RESERVED();
+            break;
+        }
         break;
+#undef gen_load_store
 
     case 3: /*LSCIp*/
         HAS_OPTION(XTENSA_OPTION_COPROCESSOR);
+        TBD();
         break;
 
     case 4: /*MAC16d*/
         HAS_OPTION(XTENSA_OPTION_MAC16);
+        TBD();
         break;
 
     case 5: /*CALLN*/
@@ -643,6 +1542,8 @@ static void disas_xtensa_insn(DisasContext *dc)
         case 2: /*CALL8w*/
         case 3: /*CALL12w*/
             HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER);
+            gen_callwi(dc, CALL_N,
+                    (dc->pc & ~3) + (CALL_OFFSET_SE << 2) + 4, 0);
             break;
         }
         break;
@@ -685,28 +1586,57 @@ static void disas_xtensa_insn(DisasContext *dc)
             switch (BRI8_M) {
             case 0: /*ENTRYw*/
                 HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER);
+                {
+                    TCGv_i32 pc = tcg_const_i32(dc->pc);
+                    TCGv_i32 s = tcg_const_i32(BRI12_S);
+                    TCGv_i32 imm = tcg_const_i32(BRI12_IMM12);
+                    gen_helper_entry(pc, s, imm);
+                    tcg_temp_free(imm);
+                    tcg_temp_free(s);
+                    tcg_temp_free(pc);
+                }
                 break;
 
             case 1: /*B1*/
                 switch (BRI8_R) {
                 case 0: /*BFp*/
                     HAS_OPTION(XTENSA_OPTION_BOOLEAN);
+                    TBD();
                     break;
 
                 case 1: /*BTp*/
                     HAS_OPTION(XTENSA_OPTION_BOOLEAN);
+                    TBD();
                     break;
 
                 case 8: /*LOOP*/
-                    break;
-
                 case 9: /*LOOPNEZ*/
-                    break;
-
                 case 10: /*LOOPGTZ*/
+                    HAS_OPTION(XTENSA_OPTION_LOOP);
+                    {
+                        uint32_t lend = dc->pc + RRI8_IMM8 + 4;
+                        TCGv_i32 tmp = tcg_const_i32(lend);
+
+                        tcg_gen_subi_i32(cpu_SR[LCOUNT], cpu_R[RRI8_S], 1);
+                        tcg_gen_movi_i32(cpu_SR[LBEG], dc->next_pc);
+                        gen_wsr_lend(dc, LEND, tmp);
+                        tcg_temp_free(tmp);
+
+                        if (BRI8_R > 8) {
+                            int label = gen_new_label();
+                            tcg_gen_brcondi_i32(
+                                    BRI8_R == 9 ? TCG_COND_NE : TCG_COND_GT,
+                                    cpu_R[RRI8_S], 0, label);
+                            gen_jumpi(dc, lend, 1);
+                            gen_set_label(label);
+                        }
+
+                        gen_jumpi(dc, dc->next_pc, 0);
+                    }
                     break;
 
                 default: /*reserved*/
+                    RESERVED();
                     break;
 
                 }
@@ -795,7 +1725,7 @@ static void disas_xtensa_insn(DisasContext *dc)
 #define gen_narrow_load_store(type) do { \
             TCGv_i32 addr = tcg_temp_new_i32(); \
             tcg_gen_addi_i32(addr, cpu_R[RRRN_S], RRRN_R << 2); \
-            tcg_gen_qemu_##type(cpu_R[RRRN_T], addr, 0); \
+            tcg_gen_qemu_##type(cpu_R[RRRN_T], addr, dc->cring); \
             tcg_temp_free(addr); \
         } while (0)
 
@@ -842,32 +1772,46 @@ static void disas_xtensa_insn(DisasContext *dc)
                 break;
 
             case 1: /*RETW.Nn*/
+                HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER);
+                {
+                    TCGv_i32 tmp = tcg_const_i32(dc->pc);
+                    gen_helper_retw(tmp, tmp);
+                    gen_jump(dc, tmp);
+                    tcg_temp_free(tmp);
+                }
                 break;
 
             case 2: /*BREAK.Nn*/
+                TBD();
                 break;
 
             case 3: /*NOP.Nn*/
                 break;
 
             case 6: /*ILL.Nn*/
+                gen_exception_cause(dc, ILLEGAL_INSTRUCTION_CAUSE);
                 break;
 
             default: /*reserved*/
+                RESERVED();
                 break;
             }
             break;
 
         default: /*reserved*/
+            RESERVED();
             break;
         }
         break;
 
     default: /*reserved*/
+        RESERVED();
         break;
     }
 
+    gen_check_loop_end(dc, 0);
     dc->pc = dc->next_pc;
+
     return;
 
 invalid_opcode:
@@ -911,10 +1855,23 @@ static void gen_intermediate_code_internal(
     dc.singlestep_enabled = env->singlestep_enabled;
     dc.tb = tb;
     dc.pc = pc_start;
+    dc.ring = tb->flags & XTENSA_TBFLAG_RING_MASK;
+    dc.cring = (tb->flags & XTENSA_TBFLAG_EXCM) ? 0 : dc.ring;
+    dc.lbeg = env->sregs[LBEG];
+    dc.lend = env->sregs[LEND];
     dc.is_jmp = DISAS_NEXT;
 
+    init_litbase(&dc);
+    init_sar_tracker(&dc);
+
     gen_icount_start();
 
+    if (env->singlestep_enabled && env->exception_taken) {
+        env->exception_taken = 0;
+        tcg_gen_movi_i32(cpu_pc, dc.pc);
+        gen_exception(EXCP_DEBUG);
+    }
+
     do {
         check_breakpoint(env, &dc);
 
@@ -947,6 +1904,9 @@ static void gen_intermediate_code_internal(
             dc.pc < next_page_start &&
             gen_opc_ptr < gen_opc_end);
 
+    reset_litbase(&dc);
+    reset_sar_tracker(&dc);
+
     if (dc.is_jmp == DISAS_NEXT) {
         gen_jumpi(&dc, dc.pc, 0);
     }
@@ -998,6 +1958,13 @@ void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf,
         cpu_fprintf(f, "A%02d=%08x%c", i, env->regs[i],
                 (i % 4) == 3 ? '\n' : ' ');
     }
+
+    cpu_fprintf(f, "\n");
+
+    for (i = 0; i < env->config->nareg; ++i) {
+        cpu_fprintf(f, "AR%02d=%08x%c", i, env->phys_regs[i],
+                (i % 4) == 3 ? '\n' : ' ');
+    }
 }
 
 void restore_state_to_opc(CPUState *env, TranslationBlock *tb, int pc_pos)