mirror of https://github.com/PCSX2/pcsx2.git
336 lines
12 KiB
C++
336 lines
12 KiB
C++
/* Pcsx2 - Pc Ps2 Emulator
|
|
* Copyright (C) 2009 Pcsx2-Playground Team
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
// Micro VU recompiler! - author: cottonvibes(@gmail.com)
|
|
|
|
#include "PrecompiledHeader.h"
|
|
#include "microVU.h"
|
|
#ifdef PCSX2_MICROVU
|
|
|
|
//------------------------------------------------------------------
|
|
// Micro VU - Global Variables
|
|
//------------------------------------------------------------------
|
|
|
|
PCSX2_ALIGNED16(microVU microVU0);
|
|
PCSX2_ALIGNED16(microVU microVU1);
|
|
|
|
PCSX2_ALIGNED16(const u32 mVU_absclip[4]) = {0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff};
|
|
PCSX2_ALIGNED16(const u32 mVU_signbit[4]) = {0x80000000, 0x80000000, 0x80000000, 0x80000000};
|
|
PCSX2_ALIGNED16(const u32 mVU_minvals[4]) = {0xff7fffff, 0xff7fffff, 0xff7fffff, 0xff7fffff};
|
|
PCSX2_ALIGNED16(const u32 mVU_maxvals[4]) = {0x7f7fffff, 0x7f7fffff, 0x7f7fffff, 0x7f7fffff};
|
|
PCSX2_ALIGNED16(const u32 mVU_one[4]) = {0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000};
|
|
PCSX2_ALIGNED16(const u32 mVU_T1[4]) = {0x3f7ffff5, 0x3f7ffff5, 0x3f7ffff5, 0x3f7ffff5};
|
|
PCSX2_ALIGNED16(const u32 mVU_T2[4]) = {0xbeaaa61c, 0xbeaaa61c, 0xbeaaa61c, 0xbeaaa61c};
|
|
PCSX2_ALIGNED16(const u32 mVU_T3[4]) = {0x3e4c40a6, 0x3e4c40a6, 0x3e4c40a6, 0x3e4c40a6};
|
|
PCSX2_ALIGNED16(const u32 mVU_T4[4]) = {0xbe0e6c63, 0xbe0e6c63, 0xbe0e6c63, 0xbe0e6c63};
|
|
PCSX2_ALIGNED16(const u32 mVU_T5[4]) = {0x3dc577df, 0x3dc577df, 0x3dc577df, 0x3dc577df};
|
|
PCSX2_ALIGNED16(const u32 mVU_T6[4]) = {0xbd6501c4, 0xbd6501c4, 0xbd6501c4, 0xbd6501c4};
|
|
PCSX2_ALIGNED16(const u32 mVU_T7[4]) = {0x3cb31652, 0x3cb31652, 0x3cb31652, 0x3cb31652};
|
|
PCSX2_ALIGNED16(const u32 mVU_T8[4]) = {0xbb84d7e7, 0xbb84d7e7, 0xbb84d7e7, 0xbb84d7e7};
|
|
PCSX2_ALIGNED16(const u32 mVU_Pi4[4]) = {0x3f490fdb, 0x3f490fdb, 0x3f490fdb, 0x3f490fdb};
|
|
//PCSX2_ALIGNED16(const u32 mVU_S1[4]) = {0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000};
|
|
PCSX2_ALIGNED16(const u32 mVU_S2[4]) = {0xbe2aaaa4, 0xbe2aaaa4, 0xbe2aaaa4, 0xbe2aaaa4};
|
|
PCSX2_ALIGNED16(const u32 mVU_S3[4]) = {0x3c08873e, 0x3c08873e, 0x3c08873e, 0x3c08873e};
|
|
PCSX2_ALIGNED16(const u32 mVU_S4[4]) = {0xb94fb21f, 0xb94fb21f, 0xb94fb21f, 0xb94fb21f};
|
|
PCSX2_ALIGNED16(const u32 mVU_S5[4]) = {0x362e9c14, 0x362e9c14, 0x362e9c14, 0x362e9c14};
|
|
PCSX2_ALIGNED16(const u32 mVU_E1[4]) = {0x3e7fffa8, 0x3e7fffa8, 0x3e7fffa8, 0x3e7fffa8};
|
|
PCSX2_ALIGNED16(const u32 mVU_E2[4]) = {0x3d0007f4, 0x3d0007f4, 0x3d0007f4, 0x3d0007f4};
|
|
PCSX2_ALIGNED16(const u32 mVU_E3[4]) = {0x3b29d3ff, 0x3b29d3ff, 0x3b29d3ff, 0x3b29d3ff};
|
|
PCSX2_ALIGNED16(const u32 mVU_E4[4]) = {0x3933e553, 0x3933e553, 0x3933e553, 0x3933e553};
|
|
PCSX2_ALIGNED16(const u32 mVU_E5[4]) = {0x36b63510, 0x36b63510, 0x36b63510, 0x36b63510};
|
|
PCSX2_ALIGNED16(const u32 mVU_E6[4]) = {0x353961ac, 0x353961ac, 0x353961ac, 0x353961ac};
|
|
PCSX2_ALIGNED16(const float mVU_FTOI_4[4]) = {16.0, 16.0, 16.0, 16.0};
|
|
PCSX2_ALIGNED16(const float mVU_FTOI_12[4]) = {4096.0, 4096.0, 4096.0, 4096.0};
|
|
PCSX2_ALIGNED16(const float mVU_FTOI_15[4]) = {32768.0, 32768.0, 32768.0, 32768.0};
|
|
PCSX2_ALIGNED16(const float mVU_ITOF_4[4]) = {0.0625f, 0.0625f, 0.0625f, 0.0625f};
|
|
PCSX2_ALIGNED16(const float mVU_ITOF_12[4]) = {0.000244140625, 0.000244140625, 0.000244140625, 0.000244140625};
|
|
PCSX2_ALIGNED16(const float mVU_ITOF_15[4]) = {0.000030517578125, 0.000030517578125, 0.000030517578125, 0.000030517578125};
|
|
|
|
//------------------------------------------------------------------
|
|
// Micro VU - Main Functions
|
|
//------------------------------------------------------------------
|
|
|
|
// Only run this once! ;)
|
|
microVUt(void) mVUinit(VURegs* vuRegsPtr) {
|
|
|
|
microVU* mVU = mVUx;
|
|
mVU->regs = vuRegsPtr;
|
|
mVU->index = vuIndex;
|
|
mVU->microSize = (vuIndex ? 0x4000 : 0x1000);
|
|
mVU->progSize = (vuIndex ? 0x4000 : 0x1000) / 8;
|
|
mVU->cacheAddr = 0xC0000000 + (vuIndex ? mVU->cacheSize : 0);
|
|
mVU->cache = NULL;
|
|
|
|
mVUreset<vuIndex>();
|
|
}
|
|
|
|
// Will Optimize later
|
|
microVUt(void) mVUreset() {
|
|
|
|
microVU* mVU = mVUx;
|
|
mVUclose<vuIndex>(); // Close
|
|
|
|
// Create Block Managers
|
|
for (int i; i <= mVU->prog.max; i++) {
|
|
for (u32 j; j < mVU->progSize; j++) {
|
|
mVU->prog.prog[i].block[j] = new microBlockManager();
|
|
}
|
|
}
|
|
|
|
// Dynarec Cache
|
|
mVU->cache = SysMmapEx(mVU->cacheAddr, mVU->cacheSize, 0x10000000, (vuIndex ? "Micro VU1" : "Micro VU0"));
|
|
if ( mVU->cache == NULL ) throw Exception::OutOfMemory(fmt_string( "microVU Error: failed to allocate recompiler memory! (addr: 0x%x)", params (u32)mVU->cache));
|
|
|
|
// Other Variables
|
|
memset(&mVU->prog, 0, sizeof(mVU->prog));
|
|
mVU->prog.finished = 1;
|
|
mVU->prog.cleared = 1;
|
|
mVU->prog.cur = -1;
|
|
mVU->prog.total = -1;
|
|
}
|
|
|
|
// Free Allocated Resources
|
|
microVUt(void) mVUclose() {
|
|
|
|
microVU* mVU = mVUx;
|
|
|
|
if ( mVU->cache ) { HostSys::Munmap( mVU->cache, mVU->cacheSize ); mVU->cache = NULL; }
|
|
|
|
// Delete Block Managers
|
|
for (int i; i <= mVU->prog.max; i++) {
|
|
for (u32 j; j < mVU->progSize; j++) {
|
|
if (mVU->prog.prog[i].block[j]) delete mVU->prog.prog[i].block[j];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clears Block Data in specified range
|
|
microVUt(void) mVUclear(u32 addr, u32 size) {
|
|
|
|
microVU* mVU = mVUx;
|
|
mVU->prog.cleared = 1; // Next execution searches/creates a new microprogram
|
|
// Note: It might be better to copy old recompiled blocks to the new microprogram rec data
|
|
// however, if games primarily do big writes, its probably not worth it.
|
|
// The cost of invalidating bad blocks is also kind of expensive, which is another reason
|
|
// that its probably not worth it...
|
|
}
|
|
|
|
// Executes for number of cycles
|
|
microVUt(void*) __fastcall mVUexecute(u32 startPC, u32 cycles) {
|
|
/*
|
|
Pseudocode: (ToDo: implement # of cycles)
|
|
1) Search for existing program
|
|
2) If program not found, goto 5
|
|
3) Search for recompiled block
|
|
4) If recompiled block found, goto 6
|
|
5) Recompile as much blocks as possible
|
|
6) Return start execution address of block
|
|
*/
|
|
microVU* mVU = mVUx;
|
|
if ( mVUsearchProg(mVU) ) { // Found Program
|
|
microBlock* block = mVU->prog.prog[mVU->prog.cur].block[startPC]->search(mVU->prog.lastPipelineState);
|
|
if (block) return block->x86ptrStart; // Found Block
|
|
}
|
|
// Recompile code
|
|
return NULL;
|
|
}
|
|
|
|
void* __fastcall mVUexecuteVU0(u32 startPC, u32 cycles) {
|
|
return mVUexecute<0>(startPC, cycles);
|
|
}
|
|
void* __fastcall mVUexecuteVU1(u32 startPC, u32 cycles) {
|
|
return mVUexecute<1>(startPC, cycles);
|
|
}
|
|
|
|
//------------------------------------------------------------------
|
|
// Micro VU - Private Functions
|
|
//------------------------------------------------------------------
|
|
|
|
// Clears program data (Sets used to 1 because calling this function implies the program will be used at least once)
|
|
__forceinline void mVUclearProg(microVU* mVU, int progIndex) {
|
|
mVU->prog.prog[progIndex].used = 1;
|
|
for (u32 i = 0; i < mVU->progSize; i++) {
|
|
mVU->prog.prog[progIndex].block[i]->reset();
|
|
}
|
|
}
|
|
|
|
// Caches Micro Program
|
|
__forceinline void mVUcacheProg(microVU* mVU, int progIndex) {
|
|
memcpy_fast(mVU->prog.prog[progIndex].data, mVU->regs->Micro, mVU->microSize);
|
|
}
|
|
|
|
// Finds the least used program, (if program list full clears and returns an old program; if not-full, returns free program)
|
|
__forceinline int mVUfindLeastUsedProg(microVU* mVU) {
|
|
if (mVU->prog.total < mVU->prog.max) {
|
|
mVU->prog.total++;
|
|
mVUcacheProg(mVU, mVU->prog.total); // Cache Micro Program
|
|
return mVU->prog.total;
|
|
}
|
|
else {
|
|
int j = 0;
|
|
u32 smallest = mVU->prog.prog[0].used;
|
|
for (int i = 1; i <= mVU->prog.total; i++) {
|
|
if (smallest > mVU->prog.prog[i].used) {
|
|
smallest = mVU->prog.prog[i].used;
|
|
j = i;
|
|
}
|
|
}
|
|
mVUclearProg(mVU, j); // Clear old data if overwriting old program
|
|
mVUcacheProg(mVU, j); // Cache Micro Program
|
|
return j;
|
|
}
|
|
}
|
|
|
|
// Searches for Cached Micro Program and sets prog.cur to it (returns 1 if program found, else returns 0)
|
|
__forceinline int mVUsearchProg(microVU* mVU) {
|
|
if (mVU->prog.cleared) { // If cleared, we need to search for new program
|
|
for (int i = 0; i <= mVU->prog.total; i++) {
|
|
if (i == mVU->prog.cur) continue; // We can skip the current program. (ToDo: Verify that games don't clear, and send the same microprogram :/)
|
|
if (!memcmp_mmx(mVU->prog.prog[i].data, mVU->regs->Micro, mVU->microSize)) {
|
|
mVU->prog.cur = i;
|
|
mVU->prog.cleared = 0;
|
|
mVU->prog.prog[i].used++;
|
|
return 1;
|
|
}
|
|
}
|
|
mVU->prog.cur = mVUfindLeastUsedProg(mVU); // If cleared and program not found, make a new program instance
|
|
mVU->prog.cleared = 0;
|
|
return 0;
|
|
}
|
|
mVU->prog.prog[mVU->prog.cur].used++;
|
|
return 1; // If !cleared, then we're still on the same program as last-time ;)
|
|
}
|
|
|
|
// Block Invalidation
|
|
__forceinline void mVUinvalidateBlock(microVU* mVU, u32 addr, u32 size) {
|
|
|
|
int i = addr/8;
|
|
int end = i+((size+(8-(size&7)))/8); // ToDo: Can be simplified to addr+size if Size is always a multiple of 8
|
|
|
|
if (!mVU->prog.cleared) {
|
|
for ( ; i < end; i++) {
|
|
if ( mVU->prog.prog[mVU->prog.cur].block[i]->clear() ) {
|
|
mVU->prog.cleared = 1;
|
|
i++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
for ( ; i < end; i++) {
|
|
mVU->prog.prog[mVU->prog.cur].block[i]->clearFast();
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------
|
|
// Dispatcher Functions
|
|
//------------------------------------------------------------------
|
|
|
|
#ifdef _MSC_VER
|
|
// Runs VU0 for number of cycles
|
|
__declspec(naked) void __fastcall startVU0(u32 startPC, u32 cycles) {
|
|
__asm {
|
|
// __fastcall = The first two DWORD or smaller arguments are passed in ECX and EDX registers; all other arguments are passed right to left.
|
|
call mVUexecuteVU0
|
|
|
|
/*backup cpu state*/
|
|
push ebx;
|
|
push ebp;
|
|
push esi;
|
|
push edi;
|
|
|
|
ldmxcsr g_sseVUMXCSR
|
|
/* Should set xmmZ? */
|
|
jmp eax
|
|
}
|
|
}
|
|
|
|
// Runs VU1 for number of cycles
|
|
__declspec(naked) void __fastcall startVU1(u32 startPC, u32 cycles) {
|
|
__asm {
|
|
|
|
call mVUexecuteVU1
|
|
|
|
/*backup cpu state*/
|
|
push ebx;
|
|
push ebp;
|
|
push esi;
|
|
push edi;
|
|
|
|
ldmxcsr g_sseVUMXCSR
|
|
|
|
jmp eax
|
|
}
|
|
}
|
|
|
|
// Exit point
|
|
__declspec(naked) void __fastcall endVU0(u32 startPC, u32 cycles) {
|
|
__asm {
|
|
|
|
//call mVUcleanUpVU0
|
|
|
|
/*restore cpu state*/
|
|
pop edi;
|
|
pop esi;
|
|
pop ebp;
|
|
pop ebx;
|
|
|
|
ldmxcsr g_sseMXCSR
|
|
emms
|
|
|
|
ret
|
|
}
|
|
}
|
|
#else
|
|
extern "C"
|
|
{
|
|
extern void __fastcall startVU0(u32 startPC, u32 cycles);
|
|
extern void __fastcall startVU1(u32 startPC, u32 cycles);
|
|
extern void __fastcall endVU0(u32 startPC, u32 cycles);
|
|
}
|
|
#endif
|
|
//------------------------------------------------------------------
|
|
// Wrapper Functions - Called by other parts of the Emu
|
|
//------------------------------------------------------------------
|
|
|
|
__forceinline void initVUrec(VURegs* vuRegs, const int vuIndex) {
|
|
if (!vuIndex) mVUinit<0>(vuRegs);
|
|
else mVUinit<1>(vuRegs);
|
|
}
|
|
|
|
__forceinline void closeVUrec(const int vuIndex) {
|
|
if (!vuIndex) mVUclose<0>();
|
|
else mVUclose<1>();
|
|
}
|
|
|
|
__forceinline void resetVUrec(const int vuIndex) {
|
|
if (!vuIndex) mVUreset<0>();
|
|
else mVUreset<1>();
|
|
}
|
|
|
|
__forceinline void clearVUrec(u32 addr, u32 size, const int vuIndex) {
|
|
if (!vuIndex) mVUclear<0>(addr, size);
|
|
else mVUclear<1>(addr, size);
|
|
}
|
|
|
|
__forceinline void runVUrec(u32 startPC, u32 cycles, const int vuIndex) {
|
|
if (!vuIndex) startVU0(startPC, cycles);
|
|
else startVU1(startPC, cycles);
|
|
}
|
|
|
|
#endif // PCSX2_MICROVU
|