diff --git a/pcsx2/VifDma_internal.h b/pcsx2/VifDma_internal.h index 7ce8556ebf..8c02fd7576 100644 --- a/pcsx2/VifDma_internal.h +++ b/pcsx2/VifDma_internal.h @@ -60,7 +60,7 @@ static __forceinline u32 vif_size(u8 num) return (num == 0) ? 0x1000 : 0x4000; } -//#define newVif // Enable 'newVif' Code (if the below macros are not defined, it will use old non-sse code) -//#define newVif1 // Use New Code for Vif1 Unpacks (needs newVif defined) +#define newVif // Enable 'newVif' Code (if the below macros are not defined, it will use old non-sse code) +#define newVif1 // Use New Code for Vif1 Unpacks (needs newVif defined) //#define newVif0 // Use New Code for Vif0 Unpacks (not implemented) #endif diff --git a/pcsx2/x86/newVif.h b/pcsx2/x86/newVif.h index d5d696dab9..9db218179e 100644 --- a/pcsx2/x86/newVif.h +++ b/pcsx2/x86/newVif.h @@ -21,20 +21,18 @@ using namespace x86Emitter; extern void mVUmergeRegs(int dest, int src, int xyzw, bool modXYZW = 0); extern void _nVifUnpack(int idx, u8 *data, u32 size); -struct instBlock { u8 data[16*64]; }; -static __pagealigned instBlock nVifUpk[2][2][4][3][16]; // [USN][Masking][curCycle][CyclesToWrite-1][Unpack Type] -static __aligned16 u32 nVifMask[3][4][4] = {0}; // [MaskNumber][CycleNumber][Vector] typedef u32 (__fastcall *nVifCall)(void*, void*); -#define nVifUnpackF(dest, src, usn, doMask, curCycle, cycles, unpackType) { \ - (((nVifCall)((void*)&nVifUpk[usn][doMask][curCycle][cycles][unpackType]))(dest, src)); \ -} + +static __pagealigned u8 nVifUpkExec[__pagesize*16]; +static __aligned16 nVifCall nVifUpk[(2*2*16)*4*4]; // ([USN][Masking][Unpack Type]) [curCycle][CyclesToWrite-1] +static __aligned16 u32 nVifMask[3][4][4] = {0}; // [MaskNumber][CycleNumber][Vector] #define _v0 0 #define _v1 0x55 #define _v2 0xaa #define _v3 0xff -#define aMax(x, y) (((x) > (y) ? (x) : (y))) -#define aMin(x, y) (((x) < (y) ? (x) : (y))) +#define aMax(x, y) std::max(x,y) +#define aMin(x, y) std::min(x,y) #define _f __forceinline #define xShiftR(regX, n) { \ @@ -42,7 +40,7 @@ typedef u32 (__fastcall *nVifCall)(void*, void*); else { xPSRA.D(regX, n); } \ } -u32 nVifT[16] = { +static const u32 nVifT[16] = { 4, // S-32 2, // S-16 1, // S-8 diff --git a/pcsx2/x86/newVif_Unpack.inl b/pcsx2/x86/newVif_Unpack.inl index f46373c70d..544560221d 100644 --- a/pcsx2/x86/newVif_Unpack.inl +++ b/pcsx2/x86/newVif_Unpack.inl @@ -36,22 +36,29 @@ void initNewVif(int idx) { nVif[idx].vifBlock = new BlockBuffer(0x2000); // 8kb Block Buffer nVif[idx].vuMemEnd = idx ? ((u8*)(VU1.Mem + 0x4000)) : ((u8*)(VU0.Mem + 0x1000)); nVif[idx].vuMemLimit= idx ? 0x3ff0 : 0xff0; - memset_8<0xcc,sizeof(nVifUpk)>(nVifUpk); + + HostSys::MemProtectStatic(nVifUpkExec, Protect_ReadWrite, false); + memset8<0xcc>( nVifUpkExec ); + + xSetPtr( nVifUpkExec ); + for (int a = 0; a < 2; a++) { for (int b = 0; b < 2; b++) { for (int c = 0; c < 4; c++) { for (int d = 0; d < 3; d++) { - nVifGen(a, b, c, d); //nVifUpk[2][2][4][3][16]; + nVifGen(a, b, c, d); }}}} + + HostSys::MemProtectStatic(nVifUpkExec, Protect_ReadOnly, true); } -int nVifUnpack(int idx, u32 *data) { - XMMRegisters::Freeze(); - BlockBuffer* vB = nVif[idx].vifBlock; - int ret = aMin(vif1.vifpacketsize, vif1.tag.size); - vif1.tag.size -= ret; - _nVifUnpack(idx, (u8*)data, ret<<2); - if (vif1.tag.size <= 0) vif1.tag.size = 0; +int nVifUnpack(int idx, u32 *data) { + XMMRegisters::Freeze(); + //BlockBuffer* vB = nVif[idx].vifBlock; + int ret = aMin(vif1.vifpacketsize, vif1.tag.size); + vif1.tag.size -= ret; + _nVifUnpack(idx, (u8*)data, ret<<2); + if (vif1.tag.size <= 0) vif1.tag.size = 0; if (vif1.tag.size <= 0) vif1.cmd = 0; XMMRegisters::Thaw(); return ret; @@ -70,9 +77,9 @@ _f void incVUptr(int idx, u8* &ptr, int amount) { if ((uptr)ptr & 0xf) DevCon.WriteLn("unaligned wtf :("); } -_f void setMasks(VIFregisters* v) { +static void setMasks(const VIFregisters& v) { for (int i = 0; i < 16; i++) { - int m = (v->mask >> (i*2)) & 3; + int m = (v.mask >> (i*2)) & 3; switch (m) { case 0: // Data nVifMask[0][i/4][i%4] = 0xffffffff; @@ -82,12 +89,12 @@ _f void setMasks(VIFregisters* v) { case 1: // Row nVifMask[0][i/4][i%4] = 0; nVifMask[1][i/4][i%4] = 0; - nVifMask[2][i/4][i%4] = ((u32*)&v->r0)[(i%4)*4]; + nVifMask[2][i/4][i%4] = ((u32*)&v.r0)[(i%4)*4]; break; case 2: // Col nVifMask[0][i/4][i%4] = 0; nVifMask[1][i/4][i%4] = 0; - nVifMask[2][i/4][i%4] = ((u32*)&v->c0)[(i/4)*4]; + nVifMask[2][i/4][i%4] = ((u32*)&v.c0)[(i/4)*4]; break; case 3: // Write Protect nVifMask[0][i/4][i%4] = 0; @@ -98,7 +105,97 @@ _f void setMasks(VIFregisters* v) { } } -_f void _nVifUnpack(int idx, u8 *data, u32 size) { +// ---------------------------------------------------------------------------- +// Unpacking Optimization notes: +// ---------------------------------------------------------------------------- +// Some games send a LOT of small packets. This is a problem because the new VIF unpacker +// has a lot of setup code to establish which unpack function to call. The best way to +// optimize this is to cache the unpack function's base (see fnbase below) and update it +// when the variables it depends on are modified: writes to vif->tag.cmd and vif->usn. +// +// A secondary optimization would be adding special handlers for packets where vifRegs->num==1. +// (which would remove the loop, simplify the incVUptr code, etc). But checking for it has +// to be simple enough that it doesn't offset the benefits (which I'm not sure is possible). +// -- air + + +template< int idx, bool doMode, bool isFill > +__releaseinline void __fastcall _nVifUnpackLoop( u8 *data, u32 size ) +{ + // Eh... template attempt, tho not sure it helped much. There's too much setup code (see + // optimization note above) -- air + + const int usn = !!(vif->usn); + const int doMask = !!(vif->tag.cmd & 0x10); + const int upkNum = vif->tag.cmd & 0xf; + const u32& vift = nVifT[upkNum]; + + u8* dest = setVUptr(idx, vif->tag.addr); + const VIFUnpackFuncTable& ft = VIFfuncTable[vif->tag.cmd & 0xf]; + UNPACKFUNCTYPE func = vif->usn ? ft.funcU : ft.funcS; + + const nVifCall* fnbase = &nVifUpk[ + ((usn*2*16) + (doMask*16) + (upkNum)) * (4*4) + ]; + + const int cycleSize = isFill ? vifRegs->cycle.cl : vifRegs->cycle.wl; + const int blockSize = isFill ? vifRegs->cycle.wl : vifRegs->cycle.cl; + + if (doMask) + setMasks(*vifRegs); + + if (vif->cl >= blockSize) { + vif->cl = 0; + } + + while (vifRegs->num > 0) { + if (vif->cl < cycleSize) { + //if (size <= 0) { DbgCon.WriteLn("_nVifUnpack: Out of Data!"); break; } + if (doMode /*|| doMask*/) { + //if (doMask) + //DevCon.WriteLn("Non SSE; unpackNum = %d", upkNum); + func((u32*)dest, (u32*)data, ft.qsize); + data += ft.gsize; + size -= ft.gsize; + vifRegs->num--; + } + else if (1) { + //DevCon.WriteLn("SSE Unpack!"); + fnbase[aMin(vif->cl, 4) * 4](dest, data); + data += vift; + size -= vift; + vifRegs->num--; + } + else { + + //DevCon.WriteLn("SSE Unpack!"); + int c = aMin((cycleSize - vif->cl), 3); + size -= vift * c; + //if (c>1) { DevCon.WriteLn("C > 1!"); } + if (c<0||c>3) { DbgCon.WriteLn("C wtf!"); } + if (size < 0) { DbgCon.WriteLn("Size Shit"); size+=vift*c;c=1;size-=vift*c;} + fnbase[(aMin(vif->cl, 4) * 4) + c-1](dest, data); + data += vift * c; + vifRegs->num -= c; + } + } + else if (isFill) { + func((u32*)dest, (u32*)data, ft.qsize); + vifRegs->num--; + } + incVUptr(idx, dest, 16); + + // Removing this modulo was a huge speedup for God of War. (62->73 fps) + // (GoW uses a lot of blockSize==1 packets, resulting in tons of loops -- so the biggest + // factor in performance ends up being the top-level conditionals of the loop, and + // also the loop prep code.) --air + + //vif->cl = (vif->cl+1) % blockSize; + if( ++vif->cl == blockSize ) vif->cl = 0; + } +} + +void _nVifUnpack(int idx, u8 *data, u32 size) { /*if (nVif[idx].vifRegs->cycle.cl >= nVif[idx].vifRegs->cycle.wl) { // skipping write if (!idx) VIFunpack<0>((u32*)data, &vif0.tag, size>>2); else VIFunpack<1>((u32*)data, &vif1.tag, size>>2); @@ -107,82 +204,56 @@ _f void _nVifUnpack(int idx, u8 *data, u32 size) { else*/ { // filling write vif = nVif[idx].vif; vifRegs = nVif[idx].vifRegs; - int isFill = !!(vifRegs->cycle.cl < vifRegs->cycle.wl); - int usn = !!(vif->usn); - int doMask = !!(vif->tag.cmd & 0x10); - int upkNum = vif->tag.cmd & 0xf; - int doMode = !!(vifRegs->mode); - if (doMask) setMasks(vifRegs); + + const bool doMode = !!vifRegs->mode; + const bool isFill = (vifRegs->cycle.cl < vifRegs->cycle.wl); + + //UnpackLoopTable[idx][doMode][isFill]( data, size ); + + if( idx ) + { + if( doMode ) + { + if( isFill ) + _nVifUnpackLoop<1,true,true>( data, size ); + else + _nVifUnpackLoop<1,true,false>( data, size ); + } + else + { + if( isFill ) + _nVifUnpackLoop<1,false,true>( data, size ); + else + _nVifUnpackLoop<1,false,false>( data, size ); + } + } + else + { + pxFailDev( "No VIF0 support yet, sorry!" ); + } //if (isFill) //DevCon.WriteLn("%s Write! [num = %d][%s]", (isFill?"Filling":"Skipping"), vifRegs->num, (vifRegs->num%3 ? "bad!" : "ok")); //DevCon.WriteLn("%s Write! [mask = %08x][type = %02d][num = %d]", (isFill?"Filling":"Skipping"), vifRegs->mask, upkNum, vifRegs->num); - u8* dest = setVUptr(idx, vif->tag.addr); - const VIFUnpackFuncTable* ft = &VIFfuncTable[vif->tag.cmd & 0xf]; - UNPACKFUNCTYPE func = vif->usn ? ft->funcU : ft->funcS; - int cycleSize = isFill ? vifRegs->cycle.cl : vifRegs->cycle.wl; - int blockSize = isFill ? vifRegs->cycle.wl : vifRegs->cycle.cl; - //vif->cl = 0; - while (vifRegs->num > 0) { - if (vif->cl >= blockSize) { - vif->cl = 0; - } - if (vif->cl < cycleSize) { - if (size <= 0) { DevCon.WriteLn("_nVifUnpack: Out of Data!"); break; } - if (doMode /*|| doMask*/) { - //if (doMask) - //DevCon.WriteLn("Non SSE; unpackNum = %d", upkNum); - func((u32*)dest, (u32*)data, ft->qsize); - data += ft->gsize; - size -= ft->gsize; - vifRegs->num--; - } - else if (1) { - //DevCon.WriteLn("SSE Unpack!"); - nVifUnpackF(dest, data, usn, doMask, aMin(vif->cl, 4), 0, upkNum); - data += nVifT[upkNum]; - size -= nVifT[upkNum]; - vifRegs->num--; - } - else { - //DevCon.WriteLn("SSE Unpack!"); - int c = aMin((cycleSize - vif->cl), 3); - int t = nVifT[upkNum]; - size -= t * c; - //if (c>1) { DevCon.WriteLn("C > 1!"); } - if (c<0||c>3) { DevCon.WriteLn("C wtf!"); } - if (size < 0) { DevCon.WriteLn("Size Shit"); size+=t*c;c=1;size-=t*c;} - nVifUnpackF(dest, data, usn, doMask, aMin(vif->cl, 4), c-1, upkNum); - data += t * c; - vifRegs->num -= c; - } - } - else if (isFill) { - func((u32*)dest, (u32*)data, ft->qsize); - vifRegs->num--; - } - incVUptr(idx, dest, 16); - vif->cl = (vif->cl+1) % blockSize; - } } } -//int nVifUnpack(int idx, u32 *data) { -// XMMRegisters::Freeze(); -// BlockBuffer* vB = nVif[idx].vifBlock; -// int ret = aMin(vif1.vifpacketsize, vif1.tag.size); -// //vB->append(data, ret<<2); -// vif1.tag.size -= ret; -// //DevCon.WriteLn("2 [0x%x][%d][%d]", vif1.tag.addr, vB->getSize(), vif1.tag.size<<2); -// //if (vif1.tag.size <= 0) { -// //DevCon.WriteLn("3 [0x%x][%d][%d]", vif1.tag.addr, vB->getSize(), vif1.tag.size<<2); -// //VIFunpack<1>(vB->getBlock(), &vif1.tag, vB->getSize()>>2); -// //_nVifUnpack(idx, vB->getBlock(), vB->getSize()); -// _nVifUnpack(idx, (u8*)data, ret<<2); -// if (vif1.tag.size <= 0) vif1.tag.size = 0; -// if (vif1.tag.size <= 0) vif1.cmd = 0; -// //vB->clear(); +//int nVifUnpack(int idx, u32 *data) { +// XMMRegisters::Freeze(); +// BlockBuffer* vB = nVif[idx].vifBlock; +// int ret = aMin(vif1.vifpacketsize, vif1.tag.size); +// //vB->append(data, ret<<2); +// vif1.tag.size -= ret; +// //DevCon.WriteLn("2 [0x%x][%d][%d]", vif1.tag.addr, vB->getSize(), vif1.tag.size<<2); +// //if (vif1.tag.size <= 0) { +// //DevCon.WriteLn("3 [0x%x][%d][%d]", vif1.tag.addr, vB->getSize(), vif1.tag.size<<2); +// //VIFunpack<1>(vB->getBlock(), &vif1.tag, vB->getSize()>>2); +// //_nVifUnpack(idx, vB->getBlock(), vB->getSize()); +// _nVifUnpack(idx, (u8*)data, ret<<2); +// if (vif1.tag.size <= 0) vif1.tag.size = 0; +// if (vif1.tag.size <= 0) vif1.cmd = 0; +// //vB->clear(); // //} // //else { vif1.tag.size+=ret; ret = -1; vB->clear(); } // XMMRegisters::Thaw(); diff --git a/pcsx2/x86/newVif_UnpackGen.inl b/pcsx2/x86/newVif_UnpackGen.inl index 08c44c004c..8a6be76fc9 100644 --- a/pcsx2/x86/newVif_UnpackGen.inl +++ b/pcsx2/x86/newVif_UnpackGen.inl @@ -66,11 +66,40 @@ void convertRGB() { xPSRL.D (xmm2, 24); // single AND... } +struct VifUnpackIndexer +{ + int usn, mask; + int curCycle, cyclesToWrite; + + nVifCall& GetCall( int packType ) const + { + int usnpart = usn*2*16; + int maskpart = mask*16; + int packpart = packType; + + int curpart = curCycle*4; + int cycpespart = cyclesToWrite; + + return nVifUpk[((usnpart+maskpart+packpart)*(4*4)) + (curpart+cycpespart)]; + } + + void xSetCall( int packType ) const + { + xAlignPtr(16); + GetCall( packType ) = (nVifCall)xGetPtr(); + } + + void xSetNullCall( int packType ) const + { + GetCall( packType ) = NULL; + } +}; + // ecx = dest, edx = src void nVifGen(int usn, int mask, int curCycle, int cycles) { - HostSys::MemProtect(nVifUpk, sizeof(nVifUpk), Protect_ReadWrite, false); + const VifUnpackIndexer indexer = { usn, mask, curCycle, cycles }; - xSetPtr(&nVifUpk[usn][mask][curCycle][cycles][0x0]); // S-32 + indexer.xSetCall(0x0); // S-32 if (cycles>=0) xMOVUPS (xmm0, ptr32[edx]); if (cycles>=0) xPSHUF.D (xmm1, xmm0, _v0); if (cycles>=1) xPSHUF.D (xmm2, xmm0, _v1); @@ -78,7 +107,7 @@ void nVifGen(int usn, int mask, int curCycle, int cycles) { if (cycles>=0) xMovDest (xmm1, xmm2, xmm3); xRET(); - xSetPtr(&nVifUpk[usn][mask][curCycle][cycles][0x1]); // S-16 + indexer.xSetCall(0x1); // S-16 if (cycles>=0) xMOVUPS (xmm0, ptr32[edx]); if (cycles>=0) xPUNPCK.LWD(xmm0, xmm0); if (cycles>=0) xShiftR (xmm0, 16); @@ -88,7 +117,7 @@ void nVifGen(int usn, int mask, int curCycle, int cycles) { if (cycles>=0) xMovDest (xmm1, xmm2, xmm3); xRET(); - xSetPtr(&nVifUpk[usn][mask][curCycle][cycles][0x2]); // S-8 + indexer.xSetCall(0x2); // S-8 if (cycles>=0) xMOVUPS (xmm0, ptr32[edx]); if (cycles>=0) xPUNPCK.LBW(xmm0, xmm0); if (cycles>=0) xPUNPCK.LWD(xmm0, xmm0); @@ -99,15 +128,16 @@ void nVifGen(int usn, int mask, int curCycle, int cycles) { if (cycles>=0) xMovDest (xmm1, xmm2, xmm3); xRET(); - xSetPtr(&nVifUpk[usn][mask][curCycle][cycles][0x3]); // ---- - xSetPtr(&nVifUpk[usn][mask][curCycle][cycles][0x4]); // V2-32 + indexer.xSetNullCall(0x3); // ---- + + indexer.xSetCall(0x4); // V2-32 if (cycles>=0) xMOVUPS (xmm0, ptr32[edx]); if (cycles>=2) xMOVUPS (xmm2, ptr32[edx+0x10]); if (cycles>=1) xPSHUF.D (xmm1, xmm0, 0xe); if (cycles>=0) xMovDest (xmm0, xmm1, xmm2); xRET(); - xSetPtr(&nVifUpk[usn][mask][curCycle][cycles][0x5]); // V2-16 + indexer.xSetCall(0x5); // V2-16 if (cycles>=0) xMOVUPS (xmm0, ptr32[edx]); if (cycles>=2) xPSHUF.D (xmm2, xmm0, _v2); if (cycles>=0) xPUNPCK.LWD(xmm0, xmm0); @@ -118,7 +148,7 @@ void nVifGen(int usn, int mask, int curCycle, int cycles) { if (cycles>=0) xMovDest (xmm0, xmm1, xmm2); xRET(); - xSetPtr(&nVifUpk[usn][mask][curCycle][cycles][0x6]); // V2-8 + indexer.xSetCall(0x6); // V2-8 if (cycles>=0) xMOVUPS (xmm0, ptr32[edx]); if (cycles>=0) xPUNPCK.LBW(xmm0, xmm0); if (cycles>=2) xPSHUF.D (xmm2, xmm0, _v2); @@ -130,15 +160,16 @@ void nVifGen(int usn, int mask, int curCycle, int cycles) { if (cycles>=0) xMovDest (xmm0, xmm1, xmm2); xRET(); - xSetPtr(&nVifUpk[usn][mask][curCycle][cycles][0x7]); // ---- - xSetPtr(&nVifUpk[usn][mask][curCycle][cycles][0x8]); // V3-32 + indexer.xSetNullCall(0x7); // ---- + + indexer.xSetCall(0x8); // V3-32 if (cycles>=0) xMOVUPS (xmm0, ptr32[edx]); if (cycles>=1) xMOVUPS (xmm1, ptr32[edx+12]); if (cycles>=2) xMOVUPS (xmm2, ptr32[edx+24]); if (cycles>=0) xMovDest (xmm0, xmm1, xmm2); xRET(); - xSetPtr(&nVifUpk[usn][mask][curCycle][cycles][0x9]); // V3-16 + indexer.xSetCall(0x9); // V3-16 if (cycles>=0) xMOVUPS (xmm0, ptr32[edx]); if (cycles>=1) xMOVUPS (xmm1, ptr32[edx+6]); if (cycles>=2) xMOVUPS (xmm2, ptr32[edx+12]); @@ -151,7 +182,7 @@ void nVifGen(int usn, int mask, int curCycle, int cycles) { if (cycles>=0) xMovDest (xmm0, xmm1, xmm2); xRET(); - xSetPtr(&nVifUpk[usn][mask][curCycle][cycles][0xa]); // V3-8 + indexer.xSetCall(0xa); // V3-8 if (cycles>=0) xMOVUPS (xmm0, ptr32[edx]); if (cycles>=1) xMOVUPS (xmm1, ptr32[edx+3]); if (cycles>=2) xMOVUPS (xmm2, ptr32[edx+6]); @@ -167,15 +198,16 @@ void nVifGen(int usn, int mask, int curCycle, int cycles) { if (cycles>=0) xMovDest (xmm0, xmm1, xmm2); xRET(); - xSetPtr(&nVifUpk[usn][mask][curCycle][cycles][0xb]); // ---- - xSetPtr(&nVifUpk[usn][mask][curCycle][cycles][0xc]); // V4-32 + indexer.xSetNullCall(0xb); // ---- + + indexer.xSetCall(0xc); // V4-32 if (cycles>=0) xMOVUPS (xmm0, ptr32[edx]); if (cycles>=1) xMOVUPS (xmm1, ptr32[edx+0x10]); if (cycles>=2) xMOVUPS (xmm2, ptr32[edx+0x20]); if (cycles>=0) xMovDest (xmm0, xmm1, xmm2); xRET(); - xSetPtr(&nVifUpk[usn][mask][curCycle][cycles][0xd]); // V4-16 + indexer.xSetCall(0xd); // V4-16 if (cycles>=0) xMOVUPS (xmm0, ptr32[edx]); if (cycles>=1) xMOVUPS (xmm1, ptr32[edx+0x10]); if (cycles>=2) xMOVUPS (xmm2, ptr32[edx+0x20]); @@ -188,7 +220,7 @@ void nVifGen(int usn, int mask, int curCycle, int cycles) { if (cycles>=0) xMovDest (xmm0, xmm1, xmm2); xRET(); - xSetPtr(&nVifUpk[usn][mask][curCycle][cycles][0xe]); // V4-8 + indexer.xSetCall(0xe); // V4-8 if (cycles>=0) xMOVUPS (xmm0, ptr32[edx]); if (cycles>=1) xMOVUPS (xmm1, ptr32[edx+4]); if (cycles>=2) xMOVUPS (xmm2, ptr32[edx+8]); @@ -206,7 +238,7 @@ void nVifGen(int usn, int mask, int curCycle, int cycles) { // A | B5 | G5 | R5 // ..0.. A 0000000 | ..0.. B 000 | ..0.. G 000 | ..0.. R 000 - xSetPtr(&nVifUpk[usn][mask][curCycle][cycles][0xf]); // V4-5 + indexer.xSetCall(0xf); // V4-5 if (cycles>=0) xMOVUPS (xmm0, ptr32[edx]); if (cycles>=0) xMOVAPS (xmm1, xmm0); if (cycles>=0) convertRGB(); @@ -219,5 +251,6 @@ void nVifGen(int usn, int mask, int curCycle, int cycles) { if (cycles>=2) convertRGB(); if (cycles>=2) xMOVAPS (ptr32[ecx+0x20], xmm2); xRET(); - HostSys::MemProtect(nVifUpk, sizeof(nVifUpk), Protect_ReadOnly, true); + + pxAssert( ((uptr)xGetPtr() - (uptr)nVifUpkExec) < sizeof(nVifUpkExec) ); }