project64/Source/Project64/N64 System/Recompiler/Code Block.cpp

716 lines
20 KiB
C++

/****************************************************************************
* *
* Project 64 - A Nintendo 64 emulator. *
* http://www.pj64-emu.com/ *
* Copyright (C) 2012 Project64. All rights reserved. *
* *
* License: *
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
* *
****************************************************************************/
#include "stdafx.h"
bool DelaySlotEffectsCompare (DWORD PC, DWORD Reg1, DWORD Reg2);
CCodeBlock::CCodeBlock(DWORD VAddrEnter, BYTE * RecompPos) :
m_VAddrEnter(VAddrEnter),
m_VAddrFirst(VAddrEnter),
m_VAddrLast(VAddrEnter),
m_CompiledLocation(RecompPos),
m_Test(1),
m_EnterSection(NULL)
{
CCodeSection * baseSection = new CCodeSection(this, VAddrEnter, 0, false);
if (baseSection == NULL)
{
g_Notify->BreakPoint(__FILEW__,__LINE__);
}
m_Sections.push_back(baseSection);
baseSection->AddParent(NULL);
baseSection->m_CompiledLocation = (BYTE *)-1;
baseSection->m_Cont.JumpPC = VAddrEnter;
baseSection->m_Cont.FallThrough = true;
baseSection->m_Cont.RegSet = baseSection->m_RegEnter;
m_EnterSection = new CCodeSection(this, VAddrEnter, 1, true);
if (m_EnterSection == NULL)
{
g_Notify->BreakPoint(__FILEW__,__LINE__);
}
baseSection->m_ContinueSection = m_EnterSection;
m_EnterSection->AddParent(baseSection);
m_Sections.push_back(m_EnterSection);
m_SectionMap.insert(SectionMap::value_type(VAddrEnter,m_EnterSection));
if (g_TransVaddr->VAddrToRealAddr(VAddrEnter,*(reinterpret_cast<void **>(&m_MemLocation[0]))))
{
m_MemLocation[1] = m_MemLocation[0] + 1;
m_MemContents[0] = *m_MemLocation[0];
m_MemContents[1] = *m_MemLocation[1];
} else {
memset(m_MemLocation,0,sizeof(m_MemLocation));
memset(m_MemContents,0,sizeof(m_MemContents));
}
AnalyseBlock();
}
CCodeBlock::~CCodeBlock()
{
for (SectionList::iterator itr = m_Sections.begin(); itr != m_Sections.end(); itr++)
{
CCodeSection * Section = *itr;
delete Section;
}
m_Sections.clear();
}
bool CCodeBlock::SetSection ( CCodeSection * & Section, CCodeSection * CurrentSection, DWORD TargetPC, bool LinkAllowed, DWORD CurrentPC )
{
if (Section != NULL)
{
g_Notify->BreakPoint(__FILEW__,__LINE__);
}
if (TargetPC >= ((CurrentPC + 0x1000) & 0xFFFFF000))
{
return false;
}
if (TargetPC < m_EnterSection->m_EnterPC)
{
return false;
}
if (LinkAllowed)
{
if (Section != NULL)
{
g_Notify->BreakPoint(__FILEW__,__LINE__);
}
SectionMap::const_iterator itr = m_SectionMap.find(TargetPC);
if (itr != m_SectionMap.end())
{
Section = itr->second;
Section->AddParent(CurrentSection);
}
}
if (Section == NULL)
{
Section = new CCodeSection(this,TargetPC,m_Sections.size(),LinkAllowed);
if (Section == NULL)
{
g_Notify->BreakPoint(__FILEW__,__LINE__);
return false;
}
m_Sections.push_back(Section);
if (LinkAllowed)
{
m_SectionMap.insert(SectionMap::value_type(TargetPC,Section));
}
Section->AddParent(CurrentSection);
if (TargetPC <= CurrentPC && TargetPC != m_VAddrEnter)
{
CCodeSection * SplitSection = NULL;
for (SectionMap::const_iterator itr = m_SectionMap.begin(); itr != m_SectionMap.end(); itr++)
{
if (itr->first >= TargetPC)
{
break;
}
SplitSection = itr->second;
}
if (SplitSection == NULL)
{
g_Notify->BreakPoint(__FILEW__,__LINE__);
}
if (SplitSection->m_EndPC == (DWORD)-1)
{
g_Notify->BreakPoint(__FILEW__,__LINE__);
}
if (SplitSection->m_EndPC >= TargetPC)
{
CPU_Message(__FUNCTION__ ": Split Section: %d with section: %d",SplitSection->m_SectionID, Section->m_SectionID);
CCodeSection * BaseSection = Section;
BaseSection->m_EndPC = SplitSection->m_EndPC;
BaseSection->SetJumpAddress(SplitSection->m_Jump.JumpPC, SplitSection->m_Jump.TargetPC,SplitSection->m_Jump.PermLoop);
BaseSection->m_JumpSection = SplitSection->m_JumpSection;
BaseSection->SetContinueAddress(SplitSection->m_Cont.JumpPC,SplitSection->m_Cont.TargetPC);
BaseSection->m_ContinueSection = SplitSection->m_ContinueSection;
BaseSection->m_JumpSection->SwitchParent(SplitSection,BaseSection);
BaseSection->m_ContinueSection->SwitchParent(SplitSection,BaseSection);
BaseSection->AddParent(SplitSection);
SplitSection->m_EndPC = TargetPC - 4;
SplitSection->m_JumpSection = NULL;
SplitSection->m_ContinueSection = BaseSection;
SplitSection->SetContinueAddress(TargetPC - 4, TargetPC);
SplitSection->SetJumpAddress((DWORD)-1,(DWORD)-1,false);
}
}
}
return true;
}
bool CCodeBlock::CreateBlockLinkage ( CCodeSection * EnterSection )
{
CCodeSection * CurrentSection = EnterSection;
CPU_Message("Section %d",CurrentSection->m_SectionID);
for (DWORD TestPC = EnterSection->m_EnterPC, EndPC = ((EnterSection->m_EnterPC + 0x1000) & 0xFFFFF000); TestPC <= EndPC; TestPC += 4)
{
if (TestPC != EndPC)
{
SectionMap::const_iterator itr = m_SectionMap.find(TestPC);
if (itr != m_SectionMap.end() && CurrentSection != itr->second)
{
if (CurrentSection->m_ContinueSection != NULL &&
CurrentSection->m_ContinueSection != itr->second)
{
g_Notify->BreakPoint(__FILEW__,__LINE__);
}
if (CurrentSection->m_ContinueSection == NULL)
{
SetSection(CurrentSection->m_ContinueSection, CurrentSection, TestPC,true,TestPC);
CurrentSection->SetContinueAddress(TestPC - 4, TestPC);
}
CurrentSection->m_EndPC = TestPC - 4;
CurrentSection = itr->second;
CPU_Message("Section %d",CurrentSection->m_SectionID);
if (EnterSection != m_EnterSection)
{
if (CurrentSection->m_JumpSection != NULL ||
CurrentSection->m_ContinueSection != NULL ||
CurrentSection->m_EndSection)
{
break;
}
}
}
} else {
CurrentSection->m_EndSection = true;
break;
}
bool LikelyBranch, EndBlock, IncludeDelaySlot, PermLoop;
DWORD TargetPC, ContinuePC;
CurrentSection->m_EndPC = TestPC;
if (!AnalyzeInstruction(TestPC, TargetPC, ContinuePC, LikelyBranch, IncludeDelaySlot, EndBlock, PermLoop))
{
g_Notify->BreakPoint(__FILEW__,__LINE__);
return false;
}
if (TestPC + 4 == EndPC && IncludeDelaySlot)
{
TargetPC = (DWORD)-1;
ContinuePC = (DWORD)-1;
EndBlock = true;
}
if (TargetPC == (DWORD)-1 && !EndBlock)
{
if (ContinuePC != (DWORD)-1)
{
g_Notify->BreakPoint(__FILEW__,__LINE__);
}
continue;
}
if (EndBlock)
{
CPU_Message(__FUNCTION__ ": End Block");
CurrentSection->m_EndSection = true;
// find other sections that need compiling
break;
}
if (ContinuePC != (DWORD)-1)
{
CPU_Message(__FUNCTION__ ": SetContinueAddress TestPC = %X ContinuePC = %X",TestPC,ContinuePC);
CurrentSection->SetContinueAddress(TestPC, ContinuePC);
if (!SetSection(CurrentSection->m_ContinueSection, CurrentSection, ContinuePC,true,TestPC))
{
ContinuePC = (DWORD)-1;
}
}
if (LikelyBranch)
{
CPU_Message(__FUNCTION__ ": SetJumpAddress TestPC = %X Target = %X",TestPC,TestPC + 4);
CurrentSection->SetJumpAddress(TestPC, TestPC + 4,false);
if (SetSection(CurrentSection->m_JumpSection, CurrentSection, TestPC + 4,false,TestPC))
{
bool BranchLikelyBranch, BranchEndBlock, BranchIncludeDelaySlot, BranchPermLoop;
DWORD BranchTargetPC, BranchContinuePC;
CCodeSection * JumpSection = CurrentSection->m_JumpSection;
if (!AnalyzeInstruction(JumpSection->m_EnterPC, BranchTargetPC, BranchContinuePC, BranchLikelyBranch, BranchIncludeDelaySlot, BranchEndBlock, BranchPermLoop))
{
g_Notify->BreakPoint(__FILEW__,__LINE__);
return false;
}
if (BranchLikelyBranch || BranchIncludeDelaySlot || BranchPermLoop)
{
g_Notify->BreakPoint(__FILEW__,__LINE__);
return false;
}
JumpSection->m_EndPC = TestPC + 4;
if (BranchEndBlock)
{
CPU_Message(__FUNCTION__ ": Jump End Block");
JumpSection->m_EndSection = true;
TargetPC = (DWORD)-1;
} else {
JumpSection->SetJumpAddress(TestPC, TargetPC,false);
}
JumpSection->SetDelaySlot();
SetSection(JumpSection->m_JumpSection,JumpSection,TargetPC,true,TestPC);
} else {
g_Notify->BreakPoint(__FILEW__,__LINE__);
}
}
else if (TargetPC != ((DWORD)-1))
{
CPU_Message(__FUNCTION__ ": SetJumpAddress TestPC = %X Target = %X",TestPC,TargetPC);
CurrentSection->SetJumpAddress(TestPC, TargetPC,PermLoop);
if (PermLoop || !SetSection(CurrentSection->m_JumpSection, CurrentSection, TargetPC,true,TestPC))
{
if (ContinuePC == (DWORD)-1)
{
CurrentSection->m_EndSection = true;
}
}
}
TestPC += IncludeDelaySlot ? 8 : 4;
//Find the next section
CCodeSection * NewSection = NULL;
for (SectionMap::const_iterator itr = m_SectionMap.begin(); itr != m_SectionMap.end(); itr++)
{
if (CurrentSection->m_JumpSection != NULL ||
CurrentSection->m_ContinueSection != NULL ||
CurrentSection->m_EndSection)
{
continue;
}
NewSection = itr->second;
break;
}
if (NewSection == NULL)
{
break;
}
if (CurrentSection == NewSection)
{
g_Notify->BreakPoint(__FILEW__,__LINE__);
}
CurrentSection = NewSection;
if (CurrentSection->m_JumpSection != NULL ||
CurrentSection->m_ContinueSection != NULL ||
CurrentSection->m_EndSection)
{
break;
}
TestPC = CurrentSection->m_EnterPC;
CPU_Message("a. Section %d",CurrentSection->m_SectionID);
TestPC -= 4;
}
for (SectionMap::iterator itr = m_SectionMap.begin(); itr != m_SectionMap.end(); itr++)
{
CCodeSection * Section = itr->second;
if (Section->m_JumpSection != NULL ||
Section->m_ContinueSection != NULL ||
Section->m_EndSection)
{
continue;
}
if (!CreateBlockLinkage(Section)) { return false; }
break;
}
if (CurrentSection->m_EndPC == (DWORD)-1)
{
g_Notify->BreakPoint(__FILEW__,__LINE__);
}
return true;
}
void CCodeBlock::DetermineLoops ( void )
{
for (SectionMap::iterator itr = m_SectionMap.begin(); itr != m_SectionMap.end(); itr++)
{
CCodeSection * Section = itr->second;
DWORD Test = NextTest();
Section->DetermineLoop(Test,Test,Section->m_SectionID);
}
}
void CCodeBlock::LogSectionInfo ( void )
{
for (SectionList::iterator itr = m_Sections.begin(); itr != m_Sections.end(); itr++)
{
CCodeSection * Section = *itr;
Section->DisplaySectionInformation();
}
}
bool CCodeBlock::AnalyseBlock ( void )
{
if (!g_System->bLinkBlocks()) { return true; }
if (!CreateBlockLinkage(m_EnterSection)) { return false; }
DetermineLoops();
LogSectionInfo();
return true;
}
bool CCodeBlock::AnalyzeInstruction ( DWORD PC, DWORD & TargetPC, DWORD & ContinuePC, bool & LikelyBranch, bool & IncludeDelaySlot, bool & EndBlock, bool & PermLoop )
{
TargetPC = (DWORD)-1;
ContinuePC = (DWORD)-1;
LikelyBranch = false;
IncludeDelaySlot = false;
EndBlock = false;
PermLoop = false;
OPCODE Command;
if (!g_MMU->LW_VAddr(PC, Command.Hex)) {
g_Notify->BreakPoint(__FILEW__,__LINE__);
return false;
}
#ifdef _DEBUG
char * Name = R4300iOpcodeName(Command.Hex,PC);
CPU_Message(" 0x%08X %s",PC,Name);
#endif
switch (Command.op)
{
case R4300i_SPECIAL:
switch (Command.funct)
{
case R4300i_SPECIAL_SLL: case R4300i_SPECIAL_SRL: case R4300i_SPECIAL_SRA:
case R4300i_SPECIAL_SLLV: case R4300i_SPECIAL_SRLV: case R4300i_SPECIAL_SRAV:
case R4300i_SPECIAL_MFHI: case R4300i_SPECIAL_MTHI: case R4300i_SPECIAL_MFLO:
case R4300i_SPECIAL_MTLO: case R4300i_SPECIAL_DSLLV: case R4300i_SPECIAL_DSRLV:
case R4300i_SPECIAL_DSRAV: case R4300i_SPECIAL_ADD: case R4300i_SPECIAL_ADDU:
case R4300i_SPECIAL_SUB: case R4300i_SPECIAL_SUBU: case R4300i_SPECIAL_AND:
case R4300i_SPECIAL_OR: case R4300i_SPECIAL_XOR: case R4300i_SPECIAL_NOR:
case R4300i_SPECIAL_SLT: case R4300i_SPECIAL_SLTU: case R4300i_SPECIAL_DADD:
case R4300i_SPECIAL_DADDU: case R4300i_SPECIAL_DSUB: case R4300i_SPECIAL_DSUBU:
case R4300i_SPECIAL_DSLL: case R4300i_SPECIAL_DSRL: case R4300i_SPECIAL_DSRA:
case R4300i_SPECIAL_DSLL32: case R4300i_SPECIAL_DSRL32: case R4300i_SPECIAL_DSRA32:
case R4300i_SPECIAL_MULT: case R4300i_SPECIAL_MULTU: case R4300i_SPECIAL_DIV:
case R4300i_SPECIAL_DIVU: case R4300i_SPECIAL_DMULT: case R4300i_SPECIAL_DMULTU:
case R4300i_SPECIAL_DDIV: case R4300i_SPECIAL_DDIVU:
break;
case R4300i_SPECIAL_JALR:
case R4300i_SPECIAL_JR:
EndBlock = true;
IncludeDelaySlot = true;
break;
case R4300i_SPECIAL_SYSCALL:
case R4300i_SPECIAL_BREAK:
EndBlock = true;
break;
default:
g_Notify->BreakPoint(__FILEW__,__LINE__);
return false;
}
break;
case R4300i_REGIMM:
switch (Command.rt)
{
case R4300i_REGIMM_BLTZ:
case R4300i_REGIMM_BLTZAL:
TargetPC = PC + ((short)Command.offset << 2) + 4;
if (TargetPC == PC + 8)
{
TargetPC = (DWORD)-1;
} else {
if (TargetPC == PC && !DelaySlotEffectsCompare(PC,Command.rs,0))
{
PermLoop = true;
}
ContinuePC = PC + 8;
IncludeDelaySlot = true;
}
break;
case R4300i_REGIMM_BGEZ:
case R4300i_REGIMM_BGEZAL:
TargetPC = PC + ((short)Command.offset << 2) + 4;
if (TargetPC == PC + 8)
{
TargetPC = (DWORD)-1;
} else {
if (TargetPC == PC)
{
if (Command.rs == 0)
{
TargetPC = (DWORD)-1;
EndBlock = true;
} else {
if (!DelaySlotEffectsCompare(PC,Command.rs,Command.rt))
{
PermLoop = true;
}
}
}
if (Command.rs != 0)
{
ContinuePC = PC + 8;
}
IncludeDelaySlot = true;
}
break;
case R4300i_REGIMM_BLTZL:
case R4300i_REGIMM_BGEZL:
TargetPC = PC + ((short)Command.offset << 2) + 4;
if (TargetPC == PC)
{
if (!DelaySlotEffectsCompare(PC,Command.rs,0))
{
PermLoop = true;
}
}
ContinuePC = PC + 8;
LikelyBranch = true;
IncludeDelaySlot = true;
break;
default:
if (Command.Hex == 0x0407000D)
{
EndBlock = true;
break;
}
g_Notify->BreakPoint(__FILEW__,__LINE__);
return false;
}
break;
case R4300i_J:
TargetPC = (PC & 0xF0000000) + (Command.target << 2);
if (TargetPC == PC)
{
PermLoop = true;
}
IncludeDelaySlot = true;
break;
case R4300i_JAL:
EndBlock = true;
IncludeDelaySlot = true;
break;
case R4300i_BEQ:
TargetPC = PC + ((short)Command.offset << 2) + 4;
if (TargetPC == PC + 8)
{
TargetPC = (DWORD)-1;
} else {
if (Command.rs != 0 || Command.rt != 0)
{
ContinuePC = PC + 8;
}
if (TargetPC == PC && !DelaySlotEffectsCompare(PC,Command.rs,Command.rt))
{
PermLoop = true;
}
IncludeDelaySlot = true;
}
break;
case R4300i_BNE:
case R4300i_BLEZ:
case R4300i_BGTZ:
TargetPC = PC + ((short)Command.offset << 2) + 4;
if (TargetPC == PC + 8)
{
TargetPC = (DWORD)-1;
} else {
if (TargetPC == PC)
{
if (!DelaySlotEffectsCompare(PC,Command.rs,Command.rt))
{
PermLoop = true;
}
}
ContinuePC = PC + 8;
IncludeDelaySlot = true;
}
break;
case R4300i_CP0:
switch (Command.rs)
{
case R4300i_COP0_MT: case R4300i_COP0_MF:
break;
default:
if ( (Command.rs & 0x10 ) != 0 )
{
switch( Command.funct ) {
case R4300i_COP0_CO_TLBR: case R4300i_COP0_CO_TLBWI:
case R4300i_COP0_CO_TLBWR: case R4300i_COP0_CO_TLBP:
break;
case R4300i_COP0_CO_ERET:
EndBlock = true;
break;
default:
g_Notify->BreakPoint(__FILEW__,__LINE__);
return false;
}
} else {
g_Notify->BreakPoint(__FILEW__,__LINE__);
return false;
}
break;
}
break;
case R4300i_CP1:
switch (Command.fmt) {
case R4300i_COP1_MF: case R4300i_COP1_DMF: case R4300i_COP1_CF: case R4300i_COP1_MT:
case R4300i_COP1_DMT: case R4300i_COP1_CT: case R4300i_COP1_S: case R4300i_COP1_D:
case R4300i_COP1_W: case R4300i_COP1_L:
break;
case R4300i_COP1_BC:
switch (Command.ft) {
case R4300i_COP1_BC_BCF:
case R4300i_COP1_BC_BCT:
TargetPC = PC + ((short)Command.offset << 2) + 4;
if (TargetPC == PC + 8)
{
TargetPC = (DWORD)-1;
} else {
if (TargetPC == PC)
{
g_Notify->BreakPoint(__FILEW__,__LINE__);
}
ContinuePC = PC + 8;
IncludeDelaySlot = true;
}
break;
case R4300i_COP1_BC_BCFL:
case R4300i_COP1_BC_BCTL:
TargetPC = PC + ((short)Command.offset << 2) + 4;
if (TargetPC == PC)
{
g_Notify->BreakPoint(__FILEW__,__LINE__);
}
ContinuePC = PC + 8;
LikelyBranch = true;
IncludeDelaySlot = true;
break;
default:
g_Notify->BreakPoint(__FILEW__,__LINE__);
}
break;
default:
g_Notify->BreakPoint(__FILEW__,__LINE__);
return false;
}
break;
case R4300i_ANDI: case R4300i_ORI: case R4300i_XORI: case R4300i_LUI:
case R4300i_ADDI: case R4300i_ADDIU: case R4300i_SLTI: case R4300i_SLTIU:
case R4300i_DADDI: case R4300i_DADDIU: case R4300i_LDL: case R4300i_LDR:
case R4300i_LB: case R4300i_LH: case R4300i_LWL: case R4300i_LW:
case R4300i_LBU: case R4300i_LHU: case R4300i_LWR: case R4300i_LWU:
case R4300i_SB: case R4300i_SH: case R4300i_SWL: case R4300i_SW:
case R4300i_SDL: case R4300i_SDR: case R4300i_SWR: case R4300i_CACHE:
case R4300i_LL: case R4300i_LWC1: case R4300i_LDC1: case R4300i_LD:
case R4300i_SC: case R4300i_SWC1: case R4300i_SDC1: case R4300i_SD:
break;
case R4300i_BEQL:
TargetPC = PC + ((short)Command.offset << 2) + 4;
if (TargetPC == PC)
{
if (!DelaySlotEffectsCompare(PC,Command.rs,Command.rt))
{
PermLoop = true;
}
}
if (Command.rs != 0 || Command.rt != 0)
{
ContinuePC = PC + 8;
}
IncludeDelaySlot = true;
LikelyBranch = true;
break;
case R4300i_BNEL:
case R4300i_BLEZL:
case R4300i_BGTZL:
TargetPC = PC + ((short)Command.offset << 2) + 4;
ContinuePC = PC + 8;
if (TargetPC == PC)
{
if (!DelaySlotEffectsCompare(PC,Command.rs,Command.rt))
{
PermLoop = true;
}
}
LikelyBranch = true;
IncludeDelaySlot = true;
break;
default:
if (Command.Hex == 0x7C1C97C0 ||
Command.Hex == 0xF1F3F5F7)
{
EndBlock = true;
break;
}
g_Notify->BreakPoint(__FILEW__,__LINE__);
return false;
}
return true;
}
bool CCodeBlock::Compile()
{
CPU_Message("====== Code Block ======");
CPU_Message("x86 code at: %X",CompiledLocation());
CPU_Message("Start of Block: %X",VAddrEnter() );
CPU_Message("No of Sections: %d",NoOfSections() );
CPU_Message("====== recompiled code ======");
EnterCodeBlock();
if (g_SyncSystem) {
//if ((DWORD)BlockInfo.CompiledLocation == 0x60A7B73B) { X86BreakPoint(__FILEW__,__LINE__); }
//MoveConstToVariable((DWORD)BlockInfo.CompiledLocation,&CurrentBlock,"CurrentBlock");
}
if (g_System->bLinkBlocks()) {
while (m_EnterSection->GenerateX86Code(NextTest()));
} else {
if (!m_EnterSection->GenerateX86Code(NextTest()))
{
return false;
}
}
CompileExitCode();
DWORD PAddr;
g_TransVaddr->TranslateVaddr(VAddrFirst(),PAddr);
MD5(g_MMU->Rdram() + PAddr,(VAddrLast() - VAddrFirst()) + 4).get_digest(m_Hash);
return true;
}
void CCodeBlock::CompileExitCode ( void )
{
for (EXIT_LIST::iterator ExitIter = m_ExitInfo.begin(); ExitIter != m_ExitInfo.end(); ExitIter++)
{
CPU_Message("");
CPU_Message(" $Exit_%d",ExitIter->ID);
SetJump32(ExitIter->JumpLoc,(DWORD *)m_RecompPos);
m_NextInstruction = ExitIter->NextInstruction;
m_EnterSection->CompileExit((DWORD)-1, ExitIter->TargetPC,ExitIter->ExitRegSet,ExitIter->reason,true,NULL);
}
}
DWORD CCodeBlock::NextTest ( void )
{
return InterlockedIncrement(&m_Test);
}