"Might be useful to overcome some dynarec bugs";
static const char h_cfg_shacks[] = "Breaks games but may give better performance\n"
"must reload game for any change to take effect";
+#ifdef ICACHE_EMULATION
static const char h_cfg_icache[] = "Allows you to play the F1 games.\n"
"Note: This breaks the PAL version of Spyro 2.";
+#endif
static menu_entry e_menu_adv_options[] =
{
case 28:
gteIRGB = value;
- gteIR1 = (value & 0x1f) << 7;
- gteIR2 = (value & 0x3e0) << 2;
- gteIR3 = (value & 0x7c00) >> 3;
+ // not gteIR1 etc. just to be consistent with dynarec
+ regs->CP2D.n.ir1 = (value & 0x1f) << 7;
+ regs->CP2D.n.ir2 = (value & 0x3e0) << 2;
+ regs->CP2D.n.ir3 = (value & 0x7c00) >> 3;
break;
case 30:
return ptr;
}
-static int verify_dirty(u_int *ptr)
+static int verify_dirty(const u_int *ptr)
{
#ifndef HAVE_ARMV7
u_int offset;
output_w32(0xe1e00000|rd_rn_rm(rt,0,rs));
}
-static void emit_mvnmi(int rs,int rt)
-{
- assem_debug("mvnmi %s,%s\n",regname[rt],regname[rs]);
- output_w32(0x41e00000|rd_rn_rm(rt,0,rs));
-}
-
static void emit_and(u_int rs1,u_int rs2,u_int rt)
{
assem_debug("and %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
output_w32(0xe0200000|rd_rn_rm(rt,rs1,rs2));
}
+static void emit_xorsar_imm(u_int rs1,u_int rs2,u_int imm,u_int rt)
+{
+ assem_debug("eor %s,%s,%s,asr #%d\n",regname[rt],regname[rs1],regname[rs2],imm);
+ output_w32(0xe0200040|rd_rn_rm(rt,rs1,rs2)|(imm<<7));
+}
+
static void emit_addimm(u_int rs,int imm,u_int rt)
{
assert(rs<16);
output_w32(0xe1a00000|rd_rn_rm(rt,0,rs)|0x50|(shift<<8));
}
-static void emit_orrshl(u_int rs,u_int shift,u_int rt)
+static unused void emit_orrshl(u_int rs,u_int shift,u_int rt)
{
assert(rs<16);
assert(rt<16);
output_w32(0xe1800000|rd_rn_rm(rt,rt,rs)|0x10|(shift<<8));
}
-static void emit_orrshr(u_int rs,u_int shift,u_int rt)
+static unused void emit_orrshr(u_int rs,u_int shift,u_int rt)
{
assert(rs<16);
assert(rt<16);
output_w32(0x33a00000|rd_rn_rm(rt,0,0)|armval);
}
+static void emit_cmovae_imm(int imm,int rt)
+{
+ assem_debug("movcs %s,#%d\n",regname[rt],imm);
+ u_int armval;
+ genimm_checked(imm,&armval);
+ output_w32(0x23a00000|rd_rn_rm(rt,0,0)|armval);
+}
+
static void emit_cmovne_reg(int rs,int rt)
{
assem_debug("movne %s,%s\n",regname[rt],regname[rs]);
output_w32(0x3a000000|offset);
}
-static void emit_callreg(u_int r)
+static unused void emit_callreg(u_int r)
{
assert(r<15);
assem_debug("blx %s\n",regname[r]);
output_w32(0xe1300000|rd_rn_rm(0,rs,rt));
}
-static void emit_rsbimm(int rs, int imm, int rt)
+static unused void emit_rsbimm(int rs, int imm, int rt)
{
u_int armval;
genimm_checked(imm,&armval);
restore_regs(reglist);
}
-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_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);
-#ifdef BIG_ENDIAN_MIPS
- if (opcode[i]==0x2e) // SWR
-#else
- if (opcode[i]==0x2a) // SWL
-#endif
- 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
-}
-
// this output is parsed by verify_dirty, get_bounds, isclean, get_clean_addr
static void do_dirty_stub_emit_args(u_int arg0)
{
/* Special assem */
-static void shift_assemble_arm(int i,struct regstat *i_regs)
-{
- if(rt1[i]) {
- if(opcode2[i]<=0x07) // SLLV/SRLV/SRAV
- {
- signed char s,t,shift;
- 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){
- if(rs1[i]==0)
- {
- emit_zeroreg(t);
- }
- else if(rs2[i]==0)
- {
- assert(s>=0);
- if(s!=t) emit_mov(s,t);
- }
- else
- {
- emit_andimm(shift,31,HOST_TEMPREG);
- if(opcode2[i]==4) // SLLV
- {
- emit_shl(s,HOST_TEMPREG,t);
- }
- if(opcode2[i]==6) // SRLV
- {
- emit_shr(s,HOST_TEMPREG,t);
- }
- if(opcode2[i]==7) // SRAV
- {
- emit_sar(s,HOST_TEMPREG,t);
- }
- }
- }
- } else { // DSLLV/DSRLV/DSRAV
- signed char sh,sl,th,tl,shift;
- th=get_reg(i_regs->regmap,rt1[i]|64);
- tl=get_reg(i_regs->regmap,rt1[i]);
- sh=get_reg(i_regs->regmap,rs1[i]|64);
- sl=get_reg(i_regs->regmap,rs1[i]);
- shift=get_reg(i_regs->regmap,rs2[i]);
- if(tl>=0){
- if(rs1[i]==0)
- {
- emit_zeroreg(tl);
- if(th>=0) emit_zeroreg(th);
- }
- else if(rs2[i]==0)
- {
- assert(sl>=0);
- if(sl!=tl) emit_mov(sl,tl);
- if(th>=0&&sh!=th) emit_mov(sh,th);
- }
- else
- {
- // FIXME: What if shift==tl ?
- assert(shift!=tl);
- int temp=get_reg(i_regs->regmap,-1);
- int real_th=th;
- if(th<0&&opcode2[i]!=0x14) {th=temp;} // DSLLV doesn't need a temporary register
- assert(sl>=0);
- assert(sh>=0);
- emit_andimm(shift,31,HOST_TEMPREG);
- if(opcode2[i]==0x14) // DSLLV
- {
- if(th>=0) emit_shl(sh,HOST_TEMPREG,th);
- emit_rsbimm(HOST_TEMPREG,32,HOST_TEMPREG);
- emit_orrshr(sl,HOST_TEMPREG,th);
- emit_andimm(shift,31,HOST_TEMPREG);
- emit_testimm(shift,32);
- emit_shl(sl,HOST_TEMPREG,tl);
- if(th>=0) emit_cmovne_reg(tl,th);
- emit_cmovne_imm(0,tl);
- }
- if(opcode2[i]==0x16) // DSRLV
- {
- assert(th>=0);
- emit_shr(sl,HOST_TEMPREG,tl);
- emit_rsbimm(HOST_TEMPREG,32,HOST_TEMPREG);
- emit_orrshl(sh,HOST_TEMPREG,tl);
- emit_andimm(shift,31,HOST_TEMPREG);
- emit_testimm(shift,32);
- emit_shr(sh,HOST_TEMPREG,th);
- emit_cmovne_reg(th,tl);
- if(real_th>=0) emit_cmovne_imm(0,th);
- }
- if(opcode2[i]==0x17) // DSRAV
- {
- assert(th>=0);
- emit_shr(sl,HOST_TEMPREG,tl);
- emit_rsbimm(HOST_TEMPREG,32,HOST_TEMPREG);
- if(real_th>=0) {
- assert(temp>=0);
- emit_sarimm(th,31,temp);
- }
- emit_orrshl(sh,HOST_TEMPREG,tl);
- emit_andimm(shift,31,HOST_TEMPREG);
- emit_testimm(shift,32);
- emit_sar(sh,HOST_TEMPREG,th);
- emit_cmovne_reg(th,tl);
- if(real_th>=0) emit_cmovne_reg(temp,th);
- }
- }
- }
- }
- }
-}
-#define shift_assemble shift_assemble_arm
-
-static void loadlr_assemble_arm(int i,struct regstat *i_regs)
-{
- 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);
-#ifdef BIG_ENDIAN_MIPS
- if (opcode[i]==0x26) // LWR
-#else
- if (opcode[i]==0x22) // LWL
-#endif
- emit_xorimm(temp,24,temp);
- 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);
- }
- emit_or(temp2,tl,tl);
- }
- //emit_storereg(rt1[i],tl); // DEBUG
- }
- if (opcode[i]==0x1A||opcode[i]==0x1B) { // LDL/LDR
- assert(0);
- }
-}
-#define loadlr_assemble loadlr_assemble_arm
-
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);
+ emit_call(pcnt_gte_start);
#endif
emit_addimm(FP,(int)&psxRegs.CP2D.r[0]-(int)&dynarec_local,0); // cop2 regs
}
{
#ifdef PCNT
emit_movimm(op,0);
- emit_call((int)pcnt_gte_end);
+ emit_call(pcnt_gte_end);
#endif
restore_regs_all(reglist);
}
}
}
+static void c2op_ctc2_31_assemble(signed char sl, signed char temp)
+{
+ //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);
+}
+
+static void do_mfc2_31_one(u_int copr,signed char temp)
+{
+ emit_readword(®_cop2d[copr],temp);
+ emit_testimm(temp,0x8000); // do we need this?
+ emit_andne_imm(temp,0,temp);
+ emit_cmpimm(temp,0xf80);
+ emit_andimm(temp,0xf80,temp);
+ emit_cmovae_imm(0xf80,temp);
+}
+
+static void c2op_mfc2_29_assemble(signed char tl, signed char temp)
+{
+ if (temp < 0) {
+ host_tempreg_acquire();
+ temp = HOST_TEMPREG;
+ }
+ do_mfc2_31_one(9,temp);
+ emit_shrimm(temp,7,tl);
+ do_mfc2_31_one(10,temp);
+ emit_orrshr_imm(temp,2,tl);
+ do_mfc2_31_one(11,temp);
+ emit_orrshl_imm(temp,3,tl);
+ emit_writeword(tl,®_cop2d[29]);
+ if (temp == HOST_TEMPREG)
+ host_tempreg_release();
+}
+
static void multdiv_assemble_arm(int i,struct regstat *i_regs)
{
// case 0x18: MULT
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+#include "pcnt.h"
#include "arm_features.h"
#if defined(BASE_ADDR_FIXED)
u_int *ptr = addr;
intptr_t offset = (u_char *)target - (u_char *)addr;
- if((*ptr&0xFC000000)==0x14000000) {
+ if ((*ptr&0xFC000000) == 0x14000000) { // b
assert(offset>=-134217728LL&&offset<134217728LL);
*ptr=(*ptr&0xFC000000)|((offset>>2)&0x3ffffff);
}
- else if((*ptr&0xff000000)==0x54000000) {
+ else if ((*ptr&0xff000000) == 0x54000000 // b.cond
+ || (*ptr&0x7e000000) == 0x34000000) { // cbz/cbnz
// Conditional branch are limited to +/- 1MB
// block max size is 256k so branching beyond the +/- 1MB limit
// should only happen when jumping to an already compiled block (see add_link)
// a workaround would be to do a trampoline jump via a stub at the end of the block
- assert(offset>=-1048576LL&&offset<1048576LL);
+ assert(-1048576 <= offset && offset < 1048576);
*ptr=(*ptr&0xFF00000F)|(((offset>>2)&0x7ffff)<<5);
}
- else if((*ptr&0x9f000000)==0x10000000) { //adr
+ else if((*ptr&0x9f000000)==0x10000000) { // adr
// generated by do_miniht_insert
assert(offset>=-1048576LL&&offset<1048576LL);
*ptr=(*ptr&0x9F00001F)|(offset&0x3)<<29|((offset>>2)&0x7ffff)<<5;
}
else
- assert(0); // should not happen
+ abort(); // should not happen
}
// from a pointer to external jump stub (which was produced by emit_extjump2)
}
// find where external branch is liked to using addr of it's stub:
-// get address that insn one after stub loads (dyna_linker arg1),
+// get address that the stub loads (dyna_linker arg1),
// treat it as a pointer to branch insn,
// return addr where that branch jumps to
static void *get_pointer(void *stub)
{
int *i_ptr = find_extjump_insn(stub);
- assert((*i_ptr&0xfc000000) == 0x14000000); // b
- return (u_char *)i_ptr+(((signed int)(*i_ptr<<6)>>6)<<2);
-}
-
-// Find the "clean" entry point from a "dirty" entry point
-// by skipping past the call to verify_code
-static void *get_clean_addr(void *addr)
-{
+ if ((*i_ptr&0xfc000000) == 0x14000000) // b
+ return i_ptr + ((signed int)(*i_ptr<<6)>>6);
+ if ((*i_ptr&0xff000000) == 0x54000000 // b.cond
+ || (*i_ptr&0x7e000000) == 0x34000000) // cbz/cbnz
+ return i_ptr + ((signed int)(*i_ptr<<8)>>13);
assert(0);
return NULL;
}
-static int verify_dirty(u_int *ptr)
-{
- assert(0);
- return 0;
-}
-
-static int isclean(void *addr)
-{
- u_int *ptr = addr;
- return (*ptr >> 24) != 0x58; // the only place ldr (literal) is used
-}
-
-static uint64_t get_from_ldr_literal(const u_int *i)
-{
- signed int ofs;
- assert((i[0] & 0xff000000) == 0x58000000);
- ofs = i[0] << 8;
- ofs >>= 5+8;
- return *(uint64_t *)(i + ofs);
-}
-
-static uint64_t get_from_movz(const u_int *i)
-{
- assert((i[0] & 0x7fe00000) == 0x52800000);
- return (i[0] >> 5) & 0xffff;
-}
-
-// get source that block at addr was compiled from (host pointers)
-static void get_bounds(void *addr, u_char **start, u_char **end)
-{
- const u_int *ptr = addr;
- assert((ptr[0] & 0xff00001f) == 0x58000001); // ldr x1, source
- assert((ptr[1] & 0xff00001f) == 0x58000002); // ldr x2, copy
- assert((ptr[2] & 0xffe0001f) == 0x52800003); // movz w3, #slen*4
- *start = (u_char *)get_from_ldr_literal(&ptr[0]);
- *end = *start + get_from_movz(&ptr[2]);
-}
-
// Allocate a specific ARM register.
static void alloc_arm_reg(struct regstat *cur,int i,signed char reg,int hr)
{
}
*/
+static u_int rn_rd(u_int rn, u_int rd)
+{
+ assert(rn < 31);
+ assert(rd < 31);
+ return (rn << 5) | rd;
+}
+
static u_int rm_rn_rd(u_int rm, u_int rn, u_int rd)
{
assert(rm < 32);
return (rm << 16) | (rn << 5) | rd;
}
+static u_int rm_ra_rn_rd(u_int rm, u_int ra, u_int rn, u_int rd)
+{
+ assert(ra < 32);
+ return rm_rn_rd(rm, rn, rd) | (ra << 10);
+}
+
static u_int imm7_rt2_rn_rt(u_int imm7, u_int rt2, u_int rn, u_int rt)
{
assert(imm7 < 0x80);
// non-empty sequence of ones (possibly rotated) with the remainder zero.
static uint32_t is_rotated_mask(u_int value)
{
- if (value == 0)
+ if (value == 0 || value == ~0)
return 0;
if (is_mask((value - 1) | value))
return 1;
lzeros = __builtin_clz(value);
tzeros = __builtin_ctz(value);
ones = 32 - lzeros - tzeros;
- *immr = 31 - tzeros;
+ *immr = lzeros;
*imms = 31 - ones;
return;
}
- assert(0);
+ abort();
}
static void emit_mov(u_int rs, u_int rt)
output_w32(0xaa000000 | rm_rn_rd(rs, WZR, rt));
}
-static void emit_movs(u_int rs, u_int rt)
-{
- assert(0); // misleading
- assem_debug("movs %s,%s\n", regname[rt], regname[rs]);
- output_w32(0x31000000 | imm12_rn_rd(0, rs, rt));
-}
-
static void emit_add(u_int rs1, u_int rs2, u_int rt)
{
assem_debug("add %s,%s,%s\n", regname[rt], regname[rs1], regname[rs2]);
output_w32(0x8b000000 | rm_rn_rd(rs2, rs1, rt));
}
-#pragma GCC diagnostic ignored "-Wunused-function"
-static void emit_adds(u_int rs1, u_int rs2, u_int rt)
-{
- assem_debug("adds %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
- output_w32(0x2b000000 | rm_rn_rd(rs2, rs1, rt));
-}
-
static void emit_adds64(u_int rs1, u_int rs2, u_int rt)
{
- assem_debug("adds %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
+ assem_debug("adds %s,%s,%s\n",regname64[rt],regname64[rs1],regname64[rs2]);
output_w32(0xab000000 | rm_rn_rd(rs2, rs1, rt));
}
output_w32(0x4b000000 | rm_imm6_rn_rd(rs2, 0, rs1, rt));
}
+static void emit_sub_asrimm(u_int rs1, u_int rs2, u_int shift, u_int rt)
+{
+ assem_debug("sub %s,%s,%s,asr #%u\n",regname[rt],regname[rs1],regname[rs2],shift);
+ output_w32(0x4b800000 | rm_imm6_rn_rd(rs2, shift, rs1, rt));
+}
+
static void emit_movz(u_int imm, u_int rt)
{
assem_debug("movz %s,#%#x\n", regname[rt], imm);
static void emit_movk_lsl16(u_int imm,u_int rt)
{
assert(imm<65536);
- assem_debug("movk %s, #%#x, lsl #16\n", regname[rt], imm);
+ assem_debug("movk %s,#%#x,lsl #16\n", regname[rt], imm);
output_w32(0x72a00000 | imm16_rd(imm, rt));
}
output_w32(0xb9400000 | imm12_rn_rd(offset >> 2, FP, rt));
}
else
- assert(0);
+ abort();
}
static void emit_readdword(void *addr, u_int rt)
assem_debug("ldr %s,[x%d+%#lx]\n", regname64[rt], FP, offset);
output_w32(0xf9400000 | imm12_rn_rd(offset >> 3, FP, rt));
}
+ else
+ abort();
+}
+
+static void emit_readshword(void *addr, u_int rt)
+{
+ uintptr_t offset = (u_char *)addr - (u_char *)&dynarec_local;
+ if (!(offset & 1) && offset <= 8190) {
+ assem_debug("ldrsh %s,[x%d+%#lx]\n", regname[rt], FP, offset);
+ output_w32(0x79c00000 | imm12_rn_rd(offset >> 1, FP, rt));
+ }
else
assert(0);
}
uintptr_t offset = (u_char *)addr - (u_char *)&dynarec_local;
if (!(offset & 7) && offset <= 32760) {
assem_debug("str %s,[x%d+%#lx]\n", regname64[rt], FP, offset);
- output_w32(0xf9000000 | imm12_rn_rd(offset >> 2, FP, rt));
+ output_w32(0xf9000000 | imm12_rn_rd(offset >> 3, FP, rt));
}
else
- assert(0);
+ abort();
}
static void emit_storereg(u_int r, u_int hr)
assem_debug("tst %s,#%#x\n", regname[rs], imm);
assert(is_rotated_mask(imm)); // good enough for PCSX
gen_logical_imm(imm, &immr, &imms);
- output_w32(0xb9000000 | n_immr_imms_rn_rd(0, immr, imms, rs, WZR));
-}
-
-static void emit_testeqimm(u_int rs,int imm)
-{
- assem_debug("tsteq %s,$%d\n",regname[rs],imm);
- assert(0); // TODO eliminate emit_testeqimm
+ output_w32(0x72000000 | n_immr_imms_rn_rd(0, immr, imms, rs, WZR));
}
static void emit_not(u_int rs,u_int rt)
output_w32(0x2a200000 | rm_rn_rd(rs, WZR, rt));
}
-static void emit_mvnmi(u_int rs,u_int rt)
-{
- assem_debug("mvnmi %s,%s\n",regname[rt],regname[rs]);
- assert(0); // eliminate
-}
-
static void emit_and(u_int rs1,u_int rs2,u_int rt)
{
assem_debug("and %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
output_w32(0x2a000000 | rm_rn_rd(rs2, rs1, rt));
}
+static void emit_bic(u_int rs1,u_int rs2,u_int rt)
+{
+ assem_debug("bic %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
+ output_w32(0x0a200000 | rm_rn_rd(rs2, rs1, rt));
+}
+
static void emit_orrshl_imm(u_int rs,u_int imm,u_int rt)
{
assem_debug("orr %s,%s,%s,lsl #%d\n",regname[rt],regname[rt],regname[rs],imm);
output_w32(0x2a400000 | rm_imm6_rn_rd(rs, imm, rt, rt));
}
+static void emit_bicsar_imm(u_int rs,u_int imm,u_int rt)
+{
+ assem_debug("bic %s,%s,%s,asr #%d\n",regname[rt],regname[rt],regname[rs],imm);
+ output_w32(0x0aa00000 | rm_imm6_rn_rd(rs, imm, rt, rt));
+}
+
static void emit_xor(u_int rs1,u_int rs2,u_int rt)
{
assem_debug("eor %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
output_w32(0x4a000000 | rm_rn_rd(rs2, rs1, rt));
}
+static void emit_xorsar_imm(u_int rs1, u_int rs2, u_int imm, u_int rt)
+{
+ assem_debug("eor %s,%s,%s,asr #%d\n",regname[rt],regname[rs1],regname[rs2],imm);
+ output_w32(0x4a800000 | rm_imm6_rn_rd(rs2, imm, rs1, rt));
+}
+
static void emit_addimm_s(u_int s, u_int is64, u_int rs, uintptr_t imm, u_int rt)
{
unused const char *st = s ? "s" : "";
output_w32(0x11000000 | is64 | s | imm12_rn_rd(imm, rs, rt));
}
else if (-imm < 4096) {
- assem_debug("sub%s %s,%s,%#lx\n", st, regname[rt], regname[rs], imm);
+ assem_debug("sub%s %s,%s,%#lx\n", st, regname[rt], regname[rs], -imm);
output_w32(0x51000000 | is64 | s | imm12_rn_rd(-imm, rs, rt));
}
else if (imm < 16777216) {
output_w32(0x11400000 | is64 | imm12_rn_rd(imm >> 12, rs, rt));
if ((imm & 0xfff) || s) {
assem_debug("add%s %s,%s,#%#lx\n",st,regname[rt],regname[rs],imm&0xfff);
- output_w32(0x11000000 | is64 | s | imm12_rn_rd(imm, rt, rt));
+ output_w32(0x11000000 | is64 | s | imm12_rn_rd(imm & 0xfff, rt, rt));
}
}
else if (-imm < 16777216) {
}
}
else
- assert(0);
+ abort();
}
static void emit_addimm(u_int rs, uintptr_t imm, u_int rt)
output_w32(0x53000000 | n_immr_imms_rn_rd(0, (31-imm)+1, 31-imm, rs, rt));
}
-static unused void emit_lslpls_imm(u_int rs,int imm,u_int rt)
+static void emit_shrimm(u_int rs,u_int imm,u_int rt)
{
- assert(0); // eliminate
+ assem_debug("lsr %s,%s,#%d\n",regname[rt],regname[rs],imm);
+ output_w32(0x53000000 | n_immr_imms_rn_rd(0, imm, 31, rs, rt));
}
-static void emit_shrimm(u_int rs,u_int imm,u_int rt)
+static void emit_shrimm64(u_int rs,u_int imm,u_int rt)
{
assem_debug("lsr %s,%s,#%d\n",regname[rt],regname[rs],imm);
- output_w32(0x53000000 | n_immr_imms_rn_rd(0, imm, 31, rs, rt));
+ output_w32(0xd3400000 | n_immr_imms_rn_rd(0, imm, 63, rs, rt));
}
static void emit_sarimm(u_int rs,u_int imm,u_int rt)
static void emit_rorimm(u_int rs,u_int imm,u_int rt)
{
- assem_debug("ror %s,%s,#%d",regname[rt],regname[rs],imm);
+ assem_debug("ror %s,%s,#%d\n",regname[rt],regname[rs],imm);
output_w32(0x13800000 | rm_imm6_rn_rd(rs, imm, rs, rt));
}
static void emit_shl(u_int rs,u_int rshift,u_int rt)
{
- assem_debug("lsl %s,%s,%s",regname[rt],regname[rs],regname[rshift]);
+ assem_debug("lsl %s,%s,%s\n",regname[rt],regname[rs],regname[rshift]);
output_w32(0x1ac02000 | rm_rn_rd(rshift, rs, rt));
}
output_w32(0x31000000 | imm12_rn_rd(-imm, rs, WZR));
}
else if (imm < 16777216 && !(imm & 0xfff)) {
- assem_debug("cmp %s,#%#x,lsl #12\n", regname[rs], imm >> 12);
+ assem_debug("cmp %s,#%#x\n", regname[rs], imm);
output_w32(0x71400000 | imm12_rn_rd(imm >> 12, rs, WZR));
}
else {
emit_cmov_imm(COND_CC, COND_CS, imm, rt);
}
-static void emit_cmovs_imm(int imm,u_int rt)
+static void emit_cmoveq_reg(u_int rs,u_int rt)
{
- emit_cmov_imm(COND_MI, COND_PL, imm, rt);
+ assem_debug("csel %s,%s,%s,eq\n",regname[rt],regname[rs],regname[rt]);
+ output_w32(0x1a800000 | (COND_EQ << 12) | rm_rn_rd(rt, rs, rt));
}
static void emit_cmovne_reg(u_int rs,u_int rt)
output_w32(0x1a800000 | (COND_MI << 12) | rm_rn_rd(rt, rs, rt));
}
+static void emit_csinvle_reg(u_int rs1,u_int rs2,u_int rt)
+{
+ assem_debug("csinv %s,%s,%s,le\n",regname[rt],regname[rs1],regname[rs2]);
+ output_w32(0x5a800000 | (COND_LE << 12) | rm_rn_rd(rs2, rs1, rt));
+}
+
static void emit_slti32(u_int rs,int imm,u_int rt)
{
if(rs!=rt) emit_zeroreg(rt);
if (-134217728 <= diff && diff <= 134217727)
output_w32(0x94000000 | ((diff >> 2) & 0x03ffffff));
else
- assert(0);
+ abort();
}
static void emit_jmp(const void *a)
output_w32(0x54000000 | (offset << 5) | COND_CS);
}
-static void emit_jcc(const void *a)
+static void emit_cb(u_int isnz, u_int is64, const void *a, u_int r)
{
- assem_debug("bcc %p\n", a);
+ assem_debug("cb%sz %s,%p\n", isnz?"n":"", is64?regname64[r]:regname[r], a);
u_int offset = genjmpcc(a);
- output_w32(0x54000000 | (offset << 5) | COND_CC);
+ is64 = is64 ? 0x80000000 : 0;
+ isnz = isnz ? 0x01000000 : 0;
+ output_w32(0x34000000 | is64 | isnz | imm19_rt(offset, r));
+}
+
+static void emit_cbz(const void *a, u_int r)
+{
+ emit_cb(0, 0, a, r);
}
static void emit_jmpreg(u_int r)
{
- assem_debug("br %s", regname64[r]);
+ assem_debug("br %s\n", regname64[r]);
output_w32(0xd61f0000 | rm_rn_rd(0, r, 0));
}
{
intptr_t offset = (u_char *)addr - out;
assert(-1048576 <= offset && offset < 1048576);
+ assert(rt < 31);
assem_debug("adr x%d,#%#lx\n", rt, offset);
output_w32(0x10000000 | ((offset&0x3) << 29) | (((offset>>2)&0x7ffff) << 5) | rt);
}
+static void emit_adrp(void *addr, u_int rt)
+{
+ intptr_t offset = ((intptr_t)addr & ~0xfffl) - ((intptr_t)out & ~0xfffl);
+ assert(-4294967296l <= offset && offset < 4294967296l);
+ assert(rt < 31);
+ offset >>= 12;
+ assem_debug("adrp %s,#%#lx(000)\n",regname64[rt],offset);
+ output_w32(0x90000000 | ((offset&0x3)<<29) | (((offset>>2)&0x7ffff)<<5) | rt);
+}
+
static void emit_readword_indexed(int offset, u_int rs, u_int rt)
{
assem_debug("ldur %s,[%s+%#x]\n",regname[rt],regname64[rs],offset);
static void emit_writeword_indexed(u_int rt, int offset, u_int rs)
{
- assem_debug("str %s,[%s+%#x]\n", regname[rt], regname[rs], offset);
- if (!(offset & 3) && offset <= 16380)
+ if (!(offset & 3) && (u_int)offset <= 16380) {
+ assem_debug("str %s,[%s+%#x]\n", regname[rt], regname[rs], offset);
output_w32(0xb9000000 | imm12_rn_rd(offset >> 2, rs, rt));
+ }
+ else if (-256 <= offset && offset < 256) {
+ assem_debug("stur %s,[%s+%#x]\n", regname[rt], regname[rs], offset);
+ output_w32(0xb8000000 | imm9_rn_rt(offset & 0x1ff, rs, rt));
+ }
else
assert(0);
}
static void emit_writehword_indexed(u_int rt, int offset, u_int rs)
{
- assem_debug("strh %s,[%s+%#x]\n", regname[rt], regname[rs], offset);
- if (!(offset & 1) && offset <= 8190)
+ if (!(offset & 1) && (u_int)offset <= 8190) {
+ assem_debug("strh %s,[%s+%#x]\n", regname[rt], regname64[rs], offset);
output_w32(0x79000000 | imm12_rn_rd(offset >> 1, rs, rt));
+ }
+ else if (-256 <= offset && offset < 256) {
+ assem_debug("sturh %s,[%s+%#x]\n", regname[rt], regname64[rs], offset);
+ output_w32(0x78000000 | imm9_rn_rt(offset & 0x1ff, rs, rt));
+ }
else
assert(0);
}
static void emit_writebyte_indexed(u_int rt, int offset, u_int rs)
{
- assem_debug("strb %s,[%s+%#x]\n", regname[rt], regname[rs], offset);
- if ((u_int)offset < 4096)
+ if ((u_int)offset < 4096) {
+ assem_debug("strb %s,[%s+%#x]\n", regname[rt], regname64[rs], offset);
output_w32(0x39000000 | imm12_rn_rd(offset, rs, rt));
+ }
+ else if (-256 <= offset && offset < 256) {
+ assem_debug("sturb %s,[%s+%#x]\n", regname[rt], regname64[rs], offset);
+ output_w32(0x38000000 | imm9_rn_rt(offset & 0x1ff, rs, rt));
+ }
else
assert(0);
}
-static void emit_umull(u_int rs1, u_int rs2, u_int hi, u_int lo)
+static void emit_umull(u_int rs1, u_int rs2, u_int rt)
{
- assem_debug("umull %s, %s, %s, %s\n",regname[lo],regname[hi],regname[rs1],regname[rs2]);
- assert(rs1<16);
- assert(rs2<16);
- assert(hi<16);
- assert(lo<16);
- assert(0);
+ assem_debug("umull %s,%s,%s\n",regname64[rt],regname[rs1],regname[rs2]);
+ output_w32(0x9ba00000 | rm_ra_rn_rd(rs2, WZR, rs1, rt));
}
-static void emit_smull(u_int rs1, u_int rs2, u_int hi, u_int lo)
+static void emit_smull(u_int rs1, u_int rs2, u_int rt)
{
- assem_debug("smull %s, %s, %s, %s\n",regname[lo],regname[hi],regname[rs1],regname[rs2]);
- assert(rs1<16);
- assert(rs2<16);
- assert(hi<16);
- assert(lo<16);
- assert(0);
+ assem_debug("smull %s,%s,%s\n",regname64[rt],regname[rs1],regname[rs2]);
+ output_w32(0x9b200000 | rm_ra_rn_rd(rs2, WZR, rs1, rt));
+}
+
+static void emit_msub(u_int rs1, u_int rs2, u_int rs3, u_int rt)
+{
+ assem_debug("msub %s,%s,%s,%s\n",regname[rt],regname[rs1],regname[rs2],regname[rs3]);
+ output_w32(0x1b008000 | rm_ra_rn_rd(rs2, rs3, rs1, rt));
}
-static void emit_clz(u_int rs,u_int rt)
+static void emit_sdiv(u_int rs1, u_int rs2, u_int rt)
+{
+ assem_debug("sdiv %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
+ output_w32(0x1ac00c00 | rm_rn_rd(rs2, rs1, rt));
+}
+
+static void emit_udiv(u_int rs1, u_int rs2, u_int rt)
+{
+ assem_debug("udiv %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]);
+ output_w32(0x1ac00800 | rm_rn_rd(rs2, rs1, rt));
+}
+
+static void emit_clz(u_int rs, u_int rt)
{
assem_debug("clz %s,%s\n",regname[rt],regname[rs]);
- assert(0);
+ output_w32(0x5ac01000 | rn_rd(rs, rt));
}
// special case for checking invalid_code
{
host_tempreg_acquire();
emit_shrimm(r, 12, HOST_TEMPREG);
- assem_debug("ldrb %s,[%s,%s]",regname[HOST_TEMPREG],regname64[rbase],regname64[HOST_TEMPREG]);
- output_w32(0x38606800 | rm_rn_rd(HOST_TEMPREG, rbase, HOST_TEMPREG));
+ assem_debug("ldrb %s,[%s,%s,uxtw]\n",regname[HOST_TEMPREG],regname64[rbase],regname[HOST_TEMPREG]);
+ output_w32(0x38604800 | rm_rn_rd(HOST_TEMPREG, rbase, HOST_TEMPREG));
emit_cmpimm(HOST_TEMPREG, imm);
host_tempreg_release();
}
-static void emit_orrne_imm(u_int rs,int imm,u_int rt)
+// special for loadlr_assemble, rs2 is destroyed
+static void emit_bic_lsl(u_int rs1,u_int rs2,u_int shift,u_int rt)
{
- assem_debug("orrne %s,%s,#%#x\n",regname[rt],regname[rs],imm);
- assert(0);
+ emit_shl(rs2, shift, rs2);
+ emit_bic(rs1, rs2, rt);
}
-static void emit_andne_imm(u_int rs,int imm,u_int rt)
+static void emit_bic_lsr(u_int rs1,u_int rs2,u_int shift,u_int rt)
{
- assem_debug("andne %s,%s,#%#x\n",regname[rt],regname[rs],imm);
- assert(0);
-}
-
-static unused void emit_addpl_imm(u_int rs,int imm,u_int rt)
-{
- assem_debug("addpl %s,%s,#%#x\n",regname[rt],regname[rs],imm);
- assert(0);
+ emit_shr(rs2, shift, rs2);
+ emit_bic(rs1, rs2, rt);
}
static void emit_loadlp_ofs(u_int ofs, u_int rt)
static void emit_movimm_from(u_int rs_val, u_int rs, u_int rt_val, u_int rt)
{
int diff = rt_val - rs_val;
- if ((-4096 <= diff && diff < 4096)
- || (-16777216 <= diff && diff < 16777216 && !(diff & 0xfff)))
+ if ((-4096 < diff && diff < 4096)
+ || (-16777216 < diff && diff < 16777216 && !(diff & 0xfff)))
emit_addimm(rs, diff, rt);
+ else if (rt_val == ~rs_val)
+ emit_not(rs, rt);
else if (is_rotated_mask(rs_val ^ rt_val))
emit_xorimm(rs, rs_val ^ rt_val, rt);
else
static int is_similar_value(u_int v1, u_int v2)
{
int diff = v1 - v2;
- return (-4096 <= diff && diff < 4096)
- || (-16777216 <= diff && diff < 16777216 && !(diff & 0xfff))
+ return (-4096 < diff && diff < 4096)
+ || (-16777216 < diff && diff < 16777216 && !(diff & 0xfff))
+ || v1 == ~v2
|| is_rotated_mask(v1 ^ v2);
}
case STOREH_STUB: emit_ubfm(rs, 15, rt); break;
case LOADW_STUB:
case STOREW_STUB: if (rs != rt) emit_mov(rs, rt); break;
- default: assert(0);
+ default: assert(0);
}
}
case LOADH_STUB: emit_ldrsh_dualindexed(temp2,rs,rt); break;
case LOADHU_STUB: emit_ldrh_dualindexed(temp2,rs,rt); break;
case LOADW_STUB: emit_ldr_dualindexed(temp2,rs,rt); break;
- default: assert(0);
+ default: assert(0);
}
}
if(regs_saved) {
if(cc<0)
emit_loadreg(CCREG,2);
emit_addimm(cc<0?2:cc,CLOCK_ADJUST(adj+1),2);
- if(is_dynamic)
- emit_readdword(&mem_rtab,1);
+ if(is_dynamic) {
+ uintptr_t l1 = ((uintptr_t *)mem_rtab)[addr>>12] << 1;
+ emit_adrp((void *)l1, 1);
+ emit_addimm64(1, l1 & 0xfff, 1);
+ }
else
emit_call(do_memhandler_pre);
case STOREB_STUB: handler=jump_handler_write8; break;
case STOREH_STUB: handler=jump_handler_write16; break;
case STOREW_STUB: handler=jump_handler_write32; break;
- default: assert(0);
+ default: assert(0);
}
assert(handler);
pass_args(rs,rt);
restore_regs(reglist);
}
-static void do_unalignedwritestub(int n)
+static int verify_code_arm64(const void *source, const void *copy, u_int size)
{
- assem_debug("do_unalignedwritestub %x\n",start+stubs[n].a*4);
- assert(0);
+ int ret = memcmp(source, copy, size);
+ //printf("%s %p,%#x = %d\n", __func__, source, size, ret);
+ return ret;
+}
+
+// this output is parsed by verify_dirty, get_bounds, isclean, get_clean_addr
+static void do_dirty_stub_base(u_int vaddr)
+{
+ assert(slen <= MAXBLOCK);
+ emit_loadlp_ofs(0, 0); // ldr x1, source
+ emit_loadlp_ofs(0, 1); // ldr x2, copy
+ emit_movz(slen*4, 2);
+ emit_call(verify_code_arm64);
+ void *jmp = out;
+ emit_cbz(0, 0);
+ emit_movz(vaddr & 0xffff, 0);
+ emit_movk_lsl16(vaddr >> 16, 0);
+ emit_call(get_addr);
+ emit_jmpreg(0);
+ set_jump_target(jmp, out);
+}
+
+static void assert_dirty_stub(const u_int *ptr)
+{
+ assert((ptr[0] & 0xff00001f) == 0x58000000); // ldr x0, source
+ assert((ptr[1] & 0xff00001f) == 0x58000001); // ldr x1, copy
+ assert((ptr[2] & 0xffe0001f) == 0x52800002); // movz w2, #slen*4
+ assert( ptr[8] == 0xd61f0000); // br x0
}
static void set_loadlp(u_int *loadl, void *lit)
*loadl |= (ofs >> 2) << 5;
}
-// this output is parsed by verify_dirty, get_bounds, isclean, get_clean_addr
-static void do_dirty_stub_emit_args(u_int arg0)
-{
- assert(slen <= MAXBLOCK);
- emit_loadlp_ofs(0, 1); // ldr x1, source
- emit_loadlp_ofs(0, 2); // ldr x2, copy
- emit_movz(slen*4, 3);
- emit_movz(arg0 & 0xffff, 0);
- emit_movk_lsl16(arg0 >> 16, 0);
-}
-
static void do_dirty_stub_emit_literals(u_int *loadlps)
{
set_loadlp(&loadlps[0], out);
{
assem_debug("do_dirty_stub %x\n",start+i*4);
u_int *loadlps = (void *)out;
- do_dirty_stub_emit_args(start + i*4);
- emit_call(verify_code);
+ do_dirty_stub_base(start + i*4);
void *entry = out;
load_regs_entry(i);
if (entry == out)
return entry;
}
-static void do_dirty_stub_ds()
+static void do_dirty_stub_ds(void)
{
- do_dirty_stub_emit_args(start + 1);
u_int *loadlps = (void *)out;
- emit_call(verify_code_ds);
+ do_dirty_stub_base(start + 1);
+ void *lit_jumpover = out;
emit_jmp(out + 8*2);
do_dirty_stub_emit_literals(loadlps);
+ set_jump_target(lit_jumpover, out);
}
-/* Special assem */
+static uint64_t get_from_ldr_literal(const u_int *i)
+{
+ signed int ofs;
+ assert((i[0] & 0xff000000) == 0x58000000);
+ ofs = i[0] << 8;
+ ofs >>= 5+8;
+ return *(uint64_t *)(i + ofs);
+}
-#define shift_assemble shift_assemble_arm64
+static uint64_t get_from_movz(const u_int *i)
+{
+ assert((i[0] & 0x7fe00000) == 0x52800000);
+ return (i[0] >> 5) & 0xffff;
+}
-static void shift_assemble_arm64(int i,struct regstat *i_regs)
+// Find the "clean" entry point from a "dirty" entry point
+// by skipping past the call to verify_code
+static void *get_clean_addr(u_int *addr)
{
- assert(0);
+ assert_dirty_stub(addr);
+ return addr + 9;
}
-#define loadlr_assemble loadlr_assemble_arm64
-static void loadlr_assemble_arm64(int i,struct regstat *i_regs)
+static int verify_dirty(const u_int *ptr)
{
- assert(0);
+ const void *source, *copy;
+ u_int len;
+ assert_dirty_stub(ptr);
+ source = (void *)get_from_ldr_literal(&ptr[0]); // ldr x1, source
+ copy = (void *)get_from_ldr_literal(&ptr[1]); // ldr x1, copy
+ len = get_from_movz(&ptr[2]); // movz w3, #slen*4
+ return !memcmp(source, copy, len);
+}
+
+static int isclean(void *addr)
+{
+ const u_int *ptr = addr;
+ if ((*ptr >> 24) == 0x58) { // the only place ldr (literal) is used
+ assert_dirty_stub(ptr);
+ return 0;
+ }
+ return 1;
+}
+
+// get source that block at addr was compiled from (host pointers)
+static void get_bounds(void *addr, u_char **start, u_char **end)
+{
+ const u_int *ptr = addr;
+ assert_dirty_stub(ptr);
+ *start = (u_char *)get_from_ldr_literal(&ptr[0]); // ldr x1, source
+ *end = *start + get_from_movz(&ptr[2]); // movz w3, #slen*4
+}
+
+/* Special assem */
+
+static void c2op_prologue(u_int op,u_int reglist)
+{
+ save_load_regs_all(1, reglist);
+#ifdef PCNT
+ emit_movimm(op, 0);
+ emit_call(pcnt_gte_start);
+#endif
+ // pointer to cop2 regs
+ emit_addimm64(FP, (u_char *)&psxRegs.CP2D.r[0] - (u_char *)&dynarec_local, 0);
+}
+
+static void c2op_epilogue(u_int op,u_int reglist)
+{
+#ifdef PCNT
+ emit_movimm(op, 0);
+ emit_call(pcnt_gte_end);
+#endif
+ save_load_regs_all(0, reglist);
}
static void c2op_assemble(int i,struct regstat *i_regs)
{
- assert(0);
+ u_int c2op=source[i]&0x3f;
+ 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_full|=1<<hr;
+ }
+ reglist=reglist_full&CALLER_SAVE_REGS;
+
+ if (gte_handlers[c2op]!=NULL) {
+ need_flags=!(gte_unneeded[i+1]>>63); // +1 because of how liveness detection works
+ need_ir=(gte_unneeded[i+1]&0xe00)!=0xe00;
+ assem_debug("gte op %08x, unneeded %016lx, 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) {
+ default:
+ (void)need_ir;
+ c2op_prologue(c2op,reglist);
+ emit_movimm(source[i],1); // opcode
+ emit_writeword(1,&psxRegs.code);
+ emit_call(need_flags?gte_handlers[c2op]:gte_handlers_nf[c2op]);
+ break;
+ }
+ c2op_epilogue(c2op,reglist);
+ }
+}
+
+static void c2op_ctc2_31_assemble(signed char sl, signed char temp)
+{
+ //value = value & 0x7ffff000;
+ //if (value & 0x7f87e000) value |= 0x80000000;
+ emit_andimm(sl, 0x7fffe000, temp);
+ emit_testimm(temp, 0xff87ffff);
+ emit_andimm(sl, 0x7ffff000, temp);
+ host_tempreg_acquire();
+ emit_orimm(temp, 0x80000000, HOST_TEMPREG);
+ emit_cmovne_reg(HOST_TEMPREG, temp);
+ host_tempreg_release();
+ assert(0); // testing needed
+}
+
+static void do_mfc2_31_one(u_int copr,signed char temp)
+{
+ emit_readshword(®_cop2d[copr],temp);
+ emit_bicsar_imm(temp,31,temp);
+ emit_cmpimm(temp,0xf80);
+ emit_csinvle_reg(temp,WZR,temp); // if (temp > 0xf80) temp = ~0;
+ emit_andimm(temp,0xf80,temp);
+}
+
+static void c2op_mfc2_29_assemble(signed char tl, signed char temp)
+{
+ if (temp < 0) {
+ host_tempreg_acquire();
+ temp = HOST_TEMPREG;
+ }
+ do_mfc2_31_one(9,temp);
+ emit_shrimm(temp,7,tl);
+ do_mfc2_31_one(10,temp);
+ emit_orrshr_imm(temp,2,tl);
+ do_mfc2_31_one(11,temp);
+ emit_orrshl_imm(temp,3,tl);
+ emit_writeword(tl,®_cop2d[29]);
+
+ if (temp == HOST_TEMPREG)
+ host_tempreg_release();
}
static void multdiv_assemble_arm64(int i,struct regstat *i_regs)
{
- assert(0);
+ // case 0x18: MULT
+ // case 0x19: MULTU
+ // case 0x1A: DIV
+ // case 0x1B: DIVU
+ if(rs1[i]&&rs2[i])
+ {
+ switch(opcode2[i])
+ {
+ case 0x18: // MULT
+ case 0x19: // MULTU
+ {
+ signed char m1=get_reg(i_regs->regmap,rs1[i]);
+ signed char m2=get_reg(i_regs->regmap,rs2[i]);
+ signed char hi=get_reg(i_regs->regmap,HIREG);
+ signed char lo=get_reg(i_regs->regmap,LOREG);
+ assert(m1>=0);
+ assert(m2>=0);
+ assert(hi>=0);
+ assert(lo>=0);
+
+ if(opcode2[i]==0x18) // MULT
+ emit_smull(m1,m2,hi);
+ else // MULTU
+ emit_umull(m1,m2,hi);
+
+ emit_mov(hi,lo);
+ emit_shrimm64(hi,32,hi);
+ break;
+ }
+ case 0x1A: // DIV
+ case 0x1B: // DIVU
+ {
+ signed char numerator=get_reg(i_regs->regmap,rs1[i]);
+ signed char denominator=get_reg(i_regs->regmap,rs2[i]);
+ signed char quotient=get_reg(i_regs->regmap,LOREG);
+ signed char remainder=get_reg(i_regs->regmap,HIREG);
+ assert(numerator>=0);
+ assert(denominator>=0);
+ assert(quotient>=0);
+ assert(remainder>=0);
+
+ if (opcode2[i] == 0x1A) // DIV
+ emit_sdiv(numerator,denominator,quotient);
+ else // DIVU
+ emit_udiv(numerator,denominator,quotient);
+ emit_msub(quotient,denominator,numerator,remainder);
+
+ // div 0 quotient (remainder is already correct)
+ host_tempreg_acquire();
+ if (opcode2[i] == 0x1A) // DIV
+ emit_sub_asrimm(0,numerator,31,HOST_TEMPREG);
+ else
+ emit_movimm(~0,HOST_TEMPREG);
+ emit_test(denominator,denominator);
+ emit_cmoveq_reg(HOST_TEMPREG,quotient);
+ host_tempreg_release();
+ break;
+ }
+ default:
+ assert(0);
+ }
+ }
+ else
+ {
+ signed char hr=get_reg(i_regs->regmap,HIREG);
+ signed char lr=get_reg(i_regs->regmap,LOREG);
+ if ((opcode2[i]==0x1A || opcode2[i]==0x1B) && rs2[i]==0) // div 0
+ {
+ if (rs1[i]) {
+ signed char numerator = get_reg(i_regs->regmap, rs1[i]);
+ assert(numerator >= 0);
+ if (hr >= 0)
+ emit_mov(numerator,hr);
+ if (lr >= 0) {
+ if (opcode2[i] == 0x1A) // DIV
+ emit_sub_asrimm(0,numerator,31,lr);
+ else
+ emit_movimm(~0,lr);
+ }
+ }
+ else {
+ if (hr >= 0) emit_zeroreg(hr);
+ if (lr >= 0) emit_movimm(~0,lr);
+ }
+ }
+ else
+ {
+ // Multiply by zero is zero.
+ if (hr >= 0) emit_zeroreg(hr);
+ if (lr >= 0) emit_zeroreg(lr);
+ }
+ }
}
#define multdiv_assemble multdiv_assemble_arm64
scratch_buf_ptr = scratch_buf;
SysPrintf("Mapped (RAM/scrp/ROM/LUTs/TC):\n");
- SysPrintf("%08x/%08x/%08x/%08x/%08x\n",
+ SysPrintf("%p/%p/%p/%p/%p\n",
psxM, psxH, psxR, mem_rtab, out);
return 0;
if (allregs_p[i] != allregs_e[i]) {
miss_log_add(i, allregs_p[i], allregs_e[i], psxRegs.pc, psxRegs.cycle);
bad++;
+ if (i > 32+2)
+ goto end;
}
}
#define get_addr_ht ESYM(get_addr_ht)
#define clean_blocks ESYM(clean_blocks)
#define gen_interupt ESYM(gen_interupt)
-#define psxException ESYM(psxException)
-#define execI ESYM(execI)
#define invalidate_addr ESYM(invalidate_addr)
#endif
.align 2
FUNCTION(verify_code_ds):
- str r8, [fp, #LO_branch_target]
+ str r8, [fp, #LO_branch_target] @ preserve HOST_BTREG?
FUNCTION(verify_code):
/* r1 = source */
/* r2 = target */
.size jump_syscall, .-jump_syscall
.align 2
- .align 2
-FUNCTION(jump_syscall_hle):
- str r0, [fp, #LO_pcaddr] /* PC must be set to EPC for psxException */
- ldr r2, [fp, #LO_last_count]
- mov r1, #0 /* in delay slot */
- add r2, r2, r10
- mov r0, #0x20 /* cause */
- str r2, [fp, #LO_cycle] /* PCSX cycle counter */
- bl psxException
-
/* note: psxException might do recursive recompiler call from it's HLE code,
* so be ready for this */
-pcsx_return:
+FUNCTION(jump_to_new_pc):
ldr r1, [fp, #LO_next_interupt]
ldr r10, [fp, #LO_cycle]
ldr r0, [fp, #LO_pcaddr]
str r1, [fp, #LO_last_count]
bl get_addr_ht
mov pc, r0
- .size jump_syscall_hle, .-jump_syscall_hle
-
- .align 2
-FUNCTION(jump_hlecall):
- ldr r2, [fp, #LO_last_count]
- str r0, [fp, #LO_pcaddr]
- add r2, r2, r10
- adr lr, pcsx_return
- str r2, [fp, #LO_cycle] /* PCSX cycle counter */
- bx r1
- .size jump_hlecall, .-jump_hlecall
-
- .align 2
-FUNCTION(jump_intcall):
- ldr r2, [fp, #LO_last_count]
- str r0, [fp, #LO_pcaddr]
- add r2, r2, r10
- adr lr, pcsx_return
- str r2, [fp, #LO_cycle] /* PCSX cycle counter */
- b execI
- .size jump_hlecall, .-jump_hlecall
+ .size jump_to_new_pc, .-jump_to_new_pc
.align 2
FUNCTION(new_dyna_leave):
dyna_linker_main
.size dyna_linker_ds, .-dyna_linker_ds
- .align 2
-
-FUNCTION(verify_code_ds):
- bl abort
-FUNCTION(verify_code):
- /* r1 = source */
- /* r2 = target */
- /* r3 = length */
- bl abort
- .size verify_code, .-verify_code
- .size verify_code_ds, .-verify_code_ds
-
.align 2
FUNCTION(cc_interrupt):
ldr w0, [rFP, #LO_last_count]
.size jump_syscall, .-jump_syscall
.align 2
- .align 2
-FUNCTION(jump_syscall_hle):
- bl abort
-
/* note: psxException might do recursive recompiler call from it's HLE code,
* so be ready for this */
-pcsx_return:
- bl abort // w10
+FUNCTION(jump_to_new_pc):
ldr w1, [fp, #LO_next_interupt]
- ldr w10, [fp, #LO_cycle]
+ ldr rCC, [fp, #LO_cycle]
ldr w0, [fp, #LO_pcaddr]
- sub w10, w10, w1
+ sub rCC, rCC, w1
str w1, [fp, #LO_last_count]
bl get_addr_ht
br x0
- .size jump_syscall_hle, .-jump_syscall_hle
-
- .align 2
-FUNCTION(jump_hlecall):
- bl abort
- .size jump_hlecall, .-jump_hlecall
-
- .align 2
-FUNCTION(jump_intcall):
- bl abort
- .size jump_intcall, .-jump_intcall
+ .size jump_to_new_pc, .-jump_to_new_pc
/* stack must be aligned by 16, and include space for save_regs() use */
.align 2
.macro pcsx_read_mem readop tab_shift
/* w0 = address, x1 = handler_tab, w2 = cycles */
- stp xzr, x30, [sp, #-16]!
ubfm w4, w0, #\tab_shift, #11
ldr x3, [x1, w4, uxtw #3]
adds x3, x3, x3
\readop w0, [x3, w4, uxtw #\tab_shift]
ret
0:
+ stp xzr, x30, [sp, #-16]!
memhandler_pre
blr x3
.endm
FUNCTION(jump_handler_read8):
- add x1, x1, #0x1000/4*4 + 0x1000/2*4 /* shift to r8 part */
+ add x1, x1, #0x1000/4*8 + 0x1000/2*8 /* shift to r8 part */
pcsx_read_mem ldrb, 0
b handler_read_end
FUNCTION(jump_handler_read16):
- add x1, x1, #0x1000/4*4 /* shift to r16 part */
+ add x1, x1, #0x1000/4*8 /* shift to r16 part */
pcsx_read_mem ldrh, 1
b handler_read_end
.macro pcsx_write_mem wrtop movop tab_shift
/* w0 = address, w1 = data, w2 = cycles, x3 = handler_tab */
- stp xzr, x30, [sp, #-16]!
ubfm w4, w0, #\tab_shift, #11
ldr x3, [x3, w4, uxtw #3]
- str w0, [rFP, #LO_address] /* some handlers still need it... */
adds x3, x3, x3
-# str lr, [rFP, #0]
bcs 0f
mov w0, w2 /* cycle return */
\wrtop w1, [x3, w4, uxtw #\tab_shift]
ret
0:
+ stp xzr, x30, [sp, #-16]!
+ str w0, [rFP, #LO_address] /* some handlers still need it... */
\movop w0, w1
memhandler_pre
blr x3
.endm
FUNCTION(jump_handler_write8):
- add x3, x3, #0x1000/4*4 + 0x1000/2*4 /* shift to r8 part */
+ add x3, x3, #0x1000/4*8 + 0x1000/2*8 /* shift to r8 part */
pcsx_write_mem strb uxtb 0
b handler_write_end
FUNCTION(jump_handler_write16):
- add x3, x3, #0x1000/4*4 /* shift to r16 part */
+ add x3, x3, #0x1000/4*8 /* shift to r16 part */
pcsx_write_mem strh uxth 1
b handler_write_end
ret
FUNCTION(jump_handle_swl):
+ /* w0 = address, w1 = data, w2 = cycles */
+ ldr x3, [fp, #LO_mem_wtab]
+ mov w4, w0, lsr #12
+ ldr x3, [x3, w4, uxtw #3]
+ adds x3, x3, x3
+ bcs 4f
+ add x3, x0, x3
+ mov w0, w2
+ tbz x3, #1, 10f // & 2
+ tbz x3, #0, 2f // & 1
+3:
+ stur w1, [x3, #-3]
+ ret
+2:
+ lsr w2, w1, #8
+ lsr w1, w1, #24
+ sturh w2, [x3, #-2]
+ strb w1, [x3]
+ ret
+10:
+ tbz x3, #0, 0f // & 1
+1:
+ lsr w1, w1, #16
+ sturh w1, [x3, #-1]
+ ret
+0:
+ lsr w2, w1, #24
+ strb w2, [x3]
+ ret
+4:
+ mov w0, w2 // todo
bl abort
+ ret
FUNCTION(jump_handle_swr):
+ /* w0 = address, w1 = data, w2 = cycles */
+ ldr x3, [fp, #LO_mem_wtab]
+ mov w4, w0, lsr #12
+ ldr x3, [x3, w4, uxtw #3]
+ adds x3, x3, x3
+ bcs 4f
+ add x3, x0, x3
+ mov w0, w2
+ tbz x3, #1, 10f // & 2
+ tbz x3, #0, 2f // & 1
+3:
+ strb w1, [x3]
+ ret
+2:
+ strh w1, [x3]
+ ret
+10:
+ tbz x3, #0, 0f // & 1
+1:
+ lsr w2, w1, #8
+ strb w1, [x3]
+ sturh w2, [x3, #1]
+ ret
+0:
+ str w1, [x3]
+ ret
+4:
+ mov w0, w2 // todo
bl abort
+ ret
#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))
#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);
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
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);
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)
}
#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 {
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;
}
#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
}
// 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
}
}
-void storelr_assemble(int i,struct regstat *i_regs)
+static void storelr_assemble(int i,struct regstat *i_regs)
{
int s,tl;
int temp;
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);
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
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
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
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)) {
break;
case 28:
case 29:
- emit_readword(®_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(®_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(®_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,®_cop2d[copr]);
+ c2op_mfc2_29_assemble(tl,temp);
break;
default:
emit_readword(®_cop2d[copr],tl);
emit_writeword(sl,®_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
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
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;
}
}
+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_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)
{
}
}
-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_call(func);
+ emit_jmp(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)
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++)
//emit_readword(&cycle,0);
//emit_addimm(0,2,0);
//emit_writeword(0,&cycle);
+ (void)get_reg2;
restore_regs(reglist);
}
#else
#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]);
#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)
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;
}
}
}
- }
- }
+ } // if needed
+ } // for hr
}
/* Pass 5 - Pre-allocate registers */
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);
emit_writeword(0,&pcaddr);
emit_writeword(0,&address);
emit_cmp(0,1);
+ #ifdef __aarch64__
+ emit_jeq(out + 4*2);
+ emit_jmp(new_dyna_leave);
+ #else
emit_jne(new_dyna_leave);
+ #endif
}
for(i=0;i<slen;i++)
{
diff --git a/libpcsxcore/new_dynarec/linkage_arm.S b/libpcsxcore/new_dynarec/linkage_arm.S
-index fcb4e1a..e2547c4 100644
+index bbc52c3..83c5b08 100644
--- a/libpcsxcore/new_dynarec/linkage_arm.S
+++ b/libpcsxcore/new_dynarec/linkage_arm.S
-@@ -439,7 +439,7 @@ FUNCTION(cc_interrupt):
+@@ -437,7 +437,7 @@ FUNCTION(cc_interrupt):
str r1, [fp, #LO_pending_exception]
and r2, r2, r10, lsr #17
add r3, fp, #LO_restore_candidate
@@ str r10, [fp, #LO_reg_cop0+36] /* Count */
ldr r4, [r2, r3]
mov r10, lr
-@@ -519,7 +519,7 @@ FUNCTION(jump_syscall_hle):
- mov r1, #0 /* in delay slot */
- add r2, r2, r10
- mov r0, #0x20 /* cause */
-- str r2, [fp, #LO_cycle] /* PCSX cycle counter */
-+@@@ str r2, [fp, #LO_cycle] /* PCSX cycle counter */
- bl psxException
-
- /* note: psxException might do recursive recompiler call from it's HLE code,
-@@ -540,7 +540,7 @@ FUNCTION(jump_hlecall):
- str r0, [fp, #LO_pcaddr]
- add r2, r2, r10
- adr lr, pcsx_return
-- str r2, [fp, #LO_cycle] /* PCSX cycle counter */
-+@@@ str r2, [fp, #LO_cycle] /* PCSX cycle counter */
- bx r1
- .size jump_hlecall, .-jump_hlecall
-
-@@ -550,7 +550,7 @@ FUNCTION(jump_intcall):
- str r0, [fp, #LO_pcaddr]
- add r2, r2, r10
- adr lr, pcsx_return
-- str r2, [fp, #LO_cycle] /* PCSX cycle counter */
-+@@@ str r2, [fp, #LO_cycle] /* PCSX cycle counter */
- b execI
- .size jump_hlecall, .-jump_hlecall
-
-@@ -559,7 +559,7 @@ FUNCTION(new_dyna_leave):
+@@ -527,7 +527,7 @@ FUNCTION(new_dyna_leave):
ldr r0, [fp, #LO_last_count]
add r12, fp, #28
add r10, r0, r10
ldmfd sp!, {r4, r5, r6, r7, r8, r9, sl, fp, ip, pc}
.size new_dyna_leave, .-new_dyna_leave
-@@ -676,7 +676,7 @@ FUNCTION(new_dyna_start):
+@@ -644,7 +644,7 @@ FUNCTION(new_dyna_start):
\readop r0, [r1, r3, lsl #\tab_shift]
.endif
movcc pc, lr
bx r1
.endm
-@@ -711,7 +711,7 @@ FUNCTION(jump_handler_read32):
+@@ -679,7 +679,7 @@ FUNCTION(jump_handler_read32):
mov r0, r1
add r2, r2, r12
push {r2, lr}
blx r3
ldr r0, [fp, #LO_next_interupt]
-@@ -739,7 +739,7 @@ FUNCTION(jump_handler_write_h):
+@@ -707,7 +707,7 @@ FUNCTION(jump_handler_write_h):
add r2, r2, r12
mov r0, r1
push {r2, lr}
ldr r0, [fp, #LO_next_interupt]
diff --git a/libpcsxcore/new_dynarec/linkage_arm64.S b/libpcsxcore/new_dynarec/linkage_arm64.S
-index 060ac48..e3007d3 100644
+index 698bd78..798abea 100644
--- a/libpcsxcore/new_dynarec/linkage_arm64.S
+++ b/libpcsxcore/new_dynarec/linkage_arm64.S
-@@ -131,7 +131,7 @@ FUNCTION(cc_interrupt):
+@@ -119,7 +119,7 @@ FUNCTION(cc_interrupt):
str wzr, [rFP, #LO_pending_exception]
and w2, w2, rCC, lsr #17
add x3, rFP, #LO_restore_candidate
# str rCC, [rFP, #LO_reg_cop0+36] /* Count */
ldr w19, [x3, w2, uxtw]
mov x21, lr
-@@ -254,7 +254,7 @@ FUNCTION(new_dyna_start):
+@@ -227,7 +227,7 @@ FUNCTION(new_dyna_start):
FUNCTION(new_dyna_leave):
ldr w0, [rFP, #LO_last_count]
add rCC, rCC, w0
ldp x19, x20, [sp, #16*1]
ldp x21, x22, [sp, #16*2]
ldp x23, x24, [sp, #16*3]
-@@ -272,7 +272,7 @@ FUNCTION(new_dyna_leave):
+@@ -245,7 +245,7 @@ FUNCTION(new_dyna_leave):
/* w0 = adddr/data, x1 = rhandler, w2 = cycles, x3 = whandler */
ldr w4, [rFP, #LO_last_count]
add w4, w4, w2
.macro memhandler_post
diff --git a/libpcsxcore/new_dynarec/new_dynarec.c b/libpcsxcore/new_dynarec/new_dynarec.c
-index e7b55b6..caa06d0 100644
+index 1452db3..8200e44 100644
--- a/libpcsxcore/new_dynarec/new_dynarec.c
+++ b/libpcsxcore/new_dynarec/new_dynarec.c
-@@ -43,10 +43,10 @@ static int sceBlock;
+@@ -44,10 +44,10 @@ static int sceBlock;
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
#endif
#define inv_debug(...)
#ifdef __i386__
-@@ -424,6 +424,9 @@ static int doesnt_expire_soon(void *tcaddr)
+@@ -423,6 +423,9 @@ static int doesnt_expire_soon(void *tcaddr)
// This is called from the recompiled JR/JALR instructions
void noinline *get_addr(u_int vaddr)
{
u_int page=get_page(vaddr);
u_int vpage=get_vpage(vaddr);
struct ll_entry *head;
-@@ -4221,13 +4224,15 @@ void do_cc(int i,signed char i_regmap[],int *adj,int addr,int taken,int invert)
+@@ -4393,13 +4396,15 @@ void do_cc(int i,signed char i_regmap[],int *adj,int addr,int taken,int invert)
}
emit_addimm_and_set_flags(cycles,HOST_CCREG);
jaddr=out;
}
add_stub(CC_STUB,jaddr,idle?idle:out,(*adj==0||invert||idle)?0:(count+2),i,addr,taken,0);
}
-@@ -4635,7 +4640,8 @@ static void rjump_assemble(int i,struct regstat *i_regs)
+@@ -4807,7 +4812,8 @@ static void rjump_assemble(int i,struct regstat *i_regs)
// special case for RFE
emit_jmp(0);
else
//load_regs_bt(branch_regs[i].regmap,branch_regs[i].dirty,-1);
#ifdef USE_MINI_HT
if(rs1[i]==31) {
-@@ -4737,7 +4743,8 @@ static void cjump_assemble(int i,struct regstat *i_regs)
+@@ -4912,7 +4918,8 @@ static void cjump_assemble(int i,struct regstat *i_regs)
else if(nop) {
emit_addimm_and_set_flags(CLOCK_ADJUST(ccadj[i]+2),cc);
void *jaddr=out;
add_stub(CC_STUB,jaddr,out,0,i,start+i*4+8,NOTTAKEN,0);
}
else {
-@@ -4924,7 +4931,8 @@ static void cjump_assemble(int i,struct regstat *i_regs)
+@@ -5099,7 +5106,8 @@ static void cjump_assemble(int i,struct regstat *i_regs)
emit_loadreg(CCREG,HOST_CCREG);
emit_addimm_and_set_flags(CLOCK_ADJUST(ccadj[i]+2),HOST_CCREG);
void *jaddr=out;
add_stub(CC_STUB,jaddr,out,0,i,start+i*4+8,NOTTAKEN,0);
emit_storereg(CCREG,HOST_CCREG);
}
-@@ -4933,7 +4941,8 @@ static void cjump_assemble(int i,struct regstat *i_regs)
+@@ -5108,7 +5116,8 @@ static void cjump_assemble(int i,struct regstat *i_regs)
assert(cc==HOST_CCREG);
emit_addimm_and_set_flags(CLOCK_ADJUST(ccadj[i]+2),cc);
void *jaddr=out;
add_stub(CC_STUB,jaddr,out,0,i,start+i*4+8,likely[i]?NULLDS:NOTTAKEN,0);
}
}
-@@ -5032,7 +5041,8 @@ static void sjump_assemble(int i,struct regstat *i_regs)
+@@ -5210,7 +5219,8 @@ static void sjump_assemble(int i,struct regstat *i_regs)
else if(nevertaken) {
emit_addimm_and_set_flags(CLOCK_ADJUST(ccadj[i]+2),cc);
void *jaddr=out;
add_stub(CC_STUB,jaddr,out,0,i,start+i*4+8,NOTTAKEN,0);
}
else {
-@@ -5188,7 +5198,8 @@ static void sjump_assemble(int i,struct regstat *i_regs)
+@@ -5366,7 +5376,8 @@ static void sjump_assemble(int i,struct regstat *i_regs)
emit_loadreg(CCREG,HOST_CCREG);
emit_addimm_and_set_flags(CLOCK_ADJUST(ccadj[i]+2),HOST_CCREG);
void *jaddr=out;
add_stub(CC_STUB,jaddr,out,0,i,start+i*4+8,NOTTAKEN,0);
emit_storereg(CCREG,HOST_CCREG);
}
-@@ -5197,7 +5208,8 @@ static void sjump_assemble(int i,struct regstat *i_regs)
+@@ -5375,7 +5386,8 @@ static void sjump_assemble(int i,struct regstat *i_regs)
assert(cc==HOST_CCREG);
emit_addimm_and_set_flags(CLOCK_ADJUST(ccadj[i]+2),cc);
void *jaddr=out;
add_stub(CC_STUB,jaddr,out,0,i,start+i*4+8,likely[i]?NULLDS:NOTTAKEN,0);
}
}
-@@ -5685,7 +5697,7 @@ void unneeded_registers(int istart,int iend,int r)
+@@ -5863,7 +5875,7 @@ void unneeded_registers(int istart,int iend,int r)
// R0 is always unneeded
u|=1;
// Save it
gte_unneeded[i]=gte_u;
/*
printf("ur (%d,%d) %x: ",istart,iend,start+i*4);
-@@ -8209,6 +8221,7 @@ int new_recompile_block(int addr)
+@@ -8387,6 +8399,7 @@ int new_recompile_block(int addr)
// This allocates registers (if possible) one instruction prior
// to use, which can avoid a load-use penalty on certain CPUs.
for(i=0;i<slen-1;i++)
{
if(!i||(itype[i-1]!=UJUMP&&itype[i-1]!=CJUMP&&itype[i-1]!=SJUMP&&itype[i-1]!=RJUMP))
-@@ -8365,6 +8378,7 @@ int new_recompile_block(int addr)
+@@ -8543,6 +8556,7 @@ int new_recompile_block(int addr)
}
}
}
/* Pass 6 - Optimize clean/dirty state */
clean_registers(0,slen-1,1);
-@@ -8659,6 +8673,11 @@ int new_recompile_block(int addr)
+@@ -8842,6 +8856,11 @@ int new_recompile_block(int addr)
case SPAN:
pagespan_assemble(i,®s[i]);break;
}
if(itype[i]==UJUMP||itype[i]==RJUMP||(source[i]>>16)==0x1000)
literal_pool(1024);
else
-@@ -8767,7 +8786,7 @@ int new_recompile_block(int addr)
+@@ -8950,7 +8969,7 @@ int new_recompile_block(int addr)
}
}
// External Branch Targets (jump_in)
for(i=0;i<slen;i++)
{
if(bt[i]||i==0)
-@@ -8882,6 +8901,10 @@ int new_recompile_block(int addr)
+@@ -9065,6 +9084,10 @@ int new_recompile_block(int addr)
}
expirep=(expirep+1)&65535;
}
#include "gte.h"
#include "psxhle.h"
#include "debug.h"
+#include "psxinterpreter.h"
static int branch = 0;
static int branch2 = 0;
#define debugI()
#endif
-void execI();
-
// Subsets
void (*psxBSC[64])();
void (*psxSPC[64])();
--- /dev/null
+
+// called by "new_dynarec"
+void execI();
+void psxNULL();