Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PSX memory card support #2067

Merged
merged 1 commit into from Sep 17, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 2 additions & 3 deletions pcsx2/R3000A.cpp
Expand Up @@ -172,9 +172,8 @@ static __fi void _psxTestInterrupts()
IopTestEvent(IopEvt_SIF0, sif0Interrupt); // SIF0
IopTestEvent(IopEvt_SIF1, sif1Interrupt); // SIF1
IopTestEvent(IopEvt_SIF2, sif2Interrupt); // SIF2
#ifndef SIO_INLINE_IRQS
IopTestEvent(IopEvt_SIO, sioInterrupt);
#endif
// Originally controlled by a preprocessor define, now PSX dependent.
if (psxHu32(HW_ICFG) & (1 << 3)) IopTestEvent(IopEvt_SIO, sioInterruptR);
IopTestEvent(IopEvt_CdvdRead, cdvdReadInterrupt);

// Profile-guided Optimization (sorta)
Expand Down
80 changes: 43 additions & 37 deletions pcsx2/Sio.cpp
Expand Up @@ -26,6 +26,7 @@ _mcd *mcd;
SIO_MODE siomode = SIO_START;
static void sioWrite8inl(u8 data);
#define SIO_WRITE void inline
#define SIO_FORCEINLINE __fi

// Magic psx values from nocash info
static const u8 memcard_psx[] = {0x5A, 0x5D, 0x5C, 0x5D, 0x04, 0x00, 0x00, 0x80};
Expand Down Expand Up @@ -79,21 +80,6 @@ void ClearMcdEjectTimeoutNow()
}
}

// 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.
Expand Down Expand Up @@ -132,12 +118,32 @@ void sioInit()
sio.packetsize = 0;
}

void SIO_FORCEINLINE sioInterrupt()
{
PAD_LOG("Sio Interrupt");
sio.StatReg|= IRQ;
iopIntcIrq(7); //Should this be used instead of the one below?
//psxHu32(0x1070)|=0x80;
bool isR3000ATest = false;

// Check the active game's type, and fire the matching interrupt.
// The 3rd bit of the HW_IFCG register lets us know if PSX mode is active. 1 = PSX, 0 = PS2
// Note that the R3000A's call to interrupts only calls the PS2 based (lack of) delays.
SIO_FORCEINLINE void sioInterrupt() {
if ((psxHu32(HW_ICFG) & (1 << 3)) && !isR3000ATest) {
if (!(psxRegs.interrupt & (1 << IopEvt_SIO)))
PSX_INT(IopEvt_SIO, 64); // PSXCLK/250000);
} else {
PAD_LOG("Sio Interrupt");
sio.StatReg |= IRQ;
iopIntcIrq(7); //Should this be used instead of the one below?
//psxHu32(0x1070)|=0x80;
}

isR3000ATest = false;
}

// An offhand way for the R3000A to access the sioInterrupt function.
// Following the design of the old preprocessor system, the R3000A should
// never call the PSX delays (oddly enough), and only the PS2. So we need
// an extra layer here to help control that.
__fi void sioInterruptR() {
isR3000ATest = true;
sioInterrupt();
}

SIO_WRITE sioWriteStart(u8 data)
Expand Down Expand Up @@ -202,7 +208,7 @@ SIO_WRITE sioWriteController(u8 data)
break;
}
//Console.WriteLn( "SIO: sent = %02X From pad data = %02X bufCnt %08X ", data, sio.buf[sio.bufCount], sio.bufCount);
SIO_INT(); //Don't all commands(transfers) cause an interrupt?
sioInterrupt(); //Don't all commands(transfers) cause an interrupt?
}

SIO_WRITE sioWriteMultitap(u8 data)
Expand Down Expand Up @@ -290,7 +296,7 @@ SIO_WRITE sioWriteMultitap(u8 data)
//default: sio.buf[sio.bufCount] = 0x00; break;
}

SIO_INT();
sioInterrupt();
}

SIO_WRITE MemcardResponse()
Expand Down Expand Up @@ -365,7 +371,7 @@ SIO_WRITE memcardErase(u8 data)
break;
}
}
SIO_INT();
sioInterrupt();
break;

default:
Expand Down Expand Up @@ -427,7 +433,7 @@ SIO_WRITE memcardWrite(u8 data)
}

}
SIO_INT();
sioInterrupt();
break;

case 2:
Expand Down Expand Up @@ -516,7 +522,7 @@ SIO_WRITE memcardRead(u8 data)
break;
}
}
SIO_INT();
sioInterrupt();
break;

case 2:
Expand Down Expand Up @@ -629,7 +635,7 @@ SIO_WRITE sioWriteMemcard(u8 data)
case 0:
SIO_STAT_READY();
memcardInit();
SIO_INT();
sioInterrupt();
break;

case 1:
Expand Down Expand Up @@ -703,7 +709,7 @@ SIO_WRITE sioWriteMemcard(u8 data)
siomode = SIO_DUMMY;
break;
}
SIO_INT();
sioInterrupt();
break;

case 2:
Expand All @@ -725,7 +731,7 @@ SIO_WRITE sioWriteMemcardPSX(u8 data)
case 0: // Same init stuff...
SIO_STAT_READY();
memcardInit();
SIO_INT();
sioInterrupt();
break;

case 1:
Expand Down Expand Up @@ -756,7 +762,7 @@ SIO_WRITE sioWriteMemcardPSX(u8 data)
siomode = SIO_DUMMY;
break;
}
SIO_INT();
sioInterrupt();
break;

case 2: break;
Expand Down Expand Up @@ -831,7 +837,7 @@ SIO_WRITE sioWriteInfraRed(u8 data)
SIO_STAT_READY();
DEVICE_PLUGGED();
siomode = SIO_DUMMY;
SIO_INT();
sioInterrupt();
}

//This bit-field in the STATUS register contains the (inveted) state of the /ACK linre from the Controller / MC.
Expand All @@ -856,12 +862,12 @@ SIO_WRITE sioWriteInfraRed(u8 data)
void chkTriggerInt() {
//Conditions for triggerring an interrupt.
//this is not correct, but ... it can be fixed later
SIO_INT(); return;
if ((sio.StatReg & IRQ)) { SIO_INT(); return; } //The interrupt flag in the main INTR_STAT reg should go active on multiple occasions. Set it here for now (hack), until the correct mechanism is made.
if ((sio.CtrlReg & ACK_INT_EN) && ((sio.StatReg & TX_RDY) || (sio.StatReg & TX_EMPTY))) { SIO_INT(); return; }
if ((sio.CtrlReg & ACK_INT_EN) && (sio.StatReg & ACK_INP)) { SIO_INT(); return; }
sioInterrupt(); return;
if ((sio.StatReg & IRQ)) { sioInterrupt(); return; } //The interrupt flag in the main INTR_STAT reg should go active on multiple occasions. Set it here for now (hack), until the correct mechanism is made.
if ((sio.CtrlReg & ACK_INT_EN) && ((sio.StatReg & TX_RDY) || (sio.StatReg & TX_EMPTY))) { sioInterrupt(); return; }
if ((sio.CtrlReg & ACK_INT_EN) && (sio.StatReg & ACK_INP)) { sioInterrupt(); return; }
//The following one may be incorrect.
//if ((sio.CtrlReg & RX_INT_EN) && ((byteCnt >= (1<< ((sio.CtrlReg & RX_BYTES_INT) >>8))) ? 1:0) ) { SIO_INT(); return; }
//if ((sio.CtrlReg & RX_INT_EN) && ((byteCnt >= (1<< ((sio.CtrlReg & RX_BYTES_INT) >>8))) ? 1:0) ) { sioInterrupt(); return; }
return;
}

Expand Down Expand Up @@ -889,7 +895,7 @@ static void sioWrite8inl(u8 data)
if (IS_LAST_BYTE_IN_PACKET != 1) //The following should be set after each byte transfer but the last one.
sio.StatReg |= ACK_INP; //Signal that Controller (or MC) has brought the /ACK (Acknowledge) line active low.

SIO_INT();
sioInterrupt();
//chkTriggerInt();
//Console.WriteLn( "SIO0 WR DATA COMMON %02X INT_STAT= %08X IOPpc= %08X " , data, psxHu32(0x1070), psxRegs.pc);
byteCnt++;
Expand Down
5 changes: 1 addition & 4 deletions pcsx2/Sio.h
Expand Up @@ -15,10 +15,6 @@

#pragma once

// Let's enable this to free the IOP event handler of some considerable load.
// Games are highly unlikely to need timed IRQ's for PAD and MemoryCard handling anyway (rama).
#define SIO_INLINE_IRQS

#include "MemoryCardFile.h"

struct _mcd
Expand Down Expand Up @@ -125,6 +121,7 @@ extern u8 sioRead8();
extern void sioWrite8(u8 value);
extern void sioWriteCtrl16(u16 value);
extern void sioInterrupt();
extern void sioInterruptR();
extern void InitializeSIO(u8 value);
extern void SetForceMcdEjectTimeoutNow();
extern void ClearMcdEjectTimeoutNow();
Expand Down
3 changes: 2 additions & 1 deletion pcsx2/gui/Dialogs/ConfigurationDialog.h
Expand Up @@ -198,13 +198,14 @@ namespace Dialogs
#ifdef __WXMSW__
pxCheckBox* m_check_CompressNTFS;
#endif
pxCheckBox* m_check_psx;

public:
virtual ~CreateMemoryCardDialog() = default;
CreateMemoryCardDialog( wxWindow* parent, const wxDirName& mcdpath, const wxString& suggested_mcdfileName);

//duplicate of MemoryCardFile::Create. Don't know why the existing method isn't used. - avih
static bool CreateIt( const wxString& mcdFile, uint sizeInMB );
static bool CreateIt( const wxString& mcdFile, uint sizeInMB, bool isPSX );
wxString result_createdMcdFilename;
//wxDirName GetPathToMcds() const;

Expand Down
45 changes: 35 additions & 10 deletions pcsx2/gui/Dialogs/CreateMemoryCardDialog.cpp
Expand Up @@ -87,6 +87,8 @@ Dialogs::CreateMemoryCardDialog::CreateMemoryCardDialog( wxWindow* parent, const
s_padding += m_check_CompressNTFS | StdExpand();
#endif

s_padding += m_check_psx;

s_padding += 12;
s_padding += s_buttons | StdCenter();

Expand All @@ -110,24 +112,44 @@ wxDirName Dialogs::CreateMemoryCardDialog::GetPathToMcds() const
// When this GUI is moved into the FileMemoryCard plugin (where it eventually belongs),
// this function will be removed and the MemoryCardFile::Create() function will be used
// instead.
bool Dialogs::CreateMemoryCardDialog::CreateIt( const wxString& mcdFile, uint sizeInMB )
bool Dialogs::CreateMemoryCardDialog::CreateIt( const wxString& mcdFile, uint sizeInMB, bool isPSX )
{
//int enc[16] = {0x77,0x7f,0x7f,0x77,0x7f,0x7f,0x77,0x7f,0x7f,0x77,0x7f,0x7f,0,0,0,0};

u8 m_effeffs[528*16];
memset8<0xff>( m_effeffs );
// PS2 Memory Card
u8 m_effeffs[528 * 16];
memset8<0xff>(m_effeffs);

Console.WriteLn( L"(FileMcd) Creating new %uMB memory card: '%s'", sizeInMB, WX_STR(mcdFile) );
// PSX Memory Card; 8192 is the size in bytes of a single block of a PSX memory card (8 KiB).
u8 m_effeffs_psx[8192];
memset8<0xff>(m_effeffs_psx);

// Since isPSX will have a default false state, it makes more sense to check "not PSX" first
if (!isPSX) {
Console.WriteLn(L"(FileMcd) Creating new PS2 %uMB memory card: '%s'", sizeInMB, WX_STR(mcdFile));
}
else {
Console.WriteLn(L"(FileMcd) Creating new PSX 128 KiB memory card: '%s'", WX_STR(mcdFile));
}

wxFFile fp( mcdFile, L"wb" );
if( !fp.IsOpened() ) return false;

static const int MC2_MBSIZE = 1024 * 528 * 2; // Size of a single megabyte of card data

for( uint i=0; i<(MC2_MBSIZE*sizeInMB)/sizeof(m_effeffs); i++ )
{
if( fp.Write( m_effeffs, sizeof(m_effeffs) ) == 0 )
return false;
if (!isPSX) {
for (uint i = 0; i<(MC2_MBSIZE*sizeInMB) / sizeof(m_effeffs); i++) {
if (fp.Write(m_effeffs, sizeof(m_effeffs)) == 0) {
return false;
}
}
} else {
// PSX cards consist of 16 blocks, each 8 KiB in size.
for (uint i = 0; i < 16; i++) {
if (fp.Write(m_effeffs_psx, sizeof(m_effeffs_psx)) == 0) {
return false;
}
}
}

return true;
Expand Down Expand Up @@ -170,9 +192,10 @@ void Dialogs::CreateMemoryCardDialog::OnOk_Click( wxCommandEvent& evt )
}
} else {
// otherwise create a file
if ( !CreateIt(
if (!CreateIt(
fullPath,
m_radio_CardSize ? m_radio_CardSize->SelectedItem().SomeInt : 8
m_radio_CardSize ? m_radio_CardSize->SelectedItem().SomeInt : 8,
m_check_psx->GetValue()
) ) {
Msgbox::Alert(
_( "Error: The memory card could not be created." ),
Expand Down Expand Up @@ -230,5 +253,7 @@ void Dialogs::CreateMemoryCardDialog::CreateControls()

m_radio_CardSize = new pxRadioPanel( this, tbl_CardSizes );
m_radio_CardSize->SetDefaultItem(0);

m_check_psx = new pxCheckBox(this, "Make this a PSX card (128 KiB only, no folders!)");
}

2 changes: 1 addition & 1 deletion pcsx2/gui/Panels/MemoryCardListPanel.cpp
Expand Up @@ -551,7 +551,7 @@ void Panels::MemoryCardListPanel_Simple::AppStatusEvent_OnSettingsApplied()
wxString errMsg;
if (isValidNewFilename(m_Cards[slot].Filename.GetFullName(), GetMcdPath(), errMsg, 5))
{
if ( !Dialogs::CreateMemoryCardDialog::CreateIt(targetFile, 8) )
if ( !Dialogs::CreateMemoryCardDialog::CreateIt(targetFile, 8, false) )
Console.Error( L"Automatic createion of MCD '%s' failed. Hope for the best...", WX_STR(targetFile) );
else
Console.WriteLn( L"memcard created: '%s'.", WX_STR(targetFile) );
Expand Down