Add storage device options menu, flash and RTC erasing

This commit is contained in:
spicyjpeg 2024-03-25 15:52:07 +01:00
parent 5e2a1e07fd
commit 94dae7c7ca
No known key found for this signature in database
GPG Key ID: 5CC87404C01DF393
9 changed files with 728 additions and 298 deletions

View File

@ -70,7 +70,23 @@
"dumpPCMCIA2": "Dumping PCMCIA card in slot 2...\nDo not turn off the 573 or unplug drives.",
"success": "All dumps have been saved to the %s directory in the root of the drive.",
"initError": "An error occurred while creating the dump directory. Turn off the system and make sure the drive is connected to the IDE bus properly, set as secondary and formatted with a single FAT16, FAT32 or exFAT partition.\n\nPath: %s\nPress the Test button to view debug logs.",
"dumpError": "An error occurred while saving one of the dumps. Ensure the drive has at least 32 MB of free space (256 MB if both PCMCIA cards are inserted) and the filesystem is not damaged.\n\nFile: %s\nPress the Test button to view debug logs."
"fileError": "An error occurred while saving one of the dumps. Ensure the drive has at least 32 MB of free space (256 MB if both PCMCIA cards are inserted) and the filesystem is not damaged.\n\nFile: %s\nPress the Test button to view debug logs."
},
"romEraseWorker": {
"erase": "Erasing device...\nDo not turn off the 573.",
"success": "The device has been successfully wiped.\n\nSectors erased: %d",
"flashError": "An error occurred while erasing sectors on one of the flash memory chips.\n\nError code:\t%s\nSectors erased:\t%d\nPress the Test button to view debug logs.",
"unsupported": "The flash memory chips on this device are not currently supported.\n\nManufacturer ID:\t%02X (lower), %02X (upper)\nDevice ID:\t\t%02X (lower), %02X (upper)\nPress the Test button to view debug logs."
},
"romRestoreWorker": {
"init": "Opening dump file...\nDo not turn off the 573 or unplug drives.",
"write": "Restoring data...\nDo not turn off the 573 or unplug drives.",
"success": "All data has been successfully restored.\n\nSectors erased:\t%d\nBytes written:\t%d",
"underflow": "The selected file was smaller than the target device's capacity, so only a partial restore was performed.\n\nSectors erased:\t%d\nBytes written:\t%d",
"overflow": "The selected file was larger than the target device's capacity, so all data past the limit was ignored. All other data has been successfully restored.\n\nSectors erased:\t%d\nBytes written:\t%d",
"fileError": "An error occurred while reading data from the file. Ensure the filesystem is not damaged.\n\nFile:\t\t%s\nSectors erased:\t%d\nBytes written:\t%d\nPress the Test button to view debug logs.",
"flashError": "An error occurred while erasing sectors on or writing data to one of the flash memory chips.\n\nError code:\t%s\nSectors erased:\t%d\nBytes written:\t%d\nPress the Test button to view debug logs.",
"unsupported": "The flash memory chips on this device are not currently supported.\n\nManufacturer ID:\t%02X (lower), %02X (upper)\nDevice ID:\t\t%02X (lower), %02X (upper)\nPress the Test button to view debug logs."
},
"systemInfoWorker": {
"hashBIOS": "Calculating BIOS ROM checksum...",
@ -250,18 +266,13 @@
"name": "Manage security cartridge",
"prompt": "Display information about, unlock, dump and manipulate the contents of the inserted security cartridge (if any)."
},
"dump": {
"name": "Dump flash, PCMCIA cards, RTC and BIOS ROM",
"prompt": "Dump the contents of the internal ROMs and PCMCIA cards to the IDE hard drive or CF card connected as secondary drive (if any).",
"confirm": "The contents of the internal flash memory, RTC RAM, BIOS ROM and any inserted PCMCIA flash cards will be dumped and the dumps saved to a new directory in the root of the hard drive or CF card currently configured as secondary on the IDE bus.\n\nDo you wish to proceed?"
},
"restore": {
"name": "Restore flash, PCMCIA cards or RTC from dump",
"prompt": "Restore the contents of the internal ROMs or any PCMCIA cards from the IDE hard drive or CF card connected as secondary drive (if any)."
"storageMenu": {
"name": "Manage flash, PCMCIA cards, RTC and BIOS ROM",
"prompt": "Dump, manipulate and erase the contents of the system's built-in storage devices or any inserted PCMCIA flash cards."
},
"systemInfo": {
"name": "View system information",
"prompt": "Display information about the system's I/O board, IDE drives, BIOS ROM, internal flash and PCMCIA flash cards."
"prompt": "Display information about the system's I/O board, IDE drives and storage devices."
},
"setResolution": {
"name": "Change screen resolution",
@ -289,6 +300,7 @@
"MessageScreen": {
"title": {
"success": "Success",
"warning": "Warning",
"error": "Error"
},
@ -322,6 +334,59 @@
"640x480i": "640x480 (4:3), interlaced"
},
"StorageMenuScreen": {
"title": "{CART_ICON} Storage device options",
"itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back",
"cardError": "The selected PCMCIA slot is empty.\n\nTurn off the system and insert a supported PCMCIA linear flash card in order to continue. DO NOT HOTPLUG CARDS; hotplugging may damage both the 573 and the card.",
"dump": {
"name": "Dump flash, PCMCIA cards, RTC and BIOS ROM",
"prompt": "Dump the contents of the internal ROMs and any inserted flash cards to the IDE hard drive or CF card connected as secondary drive (if any).",
"confirm": "The contents of the internal flash memory, RTC RAM, BIOS ROM and any inserted PCMCIA flash cards will be dumped and the dumps saved to a new directory in the root of the hard drive or CF card currently configured as secondary on the IDE bus.\n\nDo you wish to proceed?"
},
"restore": {
"filePrompt": "Note that the contents of the file will be written as-is, without adding or modifying any game-specific headers or checksums.",
"confirm": "The contents of the selected file will be written to the target device, permanently overwriting any other data currently stored on it. If the file is smaller than the device's capacity, any leftover space will be left as-is and not erased.\n\nDo you wish to proceed?",
"rtc": {
"name": "Restore RTC RAM from dump",
"prompt": "Erase RTC memory and restore its contents from a file on the IDE hard drive or CF card connected as secondary drive (if any)."
},
"flash": {
"name": "Restore internal flash from dump",
"prompt": "Erase the system's flash memory and restore its contents from a file on the IDE hard drive or CF card connected as secondary drive (if any)."
},
"pcmcia1": {
"name": "Restore PCMCIA card in slot 1 from dump",
"prompt": "Erase the flash card and restore its contents from a file on the IDE hard drive or CF card connected as secondary drive (if any)."
},
"pcmcia2": {
"name": "Restore PCMCIA card in slot 2 from dump",
"prompt": "Erase the flash card and restore its contents from a file on the IDE hard drive or CF card connected as secondary drive (if any)."
}
},
"erase": {
"confirm": "All data currently stored on the selected device will be permanently erased.\n\nDo you wish to proceed?",
"rtc": {
"name": "Erase RTC RAM",
"prompt": "Wipe all data stored in RTC memory, including any high scores and game settings. The date and time will not be reset."
},
"flash": {
"name": "Erase internal flash",
"prompt": "Wipe all data stored in the system's flash memory, including any currently installed game and its assets."
},
"pcmcia1": {
"name": "Erase PCMCIA card in slot 1",
"prompt": "Wipe all data stored in the PCMCIA flash card, including any currently installed game and its assets."
},
"pcmcia2": {
"name": "Erase PCMCIA card in slot 2",
"prompt": "Wipe all data stored in the PCMCIA flash card, including any currently installed game and its assets."
}
}
},
"SystemIDEntryScreen": {
"title": "Edit system identifier",
"body": "Enter the new digital I/O board's identifier. To obtain the ID of another board, run this tool on its respective system.\n\nUse {LEFT_BUTTON}{RIGHT_BUTTON} to move the cursor, hold {START_BUTTON} and use {LEFT_BUTTON}{RIGHT_BUTTON} to edit the highlighted digit.",
@ -378,12 +443,12 @@
},
"flash": {
"header": "Internal flash memory:\n",
"info": " JEDEC ID:\t\tmanufacturer %02X, device %02X\n CRC32:\t\t%08X\n",
"info": " Manufacturer ID:\t%02X (lower), %02X (upper)\n Device ID:\t\t%02X (lower), %02X (upper)\n CRC32:\t\t%08X\n",
"bootable": " Valid boot executable present\n"
},
"pcmcia": {
"header": "PCMCIA card in slot %d:\n",
"info": " JEDEC ID:\t\tmanufacturer %02X, device %02X\n CRC32 (16 MB):\t%08X\n CRC32 (32 MB):\t%08X\n CRC32 (64 MB):\t%08X\n",
"info": " Manufacturer ID:\t%02X (lower), %02X (upper)\n Device ID:\t\t%02X (lower), %02X (upper)\n CRC32 (16 MB):\t%08X\n CRC32 (32 MB):\t%08X\n CRC32 (64 MB):\t%08X\n",
"bootable": " Valid boot executable present\n",
"noCard": " No card inserted\n"
}

View File

@ -14,6 +14,17 @@ namespace rom {
// TODO: implement bounds checks in these
uint16_t *Region::getRawPtr(uint32_t offset, bool alignToChip) const {
if (alignToChip)
offset = 0;
auto dest = reinterpret_cast<uint16_t *>(ptr + offset);
util::assertAligned<uint16_t>(dest);
return dest;
}
void Region::read(void *data, uint32_t offset, size_t length) const {
auto source = reinterpret_cast<const uint32_t *>(ptr + offset);
auto dest = reinterpret_cast<uint32_t *>(data);
@ -60,7 +71,7 @@ void RTCRegion::read(void *data, uint32_t offset, size_t length) const {
uint32_t RTCRegion::zipCRC32(
uint32_t offset, size_t length, uint32_t crc
) const {
auto source = reinterpret_cast<const uint32_t *>(ptr + offset);
auto source = reinterpret_cast<const uint32_t *>(ptr + offset * 2);
auto table = reinterpret_cast<const uint32_t *>(CACHE_BASE);
crc = ~crc;
@ -77,6 +88,10 @@ uint32_t RTCRegion::zipCRC32(
return ~crc;
}
Driver *RTCRegion::newDriver(void) const {
return new RTCDriver(*this);
}
bool FlashRegion::isPresent(void) const {
if (!inputs)
return true;
@ -86,10 +101,25 @@ bool FlashRegion::isPresent(void) const {
return false;
}
void FlashRegion::read(void *data, uint32_t offset, size_t length) const {
uint16_t *FlashRegion::getRawPtr(uint32_t offset, bool alignToChip) const {
// The internal flash and PCMCIA cards can only be accessed 4 MB at a time.
// FIXME: this implementation will not handle reads that cross bank
// boundaries properly
int bankOffset = offset / FLASH_BANK_LENGTH;
int ptrOffset = offset % FLASH_BANK_LENGTH;
if (alignToChip)
ptrOffset = 0;
auto dest = reinterpret_cast<uint16_t *>(ptr + ptrOffset);
util::assertAligned<uint16_t>(dest);
io::setFlashBank(bank + bankOffset);
return dest;
}
void FlashRegion::read(void *data, uint32_t offset, size_t length) const {
// FIXME: this implementation will not handle unaligned reads and reads that
// cross bank boundaries properly
int bankOffset = offset / FLASH_BANK_LENGTH;
int ptrOffset = offset % FLASH_BANK_LENGTH;
@ -107,8 +137,8 @@ void FlashRegion::read(void *data, uint32_t offset, size_t length) const {
uint32_t FlashRegion::zipCRC32(
uint32_t offset, size_t length, uint32_t crc
) const {
// FIXME: this implementation will not handle reads that cross bank
// boundaries properly
// FIXME: this implementation will not handle unaligned reads and reads that
// cross bank boundaries properly
int bankOffset = offset / FLASH_BANK_LENGTH;
int ptrOffset = offset % FLASH_BANK_LENGTH;
@ -159,6 +189,15 @@ enum SharpCommand : uint16_t {
_SHARP_RESUME = 0xd0d0
};
enum FlashIdentifier : uint16_t {
// NOTE: the MBM29F017A datasheet incorrectly lists the device ID as 0xad in
// some places. The chip behaves pretty much identically to the MBM29F016A.
_ID_MBM29F016A = 0x04 | (0xad << 8),
_ID_MBM29F017A = 0x04 | (0x3d << 8),
_ID_FUJITSU_UNKNOWN = 0x04 | (0xa4 << 8),
_ID_LH28F016S = 0x89 | (0xaa << 8)
};
bool FlashRegion::hasBootExecutable(void) const {
// FIXME: this implementation will not detect executables that cross bank
// boundaries (but it shouldn't matter as executables must be <4 MB anyway)
@ -191,15 +230,49 @@ bool FlashRegion::hasBootExecutable(void) const {
}
uint32_t FlashRegion::getJEDECID(void) const {
auto ptr = getFlashPtr();
io::setFlashBank(bank);
ptr[0x000] = _SHARP_RESET;
ptr[0x000] = _SHARP_RESET;
ptr[0x555] = _JEDEC_HANDSHAKE1;
ptr[0x2aa] = _JEDEC_HANDSHAKE2;
ptr[0x555] = _JEDEC_GET_ID; // Same as _SHARP_GET_ID
auto _ptr = reinterpret_cast<volatile uint16_t *>(ptr);
return ptr[0] | (ptr[1] << 16);
_ptr[0x000] = _SHARP_RESET;
_ptr[0x000] = _SHARP_RESET;
_ptr[0x555] = _JEDEC_HANDSHAKE1;
_ptr[0x2aa] = _JEDEC_HANDSHAKE2;
_ptr[0x555] = _JEDEC_GET_ID; // Same as _SHARP_GET_ID
return _ptr[0] | (_ptr[1] << 16);
}
Driver *FlashRegion::newDriver(void) const {
if (!isPresent()) {
LOG("card not present");
return new Driver(*this);
}
uint32_t id = getJEDECID();
uint16_t low = ((id >> 0) & 0xff) | ((id >> 8) & 0xff00);
uint16_t high = ((id >> 8) & 0xff) | ((id >> 16) & 0xff00);
LOG("low=0x%04x, high=0x%04x", low, high);
if (low != high) {
// TODO: implement single-chip (16-bit) flash support
return new Driver(*this);
} else {
switch (low) {
case _ID_MBM29F016A:
case _ID_MBM29F017A:
return new MBM29F016ADriver(*this);
case _ID_FUJITSU_UNKNOWN:
return new FujitsuUnknownDriver(*this);
case _ID_LH28F016S:
return new LH28F016SDriver(*this);
default:
return new Driver(*this);
}
}
}
const BIOSRegion bios;
@ -210,12 +283,12 @@ const FlashRegion pcmcia[2]{
{ SYS573_BANK_PCMCIA2, 0x4000000, io::JAMMA_PCMCIA_CD2 }
};
/* Data common to all flash chip drivers */
/* Data common to all chip drivers */
static constexpr int _FLASH_WRITE_TIMEOUT = 10000;
static constexpr int _FLASH_ERASE_TIMEOUT = 10000000;
const char *const FLASH_DRIVER_ERROR_NAMES[]{
const char *const DRIVER_ERROR_NAMES[]{
"NO_ERROR",
"UNSUPPORTED_OP",
"CHIP_TIMEOUT",
@ -224,22 +297,59 @@ const char *const FLASH_DRIVER_ERROR_NAMES[]{
"WRITE_PROTECTED"
};
static const FlashChipSize _DUMMY_CHIP_SIZE{
static const ChipSize _DUMMY_CHIP_SIZE{
.chipLength = 0,
.eraseSectorLength = 0
};
// The onboard flash and all Konami-supplied flash cards use 2 MB chips with 64
// KB sectors.
static const FlashChipSize _STANDARD_CHIP_SIZE{
.chipLength = 0x200000,
// KB sectors and an 8-bit bus.
static const ChipSize _STANDARD_CHIP_SIZE{
.chipLength = 0x400000,
.eraseSectorLength = 0x10000
};
const FlashChipSize &FlashDriver::getChipSize(void) const {
const ChipSize &Driver::getChipSize(void) const {
return _DUMMY_CHIP_SIZE;
}
/* RTC RAM driver */
static const ChipSize _RTC_CHIP_SIZE{
.chipLength = 0x1ff8,
.eraseSectorLength = 0x1ff8
};
void RTCDriver::write(uint32_t offset, uint16_t value) {
auto ptr = reinterpret_cast<volatile uint32_t *>(_region.ptr + offset * 2);
*ptr = (value & 0x00ff) | ((value & 0xff00) << 8);
}
void RTCDriver::eraseSector(uint32_t offset) {
auto ptr = reinterpret_cast<void *>(_region.ptr);
__builtin_memset(ptr, 0, _region.regionLength * 2);
}
void RTCDriver::eraseChip(uint32_t offset) {
eraseSector(offset);
}
DriverError RTCDriver::flushWrite(uint32_t offset, uint16_t value) {
auto ptr = reinterpret_cast<volatile uint32_t *>(_region.ptr + offset * 2);
value = (value & 0x00ff) | ((value & 0xff00) << 8);
return (ptr[offset] == value) ? NO_ERROR : VERIFY_MISMATCH;
}
DriverError RTCDriver::flushErase(uint32_t offset) {
return flushWrite(offset, 0);
}
const ChipSize &RTCDriver::getChipSize(void) const {
return _RTC_CHIP_SIZE;
}
/* Fujitsu MBM29F016A driver */
enum FujitsuStatusFlag : uint16_t {
@ -250,9 +360,11 @@ enum FujitsuStatusFlag : uint16_t {
_FUJITSU_STATUS_POLL_BIT = 0x101 << 7
};
FlashDriverError MBM29F016AFlashDriver::_flush(
volatile uint16_t *ptr, uint16_t value, int timeout
DriverError MBM29F016ADriver::_flush(
uint32_t offset, uint16_t value, int timeout
) {
volatile uint16_t *ptr = _region.getRawPtr(offset);
for (; timeout > 0; timeout--) {
auto status = *ptr;
@ -260,23 +372,27 @@ FlashDriverError MBM29F016AFlashDriver::_flush(
return NO_ERROR;
if (!((status ^ value) & _FUJITSU_STATUS_POLL_BIT)) {
LOG("mismatch, exp=0x%04x, got=0x%04x", value, status);
LOG(
"mismatch @ 0x%08x, exp=0x%04x, got=0x%04x", offset, value,
status
);
return VERIFY_MISMATCH;
}
if (status & _FUJITSU_STATUS_ERROR) {
LOG("MBM29F016A error, stat=0x%04x", status);
LOG("error @ 0x%08x, stat=0x%04x", offset, status);
return CHIP_ERROR;
}
delayMicroseconds(1);
}
LOG("MBM29F016A timeout, stat=0x%04x", *ptr);
LOG("timeout @ 0x%08x, stat=0x%04x", offset, *ptr);
return CHIP_TIMEOUT;
}
void MBM29F016AFlashDriver::write(uint32_t offset, uint16_t value) {
auto ptr = _region.getFlashPtr();
void MBM29F016ADriver::write(uint32_t offset, uint16_t value) {
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
offset = (offset % FLASH_BANK_LENGTH) / 2;
ptr[0x000] = _JEDEC_RESET;
ptr[0x555] = _JEDEC_HANDSHAKE1;
@ -285,8 +401,9 @@ void MBM29F016AFlashDriver::write(uint32_t offset, uint16_t value) {
ptr[offset] = value;
}
void MBM29F016AFlashDriver::eraseSector(uint32_t offset) {
auto ptr = _region.getFlashPtr();
void MBM29F016ADriver::eraseSector(uint32_t offset) {
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
offset = (offset % FLASH_BANK_LENGTH) / 2;
ptr[0x000] = _JEDEC_RESET;
ptr[0x555] = _JEDEC_HANDSHAKE1;
@ -297,8 +414,8 @@ void MBM29F016AFlashDriver::eraseSector(uint32_t offset) {
ptr[offset] = _JEDEC_ERASE_SECTOR;
}
void MBM29F016AFlashDriver::eraseChip(uint32_t offset) {
auto ptr = _region.getFlashPtr();
void MBM29F016ADriver::eraseChip(uint32_t offset) {
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
ptr[0x000] = _JEDEC_RESET;
ptr[0x555] = _JEDEC_HANDSHAKE1;
@ -309,21 +426,17 @@ void MBM29F016AFlashDriver::eraseChip(uint32_t offset) {
ptr[0x555] = _JEDEC_ERASE_CHIP;
}
FlashDriverError MBM29F016AFlashDriver::flushWrite(
DriverError MBM29F016ADriver::flushWrite(
uint32_t offset, uint16_t value
) {
auto ptr = &(_region.getFlashPtr())[offset];
return _flush(ptr, value, _FLASH_WRITE_TIMEOUT);
return _flush(offset, value, _FLASH_WRITE_TIMEOUT);
}
FlashDriverError MBM29F016AFlashDriver::flushErase(uint32_t offset) {
auto ptr = &(_region.getFlashPtr())[offset];
return _flush(ptr, 0xffff, _FLASH_ERASE_TIMEOUT);
DriverError MBM29F016ADriver::flushErase(uint32_t offset) {
return _flush(offset, 0xffff, _FLASH_ERASE_TIMEOUT);
}
const FlashChipSize &MBM29F016AFlashDriver::getChipSize(void) const {
const ChipSize &MBM29F016ADriver::getChipSize(void) const {
return _STANDARD_CHIP_SIZE;
}
@ -333,8 +446,9 @@ const FlashChipSize &MBM29F016AFlashDriver::getChipSize(void) const {
// but using 0x5555/0x2aaa as command addresses instead of 0x555/0x2aa. This
// could actually be a >2 MB chip.
void FujitsuUnknownFlashDriver::write(uint32_t offset, uint16_t value) {
auto ptr = _region.getFlashPtr();
void FujitsuUnknownDriver::write(uint32_t offset, uint16_t value) {
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
offset = (offset % FLASH_BANK_LENGTH) / 2;
ptr[0x0000] = _JEDEC_RESET;
ptr[0x5555] = _JEDEC_HANDSHAKE1;
@ -343,8 +457,9 @@ void FujitsuUnknownFlashDriver::write(uint32_t offset, uint16_t value) {
ptr[offset] = value;
}
void FujitsuUnknownFlashDriver::eraseSector(uint32_t offset) {
auto ptr = _region.getFlashPtr();
void FujitsuUnknownDriver::eraseSector(uint32_t offset) {
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
offset = (offset % FLASH_BANK_LENGTH) / 2;
ptr[0x0000] = _JEDEC_RESET;
ptr[0x5555] = _JEDEC_HANDSHAKE1;
@ -355,8 +470,8 @@ void FujitsuUnknownFlashDriver::eraseSector(uint32_t offset) {
ptr[offset] = _JEDEC_ERASE_SECTOR;
}
void FujitsuUnknownFlashDriver::eraseChip(uint32_t offset) {
auto ptr = _region.getFlashPtr();
void FujitsuUnknownDriver::eraseChip(uint32_t offset) {
volatile uint16_t *ptr = _region.getRawPtr(offset, true);
ptr[0x0005] = _JEDEC_RESET;
ptr[0x5555] = _JEDEC_HANDSHAKE1;
@ -379,9 +494,9 @@ enum SharpStatusFlag : uint16_t {
_SHARP_STATUS_WSMS = 0x101 << 7
};
FlashDriverError LH28F016SFlashDriver::_flush(
volatile uint16_t *ptr, int timeout
) {
DriverError LH28F016SDriver::_flush(uint32_t offset, int timeout) {
volatile uint16_t *ptr = _region.getRawPtr(offset);
// Not required as all write/erase commands already put the chip into status
// reading mode.
//*ptr = _SHARP_GET_STATUS;
@ -392,13 +507,13 @@ FlashDriverError LH28F016SFlashDriver::_flush(
if (status & (_SHARP_STATUS_DPS | _SHARP_STATUS_VPPS)) {
*ptr = _SHARP_CLEAR_STATUS;
LOG("LH28F016S locked, stat=0x%04x", status);
LOG("locked @ 0x%08x, stat=0x%04x", offset, status);
return WRITE_PROTECTED;
}
if (status & (_SHARP_STATUS_BWSLBS | _SHARP_STATUS_ECLBS)) {
*ptr = _SHARP_CLEAR_STATUS;
LOG("LH28F016S error, stat=0x%04x", status);
LOG("error @ 0x%08x, stat=0x%04x", offset, status);
return CHIP_ERROR;
}
@ -408,12 +523,12 @@ FlashDriverError LH28F016SFlashDriver::_flush(
delayMicroseconds(1);
}
LOG("LH28F016S timeout, stat=0x%04x", *ptr);
LOG("timeout @ 0x%08x, stat=0x%04x", offset, *ptr);
return CHIP_TIMEOUT;
}
void LH28F016SFlashDriver::write(uint32_t offset, uint16_t value) {
auto ptr = &(_region.getFlashPtr())[offset];
void LH28F016SDriver::write(uint32_t offset, uint16_t value) {
volatile uint16_t *ptr = _region.getRawPtr(offset);
*ptr = _SHARP_RESET;
*ptr = _SHARP_CLEAR_STATUS;
@ -421,75 +536,28 @@ void LH28F016SFlashDriver::write(uint32_t offset, uint16_t value) {
*ptr = value;
}
void LH28F016SFlashDriver::eraseSector(uint32_t offset) {
auto ptr = &(_region.getFlashPtr())[offset];
void LH28F016SDriver::eraseSector(uint32_t offset) {
volatile uint16_t *ptr = _region.getRawPtr(offset);
*ptr = _SHARP_RESET;
*ptr = _SHARP_ERASE_SECTOR1;
*ptr = _SHARP_ERASE_SECTOR2;
}
FlashDriverError LH28F016SFlashDriver::flushWrite(
DriverError LH28F016SDriver::flushWrite(
uint32_t offset, uint16_t value
) {
auto ptr = &(_region.getFlashPtr())[offset];
return _flush(ptr, _FLASH_WRITE_TIMEOUT);
return _flush(offset, _FLASH_WRITE_TIMEOUT);
}
FlashDriverError LH28F016SFlashDriver::flushErase(uint32_t offset) {
auto ptr = &(_region.getFlashPtr())[offset];
return _flush(ptr, _FLASH_ERASE_TIMEOUT);
DriverError LH28F016SDriver::flushErase(uint32_t offset) {
return _flush(offset, _FLASH_ERASE_TIMEOUT);
}
const FlashChipSize &LH28F016SFlashDriver::getChipSize(void) const {
const ChipSize &LH28F016SDriver::getChipSize(void) const {
return _STANDARD_CHIP_SIZE;
}
/* Flash chip identification */
enum FlashIdentifier : uint16_t {
// NOTE: the MBM29F017A datasheet incorrectly lists the device ID as 0xad in
// some places. The chip behaves pretty much identically to the MBM29F016A.
_ID_MBM29F016A = 0x04 | (0xad << 8),
_ID_MBM29F017A = 0x04 | (0x3d << 8),
_ID_FUJITSU_UNKNOWN = 0x04 | (0xa4 << 8),
_ID_LH28F016S = 0x89 | (0xaa << 8)
};
FlashDriver *newFlashDriver(FlashRegion &region) {
if (!region.isPresent()) {
LOG("card not present");
return new FlashDriver(region);
}
uint32_t id = region.getJEDECID();
uint16_t low = ((id >> 0) & 0xff) | ((id >> 8) & 0xff00);
uint16_t high = ((id >> 8) & 0xff) | ((id >> 16) & 0xff00);
LOG("low=0x%04x, high=0x%04x", low, high);
if (low != high) {
// TODO: implement single-chip (16-bit) flash support
return new FlashDriver(region);
} else {
switch (low) {
case _ID_MBM29F016A:
case _ID_MBM29F017A:
return new MBM29F016AFlashDriver(region);
case _ID_FUJITSU_UNKNOWN:
return new FujitsuUnknownFlashDriver(region);
case _ID_LH28F016S:
return new LH28F016SFlashDriver(region);
default:
return new FlashDriver(region);
}
}
}
/* BIOS ROM headers */
static const ShellInfo _SHELL_VERSIONS[]{

View File

@ -15,6 +15,8 @@ static constexpr size_t FLASH_BANK_LENGTH = 0x400000;
static constexpr uint32_t FLASH_CRC_OFFSET = 0x20;
static constexpr uint32_t FLASH_EXE_OFFSET = 0x24;
class Driver;
class Region {
public:
uintptr_t ptr;
@ -24,10 +26,15 @@ public:
: ptr(ptr), regionLength(regionLength) {}
virtual bool isPresent(void) const { return true; }
virtual uint16_t *getRawPtr(uint32_t offset, bool alignToChip = false) const;
virtual void read(void *data, uint32_t offset, size_t length) const;
virtual uint32_t zipCRC32(
uint32_t offset, size_t length, uint32_t crc = 0
) const;
virtual bool hasBootExecutable(void) const { return false; }
virtual uint32_t getJEDECID(void) const { return 0; }
virtual Driver *newDriver(void) const { return nullptr; }
};
class BIOSRegion : public Region {
@ -43,6 +50,8 @@ public:
void read(void *data, uint32_t offset, size_t length) const;
uint32_t zipCRC32(uint32_t offset, size_t length, uint32_t crc = 0) const;
Driver *newDriver(void) const;
};
class FlashRegion : public Region {
@ -53,27 +62,23 @@ public:
inline FlashRegion(int bank, size_t regionLength, uint32_t inputs = 0)
: Region(DEV0_BASE, regionLength), bank(bank), inputs(inputs) {}
inline volatile uint16_t *getFlashPtr(void) const {
io::setFlashBank(bank);
return reinterpret_cast<volatile uint16_t *>(ptr);
}
bool isPresent(void) const;
uint16_t *getRawPtr(uint32_t offset, bool alignToChip = false) const;
void read(void *data, uint32_t offset, size_t length) const;
uint32_t zipCRC32(uint32_t offset, size_t length, uint32_t crc = 0) const;
bool hasBootExecutable(void) const;
uint32_t getJEDECID(void) const;
Driver *newDriver(void) const;
};
extern const BIOSRegion bios;
extern const RTCRegion rtc;
extern const FlashRegion flash, pcmcia[2];
/* Flash chip drivers */
/* Chip drivers */
enum FlashDriverError {
enum DriverError {
NO_ERROR = 0,
UNSUPPORTED_OP = 1,
CHIP_TIMEOUT = 2,
@ -82,84 +87,94 @@ enum FlashDriverError {
WRITE_PROTECTED = 5
};
struct FlashChipSize {
struct ChipSize {
public:
size_t chipLength, eraseSectorLength;
};
class FlashDriver {
class Driver {
protected:
FlashRegion &_region;
const Region &_region;
public:
inline FlashDriver(FlashRegion &region)
inline Driver(const Region &region)
: _region(region) {}
virtual ~FlashDriver(void) {}
// Note that all offsets must be multiples of 2, as writes are done in
// halfwords.
virtual ~Driver(void) {}
virtual void write(uint32_t offset, uint16_t value) {}
virtual void eraseSector(uint32_t offset) {}
virtual void eraseChip(uint32_t offset) {}
virtual FlashDriverError flushWrite(uint32_t offset, uint16_t value) {
virtual DriverError flushWrite(uint32_t offset, uint16_t value) {
return UNSUPPORTED_OP;
}
virtual FlashDriverError flushErase(uint32_t offset) {
virtual DriverError flushErase(uint32_t offset) {
return UNSUPPORTED_OP;
}
virtual const FlashChipSize &getChipSize(void) const;
virtual const ChipSize &getChipSize(void) const;
};
class MBM29F016AFlashDriver : public FlashDriver {
class RTCDriver : public Driver {
public:
inline RTCDriver(const RTCRegion &region)
: Driver(region) {}
void write(uint32_t offset, uint16_t value);
void eraseSector(uint32_t offset);
void eraseChip(uint32_t offset);
DriverError flushWrite(uint32_t offset, uint16_t value);
DriverError flushErase(uint32_t offset);
const ChipSize &getChipSize(void) const;
};
class MBM29F016ADriver : public Driver {
private:
FlashDriverError _flush(
volatile uint16_t *ptr, uint16_t value, int timeout
);
DriverError _flush(uint32_t offset, uint16_t value, int timeout);
public:
inline MBM29F016AFlashDriver(FlashRegion &region)
: FlashDriver(region) {}
inline MBM29F016ADriver(const FlashRegion &region)
: Driver(region) {}
void write(uint32_t offset, uint16_t value);
void eraseSector(uint32_t offset);
void eraseChip(uint32_t offset);
FlashDriverError flushWrite(uint32_t offset, uint16_t value);
FlashDriverError flushErase(uint32_t offset);
const FlashChipSize &getChipSize(void) const;
virtual void write(uint32_t offset, uint16_t value);
virtual void eraseSector(uint32_t offset);
virtual void eraseChip(uint32_t offset);
DriverError flushWrite(uint32_t offset, uint16_t value);
DriverError flushErase(uint32_t offset);
const ChipSize &getChipSize(void) const;
};
class FujitsuUnknownFlashDriver : public MBM29F016AFlashDriver {
class FujitsuUnknownDriver : public MBM29F016ADriver {
public:
inline FujitsuUnknownFlashDriver(FlashRegion &region)
: MBM29F016AFlashDriver(region) {}
inline FujitsuUnknownDriver(const FlashRegion &region)
: MBM29F016ADriver(region) {}
void write(uint32_t offset, uint16_t value);
void eraseSector(uint32_t offset);
void eraseChip(uint32_t offset);
};
class LH28F016SFlashDriver : public FlashDriver {
class LH28F016SDriver : public Driver {
private:
FlashDriverError _flush(volatile uint16_t *ptr, int timeout);
DriverError _flush(uint32_t offset, int timeout);
public:
inline LH28F016SFlashDriver(FlashRegion &region)
: FlashDriver(region) {}
inline LH28F016SDriver(const FlashRegion &region)
: Driver(region) {}
void write(uint32_t offset, uint16_t value);
void eraseSector(uint32_t offset);
void eraseChip(uint32_t offset);
FlashDriverError flushWrite(uint32_t offset, uint16_t value);
FlashDriverError flushErase(uint32_t offset);
const FlashChipSize &getChipSize(void) const;
DriverError flushWrite(uint32_t offset, uint16_t value);
DriverError flushErase(uint32_t offset);
const ChipSize &getChipSize(void) const;
};
extern const char *const FLASH_DRIVER_ERROR_NAMES[];
extern const char *const DRIVER_ERROR_NAMES[];
static inline const char *getErrorString(FlashDriverError error) {
return FLASH_DRIVER_ERROR_NAMES[error];
static inline const char *getErrorString(DriverError error) {
return DRIVER_ERROR_NAMES[error];
}
FlashDriver *newFlashDriver(FlashRegion &region);
/* BIOS ROM headers */
struct [[gnu::packed]] SonyKernelHeader {

View File

@ -50,19 +50,10 @@ public:
/* System information buffer */
enum FlashRegionInfoFlag : uint16_t {
FLASH_REGION_INFO_PRESENT = 1 << 0,
FLASH_REGION_INFO_BOOTABLE = 1 << 1
};
class FlashRegionInfo {
struct FlashRegionInfo {
public:
uint32_t flags;
uint32_t jedecID, crc[4];
inline void clearFlags(void) {
flags = 0;
}
bool bootable;
};
enum SystemInfoFlag : uint32_t {
@ -78,16 +69,8 @@ public:
const rom::ShellInfo *shell;
FlashRegionInfo flash, pcmcia[2];
inline SystemInfo(void) {
clearFlags();
}
inline void clearFlags(void) {
flags = 0;
flash.flags = 0;
pcmcia[0].clearFlags();
pcmcia[1].clearFlags();
}
inline SystemInfo(void)
: flags(0) {}
};
/* App class */
@ -102,6 +85,7 @@ class App {
friend class WarningScreen;
friend class ButtonMappingScreen;
friend class MainMenuScreen;
friend class StorageMenuScreen;
friend class SystemInfoScreen;
friend class ResolutionScreen;
friend class AboutScreen;
@ -122,6 +106,7 @@ private:
WarningScreen _warningScreen;
ButtonMappingScreen _buttonMappingScreen;
MainMenuScreen _mainMenuScreen;
StorageMenuScreen _storageMenuScreen;
SystemInfoScreen _systemInfoScreen;
ResolutionScreen _resolutionScreen;
AboutScreen _aboutScreen;
@ -172,8 +157,11 @@ private:
bool _cartReflashWorker(void);
bool _cartEraseWorker(void);
bool _startupWorker(void);
bool _romDumpWorker(void);
bool _romRestoreWorker(void);
bool _romEraseWorker(void);
bool _startupWorker(void);
bool _systemInfoWorker(void);
bool _executableWorker(void);
bool _atapiEjectWorker(void);

View File

@ -87,22 +87,14 @@ public:
static const MenuEntry _MENU_ENTRIES[]{
{
#ifdef ENABLE_CART_MENU
.name = "MainMenuScreen.cartInfo.name"_h,
.prompt = "MainMenuScreen.cartInfo.prompt"_h,
.target = &MainMenuScreen::cartInfo
}, {
#endif
.name = "MainMenuScreen.dump.name"_h,
.prompt = "MainMenuScreen.dump.prompt"_h,
.target = &MainMenuScreen::dump
.name = "MainMenuScreen.storageMenu.name"_h,
.prompt = "MainMenuScreen.storageMenu.prompt"_h,
.target = &MainMenuScreen::storageMenu
}, {
#if 0
.name = "MainMenuScreen.restore.name"_h,
.prompt = "MainMenuScreen.restore.prompt"_h,
.target = &MainMenuScreen::restore
}, {
#endif
.name = "MainMenuScreen.systemInfo.name"_h,
.prompt = "MainMenuScreen.systemInfo.prompt"_h,
.target = &MainMenuScreen::systemInfo
@ -142,21 +134,8 @@ void MainMenuScreen::cartInfo(ui::Context &ctx) {
}
}
void MainMenuScreen::dump(ui::Context &ctx) {
APP->_confirmScreen.setMessage(
*this,
[](ui::Context &ctx) {
APP->_setupWorker(&App::_romDumpWorker);
ctx.show(APP->_workerStatusScreen, false, true);
},
STR("MainMenuScreen.dump.confirm")
);
ctx.show(APP->_confirmScreen, false, true);
}
void MainMenuScreen::restore(ui::Context &ctx) {
//ctx.show(APP->_restoreMenuScreen, false, true);
void MainMenuScreen::storageMenu(ui::Context &ctx) {
ctx.show(APP->_storageMenuScreen, false, true);
}
void MainMenuScreen::systemInfo(ui::Context &ctx) {

View File

@ -31,8 +31,7 @@ protected:
public:
void cartInfo(ui::Context &ctx);
void dump(ui::Context &ctx);
void restore(ui::Context &ctx);
void storageMenu(ui::Context &ctx);
void systemInfo(ui::Context &ctx);
void setResolution(ui::Context &ctx);
void about(ui::Context &ctx);

View File

@ -1,6 +1,5 @@
#include <stdint.h>
#include "common/defs.hpp"
#include "common/file.hpp"
#include "common/ide.hpp"
#include "common/rom.hpp"
@ -11,6 +10,148 @@
#include "main/uibase.hpp"
#include "ps1/gpucmd.h"
/* Storage device submenu */
struct StorageAction {
public:
util::Hash name, prompt;
const rom::Region &region;
void (StorageMenuScreen::*target)(ui::Context &ctx);
};
static const StorageAction _STORAGE_ACTIONS[]{
{
.name = "StorageMenuScreen.dump.name"_h,
.prompt = "StorageMenuScreen.dump.prompt"_h,
.region = rom::bios, // Dummy
.target = &StorageMenuScreen::dump
}, {
.name = "StorageMenuScreen.restore.rtc.name"_h,
.prompt = "StorageMenuScreen.restore.rtc.prompt"_h,
.region = rom::rtc,
.target = &StorageMenuScreen::restore
}, {
.name = "StorageMenuScreen.restore.flash.name"_h,
.prompt = "StorageMenuScreen.restore.flash.prompt"_h,
.region = rom::flash,
.target = &StorageMenuScreen::restore
}, {
.name = "StorageMenuScreen.restore.pcmcia1.name"_h,
.prompt = "StorageMenuScreen.restore.pcmcia1.prompt"_h,
.region = rom::pcmcia[0],
.target = &StorageMenuScreen::restore
}, {
.name = "StorageMenuScreen.restore.pcmcia2.name"_h,
.prompt = "StorageMenuScreen.restore.pcmcia2.prompt"_h,
.region = rom::pcmcia[1],
.target = &StorageMenuScreen::restore
}, {
.name = "StorageMenuScreen.erase.rtc.name"_h,
.prompt = "StorageMenuScreen.erase.rtc.prompt"_h,
.region = rom::rtc,
.target = &StorageMenuScreen::erase
}, {
.name = "StorageMenuScreen.erase.flash.name"_h,
.prompt = "StorageMenuScreen.erase.flash.prompt"_h,
.region = rom::flash,
.target = &StorageMenuScreen::erase
}, {
.name = "StorageMenuScreen.erase.pcmcia1.name"_h,
.prompt = "StorageMenuScreen.erase.pcmcia1.prompt"_h,
.region = rom::pcmcia[0],
.target = &StorageMenuScreen::erase
}, {
.name = "StorageMenuScreen.erase.pcmcia2.name"_h,
.prompt = "StorageMenuScreen.erase.pcmcia2.prompt"_h,
.region = rom::pcmcia[1],
.target = &StorageMenuScreen::erase
}
};
const char *StorageMenuScreen::_getItemName(ui::Context &ctx, int index) const {
return STRH(_STORAGE_ACTIONS[index].name);
}
void StorageMenuScreen::dump(ui::Context &ctx) {
APP->_confirmScreen.setMessage(
*this,
[](ui::Context &ctx) {
APP->_setupWorker(&App::_romDumpWorker);
ctx.show(APP->_workerStatusScreen, false, true);
},
STR("StorageMenuScreen.dump.confirm")
);
ctx.show(APP->_confirmScreen, false, true);
}
void StorageMenuScreen::restore(ui::Context &ctx) {
APP->_filePickerScreen.setMessage(
*this,
[](ui::Context &ctx) {
ctx.show(APP->_confirmScreen, false, true);
},
STR("StorageMenuScreen.restore.filePrompt")
);
APP->_confirmScreen.setMessage(
APP->_filePickerScreen,
[](ui::Context &ctx) {
APP->_setupWorker(&App::_romRestoreWorker);
ctx.show(APP->_workerStatusScreen, false, true);
},
STR("StorageMenuScreen.restore.confirm")
);
APP->_filePickerScreen.loadRootAndShow(ctx);
}
void StorageMenuScreen::erase(ui::Context &ctx) {
APP->_confirmScreen.setMessage(
*this,
[](ui::Context &ctx) {
APP->_setupWorker(&App::_romEraseWorker);
ctx.show(APP->_workerStatusScreen, false, true);
},
STR("StorageMenuScreen.erase.confirm")
);
ctx.show(APP->_confirmScreen, false, true);
}
void StorageMenuScreen::show(ui::Context &ctx, bool goBack) {
_title = STR("StorageMenuScreen.title");
_prompt = STRH(_STORAGE_ACTIONS[0].prompt);
_itemPrompt = STR("StorageMenuScreen.itemPrompt");
_listLength = util::countOf(_STORAGE_ACTIONS);
ListScreen::show(ctx, goBack);
}
void StorageMenuScreen::update(ui::Context &ctx) {
auto &action = _STORAGE_ACTIONS[_activeItem];
_prompt = STRH(action.prompt);
ListScreen::update(ctx);
if (ctx.buttons.pressed(ui::BTN_START)) {
if (ctx.buttons.held(ui::BTN_LEFT) || ctx.buttons.held(ui::BTN_RIGHT)) {
ctx.show(APP->_mainMenuScreen, true, true);
} else {
if (action.region.isPresent()) {
this->_selectedRegion = &(action.region);
(this->*action.target)(ctx);
} else {
APP->_messageScreen.setMessage(
MESSAGE_ERROR, *this, STR("StorageMenuScreen.cardError")
);
ctx.show(APP->_messageScreen, false, true);
}
}
}
}
/* System information screen */
static const util::Hash _SYSTEM_INFO_IDE_HEADERS[]{
@ -155,11 +296,15 @@ void SystemInfoScreen::show(ui::Context &ctx, bool goBack) {
// Flash
_PRINT(STR("SystemInfoScreen.flash.header"));
_PRINT(
STR("SystemInfoScreen.flash.info"), info.flash.jedecID & 0xff,
(info.flash.jedecID >> 16) & 0xff, info.flash.crc[0]
STR("SystemInfoScreen.flash.info"),
(info.flash.jedecID >> 0) & 0xff,
(info.flash.jedecID >> 8) & 0xff,
(info.flash.jedecID >> 16) & 0xff,
(info.flash.jedecID >> 24) & 0xff,
info.flash.crc[0]
);
if (info.flash.flags & FLASH_REGION_INFO_BOOTABLE)
if (info.flash.bootable)
_PRINT(STR("SystemInfoScreen.flash.bootable"));
_PRINTLN();
@ -170,14 +315,17 @@ void SystemInfoScreen::show(ui::Context &ctx, bool goBack) {
_PRINT(STR("SystemInfoScreen.pcmcia.header"), i + 1);
if (card.flags & FLASH_REGION_INFO_PRESENT) {
if (rom::pcmcia[i].isPresent()) {
_PRINT(
STR("SystemInfoScreen.pcmcia.info"), card.jedecID & 0xff,
(card.jedecID >> 16) & 0xff, card.crc[0], card.crc[1],
card.crc[3]
STR("SystemInfoScreen.pcmcia.info"),
(card.jedecID >> 0) & 0xff,
(card.jedecID >> 8) & 0xff,
(card.jedecID >> 16) & 0xff,
(card.jedecID >> 24) & 0xff,
card.crc[0], card.crc[1], card.crc[3]
);
if (card.flags & FLASH_REGION_INFO_BOOTABLE)
if (card.bootable)
_PRINT(STR("SystemInfoScreen.pcmcia.bootable"));
} else {
_PRINT(STR("SystemInfoScreen.pcmcia.noCard"));

View File

@ -1,11 +1,33 @@
#pragma once
#include "common/file.hpp"
#include "common/rom.hpp"
#include "common/util.hpp"
#include "main/uibase.hpp"
#include "main/uicommon.hpp"
/* Storage device submenu */
class StorageMenuScreen : public ui::ListScreen {
private:
const rom::Region *_selectedRegion;
protected:
const char *_getItemName(ui::Context &ctx, int index) const;
public:
inline const rom::Region &getSelectedRegion(void) {
return *_selectedRegion;
}
void dump(ui::Context &ctx);
void restore(ui::Context &ctx);
void erase(ui::Context &ctx);
void show(ui::Context &ctx, bool goBack = false);
void update(ui::Context &ctx);
};
/* System information screen */
class SystemInfoScreen : public ui::TextScreen {

View File

@ -11,54 +11,6 @@
#include "main/app/app.hpp"
#include "ps1/system.h"
bool App::_startupWorker(void) {
#ifdef NDEBUG
_workerStatus.setNextScreen(_warningScreen);
#else
// Skip the warning screen in debug builds.
_workerStatus.setNextScreen(_buttonMappingScreen);
#endif
for (int i = 0; i < 2; i++) {
auto &dev = ide::devices[i];
_workerStatus.update(i, 4, WSTR("App.startupWorker.initIDE"));
if (dev.enumerate())
continue;
if (!(dev.flags & ide::DEVICE_ATAPI))
continue;
// Try to prevent the disc from keeping spinning unnecessarily.
ide::Packet packet;
packet.setStartStopUnit(ide::START_STOP_MODE_STOP_DISC);
dev.atapiPacket(packet);
}
_workerStatus.update(2, 4, WSTR("App.startupWorker.initFAT"));
// Attempt to mount the secondary drive first, then in case of failure try
// mounting the primary drive instead.
if (!_fileProvider.init("1:"))
_fileProvider.init("0:");
_workerStatus.update(3, 4, WSTR("App.startupWorker.loadResources"));
_resourceFile = _fileProvider.openFile(
EXTERNAL_DATA_DIR "/resource.zip", file::READ
);
if (_resourceFile) {
_resourceProvider.close();
if (_resourceProvider.init(_resourceFile))
_loadResources();
}
_ctx.sounds[ui::SOUND_STARTUP].play();
return true;
}
struct DumpEntry {
public:
util::Hash dumpPrompt, hashPrompt;
@ -109,9 +61,6 @@ bool App::_romDumpWorker(void) {
// Store all dumps in a subdirectory named "dumpN" within the main data
// folder.
int index = 0;
char dirPath[32], filePath[32];
file::FileInfo info;
if (!_fileProvider.getFileInfo(info, EXTERNAL_DATA_DIR)) {
@ -119,6 +68,11 @@ bool App::_romDumpWorker(void) {
goto _initError;
}
int index;
char dirPath[32], filePath[32];
index = 0;
do {
index++;
snprintf(dirPath, sizeof(dirPath), EXTERNAL_DATA_DIR "/dump%d", index);
@ -150,7 +104,7 @@ bool App::_romDumpWorker(void) {
);
if (!_file)
goto _writeError;
goto _fileError;
auto buffer = new uint8_t[chunkLength];
uint32_t offset = 0;
@ -165,7 +119,7 @@ bool App::_romDumpWorker(void) {
delete _file;
delete[] buffer;
goto _writeError;
goto _fileError;
}
offset += chunkLength;
@ -178,7 +132,7 @@ bool App::_romDumpWorker(void) {
}
_messageScreen.setMessage(
MESSAGE_SUCCESS, _mainMenuScreen, WSTR("App.romDumpWorker.success"),
MESSAGE_SUCCESS, _storageMenuScreen, WSTR("App.romDumpWorker.success"),
dirPath
);
_workerStatus.setNextScreen(_messageScreen);
@ -186,28 +140,218 @@ bool App::_romDumpWorker(void) {
_initError:
_messageScreen.setMessage(
MESSAGE_ERROR, _mainMenuScreen, WSTR("App.romDumpWorker.initError"),
MESSAGE_ERROR, _storageMenuScreen, WSTR("App.romDumpWorker.initError"),
dirPath
);
_workerStatus.setNextScreen(_messageScreen);
return false;
_writeError:
_fileError:
_messageScreen.setMessage(
MESSAGE_ERROR, _mainMenuScreen, WSTR("App.romDumpWorker.dumpError"),
MESSAGE_ERROR, _storageMenuScreen, WSTR("App.romDumpWorker.fileError"),
filePath
);
_workerStatus.setNextScreen(_messageScreen);
return false;
}
bool App::_romRestoreWorker(void) {
_workerStatus.update(0, 1, WSTR("App.romRestoreWorker.init"));
auto &region = _storageMenuScreen.getSelectedRegion();
auto driver = region.newDriver();
size_t chipLength = driver->getChipSize().chipLength;
size_t sectorLength = driver->getChipSize().eraseSectorLength;
size_t sectorsErased = 0;
size_t bytesWritten = 0;
const char *path = _filePickerScreen.selectedPath;
auto _file = _fileProvider.openFile(path, file::READ);
if (!_file)
goto _fileError;
if (!chipLength)
goto _unsupported;
rom::DriverError error;
_systemInfo.flags = 0;
// TODO: actually implement flash writing
delete _file;
delete driver;
_messageScreen.setMessage(
MESSAGE_SUCCESS, _storageMenuScreen,
WSTR("App.romRestoreWorker.success"), sectorsErased, bytesWritten
);
_workerStatus.setNextScreen(_messageScreen);
return true;
_fileError:
delete driver;
_messageScreen.setMessage(
MESSAGE_ERROR, _storageMenuScreen,
WSTR("App.romRestoreWorker.fileError"), path, sectorsErased,
bytesWritten
);
_workerStatus.setNextScreen(_messageScreen);
return false;
_flashError:
delete _file;
delete driver;
_messageScreen.setMessage(
MESSAGE_ERROR, _storageMenuScreen,
WSTR("App.romRestoreWorker.flashError"), rom::getErrorString(error),
sectorsErased, bytesWritten
);
_workerStatus.setNextScreen(_messageScreen);
return false;
_unsupported:
auto id = region.getJEDECID();
delete _file;
delete driver;
_messageScreen.setMessage(
MESSAGE_ERROR, _storageMenuScreen,
WSTR("App.romRestoreWorker.unsupported"),
(id >> 0) & 0xff,
(id >> 8) & 0xff,
(id >> 16) & 0xff,
(id >> 24) & 0xff
);
_workerStatus.setNextScreen(_messageScreen);
return false;
}
bool App::_romEraseWorker(void) {
auto &region = _storageMenuScreen.getSelectedRegion();
auto driver = region.newDriver();
size_t chipLength = driver->getChipSize().chipLength;
size_t sectorLength = driver->getChipSize().eraseSectorLength;
size_t sectorsErased = 0;
if (!chipLength)
goto _unsupported;
rom::DriverError error;
_systemInfo.flags = 0;
// Erase one sector at a time on each chip.
for (size_t i = 0; i < chipLength; i += sectorLength) {
_workerStatus.update(i, chipLength, WSTR("App.romEraseWorker.erase"));
for (size_t j = 0; j < region.regionLength; j += chipLength)
driver->eraseSector(i + j);
for (
size_t j = 0; j < region.regionLength; j += chipLength,
sectorsErased++
) {
error = driver->flushErase(i + j);
if (error)
goto _flashError;
}
}
delete driver;
_messageScreen.setMessage(
MESSAGE_SUCCESS, _storageMenuScreen, WSTR("App.romEraseWorker.success"),
sectorsErased
);
_workerStatus.setNextScreen(_messageScreen);
return true;
_flashError:
delete driver;
_messageScreen.setMessage(
MESSAGE_ERROR, _storageMenuScreen,
WSTR("App.romEraseWorker.flashError"), rom::getErrorString(error),
sectorsErased
);
_workerStatus.setNextScreen(_messageScreen);
return false;
_unsupported:
auto id = region.getJEDECID();
delete driver;
_messageScreen.setMessage(
MESSAGE_ERROR, _storageMenuScreen,
WSTR("App.romEraseWorker.unsupported"),
(id >> 0) & 0xff,
(id >> 8) & 0xff,
(id >> 16) & 0xff,
(id >> 24) & 0xff
);
_workerStatus.setNextScreen(_messageScreen);
return false;
}
bool App::_startupWorker(void) {
#ifdef NDEBUG
_workerStatus.setNextScreen(_warningScreen);
#else
// Skip the warning screen in debug builds.
_workerStatus.setNextScreen(_buttonMappingScreen);
#endif
for (int i = 0; i < 2; i++) {
auto &dev = ide::devices[i];
_workerStatus.update(i, 4, WSTR("App.startupWorker.initIDE"));
if (dev.enumerate())
continue;
if (!(dev.flags & ide::DEVICE_ATAPI))
continue;
// Try to prevent the disc from keeping spinning unnecessarily.
ide::Packet packet;
packet.setStartStopUnit(ide::START_STOP_MODE_STOP_DISC);
dev.atapiPacket(packet);
}
_workerStatus.update(2, 4, WSTR("App.startupWorker.initFAT"));
// Attempt to mount the secondary drive first, then in case of failure try
// mounting the primary drive instead.
if (!_fileProvider.init("1:"))
_fileProvider.init("0:");
_workerStatus.update(3, 4, WSTR("App.startupWorker.loadResources"));
_resourceFile = _fileProvider.openFile(
EXTERNAL_DATA_DIR "/resource.zip", file::READ
);
if (_resourceFile) {
_resourceProvider.close();
if (_resourceProvider.init(_resourceFile))
_loadResources();
}
_ctx.sounds[ui::SOUND_STARTUP].play();
return true;
}
bool App::_systemInfoWorker(void) {
// This is necessary to ensure the digital I/O ID is read at least once.
if (!_driver)
_cartDetectWorker();
_workerStatus.setNextScreen(_systemInfoScreen);
_systemInfo.clearFlags();
_systemInfo.flags = 0;
for (auto &entry : _DUMP_ENTRIES) {
if (!entry.region.isPresent())
@ -252,10 +396,7 @@ bool App::_systemInfoWorker(void) {
_systemInfo.flags |= SYSTEM_INFO_RTC_BATTERY_LOW;
_systemInfo.flash.jedecID = rom::flash.getJEDECID();
_systemInfo.flash.flags = FLASH_REGION_INFO_PRESENT;
if (rom::flash.hasBootExecutable())
_systemInfo.flash.flags |= FLASH_REGION_INFO_BOOTABLE;
_systemInfo.flash.bootable = rom::flash.hasBootExecutable();
for (int i = 0; i < 2; i++) {
auto &region = rom::pcmcia[i];
@ -263,10 +404,7 @@ bool App::_systemInfoWorker(void) {
if (region.isPresent()) {
card.jedecID = region.getJEDECID();
card.flags = FLASH_REGION_INFO_PRESENT;
if (region.hasBootExecutable())
card.flags |= FLASH_REGION_INFO_BOOTABLE;
card.bootable = region.hasBootExecutable();
}
}
@ -296,16 +434,14 @@ bool App::_executableWorker(void) {
_workerStatus.update(0, 1, WSTR("App.executableWorker.init"));
const char *path = _filePickerScreen.selectedPath;
util::ExecutableHeader header;
uintptr_t executableEnd, stackTop;
auto _file = _fileProvider.openFile(path, file::READ);
size_t length;
if (!_file)
goto _fileOpenError;
util::ExecutableHeader header;
size_t length;
length = _file->read(&header, sizeof(header));
if (length != sizeof(header))
@ -325,6 +461,8 @@ bool App::_executableWorker(void) {
delete _file;
uintptr_t executableEnd, stackTop;
executableEnd = header.textOffset + header.textLength;
stackTop = uintptr_t(header.getStackPtr());
@ -451,6 +589,14 @@ bool App::_atapiEjectWorker(void) {
bool App::_rebootWorker(void) {
_workerStatus.update(0, 1, WSTR("App.rebootWorker.reboot"));
_unloadCartData();
_resourceProvider.close();
if (_resourceFile)
delete _resourceFile;
_fileProvider.close();
_workerStatus.setStatus(WORKER_REBOOT);
// Fall back to a soft reboot if the watchdog fails to reset the system.