Files
archived-pcsx2/pcsx2/Sio.cpp
cottonvibes c05dc759e3 Big gif transfer code rewrite!
Pcsx2 now has a gifUnit class which acts like the ps2's gif and executes a single gif transfer at a time (and performs path arbitration according to priority and path3 slicing).

This new code is generally a speedup for most games. Particularly VU heavy games like GoW.
This revision breaks old saved state compatibility so don't update if you wish to keep playing with your old saved states.
Leave comments if this revision fixes or breaks anything...

Message to GS Plugin authors:
This new code now uses only 1 gif callback to transfer data to the gs plugin (GSgifTransfer).
pcsx2 also is garaunteed to send full GS primitives to the plugin. So you don't have to account for partial-transfers anymore.

Thanks goes out to shadowlady who tested around 500 games for me :D

Note 1: The old gif code is still in this revision, and can be enabled by the USE_OLD_GIF macro. The old code will be deleted soon.
Note 2: This revision also enables assertion dialogs in devel builds, and changed a lot of assume cases into assertions.


git-svn-id: http://pcsx2.googlecode.com/svn/trunk@4821 96395faa-99c1-11dd-bbfe-3dabce05a288
2011-07-24 13:02:50 +00:00

835 lines
23 KiB
C++

/* 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 "IopCommon.h"
#include "Sio.h"
#include "sio_internal.h"
_sio sio;
static const u8 cardh[4] = { 0xFF, 0xFF, 0x5a, 0x5d };
// 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?).
static const mc_command_0x26_tag mc_sizeinfo_8mb= {'+', 512, 16, 0x4000, 0x52, 0x5A};
// Ejection timeout management belongs in the MemoryCardFile plugin, except the plugin
// interface is not yet complete.
//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;
static const int FORCED_MCD_EJECTION_NUM_SLOTS =2;
static int m_ForceEjectionTimeout[FORCED_MCD_EJECTION_NUM_SLOTS];
static wxDateTime m_ForceEjection_minTriesTimestamp[FORCED_MCD_EJECTION_NUM_SLOTS];
wxString GetTimeMsStr(){
wxDateTime unow=wxDateTime::UNow();
wxString res;
res.Printf(L"%s.%03d", unow.Format(L"%H:%M:%S").c_str(), (int)unow.GetMillisecond() );
return res;
}
void _SetForceMcdEjectTimeoutNow(int port, int slot, int mcdEjectTimeoutInSioAccesses)
{
m_ForceEjectionTimeout[port]=mcdEjectTimeoutInSioAccesses;
}
//allow timeout also for the mcd manager panel
void SetForceMcdEjectTimeoutNow()
{
const int slot=0;
int port=0;
for (port=0; port<2; port++)
_SetForceMcdEjectTimeoutNow(port, slot, FORCED_MCD_EJECTION_MAX_TRIES);
}
// SIO Inline'd IRQs : Calls the SIO interrupt handlers directly instead of
// feeding them through the IOP's branch test. (see SIO.H for details)
#ifdef SIO_INLINE_IRQS
#define SIO_INT() sioInterrupt()
#define SIO_FORCEINLINE __fi
#else
__fi void SIO_INT()
{
if( !(psxRegs.interrupt & (1<<IopEvt_SIO)) )
PSX_INT(IopEvt_SIO, 64 ); // PSXCLK/250000);
}
#define SIO_FORCEINLINE __fi
#endif
// 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.
static bool IsMtapPresent( uint port )
{
return EmuConfig.MultitapEnabled( port );
//return (0 != PADqueryMtap(port+1));
}
static void _ReadMcd(u8 *data, u32 adr, int size)
{
SysPlugins.McdRead(
sio.GetMemcardIndex(), sio.activeMemcardSlot[sio.GetMemcardIndex()],
data, adr, size
);
}
static void _SaveMcd(const u8 *data, u32 adr, int size)
{
SysPlugins.McdSave(
sio.GetMemcardIndex(), sio.activeMemcardSlot[sio.GetMemcardIndex()],
data, adr, size
);
}
static void _EraseMCDBlock(u32 adr)
{
SysPlugins.McdEraseBlock( sio.GetMemcardIndex(), sio.activeMemcardSlot[sio.GetMemcardIndex()], adr );
}
static u8 sio_xor( const u8 *buf, uint length )
{
u8 i, x;
for (x=0, i=0; i<length; i++) x ^= buf[i];
return x;
}
template< typename T >
static void apply_xor( u8& dest, const T& src )
{
u8* buf = (u8*)&src;
for (uint x=0; x<sizeof(src); x++) dest ^= buf[x];
}
void sioInit()
{
memzero(sio);
memzero(m_ForceEjectionTimeout);
// Transfer(?) Ready and the Buffer is Empty
sio.StatReg = TX_RDY | TX_EMPTY;
sio.packetsize = 0;
sio.terminator = 0x55; // Command terminator 'U'
}
u8 sioRead8() {
u8 ret = 0xFF;
if (sio.StatReg & RX_RDY) {
ret = sio.buf[sio.parp];
if (sio.parp == sio.bufcount) {
sio.StatReg &= ~RX_RDY; // Receive is not Ready now?
sio.StatReg |= TX_EMPTY; // Buffer is Empty
if (sio.padst == 2) sio.padst = 0;
/*if (sio.mcdst == 1) {
sio.mcdst = 99;
sio.StatReg&= ~TX_EMPTY;
sio.StatReg|= RX_RDY;
}*/
}
}
//PAD_LOG("sio read8 ;ret = %x", ret);
return ret;
}
void SIO_CommandWrite(u8 value,int way) {
PAD_LOG("sio write8 %x", value);
// PAD COMMANDS
switch (sio.padst) {
case 1: SIO_INT();
if ((value&0x40) == 0x40) {
sio.padst = 2; sio.parp = 1;
switch (sio.CtrlReg&0x2002) {
case 0x0002:
sio.packetsize ++; // Total packet size sent
sio.buf[sio.parp] = PADpoll(value);
break;
case 0x2002:
sio.packetsize ++; // Total packet size sent
sio.buf[sio.parp] = PADpoll(value);
break;
}
if (!(sio.buf[sio.parp] & 0x0f)) {
sio.bufcount = 2 + 32;
} else {
sio.bufcount = 2 + (sio.buf[sio.parp] & 0x0f) * 2;
}
}
else sio.padst = 0;
return;
case 2:
sio.parp++;
switch (sio.CtrlReg&0x2002) {
case 0x0002: sio.packetsize ++; sio.buf[sio.parp] = PADpoll(value); break;
case 0x2002: sio.packetsize ++; sio.buf[sio.parp] = PADpoll(value); break;
}
if (sio.parp == sio.bufcount) { sio.padst = 0; return; }
SIO_INT();
return;
case 3:
// No pad connected.
sio.parp++;
if (sio.parp == sio.bufcount) { sio.padst = 0; return; }
SIO_INT();
return;
}
// MEMORY CARD COMMANDS
switch (sio.mcdst) {
case 1:
{
sio.packetsize++;
SIO_INT();
if (sio.rdwr) { sio.parp++; return; }
sio.parp = 1;
const char* log_cmdname = "";
switch (value) {
case 0x11: // RESET
log_cmdname = "Reset1";
sio.bufcount = 8;
memset8<0xff>(sio.buf);
sio.buf[3] = sio.terminator;
sio.buf[2] = '+';
sio.mcdst = 99;
sio2.packet.recvVal3 = 0x8c;
break;
// FIXME : Why are there two identical cases for resetting the
// memorycard(s)? there doesn't appear to be anything dealing with
// card slots here. --air
case 0x12: // RESET
log_cmdname = "Reset2";
sio.bufcount = 8;
memset8<0xff>(sio.buf);
sio.buf[3] = sio.terminator;
sio.buf[2] = '+';
sio.mcdst = 99;
sio2.packet.recvVal3 = 0x8c;
break;
case 0x81: // COMMIT
log_cmdname = "Commit";
sio.bufcount = 8;
memset8<0xff>(sio.buf);
sio.mcdst = 99;
sio.buf[3] = sio.terminator;
sio.buf[2] = '+';
sio2.packet.recvVal3 = 0x8c;
if(value == 0x81) {
if(sio.mc_command==0x42)
sio2.packet.recvVal1 = 0x1600; // Writing
else if(sio.mc_command==0x43) sio2.packet.recvVal1 = 0x1700; // Reading
}
break;
case 0x21:
case 0x22:
case 0x23: // SECTOR SET
log_cmdname = "SetSector";
sio.bufcount = 8; sio.mcdst = 99; sio.sector=0; sio.k=0;
memset8<0xff>(sio.buf);
sio2.packet.recvVal3 = 0x8c;
sio.buf[8]=sio.terminator;
sio.buf[7]='+';
break;
case 0x24: break;
case 0x25: break;
case 0x26:
{
log_cmdname = "GetInfo";
const uint port = sio.GetMemcardIndex();
const uint slot = sio.activeMemcardSlot[port];
mc_command_0x26_tag cmd = mc_sizeinfo_8mb;
PS2E_McdSizeInfo info;
info.SectorSize = cmd.sectorSize;
info.EraseBlockSizeInSectors = cmd.eraseBlocks;
info.McdSizeInSectors = cmd.mcdSizeInSectors;
SysPlugins.McdGetSizeInfo( port, slot, info );
pxAssertDev( cmd.mcdSizeInSectors >= mc_sizeinfo_8mb.mcdSizeInSectors,
"Mcd plugin returned an invalid memorycard size: Cards smaller than 8MB are not supported." );
cmd.sectorSize = info.SectorSize;
cmd.eraseBlocks = info.EraseBlockSizeInSectors;
cmd.mcdSizeInSectors = info.McdSizeInSectors;
// Recalculate the xor summation
// This uses a trick of removing the known xor values for a default 8mb memorycard (for which the XOR
// was calculated), and replacing it with our new values.
apply_xor( cmd.mc_xor, mc_sizeinfo_8mb.sectorSize );
apply_xor( cmd.mc_xor, mc_sizeinfo_8mb.eraseBlocks );
apply_xor( cmd.mc_xor, mc_sizeinfo_8mb.mcdSizeInSectors );
apply_xor( cmd.mc_xor, cmd.sectorSize );
apply_xor( cmd.mc_xor, cmd.eraseBlocks );
apply_xor( cmd.mc_xor, cmd.mcdSizeInSectors );
sio.bufcount = 12; sio.mcdst = 99; sio2.packet.recvVal3 = 0x83;
memset8<0xff>(sio.buf);
memcpy_fast(&sio.buf[2], &cmd, sizeof(cmd));
sio.buf[12]=sio.terminator;
}
break;
case 0x27:
case 0x28:
case 0xBF:
log_cmdname = "NotSure"; // FIXME !!
sio.bufcount = 4; sio.mcdst = 99; sio2.packet.recvVal3 = 0x8b;
memset8<0xff>(sio.buf);
sio.buf[4]=sio.terminator;
sio.buf[3]='+';
break;
// FIXME ?
// sio.lastsector and sio.mode are unused.
case 0x42: // WRITE
log_cmdname = "Write";
//sio.mode = 0;
goto __doReadWrite;
case 0x43: // READ
log_cmdname = "Read";
//sio.lastsector = sio.sector; // Reading
goto __doReadWrite;
case 0x82:
log_cmdname = "Read(?)"; // FIXME !!
//if(sio.lastsector==sio.sector) sio.mode = 2;
__doReadWrite:
sio.bufcount =133; sio.mcdst = 99;
memset8<0xff>(sio.buf);
sio.buf[133]=sio.terminator;
sio.buf[132]='+';
break;
case 0xf0:
case 0xf1:
case 0xf2:
log_cmdname = "NoClue"; // FIXME !!
sio.mcdst = 99;
break;
case 0xf3:
case 0xf7:
log_cmdname = "NoClueHereEither"; // FIXME !!
sio.bufcount = 4; sio.mcdst = 99;
memset8<0xff>(sio.buf);
sio.buf[4]=sio.terminator;
sio.buf[3]='+';
break;
case 0x52:
log_cmdname = "FixMe"; // FIXME !!
sio.rdwr = 1; memset8<0xff>(sio.buf);
sio.buf[sio.bufcount]=sio.terminator; sio.buf[sio.bufcount-1]='+';
break;
case 0x57:
log_cmdname = "FixMe"; // FIXME !!
sio.rdwr = 2; memset8<0xff>(sio.buf);
sio.buf[sio.bufcount]=sio.terminator; sio.buf[sio.bufcount-1]='+';
break;
default:
log_cmdname = "Unknown";
sio.mcdst = 0;
memset8<0xff>(sio.buf);
sio.buf[sio.bufcount]=sio.terminator; sio.buf[sio.bufcount-1]='+';
}
MEMCARDS_LOG("MC(%d) command 0x%02X [%s]", sio.GetMemcardIndex()+1, value, log_cmdname);
sio.mc_command = value;
}
return; // END CASE 1.
// FURTHER PROCESSING OF THE MEMORY CARD COMMANDS
case 99:
{
sio.packetsize++;
sio.parp++;
switch(sio.mc_command)
{
// SET_ERASE_PAGE; the next erase commands will *clear* data starting with the page set here
case 0x21:
// SET_WRITE_PAGE; the next write commands will commit data starting with the page set here
case 0x22:
// SET_READ_PAGE; the next read commands will return data starting with the page set here
case 0x23:
if (sio.parp==2)sio.sector|=(value & 0xFF)<< 0;
if (sio.parp==3)sio.sector|=(value & 0xFF)<< 8;
if (sio.parp==4)sio.sector|=(value & 0xFF)<<16;
if (sio.parp==5)sio.sector|=(value & 0xFF)<<24;
if (sio.parp==6)
{
if (sio_xor((u8 *)&sio.sector, 4) == value)
MEMCARDS_LOG("MC(%d) SET PAGE sio.sector, sector=0x%04X", sio.GetMemcardIndex()+1, sio.sector);
else
MEMCARDS_LOG("MC(%d) SET PAGE XOR value ERROR 0x%02X != ^0x%02X",
sio.GetMemcardIndex()+1, value, sio_xor((u8 *)&sio.sector, 4));
}
break;
// SET_TERMINATOR; reads the new terminator code
case 0x27:
if(sio.parp==2) {
sio.terminator = value;
sio.buf[4] = value;
MEMCARDS_LOG("MC(%d) SET TERMINATOR command, value=0x%02X", sio.GetMemcardIndex()+1, value);
}
break;
// GET_TERMINATOR; puts in position 3 the current terminator code and in 4 the default one
// depending on the param
case 0x28:
if(sio.parp == 2) {
sio.buf[2] = '+';
sio.buf[3] = sio.terminator;
//if(value == 0) sio.buf[4] = 0xFF;
sio.buf[4] = 0x55;
MEMCARDS_LOG("MC(%d) GET TERMINATOR command, value=0x%02X", sio.GetMemcardIndex()+1, value);
}
break;
// WRITE DATA
case 0x42:
if (sio.parp==2) {
sio.bufcount=5+value;
memset8<0xff>(sio.buf);
sio.buf[sio.bufcount-1]='+';
sio.buf[sio.bufcount]=sio.terminator;
MEMCARDS_LOG("MC(%d) WRITE command, size=0x%02X", sio.GetMemcardIndex()+1, value);
}
else
if ((sio.parp>2) && (sio.parp<sio.bufcount-2)) {
sio.buf[sio.parp]=value;
//MEMCARDS_LOG("MC(%d) WRITING 0x%02X", sio.GetMemcardIndex()+1, value);
} else
if (sio.parp==sio.bufcount-2) {
if (sio_xor(&sio.buf[3], sio.bufcount-5)==value) {
_SaveMcd(&sio.buf[3], (512+16)*sio.sector+sio.k, sio.bufcount-5);
sio.buf[sio.bufcount-1]=value;
sio.k+=sio.bufcount-5;
} else {
MEMCARDS_LOG("MC(%d) write XOR value error 0x%02X != ^0x%02X",
sio.GetMemcardIndex()+1, value, sio_xor(&sio.buf[3], sio.bufcount-5));
}
}
break;
// READ DATA
case 0x43:
if (sio.parp==2)
{
//int i;
sio.bufcount=value+5;
sio.buf[3]='+';
MEMCARDS_LOG("MC(%d) READ command, size=0x%02X", sio.GetMemcardIndex()+1, value);
_ReadMcd(&sio.buf[4], (512+16)*sio.sector+sio.k, value);
/*if(sio.mode==2)
{
int j;
for(j=0; j < value; j++)
sio.buf[4+j] = ~sio.buf[4+j];
}*/
sio.k+=value;
sio.buf[sio.bufcount-1]=sio_xor(&sio.buf[4], value);
sio.buf[sio.bufcount]=sio.terminator;
}
break;
// INTERNAL ERASE
case 0x82:
if(sio.parp==2)
{
sio.buf[2]='+';
sio.buf[3]=sio.terminator;
//if (sio.k != 0 || (sio.sector & 0xf) != 0)
// Console.Warning("saving : odd position for erase.");
_EraseMCDBlock((512+16)*(sio.sector&~0xf));
/* memset(sio.buf, -1, 256);
_SaveMcd(sio.buf, (512+16)*sio.sector, 256);
_SaveMcd(sio.buf, (512+16)*sio.sector+256, 256);
_SaveMcd(sio.buf, (512+16)*sio.sector+512, 16);
sio.buf[2]='+';
sio.buf[3]=sio.terminator;*/
//sio.buf[sio.bufcount] = sio.terminator;
MEMCARDS_LOG("MC(%d) INTERNAL ERASE command 0x%02X", sio.GetMemcardIndex()+1, value);
}
break;
// CARD AUTHENTICATION CHECKS
case 0xF0:
if (sio.parp==2)
{
MEMCARDS_LOG("MC(%d) CARD AUTH :0x%02X", sio.GetMemcardIndex()+1, value);
switch(value){
case 1:
case 2:
case 4:
case 15:
case 17:
case 19:
sio.bufcount=13;
memset8<0xff>(sio.buf);
sio.buf[12] = 0; // Xor value of data from index 4 to 11
sio.buf[3]='+';
sio.buf[13] = sio.terminator;
break;
case 6:
case 7:
case 11:
sio.bufcount=13;
memset8<0xff>(sio.buf);
sio.buf[12]='+';
sio.buf[13] = sio.terminator;
break;
default:
sio.bufcount=4;
memset8<0xff>(sio.buf);
sio.buf[3]='+';
sio.buf[4] = sio.terminator;
}
}
break;
}
if (sio.bufcount<=sio.parp) sio.mcdst = 0;
}
return; // END CASE 99.
}
switch (sio.mtapst)
{
case 0x1:
sio.packetsize++;
sio.parp = 1;
SIO_INT();
switch(value) {
case 0x12:
// Query number of pads supported.
sio.buf[3] = 4;
sio.mtapst = 2;
sio.bufcount = 5;
break;
case 0x13:
// Query number of memcards supported.
sio.buf[3] = 4;
sio.mtapst = 2;
sio.bufcount = 5;
break;
case 0x21:
// Set pad slot.
sio.mtapst = value;
sio.bufcount = 6; // No idea why this is 6, saved from old code.
break;
case 0x22:
// Set memcard slot.
sio.mtapst = value;
sio.bufcount = 6; // No idea why this is 6, saved from old code.
break;
}
// Commented out values are from original code. They break multitap in bios.
sio.buf[sio.bufcount-1]=0;//'+';
sio.buf[sio.bufcount]=0;//'Z';
return;
case 0x2:
sio.packetsize++;
sio.parp++;
if (sio.bufcount<=sio.parp) sio.mcdst = 0;
SIO_INT();
return;
case 0x21:
// Set pad slot.
sio.packetsize++;
sio.parp++;
sio.mtapst = 2;
if (sio.CtrlReg & 2)
{
int port = sio.GetMultitapPort();
if (IsMtapPresent(port))
sio.activePadSlot[port] = value;
}
SIO_INT();
return;
case 0x22:
// Set memcard slot.
sio.packetsize++;
sio.parp++;
sio.mtapst = 2;
if (sio.CtrlReg & 2)
{
int port = sio.GetMultitapPort();
if (IsMtapPresent(port))
sio.activeMemcardSlot[port] = value;
}
SIO_INT();
return;
}
if(sio.count == 1 || way == 0) InitializeSIO(value);
}
void InitializeSIO(u8 value)
{
switch (value) {
case 0x01: // start pad
sio.StatReg &= ~TX_EMPTY; // Now the Buffer is not empty
sio.StatReg |= RX_RDY; // Transfer is Ready
sio.bufcount = 4; // Default size, when no pad connected.
sio.parp = 0;
sio.padst = 1;
sio.packetsize = 1;
sio.count = 0;
sio2.packet.recvVal1 = 0x1100; // Pad is present
if( (sio.CtrlReg & 2) == 2 )
{
int padslot = (sio.CtrlReg>>12) & 2; // move 0x2000 bitmask into leftmost bits
if( padslot != 1 )
{
padslot >>= 1; // transform 0/2 to be 0/1 values
if (!PADsetSlot(padslot+1, 1+sio.activePadSlot[padslot]) && sio.activePadSlot[padslot])
{
// Pad is not present. Don't send poll, just return a bunch of 0's.
sio2.packet.recvVal1 = 0x1D100;
sio.padst = 3;
}
else {
sio.buf[0] = PADstartPoll(padslot+1);
}
}
}
SIO_INT();
return;
case 0x21: // start mtap
sio.StatReg &= ~TX_EMPTY; // Now the Buffer is not empty
sio.StatReg |= RX_RDY; // Transfer is Ready
sio.parp = 0;
sio.packetsize = 1;
sio.mtapst = 1;
sio.count = 0;
sio2.packet.recvVal1 = 0x1D100; // Mtap is not connected :(
if (sio.CtrlReg & 2) // No idea if this test is needed. Pads use it, memcards don't.
{
int port = sio.GetMultitapPort();
if (!IsMtapPresent(port))
{
// If "unplug" multitap mid game, set active slots to 0.
sio.activePadSlot[port] = 0;
sio.activeMemcardSlot[port] = 0;
}
else
{
sio.bufcount = 3;
sio.buf[0] = 0xFF;
sio.buf[1] = 0x80; // Have no idea if this is correct. From PSX mtap.
sio.buf[2] = 0x5A;
sio2.packet.recvVal1 = 0x1100; // Mtap is connected :)
}
}
SIO_INT();
return;
case 0x61: // start remote control sensor
sio.StatReg &= ~TX_EMPTY; // Now the Buffer is not empty
sio.StatReg |= RX_RDY; // Transfer is Ready
sio.parp = 0;
sio.packetsize = 1;
sio.count = 0;
sio2.packet.recvVal1 = 0x1100; // Pad is present
SIO_INT();
return;
case 0x81: // start memcard
{
sio.StatReg &= ~TX_EMPTY;
sio.StatReg |= RX_RDY;
memcpy(sio.buf, cardh, 4);
sio.parp = 0;
sio.bufcount = 8;
sio.mcdst = 1;
sio.packetsize = 1;
sio.rdwr = 0;
sio.count = 0;
// Memcard presence reporting!
// Note:
// 0x01100 means Memcard is present
// 0x1D100 means Memcard is missing.
const uint port = sio.GetMemcardIndex();
const uint slot = sio.activeMemcardSlot[port];
// forced ejection logic. Technically belongs in the McdIsPresent handler for
// the plugin, once the memorycard plugin system is completed.
// (ejection is only supported for the default non-multitap cards at this time)
bool forceEject = false;
if( slot == 0 && m_ForceEjectionTimeout[port]>0 )
{
if( m_ForceEjectionTimeout[port] == FORCED_MCD_EJECTION_MAX_TRIES && SysPlugins.McdIsPresent( port, slot ) )
Console.WriteLn( Color_Green, L"[%s] Auto-ejecting memcard [port:%d, slot:%d]", GetTimeMsStr().c_str(), port, slot );
--m_ForceEjectionTimeout[port];
forceEject = true;
int numTimesAccessed = FORCED_MCD_EJECTION_MAX_TRIES - m_ForceEjectionTimeout[port];
if ( numTimesAccessed == FORCED_MCD_EJECTION_MIN_TRIES )
{//minimum tries reached. start counting millisec timeout.
m_ForceEjection_minTriesTimestamp[port] = wxDateTime::UNow();
}
if ( numTimesAccessed > FORCED_MCD_EJECTION_MIN_TRIES )
{
wxTimeSpan delta = wxDateTime::UNow().Subtract( m_ForceEjection_minTriesTimestamp[port] );
if ( delta.GetMilliseconds() >= FORCED_MCD_EJECTION_MAX_MS_AFTER_MIN_TRIES )
{
DevCon.Warning( L"[%s] Auto-eject: Timeout reached after mcd was accessed %d times [port:%d, slot:%d]", GetTimeMsStr().c_str(), numTimesAccessed, port, slot);
m_ForceEjectionTimeout[port] = 0; //Done. on next sio access the card will be seen as inserted.
}
}
if( m_ForceEjectionTimeout[port] == 0 && SysPlugins.McdIsPresent( port, slot ))
Console.WriteLn( Color_Green, L"[%s] Re-inserting auto-ejected memcard [port:%d, slot:%d]", GetTimeMsStr().c_str(), port, slot);
}
if( !forceEject && SysPlugins.McdIsPresent( port, slot ) )
{
sio2.packet.recvVal1 = 0x1100;
PAD_LOG("START MEMCARD [port:%d, slot:%d] - Present", port, slot );
}
else
{
sio2.packet.recvVal1 = 0x1D100;
PAD_LOG("START MEMCARD [port:%d, slot:%d] - Missing", port, slot );
}
SIO_INT();
}
return;
}
}
void sioWrite8(u8 value)
{
SIO_CommandWrite(value,0);
}
void SIODMAWrite(u8 value)
{
SIO_CommandWrite(value,1);
}
void sioWriteCtrl16(u16 value) {
sio.CtrlReg = value & ~RESET_ERR;
if (value & RESET_ERR) sio.StatReg &= ~IRQ;
if ((sio.CtrlReg & SIO_RESET) || (!sio.CtrlReg))
{
sio.mtapst = 0; sio.padst = 0; sio.mcdst = 0; sio.parp = 0;
sio.StatReg = TX_RDY | TX_EMPTY;
psxRegs.interrupt &= ~(1<<IopEvt_SIO);
}
}
void SIO_FORCEINLINE sioInterrupt() {
PAD_LOG("Sio Interrupt");
sio.StatReg|= IRQ;
psxHu32(0x1070)|=0x80;
}
void SaveStateBase::sioFreeze()
{
// CRCs for memory cards.
u64 m_mcdCRCs[2][8];
FreezeTag( "sio" );
Freeze( sio );
// TODO : This stuff should all be moved to the memorycard plugin eventually,
// but that requires adding memorycard plugin to the savestate, and I'm not in
// the mood to do that (let's plan it for 0.9.8) --air
// Note: The Ejection system only works for the default non-multitap MemoryCards
// only. This is because it could become very (very!) slow to do a full CRC check
// on multiple 32 or 64 meg carts. I have chosen to save
if( IsSaving() )
{
for( uint port=0; port<2; ++port )
//for( uint slot=0; slot<4; ++slot )
{
const uint slot = 0; // see above comment about multitap slowness
m_mcdCRCs[port][slot] = SysPlugins.McdGetCRC( port, slot );
}
}
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( uint port=0; port<2; ++port )
//for( int slot=0; slot<4; ++slot )
{
const uint slot = 0; // see above comment about multitap slowness
u64 newCRC = SysPlugins.McdGetCRC( port, slot );
if( newCRC != m_mcdCRCs[port][slot] )
{
//m_mcdCRCs[port][slot] = newCRC;
//m_ForceEjectionTimeout[port] = 128;
_SetForceMcdEjectTimeoutNow(port, slot, FORCED_MCD_EJECTION_MAX_TRIES);
}
}
}
}