SIO: Separate SIO2 from SIO0, reimplement memcard protocol

[SAVEVERSION+] Fixes memcard issues in MGS3, Shining Force EXA, and others which do 4 sector reads
This commit is contained in:
RedPanda4552 2022-10-26 14:30:09 -04:00 committed by refractionpcsx2
parent 3f99d1d3cc
commit 1146175648
37 changed files with 2311 additions and 1652 deletions

View File

@ -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

View File

@ -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)

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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)

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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();

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<u8>(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<u8> 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<u8> 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);
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <array>
struct PS1MemoryCardState
{
size_t currentByte = 2;
u8 sectorAddrMSB = 0;
u8 sectorAddrLSB = 0;
u8 checksum = 0;
u8 expectedChecksum = 0;
std::array<u8, 128> 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;

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<MultitapMode>(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;
}
}

43
pcsx2/MultitapProtocol.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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;

View File

@ -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);

View File

@ -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 = {};

View File

@ -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
{

View File

@ -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;
}

View File

@ -49,7 +49,7 @@ struct QueryInfo
u8 response[42];
void reset();
u8 start_poll(int port);
u8 start_poll(int _port, int _slot);
template <size_t S>
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();

View File

@ -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()
{

View File

@ -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)

View File

@ -48,7 +48,7 @@ struct QueryInfo
u8 response[42];
void reset();
u8 start_poll(int port);
u8 start_poll(int port, int slot);
template <size_t S>
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();

View File

@ -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

View File

@ -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);

View File

@ -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 <queue>
#include <fmt/format.h>
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)

View File

@ -15,6 +15,8 @@
#pragma once
#include <queue>
#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();

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
// 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 <array>
#include <deque>
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<u8> 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<length; i++) x ^= buf[i];
return x;
u8 ret = msb ^ lsb;
for (const u8 byte : buf)
{
ret ^= byte;
}
return ret;
}
u64 GetChecksum()
@ -87,45 +104,135 @@ struct _mcd
}
};
struct _sio
class Sio0
{
u16 StatReg;
u16 ModeReg;
u16 CtrlReg;
u16 BaudReg;
private:
u32 txData; // 0x1f801040
u32 rxData; // 0x1f801040
u32 stat; // 0x1f801044
u16 mode; // 0x1f801048
u16 ctrl; // 0x1f80104a
u16 baud; // 0x1f80104e
u32 count; // old_sio remnant
u32 packetsize;// old_sio remnant
void ClearStatAcknowledge();
u8 buf[512];
u8 ret; // default return value;
u8 cmd; // command backup
public:
u8 flag = 0;
u16 bufCount; // current buffer counter
u16 bufSize; // supposed buffer size
SioStage sioStage = SioStage::IDLE;
u8 sioMode = SioMode::NOT_SET;
u8 sioCommand = 0;
bool padStarted = false;
bool rxDataSet = false;
u8 port; // current port
u8 slot[2]; // current slot
u8 port = 0;
u8 slot = 0;
u8 GetPort() { return port; }
u8 GetSlot() { return slot[port]; }
Sio0();
~Sio0();
void SoftReset();
void FullReset();
void Acknowledge();
void Interrupt(Sio0Interrupt sio0Interrupt);
u8 GetTxData();
u8 GetRxData();
u32 GetStat();
u16 GetMode();
u16 GetCtrl();
u16 GetBaud();
void SetTxData(u8 value);
void SetRxData(u8 value);
void SetStat(u32 value);
void SetMode(u16 value);
void SetCtrl(u16 value);
void SetBaud(u16 value);
bool IsPadCommand(u8 command);
bool IsMemcardCommand(u8 command);
bool IsPocketstationCommand(u8 command);
u8 Pad(u8 value);
u8 Memcard(u8 value);
};
extern _sio sio;
class Sio2
{
private:
void UpdateInputRecording(u8 dataIn, u8 dataOut);
public:
std::array<u32, 16> 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<u32, 4> send1; // 0x1f808240 - 0x1f80825f
std::array<u32, 4> 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<u8[]> fifoInBackup;
size_t fifoInBackupSize;
std::unique_ptr<u8[]> 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<u8> fifoIn;
extern std::deque<u8> 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();
}

161
pcsx2/SioTypes.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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

View File

@ -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);
}
}
}

View File

@ -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();

View File

@ -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()

View File

@ -378,6 +378,8 @@
<ClCompile Include="Host.cpp" />
<ClCompile Include="HostDisplay.cpp" />
<ClCompile Include="IopGte.cpp" />
<ClCompile Include="MemoryCardProtocol.cpp" />
<ClCompile Include="MultitapProtocol.cpp" />
<ClCompile Include="PINE.cpp" />
<ClCompile Include="FW.cpp" />
<ClCompile Include="MemoryCardFile.cpp" />
@ -634,7 +636,6 @@
<ClCompile Include="IopIrq.cpp" />
<ClCompile Include="IopMem.cpp" />
<ClCompile Include="windows\WinKeyCodes.cpp" />
<ClCompile Include="IopSio2.cpp" />
<ClCompile Include="R3000A.cpp" />
<ClCompile Include="R3000AInterpreter.cpp" />
<ClCompile Include="R3000AOpcodeTables.cpp" />
@ -842,6 +843,8 @@
<ClInclude Include="Host.h" />
<ClInclude Include="HostDisplay.h" />
<ClInclude Include="IopGte.h" />
<ClInclude Include="MemoryCardProtocol.h" />
<ClInclude Include="MultitapProtocol.h" />
<ClInclude Include="PINE.h" />
<ClInclude Include="FW.h" />
<ClInclude Include="MemoryCardFile.h" />
@ -957,7 +960,6 @@
<ClInclude Include="Patch.h" />
<ClInclude Include="PCSX2Base.h" />
<ClInclude Include="PrecompiledHeader.h" />
<ClInclude Include="sio_internal.h" />
<ClInclude Include="ps2\pgif.h" />
<ClInclude Include="Recording\InputRecording.h" />
<ClInclude Include="Recording\InputRecordingControls.h" />
@ -1080,7 +1082,6 @@
<ClInclude Include="IopCounters.h" />
<ClInclude Include="IopDma.h" />
<ClInclude Include="IopMem.h" />
<ClInclude Include="IopSio2.h" />
<ClInclude Include="R3000A.h" />
<ClInclude Include="Sio.h" />
<ClInclude Include="x86\iR3000A.h" />

View File

@ -656,9 +656,6 @@
<ClCompile Include="IopMem.cpp">
<Filter>System\Ps2\Iop</Filter>
</ClCompile>
<ClCompile Include="IopSio2.cpp">
<Filter>System\Ps2\Iop</Filter>
</ClCompile>
<ClCompile Include="R3000A.cpp">
<Filter>System\Ps2\Iop</Filter>
</ClCompile>
@ -1799,6 +1796,12 @@
<ClCompile Include="Frontend\ImGuiOverlays.cpp">
<Filter>Host</Filter>
</ClCompile>
<ClCompile Include="MemoryCardProtocol.cpp">
<Filter>System\Ps2\Iop</Filter>
</ClCompile>
<ClCompile Include="MultitapProtocol.cpp">
<Filter>System\Ps2\Iop</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Patch.h">
@ -1978,9 +1981,6 @@
<ClInclude Include="IopMem.h">
<Filter>System\Ps2\Iop</Filter>
</ClInclude>
<ClInclude Include="IopSio2.h">
<Filter>System\Ps2\Iop</Filter>
</ClInclude>
<ClInclude Include="R3000A.h">
<Filter>System\Ps2\Iop</Filter>
</ClInclude>
@ -2248,9 +2248,6 @@
<ClInclude Include="IopGte.h">
<Filter>System\Ps2\Iop</Filter>
</ClInclude>
<ClInclude Include="sio_internal.h">
<Filter>System\Ps2\Iop</Filter>
</ClInclude>
<ClInclude Include="ps2\pgif.h">
<Filter>System\Ps2</Filter>
</ClInclude>
@ -2999,6 +2996,12 @@
<ClInclude Include="ShaderCacheVersion.h">
<Filter>System\Include</Filter>
</ClInclude>
<ClInclude Include="MemoryCardProtocol.h">
<Filter>System\Ps2\Iop</Filter>
</ClInclude>
<ClInclude Include="MultitapProtocol.h">
<Filter>System\Ps2\Iop</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="windows\wxResources.rc">

View File

@ -226,6 +226,8 @@
<ClCompile Include="HostDisplay.cpp" />
<ClCompile Include="Frontend\HostSettings.cpp" />
<ClCompile Include="IopGte.cpp" />
<ClCompile Include="MemoryCardProtocol.cpp" />
<ClCompile Include="MultitapProtocol.cpp" />
<ClCompile Include="PAD\Host\KeyStatus.cpp" />
<ClCompile Include="PAD\Host\PAD.cpp" />
<ClCompile Include="PAD\Host\StateManagement.cpp" />
@ -406,7 +408,6 @@
<ClCompile Include="IopDma.cpp" />
<ClCompile Include="IopIrq.cpp" />
<ClCompile Include="IopMem.cpp" />
<ClCompile Include="IopSio2.cpp" />
<ClCompile Include="R3000A.cpp" />
<ClCompile Include="R3000AInterpreter.cpp" />
<ClCompile Include="R3000AOpcodeTables.cpp" />
@ -551,6 +552,8 @@
<ClInclude Include="HostDisplay.h" />
<ClInclude Include="HostSettings.h" />
<ClInclude Include="IopGte.h" />
<ClInclude Include="MemoryCardProtocol.h" />
<ClInclude Include="MultitapProtocol.h" />
<ClInclude Include="PAD\Gamepad.h" />
<ClInclude Include="PAD\Host\Global.h" />
<ClInclude Include="PAD\Host\KeyStatus.h" />
@ -567,6 +570,7 @@
<ClInclude Include="Recording\PadData.h" />
<ClInclude Include="Recording\Utilities\InputRecordingLogger.h" />
<ClInclude Include="ShaderCacheVersion.h" />
<ClInclude Include="SioTypes.h" />
<ClInclude Include="SPU2\Config.h" />
<ClInclude Include="SPU2\Global.h" />
<ClInclude Include="SPU2\Host\Config.h" />
@ -651,7 +655,6 @@
<ClInclude Include="Patch.h" />
<ClInclude Include="PCSX2Base.h" />
<ClInclude Include="PrecompiledHeader.h" />
<ClInclude Include="sio_internal.h" />
<ClInclude Include="ps2\pgif.h" />
<ClInclude Include="USB\USB.h" />
<ClInclude Include="Utilities\AsciiFile.h" />
@ -716,7 +719,6 @@
<ClInclude Include="IopCounters.h" />
<ClInclude Include="IopDma.h" />
<ClInclude Include="IopMem.h" />
<ClInclude Include="IopSio2.h" />
<ClInclude Include="R3000A.h" />
<ClInclude Include="Sio.h" />
<ClInclude Include="x86\iR3000A.h" />

View File

@ -569,9 +569,6 @@
<ClCompile Include="IopMem.cpp">
<Filter>System\Ps2\Iop</Filter>
</ClCompile>
<ClCompile Include="IopSio2.cpp">
<Filter>System\Ps2\Iop</Filter>
</ClCompile>
<ClCompile Include="R3000A.cpp">
<Filter>System\Ps2\Iop</Filter>
</ClCompile>
@ -1296,6 +1293,12 @@
<ClCompile Include="Frontend\DInputSource.cpp">
<Filter>Host</Filter>
</ClCompile>
<ClCompile Include="MemoryCardProtocol.cpp">
<Filter>System\Ps2\Iop</Filter>
</ClCompile>
<ClCompile Include="MultitapProtocol.cpp">
<Filter>System\Ps2\Iop</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Patch.h">
@ -1478,9 +1481,6 @@
<ClInclude Include="IopMem.h">
<Filter>System\Ps2\Iop</Filter>
</ClInclude>
<ClInclude Include="IopSio2.h">
<Filter>System\Ps2\Iop</Filter>
</ClInclude>
<ClInclude Include="R3000A.h">
<Filter>System\Ps2\Iop</Filter>
</ClInclude>
@ -1640,9 +1640,6 @@
<ClInclude Include="IopGte.h">
<Filter>System\Ps2\Iop</Filter>
</ClInclude>
<ClInclude Include="sio_internal.h">
<Filter>System\Ps2\Iop</Filter>
</ClInclude>
<ClInclude Include="ps2\pgif.h">
<Filter>System\Ps2</Filter>
</ClInclude>
@ -2162,6 +2159,15 @@
<ClInclude Include="Frontend\DInputSource.h">
<Filter>Host</Filter>
</ClInclude>
<ClInclude Include="SioTypes.h">
<Filter>System\Ps2\Iop</Filter>
</ClInclude>
<ClInclude Include="MemoryCardProtocol.h">
<Filter>System\Ps2\Iop</Filter>
</ClInclude>
<ClInclude Include="MultitapProtocol.h">
<Filter>System\Ps2\Iop</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuildStep Include="rdebug\deci2.h">

View File

@ -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<mem8_t>( 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) )

View File

@ -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<mem8_t>( 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<u32>(val) >> 16) & 0xFF);
sio0.SetTxData((static_cast<u32>(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<u16>(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<u16>(val));
break;
case (HW_SIO_BAUD & 0x0fff):
sio0.SetBaud(static_cast<u16>(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;

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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