drc: fix bad gte unneeded reg assumption
[pcsx_rearmed.git] / libpcsxcore / new_dynarec / assem_arm.c
index cda420f..ebf733b 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
+
 extern int cycle_count;
 extern int last_count;
 extern int pcaddr;
@@ -245,6 +255,7 @@ int verify_dirty(int addr)
   #endif
   if((*ptr&0xFF000000)!=0xeb000000) ptr++;
   assert((*ptr&0xFF000000)==0xeb000000); // bl instruction
+#ifndef DISABLE_TLB
   u_int verifier=(int)ptr+((signed int)(*ptr<<8)>>6)+8; // get target of bl
   if(verifier==(u_int)verify_code_vm||verifier==(u_int)verify_code_ds) {
     unsigned int page=source>>12;
@@ -255,6 +266,7 @@ int verify_dirty(int addr)
     }
     source = source+(map_value<<2);
   }
+#endif
   //printf("verify_dirty: %x %x %x\n",source,copy,len);
   return !memcmp((void *)source,(void *)copy,len);
 }
@@ -298,11 +310,13 @@ void get_bounds(int addr,u_int *start,u_int *end)
   #endif
   if((*ptr&0xFF000000)!=0xeb000000) ptr++;
   assert((*ptr&0xFF000000)==0xeb000000); // bl instruction
+#ifndef DISABLE_TLB
   u_int verifier=(int)ptr+((signed int)(*ptr<<8)>>6)+8; // get target of bl
   if(verifier==(u_int)verify_code_vm||verifier==(u_int)verify_code_ds) {
     if(memory_map[source>>12]>=0x80000000) source = 0;
     else source = source+(memory_map[source>>12]<<2);
   }
+#endif
   *start=source;
   *end=source+len;
 }
@@ -1134,20 +1148,21 @@ void emit_addimm(u_int rs,int imm,u_int rt)
   assert(rs<16);
   assert(rt<16);
   if(imm!=0) {
-    assert(imm>-65536&&imm<65536);
     u_int armval;
     if(genimm(imm,&armval)) {
       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);
       assem_debug("sub %s,%s,#%d\n",regname[rt],regname[rs],(-imm)&0xFF00);
       assem_debug("sub %s,%s,#%d\n",regname[rt],regname[rt],(-imm)&0xFF);
       output_w32(0xe2400000|rd_rn_imm_shift(rt,rs,(-imm)>>8,8));
       output_w32(0xe2400000|rd_rn_imm_shift(rt,rt,(-imm)&0xff,0));
     }else{
+      assert(imm<65536);
       assem_debug("add %s,%s,#%d\n",regname[rt],regname[rs],imm&0xFF00);
       assem_debug("add %s,%s,#%d\n",regname[rt],regname[rt],imm&0xFF);
       output_w32(0xe2800000|rd_rn_imm_shift(rt,rs,imm>>8,8));
@@ -1919,6 +1934,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;
@@ -2041,6 +2066,21 @@ void emit_writebyte_indexed_tlb(int rt, int addr, int rs, int map, int temp)
     }
   }
 }
+void emit_strcc_dualindexed(int rs1, int rs2, int rt)
+{
+  assem_debug("strcc %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
+  output_w32(0x37800000|rd_rn_rm(rt,rs1,rs2));
+}
+void emit_strccb_dualindexed(int rs1, int rs2, int rt)
+{
+  assem_debug("strccb %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
+  output_w32(0x37c00000|rd_rn_rm(rt,rs1,rs2));
+}
+void emit_strcch_dualindexed(int rs1, int rs2, int rt)
+{
+  assem_debug("strcch %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
+  output_w32(0x318000b0|rd_rn_rm(rt,rs1,rs2));
+}
 void emit_writeword(int rt, int addr)
 {
   u_int offset = addr-(u_int)&dynarec_local;
@@ -2138,6 +2178,14 @@ void emit_shrcc_imm(int rs,u_int imm,int rt)
   output_w32(0x31a00000|rd_rn_rm(rt,0,rs)|0x20|(imm<<7));
 }
 
+void emit_shrne_imm(int rs,u_int imm,int rt)
+{
+  assert(imm>0);
+  assert(imm<32);
+  assem_debug("lsrne %s,%s,#%d\n",regname[rt],regname[rs],imm);
+  output_w32(0x11a00000|rd_rn_rm(rt,0,rs)|0x20|(imm<<7));
+}
+
 void emit_negmi(int rs, int rt)
 {
   assem_debug("rsbmi %s,%s,#0\n",regname[rt],regname[rs]);
@@ -2540,34 +2588,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)
@@ -2609,12 +2663,24 @@ void literal_pool(int n)
   int i;
   for(i=0;i<literalcount;i++)
   {
+    u_int l_addr=(u_int)out;
+    int j;
+    for(j=0;j<i;j++) {
+      if(literals[j][1]==literals[i][1]) {
+        //printf("dup %08x\n",literals[i][1]);
+        l_addr=literals[j][0];
+        break;
+      }
+    }
     ptr=(u_int *)literals[i][0];
-    u_int offset=(u_int)out-(u_int)ptr-8;
+    u_int offset=l_addr-(u_int)ptr-8;
     assert(offset<4096);
     assert(!(offset&3));
     *ptr|=offset;
-    output_w32(literals[i][1]);
+    if(l_addr==(u_int)out) {
+      literals[i][0]=l_addr; // remember for dupes
+      output_w32(literals[i][1]);
+    }
   }
   literalcount=0;
 }
@@ -2661,7 +2727,81 @@ emit_extjump_ds(int addr, int target)
   emit_extjump2(addr, target, (int)dyna_linker_ds);
 }
 
+// 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 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;
+  int diff;
+  if(v1==v2) return 1;
+  diff=v2-v1;
+  for(xs=diff;xs!=0&&(xs&3)==0;xs>>=2)
+    ;
+  if(xs<0x100) return 1;
+  for(xs=-diff;xs!=0&&(xs&3)==0;xs>>=2)
+    ;
+  if(xs<0x100) return 1;
+  return 0;
+}
+
+// trashes r2
+static void pass_args(int a0, int a1)
+{
+  if(a0==1&&a1==0) {
+    // must swap
+    emit_mov(a0,2); emit_mov(a1,1); emit_mov(2,0);
+  }
+  else if(a0!=0&&a1==0) {
+    emit_mov(a1,1);
+    if (a0>=0) emit_mov(a0,0);
+  }
+  else {
+    if(a0>=0&&a0!=0) emit_mov(a0,0);
+    if(a1>=0&&a1!=1) emit_mov(a1,1);
+  }
+}
+
+static void mov_loadtype_adj(int type,int rs,int rt)
+{
+  switch(type) {
+    case LOADB_STUB:  emit_signextend8(rs,rt); break;
+    case LOADBU_STUB: emit_andimm(rs,0xff,rt); break;
+    case LOADH_STUB:  emit_signextend16(rs,rt); break;
+    case LOADHU_STUB: emit_andimm(rs,0xffff,rt); break;
+    case LOADW_STUB:  if(rs!=rt) emit_mov(rs,rt); break;
+    default: assert(0);
+  }
+}
+
 #ifdef PCSX
+#include "pcsxmem.h"
 #include "pcsxmem_inline.c"
 #endif
 
@@ -2734,23 +2874,14 @@ do_readstub(int n)
   if(type==LOADW_STUB)
     handler=(int)jump_handler_read32;
   assert(handler!=0);
-  if(rs!=0)
-    emit_mov(rs,0);
-  if(temp2!=1)
-    emit_mov(temp2,1);
+  pass_args(rs,temp2);
   int cc=get_reg(i_regmap,CCREG);
   if(cc<0)
     emit_loadreg(CCREG,2);
-  emit_addimm(cc<0?2:cc,CLOCK_DIVIDER*stubs[n][6]+2,2);
+  emit_addimm(cc<0?2:cc,CLOCK_ADJUST((int)stubs[n][6]+1),2);
   emit_call(handler);
   if(itype[i]==C1LS||itype[i]==C2LS||(rt>=0&&rt1[i]!=0)) {
-    switch(type) {
-      case LOADB_STUB:  emit_signextend8(0,rt); break;
-      case LOADBU_STUB: emit_andimm(0,0xff,rt); break;
-      case LOADH_STUB:  emit_signextend16(0,rt); break;
-      case LOADHU_STUB: emit_andimm(0,0xffff,rt); break;
-      case LOADW_STUB:  if(rt!=0) emit_mov(0,rt); break;
-    }
+    mov_loadtype_adj(type,0,rt);
   }
   if(restore_jump)
     set_jump_target(restore_jump,(int)out);
@@ -2853,7 +2984,7 @@ u_int get_direct_memhandler(void *table,u_int addr,int type,u_int *addr_host)
     l1<<=1;
     if(type==LOADB_STUB||type==LOADBU_STUB||type==STOREB_STUB)
       l2=((u_int *)l1)[0x1000/4 + 0x1000/2 + (addr&0xfff)];
-    else if(type==LOADH_STUB||type==LOADHU_STUB||type==STOREW_STUB)
+    else if(type==LOADH_STUB||type==LOADHU_STUB||type==STOREH_STUB)
       l2=((u_int *)l1)[0x1000/4 + (addr&0xfff)/2];
     else
       l2=((u_int *)l1)[(addr&0xfff)/4];
@@ -2875,15 +3006,16 @@ inline_readstub(int type, int i, u_int addr, signed char regmap[], int target, i
   if(rs<0) rs=get_reg(regmap,-1);
   assert(rs>=0);
 #ifdef PCSX
-  u_int handler,host_addr=0;
-  if(pcsx_direct_read(type,addr,target?rs:-1,rt))
+  u_int handler,host_addr=0,is_dynamic,far_call=0;
+  int cc=get_reg(regmap,CCREG);
+  if(pcsx_direct_read(type,addr,CLOCK_ADJUST(adj+1),cc,target?rs:-1,rt))
     return;
   handler=get_direct_memhandler(mem_rtab,addr,type,&host_addr);
   if (handler==0) {
     if(rt<0)
       return;
-    if(target==0||addr!=host_addr)
-      emit_movimm(host_addr,rs);
+    if(addr!=host_addr)
+      emit_movimm_from(addr,rs,host_addr,rs);
     switch(type) {
       case LOADB_STUB:  emit_movsbl_indexed(0,rs,rt); break;
       case LOADBU_STUB: emit_movzbl_indexed(0,rs,rt); break;
@@ -2894,6 +3026,15 @@ inline_readstub(int type, int i, u_int addr, signed char regmap[], int target, i
     }
     return;
   }
+  is_dynamic=pcsxmem_is_handler_dynamic(addr);
+  if(is_dynamic) {
+    if(type==LOADB_STUB||type==LOADBU_STUB)
+      handler=(int)jump_handler_read8;
+    if(type==LOADH_STUB||type==LOADHU_STUB)
+      handler=(int)jump_handler_read16;
+    if(type==LOADW_STUB)
+      handler=(int)jump_handler_read32;
+  }
 
   // call a memhandler
   if(rt>=0)
@@ -2903,22 +3044,30 @@ inline_readstub(int type, int i, u_int addr, signed char regmap[], int target, i
     emit_movimm(addr,0);
   else if(rs!=0)
     emit_mov(rs,0);
-  int cc=get_reg(regmap,CCREG);
-  if(cc<0)
-    emit_loadreg(CCREG,2);
-  emit_readword((int)&last_count,3);
-  emit_addimm(cc<0?2:cc,CLOCK_DIVIDER*(adj+1),2);
-  emit_add(2,3,3);
-  emit_writeword(3,(int)&Count);
-
   int offset=(int)handler-(int)out-8;
   if(offset<-33554432||offset>=33554432) {
     // unreachable memhandler, a plugin func perhaps
-    emit_movimm(handler,1);
-    emit_callreg(1);
+    emit_movimm(handler,12);
+    far_call=1;
   }
+  if(cc<0)
+    emit_loadreg(CCREG,2);
+  if(is_dynamic) {
+    emit_movimm(((u_int *)mem_rtab)[addr>>12]<<1,1);
+    emit_addimm(cc<0?2:cc,CLOCK_ADJUST(adj+1),2);
+  }
+  else {
+    emit_readword((int)&last_count,3);
+    emit_addimm(cc<0?2:cc,CLOCK_ADJUST(adj+1),2);
+    emit_add(2,3,2);
+    emit_writeword(2,(int)&Count);
+  }
+
+  if(far_call)
+    emit_callreg(12);
   else
     emit_call(handler);
+
   if(rt>=0) {
     switch(type) {
       case LOADB_STUB:  emit_signextend8(0,rt); break;
@@ -2968,7 +3117,7 @@ inline_readstub(int type, int i, u_int addr, signed char regmap[], int target, i
   //emit_movimm(ftable,0);
   emit_movimm(((u_int *)ftable)[addr>>16],0);
   //emit_readword((int)&last_count,12);
-  emit_addimm(cc<0?2:cc,CLOCK_DIVIDER*(adj+1),2);
+  emit_addimm(cc<0?2:cc,CLOCK_ADJUST(adj+1),2);
 #ifndef PCSX
   if((signed int)addr>=(signed int)0xC0000000) {
     // Pagefault address
@@ -2985,7 +3134,7 @@ inline_readstub(int type, int i, u_int addr, signed char regmap[], int target, i
   // but not doing so causes random crashes...
   emit_readword((int)&Count,HOST_TEMPREG);
   emit_readword((int)&next_interupt,2);
-  emit_addimm(HOST_TEMPREG,-CLOCK_DIVIDER*(adj+1),HOST_TEMPREG);
+  emit_addimm(HOST_TEMPREG,-CLOCK_ADJUST(adj+1),HOST_TEMPREG);
   emit_writeword(2,(int)&last_count);
   emit_sub(HOST_TEMPREG,2,cc<0?HOST_TEMPREG:cc);
   if(cc<0) {
@@ -3036,6 +3185,67 @@ do_writestub(int n)
   }
   assert(rs>=0);
   assert(rt>=0);
+#ifdef PCSX
+  int rtmp,temp=-1,temp2=HOST_TEMPREG,regs_saved=0,restore_jump=0,ra;
+  int reglist2=reglist|(1<<rs)|(1<<rt);
+  for(rtmp=0;rtmp<=12;rtmp++) {
+    if(((1<<rtmp)&0x13ff)&&((1<<rtmp)&reglist2)==0) {
+      temp=rtmp; break;
+    }
+  }
+  if(temp==-1) {
+    save_regs(reglist);
+    regs_saved=1;
+    for(rtmp=0;rtmp<=3;rtmp++)
+      if(rtmp!=rs&&rtmp!=rt)
+        {temp=rtmp;break;}
+  }
+  if((regs_saved||(reglist2&8)==0)&&temp!=3&&rs!=3&&rt!=3)
+    temp2=3;
+  emit_readword((int)&mem_wtab,temp);
+  emit_shrimm(rs,12,temp2);
+  emit_readword_dualindexedx4(temp,temp2,temp2);
+  emit_lsls_imm(temp2,1,temp2);
+  switch(type) {
+    case STOREB_STUB: emit_strccb_dualindexed(temp2,rs,rt); break;
+    case STOREH_STUB: emit_strcch_dualindexed(temp2,rs,rt); break;
+    case STOREW_STUB: emit_strcc_dualindexed(temp2,rs,rt); break;
+    default:          assert(0);
+  }
+  if(regs_saved) {
+    restore_jump=(int)out;
+    emit_jcc(0); // jump to reg restore
+  }
+  else
+    emit_jcc(stubs[n][2]); // return address (invcode check)
+
+  if(!regs_saved)
+    save_regs(reglist);
+  int handler=0;
+  switch(type) {
+    case STOREB_STUB: handler=(int)jump_handler_write8; break;
+    case STOREH_STUB: handler=(int)jump_handler_write16; break;
+    case STOREW_STUB: handler=(int)jump_handler_write32; break;
+  }
+  assert(handler!=0);
+  pass_args(rs,rt);
+  if(temp2!=3)
+    emit_mov(temp2,3);
+  int cc=get_reg(i_regmap,CCREG);
+  if(cc<0)
+    emit_loadreg(CCREG,2);
+  emit_addimm(cc<0?2:cc,CLOCK_ADJUST((int)stubs[n][6]+1),2);
+  // returns new cycle_count
+  emit_call(handler);
+  emit_addimm(0,-CLOCK_ADJUST((int)stubs[n][6]+1),cc<0?2:cc);
+  if(cc<0)
+    emit_storereg(CCREG,2);
+  if(restore_jump)
+    set_jump_target(restore_jump,(int)out);
+  restore_regs(reglist);
+  ra=stubs[n][2];
+  emit_jmp(ra);
+#else // if !PCSX
   if(addr<0) addr=get_reg(i_regmap,-1);
   assert(addr>=0);
   int ftable=0;
@@ -3107,6 +3317,7 @@ do_writestub(int n)
   //  emit_loadreg(CCREG,cc);
   //}
   emit_jmp(stubs[n][2]); // return address
+#endif // !PCSX
 }
 
 inline_writestub(int type, int i, u_int addr, signed char regmap[], int target, int adj, u_int reglist)
@@ -3117,9 +3328,35 @@ inline_writestub(int type, int i, u_int addr, signed char regmap[], int target,
   assert(rs>=0);
   assert(rt>=0);
 #ifdef PCSX
-  if(pcsx_direct_write(type,addr,rs,rt,regmap))
+  u_int handler,host_addr=0;
+  handler=get_direct_memhandler(mem_wtab,addr,type,&host_addr);
+  if (handler==0) {
+    if(addr!=host_addr)
+      emit_movimm_from(addr,rs,host_addr,rs);
+    switch(type) {
+      case STOREB_STUB: emit_writebyte_indexed(rt,0,rs); break;
+      case STOREH_STUB: emit_writehword_indexed(rt,0,rs); break;
+      case STOREW_STUB: emit_writeword_indexed(rt,0,rs); break;
+      default:          assert(0);
+    }
     return;
-#endif
+  }
+
+  // call a memhandler
+  save_regs(reglist);
+  pass_args(rs,rt);
+  int cc=get_reg(regmap,CCREG);
+  if(cc<0)
+    emit_loadreg(CCREG,2);
+  emit_addimm(cc<0?2:cc,CLOCK_ADJUST(adj+1),2);
+  emit_movimm(handler,3);
+  // returns new cycle_count
+  emit_call((int)jump_handler_write_h);
+  emit_addimm(0,-CLOCK_ADJUST(adj+1),cc<0?2:cc);
+  if(cc<0)
+    emit_storereg(CCREG,2);
+  restore_regs(reglist);
+#else // if !pcsx
   int ftable=0;
   if(type==STOREB_STUB)
     ftable=(int)writememb;
@@ -3172,7 +3409,7 @@ inline_writestub(int type, int i, u_int addr, signed char regmap[], int target,
   //emit_movimm(ftable,0);
   emit_movimm(((u_int *)ftable)[addr>>16],0);
   //emit_readword((int)&last_count,12);
-  emit_addimm(cc<0?2:cc,CLOCK_DIVIDER*(adj+1),2);
+  emit_addimm(cc<0?2:cc,CLOCK_ADJUST(adj+1),2);
 #ifndef PCSX
   if((signed int)addr>=(signed int)0xC0000000) {
     // Pagefault address
@@ -3186,7 +3423,7 @@ inline_writestub(int type, int i, u_int addr, signed char regmap[], int target,
   emit_call((int)&indirect_jump);
   emit_readword((int)&Count,HOST_TEMPREG);
   emit_readword((int)&next_interupt,2);
-  emit_addimm(HOST_TEMPREG,-CLOCK_DIVIDER*(adj+1),HOST_TEMPREG);
+  emit_addimm(HOST_TEMPREG,-CLOCK_ADJUST(adj+1),HOST_TEMPREG);
   emit_writeword(2,(int)&last_count);
   emit_sub(HOST_TEMPREG,2,cc<0?HOST_TEMPREG:cc);
   if(cc<0) {
@@ -3194,6 +3431,7 @@ inline_writestub(int type, int i, u_int addr, signed char regmap[], int target,
   }
   //emit_popa();
   restore_regs(reglist);
+#endif
 }
 
 do_unalignedwritestub(int n)
@@ -3217,6 +3455,21 @@ do_unalignedwritestub(int n)
   reglist|=(1<<addr);
   reglist&=~(1<<temp2);
 
+#if 1
+  // don't bother with it and call write handler
+  save_regs(reglist);
+  pass_args(addr,rt);
+  int cc=get_reg(i_regmap,CCREG);
+  if(cc<0)
+    emit_loadreg(CCREG,2);
+  emit_addimm(cc<0?2:cc,CLOCK_ADJUST((int)stubs[n][6]+1),2);
+  emit_call((int)(opcode[i]==0x2a?jump_handle_swl:jump_handle_swr));
+  emit_addimm(0,-CLOCK_ADJUST((int)stubs[n][6]+1),cc<0?2:cc);
+  if(cc<0)
+    emit_storereg(CCREG,2);
+  restore_regs(reglist);
+  emit_jmp(stubs[n][2]); // return address
+#else
   emit_andimm(addr,0xfffffffc,temp2);
   emit_writeword(temp2,(int)&address);
 
@@ -3279,6 +3532,7 @@ do_unalignedwritestub(int n)
   }
   restore_regs(reglist);
   emit_jmp(stubs[n][2]); // return address
+#endif
 }
 
 void printregs(int edi,int esi,int ebp,int esp,int b,int d,int c,int a)
@@ -3361,10 +3615,12 @@ do_cop1stub(int n)
   wb_dirtys(i_regs->regmap_entry,i_regs->was32,i_regs->wasdirty);
   if(regs[i].regmap_entry[HOST_CCREG]!=CCREG) emit_loadreg(CCREG,HOST_CCREG);
   emit_movimm(start+(i-ds)*4,EAX); // Get PC
-  emit_addimm(HOST_CCREG,CLOCK_DIVIDER*ccadj[i],HOST_CCREG); // CHECK: is this right?  There should probably be an extra cycle...
+  emit_addimm(HOST_CCREG,CLOCK_ADJUST(ccadj[i]),HOST_CCREG); // CHECK: is this right?  There should probably be an extra cycle...
   emit_jmp(ds?(int)fp_exception_ds:(int)fp_exception);
 }
 
+#ifndef DISABLE_TLB
+
 /* TLB */
 
 int do_tlb_r(int s,int ar,int map,int x,int a,int shift,int c,u_int addr)
@@ -3448,6 +3704,17 @@ generate_map_const(u_int addr,int reg) {
   emit_movimm((addr>>12)+(((u_int)memory_map-(u_int)&dynarec_local)>>2),reg);
 }
 
+#else
+
+static int do_tlb_r() { return 0; }
+static int do_tlb_r_branch() { return 0; }
+static int gen_tlb_addr_r() { return 0; }
+static int do_tlb_w() { return 0; }
+static int do_tlb_w_branch() { return 0; }
+static int gen_tlb_addr_w() { return 0; }
+
+#endif // DISABLE_TLB
+
 /* Special assem */
 
 void shift_assemble_arm(int i,struct regstat *i_regs)
@@ -3559,6 +3826,182 @@ void shift_assemble_arm(int i,struct regstat *i_regs)
     }
   }
 }
+
+#ifdef PCSX
+static void speculate_mov(int rs,int rt)
+{
+  if(rt!=0) {
+    smrv_strong_next|=1<<rt;
+    smrv[rt]=smrv[rs];
+  }
+}
+
+static void speculate_mov_weak(int rs,int rt)
+{
+  if(rt!=0) {
+    smrv_weak_next|=1<<rt;
+    smrv[rt]=smrv[rs];
+  }
+}
+
+static void speculate_register_values(int i)
+{
+  if(i==0) {
+    memcpy(smrv,psxRegs.GPR.r,sizeof(smrv));
+    // gp,sp are likely to stay the same throughout the block
+    smrv_strong_next=(1<<28)|(1<<29)|(1<<30);
+    smrv_weak_next=~smrv_strong_next;
+    //printf(" llr %08x\n", smrv[4]);
+  }
+  smrv_strong=smrv_strong_next;
+  smrv_weak=smrv_weak_next;
+  switch(itype[i]) {
+    case ALU:
+      if     ((smrv_strong>>rs1[i])&1) speculate_mov(rs1[i],rt1[i]);
+      else if((smrv_strong>>rs2[i])&1) speculate_mov(rs2[i],rt1[i]);
+      else if((smrv_weak>>rs1[i])&1) speculate_mov_weak(rs1[i],rt1[i]);
+      else if((smrv_weak>>rs2[i])&1) speculate_mov_weak(rs2[i],rt1[i]);
+      else {
+        smrv_strong_next&=~(1<<rt1[i]);
+        smrv_weak_next&=~(1<<rt1[i]);
+      }
+      break;
+    case SHIFTIMM:
+      smrv_strong_next&=~(1<<rt1[i]);
+      smrv_weak_next&=~(1<<rt1[i]);
+      // fallthrough
+    case IMM16:
+      if(rt1[i]&&is_const(&regs[i],rt1[i])) {
+        int value,hr=get_reg(regs[i].regmap,rt1[i]);
+        if(hr>=0) {
+          if(get_final_value(hr,i,&value))
+               smrv[rt1[i]]=value;
+          else smrv[rt1[i]]=constmap[i][hr];
+          smrv_strong_next|=1<<rt1[i];
+        }
+      }
+      else {
+        if     ((smrv_strong>>rs1[i])&1) speculate_mov(rs1[i],rt1[i]);
+        else if((smrv_weak>>rs1[i])&1) speculate_mov_weak(rs1[i],rt1[i]);
+      }
+      break;
+    case LOAD:
+      if(start<0x2000&&(rt1[i]==26||(smrv[rt1[i]]>>24)==0xa0)) {
+        // special case for BIOS
+        smrv[rt1[i]]=0xa0000000;
+        smrv_strong_next|=1<<rt1[i];
+        break;
+      }
+      // fallthrough
+    case SHIFT:
+    case LOADLR:
+    case MOV:
+      smrv_strong_next&=~(1<<rt1[i]);
+      smrv_weak_next&=~(1<<rt1[i]);
+      break;
+    case COP0:
+    case COP2:
+      if(opcode2[i]==0||opcode2[i]==2) { // MFC/CFC
+        smrv_strong_next&=~(1<<rt1[i]);
+        smrv_weak_next&=~(1<<rt1[i]);
+      }
+      break;
+    case C2LS:
+      if (opcode[i]==0x32) { // LWC2
+        smrv_strong_next&=~(1<<rt1[i]);
+        smrv_weak_next&=~(1<<rt1[i]);
+      }
+      break;
+  }
+#if 0
+  int r=4;
+  printf("x %08x %08x %d %d c %08x %08x\n",smrv[r],start+i*4,
+    ((smrv_strong>>r)&1),(smrv_weak>>r)&1,regs[i].isconst,regs[i].wasconst);
+#endif
+}
+
+enum {
+  MTYPE_8000 = 0,
+  MTYPE_8020,
+  MTYPE_0000,
+  MTYPE_A000,
+  MTYPE_1F80,
+};
+
+static int get_ptr_mem_type(u_int a)
+{
+  if(a < 0x00200000) {
+    if(a<0x1000&&((start>>20)==0xbfc||(start>>24)==0xa0))
+      // return wrong, must use memhandler for BIOS self-test to pass
+      // 007 does similar stuff from a00 mirror, weird stuff
+      return MTYPE_8000;
+    return MTYPE_0000;
+  }
+  if(0x1f800000 <= a && a < 0x1f801000)
+    return MTYPE_1F80;
+  if(0x80200000 <= a && a < 0x80800000)
+    return MTYPE_8020;
+  if(0xa0000000 <= a && a < 0xa0200000)
+    return MTYPE_A000;
+  return MTYPE_8000;
+}
+#endif
+
+static int emit_fastpath_cmp_jump(int i,int addr,int *addr_reg_override)
+{
+  int jaddr,type=0;
+
+#ifdef PCSX
+  int mr=rs1[i];
+  if(((smrv_strong|smrv_weak)>>mr)&1) {
+    type=get_ptr_mem_type(smrv[mr]);
+    //printf("set %08x @%08x r%d %d\n", smrv[mr], start+i*4, mr, type);
+  }
+  else {
+    // use the mirror we are running on
+    type=get_ptr_mem_type(start);
+    //printf("set nospec   @%08x r%d %d\n", start+i*4, mr, type);
+  }
+
+  if(type==MTYPE_8020) { // RAM 80200000+ mirror
+    emit_andimm(addr,~0x00e00000,HOST_TEMPREG);
+    addr=*addr_reg_override=HOST_TEMPREG;
+    type=0;
+  }
+  else if(type==MTYPE_0000) { // RAM 0 mirror
+    emit_orimm(addr,0x80000000,HOST_TEMPREG);
+    addr=*addr_reg_override=HOST_TEMPREG;
+    type=0;
+  }
+  else if(type==MTYPE_A000) { // RAM A mirror
+    emit_andimm(addr,~0x20000000,HOST_TEMPREG);
+    addr=*addr_reg_override=HOST_TEMPREG;
+    type=0;
+  }
+  else if(type==MTYPE_1F80) { // scratchpad
+    emit_addimm(addr,-0x1f800000,HOST_TEMPREG);
+    emit_cmpimm(HOST_TEMPREG,0x1000);
+    jaddr=(int)out;
+    emit_jc(0);
+  }
+#endif
+
+  if(type==0)
+  {
+    emit_cmpimm(addr,RAM_SIZE);
+    jaddr=(int)out;
+    #ifdef CORTEX_A8_BRANCH_PREDICTION_HACK
+    // Hint to branch predictor that the branch is unlikely to be taken
+    if(rs1[i]>=28)
+      emit_jno_unlikely(0);
+    else
+    #endif
+      emit_jno(0);
+  }
+
+  return jaddr;
+}
+
 #define shift_assemble shift_assemble_arm
 
 void loadlr_assemble_arm(int i,struct regstat *i_regs)
@@ -3567,6 +4010,7 @@ void loadlr_assemble_arm(int i,struct regstat *i_regs)
   int offset;
   int jaddr=0;
   int memtarget=0,c=0;
+  int fastload_reg_override=0;
   u_int hr,reglist=0;
   th=get_reg(i_regs->regmap,rt1[i]|64);
   tl=get_reg(i_regs->regmap,rt1[i]);
@@ -3601,9 +4045,7 @@ void loadlr_assemble_arm(int i,struct regstat *i_regs)
       }else{
         emit_andimm(addr,0xFFFFFFF8,temp2); // LDL/LDR
       }
-      emit_cmpimm(addr,RAM_SIZE);
-      jaddr=(int)out;
-      emit_jno(0);
+      jaddr=emit_fastpath_cmp_jump(i,temp2,&fastload_reg_override);
     }
     else {
       if (opcode[i]==0x22||opcode[i]==0x26) {
@@ -3636,8 +4078,10 @@ void loadlr_assemble_arm(int i,struct regstat *i_regs)
   }
   if (opcode[i]==0x22||opcode[i]==0x26) { // LWL/LWR
     if(!c||memtarget) {
+      int a=temp2;
+      if(fastload_reg_override) a=fastload_reg_override;
       //emit_readword_indexed((int)rdram-0x80000000,temp2,temp2);
-      emit_readword_indexed_tlb(0,temp2,map,temp2);
+      emit_readword_indexed_tlb(0,a,map,temp2);
       if(jaddr) add_stub(LOADW_STUB,jaddr,(int)out,i,temp2,(int)i_regs,ccadj[i],reglist);
     }
     else
@@ -3664,7 +4108,7 @@ void loadlr_assemble_arm(int i,struct regstat *i_regs)
     //emit_storereg(rt1[i],tl); // DEBUG
   }
   if (opcode[i]==0x1A||opcode[i]==0x1B) { // LDL/LDR
-    // FIXME: little endian
+    // FIXME: little endian, fastload_reg_override
     int temp2h=get_reg(i_regs->regmap,FTEMP|64);
     if(!c||memtarget) {
       //if(th>=0) emit_readword_indexed((int)rdram-0x80000000,temp2,temp2h);
@@ -3726,7 +4170,7 @@ void cop0_assemble(int i,struct regstat *i_regs)
         emit_readword((int)&last_count,ECX);
         emit_loadreg(CCREG,HOST_CCREG); // TODO: do proper reg alloc
         emit_add(HOST_CCREG,ECX,HOST_CCREG);
-        emit_addimm(HOST_CCREG,CLOCK_DIVIDER*ccadj[i],HOST_CCREG);
+        emit_addimm(HOST_CCREG,CLOCK_ADJUST(ccadj[i]),HOST_CCREG);
         emit_writeword(HOST_CCREG,(int)&Count);
       }
       emit_call((int)MFC0);
@@ -3741,19 +4185,21 @@ void cop0_assemble(int i,struct regstat *i_regs)
     signed char s=get_reg(i_regs->regmap,rs1[i]);
     char copr=(source[i]>>11)&0x1f;
     assert(s>=0);
+#ifdef MUPEN64
     emit_writeword(s,(int)&readmem_dword);
     wb_register(rs1[i],i_regs->regmap,i_regs->dirty,i_regs->is32);
-#ifdef MUPEN64
     emit_addimm(FP,(int)&fake_pc-(int)&dynarec_local,0);
     emit_movimm((source[i]>>11)&0x1f,1);
     emit_writeword(0,(int)&PC);
     emit_writebyte(1,(int)&(fake_pc.f.r.nrd));
+#else
+    wb_register(rs1[i],i_regs->regmap,i_regs->dirty,i_regs->is32);
 #endif
     if(copr==9||copr==11||copr==12||copr==13) {
-      emit_readword((int)&last_count,ECX);
+      emit_readword((int)&last_count,HOST_TEMPREG);
       emit_loadreg(CCREG,HOST_CCREG); // TODO: do proper reg alloc
-      emit_add(HOST_CCREG,ECX,HOST_CCREG);
-      emit_addimm(HOST_CCREG,CLOCK_DIVIDER*ccadj[i],HOST_CCREG);
+      emit_add(HOST_CCREG,HOST_TEMPREG,HOST_CCREG);
+      emit_addimm(HOST_CCREG,CLOCK_ADJUST(ccadj[i]),HOST_CCREG);
       emit_writeword(HOST_CCREG,(int)&Count);
     }
     // What a mess.  The status register (12) can enable interrupts,
@@ -3769,19 +4215,25 @@ 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);
+        emit_loadreg(rs1[i],1);
         emit_movimm(copr,0);
         emit_call((int)pcsx_mtc0_ds);
+        emit_loadreg(rs1[i],s);
         return;
       }
 #endif
-      emit_movimm(start+i*4+4,0);
-      emit_movimm(0,1);
-      emit_writeword(0,(int)&pcaddr);
-      emit_writeword(1,(int)&pending_exception);
+      emit_movimm(start+i*4+4,HOST_TEMPREG);
+      emit_writeword(HOST_TEMPREG,(int)&pcaddr);
+      emit_movimm(0,HOST_TEMPREG);
+      emit_writeword(HOST_TEMPREG,(int)&pending_exception);
     }
     //else if(copr==12&&is_delayslot) emit_call((int)MTC0_R12);
     //else
 #ifdef PCSX
+    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);
 #else
@@ -3789,23 +4241,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_addimm(HOST_CCREG,-CLOCK_DIVIDER*ccadj[i],HOST_CCREG);
-      emit_sub(HOST_CCREG,ECX,HOST_CCREG);
-      emit_writeword(ECX,(int)&last_count);
+      emit_readword((int)&next_interupt,HOST_TEMPREG);
+      emit_addimm(HOST_CCREG,-CLOCK_ADJUST(ccadj[i]),HOST_CCREG);
+      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
@@ -3822,7 +4272,7 @@ void cop0_assemble(int i,struct regstat *i_regs)
       emit_readword((int)&last_count,ECX);
       if(i_regs->regmap[HOST_CCREG]!=CCREG) emit_loadreg(CCREG,HOST_CCREG);
       emit_add(HOST_CCREG,ECX,HOST_CCREG);
-      emit_addimm(HOST_CCREG,CLOCK_DIVIDER*ccadj[i],HOST_CCREG);
+      emit_addimm(HOST_CCREG,CLOCK_ADJUST(ccadj[i]),HOST_CCREG);
       emit_writeword(HOST_CCREG,(int)&Count);
       emit_call((int)TLBWR_new);
     }
@@ -3843,7 +4293,7 @@ void cop0_assemble(int i,struct regstat *i_regs)
     {
       int count=ccadj[i];
       if(i_regs->regmap[HOST_CCREG]!=CCREG) emit_loadreg(CCREG,HOST_CCREG);
-      emit_addimm(HOST_CCREG,CLOCK_DIVIDER*count,HOST_CCREG); // TODO: Should there be an extra cycle here?
+      emit_addimm(HOST_CCREG,CLOCK_ADJUST(count),HOST_CCREG); // TODO: Should there be an extra cycle here?
       emit_jmp((int)jump_eret);
     }
 #endif
@@ -3991,36 +4441,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)
@@ -4751,6 +5320,7 @@ void multdiv_assemble_arm(int i,struct regstat *i_regs)
       }
     }
     else // 64-bit
+#ifndef FORCE32
     {
       if(opcode2[i]==0x1C) // DMULT
       {
@@ -4924,6 +5494,9 @@ void multdiv_assemble_arm(int i,struct regstat *i_regs)
         if(lol>=0) emit_loadreg(LOREG,lol);
       }
     }
+#else
+    assert(0);
+#endif
   }
   else
   {