282 lines
7.7 KiB
C++
282 lines
7.7 KiB
C++
#include "Flash.h"
|
|
#include "../Util.h"
|
|
#include "GBA.h"
|
|
#include "Globals.h"
|
|
#include "Sram.h"
|
|
#include <memory.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#define FLASH_READ_ARRAY 0
|
|
#define FLASH_CMD_1 1
|
|
#define FLASH_CMD_2 2
|
|
#define FLASH_AUTOSELECT 3
|
|
#define FLASH_CMD_3 4
|
|
#define FLASH_CMD_4 5
|
|
#define FLASH_CMD_5 6
|
|
#define FLASH_ERASE_COMPLETE 7
|
|
#define FLASH_PROGRAM 8
|
|
#define FLASH_SETBANK 9
|
|
|
|
uint8_t flashSaveMemory[SIZE_FLASH1M];
|
|
|
|
int flashState = FLASH_READ_ARRAY;
|
|
int flashReadState = FLASH_READ_ARRAY;
|
|
int flashSize = SIZE_FLASH512;
|
|
int flashDeviceID = 0x1b;
|
|
int flashManufacturerID = 0x32;
|
|
int flashBank = 0;
|
|
|
|
void flashInit()
|
|
{
|
|
memset(flashSaveMemory, 0xff, sizeof(flashSaveMemory));
|
|
}
|
|
|
|
void flashReset()
|
|
{
|
|
flashState = FLASH_READ_ARRAY;
|
|
flashReadState = FLASH_READ_ARRAY;
|
|
flashBank = 0;
|
|
}
|
|
|
|
void flashSetSize(int size)
|
|
{
|
|
// log("Setting flash size to %d\n", size);
|
|
if (size == SIZE_FLASH512) {
|
|
flashDeviceID = 0x1b;
|
|
flashManufacturerID = 0x32;
|
|
} else {
|
|
flashDeviceID = 0x13; //0x09;
|
|
flashManufacturerID = 0x62; //0xc2;
|
|
}
|
|
// Added to make 64k saves compatible with 128k ones
|
|
// (allow wrongfuly set 64k saves to work for Pokemon games)
|
|
if ((size == SIZE_FLASH1M) && (flashSize == SIZE_FLASH512))
|
|
memcpy((uint8_t*)(flashSaveMemory + SIZE_FLASH512), (uint8_t*)(flashSaveMemory), SIZE_FLASH512);
|
|
flashSize = size;
|
|
}
|
|
|
|
uint8_t flashRead(uint32_t address)
|
|
{
|
|
// log("Reading %08x from %08x\n", address, reg[15].I);
|
|
// log("Current read state is %d\n", flashReadState);
|
|
address &= 0xFFFF;
|
|
|
|
switch (flashReadState) {
|
|
case FLASH_READ_ARRAY:
|
|
return flashSaveMemory[(flashBank << 16) + address];
|
|
case FLASH_AUTOSELECT:
|
|
switch (address & 0xFF) {
|
|
case 0:
|
|
// manufacturer ID
|
|
return flashManufacturerID;
|
|
case 1:
|
|
// device ID
|
|
return flashDeviceID;
|
|
}
|
|
break;
|
|
case FLASH_ERASE_COMPLETE:
|
|
flashState = FLASH_READ_ARRAY;
|
|
flashReadState = FLASH_READ_ARRAY;
|
|
return 0xFF;
|
|
};
|
|
return 0;
|
|
}
|
|
|
|
void flashSaveDecide(uint32_t address, uint8_t byte)
|
|
{
|
|
if (saveType == GBA_SAVE_EEPROM)
|
|
return;
|
|
|
|
if (cpuSramEnabled && cpuFlashEnabled) {
|
|
if (address == 0x0e005555) {
|
|
saveType = GBA_SAVE_FLASH;
|
|
cpuSramEnabled = false;
|
|
cpuSaveGameFunc = flashWrite;
|
|
} else {
|
|
saveType = GBA_SAVE_SRAM;
|
|
cpuFlashEnabled = false;
|
|
cpuSaveGameFunc = sramWrite;
|
|
}
|
|
|
|
log("%s emulation is enabled by writing to: $%08x : %02x\n",
|
|
cpuSramEnabled ? "SRAM" : "FLASH", address, byte);
|
|
}
|
|
|
|
(*cpuSaveGameFunc)(address, byte);
|
|
}
|
|
|
|
void flashDelayedWrite(uint32_t address, uint8_t byte)
|
|
{
|
|
saveType = GBA_SAVE_FLASH;
|
|
cpuSaveGameFunc = flashWrite;
|
|
flashWrite(address, byte);
|
|
}
|
|
|
|
void flashWrite(uint32_t address, uint8_t byte)
|
|
{
|
|
// log("Writing %02x at %08x\n", byte, address);
|
|
// log("Current state is %d\n", flashState);
|
|
address &= 0xFFFF;
|
|
switch (flashState) {
|
|
case FLASH_READ_ARRAY:
|
|
if (address == 0x5555 && byte == 0xAA)
|
|
flashState = FLASH_CMD_1;
|
|
break;
|
|
case FLASH_CMD_1:
|
|
if (address == 0x2AAA && byte == 0x55)
|
|
flashState = FLASH_CMD_2;
|
|
else
|
|
flashState = FLASH_READ_ARRAY;
|
|
break;
|
|
case FLASH_CMD_2:
|
|
if (address == 0x5555) {
|
|
if (byte == 0x90) {
|
|
flashState = FLASH_AUTOSELECT;
|
|
flashReadState = FLASH_AUTOSELECT;
|
|
} else if (byte == 0x80) {
|
|
flashState = FLASH_CMD_3;
|
|
} else if (byte == 0xF0) {
|
|
flashState = FLASH_READ_ARRAY;
|
|
flashReadState = FLASH_READ_ARRAY;
|
|
} else if (byte == 0xA0) {
|
|
flashState = FLASH_PROGRAM;
|
|
} else if (byte == 0xB0 && flashSize == SIZE_FLASH1M) {
|
|
flashState = FLASH_SETBANK;
|
|
} else {
|
|
flashState = FLASH_READ_ARRAY;
|
|
flashReadState = FLASH_READ_ARRAY;
|
|
}
|
|
} else {
|
|
flashState = FLASH_READ_ARRAY;
|
|
flashReadState = FLASH_READ_ARRAY;
|
|
}
|
|
break;
|
|
case FLASH_CMD_3:
|
|
if (address == 0x5555 && byte == 0xAA) {
|
|
flashState = FLASH_CMD_4;
|
|
} else {
|
|
flashState = FLASH_READ_ARRAY;
|
|
flashReadState = FLASH_READ_ARRAY;
|
|
}
|
|
break;
|
|
case FLASH_CMD_4:
|
|
if (address == 0x2AAA && byte == 0x55) {
|
|
flashState = FLASH_CMD_5;
|
|
} else {
|
|
flashState = FLASH_READ_ARRAY;
|
|
flashReadState = FLASH_READ_ARRAY;
|
|
}
|
|
break;
|
|
case FLASH_CMD_5:
|
|
if (byte == 0x30) {
|
|
// SECTOR ERASE
|
|
memset(&flashSaveMemory[(flashBank << 16) + (address & 0xF000)],
|
|
0,
|
|
0x1000);
|
|
systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED;
|
|
flashReadState = FLASH_ERASE_COMPLETE;
|
|
} else if (byte == 0x10) {
|
|
// CHIP ERASE
|
|
memset(flashSaveMemory, 0, flashSize);
|
|
systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED;
|
|
flashReadState = FLASH_ERASE_COMPLETE;
|
|
} else {
|
|
flashState = FLASH_READ_ARRAY;
|
|
flashReadState = FLASH_READ_ARRAY;
|
|
}
|
|
break;
|
|
case FLASH_AUTOSELECT:
|
|
if (byte == 0xF0) {
|
|
flashState = FLASH_READ_ARRAY;
|
|
flashReadState = FLASH_READ_ARRAY;
|
|
} else if (address == 0x5555 && byte == 0xAA)
|
|
flashState = FLASH_CMD_1;
|
|
else {
|
|
flashState = FLASH_READ_ARRAY;
|
|
flashReadState = FLASH_READ_ARRAY;
|
|
}
|
|
break;
|
|
case FLASH_PROGRAM:
|
|
flashSaveMemory[(flashBank << 16) + address] = byte;
|
|
systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED;
|
|
flashState = FLASH_READ_ARRAY;
|
|
flashReadState = FLASH_READ_ARRAY;
|
|
break;
|
|
case FLASH_SETBANK:
|
|
if (address == 0) {
|
|
flashBank = (byte & 1);
|
|
}
|
|
flashState = FLASH_READ_ARRAY;
|
|
flashReadState = FLASH_READ_ARRAY;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static variable_desc flashSaveData3[] = {
|
|
{ &flashState, sizeof(int) },
|
|
{ &flashReadState, sizeof(int) },
|
|
{ &flashSize, sizeof(int) },
|
|
{ &flashBank, sizeof(int) },
|
|
{ &flashSaveMemory[0], SIZE_FLASH1M },
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
#ifdef __LIBRETRO__
|
|
void flashSaveGame(uint8_t*& data)
|
|
{
|
|
utilWriteDataMem(data, flashSaveData3);
|
|
}
|
|
|
|
void flashReadGame(const uint8_t*& data, int)
|
|
{
|
|
utilReadDataMem(data, flashSaveData3);
|
|
}
|
|
|
|
#else // !__LIBRETRO__
|
|
static variable_desc flashSaveData[] = {
|
|
{ &flashState, sizeof(int) },
|
|
{ &flashReadState, sizeof(int) },
|
|
{ &flashSaveMemory[0], SIZE_FLASH512 },
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
static variable_desc flashSaveData2[] = {
|
|
{ &flashState, sizeof(int) },
|
|
{ &flashReadState, sizeof(int) },
|
|
{ &flashSize, sizeof(int) },
|
|
{ &flashSaveMemory[0], SIZE_FLASH1M },
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
void flashSaveGame(gzFile gzFile)
|
|
{
|
|
utilWriteData(gzFile, flashSaveData3);
|
|
}
|
|
|
|
void flashReadGame(gzFile gzFile, int version)
|
|
{
|
|
if (version < SAVE_GAME_VERSION_5)
|
|
utilReadData(gzFile, flashSaveData);
|
|
else if (version < SAVE_GAME_VERSION_7) {
|
|
utilReadData(gzFile, flashSaveData2);
|
|
flashBank = 0;
|
|
flashSetSize(flashSize);
|
|
} else {
|
|
utilReadData(gzFile, flashSaveData3);
|
|
}
|
|
}
|
|
|
|
void flashReadGameSkip(gzFile gzFile, int version)
|
|
{
|
|
// skip the flash data in a save game
|
|
if (version < SAVE_GAME_VERSION_5)
|
|
utilReadDataSkip(gzFile, flashSaveData);
|
|
else if (version < SAVE_GAME_VERSION_7) {
|
|
utilReadDataSkip(gzFile, flashSaveData2);
|
|
} else {
|
|
utilReadDataSkip(gzFile, flashSaveData3);
|
|
}
|
|
}
|
|
#endif
|