pcsx2/pcsx2/x86/microVU.h

314 lines
11 KiB
C++

/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2010 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
//#define mVUlogProg // Dumps MicroPrograms to \logs\*.html
//#define mVUprofileProg // Shows opcode statistics in console
class AsciiFile;
using namespace x86Emitter;
#include <deque>
#include <algorithm>
#include <memory>
#include "Common.h"
#include "VU.h"
#include "MTVU.h"
#include "GS.h"
#include "Gif_Unit.h"
#include "iR5900.h"
#include "R5900OpcodeTables.h"
#include "System/RecTypes.h"
#include "common/emitter/x86emitter.h"
#include "microVU_Misc.h"
#include "microVU_IR.h"
#include "microVU_Profiler.h"
#include "common/Perf.h"
struct microBlockLink
{
microBlock block;
microBlockLink* next;
};
class microBlockManager
{
private:
microBlockLink *qBlockList, *qBlockEnd; // Quick Search
microBlockLink *fBlockList, *fBlockEnd; // Full Search
int qListI, fListI;
public:
inline int getFullListCount() const { return fListI; }
microBlockManager()
{
qListI = fListI = 0;
qBlockEnd = qBlockList = NULL;
fBlockEnd = fBlockList = NULL;
}
~microBlockManager() { reset(); }
void reset()
{
for (microBlockLink* linkI = qBlockList; linkI != NULL;)
{
microBlockLink* freeI = linkI;
safe_delete_array(linkI->block.jumpCache);
linkI = linkI->next;
_aligned_free(freeI);
}
for (microBlockLink* linkI = fBlockList; linkI != NULL;)
{
microBlockLink* freeI = linkI;
safe_delete_array(linkI->block.jumpCache);
linkI = linkI->next;
_aligned_free(freeI);
}
qListI = fListI = 0;
qBlockEnd = qBlockList = NULL;
fBlockEnd = fBlockList = NULL;
};
microBlock* add(microBlock* pBlock)
{
microBlock* thisBlock = search(&pBlock->pState);
if (!thisBlock)
{
u8 fullCmp = pBlock->pState.needExactMatch;
if (fullCmp)
fListI++;
else
qListI++;
microBlockLink*& blockList = fullCmp ? fBlockList : qBlockList;
microBlockLink*& blockEnd = fullCmp ? fBlockEnd : qBlockEnd;
microBlockLink* newBlock = (microBlockLink*)_aligned_malloc(sizeof(microBlockLink), 16);
newBlock->block.jumpCache = NULL;
newBlock->next = NULL;
if (blockEnd)
{
blockEnd->next = newBlock;
blockEnd = newBlock;
}
else
{
blockEnd = blockList = newBlock;
}
memcpy(&newBlock->block, pBlock, sizeof(microBlock));
thisBlock = &newBlock->block;
}
return thisBlock;
}
__ri microBlock* search(microRegInfo* pState)
{
if (pState->needExactMatch) // Needs Detailed Search (Exact Match of Pipeline State)
{
for (microBlockLink* linkI = fBlockList; linkI != NULL; linkI = linkI->next)
{
if (mVUquickSearch((void*)pState, (void*)&linkI->block.pState, sizeof(microRegInfo)))
return &linkI->block;
}
}
else // Can do Simple Search (Only Matches the Important Pipeline Stuff)
{
for (microBlockLink* linkI = qBlockList; linkI != NULL; linkI = linkI->next)
{
if (linkI->block.pState.quick32[0] != pState->quick32[0]) continue;
if (linkI->block.pState.quick32[1] != pState->quick32[1]) continue;
if (doConstProp && (linkI->block.pState.vi15 != pState->vi15)) continue;
if (doConstProp && (linkI->block.pState.vi15v != pState->vi15v)) continue;
return &linkI->block;
}
}
return NULL;
}
void printInfo(int pc, bool printQuick)
{
int listI = printQuick ? qListI : fListI;
if (listI < 7)
return;
microBlockLink* linkI = printQuick ? qBlockList : fBlockList;
for (int i = 0; i <= listI; i++)
{
u32 viCRC = 0, vfCRC = 0, crc = 0, z = sizeof(microRegInfo) / 4;
for (u32 j = 0; j < 4; j++) viCRC -= ((u32*)linkI->block.pState.VI)[j];
for (u32 j = 0; j < 32; j++) vfCRC -= linkI->block.pState.VF[j].reg;
for (u32 j = 0; j < z; j++) crc -= ((u32*)&linkI->block.pState)[j];
DevCon.WriteLn(Color_Green,
"[%04x][Block #%d][crc=%08x][q=%02d][p=%02d][xgkick=%d][vi15=%04x][vi15v=%d][viBackup=%02d]"
"[flags=%02x][exactMatch=%x][blockType=%d][viCRC=%08x][vfCRC=%08x]",
pc, i, crc, linkI->block.pState.q,
linkI->block.pState.p, linkI->block.pState.xgkick, linkI->block.pState.vi15, linkI->block.pState.vi15v,
linkI->block.pState.viBackUp, linkI->block.pState.flagInfo, linkI->block.pState.needExactMatch,
linkI->block.pState.blockType, viCRC, vfCRC);
linkI = linkI->next;
}
}
};
struct microRange
{
s32 start; // Start PC (The opcode the block starts at)
s32 end; // End PC (The opcode the block ends with)
};
#define mProgSize (0x4000 / 4)
struct microProgram
{
u32 data [mProgSize]; // Holds a copy of the VU microProgram
microBlockManager* block[mProgSize / 2]; // Array of Block Managers
std::deque<microRange>* ranges; // The ranges of the microProgram that have already been recompiled
u32 startPC; // Start PC of this program
int idx; // Program index
};
typedef std::deque<microProgram*> microProgramList;
struct microProgramQuick
{
microBlockManager* block; // Quick reference to valid microBlockManager for current startPC
microProgram* prog; // The microProgram who is the owner of 'block'
};
struct microProgManager
{
microIR<mProgSize> IRinfo; // IR information
microProgramList* prog [mProgSize/2]; // List of microPrograms indexed by startPC values
microProgramQuick quick[mProgSize/2]; // Quick reference to valid microPrograms for current execution
microProgram* cur; // Pointer to currently running MicroProgram
int total; // Total Number of valid MicroPrograms
int isSame; // Current cached microProgram is Exact Same program as mVU.regs().Micro (-1 = unknown, 0 = No, 1 = Yes)
int cleared; // Micro Program is Indeterminate so must be searched for (and if no matches are found then recompile a new one)
u32 curFrame; // Frame Counter
u8* x86ptr; // Pointer to program's recompilation code
u8* x86start; // Start of program's rec-cache
u8* x86end; // Limit of program's rec-cache
microRegInfo lpState; // Pipeline state from where program left off (useful for continuing execution)
};
static const uint mVUdispCacheSize = __pagesize; // Dispatcher Cache Size (in bytes)
static const uint mVUcacheSafeZone = 3; // Safe-Zone for program recompilation (in megabytes)
static const uint mVUcacheReserve = 64; // mVU0, mVU1 Reserve Cache Size (in megabytes)
struct microVU
{
alignas(16) u32 statFlag[4]; // 4 instances of status flag (backup for xgkick)
alignas(16) u32 macFlag [4]; // 4 instances of mac flag (used in execution)
alignas(16) u32 clipFlag[4]; // 4 instances of clip flag (used in execution)
alignas(16) u32 xmmCTemp[4]; // Backup used in mVUclamp2()
alignas(16) u32 xmmBackup[16][4]; // Backup for xmm0~xmm15
u32 index; // VU Index (VU0 or VU1)
u32 cop2; // VU is in COP2 mode? (No/Yes)
u32 vuMemSize; // VU Main Memory Size (in bytes)
u32 microMemSize; // VU Micro Memory Size (in bytes)
u32 progSize; // VU Micro Memory Size (in u32's)
u32 progMemMask; // VU Micro Memory Size (in u32's)
u32 cacheSize; // VU Cache Size
microProgManager prog; // Micro Program Data
microProfiler profiler; // Opcode Profiler
std::unique_ptr<microRegAlloc> regAlloc; // Reg Alloc Class
std::unique_ptr<AsciiFile> logFile; // Log File Pointer
RecompiledCodeReserve* cache_reserve;
u8* cache; // Dynarec Cache Start (where we will start writing the recompiled code to)
u8* dispCache; // Dispatchers Cache (where startFunct and exitFunct are written to)
u8* startFunct; // Function Ptr to the recompiler dispatcher (start)
u8* exitFunct; // Function Ptr to the recompiler dispatcher (exit)
u8* startFunctXG; // Function Ptr to the recompiler dispatcher (xgkick resume)
u8* exitFunctXG; // Function Ptr to the recompiler dispatcher (xgkick exit)
u8* resumePtrXG; // Ptr to recompiled code position to resume xgkick
u32 code; // Contains the current Instruction
u32 divFlag; // 1 instance of I/D flags
u32 VIbackup; // Holds a backup of a VI reg if modified before a branch
u32 VIxgkick; // Holds a backup of a VI reg used for xgkick-delays
u32 branch; // Holds branch compare result (IBxx) OR Holds address to Jump to (JALR/JR)
u32 badBranch; // For Branches in Branch Delay Slots, holds Address the first Branch went to + 8
u32 evilBranch; // For Branches in Branch Delay Slots, holds Address to Jump to
u32 evilevilBranch;// For Branches in Branch Delay Slots (chained), holds Address to Jump to
u32 p; // Holds current P instance index
u32 q; // Holds current Q instance index
u32 totalCycles; // Total Cycles that mVU is expected to run for
u32 cycles; // Cycles Counter
VURegs& regs() const { return ::vuRegs[index]; }
__fi REG_VI& getVI(uint reg) const { return regs().VI[reg]; }
__fi VECTOR& getVF(uint reg) const { return regs().VF[reg]; }
__fi VIFregisters& getVifRegs() const
{
return (index && THREAD_VU1) ? vu1Thread.vifRegs : regs().GetVifRegs();
}
};
// microVU rec structs
alignas(16) microVU microVU0;
alignas(16) microVU microVU1;
// Debug Helper
int mVUdebugNow = 0;
// Main Functions
extern void mVUclear(mV, u32, u32);
extern void mVUreset(microVU& mVU, bool resetReserve);
extern void* mVUblockFetch(microVU& mVU, u32 startPC, uptr pState);
_mVUt extern void* __fastcall mVUcompileJIT(u32 startPC, uptr ptr);
// Prototypes for Linux
extern void __fastcall mVUcleanUpVU0();
extern void __fastcall mVUcleanUpVU1();
mVUop(mVUopU);
mVUop(mVUopL);
// Private Functions
extern void mVUcacheProg(microVU& mVU, microProgram& prog);
extern void mVUdeleteProg(microVU& mVU, microProgram*& prog);
_mVUt extern void* mVUsearchProg(u32 startPC, uptr pState);
extern void* __fastcall mVUexecuteVU0(u32 startPC, u32 cycles);
extern void* __fastcall mVUexecuteVU1(u32 startPC, u32 cycles);
// recCall Function Pointer
typedef void(__fastcall* mVUrecCall)(u32, u32);
typedef void (*mVUrecCallXG)(void);
template <typename T>
void makeUnique(T& v)
{ // Removes Duplicates
v.erase(unique(v.begin(), v.end()), v.end());
}
template <typename T>
void sortVector(T& v)
{
sort(v.begin(), v.end());
}
// Include all the *.inl files (microVU compiles as 1 Translation Unit)
#include "microVU_Clamp.inl"
#include "microVU_Misc.inl"
#include "microVU_Log.inl"
#include "microVU_Analyze.inl"
#include "microVU_Alloc.inl"
#include "microVU_Upper.inl"
#include "microVU_Lower.inl"
#include "microVU_Tables.inl"
#include "microVU_Flags.inl"
#include "microVU_Branch.inl"
#include "microVU_Compile.inl"
#include "microVU_Execute.inl"
#include "microVU_Macro.inl"