drc: handle some load delay cases with interpreter
[pcsx_rearmed.git] / libpcsxcore / new_dynarec / new_dynarec.c
index 1435017..39204ce 100644 (file)
@@ -121,7 +121,11 @@ struct ll_entry
   char shadow[1048576]  __attribute__((aligned(16)));
   void *copy;
   int expirep;
+#ifndef PCSX
   u_int using_tlb;
+#else
+  static const u_int using_tlb=0;
+#endif
   u_int stop_after_jal;
   extern u_char restore_candidate[512];
   extern int cycle_count;
@@ -1182,6 +1186,9 @@ void invalidate_block(u_int block)
   
   // Don't trap writes
   invalid_code[block]=1;
+#ifdef PCSX
+  invalid_code[((u_int)0x80000000>>12)|page]=1;
+#endif
 #ifndef DISABLE_TLB
   // If there is a valid TLB entry for this page, remove write protect
   if(tlb_LUT_w[block]) {
@@ -2780,8 +2787,10 @@ void load_assemble(int i,struct regstat *i_regs)
   if(i_regs->regmap[HOST_CCREG]==CCREG) reglist&=~(1<<HOST_CCREG);
   if(s>=0) {
     c=(i_regs->wasconst>>s)&1;
-    memtarget=((signed int)(constmap[i][s]+offset))<(signed int)0x80000000+RAM_SIZE;
-    if(using_tlb&&((signed int)(constmap[i][s]+offset))>=(signed int)0xC0000000) memtarget=1;
+    if (c) {
+      memtarget=((signed int)(constmap[i][s]+offset))<(signed int)0x80000000+RAM_SIZE;
+      if(using_tlb&&((signed int)(constmap[i][s]+offset))>=(signed int)0xC0000000) memtarget=1;
+    }
   }
   //printf("load_assemble: c=%d\n",c);
   //if(c) printf("load_assemble: const=%x\n",(int)constmap[i][s]+offset);
@@ -3081,8 +3090,10 @@ void store_assemble(int i,struct regstat *i_regs)
   offset=imm[i];
   if(s>=0) {
     c=(i_regs->wasconst>>s)&1;
-    memtarget=((signed int)(constmap[i][s]+offset))<(signed int)0x80000000+RAM_SIZE;
-    if(using_tlb&&((signed int)(constmap[i][s]+offset))>=(signed int)0xC0000000) memtarget=1;
+    if(c) {
+      memtarget=((signed int)(constmap[i][s]+offset))<(signed int)0x80000000+RAM_SIZE;
+      if(using_tlb&&((signed int)(constmap[i][s]+offset))>=(signed int)0xC0000000) memtarget=1;
+    }
   }
   assert(tl>=0);
   assert(temp>=0);
@@ -3129,38 +3140,36 @@ void store_assemble(int i,struct regstat *i_regs)
 
   if (opcode[i]==0x28) { // SB
     if(!c||memtarget) {
-      int x=0;
+      int x=0,a=temp;
 #ifdef BIG_ENDIAN_MIPS
       if(!c) emit_xorimm(addr,3,temp);
       else x=((constmap[i][s]+offset)^3)-(constmap[i][s]+offset);
 #else
-      if(c) x=(constmap[i][s]+offset)-(constmap[i][s]+offset);
-      else if (addr!=temp) emit_mov(addr,temp);
+      if(!c) a=addr;
 #endif
       //gen_tlb_addr_w(temp,map);
       //emit_writebyte_indexed(tl,(int)rdram-0x80000000,temp);
-      emit_writebyte_indexed_tlb(tl,x,temp,map,temp);
+      emit_writebyte_indexed_tlb(tl,x,a,map,a);
     }
     type=STOREB_STUB;
   }
   if (opcode[i]==0x29) { // SH
     if(!c||memtarget) {
-      int x=0;
+      int x=0,a=temp;
 #ifdef BIG_ENDIAN_MIPS
       if(!c) emit_xorimm(addr,2,temp);
       else x=((constmap[i][s]+offset)^2)-(constmap[i][s]+offset);
 #else
-      if(c) x=(constmap[i][s]+offset)-(constmap[i][s]+offset);
-      else if (addr!=temp) emit_mov(addr,temp);
+      if(!c) a=addr;
 #endif
       //#ifdef
       //emit_writehword_indexed_tlb(tl,x,temp,map,temp);
       //#else
       if(map>=0) {
-        gen_tlb_addr_w(temp,map);
-        emit_writehword_indexed(tl,x,temp);
+        gen_tlb_addr_w(a,map);
+        emit_writehword_indexed(tl,x,a);
       }else
-        emit_writehword_indexed(tl,(int)rdram-0x80000000+x,temp);
+        emit_writehword_indexed(tl,(int)rdram-0x80000000+x,a);
     }
     type=STOREH_STUB;
   }
@@ -3254,7 +3263,7 @@ void storelr_assemble(int i,struct regstat *i_regs)
   int jaddr=0,jaddr2;
   int case1,case2,case3;
   int done0,done1,done2;
-  int memtarget,c=0;
+  int memtarget=0,c=0;
   int agr=AGEN1+(i&1);
   u_int hr,reglist=0;
   th=get_reg(i_regs->regmap,rs2[i]|64);
@@ -3265,8 +3274,10 @@ void storelr_assemble(int i,struct regstat *i_regs)
   offset=imm[i];
   if(s>=0) {
     c=(i_regs->isconst>>s)&1;
-    memtarget=((signed int)(constmap[i][s]+offset))<(signed int)0x80000000+RAM_SIZE;
-    if(using_tlb&&((signed int)(constmap[i][s]+offset))>=(signed int)0xC0000000) memtarget=1;
+    if(c) {
+      memtarget=((signed int)(constmap[i][s]+offset))<(signed int)0x80000000+RAM_SIZE;
+      if(using_tlb&&((signed int)(constmap[i][s]+offset))>=(signed int)0xC0000000) memtarget=1;
+    }
   }
   assert(tl>=0);
   for(hr=0;hr<HOST_REGS;hr++) {
@@ -7709,20 +7720,10 @@ void disassemble_inst(int i)
     }
 }
 
-void new_dynarec_init()
+// clear the state completely, instead of just marking
+// things invalid like invalidate_all_pages() does
+void new_dynarec_clear_full()
 {
-  printf("Init new dynarec\n");
-  out=(u_char *)BASE_ADDR;
-  if (mmap (out, 1<<TARGET_SIZE_2,
-            PROT_READ | PROT_WRITE | PROT_EXEC,
-            MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
-            -1, 0) <= 0) {printf("mmap() failed\n");}
-#ifdef MUPEN64
-  rdword=&readmem_dword;
-  fake_pc.f.r.rs=&readmem_dword;
-  fake_pc.f.r.rt=&readmem_dword;
-  fake_pc.f.r.rd=&readmem_dword;
-#endif
   int n;
   for(n=0x80000;n<0x80800;n++)
     invalid_code[n]=1;
@@ -7730,23 +7731,47 @@ void new_dynarec_init()
     hash_table[n][0]=hash_table[n][2]=-1;
   memset(mini_ht,-1,sizeof(mini_ht));
   memset(restore_candidate,0,sizeof(restore_candidate));
+  memset(shadow,0,sizeof(shadow));
   copy=shadow;
   expirep=16384; // Expiry pointer, +2 blocks
   pending_exception=0;
   literalcount=0;
-#ifdef HOST_IMM8
-  // Copy this into local area so we don't have to put it in every literal pool
-  invc_ptr=invalid_code;
-#endif
   stop_after_jal=0;
   // TLB
+#ifndef DISABLE_TLB
   using_tlb=0;
+#endif
   for(n=0;n<524288;n++) // 0 .. 0x7FFFFFFF
     memory_map[n]=-1;
   for(n=524288;n<526336;n++) // 0x80000000 .. 0x807FFFFF
     memory_map[n]=((u_int)rdram-0x80000000)>>2;
   for(n=526336;n<1048576;n++) // 0x80800000 .. 0xFFFFFFFF
     memory_map[n]=-1;
+  for(n=0;n<4096;n++) ll_clear(jump_in+n);
+  for(n=0;n<4096;n++) ll_clear(jump_out+n);
+  for(n=0;n<4096;n++) ll_clear(jump_dirty+n);
+}
+
+void new_dynarec_init()
+{
+  printf("Init new dynarec\n");
+  out=(u_char *)BASE_ADDR;
+  if (mmap (out, 1<<TARGET_SIZE_2,
+            PROT_READ | PROT_WRITE | PROT_EXEC,
+            MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
+            -1, 0) <= 0) {printf("mmap() failed\n");}
+#ifdef MUPEN64
+  rdword=&readmem_dword;
+  fake_pc.f.r.rs=&readmem_dword;
+  fake_pc.f.r.rt=&readmem_dword;
+  fake_pc.f.r.rd=&readmem_dword;
+#endif
+  int n;
+  new_dynarec_clear_full();
+#ifdef HOST_IMM8
+  // Copy this into local area so we don't have to put it in every literal pool
+  invc_ptr=invalid_code;
+#endif
 #ifdef MUPEN64
   for(n=0;n<0x8000;n++) { // 0 .. 0x7FFFFFFF
     writemem[n] = write_nomem_new;
@@ -8217,18 +8242,6 @@ int new_recompile_block(int addr)
         printf("NI %08x @%08x (%08x)\n", source[i], addr + i*4, addr);
         break;
     }
-#ifdef PCSX
-    /* detect branch in delay slot early */
-    if(type==RJUMP||type==UJUMP||type==CJUMP||type==SJUMP||type==FJUMP) {
-      opcode[i+1]=source[i+1]>>26;
-      opcode2[i+1]=source[i+1]&0x3f;
-      if((0<opcode[i+1]&&opcode[i+1]<8)||(opcode[i+1]==0&&(opcode2[i+1]==8||opcode2[i+1]==9))) {
-        printf("branch in delay slot @%08x (%08x)\n", addr + i*4+4, addr);
-        // don't handle first branch and call interpreter if it's hit
-        type=INTCALL;
-      }
-    }
-#endif
     itype[i]=type;
     opcode2[i]=op2;
     /* Get registers/immediates */
@@ -8461,19 +8474,50 @@ int new_recompile_block(int addr)
     else if(type==CJUMP||type==SJUMP||type==FJUMP)
       ba[i]=start+i*4+4+((signed int)((unsigned int)source[i]<<16)>>14);
     else ba[i]=-1;
-    /* Is this the end of the block? */
-    if(i>0&&(itype[i-1]==UJUMP||itype[i-1]==RJUMP||(source[i-1]>>16)==0x1000)) {
 #ifdef PCSX
+    if(i>0&&(itype[i-1]==RJUMP||itype[i-1]==UJUMP||itype[i-1]==CJUMP||itype[i-1]==SJUMP||itype[i-1]==FJUMP)) {
+      int do_in_intrp=0;
+      // branch in delay slot?
+      if(type==RJUMP||type==UJUMP||type==CJUMP||type==SJUMP||type==FJUMP) {
+        // don't handle first branch and call interpreter if it's hit
+        printf("branch in delay slot @%08x (%08x)\n", addr + i*4, addr);
+        do_in_intrp=1;
+      }
+      // basic load delay detection
+      else if((type==LOAD||type==LOADLR||type==COP0||type==COP2||type==C2LS)&&rt1[i]!=0) {
+        int t=(ba[i-1]-start)/4;
+        if(0 <= t && t < i &&(rt1[i]==rs1[t]||rt1[i]==rs2[t])&&itype[t]!=CJUMP&&itype[t]!=SJUMP) {
+          // jump target wants DS result - potential load delay effect
+          printf("load delay @%08x (%08x)\n", addr + i*4, addr);
+          do_in_intrp=1;
+          bt[t+1]=1; // expected return from interpreter
+        }
+        else if(i>=2&&rt1[i-2]==2&&rt1[i]==2&&rs1[i]!=2&&rs2[i]!=2&&rs1[i-1]!=2&&rs2[i-1]!=2&&
+              !(i>=3&&(itype[i-3]==RJUMP||itype[i-3]==UJUMP||itype[i-3]==CJUMP||itype[i-3]==SJUMP))) {
+          // v0 overwrite like this is a sign of trouble, bail out
+          printf("v0 overwrite @%08x (%08x)\n", addr + i*4, addr);
+          do_in_intrp=1;
+        }
+      }
       // check for link register access in delay slot
+      // TODO: teach the recompiler to handle this
       int rt1_=rt1[i-1];
       if(rt1_!=0&&(rs1[i]==rt1_||rs2[i]==rt1_||rt1[i]==rt1_||rt2[i]==rt1_)) {
         printf("link access in delay slot @%08x (%08x)\n", addr + i*4, addr);
+        do_in_intrp=1;
+      }
+      if(do_in_intrp) {
+        rs1[i-1]=CCREG;
+        rs2[i-1]=rt1[i-1]=rt2[i-1]=0;
         ba[i-1]=-1;
         itype[i-1]=INTCALL;
         done=2;
+        i--; // don't compile the DS
       }
-      else
+    }
 #endif
+    /* Is this the end of the block? */
+    if(i>0&&(itype[i-1]==UJUMP||itype[i-1]==RJUMP||(source[i-1]>>16)==0x1000)) {
       if(rt1[i-1]==0) { // Continue past subroutine call (JAL)
         done=2;
       }
@@ -9478,6 +9522,16 @@ int new_recompile_block(int addr)
     {
       cc=0;
     }
+#ifdef PCSX
+    else if(/*itype[i]==LOAD||*/itype[i]==STORE||itype[i]==C1LS) // load causes weird timing issues
+    {
+      cc+=2; // 2 cycle penalty (after CLOCK_DIVIDER)
+    }
+    else if(itype[i]==C2LS)
+    {
+      cc+=4;
+    }
+#endif
     else
     {
       cc++;
@@ -11000,6 +11054,12 @@ int new_recompile_block(int addr)
     }
 #endif
   }
+#ifdef PCSX
+  // PCSX maps all RAM mirror invalid_code tests to 0x80000000..0x80000000+RAM_SIZE
+  if(get_page(start)<(RAM_SIZE>>12))
+    for(i=start>>12;i<=(start+slen*4)>>12;i++)
+      invalid_code[((u_int)0x80000000>>12)|i]=0;
+#endif
   
   /* Pass 10 - Free memory by expiring oldest blocks */