#ifdef __arm__
#include "assem_arm.h"
#endif
+#ifdef __aarch64__
+#include "assem_arm64.h"
+#endif
#define MAXBLOCK 4096
#define MAX_OUTPUT_BLOCK_SIZE 262144
int new_dynarec_hacks;
int new_dynarec_did_compile;
+
+ extern int cycle_count; // ... until end of the timeslice, counts -N -> 0
+ extern int last_count; // last absolute target, often = next_interupt
+ extern int pcaddr;
+ extern int pending_exception;
+ extern int branch_target;
+ extern u_int mini_ht[32][2];
extern u_char restore_candidate[512];
- extern int cycle_count;
/* registers that may be allocated */
/* 1-31 gpr */
static void add_stub_r(enum stub_type type, void *addr, void *retaddr,
int i, int addr_reg, struct regstat *i_regs, int ccadj, u_int reglist);
static void add_to_linker(void *addr, u_int target, int ext);
+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 mprotect_w_x(void *start, void *end, int is_x)
{
__clear_cache(start, end);
#endif
(void)len;
+#else
+ __clear_cache(start, end);
#endif
mprotect_w_x(start, end, 1);
}
}
+#ifdef DRC_DBG
+extern void gen_interupt();
+extern void do_insn_cmp();
+#define FUNCNAME(f) { (intptr_t)f, " " #f }
+static const struct {
+ intptr_t addr;
+ const char *name;
+} function_names[] = {
+ FUNCNAME(cc_interrupt),
+ FUNCNAME(gen_interupt),
+ FUNCNAME(get_addr_ht),
+ FUNCNAME(get_addr),
+ FUNCNAME(jump_handler_read8),
+ FUNCNAME(jump_handler_read16),
+ FUNCNAME(jump_handler_read32),
+ FUNCNAME(jump_handler_write8),
+ FUNCNAME(jump_handler_write16),
+ FUNCNAME(jump_handler_write32),
+ FUNCNAME(invalidate_addr),
+ FUNCNAME(verify_code_vm),
+ FUNCNAME(verify_code),
+ FUNCNAME(jump_hlecall),
+ FUNCNAME(jump_syscall_hle),
+ FUNCNAME(new_dyna_leave),
+ FUNCNAME(pcsx_mtc0),
+ FUNCNAME(pcsx_mtc0_ds),
+ FUNCNAME(do_insn_cmp),
+};
+
+static const char *func_name(intptr_t a)
+{
+ int i;
+ for (i = 0; i < sizeof(function_names)/sizeof(function_names[0]); i++)
+ if (function_names[i].addr == a)
+ return function_names[i].name;
+ return "";
+}
+#else
+#define func_name(x) ""
+#endif
+
#ifdef __i386__
#include "assem_x86.c"
#endif
#ifdef __arm__
#include "assem_arm.c"
#endif
+#ifdef __aarch64__
+#include "assem_arm64.c"
+#endif
// Add virtual address mapping to linked list
void ll_add(struct ll_entry **head,int vaddr,void *addr)
{
inv_debug("EXP: Kill pointer at %p (%x)\n",head->addr,head->vaddr);
void *host_addr=find_extjump_insn(head->addr);
- #ifdef __arm__
+ #if defined(__arm__) || defined(__aarch64__)
mark_clear_cache(host_addr);
#endif
set_jump_target(host_addr, head->addr);
while(head!=NULL) {
inv_debug("INVALIDATE: kill pointer to %x (%p)\n",head->vaddr,head->addr);
void *host_addr=find_extjump_insn(head->addr);
- #ifdef __arm__
+ #if defined(__arm__) || defined(__aarch64__)
mark_clear_cache(host_addr);
#endif
set_jump_target(host_addr, head->addr);
for(first=page+1;first<last;first++) {
invalidate_page(first);
}
- #ifdef __arm__
+ #if defined(__arm__) || defined(__aarch64__)
do_clear_cache();
#endif
}
}
+/* Register allocation */
+
+// Note: registers are allocated clean (unmodified state)
+// if you intend to modify the register, you must call dirty_reg().
+static void alloc_reg(struct regstat *cur,int i,signed char reg)
+{
+ int r,hr;
+ int preferred_reg = (reg&7);
+ if(reg==CCREG) preferred_reg=HOST_CCREG;
+ if(reg==PTEMP||reg==FTEMP) preferred_reg=12;
+
+ // Don't allocate unused registers
+ if((cur->u>>reg)&1) return;
+
+ // see if it's already allocated
+ for(hr=0;hr<HOST_REGS;hr++)
+ {
+ if(cur->regmap[hr]==reg) return;
+ }
+
+ // Keep the same mapping if the register was already allocated in a loop
+ preferred_reg = loop_reg(i,reg,preferred_reg);
+
+ // Try to allocate the preferred register
+ if(cur->regmap[preferred_reg]==-1) {
+ cur->regmap[preferred_reg]=reg;
+ cur->dirty&=~(1<<preferred_reg);
+ cur->isconst&=~(1<<preferred_reg);
+ return;
+ }
+ r=cur->regmap[preferred_reg];
+ assert(r < 64);
+ if((cur->u>>r)&1) {
+ cur->regmap[preferred_reg]=reg;
+ cur->dirty&=~(1<<preferred_reg);
+ cur->isconst&=~(1<<preferred_reg);
+ return;
+ }
+
+ // Clear any unneeded registers
+ // We try to keep the mapping consistent, if possible, because it
+ // makes branches easier (especially loops). So we try to allocate
+ // first (see above) before removing old mappings. If this is not
+ // possible then go ahead and clear out the registers that are no
+ // longer needed.
+ for(hr=0;hr<HOST_REGS;hr++)
+ {
+ r=cur->regmap[hr];
+ if(r>=0) {
+ assert(r < 64);
+ if((cur->u>>r)&1) {cur->regmap[hr]=-1;break;}
+ }
+ }
+ // Try to allocate any available register, but prefer
+ // registers that have not been used recently.
+ if(i>0) {
+ for(hr=0;hr<HOST_REGS;hr++) {
+ if(hr!=EXCLUDE_REG&&cur->regmap[hr]==-1) {
+ if(regs[i-1].regmap[hr]!=rs1[i-1]&®s[i-1].regmap[hr]!=rs2[i-1]&®s[i-1].regmap[hr]!=rt1[i-1]&®s[i-1].regmap[hr]!=rt2[i-1]) {
+ cur->regmap[hr]=reg;
+ cur->dirty&=~(1<<hr);
+ cur->isconst&=~(1<<hr);
+ return;
+ }
+ }
+ }
+ }
+ // Try to allocate any available register
+ for(hr=0;hr<HOST_REGS;hr++) {
+ if(hr!=EXCLUDE_REG&&cur->regmap[hr]==-1) {
+ cur->regmap[hr]=reg;
+ cur->dirty&=~(1<<hr);
+ cur->isconst&=~(1<<hr);
+ return;
+ }
+ }
+
+ // Ok, now we have to evict someone
+ // Pick a register we hopefully won't need soon
+ u_char hsn[MAXREG+1];
+ memset(hsn,10,sizeof(hsn));
+ int j;
+ lsn(hsn,i,&preferred_reg);
+ //printf("eax=%d ecx=%d edx=%d ebx=%d ebp=%d esi=%d edi=%d\n",cur->regmap[0],cur->regmap[1],cur->regmap[2],cur->regmap[3],cur->regmap[5],cur->regmap[6],cur->regmap[7]);
+ //printf("hsn(%x): %d %d %d %d %d %d %d\n",start+i*4,hsn[cur->regmap[0]&63],hsn[cur->regmap[1]&63],hsn[cur->regmap[2]&63],hsn[cur->regmap[3]&63],hsn[cur->regmap[5]&63],hsn[cur->regmap[6]&63],hsn[cur->regmap[7]&63]);
+ if(i>0) {
+ // Don't evict the cycle count at entry points, otherwise the entry
+ // stub will have to write it.
+ if(bt[i]&&hsn[CCREG]>2) hsn[CCREG]=2;
+ if(i>1&&hsn[CCREG]>2&&(itype[i-2]==RJUMP||itype[i-2]==UJUMP||itype[i-2]==CJUMP||itype[i-2]==SJUMP)) hsn[CCREG]=2;
+ for(j=10;j>=3;j--)
+ {
+ // Alloc preferred register if available
+ if(hsn[r=cur->regmap[preferred_reg]&63]==j) {
+ for(hr=0;hr<HOST_REGS;hr++) {
+ // Evict both parts of a 64-bit register
+ if((cur->regmap[hr]&63)==r) {
+ cur->regmap[hr]=-1;
+ cur->dirty&=~(1<<hr);
+ cur->isconst&=~(1<<hr);
+ }
+ }
+ cur->regmap[preferred_reg]=reg;
+ return;
+ }
+ for(r=1;r<=MAXREG;r++)
+ {
+ if(hsn[r]==j&&r!=rs1[i-1]&&r!=rs2[i-1]&&r!=rt1[i-1]&&r!=rt2[i-1]) {
+ for(hr=0;hr<HOST_REGS;hr++) {
+ if(hr!=HOST_CCREG||j<hsn[CCREG]) {
+ if(cur->regmap[hr]==r+64) {
+ cur->regmap[hr]=reg;
+ cur->dirty&=~(1<<hr);
+ cur->isconst&=~(1<<hr);
+ return;
+ }
+ }
+ }
+ for(hr=0;hr<HOST_REGS;hr++) {
+ if(hr!=HOST_CCREG||j<hsn[CCREG]) {
+ if(cur->regmap[hr]==r) {
+ cur->regmap[hr]=reg;
+ cur->dirty&=~(1<<hr);
+ cur->isconst&=~(1<<hr);
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ for(j=10;j>=0;j--)
+ {
+ for(r=1;r<=MAXREG;r++)
+ {
+ if(hsn[r]==j) {
+ for(hr=0;hr<HOST_REGS;hr++) {
+ if(cur->regmap[hr]==r+64) {
+ cur->regmap[hr]=reg;
+ cur->dirty&=~(1<<hr);
+ cur->isconst&=~(1<<hr);
+ return;
+ }
+ }
+ for(hr=0;hr<HOST_REGS;hr++) {
+ if(cur->regmap[hr]==r) {
+ cur->regmap[hr]=reg;
+ cur->dirty&=~(1<<hr);
+ cur->isconst&=~(1<<hr);
+ return;
+ }
+ }
+ }
+ }
+ }
+ SysPrintf("This shouldn't happen (alloc_reg)");exit(1);
+}
+
+// Allocate a temporary register. This is done without regard to
+// dirty status or whether the register we request is on the unneeded list
+// Note: This will only allocate one register, even if called multiple times
+static void alloc_reg_temp(struct regstat *cur,int i,signed char reg)
+{
+ int r,hr;
+ int preferred_reg = -1;
+
+ // see if it's already allocated
+ for(hr=0;hr<HOST_REGS;hr++)
+ {
+ if(hr!=EXCLUDE_REG&&cur->regmap[hr]==reg) return;
+ }
+
+ // Try to allocate any available register
+ for(hr=HOST_REGS-1;hr>=0;hr--) {
+ if(hr!=EXCLUDE_REG&&cur->regmap[hr]==-1) {
+ cur->regmap[hr]=reg;
+ cur->dirty&=~(1<<hr);
+ cur->isconst&=~(1<<hr);
+ return;
+ }
+ }
+
+ // Find an unneeded register
+ for(hr=HOST_REGS-1;hr>=0;hr--)
+ {
+ r=cur->regmap[hr];
+ if(r>=0) {
+ assert(r < 64);
+ if((cur->u>>r)&1) {
+ if(i==0||((unneeded_reg[i-1]>>r)&1)) {
+ cur->regmap[hr]=reg;
+ cur->dirty&=~(1<<hr);
+ cur->isconst&=~(1<<hr);
+ return;
+ }
+ }
+ }
+ }
+
+ // Ok, now we have to evict someone
+ // Pick a register we hopefully won't need soon
+ // TODO: we might want to follow unconditional jumps here
+ // TODO: get rid of dupe code and make this into a function
+ u_char hsn[MAXREG+1];
+ memset(hsn,10,sizeof(hsn));
+ int j;
+ lsn(hsn,i,&preferred_reg);
+ //printf("hsn: %d %d %d %d %d %d %d\n",hsn[cur->regmap[0]&63],hsn[cur->regmap[1]&63],hsn[cur->regmap[2]&63],hsn[cur->regmap[3]&63],hsn[cur->regmap[5]&63],hsn[cur->regmap[6]&63],hsn[cur->regmap[7]&63]);
+ if(i>0) {
+ // Don't evict the cycle count at entry points, otherwise the entry
+ // stub will have to write it.
+ if(bt[i]&&hsn[CCREG]>2) hsn[CCREG]=2;
+ if(i>1&&hsn[CCREG]>2&&(itype[i-2]==RJUMP||itype[i-2]==UJUMP||itype[i-2]==CJUMP||itype[i-2]==SJUMP)) hsn[CCREG]=2;
+ for(j=10;j>=3;j--)
+ {
+ for(r=1;r<=MAXREG;r++)
+ {
+ if(hsn[r]==j&&r!=rs1[i-1]&&r!=rs2[i-1]&&r!=rt1[i-1]&&r!=rt2[i-1]) {
+ for(hr=0;hr<HOST_REGS;hr++) {
+ if(hr!=HOST_CCREG||hsn[CCREG]>2) {
+ if(cur->regmap[hr]==r+64) {
+ cur->regmap[hr]=reg;
+ cur->dirty&=~(1<<hr);
+ cur->isconst&=~(1<<hr);
+ return;
+ }
+ }
+ }
+ for(hr=0;hr<HOST_REGS;hr++) {
+ if(hr!=HOST_CCREG||hsn[CCREG]>2) {
+ if(cur->regmap[hr]==r) {
+ cur->regmap[hr]=reg;
+ cur->dirty&=~(1<<hr);
+ cur->isconst&=~(1<<hr);
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ for(j=10;j>=0;j--)
+ {
+ for(r=1;r<=MAXREG;r++)
+ {
+ if(hsn[r]==j) {
+ for(hr=0;hr<HOST_REGS;hr++) {
+ if(cur->regmap[hr]==r+64) {
+ cur->regmap[hr]=reg;
+ cur->dirty&=~(1<<hr);
+ cur->isconst&=~(1<<hr);
+ return;
+ }
+ }
+ for(hr=0;hr<HOST_REGS;hr++) {
+ if(cur->regmap[hr]==r) {
+ cur->regmap[hr]=reg;
+ cur->dirty&=~(1<<hr);
+ cur->isconst&=~(1<<hr);
+ return;
+ }
+ }
+ }
+ }
+ }
+ SysPrintf("This shouldn't happen");exit(1);
+}
+
static void mov_alloc(struct regstat *current,int i)
{
// Note: Don't need to actually alloc the source registers
}
}
+static void wb_valid(signed char pre[],signed char entry[],u_int dirty_pre,u_int dirty,uint64_t u)
+{
+ //if(dirty_pre==dirty) return;
+ int hr,reg;
+ for(hr=0;hr<HOST_REGS;hr++) {
+ if(hr!=EXCLUDE_REG) {
+ reg=pre[hr];
+ if(((~u)>>(reg&63))&1) {
+ if(reg>0) {
+ if(((dirty_pre&~dirty)>>hr)&1) {
+ if(reg>0&®<34) {
+ emit_storereg(reg,hr);
+ }
+ else if(reg>=64) {
+ assert(0);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
void rlist()
{
int i;
printf("\n");
}
-void alu_assemble(int i,struct regstat *i_regs)
+// trashes r2
+static void pass_args(int a0, int a1)
+{
+ if(a0==1&&a1==0) {
+ // must swap
+ emit_mov(a0,2); emit_mov(a1,1); emit_mov(2,0);
+ }
+ else if(a0!=0&&a1==0) {
+ emit_mov(a1,1);
+ if (a0>=0) emit_mov(a0,0);
+ }
+ else {
+ if(a0>=0&&a0!=0) emit_mov(a0,0);
+ if(a1>=0&&a1!=1) emit_mov(a1,1);
+ }
+}
+
+static void alu_assemble(int i,struct regstat *i_regs)
{
if(opcode2[i]>=0x20&&opcode2[i]<=0x23) { // ADD/ADDU/SUB/SUBU
if(rt1[i]) {
}
#endif
-void load_assemble(int i,struct regstat *i_regs)
+enum {
+ MTYPE_8000 = 0,
+ MTYPE_8020,
+ MTYPE_0000,
+ MTYPE_A000,
+ MTYPE_1F80,
+};
+
+static int get_ptr_mem_type(u_int a)
+{
+ if(a < 0x00200000) {
+ if(a<0x1000&&((start>>20)==0xbfc||(start>>24)==0xa0))
+ // return wrong, must use memhandler for BIOS self-test to pass
+ // 007 does similar stuff from a00 mirror, weird stuff
+ return MTYPE_8000;
+ return MTYPE_0000;
+ }
+ if(0x1f800000 <= a && a < 0x1f801000)
+ return MTYPE_1F80;
+ if(0x80200000 <= a && a < 0x80800000)
+ return MTYPE_8020;
+ if(0xa0000000 <= a && a < 0xa0200000)
+ return MTYPE_A000;
+ return MTYPE_8000;
+}
+
+static void *emit_fastpath_cmp_jump(int i,int addr,int *addr_reg_override)
+{
+ void *jaddr = NULL;
+ int type=0;
+ int mr=rs1[i];
+ if(((smrv_strong|smrv_weak)>>mr)&1) {
+ type=get_ptr_mem_type(smrv[mr]);
+ //printf("set %08x @%08x r%d %d\n", smrv[mr], start+i*4, mr, type);
+ }
+ else {
+ // use the mirror we are running on
+ type=get_ptr_mem_type(start);
+ //printf("set nospec @%08x r%d %d\n", start+i*4, mr, type);
+ }
+
+ if(type==MTYPE_8020) { // RAM 80200000+ mirror
+ emit_andimm(addr,~0x00e00000,HOST_TEMPREG);
+ addr=*addr_reg_override=HOST_TEMPREG;
+ type=0;
+ }
+ else if(type==MTYPE_0000) { // RAM 0 mirror
+ emit_orimm(addr,0x80000000,HOST_TEMPREG);
+ addr=*addr_reg_override=HOST_TEMPREG;
+ type=0;
+ }
+ else if(type==MTYPE_A000) { // RAM A mirror
+ emit_andimm(addr,~0x20000000,HOST_TEMPREG);
+ addr=*addr_reg_override=HOST_TEMPREG;
+ type=0;
+ }
+ else if(type==MTYPE_1F80) { // scratchpad
+ if (psxH == (void *)0x1f800000) {
+ emit_addimm(addr,-0x1f800000,HOST_TEMPREG);
+ emit_cmpimm(HOST_TEMPREG,0x1000);
+ jaddr=out;
+ emit_jc(0);
+ }
+ else {
+ // do the usual RAM check, jump will go to the right handler
+ type=0;
+ }
+ }
+
+ if(type==0)
+ {
+ emit_cmpimm(addr,RAM_SIZE);
+ jaddr=out;
+ #ifdef CORTEX_A8_BRANCH_PREDICTION_HACK
+ // Hint to branch predictor that the branch is unlikely to be taken
+ if(rs1[i]>=28)
+ emit_jno_unlikely(0);
+ else
+ #endif
+ emit_jno(0);
+ if(ram_offset!=0) {
+ emit_addimm(addr,ram_offset,HOST_TEMPREG);
+ addr=*addr_reg_override=HOST_TEMPREG;
+ }
+ }
+
+ return jaddr;
+}
+
+// return memhandler, or get directly accessable address and return 0
+static void *get_direct_memhandler(void *table, u_int addr,
+ enum stub_type type, uintptr_t *addr_host)
+{
+ uintptr_t l1, l2 = 0;
+ l1 = ((uintptr_t *)table)[addr>>12];
+ if ((l1 & (1ul << (sizeof(l1)*8-1))) == 0) {
+ uintptr_t v = l1 << 1;
+ *addr_host = v + addr;
+ return NULL;
+ }
+ else {
+ l1 <<= 1;
+ if (type == LOADB_STUB || type == LOADBU_STUB || type == STOREB_STUB)
+ l2 = ((uintptr_t *)l1)[0x1000/4 + 0x1000/2 + (addr&0xfff)];
+ else if (type == LOADH_STUB || type == LOADHU_STUB || type == STOREH_STUB)
+ l2=((uintptr_t *)l1)[0x1000/4 + (addr&0xfff)/2];
+ else
+ l2=((uintptr_t *)l1)[(addr&0xfff)/4];
+ if ((l2 & (1<<31)) == 0) {
+ uintptr_t v = l2 << 1;
+ *addr_host = v + (addr&0xfff);
+ return NULL;
+ }
+ return (void *)(l2 << 1);
+ }
+}
+
+static void load_assemble(int i,struct regstat *i_regs)
{
int s,th,tl,addr;
int offset;
}
}
-void c1ls_assemble(int i,struct regstat *i_regs)
+static void cop0_assemble(int i,struct regstat *i_regs)
+{
+ if(opcode2[i]==0) // MFC0
+ {
+ signed char t=get_reg(i_regs->regmap,rt1[i]);
+ u_int copr=(source[i]>>11)&0x1f;
+ //assert(t>=0); // Why does this happen? OOT is weird
+ if(t>=0&&rt1[i]!=0) {
+ emit_readword(®_cop0[copr],t);
+ }
+ }
+ else if(opcode2[i]==4) // MTC0
+ {
+ signed char s=get_reg(i_regs->regmap,rs1[i]);
+ char copr=(source[i]>>11)&0x1f;
+ assert(s>=0);
+ wb_register(rs1[i],i_regs->regmap,i_regs->dirty);
+ if(copr==9||copr==11||copr==12||copr==13) {
+ emit_readword(&last_count,HOST_TEMPREG);
+ emit_loadreg(CCREG,HOST_CCREG); // TODO: do proper reg alloc
+ emit_add(HOST_CCREG,HOST_TEMPREG,HOST_CCREG);
+ emit_addimm(HOST_CCREG,CLOCK_ADJUST(ccadj[i]),HOST_CCREG);
+ emit_writeword(HOST_CCREG,&Count);
+ }
+ // What a mess. The status register (12) can enable interrupts,
+ // so needs a special case to handle a pending interrupt.
+ // The interrupt must be taken immediately, because a subsequent
+ // instruction might disable interrupts again.
+ if(copr==12||copr==13) {
+ if (is_delayslot) {
+ // burn cycles to cause cc_interrupt, which will
+ // reschedule next_interupt. Relies on CCREG from above.
+ assem_debug("MTC0 DS %d\n", copr);
+ emit_writeword(HOST_CCREG,&last_count);
+ emit_movimm(0,HOST_CCREG);
+ emit_storereg(CCREG,HOST_CCREG);
+ emit_loadreg(rs1[i],1);
+ emit_movimm(copr,0);
+ emit_call(pcsx_mtc0_ds);
+ emit_loadreg(rs1[i],s);
+ return;
+ }
+ emit_movimm(start+i*4+4,HOST_TEMPREG);
+ emit_writeword(HOST_TEMPREG,&pcaddr);
+ 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);
+ if(copr==9||copr==11||copr==12||copr==13) {
+ emit_readword(&Count,HOST_CCREG);
+ emit_readword(&next_interupt,HOST_TEMPREG);
+ emit_addimm(HOST_CCREG,-CLOCK_ADJUST(ccadj[i]),HOST_CCREG);
+ emit_sub(HOST_CCREG,HOST_TEMPREG,HOST_CCREG);
+ emit_writeword(HOST_TEMPREG,&last_count);
+ emit_storereg(CCREG,HOST_CCREG);
+ }
+ if(copr==12||copr==13) {
+ assert(!is_delayslot);
+ emit_readword(&pending_exception,14);
+ emit_test(14,14);
+ emit_jne(&do_interrupt);
+ }
+ emit_loadreg(rs1[i],s);
+ if(get_reg(i_regs->regmap,rs1[i]|64)>=0)
+ emit_loadreg(rs1[i]|64,get_reg(i_regs->regmap,rs1[i]|64));
+ }
+ else
+ {
+ assert(opcode2[i]==0x10);
+ //if((source[i]&0x3f)==0x10) // RFE
+ {
+ emit_readword(&Status,0);
+ emit_andimm(0,0x3c,1);
+ emit_andimm(0,~0xf,0);
+ emit_orrshr_imm(1,2,0);
+ emit_writeword(0,&Status);
+ }
+ }
+}
+
+static void cop1_unusable(int i,struct regstat *i_regs)
+{
+ // XXX: should just just do the exception instead
+ //if(!cop1_usable)
+ {
+ void *jaddr=out;
+ emit_jmp(0);
+ add_stub_r(FP_STUB,jaddr,out,i,0,i_regs,is_delayslot,0);
+ }
+}
+
+static void cop1_assemble(int i,struct regstat *i_regs)
{
cop1_unusable(i, i_regs);
}
-void c2ls_assemble(int i,struct regstat *i_regs)
+static void c1ls_assemble(int i,struct regstat *i_regs)
+{
+ cop1_unusable(i, i_regs);
+}
+
+// FP_STUB
+static void do_cop1stub(int n)
+{
+ literal_pool(256);
+ assem_debug("do_cop1stub %x\n",start+stubs[n].a*4);
+ set_jump_target(stubs[n].addr, out);
+ int i=stubs[n].a;
+// int rs=stubs[n].b;
+ struct regstat *i_regs=(struct regstat *)stubs[n].c;
+ int ds=stubs[n].d;
+ if(!ds) {
+ load_all_consts(regs[i].regmap_entry,regs[i].wasdirty,i);
+ //if(i_regs!=®s[i]) printf("oops: regs[i]=%x i_regs=%x",(int)®s[i],(int)i_regs);
+ }
+ //else {printf("fp exception in delay slot\n");}
+ wb_dirtys(i_regs->regmap_entry,i_regs->wasdirty);
+ if(regs[i].regmap_entry[HOST_CCREG]!=CCREG) emit_loadreg(CCREG,HOST_CCREG);
+ emit_movimm(start+(i-ds)*4,EAX); // Get PC
+ emit_addimm(HOST_CCREG,CLOCK_ADJUST(ccadj[i]),HOST_CCREG); // CHECK: is this right? There should probably be an extra cycle...
+ emit_jmp(ds?fp_exception_ds:fp_exception);
+}
+
+static void cop2_get_dreg(u_int copr,signed char tl,signed char temp)
+{
+ switch (copr) {
+ case 1:
+ case 3:
+ case 5:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ emit_readword(®_cop2d[copr],tl);
+ emit_signextend16(tl,tl);
+ emit_writeword(tl,®_cop2d[copr]); // hmh
+ break;
+ case 7:
+ case 16:
+ case 17:
+ case 18:
+ case 19:
+ emit_readword(®_cop2d[copr],tl);
+ emit_andimm(tl,0xffff,tl);
+ emit_writeword(tl,®_cop2d[copr]);
+ break;
+ case 15:
+ emit_readword(®_cop2d[14],tl); // SXY2
+ emit_writeword(tl,®_cop2d[copr]);
+ 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]);
+ break;
+ default:
+ emit_readword(®_cop2d[copr],tl);
+ break;
+ }
+}
+
+static void cop2_put_dreg(u_int copr,signed char sl,signed char temp)
+{
+ switch (copr) {
+ case 15:
+ emit_readword(®_cop2d[13],temp); // SXY1
+ emit_writeword(sl,®_cop2d[copr]);
+ emit_writeword(temp,®_cop2d[12]); // SXY0
+ emit_readword(®_cop2d[14],temp); // SXY2
+ emit_writeword(sl,®_cop2d[14]);
+ emit_writeword(temp,®_cop2d[13]); // SXY1
+ break;
+ case 28:
+ emit_andimm(sl,0x001f,temp);
+ emit_shlimm(temp,7,temp);
+ emit_writeword(temp,®_cop2d[9]);
+ emit_andimm(sl,0x03e0,temp);
+ emit_shlimm(temp,2,temp);
+ emit_writeword(temp,®_cop2d[10]);
+ emit_andimm(sl,0x7c00,temp);
+ emit_shrimm(temp,3,temp);
+ emit_writeword(temp,®_cop2d[11]);
+ emit_writeword(sl,®_cop2d[28]);
+ break;
+ case 30:
+ emit_movs(sl,temp);
+ emit_mvnmi(temp,temp);
+#if defined(HAVE_ARMV5) || defined(__aarch64__)
+ emit_clz(temp,temp);
+#else
+ emit_movs(temp,HOST_TEMPREG);
+ emit_movimm(0,temp);
+ emit_jeq((int)out+4*4);
+ emit_addpl_imm(temp,1,temp);
+ emit_lslpls_imm(HOST_TEMPREG,1,HOST_TEMPREG);
+ emit_jns((int)out-2*4);
+#endif
+ emit_writeword(sl,®_cop2d[30]);
+ emit_writeword(temp,®_cop2d[31]);
+ break;
+ case 31:
+ break;
+ default:
+ emit_writeword(sl,®_cop2d[copr]);
+ break;
+ }
+}
+
+static void c2ls_assemble(int i,struct regstat *i_regs)
{
int s,tl;
int ar;
}
}
+static void cop2_assemble(int i,struct regstat *i_regs)
+{
+ u_int copr=(source[i]>>11)&0x1f;
+ signed char temp=get_reg(i_regs->regmap,-1);
+ if (opcode2[i]==0) { // MFC2
+ signed char tl=get_reg(i_regs->regmap,rt1[i]);
+ if(tl>=0&&rt1[i]!=0)
+ cop2_get_dreg(copr,tl,temp);
+ }
+ else if (opcode2[i]==4) { // MTC2
+ signed char sl=get_reg(i_regs->regmap,rs1[i]);
+ cop2_put_dreg(copr,sl,temp);
+ }
+ else if (opcode2[i]==2) // CFC2
+ {
+ signed char tl=get_reg(i_regs->regmap,rt1[i]);
+ if(tl>=0&&rt1[i]!=0)
+ emit_readword(®_cop2c[copr],tl);
+ }
+ else if (opcode2[i]==6) // CTC2
+ {
+ signed char sl=get_reg(i_regs->regmap,rs1[i]);
+ switch(copr) {
+ case 4:
+ case 12:
+ case 20:
+ case 26:
+ case 27:
+ case 29:
+ case 30:
+ 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);
+ break;
+ default:
+ temp=sl;
+ break;
+ }
+ emit_writeword(temp,®_cop2c[copr]);
+ assert(sl>=0);
+ }
+}
+
#ifndef multdiv_assemble
void multdiv_assemble(int i,struct regstat *i_regs)
{
emit_jmp(jump_intcall);
}
+static void speculate_mov(int rs,int rt)
+{
+ if(rt!=0) {
+ smrv_strong_next|=1<<rt;
+ smrv[rt]=smrv[rs];
+ }
+}
+
+static void speculate_mov_weak(int rs,int rt)
+{
+ if(rt!=0) {
+ smrv_weak_next|=1<<rt;
+ smrv[rt]=smrv[rs];
+ }
+}
+
+static void speculate_register_values(int i)
+{
+ if(i==0) {
+ memcpy(smrv,psxRegs.GPR.r,sizeof(smrv));
+ // gp,sp are likely to stay the same throughout the block
+ smrv_strong_next=(1<<28)|(1<<29)|(1<<30);
+ smrv_weak_next=~smrv_strong_next;
+ //printf(" llr %08x\n", smrv[4]);
+ }
+ smrv_strong=smrv_strong_next;
+ smrv_weak=smrv_weak_next;
+ switch(itype[i]) {
+ case ALU:
+ if ((smrv_strong>>rs1[i])&1) speculate_mov(rs1[i],rt1[i]);
+ else if((smrv_strong>>rs2[i])&1) speculate_mov(rs2[i],rt1[i]);
+ else if((smrv_weak>>rs1[i])&1) speculate_mov_weak(rs1[i],rt1[i]);
+ else if((smrv_weak>>rs2[i])&1) speculate_mov_weak(rs2[i],rt1[i]);
+ else {
+ smrv_strong_next&=~(1<<rt1[i]);
+ smrv_weak_next&=~(1<<rt1[i]);
+ }
+ break;
+ case SHIFTIMM:
+ smrv_strong_next&=~(1<<rt1[i]);
+ smrv_weak_next&=~(1<<rt1[i]);
+ // fallthrough
+ case IMM16:
+ if(rt1[i]&&is_const(®s[i],rt1[i])) {
+ int value,hr=get_reg(regs[i].regmap,rt1[i]);
+ if(hr>=0) {
+ if(get_final_value(hr,i,&value))
+ smrv[rt1[i]]=value;
+ else smrv[rt1[i]]=constmap[i][hr];
+ smrv_strong_next|=1<<rt1[i];
+ }
+ }
+ else {
+ if ((smrv_strong>>rs1[i])&1) speculate_mov(rs1[i],rt1[i]);
+ else if((smrv_weak>>rs1[i])&1) speculate_mov_weak(rs1[i],rt1[i]);
+ }
+ break;
+ case LOAD:
+ if(start<0x2000&&(rt1[i]==26||(smrv[rt1[i]]>>24)==0xa0)) {
+ // special case for BIOS
+ smrv[rt1[i]]=0xa0000000;
+ smrv_strong_next|=1<<rt1[i];
+ break;
+ }
+ // fallthrough
+ case SHIFT:
+ case LOADLR:
+ case MOV:
+ smrv_strong_next&=~(1<<rt1[i]);
+ smrv_weak_next&=~(1<<rt1[i]);
+ break;
+ case COP0:
+ case COP2:
+ if(opcode2[i]==0||opcode2[i]==2) { // MFC/CFC
+ smrv_strong_next&=~(1<<rt1[i]);
+ smrv_weak_next&=~(1<<rt1[i]);
+ }
+ break;
+ case C2LS:
+ if (opcode[i]==0x32) { // LWC2
+ smrv_strong_next&=~(1<<rt1[i]);
+ smrv_weak_next&=~(1<<rt1[i]);
+ }
+ break;
+ }
+#if 0
+ int r=4;
+ printf("x %08x %08x %d %d c %08x %08x\n",smrv[r],start+i*4,
+ ((smrv_strong>>r)&1),(smrv_weak>>r)&1,regs[i].isconst,regs[i].wasconst);
+#endif
+}
+
void ds_assemble(int i,struct regstat *i_regs)
{
speculate_register_values(i);
static void do_ccstub(int n)
{
literal_pool(256);
- assem_debug("do_ccstub %x\n",start+stubs[n].b*4);
+ assem_debug("do_ccstub %lx\n",start+stubs[n].b*4);
set_jump_target(stubs[n].addr, out);
int i=stubs[n].b;
if(stubs[n].d==NULLDS) {
#define DRC_TEST_VAL 0x74657374
-static int new_dynarec_test(void)
+static void new_dynarec_test(void)
{
- int (*testfunc)(void) = (void *)out;
+ int (*testfunc)(void);
void *beginning;
- int ret;
+ int ret[2];
+ size_t i;
- beginning = start_block();
- emit_movimm(DRC_TEST_VAL,0); // test
- emit_jmpreg(14);
- literal_pool(0);
- end_block(beginning);
- SysPrintf("testing if we can run recompiled code..\n");
- ret = testfunc();
- if (ret == DRC_TEST_VAL)
+ // check structure linkage
+ if ((void *)reg != (void *)&psxRegs
+ || (u_char *)rcnts - (u_char *)reg != sizeof(psxRegs))
+ {
+ SysPrintf("linkage_arm miscompilation/breakage detected.\n");
+ }
+
+ SysPrintf("testing if we can run recompiled code...\n");
+ ((volatile u_int *)out)[0]++; // make cache dirty
+
+ for (i = 0; i < ARRAY_SIZE(ret); i++) {
+ out = translation_cache;
+ beginning = start_block();
+ emit_movimm(DRC_TEST_VAL + i, 0); // test
+ emit_ret();
+ literal_pool(0);
+ end_block(beginning);
+ testfunc = beginning;
+ ret[i] = testfunc();
+ }
+
+ if (ret[0] == DRC_TEST_VAL && ret[1] == DRC_TEST_VAL + 1)
SysPrintf("test passed.\n");
else
- SysPrintf("test failed: %08x\n", ret);
+ SysPrintf("test failed, will likely crash soon (r=%08x %08x)\n", ret[0], ret[1]);
out = translation_cache;
- return ret == DRC_TEST_VAL;
}
// clear the state completely, instead of just marking
#endif
case 0x12: strcpy(insn[i],"COP2"); type=NI;
op2=(source[i]>>21)&0x1f;
- //if (op2 & 0x10) {
+ //if (op2 & 0x10)
if (source[i]&0x3f) { // use this hack to support old savestates with patched gte insns
if (gte_handlers[source[i]&0x3f]!=NULL) {
if (gte_regnames[source[i]&0x3f]!=NULL)
break;
case 3:
// Clear jump_out
- #ifdef __arm__
+ #if defined(__arm__) || defined(__aarch64__)
if((expirep&2047)==0)
do_clear_cache();
#endif