drc: new far call mechanism
[pcsx_rearmed.git] / libpcsxcore / new_dynarec / new_dynarec.c
index 0815329..cfeddc2 100644 (file)
@@ -35,7 +35,8 @@ static int sceBlock;
 #endif
 
 #include "new_dynarec_config.h"
-#include "../psxhle.h" //emulator interface
+#include "../psxhle.h"
+#include "../psxinterpreter.h"
 #include "emu_if.h" //emulator interface
 
 #define noinline __attribute__((noinline,noclone))
@@ -65,6 +66,23 @@ static int sceBlock;
 #define MAXBLOCK 4096
 #define MAX_OUTPUT_BLOCK_SIZE 262144
 
+struct ndrc_mem
+{
+  u_char translation_cache[1 << TARGET_SIZE_2];
+  struct
+  {
+    struct tramp_insns ops[2048 / sizeof(struct tramp_insns)];
+    const void *f[2048 / sizeof(void *)];
+  } tramp;
+};
+
+#ifdef BASE_ADDR_DYNAMIC
+static struct ndrc_mem *ndrc;
+#else
+static struct ndrc_mem ndrc_ __attribute__((aligned(4096)));
+static struct ndrc_mem *ndrc = &ndrc_;
+#endif
+
 // stubs
 enum stub_type {
   CC_STUB = 1,
@@ -166,7 +184,7 @@ struct link_entry
   static char ooo[MAXBLOCK];
   static uint64_t unneeded_reg[MAXBLOCK];
   static uint64_t branch_unneeded_reg[MAXBLOCK];
-  static signed char regmap_pre[MAXBLOCK][HOST_REGS];
+  static signed char regmap_pre[MAXBLOCK][HOST_REGS]; // pre-instruction i?
   static uint64_t current_constmap[HOST_REGS];
   static uint64_t constmap[MAXBLOCK][HOST_REGS];
   static struct regstat regs[MAXBLOCK];
@@ -272,7 +290,7 @@ struct link_entry
 #define DJT_2 (void *)2l
 
 // asm linkage
-int new_recompile_block(int addr);
+int new_recompile_block(u_int addr);
 void *get_addr_ht(u_int vaddr);
 void invalidate_block(u_int block);
 void invalidate_addr(u_int addr);
@@ -284,9 +302,7 @@ void verify_code_ds();
 void cc_interrupt();
 void fp_exception();
 void fp_exception_ds();
-void jump_syscall_hle();
-void jump_hlecall();
-void jump_intcall();
+void jump_to_new_pc();
 void new_dyna_leave();
 
 // Needed by assembler
@@ -298,7 +314,7 @@ static void load_needed_regs(signed char i_regmap[],signed char next_regmap[]);
 static void load_regs_entry(int t);
 static void load_all_consts(signed char regmap[],u_int dirty,int i);
 
-static int verify_dirty(u_int *ptr);
+static int verify_dirty(const u_int *ptr);
 static int get_final_value(int hr, int i, int *value);
 static void add_stub(enum stub_type type, void *addr, void *retaddr,
   u_int a, uintptr_t b, uintptr_t c, u_int d, u_int e);
@@ -309,6 +325,8 @@ static void *emit_fastpath_cmp_jump(int i,int addr,int *addr_reg_override);
 static void *get_direct_memhandler(void *table, u_int addr,
   enum stub_type type, uintptr_t *addr_host);
 static void pass_args(int a0, int a1);
+static void emit_far_jump(const void *f);
+static void emit_far_call(const void *f);
 
 static void mprotect_w_x(void *start, void *end, int is_x)
 {
@@ -361,8 +379,8 @@ static void end_tcache_write(void *start, void *end)
 static void *start_block(void)
 {
   u_char *end = out + MAX_OUTPUT_BLOCK_SIZE;
-  if (end > translation_cache + (1<<TARGET_SIZE_2))
-    end = translation_cache + (1<<TARGET_SIZE_2);
+  if (end > ndrc->translation_cache + sizeof(ndrc->translation_cache))
+    end = ndrc->translation_cache + sizeof(ndrc->translation_cache);
   start_tcache_write(out, end);
   return out;
 }
@@ -832,13 +850,14 @@ static const struct {
   FUNCNAME(jump_handler_write16),
   FUNCNAME(jump_handler_write32),
   FUNCNAME(invalidate_addr),
-  FUNCNAME(verify_code),
-  FUNCNAME(jump_hlecall),
-  FUNCNAME(jump_syscall_hle),
+  FUNCNAME(jump_to_new_pc),
   FUNCNAME(new_dyna_leave),
   FUNCNAME(pcsx_mtc0),
   FUNCNAME(pcsx_mtc0_ds),
   FUNCNAME(do_insn_cmp),
+#ifdef __arm__
+  FUNCNAME(verify_code),
+#endif
 };
 
 static const char *func_name(const void *a)
@@ -866,6 +885,48 @@ static const char *func_name(const void *a)
 #include "assem_arm64.c"
 #endif
 
+static void *get_trampoline(const void *f)
+{
+  size_t i;
+
+  for (i = 0; i < ARRAY_SIZE(ndrc->tramp.f); i++) {
+    if (ndrc->tramp.f[i] == f || ndrc->tramp.f[i] == NULL)
+      break;
+  }
+  if (i == ARRAY_SIZE(ndrc->tramp.f)) {
+    SysPrintf("trampoline table is full, last func %p\n", f);
+    abort();
+  }
+  if (ndrc->tramp.f[i] == NULL) {
+    start_tcache_write(&ndrc->tramp.f[i], &ndrc->tramp.f[i + 1]);
+    ndrc->tramp.f[i] = f;
+    end_tcache_write(&ndrc->tramp.f[i], &ndrc->tramp.f[i + 1]);
+  }
+  return &ndrc->tramp.ops[i];
+}
+
+static void emit_far_jump(const void *f)
+{
+  if (can_jump_or_call(f)) {
+    emit_jmp(f);
+    return;
+  }
+
+  f = get_trampoline(f);
+  emit_jmp(f);
+}
+
+static void emit_far_call(const void *f)
+{
+  if (can_jump_or_call(f)) {
+    emit_call(f);
+    return;
+  }
+
+  f = get_trampoline(f);
+  emit_call(f);
+}
+
 // Add virtual address mapping to linked list
 void ll_add(struct ll_entry **head,int vaddr,void *addr)
 {
@@ -1167,7 +1228,7 @@ static void do_invstub(int n)
   set_jump_target(stubs[n].addr, out);
   save_regs(reglist);
   if(stubs[n].b!=0) emit_mov(stubs[n].b,0);
-  emit_call(invalidate_addr);
+  emit_far_call(invalidate_addr);
   restore_regs(reglist);
   emit_jmp(stubs[n].retaddr); // return address
 }
@@ -2361,11 +2422,44 @@ void shiftimm_assemble(int i,struct regstat *i_regs)
 }
 
 #ifndef shift_assemble
-void shift_assemble(int i,struct regstat *i_regs)
+static void shift_assemble(int i,struct regstat *i_regs)
 {
-  printf("Need shift_assemble for this architecture.\n");
-  abort();
+  signed char s,t,shift;
+  if (rt1[i] == 0)
+    return;
+  assert(opcode2[i]<=0x07); // SLLV/SRLV/SRAV
+  t = get_reg(i_regs->regmap, rt1[i]);
+  s = get_reg(i_regs->regmap, rs1[i]);
+  shift = get_reg(i_regs->regmap, rs2[i]);
+  if (t < 0)
+    return;
+
+  if(rs1[i]==0)
+    emit_zeroreg(t);
+  else if(rs2[i]==0) {
+    assert(s>=0);
+    if(s!=t) emit_mov(s,t);
+  }
+  else {
+    host_tempreg_acquire();
+    emit_andimm(shift,31,HOST_TEMPREG);
+    switch(opcode2[i]) {
+    case 4: // SLLV
+      emit_shl(s,HOST_TEMPREG,t);
+      break;
+    case 6: // SRLV
+      emit_shr(s,HOST_TEMPREG,t);
+      break;
+    case 7: // SRAV
+      emit_sar(s,HOST_TEMPREG,t);
+      break;
+    default:
+      assert(0);
+    }
+    host_tempreg_release();
+  }
 }
+
 #endif
 
 enum {
@@ -2430,7 +2524,7 @@ static void *emit_fastpath_cmp_jump(int i,int addr,int *addr_reg_override)
   else if(type==MTYPE_1F80) { // scratchpad
     if (psxH == (void *)0x1f800000) {
       host_tempreg_acquire();
-      emit_addimm(addr,-0x1f800000,HOST_TEMPREG);
+      emit_xorimm(addr,0x1f800000,HOST_TEMPREG);
       emit_cmpimm(HOST_TEMPREG,0x1000);
       host_tempreg_release();
       jaddr=out;
@@ -2631,10 +2725,86 @@ static void load_assemble(int i,struct regstat *i_regs)
 }
 
 #ifndef loadlr_assemble
-void loadlr_assemble(int i,struct regstat *i_regs)
+static void loadlr_assemble(int i,struct regstat *i_regs)
 {
-  printf("Need loadlr_assemble for this architecture.\n");
-  abort();
+  int s,tl,temp,temp2,addr;
+  int offset;
+  void *jaddr=0;
+  int memtarget=0,c=0;
+  int fastio_reg_override=-1;
+  u_int hr,reglist=0;
+  tl=get_reg(i_regs->regmap,rt1[i]);
+  s=get_reg(i_regs->regmap,rs1[i]);
+  temp=get_reg(i_regs->regmap,-1);
+  temp2=get_reg(i_regs->regmap,FTEMP);
+  addr=get_reg(i_regs->regmap,AGEN1+(i&1));
+  assert(addr<0);
+  offset=imm[i];
+  for(hr=0;hr<HOST_REGS;hr++) {
+    if(i_regs->regmap[hr]>=0) reglist|=1<<hr;
+  }
+  reglist|=1<<temp;
+  if(offset||s<0||c) addr=temp2;
+  else addr=s;
+  if(s>=0) {
+    c=(i_regs->wasconst>>s)&1;
+    if(c) {
+      memtarget=((signed int)(constmap[i][s]+offset))<(signed int)0x80000000+RAM_SIZE;
+    }
+  }
+  if(!c) {
+    emit_shlimm(addr,3,temp);
+    if (opcode[i]==0x22||opcode[i]==0x26) {
+      emit_andimm(addr,0xFFFFFFFC,temp2); // LWL/LWR
+    }else{
+      emit_andimm(addr,0xFFFFFFF8,temp2); // LDL/LDR
+    }
+    jaddr=emit_fastpath_cmp_jump(i,temp2,&fastio_reg_override);
+  }
+  else {
+    if(ram_offset&&memtarget) {
+      host_tempreg_acquire();
+      emit_addimm(temp2,ram_offset,HOST_TEMPREG);
+      fastio_reg_override=HOST_TEMPREG;
+    }
+    if (opcode[i]==0x22||opcode[i]==0x26) {
+      emit_movimm(((constmap[i][s]+offset)<<3)&24,temp); // LWL/LWR
+    }else{
+      emit_movimm(((constmap[i][s]+offset)<<3)&56,temp); // LDL/LDR
+    }
+  }
+  if (opcode[i]==0x22||opcode[i]==0x26) { // LWL/LWR
+    if(!c||memtarget) {
+      int a=temp2;
+      if(fastio_reg_override>=0) a=fastio_reg_override;
+      emit_readword_indexed(0,a,temp2);
+      if(fastio_reg_override==HOST_TEMPREG) host_tempreg_release();
+      if(jaddr) add_stub_r(LOADW_STUB,jaddr,out,i,temp2,i_regs,ccadj[i],reglist);
+    }
+    else
+      inline_readstub(LOADW_STUB,i,(constmap[i][s]+offset)&0xFFFFFFFC,i_regs->regmap,FTEMP,ccadj[i],reglist);
+    if(rt1[i]) {
+      assert(tl>=0);
+      emit_andimm(temp,24,temp);
+      if (opcode[i]==0x22) // LWL
+        emit_xorimm(temp,24,temp);
+      host_tempreg_acquire();
+      emit_movimm(-1,HOST_TEMPREG);
+      if (opcode[i]==0x26) {
+        emit_shr(temp2,temp,temp2);
+        emit_bic_lsr(tl,HOST_TEMPREG,temp,tl);
+      }else{
+        emit_shl(temp2,temp,temp2);
+        emit_bic_lsl(tl,HOST_TEMPREG,temp,tl);
+      }
+      host_tempreg_release();
+      emit_or(temp2,tl,tl);
+    }
+    //emit_storereg(rt1[i],tl); // DEBUG
+  }
+  if (opcode[i]==0x1A||opcode[i]==0x1B) { // LDL/LDR
+    assert(0);
+  }
 }
 #endif
 
@@ -2746,7 +2916,7 @@ void store_assemble(int i,struct regstat *i_regs)
   }
   // basic current block modification detection..
   // not looking back as that should be in mips cache already
-  // (note: doesn't seem to trigger, migh be broken)
+  // (see Spyro2 title->attract mode)
   if(c&&start+i*4<addr_val&&addr_val<start+slen*4) {
     SysPrintf("write to %08x hits block %08x, pc=%08x\n",addr_val,start,start+i*4);
     assert(i_regs->regmap==regs[i].regmap); // not delay slot
@@ -2756,13 +2926,13 @@ void store_assemble(int i,struct regstat *i_regs)
       emit_movimm(start+i*4+4,0);
       emit_writeword(0,&pcaddr);
       emit_addimm(HOST_CCREG,2,HOST_CCREG);
-      emit_call(get_addr_ht);
+      emit_far_call(get_addr_ht);
       emit_jmpreg(0);
     }
   }
 }
 
-void storelr_assemble(int i,struct regstat *i_regs)
+static void storelr_assemble(int i,struct regstat *i_regs)
 {
   int s,tl;
   int temp;
@@ -2802,7 +2972,8 @@ void storelr_assemble(int i,struct regstat *i_regs)
       emit_jmp(0);
     }
   }
-  emit_addimm_no_flags(ram_offset,temp);
+  if(ram_offset)
+    emit_addimm_no_flags(ram_offset,temp);
 
   if (opcode[i]==0x2C||opcode[i]==0x2D) { // SDL/SDR
     assert(0);
@@ -2819,15 +2990,11 @@ void storelr_assemble(int i,struct regstat *i_regs)
   if (opcode[i]==0x2A) { // SWL
     emit_writeword_indexed(tl,0,temp);
   }
-  if (opcode[i]==0x2E) { // SWR
+  else if (opcode[i]==0x2E) { // SWR
     emit_writebyte_indexed(tl,3,temp);
   }
-  if (opcode[i]==0x2C) { // SDL
-    assert(0);
-  }
-  if (opcode[i]==0x2D) { // SDR
+  else
     assert(0);
-  }
   done0=out;
   emit_jmp(0);
   // 1
@@ -2840,16 +3007,10 @@ void storelr_assemble(int i,struct regstat *i_regs)
     emit_writebyte_indexed(tl,1,temp);
     if(rs2[i]) emit_rorimm(tl,8,tl);
   }
-  if (opcode[i]==0x2E) { // SWR
+  else if (opcode[i]==0x2E) { // SWR
     // Write two lsb into two most significant bytes
     emit_writehword_indexed(tl,1,temp);
   }
-  if (opcode[i]==0x2C) { // SDL
-    assert(0);
-  }
-  if (opcode[i]==0x2D) { // SDR
-    assert(0);
-  }
   done1=out;
   emit_jmp(0);
   // 2
@@ -2863,19 +3024,13 @@ void storelr_assemble(int i,struct regstat *i_regs)
     emit_writehword_indexed(tl,-2,temp);
     if(rs2[i]) emit_rorimm(tl,16,tl);
   }
-  if (opcode[i]==0x2E) { // SWR
+  else if (opcode[i]==0x2E) { // SWR
     // Write 3 lsb into three most significant bytes
     emit_writebyte_indexed(tl,-1,temp);
     if(rs2[i]) emit_rorimm(tl,8,tl);
     emit_writehword_indexed(tl,0,temp);
     if(rs2[i]) emit_rorimm(tl,24,tl);
   }
-  if (opcode[i]==0x2C) { // SDL
-    assert(0);
-  }
-  if (opcode[i]==0x2D) { // SDR
-    assert(0);
-  }
   done2=out;
   emit_jmp(0);
   // 3
@@ -2886,25 +3041,13 @@ void storelr_assemble(int i,struct regstat *i_regs)
     emit_writebyte_indexed(tl,-3,temp);
     if(rs2[i]) emit_rorimm(tl,8,tl);
   }
-  if (opcode[i]==0x2E) { // SWR
+  else if (opcode[i]==0x2E) { // SWR
     // Write entire word
     emit_writeword_indexed(tl,-3,temp);
   }
-  if (opcode[i]==0x2C) { // SDL
-    assert(0);
-  }
-  if (opcode[i]==0x2D) { // SDR
-    assert(0);
-  }
   set_jump_target(done0, out);
   set_jump_target(done1, out);
   set_jump_target(done2, out);
-  if (opcode[i]==0x2C) { // SDL
-    assert(0);
-  }
-  if (opcode[i]==0x2D) { // SDR
-    assert(0);
-  }
   if(!c||!memtarget)
     add_stub_r(STORELR_STUB,jaddr,out,i,temp,i_regs,ccadj[i],reglist);
   if(!(i_regs->waswritten&(1<<rs1[i]))&&!(new_dynarec_hacks&NDHACK_NO_SMC_CHECK)) {
@@ -2964,7 +3107,7 @@ static void cop0_assemble(int i,struct regstat *i_regs)
         emit_storereg(CCREG,HOST_CCREG);
         emit_loadreg(rs1[i],1);
         emit_movimm(copr,0);
-        emit_call(pcsx_mtc0_ds);
+        emit_far_call(pcsx_mtc0_ds);
         emit_loadreg(rs1[i],s);
         return;
       }
@@ -2973,14 +3116,12 @@ static void cop0_assemble(int i,struct regstat *i_regs)
       emit_movimm(0,HOST_TEMPREG);
       emit_writeword(HOST_TEMPREG,&pending_exception);
     }
-    //else if(copr==12&&is_delayslot) emit_call((int)MTC0_R12);
-    //else
     if(s==HOST_CCREG)
       emit_loadreg(rs1[i],1);
     else if(s!=1)
       emit_mov(s,1);
     emit_movimm(copr,0);
-    emit_call(pcsx_mtc0);
+    emit_far_call(pcsx_mtc0);
     if(copr==9||copr==11||copr==12||copr==13) {
       emit_readword(&Count,HOST_CCREG);
       emit_readword(&next_interupt,HOST_TEMPREG);
@@ -2997,7 +3138,7 @@ static void cop0_assemble(int i,struct regstat *i_regs)
       emit_jeq(0);
       emit_readword(&pcaddr, 0);
       emit_addimm(HOST_CCREG,2,HOST_CCREG);
-      emit_call(get_addr_ht);
+      emit_far_call(get_addr_ht);
       emit_jmpreg(0);
       set_jump_target(jaddr, out);
     }
@@ -3057,7 +3198,7 @@ static void do_cop1stub(int n)
   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_ADJUST(ccadj[i]),HOST_CCREG); // CHECK: is this right?  There should probably be an extra cycle...
-  emit_jmp(ds?fp_exception_ds:fp_exception);
+  emit_far_jump(ds?fp_exception_ds:fp_exception);
 }
 
 static void cop2_get_dreg(u_int copr,signed char tl,signed char temp)
@@ -3089,22 +3230,7 @@ static void cop2_get_dreg(u_int copr,signed char tl,signed char temp)
       break;
     case 28:
     case 29:
-      emit_readword(&reg_cop2d[9],temp);
-      emit_testimm(temp,0x8000); // do we need this?
-      emit_andimm(temp,0xf80,temp);
-      emit_andne_imm(temp,0,temp);
-      emit_shrimm(temp,7,tl);
-      emit_readword(&reg_cop2d[10],temp);
-      emit_testimm(temp,0x8000);
-      emit_andimm(temp,0xf80,temp);
-      emit_andne_imm(temp,0,temp);
-      emit_orrshr_imm(temp,2,tl);
-      emit_readword(&reg_cop2d[11],temp);
-      emit_testimm(temp,0x8000);
-      emit_andimm(temp,0xf80,temp);
-      emit_andne_imm(temp,0,temp);
-      emit_orrshl_imm(temp,3,tl);
-      emit_writeword(tl,&reg_cop2d[copr]);
+      c2op_mfc2_29_assemble(tl,temp);
       break;
     default:
       emit_readword(&reg_cop2d[copr],tl);
@@ -3136,8 +3262,7 @@ static void cop2_put_dreg(u_int copr,signed char sl,signed char temp)
       emit_writeword(sl,&reg_cop2d[28]);
       break;
     case 30:
-      emit_movs(sl,temp);
-      emit_mvnmi(temp,temp);
+      emit_xorsar_imm(sl,sl,31,temp);
 #if defined(HAVE_ARMV5) || defined(__aarch64__)
       emit_clz(temp,temp);
 #else
@@ -3197,7 +3322,7 @@ static void c2ls_assemble(int i,struct regstat *i_regs)
   assert(ar>=0);
 
   if (opcode[i]==0x3a) { // SWC2
-    cop2_get_dreg(copr,tl,HOST_TEMPREG);
+    cop2_get_dreg(copr,tl,-1);
     type=STOREW_STUB;
   }
   else
@@ -3291,14 +3416,7 @@ static void cop2_assemble(int i,struct regstat *i_regs)
         emit_signextend16(sl,temp);
         break;
       case 31:
-        //value = value & 0x7ffff000;
-        //if (value & 0x7f87e000) value |= 0x80000000;
-        emit_shrimm(sl,12,temp);
-        emit_shlimm(temp,12,temp);
-        emit_testimm(temp,0x7f000000);
-        emit_testeqimm(temp,0x00870000);
-        emit_testeqimm(temp,0x0000e000);
-        emit_orrne_imm(temp,0x80000000,temp);
+        c2op_ctc2_31_assemble(sl,temp);
         break;
       default:
         temp=sl;
@@ -3309,6 +3427,90 @@ static void cop2_assemble(int i,struct regstat *i_regs)
   }
 }
 
+static void do_unalignedwritestub(int n)
+{
+  assem_debug("do_unalignedwritestub %x\n",start+stubs[n].a*4);
+  literal_pool(256);
+  set_jump_target(stubs[n].addr, out);
+
+  int i=stubs[n].a;
+  struct regstat *i_regs=(struct regstat *)stubs[n].c;
+  int addr=stubs[n].b;
+  u_int reglist=stubs[n].e;
+  signed char *i_regmap=i_regs->regmap;
+  int temp2=get_reg(i_regmap,FTEMP);
+  int rt;
+  rt=get_reg(i_regmap,rs2[i]);
+  assert(rt>=0);
+  assert(addr>=0);
+  assert(opcode[i]==0x2a||opcode[i]==0x2e); // SWL/SWR only implemented
+  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].d+1),2);
+  emit_far_call((opcode[i]==0x2a?jump_handle_swl:jump_handle_swr));
+  emit_addimm(0,-CLOCK_ADJUST((int)stubs[n].d+1),cc<0?2:cc);
+  if(cc<0)
+    emit_storereg(CCREG,2);
+  restore_regs(reglist);
+  emit_jmp(stubs[n].retaddr); // return address
+#else
+  emit_andimm(addr,0xfffffffc,temp2);
+  emit_writeword(temp2,&address);
+
+  save_regs(reglist);
+  emit_shrimm(addr,16,1);
+  int cc=get_reg(i_regmap,CCREG);
+  if(cc<0) {
+    emit_loadreg(CCREG,2);
+  }
+  emit_movimm((u_int)readmem,0);
+  emit_addimm(cc<0?2:cc,2*stubs[n].d+2,2);
+  emit_call((int)&indirect_jump_indexed);
+  restore_regs(reglist);
+
+  emit_readword(&readmem_dword,temp2);
+  int temp=addr; //hmh
+  emit_shlimm(addr,3,temp);
+  emit_andimm(temp,24,temp);
+  if (opcode[i]==0x2a) // SWL
+    emit_xorimm(temp,24,temp);
+  emit_movimm(-1,HOST_TEMPREG);
+  if (opcode[i]==0x2a) { // SWL
+    emit_bic_lsr(temp2,HOST_TEMPREG,temp,temp2);
+    emit_orrshr(rt,temp,temp2);
+  }else{
+    emit_bic_lsl(temp2,HOST_TEMPREG,temp,temp2);
+    emit_orrshl(rt,temp,temp2);
+  }
+  emit_readword(&address,addr);
+  emit_writeword(temp2,&word);
+  //save_regs(reglist); // don't need to, no state changes
+  emit_shrimm(addr,16,1);
+  emit_movimm((u_int)writemem,0);
+  //emit_call((int)&indirect_jump_indexed);
+  emit_mov(15,14);
+  emit_readword_dualindexedx4(0,1,15);
+  emit_readword(&Count,HOST_TEMPREG);
+  emit_readword(&next_interupt,2);
+  emit_addimm(HOST_TEMPREG,-2*stubs[n].d-2,HOST_TEMPREG);
+  emit_writeword(2,&last_count);
+  emit_sub(HOST_TEMPREG,2,cc<0?HOST_TEMPREG:cc);
+  if(cc<0) {
+    emit_storereg(CCREG,HOST_TEMPREG);
+  }
+  restore_regs(reglist);
+  emit_jmp(stubs[n].retaddr); // return address
+#endif
+}
+
 #ifndef multdiv_assemble
 void multdiv_assemble(int i,struct regstat *i_regs)
 {
@@ -3333,43 +3535,44 @@ static void mov_assemble(int i,struct regstat *i_regs)
   }
 }
 
-static void syscall_assemble(int i,struct regstat *i_regs)
+// call interpreter, exception handler, things that change pc/regs/cycles ...
+static void call_c_cpu_handler(int i, const struct regstat *i_regs, u_int pc, void *func)
 {
   signed char ccreg=get_reg(i_regs->regmap,CCREG);
   assert(ccreg==HOST_CCREG);
   assert(!is_delayslot);
   (void)ccreg;
-  emit_movimm(start+i*4,EAX); // Get PC
-  emit_addimm(HOST_CCREG,CLOCK_ADJUST(ccadj[i]),HOST_CCREG); // CHECK: is this right?  There should probably be an extra cycle...
-  emit_jmp(jump_syscall_hle); // XXX
+
+  emit_movimm(pc,3); // Get PC
+  emit_readword(&last_count,2);
+  emit_writeword(3,&psxRegs.pc);
+  emit_addimm(HOST_CCREG,CLOCK_ADJUST(ccadj[i]),HOST_CCREG); // XXX
+  emit_add(2,HOST_CCREG,2);
+  emit_writeword(2,&psxRegs.cycle);
+  emit_far_call(func);
+  emit_far_jump(jump_to_new_pc);
+}
+
+static void syscall_assemble(int i,struct regstat *i_regs)
+{
+  emit_movimm(0x20,0); // cause code
+  emit_movimm(0,1);    // not in delay slot
+  call_c_cpu_handler(i,i_regs,start+i*4,psxException);
 }
 
 static void hlecall_assemble(int i,struct regstat *i_regs)
 {
-  extern void psxNULL();
-  signed char ccreg=get_reg(i_regs->regmap,CCREG);
-  assert(ccreg==HOST_CCREG);
-  assert(!is_delayslot);
-  (void)ccreg;
-  emit_movimm(start+i*4+4,0); // Get PC
+  void *hlefunc = psxNULL;
   uint32_t hleCode = source[i] & 0x03ffffff;
-  if (hleCode >= ARRAY_SIZE(psxHLEt))
-    emit_movimm((uintptr_t)psxNULL,1);
-  else
-    emit_movimm((uintptr_t)psxHLEt[hleCode],1);
-  emit_addimm(HOST_CCREG,CLOCK_ADJUST(ccadj[i]),HOST_CCREG); // XXX
-  emit_jmp(jump_hlecall);
+  if (hleCode < ARRAY_SIZE(psxHLEt))
+    hlefunc = psxHLEt[hleCode];
+
+  call_c_cpu_handler(i,i_regs,start+i*4+4,hlefunc);
 }
 
 static void intcall_assemble(int i,struct regstat *i_regs)
 {
-  signed char ccreg=get_reg(i_regs->regmap,CCREG);
-  assert(ccreg==HOST_CCREG);
-  assert(!is_delayslot);
-  (void)ccreg;
-  emit_movimm(start+i*4,0); // Get PC
-  emit_addimm(HOST_CCREG,CLOCK_ADJUST(ccadj[i]),HOST_CCREG);
-  emit_jmp(jump_intcall);
+  call_c_cpu_handler(i,i_regs,start+i*4,execI);
 }
 
 static void speculate_mov(int rs,int rt)
@@ -4075,7 +4278,7 @@ static int match_bt(signed char i_regmap[],uint64_t i_dirty,int addr)
 static void drc_dbg_emit_do_cmp(int i)
 {
   extern void do_insn_cmp();
-  extern int cycle;
+  //extern int cycle;
   u_int hr,reglist=0;
 
   for(hr=0;hr<HOST_REGS;hr++)
@@ -4083,10 +4286,11 @@ static void drc_dbg_emit_do_cmp(int i)
   save_regs(reglist);
   emit_movimm(start+i*4,0);
   emit_writeword(0,&pcaddr);
-  emit_call(do_insn_cmp);
+  emit_far_call(do_insn_cmp);
   //emit_readword(&cycle,0);
   //emit_addimm(0,2,0);
   //emit_writeword(0,&cycle);
+  (void)get_reg2;
   restore_regs(reglist);
 }
 #else
@@ -4413,7 +4617,7 @@ static void do_ccstub(int n)
   // Update cycle count
   assert(branch_regs[i].regmap[HOST_CCREG]==CCREG||branch_regs[i].regmap[HOST_CCREG]==-1);
   if(stubs[n].a) emit_addimm(HOST_CCREG,CLOCK_ADJUST((signed int)stubs[n].a),HOST_CCREG);
-  emit_call(cc_interrupt);
+  emit_far_call(cc_interrupt);
   if(stubs[n].a) emit_addimm(HOST_CCREG,-CLOCK_ADJUST((signed int)stubs[n].a),HOST_CCREG);
   if(stubs[n].d==TAKEN) {
     if(internal_branch(ba[i]))
@@ -4667,6 +4871,9 @@ static void cjump_assemble(int i,struct regstat *i_regs)
   #ifdef CORTEX_A8_BRANCH_PREDICTION_HACK
   if(i>(ba[i]-start)>>2) invert=1;
   #endif
+  #ifdef __aarch64__
+  invert=1; // because of near cond. branches
+  #endif
 
   if(ooo[i]) {
     s1l=get_reg(branch_regs[i].regmap,rs1[i]);
@@ -4956,6 +5163,9 @@ static void sjump_assemble(int i,struct regstat *i_regs)
   #ifdef CORTEX_A8_BRANCH_PREDICTION_HACK
   if(i>(ba[i]-start)>>2) invert=1;
   #endif
+  #ifdef __aarch64__
+  invert=1; // because of near cond. branches
+  #endif
 
   //if(opcode2[i]>=0x10) return; // FIXME (BxxZAL)
   //assert(opcode2[i]<0x10||rs1[i]==0); // FIXME (BxxZAL)
@@ -6247,7 +6457,7 @@ static void new_dynarec_test(void)
   ((volatile u_int *)out)[0]++; // make cache dirty
 
   for (i = 0; i < ARRAY_SIZE(ret); i++) {
-    out = translation_cache;
+    out = ndrc->translation_cache;
     beginning = start_block();
     emit_movimm(DRC_TEST_VAL + i, 0); // test
     emit_ret();
@@ -6261,7 +6471,7 @@ static void new_dynarec_test(void)
     SysPrintf("test passed.\n");
   else
     SysPrintf("test failed, will likely crash soon (r=%08x %08x)\n", ret[0], ret[1]);
-  out = translation_cache;
+  out = ndrc->translation_cache;
 }
 
 // clear the state completely, instead of just marking
@@ -6269,7 +6479,7 @@ static void new_dynarec_test(void)
 void new_dynarec_clear_full()
 {
   int n;
-  out = translation_cache;
+  out = ndrc->translation_cache;
   memset(invalid_code,1,sizeof(invalid_code));
   memset(hash_table,0xff,sizeof(hash_table));
   memset(mini_ht,-1,sizeof(mini_ht));
@@ -6291,30 +6501,24 @@ void new_dynarec_init()
 {
   SysPrintf("Init new dynarec\n");
 
-  // allocate/prepare a buffer for translation cache
-  // see assem_arm.h for some explanation
-#if   defined(BASE_ADDR_FIXED)
-  if (mmap(translation_cache, 1 << TARGET_SIZE_2,
-            PROT_READ | PROT_WRITE | PROT_EXEC,
-            MAP_PRIVATE | MAP_ANONYMOUS,
-            -1, 0) != translation_cache) {
-    SysPrintf("mmap() failed: %s\n", strerror(errno));
-    SysPrintf("disable BASE_ADDR_FIXED and recompile\n");
-    abort();
-  }
-#elif defined(BASE_ADDR_DYNAMIC)
+#ifdef BASE_ADDR_DYNAMIC
   #ifdef VITA
   sceBlock = sceKernelAllocMemBlockForVM("code", 1 << TARGET_SIZE_2);
   if (sceBlock < 0)
     SysPrintf("sceKernelAllocMemBlockForVM failed\n");
-  int ret = sceKernelGetMemBlockBase(sceBlock, (void **)&translation_cache);
+  int ret = sceKernelGetMemBlockBase(sceBlock, (void **)&ndrc);
   if (ret < 0)
     SysPrintf("sceKernelGetMemBlockBase failed\n");
   #else
-  translation_cache = mmap (NULL, 1 << TARGET_SIZE_2,
+  uintptr_t desired_addr = 0;
+  #ifdef __ELF__
+  extern char _end;
+  desired_addr = ((uintptr_t)&_end + 0xffffff) & ~0xffffffl;
+  #endif
+  ndrc = mmap((void *)desired_addr, sizeof(*ndrc),
             PROT_READ | PROT_WRITE | PROT_EXEC,
             MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-  if (translation_cache == MAP_FAILED) {
+  if (ndrc == MAP_FAILED) {
     SysPrintf("mmap() failed: %s\n", strerror(errno));
     abort();
   }
@@ -6322,11 +6526,12 @@ void new_dynarec_init()
 #else
   #ifndef NO_WRITE_EXEC
   // not all systems allow execute in data segment by default
-  if (mprotect(translation_cache, 1<<TARGET_SIZE_2, PROT_READ | PROT_WRITE | PROT_EXEC) != 0)
+  if (mprotect(ndrc, sizeof(ndrc->translation_cache) + sizeof(ndrc->tramp.ops),
+               PROT_READ | PROT_WRITE | PROT_EXEC) != 0)
     SysPrintf("mprotect() failed: %s\n", strerror(errno));
   #endif
 #endif
-  out = translation_cache;
+  out = ndrc->translation_cache;
   cycle_multiplier=200;
   new_dynarec_clear_full();
 #ifdef HOST_IMM8
@@ -6345,12 +6550,12 @@ void new_dynarec_init()
 void new_dynarec_cleanup()
 {
   int n;
-#if defined(BASE_ADDR_FIXED) || defined(BASE_ADDR_DYNAMIC)
+#ifdef BASE_ADDR_DYNAMIC
   #ifdef VITA
   sceKernelFreeMemBlock(sceBlock);
   sceBlock = -1;
   #else
-  if (munmap(translation_cache, 1<<TARGET_SIZE_2) < 0)
+  if (munmap(ndrc, sizeof(*ndrc)) < 0)
     SysPrintf("munmap() failed\n");
   #endif
 #endif
@@ -6484,7 +6689,7 @@ void new_dynarec_load_blocks(const void *save, int size)
   memcpy(&psxRegs.GPR, regs_save, sizeof(regs_save));
 }
 
-int new_recompile_block(int addr)
+int new_recompile_block(u_int addr)
 {
   u_int pagelimit = 0;
   u_int state_rflags = 0;
@@ -6513,7 +6718,7 @@ int new_recompile_block(int addr)
     invalid_code[start>>12]=0;
     emit_movimm(start,0);
     emit_writeword(0,&pcaddr);
-    emit_jmp(new_dyna_leave);
+    emit_far_jump(new_dyna_leave);
     literal_pool(0);
     end_block(beginning);
     ll_add_flags(jump_in+page,start,state_rflags,(void *)beginning);
@@ -7891,7 +8096,7 @@ int new_recompile_block(int addr)
             {
               if(i<slen-1&&!is_ds[i]) {
                 assert(regs[i].regmap[hr]<64);
-                if(regmap_pre[i+1][hr]!=-1 || regs[i].regmap[hr]!=-1)
+                if(regmap_pre[i+1][hr]!=-1 || regs[i].regmap[hr]>0)
                 if(regmap_pre[i+1][hr]!=regs[i].regmap[hr])
                 {
                   SysPrintf("fail: %x (%d %d!=%d)\n",start+i*4,hr,regmap_pre[i+1][hr],regs[i].regmap[hr]);
@@ -7906,8 +8111,8 @@ int new_recompile_block(int addr)
             }
           }
         }
-      }
-    }
+      } // if needed
+    } // for hr
   }
 
   /* Pass 5 - Pre-allocate registers */
@@ -8533,7 +8738,7 @@ int new_recompile_block(int addr)
   void *instr_addr0_override = NULL;
 
   if (start == 0x80030000) {
-    // nasty hack for fastbios thing
+    // nasty hack for the fastbios thing
     // override block entry to this code
     instr_addr0_override = out;
     emit_movimm(start,0);
@@ -8543,7 +8748,12 @@ int new_recompile_block(int addr)
     emit_writeword(0,&pcaddr);
     emit_writeword(0,&address);
     emit_cmp(0,1);
+    #ifdef __aarch64__
+    emit_jeq(out + 4*2);
+    emit_far_jump(new_dyna_leave);
+    #else
     emit_jne(new_dyna_leave);
+    #endif
   }
   for(i=0;i<slen;i++)
   {
@@ -8812,8 +9022,8 @@ int new_recompile_block(int addr)
 
   // If we're within 256K of the end of the buffer,
   // start over from the beginning. (Is 256K enough?)
-  if (out > translation_cache+(1<<TARGET_SIZE_2)-MAX_OUTPUT_BLOCK_SIZE)
-    out = translation_cache;
+  if (out > ndrc->translation_cache + sizeof(ndrc->translation_cache) - MAX_OUTPUT_BLOCK_SIZE)
+    out = ndrc->translation_cache;
 
   // Trap writes to any of the pages we compiled
   for(i=start>>12;i<=(start+slen*4)>>12;i++) {
@@ -8830,11 +9040,11 @@ int new_recompile_block(int addr)
 
   /* Pass 10 - Free memory by expiring oldest blocks */
 
-  int end=(((out-translation_cache)>>(TARGET_SIZE_2-16))+16384)&65535;
+  int end=(((out-ndrc->translation_cache)>>(TARGET_SIZE_2-16))+16384)&65535;
   while(expirep!=end)
   {
     int shift=TARGET_SIZE_2-3; // Divide into 8 blocks
-    uintptr_t base=(uintptr_t)translation_cache+((expirep>>13)<<shift); // Base address of this block
+    uintptr_t base=(uintptr_t)ndrc->translation_cache+((expirep>>13)<<shift); // Base address of this block
     inv_debug("EXP: Phase %d\n",expirep);
     switch((expirep>>11)&3)
     {