diff --git a/pcsx2/CDVD/CDVD.cpp b/pcsx2/CDVD/CDVD.cpp index e194e59c17..f31149bae8 100644 --- a/pcsx2/CDVD/CDVD.cpp +++ b/pcsx2/CDVD/CDVD.cpp @@ -402,6 +402,14 @@ void cdvdReloadElfInfo(wxString elfoverride) ) ); //Console.Error( "Playstation1 game discs are not supported by PCSX2." ); + + // PCSX2 currently only recognizes *.elf executables in proper PS2 format. + // To support different PSX titles in the console title and for savestates, this code bypasses all the detection, + // simply using the exe name, stripped of problematic characters. + wxString fname = elfpath.AfterLast('\\'); + wxString fname2 = fname.BeforeFirst(';'); + DiscSerial = fname2; + Console.SetTitle(DiscSerial); return; } diff --git a/pcsx2/CDVD/CdRom.cpp b/pcsx2/CDVD/CdRom.cpp index 5b6a14c11d..58b11a73b4 100644 --- a/pcsx2/CDVD/CdRom.cpp +++ b/pcsx2/CDVD/CdRom.cpp @@ -127,6 +127,8 @@ static void ReadTrack() { cdr.Prev[2] = itob(cdr.SetSector[2]); CDVD_LOG("KEY *** %x:%x:%x", cdr.Prev[0], cdr.Prev[1], cdr.Prev[2]); + if (EmuConfig.CdvdVerboseReads) + DevCon.WriteLn("CD Read Sector %x", msf_to_lsn(cdr.SetSector)); cdr.RErr = DoCDVDreadTrack(msf_to_lsn(cdr.SetSector), CDVD_MODE_2340); } @@ -912,7 +914,9 @@ void psxDma3(u32 madr, u32 bcr, u32 chcr) { case 0x11000000: case 0x11400100: if (cdr.Readed == 0) { - CDVD_LOG("*** DMA 3 *** NOT READY"); + DevCon.Warning("*** DMA 3 *** NOT READY"); + HW_DMA3_CHCR &= ~0x01000000; //hack + psxDmaInterrupt(3); //hack return; } diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index 01b5d9f966..cc8c9a4e75 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -66,7 +66,7 @@ set(pcsx2Sources IopIrq.cpp IopMem.cpp IopSio2.cpp -# Mdec.cpp + Mdec.cpp Memory.cpp MMI.cpp MTGS.cpp @@ -137,7 +137,7 @@ set(pcsx2Headers IopHw.h IopMem.h IopSio2.h -# Mdec.h + Mdec.h MTVU.h Memory.h MemoryTypes.h @@ -408,6 +408,7 @@ set(pcsx2LinuxHeaders # ps2 sources set(pcsx2ps2Sources ps2/BiosTools.cpp + ps2/pgif.cpp ps2/LegacyDmac.cpp ps2/Iop/IopHwRead.cpp ps2/Iop/IopHwWrite.cpp) @@ -416,6 +417,7 @@ set(pcsx2ps2Sources set(pcsx2ps2Headers ps2/BiosTools.h ps2/eeHwTraceLog.inl + ps2/pgif.h ps2/HwInternal.h ps2/Iop/IopHw_Internal.h) diff --git a/pcsx2/Hw.h b/pcsx2/Hw.h index 79a2e50297..9e901956dd 100644 --- a/pcsx2/Hw.h +++ b/pcsx2/Hw.h @@ -80,7 +80,9 @@ namespace EEMemoryMap static const uint SIO_Start = 0x1000F100; static const uint SIO_End = 0x1000F200; static const uint SBUS_Start = 0x1000F200; - static const uint SBUS_End = 0x1000F400; + static const uint SBUS_End = 0x1000F300; + static const uint SBUS_PS1_Start = 0x1000F300; + static const uint SBUS_PS1_End = 0x1000F400; // MCH area -- Really not sure what this area is. Information is lacking. static const uint MCH_Start = 0x1000F400; @@ -330,7 +332,7 @@ enum EERegisterAddresses SBUS_F250 = 0x1000F250, SBUS_F260 = 0x1000F260, SBUS_F300 = 0x1000F300, - SBUS_F380 = 0x1000F380, + SBUS_F380 = 0x1000F380, MCH_RICM = 0x1000F430, MCH_DRD = 0x1000F440, diff --git a/pcsx2/HwRead.cpp b/pcsx2/HwRead.cpp index d8501a4e31..a39e7bf5a4 100644 --- a/pcsx2/HwRead.cpp +++ b/pcsx2/HwRead.cpp @@ -21,6 +21,8 @@ #include "ps2/HwInternal.h" #include "ps2/eeHwTraceLog.inl" +#include "ps2/pgif.h" + using namespace R5900; static __fi void IntCHackCheck() @@ -92,8 +94,13 @@ mem32_t __fastcall _hwRead32(u32 mem) return psHu32(INTC_STAT); } - - + // todo: psx mode: this is new + if (((mem & 0x1FFFFFFF) >= EEMemoryMap::SBUS_PS1_Start) && ((mem & 0x1FFFFFFF) < EEMemoryMap::SBUS_PS1_End)) { + return PGIFr((mem & 0x1FFFFFFF)); + } + + // WARNING: this code is never executed anymore due to previous condition. + // It requires investigation of what to do. if ((mem & 0x1000ff00) == 0x1000f300) { int ret = 0; @@ -139,14 +146,21 @@ mem32_t __fastcall _hwRead32(u32 mem) switch( mem ) { case SIO_ISR: - case SBUS_F260: + case 0x1000f410: case MCH_RICM: return 0; case SBUS_F240: +#if PSX_EXTRALOGS + DevCon.Warning("Read SBUS_F240 %x ", psHu32(SBUS_F240)); +#endif return psHu32(SBUS_F240) | 0xF0000102; - + case SBUS_F260: +#if PSX_EXTRALOGS + DevCon.Warning("Read SBUS_F260 %x ", psHu32(SBUS_F260)); +#endif + return psHu32(SBUS_F260); case MCH_DRD: if( !((psHu32(MCH_RICM) >> 6) & 0xF) ) { @@ -347,6 +361,14 @@ void __fastcall _hwRead128(u32 mem, mem128_t* result ) ZeroQWC( result ); break; case 0x0F: + // todo: psx mode: this is new + if (((mem & 0x1FFFFFFF) >= EEMemoryMap::SBUS_PS1_Start) && ((mem & 0x1FFFFFFF) < EEMemoryMap::SBUS_PS1_End)) { + PGIFrQword((mem & 0x1FFFFFFF), result); + return; + } + + // WARNING: this code is never executed anymore due to previous condition. + // It requires investigation of what to do. if ((mem & 0xffffff00) == 0x1000f300) { DevCon.Warning("128bit read from %x wibble", mem); diff --git a/pcsx2/HwWrite.cpp b/pcsx2/HwWrite.cpp index 28df7b2986..390ac3b0d9 100644 --- a/pcsx2/HwWrite.cpp +++ b/pcsx2/HwWrite.cpp @@ -22,6 +22,8 @@ #include "ps2/HwInternal.h" #include "ps2/eeHwTraceLog.inl" +#include "ps2/pgif.h" + using namespace R5900; // Shift the middle 8 bits (bits 4-12) into the lower 8 bits. @@ -47,7 +49,11 @@ void __fastcall _hwWrite32( u32 mem, u32 value ) #if PSX_EXTRALOGS if ((mem & 0x1000ff00) == 0x1000f300) DevCon.Warning("32bit Write to SIF Register %x value %x", mem, value); //if ((mem & 0x1000ff00) == 0x1000f200) DevCon.Warning("Write to SIF Register %x value %x", mem, value); + + // todo: psx mode: this is new + if (((mem & 0x1FFFFFF0) == 0x1000f010) || ((mem & 0x1FFFFFF0) == 0x1000f010)) Console.WriteLn("EE ###INTC hwWR 0x%08X = 0x%08X PC=%08X ", mem, value, cpuRegs.pc); #endif + switch (page) { case 0x00: if (!rcntWrite32<0x00>(mem, value)) return; break; @@ -172,7 +178,10 @@ void __fastcall _hwWrite32( u32 mem, u32 value ) psHu32(mem) &= ~value; return; - mcase(SBUS_F240): + mcase(SBUS_F240) : +#if PSX_EXTRALOGS + DevCon.Warning("Write SBUS_F240 %x ", value); +#endif if(!(value & 0x100)) psHu32(mem) &= ~0x100; else @@ -180,9 +189,14 @@ void __fastcall _hwWrite32( u32 mem, u32 value ) return; mcase(SBUS_F260): - psHu32(mem) = 0; +#if PSX_EXTRALOGS + DevCon.Warning("Write SBUS_F260 %x ", psHu32(SBUS_F260)); +#endif + psHu32(mem) = value; return; + // TODO: psx handling is done in the default case. Keep the code until we decide if we decide which interface to use (sif2/Pgif dma) +#if 0 mcase(SBUS_F300) : psxHu32(0x1f801814) = value; /* @@ -211,6 +225,8 @@ void __fastcall _hwWrite32( u32 mem, u32 value ) mcase(SBUS_F380) : psHu32(mem) = value; return; +#endif + mcase(MCH_RICM)://MCH_RICM: x:4|SA:12|x:5|SDEV:1|SOP:4|SBC:1|SDEV:5 if ((((value >> 16) & 0xFFF) == 0x21) && (((value >> 6) & 0xF) == 1) && (((psHu32(0xf440) >> 7) & 1) == 0))//INIT & SRP=0 rdram_sdevid = 0; // if SIO repeater is cleared, reset sdevid @@ -225,6 +241,13 @@ void __fastcall _hwWrite32( u32 mem, u32 value ) mcase(DMAC_ENABLEW): if (!dmacWrite32<0x0f>(DMAC_ENABLEW, value)) return; + default: + // TODO: psx add the real address in a sbus mcase + if (((mem & 0x1FFFFFFF) >= EEMemoryMap::SBUS_PS1_Start) && ((mem & 0x1FFFFFFF) < EEMemoryMap::SBUS_PS1_End)) { + PGIFw((mem & 0x1FFFFFFF), value); + return; + } + //mcase(SIO_ISR): //mcase(0x1000f410): // Mystery Regs! No one knows!? @@ -423,9 +446,16 @@ void __fastcall _hwWrite128(u32 mem, const mem128_t* srcval) //WriteFIFO_IPUout(srcval); } - + return; + case 0x0F: + // todo: psx mode: this is new + if (((mem & 0x1FFFFFFF) >= EEMemoryMap::SBUS_PS1_Start) && ((mem & 0x1FFFFFFF) < EEMemoryMap::SBUS_PS1_End)) { + PGIFwQword((mem & 0x1FFFFFFF), (void*)srcval); + return; + } + default: break; } diff --git a/pcsx2/IopDma.cpp b/pcsx2/IopDma.cpp index 1613f88e81..ce76c41851 100644 --- a/pcsx2/IopDma.cpp +++ b/pcsx2/IopDma.cpp @@ -134,11 +134,13 @@ void spu2DMA7Irq() #ifndef DISABLE_PSX_GPU_DMAS void psxDma2(u32 madr, u32 bcr, u32 chcr) // GPU { - DevCon.Warning("SIF2 IOP CHCR = %x MADR = %x BCR = %x first 16bits %x", chcr, madr, bcr, iopMemRead16(madr)); + //DevCon.Warning("SIF2 IOP CHCR = %x MADR = %x BCR = %x first 16bits %x", chcr, madr, bcr, iopMemRead16(madr)); sif2.iop.busy = true; sif2.iop.end = false; //SIF2Dma(); - dmaSIF2(); + // todo: psxmode: dmaSIF2 appears to interface with PGPU but everything is already handled without it. + // it slows down psxmode if it's run. + //dmaSIF2(); } diff --git a/pcsx2/IopHw.cpp b/pcsx2/IopHw.cpp index 5ed32b3367..c58bfeb47b 100644 --- a/pcsx2/IopHw.cpp +++ b/pcsx2/IopHw.cpp @@ -19,6 +19,8 @@ #include "iR5900.h" #include "Sio.h" +#include "Mdec.h" +#include "ps2/pgif.h" // for pgpu reset // NOTE: Any modifications to read/write fns should also go into their const counterparts // found in iPsxHw.cpp. @@ -29,7 +31,10 @@ void psxHwReset() { memset(iopHw, 0, 0x10000); -// mdecInit(); //initialize mdec decoder + // todo: psxmode: this should be in an EE reset routine, since PGIF is on that IC + pgifInit(); + + mdecInit(); //initialize mdec decoder cdrReset(); cdvdReset(); psxRcntInit(); diff --git a/pcsx2/IopHw.h b/pcsx2/IopHw.h index d5a2e5ec89..85660755cc 100644 --- a/pcsx2/IopHw.h +++ b/pcsx2/IopHw.h @@ -18,12 +18,14 @@ #include "IopMem.h" static const u32 - HW_USB_START = 0x1f801600, - HW_USB_END = 0x1f801700, - HW_FW_START = 0x1f808400, - HW_FW_END = 0x1f808550, // end addr for FW is a guess... - HW_SPU2_START = 0x1f801c00, - HW_SPU2_END = 0x1f801e00; + HW_PS1_GPU_START = 0x1F8010A0, + HW_PS1_GPU_END = 0x1F8010B0, + HW_USB_START = 0x1f801600, + HW_USB_END = 0x1f801700, + HW_FW_START = 0x1f808400, + HW_FW_END = 0x1f808550, // end addr for FW is a guess... + HW_SPU2_START = 0x1f801c00, + HW_SPU2_END = 0x1f801e00; static const u32 HW_SSBUS_SPD_ADDR = 0x1f801000, diff --git a/pcsx2/Mdec.cpp b/pcsx2/Mdec.cpp index 73685694a9..95f6d82654 100644 --- a/pcsx2/Mdec.cpp +++ b/pcsx2/Mdec.cpp @@ -18,7 +18,7 @@ /* This code was based on the FPSE v0.08 Mdec decoder*/ -#if 0 +#include "PrecompiledHeader.h" #include #include @@ -26,6 +26,25 @@ #include "IopCommon.h" #include "Mdec.h" +struct { + u32 command; + u32 status; + u16 *rl; + int rlsize; +} mdec; + +struct config_mdec { + u32 Mdec; +}; +struct config_mdec Config; + +u32 mdecArr2[0x100000] = { 0 }; + +u32 mdecMem[0x100000]; //watherver large size. //Memory only used to get DMA data and not really for anything else. + //Sould be optimized(the funcs. that use it) to read IOP RAM direcly. +#define PSXM(x) ((uptr)mdecMem + x) + + int iq_y[DCTSIZE2],iq_uv[DCTSIZE2]; static void idct1(int *block) @@ -150,7 +169,11 @@ void idct(int *block,int k) } void mdecInit(void) { - mdec.rl = (u16*)&psxM[0x100000]; + + Config.Mdec = 1; //XXXXXXXXXXXXXXXXX 0 or 1 + + mdec.rl = (u16*)PSXM(0); + //mdec.rl = (u16*)&psxM[0x100000]; mdec.command = 0; mdec.status = 0; round_init(); @@ -158,7 +181,7 @@ void mdecInit(void) { void mdecWrite0(u32 data) { - CDR_LOG("mdec0 write %lx", data); + MDEC_LOG("mdec0 write %lx", data); mdec.command = data; if ((data&0xf5ff0000)==0x30000000) { @@ -167,7 +190,7 @@ void mdecWrite0(u32 data) { } void mdecWrite1(u32 data) { - CDR_LOG("mdec1 write %lx", data); + MDEC_LOG("mdec1 write %lx", data); if (data&0x80000000) { // mdec reset round_init(); @@ -176,35 +199,47 @@ void mdecWrite1(u32 data) { } u32 mdecRead0(void) { - CDR_LOG("mdec0 read %lx", mdec.command); + MDEC_LOG("mdec0 read %lx", mdec.command); return mdec.command; } u32 mdecRead1(void) { -#ifdef CDR_LOG - CDR_LOG("mdec1 read %lx", mdec.status); -#endif + MDEC_LOG("mdec1 read %lx", mdec.status); + return mdec.status; } void psxDma0(u32 adr, u32 bcr, u32 chcr) { int cmd = mdec.command; - int size; - CDR_LOG("DMA0 %lx %lx %lx", adr, bcr, chcr); + MDEC_LOG("DMA0 %lx %lx %lx", adr, bcr, chcr); - if (chcr!=0x01000201) return; + if (chcr != 0x01000201) return; - size = (bcr>>16)*(bcr&0xffff); - - if (cmd==0x40000001) { - u8 *p = (u8*)PSXM(adr); - iqtab_init(iq_y,p); - iqtab_init(iq_uv,p+64); + // bcr LSBs are the blocksize in words + // bcr MSBs are the number of block + int size = (bcr >> 16)*(bcr & 0xffff); + if (size < 0) { + // Need to investigate what happen if the transfer is huge + Console.Error("psxDma0 DMA transfer overflow !"); + return; } - else if ((cmd&0xf5ff0000)==0x30000000) { - mdec.rl = (u16*)PSXM(adr); + + for (int i = 0; i<(size); i++) { + *(u32*)PSXM(((i + 0) * 4)) = iopMemRead32(adr + ((i + 0) * 4)); + if (i <20) + MDEC_LOG(" data %08X %08X ", iopMemRead32((adr & 0x00FFFFFF) + (i * 4)), *(u32*)PSXM((i * 4))); + } + + + if (cmd == 0x40000001) { + u8 *p = (u8*)PSXM(0); //u8 *p = (u8*)PSXM(adr); + iqtab_init(iq_y, p); + iqtab_init(iq_uv, p + 64); + } + else if ((cmd & 0xf5ff0000) == 0x30000000) { + mdec.rl = (u16*)PSXM(0); //mdec.rl = (u16*)PSXM(adr); } HW_DMA0_CHCR &= ~0x01000000; @@ -214,14 +249,22 @@ void psxDma0(u32 adr, u32 bcr, u32 chcr) { void psxDma1(u32 adr, u32 bcr, u32 chcr) { int blk[DCTSIZE2*6]; unsigned short *image; - int size; - CDR_LOG("DMA1 %lx %lx %lx (cmd = %lx)", adr, bcr, chcr, mdec.command); + MDEC_LOG("DMA1 %lx %lx %lx (cmd = %lx)", adr, bcr, chcr, mdec.command); - if (chcr!=0x01000200) return; + if (chcr != 0x01000200) return; + // bcr LSBs are the blocksize in words + // bcr MSBs are the number of block + int size = (bcr >> 16)*(bcr & 0xffff); + int size2 = (bcr >> 16)*(bcr & 0xffff); + if (size < 0) { + // Need to investigate what happen if the transfer is huge + Console.Error("psxDma1 DMA transfer overflow !"); + return; + } + + image = (u16*)mdecArr2;//(u16*)PSXM(0); //image = (u16*)PSXM(adr); - size = (bcr>>16)*(bcr&0xffff); - image = (u16*)PSXM(adr); if (mdec.command&0x08000000) { for (;size>0;size-=(16*16)/2,image+=(16*16)) { mdec.rl = rl2blk(blk,mdec.rl); @@ -234,6 +277,12 @@ void psxDma1(u32 adr, u32 bcr, u32 chcr) { } } + for (int i = 0; i<(size2); i++) { + iopMemWrite32(((adr & 0x00FFFFFF) + (i * 4) + 0), mdecArr2[i]); + if (i <20) + MDEC_LOG(" data %08X %08X ", iopMemRead32((adr & 0x00FFFFFF) + (i * 4)), mdecArr2[i]); + } + HW_DMA1_CHCR &= ~0x01000000; psxDmaInterrupt(1); } @@ -407,15 +456,12 @@ void yuv2rgb24(int *blk,unsigned char *image) { } } -int SaveState::mdecFreeze() { - Freeze(mdec); - Freeze(iq_y); - Freeze(iq_uv); - - return 0; - -} -#endif - - - +//todo: psxmode: add mdec savestate support +//int SaveState::mdecFreeze() { +// Freeze(mdec); +// Freeze(iq_y); +// Freeze(iq_uv); +// +// return 0; +// +//} diff --git a/pcsx2/Mdec.h b/pcsx2/Mdec.h index ae6fb1a598..81556ae64c 100644 --- a/pcsx2/Mdec.h +++ b/pcsx2/Mdec.h @@ -85,14 +85,15 @@ image[n+1] = ROUND(Y); \ image[n+0] = ROUND(Y); -void mdecInit(); -void mdecWrite0(u32 data); -void mdecWrite1(u32 data); -u32 mdecRead0(); -u32 mdecRead1(); -void psxDma0(u32 madr, u32 bcr, u32 chcr); -void psxDma1(u32 madr, u32 bcr, u32 chcr); -int mdecFreeze(gzFile f, int Mode); +extern void mdecInit(); +extern void mdecWrite0(u32 data); +extern void mdecWrite1(u32 data); +extern u32 mdecRead0(); +extern u32 mdecRead1(); +extern void psxDma0(u32 madr, u32 bcr, u32 chcr); +extern void psxDma1(u32 madr, u32 bcr, u32 chcr); +//int mdecFreeze(gzFile f, int Mode); + u16* rl2blk(int *blk,u16 *mdec_rl); void iqtab_init(int *iqtab,unsigned char *iq_y); @@ -100,11 +101,4 @@ void round_init(void); void yuv2rgb24(int *blk,unsigned char *image); void yuv2rgb15(int *blk,u16 *image); -struct { - u32 command; - u32 status; - u16 *rl; - int rlsize; -} mdec; - #endif /* __MDEC_H__ */ diff --git a/pcsx2/R5900.cpp b/pcsx2/R5900.cpp index e88c7cbaa0..3b6ab593a1 100644 --- a/pcsx2/R5900.cpp +++ b/pcsx2/R5900.cpp @@ -601,7 +601,7 @@ void __fastcall eeloadHook() } } - if (!g_GameStarted && disctype == 2 && elfname == discelf) + if (!g_GameStarted && (disctype == 2 || disctype == 1) && elfname == discelf) g_GameLoading = true; } diff --git a/pcsx2/Sio.cpp b/pcsx2/Sio.cpp index a477b98ff2..c21941197d 100644 --- a/pcsx2/Sio.cpp +++ b/pcsx2/Sio.cpp @@ -136,7 +136,8 @@ void SIO_FORCEINLINE sioInterrupt() { PAD_LOG("Sio Interrupt"); sio.StatReg|= IRQ; - psxHu32(0x1070)|=0x80; + iopIntcIrq(7); //Should this be used instead of the one below? + //psxHu32(0x1070)|=0x80; } SIO_WRITE sioWriteStart(u8 data) @@ -173,22 +174,35 @@ SIO_WRITE sioWriteStart(u8 data) sioWrite8inl(data); } +int byteCnt = 0; + +//This is only for digital pad! .... a fast hacky fix for testing +//#define IS_LAST_BYTE_IN_PACKET ((byteCnt >= 3) ? 1 : 0) +//this should be done on the last byte, but it works like this fine for now... How does one know which number is the last byte anyway (or are there any more following it). +//maybe in the end a small LUT will be necessary that has the number of bytes for each command... +//On the real PS1, if the controller doesn't supply an /ACK signal after each byte but the last, the transmission falls through... but it seems it is actually the interrupt +//what 'continues' a transfer. +//On the real PS1, asserting /ACK after the last byte would cause the transfer to fail. +#define IS_LAST_BYTE_IN_PACKET ((sio.bufCount >= 3) ? 1 : 0) + SIO_WRITE sioWriteController(u8 data) { + //if (data == 0x01) byteCnt = 0; switch(sio.bufCount) { case 0: + byteCnt = 0; //hope this gets only cleared on the first byte... SIO_STAT_READY(); DEVICE_PLUGGED(); sio.buf[0] = PADstartPoll(sio.port + 1); break; - default: + default: sio.buf[sio.bufCount] = PADpoll(data); break; } - - SIO_INT(); + //Console.WriteLn( "SIO: sent = %02X From pad data = %02X bufCnt %08X ", data, sio.buf[sio.bufCount], sio.bufCount); + SIO_INT(); //Don't all commands(transfers) cause an interrupt? } SIO_WRITE sioWriteMultitap(u8 data) @@ -456,7 +470,8 @@ SIO_WRITE memcardWrite(u8 data) SIO_WRITE memcardRead(u8 data) { - //static u8 checksum_pos = 0; + /*static u8 checksum_pos = 0;*/ + // psxmode: check if memcard reads need checksum_pos as well as writes (function above!) static u8 transfer_size = 0; static bool once = false; @@ -712,8 +727,8 @@ SIO_WRITE sioWriteMemcardPSX(u8 data) break; case 1: - sio.cmd = data; - switch(data) + sio.cmd = data; + switch(data) { case 0x53: // PSX 'S'tate // haven't seen it happen yet sio.buf[1] = mcd->FLAG; @@ -729,7 +744,7 @@ SIO_WRITE sioWriteMemcardPSX(u8 data) sio.buf[4] = 0x00; break; - case 0x58: // POCKETSTATION!! Grrrr // Lots of love to the PS2DEV/ps2sdk + case 0x58: // POCKETSTATION!! Grrrr // Lots of love to the PS2DEV/ps2sdk DEVICE_UNPLUGGED(); // Check is for 0x01000 on stat siomode = SIO_DUMMY; break; @@ -754,7 +769,7 @@ SIO_WRITE sioWriteMemcardPSX(u8 data) sio.buf[6] = data; mcd->sectorAddr |= data; mcd->goodSector = !(mcd->sectorAddr > 0x3FF); - mcd->transferAddr = 128 * mcd->sectorAddr; + mcd->transferAddr = 128 * mcd->sectorAddr; break; case 6: @@ -800,7 +815,7 @@ SIO_WRITE sioWriteMemcardPSX(u8 data) sio.buf[136] = 0x5D; // (47h=Good, 4Eh=BadChecksum, FFh=BadSector) - sio.buf[137] = data == xorcheck ? 0x47 : 0x4E; + sio.buf[137] = data == xorcheck ? 0x47 : 0x4E; if(!mcd->goodSector) sio.buf[137] = 0xFF; else mcd->Write(&sio.buf[7], 0x80); siomode = SIO_DUMMY; @@ -817,8 +832,41 @@ SIO_WRITE sioWriteInfraRed(u8 data) SIO_INT(); } +//This bit-field in the STATUS register contains the (inveted) state of the /ACK linre from the Controller / MC. +//1 = /ACK_line_active_low +//Should go into Sio.h +#define ACK_INP 0x80 + +//This is named RESET_ERR in sio_internal.h. +#define CLR_INTR 0x0010 +//Set the ammount of received bytes that triggers an interrupt. +//0=1, 1=2, 2=4, 3=8 receivedBytesIntTriger = 1<< ((ctrl & RX_BYTES_INT) >>8) +#define RX_BYTES_INT 0x0300 +//Enable interrupt on TX ready and TX empty +#define TX_INT_EN 0x0400 +//Trigger interrupt after receiving several (see above) bytes. +#define RX_INT_EN 0x0800 +//Controll register: Enable the /ACK line trigerring the interrupt. +#define ACK_INT_EN 0x1000 +//Selects slot 1 or 2 +#define SLOT_NR 0x2000 + +void chkTriggerInt() { + //Conditions for triggerring an interrupt. + //this is not correct, but ... it can be fixed later + SIO_INT(); return; + if ((sio.StatReg & IRQ)) { SIO_INT(); return; } //The interrupt flag in the main INTR_STAT reg should go active on multiple occasions. Set it here for now (hack), until the correct mechanism is made. + if ((sio.CtrlReg & ACK_INT_EN) && ((sio.StatReg & TX_RDY) || (sio.StatReg & TX_EMPTY))) { SIO_INT(); return; } + if ((sio.CtrlReg & ACK_INT_EN) && (sio.StatReg & ACK_INP)) { SIO_INT(); return; } + //The following one may be incorrect. + //if ((sio.CtrlReg & RX_INT_EN) && ((byteCnt >= (1<< ((sio.CtrlReg & RX_BYTES_INT) >>8))) ? 1:0) ) { SIO_INT(); return; } + return; +} + + static void sioWrite8inl(u8 data) { +// Console.WriteLn( "SIO DATA write %02X mode %08X " , data, siomode); switch(siomode) { case SIO_START: sioWriteStart(data); break; @@ -834,12 +882,44 @@ static void sioWrite8inl(u8 data) case SIO_MEMCARD_PSX: sioWriteMemcardPSX(data); break; case SIO_DUMMY: break; }; + sio.StatReg |= RX_RDY; //Why not set the byte-received flag, when for EVERY sent byte, one is received... it's just how SPI is... + sio.StatReg |= TX_EMPTY; //The current byte *has* been sent, so it is empty. + if (IS_LAST_BYTE_IN_PACKET != 1) //The following should be set after each byte transfer but the last one. + sio.StatReg |= ACK_INP; //Signal that Controller (or MC) has brought the /ACK (Acknowledge) line active low. + + SIO_INT(); + //chkTriggerInt(); + //Console.WriteLn( "SIO0 WR DATA COMMON %02X INT_STAT= %08X IOPpc= %08X " , data, psxHu32(0x1070), psxRegs.pc); + byteCnt++; } -void sioWriteCtrl16(u16 value) +int clrAckCnt =0; + +void sioStatRead() { + +if (clrAckCnt > 1) { //This check can probably be removed... + sio.StatReg &= ~ACK_INP; //clear (goes inactive) /ACK line. +//sio.StatReg &= ~TX_RDY; +// sio.StatReg &= ~0x200; //irq + //if (byteCnt == 1) + // sio.StatReg &= ~RX_RDY; +clrAckCnt = 0; +} + //The /ACK line should go active for >2us, in a time window between 12us and 100us after each byte is sent (received by the controller). + //If that doesn't happen, the controller is considered missing. + //The /ACK line must NOT go active after the last byte in the transmission! (Otherwise some err. may happen - tested.) +if ((sio.StatReg & ACK_INP)) clrAckCnt++; + + chkTriggerInt(); + return; +} + +void sioWriteCtrl16(u16 value) { static u8 tcount[2]; +// Console.WriteLn( "SIO0 WR CTRL %02X IOPpc= %08X " , value, psxRegs.pc); + tcount[sio.port] = sio.bufCount; sio.port = (value >> 13) & 1; @@ -859,24 +939,29 @@ void sioWriteCtrl16(u16 value) psxRegs.interrupt &= ~(1<( addr, ret, true ); return ret; } - ////////////////////////////////////////////////////////////////////////////////////////// // template< typename T > @@ -126,7 +134,7 @@ static __fi T _HwRead_16or32_Page1( u32 addr ) ); u32 masked_addr = pgmsk( addr ); - T ret; + T ret = 0; // ------------------------------------------------------------------------ // Counters, 16-bit varieties! @@ -216,10 +224,23 @@ static __fi T _HwRead_16or32_Page1( u32 addr ) ret = SPU2read( addr ); else { - DbgCon.Warning( "HwRead32 from SPU2? @ 0x%08X .. What manner of trickery is this?!", addr ); + DevCon.Warning( "HwRead32 from SPU2? @ 0x%08X .. What manner of trickery is this?!", addr ); ret = psxHu32(addr); } } + // ------------------------------------------------------------------------ + // PS1 GPU access + // + else if( (masked_addr >= pgmsk(HW_PS1_GPU_START)) && (masked_addr < pgmsk(HW_PS1_GPU_END)) ) + { + // todo: psx mode: this is new + if( sizeof(T) == 2 ) + DevCon.Warning( "HwRead16 from PS1 GPU? @ 0x%08X .. What manner of trickery is this?!", addr ); + + pxAssert(sizeof(T) == 4); + + ret = psxDma2GpuR(addr); + } else { switch( masked_addr ) @@ -237,6 +258,8 @@ static __fi T _HwRead_16or32_Page1( u32 addr ) mcase(HW_SIO_STAT): ret = sio.StatReg; + sioStatRead(); + // Console.WriteLn( "SIO0 Read STAT %02X INT_STAT= %08X IOPpc= %08X " , ret, psxHu32(0x1070), psxRegs.pc); break; mcase(HW_SIO_MODE): @@ -301,51 +324,26 @@ static __fi T _HwRead_16or32_Page1( u32 addr ) break; mcase(HW_PS1_GPU_DATA) : - ret = psxHu32(addr); + ret = psxGPUr(addr); + //ret = psxHu32(addr); // old DevCon.Warning("GPU Data Read %x", ret); break; mcase(HW_PS1_GPU_STATUS) : - //ret = psxHu32(addr); - /*if (sif2.fifo.size == 0x8) psxHu32(0x1f801814) &= ~(3 << 25); - else psxHu32(0x1f801814) |= (3 << 25);*/ - /*switch ((psxHu32(HW_PS1_GPU_STATUS) >> 29) & 0x3) - { - case 0x0: - //DevCon.Warning("Set DMA Mode OFF"); - psxHu32(HW_PS1_GPU_STATUS) &= ~0x2000000; - break; - case 0x1: - //DevCon.Warning("Set DMA Mode FIFO"); - psxHu32(HW_PS1_GPU_STATUS) |= 0x2000000; - break; - case 0x2: - //DevCon.Warning("Set DMA Mode CPU->GPU"); - psxHu32(HW_PS1_GPU_STATUS) = (psxHu32(HW_PS1_GPU_STATUS) & ~0x2000000) | ((psxHu32(HW_PS1_GPU_STATUS) & 0x10000000) >> 3); - break; - case 0x3: - //DevCon.Warning("Set DMA Mode GPUREAD->CPU"); - psxHu32(HW_PS1_GPU_STATUS) = (psxHu32(HW_PS1_GPU_STATUS) & ~0x2000000) | ((psxHu32(HW_PS1_GPU_STATUS) & 0x8000000) >> 2); - break; - }*/ - ret = psxHu32(addr); //Idle & Ready to recieve command. - //psxHu32(addr) = psHu32(0x1000f300); -#if PSX_EXTRALOGS - DevCon.Warning("GPU Status Read %x Sif fifo size %x", ret, sif2.fifo.size); -#endif - //ret = -1; // fake alive GPU :p + ret = psxGPUr(addr); break; mcase (0x1f801820): // MDEC - ret = psxHu32(addr); + // ret = psxHu32(addr); // old + ret = mdecRead0(); #if PSX_EXTRALOGS DevCon.Warning("MDEC 1820 Read %x", ret); #endif break; mcase (0x1f801824): // MDEC - - ret = psxHu32(addr); + //ret = psxHu32(addr); // old + ret = mdecRead1(); #if PSX_EXTRALOGS DevCon.Warning("MDEC 1824 Read %x", ret); #endif @@ -461,12 +459,15 @@ mem32_t __fastcall iopHwRead32_Page8( u32 addr ) // 4-byte FIFO input? // The old IOP system just ignored it, so that's what we do here. I've included commented code // for treating it as a 16/32 bit write though [which is what the SIO does, for example). - mcase(HW_SIO2_FIFO): + mcase(HW_SIO2_FIFO) : //ret = sio2_fifoOut(); //ret |= sio2_fifoOut() << 8; //ret |= sio2_fifoOut() << 16; //ret |= sio2_fifoOut() << 24; //break; + DevCon.Warning("HW_SIO2_FIFO read"); + ret = psxHu32(addr); + break; default: ret = psxHu32(addr); diff --git a/pcsx2/ps2/Iop/IopHwWrite.cpp b/pcsx2/ps2/Iop/IopHwWrite.cpp index e0ee4c261e..5ef5b446df 100644 --- a/pcsx2/ps2/Iop/IopHwWrite.cpp +++ b/pcsx2/ps2/Iop/IopHwWrite.cpp @@ -19,6 +19,9 @@ #include "Sio.h" #include "CDVD/CdRom.h" +#include "ps2/pgif.h" +#include "Mdec.h" + namespace IopMemory { using namespace Internal; @@ -248,6 +251,19 @@ static __fi void _HwWrite_16or32_Page1( u32 addr, T val ) //psxHu(addr) = val; } } + // ------------------------------------------------------------------------ + // PS1 GPU access + // + else if( (masked_addr >= pgmsk(HW_PS1_GPU_START)) && (masked_addr < pgmsk(HW_PS1_GPU_END)) ) + { + // todo: psx mode: this is new + if( sizeof(T) == 2 ) + DevCon.Warning( "HwWrite16 to PS1 GPU? @ 0x%08X .. What manner of trickery is this?!", addr ); + + pxAssert(sizeof(T) == 4); + + psxDma2GpuW(addr, val); + } else { switch( masked_addr ) @@ -279,7 +295,8 @@ static __fi void _HwWrite_16or32_Page1( u32 addr, T val ) break; mcase(HW_SIO_CTRL): - sio.CtrlReg = (u16)val; + //sio.CtrlReg = (u16)val; + sioWriteCtrl16((u16)val); break; mcase(HW_SIO_BAUD): @@ -295,6 +312,10 @@ static __fi void _HwWrite_16or32_Page1( u32 addr, T val ) mcase(HW_IREG): psxHu(addr) &= val; + if ((val == 0xffffffff) ) { + psxHu32(addr) |= 1 << 2; + psxHu32(addr) |= 1 << 3; + } break; mcase(HW_IREG+2): @@ -342,16 +363,18 @@ static __fi void _HwWrite_16or32_Page1( u32 addr, T val ) // ------------------------------------------------------------------------ // - mcase(0x1f801088) : // DMA0 CHCR -- MDEC IN [ignored] - //DmaExec(0); - DevCon.Warning("MDEC IN (DMA0) Started"); //Can be disabled later, need to see when this is used. - HW_DMA0_CHCR &= ~0x01000000; + mcase(0x1f801088) : // DMA0 CHCR -- MDEC IN + // psx mode + HW_DMA0_CHCR = val; + //Console.WriteLn("MDEC WR DMA0 %08X = %08X", addr, val); + psxDma0(HW_DMA0_MADR, HW_DMA0_BCR, HW_DMA0_CHCR); break; - mcase(0x1f801098): // DMA1 CHCR -- MDEC OUT [ignored] - //DmaExec(1); - DevCon.Warning("MDEC IN (DMA1) Started"); //Can be disabled later, need to see when this is used. - HW_DMA1_CHCR &= ~0x01000000; + mcase(0x1f801098): // DMA1 CHCR -- MDEC OUT + // psx mode + HW_DMA1_CHCR = val; + //Console.WriteLn("MDEC WR DMA1 %08X = %08X", addr, val); + psxDma1(HW_DMA1_MADR, HW_DMA1_BCR, HW_DMA1_CHCR); break; mcase(0x1f8010ac): DevCon.Warning("SIF2 IOP TADR?? write"); @@ -359,6 +382,7 @@ static __fi void _HwWrite_16or32_Page1( u32 addr, T val ) break; mcase(0x1f8010a8) : // DMA2 CHCR -- GPU [ignored] + // todo: psx mode: Original mod doesn't do "psxHu(addr) = val;" psxHu(addr) = val; DmaExec(2); break; @@ -511,103 +535,20 @@ static __fi void _HwWrite_16or32_Page1( u32 addr, T val ) // mcase(HW_PS1_GPU_DATA) : - DevCon.Warning("GPUDATA Write %x", val); - /*if (val == 0x00000000) - { - psxHu32(HW_PS1_GPU_STATUS) = 0x14802000; - } - else if (val == 0x01000000) - { - DevCon.Warning("GP0 FIFO Clear"); - sif2.fifo.clear(); - } - else - {*/ - psxHu(HW_PS1_GPU_DATA) = val; // guess - WriteFifoSingleWord(); - - //} - //GPU_writeData(value); // really old code from PCSX? (rama) - break; + psxGPUw(addr, val); + break; mcase (HW_PS1_GPU_STATUS): - DevCon.Warning("GPUSTATUS Write Command %x Param %x", (u32)val >> 24, val & 0xffffff); - //psxHu(addr) = val; // guess - //psxHu(HW_PS1_GPU_STATUS) = val; - //WriteFifoSingleWord(); - // psxHu(HW_PS1_GPU_DATA) = val; // guess - // WriteFifoSingleWord(); - /*if (val == 0) //Reset - { - psxHu32(HW_PS1_GPU_STATUS) = 0x14802000; - } - else if (val == 0x10000007) //Get GPU version - { - //DevCon.Warning("Get Version"); - psxHu(HW_PS1_GPU_DATA) = 2; - } - else if ((val & 0xff000000) == 0x04000000) - { - //psxHu32(HW_PS1_GPU_STATUS) = psxHu32(HW_PS1_GPU_STATUS) &= ~0x60000000; - psxHu32(HW_PS1_GPU_STATUS) |= (val & 0x3) << 29; - switch (val & 0x3) - { - case 0x0: - //DevCon.Warning("Set DMA Mode OFF"); - psxHu32(HW_PS1_GPU_STATUS) &= ~0x2000000; - break; - case 0x1: - //DevCon.Warning("Set DMA Mode FIFO"); - psxHu32(HW_PS1_GPU_STATUS) |= 0x2000000; - break; - case 0x2: - //DevCon.Warning("Set DMA Mode CPU->GPU"); - psxHu32(HW_PS1_GPU_STATUS) = (psxHu32(HW_PS1_GPU_STATUS) & ~0x2000000) | ((psxHu32(HW_PS1_GPU_STATUS) & 0x10000000) >> 3); - break; - case 0x3: - //DevCon.Warning("Set DMA Mode GPUREAD->CPU"); - psxHu32(HW_PS1_GPU_STATUS) = (psxHu32(HW_PS1_GPU_STATUS) & ~0x2000000) | ((psxHu32(HW_PS1_GPU_STATUS) & 0x8000000) >> 2); - break; - } - - } - else if (val == 0x03000000) - { - // DevCon.Warning("Turn Display on"); - psxHu32(HW_PS1_GPU_STATUS) &= ~(1 << 23); - } - else if ((val & 0xff000000) == 0x05000000) - { - DevCon.Warning("Start display area"); - - //psxHu32(HW_PS1_GPU_STATUS) |= 0x80000000; - //psxHu32(HW_PS1_GPU_STATUS) &= ~(1 << 26); - } - else if ((val & 0xff000000) == 0x08000000) - { - //DevCon.Warning("Display Mode"); - psxHu32(HW_PS1_GPU_STATUS) &= ~0x7F4000; - psxHu32(HW_PS1_GPU_STATUS) |= (u32)(val & 0x3f) << 17; - psxHu32(HW_PS1_GPU_STATUS) |= (u32)(val & 0x40) << 10; - psxHu32(HW_PS1_GPU_STATUS) |= (u32)(val & 0x80) << 7; - //psxHu32(HW_PS1_GPU_STATUS) |= 0x80000000; - //psxHu32(HW_PS1_GPU_STATUS) &= ~(1 << 26); - } - else - { - //DevCon.Warning("Unknown GP1 Command"); - }*/ - //GPU_writeStatus(value); // really old code from PCSX? (rama) - break; + psxGPUw(addr, val); + psxHu(addr) = val; // guess + break; mcase (0x1f801820): // MDEC - DevCon.Warning("MDEX 1820 Write %x", val); psxHu(addr) = val; // guess - //mdecWrite0(value); // really old code from PCSX? (rama) - break; + mdecWrite0(val); + break; mcase (0x1f801824): // MDEC - DevCon.Warning("MDEX 1824 Write %x", val); psxHu(addr) = val; // guess - //mdecWrite1(value); // really old code from PCSX? (rama) - break; + mdecWrite1(val); + break; // ------------------------------------------------------------------------ diff --git a/pcsx2/ps2/pgif.cpp b/pcsx2/ps2/pgif.cpp new file mode 100644 index 0000000000..ec51e07e00 --- /dev/null +++ b/pcsx2/ps2/pgif.cpp @@ -0,0 +1,1121 @@ +/* PCSX2 - PS2 Emulator for PCs +* Copyright (C) 2016-2016 PCSX2 Dev Team +* Copyright (C) 2016 Wisi +* +* 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 "ps2/Iop/IopHw_Internal.h" +#include "ps2/HwInternal.h" +#include "ps2/pgif.h" + +#define psxHu(mem) (*(u32 *)&iopHw[(mem)&0xffff]) + + +//using namespace Internal; + +int doAnyIopLs = 0; + + + +//Debug printf: +#define REG_LOG 0 +#define LOG_PGPU_DMA 0 + + + +//Constants here control code that is either not certainly correct or may affect compatibility: + +//Default=1. Enables changing the data FIFO/DMA direction bit in the Status register by the PGIF Ctrl register. +#define STAT_DIR_BY_IF_CTRL 1 +//Specifies which bits (29 and 30) from the "Set DMA direction" command to copy to the PGPU Status reg. +//Default(works better)=0x40000000, but makes more sense to be =0x20000000 or =0x60000000. +//Status reg. bits 30:29: DMA Direction (0=Off, 1=FIFO, 2=CPUtoGP0, 3=GPUREADtoCPU) ;GPUSTAT.29-30 +#define STAT_DIR_BY_CMD_BITS 0x40000000 +//#define STAT_DIR_BY_CMD_BITS 0x60000000 + +//Enables interrupt on the GP0(1Fh) - Interrupt Request (IRQ1) command. Default=0. +#define PGPU_IRQ1_ENABLE 0 +//Default=1. +#define PREVENT_IRQ_ON_NORM_DMA_TO_GPU 1 + +//When intercepted, GP1() commands are acted-upon and the their effects are emulated by this code, besides PS1DRV. +//This seems to be what the PGIF does, but disabling it (=0) doesn't break too much stuff either. +//Default=0. Do intercept when IOP code writes (sends) to FIFO the CMD. +#define CMD_INTERCEPT_AT_WR 0 +//Default=1. Do intercept when PS1DRV reads from FIFO the CMD. +#define CMD_INTERCEPT_AT_RD 1 + +//PGIF_DAT_RB_LEAVE_FREE - How many elements of the FIFO buffer to leave free in DMA. +//Can be 0 and no faults are observed. +//As the buffer has 32 elements, and normal DMA reads are usually done in 4 qwords (16 words), +//this must be less than 16, otherwise the PS1DRV will never read from the FIFO. +//At one point (in Linked-List DMA), PS1DRV will expect at least a certain number of elements, that is sent as argument to the func. +#define PGIF_DAT_RB_LEAVE_FREE 1 + +//Default=0. If =1, additional modification to the Status register (0x1F801814) is not done by the PGIF emulator. +//It is unknown if the PGIF emulates it, or all that's necessary is done by PS1DRV, which should have(?) complete write control over it. +#define SKIP_UPD_PGPU_STAT 0 + + + +//NOTES (TODO): +/* +- Change this file's name to PGIF.cpp and the corresponding header. +- 8 and 16 bit access to the PGPU regs is not emulated... is it ever used? Emulating it would be tricky. + + +------ + +Much of the code is ("very") unoptimized, because it is a bit cleaner and more complete this way. + +All the PS1 GPU info comes from psx-spx: http://problemkaputt.de/psx-spx.htm + + +*/ + +//Registers 0x00 - 0x40 are similar, in that they all serve as immediate-response registers to commands GP0(E1) - GP0(E5). +//PGPU_STAT 0x1000F300 The GP1 - Status register, whgich PS1DRV writes (emulates) for the IOP to read. +#define PGPU_STAT 0x1000F300 +//PGIF_1-PGIF_4 - "immediate response registers" - hold the return values for commands that require immediate response. +//They correspond to GP0() E2-E5 commands. +#define PGIF_1 0x1000F310 +#define PGIF_2 0x1000F320 +#define PGIF_3 0x1000F330 +#define PGIF_4 0x1000F340 +//PGIF_CTRL 0x1000F380 Main register for PGIF status info & control. +#define PGIF_CTRL 0x1000F380 +//PGPU_CMD_FIFO FIFO buffer for GPU GP1 (CMD reg) CMDs IOP->EE only (unknown if reverse direction is possible). +#define PGPU_CMD_FIFO 0x1000F3C0 +//PGPU_DAT_FIFO FIFO buffer for GPU GP0 (DATA reg) IOP->EE, but also EE->IOP. Direction is controlled by reg. 0x80/bit4 (most likely). +//Official name is "GFIFO", according to PS1DRV. +#define PGPU_DAT_FIFO 0x1000F3E0 +//Registers 0x50 - 0x70, 0x90 - 0xB0, 0xD0, 0xF0 are not known to be used for anything. +/*the following three are from the homebrew PS2SDK, but their names are somewhat misleading: +#define PGIF_GPU_STAT (0x1000F300) +#define PGIF_CFIFO_STAT (0x1000F380) +#define PGIF_CFIFO_DATA (0x1000F3C0)*/ + +//Bit-fields definitions for the PGPU Status register: +#define PGPU_STAT_IRQ1 0x01000000 + +//Specifies the bits in the PGIF Control (0xf380) register that are not writable from EE side, by the PS1DRV. +#define PGIF_CTRL_RO_MASK 0x00000000 + + +#define PGPU_DMA_MADR 0x1F8010A0 +#define PGPU_DMA_BCR 0x1F8010A4 +#define PGPU_DMA_CHCR 0x1F8010A8 +//is there a tadr? +#define PGPU_DMA_TADR 0x1F8010AC + +#define DMA_START_BUSY 0x01000000 +#define DMA_TRIGGER 0x10000000 +//write to peripheral +#define DMA_DIR_FROM_RAM 0x1 +#define DMA_LINKED_LIST 0x00000400 +#define DMA_LL_END_CODE 0x00FFFFFF + + + +//Local variables for emulation: + +static u32 PGpuStatReg = 0; //Read-only to IOP +//Hold the values of the immediate-response registers. +u32 pgif1reg = 0; +u32 pgif2reg = 0; +u32 pgif3reg = 0; +u32 pgif4reg = 0; + +static u32 PGifCtrlReg = 0; //PGifIfStat +static u32 PGifCtrlRegOld = 0; +static u32 PGpuDataReg = 0; //Read-only to IOP + +u32 lastGpuCmd = 0; +u32 lastGpuData = 0; + +//int clrBufCnt = 0; + + +#if 0 +u32 pgpuDmaMadr = 0; +u32 pgpuDmaBcr = 0; +u32 pgpuDmaChcr = 0; +u32 pgpuDmaTadr = 0; +#else +#define pgpuDmaMadr HW_DMA2_MADR +#define pgpuDmaBcr HW_DMA2_BCR +#define pgpuDmaChcr HW_DMA2_CHCR +//u32 pgpuDmaChcr = 0; +#define pgpuDmaTadr HW_DMA2_TADR +#endif + +//Internal flags: +int PgpuDmaLlAct = 0; +int PgpuDmaNrActToGpu = 0; +int PgpuDmaNrActToIop = 0; + +u32 pgpuDmaTrAddr = 0; +u32 wCnt = 0; // +u32 wordNr = 0; //total number of words +u32 nextAddr = 0; +u32 dntPrt = 0; + +u32 nrWordsN = 0; //total number of words in Normal DMA +u32 crWordN = 0; //current word number in Normal DMA +u32 addrNdma = 0; + + + +void fillFifoOnDrain(void); + +void drainPgpuDmaLl(void); +void drainPgpuDmaNrToGpu(void); +void drainPgpuDmaNrToIop(void); + +//u32 getUpdPgifCtrlReg(void); + + + +//Generic FIFO-related: +struct ringBuf_t +{ + u32 *buf; + int size; + int count; + int head; + int tail; +}; + +void ringBufPut(struct ringBuf_t *rb, u32 *data) +{ + if (rb->count < rb->size) { //there is available space + *(rb->buf + rb->head) = *data; + if ((++(rb->head)) >= rb->size) + rb->head = 0; //wrap back when the end is reached + rb->count++; + } else { + Console.WriteLn("############################# PGIF FIFO overflow! sz= %X", rb->size); + } +} + +void ringBufGet(struct ringBuf_t *rb, u32 *data) +{ + if (rb->count > 0) { //there is available data + *data = *(rb->buf + rb->tail); + if ((++(rb->tail)) >= rb->size) + rb->tail = 0; //wrap back when the end is reached + rb->count--; + } else { + Console.WriteLn("############################# PGIF FIFO underflow! sz= %X", rb->size); + } +} + +void ringBufClear(struct ringBuf_t *rb) +{ + rb->head = 0; + rb->tail = 0; + rb->count = 0; + memset(rb->buf, 0, rb->size * sizeof(u32)); + return; +} + + +//Ring buffers definition and initialization: +//Command (GP1) FIFO, size= 0x8 words: +#define PGIF_CMD_RB_SZ 0x8 +struct ringBuf_t pgifCmdRbC; //Ring buffer control variables. +u32 pgiffCmdRbD[PGIF_CMD_RB_SZ] = {0}; //Ring buffer data. + +//Data (GP0) FIFO - the so-called (in PS1DRV) "GFIFO", (real) size= 0x20 words: +//#define PGIF_DAT_RB_SZ 0x20 +//FIXED: Values 0xD54 and higher cause PS logo to disappair. +//Using small (the real) FIFO size, disturbs MDEC video (and other stuff), +//because the MDEC does DMA instantly, while this emulation drains the FIFO, +//only when the PS1DRV gets data from it, which depends on IOP-EE sync, among other things. +//The reson it works in the real hardware, is that the MDEC DMA would run in the pauses of GPU DMA, +//thus the GPU DMA would never get data, MDEC hasn't yet written to RAM yet (too early). +#define PGIF_DAT_RB_SZ 0x20000 +struct ringBuf_t pgifDatRbC; //Ring buffer control variables. +u32 pgiffDatRbD[PGIF_DAT_RB_SZ] = {0}; //Ring buffer data. + + + +//TODO: Make this be called by IopHw.cpp / psxHwReset()... but maybe it should be called by the EE reset func, +//given that the PGIF is in the EE ASIC, on the other side of the SBUS. +void pgifInit() +{ + pgifCmdRbC.buf = pgiffCmdRbD; + pgifCmdRbC.size = PGIF_CMD_RB_SZ; + ringBufClear(&pgifCmdRbC); + pgifDatRbC.buf = pgiffDatRbD; + pgifDatRbC.size = PGIF_DAT_RB_SZ; + ringBufClear(&pgifDatRbC); + + PGpuStatReg = 0; + pgif1reg = 0; + pgif2reg = 0; + pgif3reg = 0; + pgif4reg = 0; + PGifCtrlReg = 0; + PGpuDataReg = 0; + + + pgpuDmaMadr = 0; + pgpuDmaBcr = 0; + pgpuDmaChcr = 0; + pgpuDmaTadr = 0; + + PgpuDmaLlAct = 0; + PgpuDmaNrActToGpu = 0; + PgpuDmaNrActToIop = 0; + + pgpuDmaTrAddr = 0; + wCnt = 0; // + wordNr = 0; //total number of words + nextAddr = 0; + + nrWordsN = 0; //total number of words in Normal DMA + crWordN = 0; //current word number in Normal DMA + addrNdma = 0; + + return; +} + +//Basically resetting the PS1 GPU. +void pgifReset() +{ + //pgifInit(); + //return; + + ringBufClear(&pgifDatRbC); + ringBufClear(&pgifCmdRbC); + + PGpuStatReg = 0; + pgif1reg = 0; + pgif2reg = 0; + pgif3reg = 0; + pgif4reg = 0; + PGifCtrlReg = 0; + PGpuDataReg = 0; + + //pgpuDmaMadr = 0; + //pgpuDmaBcr = 0; + //pgpuDmaChcr = 0; + //pgpuDmaTadr = 0; + + //PgpuDmaLlAct = 0; + //PgpuDmaNrActToGpu = 0; + //PgpuDmaNrActToIop = 0; + + //pgpuDmaTrAddr = 0; + //wCnt = 0; // + //wordNr = 0; //total number of words + //nextAddr = 0; + + //nrWordsN = 0; //total number of words in Normal DMA + //crWordN = 0; //current word number in Normal DMA + //addrNdma = 0; + + return; +} + +//------ + +//Interrupt-related (IOP, EE and DMA): + +void triggerPgifInt(int subCause) +{ //For the PGIF on EE side. + // Console.WriteLn("TRIGGERRING PGIF IRQ! %08X %08X ", psHu32(INTC_STAT), psHu32(INTC_MASK)); + //Left-overs from tests: + //iopCycleEE = -1; //0000; + //iopBreak = 0; + //--cpuTestINTCInts(); + //--cpuSetEvent(); + hwIntcIrq(15); + cpuSetEvent(); + return; +} + +void getIrqCmd(u32 data) +{ //For the IOP GPU. This is triggered by the GP0(1Fh) - Interrupt Request (IRQ1) command. +//This may break stuff, because it doesn't detect whether this is really a GP0() command or data... +//WARNIG: Consider disabling this. +#if PGPU_IRQ1_ENABLE == 1 + if ((data & 0xFF000000) == 0x1F000000) { + iopIntcIrq(1); + PGpuStatReg |= PGPU_STAT_IRQ1; + } +#endif +} + +void ackGpuIrq() +{ //Acknowledge for the IOP GPU interrupt. + //This is a "null"-function in PS1DRV... maybe because hardware takes care of it... but does any software use it? + PGpuStatReg &= ~PGPU_STAT_IRQ1; +} + +void pgpuDmaIntr(int trigDma) +{ //For the IOP GPU DMA channel. +//trigDma: 1=normal,ToGPU; 2=normal,FromGPU, 3=LinkedList +#if PREVENT_IRQ_ON_NORM_DMA_TO_GPU == 1 + if (trigDma != 1) //Interrupt on ToGPU DMA breaks some games. TODO: Why? +#endif + psxDmaInterrupt(2); +} + + +//Pass-through & intercepting functions: + +u32 immRespHndl(u32 cmd, u32 data) +{ //Handles the GP1(10h) command, that requires immediate responce. + //The data argument is the old data of the register (shouldn't be critical what it contains). + //Descriptions here are taken directly from psx-spx. + switch ((cmd & 0xFF)) { + case 0x00: + case 0x01: + break; //Returns Nothing (old value in GPUREAD remains unchanged) + case 0x02: + data = pgif1reg & 0x001FFFFF; + break; //Read Texture Window setting ;GP0(E2h) ;20bit/MSBs=Nothing + case 0x03: + data = pgif2reg & 0x001FFFFF; + break; //Read Draw area top left ;GP0(E3h) ;20bit/MSBs=Nothing + case 0x04: + data = pgif3reg & 0x001FFFFF; + break; //Read Draw area bottom right ;GP0(E4h) ;20bit/MSBs=Nothing + case 0x05: + data = pgif4reg & 0x003FFFFF; + break; //Read Draw offset ;GP0(E5h) ;22bit + case 0x06: + break; //Returns Nothing (old value in GPUREAD remains unchanged) + case 0x07: + data = 0x2; + break; //Read GPU Type (usually 2) See "GPU Versions" notes below + case 0x08: + data = 0; + break; //Unknown (Returns 00000000h) + //default: //Returns Nothing (old value in GPUREAD remains unchanged) + } + //TODO: Is the PS2 "PS1 GPU" really a "version 2"-GPU??? + return data; +} + +void ckhCmdSetPgif(u32 cmd) +{ //Check GP1() command and configure PGIF accordingly. + //Maybe all below are hanled by PS1DRV and this functions is only wasting time and memory and cusing errors...but that doesn't seem to be the case. + u32 cmdNr = ((cmd >> 24) & 0xFF) & 0x3F; //Higher commands are known to mirror lower commands. + u32 data = 0x00000000; + switch (cmdNr) { + case 0x00: + ringBufClear(&pgifDatRbC); + // ringBufClear(&pgifCmdRbC); //Makes more sense to leave it disabled - if the PS1 program has written a reset command, it would be the last in the FIFO anyway. + ackGpuIrq(); + // pgpu1reg = 0; pgpu2reg = 0; pgpu3reg = 0; pgpu4reg = 0; + //More stuff to clear here.... + pgifReset(); + ringBufPut(&pgifDatRbC, &data); //write a reset command for the PS1DRV to get. + break; + case 0x01: //Reset Command Buffer (only?)... This would actually mean the Data (GP0()) register buffer. + //ringBufClear(&pgifCmdRbC); //as above. + ringBufClear(&pgifDatRbC); + break; + case 0x02: //Acknowledge GPU IRQ + ackGpuIrq(); + break; + case 0x04: //DMA Direction / Data Request + //The PS1DRV doesn't seem to take care of this... + //Removing the following two, "damages" the PS logo, so they are probably necessary. + //The PS1 program will not start DMA GPU->IOP (only) if this is missing. + PGpuStatReg &= ~STAT_DIR_BY_CMD_BITS; //TODO: Which bits should be controlled here? + PGpuStatReg |= (cmd << 29) & STAT_DIR_BY_CMD_BITS; //0x60000000; //0x20000000 bit 30 is handled by PS1DRV + //PGpuStatReg &= ~0x60000000; //TODO: Which bits should be controlled here? + //PGpuStatReg |= (cmd <<29) & 0x40000000; //0x60000000; //0x20000000 bit 30 is handled by PS1DRV + //0-1 DMA Direction (0=Off, 1=FIFO, 2=CPUtoGP0, 3=GPUREADtoCPU) ;GPUSTAT.29-30 + //2-23 Not used (zero) + drainPgpuDmaLl(); //See comment in this function. + break; + } +} + +u32 getUpdPgpuStatReg() +{ +#if SKIP_UPD_PGPU_STAT == 1 + return PGpuStatReg; +#endif + //The code below is not necessary! ... but not always...why? + //Does PS1DRV does set bit 27 - reverse direction (VRAM->CPU) in some conditions... but not always...TODO: Find why. + //The PS1 program pools this bit to determine if there is data in the FIFO, it can get. Then starts DMA to get it. + //The PS1 program will not send the DMA direction command (GP1(04h)) and will not start DMA until this bit (27) becomes set. + //Maybe move this to the PGifCtrlReg write handler...? + PGpuStatReg &= ~0x08000000; //VRAM->CPU data ready + PGpuStatReg &= ~0x02000000; //common "ready" flag + //PGpuStatReg |= 0x10000000; //GPU is ready to receive DMA data. PS1DRV seems to set and clear this bit, + //but it is cleared in the next code anyway, because it seems safer. + //PGpuStatReg |= 0x04000000; //GPU is ready to receive GP0() cmds. + //Setting this bit is best left to the PS1DRV (PS logo polygons start missing otherwise). +#if GPU_STAT_DIR_BY_IF_CTRL == 0 + if ((PGifCtrlReg & 0x10) || (PGpuStatReg & 0x20000000)) { //Reverse direction bit GPU->CPU. +#else + PGpuStatReg &= ~0x20000000; + if (PGifCtrlReg & 0x10) { + PGpuStatReg |= 0x20000000; +#endif + //Or maybe bit 29 of the GPU STATUS reg, should copy the state of bit 4 of the PGIF_CTRL reg? + PGpuStatReg &= ~0x10000000; //If a read (GPI->CPU) is to be done, then obviously, the GPU can't receive data. + PGpuStatReg &= ~0x04000000; //Same with receiving GP0() commands. + if (pgifDatRbC.count > 0) //Data FIFO has data. + PGpuStatReg |= 0x08000000; //Could skip the second check, but that could cause buffer underflow on direct read. + //Should the above be set only when there are at least 16 words of data? + } + + switch ((PGpuStatReg >> 29) & 0x3) { + case 0x00: + break; // When GP1(04h)=0 ---> Always zero (0) + case 0x01: // When GP1(04h)=1 ---> FIFO State (0=Full, 1=Not Full) + if (pgifDatRbC.count < (pgifDatRbC.size - 0)) + PGpuStatReg |= 0x02000000; //Not (yet) full, so set the bit. + break; + case 0x02: // When GP1(04h)=2 ---> Same as GPUSTAT.28 + if (PGpuStatReg & 0x10000000) //Ready to receive DMA Block (0=No, 1=Ready) ;GP0(...) ;via GP0 + PGpuStatReg |= 0x02000000; + break; + case 0x03: //When GP1(04h)=3 ---> Same as GPUSTAT.27 + if (PGpuStatReg & 0x08000000) //Ready to send VRAM to CPU (0=No, 1=Ready) ;GP0(C0h) ;via GPUREAD + PGpuStatReg |= 0x02000000; + break; + } + return PGpuStatReg; +} + +//This returns "correct" element-in-FIFO count, even if extremely large buffer is used. +int getDatRbC_Count() +{ + return std::min(pgifDatRbC.count, 0x1F); +} + +void setPgifCtrlReg(u32 data) +{ + PGifCtrlReg = (data & (~PGIF_CTRL_RO_MASK)) | (PGifCtrlReg & PGIF_CTRL_RO_MASK); +} + +u32 getUpdPgifCtrlReg() +{ + PGifCtrlReg &= 0xFFFFE0FF; + if (pgifCmdRbC.count == 0) //fake that DATA FIFO is empty, so that PS1DRV processes any commands on the GP1() register first. + PGifCtrlReg |= ((getDatRbC_Count() & 0x1F) << 8) & 0x1F00; + PGifCtrlReg &= 0xFFF8FFFF; + PGifCtrlReg |= ((pgifCmdRbC.count & 0x07) << 16) & 0x70000; + PGifCtrlReg &= 0x7FFFFFFF; //bit 31= (DMA?) transfer active. Has to be clear, for PS1DRV to continue. - + //The above is most likely the 9-th bit of the FIFO counter. It is 0x20 in size most likly... though it could be 0x2F. + //It doesn't look as it is (ever) used as an overflow flag. It is usually added to the main part of the counter as a 9-th bit + //and then comaprisons to some number of elements are made. + //Always fill FIFO (for drain by PS1DRV) at least 0x10 words - it will refuse to drain it. + if (pgifCmdRbC.count == 0) //fake that DATA FIFO is empty, so that PS1DRV processes any commands on the GP1() register first. + PGifCtrlReg |= ((getDatRbC_Count() & 0x20) << 26) & 0x80000000; + //if ((PgpuDmaLlAct == 1) || (PgpuDmaNrActToGpu == 1)) + // PGifCtrlReg |= 0x80000000; + return PGifCtrlReg; +} + + +void cmdRingBufGet(u32 *data) +{ //Used by PGIF/PS1DRV. + ringBufGet(&pgifCmdRbC, data); +#if CMD_INTERCEPT_AT_RD == 1 + ckhCmdSetPgif(*data); //Let's assume that the PGIF hardware processes GPU commands just when the EE reads them from its FIFO. +#endif +} + +void datRingBufGet(u32 *data) +{ // + //if (pgifDatRbC.count >= (PGIF_DAT_RB_SZ -1)) { Console.WriteLn( "PGIF DAT BUF FULL %08X ", pgifDatRbC.count); + if (pgifDatRbC.count > 0) { + ringBufGet(&pgifDatRbC, data); + getIrqCmd(*data); //Checks if an IRQ CMD passes through here a triggers IRQ when it does. + } else { //Return last value in register: +#if 0 + if ((((lastGpuCmd >>24) & 0xFF) & 0x30) == 0x10) { //Handle the immediate-response command. + //Cmds 0x10 - 0x1F are all 0x10, and 0x40-0xFF mirror 0x00-0x3F. + PGpuDataReg = immRespHndl(lastGpuCmd, PGpuDataReg); //Would it be better if this is done when data is read at datRingBufGet()? + } +#endif + *data = PGpuDataReg; + } +} + + + +//PS1 GPU registers I/O handlers: + +void psxGPUw(int addr, u32 data) +{ +#if REG_LOG == 1 + Console.WriteLn("PGPU write 0x%08X = 0x%08X", addr, data); +#endif + + if (addr == HW_PS1_GPU_DATA) { + //PGpuDataReg = data; //just until it is certain that nothing uses the old value in the latch. + lastGpuData = data; + ringBufPut(&pgifDatRbC, &data); + } else if (addr == HW_PS1_GPU_STATUS) { //Command register GP1 (write-only). + lastGpuCmd = data; + if ((((data >> 24) & 0xFF) & 0x30) == 0x10) { //Handle the immediate-response command. + //Cmds 0x10 - 0x1F are all 0x10, and 0x40-0xFF mirror 0x00-0x3F. + PGpuDataReg = immRespHndl(lastGpuCmd, PGpuDataReg); //Would it be better if this is done when data is read at datRingBufGet()? + } else { //Should the "immediate-response" command not be sent to the GPU (PS1DRV)..? + triggerPgifInt(0); + ringBufPut(&pgifCmdRbC, &data); + } +#if CMD_INTERCEPT_AT_WR == 1 + ckhCmdSetPgif(data); +#endif + } +} + +u32 psxGPUr(int addr) +{ + u32 data = 0; + if (addr == HW_PS1_GPU_DATA) { + //data = PGpuDataReg; + //The following is commented-out, because it seems more logical to return GP1() CMD responces only when Data FIFO is empty. + //if ((lastGpuCmd & 0xFF000000) == 0x10000000) { + // data = immRespHndl(lastGpuCmd, PGpuDataReg); + // lastGpuCmd = 0; + //} else { + datRingBufGet(&data); + //} + } else if (addr == HW_PS1_GPU_STATUS) { + data = getUpdPgpuStatReg(); + } +#if REG_LOG == 1 + if (addr != HW_PS1_GPU_STATUS) //prints way too many times (pools) + Console.WriteLn("PGPU read 0x%08X = 0x%08X EEpc= %08X ", addr, data, cpuRegs.pc); +#endif + return data; +} + +//PGIF registers I/O handlers: + +void PGIFw(int addr, u32 data) +{ +#if REG_LOG == 1 + Console.WriteLn("PGIF write 0x%08X = 0x%08X 0x%08X EEpc= %08X IOPpc= %08X ", addr, data, getUpdPgifCtrlReg(), cpuRegs.pc, psxRegs.pc); +#endif + + if (addr == PGPU_CMD_FIFO) { + Console.WriteLn("PGIF CMD FIFO write by EE (SHOULDN'T HAPPEN) 0x%08X = 0x%08X", addr, data); + } else if (addr == PGPU_DAT_FIFO) { //reverse write - mind the direction set by reg f380 + //PGpuDataReg = data; + ringBufPut(&pgifDatRbC, &data); + // Console.WriteLn( "\n\r PGIF REVERSE !!! DATA write 0x%08X = 0x%08X IF_CTRL= %08X PGPU_STAT= %08X CmdCnt 0x%X \n\r", addr, data, getUpdPgifCtrlReg(), getUpdPgpuStatReg(), pgifCmdRbC.count); + drainPgpuDmaNrToIop(); + } else if (addr == PGPU_STAT) { + PGpuStatReg = data; //Should all bits be writable? + } else if (addr == PGIF_CTRL) { //PGIF config + setPgifCtrlReg(data); + PGifCtrlRegOld = PGifCtrlReg; //data + fillFifoOnDrain(); //Now this checks the 0x8 bit of the PGIF_CTRL reg, so it here too, + //so that it gets updated immediately once it is set. + } else if (addr == PGIF_1) { + pgif1reg = data; //GP0(E2h) + } else if (addr == PGIF_2) { + pgif2reg = data; + } else if (addr == PGIF_3) { + pgif3reg = data; + } else if (addr == PGIF_4) { + pgif4reg = data; + } +} + +u32 PGIFr(int addr) +{ + u32 data = 0; + if (addr == PGPU_CMD_FIFO) { + cmdRingBufGet(&data); + } else if (addr == PGPU_DAT_FIFO) { + fillFifoOnDrain(); + datRingBufGet(&data); + } else if (addr == PGPU_STAT) { + data = PGpuStatReg; + } else if (addr == PGIF_CTRL) { //PGIF config + data = getUpdPgifCtrlReg(); + } else if (addr == PGIF_1) { + data = pgif1reg; //GP0(E2h) + } else if (addr == PGIF_2) { + data = pgif2reg; + } else if (addr == PGIF_3) { + data = pgif3reg; + } else if (addr == PGIF_4) { + data = pgif4reg; + } + +#if REG_LOG == 1 + if (addr != PGPU_DAT_FIFO) + if ((addr != PGIF_CTRL) || ((addr == PGIF_CTRL) && (getUpdPgifCtrlReg() != data))) + if (addr != PGPU_STAT) + Console.WriteLn("PGIF read %08X = %08X GPU_ST %08X IF_STAT %08X IOPpc %08X EEpc= %08X ", addr, data, getUpdPgpuStatReg(), getUpdPgifCtrlReg(), psxRegs.pc, cpuRegs.pc); +#endif + return data; +} + +void PGIFrQword(u32 addr, void *dat) +{ + u32 *data = (u32 *)dat; + + if (addr == PGPU_CMD_FIFO) { //shouldn't happen + Console.WriteLn("PGIF QW CMD read =ERR!"); + } else if (addr == PGPU_DAT_FIFO) { + fillFifoOnDrain(); + datRingBufGet(data + 0); + datRingBufGet(data + 1); + datRingBufGet(data + 2); + datRingBufGet(data + 3); + + fillFifoOnDrain(); + } else { + Console.WriteLn("PGIF QWord Read from add %08X ERR - shouldnt happen!", addr); + } +#if REG_LOG == 1 + if (addr != PGPU_DAT_FIFO) + Console.WriteLn("PGIF QW read 0x%08X = %08X %08X %08X %08X ", addr, *(u32 *)(data + 0), *(u32 *)(data + 1), *(u32 *)(data + 2), *(u32 *)(data + 3)); +#endif +} + +void PGIFwQword(u32 addr, void *dat) +{ + u32 *data = (u32 *)dat; + DevCon.Warning("WARNING PGIF WRITE BY PS1DRV ! - NOT KNOWN TO EVER BE DONE!"); + Console.WriteLn("PGIF QW write 0x%08X = 0x%08X %08X %08X %08X ", addr, *(u32 *)(data + 0), *(u32 *)(data + 1), *(u32 *)(data + 2), *(u32 *)(data + 3)); + if (addr == PGPU_CMD_FIFO) { //shouldn't happen + Console.WriteLn("PGIF QW CMD write =ERR!"); + } else if (addr == PGPU_DAT_FIFO) { + ringBufPut(&pgifDatRbC, (u32 *)(data + 0)); + ringBufPut(&pgifDatRbC, (u32 *)(data + 1)); + ringBufPut(&pgifDatRbC, (u32 *)(data + 2)); + ringBufPut(&pgifDatRbC, (u32 *)(data + 3)); + drainPgpuDmaNrToIop(); + } +} + + + +//DMA-emulating functions: + +//This function is used as a global FIFO-DMA-fill function and both Linked-list normal DMA call it, +//regardless which DMA mode runs. +void fillFifoOnDrain() +{ + if ((getUpdPgifCtrlReg() & 0x8) == 0) + return; //Skip filing FIFO with elements, if PS1DRV hasn't set this bit. + //Maybe it could be cleared once FIFO has data? + //This bit could be an enabler for the DREQ (EE->IOP for SIF2=PGIF) line. The FIFO is filled only when this is set + //and the IOP acknowledges it. + drainPgpuDmaLl(); + drainPgpuDmaNrToGpu(); + //This is done here in a loop, rather than recursively in each function, because a very large buffer causes stack oveflow. + while ((pgifDatRbC.count < ((pgifDatRbC.size) - PGIF_DAT_RB_LEAVE_FREE)) && ((PgpuDmaNrActToGpu != 0) || (PgpuDmaLlAct != 0))) { + drainPgpuDmaLl(); + drainPgpuDmaNrToGpu(); + } + //Flags' names: PgpuDmaLlAct is for linked-list, PgpuDmaNrActToGpu is for normal IOP->GPU + if ((PgpuDmaLlAct == 1) || (PgpuDmaNrActToGpu == 1)) + if (PgpuDmaNrActToIop != 1) + PGifCtrlReg &= ~0x8; + //Clear bit as DMA will be run - normally it should be cleared only once the current request finishes, but the IOP won't notice anything anyway. + //WARNING: Check if GPU->IOP DMA uses this flag, and if it does (it would make sense for it to use it as well), + //then only clear it here if the mode is not GPU->IOP. +} + +void drainPgpuDmaLl() +{ + u32 data = 0; + if (PgpuDmaLlAct == 0) + return; //process only on transfer active + + //Some games (Breath of Fire 3 US) set-up linked-list DMA, but don't immediatelly have the list correctly set-up, + //so the result is that this function loops indefinitely, because of some links pointing back to themselves, forming a loop. + //The solution is to only start DMA once the GP1(04h) - DMA Direction / Data Request command has been set to the value 0x2 (CPU->GPU DMA) + //This func will be caled by this command as well. + //if ((PGpuStatReg & 0x60000000) != 0x40000000) return; + + if (pgifDatRbC.count >= ((pgifDatRbC.size) - PGIF_DAT_RB_LEAVE_FREE)) + return; //buffer full - needs to be drained first. + + if ((nextAddr == 0x000C8000) || (nextAddr == 0x0) || (nextAddr == 0x3)) + dntPrt++; + // if (dntPrt <5) + //Console.WriteLn( "LLDMAFILL nxtAdr %08X trAddr %08X wordNr %08X wCnt %08X IF_CTRL= %08X ", nextAddr, pgpuDmaTrAddr, wordNr, wCnt, getUpdPgifCtrlReg(), cpuRegs.pc, psxRegs.pc); + + if (wCnt >= wordNr) { //Reached end of sequence, or the beginning of a new one + //if ((nextAddr == DMA_LL_END_CODE) || (nextAddr == 0x00000000)) { //complete + //why does BoF3 US get to a link pointing to address 0?? + if (nextAddr == DMA_LL_END_CODE) { + dntPrt = 0; + PgpuDmaLlAct = 0; + pgpuDmaMadr = 0x00FFFFFF; + //pgpuDmaMadr = nextAddr; //The END_CODE should go in the MADR reg. + //But instead copy the whole header code, each time one is encuntered - makes a bit more sense. + pgpuDmaChcr &= ~DMA_TRIGGER; //DMA is no longer active (in transfer) + pgpuDmaChcr &= ~DMA_START_BUSY; //Transfer completed => clear busy flag + pgpuDmaIntr(3); +#if LOG_PGPU_DMA == 1 + Console.WriteLn("PGPU DMA LINKED LIST FINISHED "); +#endif + } else { + data = iopMemRead32(nextAddr); + // Console.WriteLn( "PGPU LL DMA HDR= %08X ", data); + //data = psxHu32(nextAddr); //The (next) current header word. + //Console.WriteLn( "PGPU LL DMA data= %08X ", data); + pgpuDmaMadr = data; //Copy the header word in MADR. + //It is unknown whether the whole word should go here, but because this is a "Memory ADdress Register", leave only the address. + pgpuDmaMadr &= 0x00FFFFFF; // correct or not? + pgpuDmaTrAddr = nextAddr + 4; //start of data section of packet + wCnt = 0; + wordNr = (data >> 24) & 0xFF; // Current length of packet and future address of header word. + nextAddr = data & 0x00FFFFFF; + } + } else { + //if (wCnt < wordNr) { + data = iopMemRead32(pgpuDmaTrAddr); //0; //psxHu32(pgpuDmaTrAddr); //Get the word of the packet from IOP RAM. +#if LOG_PGPU_DMA == 1 +// Console.WriteLn( "PGPU LL DMA data= %08X addr %08X ", data, pgpuDmaTrAddr); +#endif + ringBufPut(&pgifDatRbC, &data); + pgpuDmaTrAddr += 4; + wCnt++; + } +} + +void drainPgpuDmaNrToGpu() +{ + u32 data = 0; + + if (PgpuDmaNrActToGpu == 0) + return; //process only on transfer active + if (pgifDatRbC.count >= ((pgifDatRbC.size) - PGIF_DAT_RB_LEAVE_FREE)) + return; //buffer full - needs to be drained first. + + if (crWordN < nrWordsN) { + data = iopMemRead32(addrNdma); //Get the word of the packet from IOP RAM. +#if LOG_PGPU_DMA == 1 +// Console.WriteLn( "PGPU NRM DMA data= %08X addr %08X ", data, pgpuDmaTrAddr); +#endif + ringBufPut(&pgifDatRbC, &data); + addrNdma += 4; + crWordN++; + //The following two are common to all DMA channels, and in future, some global hanler may be used... or not. + //pgpuDmaMadr += 4; //It is unclear if this should be done exactly so... + pgpuDmaMadr = 4 + pgpuDmaMadr; + if ((crWordN % (pgpuDmaBcr & 0xFFFF)) == 0) //Because this is actually the "next word number", it can be compared to the size of the block. //End of block reached. + if (pgpuDmaBcr >= 0x10000) + pgpuDmaBcr = pgpuDmaBcr - 0x10000; //pgpuDmaBcr -= 0x10000; + //This is very inoptimized... + } + if (crWordN >= nrWordsN) { //Reached end of sequence = complete + PgpuDmaNrActToGpu = 0; + pgpuDmaChcr &= ~DMA_TRIGGER; //DMA is no longer active (in transfer) + pgpuDmaChcr &= ~DMA_START_BUSY; //Transfer completed => clear busy flag + pgpuDmaIntr(1); +#if LOG_PGPU_DMA == 1 + Console.WriteLn("PGPU DMA Norm IOP->GPU FINISHED "); +#endif + } +} + +void drainPgpuDmaNrToIop() +{ + u32 data; + if (PgpuDmaNrActToIop == 0) + return; + if (pgifDatRbC.count <= 0) + return; + /* if (crWordN >= nrWordsN) { + PgpuDmaNrActToIop = 0; + pgpuDmaChcr &= ~DMA_TRIGGER; //DMA is no longer active (in transfer) + pgpuDmaChcr &= ~DMA_START_BUSY; //Transfer completed => clear busy flag + psxDmaInterrupt(2); + } else { */ + if (crWordN < nrWordsN) { //This is not the best way, but... is there another? + ringBufGet(&pgifDatRbC, &data); + iopMemWrite32(addrNdma, data); + pgpuDmaMadr = 4 + pgpuDmaMadr; //addrNdma += 4; + crWordN++; + //The following two are common to all DMA channels, and in future, some global hanler may be used... or not. + pgpuDmaMadr += 4; //It is unclear if this should be done exactly so... + if ((crWordN % (pgpuDmaBcr & 0xFFFF)) == 0) //Because this is actually the "next word number", it can be compared to the size of the block. //End of block reached. + if (pgpuDmaBcr >= 0x10000) + pgpuDmaBcr = pgpuDmaBcr - 0x10000; // pgpuDmaBcr -= 0x10000; + //This is very unoptimized... better use separate block counter. + } + if (crWordN >= nrWordsN) { + PgpuDmaNrActToIop = 0; + pgpuDmaChcr &= ~DMA_TRIGGER; //DMA is no longer active (in transfer) + pgpuDmaChcr &= ~DMA_START_BUSY; //Transfer completed => clear busy flag + pgpuDmaIntr(2); +#if LOG_PGPU_DMA == 1 + Console.WriteLn("PGPU DMA GPU->IOP FINISHED "); +#endif + } + + if (pgifDatRbC.count > 0) + drainPgpuDmaNrToIop(); +} + + +void processPgpuDma() +{ //For normal mode & linked list. + if ((pgpuDmaChcr & DMA_START_BUSY) == 0) + return; //not really neessary in this case. + pgpuDmaChcr |= DMA_TRIGGER; //DMA is active (in transfer) +#if LOG_PGPU_DMA == 1 + Console.WriteLn("PGPU DMA EXEC CHCR %08X BCR %08X MADR %08X ", pgpuDmaChcr, pgpuDmaBcr, pgpuDmaMadr); +#endif + if (pgpuDmaChcr & DMA_LINKED_LIST) { +#if LOG_PGPU_DMA == 1 + Console.WriteLn("PGPU DMA LINKED LIST WARNING!!! dir= %d ", (pgpuDmaChcr & 1)); //for (i=0;i<0x0FFFFFFF;i++) {} +#endif + if (pgpuDmaChcr & DMA_DIR_FROM_RAM) { //Linked-list only supported on direction to GPU. + PgpuDmaLlAct = 1; + //for the very start, the address of the first word should come for the address, stored at MADR + nextAddr = (pgpuDmaMadr & 0x1FFFFFFF); //The address in IOP RAM where to load the first header word from + //u32 data = iopMemRead32(nextAddr); + //Console.WriteLn( "PGPU LL DMA data= %08X ", data); + + wCnt = 0; //current word + wordNr = 0; //total number of words +#if LOG_PGPU_DMA == 1 + Console.WriteLn("LL DMA FILL "); +#endif + fillFifoOnDrain(); //drainPgpuDmaLl(); //fill a single word in fifo now, because otherwise PS1DRV won't know that there is aything in it and it wouldn't know that a transfer is pending. + return; + } else { + Console.WriteLn("ERR: Linked list GPU DMA TO IOP!"); + return; + } + } +#if LOG_PGPU_DMA == 1 + Console.WriteLn("WARNING! NORMAL DMA TO EE "); //for (i=0;i<0x1FFFFFFF;i++) {} +#endif + crWordN = 0; + addrNdma = pgpuDmaMadr & 0x1FFFFFFF; + nrWordsN = ((pgpuDmaBcr & 0xFFFF) * ((pgpuDmaBcr >> 16) & 0xFFFF)); + + if (pgpuDmaChcr & DMA_DIR_FROM_RAM) { //to peripheral + PgpuDmaNrActToGpu = 1; + fillFifoOnDrain(); //drainPgpuDmaNrToGpu(); + } else { +#if LOG_PGPU_DMA == 1 + Console.WriteLn(" NORMAL DMA TO IOP "); +#endif + PgpuDmaNrActToIop = 1; + drainPgpuDmaNrToIop(); + } +} + + +//DMA registers I/O: + +u32 psxDma2GpuR(u32 addr) +{ + u32 data = 0; + addr &= 0x1FFFFFFF; + if (addr == PGPU_DMA_MADR) { + data = pgpuDmaMadr; + } else if (addr == PGPU_DMA_BCR) { + data = pgpuDmaBcr; + } else if (addr == PGPU_DMA_CHCR) { + if ((PgpuDmaLlAct != 1) && (PgpuDmaNrActToGpu != 1) && (PgpuDmaNrActToIop != 1)) + pgpuDmaChcr &= ~0x01000000; + data = pgpuDmaChcr; + //When using the memory for registers, rather than the local variables (see the definition of pgpuDmaChcr), + //bit 0x01000000 gets set again, very soon after transfer ended(by the game) and then when the game pools it to detect dma completion, + //it times-out. the above condition works around that... but that is an indicator that st. is wrong. -how could the game reset it so soon? + //maybe it just writes to it twice...? or maybe it comes as a high 16-bit write... - todo: find the writer. + } else if (addr == PGPU_DMA_TADR) { + data = pgpuDmaTadr; + Console.WriteLn("PGPU DMA read TADR !!! "); + } + if (addr != PGPU_DMA_CHCR) + Console.WriteLn("PGPU DMA read 0x%08X = 0x%08X", addr, data); + return data; +} + +void psxDma2GpuW(u32 addr, u32 data) +{ +#if LOG_PGPU_DMA == 1 + Console.WriteLn("PGPU DMA write 0x%08X = 0x%08X", addr, data); +#endif + addr &= 0x1FFFFFFF; + if (addr == PGPU_DMA_MADR) { + pgpuDmaMadr = (data & 0x00FFFFFF); + } else if (addr == PGPU_DMA_BCR) { + pgpuDmaBcr = data; + } else if (addr == PGPU_DMA_CHCR) { + pgpuDmaChcr = data; + if (pgpuDmaChcr & DMA_START_BUSY) { + processPgpuDma(); + } + } else if (addr == PGPU_DMA_TADR) { + pgpuDmaTadr = data; + Console.WriteLn("PGPU DMA read TADR !!! "); + } +} + + + +//######################################################################################################### +//Other debug & test code, not directly relatedto the PGIF: + +//------------------------- +//debug print from IOP: + +//BIOS POST +void ps12PostOut(u32 mem, u8 value) +{ + Console.WriteLn("XXXXXXXXXXXXXXXXXXXXXXX PS12 POST = %02X ", value); + //iopMemWrite32(0x0000B9B0, 1); //a bit of high-level emulation to enable DUART TTY in PS1 kernel...which doesn't work +} + + +void psDuartW(u32 mem, u8 value) +{ + Console.WriteLn("XXXXXXXXXXXXXXXXXXXXXXX DUART reg %08X = %02X >%c<", mem, value, value); + if ((mem & 0x1FFFFFFF) == 0x1F802023) { + Console.WriteLn("XXXXXXXXXXXXXXXXXXXXXXX DUART A = %02X >%c<", value, value); + } else if ((mem & 0x1FFFFFFF) == 0x1F80202B) { + Console.WriteLn("XXXXXXXXXXXXXXXXXXXXXXX DUART B = %02X >%c<", value, value); + } +} + +u8 psExp2R8(u32 mem) +{ + if ((mem & 0x1FFFFFFF) == 0x1F802000) { + return 0x18; + } + return 0xFF; +} + +int flgint = 0; + +char strb[200] = {0}; +void kernelTTYFileDescrWrite(u32 mem, u32 data) +{ + //data is an address actually + char stra[100]; + int i; + // if (mem == ) { + for (i = 0; i < 100; i++) + stra[i] = iopMemRead8(data + i); + stra[99] = 0; + + //Console.WriteLn("Wr to PS1 kernel file TTY descr. data = %08X at addr %08X >%s< ", data, iopMemRead32(data), stra); + + //Console.WriteLn("%s", stra); + + + if ((strlen(strb) + strlen(stra)) >= 200) { + //Console.WriteLn("%s", strb); + strb[0] = 0; + } else { + + strcat(strb, stra); + if ((stra[strlen(stra) - 1] == 0xA) || (stra[strlen(stra) - 1] == 0xD) || (stra[strlen(stra) - 2] == 0xA) || (stra[strlen(stra) - 2] == 0xD)) { + Console.WriteLn(">%s<", strb); + if (strcmp(strb, "VSync") == 0) + flgint ^= 1; + if (strncmp(strb, "Execute", 5) == 0) { + Console.WriteLn(" ###EXecute was just printed####"); + doAnyIopLs = 1; + } + strb[0] = 0; + } + } + //} +} + + +//0x80051080 +u32 getIntTmrKReg(u32 mem, u32 data) +{ + u32 outd = data; + if (PGifCtrlReg != 0) { + Console.WriteLn("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); + //outd = 0; + outd = 0x7FFFFFFF; + } + return outd; +} + +void HPCoS_print(u32 mem, u32 data) +{ + int i; + if ((mem & 0x1FFFFFFF) == 0x1103A0) { + //Console.WriteLn("HPCoS BufPnt = %08X ", data); + char str[201]; + //if (data == 0) { + for (i = 0; i < 200; i++) { + str[i] = iopMemRead8(0x117328 + i); + if (str[i] == 0) + break; + } + str[i] = 0; + // Console.WriteLn(" FFFFFFFFFFFFFFFFFFFFFFFFFFFF >%s< ", str); + //} + } //else if ((mem & 0x1FFFFFFF) == 0x117328) { + // Console.WriteLn("HPCoS BufAdr = %08X >%c<", data, data); + + //} +} + +int startPrntPc = 0; +void anyIopLS(u32 addr, u32 data, int Wr) +{ + if (doAnyIopLs == 0) + return; +//#define nrRuns 0xFFFFFF +#define nrRuns 0xFFFFFFF + //if ((startPrntPc > nrRuns) && (startPrntPc < (nrRuns + 2000))) + // Console.WriteLn( "MEM %d 0x%08X = 0x%08X IOPpc= %08X ", Wr, addr, data, psxRegs.pc); + //else + if (startPrntPc <= (nrRuns + 2000)) + startPrntPc++; +} + + +void dma6_OTClear() +{ + if ((psxHu(0x1f8010e8) & 0x01000000) == 0) + return; + u32 madr = psxHu(0x1f8010e0); + u32 blkSz = psxHu(0x1f8010e4) & 0xFFFF; + //u32 blkCnt = (psxHu(0x1f8010e4) >> 16) & 0xFFFF; + u32 addrPnt = madr; + u32 pntCnt = 0; + Console.WriteLn("DMA6 OT clr madr %08X bcr %08X chcr %08X", psxHu(0x1f8010e0), psxHu(0x1f8010e4), psxHu(0x1f8010e8)); + + while (pntCnt < blkSz) { //Very unoptimized... + iopMemWrite32(addrPnt, ((addrPnt - 4) & 0x00FFFFFF)); + addrPnt -= 4; + pntCnt++; + } + iopMemWrite32((addrPnt + 4), 0x00FFFFFF); + + //psxHu(0x1f8010e0) + //psxHu(0x1f8010e4) + + psxHu(0x1f8010e8) &= ~0x01000000; + psxDmaInterrupt(6); +} + +//NOTE: use iopPhysMem(madr) for getting the physical (local) address of IOP RAM stuff. ... does this account for the complete 16MB range? diff --git a/pcsx2/ps2/pgif.h b/pcsx2/ps2/pgif.h new file mode 100644 index 0000000000..5472369206 --- /dev/null +++ b/pcsx2/ps2/pgif.h @@ -0,0 +1,40 @@ +/* PCSX2 - PS2 Emulator for PCs +* Copyright (C) 2016-2016 PCSX2 Dev Team +* Copyright (C) 2016 Wisi +* +* 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 . +*/ + +void pgifInit(void); + +extern void psxGPUw(int, u32); +extern u32 psxGPUr(int); + +extern void PGIFw(int, u32); +extern u32 PGIFr(int); + +extern void PGIFwQword(u32 addr, void *); +extern void PGIFrQword(u32 addr, void *); + +extern u32 psxDma2GpuR(u32 addr); +extern void psxDma2GpuW(u32 addr, u32 data); + + +extern void ps12PostOut(u32 mem, u8 value); +extern void psDuartW(u32 mem, u8 value); +extern u8 psExp2R8(u32 mem); +extern void kernelTTYFileDescrWrite(u32 mem, u32 data); +extern u32 getIntTmrKReg(u32 mem, u32 data); +extern void testInt(void); +extern void HPCoS_print(u32 mem, u32 data); +extern void anyIopLS(u32 addr, u32 data, int Wr); +extern void dma6_OTClear(void); \ No newline at end of file diff --git a/pcsx2/windows/VCprojects/pcsx2.vcxproj b/pcsx2/windows/VCprojects/pcsx2.vcxproj index c46fd0e040..f92af37cb8 100644 --- a/pcsx2/windows/VCprojects/pcsx2.vcxproj +++ b/pcsx2/windows/VCprojects/pcsx2.vcxproj @@ -188,6 +188,7 @@ true + @@ -195,6 +196,7 @@ Create + @@ -443,8 +445,10 @@ + + @@ -625,4 +629,4 @@ - + \ No newline at end of file diff --git a/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters b/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters index 5b44d679e4..4dfc850747 100644 --- a/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters +++ b/pcsx2/windows/VCprojects/pcsx2.vcxproj.filters @@ -874,6 +874,12 @@ System\Ps2\Iop + + System\Ps2 + + + System\Ps2\Iop + @@ -1308,6 +1314,12 @@ System\Ps2\Iop + + System\Ps2 + + + System\Ps2\Iop +