mirror of
https://github.com/emu-russia/pureikyubu.git
synced 2025-04-02 10:42:15 -04:00
676 lines
23 KiB
C++
676 lines
23 KiB
C++
// TODO : TEST WHEN DSP IS WORKING
|
|
// TODO : EXIINT and 0x8101 !!!!
|
|
// TODO : figure out what 0x81 code is supposed to do on a real memcard
|
|
|
|
#include "pch.h"
|
|
|
|
using namespace Debug;
|
|
|
|
/************************** Commands ***************************************/
|
|
|
|
/*
|
|
Commands for MX25L4004
|
|
ReadArray 0x52 SA2 SA1 PN BA 0x00 0x00 0x00 0x00
|
|
ArrayToBuffer 0x53 SA2 SA1 PN (not implemented yet)
|
|
ReadBuffer 0x81 BA
|
|
WriteBuffer 0x82 BA (not implemented yet)
|
|
StatusRead 0x83 0x00
|
|
ClearStatus 0x89
|
|
ReadId 0x85 0x00
|
|
ReadErrorBuffer 0x86 0x00 (not implemented yet)
|
|
SectorErase 0xF1 SA2 SA1
|
|
PageProgram 0xF2 SA2 SA1 PN BA
|
|
ExtraByteProgram 0xF3 SA2 SA1 PN BA (not implemented yet)
|
|
Sleep 0X88
|
|
WakeUp 0x87
|
|
|
|
Parameters:
|
|
(SA2 << 8) | SA1 = sector
|
|
PN = Page number
|
|
BA = Bytes Address
|
|
*/
|
|
|
|
#define MEMCARD_COMMAND_UNDEFINED 0xFF
|
|
#define MEMCARD_COMMAND_GETEXIID 0x00
|
|
#define MEMCARD_COMMAND_READID 0x85
|
|
#define MEMCARD_COMMAND_GETSTATUS 0x83
|
|
#define MEMCARD_COMMAND_CLEARSTATUS 0x89
|
|
#define MEMCARD_COMMAND_READARRAY 0x52
|
|
#define MEMCARD_COMMAND_PAGEPROGRAM 0xF2
|
|
#define MEMCARD_COMMAND_ERASESECTOR 0xF1
|
|
#define MEMCARD_COMMAND_ERASECARD 0xF4
|
|
#define MEMCARD_COMMAND_SLEEP 0x88
|
|
#define MEMCARD_COMMAND_WAKEUP 0x87
|
|
#define MEMCARD_COMMAND_ENABLEINTER 0x81
|
|
|
|
#define MEMCARD_VALID_ADDRESS 0x03FF037F
|
|
#define MEMCARD_BA_EXTRABYTES 0x80 // when a BA is passed as arg, MEMCARD_BA_EXTRABYTES defines wheter the arg are regular bytes or extra bytes
|
|
/******************************************************************************************************/
|
|
|
|
const uint32_t Memcard_BytesMask [5] = { 0x00000000, 0xFF000000, 0xFFFF0000, 0xFFFFFF00, 0xFFFFFFFF };
|
|
|
|
/*
|
|
* These functions just execute the readed command.
|
|
*/
|
|
|
|
/* 0x83 0x00
|
|
* Returns the status byte of the memcard.
|
|
*/
|
|
static void MCGetStatusProc (Memcard * memcard);
|
|
|
|
/* 0x89
|
|
* Clear errors bits on the status byte of the memcard.
|
|
*/
|
|
static void MCClearStatusProc (Memcard * memcard);
|
|
|
|
/* 0xF2 SA2 SA1 PN BA
|
|
* Reads a specified number of bytes from the specified pointer
|
|
* and writes them to the memcard, at the specified sector, page and byte address
|
|
*/
|
|
static void MCPageProgramProc (Memcard * memcard);
|
|
|
|
/* 0x52 SA2 SA1 PN BA 0x00 0x00 0x00 0x00
|
|
* Reads a specified number of bytes from the memcard
|
|
* at the specified sector, page and byte address,
|
|
* and writes them to the specified pointer
|
|
*/
|
|
static void MCReadArrayProc (Memcard * memcard);
|
|
|
|
/* 0xF1 SA2 SA1
|
|
* Erases the specified sector
|
|
*/
|
|
static void MCSectorEraseProc (Memcard * memcard);
|
|
|
|
/* 0x00 0x00
|
|
* Returns the Id of this EXI device ( the size of the memcards in MBits in this case )
|
|
* Returns 0 on error
|
|
*/
|
|
static void MCGetEXIDeviceIdProc (Memcard * memcard);
|
|
|
|
/* 0xF4 0x00 0x00
|
|
* Erases all the memcard data
|
|
*/
|
|
static void MCCardEraseProc (Memcard * memcard);
|
|
|
|
/* 0x81 EN
|
|
* Enables/disables interrupts
|
|
*/
|
|
static void MCEnableInterruptsProc (Memcard * memcard);
|
|
|
|
/* 0X85 0x00
|
|
* Returns the memcard id (Manufacturer and device id).
|
|
*/
|
|
static void MCReadIdProc (Memcard * memcard);
|
|
|
|
/* 0X88
|
|
* Puts the memcard in sleep mode
|
|
*/
|
|
static void MCSleepProc (Memcard * memcard);
|
|
|
|
/* 0X87
|
|
* Puts the memcard in non-sleep mode
|
|
*/
|
|
static void MCWakeUpProc (Memcard * memcard);
|
|
|
|
#define Num_Memcard_ValidCommands 11
|
|
const MCCommand Memcard_ValidCommands[Num_Memcard_ValidCommands] = {
|
|
{ 0, 1, MCImmRead, MCGetStatusProc,
|
|
MEMCARD_COMMAND_GETSTATUS }, //#define MEMCARD_COMMAND_GETSTATUS 0x83
|
|
{ 0, 0, MCImmWrite, MCClearStatusProc,
|
|
MEMCARD_COMMAND_CLEARSTATUS }, //#define MEMCARD_COMMAND_CLEARSTATUS 0x89
|
|
{ 4, 0, MCDmaWrite, MCPageProgramProc,
|
|
MEMCARD_COMMAND_PAGEPROGRAM }, //#define MEMCARD_COMMAND_PAGEPROGRAM 0xF2
|
|
{ 4, 4, MCImmRead | MCDmaRead, MCReadArrayProc,
|
|
MEMCARD_COMMAND_READARRAY }, //#define MEMCARD_COMMAND_READARRAY 0x52
|
|
{ 2, 0, MCImmWrite, MCSectorEraseProc,
|
|
MEMCARD_COMMAND_ERASESECTOR }, //#define MEMCARD_COMMAND_ERASESECTOR 0xF1
|
|
{ 0, 1, MCImmRead, MCGetEXIDeviceIdProc,
|
|
MEMCARD_COMMAND_GETEXIID }, //#define MEMCARD_COMMAND_GETEXIID 0x00
|
|
{ 0, 0, MCImmWrite, MCCardEraseProc,
|
|
MEMCARD_COMMAND_ERASECARD }, //#define MEMCARD_COMMAND_ERASECARD 0xF4
|
|
{ 1, 0, MCImmWrite, MCEnableInterruptsProc,
|
|
MEMCARD_COMMAND_ENABLEINTER }, //#define MEMCARD_COMMAND_ENABLEINTER 0x81
|
|
{ 0, 1, MCImmRead, MCReadIdProc,
|
|
MEMCARD_COMMAND_READID }, //#define MEMCARD_COMMAND_READID 0x85
|
|
{ 0, 0, MCImmWrite, MCSleepProc,
|
|
MEMCARD_COMMAND_SLEEP }, //#define MEMCARD_COMMAND_SLEEP 0x88
|
|
{ 0, 0, MCImmWrite, MCWakeUpProc,
|
|
MEMCARD_COMMAND_WAKEUP } //#define MEMCARD_COMMAND_WAKEUP 0x87
|
|
};
|
|
|
|
const uint32_t Memcard_ValidSizes[Num_Memcard_ValidSizes] = {
|
|
0x00080000, //524288 bytes , // Memory Card 59
|
|
0x00100000, //1048576 bytes , // Memory Card 123
|
|
0x00200000, //2097152 bytes , // Memory Card 251
|
|
0x00400000, //4194304 bytes , // Memory Card 507
|
|
0x00800000, //8388608 bytes , // Memory Card 1019
|
|
0x01000000 //16777216 bytes , // Memory Card 2043
|
|
};
|
|
|
|
bool Memcard_Connected[2] = { false, false };
|
|
bool SyncSave = false;
|
|
bool MCOpened = false;
|
|
|
|
Memcard memcard[2];
|
|
|
|
static uint32_t MCCalculateOffset (uint32_t mc_address) {
|
|
if (mc_address & MEMCARD_BA_EXTRABYTES)
|
|
Halt ("MC :: Extra bytes are not supported\n");
|
|
return (mc_address & 0x0000007F) |
|
|
((mc_address & 0x00000300) >> 1) |
|
|
((mc_address & 0x7FFF0000) >> 7);
|
|
}
|
|
|
|
static void MCSyncSave (Memcard * memcard, uint32_t offset , uint32_t size) {
|
|
if (SyncSave == true) // Bad idea!!
|
|
{
|
|
if (fseek(memcard->file, offset, SEEK_SET) != 0) {
|
|
Halt("MC :: Error at seeking the memcard file.\n");
|
|
return;
|
|
}
|
|
if (fwrite(&memcard->data[offset], size, 1, memcard->file) != 1) {
|
|
Halt("MC :: Error at writing the memcard file.\n");
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* All the following procedures asume that (memcard->connected == TRUE)
|
|
*/
|
|
/**********************************MCGetStatusProc*********************************************/
|
|
static void MCGetStatusProc (Memcard * memcard){
|
|
EXIRegs * exi = memcard->exi;
|
|
|
|
int auxbytes = (EXI_CR_TLEN(exi->cr) + 1);
|
|
exi->data = (exi->data & ~Memcard_BytesMask[auxbytes]) |
|
|
(((uint32_t)memcard->status << 24) & Memcard_BytesMask[auxbytes]);
|
|
}
|
|
/**********************************MCClearStatusProc*********************************************/
|
|
static void MCClearStatusProc (Memcard * memcard) {
|
|
// TODO : Verify which bits are cleared
|
|
//memcard[cardnum].status &= (~MEMCARD_STATUS_ERASEERROR | ~MEMCARD_STATUS_PROGRAMEERROR);
|
|
memcard->status &= (~MEMCARD_STATUS_ERASEERROR |
|
|
~MEMCARD_STATUS_PROGRAMEERROR |
|
|
~MEMCARD_STATUS_ARRAYTOBUFFER);
|
|
}
|
|
/**********************************MCPageProgramProc*********************************************/
|
|
static void MCPageProgramProc (Memcard * memcard){
|
|
EXIRegs * exi = memcard->exi;
|
|
|
|
uint32_t offset;
|
|
uint8_t auxbyte = (uint8_t)(memcard->commandData & 0x000000FF);
|
|
uint32_t auxdata = memcard->commandData;
|
|
uint8_t *abuf;
|
|
uint32_t size;
|
|
if (exi->cr & EXI_CR_DMA) {
|
|
abuf = &mi.ram[exi->madr & RAMMASK];
|
|
size = exi->len;
|
|
}
|
|
else {
|
|
Halt("MC : Unhandled Imm Page Program.\n");
|
|
return;
|
|
}
|
|
|
|
|
|
offset = MCCalculateOffset(auxdata);
|
|
|
|
if (offset >= memcard->size + size) {
|
|
Halt("MC :: PageProgram offset is out of range\n");
|
|
return;
|
|
}
|
|
|
|
/* memcard->status |= MEMCARD_STATUS_BUSY; */
|
|
|
|
memcpy(&memcard->data[offset], abuf, size);
|
|
|
|
MCSyncSave (memcard, offset, size);
|
|
|
|
/* memcard->status &= ~MEMCARD_STATUS_BUSY; */
|
|
|
|
exi->csr |= EXI_CSR_EXIINT;
|
|
}
|
|
/**********************************MCReadArrayProc*********************************************/
|
|
static void MCReadArrayProc (Memcard * memcard){
|
|
EXIRegs * exi = memcard->exi;
|
|
|
|
uint32_t offset;
|
|
uint8_t auxbyte = (uint8_t)(memcard->commandData & 0x000000FF);
|
|
int auxbytes = (EXI_CR_TLEN(exi->cr) + 1);
|
|
uint32_t auxdata = memcard->commandData;
|
|
uint8_t *abuf;
|
|
uint32_t size;
|
|
|
|
if (exi->cr & EXI_CR_DMA) {
|
|
abuf = &mi.ram[exi->madr & RAMMASK];
|
|
size = exi->len;
|
|
}
|
|
else {
|
|
abuf = (uint8_t *)&exi->data;
|
|
size = auxbytes;
|
|
}
|
|
|
|
offset = MCCalculateOffset(auxdata);
|
|
|
|
if (offset >= memcard->size + size) {
|
|
Halt("MC :: ReadArray offset is out of range\n");
|
|
return;
|
|
}
|
|
|
|
/* memcard->status |= MEMCARD_STATUS_BUSY; */
|
|
|
|
memcpy(abuf, &memcard->data[offset], size);
|
|
|
|
/* memcard->status &= ~MEMCARD_STATUS_BUSY; */
|
|
|
|
memcard->status |= MEMCARD_STATUS_ARRAYTOBUFFER;
|
|
|
|
|
|
if (exi->cr & EXI_CR_DMA) {
|
|
}
|
|
else {
|
|
exi->data = _byteswap_ulong( exi->data ) << (auxbytes - 4);
|
|
}
|
|
}
|
|
/**********************************MCSectorEraseProc*********************************************/
|
|
static void MCSectorEraseProc (Memcard * memcard){
|
|
EXIRegs * exi = memcard->exi;
|
|
uint32_t offset;
|
|
|
|
|
|
offset = MCCalculateOffset(memcard->commandData);
|
|
|
|
if (offset >= memcard->size) {
|
|
Halt("MC :: Erase sector is out of range\n");
|
|
return;
|
|
}
|
|
|
|
/* memcard->status |= MEMCARD_STATUS_BUSY; */
|
|
|
|
memset(&memcard->data[offset], MEMCARD_ERASEBYTE, Memcard_BlockSize);
|
|
|
|
MCSyncSave (memcard, offset, Memcard_BlockSize);
|
|
|
|
/* memcard->status &= ~MEMCARD_STATUS_BUSY; */
|
|
|
|
exi->csr |= EXI_CSR_EXIINT;
|
|
}
|
|
/**********************************MCSectorEraseProc*********************************************/
|
|
static void MCGetEXIDeviceIdProc (Memcard * memcard) {
|
|
EXIRegs * exi = memcard->exi;
|
|
|
|
int auxbytes = (EXI_CR_TLEN(exi->cr) + 1);
|
|
exi->data = (exi->data & ~Memcard_BytesMask[auxbytes]) |
|
|
((uint32_t)(memcard->size >> 17) & Memcard_BytesMask[auxbytes]);
|
|
}
|
|
/**********************************MCCardEraseProc*********************************************/
|
|
static void MCCardEraseProc (Memcard * memcard) {
|
|
uint32_t offset = 0;
|
|
|
|
/* memcard->status |= MEMCARD_STATUS_BUSY; */
|
|
|
|
memset(&memcard->data[offset], MEMCARD_ERASEBYTE, memcard->size);
|
|
|
|
MCSyncSave (memcard, offset, memcard->size);
|
|
|
|
/* memcard->status &= ~MEMCARD_STATUS_BUSY; */
|
|
}
|
|
/**********************************MCEnableInterruptsProc*********************************************/
|
|
static void MCEnableInterruptsProc (Memcard * memcard){
|
|
EXIRegs * exi = memcard->exi;
|
|
|
|
if ( memcard->commandData & (0x01 << 24) )
|
|
Report (Channel::MC, "Enable Interrupts\n");
|
|
else
|
|
Report (Channel::MC, "Disable Interrupts\n");
|
|
}
|
|
/**********************************MCReadIdProc*********************************************/
|
|
static void MCReadIdProc (Memcard * memcard){
|
|
EXIRegs * exi = memcard->exi;
|
|
|
|
int auxbytes = (EXI_CR_TLEN(exi->cr) + 1);
|
|
exi->data = (exi->data & ~Memcard_BytesMask[auxbytes]) |
|
|
(((uint32_t)memcard->ID << 16) & Memcard_BytesMask[auxbytes]);
|
|
}
|
|
/**********************************MCSleepProc*********************************************/
|
|
static void MCSleepProc (Memcard * memcard) {
|
|
memcard->status |= MEMCARD_STATUS_SLEEP;
|
|
}
|
|
/**********************************MCWakeUpProc*********************************************/
|
|
static void MCWakeUpProc (Memcard * memcard) {
|
|
memcard->status &= ~MEMCARD_STATUS_SLEEP;
|
|
}
|
|
|
|
/********************************************************************************************/
|
|
void MCTransfer () {
|
|
uint32_t auxdata, auxdma;
|
|
int auxbytes, i;
|
|
Memcard *auxmc;
|
|
EXIRegs *auxexi;
|
|
|
|
if ((exi.regs[MEMCARD_SLOTA].cr & EXI_CR_TSTART) &&
|
|
(exi.regs[MEMCARD_SLOTA].csr & EXI_CSR_CS0B))
|
|
auxmc = &memcard[MEMCARD_SLOTA];
|
|
else if ((exi.regs[MEMCARD_SLOTB].cr & EXI_CR_TSTART) &&
|
|
(exi.regs[MEMCARD_SLOTB].csr & EXI_CSR_CS0B))
|
|
auxmc = &memcard[MEMCARD_SLOTB];
|
|
else return;
|
|
|
|
if (auxmc->connected == false) return;
|
|
|
|
auxexi = auxmc->exi;
|
|
auxdma = auxexi->cr & EXI_CR_DMA;
|
|
|
|
switch (EXI_CR_RW(auxexi->cr)) {
|
|
case 0: // Read Transfer
|
|
if (auxmc->ready) {
|
|
if ( auxdma && (auxmc->executionFlags & MCDmaRead ) )
|
|
auxmc->procedure (auxmc);
|
|
else if ( !auxdma && (auxmc->executionFlags & MCImmRead ) )
|
|
auxmc->procedure (auxmc);
|
|
}
|
|
break;
|
|
case 1: // Write Transfer
|
|
auxdata = auxexi->data;
|
|
auxbytes = (EXI_CR_TLEN(auxexi->cr) + 1);
|
|
if (!auxdma) {
|
|
while (auxbytes > 0) {
|
|
if (auxmc->Command == MEMCARD_COMMAND_UNDEFINED || auxmc->ready ) {
|
|
auxmc->Command = (uint8_t)(auxdata >> 24);
|
|
for (i = 0; i < Num_Memcard_ValidCommands; i++)
|
|
if (auxmc->Command == Memcard_ValidCommands[i].Command) {
|
|
auxmc->databytes = Memcard_ValidCommands[i].databytes;
|
|
auxmc->dummybytes = Memcard_ValidCommands[i].dummybytes;
|
|
auxmc->executionFlags = Memcard_ValidCommands[i].executionFlags;
|
|
auxmc->procedure = Memcard_ValidCommands[i].procedure;
|
|
auxmc->databytesread = 0;
|
|
auxmc->dummybytesread = 0;
|
|
auxmc->commandData = 0x00000000;
|
|
auxmc->ready = false;
|
|
break;
|
|
}
|
|
|
|
if (i >= Num_Memcard_ValidCommands) {
|
|
Halt("MC :: Unrecognized Memcard Command %02x\n", auxmc->Command);
|
|
auxmc->Command = MEMCARD_COMMAND_UNDEFINED;
|
|
}
|
|
else {
|
|
Report (Channel::MC, "Recognized Memcard Command %02x\n", auxmc->Command);
|
|
}
|
|
}
|
|
else if (auxmc->databytesread < auxmc->databytes) {
|
|
auxmc->commandData |= ((auxdata & 0xFF000000) >> (auxmc->databytesread * 8));
|
|
auxmc->databytesread++;
|
|
}
|
|
else if (auxmc->dummybytesread < auxmc->dummybytes) {
|
|
auxmc->dummybytesread++;
|
|
}
|
|
else
|
|
Halt("MC :: Extra bytes at transfer , data : %02x\n", (uint8_t)(auxdata >> 24));
|
|
auxdata = auxdata << 8;
|
|
auxbytes--;
|
|
}
|
|
|
|
if (auxmc->Command != MEMCARD_COMMAND_UNDEFINED &&
|
|
auxmc->databytesread == auxmc->databytes &&
|
|
auxmc->dummybytesread == auxmc->dummybytes)
|
|
auxmc->ready = true;
|
|
}
|
|
|
|
if (auxmc->ready) {
|
|
if ( auxdma && (auxmc->executionFlags & MCDmaWrite ) )
|
|
auxmc->procedure (auxmc);
|
|
else if ( !auxdma && (auxmc->executionFlags & MCImmWrite ) )
|
|
auxmc->procedure (auxmc);
|
|
}
|
|
break;
|
|
default:
|
|
Halt ("MC: Unknown memcard transfer type");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Checks if the memcard is connected.
|
|
*/
|
|
bool MCIsConnected(int cardnum) {
|
|
// Invalid memcard number
|
|
assert((cardnum == MEMCARD_SLOTA) || (cardnum == MEMCARD_SLOTB));
|
|
return memcard[cardnum].connected;
|
|
}
|
|
|
|
/*
|
|
* Creates a new memcard file
|
|
* memcard_id should be one of the following:
|
|
* MEMCARD_ID_64 (0x0004)
|
|
* MEMCARD_ID_128 (0x0008)
|
|
* MEMCARD_ID_256 (0x0010)
|
|
* MEMCARD_ID_512 (0x0020)
|
|
* MEMCARD_ID_1024 (0x0040)
|
|
* MEMCARD_ID_2048 (0x0080)
|
|
*/
|
|
bool MCCreateMemcardFile(const TCHAR *path, uint16_t memcard_id) {
|
|
FILE * newfile;
|
|
uint32_t b, blocks;
|
|
uint8_t newfile_buffer[Memcard_BlockSize];
|
|
|
|
switch (memcard_id) {
|
|
case MEMCARD_ID_64:
|
|
case MEMCARD_ID_128:
|
|
case MEMCARD_ID_256:
|
|
case MEMCARD_ID_512:
|
|
case MEMCARD_ID_1024:
|
|
case MEMCARD_ID_2048:
|
|
/* 17 = Mbits to byte conversion */
|
|
blocks = ((uint32_t)memcard_id) << (17 - Memcard_BlockSize_log2);
|
|
break;
|
|
default:
|
|
Halt ("MC: Wrong card id for creating file.");
|
|
return false;
|
|
}
|
|
|
|
newfile = nullptr;
|
|
_tfopen_s(&newfile, path, _T("wb")) ;
|
|
|
|
if (newfile == NULL) {
|
|
Halt( "MC: Error while trying to create memcard file.");
|
|
return false;
|
|
}
|
|
|
|
memset(newfile_buffer, MEMCARD_ERASEBYTE, Memcard_BlockSize);
|
|
for (b = 0; b < blocks; b++) {
|
|
if (fwrite (newfile_buffer, Memcard_BlockSize, 1, newfile) != 1) {
|
|
Halt("MC: Error while trying to write memcard file.");
|
|
|
|
fclose (newfile);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
fclose (newfile);
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Sets the memcard to use the specified file. If the memcard is connected,
|
|
* it will be first disconnected (to ensure that changes are saved)
|
|
* if param connect is TRUE, then the memcard will be connected to the new file
|
|
*/
|
|
void MCUseFile(int cardnum, const TCHAR *path, bool connect) {
|
|
|
|
// Invalid memcard number
|
|
assert((cardnum == MEMCARD_SLOTA) || (cardnum == MEMCARD_SLOTB));
|
|
if (memcard[cardnum].connected == true) MCDisconnect(cardnum);
|
|
|
|
memset(memcard[cardnum].filename, 0, sizeof (memcard[cardnum].filename));
|
|
_tcscpy_s(memcard[cardnum].filename, _countof(memcard[cardnum].filename) - 1, path);
|
|
|
|
if (connect == true) MCConnect(cardnum);
|
|
}
|
|
|
|
/*
|
|
* Starts the memcard system and loads the saved settings.
|
|
* If no settings are found, default memcards are created.
|
|
* Then both memcards are connected (based on settings)
|
|
*/
|
|
void MCOpen (HWConfig * config)
|
|
{
|
|
Report (Channel::MC, "Memory cards\n");
|
|
|
|
MCOpened = true;
|
|
memset(memcard, 0 , 2 * sizeof (Memcard));
|
|
memcard[MEMCARD_SLOTA].Command = MEMCARD_COMMAND_UNDEFINED;
|
|
memcard[MEMCARD_SLOTB].Command = MEMCARD_COMMAND_UNDEFINED;
|
|
memcard[MEMCARD_SLOTA].ready = true;
|
|
memcard[MEMCARD_SLOTB].ready = true;
|
|
memcard[MEMCARD_SLOTA].exi = &exi.regs[MEMCARD_SLOTA];
|
|
memcard[MEMCARD_SLOTB].exi = &exi.regs[MEMCARD_SLOTB];
|
|
|
|
/* load settings */
|
|
Memcard_Connected[MEMCARD_SLOTA] = config->MemcardA_Connected;
|
|
Memcard_Connected[MEMCARD_SLOTB] = config->MemcardB_Connected;
|
|
_tcscpy_s(memcard[MEMCARD_SLOTA].filename, _countof(memcard[MEMCARD_SLOTA].filename) - 1, config->MemcardA_Filename);
|
|
_tcscpy_s(memcard[MEMCARD_SLOTB].filename, _countof(memcard[MEMCARD_SLOTB].filename) - 1, config->MemcardB_Filename);
|
|
SyncSave = config->Memcard_SyncSave;
|
|
|
|
MCConnect();
|
|
}
|
|
|
|
/*
|
|
* Disconnects both Memcard. Closes the memcard system and saves the current settings
|
|
*/
|
|
void MCClose () {
|
|
MCOpened = false;
|
|
MCDisconnect();
|
|
}
|
|
|
|
/*
|
|
* Connects the choosen memcard
|
|
*
|
|
* cardnum = -1 for both (based on the Memcard_Connected setting)
|
|
*/
|
|
bool MCConnect (int cardnum) {
|
|
bool ret = true;
|
|
int i;
|
|
switch (cardnum) {
|
|
case -1:
|
|
if (Memcard_Connected[MEMCARD_SLOTA] /*== TRUE*/) ret = MCConnect(MEMCARD_SLOTA);
|
|
if (Memcard_Connected[MEMCARD_SLOTB] /*== TRUE*/) ret = ret && MCConnect(MEMCARD_SLOTB);
|
|
return ret;
|
|
break;
|
|
case MEMCARD_SLOTA:
|
|
case MEMCARD_SLOTB:
|
|
if (memcard[cardnum].connected /*== TRUE*/) MCDisconnect(cardnum) ;
|
|
|
|
size_t memcardSize = Util::FileSize(memcard[cardnum].filename);
|
|
|
|
memcard[cardnum].file = nullptr;
|
|
_tfopen_s (&memcard[cardnum].file, memcard[cardnum].filename, _T("r+b"));
|
|
if (memcard[cardnum].file == nullptr) {
|
|
static char slt[2] = { 'A', 'B' };
|
|
|
|
// TODO: redirect user to memcard configure dialog ?
|
|
Report(
|
|
Channel::MC,
|
|
"Couldnt open memcard (slot %c),\n"
|
|
"location : %s\n\n"
|
|
"Check path or file attributes.",
|
|
slt[cardnum], Util::TcharToString(memcard[cardnum].filename).c_str()
|
|
);
|
|
return false;
|
|
}
|
|
|
|
for (i = 0 ; i < Num_Memcard_ValidSizes && Memcard_ValidSizes[i] != (uint32_t)memcardSize; i++);
|
|
|
|
if (i >= Num_Memcard_ValidSizes) {
|
|
// DBReport(YEL "memcard file doesnt have a valid size\n");
|
|
MessageBox (NULL, _T("memcard file doesnt have a valid size"), _T("Memcard Error"), 0);
|
|
fclose(memcard[cardnum].file);
|
|
memcard[cardnum].file = nullptr;
|
|
return false;
|
|
}
|
|
|
|
memcard[cardnum].size = (uint32_t)memcardSize;
|
|
memcard[cardnum].data = (uint8_t *)malloc(memcard[cardnum].size);
|
|
|
|
if (memcard[cardnum].data == nullptr) {
|
|
// DBReport(YEL "couldnt allocate enough memory for memcard\n");
|
|
MessageBox (nullptr, _T("couldnt allocate enough memory for memcard"), _T("Memcard Error"), 0);
|
|
fclose(memcard[cardnum].file);
|
|
memcard[cardnum].file = nullptr;
|
|
return false;
|
|
}
|
|
|
|
if (fseek(memcard[cardnum].file, 0, SEEK_SET) != 0) {
|
|
// DBReport(YEL "error at locating file cursor\n");
|
|
MessageBox (nullptr, _T("error at locating file cursor"), _T("Memcard Error"), 0);
|
|
free (memcard[cardnum].data);
|
|
memcard[cardnum].data = nullptr;
|
|
fclose(memcard[cardnum].file);
|
|
memcard[cardnum].file = nullptr;
|
|
return false;
|
|
}
|
|
|
|
if (fread(memcard[cardnum].data, memcard[cardnum].size, 1, memcard[cardnum].file) != 1) {
|
|
// DBReport(YEL "error at reading the memcard file\n");
|
|
MessageBox (nullptr, _T("error at reading the memcard file"), _T("Memcard Error"), 0);
|
|
free (memcard[cardnum].data);
|
|
memcard[cardnum].data = nullptr;
|
|
fclose(memcard[cardnum].file);
|
|
memcard[cardnum].file = nullptr;
|
|
return false;
|
|
}
|
|
|
|
/* if nothing fails... */
|
|
memcard[cardnum].ID = ((uint16_t)0xC2) << 8 | (uint16_t)0x42; // Datel's code just for now
|
|
memcard[cardnum].status = MEMCARD_STATUS_READY;
|
|
memcard[cardnum].connected = true;
|
|
EXIAttach(cardnum); // connect device
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Saves the data from the memcard to disk and disconnects the choosen memcard
|
|
*
|
|
* cardnum = -1 for both
|
|
*/
|
|
bool MCDisconnect (int cardnum) {
|
|
bool ret = true;
|
|
switch (cardnum) {
|
|
case -1:
|
|
ret = MCDisconnect(MEMCARD_SLOTA) && MCDisconnect(MEMCARD_SLOTB);
|
|
break;
|
|
case MEMCARD_SLOTA:
|
|
case MEMCARD_SLOTB:
|
|
if (!memcard[cardnum].connected) break ;
|
|
|
|
if (fseek(memcard[cardnum].file, 0, SEEK_SET) != 0)
|
|
{
|
|
ret = false;
|
|
}
|
|
else
|
|
{
|
|
/* write to the file */
|
|
if (fwrite(memcard[cardnum].data, memcard[cardnum].size, 1, memcard[cardnum].file) != 1)
|
|
{
|
|
ret = false;
|
|
}
|
|
|
|
}
|
|
/* close the file */
|
|
fclose(memcard[cardnum].file);
|
|
|
|
free(memcard[cardnum].data);
|
|
|
|
memcard[cardnum].ID = 0;
|
|
memcard[cardnum].size = 0;
|
|
memcard[cardnum].file = nullptr;
|
|
memcard[cardnum].data = nullptr;
|
|
memcard[cardnum].status = 0;
|
|
memcard[cardnum].connected = false;
|
|
EXIDetach(cardnum); // disconnect device
|
|
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|