pcsx2/pcsx2/x86/microVU.cpp

290 lines
9.8 KiB
C++

/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2009 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/>.
*/
// Micro VU recompiler! - author: cottonvibes(@gmail.com)
#include "PrecompiledHeader.h"
#include "microVU.h"
//------------------------------------------------------------------
// Micro VU - Global Variables
//------------------------------------------------------------------
PCSX2_ALIGNED16(microVU microVU0);
PCSX2_ALIGNED16(microVU microVU1);
declareAllVariables // Declares All Global Variables :D
//------------------------------------------------------------------
// Micro VU - Main Functions
//------------------------------------------------------------------
// Only run this once per VU! ;)
microVUt(void) mVUinit(VURegs* vuRegsPtr, int vuIndex) {
microVU* mVU = mVUx;
memset(&mVU->prog, 0, sizeof(mVU->prog));
mVU->regs = vuRegsPtr;
mVU->index = vuIndex;
mVU->vuMemSize = (vuIndex ? 0x4000 : 0x1000);
mVU->microMemSize = (vuIndex ? 0x4000 : 0x1000);
mVU->progSize = (vuIndex ? 0x4000 : 0x1000) / 4;
mVU->cache = NULL;
mVU->cacheSize = mVUcacheSize;
mVU->prog.max = mMaxProg - 1;
mVU->prog.prog = (microProgram*)_aligned_malloc(sizeof(microProgram)*(mVU->prog.max+1), 64);
mVU->prog.progList = new int[mMaxProg];
mVU->regAlloc = new microRegAlloc(mVU->regs);
mVUprint((vuIndex) ? "microVU1: init" : "microVU0: init");
mVU->cache = SysMmapEx((vuIndex ? 0x5f240000 : 0x5e240000), mVU->cacheSize + 0x1000, 0, (vuIndex ? "Micro VU1" : "Micro VU0"));
if (!mVU->cache) throw Exception::OutOfMemory( "microVU Error: Failed to allocate recompiler memory!" );
memset(mVU->cache, 0xcc, mVU->cacheSize + 0x1000);
memset(mVU->prog.prog, 0, sizeof(microProgram)*(mVU->prog.max+1));
// Setup Entrance/Exit Points
x86SetPtr(mVU->cache);
mVUdispatcherA(mVU);
mVUdispatcherB(mVU);
mVUemitSearch();
mVUreset(mVU);
}
// Resets Rec Data
microVUt(void) mVUreset(mV) {
mVUprint((mVU->index) ? "microVU1: reset" : "microVU0: reset");
// Clear All Program Data
//memset(&mVU->prog, 0, sizeof(mVU->prog));
memset(&mVU->prog.lpState, 0, sizeof(mVU->prog.lpState));
// Program Variables
mVU->prog.cleared = 1;
mVU->prog.isSame = -1;
mVU->prog.cur = -1;
mVU->prog.total = -1;
mVU->prog.curFrame = 0;
mVU->prog.max = mMaxProg - 1;
// Setup Dynarec Cache Limits for Each Program
u8* z = (mVU->cache + 0x1000); // Dispatcher Code is in first page of cache
mVU->prog.x86start = z;
mVU->prog.x86ptr = z;
mVU->prog.x86end = (u8*)((uptr)z + (uptr)(mVU->cacheSize - (_1mb * 3))); // 3mb "Safe Zone"
for (int i = 0; i <= mVU->prog.max; i++) {
if (!mVU->index) mVUclearProg<0>(i);
else mVUclearProg<1>(i);
mVU->prog.progList[i] = i;
}
}
// Free Allocated Resources
microVUt(void) mVUclose(mV) {
mVUprint((mVU->index) ? "microVU1: close" : "microVU0: close");
if (mVU->cache) { HostSys::Munmap(mVU->cache, mVU->cacheSize); mVU->cache = NULL; }
// Delete Programs and Block Managers
if (mVU->prog.prog) {
for (int i = 0; i <= mVU->prog.max; i++) {
for (u32 j = 0; j < (mVU->progSize / 2); j++) {
safe_delete(mVU->prog.prog[i].block[j]);
}
}
safe_aligned_free(mVU->prog.prog);
}
safe_delete_array(mVU->prog.progList);
safe_delete(mVU->regAlloc);
}
// Clears Block Data in specified range
microVUt(void) mVUclear(mV, u32 addr, u32 size) {
//if (!mVU->prog.cleared) {
//memset(&mVU->prog.lpState, 0, sizeof(mVU->prog.lpState));
mVU->prog.cleared = 1; // Next execution searches/creates a new microprogram
//}
}
//------------------------------------------------------------------
// Micro VU - Private Functions
//------------------------------------------------------------------
// Clears program data
microVUf(void) mVUclearProg(int progIndex) {
microVU* mVU = mVUx;
mVUprogI.used = 0;
mVUprogI.isDead = 1;
mVUprogI.isOld = 1;
mVUprogI.frame = mVU->prog.curFrame;
for (int j = 0; j <= mVUprogI.ranges.max; j++) {
mVUprogI.ranges.range[j][0] = -1; // Set range to
mVUprogI.ranges.range[j][1] = -1; // indeterminable status
mVUprogI.ranges.total = -1;
}
for (u32 i = 0; i < (mVU->progSize / 2); i++) {
safe_delete(mVUprogI.block[i]);
}
}
// Caches Micro Program
microVUf(void) mVUcacheProg(int progIndex) {
microVU* mVU = mVUx;
memcpy_fast(mVU->prog.prog[progIndex].data, mVU->regs->Micro, mVU->microMemSize);
mVUdumpProg(progIndex);
}
// Sorts the program list (Moves progIndex to Beginning of ProgList)
microVUt(void) mVUsortProg(mV, int progIndex) {
int* temp = new int[mVU->prog.max+1];
int offset = 0;
for (int i = 0; i <= (mVU->prog.max-1); i++) {
if (progIndex == mVU->prog.progList[i]) offset = 1;
temp[i+1] = mVU->prog.progList[i+offset];
}
temp[0] = progIndex;
delete[] mVU->prog.progList;
mVU->prog.progList = temp;
}
// Finds the least used program, (if program list full clears and returns an old program; if not-full, returns free program)
microVUf(int) mVUfindLeastUsedProg() {
microVU* mVU = mVUx;
for (int i = 0; i <= mVU->prog.max; i++) {
if (mVU->prog.prog[i].isDead) {
mVU->prog.total++;
mVUcacheProg<vuIndex>(i); // Cache Micro Program
mVU->prog.prog[i].isDead = 0;
mVU->prog.prog[i].isOld = 0;
mVU->prog.prog[i].used = 1;
mVUsortProg(mVU, i);
Console::Notice("microVU%d: Cached MicroPrograms = [%03d] [%03d]", vuIndex, i+1, mVU->prog.total+1);
return i;
}
}
static int clearIdx = 0;
int pIdx = clearIdx;
for (int i = 0; i < ((mVU->prog.max+1)/4); i++) {
mVUclearProg<vuIndex>(clearIdx);
clearIdx = aWrap(clearIdx+1, mVU->prog.max);
}
mVU->prog.total -= ((mVU->prog.max+1)/4)-1;
mVUcacheProg<vuIndex>(pIdx); // Cache Micro Program
mVU->prog.prog[pIdx].isDead = 0;
mVU->prog.prog[pIdx].isOld = 0;
mVU->prog.prog[pIdx].used = 1;
mVUsortProg(mVU, pIdx);
Console::Notice("microVU%d: Cached MicroPrograms = [%03d] [%03d]", vuIndex, pIdx+1, mVU->prog.total+1);
return pIdx;
}
// Finds and Ages/Kills Programs if they haven't been used in a while.
microVUt(void) mVUvsyncUpdate(mV) {
for (int i = 0; i <= mVU->prog.max; i++) {
if (mVU->prog.prog[i].isDead) continue;
if (mVU->prog.prog[i].used) {
mVU->prog.prog[i].used = 0;
mVU->prog.prog[i].frame = mVU->prog.curFrame;
}
else if (((mVU->prog.curFrame - mVU->prog.prog[i].frame) >= (360 * 10)) && (i != mVU->prog.cur)) {
mVU->prog.total--;
if (!mVU->index) mVUclearProg<0>(i);
else mVUclearProg<1>(i);
DevCon::Status("microVU%d: Killing Dead Program [%03d]", mVU->index, i+1);
}
else if (((mVU->prog.curFrame - mVU->prog.prog[i].frame) >= (30 * 1)) && !mVU->prog.prog[i].isOld) {
mVU->prog.prog[i].isOld = 1;
//DevCon::Status("microVU%d: Aging Old Program [%03d]", mVU->index, i+1);
}
}
mVU->prog.curFrame++;
}
microVUf(bool) mVUcmpPartial(int progIndex) {
microVU* mVU = mVUx;
for (int i = 0; i <= mVUprogI.ranges.total; i++) {
if ((mVUprogI.ranges.range[i][0] < 0)
|| (mVUprogI.ranges.range[i][1] < 0)) { DevCon::Error("microVU%d: Negative Range![%d][%d]", mVU->index, i, mVUprogI.ranges.total); }
if (memcmp_mmx(cmpOffset(mVUprogI.data), cmpOffset(mVU->regs->Micro), ((mVUprogI.ranges.range[i][1] + 8) - mVUprogI.ranges.range[i][0]))) {
return 0;
}
}
return 1;
}
// Compare Cached microProgram to mVU->regs->Micro
microVUf(bool) mVUcmpProg(int progIndex, const bool checkOld, const bool cmpWholeProg) {
microVU* mVU = mVUx;
if (!mVUprogI.isDead && (checkOld == mVUprogI.isOld)) {
if ((cmpWholeProg && !memcmp_mmx((u8*)mVUprogI.data, mVU->regs->Micro, mVU->microMemSize))
|| (!cmpWholeProg && mVUcmpPartial<vuIndex>(progIndex))) {
mVU->prog.cur = progIndex;
mVU->prog.cleared = 0;
mVU->prog.isSame = cmpWholeProg ? 1 : -1;
mVU->prog.prog[progIndex].used = 1;
mVU->prog.prog[progIndex].isOld = 0;
return 1;
}
}
return 0;
}
// Searches for Cached Micro Program and sets prog.cur to it (returns 1 if program found, else returns 0)
microVUf(int) mVUsearchProg() {
microVU* mVU = mVUx;
if (mVU->prog.cleared) { // If cleared, we need to search for new program
for (int i = mVU->prog.max; i >= 0; i--) {
if (mVUcmpProg<vuIndex>(mVU->prog.progList[i], 0, 0))
return 1; // Check Young Programs
}
for (int i = mVU->prog.max; i >= 0; i--) {
if (mVUcmpProg<vuIndex>(mVU->prog.progList[i], 1, 0))
return 1; // Check Old Programs
}
mVU->prog.cur = mVUfindLeastUsedProg<vuIndex>(); // If cleared and program not found, make a new program instance
mVU->prog.cleared = 0;
mVU->prog.isSame = 1;
return 0;
}
mVU->prog.prog[mVU->prog.cur].used = 1;
mVU->prog.prog[mVU->prog.cur].isOld = 0;
return 1; // If !cleared, then we're still on the same program as last-time ;)
}
//------------------------------------------------------------------
// Wrapper Functions - Called by other parts of the Emu
//------------------------------------------------------------------
void initVUrec (VURegs* vuRegs, const int vuIndex) { mVUinit(vuRegs, vuIndex); }
void closeVUrec(const int vuIndex) { mVUclose(mVUx); }
void resetVUrec(const int vuIndex) { mVUreset(mVUx); }
void vsyncVUrec(const int vuIndex) { mVUvsyncUpdate(mVUx); }
void __fastcall clearVUrec(u32 addr, u32 size, const int vuIndex) {
mVUclear(mVUx, addr, size);
}
void __fastcall runVUrec(u32 startPC, u32 cycles, const int vuIndex) {
if (!vuIndex) ((mVUrecCall)microVU0.startFunct)(startPC, cycles);
else ((mVUrecCall)microVU1.startFunct)(startPC, cycles);
}