Merge branch 'master' of /home/nchip/public_html/qemu into garage-push
[qemu] / target-sh4 / translate.c
index 7822496..1f461b7 100644 (file)
  *
  * 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301 USA
  */
 #include <stdarg.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 #include <inttypes.h>
-#include <assert.h>
 
 #define DEBUG_DISAS
 #define SH4_DEBUG_DISAS
@@ -49,6 +48,8 @@ typedef struct DisasContext {
     int memidx;
     uint32_t delayed_pc;
     int singlestep_enabled;
+    uint32_t features;
+    int has_movcal;
 } DisasContext;
 
 #if defined(CONFIG_USER_ONLY)
@@ -71,7 +72,7 @@ static TCGv_ptr cpu_env;
 static TCGv cpu_gregs[24];
 static TCGv cpu_pc, cpu_sr, cpu_ssr, cpu_spc, cpu_gbr;
 static TCGv cpu_vbr, cpu_sgr, cpu_dbr, cpu_mach, cpu_macl;
-static TCGv cpu_pr, cpu_fpscr, cpu_fpul;
+static TCGv cpu_pr, cpu_fpscr, cpu_fpul, cpu_ldst;
 static TCGv cpu_fregs[32];
 
 /* internal register indexes */
@@ -143,6 +144,8 @@ static void sh4_translate_init(void)
     cpu_delayed_pc = tcg_global_mem_new_i32(TCG_AREG0,
                                            offsetof(CPUState, delayed_pc),
                                            "_delayed_pc_");
+    cpu_ldst = tcg_global_mem_new_i32(TCG_AREG0,
+                                     offsetof(CPUState, ldst), "_ldst_");
 
     for (i = 0; i < 32; i++)
         cpu_fregs[i] = tcg_global_mem_new_i32(TCG_AREG0,
@@ -183,10 +186,15 @@ void cpu_dump_state(CPUState * env, FILE * f,
 
 static void cpu_sh4_reset(CPUSH4State * env)
 {
+    if (qemu_loglevel_mask(CPU_LOG_RESET)) {
+        qemu_log("CPU Reset (CPU %d)\n", env->cpu_index);
+        log_cpu_state(env, 0);
+    }
+
 #if defined(CONFIG_USER_ONLY)
-    env->sr = SR_FD;            /* FD - kernel does lazy fpu context switch */
+    env->sr = 0;
 #else
-    env->sr = 0x700000F0;      /* MD, RB, BL, I3-I0 */
+    env->sr = SR_MD | SR_RB | SR_BL | SR_I3 | SR_I2 | SR_I1 | SR_I0;
 #endif
     env->vbr = 0;
     env->pc = 0xA0000000;
@@ -206,6 +214,7 @@ typedef struct {
     uint32_t pvr;
     uint32_t prr;
     uint32_t cvr;
+    uint32_t features;
 } sh4_def_t;
 
 static sh4_def_t sh4_defs[] = {
@@ -215,18 +224,21 @@ static sh4_def_t sh4_defs[] = {
        .pvr = 0x00050000,
        .prr = 0x00000100,
        .cvr = 0x00110000,
+       .features = SH_FEATURE_BCR3_AND_BCR4,
     }, {
        .name = "SH7751R",
        .id = SH_CPU_SH7751R,
        .pvr = 0x04050005,
        .prr = 0x00000113,
        .cvr = 0x00110000,      /* Neutered caches, should be 0x20480000 */
+       .features = SH_FEATURE_BCR3_AND_BCR4,
     }, {
        .name = "SH7785",
        .id = SH_CPU_SH7785,
        .pvr = 0x10300700,
        .prr = 0x00000200,
        .cvr = 0x71440211,
+       .features = SH_FEATURE_SH4A,
      },
 };
 
@@ -237,7 +249,7 @@ static const sh4_def_t *cpu_sh4_find_by_name(const char *name)
     if (strcasecmp(name, "any") == 0)
        return &sh4_defs[0];
 
-    for (i = 0; i < sizeof(sh4_defs) / sizeof(*sh4_defs); i++)
+    for (i = 0; i < ARRAY_SIZE(sh4_defs); i++)
        if (strcasecmp(name, sh4_defs[i].name) == 0)
            return &sh4_defs[i];
 
@@ -248,7 +260,7 @@ void sh4_cpu_list(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...))
 {
     int i;
 
-    for (i = 0; i < sizeof(sh4_defs) / sizeof(*sh4_defs); i++)
+    for (i = 0; i < ARRAY_SIZE(sh4_defs); i++)
        (*cpu_fprintf)(f, "%s\n", sh4_defs[i].name);
 }
 
@@ -269,14 +281,15 @@ CPUSH4State *cpu_sh4_init(const char *cpu_model)
     if (!def)
        return NULL;
     env = qemu_mallocz(sizeof(CPUSH4State));
-    if (!env)
-       return NULL;
+    env->features = def->features;
     cpu_exec_init(env);
+    env->movcal_backup_tail = &(env->movcal_backup);
     sh4_translate_init();
     env->cpu_model_str = cpu_model;
     cpu_sh4_reset(env);
     cpu_sh4_register(env, def);
     tlb_flush(env, 1);
+    qemu_init_vcpu(env);
     return env;
 }
 
@@ -484,6 +497,37 @@ static inline void gen_store_fpr64 (TCGv_i64 t, int reg)
 
 static void _decode_opc(DisasContext * ctx)
 {
+    /* This code tries to make movcal emulation sufficiently
+       accurate for Linux purposes.  This instruction writes
+       memory, and prior to that, always allocates a cache line.
+       It is used in two contexts:
+       - in memcpy, where data is copied in blocks, the first write
+       of to a block uses movca.l for performance.
+       - in arch/sh/mm/cache-sh4.c, movcal.l + ocbi combination is used
+       to flush the cache. Here, the data written by movcal.l is never
+       written to memory, and the data written is just bogus.
+
+       To simulate this, we simulate movcal.l, we store the value to memory,
+       but we also remember the previous content. If we see ocbi, we check
+       if movcal.l for that address was done previously. If so, the write should
+       not have hit the memory, so we restore the previous content.
+       When we see an instruction that is neither movca.l
+       nor ocbi, the previous content is discarded.
+
+       To optimize, we only try to flush stores when we're at the start of
+       TB, or if we already saw movca.l in this TB and did not flush stores
+       yet.  */
+    if (ctx->has_movcal)
+       {
+         int opcode = ctx->opcode & 0xf0ff;
+         if (opcode != 0x0093 /* ocbi */
+             && opcode != 0x00c3 /* movca.l */)
+             {
+                 gen_helper_discard_movcal_backup ();
+                 ctx->has_movcal = 0;
+             }
+       }
+
 #if 0
     fprintf(stderr, "Translating opcode 0x%04x\n", ctx->opcode);
 #endif
@@ -781,8 +825,8 @@ static void _decode_opc(DisasContext * ctx)
        {
            int label1 = gen_new_label();
            int label2 = gen_new_label();
-           TCGv cmp1 = tcg_temp_local_new(TCG_TYPE_I32);
-           TCGv cmp2 = tcg_temp_local_new(TCG_TYPE_I32);
+           TCGv cmp1 = tcg_temp_local_new();
+           TCGv cmp2 = tcg_temp_local_new();
            tcg_gen_xor_i32(cmp1, REG(B7_4), REG(B11_8));
            tcg_gen_andi_i32(cmp2, cmp1, 0xff000000);
            tcg_gen_brcondi_i32(TCG_COND_EQ, cmp2, 0, label1);
@@ -931,7 +975,7 @@ static void _decode_opc(DisasContext * ctx)
            int label2 = gen_new_label();
            int label3 = gen_new_label();
            int label4 = gen_new_label();
-           TCGv shift = tcg_temp_local_new(TCG_TYPE_I32);
+           TCGv shift = tcg_temp_local_new();
            tcg_gen_brcondi_i32(TCG_COND_LT, REG(B7_4), 0, label1);
            /* Rm positive, shift to the left */
            tcg_gen_andi_i32(shift, REG(B7_4), 0x1f);
@@ -962,7 +1006,7 @@ static void _decode_opc(DisasContext * ctx)
            int label1 = gen_new_label();
            int label2 = gen_new_label();
            int label3 = gen_new_label();
-           TCGv shift = tcg_temp_local_new(TCG_TYPE_I32);
+           TCGv shift = tcg_temp_local_new();
            tcg_gen_brcondi_i32(TCG_COND_LT, REG(B7_4), 0, label1);
            /* Rm positive, shift to the left */
            tcg_gen_andi_i32(shift, REG(B7_4), 0x1f);
@@ -1172,6 +1216,17 @@ static void _decode_opc(DisasContext * ctx)
            }
        }
        return;
+    case 0xf00e: /* fmac FR0,RM,Rn */
+        {
+            CHECK_FPU_ENABLED
+            if (ctx->fpscr & FPSCR_PR) {
+                break; /* illegal instruction */
+            } else {
+                gen_helper_fmac_FT(cpu_fregs[FREG(B11_8)],
+                                   cpu_fregs[FREG(0)], cpu_fregs[FREG(B7_4)], cpu_fregs[FREG(B11_8)]);
+                return;
+            }
+        }
     }
 
     switch (ctx->opcode & 0xff00) {
@@ -1523,7 +1578,13 @@ static void _decode_opc(DisasContext * ctx)
        }
        return;
     case 0x00c3:               /* movca.l R0,@Rm */
-       tcg_gen_qemu_st32(REG(0), REG(B11_8), ctx->memidx);
+        {
+            TCGv val = tcg_temp_new();
+            tcg_gen_qemu_ld32u(val, REG(B11_8), ctx->memidx);
+            gen_helper_movcal (REG(B11_8), val);            
+            tcg_gen_qemu_st32(REG(0), REG(B11_8), ctx->memidx);
+        }
+        ctx->has_movcal = 1;
        return;
     case 0x40a9:
        /* MOVUA.L @Rm,R0 (Rm) -> R0
@@ -1539,11 +1600,40 @@ static void _decode_opc(DisasContext * ctx)
     case 0x0029:               /* movt Rn */
        tcg_gen_andi_i32(REG(B11_8), cpu_sr, SR_T);
        return;
+    case 0x0073:
+        /* MOVCO.L
+              LDST -> T
+               If (T == 1) R0 -> (Rn)
+               0 -> LDST
+        */
+        if (ctx->features & SH_FEATURE_SH4A) {
+           int label = gen_new_label();
+           gen_clr_t();
+           tcg_gen_or_i32(cpu_sr, cpu_sr, cpu_ldst);
+           tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_ldst, 0, label);
+           tcg_gen_qemu_st32(REG(0), REG(B11_8), ctx->memidx);
+           gen_set_label(label);
+           tcg_gen_movi_i32(cpu_ldst, 0);
+           return;
+       } else
+           break;
+    case 0x0063:
+        /* MOVLI.L @Rm,R0
+               1 -> LDST
+               (Rm) -> R0
+               When interrupt/exception
+               occurred 0 -> LDST
+        */
+       if (ctx->features & SH_FEATURE_SH4A) {
+           tcg_gen_movi_i32(cpu_ldst, 0);
+           tcg_gen_qemu_ld32s(REG(0), REG(B11_8), ctx->memidx);
+           tcg_gen_movi_i32(cpu_ldst, 1);
+           return;
+       } else
+           break;
     case 0x0093:               /* ocbi @Rn */
        {
-           TCGv dummy = tcg_temp_new();
-           tcg_gen_qemu_ld32s(dummy, REG(B11_8), ctx->memidx);
-           tcg_temp_free(dummy);
+           gen_helper_ocbi (REG(B11_8));
        }
        return;
     case 0x00a3:               /* ocbp @Rn */
@@ -1562,6 +1652,21 @@ static void _decode_opc(DisasContext * ctx)
        return;
     case 0x0083:               /* pref @Rn */
        return;
+    case 0x00d3:               /* prefi @Rn */
+       if (ctx->features & SH_FEATURE_SH4A)
+           return;
+       else
+           break;
+    case 0x00e3:               /* icbi @Rn */
+       if (ctx->features & SH_FEATURE_SH4A)
+           return;
+       else
+           break;
+    case 0x00ab:               /* synco */
+       if (ctx->features & SH_FEATURE_SH4A)
+           return;
+       else
+           break;
     case 0x4024:               /* rotcl Rn */
        {
            TCGv tmp = tcg_temp_new();
@@ -1626,9 +1731,9 @@ static void _decode_opc(DisasContext * ctx)
     case 0x401b:               /* tas.b @Rn */
        {
            TCGv addr, val;
-           addr = tcg_temp_local_new(TCG_TYPE_I32);
+           addr = tcg_temp_local_new();
            tcg_gen_mov_i32(addr, REG(B11_8));
-           val = tcg_temp_local_new(TCG_TYPE_I32);
+           val = tcg_temp_local_new();
            tcg_gen_qemu_ld8u(val, addr, ctx->memidx);
            gen_cmp_imm(TCG_COND_EQ, val, 0);
            tcg_gen_ori_i32(val, val, 0x80);
@@ -1743,9 +1848,11 @@ static void _decode_opc(DisasContext * ctx)
        }
        return;
     }
-
+#if 0
     fprintf(stderr, "unknown instruction 0x%04x at pc 0x%08x\n",
            ctx->opcode, ctx->pc);
+    fflush(stderr);
+#endif
     gen_helper_raise_illegal_instruction();
     ctx->bstate = BS_EXCP;
 }
@@ -1805,13 +1912,13 @@ gen_intermediate_code_internal(CPUState * env, TranslationBlock * tb,
     ctx.delayed_pc = -1; /* use delayed pc from env pointer */
     ctx.tb = tb;
     ctx.singlestep_enabled = env->singlestep_enabled;
+    ctx.features = env->features;
+    ctx.has_movcal = (tb->flags & TB_FLAG_PENDING_MOVCA);
 
 #ifdef DEBUG_DISAS
-    if (loglevel & CPU_LOG_TB_CPU) {
-       fprintf(logfile,
-               "------------------------------------------------\n");
-       cpu_dump_state(env, logfile, fprintf, 0);
-    }
+    qemu_log_mask(CPU_LOG_TB_CPU,
+                 "------------------------------------------------\n");
+    log_cpu_state_mask(CPU_LOG_TB_CPU, env, 0);
 #endif
 
     ii = -1;
@@ -1860,9 +1967,8 @@ gen_intermediate_code_internal(CPUState * env, TranslationBlock * tb,
            break;
         if (num_insns >= max_insns)
             break;
-#ifdef SH4_SINGLE_STEP
-       break;
-#endif
+        if (singlestep)
+            break;
     }
     if (tb->cflags & CF_LAST_IO)
         gen_io_end();
@@ -1904,13 +2010,12 @@ gen_intermediate_code_internal(CPUState * env, TranslationBlock * tb,
 
 #ifdef DEBUG_DISAS
 #ifdef SH4_DEBUG_DISAS
-    if (loglevel & CPU_LOG_TB_IN_ASM)
-       fprintf(logfile, "\n");
+    qemu_log_mask(CPU_LOG_TB_IN_ASM, "\n");
 #endif
-    if (loglevel & CPU_LOG_TB_IN_ASM) {
-       fprintf(logfile, "IN:\n");      /* , lookup_symbol(pc_start)); */
-       target_disas(logfile, pc_start, ctx.pc - pc_start, 0);
-       fprintf(logfile, "\n");
+    if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
+       qemu_log("IN:\n");      /* , lookup_symbol(pc_start)); */
+       log_target_disas(pc_start, ctx.pc - pc_start, 0);
+       qemu_log("\n");
     }
 #endif
 }