/* 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 . */ #include "PrecompiledHeader.h" #include "Common.h" #include "GS.h" #include "Gif_Unit.h" #include "Vif_Dma.h" #include "MTVU.h" Gif_Unit gifUnit; // Returns true on stalling SIGNAL bool Gif_HandlerAD(u8* pMem) { u32 reg = pMem[8]; u32* data = (u32*)pMem; if (reg == 0x50) vif1.BITBLTBUF._u64 = *(u64*)pMem; elif (reg == 0x52) vif1.TRXREG._u64 = *(u64*)pMem; elif (reg == 0x53) { // TRXDIR if ((pMem[0] & 3) == 1) { // local -> host u8 bpp = 32; // Onimusha does TRXDIR without BLTDIVIDE first, assume 32bit switch(vif1.BITBLTBUF.SPSM & 7) { case 0: bpp = 32; break; case 1: bpp = 24; break; case 2: bpp = 16; break; case 3: bpp = 8; break; default: // 4 is 4 bit but this is forbidden Console.Error("Illegal format for GS upload: SPSM=0%02o", vif1.BITBLTBUF.SPSM); break; } // qwords, rounded down; any extra bits are lost // games must take care to ensure transfer rectangles are exact multiples of a qword vif1.GSLastDownloadSize = vif1.TRXREG.RRW * vif1.TRXREG.RRH * bpp >> 7; } } elif (reg == 0x60) { // SIGNAL if (CSRreg.SIGNAL) { // Time to ignore all subsequent drawing operations. GUNIT_WARN(Color_Orange, "GIF Handler - Stalling SIGNAL"); if(!gifUnit.gsSIGNAL.queued) { gifUnit.gsSIGNAL.queued = true; gifUnit.gsSIGNAL.data[0] = data[0]; gifUnit.gsSIGNAL.data[1] = data[1]; return true; // Stalling SIGNAL } } else { GUNIT_WARN("GIF Handler - SIGNAL"); GSSIGLBLID.SIGID = (GSSIGLBLID.SIGID&~data[1])|(data[0]&data[1]); if (!(GSIMR&0x100)) gsIrq(); CSRreg.SIGNAL = true; } } elif (reg == 0x61) { // FINISH GUNIT_WARN("GIF Handler - FINISH"); CSRreg.FINISH = true; } elif (reg == 0x62) { // LABEL GUNIT_WARN("GIF Handler - LABEL"); GSSIGLBLID.LBLID = (GSSIGLBLID.LBLID&~data[1])|(data[0]&data[1]); } elif (reg >= 0x63 && reg != 0x7f) { //DevCon.Warning("GIF Handler - Write to unknown register! [reg=%x]", reg); } return false; } // Returns true if pcsx2 needed to process the packet... bool Gif_HandlerAD_Debug(u8* pMem) { u32 reg = pMem[8]; if (reg == 0x50) { Console.Error("GIF Handler Debug - BITBLTBUF"); return 1; } elif (reg == 0x52) { Console.Error("GIF Handler Debug - TRXREG"); return 1; } elif (reg == 0x53) { Console.Error("GIF Handler Debug - TRXDIR"); return 1; } elif (reg == 0x60) { Console.Error("GIF Handler Debug - SIGNAL"); return 1; } elif (reg == 0x61) { Console.Error("GIF Handler Debug - FINISH"); return 1; } elif (reg == 0x62) { Console.Error("GIF Handler Debug - LABEL"); return 1; } elif (reg >= 0x63 && reg != 0x7f) { DevCon.Warning("GIF Handler Debug - Write to unknown register! [reg=%x]", reg); } return 0; } void Gif_FinishIRQ() { if (CSRreg.FINISH && !(GSIMR&0x200)) { gsIrq(); } } // Used in MTVU mode... MTVU will later complete a real packet void Gif_AddGSPacketMTVU(GS_Packet& gsPack, GIF_PATH path) { GetMTGS().SendSimpleGSPacket(GS_RINGTYPE_MTVU_GSPACKET, 0, 0, path); } void Gif_AddCompletedGSPacket(GS_Packet& gsPack, GIF_PATH path) { //DevCon.WriteLn("Adding Completed Gif Packet [size=%x]", gsPack.size); if (COPY_GS_PACKET_TO_MTGS) { GetMTGS().PrepDataPacket(path, gsPack.size/16); MemCopy_WrappedDest((u128*)&gifUnit.gifPath[path].buffer[gsPack.offset], RingBuffer.m_Ring, GetMTGS().m_packet_writepos, RingBufferSize, gsPack.size/16); GetMTGS().SendDataPacket(); } else { pxAssertDev(!gsPack.readAmount, "Gif Unit - gsPack.readAmount only valid for MTVU path 1!"); AtomicExchangeAdd(gifUnit.gifPath[path].readAmount, gsPack.size); GetMTGS().SendSimpleGSPacket(GS_RINGTYPE_GSPACKET, gsPack.offset, gsPack.size, path); } } void Gif_AddBlankGSPacket(u32 size, GIF_PATH path) { //DevCon.WriteLn("Adding Blank Gif Packet [size=%x]", size); AtomicExchangeAdd(gifUnit.gifPath[path].readAmount, size); GetMTGS().SendSimpleGSPacket(GS_RINGTYPE_GSPACKET, ~0u, size, path); } void Gif_MTGS_Wait(bool isMTVU) { GetMTGS().WaitGS(false, true, isMTVU); } void SaveStateBase::gifPathFreeze(u32 path) { Gif_Path& gifPath = gifUnit.gifPath[path]; pxAssertDev(!gifPath.readAmount, "Gif Path readAmount should be 0!"); pxAssertDev(!gifPath.gsPack.readAmount, "GS Pack readAmount should be 0!"); pxAssertDev(!gifPath.GetPendingGSPackets(), "MTVU GS Pack Queue should be 0!"); if (!gifPath.isMTVU()) { // FixMe: savestate freeze bug (Gust games) with MTVU enabled if (IsSaving()) { // Move all the buffered data to the start of buffer gifPath.RealignPacket(); // May add readAmount which we need to clear on load } } u8* bufferPtr = gifPath.buffer; // Backup current buffer ptr Freeze(gifPath.mtvu.fakePackets); FreezeMem(&gifPath, sizeof(gifPath) - sizeof(gifPath.mtvu)); FreezeMem(bufferPtr, gifPath.curSize); gifPath.buffer = bufferPtr; if(!IsSaving()) { gifPath.readAmount = 0; gifPath.gsPack.readAmount = 0; } } void SaveStateBase::gifFreeze() { bool mtvuMode = THREAD_VU1; pxAssert(vu1Thread.IsDone()); GetMTGS().WaitGS(); FreezeTag("Gif Unit"); Freeze(mtvuMode); Freeze(gifUnit.stat); Freeze(gifUnit.gsSIGNAL); Freeze(gifUnit.lastTranType); gifPathFreeze(GIF_PATH_1); gifPathFreeze(GIF_PATH_2); gifPathFreeze(GIF_PATH_3); if (!IsSaving()) { if (mtvuMode != THREAD_VU1) { DevCon.Warning("gifUnit: MTVU Mode has switched between save/load state"); // ToDo: gifUnit.SwitchMTVU(mtvuMode); } } }