drc: inv: fix ram ofset and mirror handling
[pcsx_rearmed.git] / libpcsxcore / new_dynarec / assem_arm.c
index 29ad13f..f2c2efa 100644 (file)
  *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.          *
  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
+#ifdef PCSX
+#include "../gte.h"
+#define FLAGLESS
+#include "../gte.h"
+#undef FLAGLESS
+#include "../gte_arm.h"
+#include "../gte_neon.h"
+#include "pcnt.h"
+#endif
+
+#if !BASE_ADDR_FIXED
+char translation_cache[1 << TARGET_SIZE_2] __attribute__((aligned(4096)));
+#endif
+
 extern int cycle_count;
 extern int last_count;
 extern int pcaddr;
@@ -278,6 +292,7 @@ int isclean(int addr)
   return 1;
 }
 
+// get source that block at addr was compiled from (host pointers)
 void get_bounds(int addr,u_int *start,u_int *end)
 {
   u_int *ptr=(u_int *)addr;
@@ -1143,7 +1158,7 @@ void emit_addimm(u_int rs,int imm,u_int rt)
       assem_debug("add %s,%s,#%d\n",regname[rt],regname[rs],imm);
       output_w32(0xe2800000|rd_rn_rm(rt,rs,0)|armval);
     }else if(genimm(-imm,&armval)) {
-      assem_debug("sub %s,%s,#%d\n",regname[rt],regname[rs],imm);
+      assem_debug("sub %s,%s,#%d\n",regname[rt],regname[rs],-imm);
       output_w32(0xe2400000|rd_rn_rm(rt,rs,0)|armval);
     }else if(imm<0) {
       assert(imm>-65536);
@@ -1924,6 +1939,16 @@ void emit_movzwl_indexed(int offset, int rs, int rt)
     output_w32(0xe15000b0|rd_rn_rm(rt,rs,0)|(((-offset)<<4)&0xf00)|((-offset)&0xf));
   }
 }
+static void emit_ldrd(int offset, int rs, int rt)
+{
+  assert(offset>-256&&offset<256);
+  assem_debug("ldrd %s,%s+%d\n",regname[rt],regname[rs],offset);
+  if(offset>=0) {
+    output_w32(0xe1c000d0|rd_rn_rm(rt,rs,0)|((offset<<4)&0xf00)|(offset&0xf));
+  }else{
+    output_w32(0xe14000d0|rd_rn_rm(rt,rs,0)|(((-offset)<<4)&0xf00)|((-offset)&0xf));
+  }
+}
 void emit_readword(int addr, int rt)
 {
   u_int offset = addr-(u_int)&dynarec_local;
@@ -2568,34 +2593,40 @@ void emit_jno_unlikely(int a)
   output_w32(0x72800000|rd_rn_rm(15,15,0));
 }
 
-// Save registers before function call
-void save_regs(u_int reglist)
+static void save_regs_all(u_int reglist)
 {
-  reglist&=0x100f; // only save the caller-save registers, r0-r3, r12
+  int i;
   if(!reglist) return;
   assem_debug("stmia fp,{");
-  if(reglist&1) assem_debug("r0, ");
-  if(reglist&2) assem_debug("r1, ");
-  if(reglist&4) assem_debug("r2, ");
-  if(reglist&8) assem_debug("r3, ");
-  if(reglist&0x1000) assem_debug("r12");
+  for(i=0;i<16;i++)
+    if(reglist&(1<<i))
+      assem_debug("r%d,",i);
   assem_debug("}\n");
   output_w32(0xe88b0000|reglist);
 }
-// Restore registers after function call
-void restore_regs(u_int reglist)
+static void restore_regs_all(u_int reglist)
 {
-  reglist&=0x100f; // only restore the caller-save registers, r0-r3, r12
+  int i;
   if(!reglist) return;
   assem_debug("ldmia fp,{");
-  if(reglist&1) assem_debug("r0, ");
-  if(reglist&2) assem_debug("r1, ");
-  if(reglist&4) assem_debug("r2, ");
-  if(reglist&8) assem_debug("r3, ");
-  if(reglist&0x1000) assem_debug("r12");
+  for(i=0;i<16;i++)
+    if(reglist&(1<<i))
+      assem_debug("r%d,",i);
   assem_debug("}\n");
   output_w32(0xe89b0000|reglist);
 }
+// Save registers before function call
+static void save_regs(u_int reglist)
+{
+  reglist&=0x100f; // only save the caller-save registers, r0-r3, r12
+  save_regs_all(reglist);
+}
+// Restore registers after function call
+static void restore_regs(u_int reglist)
+{
+  reglist&=0x100f; // only restore the caller-save registers, r0-r3, r12
+  restore_regs_all(reglist);
+}
 
 // Write back consts using r14 so we don't disturb the other registers
 void wb_consts(signed char i_regmap[],uint64_t i_is32,u_int i_dirty,int i)
@@ -2704,14 +2735,45 @@ emit_extjump_ds(int addr, int target)
 // put rt_val into rt, potentially making use of rs with value rs_val
 static void emit_movimm_from(u_int rs_val,int rs,u_int rt_val,int rt)
 {
-  u_int xor=rs_val^rt_val;
+  u_int armval;
+  int diff;
+  if(genimm(rt_val,&armval)) {
+    assem_debug("mov %s,#%d\n",regname[rt],rt_val);
+    output_w32(0xe3a00000|rd_rn_rm(rt,0,0)|armval);
+    return;
+  }
+  if(genimm(~rt_val,&armval)) {
+    assem_debug("mvn %s,#%d\n",regname[rt],rt_val);
+    output_w32(0xe3e00000|rd_rn_rm(rt,0,0)|armval);
+    return;
+  }
+  diff=rt_val-rs_val;
+  if(genimm(diff,&armval)) {
+    assem_debug("add %s,%s,#%d\n",regname[rt],regname[rs],diff);
+    output_w32(0xe2800000|rd_rn_rm(rt,rs,0)|armval);
+    return;
+  }else if(genimm(-diff,&armval)) {
+    assem_debug("sub %s,%s,#%d\n",regname[rt],regname[rs],-diff);
+    output_w32(0xe2400000|rd_rn_rm(rt,rs,0)|armval);
+    return;
+  }
+  emit_movimm(rt_val,rt);
+}
+
+// return 1 if above function can do it's job cheaply
+static int is_similar_value(u_int v1,u_int v2)
+{
   u_int xs;
-  for(xs=xor;xs!=0&&(xs&3)==0;xs>>=2)
+  int diff;
+  if(v1==v2) return 1;
+  diff=v2-v1;
+  for(xs=diff;xs!=0&&(xs&3)==0;xs>>=2)
     ;
-  if(xs<0x100)
-    emit_xorimm(rs,xor,rt);
-  else
-    emit_movimm(rt_val,rt);
+  if(xs<0x100) return 1;
+  for(xs=-diff;xs!=0&&(xs&3)==0;xs>>=2)
+    ;
+  if(xs<0x100) return 1;
+  return 0;
 }
 
 // trashes r2
@@ -2778,7 +2840,7 @@ do_readstub(int n)
       temp=r; break;
     }
   }
-  if(rt>=0)
+  if(rt>=0&&rt1[i]!=0)
     reglist&=~(1<<rt);
   if(temp==-1) {
     save_regs(reglist);
@@ -2955,7 +3017,7 @@ inline_readstub(int type, int i, u_int addr, signed char regmap[], int target, i
     return;
   handler=get_direct_memhandler(mem_rtab,addr,type,&host_addr);
   if (handler==0) {
-    if(rt<0)
+    if(rt<0||rt1[i]==0)
       return;
     if(addr!=host_addr)
       emit_movimm_from(addr,rs,host_addr,rs);
@@ -2980,7 +3042,7 @@ inline_readstub(int type, int i, u_int addr, signed char regmap[], int target, i
   }
 
   // call a memhandler
-  if(rt>=0)
+  if(rt>=0&&rt1[i]!=0)
     reglist&=~(1<<rt);
   save_regs(reglist);
   if(target==0)
@@ -3011,7 +3073,7 @@ inline_readstub(int type, int i, u_int addr, signed char regmap[], int target, i
   else
     emit_call(handler);
 
-  if(rt>=0) {
+  if(rt>=0&&rt1[i]!=0) {
     switch(type) {
       case LOADB_STUB:  emit_signextend8(0,rt); break;
       case LOADBU_STUB: emit_andimm(0,0xff,rt); break;
@@ -3187,7 +3249,6 @@ do_writestub(int n)
     set_jump_target(restore_jump,(int)out);
   restore_regs(reglist);
   ra=stubs[n][2];
-  if(!restore_jump) ra+=4*3; // skip invcode check
   emit_jmp(ra);
 #else // if !PCSX
   if(addr<0) addr=get_reg(i_regmap,-1);
@@ -3941,6 +4002,10 @@ static int emit_fastpath_cmp_jump(int i,int addr,int *addr_reg_override)
     else
     #endif
       emit_jno(0);
+    if(ram_offset!=0) {
+      emit_addimm(addr,ram_offset,HOST_TEMPREG);
+      addr=*addr_reg_override=HOST_TEMPREG;
+    }
   }
 
   return jaddr;
@@ -3992,6 +4057,10 @@ void loadlr_assemble_arm(int i,struct regstat *i_regs)
       jaddr=emit_fastpath_cmp_jump(i,temp2,&fastload_reg_override);
     }
     else {
+      if(ram_offset&&memtarget) {
+        emit_addimm(temp2,ram_offset,HOST_TEMPREG);
+        fastload_reg_override=HOST_TEMPREG;
+      }
       if (opcode[i]==0x22||opcode[i]==0x26) {
         emit_movimm(((constmap[i][s]+offset)<<3)&24,temp); // LWL/LWR
       }else{
@@ -4159,10 +4228,10 @@ void cop0_assemble(int i,struct regstat *i_regs)
         emit_writeword(HOST_CCREG,(int)&last_count);
         emit_movimm(0,HOST_CCREG);
         emit_storereg(CCREG,HOST_CCREG);
-        if(s!=1)
-          emit_mov(s,1);
+        emit_loadreg(rs1[i],1);
         emit_movimm(copr,0);
         emit_call((int)pcsx_mtc0_ds);
+        emit_loadreg(rs1[i],s);
         return;
       }
 #endif
@@ -4174,7 +4243,9 @@ void cop0_assemble(int i,struct regstat *i_regs)
     //else if(copr==12&&is_delayslot) emit_call((int)MTC0_R12);
     //else
 #ifdef PCSX
-    if(s!=1)
+    if(s==HOST_CCREG)
+      emit_loadreg(rs1[i],1);
+    else if(s!=1)
       emit_mov(s,1);
     emit_movimm(copr,0);
     emit_call((int)pcsx_mtc0);
@@ -4183,23 +4254,21 @@ void cop0_assemble(int i,struct regstat *i_regs)
 #endif
     if(copr==9||copr==11||copr==12||copr==13) {
       emit_readword((int)&Count,HOST_CCREG);
-      emit_readword((int)&next_interupt,ECX);
+      emit_readword((int)&next_interupt,HOST_TEMPREG);
       emit_addimm(HOST_CCREG,-CLOCK_ADJUST(ccadj[i]),HOST_CCREG);
-      emit_sub(HOST_CCREG,ECX,HOST_CCREG);
-      emit_writeword(ECX,(int)&last_count);
+      emit_sub(HOST_CCREG,HOST_TEMPREG,HOST_CCREG);
+      emit_writeword(HOST_TEMPREG,(int)&last_count);
       emit_storereg(CCREG,HOST_CCREG);
     }
     if(copr==12||copr==13) {
       assert(!is_delayslot);
       emit_readword((int)&pending_exception,14);
+      emit_test(14,14);
+      emit_jne((int)&do_interrupt);
     }
     emit_loadreg(rs1[i],s);
     if(get_reg(i_regs->regmap,rs1[i]|64)>=0)
       emit_loadreg(rs1[i]|64,get_reg(i_regs->regmap,rs1[i]|64));
-    if(copr==12||copr==13) {
-      emit_test(14,14);
-      emit_jne((int)&do_interrupt);
-    }
     cop1_usable=0;
   }
   else
@@ -4385,36 +4454,155 @@ void cop2_assemble(int i,struct regstat *i_regs)
   }
 }
 
-void c2op_assemble(int i,struct regstat *i_regs)
+static void c2op_prologue(u_int op,u_int reglist)
+{
+  save_regs_all(reglist);
+#ifdef PCNT
+  emit_movimm(op,0);
+  emit_call((int)pcnt_gte_start);
+#endif
+  emit_addimm(FP,(int)&psxRegs.CP2D.r[0]-(int)&dynarec_local,0); // cop2 regs
+}
+
+static void c2op_epilogue(u_int op,u_int reglist)
+{
+#ifdef PCNT
+  emit_movimm(op,0);
+  emit_call((int)pcnt_gte_end);
+#endif
+  restore_regs_all(reglist);
+}
+
+static void c2op_call_MACtoIR(int lm,int need_flags)
+{
+  if(need_flags)
+    emit_call((int)(lm?gteMACtoIR_lm1:gteMACtoIR_lm0));
+  else
+    emit_call((int)(lm?gteMACtoIR_lm1_nf:gteMACtoIR_lm0_nf));
+}
+
+static void c2op_call_rgb_func(void *func,int lm,int need_ir,int need_flags)
+{
+  emit_call((int)func);
+  // func is C code and trashes r0
+  emit_addimm(FP,(int)&psxRegs.CP2D.r[0]-(int)&dynarec_local,0);
+  if(need_flags||need_ir)
+    c2op_call_MACtoIR(lm,need_flags);
+  emit_call((int)(need_flags?gteMACtoRGB:gteMACtoRGB_nf));
+}
+
+static void c2op_assemble(int i,struct regstat *i_regs)
 {
   signed char temp=get_reg(i_regs->regmap,-1);
   u_int c2op=source[i]&0x3f;
-  u_int hr,reglist=0;
-  int need_flags;
+  u_int hr,reglist_full=0,reglist;
+  int need_flags,need_ir;
   for(hr=0;hr<HOST_REGS;hr++) {
-    if(i_regs->regmap[hr]>=0) reglist|=1<<hr;
+    if(i_regs->regmap[hr]>=0) reglist_full|=1<<hr;
   }
-  if(i==0||itype[i-1]!=C2OP)
-    save_regs(reglist);
+  reglist=reglist_full&0x100f;
 
   if (gte_handlers[c2op]!=NULL) {
-    int cc=get_reg(i_regs->regmap,CCREG);
-    emit_movimm(source[i],1); // opcode
-    if (cc>=0&&gte_cycletab[c2op])
-      emit_addimm(cc,gte_cycletab[c2op]/2,cc); // XXX: could just adjust ccadj?
-    emit_addimm(FP,(int)&psxRegs.CP2D.r[0]-(int)&dynarec_local,0); // cop2 regs
-    emit_writeword(1,(int)&psxRegs.code);
     need_flags=!(gte_unneeded[i+1]>>63); // +1 because of how liveness detection works
-    assem_debug("gte unneeded %016llx, need_flags %d\n",gte_unneeded[i+1],need_flags);
-#ifdef ARMv5_ONLY
-    // let's take more risk here
-    need_flags=need_flags&&gte_reads_flags;
+    need_ir=(gte_unneeded[i+1]&0xe00)!=0xe00;
+    assem_debug("gte op %08x, unneeded %016llx, need_flags %d, need_ir %d\n",
+      source[i],gte_unneeded[i+1],need_flags,need_ir);
+    if(new_dynarec_hacks&NDHACK_GTE_NO_FLAGS)
+      need_flags=0;
+    int shift = (source[i] >> 19) & 1;
+    int lm = (source[i] >> 10) & 1;
+    switch(c2op) {
+#ifndef DRC_DBG
+      case GTE_MVMVA: {
+        int v  = (source[i] >> 15) & 3;
+        int cv = (source[i] >> 13) & 3;
+        int mx = (source[i] >> 17) & 3;
+        reglist=reglist_full&0x10ff; // +{r4-r7}
+        c2op_prologue(c2op,reglist);
+        /* r4,r5 = VXYZ(v) packed; r6 = &MX11(mx); r7 = &CV1(cv) */
+        if(v<3)
+          emit_ldrd(v*8,0,4);
+        else {
+          emit_movzwl_indexed(9*4,0,4);  // gteIR
+          emit_movzwl_indexed(10*4,0,6);
+          emit_movzwl_indexed(11*4,0,5);
+          emit_orrshl_imm(6,16,4);
+        }
+        if(mx<3)
+          emit_addimm(0,32*4+mx*8*4,6);
+        else
+          emit_readword((int)&zeromem_ptr,6);
+        if(cv<3)
+          emit_addimm(0,32*4+(cv*8+5)*4,7);
+        else
+          emit_readword((int)&zeromem_ptr,7);
+#ifdef __ARM_NEON__
+        emit_movimm(source[i],1); // opcode
+        emit_call((int)gteMVMVA_part_neon);
+        if(need_flags) {
+          emit_movimm(lm,1);
+          emit_call((int)gteMACtoIR_flags_neon);
+        }
+#else
+        if(cv==3&&shift)
+          emit_call((int)gteMVMVA_part_cv3sh12_arm);
+        else {
+          emit_movimm(shift,1);
+          emit_call((int)(need_flags?gteMVMVA_part_arm:gteMVMVA_part_nf_arm));
+        }
+        if(need_flags||need_ir)
+          c2op_call_MACtoIR(lm,need_flags);
 #endif
-    emit_call((int)(need_flags?gte_handlers[c2op]:gte_handlers_nf[c2op]));
+        break;
+      }
+      case GTE_OP:
+        c2op_prologue(c2op,reglist);
+        emit_call((int)(shift?gteOP_part_shift:gteOP_part_noshift));
+        if(need_flags||need_ir) {
+          emit_addimm(FP,(int)&psxRegs.CP2D.r[0]-(int)&dynarec_local,0);
+          c2op_call_MACtoIR(lm,need_flags);
+        }
+        break;
+      case GTE_DPCS:
+        c2op_prologue(c2op,reglist);
+        c2op_call_rgb_func(shift?gteDPCS_part_shift:gteDPCS_part_noshift,lm,need_ir,need_flags);
+        break;
+      case GTE_INTPL:
+        c2op_prologue(c2op,reglist);
+        c2op_call_rgb_func(shift?gteINTPL_part_shift:gteINTPL_part_noshift,lm,need_ir,need_flags);
+        break;
+      case GTE_SQR:
+        c2op_prologue(c2op,reglist);
+        emit_call((int)(shift?gteSQR_part_shift:gteSQR_part_noshift));
+        if(need_flags||need_ir) {
+          emit_addimm(FP,(int)&psxRegs.CP2D.r[0]-(int)&dynarec_local,0);
+          c2op_call_MACtoIR(lm,need_flags);
+        }
+        break;
+      case GTE_DCPL:
+        c2op_prologue(c2op,reglist);
+        c2op_call_rgb_func(gteDCPL_part,lm,need_ir,need_flags);
+        break;
+      case GTE_GPF:
+        c2op_prologue(c2op,reglist);
+        c2op_call_rgb_func(shift?gteGPF_part_shift:gteGPF_part_noshift,lm,need_ir,need_flags);
+        break;
+      case GTE_GPL:
+        c2op_prologue(c2op,reglist);
+        c2op_call_rgb_func(shift?gteGPL_part_shift:gteGPL_part_noshift,lm,need_ir,need_flags);
+        break;
+#endif
+      default:
+        c2op_prologue(c2op,reglist);
+#ifdef DRC_DBG
+        emit_movimm(source[i],1); // opcode
+        emit_writeword(1,(int)&psxRegs.code);
+#endif
+        emit_call((int)(need_flags?gte_handlers[c2op]:gte_handlers_nf[c2op]));
+        break;
+    }
+    c2op_epilogue(c2op,reglist);
   }
-
-  if(i>=slen-1||itype[i+1]!=C2OP)
-    restore_regs(reglist);
 }
 
 void cop1_unusable(int i,struct regstat *i_regs)
@@ -5505,7 +5693,7 @@ void do_clear_cache()
       for(j=0;j<32;j++) 
       {
         if(bitmap&(1<<j)) {
-          start=BASE_ADDR+i*131072+j*4096;
+          start=(u_int)BASE_ADDR+i*131072+j*4096;
           end=start+4095;
           j++;
           while(j<32) {