rendered paste body/* Pcsx2 - Pc Ps2 Emulator * Copyright (C) 2002-2003 Pcsx2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */// rewritten by zerofrog to add multithreading/gs caching to GS and VU1// initial port to pthreads by kommerck// Resources: http://www-128.ibm.com/developerworks/linux/library/l-ipc2lin3.html#include "Common.h"#include <xmmintrin.h>#include <assert.h>#include "VU0.h"#include "VUmicro.h"#include <pthread.h>#include <semaphore.h>#include <sys/types.h>#include <sys/mman.h>#include <errno.h>#ifdef WIN32_VIRTUAL_MEM#define gif ((DMACh*)&PS2MEM_HW[0xA000])#else#define gif ((DMACh*)&psH[0xA000])#endif#define ENABLE_GS_CACHING_SIZE 0x4000 // min size//#define _RINGBUF_DEBUGextern void logtime(int);#ifdef WIN32_VIRTUAL_MEM#define PS2GS_BASE(mem) ((PS2MEM_BASE+0x12000000)+(mem&0x13ff))#elseu8 g_RealGSMem[0x2000];#define PS2GS_BASE(mem) (g_RealGSMem+(mem&0x13ff))#endif#ifdef GSCAPTUREu32 g_loggs = 0;u32 g_gstransnum = 0;u32 g_gslimit = 0;u32 g_gsfinalnum = 0;#endif// dummy GS for processing SIGNAL, FINISH, and LABEL commandstypedef struct{ u32 nloop : 15; u32 eop : 1; u32 dummy0 : 16; u32 dummy1 : 14; u32 pre : 1; u32 prim : 11; u32 flg : 2; u32 nreg : 4; u32 regs[2]; u32 curreg;} GIFTAG;static GIFTAG g_path[3];__declspec(align(16)) static BYTE s_byRegs[3][16]; //HANDLE g_hGsEvent = NULL, // set when path3 is ready to be processed// g_hVuGSExit = NULL; // set when thread needs to exit//HANDLE g_hGSOpen = NULL, g_hGSDone = NULL;//HANDLE g_hVuGSThread = NULL;sem_t g_hGsEvent;sem_t g_hVuGSExit;sem_t g_hGSOpen;sem_t g_hGSDone;pthread_t g_hVuGSThread;u32 CSRw;HANDLE g_hAllGsReady[3] = {NULL};//DWORD WINAPI VuGSThreadProc(LPVOID lpParam);void* VuGSThreadProc(void* lpParam);// g_pGSRingPos == g_pGSWritePos => fifo is emptyu8* g_pGSRingPos = NULL, // cur pos ring is at *g_pGSWritePos = NULL; // cur pos ee thread is atu8 *g_pGSTempWritePos = NULL, *g_pGSTempReadPos = NULL; // cur pos ee thread is atextern int g_nCounters[];extern void * memcpy_amd(void *dest, const void *src, size_t n);//CRITICAL_SECTION g_PageAddrSection;pthread_mutex_t g_PageAddrSection;u32 *g_pGSCurFreePages = NULL, *g_pGSWriteFreePages = NULL;//FIXME: it's not the best way to do this, and is slower as the original too#ifdef __GNUC__void* InterlockedExchangePointer(void** target, void* value)//Based on TBCOOL's suggestion//don't think it works as expected now{ __asm__ __volatile__ ( " lock\n" " xchgl %1,%0\n" : "=a" (*target), "=b" (value) : "a" (*target), "b" (value) : "%ebx", "%eax", "memory" ); return value;}#endif//TODO: 64bit InterlockedExchangePointer?void gsInit(){ //if( VirtualAlloc(GS_PAGEADDRS, GSPAGES_STRIDE*(0x10000000>>GS_SHIFT), MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE) != GS_PAGEADDRS ) { //FIXME: this mmap behaviour must be wrong, not sure if VirtualAlloc starts at 0, // and I'm rather unsure about PROT_READ|PROT_WRITE flags if (mmap(GS_PAGEADDRS, GSPAGES_STRIDE*(0x10000000>>GS_SHIFT), PROT_READ|PROT_WRITE, MAP_ANONYMOUS, -1, 0) != GS_PAGEADDRS) { //MessageBox(NULL, "Cannot alloc GS page buffer\n", "Error", MB_OK); printf("ERROR: Cannot mmap GS page buffer: error %i\n", errno); exit(0); } memset(GS_PAGEADDRS, 0, GSPAGES_STRIDE*(0x02000000>>GS_SHIFT)); // FIXME: Perhaps it's better to run mmap this way: //GS_PAGEADDRS=mmap(GS_PAGEADDRS, GSPAGES_STRIDE*(0x02000000>>GS_SHIFT), PROT_READ|PROT_WRITE, MAP_ANONYMOUS, -1, 0) != GS_PAGEADDRS) if( CHECK_MULTIGS ) { // g_hGsEvent = CreateEvent(NULL, FALSE, FALSE, NULL);// g_hVuGSExit = CreateEvent(NULL, FALSE, FALSE, NULL);// g_hGSOpen = CreateEvent(NULL, FALSE, FALSE, NULL);// g_hGSDone = CreateEvent(NULL, FALSE, FALSE, NULL); // Semaptores are used instead of Win32 events. sem_init(g_hGsEvent); sem_init(g_hVuGSExit); sem_init(g_hGSOpen); sem_init(g_hGSDone); //SysPrintf("gsInit\n");// g_pGSRingPos = VirtualAlloc(GS_RINGBUFFERBASE, GS_RINGBUFFERSIZE, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); g_pGSRingPos = mmap(GS__RINGBUFFERBASE, GS_RINGBUFFERSIZE, PROT_READ|PROT_WRITE, MAP_ANONYMOUS, -1, 0); if( g_pGSRingPos != GS_RINGBUFFERBASE ) {// MessageBox(NULL, "Cannot alloc GS ring buffer\n", "Error", MB_OK); printf("ERROR: Cannot mmap GS ring buffer: error %i\n", errno); exit(0); }// g_pGSTempReadPos = g_pGSTempWritePos = VirtualAlloc(GS_RINGTEMPBASE, GS_RINGTEMPSIZE, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE); g_pGSTempReadPos = g_pGSTempWritePos = mmap(GS_RINGTEMPBASE, GS_RINGTEMPSIZE, PROT_READ|PROT_WRITE, MAP_ANONYMOUS, -1, 0); if( g_pGSTempWritePos != GS_RINGTEMPBASE ) {// MessageBox(NULL, "Cannot alloc GS RingTemp buffer\n", "Error", MB_OK); printf("ERROR: Cannot mmap GS ringtemp buffer: error %i\n", errno); exit(0); }// InitializeCriticalSection(&g_PageAddrSection); pthread_mutexattr_t g_PageAddrSectionAttr; pthread_mutexattr_settype(&g_PageAddrSectionAttr, PTHREAD_MUTEX_RECURSIVE_NP); //should it be recursive pthread_mutex_init(&g_PageAddrSection, &g_PageAddrSectionAttr); pthread_mutexattr_destroy(&g_PageAddrSectionAttr); InterlockedExchangePointer(&g_pGSWritePos, GS_RINGBUFFERBASE);// g_hVuGSThread = pthread_create(NULL, 0, VuGSThreadProc, NULL, 0, NULL); pthread_create(&g_hVuGSThread, NULL, VuGSThreadProc, NULL); }}void gsWaitGS(){ while( g_pGSRingPos != g_pGSWritePos ) Sleep(1);}void gsShutdown(){// VirtualFree(GS_PAGEADDRS, GSPAGES_STRIDE*(0x02000000>>GS_SHIFT), MEM_DECOMMIT|MEM_RELEASE); munmap(GS_PAGEADDRS, GSPAGES_STRIDE*(0x02000000>>GS_SHIFT));// ----ported until here---- FIXME: continue porting if( CHECK_MULTIGS ) { SetEvent(g_hVuGSExit); //SysPrintf("Closing gs thread\n"); WaitForSingleObject(g_hVuGSThread, INFINITE); CloseHandle(g_hVuGSThread); CloseHandle(g_hGsEvent); CloseHandle(g_hVuGSExit); CloseHandle(g_hGSOpen); CloseHandle(g_hGSDone); g_pGSWriteFreePages = g_pGSCurFreePages = NULL; g_pGSTempReadPos = NULL; DeleteCriticalSection(&g_PageAddrSection);// VirtualFree(GS_RINGBUFFERBASE, GS_RINGBUFFERSIZE, MEM_DECOMMIT|MEM_RELEASE);// VirtualFree(GS_RINGTEMPBASE, GS_RINGTEMPSIZE, MEM_DECOMMIT|MEM_RELEASE); munmap(GS_RINGBUFFERBASE, GS_RINGBUFFERSIZE); munmap(GS_RINGTEMPBASE, GS_RINGTEMPSIZE); } else GSclose();}static void GSRingTempReserve(u32 size){ u8* writepos = g_pGSTempWritePos; assert( size <= GS_RINGTEMPSIZE ); // there always has to be memory if( g_pGSTempWritePos + size > GS_RINGTEMPEND ) { if( writepos < g_pGSTempReadPos ) { do { SetEvent(g_hGsEvent); Sleep(1); } while( writepos < g_pGSTempReadPos ); } // notify GS writepos = GS_RINGTEMPBASE; } if( writepos < g_pGSTempReadPos && writepos+size >= g_pGSTempReadPos ) { do { SetEvent(g_hGsEvent); Sleep(1); } while( writepos < g_pGSTempReadPos && writepos+size >= g_pGSTempReadPos ); } g_pGSTempWritePos = writepos;}static u8* GSRingTempCopy(u32 size){ u8* writepos; assert( size <= GS_RINGTEMPSIZE ); //GSRingTempReserve(size); writepos = g_pGSTempWritePos; g_pGSTempWritePos += size; assert( g_pGSTempWritePos <= GS_RINGTEMPEND ); //SysPrintf("writepos copy: %x %d\n", writepos, size); return writepos;}typedef u8* PU8;u8* GSRingBufCopy(void* mem, u32 size, u32 type){ u8* writepos = g_pGSWritePos; u8* tempbuf; assert( size < 0x10000*16 ); assert( writepos < GS_RINGBUFFEREND ); assert( ((u32)writepos & 15) == 0 ); assert( (size&15) == 0); size += 16; tempbuf = *(volatile PU8*)&g_pGSRingPos; if( writepos + size > GS_RINGBUFFEREND ) { // skip to beginning while( writepos < tempbuf || tempbuf == GS_RINGBUFFERBASE ) { SetEvent(g_hGsEvent); Sleep(1); tempbuf = *(volatile PU8*)&g_pGSRingPos; } // notify GS if( writepos != GS_RINGBUFFEREND ) { *(u32*)writepos = GS_RINGTYPE_RESTART; } writepos = GS_RINGBUFFERBASE; } while( writepos < tempbuf && (writepos+size >= g_pGSRingPos || (writepos+size == GS_RINGBUFFEREND && g_pGSRingPos == GS_RINGBUFFERBASE)) ) { SetEvent(g_hGsEvent); Sleep(1); tempbuf = *(volatile PU8*)&g_pGSRingPos; }#ifdef WIN32_VIRTUAL_MEM if( ENABLE_GS_CACHING && size >= ENABLE_GS_CACHING_SIZE && mem != NULL && (u8*)mem < PS2MEM_BASE+0x10000000 ) { u32 val; u32 i; u32* page = GS_PAGEADDRS+(((u32)((u8*)mem-PS2MEM_BASE)&0x01ffffff)>>GS_SHIFT)*(GSPAGES_MEMADDRS+1); u32 numpages = (((u32)mem+size-1)>>GS_SHIFT) - ((u32)mem>>GS_SHIFT)+1; // inc all pages for(i = 0; i < numpages; ++i) { if( (u8)page[0] >= GSPAGES_MEMADDRS ) break; val = InterlockedExchangeAdd(page, 0x40000001); assert( (u8)val < GSPAGES_MEMADDRS ); // upper 2 bits are index page[(val>>30)+1] = (u32)writepos; page += GSPAGES_MEMADDRS+1; } if( i == numpages ) { // succeeded, so fill necessary data *(u32*)writepos = GS_RINGTYPE_MEMREF|type|(((size-16)>>4)<<16); *(u32*)(writepos+4) = (u32)mem; *(u32*)(writepos+8) = (u32)(page-(GSPAGES_MEMADDRS+1)*numpages); *(u32*)(writepos+12) = (u32)numpages; writepos += 16;#ifndef _RINGBUF_DEBUG if( writepos == GS_RINGBUFFEREND ) writepos = GS_RINGBUFFERBASE; InterlockedExchangePointer(&g_pGSWritePos, writepos);// SetEvent(g_hGsEvent);// while( g_pGSWritePos != g_pGSRingPos ) Sleep(1); return NULL;#else return writepos;#endif } SysPrintf("not enough pages %x, %d\n", mem, size); // BUG, the interlocked exchange add should be done in a crit section! // dec the current pages while(i-- > 0) { page -= GSPAGES_MEMADDRS+1; InterlockedExchangeAdd(page, -0x40000001); } // don't have enough mem, so fall through }#endif // just copy *(u32*)writepos = type|(((size-16)>>4)<<16); return writepos+16;}void GSRingBufVSync(int field){ u8* writepos = g_pGSWritePos; u8* tempbuf; assert( writepos + 16 <= GS_RINGBUFFEREND ); tempbuf = *(volatile PU8*)&g_pGSRingPos; if( writepos < tempbuf && writepos+16 >= tempbuf ) { do { SetEvent(g_hGsEvent); Sleep(1); tempbuf = *(volatile PU8*)&g_pGSRingPos; } while(writepos < tempbuf && writepos+16 >= tempbuf ); } *(u32*)writepos = GS_RINGTYPE_VSYNC; *(int*)(writepos+4) = field; writepos += 16; if( writepos == GS_RINGBUFFEREND ) writepos = GS_RINGBUFFERBASE; InterlockedExchangePointer(&g_pGSWritePos, writepos); if( !CHECK_DUALCORE ) SetEvent(g_hGsEvent);}// called from gs thread or exception handlerstatic void GSFreeRingBuf(u8* pringbuf){ u32 mem = *(u32*)(pringbuf+4) & ~0xfff; u32* pageaddr = *(u32**)(pringbuf+8); u32 pages = *(u32*)(pringbuf+12); u32 val, index; mem &= ~0xfff; while(pages-- > 0 ) { val = pageaddr[0]; assert( (val&0xff) > 0 ); index = (((val>>30)-(val&7))&3); if( pageaddr[ index + 1 ] != (u32)pringbuf ) { // deleting before others, so remove it from the entry for(val = 1; val < 5; ++val ) { if( pageaddr[val] == (u32)pringbuf ) { val--; // shift the rest while(index != val) { pageaddr[1+(val)%GSPAGES_MEMADDRS] = pageaddr[(val-1)%GSPAGES_MEMADDRS+1]; val = (val-1)%GSPAGES_MEMADDRS; } break; } } } InterlockedExchangeAdd(pageaddr, -1); mem += 0x1000; pageaddr += GSPAGES_MEMADDRS+1; }}void GSFreePage(u32* page){ u32 indices, num; u32 gssize; u8* gsmem, *ringmem; num = *(u32*)page; // count approximately the size needed to be allocated indices = ((num>>30)-(num&0xff))&3; num &= 0xff; gssize = 0; while(num-- > 0) { // first copy to a new memory ringmem = *(u8**)(page + 1 + indices); gssize += (*(u32*)ringmem >> 16) << 4; // go to the next index indices = (indices+1)%GSPAGES_MEMADDRS; } GSRingTempReserve(gssize); // lock section to start freeing EnterCriticalSection(&g_PageAddrSection); // check again since could have changed num = *(u32*)page; if( (u8)num ) { // note that values might be different indices = ((num>>30)-(num&0xff))&3; num &= 0xff; while(num-- > 0) { // first copy to a new memory ringmem = *(u8**)(page + 1 + indices); assert( !(*(u32*)ringmem & GS_RINGTYPE_MEMNOFREE) ); gssize = (*(u32*)ringmem >> 16) << 4; gsmem = GSRingTempCopy(gssize); memcpy(gsmem, *(u8**)(ringmem+4), gssize); GSFreeRingBuf(ringmem); // indicate that using different mem *(u8**)(ringmem+4) = gsmem; *(u32*)ringmem |= GS_RINGTYPE_MEMNOFREE; // go to the next index indices = (indices+1)%GSPAGES_MEMADDRS; } } LeaveCriticalSection(&g_PageAddrSection);}void gsReset(){ memset(GS_PAGEADDRS, 0, GSPAGES_STRIDE*(0x02000000>>GS_SHIFT)); if( CHECK_MULTIGS ) { ResetEvent(g_hGsEvent); ResetEvent(g_hVuGSExit); g_pGSRingPos = g_pGSWritePos; g_pGSCurFreePages = g_pGSWriteFreePages; g_pGSTempReadPos = g_pGSTempWritePos = GS_RINGTEMPBASE; } memset(g_path, 0, sizeof(g_path)); memset(s_byRegs, 0, sizeof(s_byRegs));#ifndef WIN32_VIRTUAL_MEM memset(g_RealGSMem, 0, 0x2000);#endif GSCSRr = 0x551B4000; // Set the FINISH bit to 1 for now GSIMR = 0x7f00;}void CSRwrite(u32 value){ GSwriteCSR(value); GSCSRr = ((GSCSRr&~value)&0x1f)|(GSCSRr&~0x1f); if( value & 0x100 ) { // FLUSH //SysPrintf("GS_CSR FLUSH GS fifo: %x (CSRr=%x)\n", value, GSCSRr); } CSRw |= value & ~0x60; if (value & 0x200) { // resetGS //GSCSRr = 0x400E; // The host FIFO neeeds to be empty too or GSsync will fail (saqib) //GSIMR = 0xff00; GSreset(); GSCSRr = 0x551B4000; // Set the FINISH bit to 1 - GS is always at a finish state as we don't have a FIFO(saqib) //Since when!! Refraction GSIMR = 0x7F00; //This is bits 14-8 thats all that should be 1 }}static void IMRwrite(u32 value) { //SysPrintf("GS_IMR: %x\n", value); GSIMR = value & 0x7f00;}void gsWrite8(u32 mem, u8 value) { switch (mem) { case 0x12001000: // GS_CSR CSRwrite((CSRw & ~0x000000ff) | value); break; case 0x12001001: // GS_CSR CSRwrite((CSRw & ~0x0000ff00) | (value << 8)); break; case 0x12001002: // GS_CSR CSRwrite((CSRw & ~0x00ff0000) | (value << 16)); break; case 0x12001003: // GS_CSR CSRwrite((CSRw & ~0xff000000) | (value << 24)); break; default: *PS2GS_BASE(mem) = value; }#ifdef HW_LOG HW_LOG("GS write 8 at %8.8lx with data %8.8lx\n", mem, value);#endif}extern void UpdateVSyncRate();void gsWrite16(u32 mem, u16 value) { switch (mem) { case 0x12000010: // GS_SMODE1 if((value & 0x6000) == 0x6000) Config.PsxType |= 1; // PAL else Config.PsxType &= ~1; // NTSC *(u16*)PS2GS_BASE(mem) = value; UpdateVSyncRate(); break; case 0x12000020: // GS_SMODE2 if(value & 0x1) Config.PsxType |= 2; // Interlaced else Config.PsxType &= ~2; // Non-Interlaced *(u16*)PS2GS_BASE(mem) = value; break; case 0x12001000: // GS_CSR CSRwrite( (CSRw&0xffff0000) | value); break; case 0x12001002: // GS_CSR CSRwrite( (CSRw&0xffff) | ((u32)value<<16)); break; default: *(u16*)PS2GS_BASE(mem) = value; }#ifdef HW_LOG HW_LOG("GS write 16 at %8.8lx with data %8.8lx\n", mem, value);#endif}void gsWrite32(u32 mem, u32 value) { switch (mem) { case 0x12000010: // GS_SMODE1 if((value & 0x6000) == 0x6000) Config.PsxType |= 1; // PAL else Config.PsxType &= ~1; // NTSC *(u32*)PS2GS_BASE(mem) = value; UpdateVSyncRate(); break; case 0x12000020: // GS_SMODE2 if(value & 0x1) Config.PsxType |= 2; // Interlaced else Config.PsxType &= ~2; // Non-Interlaced *(u32*)PS2GS_BASE(mem) = value; break; case 0x12001000: // GS_CSR CSRwrite(value); break; case 0x12001010: // GS_IMR IMRwrite(value); break; default: *(u32*)PS2GS_BASE(mem) = value; }#ifdef HW_LOG HW_LOG("GS write 32 at %8.8lx with data %8.8lx\n", mem, value);#endif}void gsWrite64(u32 mem, u64 value) { switch (mem) { case 0x12000010: // GS_SMODE1 if((value & 0x6000) == 0x6000) Config.PsxType |= 1; // PAL else Config.PsxType &= ~1; // NTSC UpdateVSyncRate(); *(u64*)PS2GS_BASE(mem) = value; break; case 0x12000020: // GS_SMODE2 if(value & 0x1) Config.PsxType |= 2; // Interlaced else Config.PsxType &= ~2; // Non-Interlaced *(u64*)PS2GS_BASE(mem) = value; break; case 0x12001000: // GS_CSR CSRwrite((u32)value); break; case 0x12001010: // GS_IMR IMRwrite((u32)value); break; default: *(u64*)PS2GS_BASE(mem) = value; }#ifdef HW_LOG HW_LOG("GS write 64 at %8.8lx with data %8.8lx_%8.8lx\n", mem, ((u32*)&value)[1], (u32)value);#endif}u8 gsRead8(u32 mem){#ifdef HW_LOG //HW_LOG("GS read 8 %8.8lx, at %8.8lx\n", *(u8*)(PS2MEM_BASE+(mem&~0xc00)), mem);#endif return *(u8*)PS2GS_BASE(mem);}u16 gsRead16(u32 mem){#ifdef HW_LOG //HW_LOG("GS read 16 %8.8lx, at %8.8lx\n", *(u16*)(PS2MEM_BASE+(mem&~0xc00)), mem);#endif return *(u16*)PS2GS_BASE(mem);}u32 gsRead32(u32 mem) {#ifdef HW_LOG //HW_LOG("GS read 32 %8.8lx, at %8.8lx\n", *(u32*)(PS2MEM_BASE+(mem&~0xc00)), mem);#endif return *(u32*)PS2GS_BASE(mem);}u64 gsRead64(u32 mem){#ifdef HW_LOG //HW_LOG("GS read 64 %8.8lx (%8.8x), at %8.8lx\n", *(u64*)(PS2MEM_BASE+(mem&~0xc00)), mem);#endif return *(u64*)PS2GS_BASE(mem);}void gsIrq() { hwIntcIrq(0);}static void GSRegHandlerSIGNAL(u32* data){ GSSIGLBLID->SIGID = (GSSIGLBLID->SIGID&~data[1])|(data[0]&data[1]); if (CSRw & 0x1) { GSCSRr |= 1; // signal CSRw &= ~1; } if (!(GSIMR&0x100) ) gsIrq();}static void GSRegHandlerFINISH(u32* data){ GSCSRr |= 2; // finish if (CSRw & 0x2) { CSRw &= ~2; if (!(GSIMR&0x200) ) gsIrq(); }}static void GSRegHandlerLABEL(u32* data){ GSSIGLBLID->LBLID = (GSSIGLBLID->LBLID&~data[1])|(data[0]&data[1]);}typedef void (*GIFRegHandler)(u32* data);static GIFRegHandler s_GSHandlers[3] = { GSRegHandlerSIGNAL, GSRegHandlerFINISH, GSRegHandlerLABEL };// ffx pal, ntsc, jap#define GSHACK_FFX (ElfCRC==0xbb3d833a||ElfCRC==0xa39517ab||ElfCRC==0x6A4EFE60)// simulates a GIF tagu32 GSgifTransferDummy(int path, u32 *pMem, u32 size){ int nreg, i, nloop; u32 curreg; u32 tempreg; GIFTAG* ptag = &g_path[path];#ifdef GSCAPTURE u32 tempcount = 0;#endif if( path == 0 ) { nloop = 0; } else { nloop = ptag->nloop; curreg = ptag->curreg; nreg = ptag->nreg == 0 ? 16 : ptag->nreg; } while(size > 0) { if(nloop == 0) { ptag = (GIFTAG*)pMem; nreg = ptag->nreg == 0 ? 16 : ptag->nreg;#ifdef GSCAPTURE if( g_loggs && g_gstransnum == 19 && tempcount++ > g_gslimit ) { break; }#endif pMem+= 4; size--; if(ptag->nloop == 0 ) { if( path == 0 ) { // ffx hack if( GSHACK_FFX ) continue; } g_path[path].nloop = 0; return size; } tempreg = ptag->regs[0]; for(i = 0; i < nreg; ++i, tempreg >>= 4) { if( i == 8 ) tempreg = ptag->regs[1]; s_byRegs[path][i] = tempreg&0xf; } nloop = ptag->nloop; curreg = 0; } switch(ptag->flg) { case 0: // PACKED { for(; size > 0; size--, pMem += 4) { if( s_byRegs[path][curreg] == 0xe && (pMem[2]&0xff) >= 0x60 ) { if( (pMem[2]&0xff) < 0x64 ) s_GSHandlers[pMem[2]&0x3](pMem); } curreg++; if (nreg == curreg) { curreg = 0; if( nloop-- <= 1 ) { size--; pMem += 4; break; } } } if( nloop > 0 ) { assert(size == 0); g_path[path] = *ptag; g_path[path].nloop = nloop; g_path[path].curreg = curreg; return 0; } break; } case 1: // REGLIST { size *= 2; tempreg = ptag->regs[0]; for(i = 0; i < nreg; ++i, tempreg >>= 4) { if( i == 8 ) tempreg = ptag->regs[1]; assert( (tempreg&0xf) < 0x64 ); s_byRegs[path][i] = tempreg&0xf; } for(; size > 0; pMem+= 2, size--) { if( s_byRegs[path][curreg] >= 0x60 ) s_GSHandlers[s_byRegs[path][curreg]&3](pMem); curreg++; if (nreg == curreg) { curreg = 0; if( nloop-- <= 1 ) { size--; pMem += 2; break; } } } if( size & 1 ) pMem += 2; size /= 2; if( nloop > 0 ) { assert(size == 0); g_path[path] = *ptag; g_path[path].nloop = nloop; g_path[path].curreg = curreg; return 0; } break; } case 2: // GIF_IMAGE (FROM_VFRAM) case 3: { // simulate if( (int)size < nloop ) { g_path[path] = *ptag; g_path[path].nloop = nloop-size; return 0; } else { pMem += nloop*4; size -= nloop; nloop = 0; } break; } } if( path == 0 && ptag->eop ) { g_path[0].nloop = 0; return size; } } g_path[path] = *ptag; g_path[path].curreg = curreg; g_path[path].nloop = nloop; return size;}int gsInterrupt() {#ifdef GS_LOG GS_LOG("gsInterrupt: %8.8x\n", cpuRegs.cycle);#endif if(gif->qwc > 0) { dmaGIF(); return 0; } gif->chcr &= ~0x100; GSCSRr &= ~0xC000; //Clear FIFO stuff GSCSRr |= 0x4000; //FIFO empty psHu32(GIF_STAT)&= ~0xE00; // OPH=0 | APATH=0 psHu32(GIF_STAT)&= ~0x1F000000; // QFC=0 hwDmacIrq(DMAC_GIF); return 1;}#ifdef GSCAPTURE#define WRITINGDMA_TRANSFER(pMem, qwc) \ extern u32 g_loggs, g_gstransnum, g_gsfinalnum; \ if( !g_loggs || (g_loggs && g_gstransnum++ < g_gsfinalnum)) { \ GSgifTransfer3(pMem, qwc-GSgifTransferDummy(2, pMem, qwc)); \ } \#else#define WRITINGDMA_TRANSFER(pMem, qwc) GSgifTransfer3(pMem, qwc);#endif#define WRITERING_DMA(pMem, qwc) { \ if( CHECK_MULTIGS) { \ u8* pgsmem = GSRingBufCopy(pMem, (qwc)<<4, GS_RINGTYPE_P3); \ if( pgsmem != NULL ) { \ memcpy_amd(pgsmem, pMem, (qwc)<<4); \ \ pgsmem += (qwc)<<4; \ assert( pgsmem <= GS_RINGBUFFEREND ); \ if( pgsmem == GS_RINGBUFFEREND ) pgsmem = GS_RINGBUFFERBASE; \ InterlockedExchangePointer(&g_pGSWritePos, pgsmem); \ GSgifTransferDummy(2, pMem, qwc); \ } \ \ if( !CHECK_DUALCORE ) SetEvent(g_hGsEvent); \ } \ else { \ WRITINGDMA_TRANSFER(pMem, qwc); \ } \} \int _GIFchain() { u32 qwc = gif->qwc; u32 *pMem; if (qwc == 0) return 0; pMem = (u32*)dmaGetAddr(gif->madr); if (pMem == NULL) { //SysPrintf("NULL GIFchain\n"); return -1; } WRITERING_DMA(pMem, qwc); gif->madr+= qwc*16; gif->qwc = 0; return (qwc)*BIAS;}#define GIFchain() \ if (gif->qwc) { \ cycles+= _GIFchain(); /* guessing */ \ }int gscount = 0;static int prevcycles = 0;static u32* prevtag = NULL;void dmaGIF() { u32 *ptag; int done=0; int cycles=prevcycles; u32 id; //logtime(3); if ((psHu32(DMAC_CTRL) & 0xC) == 0xC/* && (gif->tadr == psHu32(0xe050))*/) { // GIF MFIFO return; }#ifdef GIF_LOG GIF_LOG("dmaGIF chcr = %lx, madr = %lx, qwc = %lx\n" " tadr = %lx, asr0 = %lx, asr1 = %lx\n", gif->chcr, gif->madr, gif->qwc, gif->tadr, gif->asr0, gif->asr1);#endif if (psHu32(GIF_MODE) & 0x4) { } else if (vif1Regs->mskpath3 || psHu32(GIF_MODE) & 0x1) { gif->chcr &= ~0x100; hwDmacIrq(2); return; } if ((psHu32(DMAC_CTRL) & 0xC0) == 0x80) { // STD == GIF /*gif->chcr &=~ 0x100; hwDmacIrq(2); return;*/ //SysPrintf("GS Stall Control Source = %x, Drain = %x\n", (psHu32(0xe000) >> 4) & 0x3, (psHu32(0xe000) >> 6) & 0x3); if( gif->madr >= psHu32(DMAC_STADR) ) { return; } } GSCSRr &= ~0xC000; //Clear FIFO stuff GSCSRr |= 0x8000; //FIFO full psHu32(GIF_STAT)|= 0xE00; // OPH=1 | APATH=3 psHu32(GIF_STAT)|= 0x10000000; // FQC=31, hack ;) if( prevcycles != 0 ) { assert( prevtag != NULL ); ptag = prevtag; // transfer interrupted, so continue GIFchain(); if (gif->chcr & 0x80 && ptag[0] >> 31) { //Check TIE bit of CHCR and IRQ bit of tag#ifdef GIF_LOG GIF_LOG("dmaIrq Set\n");#endif done = 1; } } // Transfer Dn_QWC from Dn_MADR to GIF if ((gif->chcr & 0xc) == 0 || gif->qwc > 0) { // Normal Mode //gscount++; GIFchain(); } else { // Chain Mode while (done == 0) { // Loop while Dn_CHCR.STR is 1 ptag = (u32*)dmaGetAddr(gif->tadr); //Set memory pointer to TADR if (ptag == NULL) { //Is ptag empty? psHu32(DMAC_STAT)|= 1<<15; //If yes, set BEIS (BUSERR) in DMAC_STAT register break; } cycles+=1; // Add 1 cycles from the QW read for the tag // Transfer dma tag if tte is set if (gif->chcr & 0x40) { //u32 temptag[4];#ifdef GIF_LOG //SysPrintf("GIF TTE: %x_%x\n", ptag[3], ptag[2]);#endif// temptag[2] = ptag[2];// temptag[3] = ptag[3];// GSgifTransfer3(temptag, 1); } gif->chcr = ( gif->chcr & 0xFFFF ) | ( (*ptag) & 0xFFFF0000 ); //Transfer upper part of tag to CHCR bits 31-15 id = (ptag[0] >> 28) & 0x7; //ID for DmaChain copied from bit 28 of the tag gif->qwc = (u16)ptag[0]; //QWC set to lower 16bits of the tag gif->madr = ptag[1]; //MADR = ADDR field#ifdef GIF_LOG GIF_LOG("dmaChain %8.8x_%8.8x size=%d, id=%d, addr=%lx\n", ptag[1], ptag[0], gif->qwc, id, gif->madr);#endif done = hwDmacSrcChainWithStack(gif, id); if (!done && (psHu32(DMAC_CTRL) & 0xC0) == 0x80) { // STD == GIF // there are still bugs, need to also check if gif->madr +16*qwc >= stadr, if not, stall if( gif->madr >= psHu32(DMAC_STADR) ) { // stalled prevcycles = cycles; prevtag = ptag; hwDmacIrq(13); return; } } GIFchain(); //Transfers the data set by the switch if ((gif->chcr & 0x80) && ptag[0] >> 31) { //Check TIE bit of CHCR and IRQ bit of tag#ifdef GIF_LOG GIF_LOG("dmaIrq Set\n");#endif //SysPrintf("GIF TIE\n"); // SysPrintf("GSdmaIrq Set\n"); done = 1; //gif->qwc = 0; break; } } } prevtag = NULL; prevcycles = 0; INT(2, cycles);}static int mfifocycles;int mfifoGIFrbTransfer() { u32 maddr = psHu32(DMAC_RBOR); int msize = psHu32(DMAC_RBSR)+16; u32 *src; /* Check if the transfer should wrap around the ring buffer */ if ((gif->madr+gif->qwc*16) > (maddr+msize)) { int s1 = (maddr+msize) - gif->madr; int s2 = gif->qwc*16 - s1; /* it does, so first copy 's1' bytes from 'addr' to 'data' */ src = (u32*)PSM(gif->madr); if (src == NULL) return -1; WRITERING_DMA(src, s1&~15); /* and second copy 's2' bytes from 'maddr' to '&data[s1]' */ src = (u32*)PSM(maddr); if (src == NULL) return -1; WRITERING_DMA(src, s2&~15); } else { /* it doesn't, so just transfer 'qwc*16' words from 'gif->madr' to GS */ src = (u32*)PSM(gif->madr); if (src == NULL) return -1; WRITERING_DMA(src, gif->qwc); } gif->madr+= gif->qwc*16; //gif->madr = psHu32(DMAC_RBOR) + (gif->madr & psHu32(DMAC_RBSR)); return 0;}int mfifoGIFchain() { u32 maddr = psHu32(DMAC_RBOR); int msize = psHu32(DMAC_RBSR)+16; u32 *pMem; /* Is QWC = 0? if so there is nothing to transfer */ if (gif->qwc == 0) return 0; if (gif->madr >= maddr && gif->madr < (maddr+msize)) { if (mfifoGIFrbTransfer() == -1) return -1; } else { pMem = (u32*)dmaGetAddr(gif->madr); if (pMem == NULL) return -1; WRITERING_DMA(pMem, gif->qwc); gif->madr+= gif->qwc*16; } mfifocycles+= (gif->qwc) * BIAS; /* guessing */ gif->qwc = 0; return 0;}void mfifoGIFtransfer(int qwc) { u32 *ptag; int id; int done = 0; mfifocycles = 0;#ifdef GIF_LOG GIF_LOG("mfifoGIFtransfer qwc=0x%x\n", qwc);#endif //if((gif->chcr & 0x100) == 0)SysPrintf("MFIFO GIF not ready %x\n", gif->chcr); while (qwc > 0 && done == 0) { // Loop while Dn_CHCR.STR is 1 gif->tadr = psHu32(DMAC_RBOR) + (gif->tadr & psHu32(DMAC_RBSR)); ptag = (u32*)dmaGetAddr(gif->tadr); id = (ptag[0] >> 28) & 0x7; gif->qwc = (u16)ptag[0]; gif->madr = ptag[1]; mfifocycles += 2;#ifdef GIF_LOG GIF_LOG("dmaChain %8.8x_%8.8x size=%d, id=%d, addr=%lx\n", ptag[1], ptag[0], gif->qwc, id, gif->madr);#endif// if(gif->chcr & 0x40)// GSgifTransfer3(ptag, 1); switch (id) { case 0: // refe gif->tadr += 16; qwc = 0; INT(11,mfifocycles); done = 1; break; case 1: // cnt gif->madr = gif->tadr + 16; qwc-= gif->qwc + 1; // Set the taddr to the next tag gif->tadr += 16 + (gif->qwc * 16); break; case 3: // ref case 4: // refs gif->tadr += 16; qwc--; break; case 7: // end gif->madr = gif->tadr + 16; gif->tadr = gif->madr + (gif->qwc * 16); qwc = 0; INT(11,mfifocycles); done = 1; break; } if (mfifoGIFchain() == -1) { break; } if ((gif->chcr & 0x80) && (ptag[0] >> 31)) {#ifdef GIF_LOG GIF_LOG("dmaIrq Set\n");#endif INT(11,mfifocycles); //SysPrintf("mfifoGIFTransfer: dmaIrq Set\n"); //qwc = 0; done = 1; } } if(done == 0 && qwc == 0)hwDmacIrq(14); /*if(qwc == 0 && gif->chcr & 0x100) { hwDmacIrq(14); return; }*/ //INT(11,cycles); //hwDmacIrq(2); }int gifMFIFOInterrupt() { mfifocycles = 0; gif->chcr &= ~0x100; hwDmacIrq(DMAC_GIF); return 1;}extern long pDsp;DWORD WINAPI VuGSThreadProc(LPVOID lpParam){ HANDLE handles[2] = { g_hGsEvent, g_hVuGSExit }; //SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); u8* writepos; u32 tag; u32 counter = 0; { int ret; HANDLE openhandles[2] = { g_hGSOpen, g_hVuGSExit }; if( WaitForMultipleObjects(2, openhandles, FALSE, INFINITE) == WAIT_OBJECT_0+1 ) { return 0; } ret = GSopen((void *)&pDsp, "PCSX2", 1); GSCSRr = 0x551B4000; // 0x55190000 //SysPrintf("gsOpen done\n"); if (ret != 0) { SysMessage (_("Error Opening GS Plugin")); return -1; } SetEvent(g_hGSDone); } //SysPrintf("Starting GS thread\n"); while(1) { if( !CHECK_DUALCORE ) { if( WaitForMultipleObjects(2, handles, FALSE, INFINITE) == WAIT_OBJECT_0+1 ) { GSclose(); return 0; } } else if( (counter++ & 0xffff) == 0 ) { if( WaitForSingleObject(g_hVuGSExit, 0) == WAIT_OBJECT_0 ) { GSclose(); return 0; } } if( g_pGSRingPos != g_pGSWritePos ) { do { writepos = *(volatile PU8*)&g_pGSWritePos; while( g_pGSRingPos != writepos ) { assert( g_pGSRingPos < GS_RINGBUFFEREND ); // process until writepos tag = *(u32*)g_pGSRingPos; switch( tag&0xffff ) { case GS_RINGTYPE_RESTART: InterlockedExchangePointer(&g_pGSRingPos, GS_RINGBUFFERBASE); if( GS_RINGBUFFERBASE == writepos ) goto ExitGS; continue; case GS_RINGTYPE_P1: GSgifTransfer1((u32*)(g_pGSRingPos+16), 0); InterlockedExchangeAdd((PLONG)&g_pGSRingPos, 16 + ((tag>>16)<<4)); break; case GS_RINGTYPE_P2: GSgifTransfer2((u32*)(g_pGSRingPos+16), tag>>16); InterlockedExchangeAdd((PLONG)&g_pGSRingPos, 16 + ((tag>>16)<<4)); break; case GS_RINGTYPE_P3: GSgifTransfer3((u32*)(g_pGSRingPos+16), tag>>16); InterlockedExchangeAdd((PLONG)&g_pGSRingPos, 16 + ((tag>>16)<<4)); break; case GS_RINGTYPE_VSYNC: GSvsync(*(int*)(g_pGSRingPos+4)); InterlockedExchangeAdd((PLONG)&g_pGSRingPos, 16); break;#ifdef GS_ENABLE_CACHING case GS_RINGTYPE_P2|GS_RINGTYPE_MEMREF: case GS_RINGTYPE_P2|GS_RINGTYPE_MEMREF|GS_RINGTYPE_MEMNOFREE: EnterCriticalSection(&g_PageAddrSection); // read again since could have changed tag = *(u32*)g_pGSRingPos; GSgifTransfer2(*(u32**)(g_pGSRingPos+4), tag>>16); if( ENABLE_GS_CACHING ) { if( !(tag & GS_RINGTYPE_MEMNOFREE) ) { GSFreeRingBuf(g_pGSRingPos); } else { // using temp memory, so release g_pGSTempReadPos = *(u8**)(g_pGSRingPos+4) + ((tag>>16)<<4); } } LeaveCriticalSection(&g_PageAddrSection);#ifdef _RINGBUF_DEBUG InterlockedExchangeAdd((PLONG)&g_pGSRingPos, 16 + ((tag>>16)<<4));#else InterlockedExchangeAdd((PLONG)&g_pGSRingPos, 16);#endif break; case GS_RINGTYPE_P3|GS_RINGTYPE_MEMREF: case GS_RINGTYPE_P3|GS_RINGTYPE_MEMREF|GS_RINGTYPE_MEMNOFREE: EnterCriticalSection(&g_PageAddrSection); // read again since could have changed tag = *(u32*)g_pGSRingPos;#ifdef _RINGBUF_DEBUG assert( memcmp(*(u32**)(g_pGSRingPos+4), g_pGSRingPos+16, (tag>>16)*16) == 0 );#endif GSgifTransfer3(*(u32**)(g_pGSRingPos+4), tag>>16); if( ENABLE_GS_CACHING ) { if( !(tag & GS_RINGTYPE_MEMNOFREE) ) { GSFreeRingBuf(g_pGSRingPos); } else { // using temp memory, so release g_pGSTempReadPos = *(u8**)(g_pGSRingPos+4) + ((tag>>16)<<4); } } LeaveCriticalSection(&g_PageAddrSection);#ifdef _RINGBUF_DEBUG InterlockedExchangeAdd((PLONG)&g_pGSRingPos, 16 + ((tag>>16)<<4));#else InterlockedExchangeAdd((PLONG)&g_pGSRingPos, 16);#endif break;#endif // end GS_ENABLE_CACHING case GS_RINGTYPE_VIFFIFO: { u64* pMem; assert( vif1ch->madr == *(u32*)(g_pGSRingPos+4) ); assert( vif1ch->qwc == (tag>>16) ); pMem = (u64*)dmaGetAddr(vif1ch->madr); if (pMem == NULL) { psHu32(DMAC_STAT)|= 1<<15; break; } GSreadFIFO2(pMem, tag>>16); // set incase read psHu64(0x5000) = pMem[2*(tag>>16)-2]; psHu64(0x5008) = pMem[2*(tag>>16)-1]; assert( vif1ch->madr == *(u32*)(g_pGSRingPos+4) ); assert( vif1ch->qwc == (tag>>16) ); tag = (tag>>16) - (cpuRegs.cycle- *(u32*)(g_pGSRingPos+8)); if( tag & 0x80000000 ) tag = 0; vif1Regs->stat&= ~0x1f000000; vif1ch->qwc = 0; INT(1, tag); InterlockedExchangeAdd((PLONG)&g_pGSRingPos, 16); break; } default: SysPrintf("VsGSThreadProc, bad packet\n"); g_pGSRingPos = g_pGSWritePos; //flushall(); } assert( g_pGSRingPos <= GS_RINGBUFFEREND ); if( g_pGSRingPos == GS_RINGBUFFEREND ) InterlockedExchangePointer(&g_pGSRingPos, GS_RINGBUFFERBASE); if( g_pGSRingPos == g_pGSWritePos ) { break; } }ExitGS: ; } while(g_pGSRingPos != *(volatile PU8*)&g_pGSWritePos); } // process vu1 } return 0;}int gsFreeze(gzFile f, int Mode) { gzfreeze(PS2MEM_GS, 0x2000); gzfreeze(&CSRw, sizeof(CSRw)); gzfreeze(g_path, sizeof(g_path)); gzfreeze(s_byRegs, sizeof(s_byRegs)); return 0;}#undef GIFchain