nulldc-360/nullDC/emitter/emitter.cpp
2012-09-09 18:16:36 +02:00

624 lines
13 KiB
C++

#include "types.h"
#include "emitter.h"
#include <dc/sh4/sh4_registers.h>
extern "C"{
#include <ppc/cache.h>
}
int ppc_condition_flags[][3] = { // bo,bi,logical
{PPC_CC_T, PPC_CC_OVR,0}, // 0
{PPC_CC_F, PPC_CC_OVR,0}, // 1
{PPC_CC_T, PPC_CC_NEG,1}, // 2
{PPC_CC_F, PPC_CC_NEG,1}, // 3
{PPC_CC_T, PPC_CC_ZER,0}, // 4
{PPC_CC_F, PPC_CC_ZER,0}, // 5
{PPC_CC_F, PPC_CC_POS,1}, // 6
{PPC_CC_T, PPC_CC_POS,1}, // 7
{PPC_CC_A, PPC_CC_NEG,0}, // 8 fake
{PPC_CC_A, PPC_CC_NEG,0}, // 9 fake
{PPC_CC_A, PPC_CC_NEG,0}, // A fake
{PPC_CC_A, PPC_CC_NEG,0}, // B fake
{PPC_CC_T, PPC_CC_NEG,0}, // C
{PPC_CC_F, PPC_CC_NEG,0}, // D
{PPC_CC_F, PPC_CC_POS,0}, // E
{PPC_CC_T, PPC_CC_POS,0}, // F
};
bool IsS8(u32 value)
{
if (((value&0xFFFFFF80)==0xFFFFFF80) ||
(value&0xFFFFFF80)==0 )
return true;
else
return false;
}
ppc_ptr ppc_ptr::create(unat ptr)
{
ppc_ptr rv(0);
rv.ptr_int=ptr;
return rv;
}
ppc_ptr_imm ppc_ptr_imm::create(unat ptr)
{
ppc_ptr_imm rv(0);
rv.ptr_int=ptr;
return rv;
}
void* ppc_Label::GetPtr()
{
return owner->ppc_buff + this->target_opcode;
}
void ppc_block::Init(dyna_reallocFP* ral,dyna_finalizeFP* alf)
{
ralloc=ral;
allocfin=alf;
ppc_buff=0;
ppc_indx=0;
ppc_size=0;
do_realloc=true;
bc_tab_next_idx=0;
}
//Generates code.if user_data is non zero , user_data_size bytes are allocated after the executable code
//and user_data is set to the first byte of em.Allways 16 byte alligned
void* ppc_block::Generate()
{
if (do_realloc)
{
u8* final_buffer=0;
final_buffer=(u8*)allocfin(ppc_buff,ppc_size,ppc_indx);
if (final_buffer==0)
return 0;
ppc_buff=final_buffer;
}
ApplyPatches(ppc_buff);
memicbi(ppc_buff,ppc_indx);
bool force=false;
// force=true;
// force=(u32)ppc_buff>=0x8054f510;
if(do_disasm || force) printf("Gen %p %04x %04x\n",ppc_buff,ppc_size,ppc_indx);
if (do_disasm || force) for(u32 i=0;i<ppc_indx;i+=4) disassemble((u32)&ppc_buff[i],*(u32*)&ppc_buff[i]);
do_disasm=false;
return &ppc_buff[0];
}
struct ppc_block_externs_i : ppc_block_externs
{
struct extern_entry { u8* dst;u32 offs:28;u32 size:4; };
vector<extern_entry> externs;
void Apply(u8* base)
{
for (u32 i=0;i<externs.size();i++)
{
u8* dest=(u8*)externs[i].dst;
u8* code_offset=base+externs[i].offs;
u8* diff_offset=code_offset+externs[i].size;
u32 diff=(u32)(dest-diff_offset);
if (externs[i].size==1)
{
verify(IsS8(diff));
*code_offset=(u8)diff;
}
else if (externs[i].size==2)
{
*(u16*)code_offset=(u16)diff;
}
else if (externs[i].size==4)
{
*(u32*)code_offset=(u32)diff;
}
}
}
bool Modify(u32 offs,u8* dst)
{
for (u32 i=0;i<externs.size();i++)
{
if (externs[i].offs==offs)
{
externs[i].dst=dst;
return true;
}
}
return false;
}
void Add(u8* dst,u32 offset,u32 size)
{
extern_entry vta={dst,offset,size};
externs.push_back(vta);
}
void Free()
{
delete this;
}
};
ppc_block_externs::~ppc_block_externs() {}
void ppc_block_externs::Apply(void* lolwhut) { ((ppc_block_externs_i*)this)->Apply((u8*)lolwhut); }
bool ppc_block_externs::Modify(u32 offs,u8* dst) { return ((ppc_block_externs_i*)this)->Modify(offs,dst); }
void ppc_block_externs::Free() { ((ppc_block_externs_i*)this)->Free(); }
ppc_block_externs* ppc_block::GetExterns()
{
ppc_block_externs_i* rv=new ppc_block_externs_i();
u8* base=ppc_buff;
for (u32 i=0;i<patches.size();i++)
{
u8* dest=(u8*)patches[i].dest;
if (patches[i].type&16)
{
if (patches[i].lbl->owner==this)
dest = base + patches[i].lbl->target_opcode;
else
dest = patches[i].lbl->owner->ppc_buff + patches[i].lbl->target_opcode;
}
u32 start_diff=(u32)(dest-base);
if (start_diff>=ppc_indx)
{
rv->Add(dest,patches[i].offset,patches[i].type&0xF);
}
}
return rv;
}
/*void ppc_block::CopyTo(void* to)
{
memcpy(to,ppc_buff,ppc_indx);
free(ppc_buff);
ppc_buff=(u8*)to;
ApplyPatches(ppc_buff);
}
*/
//wut ?
void ppc_block::ApplyPatches(u8* base)
{
for (u32 i=0;i<patches.size();i++)
{
u8* dest=(u8*)patches[i].dest;
u8* code_offset=base+patches[i].offset;
u8* diff_offset=code_offset;//gli wtf?!? +(patches[i].type&0xF);
if (patches[i].type&16)
{
if (patches[i].lbl->owner==this)
dest = base + patches[i].lbl->target_opcode;
else
dest = patches[i].lbl->owner->ppc_buff + patches[i].lbl->target_opcode;
}
s32 diff=(s32)(dest-diff_offset);
if ((patches[i].type&0xF)==1)
{
verify(IsS8(diff));
*code_offset=(u8)diff;
}
else if ((patches[i].type&0xF)==2)
{
*(u16*)code_offset=(u16)diff;
}
else if ((patches[i].type&0xF)==4)
{
*(u32*)code_offset=(u32)diff;
}
else if ((patches[i].type&0xF)==5) // 5 is for BC
{
verify(!(diff&3));
verify(diff<0x10000);
*(u32*)code_offset|=(u32)diff&0xffff;
}
else if ((patches[i].type&0xF)==6) // 6 is for B
{
verify(!(diff&3));
verify(diff<0x04000000);
*(u32*)code_offset|=(u32)diff&0x03ffffff;
}
}
/* process branches */
u32 i;
PowerPC_instr op,newop;
u32 opaddr,jmpaddr;
for(i=0;i<ppc_indx;i+=4)
{
opaddr=(u32)&ppc_buff[i];
op=*(u32*)&ppc_buff[i];
jmpaddr=(op&0x03ffffff)-(opaddr&0x7fffffff);
if((op&0xfc000000) == 0) // b
{
//printf("b %08x %08x %08x\n",opaddr,op,jmpaddr);
GEN_B(newop,jmpaddr>>2,0,0);
*(PowerPC_instr*)opaddr=newop;
}
else if((op&0xfc000000) == 1<<26) // bl
{
//printf("bl %08x %08x %08x\n",opaddr,op,jmpaddr);
GEN_B(newop,jmpaddr>>2,0,1);
*(PowerPC_instr*)opaddr=newop;
}
else if((op&0xfc000000) == 5<<26) // bc
{
u32 absaddr=bc_tab[(op>>2)&0x3fff];
jmpaddr=absaddr-opaddr;
if((s32)jmpaddr>=-32768 && (s32)jmpaddr<=32767)
{
//printf("bc %08x %08x %08x\n",opaddr,op,jmpaddr);
}
else
{
// invert branch
int bo,bo_n=0;
bo=(op>>PPC_RD_SHIFT)&PPC_REG_MASK;
switch(bo&~3){
case PPC_CC_F: bo_n=PPC_CC_T;break;
case PPC_CC_T: bo_n=PPC_CC_F;break;
default: verify(false);
}
bo_n|=bo&3;
op&=~(PPC_REG_MASK<<PPC_RD_SHIFT);
PPC_SET_BO(op,bo_n);
// add real branch next to the bc, with the original link bit
int lk=op&1;
op&=~1;
PowerPC_instr b;
jmpaddr-=4;
verify((s32)jmpaddr>=-(1<<25) && (s32)jmpaddr<=(1<<25));
GEN_B(b,jmpaddr>>2,0,lk);
verify(*((PowerPC_instr*)opaddr+1)==PPC_NOP);
*((PowerPC_instr*)opaddr+1)=b;
jmpaddr=8;
//printf("ind bc %08x %08x %08x\n",opaddr,op,jmpaddr);
}
newop=op&~0xfc00fffc;
PPC_SET_OPCODE(newop,PPC_OPCODE_BC);
PPC_SET_BD(newop,jmpaddr>>2);
*(PowerPC_instr*)opaddr=newop;
}
}
}
ppc_block::ppc_block()
{
labels.reserve(64);
do_disasm=false;
do_disasm_imm=false;
}
ppc_block::~ppc_block()
{
//ensure everything is free'd :)
Free();
}
//Will free any used resources exept generated code
void ppc_block::Free()
{
for (u32 i =0;i<labels.size();i++)
delete labels[i];
labels.clear();
}
void ppc_block::ppc_buffer_ensure(u32 size)
{
if (this->ppc_size<(size+ppc_indx))
{
verify(do_realloc!=false);
u32 old_size=ppc_size;
ppc_size+=128;
ppc_size*=2;
ppc_buff=(u8*)ralloc(ppc_buff,old_size,ppc_size);
}
}
void ppc_block::write8(u32 value)
{
ppc_buffer_ensure(15);
//printf("ppc_block::write8 %02X\n",value);
ppc_buff[ppc_indx]=value;
ppc_indx+=1;
}
void ppc_block::write16(u32 value)
{
ppc_buffer_ensure(15);
//printf("ppc_block::write16 %04X\n",value);
*(u16*)&ppc_buff[ppc_indx]=value;
ppc_indx+=2;
}
void ppc_block::write32(u32 value)
{
ppc_buffer_ensure(15);
//printf("ppc_block::write32 %08X\n",value);
// if(do_disasm_imm) printf("do_disasm_imm\n");
if (do_disasm_imm) disassemble(ppc_indx,value);
*(u32*)&ppc_buff[ppc_indx]=value;
ppc_indx+=4;
}
//Label related code
//NOTE : Label position in mem must not chainge
void ppc_block::CreateLabel(ppc_Label* lbl,bool mark,u32 sz)
{
memset(lbl,0xFFFFFFFF,sizeof(ppc_Label));
lbl->owner=this;
lbl->marked=false;
lbl->patch_sz=sz;
if (mark)
MarkLabel(lbl);
}
//Allocate a label and create it :).Will be delete'd when calling free and/or dtor
ppc_Label* ppc_block::CreateLabel(bool mark,u32 sz)
{
ppc_Label* lbl = new ppc_Label();
CreateLabel(lbl,mark,sz);
labels.push_back(lbl);
return lbl;
}
//Mark a label so that it points to next emitted opcode
void ppc_block::MarkLabel(ppc_Label* lbl)
{
verify(lbl->marked==false);
lbl->marked=true;
lbl->target_opcode=ppc_indx;
//lbl->target_opcode=(u32)opcodes.size();
}
ppc_gpr_reg ppc_block::getHighReg(u16 value)
{
verify((((u32)(&sh4r))&0xffff)==0);
if(value==HA((u32)&sh4r))
{
return (ppc_reg)RSH4R;
}
EMIT_LIS(this,RTMP,value);
return (ppc_reg)RTMP;
}
void ppc_block::emitBranch(void * addr, int lk)
{
u32 faa=(u32)addr&0x7fffffff;
u32 aa=faa&0x03ffffff;
if(aa==faa) // 26 bits max for rel
{
write32(aa|((lk?1:0)<<26)); // primary opcode 0=b; 1=bl
}
else
{
emitLongBranch(addr,lk);
}
}
void ppc_block::emitLongBranch(void * addr, int lk)
{
EMIT_LIS(this,RTMP,((u32)addr)>>16);
EMIT_ORI(this,RTMP,RTMP,(u32)addr);
EMIT_MTLR(this,RTMP);
EMIT_BLR(this,lk);
}
void ppc_block::emitBranchConditional(void * addr, int bo, int bi, int lk, int force_short)
{
PowerPC_instr ppc=NEW_PPC_INSTR();
PPC_SET_OPCODE(ppc,5); // primary opcode 5=bc
verify(bc_tab_next_idx<0x4000);
bc_tab[bc_tab_next_idx]=(u32)addr;
PPC_SET_BD(ppc,bc_tab_next_idx++);
PPC_SET_BO(ppc,bo);
PPC_SET_BI(ppc,bi);
PPC_SET_LK(ppc,lk);
write32(ppc);
if (!force_short) write32(PPC_NOP);
}
void ppc_block::emitLoadDouble(ppc_fpr_reg reg, void * addr)
{
ppc_reg hr=getHighReg(HA((u32)addr));
EMIT_LFD(this,reg,(u32)addr,hr);
}
void ppc_block::emitLoadFloat(ppc_fpr_reg reg, void * addr)
{
ppc_reg hr=getHighReg(HA((u32)addr));
EMIT_LFS(this,reg,(u32)addr,hr);
}
void ppc_block::emitLoad32(ppc_gpr_reg reg, void * addr)
{
ppc_reg hr=getHighReg(HA((u32)addr));
EMIT_LWZ(this,reg,(u32)addr,hr);
}
void ppc_block::emitLoad16(ppc_gpr_reg reg, void * addr)
{
ppc_reg hr=getHighReg(HA((u32)addr));
EMIT_LHZ(this,reg,(u32)addr,hr);
}
void ppc_block::emitLoad8(ppc_gpr_reg reg, void * addr)
{
ppc_reg hr=getHighReg(HA((u32)addr));
EMIT_LBZ(this,reg,(u32)addr,hr);
}
void ppc_block::emitStoreDouble(void * addr, ppc_fpr_reg reg)
{
ppc_reg hr=getHighReg(HA((u32)addr));
EMIT_STFD(this,reg,(u32)addr,hr);
}
void ppc_block::emitStoreFloat(void * addr, ppc_fpr_reg reg)
{
ppc_reg hr=getHighReg(HA((u32)addr));
EMIT_STFS(this,reg,(u32)addr,hr);
}
void ppc_block::emitStore32(void * addr, ppc_gpr_reg reg)
{
ppc_reg hr=getHighReg(HA((u32)addr));
EMIT_STW(this,reg,(u32)addr,hr);
}
void ppc_block::emitStore16(void * addr, ppc_gpr_reg reg)
{
ppc_reg hr=getHighReg(HA((u32)addr));
EMIT_STH(this,reg,(u32)addr,hr);
}
void ppc_block::emitStore8(void * addr, ppc_gpr_reg reg)
{
ppc_reg hr=getHighReg(HA((u32)addr));
EMIT_STB(this,reg,(u32)addr,hr);
}
void ppc_block::emitMoveRegister(ppc_gpr_reg to,ppc_gpr_reg from)
{
EMIT_OR(this,to,from,from);
}
void ppc_block::emitLoadImmediate32(ppc_gpr_reg reg, u32 val)
{
if((s32)val>=-32768 && (s32)val<=32767)
{
EMIT_LI(this,reg,val);
}
else
{
EMIT_LIS(this,reg,val>>16);
if (val&0xffff) EMIT_ORI(this,reg,reg,val);
}
}
void ppc_block::emitBranchConditionalToLabel(ppc_Label * lab,int lk,int bo,int bi)
{
verify(lab);
verify((u32)lab>=0x80000000);
code_patch cp;
cp.type=5|16;
cp.lbl=lab;
cp.offset=ppc_indx; // next op
patches.push_back(cp);
EMIT_BC(this,0,0,lk,bo,bi);
}
void ppc_block::emitBranchToLabel(ppc_Label * lab,int lk)
{
verify(lab);
verify((u32)lab>=0x80000000);
code_patch cp;
cp.type=6|16;
cp.lbl=lab;
cp.offset=ppc_indx; // next op
patches.push_back(cp);
EMIT_B(this,0,0,lk);
}
extern "C" {
void debugValue(u32 value){
printf("dv %d(%x)\n",value,value);
}
}
static u32 r[32];
void ppc_block::emitDebugValue(u32 value)
{
emitStore32(&r[3],R3);
emitStore32(&r[4],R4);
emitStore32(&r[5],R5);
emitLoadImmediate32(R3,value);
emitBranch((void*)debugValue,1);
emitLoad32(R3,&r[3]);
emitLoad32(R4,&r[4]);
emitLoad32(R5,&r[5]);
}
void ppc_block::emitDebugReg(ppc_gpr_reg reg)
{
emitStore32(&r[3],R3);
emitStore32(&r[4],R4);
emitStore32(&r[5],R5);
if (reg!=R3) emitMoveRegister(R3,reg);
emitBranch((void*)debugValue,1);
emitLoad32(R3,&r[3]);
emitLoad32(R4,&r[4]);
emitLoad32(R5,&r[5]);
}
void ppc_block::vectorWriteBack(u32 reg)
{
if (reg>=xf_0 && reg<=xf_15)
{
u32 vr=(reg-xf_0)/4;
// printf("VectorWriteBack %d %d\n",reg,vr);
switch (vr)
{
case 0: EMIT_LI(this,RTMP,offsetof(Sh4RegContext,xf[0])); EMIT_STVX(this,RXF0,RTMP,RSH4R); break;
case 1: EMIT_LI(this,RTMP,offsetof(Sh4RegContext,xf[4])); EMIT_STVX(this,RXF4,RTMP,RSH4R); break;
case 2: EMIT_LI(this,RTMP,offsetof(Sh4RegContext,xf[8])); EMIT_STVX(this,RXF8,RTMP,RSH4R); break;
case 3: EMIT_LI(this,RTMP,offsetof(Sh4RegContext,xf[12])); EMIT_STVX(this,RXF12,RTMP,RSH4R); break;
}
}
}
void ppc_block::vectorReload(u32 reg)
{
if (reg>=xf_0 && reg<=xf_15)
{
u32 vr=(reg-xf_0)/4;
// printf("VectorReload %d %d\n",reg,vr);
switch (vr)
{
case 0: EMIT_LI(this,RTMP,offsetof(Sh4RegContext,xf[0])); EMIT_LVX(this,RXF0,RTMP,RSH4R); break;
case 1: EMIT_LI(this,RTMP,offsetof(Sh4RegContext,xf[4])); EMIT_LVX(this,RXF4,RTMP,RSH4R); break;
case 2: EMIT_LI(this,RTMP,offsetof(Sh4RegContext,xf[8])); EMIT_LVX(this,RXF8,RTMP,RSH4R); break;
case 3: EMIT_LI(this,RTMP,offsetof(Sh4RegContext,xf[12])); EMIT_LVX(this,RXF12,RTMP,RSH4R); break;
}
}
}