/* PCSX2 - PS2 Emulator for PCs * Copyright (C) 2002-2010 PCSX2 Dev Team * * PCSX2 is free software: you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. * * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with PCSX2. * If not, see . */ #include "PrecompiledHeader.h" #include "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. static int m_ForceEjectionTimeout[2]; // 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< static void apply_xor( u8& dest, const T& src ) { u8* buf = (u8*)&src; for (uint x=0; x(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 ); pxAssumeDev( 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.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] ) { --m_ForceEjectionTimeout[port]; forceEject = true; } 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<