From 1146175648b9e2d5350fb9b5fb83afe32081d781 Mon Sep 17 00:00:00 2001 From: RedPanda4552 Date: Wed, 26 Oct 2022 14:30:09 -0400 Subject: [PATCH] SIO: Separate SIO2 from SIO0, reimplement memcard protocol [SAVEVERSION+] Fixes memcard issues in MGS3, Shining Force EXA, and others which do 4 sector reads --- pcsx2/CMakeLists.txt | 7 +- pcsx2/Frontend/HostSettings.cpp | 7 +- pcsx2/IopDma.cpp | 71 +- pcsx2/IopDma.h | 4 + pcsx2/IopHw.cpp | 5 +- pcsx2/IopSio2.cpp | 271 ---- pcsx2/IopSio2.h | 91 -- pcsx2/MemoryCardProtocol.cpp | 501 ++++++ pcsx2/MemoryCardProtocol.h | 64 + pcsx2/MultitapProtocol.cpp | 89 ++ pcsx2/MultitapProtocol.h | 43 + pcsx2/PAD/Gamepad.h | 3 +- pcsx2/PAD/Host/PAD.cpp | 9 +- pcsx2/PAD/Host/PAD.h | 3 +- pcsx2/PAD/Host/StateManagement.cpp | 13 +- pcsx2/PAD/Host/StateManagement.h | 5 +- pcsx2/PAD/Linux/PAD.cpp | 9 +- pcsx2/PAD/Linux/state_management.cpp | 13 +- pcsx2/PAD/Linux/state_management.h | 5 +- pcsx2/PAD/Windows/PAD.cpp | 17 +- pcsx2/R3000A.cpp | 22 +- pcsx2/Recording/InputRecording.cpp | 31 +- pcsx2/Recording/InputRecording.h | 6 +- pcsx2/SaveState.h | 2 +- pcsx2/Sio.cpp | 1846 +++++++++++----------- pcsx2/Sio.h | 188 ++- pcsx2/SioTypes.h | 161 ++ pcsx2/VMManager.cpp | 8 +- pcsx2/gui/AppCoreThread.cpp | 2 +- pcsx2/gui/Panels/MemoryCardListPanel.cpp | 2 +- pcsx2/pcsx2.vcxproj | 7 +- pcsx2/pcsx2.vcxproj.filters | 21 +- pcsx2/pcsx2core.vcxproj | 8 +- pcsx2/pcsx2core.vcxproj.filters | 24 +- pcsx2/ps2/Iop/IopHwRead.cpp | 155 +- pcsx2/ps2/Iop/IopHwWrite.cpp | 146 +- pcsx2/sio_internal.h | 104 -- 37 files changed, 2311 insertions(+), 1652 deletions(-) delete mode 100644 pcsx2/IopSio2.cpp delete mode 100644 pcsx2/IopSio2.h create mode 100644 pcsx2/MemoryCardProtocol.cpp create mode 100644 pcsx2/MemoryCardProtocol.h create mode 100644 pcsx2/MultitapProtocol.cpp create mode 100644 pcsx2/MultitapProtocol.h create mode 100644 pcsx2/SioTypes.h delete mode 100644 pcsx2/sio_internal.h diff --git a/pcsx2/CMakeLists.txt b/pcsx2/CMakeLists.txt index bda21b3471..b13260bb8a 100644 --- a/pcsx2/CMakeLists.txt +++ b/pcsx2/CMakeLists.txt @@ -141,16 +141,17 @@ set(pcsx2Sources IopHw.cpp IopIrq.cpp IopMem.cpp - IopSio2.cpp PINE.cpp Mdec.cpp Memory.cpp MemoryCardFile.cpp MemoryCardFolder.cpp + MemoryCardProtocol.cpp MMI.cpp MTGS.cpp MTVU.cpp MultipartFileReader.cpp + MultitapProtocol.cpp Patch.cpp Patch_Memory.cpp Pcsx2Config.cpp @@ -217,14 +218,15 @@ set(pcsx2Headers IopGte.h IopHw.h IopMem.h - IopSio2.h PINE.h Mdec.h MTVU.h Memory.h MemoryCardFile.h MemoryCardFolder.h + MemoryCardProtocol.h MemoryTypes.h + MultitapProtocol.h Patch.h PCSX2Base.h PerformanceMetrics.h @@ -239,7 +241,6 @@ set(pcsx2Headers Sif.h SingleRegisterTypes.h Sio.h - sio_internal.h SPR.h SysForwardDefs.h System.h diff --git a/pcsx2/Frontend/HostSettings.cpp b/pcsx2/Frontend/HostSettings.cpp index bbfc91005e..c99fce06cf 100644 --- a/pcsx2/Frontend/HostSettings.cpp +++ b/pcsx2/Frontend/HostSettings.cpp @@ -240,12 +240,7 @@ void Host::Internal::UpdateEmuFolders() { FileMcd_EmuClose(); FileMcd_EmuOpen(); - - for (u32 port = 0; port < 2; port++) - { - for (u32 slot = 0; slot < 4; slot++) - SetForceMcdEjectTimeoutNow(port, slot); - } + AutoEject::SetAll(); } if (EmuFolders::Textures != old_textures_directory) diff --git a/pcsx2/IopDma.cpp b/pcsx2/IopDma.cpp index 1e7912f7c8..28fa1b36db 100644 --- a/pcsx2/IopDma.cpp +++ b/pcsx2/IopDma.cpp @@ -20,6 +20,7 @@ #include "IopCounters.h" #include "IopHw.h" #include "IopDma.h" +#include "Sio.h" #include "Sif.h" #include "DEV9/DEV9.h" @@ -224,4 +225,72 @@ void psxDma10(u32 madr, u32 bcr, u32 chcr) SIF1Dma(); } -/* psxDma11 & psxDma 12 are in IopSio2.cpp, along with the appropriate interrupt functions. */ +void psxDma11(u32 madr, u32 bcr, u32 chcr) +{ + unsigned int i, j; + int size = (bcr >> 16) * (bcr & 0xffff); + PSXDMA_LOG("*** DMA 11 - SIO2 in *** %lx addr = %lx size = %lx", chcr, madr, bcr); + // Set dmaBlockSize, so SIO2 knows to count based on the DMA block rather than SEND3 length. + // When SEND3 is written, SIO2 will automatically reset this to zero. + sio2.dmaBlockSize = (bcr & 0xffff) * 4; + + if (chcr != 0x01000201) + { + return; + } + + for (i = 0; i < (bcr >> 16); i++) + { + for (j = 0; j < ((bcr & 0xFFFF) * 4); j++) + { + const u8 data = iopMemRead8(madr); + sio2.Write(data); + madr++; + } + } + + HW_DMA11_MADR = madr; + PSX_INT(IopEvt_Dma11, (size >> 2)); +} + +void psxDMA11Interrupt() +{ + if (HW_DMA11_CHCR & 0x01000000) + { + HW_DMA11_CHCR &= ~0x01000000; + psxDmaInterrupt2(4); + } +} + +void psxDma12(u32 madr, u32 bcr, u32 chcr) +{ + int size = ((bcr >> 16) * (bcr & 0xFFFF)) * 4; + PSXDMA_LOG("*** DMA 12 - SIO2 out *** %lx addr = %lx size = %lx", chcr, madr, size); + + if (chcr != 0x41000200) + { + return; + } + + bcr = size; + + while (bcr > 0) + { + const u8 data = sio2.Read(); + iopMemWrite8(madr, data); + bcr--; + madr++; + } + + HW_DMA12_MADR = madr; + PSX_INT(IopEvt_Dma12, (size >> 2)); +} + +void psxDMA12Interrupt() +{ + if (HW_DMA12_CHCR & 0x01000000) + { + HW_DMA12_CHCR &= ~0x01000000; + psxDmaInterrupt2(5); + } +} diff --git a/pcsx2/IopDma.h b/pcsx2/IopDma.h index 5a2122df3a..6427ab4220 100644 --- a/pcsx2/IopDma.h +++ b/pcsx2/IopDma.h @@ -23,9 +23,13 @@ extern void psxDma7(u32 madr, u32 bcr, u32 chcr); extern void psxDma8(u32 madr, u32 bcr, u32 chcr); extern void psxDma9(u32 madr, u32 bcr, u32 chcr); extern void psxDma10(u32 madr, u32 bcr, u32 chcr); +extern void psxDma11(u32 madr, u32 bcr, u32 chcr); +extern void psxDma12(u32 madr, u32 bcr, u32 chcr); extern int psxDma4Interrupt(); extern int psxDma7Interrupt(); +extern void psxDMA11Interrupt(); +extern void psxDMA12Interrupt(); extern void dev9Interrupt(); extern void dev9Irq(int cycles); extern void usbInterrupt(); diff --git a/pcsx2/IopHw.cpp b/pcsx2/IopHw.cpp index 17d564ebc1..88e9ecd063 100644 --- a/pcsx2/IopHw.cpp +++ b/pcsx2/IopHw.cpp @@ -21,7 +21,6 @@ #include "iR5900.h" #include "Sio.h" #include "Mdec.h" -#include "IopSio2.h" #include "IopCounters.h" #include "IopHw.h" #include "IopDma.h" @@ -42,8 +41,8 @@ void psxHwReset() { cdrReset(); cdvdReset(); psxRcntInit(); - sioInit(); - //sio2Reset(); + sio0.FullReset(); + sio2.FullReset(); } __fi u8 psxHw4Read8(u32 add) diff --git a/pcsx2/IopSio2.cpp b/pcsx2/IopSio2.cpp deleted file mode 100644 index ec61547507..0000000000 --- a/pcsx2/IopSio2.cpp +++ /dev/null @@ -1,271 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 PCSX2 Dev Team - * - * PCSX2 is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - - -#include "PrecompiledHeader.h" -#include "Common.h" - -#include "Sio.h" -#include "sio_internal.h" -#include "IopSio2.h" -#include "IopHw.h" -#include "IopDma.h" - -sio2Struct sio2; - -/* -w [8268]=0x3bc sio2_start/sio2man -r [8270] padman_start/padman - padman->7480[00]=bit4; - padman->7480[13]=bit5; - packetExchange(&703F8); -w [8268]|=0x0C; -........ -w [8268]|=0x01; - -only recv2 & dataout influences padman -*/ - -// 0xBF808200,0xBF808204,0xBF808208,0xBF80820C, -// 0xBF808210,0xBF808214,0xBF808218,0xBF80821C, packet->sendArray3 -// 0xBF808220,0xBF808224,0xBF808228,0xBF80822C, call12/13_s/getparams -// 0xBF808230,0xBF808234,0xBF808238,0xBF80823C, - -// 0xBF808240,0xBF808248,0xBF808250,0xBF808258, packet->sendArray1/call_7/8 -// 0xBF808244,0xBF80824C,0xBF808254,0xBF80825C, packet->sendArray2/call_9/10 - -// 0xBF808260, serial data/fifo in/out s/get8260_datain/out packet->sendbuf(nomem!) -// 0xBF808268, ctrl s/get8268_ctrl - -// 0xBF80826C, packet->recv1/2/3 get826C_recv1, get8270_recv2, get8274_recv3 -// 0xBF808270,0xBF808274, - -// 0xBF808278,0xBF80827C, s/get8278, s/get827C -// 0xBF808280 interrupt related s/get8280_intr - - -void sio2Reset() { - DevCon.WriteLn( "Sio2 Reset" ); - memzero(sio2); - sio2.packet.recvVal1 = 0x1D100; // Nothing is connected at start -} - -u32 sio2_getRecv1() { - PAD_LOG("Reading Recv1 = %x",sio2.packet.recvVal1); - - return sio2.packet.recvVal1; -} - -u32 sio2_getRecv2() { - PAD_LOG("Reading Recv2 = %x",0xF); - - return 0xf; -}//0, 0x10, 0x20, 0x10 | 0x20; bits 4 & 5 - -u32 sio2_getRecv3() { - if(sio2.packet.recvVal3 == 0x8C || sio2.packet.recvVal3 == 0x8b || - sio2.packet.recvVal3 == 0x83) - { - PAD_LOG("Reading Recv3 = %x",sio2.packet.recvVal3); - - sio.packetsize = sio2.packet.recvVal3; - sio2.packet.recvVal3 = 0; // Reset - return sio.packetsize; - } - else - { - PAD_LOG("Reading Recv3 = %x",sio.packetsize << 16); - - return sio.packetsize << 16; - } -} - -void sio2_setSend1(u32 index, u32 value){sio2.packet.sendArray1[index]=value;} //0->3 -u32 sio2_getSend1(u32 index){return sio2.packet.sendArray1[index];} //0->3 -void sio2_setSend2(u32 index, u32 value){sio2.packet.sendArray2[index]=value;} //0->3 -u32 sio2_getSend2(u32 index){return sio2.packet.sendArray2[index];} //0->3 - -void sio2_setSend3(u32 index, u32 value) -{ -// int i; - sio2.packet.sendArray3[index]=value; -// if (index==15){ -// for (i=0; i<4; i++){PAD_LOG("0x%08X ", sio2.packet.sendArray1[i]);}PAD_LOG("\n"); -// for (i=0; i<4; i++){PAD_LOG("0x%08X ", sio2.packet.sendArray2[i]);}PAD_LOG("\n"); -// for (i=0; i<8; i++){PAD_LOG("0x%08X ", sio2.packet.sendArray3[i]);}PAD_LOG("\n"); -// for ( ; i<16; i++){PAD_LOG("0x%08X ", sio2.packet.sendArray3[i]);}PAD_LOG("\n"); - PAD_LOG("[%d] : 0x%08X", index,sio2.packet.sendArray3[index]); -// } -} //0->15 - -u32 sio2_getSend3(u32 index) {return sio2.packet.sendArray3[index];} //0->15 - -void sio2_setCtrl(u32 value){ - sio2.ctrl=value; - if (sio2.ctrl & 1){ //recv packet - //handle data that had been sent - - iopIntcIrq( 17 ); - //SBUS - sio2.recvIndex=0; - sio2.ctrl &= ~1; - } else { // send packet - //clean up - sio2.packet.sendSize=0; //reset size - sio2.cmdport=0; - sio2.cmdlength=0; - sioWriteCtrl16(SIO_RESET); - } -} -u32 sio2_getCtrl(){return sio2.ctrl;} - -void sio2_setIntr(u32 value){sio2.intr=value;} -u32 sio2_getIntr(){ - return sio2.intr; -} - -void sio2_set8278(u32 value){sio2._8278=value;} -u32 sio2_get8278(){return sio2._8278;} -void sio2_set827C(u32 value){sio2._827C=value;} -u32 sio2_get827C(){return sio2._827C;} - -void sio2_serialIn(u8 value){ - u16 ctrl=0x0002; - if (sio2.packet.sendArray3[sio2.cmdport] && (sio2.cmdlength==0)) - { - - sio2.cmdlength=(sio2.packet.sendArray3[sio2.cmdport] >> 8) & 0x1FF; - ctrl &= ~0x2000; - ctrl |= (sio2.packet.sendArray3[sio2.cmdport] & 1) << 13; - //sioWriteCtrl16(SIO_RESET); - sioWriteCtrl16(ctrl); - PSXDMA_LOG("sio2_fifoIn: ctrl = %x, cmdlength = %x, cmdport = %d (%x)", ctrl, sio2.cmdlength, sio2.cmdport, sio2.packet.sendArray3[sio2.cmdport]); - - sio2.cmdport++; - } - - if (sio2.cmdlength) sio2.cmdlength--; - sioWrite8(value); - - if (sio2.packet.sendSize >= BUFSIZE) {//asadr - Console.Warning("*PCSX2*: sendSize >= %d", BUFSIZE); - } else { - sio2.buf[sio2.packet.sendSize] = sioRead8(); - sio2.packet.sendSize++; - } -} -extern void SIODMAWrite(u8 value); - -void sio2_fifoIn(u8 value){ - u16 ctrl=0x0002; - if (sio2.packet.sendArray3[sio2.cmdport] && (sio2.cmdlength==0)) - { - - sio2.cmdlength=(sio2.packet.sendArray3[sio2.cmdport] >> 8) & 0x1FF; - ctrl &= ~0x2000; - ctrl |= (sio2.packet.sendArray3[sio2.cmdport] & 1) << 13; - //sioWriteCtrl16(SIO_RESET); - sioWriteCtrl16(ctrl); - PSXDMA_LOG("sio2_fifoIn: ctrl = %x, cmdlength = %x, cmdport = %d (%x)", ctrl, sio2.cmdlength, sio2.cmdport, sio2.packet.sendArray3[sio2.cmdport]); - - sio2.cmdport++; - } - - if (sio2.cmdlength) sio2.cmdlength--; - SIODMAWrite(value); - - if (sio2.packet.sendSize >= BUFSIZE) {//asadr - Console.WriteLn("*PCSX2*: sendSize >= %d", BUFSIZE); - } else { - sio2.buf[sio2.packet.sendSize] = sioRead8(); - sio2.packet.sendSize++; - } -} - -u8 sio2_fifoOut(){ - if (sio2.recvIndex <= sio2.packet.sendSize){ - //PAD_LOG("READING %x\n",sio2.buf[sio2.recvIndex]); - return sio2.buf[sio2.recvIndex++]; - } else { - Console.Error( "*PCSX2*: buffer overrun" ); - } - return 0; // No Data -} - -void SaveStateBase::sio2Freeze() -{ - FreezeTag( "sio2" ); - Freeze(sio2); -} - -///////////////////////////////////////////////// -//////////////////////////////////////////// DMA -///////////////////////////////////////////////// - -void psxDma11(u32 madr, u32 bcr, u32 chcr) { - unsigned int i, j; - int size = (bcr >> 16) * (bcr & 0xffff); - PSXDMA_LOG("*** DMA 11 - SIO2 in *** %lx addr = %lx size = %lx", chcr, madr, bcr); - - if (chcr != 0x01000201) return; - - for(i = 0; i < (bcr >> 16); i++) - { - sio.count = 1; - for(j = 0; j < ((bcr & 0xFFFF) * 4); j++) - { - sio2_fifoIn(iopMemRead8(madr)); - madr++; - if(sio2.packet.sendSize == BUFSIZE) - goto finished; - } - } - -finished: - HW_DMA11_MADR = madr; - PSX_INT(IopEvt_Dma11,(size>>2)); // Interrupts should always occur at the end -} - -void psxDMA11Interrupt() -{ - HW_DMA11_CHCR &= ~0x01000000; - psxDmaInterrupt2(4); -} - -void psxDma12(u32 madr, u32 bcr, u32 chcr) { - int size = ((bcr >> 16) * (bcr & 0xFFFF)) * 4; - PSXDMA_LOG("*** DMA 12 - SIO2 out *** %lx addr = %lx size = %lx", chcr, madr, size); - - if (chcr != 0x41000200) return; - - sio2.recvIndex = 0; // Set To start; saqib - - bcr = size; - while (bcr > 0) { - iopMemWrite8( madr, sio2_fifoOut() ); - bcr--; madr++; - if(sio2.recvIndex == sio2.packet.sendSize) break; - } - HW_DMA12_MADR = madr; - PSX_INT(IopEvt_Dma12,(size>>2)); // Interrupts should always occur at the end -} - -void psxDMA12Interrupt() -{ - HW_DMA12_CHCR &= ~0x01000000; - psxDmaInterrupt2(5); -} - -//#endif diff --git a/pcsx2/IopSio2.h b/pcsx2/IopSio2.h deleted file mode 100644 index adbc3ddc56..0000000000 --- a/pcsx2/IopSio2.h +++ /dev/null @@ -1,91 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 PCSX2 Dev Team - * - * PCSX2 is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#pragma once - -#define BUFSIZE 8448 - -//from sio2man.c - -struct SIO2_packet { - unsigned int recvVal1; // 0x00 - unsigned int sendArray1[4]; // 0x04-0x10 - unsigned int sendArray2[4]; // 0x14-0x20 - - unsigned int recvVal2; // 0x24 - - unsigned int sendArray3[16]; // 0x28-0x64 - - unsigned int recvVal3; // 0x68 - - int sendSize; // 0x6C - int recvSize; // 0x70 - - unsigned char *sendBuf; // 0x74 - unsigned char *recvBuf; // 0x78 - - unsigned int dmacAddress1; - unsigned int dmacSize1; - unsigned int dmacCount1; - unsigned int dmacAddress2; - unsigned int dmacSize2; - unsigned int dmacCount2; -}; - -struct sio2Struct { - struct SIO2_packet packet; - u32 ctrl; - u32 intr; - u32 _8278, _827C; - int recvIndex; - u32 hackedRecv; - int cmdport; - int cmdlength; //length of a command sent to a port - //is less_equal than the dma send size - u8 buf[BUFSIZE]; -}; - -extern sio2Struct sio2; - -void sio2Reset(); - -u32 sio2_getRecv1(); -u32 sio2_getRecv2(); -u32 sio2_getRecv3(); -void sio2_setSend1(u32 index, u32 value); //0->3 -u32 sio2_getSend1(u32 index); //0->3 -void sio2_setSend2(u32 index, u32 value); //0->3 -u32 sio2_getSend2(u32 index); //0->3 -void sio2_setSend3(u32 index, u32 value); //0->15 -u32 sio2_getSend3(u32 index); //0->15 - -void sio2_setCtrl(u32 value); -u32 sio2_getCtrl(); -void sio2_setIntr(u32 value); -u32 sio2_getIntr(); -void sio2_set8278(u32 value); -u32 sio2_get8278(); -void sio2_set827C(u32 value); -u32 sio2_get827C(); - -void sio2_serialIn(u8 value); -void sio2_fifoIn(u8 value); -u8 sio2_fifoOut(); - -void psxDma11(u32 madr, u32 bcr, u32 chcr); -void psxDma12(u32 madr, u32 bcr, u32 chcr); - -void psxDMA11Interrupt(); -void psxDMA12Interrupt(); diff --git a/pcsx2/MemoryCardProtocol.cpp b/pcsx2/MemoryCardProtocol.cpp new file mode 100644 index 0000000000..ab4ca68622 --- /dev/null +++ b/pcsx2/MemoryCardProtocol.cpp @@ -0,0 +1,501 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2022 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#include "PrecompiledHeader.h" + +#include "MemoryCardProtocol.h" +#include "Sio.h" + +#define MC_LOG_ENABLE 0 +#define MC_LOG if (MC_LOG_ENABLE) DevCon + +#define PS1_FAIL() if (this->PS1Fail()) return; + +MemoryCardProtocol g_MemoryCardProtocol; + +// Check if the memcard is for PS1, and if we are working on a command sent over SIO2. +// If so, return dead air. +bool MemoryCardProtocol::PS1Fail() +{ + if (mcd->IsPSX() && sio2.commandLength > 0) + { + while (fifoOut.size() < sio2.commandLength) + { + fifoOut.push_back(0x00); + } + + return true; + } + + return false; +} + +// A repeated pattern in memcard commands is to pad with zero bytes, +// then end with 0x2b and terminator bytes. This function is a shortcut for that. +void MemoryCardProtocol::The2bTerminator(size_t length) +{ + while (fifoOut.size() < length - 2) + { + fifoOut.push_back(0x00); + } + + fifoOut.push_back(0x2b); + fifoOut.push_back(mcd->term); +} + +// After one read or write, the memcard is almost certainly going to be issued a new read or write +// for the next segment of the same sector. Bump the transferAddr to where that segment begins. +// If it is the end and a new sector is being accessed, the SetSector function will deal with +// both sectorAddr and transferAddr. +void MemoryCardProtocol::ReadWriteIncrement(size_t length) +{ + mcd->transferAddr += length; +} + +void MemoryCardProtocol::RecalculatePS1Addr() +{ + mcd->sectorAddr = ((ps1McState.sectorAddrMSB << 8) | ps1McState.sectorAddrLSB); + mcd->goodSector = (mcd->sectorAddr <= 0x03ff); + mcd->transferAddr = 128 * mcd->sectorAddr; +} + +void MemoryCardProtocol::ResetPS1State() +{ + ps1McState.currentByte = 2; + ps1McState.sectorAddrMSB = 0; + ps1McState.sectorAddrLSB = 0; + ps1McState.checksum = 0; + ps1McState.expectedChecksum = 0; + memset(ps1McState.buf.data(), 0, ps1McState.buf.size()); +} + +void MemoryCardProtocol::Probe() +{ + MC_LOG.WriteLn("%s", __FUNCTION__); + PS1_FAIL(); + The2bTerminator(4); +} + +void MemoryCardProtocol::UnknownWriteDeleteEnd() +{ + MC_LOG.WriteLn("%s", __FUNCTION__); + PS1_FAIL(); + The2bTerminator(4); +} + +void MemoryCardProtocol::SetSector() +{ + MC_LOG.WriteLn("%s", __FUNCTION__); + PS1_FAIL(); + const u8 sectorLSB = fifoIn.front(); + fifoIn.pop_front(); + const u8 sector2nd = fifoIn.front(); + fifoIn.pop_front(); + const u8 sector3rd = fifoIn.front(); + fifoIn.pop_front(); + const u8 sectorMSB = fifoIn.front(); + fifoIn.pop_front(); + const u8 expectedChecksum = fifoIn.front(); + fifoIn.pop_front(); + + u8 computedChecksum = sectorLSB ^ sector2nd ^ sector3rd ^ sectorMSB; + mcd->goodSector = (computedChecksum == expectedChecksum); + + if (!mcd->goodSector) + { + Console.Warning("%s() Warning! Memcard sector checksum failed! (Expected %02X != Actual %02X) Please report to the PCSX2 team!", __FUNCTION__, expectedChecksum, computedChecksum); + } + + u32 newSector = sectorLSB | (sector2nd << 8) | (sector3rd << 16) | (sectorMSB << 24); + mcd->sectorAddr = newSector; + + McdSizeInfo info; + mcd->GetSizeInfo(info); + mcd->transferAddr = (info.SectorSize + 16) * mcd->sectorAddr; + + The2bTerminator(9); +} + +void MemoryCardProtocol::GetSpecs() +{ + MC_LOG.WriteLn("%s", __FUNCTION__); + PS1_FAIL(); + u8 checksum = 0x00; + McdSizeInfo info; + mcd->GetSizeInfo(info); + fifoOut.push_back(0x2b); + + const u8 sectorSizeLSB = (info.SectorSize & 0xff); + checksum ^= sectorSizeLSB; + fifoOut.push_back(sectorSizeLSB); + + const u8 sectorSizeMSB = (info.SectorSize >> 8); + checksum ^= sectorSizeMSB; + fifoOut.push_back(sectorSizeMSB); + + const u8 eraseBlockSizeLSB = (info.EraseBlockSizeInSectors & 0xff); + checksum ^= eraseBlockSizeLSB; + fifoOut.push_back(eraseBlockSizeLSB); + + const u8 eraseBlockSizeMSB = (info.EraseBlockSizeInSectors >> 8); + checksum ^= eraseBlockSizeMSB; + fifoOut.push_back(eraseBlockSizeMSB); + + const u8 sectorCountLSB = (info.McdSizeInSectors & 0xff); + checksum ^= sectorCountLSB; + fifoOut.push_back(sectorCountLSB); + + const u8 sectorCount2nd = (info.McdSizeInSectors >> 8); + checksum ^= sectorCount2nd; + fifoOut.push_back(sectorCount2nd); + + const u8 sectorCount3rd = (info.McdSizeInSectors >> 16); + checksum ^= sectorCount3rd; + fifoOut.push_back(sectorCount3rd); + + const u8 sectorCountMSB = (info.McdSizeInSectors >> 24); + checksum ^= sectorCountMSB; + fifoOut.push_back(sectorCountMSB); + + fifoOut.push_back(info.Xor); + fifoOut.push_back(mcd->term); +} + +void MemoryCardProtocol::SetTerminator() +{ + MC_LOG.WriteLn("%s", __FUNCTION__); + PS1_FAIL(); + const u8 newTerminator = fifoIn.front(); + fifoIn.pop_front(); + const u8 oldTerminator = mcd->term; + mcd->term = newTerminator; + fifoOut.push_back(0x00); + fifoOut.push_back(0x2b); + fifoOut.push_back(oldTerminator); +} + +void MemoryCardProtocol::GetTerminator() +{ + MC_LOG.WriteLn("%s", __FUNCTION__); + PS1_FAIL(); + fifoOut.push_back(0x2b); + fifoOut.push_back(mcd->term); + fifoOut.push_back(static_cast(Terminator::DEFAULT)); +} + +void MemoryCardProtocol::WriteData() +{ + MC_LOG.WriteLn("%s", __FUNCTION__); + PS1_FAIL(); + fifoOut.push_back(0x00); + fifoOut.push_back(0x2b); + const u8 writeLength = fifoIn.front(); + fifoIn.pop_front(); + u8 checksum = 0x00; + std::vector buf; + + for (size_t writeCounter = 0; writeCounter < writeLength; writeCounter++) + { + const u8 writeByte = fifoIn.front(); + fifoIn.pop_front(); + checksum ^= writeByte; + buf.push_back(writeByte); + fifoOut.push_back(0x00); + } + + mcd->Write(buf.data(), buf.size()); + fifoOut.push_back(checksum); + fifoOut.push_back(mcd->term); + + ReadWriteIncrement(writeLength); +} + +void MemoryCardProtocol::ReadData() +{ + MC_LOG.WriteLn("%s", __FUNCTION__); + PS1_FAIL(); + const u8 readLength = fifoIn.front(); + fifoIn.pop_front(); + fifoOut.push_back(0x00); + fifoOut.push_back(0x2b); + std::vector buf; + buf.resize(readLength); + mcd->Read(buf.data(), buf.size()); + u8 checksum = 0x00; + + for (const u8 readByte : buf) + { + checksum ^= readByte; + fifoOut.push_back(readByte); + } + + fifoOut.push_back(checksum); + fifoOut.push_back(mcd->term); + + ReadWriteIncrement(readLength); +} + +u8 MemoryCardProtocol::PS1Read(u8 data) +{ + MC_LOG.WriteLn("%s", __FUNCTION__); + bool sendAck = true; + u8 ret = 0; + + switch (ps1McState.currentByte) + { + case 2: + ret = 0x5a; + break; + case 3: + ret = 0x5d; + break; + case 4: + ps1McState.sectorAddrMSB = data; + ret = 0x00; + break; + case 5: + ps1McState.sectorAddrLSB = data; + ret = 0x00; + RecalculatePS1Addr(); + break; + case 6: + ret = 0x5c; + break; + case 7: + ret = 0x5d; + break; + case 8: + ret = ps1McState.sectorAddrMSB; + break; + case 9: + ret = ps1McState.sectorAddrLSB; + break; + case 138: + ret = ps1McState.checksum; + break; + case 139: + ret = 0x47; + sendAck = false; + break; + case 10: + ps1McState.checksum = ps1McState.sectorAddrMSB ^ ps1McState.sectorAddrLSB; + mcd->Read(ps1McState.buf.data(), ps1McState.buf.size()); + default: + ret = ps1McState.buf.at(ps1McState.currentByte - 10); + ps1McState.checksum ^= ret; + break; + } + + if (sendAck) + { + sio0.Acknowledge(); + } + + ps1McState.currentByte++; + return ret; +} + +u8 MemoryCardProtocol::PS1State(u8 data) +{ + DevCon.Error("%s(%02X) I do not exist, please change that ASAP.", __FUNCTION__, data); + assert(false); + return 0x00; +} + +u8 MemoryCardProtocol::PS1Write(u8 data) +{ + MC_LOG.WriteLn("%s", __FUNCTION__); + bool sendAck = true; + u8 ret = 0; + + switch (ps1McState.currentByte) + { + case 2: + ret = 0x5a; + break; + case 3: + ret = 0x5d; + break; + case 4: + ps1McState.sectorAddrMSB = data; + ret = 0x00; + break; + case 5: + ps1McState.sectorAddrLSB = data; + ret = 0x00; + RecalculatePS1Addr(); + break; + case 134: + ps1McState.expectedChecksum = data; + ret = 0; + break; + case 135: + ret = 0x5c; + break; + case 136: + ret = 0x5d; + break; + case 137: + if (!mcd->goodSector) + { + ret = 0xff; + } + else if (ps1McState.expectedChecksum != ps1McState.checksum) + { + ret = 0x4e; + } + else + { + mcd->Write(ps1McState.buf.data(), ps1McState.buf.size()); + ret = 0x47; + // Clear the "directory unread" bit of the flag byte. Per no$psx, this is cleared + // on writes, not reads. + mcd->FLAG &= 0x07; + } + + sendAck = false; + break; + case 6: + ps1McState.checksum = ps1McState.sectorAddrMSB ^ ps1McState.sectorAddrLSB; + default: + ps1McState.buf.at(ps1McState.currentByte - 6) = data; + ps1McState.checksum ^= data; + ret = 0x00; + break; + } + + if (sendAck) + { + sio0.Acknowledge(); + } + + ps1McState.currentByte++; + return ret; +} + +u8 MemoryCardProtocol::PS1Pocketstation(u8 data) +{ + MC_LOG.WriteLn("%s", __FUNCTION__); + sio2.SetRecv1(Recv1::DISCONNECTED); + return 0x00; +} + +void MemoryCardProtocol::ReadWriteEnd() +{ + MC_LOG.WriteLn("%s", __FUNCTION__); + PS1_FAIL(); + The2bTerminator(4); +} + +void MemoryCardProtocol::EraseBlock() +{ + MC_LOG.WriteLn("%s", __FUNCTION__); + PS1_FAIL(); + mcd->EraseBlock(); + The2bTerminator(4); +} + +void MemoryCardProtocol::UnknownBoot() +{ + MC_LOG.WriteLn("%s", __FUNCTION__); + PS1_FAIL(); + The2bTerminator(5); +} + +void MemoryCardProtocol::AuthXor() +{ + MC_LOG.WriteLn("%s", __FUNCTION__); + PS1_FAIL(); + const u8 modeByte = fifoIn.front(); + fifoIn.pop_front(); + + switch (modeByte) + { + // When encountered, the command length in RECV3 is guaranteed to be 14, + // and the PS2 is expecting us to XOR the data it is about to send. + case 0x01: + case 0x02: + case 0x04: + case 0x0f: + case 0x11: + case 0x13: + { + // Long + XOR + fifoOut.push_back(0x00); + fifoOut.push_back(0x2b); + u8 xorResult = 0x00; + + for (size_t xorCounter = 0; xorCounter < 8; xorCounter++) + { + const u8 toXOR = fifoIn.front(); + fifoIn.pop_front(); + xorResult ^= toXOR; + fifoOut.push_back(0x00); + } + + fifoOut.push_back(xorResult); + fifoOut.push_back(mcd->term); + break; + } + // When encountered, the command length in RECV3 is guaranteed to be 5, + // and there is no attempt to XOR anything. + case 0x00: + case 0x03: + case 0x05: + case 0x08: + case 0x09: + case 0x0a: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x10: + case 0x12: + case 0x14: + { + // Short + No XOR + The2bTerminator(5); + break; + } + // When encountered, the command length in RECV3 is guaranteed to be 14, + // and the PS2 is about to send us data, BUT the PS2 does NOT want us + // to send the XOR, it wants us to send the 0x2b and terminator as the + // last two bytes. + case 0x06: + case 0x07: + case 0x0b: + { + // Long + No XOR + The2bTerminator(14); + break; + } + default: + Console.Warning("%s(queue) Unexpected modeByte (%02X), please report to the PCSX2 team", __FUNCTION__, modeByte); + break; + } +} + +void MemoryCardProtocol::AuthF3() +{ + MC_LOG.WriteLn("%s", __FUNCTION__); + PS1_FAIL(); + The2bTerminator(5); +} + +void MemoryCardProtocol::AuthF7() +{ + MC_LOG.WriteLn("%s", __FUNCTION__); + PS1_FAIL(); + The2bTerminator(5); +} diff --git a/pcsx2/MemoryCardProtocol.h b/pcsx2/MemoryCardProtocol.h new file mode 100644 index 0000000000..6e8ed85094 --- /dev/null +++ b/pcsx2/MemoryCardProtocol.h @@ -0,0 +1,64 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2022 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#pragma once + +#include + +struct PS1MemoryCardState +{ + size_t currentByte = 2; + u8 sectorAddrMSB = 0; + u8 sectorAddrLSB = 0; + u8 checksum = 0; + u8 expectedChecksum = 0; + std::array buf; +}; + +// A global class which contains the behavior of each memory card command. +class MemoryCardProtocol +{ +private: + PS1MemoryCardState ps1McState; + + bool PS1Fail(); + void The2bTerminator(size_t length); + void ReadWriteIncrement(size_t length); + void RecalculatePS1Addr(); + +public: + void ResetPS1State(); + + void Probe(); + void UnknownWriteDeleteEnd(); + void SetSector(); + void GetSpecs(); + void SetTerminator(); + void GetTerminator(); + void WriteData(); + void ReadData(); + u8 PS1Read(u8 data); + u8 PS1State(u8 data); + u8 PS1Write(u8 data); + u8 PS1Pocketstation(u8 data); + void ReadWriteEnd(); + void EraseBlock(); + void UnknownBoot(); + void AuthXor(); + void AuthF3(); + void AuthF7(); +}; + +extern MemoryCardProtocol g_MemoryCardProtocol; diff --git a/pcsx2/MultitapProtocol.cpp b/pcsx2/MultitapProtocol.cpp new file mode 100644 index 0000000000..0d4a72b3c4 --- /dev/null +++ b/pcsx2/MultitapProtocol.cpp @@ -0,0 +1,89 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2022 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#include "PrecompiledHeader.h" + +#include "MultitapProtocol.h" +#include "Sio.h" + +#define MT_LOG_ENABLE 0 +#define MT_LOG if (MT_LOG_ENABLE) DevCon + +MultitapProtocol g_MultitapProtocol; + +void MultitapProtocol::SupportCheck() +{ + MT_LOG.WriteLn("%s", __FUNCTION__); + fifoOut.push_back(0x5a); + fifoOut.push_back(0x04); + fifoOut.push_back(0x00); + fifoOut.push_back(0x5a); +} + +void MultitapProtocol::Select() +{ + MT_LOG.WriteLn("%s", __FUNCTION__); + const u8 newSlot = fifoIn.front(); + fifoIn.pop_front(); + const bool isInBounds = (newSlot >= 0 && newSlot < SIO::SLOTS); + + if (isInBounds) + { + sio2.slot = newSlot; + MT_LOG.WriteLn("Slot changed to %d", sio2.slot); + } + + fifoOut.push_back(0x5a); + fifoOut.push_back(0x00); + fifoOut.push_back(0x00); + fifoOut.push_back(isInBounds ? newSlot : 0xff); + fifoOut.push_back(isInBounds ? 0x5a : 0x66); +} + +MultitapProtocol::MultitapProtocol() = default; +MultitapProtocol::~MultitapProtocol() = default; + +void MultitapProtocol::SoftReset() +{ +} + +void MultitapProtocol::FullReset() +{ + SoftReset(); + + sio2.slot = 0; +} + +void MultitapProtocol::SendToMultitap() +{ + const u8 commandByte = fifoIn.front(); + fifoIn.pop_front(); + fifoOut.push_back(0x80); + + switch (static_cast(commandByte)) + { + case MultitapMode::PAD_SUPPORT_CHECK: + case MultitapMode::MEMCARD_SUPPORT_CHECK: + SupportCheck(); + break; + case MultitapMode::SELECT_PAD: + case MultitapMode::SELECT_MEMCARD: + Select(); + break; + default: + DevCon.Warning("%s() Unhandled MultitapMode (%02X)", __FUNCTION__, commandByte); + break; + } +} diff --git a/pcsx2/MultitapProtocol.h b/pcsx2/MultitapProtocol.h new file mode 100644 index 0000000000..79fd113b99 --- /dev/null +++ b/pcsx2/MultitapProtocol.h @@ -0,0 +1,43 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2022 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#pragma once + +enum class MultitapMode +{ + NOT_SET = 0xff, + PAD_SUPPORT_CHECK = 0x12, + MEMCARD_SUPPORT_CHECK = 0x13, + SELECT_PAD = 0x21, + SELECT_MEMCARD = 0x22, +}; + +class MultitapProtocol +{ +private: + void SupportCheck(); + void Select(); + +public: + MultitapProtocol(); + ~MultitapProtocol(); + + void SoftReset(); + void FullReset(); + + void SendToMultitap(); +}; + +extern MultitapProtocol g_MultitapProtocol; diff --git a/pcsx2/PAD/Gamepad.h b/pcsx2/PAD/Gamepad.h index 6e878e445a..9842794081 100644 --- a/pcsx2/PAD/Gamepad.h +++ b/pcsx2/PAD/Gamepad.h @@ -24,8 +24,9 @@ void PADshutdown(); s32 PADopen(const WindowInfo& wi); void PADupdate(int pad); void PADclose(); -u8 PADstartPoll(int pad); +u8 PADstartPoll(int port, int slot); u8 PADpoll(u8 value); +bool PADcomplete(); HostKeyEvent* PADkeyEvent(); void PADconfigure(); s32 PADfreeze(FreezeAction mode, freezeData* data); diff --git a/pcsx2/PAD/Host/PAD.cpp b/pcsx2/PAD/Host/PAD.cpp index 49292dcc07..71a74855c3 100644 --- a/pcsx2/PAD/Host/PAD.cpp +++ b/pcsx2/PAD/Host/PAD.cpp @@ -173,9 +173,9 @@ s32 PADfreeze(FreezeAction mode, freezeData* data) return 0; } -u8 PADstartPoll(int pad) +u8 PADstartPoll(int _port, int _slot) { - return pad_start_poll(pad); + return pad_start_poll(_port, _slot); } u8 PADpoll(u8 value) @@ -244,6 +244,11 @@ std::string PAD::GetConfigSection(u32 pad_index) return fmt::format("Pad{}", pad_index + 1); } +bool PADcomplete() +{ + return pad_complete(); +} + void PAD::LoadConfig(const SettingsInterface& si) { PAD::s_macro_buttons = {}; diff --git a/pcsx2/PAD/Host/PAD.h b/pcsx2/PAD/Host/PAD.h index b3b15f9971..5fa4fe7582 100644 --- a/pcsx2/PAD/Host/PAD.h +++ b/pcsx2/PAD/Host/PAD.h @@ -33,8 +33,9 @@ s32 PADopen(const WindowInfo& wi); void PADclose(); s32 PADsetSlot(u8 port, u8 slot); s32 PADfreeze(FreezeAction mode, freezeData* data); -u8 PADstartPoll(int pad); +u8 PADstartPoll(int _port, int _slot); u8 PADpoll(u8 value); +bool PADcomplete(); namespace PAD { diff --git a/pcsx2/PAD/Host/StateManagement.cpp b/pcsx2/PAD/Host/StateManagement.cpp index 71ae8342c7..71e30eba29 100644 --- a/pcsx2/PAD/Host/StateManagement.cpp +++ b/pcsx2/PAD/Host/StateManagement.cpp @@ -62,7 +62,7 @@ void QueryInfo::reset() memset(response, 0xF3, sizeof(response)); } -u8 QueryInfo::start_poll(int _port) +u8 QueryInfo::start_poll(int _port, int _slot) { if (_port >= 2) { @@ -71,7 +71,7 @@ u8 QueryInfo::start_poll(int _port) } port = _port; - slot = slots[port]; + slot = _slot; const u32 ext_port = sioConvertPortAndSlotToPad(port, slot); @@ -184,9 +184,9 @@ inline bool IsDualshock2() #endif } -u8 pad_start_poll(u8 pad) +u8 pad_start_poll(u8 _port, u8 _slot) { - return query.start_poll(pad - 1); + return query.start_poll(_port, _slot); } u8 pad_poll(u8 value) @@ -516,3 +516,8 @@ u8 pad_poll(u8 value) return query.response[query.lastByte]; } } + +bool pad_complete() +{ + return query.queryDone; +} \ No newline at end of file diff --git a/pcsx2/PAD/Host/StateManagement.h b/pcsx2/PAD/Host/StateManagement.h index 997786779d..acca386607 100644 --- a/pcsx2/PAD/Host/StateManagement.h +++ b/pcsx2/PAD/Host/StateManagement.h @@ -49,7 +49,7 @@ struct QueryInfo u8 response[42]; void reset(); - u8 start_poll(int port); + u8 start_poll(int _port, int _slot); template void set_result(const u8 (&rsp)[S]) @@ -127,5 +127,6 @@ extern QueryInfo query; extern Pad pads[2][4]; extern int slots[2]; -extern u8 pad_start_poll(u8 pad); +extern u8 pad_start_poll(u8 _port, u8 _slot); extern u8 pad_poll(u8 value); +extern bool pad_complete(); \ No newline at end of file diff --git a/pcsx2/PAD/Linux/PAD.cpp b/pcsx2/PAD/Linux/PAD.cpp index 8abfad95cd..496766c455 100644 --- a/pcsx2/PAD/Linux/PAD.cpp +++ b/pcsx2/PAD/Linux/PAD.cpp @@ -199,9 +199,9 @@ s32 PADfreeze(FreezeAction mode, freezeData* data) return 0; } -u8 PADstartPoll(int pad) +u8 PADstartPoll(int port, int slot) { - return pad_start_poll(pad); + return pad_start_poll(port, slot); } u8 PADpoll(u8 value) @@ -209,6 +209,11 @@ u8 PADpoll(u8 value) return pad_poll(value); } +bool PADcomplete() +{ + return pad_complete(); +} + // PADkeyEvent is called every vsync (return NULL if no event) HostKeyEvent* PADkeyEvent() { diff --git a/pcsx2/PAD/Linux/state_management.cpp b/pcsx2/PAD/Linux/state_management.cpp index 37a0867a68..8cfc47c7a4 100644 --- a/pcsx2/PAD/Linux/state_management.cpp +++ b/pcsx2/PAD/Linux/state_management.cpp @@ -51,7 +51,7 @@ void QueryInfo::reset() memset(response, 0xF3, sizeof(response)); } -u8 QueryInfo::start_poll(int _port) +u8 QueryInfo::start_poll(int _port, int _slot) { if (port > 1) { @@ -61,7 +61,7 @@ u8 QueryInfo::start_poll(int _port) queryDone = 0; port = _port; - slot = slots[port]; + slot = _slot; numBytes = 2; lastByte = 0; @@ -160,9 +160,14 @@ inline bool IsDualshock2() #endif } -u8 pad_start_poll(u8 pad) +u8 pad_start_poll(u8 port, u8 slot) { - return query.start_poll(pad - 1); + return query.start_poll(port, slot); +} + +bool pad_complete() +{ + return query.queryDone; } u8 pad_poll(u8 value) diff --git a/pcsx2/PAD/Linux/state_management.h b/pcsx2/PAD/Linux/state_management.h index b3d959362d..005c554d37 100644 --- a/pcsx2/PAD/Linux/state_management.h +++ b/pcsx2/PAD/Linux/state_management.h @@ -48,7 +48,7 @@ struct QueryInfo u8 response[42]; void reset(); - u8 start_poll(int port); + u8 start_poll(int port, int slot); template void set_result(const u8 (&rsp)[S]) @@ -129,5 +129,6 @@ extern QueryInfo query; extern Pad pads[2][4]; extern int slots[2]; -extern u8 pad_start_poll(u8 pad); +extern u8 pad_start_poll(u8 port, u8 slot); extern u8 pad_poll(u8 value); +extern bool pad_complete(); diff --git a/pcsx2/PAD/Windows/PAD.cpp b/pcsx2/PAD/Windows/PAD.cpp index fe73929764..03581c6f7a 100644 --- a/pcsx2/PAD/Windows/PAD.cpp +++ b/pcsx2/PAD/Windows/PAD.cpp @@ -1057,21 +1057,26 @@ void PADclose() } } -u8 PADstartPoll(int port) +bool PADcomplete() +{ + return query.queryDone; +} + +u8 PADstartPoll(int port, int slot) { DEBUG_NEW_SET(); - port--; - if ((unsigned int)port <= 1 && pads[port][slots[port]].enabled) + + if ((unsigned int)port <= 1 && pads[port][slot].enabled) { query.queryDone = 0; query.port = port; - query.slot = slots[port]; + query.slot = slot; query.numBytes = 2; query.lastByte = 0; DEBUG_IN(port); DEBUG_OUT(0xFF); - DEBUG_IN(slots[port]); - DEBUG_OUT(pads[port][slots[port]].enabled); + DEBUG_IN(slot); + DEBUG_OUT(pads[port][slot].enabled); return 0xFF; } else diff --git a/pcsx2/R3000A.cpp b/pcsx2/R3000A.cpp index cc0ed1332a..bddb8332f5 100644 --- a/pcsx2/R3000A.cpp +++ b/pcsx2/R3000A.cpp @@ -22,7 +22,6 @@ #include "Sif.h" #include "DebugTools/Breakpoints.h" #include "R5900OpcodeTables.h" -#include "IopSio2.h" #include "IopCounters.h" #include "IopBios.h" #include "IopHw.h" @@ -177,13 +176,30 @@ static __fi void IopTestEvent( IopEventId n, void (*callback)() ) psxSetNextBranch( psxRegs.sCycle[n], psxRegs.eCycle[n] ); } +static __fi void Sio0TestEvent(IopEventId n) +{ + if (!(psxRegs.interrupt & (1 << n))) + { + return; + } + + if (psxTestCycle(psxRegs.sCycle[n], psxRegs.eCycle[n])) + { + psxRegs.interrupt &= ~(1 << n); + sio0.Interrupt(Sio0Interrupt::TEST_EVENT); + } + else + { + psxSetNextBranch(psxRegs.sCycle[n], psxRegs.eCycle[n]); + } +} + static __fi void _psxTestInterrupts() { IopTestEvent(IopEvt_SIF0, sif0Interrupt); // SIF0 IopTestEvent(IopEvt_SIF1, sif1Interrupt); // SIF1 IopTestEvent(IopEvt_SIF2, sif2Interrupt); // SIF2 - // Originally controlled by a preprocessor define, now PSX dependent. - if (psxHu32(HW_ICFG) & (1 << 3)) IopTestEvent(IopEvt_SIO, sioInterruptR); + Sio0TestEvent(IopEvt_SIO); IopTestEvent(IopEvt_CdvdRead, cdvdReadInterrupt); IopTestEvent(IopEvt_CdvdSectorReady, cdvdSectorReady); diff --git a/pcsx2/Recording/InputRecording.cpp b/pcsx2/Recording/InputRecording.cpp index 6d08955eaa..e4e678da03 100644 --- a/pcsx2/Recording/InputRecording.cpp +++ b/pcsx2/Recording/InputRecording.cpp @@ -93,21 +93,21 @@ void InputRecording::RecordingReset() g_InputRecordingControls.Resume(); } -void InputRecording::ControllerInterrupt(u8& data, u8& port, u16& bufCount, u8 buf[]) +void InputRecording::ControllerInterrupt(u8 port, size_t fifoSize, u8 dataIn, u8 dataOut) { // TODO - Multi-Tap Support - if (bufCount == 1) - fInterruptFrame = data == READ_DATA_AND_VIBRATE_FIRST_BYTE; - else if (bufCount == 2) + if (fifoSize == 1) + fInterruptFrame = dataIn == READ_DATA_AND_VIBRATE_FIRST_BYTE; + else if (fifoSize == 2) { - if (buf[bufCount] != READ_DATA_AND_VIBRATE_SECOND_BYTE) + if (dataOut != READ_DATA_AND_VIBRATE_SECOND_BYTE) fInterruptFrame = false; } else if (fInterruptFrame) { - u8& bufVal = buf[bufCount]; - const u16 bufIndex = bufCount - 3; + u8& bufVal = dataOut; + const u16 bufIndex = fifoSize - 3; if (state == InputRecordingMode::Replaying) { if (frameCounter >= 0 && frameCounter < INT_MAX) @@ -480,6 +480,7 @@ wxString InputRecording::resolveGameName() #include "InputRecordingControls.h" #include "Utilities/InputRecordingLogger.h" +#include #include void SaveStateBase::InputRecordingFreeze() @@ -526,21 +527,21 @@ void InputRecording::RecordingReset() g_InputRecordingControls.Resume(); } -void InputRecording::ControllerInterrupt(u8& data, u8& port, u16& bufCount, u8 buf[]) +// TODO: Refactor this +void InputRecording::ControllerInterrupt(u8 port, size_t fifoSize, u8 dataIn, u8 dataOut) { // TODO - Multi-Tap Support - - if (bufCount == 1) - fInterruptFrame = data == READ_DATA_AND_VIBRATE_FIRST_BYTE; - else if (bufCount == 2) + if (fifoSize == 1) + fInterruptFrame = dataIn == READ_DATA_AND_VIBRATE_FIRST_BYTE; + else if (fifoSize == 2) { - if (buf[bufCount] != READ_DATA_AND_VIBRATE_SECOND_BYTE) + if (dataOut != READ_DATA_AND_VIBRATE_SECOND_BYTE) fInterruptFrame = false; } else if (fInterruptFrame) { - u8& bufVal = buf[bufCount]; - const u16 bufIndex = bufCount - 3; + u8& bufVal = dataOut; + const u16 bufIndex = fifoSize - 3; if (state == InputRecordingMode::Replaying) { if (frameCounter >= 0 && frameCounter < INT_MAX) diff --git a/pcsx2/Recording/InputRecording.h b/pcsx2/Recording/InputRecording.h index 80fc6d9879..2fc0470088 100644 --- a/pcsx2/Recording/InputRecording.h +++ b/pcsx2/Recording/InputRecording.h @@ -15,6 +15,8 @@ #pragma once +#include + #ifndef PCSX2_CORE // TODO - Vaser - kill with wxWidgets @@ -36,7 +38,7 @@ public: // Main handler for ingesting input data and either saving it to the recording file (recording) // or mutating it to the contents of the recording file (replaying) - void ControllerInterrupt(u8& data, u8& port, u16& BufCount, u8 buf[]); + void ControllerInterrupt(u8 port, size_t fifoSize, u8 dataIn, u8 dataOut); // The running frame counter for the input recording s32 GetFrameCounter(); @@ -168,7 +170,7 @@ public: // Main handler for ingesting input data and either saving it to the recording file (recording) // or mutating it to the contents of the recording file (replaying) - void ControllerInterrupt(u8& data, u8& port, u16& BufCount, u8 buf[]); + void ControllerInterrupt(u8 port, size_t fifoSize, u8 dataIn, u8 dataOut); // The running frame counter for the input recording s32 GetFrameCounter(); diff --git a/pcsx2/SaveState.h b/pcsx2/SaveState.h index 2b5cfa39fb..97813b9156 100644 --- a/pcsx2/SaveState.h +++ b/pcsx2/SaveState.h @@ -33,7 +33,7 @@ enum class FreezeAction // [SAVEVERSION+] // This informs the auto updater that the users savestates will be invalidated. -static const u32 g_SaveVersion = (0x9A2F << 16) | 0x0000; +static const u32 g_SaveVersion = (0x9A30 << 16) | 0x0000; // the freezing data between submodules and core diff --git a/pcsx2/Sio.cpp b/pcsx2/Sio.cpp index 62fb30a96b..f0ee1ab3dd 100644 --- a/pcsx2/Sio.cpp +++ b/pcsx2/Sio.cpp @@ -1,5 +1,5 @@ /* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 PCSX2 Dev Team + * Copyright (C) 2002-2022 PCSX2 Dev Team * * PCSX2 is free software: you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Found- @@ -20,67 +20,29 @@ #include "Common.h" #include "Sio.h" -#include "sio_internal.h" +#include "MemoryCardProtocol.h" +#include "MultitapProtocol.h" +#include "Config.h" #include "PAD/Gamepad.h" #include "common/Timer.h" #include "Recording/InputRecording.h" +#define SIO0LOG_ENABLE 0 +#define SIO2LOG_ENABLE 0 + +#define Sio0Log if (SIO0LOG_ENABLE) DevCon +#define Sio2Log if (SIO2LOG_ENABLE) DevCon + +std::deque fifoIn; +std::deque fifoOut; + +Sio0 sio0; +Sio2 sio2; -_sio sio; _mcd mcds[2][4]; _mcd *mcd; -SIO_MODE siomode = SIO_START; -static void sioWrite8inl(u8 data); -#define SIO_WRITE void inline -#define SIO_FORCEINLINE __fi - -// Magic psx values from nocash info -static const u8 memcard_psx[] = {0x5A, 0x5D, 0x5C, 0x5D, 0x04, 0x00, 0x00, 0x80}; - -// Memory Card Specs for standard Sony 8mb carts: -// Flags (magic sio '+' thingie!), Sector size, eraseBlockSize (in pages), card size (in pages), xor checksum (superblock?), terminator (unused?). -// FIXME variable commented out since it's not used atm. -// static const mc_command_0x26_tag mc_sizeinfo_8mb= {'+', 512, 16, 0x4000, 0x52, 0x5A}; - -// Ejection timeout management belongs in MemoryCardFile -// -//Reinsert the card after auto-eject: after max tries or after min tries + XXX milliseconds, whichever comes first. -//E.g. if the game polls the card 100 times/sec and max tries=100, then after 1 second it will see the card as inserted (ms timeout not reached). -//E.g. if the game polls the card 1 time/sec, then it will see the card ejected 4 times, and on the 5th it will see it as inserted (4 secs from the initial access). -//(A 'try' in this context is the game accessing SIO) -static const int FORCED_MCD_EJECTION_MIN_TRIES =2; -static const int FORCED_MCD_EJECTION_MAX_TRIES =128; -static const float FORCED_MCD_EJECTION_MAX_MS_AFTER_MIN_TRIES =2800; - -//allow timeout also for the mcd manager panel -void SetForceMcdEjectTimeoutNow( uint port, uint slot ) -{ - mcds[port][slot].ForceEjection_Timeout = FORCED_MCD_EJECTION_MAX_TRIES; -} -void SetForceMcdEjectTimeoutNow() -{ - for ( uint port = 0; port < 2; ++port ) { - for ( uint slot = 0; slot < 4; ++slot ) { - SetForceMcdEjectTimeoutNow( port, slot ); - } - } -} - -void ClearMcdEjectTimeoutNow( uint port, uint slot ) -{ - mcds[port][slot].ForceEjection_Timeout = 0; -} -void ClearMcdEjectTimeoutNow() -{ - for ( uint port = 0; port < 2; ++port ) { - for ( uint slot = 0; slot < 4; ++slot ) { - ClearMcdEjectTimeoutNow( port, slot ); - } - } -} - // Currently only check if pad wants mtap to be active. // Could lets PCSX2 have its own options, if anyone ever // wants to add support for using the extra memcard slots. @@ -89,910 +51,800 @@ static bool IsMtapPresent( uint port ) return EmuConfig.MultitapEnabled( port ); } -void sioInit() +// ============================================================================ +// SIO0 +// ============================================================================ + +void Sio0::ClearStatAcknowledge() { - memzero(sio); + stat &= ~(SIO0_STAT::ACK); +} - sio.bufSize = 4; - siomode = SIO_START; +Sio0::Sio0() +{ + this->FullReset(); +} - for(int i = 0; i < 2; i++) +Sio0::~Sio0() = default; + +void Sio0::SoftReset() +{ + padStarted = false; + sioMode = SioMode::NOT_SET; + sioCommand = 0; + sioStage = SioStage::IDLE; +} + +void Sio0::FullReset() +{ + SoftReset(); + + port = 0; + slot = 0; + + for (int i = 0; i < 2; i++) { - for(int j = 0; j < 4; j++) + for (int j = 0; j < 4; j++) { mcds[i][j].term = 0x55; mcds[i][j].port = i; mcds[i][j].slot = j; mcds[i][j].FLAG = 0x08; - mcds[i][j].ForceEjection_Timeout = 0; + mcds[i][j].autoEjectTicks = 0; } - - sio.slot[i] = 0; } - sio.port = 0; mcd = &mcds[0][0]; - - // Transfer(?) Ready and the Buffer is Empty - sio.StatReg = TX_RDY | TX_EMPTY; - sio.packetsize = 0; } -bool isR3000ATest = false; - -// Check the active game's type, and fire the matching interrupt. -// The 3rd bit of the HW_IFCG register lets us know if PSX mode is active. 1 = PSX, 0 = PS2 -// Note that the R3000A's call to interrupts only calls the PS2 based (lack of) delays. -SIO_FORCEINLINE void sioInterrupt() { - if ((psxHu32(HW_ICFG) & (1 << 3)) && !isR3000ATest) { - if (!(psxRegs.interrupt & (1 << IopEvt_SIO))) - PSX_INT(IopEvt_SIO, 64); // PSXCLK/250000); - } else { - PAD_LOG("Sio Interrupt"); - sio.StatReg |= IRQ; - iopIntcIrq(7); //Should this be used instead of the one below? - //psxHu32(0x1070)|=0x80; - } - - isR3000ATest = false; -} - -// An offhand way for the R3000A to access the sioInterrupt function. -// Following the design of the old preprocessor system, the R3000A should -// never call the PSX delays (oddly enough), and only the PS2. So we need -// an extra layer here to help control that. -__fi void sioInterruptR() { - isR3000ATest = true; - sioInterrupt(); -} - -SIO_WRITE sioWriteStart(u8 data) +// Simulates the ACK line on the bus. Peripherals are expected to send an ACK signal +// over this line to tell the PS1 "keep sending me things I'm not done yet". The PS1 +// then uses this after it receives the peripheral's response to decide what to do. +void Sio0::Acknowledge() { - u32 sioreg = sio2.packet.sendArray3[sio2.cmdport ? (sio2.cmdport - 1) : 0]; - - //u16 size1 = (sioreg >> 8) & 0x1FF; - u16 size2 = (sioreg >> 18) & 0x1FF; - - //if(size1 != size2) - // DevCon.Warning("SIO: Bad command length [%02X] (%02X|%02X)", data, size1, size2); - - // On mismatch, sio2.cmdlength (size1) is smaller than what it should (Persona 3) - // while size2 is the proper length. -KrossX - sio.bufSize = size2; //std::max(size1, size2); - - if(sio.bufSize) - sio.bufSize--; - - switch(data) - { - case 0x01: siomode = SIO_CONTROLLER; break; - case 0x21: siomode = SIO_MULTITAP; break; - case 0x61: siomode = SIO_INFRARED; break; - case 0x81: siomode = SIO_MEMCARD; break; - - default: - DevCon.Warning("%s cmd: %02X??", __FUNCTION__, data); - DEVICE_UNPLUGGED(); - siomode = SIO_DUMMY; - break; - } - - sioWrite8inl(data); + stat |= SIO0_STAT::ACK; } -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) +void Sio0::Interrupt(Sio0Interrupt sio0Interrupt) { - //if (data == 0x01) byteCnt = 0; - switch(sio.bufCount) + switch (sio0Interrupt) { - 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: - sio.buf[sio.bufCount] = PADpoll(data); - if (EmuConfig.EnableRecordingTools) - { - // Only examine controllers 1 / 2 - if (sio.slot[sio.port] == 0 || sio.slot[sio.port] == 1) - { - g_InputRecording.ControllerInterrupt(data, sio.port, sio.bufCount, sio.buf); - } - } - break; - } - //Console.WriteLn( "SIO: sent = %02X From pad data = %02X bufCnt %08X ", data, sio.buf[sio.bufCount], sio.bufCount); - sioInterrupt(); //Don't all commands(transfers) cause an interrupt? -} - -SIO_WRITE sioWriteMultitap(u8 data) -{ - static u8 siocmd = 0; - //sio.packetsize++; - - switch(sio.bufCount) - { - case 0: - if(IsMtapPresent(sio.port)) - { - SIO_STAT_READY(); - DEVICE_PLUGGED(); - sio.buf[0] = 0xFF; - sio.buf[1] = 0x80; - sio.buf[2] = 0x5A; - } - else - { - DEVICE_UNPLUGGED(); - sio.buf[0] = 0x00; - siomode = SIO_DUMMY; - } - break; - - case 1: - siocmd = data; - switch(data) - { - case 0x12: // Pads supported /// slots supported, port 0,1 - case 0x13: // Memcards supported //// slots supported, port 2,3 - sio.buf[3] = 0x04; - sio.buf[4] = 0x00; - sio.buf[5] = 0x5A; // 0x66 here, disables the thing. - //sio.bufSize = 5; + case Sio0Interrupt::TEST_EVENT: + iopIntcIrq(7); break; - - case 0x21: // Select pad - case 0x22: // Select memcard - sio.buf[3] = 0x00; - sio.buf[4] = 0x00; - sio.buf[5] = 0x00; - sio.buf[6] = 0x5A; - //sio.bufSize = 6; + case Sio0Interrupt::STAT_READ: + ClearStatAcknowledge(); + break; + case Sio0Interrupt::TX_DATA_WRITE: break; - default: - DevCon.Warning("%s cmd: %02X??", __FUNCTION__, data); - sio.buf[3] = 0x00; - sio.buf[4] = 0x00; - sio.buf[5] = 0x00; - sio.buf[6] = 0x00; + Console.Error("%s(%d) Invalid parameter", __FUNCTION__, sio0Interrupt); + assert(false); break; - - } - break; - - case 2: // Respond to 0x21/0x22 with requested port - switch(siocmd) - { - case 0x21: - { - sio.slot[sio.port] = data; - - u32 ret = PADsetSlot(sio.port+1, data+1); - sio.buf[5] = ret? data : 0xFF; - sio.buf[6] = ret? 0x5A : 0x66; - } - break; - - case 0x22: - { - sio.slot[sio.port] = data; - sio.buf[5] = data; - } - break; - } - break; - - case 3: break; - case 5: break; - case 6: break; - - //default: sio.buf[sio.bufCount] = 0x00; break; } - - sioInterrupt(); + + if (!(psxRegs.interrupt & (1 << IopEvt_SIO))) + { + PSX_INT(IopEvt_SIO, PSXCLK / 250000); // PSXCLK/250000); + } } -SIO_WRITE MemcardResponse() +u8 Sio0::GetTxData() { - if(sio.bufSize > 1) - { - sio.buf[sio.bufSize - 1] = 0x2B; - sio.buf[sio.bufSize - 0] = mcd->term; - } + Sio0Log.WriteLn("%s() SIO0 TX_DATA Read (%02X)", __FUNCTION__, txData); + return txData; } -SIO_WRITE memcardAuth(u8 data) +u8 Sio0::GetRxData() { - static bool doXorCheck = false; - static u8 xorResult = 0; - - if(sio.bufCount == 2) - { - switch(data) - { - case 0x01: case 0x02: case 0x04: - case 0x0F: case 0x11: case 0x13: - doXorCheck = true; - xorResult = 0; - sio.buf[3] = 0x2B; - sio.buf[sio.bufSize] = mcd->term; - break; - - default: - doXorCheck = false; - MemcardResponse(); - break; - } - } - else if(doXorCheck) - { - switch(sio.bufCount) - { - case 3: break; - case 12: sio.buf[12] = xorResult; break; - default: xorResult ^= data; break; - }; - } + Sio0Log.WriteLn("%s() SIO0 RX_DATA Read (%02X)", __FUNCTION__, rxData); + stat |= (SIO0_STAT::TX_READY | SIO0_STAT::TX_EMPTY); + stat &= ~(SIO0_STAT::RX_FIFO_NOT_EMPTY); + return rxData; } -SIO_WRITE memcardErase(u8 data) +u32 Sio0::GetStat() { - switch(sio.bufCount) - { - case 0: - if(data != 0x81) sio.bufCount = -1; - break; - - case 1: - { - u8 header[] = {0xFF, 0xFF, 0xFF, 0x2B, mcd->term}; - - switch(data) - { - case 0x82: // Erase - //siomode = SIO_DUMMY; // Nothing more to do here. - memcpy(sio.buf, &header[1], 4); - sio.bufSize = 3; - mcd->EraseBlock(); - break; - - default: - DevCon.Warning("%s cmd: %02X??", __FUNCTION__, data); - sio.bufCount = -1; - //sio.bufSize = 3; - //sio.bufCount = 4; - break; - } - } - sioInterrupt(); - break; - - default: - if(sio.bufCount > sio.bufSize) - { - if(data == 0x81) - { - SIO_STAT_READY(); - sio.bufCount = 0x00; - } - } - break; - } -} - -SIO_WRITE memcardWrite(u8 data) -{ - static u8 checksum_pos = 0; - static u8 transfer_size = 0; - static bool once = false; - - switch(sio.bufCount) - { - case 0: - if(data != 0x81) sio.bufCount = -1; - break; - - case 1: - { - u8 header[] = {0xFF, 0xFF, 0xFF, 0x2B, mcd->term}; - - switch(data) - { - case 0x42: // Write - memcpy(sio.buf, header, 4); - once = true; - break; - - case 0x81: // Commit - if(once) - { - siomode = SIO_DUMMY; // Nothing more to do here. - memcpy(sio.buf, &header[1], 4); - sio.bufSize = 3; - - sio2.packet.recvVal1 = 0x1600; // Writing - sio2.packet.recvVal3 = 0x8C; - - once = false; - break; - } - [[fallthrough]]; - - default: - DevCon.Warning("%s cmd: %02X??", __FUNCTION__, data); - sio.bufCount = -1; - //sio.bufSize = 3; - //sio.bufCount = 4; - break; - } - - } - sioInterrupt(); - break; - - case 2: - transfer_size = data; - - // Note: coverity wrongly detects a buffer overflow. Because data + 5 could be > 512... - // So let's add a mask-nop to avoid various useless reports. - sio.buf[(data & 0xFF) + 5] = mcd->term; - sio.bufSize = data + 5; - checksum_pos = data + 4; - break; - - default: - - if(sio.bufCount < checksum_pos) - { - sio.buf[sio.bufCount+1] = data; - } - else if(sio.bufCount == checksum_pos) - { - u8 xor_check = mcd->DoXor(&sio.buf[4], checksum_pos - 4); - - if(xor_check != sio.buf[sio.bufCount]) - Console.Warning("MemWrite: Checksum invalid! XOR: %02X, IN: %02X\n", xor_check, sio.buf[sio.bufCount]); - - sio.buf[sio.bufCount] = xor_check; - mcd->Write(&sio.buf[4], transfer_size); - mcd->transferAddr += transfer_size; - } - - if(sio.bufCount > sio.bufSize) - { - if(data == 0x81) - { - SIO_STAT_READY(); - sio.bufCount = 0x00; - } - } - break; - } -} - -SIO_WRITE memcardRead(u8 data) -{ - /*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; - - switch(sio.bufCount) - { - case 0: - if(data != 0x81) sio.bufCount = -1; - break; - - case 1: - { - u8 header[] = {0xFF, 0xFF, 0xFF, 0x2B, mcd->term}; - - switch(data) - { - case 0x43: // Read - memcpy(sio.buf, header, 4); - once = true; - break; - - case 0x81: // Commit - if(once) - { - siomode = SIO_DUMMY; // Nothing more to do here. - memcpy(sio.buf, &header[1], 4); - sio.bufSize = 3; - - sio2.packet.recvVal1 = 0x1700; // Reading - sio2.packet.recvVal3 = 0x8C; - - once = false; - break; - } - [[fallthrough]]; - - default: - DevCon.Warning("%s cmd: %02X??", __FUNCTION__, data); - sio.bufCount = -1; - //sio.bufSize = 3; - //sio.bufCount = 4; - break; - } - } - sioInterrupt(); - break; - - case 2: - transfer_size = data; - - mcd->Read(&sio.buf[4], transfer_size); - mcd->transferAddr += transfer_size; - - sio.buf[transfer_size + 4] = mcd->DoXor(&sio.buf[4], transfer_size); - sio.buf[transfer_size + 5] = mcd->term; - sio.bufSize = transfer_size + 5; - break; - - default: - if(sio.bufCount > sio.bufSize) - { - if(data == 0x81) - { - SIO_STAT_READY(); - sio.bufCount = 0x00; - } - } - break; - } -} - - -SIO_WRITE memcardSector(u8 data) -{ - static u8 xor_check = 0; - - switch(sio.bufCount) - { - case 2: mcd->sectorAddr = data << 0; xor_check = data; break; - case 3: mcd->sectorAddr |= data << 8; xor_check ^= data; break; - case 4: mcd->sectorAddr |= data << 16; xor_check ^= data; break; - case 5: mcd->sectorAddr |= data << 24; xor_check ^= data; break; - case 6: mcd->goodSector = data == xor_check; break; - case 8: mcd->transferAddr = (512+16) * mcd->sectorAddr; break; - case 9: - { - switch(sio.cmd) - { - case 0x21: siomode = SIO_MEMCARD_ERASE; break; - case 0x22: siomode = SIO_MEMCARD_WRITE; break; - case 0x23: siomode = SIO_MEMCARD_READ; break; - } - - memset8<0xFF>(sio.buf); - sio.bufCount = -1; - } - } -} - -SIO_WRITE memcardInit() -{ - mcd = &mcds[sio.GetPort()][sio.GetSlot()]; - - // forced ejection logic. Technically belongs in the McdIsPresent handler. - - bool forceEject = false; - - if(mcd->ForceEjection_Timeout) - { - if(mcd->ForceEjection_Timeout == FORCED_MCD_EJECTION_MAX_TRIES && mcd->IsPresent()) - Console.WriteLn( Color_Green, "Auto-ejecting memcard [port:%d, slot:%d]", sio.GetPort(), sio.GetSlot()); - - mcd->ForceEjection_Timeout--; - forceEject = true; - - u32 numTimesAccessed = FORCED_MCD_EJECTION_MAX_TRIES - mcd->ForceEjection_Timeout; - - //minimum tries reached. start counting millisec timeout. - if(numTimesAccessed == FORCED_MCD_EJECTION_MIN_TRIES) - mcd->ForceEjection_Timestamp = Common::Timer::GetCurrentValue(); - - if(numTimesAccessed > FORCED_MCD_EJECTION_MIN_TRIES) - { - if(Common::Timer::ConvertValueToMilliseconds(Common::Timer::GetCurrentValue() - mcd->ForceEjection_Timestamp) >= FORCED_MCD_EJECTION_MAX_MS_AFTER_MIN_TRIES) - { - DevCon.Warning( "Auto-eject: Timeout reached after mcd was accessed %d times [port:%d, slot:%d]", numTimesAccessed, sio.GetPort(), sio.GetSlot()); - mcd->ForceEjection_Timeout = 0; //Done. on next sio access the card will be seen as inserted. - } - } - - if(mcd->ForceEjection_Timeout == 0 && mcd->IsPresent()) - Console.WriteLn( Color_Green, "Re-inserting auto-ejected memcard [port:%d, slot:%d]", sio.GetPort(), sio.GetSlot()); - } - - if(!forceEject && mcd->IsPresent()) - { - DEVICE_PLUGGED(); - siomode = mcd->IsPSX() ? SIO_MEMCARD_PSX : SIO_MEMCARD; - } - else - { - DEVICE_UNPLUGGED(); - siomode = SIO_DUMMY; - } - - -} - -SIO_WRITE sioWriteMemcard(u8 data) -{ - switch(sio.bufCount) - { - case 0: - SIO_STAT_READY(); - memcardInit(); - sioInterrupt(); - break; - - case 1: - sio.cmd = data; - switch(data) - { - case 0x21: // SET_SECTOR_ERASE - case 0x22: // SET_SECTOR_WRITE - case 0x23: // SET_SECTOR_READ - sio2.packet.recvVal3 = 0x8C; - MemcardResponse(); - siomode = SIO_MEMCARD_SECTOR; - break; - - case 0x26: // GET_SPECS ? - { - sio2.packet.recvVal3 = 0x83; - - mc_command_0x26_tag cmd; - McdSizeInfo info; - - mcd->GetSizeInfo(info); - - cmd.field_151 = 0x2B; - cmd.sectorSize = info.SectorSize; - cmd.eraseBlocks = info.EraseBlockSizeInSectors; - cmd.mcdSizeInSectors = info.McdSizeInSectors; - cmd.mc_xor = info.Xor; - cmd.Z = mcd->term; - - memcpy(&sio.buf[2], &cmd, sizeof(mc_command_0x26_tag)); - } - break; - - case 0x27: // SET_TERMINATOR - sio2.packet.recvVal3 = 0x8B; - break; - - case 0x28: // GET_TERMINATOR - sio2.packet.recvVal3 = 0x8B; - sio.buf[2] = 0x2B; - sio.buf[3] = mcd->term; - sio.buf[4] = 0x55; // 0x55 or 0xFF ? - break; - - // If the PS2 commands fail, it falls back into PSX mode - case 0x52: // PSX 'R'ead - case 0x53: // PSX 'S'tate - case 0x57: // PSX 'W'rite - case 0x58: // PSX Pocketstation - siomode = SIO_DUMMY; - break; - - case 0xF0: // Auth stuff - siomode = SIO_MEMCARD_AUTH; - break; - - case 0x11: // On Boot/Probe - case 0x12: // On Write/Delete/Recheck? - sio2.packet.recvVal3 = 0x8C; - [[fallthrough]]; - - case 0x81: // Checked right after copy/delete - case 0xBF: // Wtf?? On game booting? - case 0xF3: // Reset? - case 0xF7: // No idea - MemcardResponse(); - siomode = SIO_DUMMY; - break; - - default: - DevCon.Warning("%s cmd: %02X??", __FUNCTION__, data); - siomode = SIO_DUMMY; - break; - } - sioInterrupt(); - break; - - case 2: - switch(sio.cmd) - { - case 0x27: // SET_TERMINATOR - mcd->term = data; - MemcardResponse(); - break; - } - break; - } -} - -SIO_WRITE sioWriteMemcardPSX(u8 data) -{ - switch(sio.bufCount) - { - case 0: // Same init stuff... - SIO_STAT_READY(); - memcardInit(); - sioInterrupt(); - break; - - case 1: - sio.cmd = data; - switch(data) - { - case 0x53: // PSX 'S'tate // haven't seen it happen yet - sio.buf[1] = mcd->FLAG; - memcpy(&sio.buf[2], memcard_psx, 8); - siomode = SIO_DUMMY; - break; - - case 0x52: // PSX 'R'ead / Probe - case 0x57: // PSX 'W'rite - case 0x58: // POCKETSTATION!! Grrrr // Lots of love to the PS2DEV/ps2sdk - sio.buf[1] = 0x00; //mcd->FLAG; - sio.buf[2] = 0x5A; // probe end, success "0x5A" - sio.buf[3] = 0x5D; - sio.buf[4] = 0x00; - break; - // Old handing for Pocketstation, effectively discarded the calls. - // Keeping it around for reference. - //case 0x58: // POCKETSTATION!! Grrrr // Lots of love to the PS2DEV/ps2sdk - // DEVICE_UNPLUGGED(); // Check is for 0x01000 on stat - // siomode = SIO_DUMMY; - // break; - - default: - //printf("%s cmd: %02X??\n", __FUNCTION__, data); - siomode = SIO_DUMMY; - break; - } - sioInterrupt(); - break; - - case 2: break; - case 3: break; - - case 4: - sio.buf[5] = data; - mcd->sectorAddr = data << 8; - break; - - case 5: - sio.buf[6] = data; - mcd->sectorAddr |= data; - mcd->goodSector = !(mcd->sectorAddr > 0x3FF); - mcd->transferAddr = 128 * mcd->sectorAddr; - break; - - case 6: - if(sio.cmd == 0x52) - { - // READ - - if(!mcd->goodSector) - { - memset8<0xFF>(sio.buf); - siomode = SIO_DUMMY; - } - else - { - sio.buf[8] = sio.buf[5]; - sio.buf[9] = sio.buf[6]; - sio.buf[6] = 0x5C; - sio.buf[7] = 0x5D; - - mcd->Read(&sio.buf[10], 0x80); - - sio.buf[138] = mcd->DoXor(&sio.buf[8], 0x80 + 2); - sio.buf[139] = 0x47; - siomode = SIO_DUMMY; - } - } - else - { - sio.buf[sio.bufCount+1] = data; - } - break; - - default: - // WRITE - - sio.buf[sio.bufCount+1] = data; - - if(sio.bufCount == 134) - { - u8 xorcheck = mcd->DoXor(&sio.buf[5], 0x80+2); - - sio.buf[135] = 0x5C; - sio.buf[136] = 0x5D; - - // (47h=Good, 4Eh=BadChecksum, FFh=BadSector) - sio.buf[137] = data == xorcheck ? 0x47 : 0x4E; - if(!mcd->goodSector) sio.buf[137] = 0xFF; - else mcd->Write(&sio.buf[7], 0x80); - siomode = SIO_DUMMY; - } - break; - } -} - -SIO_WRITE sioWriteInfraRed(u8 data) -{ - SIO_STAT_READY(); - DEVICE_PLUGGED(); - siomode = SIO_DUMMY; - sioInterrupt(); -} - -//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 - sioInterrupt(); return; - if ((sio.StatReg & IRQ)) { sioInterrupt(); 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))) { sioInterrupt(); return; } - if ((sio.CtrlReg & ACK_INT_EN) && (sio.StatReg & ACK_INP)) { sioInterrupt(); return; } - //The following one may be incorrect. - //if ((sio.CtrlReg & RX_INT_EN) && ((byteCnt >= (1<< ((sio.CtrlReg & RX_BYTES_INT) >>8))) ? 1:0) ) { sioInterrupt(); 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; - case SIO_CONTROLLER: sioWriteController(data); break; - case SIO_MULTITAP: sioWriteMultitap(data); break; - case SIO_INFRARED: sioWriteInfraRed(data); break; - case SIO_MEMCARD: sioWriteMemcard(data); break; - case SIO_MEMCARD_AUTH: memcardAuth(data); break; - case SIO_MEMCARD_ERASE: memcardErase(data); break; - case SIO_MEMCARD_WRITE: memcardWrite(data); break; - case SIO_MEMCARD_READ: memcardRead(data); break; - case SIO_MEMCARD_SECTOR: memcardSector(data); break; - 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. - - sioInterrupt(); - //chkTriggerInt(); - //Console.WriteLn( "SIO0 WR DATA COMMON %02X INT_STAT= %08X IOPpc= %08X " , data, psxHu32(0x1070), psxRegs.pc); - byteCnt++; -} - -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; - - //printf("RegCtrl: %04X, %d\n", value, sio.bufCount); - - sio.CtrlReg = value & ~RESET_ERR; - if (value & RESET_ERR) sio.StatReg &= ~IRQ; - - if ((sio.CtrlReg & SIO_RESET) || (!sio.CtrlReg)) - { - siomode = SIO_START; - - tcount[0] = 0; - tcount[1] = 0; - - sio.StatReg = TX_RDY | TX_EMPTY; - psxRegs.interrupt &= ~(1<autoEjectTicks) + { + SetRxData(0x00); + mcd->autoEjectTicks--; + + if (mcd->autoEjectTicks == 0) + { + Host::AddKeyedFormattedOSDMessage( + StringUtil::StdStringFromFormat("AutoEjectSlotClear%u%u", port, slot), + 10.0f, + "Memory card in port %d / slot %d reinserted", + port + 1, + slot + 1); + } + + return; + } + + // If memcard is missing, not PS1, or auto ejected, do not let SIO0 stage advance, + // reply with dead air and no ACK. + if (!mcd->IsPresent() || !mcd->IsPSX()) + { + SetRxData(0x00); + return; + } + + Acknowledge(); + break; + } + + SetRxData(res); + sioStage = SioStage::WAITING_COMMAND; + break; + case SioStage::WAITING_COMMAND: + stat &= ~(SIO0_STAT::TX_READY); + + if (IsPadCommand(value)) + { + res = PADpoll(value); + SetRxData(res); + + if (!PADcomplete()) + { + Acknowledge(); + } + + sioStage = SioStage::WORKING; + } + else if (IsMemcardCommand(value)) + { + SetRxData(flag); + Acknowledge(); + sioCommand = value; + sioStage = SioStage::WORKING; + } + else if (IsPocketstationCommand(value)) + { + // Set the line low, no acknowledge. + SetRxData(0x00); + sioStage = SioStage::IDLE; + } + else + { + Console.Error("%s(%02X) Bad SIO command", __FUNCTION__, value); + SetRxData(0xff); + SoftReset(); + } + + break; + case SioStage::WORKING: + switch (sioMode) + { + case SioMode::PAD: + res = PADpoll(value); + SetRxData(res); + + if (!PADcomplete()) + { + Acknowledge(); + } + + break; + case SioMode::MEMCARD: + SetRxData(Memcard(value)); + break; + default: + Console.Error("%s(%02X) Unhandled SioMode: %02X", __FUNCTION__, value, sioMode); + SetRxData(0xff); + SoftReset(); + break; + } + + break; + default: + Console.Error("%s(%02X) Unhandled SioStage: %02X", __FUNCTION__, value, static_cast(sioStage)); + SetRxData(0xff); + SoftReset(); + break; + } + + Interrupt(Sio0Interrupt::TX_DATA_WRITE); +} + +void Sio0::SetRxData(u8 value) +{ + Sio0Log.WriteLn("%s(%02X) SIO0 RX_DATA Write", __FUNCTION__, value); + rxData = value; +} + +void Sio0::SetStat(u32 value) +{ + Sio0Log.Error("%s(%08X) SIO0 STAT Write", __FUNCTION__, value); +} + +void Sio0::SetMode(u16 value) +{ + Sio0Log.WriteLn("%s(%04X) SIO0 MODE Write", __FUNCTION__, value); + mode = value; +} + +void Sio0::SetCtrl(u16 value) +{ + Sio0Log.WriteLn("%s(%04X) SIO0 CTRL Write", __FUNCTION__, value); + ctrl = value; + port = (ctrl & SIO0_CTRL::PORT) > 0; + + // CTRL appears to be set to 0 between every "transaction". + // Not documented anywhere, but we'll use this to "reset" + // the SIO0 state, particularly during the annoying probes + // to memcards that occur when a game boots. + if (ctrl == 0) + { + g_MemoryCardProtocol.ResetPS1State(); + SoftReset(); + } + + // If CTRL acknowledge, reset STAT bits 3 and 9 + if (ctrl & SIO0_CTRL::ACK) + { + stat &= ~(SIO0_STAT::IRQ | SIO0_STAT::RX_PARITY_ERROR); + } + + if (ctrl & SIO0_CTRL::RESET) + { + stat = 0; + ctrl = 0; + mode = 0; + SoftReset(); + } +} + +void Sio0::SetBaud(u16 value) +{ + Sio0Log.WriteLn("%s(%04X) SIO0 BAUD Write", __FUNCTION__, value); + baud = value; +} + +bool Sio0::IsPadCommand(u8 command) +{ + return command >= PadCommand::UNK_0 && command <= PadCommand::ANALOG; +} + +bool Sio0::IsMemcardCommand(u8 command) +{ + return command == MemcardCommand::PS1_READ || command == MemcardCommand::PS1_STATE || command == MemcardCommand::PS1_WRITE; +} + +bool Sio0::IsPocketstationCommand(u8 command) +{ + return command == MemcardCommand::PS1_POCKETSTATION; +} + +u8 Sio0::Pad(u8 value) +{ + if (PADcomplete()) + { + padStarted = false; + } + else if (!padStarted) + { + padStarted = true; + PADstartPoll(port, slot); + Acknowledge(); + } + + return PADpoll(value); +} + +u8 Sio0::Memcard(u8 value) +{ + switch (sioCommand) + { + case MemcardCommand::PS1_READ: + return g_MemoryCardProtocol.PS1Read(value); + case MemcardCommand::PS1_STATE: + return g_MemoryCardProtocol.PS1State(value); + case MemcardCommand::PS1_WRITE: + return g_MemoryCardProtocol.PS1Write(value); + case MemcardCommand::PS1_POCKETSTATION: + return g_MemoryCardProtocol.PS1Pocketstation(value); + default: + Console.Error("%s(%02X) Unhandled memcard command (%02X)", __FUNCTION__, value, sioCommand); + SoftReset(); + break; + } + + return 0xff; +} + +// ============================================================================ +// SIO2 +// ============================================================================ + +void Sio2::UpdateInputRecording(u8 dataIn, u8 dataOut) +{ + if (EmuConfig.EnableRecordingTools) + { + // Ignore multitapped slots, only allow physical ports + if (slot == 0) + { + g_InputRecording.ControllerInterrupt(port, fifoOut.size(), dataIn, dataOut); + } + } +} + + +Sio2::Sio2() +{ + this->FullReset(); +} + +Sio2::~Sio2() = default; + +void Sio2::SoftReset() +{ + send3Read = false; + send3Position = 0; + commandLength = 0; + processedLength = 0; + // Clear dmaBlockSize, in case the next SIO2 command is not sent over DMA11. + dmaBlockSize = 0; + send3Complete = false; + + // Anything in fifoIn which was not necessary to consume should be cleared out prior to the next SIO2 cycle. + while (!fifoIn.empty()) + { + fifoIn.pop_front(); + } +} + +void Sio2::FullReset() +{ + this->SoftReset(); + + for (size_t i = 0; i < send3.size(); i++) + { + send3.at(i) = 0; + } + + for (size_t i = 0; i < send1.size(); i++) + { + send1.at(i) = 0; + send2.at(i) = 0; + } + + dataIn = 0; + dataOut = 0; + SetCtrl(Sio2Ctrl::SIO2MAN_RESET); + SetRecv1(Recv1::DISCONNECTED); + recv2 = Recv2::DEFAULT; + recv3 = Recv3::DEFAULT; + unknown1 = 0; + unknown2 = 0; + iStat = 0; + + port = 0; + slot = 0; + + while (!fifoOut.empty()) + { + fifoOut.pop_front(); + } + + for (int i = 0; i < 2; i++) + { + for (int j = 0; j < 4; j++) + { + mcds[i][j].term = 0x55; + mcds[i][j].port = i; + mcds[i][j].slot = j; + mcds[i][j].FLAG = 0x08; + mcds[i][j].autoEjectTicks = 0; + } + } + + mcd = &mcds[0][0]; +} + + +void Sio2::Interrupt() +{ + iopIntcIrq(17); +} + +void Sio2::SetCtrl(u32 value) +{ + this->ctrl = value; + + if (this->ctrl & Sio2Ctrl::START_TRANSFER) + { + Interrupt(); + } +} + +void Sio2::SetSend3(size_t position, u32 value) +{ + this->send3.at(position) = value; + + if (position == 0) + { + SoftReset(); + } +} + +void Sio2::SetRecv1(u32 value) +{ + this->recv1 = value; +} + +void Sio2::Pad() +{ + // Send PAD our current port, and get back whatever it says the first response byte should be. + const u8 firstResponseByte = PADstartPoll(port, slot); + fifoOut.push_back(firstResponseByte); + UpdateInputRecording(0x01, firstResponseByte); + // Some games will refuse to read ALL pads, if RECV1 is not set to the CONNECTED value when ANY pad is polled, + // REGARDLESS of whether that pad is truly connected or not. + SetRecv1(Recv1::CONNECTED); + + // Then for every byte in fifoIn, pass to PAD and see what it kicks back to us. + while (!fifoIn.empty()) + { + const u8 commandByte = fifoIn.front(); + fifoIn.pop_front(); + const u8 responseByte = PADpoll(commandByte); + fifoOut.push_back(responseByte); + UpdateInputRecording(commandByte, responseByte); + } +} + +void Sio2::Multitap() +{ + fifoOut.push_back(0x00); + + const bool multitapEnabled = (port == 0 && EmuConfig.MultitapPort0_Enabled) || (port == 1 && EmuConfig.MultitapPort1_Enabled); + SetRecv1(multitapEnabled ? Recv1::CONNECTED : Recv1::DISCONNECTED); + + if (multitapEnabled) + { + g_MultitapProtocol.SendToMultitap(); + } + else + { + while (fifoOut.size() < commandLength) + { + fifoOut.push_back(0x00); + } + } +} + +void Sio2::Infrared() +{ + SetRecv1(Recv1::DISCONNECTED); + + const u8 commandByte = fifoIn.front(); + fifoIn.pop_front(); + const u8 responseByte = 0xff; + + while (fifoOut.size() < commandLength) + { + fifoOut.push_back(responseByte); + } +} + +void Sio2::Memcard() +{ + mcd = &mcds[port][slot]; + + // Check if auto ejection is active. If so, set RECV1 to DISCONNECTED, + // and zero out the fifo to simulate dead air over the wire. + if (mcd->autoEjectTicks) + { + SetRecv1(Recv1::DISCONNECTED); + fifoOut.push_back(0x00); // Because Sio2::Write pops the first fifoIn member + + while (!fifoIn.empty()) + { + fifoIn.pop_front(); + fifoOut.push_back(0x00); + } + + mcd->autoEjectTicks--; + + if (mcd->autoEjectTicks == 0) + { + Host::AddKeyedFormattedOSDMessage( + StringUtil::StdStringFromFormat("AutoEjectSlotClear%u%u", port, slot), + 10.0f, + "Memory card in port %d / slot %d reinserted", + port + 1, + slot + 1); + } + + return; + } + + SetRecv1(mcd->IsPresent() ? Recv1::CONNECTED : Recv1::DISCONNECTED); + + const u8 commandByte = fifoIn.front(); + fifoIn.pop_front(); + const u8 responseByte = mcd->IsPresent() ? 0x00 : 0xff; + fifoOut.push_back(responseByte); + // Technically, the FLAG byte is only for PS1 memcards. However, + // since this response byte is still a dud on PS2 memcards, we can + // basically just cheat and always make this our second response byte for memcards. + fifoOut.push_back(mcd->FLAG); + u8 ps1Input = 0; + u8 ps1Output = 0; + + switch (commandByte) + { + case MemcardCommand::PROBE: + g_MemoryCardProtocol.Probe(); + break; + case MemcardCommand::UNKNOWN_WRITE_DELETE_END: + g_MemoryCardProtocol.UnknownWriteDeleteEnd(); + break; + case MemcardCommand::SET_ERASE_SECTOR: + case MemcardCommand::SET_WRITE_SECTOR: + case MemcardCommand::SET_READ_SECTOR: + g_MemoryCardProtocol.SetSector(); + break; + case MemcardCommand::GET_SPECS: + g_MemoryCardProtocol.GetSpecs(); + break; + case MemcardCommand::SET_TERMINATOR: + g_MemoryCardProtocol.SetTerminator(); + break; + case MemcardCommand::GET_TERMINATOR: + g_MemoryCardProtocol.GetTerminator(); + break; + case MemcardCommand::WRITE_DATA: + g_MemoryCardProtocol.WriteData(); + break; + case MemcardCommand::READ_DATA: + g_MemoryCardProtocol.ReadData(); + break; + case MemcardCommand::PS1_READ: + g_MemoryCardProtocol.ResetPS1State(); + + while (!fifoIn.empty()) + { + ps1Input = fifoIn.front(); + ps1Output = g_MemoryCardProtocol.PS1Read(ps1Input); + fifoIn.pop_front(); + fifoOut.push_back(ps1Output); + } + + break; + case MemcardCommand::PS1_STATE: + g_MemoryCardProtocol.ResetPS1State(); + + while (!fifoIn.empty()) + { + ps1Input = fifoIn.front(); + ps1Output = g_MemoryCardProtocol.PS1State(ps1Input); + fifoIn.pop_front(); + fifoOut.push_back(ps1Output); + } + + break; + case MemcardCommand::PS1_WRITE: + g_MemoryCardProtocol.ResetPS1State(); + + while (!fifoIn.empty()) + { + ps1Input = fifoIn.front(); + ps1Output = g_MemoryCardProtocol.PS1Write(ps1Input); + fifoIn.pop_front(); + fifoOut.push_back(ps1Output); + } + + break; + case MemcardCommand::PS1_POCKETSTATION: + g_MemoryCardProtocol.ResetPS1State(); + + while (!fifoIn.empty()) + { + ps1Input = fifoIn.front(); + ps1Output = g_MemoryCardProtocol.PS1Pocketstation(ps1Input); + fifoIn.pop_front(); + fifoOut.push_back(ps1Output); + } + + break; + case MemcardCommand::READ_WRITE_END: + g_MemoryCardProtocol.ReadWriteEnd(); + break; + case MemcardCommand::ERASE_BLOCK: + g_MemoryCardProtocol.EraseBlock(); + break; + case MemcardCommand::UNKNOWN_BOOT: + g_MemoryCardProtocol.UnknownBoot(); + break; + case MemcardCommand::AUTH_XOR: + g_MemoryCardProtocol.AuthXor(); + break; + case MemcardCommand::AUTH_F3: + g_MemoryCardProtocol.AuthF3(); + break; + case MemcardCommand::AUTH_F7: + g_MemoryCardProtocol.AuthF7(); + break; + default: + Console.Warning("%s() Unhandled memcard command %02X, things are about to break!", __FUNCTION__, commandByte); + break; + } +} + +void Sio2::Write(u8 data) +{ + Sio2Log.WriteLn("%s(%02X) SIO2 DATA Write", __FUNCTION__, data); + + if (!send3Read) + { + // No more SEND3 positions to access, but the game is still sending us SIO2 writes. Lets ignore them. + if (send3Position > send3.size()) + { + Console.Warning("%s(%02X) Received data after exhausting all SEND3 values!", __FUNCTION__, data); + return; + } + + const u32 currentSend3 = send3.at(send3Position); + port = currentSend3 & Send3::PORT; + commandLength = (currentSend3 >> 8) & Send3::COMMAND_LENGTH_MASK; + send3Read = true; + + // The freshly read SEND3 position had a length of 0, so we are done handling SIO2 commands until + // the next SEND3 writes. + if (commandLength == 0) + { + send3Complete = true; + } + + // If the prior command did not need to fully pop fifoIn, do so now, + // so that the next command isn't trying to read the last command's leftovers. + while (!fifoIn.empty()) + { + fifoIn.pop_front(); + } + } + + if (send3Complete) + { + return; + } + + fifoIn.push_back(data); + + // We have received as many command bytes as we expect, and... + // + // ... These were from direct writes into IOP memory (DMA block size is zero when direct writes occur) + // ... These were from SIO2 DMA (DMA block size is non-zero when SIO2 DMA occurs) + if ((fifoIn.size() == sio2.commandLength && sio2.dmaBlockSize == 0) || fifoIn.size() == sio2.dmaBlockSize) + { + // Go ahead and prep so the next write triggers a load of the new SEND3 value. + sio2.send3Read = false; + sio2.send3Position++; + + // Check the SIO mode + const u8 sioMode = fifoIn.front(); + fifoIn.pop_front(); + + switch (sioMode) + { + case SioMode::PAD: + this->Pad(); + break; + case SioMode::MULTITAP: + this->Multitap(); + break; + case SioMode::INFRARED: + this->Infrared(); + break; + case SioMode::MEMCARD: + this->Memcard(); + break; + default: + Console.Error("%s(%02X) Unhandled SIO mode %02X", __FUNCTION__, data, sioMode); + fifoOut.push_back(0x00); + SetRecv1(Recv1::DISCONNECTED); + break; + } + + // If command was sent over SIO2 DMA, align fifoOut to the block size + if (sio2.dmaBlockSize > 0) + { + const size_t dmaDiff = fifoOut.size() % sio2.dmaBlockSize; + + if (dmaDiff > 0) + { + const size_t padding = sio2.dmaBlockSize - dmaDiff; + + for (size_t i = 0; i < padding; i++) + { + fifoOut.push_back(0x00); + } + } + } + } +} + +u8 Sio2::Read() +{ + u8 ret = 0x00; + + if (!fifoOut.empty()) + { + ret = fifoOut.front(); + fifoOut.pop_front(); + } + else + { + Console.Warning("%s() fifoOut underflow! Returning 0x00.", __FUNCTION__); + } + + Sio2Log.WriteLn("%s() SIO2 DATA Read (%02X)", __FUNCTION__, ret); + return ret; } void sioNextFrame() { @@ -1007,7 +859,87 @@ void sioSetGameSerial( const std::string& serial ) { for ( uint port = 0; port < 2; ++port ) { for ( uint slot = 0; slot < 4; ++slot ) { if ( mcds[port][slot].ReIndex( serial ) ) { - SetForceMcdEjectTimeoutNow( port, slot ); + AutoEject::Set( port, slot ); + } + } + } +} + +void SaveStateBase::sio2Freeze() +{ + FreezeTag("sio2"); + + if (IsSaving()) + { + std::deque::iterator iter; + size_t backupSize; + + // Copy fifoIn + if (fifoIn.size()) + { + sio2.fifoInBackup = std::make_unique(fifoIn.size()); + iter = fifoIn.begin(); + backupSize = 0; + + while (iter != fifoIn.end()) + { + const u8 val = *iter++; + sio2.fifoInBackup.get()[backupSize++] = val; + } + + sio2.fifoInBackupSize = backupSize; + } + else + { + sio2.fifoInBackupSize = 0; + } + + // Copy fifoOut + if (fifoOut.size()) + { + sio2.fifoOutBackup = std::make_unique(fifoOut.size()); + iter = fifoOut.begin(); + backupSize = 0; + + while (iter != fifoOut.end()) + { + const u8 val = *iter++; + sio2.fifoOutBackup.get()[backupSize++] = val; + } + + sio2.fifoOutBackupSize = backupSize; + } + else + { + sio2.fifoOutBackupSize = 0; + } + } + + Freeze(sio2); + + if (IsLoading()) + { + AutoEject::SetAll(); + + // Restore fifoIn + fifoIn.clear(); + + if (sio2.fifoInBackupSize) + { + for (size_t i = 0; i < sio2.fifoInBackupSize; i++) + { + fifoIn.push_back(sio2.fifoInBackup.get()[i]); + } + } + + // Restore fifoOut + fifoOut.clear(); + + if (sio2.fifoOutBackupSize) + { + for (size_t j = 0; j < sio2.fifoOutBackupSize; j++) + { + fifoOut.push_back(sio2.fifoOutBackup.get()[j]); } } } @@ -1015,45 +947,8 @@ void sioSetGameSerial( const std::string& serial ) { void SaveStateBase::sioFreeze() { - // CRCs for memory cards. - u64 m_mcdCRCs[2][8]; - - FreezeTag( "sio" ); - Freeze( sio ); - - if( IsSaving() ) - { - for( uint port=0; port<2; ++port ) - for( uint slot=0; slot<4; ++slot ) - m_mcdCRCs[port][slot] = mcds[port][slot].GetChecksum(); - } - - Freeze( m_mcdCRCs ); - - if( IsLoading() && EmuConfig.McdEnableEjection ) - { - // Notes on the ForceEjectionTimeout: - // * TOTA works with values as low as 20 here. - // It "times out" with values around 1800 (forces user to check the memcard - // twice to find it). Other games could be different. :| - // - // * At 64: Disgaea 1 and 2, and Grandia 2 end up displaying a quick "no memcard!" - // notice before finding the memorycard and re-enumerating it. A very minor - // annoyance, but no breakages. - - // * GuitarHero will break completely with almost any value here, by design, because - // it has a "rule" that the memcard should never be ejected during a song. So by - // ejecting it, the game freezes (which is actually good emulation, but annoying!) - - for( u8 port=0; port<2; ++port ) - for( u8 slot=0; slot<4; ++slot ) - { - u64 checksum = mcds[port][slot].GetChecksum(); - - if( checksum != m_mcdCRCs[port][slot] ) - mcds[port][slot].ForceEjection_Timeout = FORCED_MCD_EJECTION_MAX_TRIES; - } - } + FreezeTag("sio0"); + Freeze(sio0); } std::tuple sioConvertPadToPortAndSlot(u32 index) @@ -1085,3 +980,40 @@ bool sioPortAndSlotIsMultitap(u32 port, u32 slot) { return (slot != 0); } + +void AutoEject::Set(size_t port, size_t slot) +{ + if (EmuConfig.McdEnableEjection) + { + mcds[port][slot].autoEjectTicks = 60; + } +} + +void AutoEject::Clear(size_t port, size_t slot) +{ + mcds[port][slot].autoEjectTicks = 0; +} + +void AutoEject::SetAll() +{ + Host::AddKeyedFormattedOSDMessage("AutoEjectAllSet", 10.0f, "Force ejecting all memory cards"); + + for (size_t port = 0; port < SIO::PORTS; port++) + { + for (size_t slot = 0; slot < SIO::SLOTS; slot++) + { + AutoEject::Set(port, slot); + } + } +} + +void AutoEject::ClearAll() +{ + for (size_t port = 0; port < SIO::PORTS; port++) + { + for (size_t slot = 0; slot < SIO::SLOTS; slot++) + { + AutoEject::Clear(port, slot); + } + } +} \ No newline at end of file diff --git a/pcsx2/Sio.h b/pcsx2/Sio.h index caa632fac6..a4cf5c84da 100644 --- a/pcsx2/Sio.h +++ b/pcsx2/Sio.h @@ -1,5 +1,5 @@ /* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 PCSX2 Dev Team + * Copyright (C) 2002-2022 PCSX2 Dev Team * * PCSX2 is free software: you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Found- @@ -13,26 +13,35 @@ * If not, see . */ +// Huge thanks to PSI for his work reversing the PS2, his documentation on SIO2 pretty much saved +// this entire implementation. https://psi-rockin.github.io/ps2tek/#sio2registers + #pragma once +#include "SioTypes.h" #include "MemoryCardFile.h" +#include +#include struct _mcd { + u8 currentCommand; u8 term; // terminator value; bool goodSector; // xor sector check + u8 msb; + u8 lsb; u32 sectorAddr; // read/write sector address u32 transferAddr; // Transfer address + std::vector buf; // Buffer for reading and writing + u8 FLAG; // for PSX; u8 port; // port u8 slot; // and slot for this memcard - // Auto Eject - u32 ForceEjection_Timeout; // in SIO checks - u64 ForceEjection_Timestamp; + size_t autoEjectTicks; void GetSizeInfo(McdSizeInfo &info) { @@ -46,18 +55,21 @@ struct _mcd void EraseBlock() { + //DevCon.WriteLn("Memcard Erase (sectorAddr = %08X)", sectorAddr); FileMcd_EraseBlock(port, slot, transferAddr); } // Read from memorycard to dest void Read(u8 *dest, int size) { + //DevCon.WriteLn("Memcard Read (sectorAddr = %08X)", sectorAddr); FileMcd_Read(port, slot, dest, transferAddr, size); } // Write to memorycard from src void Write(u8 *src, int size) { + //DevCon.WriteLn("Memcard Write (sectorAddr = %08X)", sectorAddr); FileMcd_Save(port, slot, src,transferAddr, size); } @@ -66,11 +78,16 @@ struct _mcd return FileMcd_IsPresent(port, slot); } - u8 DoXor(const u8 *buf, uint length) + u8 DoXor() { - u8 i, x; - for (x=0, i=0; i send3; // 0x1f808200 - 0x1f80823f + // SEND1 and SEND2 are an unusual bunch. It's not entirely clear just from + // documentation but these registers almost seem like they are the same thing; + // when bit 2 is set, SEND2 is being read/written. When bit 2 isn't set, it is + // SEND1. Their use is not really known, either. + std::array send1; // 0x1f808240 - 0x1f80825f + std::array send2; // 0x1f808240 - 0x1f80825f + u32 dataIn; // 0x1f808260 + u32 dataOut; // 0x1f808264 + u32 ctrl; // 0x1f808268 + u32 recv1; // 0x1f80826c + u32 recv2; // 0x1f808270 + u32 recv3; // 0x1f808274 + u32 unknown1; // 0x1f808278 + u32 unknown2; // 0x1f80827c + u32 iStat; // 0x1f808280 + + u8 port = 0; + u8 slot = 0; + + // The current working index of SEND3. The SEND3 register is a 16 position + // array of command descriptors. Each descriptor describes the port the command + // is targeting, as well as the length of the command in bytes. + bool send3Read = false; + size_t send3Position = 0; + size_t commandLength = 0; + size_t processedLength = 0; + // Tracks the size of a single block of DMA11/DMA12 data. psxDma11 will set this prior + // to doing writes, and Sio2::SetSend3 will clear this to ensure a non-DMA write into SIO2 + // does not accidentally use dmaBlockSize. + size_t dmaBlockSize = 0; + bool send3Complete = false; + + std::unique_ptr fifoInBackup; + size_t fifoInBackupSize; + std::unique_ptr fifoOutBackup; + size_t fifoOutBackupSize; + + Sio2(); + ~Sio2(); + + void SoftReset(); + void FullReset(); + + void Interrupt(); + + void SetCtrl(u32 value); + void SetSend3(size_t position, u32 value); + void SetRecv1(u32 value); + + void Pad(); + void Multitap(); + void Infrared(); + void Memcard(); + + void Write(u8 data); + u8 Read(); +}; + +extern std::deque fifoIn; +extern std::deque fifoOut; + +extern Sio0 sio0; +extern Sio2 sio2; + extern _mcd mcds[2][4]; extern _mcd *mcd; -extern void sioInit(); -extern u8 sioRead8(); -extern void sioWrite8(u8 value); -extern void sioWriteCtrl16(u16 value); -extern void sioInterrupt(); -extern void sioInterruptR(); -extern void SetForceMcdEjectTimeoutNow(uint port, uint slot); -extern void SetForceMcdEjectTimeoutNow(); -extern void ClearMcdEjectTimeoutNow(); -extern void sioStatRead(); -extern void sioSetGameSerial(const std::string& serial); extern void sioNextFrame(); /// Converts a global pad index to a multitap port and slot. @@ -137,3 +244,12 @@ extern u32 sioConvertPortAndSlotToPad(u32 port, u32 slot); /// Returns true if the given pad index is a multitap slot. extern bool sioPadIsMultitapSlot(u32 index); extern bool sioPortAndSlotIsMultitap(u32 port, u32 slot); +extern void sioSetGameSerial(const std::string& serial); + +namespace AutoEject +{ + extern void Set(size_t port, size_t slot); + extern void Clear(size_t port, size_t slot); + extern void SetAll(); + extern void ClearAll(); +} \ No newline at end of file diff --git a/pcsx2/SioTypes.h b/pcsx2/SioTypes.h new file mode 100644 index 0000000000..f6a0a68c9b --- /dev/null +++ b/pcsx2/SioTypes.h @@ -0,0 +1,161 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2022 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#pragma once + +enum class SioStage +{ + IDLE, + WAITING_COMMAND, + WORKING +}; + +namespace SioMode +{ + static constexpr u8 NOT_SET = 0x00; + static constexpr u8 PAD = 0x01; + static constexpr u8 MULTITAP = 0x21; + static constexpr u8 INFRARED = 0x61; + static constexpr u8 MEMCARD = 0x81; +} // namespace SioMode + +namespace PadCommand +{ + static constexpr u8 UNK_0 = 0x40; + static constexpr u8 QUERY_BUTTONS = 0x41; + static constexpr u8 POLL = 0x42; + static constexpr u8 CONFIG = 0x43; + static constexpr u8 MODE_SWITCH = 0x44; + static constexpr u8 STATUS = 0x45; + static constexpr u8 CONST_1 = 0x46; + static constexpr u8 CONST_2 = 0x47; + static constexpr u8 UNK_8 = 0x48; + static constexpr u8 UNK_9 = 0x49; + static constexpr u8 UNK_A = 0x4a; + static constexpr u8 UNK_B = 0x4b; + static constexpr u8 CONST_3 = 0x4c; + static constexpr u8 VIBRATION = 0x4d; + static constexpr u8 UNK_E = 0x4e; + static constexpr u8 ANALOG = 0x4f; +} // namespace PadCommand + +namespace MemcardCommand +{ + static constexpr u8 NOT_SET = 0x00; + static constexpr u8 PROBE = 0x11; + static constexpr u8 UNKNOWN_WRITE_DELETE_END = 0x12; + static constexpr u8 SET_ERASE_SECTOR = 0x21; + static constexpr u8 SET_WRITE_SECTOR = 0x22; + static constexpr u8 SET_READ_SECTOR = 0x23; + static constexpr u8 GET_SPECS = 0x26; + static constexpr u8 SET_TERMINATOR = 0x27; + static constexpr u8 GET_TERMINATOR = 0x28; + static constexpr u8 WRITE_DATA = 0x42; + static constexpr u8 READ_DATA = 0x43; + static constexpr u8 PS1_READ = 0x52; + static constexpr u8 PS1_STATE = 0x53; + static constexpr u8 PS1_WRITE = 0x57; + static constexpr u8 PS1_POCKETSTATION = 0x58; + static constexpr u8 READ_WRITE_END = 0x81; + static constexpr u8 ERASE_BLOCK = 0x82; + static constexpr u8 UNKNOWN_BOOT = 0xbf; + static constexpr u8 AUTH_XOR = 0xf0; + static constexpr u8 AUTH_F3 = 0xf3; + static constexpr u8 AUTH_F7 = 0xf7; +} // namespace MemcardCommand + +enum class Sio0Interrupt +{ + TEST_EVENT, + STAT_READ, + TX_DATA_WRITE +}; // namespace Sio0Interrupt + +namespace SIO +{ + static constexpr u8 PORTS = 2; + static constexpr u8 SLOTS = 4; +} // namespace SIO + +namespace SIO0_STAT +{ + static constexpr u32 TX_READY = 0x01; + static constexpr u32 RX_FIFO_NOT_EMPTY = 0x02; + static constexpr u32 TX_EMPTY = 0x04; + static constexpr u32 RX_PARITY_ERROR = 0x08; + static constexpr u32 ACK = 0x80; + static constexpr u32 IRQ = 0x0200; +} // namespace SIO0_STAT + +namespace SIO0_CTRL +{ + static constexpr u16 TX_ENABLE = 0x01; + static constexpr u16 RX_ENABLE = 0x04; + static constexpr u16 ACK = 0x10; + static constexpr u16 RESET = 0x40; + static constexpr u16 RX_INT_MODE_LSB = 0x0100; + static constexpr u16 RX_INT_MODE_MSB = 0x0200; + static constexpr u16 TX_INT_ENABLE = 0x0400; + static constexpr u16 RX_INT_ENABLE = 0x0800; + static constexpr u16 ACK_INT_ENABLE = 0x1000; + static constexpr u16 PORT = 0x2000; +} // namespace SIO0_CTRL + +namespace Send3 +{ + static constexpr u32 PORT = 0x01; + static constexpr u16 COMMAND_LENGTH_MASK = 0x3ff; +} // namespace Send3 + +namespace Sio2Ctrl +{ + static constexpr u32 START_TRANSFER = 0x1; + static constexpr u32 RESET = 0xc; + static constexpr u32 PORT = 0x2000; + // The value which SIO2MAN resets SIO2_CTRL to after a system reset. + static constexpr u32 SIO2MAN_RESET = 0x000003bc; +} // namespace Sio2Ctrl + +namespace Recv1 +{ + static constexpr u32 DISCONNECTED = 0x1d100; + static constexpr u32 CONNECTED = 0x1100; +} // namespace Recv1 + +namespace Recv2 +{ + static constexpr u32 DEFAULT = 0xf; +} // namespace Recv2 + +// Most RECV3 values are mysterious, undocumented, and their purpose +// can only be inferred from how old, mostly incorrect PCSX2 code tried +// to use them. We're going to try and respect these where it seems like +// it may make sense to do so, but these are still largely unknown and +// tests suggest they are not even used at all. +namespace Recv3 +{ + static constexpr u32 DEFAULT = 0x0; + // Set when getting memcard specs + static constexpr u32 SPECS = 0x83; + // Set when getting or setting the terminator byte + static constexpr u32 TERMINATOR = 0x8b; + // Set when setting the read/write sector + static constexpr u32 READ_WRITE_END = 0x8c; +} // namespace Recv3 + +namespace Terminator +{ + static constexpr u32 DEFAULT = 0x55; +} // namespace Terminator diff --git a/pcsx2/VMManager.cpp b/pcsx2/VMManager.cpp index 2adc82ccdc..2647e6fe5f 100644 --- a/pcsx2/VMManager.cpp +++ b/pcsx2/VMManager.cpp @@ -678,7 +678,7 @@ void VMManager::UpdateRunningGame(bool resetting, bool game_starting) // If we don't reset the timer here, when using folder memcards the reindex will cause an eject, // which a bunch of games don't like since they access the memory card on boot. if (game_starting || resetting) - ClearMcdEjectTimeoutNow(); + AutoEject::ClearAll(); } UpdateGameSettingsLayer(); @@ -687,7 +687,7 @@ void VMManager::UpdateRunningGame(bool resetting, bool game_starting) // Clear the memory card eject notification again when booting for the first time, or starting. // Otherwise, games think the card was removed on boot. if (game_starting || resetting) - ClearMcdEjectTimeoutNow(); + AutoEject::ClearAll(); // Check this here, for two cases: dynarec on, and when enable cheats is set per-game. if (s_patches_crc != s_game_crc) @@ -1691,8 +1691,8 @@ void VMManager::CheckForMemoryCardConfigChanges(const Pcsx2Config& old_config) if (EmuConfig.Mcd[index].Enabled != old_config.Mcd[index].Enabled || EmuConfig.Mcd[index].Filename != old_config.Mcd[index].Filename) { - Console.WriteLn("Replugging memory card %u (port %u slot %u) due to source change", index, port, slot); - SetForceMcdEjectTimeoutNow(port, slot); + Console.WriteLn("Ejecting memory card %u (port %u slot %u) due to source change", index, port, slot); + AutoEject::Set(port, slot); } } } diff --git a/pcsx2/gui/AppCoreThread.cpp b/pcsx2/gui/AppCoreThread.cpp index 7742d6bdbe..c17cb6015c 100644 --- a/pcsx2/gui/AppCoreThread.cpp +++ b/pcsx2/gui/AppCoreThread.cpp @@ -568,7 +568,7 @@ void AppCoreThread::GameStartingInThread() m_ExecMode = ExecMode_Paused; OnResumeReady(); _reset_stuff_as_needed(); - ClearMcdEjectTimeoutNow(); // probably safe to do this when a game boots, eliminates annoying prompts + AutoEject::ClearAll(); // probably safe to do this when a game boots, eliminates annoying prompts m_ExecMode = ExecMode_Opened; _parent::GameStartingInThread(); diff --git a/pcsx2/gui/Panels/MemoryCardListPanel.cpp b/pcsx2/gui/Panels/MemoryCardListPanel.cpp index 43ed9599f3..d7063b7f22 100644 --- a/pcsx2/gui/Panels/MemoryCardListPanel.cpp +++ b/pcsx2/gui/Panels/MemoryCardListPanel.cpp @@ -563,7 +563,7 @@ void Panels::MemoryCardListPanel_Simple::Apply() if (!used) Console.WriteLn("No active slots."); - SetForceMcdEjectTimeoutNow(); + AutoEject::SetAll(); } void Panels::MemoryCardListPanel_Simple::AppStatusEvent_OnSettingsApplied() diff --git a/pcsx2/pcsx2.vcxproj b/pcsx2/pcsx2.vcxproj index 58a35bd06d..4dd26a5cf2 100644 --- a/pcsx2/pcsx2.vcxproj +++ b/pcsx2/pcsx2.vcxproj @@ -378,6 +378,8 @@ + + @@ -634,7 +636,6 @@ - @@ -842,6 +843,8 @@ + + @@ -957,7 +960,6 @@ - @@ -1080,7 +1082,6 @@ - diff --git a/pcsx2/pcsx2.vcxproj.filters b/pcsx2/pcsx2.vcxproj.filters index 4fe0482703..b8b27ca676 100644 --- a/pcsx2/pcsx2.vcxproj.filters +++ b/pcsx2/pcsx2.vcxproj.filters @@ -656,9 +656,6 @@ System\Ps2\Iop - - System\Ps2\Iop - System\Ps2\Iop @@ -1799,6 +1796,12 @@ Host + + System\Ps2\Iop + + + System\Ps2\Iop + @@ -1978,9 +1981,6 @@ System\Ps2\Iop - - System\Ps2\Iop - System\Ps2\Iop @@ -2248,9 +2248,6 @@ System\Ps2\Iop - - System\Ps2\Iop - System\Ps2 @@ -2999,6 +2996,12 @@ System\Include + + System\Ps2\Iop + + + System\Ps2\Iop + diff --git a/pcsx2/pcsx2core.vcxproj b/pcsx2/pcsx2core.vcxproj index e9b2291987..8d80861a12 100644 --- a/pcsx2/pcsx2core.vcxproj +++ b/pcsx2/pcsx2core.vcxproj @@ -226,6 +226,8 @@ + + @@ -406,7 +408,6 @@ - @@ -551,6 +552,8 @@ + + @@ -567,6 +570,7 @@ + @@ -651,7 +655,6 @@ - @@ -716,7 +719,6 @@ - diff --git a/pcsx2/pcsx2core.vcxproj.filters b/pcsx2/pcsx2core.vcxproj.filters index 48823a76d1..2128848783 100644 --- a/pcsx2/pcsx2core.vcxproj.filters +++ b/pcsx2/pcsx2core.vcxproj.filters @@ -569,9 +569,6 @@ System\Ps2\Iop - - System\Ps2\Iop - System\Ps2\Iop @@ -1296,6 +1293,12 @@ Host + + System\Ps2\Iop + + + System\Ps2\Iop + @@ -1478,9 +1481,6 @@ System\Ps2\Iop - - System\Ps2\Iop - System\Ps2\Iop @@ -1640,9 +1640,6 @@ System\Ps2\Iop - - System\Ps2\Iop - System\Ps2 @@ -2162,6 +2159,15 @@ Host + + System\Ps2\Iop + + + System\Ps2\Iop + + + System\Ps2\Iop + diff --git a/pcsx2/ps2/Iop/IopHwRead.cpp b/pcsx2/ps2/Iop/IopHwRead.cpp index 9402348e24..2673285cbe 100644 --- a/pcsx2/ps2/Iop/IopHwRead.cpp +++ b/pcsx2/ps2/Iop/IopHwRead.cpp @@ -24,12 +24,17 @@ #include "DEV9/DEV9.h" #include "USB/USB.h" #include "IopCounters.h" -#include "IopSio2.h" #include "IopDma.h" #include "ps2/pgif.h" #include "Mdec.h" +#define SIO0LOG_ENABLE 0 +#define SIO2LOG_ENABLE 0 + +#define Sio0Log if (SIO0LOG_ENABLE) DevCon +#define Sio2Log if (SIO2LOG_ENABLE) DevCon + namespace IopMemory { using namespace Internal; @@ -46,12 +51,21 @@ mem8_t iopHwRead8_Page1( u32 addr ) mem8_t ret; // using a return var can be helpful in debugging. switch( masked_addr ) { - mcase(HW_SIO_DATA) : - // 1F801040h 1/4 JOY_DATA Joypad/Memory Card Data (R/W) - // psxmode: documentation suggests a valid 8 bit read and the rest of the 32 bit register is unclear. - // todo: check this and compare with the HW_SIO_DATA read around line 245 as well. - ret = sioRead8(); - break; + case (HW_SIO_DATA & 0x0fff): + ret = sio0.GetRxData(); + break; + case (HW_SIO_STAT & 0x0fff): + Sio0Log.Error("%s(%08X) Unexpected SIO0 STAT 8 bit read", __FUNCTION__, addr); + break; + case (HW_SIO_MODE & 0x0fff): + Sio0Log.Error("%s(%08X) Unexpected SIO0 MODE 8 bit read", __FUNCTION__, addr); + break; + case (HW_SIO_CTRL & 0x0fff): + Sio0Log.Error("%s(%08X) Unexpected SIO0 CTRL 8 bit read", __FUNCTION__, addr); + break; + case (HW_SIO_BAUD & 0x0fff): + Sio0Log.Error("%s(%08X) Unexpected SIO0 BAUD 8 bit read", __FUNCTION__, addr); + break; // for use of serial port ignore for now //case 0x50: ret = serial_read8(); break; @@ -118,10 +132,14 @@ mem8_t iopHwRead8_Page8( u32 addr ) mem8_t ret; - if( addr == HW_SIO2_FIFO ) - ret = sio2_fifoOut();//sio2 serial data feed/fifo_out + if (addr == HW_SIO2_FIFO) + { + ret = sio2.Read(); + } else - ret = psxHu8( addr ); + { + ret = psxHu8(addr); + } IopHwTraceLog( addr, ret, true ); return ret; @@ -255,38 +273,34 @@ static __fi T _HwRead_16or32_Page1( u32 addr ) switch( masked_addr ) { // ------------------------------------------------------------------------ - mcase(HW_SIO_DATA): - ret = sioRead8(); - ret |= sioRead8() << 8; - if( sizeof(T) == 4 ) + case (HW_SIO_DATA & 0x0fff): + Console.Warning("%s(%08X) Unexpected 16 or 32 bit access to SIO0 data register!", __FUNCTION__, addr); + ret = sio0.GetRxData(); + ret |= sio0.GetRxData() << 8; + if (sizeof(T) == 4) { - ret |= sioRead8() << 16; - ret |= sioRead8() << 24; + ret |= sio0.GetRxData() << 16; + ret |= sio0.GetRxData() << 24; } - break; - - 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): - ret = sio.ModeReg; - if( sizeof(T) == 4 ) + break; + case (HW_SIO_STAT & 0x0fff): + ret = sio0.GetStat(); + break; + case (HW_SIO_MODE & 0x0fff): + ret = sio0.GetMode(); + + if (sizeof(T) == 4) { - // My guess on 32-bit accesses. Dunno yet what the real hardware does. --air - ret |= sio.CtrlReg << 16; + Console.Warning("%s(%08X) Unexpected 32 bit access to SIO0 MODE register!", __FUNCTION__, addr); } - break; - - mcase(HW_SIO_CTRL): - ret = sio.CtrlReg; - break; - - mcase(HW_SIO_BAUD): - ret = sio.BaudReg; - break; + + break; + case (HW_SIO_CTRL & 0x0fff): + ret = sio0.GetCtrl(); + break; + case (HW_SIO_BAUD & 0x0fff): + ret = sio0.GetBaud(); + break; // ------------------------------------------------------------------------ //Serial port stuff not support now ;P @@ -421,45 +435,60 @@ mem32_t iopHwRead32_Page8( u32 addr ) if( masked_addr < 0x240 ) { const int parm = (masked_addr-0x200) / 4; - ret = sio2_getSend3( parm ); + ret = sio2.send3.at(parm); + Sio2Log.WriteLn("%s(%08X) SIO2 SEND3 Read (%08X)", __FUNCTION__, addr, ret); } else if( masked_addr < 0x260 ) { // SIO2 Send commands alternate registers. First reg maps to Send1, second // to Send2, third to Send1, etc. And the following clever code does this: - const int parm = (masked_addr-0x240) / 8; - ret = (masked_addr & 4) ? sio2_getSend2( parm ) : sio2_getSend1( parm ); + ret = (masked_addr & 4) ? sio2.send2.at(parm) : sio2.send1.at(parm); + Sio2Log.WriteLn("%s(%08X) SIO2 SEND1/2 Read (%08X)", __FUNCTION__, addr, ret); } else if( masked_addr <= 0x280 ) { switch( masked_addr ) { - mcase(HW_SIO2_CTRL): ret = sio2_getCtrl(); break; - mcase(HW_SIO2_RECV1): ret = sio2_getRecv1(); break; - mcase(HW_SIO2_RECV2): ret = sio2_getRecv2(); break; - mcase(HW_SIO2_RECV3): ret = sio2_getRecv3(); break; - mcase(0x1f808278): ret = sio2_get8278(); break; - mcase(0x1f80827C): ret = sio2_get827C(); break; - mcase(HW_SIO2_INTR): ret = sio2_getIntr(); break; - - // HW_SIO2_FIFO -- A yet unknown: Should this be ignored on 32 bit writes, or handled as a - // 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) : - //ret = sio2_fifoOut(); - //ret |= sio2_fifoOut() << 8; - //ret |= sio2_fifoOut() << 16; - //ret |= sio2_fifoOut() << 24; - //break; - DevCon.Warning("HW_SIO2_FIFO read"); + case (HW_SIO2_DATAIN & 0x0fff): ret = psxHu32(addr); - break; - + Sio2Log.Warning("%s(%08X) Unexpected 32 bit read of HW_SIO2_DATAIN (%08X)", __FUNCTION__, addr, ret); + break; + case (HW_SIO2_FIFO & 0x0fff): + ret = psxHu32(addr); + Sio2Log.Warning("%s(%08X) Unexpected 32 bit read of HW_SIO2_FIFO (%08X)", __FUNCTION__, addr, ret); + break; + case (HW_SIO2_CTRL & 0x0fff): + ret = sio2.ctrl; + Sio2Log.WriteLn("%s(%08X) SIO2 CTRL Read (%08X)", __FUNCTION__, addr, ret); + break; + case (HW_SIO2_RECV1 & 0xfff): + ret = sio2.recv1; + Sio2Log.WriteLn("%s(%08X) SIO2 RECV1 Read (%08X)", __FUNCTION__, addr, ret); + break; + case (HW_SIO2_RECV2 & 0x0fff): + ret = sio2.recv2; + Sio2Log.WriteLn("%s(%08X) SIO2 RECV2 Read (%08X)", __FUNCTION__, addr, ret); + break; + case (HW_SIO2_RECV3 & 0x0fff): + ret = sio2.recv3; + Sio2Log.WriteLn("%s(%08X) SIO2 RECV3 Read (%08X)", __FUNCTION__, addr, ret); + break; + case (0x1f808278 & 0x0fff): + ret = sio2.unknown1; + Sio2Log.WriteLn("%s(%08X) SIO2 UNK1 Read (%08X)", __FUNCTION__, addr, ret); + break; + case (0x1f80827C & 0x0fff): + ret = sio2.unknown2; + Sio2Log.WriteLn("%s(%08X) SIO2 UNK2 Read (%08X)", __FUNCTION__, addr, ret); + break; + case (HW_SIO2_INTR & 0x0fff): + ret = sio2.iStat; + Sio2Log.WriteLn("%s(%08X) SIO2 ISTAT Read (%08X)", __FUNCTION__, addr, ret); + break; default: ret = psxHu32(addr); - break; + break; } } else if( masked_addr >= pgmsk(HW_FW_START) && masked_addr <= pgmsk(HW_FW_END) ) diff --git a/pcsx2/ps2/Iop/IopHwWrite.cpp b/pcsx2/ps2/Iop/IopHwWrite.cpp index 9e47d47e1b..a1e0378ef8 100644 --- a/pcsx2/ps2/Iop/IopHwWrite.cpp +++ b/pcsx2/ps2/Iop/IopHwWrite.cpp @@ -23,13 +23,18 @@ #include "DEV9/DEV9.h" #include "USB/USB.h" #include "IopCounters.h" -#include "IopSio2.h" #include "IopDma.h" #include "R3000A.h" #include "ps2/pgif.h" #include "Mdec.h" +#define SIO0LOG_ENABLE 0 +#define SIO2LOG_ENABLE 0 + +#define Sio0Log if (SIO0LOG_ENABLE) DevCon +#define Sio2Log if (SIO2LOG_ENABLE) DevCon + namespace IopMemory { using namespace Internal; @@ -80,8 +85,21 @@ void iopHwWrite8_Page1( u32 addr, mem8_t val ) switch( masked_addr ) { - mcase(HW_SIO_DATA): sioWrite8( val ); break; - + case (HW_SIO_DATA & 0x0fff): + sio0.SetTxData(val); + break; + case (HW_SIO_STAT & 0x0fff): + Sio0Log.Error("%s(%08X, %08X) Unexpected SIO0 STAT 8 bit write", __FUNCTION__, addr, val); + break; + case (HW_SIO_MODE & 0x0fff): + Sio0Log.Error("%s(%08X, %08X) Unexpected SIO0 MODE 8 bit write", __FUNCTION__, addr, val); + break; + case (HW_SIO_CTRL & 0x0fff): + Sio0Log.Error("%s(%08X, %08X) Unexpected SIO0 CTRL 8 bit write", __FUNCTION__, addr, val); + break; + case (HW_SIO_BAUD & 0x0fff): + Sio0Log.Error("%s(%08X, %08X) Unexpected SIO0 BAUD 8 bit write", __FUNCTION__, addr, val); + break; // for use of serial port ignore for now //case 0x50: serial_write8( val ); break; @@ -156,10 +174,14 @@ void iopHwWrite8_Page8( u32 addr, mem8_t val ) // all addresses are assumed to be prefixed with 0x1f808xxx: pxAssert( (addr >> 12) == 0x1f808 ); - if( addr == HW_SIO2_DATAIN ) // sio2 serial data feed input - sio2_serialIn( val ); + if (addr == HW_SIO2_DATAIN) + { + sio2.Write(val); + } else - psxHu8( addr ) = val; + { + psxHu8(addr) = val; + } IopHwTraceLog( addr, val, false ); } @@ -277,39 +299,39 @@ static __fi void _HwWrite_16or32_Page1( u32 addr, T val ) switch( masked_addr ) { // ------------------------------------------------------------------------ - mcase(HW_SIO_DATA): - sioWrite8( val & 0xFF ); - sioWrite8( (val >> 8) & 0xFF ); - if( sizeof(T) == 4 ) + case (HW_SIO_DATA & 0x0fff): + Console.Error("%s(%08X, %08X) Unexpected 16 or 32 bit write to SIO0 DATA!", __FUNCTION__, addr, val); +/* + sio0.SetTxData(val & 0xFF); + sio0.SetTxData((val >> 8) & 0xFF); + + if (sizeof(T) == 4) { - // u32 gets rid of compiler warnings when using the u16 version of this template - sioWrite8( ((u32)val >> 16) & 0xFF ); - sioWrite8( ((u32)val >> 24) & 0xFF ); + sio0.SetTxData((static_cast(val) >> 16) & 0xFF); + sio0.SetTxData((static_cast(val) >> 24) & 0xFF); } - break; +*/ + break; + case (HW_SIO_STAT & 0x0fff): + Console.Error("%s(%08X, %08X) Write issued to read-only SIO0 STAT!", __FUNCTION__, addr, val); + break; + case (HW_SIO_MODE & 0x0fff): + sio0.SetMode(static_cast(val)); - mcase(HW_SIO_STAT): // read-only? - //regname = "SIO_STAT (read-only?)"; - //sio.StatReg; - break; - - mcase(HW_SIO_MODE): - sio.ModeReg = (u16)val; - if( sizeof(T) == 4 ) + if (sizeof(T) == 4) { - // My guess on 32-bit accesses. Dunno yet what the real hardware does. --air - sio.CtrlReg = (u16)((u32)val >> 16); + Console.Error("%s(%08X, %08X) 32 bit write to 16 bit SIO0 MODE register!", __FUNCTION__, addr, val); } - break; + + break; - mcase(HW_SIO_CTRL): - //sio.CtrlReg = (u16)val; - sioWriteCtrl16((u16)val); - break; - - mcase(HW_SIO_BAUD): - sio.BaudReg = (u16)val; - break; + case (HW_SIO_CTRL & 0x0fff): + sio0.SetCtrl(static_cast(val)); + break; + + case (HW_SIO_BAUD & 0x0fff): + sio0.SetBaud(static_cast(val)); + break; // ------------------------------------------------------------------------ //Serial port stuff not support now ;P @@ -588,26 +610,66 @@ void iopHwWrite32_Page8( u32 addr, mem32_t val ) { if( masked_addr < 0x240 ) { - const int parm = (masked_addr-0x200) / 4; - sio2_setSend3( parm, val ); + Sio2Log.WriteLn("%s(%08X, %08X) SIO2 SEND3 Write (len = %d / %d) (port = %d)", __FUNCTION__, addr, val, (val >> 8) & Send3::COMMAND_LENGTH_MASK, (val >> 18) & Send3::COMMAND_LENGTH_MASK, val & 0x01); + const int parm = (masked_addr - 0x200) / 4; + sio2.SetSend3(parm, val); } else if( masked_addr < 0x260 ) { // SIO2 Send commands alternate registers. First reg maps to Send1, second // to Send2, third to Send1, etc. And the following clever code does this: - const int parm = (masked_addr-0x240) / 8; - if(masked_addr & 4) sio2_setSend2( parm, val ); else sio2_setSend1( parm, val ); + const int parm = (masked_addr - 0x240) / 8; + + if (masked_addr & 4) + { + Sio2Log.WriteLn("%s(%08X, %08X) SIO2 SEND2 Write", __FUNCTION__, addr, val); + sio2.send2.at(parm) = val; + } + else + { + Sio2Log.WriteLn("%s(%08X, %08X) SIO2 SEND1 Write", __FUNCTION__, addr, val); + sio2.send1.at(parm) = val; + } } else if( masked_addr <= 0x280 ) { switch( masked_addr ) { - mcase(HW_SIO2_CTRL): sio2_setCtrl( val ); break; - mcase(0x1f808278): sio2_set8278( val ); break; - mcase(0x1f80827C): sio2_set827C( val ); break; - mcase(HW_SIO2_INTR): sio2_setIntr( val ); break; - + case (HW_SIO2_DATAIN & 0x0fff): + Sio2Log.Warning("%s(%08X, %08X) Unexpected 32 bit write to HW_SIO2_DATAIN", __FUNCTION__, addr, val); + break; + case (HW_SIO2_FIFO & 0x0fff): + Sio2Log.Warning("%s(%08X, %08X) Unexpected 32 bit write to HW_SIO2_FIFO", __FUNCTION__, addr, val); + break; + case (HW_SIO2_CTRL & 0x0fff): + Sio2Log.WriteLn("%s(%08X, %08X) SIO2 CTRL Write", __FUNCTION__, addr, val); + sio2.SetCtrl(val); + break; + case (HW_SIO2_RECV1 & 0x0fff): + Sio2Log.WriteLn("%s(%08X, %08X) SIO2 RECV1 Write", __FUNCTION__, addr, val); + sio2.recv1 = val; + break; + case (HW_SIO2_RECV2 & 0x0fff): + Sio2Log.WriteLn("%s(%08X, %08X) SIO2 RECV2 Write", __FUNCTION__, addr, val); + sio2.recv2 = val; + break; + case (HW_SIO2_RECV3 & 0x0fff): + Sio2Log.WriteLn("%s(%08X, %08X) SIO2 RECV3 Write", __FUNCTION__, addr, val); + sio2.recv3 = val; + break; + case (HW_SIO2_8278 & 0x0fff): + Sio2Log.WriteLn("%s(%08X, %08X) SIO2 UNK1 Write", __FUNCTION__, addr, val); + sio2.unknown1 = val; + break; + case (HW_SIO2_827C & 0x0fff): + Sio2Log.WriteLn("%s(%08X, %08X) SIO2 UNK2 Write", __FUNCTION__, addr, val); + sio2.unknown2 = val; + break; + case (HW_SIO2_INTR & 0x0fff): + Sio2Log.WriteLn("%s(%08X, %08X) SIO2 ISTAT Write", __FUNCTION__, addr, val); + sio2.iStat = val; + break; // Other SIO2 registers are read-only, no-ops on write. default: psxHu32(addr) = val; diff --git a/pcsx2/sio_internal.h b/pcsx2/sio_internal.h deleted file mode 100644 index ce77c041cc..0000000000 --- a/pcsx2/sio_internal.h +++ /dev/null @@ -1,104 +0,0 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2009 PCSX2 Dev Team - * - * PCSX2 is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with PCSX2. - * If not, see . - */ - -#pragma once -#include "IopSio2.h" - -// sio_internal.h -- contains defines and structs used by sio and sio2, which -// are of little or no use to the rest of the world. - -// Status Flags -static const int - TX_RDY = 0x0001, - RX_RDY = 0x0002, - TX_EMPTY = 0x0004, - PARITY_ERR = 0x0008, - RX_OVERRUN = 0x0010, - FRAMING_ERR = 0x0020, - SYNC_DETECT = 0x0040, - DSR = 0x0080, - CTS = 0x0100, - IRQ = 0x0200; - -// Control Flags -static const int - TX_PERM = 0x0001, - DTR = 0x0002, - RX_PERM = 0x0004, - BREAK = 0x0008, - RESET_ERR = 0x0010, - RTS = 0x0020, - SIO_RESET = 0x0040; - -void inline SIO_STAT_READY() -{ - sio.StatReg &= ~TX_EMPTY; // Now the Buffer is not empty - sio.StatReg |= RX_RDY; // Transfer is Ready -} - -void inline SIO_STAT_EMPTY() -{ - sio.StatReg &= ~RX_RDY; // Receive is not Ready now? - sio.StatReg |= TX_EMPTY; // Buffer is Empty -} - -void inline DEVICE_PLUGGED() -{ - sio.ret = 0xFF; - sio2.packet.recvVal1 = 0x01100; - memset8<0xFF>(sio.buf); -} - -void inline DEVICE_UNPLUGGED() -{ - sio.ret = 0x00; - sio2.packet.recvVal1 = 0x1D100; - memset8<0x00>(sio.buf); -} - -enum SIO_MODE -{ - SIO_START = 0, - SIO_CONTROLLER, - SIO_MULTITAP, - SIO_INFRARED, - SIO_MEMCARD, - SIO_MEMCARD_AUTH, - SIO_MEMCARD_ERASE, - SIO_MEMCARD_WRITE, - SIO_MEMCARD_READ, - SIO_MEMCARD_SECTOR, - SIO_MEMCARD_PSX, - SIO_DUMMY -}; - - -#ifdef _MSC_VER -#pragma pack(1) -#endif -struct mc_command_0x26_tag{ - u8 field_151; //+02 flags - u16 sectorSize; //+03 Size of each sector(page), in bytes. - u16 eraseBlocks; //+05 Number of sectors in the erase block - u32 mcdSizeInSectors; //+07 card size in sectors (pages). - u8 mc_xor; //+0b XOR Checksum of the superblock? (minus format ident and version?) - u8 Z; //+0c terminator? Appears to be overwritten/unused. -#ifdef _MSC_VER -}; -#pragma pack() -#else -} __attribute__((packed)); -#endif -