From f4a8d16b2049ebbf7e528b9a990b99f855173029 Mon Sep 17 00:00:00 2001 From: spicyjpeg Date: Wed, 5 Jun 2024 18:50:00 +0200 Subject: [PATCH] Bump to 0.4.5, add flash executable loading, fix bugs --- CMakeLists.txt | 2 +- src/boot/main.cpp | 4 +- src/common/args.cpp | 4 +- src/common/args.hpp | 4 +- src/common/file/fat.cpp | 6 +++ src/common/file/file.hpp | 11 ++--- src/common/file/misc.cpp | 38 ++++++++++------- src/common/file/zip.cpp | 9 ++++ src/common/ide.cpp | 10 ++++- src/common/io.cpp | 6 +-- src/common/rom.cpp | 57 ++++++++++++++++--------- src/common/rom.hpp | 6 ++- src/launcher/main.cpp | 80 ++++++++++++++++++++++++++++-------- src/main/app/app.cpp | 4 +- src/main/app/miscworkers.cpp | 8 ++-- src/main/app/modals.cpp | 2 +- src/main/app/romactions.cpp | 4 +- 17 files changed, 177 insertions(+), 78 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bb6ec15..eb2aca8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_LIST_DIR}/cmake/toolchain.cmake") project( cart-tool LANGUAGES C CXX ASM - VERSION 0.4.4 + VERSION 0.4.5 DESCRIPTION "Konami System 573 security cartridge tool" ) diff --git a/src/boot/main.cpp b/src/boot/main.cpp index 4d6731f..007931b 100644 --- a/src/boot/main.cpp +++ b/src/boot/main.cpp @@ -7,8 +7,8 @@ extern "C" const uint8_t _resourceArchive[]; extern "C" const size_t _resourceArchiveLength; -static char _ptrArg[]{ "resource.ptr=xxxxxxxx" }; -static char _lengthArg[]{ "resource.length=xxxxxxxx" }; +static char _ptrArg[]{ "resource.ptr=xxxxxxxx\0" }; +static char _lengthArg[]{ "resource.length=xxxxxxxx\0" }; struct [[gnu::packed]] ZIPFileHeader { public: diff --git a/src/common/args.cpp b/src/common/args.cpp index 2b9babb..3bb7fbf 100644 --- a/src/common/args.cpp +++ b/src/common/args.cpp @@ -87,8 +87,8 @@ bool ExecutableLauncherArgs::parseArgument(const char *arg) { loadAddress = reinterpret_cast(strtol(&arg[5], nullptr, 16)); return true; - case "drive"_h: - drive = int(strtol(&arg[6], nullptr, 0)); + case "device"_h: + device = int(strtol(&arg[6], nullptr, 0)); return true; case "frag"_h: diff --git a/src/common/args.hpp b/src/common/args.hpp index b8914e7..4146e7c 100644 --- a/src/common/args.hpp +++ b/src/common/args.hpp @@ -52,7 +52,7 @@ public: void *entryPoint, *initialGP, *stackTop; void *loadAddress; - int drive; + int device; // 0-63 = flash, -1 or -2 = IDE size_t numArgs, numFragments; const char *executableArgs[util::MAX_EXECUTABLE_ARGS]; @@ -60,7 +60,7 @@ public: inline ExecutableLauncherArgs(void) : entryPoint(nullptr), initialGP(nullptr), stackTop(nullptr), - loadAddress(nullptr), drive(0), numArgs(0), numFragments(0) {} + loadAddress(nullptr), device(0), numArgs(0), numFragments(0) {} bool parseArgument(const char *arg); }; diff --git a/src/common/file/fat.cpp b/src/common/file/fat.cpp index cdde0dd..484aad7 100644 --- a/src/common/file/fat.cpp +++ b/src/common/file/fat.cpp @@ -104,6 +104,9 @@ void FATDirectory::close(void) { /* FAT filesystem provider */ bool FATProvider::init(int drive) { + if (type) + return false; + _drive[0] = drive + '0'; auto error = f_mount(&_fs, _drive, 1); @@ -123,6 +126,9 @@ bool FATProvider::init(int drive) { } void FATProvider::close(void) { + if (!type) + return; + auto error = f_unmount(_drive); if (error) { diff --git a/src/common/file/file.hpp b/src/common/file/file.hpp index c4aaaef..4286cc9 100644 --- a/src/common/file/file.hpp +++ b/src/common/file/file.hpp @@ -20,11 +20,12 @@ enum FileSystemType { FAT12 = 1, FAT16 = 2, FAT32 = 3, - ISO9660 = 4, - ZIP_MEMORY = 5, - ZIP_FILE = 6, - HOST = 7, - VFS = 8 + EXFAT = 4, + ISO9660 = 5, + ZIP_MEMORY = 6, + ZIP_FILE = 7, + HOST = 8, + VFS = 9 }; // These are functionally equivalent to the FA_* flags used by FatFs. diff --git a/src/common/file/misc.cpp b/src/common/file/misc.cpp index e608da1..434dfbf 100644 --- a/src/common/file/misc.cpp +++ b/src/common/file/misc.cpp @@ -180,24 +180,33 @@ VFSMountPoint *VFSProvider::_getMounted(const char *path) { bool VFSProvider::mount(const char *prefix, Provider *provider, bool force) { auto hash = util::hash(prefix, VFS_PREFIX_SEPARATOR); + VFSMountPoint *freeMP = nullptr; + for (auto &mp : _mountPoints) { - if (force) { - if (mp.prefix && (mp.prefix != hash)) - continue; - } else { - if (mp.prefix) - continue; + if (!mp.prefix) { + freeMP = ∓ + } else if (mp.prefix == hash) { + if (force) { + freeMP = ∓ + break; + } + + LOG_FS("%s was already mapped", prefix); + return false; } - - mp.prefix = hash; - mp.pathOffset = __builtin_strlen(prefix); - mp.provider = provider; - - LOG_FS("mapped %s", prefix); - return true; } - return false; + if (!freeMP) { + LOG_FS("no mount points left for %s", prefix); + return false; + } + + freeMP->prefix = hash; + freeMP->pathOffset = __builtin_strlen(prefix); + freeMP->provider = provider; + + LOG_FS("mapped %s", prefix); + return true; } bool VFSProvider::unmount(const char *prefix) { @@ -215,6 +224,7 @@ bool VFSProvider::unmount(const char *prefix) { return true; } + LOG_FS("%s was not mapped", prefix); return false; } diff --git a/src/common/file/zip.cpp b/src/common/file/zip.cpp index a4e3b5e..09fc14f 100644 --- a/src/common/file/zip.cpp +++ b/src/common/file/zip.cpp @@ -97,6 +97,9 @@ static constexpr uint32_t _ZIP_FLAGS = 0 | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY; bool ZIPProvider::init(File *file) { + if (type) + return false; + mz_zip_zero_struct(&_zip); _file = file; @@ -128,6 +131,9 @@ bool ZIPProvider::init(File *file) { } bool ZIPProvider::init(const void *zipData, size_t length) { + if (type) + return false; + mz_zip_zero_struct(&_zip); _file = nullptr; @@ -146,6 +152,9 @@ bool ZIPProvider::init(const void *zipData, size_t length) { } void ZIPProvider::close(void) { + if (!type) + return; + mz_zip_reader_end(&_zip); #if 0 diff --git a/src/common/ide.cpp b/src/common/ide.cpp index 61a39c5..774f261 100644 --- a/src/common/ide.cpp +++ b/src/common/ide.cpp @@ -607,8 +607,9 @@ DeviceError Device::enumerate(void) { // actually present. A strict timeout is used in the commands below in order // to prevent blocking for too long. IdentifyBlock block; + auto signature = _getCylinder(); - if (_getCylinder() == _ATAPI_SIGNATURE) { + if (signature == _ATAPI_SIGNATURE) { flags |= DEVICE_ATAPI; _write(CS0_COMMAND, ATA_IDENTIFY_PACKET); @@ -660,6 +661,13 @@ DeviceError Device::enumerate(void) { // Find out the fastest PIO transfer mode supported and enable it. int mode = block.getHighestPIOMode(); + _select(0); + + error = _waitForIdle(); + + if (error) + return error; + _write(CS0_FEATURES, FEATURE_TRANSFER_MODE); _write(CS0_COUNT, TRANSFER_MODE_PIO | mode); _write(CS0_COMMAND, ATA_SET_FEATURES); diff --git a/src/common/io.cpp b/src/common/io.cpp index 78db557..583feae 100644 --- a/src/common/io.cpp +++ b/src/common/io.cpp @@ -36,12 +36,12 @@ void init(void) { | ( 4 << 24) // DMA read/write delay | BIU_CTRL_DMA_DELAY; +#if 0 // Revision D of the main board has footprints for either eight 8-bit RAM // chips wired as two 32-bit banks, or two 16-bit chips wired as a single - // bank. Normally the kernel takes care of setting up the memory controller - // appropriately, but this makes sure the configuration is correct if e.g. - // the tool is booted through OpenBIOS instead. + // bank. DRAM_CTRL = isDualBankRAM() ? 0x0c80 : 0x4788; +#endif _bankSwitchReg = 0; _cartOutputReg = 0; diff --git a/src/common/rom.cpp b/src/common/rom.cpp index 6a28ff3..ec56c31 100644 --- a/src/common/rom.cpp +++ b/src/common/rom.cpp @@ -29,9 +29,21 @@ void Region::read(void *data, uint32_t offset, size_t length) const { auto source = reinterpret_cast(ptr + offset); auto dest = reinterpret_cast(data); + // TODO: use memcpy() instead once an optimized implementation is added util::assertAligned(source); util::assertAligned(dest); + for (; length >= 32; length -= 32, dest += 8, source += 8) { + dest[0] = source[0]; + dest[1] = source[1]; + dest[2] = source[2]; + dest[3] = source[3]; + dest[4] = source[4]; + dest[5] = source[5]; + dest[6] = source[6]; + dest[7] = source[7]; + } + for (; length; length -= 4) *(dest++) = *(source++); } @@ -64,8 +76,19 @@ void RTCRegion::read(void *data, uint32_t offset, size_t length) const { // The RTC is an 8-bit device connected to a 16-bit bus, i.e. each byte must // be read as a 16-bit value and then the upper 8 bits must be discarded. + for (; length >= 8; length -= 8, dest += 8, source += 8) { + dest[0] = uint8_t(source[0]); + dest[1] = uint8_t(source[1]); + dest[2] = uint8_t(source[2]); + dest[3] = uint8_t(source[3]); + dest[4] = uint8_t(source[4]); + dest[5] = uint8_t(source[5]); + dest[6] = uint8_t(source[6]); + dest[7] = uint8_t(source[7]); + } + for (; length; length--) - *(dest++) = *(source++) & 0xff; + *(dest++) = uint8_t(*(source++)); } uint32_t RTCRegion::zipCRC32( @@ -120,18 +143,11 @@ uint16_t *FlashRegion::getRawPtr(uint32_t offset, bool alignToChip) const { 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; + auto bankOffset = offset / FLASH_BANK_LENGTH; + auto ptrOffset = offset % FLASH_BANK_LENGTH; - auto source = reinterpret_cast(ptr + ptrOffset); - auto dest = reinterpret_cast(data); - - util::assertAligned(source); - util::assertAligned(dest); io::setFlashBank(bank + bankOffset); - - for (; length; length -= 4) - *(dest++) = *(source++); + Region::read(data, ptrOffset, length); } uint32_t FlashRegion::zipCRC32( @@ -139,8 +155,8 @@ uint32_t FlashRegion::zipCRC32( ) 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; + auto bankOffset = offset / FLASH_BANK_LENGTH; + auto ptrOffset = offset % FLASH_BANK_LENGTH; auto source = reinterpret_cast(ptr + ptrOffset); auto table = reinterpret_cast(CACHE_BASE); @@ -176,7 +192,7 @@ enum FlashIdentifier : uint16_t { _ID_28F640J5 = 0x89 | (0x15 << 8) }; -bool FlashRegion::hasBootExecutable(void) const { +const util::ExecutableHeader *FlashRegion::getBootExecutableHeader(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) auto data = reinterpret_cast(ptr + FLASH_EXECUTABLE_OFFSET); @@ -185,10 +201,10 @@ bool FlashRegion::hasBootExecutable(void) const { io::setFlashBank(bank); - auto &header = *reinterpret_cast(data); + auto header = reinterpret_cast(data); - if (!header.validateMagic()) - return false; + if (!header->validateMagic()) + return nullptr; // The integrity of the executable is verified by calculating the CRC32 of // its bytes whose offsets are powers of 2 (i.e. the bytes at indices 0, 1, @@ -196,7 +212,7 @@ bool FlashRegion::hasBootExecutable(void) const { // header.textLength + util::EXECUTABLE_BODY_OFFSET, as the CRC is also // calculated on the header, but Konami's shell ignores the last 2048 bytes // due to a bug. - size_t length = header.textLength; + size_t length = header->textLength; uint32_t crc = ~0; crc = (crc >> 8) ^ table[(crc ^ *data) & 0xff]; @@ -204,7 +220,10 @@ bool FlashRegion::hasBootExecutable(void) const { for (size_t i = 1; i < length; i <<= 1) crc = (crc >> 8) ^ table[(crc ^ data[i]) & 0xff]; - return (~crc == *crcPtr); + if (~crc != *crcPtr) + return nullptr; + + return header; } uint32_t FlashRegion::getJEDECID(void) const { diff --git a/src/common/rom.hpp b/src/common/rom.hpp index b44f1e3..831d74f 100644 --- a/src/common/rom.hpp +++ b/src/common/rom.hpp @@ -32,7 +32,9 @@ public: uint32_t offset, size_t length, uint32_t crc = 0 ) const; - virtual bool hasBootExecutable(void) const { return false; } + virtual const util::ExecutableHeader *getBootExecutableHeader(void) const { + return nullptr; + } virtual uint32_t getJEDECID(void) const { return 0; } virtual Driver *newDriver(void) const { return nullptr; } }; @@ -67,7 +69,7 @@ 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; - bool hasBootExecutable(void) const; + const util::ExecutableHeader *getBootExecutableHeader(void) const; uint32_t getJEDECID(void) const; Driver *newDriver(void) const; }; diff --git a/src/launcher/main.cpp b/src/launcher/main.cpp index 6cd1bf8..2b0416a 100644 --- a/src/launcher/main.cpp +++ b/src/launcher/main.cpp @@ -7,30 +7,42 @@ extern "C" uint8_t _textStart[]; -int main(int argc, const char **argv) { - io::init(); +static constexpr size_t _LOAD_CHUNK_LENGTH = 0x8000; - args::ExecutableLauncherArgs args; +static int _loadFromFlash(args::ExecutableLauncherArgs &args) { + io::setFlashBank(args.device); - for (; argc > 0; argc--) - args.parseArgument(*(argv++)); + // The executable's offset and length are always passed as a single + // fragment. + auto ptr = reinterpret_cast(args.loadAddress); + auto source = uintptr_t(args.fragments[0].lba); + auto length = size_t(args.fragments[0].length); -#if defined(ENABLE_APP_LOGGING) || defined(ENABLE_IDE_LOGGING) - util::logger.setupSyslog(args.baudRate); -#endif + while (length) { + size_t chunkLength = util::min(length, _LOAD_CHUNK_LENGTH); - if (!args.entryPoint || !args.loadAddress || !args.numFragments) { - LOG_APP("required arguments missing"); - return 1; + __builtin_memcpy( + reinterpret_cast(ptr), + reinterpret_cast(source), chunkLength + ); + io::clearWatchdog(); + + ptr += chunkLength; + source += chunkLength; + length -= chunkLength; } - if (!args.stackTop) - args.stackTop = _textStart - 16; + return 0; +} - auto &dev = ide::devices[args.drive]; +static int _loadFromIDE(args::ExecutableLauncherArgs &args) { + int drive = -(args.device + 1); + auto &dev = ide::devices[drive]; - if (dev.enumerate()) { - LOG_APP("drive %d initialization failed", args.drive); + auto error = dev.enumerate(); + + if (error) { + LOG_APP("drive %d: %s", drive, ide::getErrorString(error)); return 2; } @@ -57,8 +69,10 @@ int main(int argc, const char **argv) { length -= skipSectors; } - if (dev.readData(reinterpret_cast(ptr), lba, length)) { - LOG_APP("read failed, lba=0x%08x", lba); + error = dev.readData(reinterpret_cast(ptr), lba, length); + + if (error) { + LOG_APP("drive %d: %s", drive, ide::getErrorString(error)); return 3; } @@ -66,6 +80,36 @@ int main(int argc, const char **argv) { ptr += length * sectorSize; } + return 0; +} + +int main(int argc, const char **argv) { + io::init(); + + args::ExecutableLauncherArgs args; + + for (; argc > 0; argc--) + args.parseArgument(*(argv++)); + +#if defined(ENABLE_APP_LOGGING) || defined(ENABLE_IDE_LOGGING) + util::logger.setupSyslog(args.baudRate); +#endif + + if (!args.entryPoint || !args.loadAddress || !args.numFragments) { + LOG_APP("required arguments missing"); + return 1; + } + + if (!args.stackTop) + args.stackTop = _textStart - 16; + + int error = (args.device >= 0) + ? _loadFromFlash(args) + : _loadFromIDE(args); + + if (error) + return error; + // Launch the executable. util::ExecutableLoader loader( args.entryPoint, args.initialGP, args.stackTop diff --git a/src/main/app/app.cpp b/src/main/app/app.cpp index 8d823c5..aa7a107 100644 --- a/src/main/app/app.cpp +++ b/src/main/app/app.cpp @@ -78,7 +78,7 @@ FileIOManager::FileIOManager(void) void FileIOManager::initIDE(void) { closeIDE(); - char name[6]{ "ide#:" }; + char name[8]{ "ide#:\0" }; for (size_t i = 0; i < util::countOf(ide::devices); i++) { auto &dev = ide::devices[i]; @@ -117,7 +117,7 @@ void FileIOManager::initIDE(void) { } void FileIOManager::closeIDE(void) { - char name[6]{ "ide#:" }; + char name[8]{ "ide#:\0" }; for (size_t i = 0; i < util::countOf(ide::devices); i++) { if (ide[i]) { diff --git a/src/main/app/miscworkers.cpp b/src/main/app/miscworkers.cpp index 687318c..10d2bf7 100644 --- a/src/main/app/miscworkers.cpp +++ b/src/main/app/miscworkers.cpp @@ -41,7 +41,7 @@ bool App::_ideInitWorker(void) { #ifdef ENABLE_AUTOBOOT // Only try to autoboot if DIP switch 1 is on. if (io::getDIPSwitch(0)) { - _workerStatus.update(3, 4, WSTR("App.fileInitWorker.autoboot")); + _workerStatus.update(3, 4, WSTR("App.ideInitWorker.autoboot")); for (auto path : _AUTOBOOT_PATHS) { file::FileInfo info; @@ -62,14 +62,14 @@ bool App::_ideInitWorker(void) { } bool App::_fileInitWorker(void) { - _workerStatus.update(0, 4, WSTR("App.fileInitWorker.unmount")); + _workerStatus.update(0, 3, WSTR("App.fileInitWorker.unmount")); _fileIO.closeResourceFile(); _fileIO.close(); - _workerStatus.update(1, 4, WSTR("App.fileInitWorker.mount")); + _workerStatus.update(1, 3, WSTR("App.fileInitWorker.mount")); _fileIO.initIDE(); - _workerStatus.update(2, 4, WSTR("App.fileInitWorker.loadResources")); + _workerStatus.update(2, 3, WSTR("App.fileInitWorker.loadResources")); if (_fileIO.loadResourceFile(EXTERNAL_DATA_DIR "/resource.zip")) _loadResources(); diff --git a/src/main/app/modals.cpp b/src/main/app/modals.cpp index cee3e78..c232aa8 100644 --- a/src/main/app/modals.cpp +++ b/src/main/app/modals.cpp @@ -224,7 +224,7 @@ void FilePickerScreen::update(ui::Context &ctx) { } #endif - char name[6]{ "ide#:" }; + char name[8]{ "ide#:\0" }; int drive = _drives[index]; auto &dev = ide::devices[drive]; diff --git a/src/main/app/romactions.cpp b/src/main/app/romactions.cpp index c983131..915f503 100644 --- a/src/main/app/romactions.cpp +++ b/src/main/app/romactions.cpp @@ -72,7 +72,7 @@ void StorageInfoScreen::show(ui::Context &ctx, bool goBack) { (id >> 24) & 0xff ); - if (rom::flash.hasBootExecutable()) + if (rom::flash.getBootExecutableHeader()) _PRINT(STR("StorageInfoScreen.flash.bootable")); // TODO: show information about currently installed game @@ -96,7 +96,7 @@ void StorageInfoScreen::show(ui::Context &ctx, bool goBack) { (id >> 24) & 0xff ); - if (card.hasBootExecutable()) + if (card.getBootExecutableHeader()) _PRINT(STR("StorageInfoScreen.pcmcia.bootable")); } else { _PRINT(STR("StorageInfoScreen.pcmcia.noCard"));