diff --git a/src/cpu/core_dynrec/risc_x64.h b/src/cpu/core_dynrec/risc_x64.h index 0f1d9490..44ccf5a3 100644 --- a/src/cpu/core_dynrec/risc_x64.h +++ b/src/cpu/core_dynrec/risc_x64.h @@ -82,45 +82,98 @@ static void gen_mov_regs(HostReg reg_dst,HostReg reg_src) { cache_addb(0xc0+(reg_dst<<3)+reg_src); } +// move a 64bit constant value into a full register +static void gen_mov_reg_qword(HostReg dest_reg,Bit64u imm) { + cache_addb(0x48); + cache_addb(0xb8+dest_reg); // mov dest_reg,imm + cache_addq(imm); +} -static INLINE void gen_reg_memaddr(HostReg reg,void* data) { - Bit64s diff = (Bit64s)data-((Bit64s)cache.pos+5); + +// This function generates an instruction with register addressing and a memory location +static INLINE void gen_reg_memaddr(HostReg reg,void* data,Bit8u op,Bit8u prefix=0) { + Bit64s diff = (Bit64s)data-((Bit64s)cache.pos+(prefix?7:6)); // if ((diff<0x80000000LL) && (diff>-0x80000000LL)) { //clang messes itself up on this... if ( (diff>>63) == (diff>>31) ) { //signed bit extend, test to see if value fits in a Bit32s + // mov reg,[rip+diff] (or similar, depending on the op) to fetch *data + if(prefix) cache_addb(prefix); + cache_addb(op); cache_addb(0x05+(reg<<3)); // RIP-relative addressing is offset after the instruction cache_addd((Bit32u)(((Bit64u)diff)&0xffffffffLL)); } else if ((Bit64u)data<0x100000000LL) { + // mov reg,[data] (or similar, depending on the op) when absolute address of data is <4GB + if(prefix) cache_addb(prefix); + cache_addb(op); cache_addw(0x2504+(reg<<3)); cache_addd((Bit32u)(((Bit64u)data)&0xffffffffLL)); } else { - E_Exit("DRC64:Unhandled memory reference"); + // load 64-bit data into tmp_reg and do mov reg,[tmp_reg] (or similar, depending on the op) + HostReg tmp_reg = HOST_EAX; + if(reg == HOST_EAX) tmp_reg = HOST_ECX; + + cache_addb(0x50+tmp_reg); // push rax/rcx + gen_mov_reg_qword(tmp_reg,(Bit64u)data); + + if(prefix) cache_addb(prefix); + cache_addb(op); + cache_addb(tmp_reg+(reg<<3)); + + cache_addb(0x58+tmp_reg); // pop rax/rcx } } -static INLINE void gen_memaddr(Bitu op,void* data,Bitu off) { - Bit64s diff; - diff = (Bit64s)data-((Bit64s)cache.pos+off+5); +// Same as above, but with immediate addressing and a memory location +static INLINE void gen_memaddr(Bitu modreg,void* data,Bitu off,Bitu imm,Bit8u op,Bit8u prefix=0) { + Bit64s diff = (Bit64s)data-((Bit64s)cache.pos+off+(prefix?7:6)); // if ((diff<0x80000000LL) && (diff>-0x80000000LL)) { if ( (diff>>63) == (diff>>31) ) { // RIP-relative addressing is offset after the instruction - cache_addb(op+1); - cache_addd((Bit32u)(((Bit64u)diff)&0xffffffffLL)); + if(prefix) cache_addb(prefix); + cache_addw(op+((modreg+1)<<8)); + cache_addd((Bit32u)(((Bit64u)diff)&0xffffffffLL)); + + switch(off) { + case 1: cache_addb(((Bit8u)imm&0xff)); break; + case 2: cache_addw(((Bit16u)imm&0xffff)); break; + case 4: cache_addd(((Bit32u)imm&0xffffffff)); break; + } + } else if ((Bit64u)data<0x100000000LL) { - cache_addb(op); + if(prefix) cache_addb(prefix); + cache_addw(op+(modreg<<8)); cache_addb(0x25); cache_addd((Bit32u)(((Bit64u)data)&0xffffffffLL)); + + switch(off) { + case 1: cache_addb(((Bit8u)imm&0xff)); break; + case 2: cache_addw(((Bit16u)imm&0xffff)); break; + case 4: cache_addd(((Bit32u)imm&0xffffffff)); break; + } + } else { - E_Exit("DRC64:Unhandled memory reference"); + HostReg tmp_reg = HOST_EAX; + + cache_addb(0x50+tmp_reg); // push rax + gen_mov_reg_qword(tmp_reg,(Bit64u)data); + + if(prefix) cache_addb(prefix); + cache_addw(op+((modreg-4+tmp_reg)<<8)); + + switch(off) { + case 1: cache_addb(((Bit8u)imm&0xff)); break; + case 2: cache_addw(((Bit16u)imm&0xffff)); break; + case 4: cache_addd(((Bit32u)imm&0xffffffff)); break; + } + + cache_addb(0x58+tmp_reg); // pop rax } } // move a 32bit (dword==true) or 16bit (dword==false) value from memory into dest_reg // 16bit moves may destroy the upper 16bit of the destination register -static void gen_mov_word_to_reg(HostReg dest_reg,void* data,bool dword) { - if (!dword) cache_addb(0x66); - cache_addb(0x8b); // mov reg,[data] - gen_reg_memaddr(dest_reg,data); +static void gen_mov_word_to_reg(HostReg dest_reg,void* data,bool dword,Bit8u prefix=0) { + gen_reg_memaddr(dest_reg,data,0x8b,(dword?prefix:0x66)); // mov reg,[data] } // move a 16bit constant value into dest_reg @@ -138,10 +191,8 @@ static void gen_mov_dword_to_reg_imm(HostReg dest_reg,Bit32u imm) { } // move 32bit (dword==true) or 16bit (dword==false) of a register into memory -static void gen_mov_word_from_reg(HostReg src_reg,void* dest,bool dword) { - if (!dword) cache_addb(0x66); - cache_addb(0x89); // mov [data],reg - gen_reg_memaddr(src_reg,dest); +static void gen_mov_word_from_reg(HostReg src_reg,void* dest,bool dword,Bit8u prefix=0) { + gen_reg_memaddr(src_reg,dest,0x89,(dword?prefix:0x66)); // mov [data],reg } // move an 8bit value from memory into dest_reg @@ -149,8 +200,7 @@ static void gen_mov_word_from_reg(HostReg src_reg,void* dest,bool dword) { // this function does not use FC_OP1/FC_OP2 as dest_reg as these // registers might not be directly byte-accessible on some architectures static void gen_mov_byte_to_reg_low(HostReg dest_reg,void* data) { - cache_addb(0x8a); // mov reg,[data] - gen_reg_memaddr(dest_reg,data); + gen_reg_memaddr(dest_reg,data,0x8a); // mov reg, byte [data] } // move an 8bit value from memory into dest_reg @@ -158,9 +208,7 @@ static void gen_mov_byte_to_reg_low(HostReg dest_reg,void* data) { // this function can use FC_OP1/FC_OP2 as dest_reg which are // not directly byte-accessible on some architectures static void gen_mov_byte_to_reg_low_canuseword(HostReg dest_reg,void* data) { - cache_addb(0x66); - cache_addb(0x8b); // mov reg,[data] - gen_reg_memaddr(dest_reg,data); + gen_reg_memaddr(dest_reg,data,0x8b,0x66); // mov reg, word [data] } // move an 8bit constant value into dest_reg @@ -184,8 +232,7 @@ static void gen_mov_byte_to_reg_low_imm_canuseword(HostReg dest_reg,Bit8u imm) { // move the lowest 8bit of a register into memory static void gen_mov_byte_from_reg_low(HostReg src_reg,void* dest) { - cache_addb(0x88); // mov [data],reg - gen_reg_memaddr(src_reg,dest); + gen_reg_memaddr(src_reg,dest,0x88); // mov byte [data],reg } @@ -208,8 +255,7 @@ static void gen_extend_word(bool sign,HostReg reg) { // add a 32bit value from memory to a full register static void gen_add(HostReg reg,void* op) { - cache_addb(0x03); // add reg,[data] - gen_reg_memaddr(reg,op); + gen_reg_memaddr(reg,op,0x03); // add reg,[data] } // add a 32bit constant value to a full register @@ -228,31 +274,20 @@ static void gen_and_imm(HostReg reg,Bit32u imm) { // move a 32bit constant value into memory static void gen_mov_direct_dword(void* dest,Bit32u imm) { - cache_addb(0xc7); // mov [data],imm - gen_memaddr(0x04,dest,4); - cache_addd(imm); + gen_memaddr(0x4,dest,4,imm,0xc7); // mov [data],imm } -// move a 64bit constant value into a full register -static void gen_mov_reg_qword(HostReg dest_reg,Bit64u imm) { - cache_addb(0x48); - cache_addb(0xb8+dest_reg); // mov dest_reg,imm - cache_addq(imm); -} // move an address into memory static void INLINE gen_mov_direct_ptr(void* dest,DRC_PTR_SIZE_IM imm) { gen_mov_reg_qword(HOST_EAX,imm); - cache_addb(0x48); - gen_mov_word_from_reg(HOST_EAX,dest,true); + gen_mov_word_from_reg(HOST_EAX,dest,true,0x48); // 0x48 prefixes full 64-bit mov } // add an 8bit constant value to a memory value static void gen_add_direct_byte(void* dest,Bit8s imm) { - cache_addb(0x83); // add [data],imm - gen_memaddr(0x4,dest,1); - cache_addb(imm); + gen_memaddr(0x4,dest,1,imm,0x83); // add [data],imm } // add a 32bit (dword==true) or 16bit (dword==false) constant value to a memory value @@ -261,22 +296,12 @@ static void gen_add_direct_word(void* dest,Bit32u imm,bool dword) { gen_add_direct_byte(dest,(Bit8s)imm); return; } - if (!dword) cache_addb(0x66); - cache_addb(0x81); // add [data],imm - if (dword) { - gen_memaddr(0x4,dest,4); // size of following immediate value - cache_addd((Bit32u)imm); - } else { - gen_memaddr(0x4,dest,2); // size of following immediate value - cache_addw((Bit16u)imm); - } + gen_memaddr(0x4,dest,(dword?4:2),imm,0x81,(dword?0:0x66)); // add [data],imm } // subtract an 8bit constant value from a memory value static void gen_sub_direct_byte(void* dest,Bit8s imm) { - cache_addb(0x83); // sub [data],imm - gen_memaddr(0x2c,dest,1); - cache_addb(imm); + gen_memaddr(0x2c,dest,1,imm,0x83); } // subtract a 32bit (dword==true) or 16bit (dword==false) constant value from a memory value @@ -285,15 +310,7 @@ static void gen_sub_direct_word(void* dest,Bit32u imm,bool dword) { gen_sub_direct_byte(dest,(Bit8s)imm); return; } - if (!dword) cache_addb(0x66); - cache_addw(0x81); // sub [data],imm - if (dword) { - gen_memaddr(0x2c,dest,4); // size of following immediate value - cache_addd((Bit32u)imm); - } else { - gen_memaddr(0x2c,dest,2); // size of following immediate value - cache_addw((Bit16u)imm); - } + gen_memaddr(0x2c,dest,(dword?4:2),imm,0x81,(dword?0:0x66)); // sub [data],imm } @@ -510,12 +527,10 @@ static void INLINE gen_load_param_mem(Bitu mem,Bitu param) { break; #if defined (_MSC_VER) case 2: // mov r8,[mem] - cache_addb(0x49); - gen_mov_word_to_reg(0,(void*)mem,true); + gen_mov_word_to_reg(0,(void*)mem,true,0x49); // 0x49, use x64 rX regs break; case 3: // mov r9,[mem] - cache_addb(0x49); - gen_mov_word_to_reg(1,(void*)mem,true); + gen_mov_word_to_reg(1,(void*)mem,true,0x49); // 0x49, use x64 rX regs break; #else case 2: // mov rdx,[mem]