From ef46aad5b0f3063c519a40df52cbb883285e613c Mon Sep 17 00:00:00 2001 From: spicyjpeg Date: Sun, 2 Jun 2024 01:45:15 +0200 Subject: [PATCH] Rewrite IDE driver completely, fix VFS and flash driver --- CMakeLists.txt | 1 + assets/app.strings.json | 9 +- src/common/ide.cpp | 730 +++++++++++++++++++---------------- src/common/ide.hpp | 287 ++++---------- src/common/idedefs.hpp | 238 ++++++++++++ src/common/rom.cpp | 388 +------------------ src/common/rom.hpp | 110 ------ src/common/romdrivers.cpp | 336 ++++++++++++++++ src/common/romdrivers.hpp | 161 ++++++++ src/main/app/app.cpp | 57 ++- src/main/app/app.hpp | 4 + src/main/app/miscworkers.cpp | 31 +- src/main/app/modals.cpp | 10 +- src/main/app/romworkers.cpp | 1 + src/main/app/tests.cpp | 2 + 15 files changed, 1299 insertions(+), 1066 deletions(-) create mode 100644 src/common/idedefs.hpp create mode 100644 src/common/romdrivers.cpp create mode 100644 src/common/romdrivers.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c1d2f56..9da7700 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,6 +106,7 @@ addExecutable( src/common/io.cpp src/common/pad.cpp src/common/rom.cpp + src/common/romdrivers.cpp src/common/spu.cpp src/common/util.cpp src/libc/crt0.c diff --git a/assets/app.strings.json b/assets/app.strings.json index 66a6892..4e4df64 100644 --- a/assets/app.strings.json +++ b/assets/app.strings.json @@ -6,8 +6,11 @@ "App": { "ideInitWorker": { - "initDrives": "Initializing IDE devices...\nDo not turn off the 573 or unplug drives.", - "initFileIO": "Detecting and mounting filesystems...\nDo not turn off the 573 or unplug drives.", + "init": "Initializing IDE devices...\nDo not turn off the 573 or unplug drives." + }, + "fileInitWorker": { + "unmount": "Unmounting all mounted filesystems...\nDo not turn off the 573 or unplug drives.", + "mount": "Detecting and mounting filesystems...\nDo not turn off the 573 or unplug drives.", "loadResources": "Loading resource pack...\nDo not turn off the 573 or unplug drives.", "autoboot": "Searching for boot executables...\nDo not turn off the 573 or unplug drives." }, @@ -113,7 +116,7 @@ "AudioTestScreen": { "title": "{RIGHT_ARROW} Audio output test", - "prompt": "Note that the speaker amplifier and analog audio passthrough are disabled by default and will be disabled again once this screen is closed.", + "prompt": "Note that the speaker amplifier and analog audio passthrough are disabled by this tool by default.", "itemPrompt": "{RIGHT_ARROW} Press {START_BUTTON} to select, hold {LEFT_BUTTON}{RIGHT_BUTTON} + {START_BUTTON} to go back", "playLeft": "Play sound on left channel", diff --git a/src/common/ide.cpp b/src/common/ide.cpp index 2154057..0a7cfbb 100644 --- a/src/common/ide.cpp +++ b/src/common/ide.cpp @@ -2,6 +2,7 @@ #include #include #include "common/ide.hpp" +#include "common/idedefs.hpp" #include "common/io.hpp" #include "common/util.hpp" #include "ps1/registers573.h" @@ -22,13 +23,6 @@ namespace ide { -static constexpr int _COMMAND_TIMEOUT = 30000000; -static constexpr int _REQUEST_SENSE_TIMEOUT = 500000; -static constexpr int _DETECT_DRIVE_TIMEOUT = 500000; - -static constexpr int _DMA_TIMEOUT = 10000; -static constexpr int _SRST_DELAY = 5000; - static const char *const _SENSE_KEY_NAMES[]{ "NO_SENSE", "RECOVERED_ERROR", @@ -39,25 +33,25 @@ static const char *const _SENSE_KEY_NAMES[]{ "UNIT_ATTENTION", "DATA_PROTECT", "BLANK_CHECK", - nullptr, - nullptr, + "UNKNOWN_9", + "UNKNOWN_A", "ABORTED_COMMAND", - nullptr, - nullptr, + "UNKNOWN_C", + "UNKNOWN_D", "MISCOMPARE", - nullptr + "UNKNOWN_F" }; const char *const DEVICE_ERROR_NAMES[]{ "NO_ERROR", "UNSUPPORTED_OP", "NO_DRIVE", + "NOT_YET_READY", "STATUS_TIMEOUT", + "CHECKSUM_MISMATCH", "DRIVE_ERROR", "DISC_ERROR", - "DISC_CHANGED", - "INCOMPLETE_DATA", - "CHECKSUM_MISMATCH" + "DISC_CHANGED" }; /* Utilities */ @@ -92,6 +86,50 @@ static void _copyString(char *output, const uint16_t *input, size_t length) { } #endif +static DeviceError _senseDataToError(const SenseData &data) { + auto key = data.senseKey & 15; + auto asc = data.getPackedASC(); + auto lba = data.getErrorLBA(); + + LOG("%s", _SENSE_KEY_NAMES[key]); + LOG("err=0x%02x, key=0x%02x", data.errorCode, data.senseKey); + LOG("asc=0x%02x, ascq=0x%02x", data.asc, data.ascQualifier); + + if (lba) { + LOG("lba=0x%08x", lba); + } + + switch (key) { + case SENSE_KEY_NO_SENSE: + case SENSE_KEY_RECOVERED_ERROR: + return NO_ERROR; + + case SENSE_KEY_NOT_READY: + return ( + !asc || + (asc == ASC_NOT_READY) || + (asc == ASC_NOT_READY_IN_PROGRESS) + ) + ? NOT_YET_READY + : DISC_ERROR; + + case SENSE_KEY_MEDIUM_ERROR: + case SENSE_KEY_DATA_PROTECT: + return DISC_ERROR; + + case SENSE_KEY_UNIT_ATTENTION: + return (asc == ASC_RESET_OCCURRED) + ? NO_ERROR + : DISC_CHANGED; + + case SENSE_KEY_ABORTED_COMMAND: + return UNSUPPORTED_OP; + + default: + return DRIVE_ERROR; + } +} + bool IdentifyBlock::validateChecksum(void) const { if ((checksum & 0xff) != 0xa5) return true; @@ -122,29 +160,129 @@ int IdentifyBlock::getHighestPIOMode(void) const { /* Device class */ +static constexpr int _DMA_TIMEOUT = 10000; + Device devices[2]{ (DEVICE_PRIMARY), (DEVICE_SECONDARY) }; -DeviceError Device::_waitForStatus( - uint8_t mask, uint8_t value, int timeout, bool ignoreErrors -) { +Device::Device(uint32_t flags) +: flags(flags), capacity(0), lastStatusReg(0), lastErrorReg(0) { + util::clear(lastSenseData); +} + +void Device::_readPIO(void *data, size_t length) const { + length++; + length /= 2; + + util::assertAligned(data); + + auto ptr = reinterpret_cast(data); + + for (; length > 0; length--) + *(ptr++) = SYS573_IDE_CS0_BASE[CS0_DATA]; +} + +void Device::_writePIO(const void *data, size_t length) const { + length++; + length /= 2; + + util::assertAligned(data); + + auto ptr = reinterpret_cast(data); + + for (; length > 0; length--) + SYS573_IDE_CS0_BASE[CS0_DATA] = *(ptr++); +} + +bool Device::_readDMA(void *data, size_t length) const { + length += 3; + length /= 4; + + util::assertAligned(data); + + DMA_MADR(DMA_PIO) = reinterpret_cast(data); + DMA_BCR (DMA_PIO) = length; + DMA_CHCR(DMA_PIO) = 0 + | DMA_CHCR_READ + | DMA_CHCR_MODE_BURST + | DMA_CHCR_ENABLE + | DMA_CHCR_TRIGGER; + + return waitForDMATransfer(DMA_PIO, _DMA_TIMEOUT); +} + +bool Device::_writeDMA(const void *data, size_t length) const { + length += 3; + length /= 4; + + util::assertAligned(data); + + DMA_MADR(DMA_PIO) = reinterpret_cast(data); + DMA_BCR (DMA_PIO) = length; + DMA_CHCR(DMA_PIO) = 0 + | DMA_CHCR_WRITE + | DMA_CHCR_MODE_BURST + | DMA_CHCR_ENABLE + | DMA_CHCR_TRIGGER; + + return waitForDMATransfer(DMA_PIO, _DMA_TIMEOUT); +} + +/* Status and error polling */ + +static constexpr int _COMMAND_TIMEOUT = 30000000; +static constexpr int _DRQ_TIMEOUT = 30000000; +static constexpr int _DETECT_TIMEOUT = 500000; +static constexpr int _SRST_DELAY = 5000; + +// Note that ATA drives will always assert DRDY when ready, but ATAPI drives +// will not. This is an intentional feature meant to prevent ATA-only drivers +// from misdetecting ATAPI drives. +DeviceError Device::_waitForIdle(bool drdy, int timeout, bool ignoreError) { if (!timeout) timeout = _COMMAND_TIMEOUT; for (; timeout > 0; timeout -= 10) { - uint8_t status = _read(CS0_STATUS); + auto status = _read(CS0_STATUS); - if (!ignoreErrors && (status & CS0_STATUS_ERR)) { - LOG( - "drive %d error, stat=0x%02x, err=0x%02x", - (flags / DEVICE_SECONDARY) & 1, _read(CS0_STATUS), - _read(CS0_ERROR) - ); + // Only check for errors *after* BSY is cleared. + if (!(status & CS0_STATUS_BSY)) { + if ((status & CS0_STATUS_ERR) && !ignoreError) { + _handleError(); + return DRIVE_ERROR; + } - _write(CS0_COMMAND, ATA_DEVICE_RESET); - return DRIVE_ERROR; + if ((status & CS0_STATUS_DRDY) || !drdy) + return NO_ERROR; } - if ((status & mask) == value) + delayMicroseconds(10); +#ifndef ENABLE_FULL_IDE_DRIVER + io::clearWatchdog(); +#endif + } + + LOG("timeout, ignore=%d", ignoreError); + _handleTimeout(); + return STATUS_TIMEOUT; +} + +DeviceError Device::_waitForDRQ(int timeout, bool ignoreError) { + if (!timeout) + timeout = _DRQ_TIMEOUT; + + for (; timeout > 0; timeout -= 10) { + auto status = _read(CS0_STATUS); + + // Check for errors *before* DRQ is set but *after* BSY is cleared. + // Confused yet? + if (!(status & CS0_STATUS_BSY)) { + if ((status & CS0_STATUS_ERR) && !ignoreError) { + _handleError(); + return DRIVE_ERROR; + } + } + + if (status & CS0_STATUS_DRQ) return NO_ERROR; delayMicroseconds(10); @@ -153,66 +291,48 @@ DeviceError Device::_waitForStatus( #endif } - LOG( - "drive %d timeout, stat=0x%02x, err=0x%02x", - (flags / DEVICE_SECONDARY) & 1, _read(CS0_STATUS), _read(CS0_ERROR) - ); - - _write(CS0_COMMAND, ATA_DEVICE_RESET); + LOG("timeout, ignore=%d", ignoreError); + _handleTimeout(); return STATUS_TIMEOUT; } -DeviceError Device::_select( - uint8_t devSelFlags, int timeout, bool ignoreErrors -) { - if (flags & DEVICE_SECONDARY) - _write(CS0_DEVICE_SEL, devSelFlags | CS0_DEVICE_SEL_SECONDARY); - else - _write(CS0_DEVICE_SEL, devSelFlags | CS0_DEVICE_SEL_PRIMARY); +void Device::_handleError(void) { + lastStatusReg = _read(CS0_STATUS); + lastErrorReg = _read(CS0_ERROR); - return _waitForStatus(CS0_STATUS_BSY, 0, timeout, ignoreErrors); + LOG( + "drive=%d, st=0x%02x, err=0x%02x", (flags / DEVICE_SECONDARY) & 1, + lastStatusReg, lastErrorReg + ); + + // Issuing a device reset command to an ATAPI drive would result in the + // error's sense data being lost. + if (!(flags & DEVICE_ATAPI)) + _write(CS0_COMMAND, ATA_DEVICE_RESET); } -DeviceError Device::_setLBA(uint64_t lba, size_t count, int timeout) { - if (flags & DEVICE_HAS_LBA48) { - //assert(lba < (1ULL << 48)); - //assert(count <= (1 << 16)); +void Device::_handleTimeout(void) { + lastStatusReg = _read(CS0_STATUS); + lastErrorReg = _read(CS0_ERROR); - auto error = _select(CS0_DEVICE_SEL_LBA, timeout); + LOG( + "drive=%d, st=0x%02x, err=0x%02x", (flags / DEVICE_SECONDARY) & 1, + lastStatusReg, lastErrorReg + ); - if (error) - return error; - - _write(CS0_COUNT, (count >> 8) & 0xff); - _write(CS0_SECTOR, (lba >> 24) & 0xff); - _write(CS0_CYLINDER_L, (lba >> 32) & 0xff); - _write(CS0_CYLINDER_H, (lba >> 40) & 0xff); - } else { - //assert(lba < (1ULL << 28)); - //assert(count <= (1 << 8)); - - auto error = _select(CS0_DEVICE_SEL_LBA | ((lba >> 24) & 15), timeout); - - if (error) - return error; - } - - _write(CS0_COUNT, (count >> 0) & 0xff); - _write(CS0_SECTOR, (lba >> 0) & 0xff); - _write(CS0_CYLINDER_L, (lba >> 8) & 0xff); - _write(CS0_CYLINDER_H, (lba >> 16) & 0xff); - - return NO_ERROR; + _write(CS0_COMMAND, ATA_DEVICE_RESET); } -DeviceError Device::_detectDrive(void) { +DeviceError Device::_resetDrive(void) { // Issue a software reset, which affects both devices on the bus. _write(CS1_DEVICE_CTRL, CS1_DEVICE_CTRL_IEN | CS1_DEVICE_CTRL_SRST); delayMicroseconds(_SRST_DELAY); _write(CS1_DEVICE_CTRL, CS1_DEVICE_CTRL_IEN); delayMicroseconds(_SRST_DELAY); - if (_select(0, _DETECT_DRIVE_TIMEOUT, true)) { + _select(0); + + if (_waitForIdle(false, _DETECT_TIMEOUT, true)) { LOG("drive %d select timeout", (flags / DEVICE_SECONDARY) & 1); return NO_DRIVE; } @@ -225,129 +345,62 @@ DeviceError Device::_detectDrive(void) { // the written value. This should not fail even if the drive is busy. uint8_t pattern = 0x55; - for (int timeout = _DETECT_DRIVE_TIMEOUT; timeout > 0; timeout -= 100) { + for (int timeout = _DETECT_TIMEOUT; timeout > 0; timeout -= 10) { _write(CS0_COUNT, pattern); - // Note that ATA drives will also assert DRDY when ready, but ATAPI - // drives will not. - if (_read(CS0_COUNT) == pattern) - return _waitForStatus(CS0_STATUS_BSY, 0); + if (_read(CS0_COUNT) == pattern) { + _write(CS0_COUNT, 0); + return NO_ERROR; + } - pattern >>= 1; - if (!(pattern & 1)) - pattern |= 1 << 7; + pattern ^= 0xff; - delayMicroseconds(100); + delayMicroseconds(10); #ifndef ENABLE_FULL_IDE_DRIVER io::clearWatchdog(); #endif } - LOG("drive %d not found", (flags / DEVICE_SECONDARY) & 1); + LOG("drive %d not responding", (flags / DEVICE_SECONDARY) & 1); return NO_DRIVE; } -DeviceError Device::_readPIO( - void *data, size_t length, int timeout, bool ignoreErrors -) { - util::assertAligned(data); +/* ATA-specific function */ - auto error = _waitForStatus( - CS0_STATUS_DRQ | CS0_STATUS_BSY, CS0_STATUS_DRQ, timeout - ); +DeviceError Device::_ataSetLBA(uint64_t lba, size_t count, int timeout) { + if (flags & DEVICE_HAS_LBA48) { + //assert(lba < (1ULL << 48)); + //assert(count <= (1 << 16)); + _select(CS0_DEVICE_SEL_LBA); - if (error) - return error; + auto error = _waitForIdle(true); - auto ptr = reinterpret_cast(data); + if (error) + return error; - for (; length; length -= 2) - *(ptr++) = SYS573_IDE_CS0_BASE[CS0_DATA]; + _write(CS0_COUNT, (count >> 8) & 0xff); + _write(CS0_SECTOR, (lba >> 24) & 0xff); + _write(CS0_CYLINDER_L, (lba >> 32) & 0xff); + _write(CS0_CYLINDER_H, (lba >> 40) & 0xff); + } else { + //assert(lba < (1ULL << 28)); + //assert(count <= (1 << 8)); + _select(CS0_DEVICE_SEL_LBA | ((lba >> 24) & 15)); - return NO_ERROR; -} + auto error = _waitForIdle(true); -DeviceError Device::_writePIO( - const void *data, size_t length, int timeout, bool ignoreErrors -) { - util::assertAligned(data); - - auto error = _waitForStatus( - CS0_STATUS_DRQ | CS0_STATUS_BSY, CS0_STATUS_DRQ, timeout - ); - - if (error) - return error; - - auto ptr = reinterpret_cast(data); - - for (; length; length -= 2) - SYS573_IDE_CS0_BASE[CS0_DATA] = *(ptr++); - - return NO_ERROR; -} - -DeviceError Device::_readDMA( - void *data, size_t length, int timeout, bool ignoreErrors -) { - length /= 4; - - util::assertAligned(data); - - auto error = _waitForStatus( - CS0_STATUS_DRQ | CS0_STATUS_BSY, CS0_STATUS_DRQ, timeout - ); - - if (error) - return error; - - DMA_MADR(DMA_PIO) = reinterpret_cast(data); - DMA_BCR (DMA_PIO) = length; - DMA_CHCR(DMA_PIO) = 0 - | DMA_CHCR_READ - | DMA_CHCR_MODE_BURST - | DMA_CHCR_ENABLE - | DMA_CHCR_TRIGGER; - - if (!waitForDMATransfer(DMA_PIO, _DMA_TIMEOUT)) { - LOG("DMA transfer timeout"); - return INCOMPLETE_DATA; + if (error) + return error; } + _write(CS0_COUNT, (count >> 0) & 0xff); + _write(CS0_SECTOR, (lba >> 0) & 0xff); + _write(CS0_CYLINDER_L, (lba >> 8) & 0xff); + _write(CS0_CYLINDER_H, (lba >> 16) & 0xff); return NO_ERROR; } -DeviceError Device::_writeDMA( - const void *data, size_t length, int timeout, bool ignoreErrors -) { - length /= 4; - - util::assertAligned(data); - - auto error = _waitForStatus( - CS0_STATUS_DRQ | CS0_STATUS_BSY, CS0_STATUS_DRQ, timeout - ); - - if (error) - return error; - - DMA_MADR(DMA_PIO) = reinterpret_cast(data); - DMA_BCR (DMA_PIO) = length; - DMA_CHCR(DMA_PIO) = 0 - | DMA_CHCR_WRITE - | DMA_CHCR_MODE_BURST - | DMA_CHCR_ENABLE - | DMA_CHCR_TRIGGER; - - if (!waitForDMATransfer(DMA_PIO, _DMA_TIMEOUT)) { - LOG("DMA transfer timeout"); - return INCOMPLETE_DATA; - } - - return NO_ERROR; -} - -DeviceError Device::_ideReadWrite( +DeviceError Device::_ataTransfer( uintptr_t ptr, uint64_t lba, size_t count, bool write ) { uint8_t cmd; @@ -364,7 +417,7 @@ DeviceError Device::_ideReadWrite( while (count) { size_t chunkLength = util::min(count, maxLength); - auto error = _setLBA(lba, chunkLength); + auto error = _ataSetLBA(lba, chunkLength); if (error) return error; @@ -373,41 +426,125 @@ DeviceError Device::_ideReadWrite( // Data must be transferred one sector at a time as the drive may // deassert DRQ between sectors. - for (size_t i = chunkLength; i; i--) { - if (write) - error = _writePIO( - reinterpret_cast(ptr), ATA_SECTOR_SIZE - ); - else - error = _readPIO( - reinterpret_cast(ptr), ATA_SECTOR_SIZE - ); + for (size_t i = chunkLength; i; i--, ptr += ATA_SECTOR_SIZE) { + auto error = _waitForDRQ(); if (error) return error; - ptr += ATA_SECTOR_SIZE; + if (write) + _writePIO(reinterpret_cast(ptr), ATA_SECTOR_SIZE); + else + _readPIO(reinterpret_cast(ptr), ATA_SECTOR_SIZE); } - error = _waitForStatus( - CS0_STATUS_DRDY | CS0_STATUS_BSY, CS0_STATUS_DRDY - ); - - if (error) - return error; - lba += chunkLength; count -= chunkLength; } - return NO_ERROR; + return _waitForIdle(); +} + +/* ATAPI-specific functions */ + +static constexpr int _ATAPI_READY_TIMEOUT = 30000000; +static constexpr int _REQ_SENSE_TIMEOUT = 500000; + +// ATAPI devices will set the CHK (ERR) status flag whenever new sense data is +// available in response to a command. In such cases, the error should be +// cleared by sending a "request sense" command. +DeviceError Device::_atapiRequestSense(void) { + Packet packet; + + packet.setRequestSense(); + _select(0); + + auto error = _waitForIdle(false, _REQ_SENSE_TIMEOUT, true); + + if (!error) { + _setCylinder(sizeof(SenseData)); + _write(CS0_COMMAND, ATA_PACKET); + + error = _waitForDRQ(_REQ_SENSE_TIMEOUT, true); + } + if (!error) { + _writePIO(&packet, getPacketSize()); + + error = _waitForDRQ(_REQ_SENSE_TIMEOUT, true); + } + if (!error) { + _readPIO(&lastSenseData, sizeof(SenseData)); + LOG("sense data ok"); + } else { + // If the request sense command fails, fall back to reading the sense + // key from the error register. + util::clear(lastSenseData); + + lastSenseData.senseKey = lastErrorReg >> 4; + LOG("%s", DEVICE_ERROR_NAMES[error]); + } + + return _senseDataToError(lastSenseData); +} + +DeviceError Device::_atapiPacket(const Packet &packet, size_t dataLength) { + if (!(flags & DEVICE_READY)) + return NO_DRIVE; + if (!(flags & DEVICE_ATAPI)) + return UNSUPPORTED_OP; + + LOG("cmd=0x%02x, length=0x%x", packet.command, dataLength); + + // Keep resending the command as long as the drive reports it is in progress + // of becoming ready (i.e. spinning up). + for (int timeout = _ATAPI_READY_TIMEOUT; timeout > 0; timeout -= 10000) { + _select(0); + + auto error = _waitForIdle(); + + if (!error) { + _setCylinder(dataLength); + _write(CS0_COMMAND, ATA_PACKET); + + error = _waitForDRQ(); + } + if (!error) { + _writePIO(&packet, getPacketSize()); + + error = dataLength + ? _waitForDRQ() + : _waitForIdle(); + } + if (!error) + return NO_ERROR; + + // If an error occurred, fetch sense data to determine whether to resend + // the command. + LOG("%s, cmd=0x%02x", DEVICE_ERROR_NAMES[error], packet.command); + + error = _atapiRequestSense(); + + if (error && (error != NOT_YET_READY)) { + LOG("%s (from sense)", DEVICE_ERROR_NAMES[error]); + return error; + } + + delayMicroseconds(10000); +#ifndef ENABLE_FULL_IDE_DRIVER + io::clearWatchdog(); +#endif + } + + LOG("retry timeout, cmd=0x%02x", packet.command); + return STATUS_TIMEOUT; } DeviceError Device::_atapiRead(uintptr_t ptr, uint32_t lba, size_t count) { Packet packet; packet.setRead(lba, count); - auto error = atapiPacket(packet, ATAPI_SECTOR_SIZE); + + auto error = _atapiPacket(packet, ATAPI_SECTOR_SIZE); if (error) return error; @@ -415,19 +552,28 @@ DeviceError Device::_atapiRead(uintptr_t ptr, uint32_t lba, size_t count) { // Data must be transferred one sector at a time as the drive may deassert // DRQ between sectors. for (; count; count--) { - if (_readPIO(reinterpret_cast(ptr), ATAPI_SECTOR_SIZE)) - return atapiPoll(); + auto error = _waitForDRQ(); - ptr += ATAPI_SECTOR_SIZE; + if (error) + return error; + + size_t chunkLength = _getCylinder(); + + _readPIO(reinterpret_cast(ptr), chunkLength); + ptr += chunkLength; } - return _waitForStatus(CS0_STATUS_BSY, 0); + return _waitForIdle(); } +/* Public API */ + +static constexpr uint16_t _ATAPI_SIGNATURE = 0xeb14; + DeviceError Device::enumerate(void) { flags &= DEVICE_PRIMARY | DEVICE_SECONDARY; - auto error = _detectDrive(); + auto error = _resetDrive(); if (error) return error; @@ -440,17 +586,25 @@ DeviceError Device::enumerate(void) { // to prevent blocking for too long. IdentifyBlock block; - //_select(); - - if ((_read(CS0_CYLINDER_L) == 0x14) && (_read(CS0_CYLINDER_H) == 0xeb)) { + if (_getCylinder() == _ATAPI_SIGNATURE) { flags |= DEVICE_ATAPI; _write(CS0_COMMAND, ATA_IDENTIFY_PACKET); + } else { + _write(CS0_COMMAND, ATA_IDENTIFY); + } - if (_readPIO(&block, sizeof(IdentifyBlock), _DETECT_DRIVE_TIMEOUT)) - return NO_DRIVE; - if (!block.validateChecksum()) - return CHECKSUM_MISMATCH; + if (_waitForDRQ(_DETECT_TIMEOUT)) + return NO_DRIVE; + + _readPIO(&block, sizeof(IdentifyBlock)); + + if (!block.validateChecksum()) + return CHECKSUM_MISMATCH; + + // Parse the identification block. + if (flags & DEVICE_ATAPI) { + LOG("ATAPI drive found"); if ( (block.deviceFlags & IDENTIFY_DEV_ATAPI_TYPE_BITMASK) @@ -463,12 +617,7 @@ DeviceError Device::enumerate(void) { ) flags |= DEVICE_HAS_PACKET16; } else { - _write(CS0_COMMAND, ATA_IDENTIFY); - - if (_readPIO(&block, sizeof(IdentifyBlock), _DETECT_DRIVE_TIMEOUT)) - return NO_DRIVE; - if (!block.validateChecksum()) - return CHECKSUM_MISMATCH; + LOG("ATA drive found"); if (block.commandSetFlags[1] & (1 << 10)) { flags |= DEVICE_HAS_LBA48; @@ -481,116 +630,42 @@ DeviceError Device::enumerate(void) { } #ifdef ENABLE_FULL_IDE_DRIVER - _copyString(model, block.model, sizeof(block.model)); - _copyString(revision, block.revision, sizeof(block.revision)); + _copyString(model, block.model, sizeof(block.model)); + _copyString(revision, block.revision, sizeof(block.revision)); _copyString(serialNumber, block.serialNumber, sizeof(block.serialNumber)); - - LOG("drive %d: %s", (flags / DEVICE_SECONDARY) & 1, model); #endif // Find out the fastest PIO transfer mode supported and enable it. int mode = block.getHighestPIOMode(); _write(CS0_FEATURES, FEATURE_TRANSFER_MODE); - _write(CS0_COUNT, (1 << 3) | mode); + _write(CS0_COUNT, TRANSFER_MODE_PIO | mode); _write(CS0_COMMAND, ATA_SET_FEATURES); - error = _waitForStatus(CS0_STATUS_BSY, 0); + error = _waitForIdle(); if (error) return error; - LOG("done, stat=0x%02x, mode=PIO%d", _read(CS0_STATUS), mode); + LOG("drive %d ready, mode=PIO%d", (flags / DEVICE_SECONDARY) & 1, mode); flags |= DEVICE_READY; - return NO_ERROR; + + // Make sure any pending ATAPI sense data is cleared. + return poll(); } -DeviceError Device::atapiPacket(const Packet &packet, size_t transferLength) { +DeviceError Device::poll(void) { if (!(flags & DEVICE_READY)) return NO_DRIVE; - if (!(flags & DEVICE_ATAPI)) - return UNSUPPORTED_OP; - auto error = _select(0); + if (flags & DEVICE_ATAPI) { + Packet packet; - if (error) - return atapiPoll(); - - _write(CS0_CYLINDER_L, (transferLength >> 0) & 0xff); - _write(CS0_CYLINDER_H, (transferLength >> 8) & 0xff); - _write(CS0_COMMAND, ATA_PACKET); - - error = _writePIO(&packet, (flags & DEVICE_HAS_PACKET16) ? 16 : 12); - - if (error) - return atapiPoll(); - - return _waitForStatus(CS0_STATUS_BSY, 0); -} - -DeviceError Device::atapiPoll(void) { - if (!(flags & DEVICE_READY)) - return NO_DRIVE; - if (!(flags & DEVICE_ATAPI)) - return UNSUPPORTED_OP; - - Packet packet; - SenseData data; - - packet.setRequestSense(); - - // If an error occurs, the error flag in the status register will be set but - // the drive will still accept a request sense command. - auto error = _select(0, _REQUEST_SENSE_TIMEOUT, true); - - if (error) - return error; - - _write(CS0_CYLINDER_L, (sizeof(data) >> 0) & 0xff); - _write(CS0_CYLINDER_H, (sizeof(data) >> 8) & 0xff); - _write(CS0_COMMAND, ATA_PACKET); - - error = _writePIO( - &packet, (flags & DEVICE_HAS_PACKET16) ? 16 : 12, _REQUEST_SENSE_TIMEOUT - ); - - //if (!error) - //error = _waitForStatus(CS0_STATUS_BSY, 0, _REQUEST_SENSE_TIMEOUT); - if (!error) - error = _readPIO(&data, sizeof(data), _REQUEST_SENSE_TIMEOUT); - - int senseKey; - - if (error) { - // If the request sense command fails, fall back to reading the sense - // key from the IDE error register. - senseKey = (_read(CS0_ERROR) >> 4) & 15; - LOG("request sense failed"); + packet.setTestUnitReady(); + return _atapiPacket(packet); } else { - senseKey = data.senseKey & 15; - LOG( - "key=0x%02x, asc=0x%02x, ascq=0x%02x", data.senseKey, data.asc, - data.ascQualifier - ); - } - - LOG("sense key: %s (%d)", _SENSE_KEY_NAMES[senseKey], senseKey); - - switch (senseKey) { - case SENSE_KEY_NO_SENSE: - case SENSE_KEY_RECOVERED_ERROR: - return NO_ERROR; - - case SENSE_KEY_NOT_READY: - case SENSE_KEY_MEDIUM_ERROR: - case SENSE_KEY_DATA_PROTECT: - return DISC_ERROR; - - case SENSE_KEY_UNIT_ATTENTION: - return DISC_CHANGED; - - default: - return DRIVE_ERROR; + _select(CS0_DEVICE_SEL_LBA); + return _waitForIdle(true); } } @@ -605,7 +680,7 @@ DeviceError Device::readData(void *data, uint64_t lba, size_t count) { reinterpret_cast(data), static_cast(lba), count ); else - return _ideReadWrite( + return _ataTransfer( reinterpret_cast(data), lba, count, false ); } @@ -618,51 +693,60 @@ DeviceError Device::writeData(const void *data, uint64_t lba, size_t count) { if (flags & (DEVICE_READ_ONLY | DEVICE_ATAPI)) return UNSUPPORTED_OP; - return _ideReadWrite(reinterpret_cast(data), lba, count, true); + return _ataTransfer(reinterpret_cast(data), lba, count, true); } DeviceError Device::goIdle(bool standby) { if (!(flags & DEVICE_READY)) return NO_DRIVE; + if (flags & DEVICE_ATAPI) + return startStopUnit(START_STOP_MODE_STOP_DISC); - if (flags & DEVICE_ATAPI) { - Packet packet; + _select(CS0_DEVICE_SEL_LBA); - packet.setStartStopUnit(START_STOP_MODE_STOP_DISC); - return atapiPacket(packet); - } else { - auto error = _select(CS0_DEVICE_SEL_LBA); + auto error = _waitForIdle(true); - if (error) - return error; + if (error) + return error; - if (standby) - _write(CS0_COMMAND, ATA_STANDBY_IMMEDIATE); - else - _write(CS0_COMMAND, ATA_IDLE_IMMEDIATE); + _write(CS0_COMMAND, standby ? ATA_STANDBY_IMMEDIATE : ATA_IDLE_IMMEDIATE); + return _waitForIdle(); +} - return _waitForStatus(CS0_STATUS_BSY, 0); - } +DeviceError Device::startStopUnit(ATAPIStartStopMode mode) { + if (!(flags & DEVICE_READY)) + return NO_DRIVE; + if (!(flags & DEVICE_ATAPI)) + return UNSUPPORTED_OP; + + Packet packet; + + packet.setStartStopUnit(mode); + return _atapiPacket(packet); } DeviceError Device::flushCache(void) { if (!(flags & DEVICE_READY)) return NO_DRIVE; if (!(flags & DEVICE_HAS_FLUSH)) +#if 0 + return UNSUPPORTED_OP; +#else return NO_ERROR; - //return UNSUPPORTED_OP; +#endif - auto error = _select(CS0_DEVICE_SEL_LBA); + _select(CS0_DEVICE_SEL_LBA); + + auto error = _waitForIdle(true); if (error) return error; - if (flags & DEVICE_HAS_LBA48) - _write(CS0_COMMAND, ATA_FLUSH_CACHE_EXT); - else - _write(CS0_COMMAND, ATA_FLUSH_CACHE); - - return _waitForStatus(CS0_STATUS_DRDY | CS0_STATUS_BSY, CS0_STATUS_DRDY); + _write( + CS0_COMMAND, + (flags & DEVICE_HAS_LBA48) ? ATA_FLUSH_CACHE_EXT : ATA_FLUSH_CACHE + ); + return _waitForIdle(); } } diff --git a/src/common/ide.hpp b/src/common/ide.hpp index 15559cf..29130d3 100644 --- a/src/common/ide.hpp +++ b/src/common/ide.hpp @@ -3,169 +3,15 @@ #include #include +#include "common/idedefs.hpp" #include "common/util.hpp" #include "ps1/registers573.h" namespace ide { -/* Register definitions */ - -enum CS0Register { - CS0_DATA = 0, - CS0_ERROR = 1, - CS0_FEATURES = 1, - CS0_COUNT = 2, - CS0_SECTOR = 3, - CS0_CYLINDER_L = 4, - CS0_CYLINDER_H = 5, - CS0_DEVICE_SEL = 6, - CS0_STATUS = 7, - CS0_COMMAND = 7 -}; - -enum CS1Register { - CS1_ALT_STATUS = 6, - CS1_DEVICE_CTRL = 6 -}; - -enum CS0StatusFlag : uint8_t { - CS0_STATUS_ERR = 1 << 0, // Error - CS0_STATUS_DRQ = 1 << 3, // Data request - CS0_STATUS_DSC = 1 << 4, // Device seek complete (ATA) - CS0_STATUS_SERV = 1 << 4, // Service (ATAPI) - CS0_STATUS_DF = 1 << 5, // Device fault - CS0_STATUS_DRDY = 1 << 6, // Device ready - CS0_STATUS_BSY = 1 << 7 // Busy -}; - -enum CS0DeviceSelectFlag : uint8_t { - CS0_DEVICE_SEL_PRIMARY = 10 << 4, - CS0_DEVICE_SEL_SECONDARY = 11 << 4, - CS0_DEVICE_SEL_LBA = 1 << 6 -}; - -enum CS1DeviceControlFlag : uint8_t { - CS1_DEVICE_CTRL_IEN = 1 << 1, // Interrupt enable - CS1_DEVICE_CTRL_SRST = 1 << 2, // Software reset - CS1_DEVICE_CTRL_HOB = 1 << 7 // High-order bit (LBA48) -}; - -/* ATA protocol definitions */ - -static constexpr size_t ATA_SECTOR_SIZE = 512; - -enum ATACommand : uint8_t { - ATA_NOP = 0x00, - ATA_DEVICE_RESET = 0x08, - ATA_READ_SECTORS = 0x20, - ATA_READ_SECTORS_EXT = 0x24, - ATA_READ_DMA_EXT = 0x25, - ATA_READ_DMA_QUEUED_EXT = 0x26, - ATA_WRITE_SECTORS = 0x30, - ATA_WRITE_SECTORS_EXT = 0x34, - ATA_WRITE_DMA_EXT = 0x35, - ATA_WRITE_DMA_QUEUED_EXT = 0x36, - ATA_SEEK = 0x70, - ATA_EXECUTE_DIAGNOSTIC = 0x90, - ATA_PACKET = 0xa0, - ATA_IDENTIFY_PACKET = 0xa1, - ATA_SERVICE = 0xa2, - ATA_DEVICE_CONFIG = 0xb1, - ATA_ERASE_SECTORS = 0xc0, - ATA_READ_DMA_QUEUED = 0xc7, - ATA_READ_DMA = 0xc8, - ATA_WRITE_DMA = 0xca, - ATA_WRITE_DMA_QUEUED = 0xcc, - ATA_STANDBY_IMMEDIATE = 0xe0, - ATA_IDLE_IMMEDIATE = 0xe1, - ATA_STANDBY = 0xe2, - ATA_IDLE = 0xe3, - ATA_CHECK_POWER_MODE = 0xe5, - ATA_SLEEP = 0xe6, - ATA_FLUSH_CACHE = 0xe7, - ATA_FLUSH_CACHE_EXT = 0xea, - ATA_IDENTIFY = 0xec, - ATA_SET_FEATURES = 0xef -}; - -enum ATAFeature : uint8_t { - FEATURE_8BIT_DATA = 0x01, - FEATURE_WRITE_CACHE = 0x02, - FEATURE_TRANSFER_MODE = 0x03, - FEATURE_APM = 0x05, - FEATURE_AAM = 0x42, - FEATURE_RELEASE_IRQ = 0x5d, - FEATURE_SERVICE_IRQ = 0x5e, - FEATURE_DISABLE = 0x80 -}; - -/* ATAPI protocol definitions */ - +static constexpr size_t ATA_SECTOR_SIZE = 512; static constexpr size_t ATAPI_SECTOR_SIZE = 2048; -enum ATAPICommand : uint8_t { - ATAPI_TEST_UNIT_READY = 0x00, - ATAPI_REQUEST_SENSE = 0x03, - ATAPI_INQUIRY = 0x12, - ATAPI_START_STOP_UNIT = 0x1b, - ATAPI_PREVENT_REMOVAL = 0x1e, - ATAPI_READ_CAPACITY = 0x25, - ATAPI_READ10 = 0x28, - ATAPI_SEEK = 0x2b, - ATAPI_READ_SUBCHANNEL = 0x42, - ATAPI_READ_TOC = 0x43, - ATAPI_READ_HEADER = 0x44, - ATAPI_PLAY_AUDIO = 0x45, - ATAPI_PLAY_AUDIO_MSF = 0x47, - ATAPI_PAUSE_RESUME = 0x4b, - ATAPI_STOP = 0x4e, - ATAPI_MODE_SELECT = 0x55, - ATAPI_MODE_SENSE = 0x5a, - ATAPI_LOAD_UNLOAD_CD = 0xa6, - ATAPI_READ12 = 0xa8, - ATAPI_READ_CD_MSF = 0xb9, - ATAPI_SCAN = 0xba, - ATAPI_SET_CD_SPEED = 0xbb, - ATAPI_MECHANISM_STATUS = 0xbd, - ATAPI_READ_CD = 0xbe -}; - -enum ATAPISenseKey : uint8_t { - SENSE_KEY_NO_SENSE = 0x0, - SENSE_KEY_RECOVERED_ERROR = 0x1, - SENSE_KEY_NOT_READY = 0x2, - SENSE_KEY_MEDIUM_ERROR = 0x3, - SENSE_KEY_HARDWARE_ERROR = 0x4, - SENSE_KEY_ILLEGAL_REQUEST = 0x5, - SENSE_KEY_UNIT_ATTENTION = 0x6, - SENSE_KEY_DATA_PROTECT = 0x7, - SENSE_KEY_BLANK_CHECK = 0x8, - SENSE_KEY_ABORTED_COMMAND = 0xb, - SENSE_KEY_MISCOMPARE = 0xe -}; - -enum ATAPIStartStopMode : uint8_t { - START_STOP_MODE_STOP_DISC = 0, - START_STOP_MODE_START_DISC = 1, - START_STOP_MODE_OPEN_TRAY = 2, - START_STOP_MODE_CLOSE_TRAY = 3 -}; - -enum ATAPIModePage : uint8_t { - MODE_PAGE_ERROR_RECOVERY = 0x01, - MODE_PAGE_CDROM = 0x0d, - MODE_PAGE_CDROM_AUDIO = 0x0e, - MODE_PAGE_CDROM_CAPABILITIES = 0x2a, - MODE_PAGE_ALL = 0x3f -}; - -enum ATAPIModePageType : uint8_t { - MODE_PAGE_TYPE_CURRENT = 0, - MODE_PAGE_TYPE_CHANGEABLE = 1, - MODE_PAGE_TYPE_DEFAULT = 2, - MODE_PAGE_TYPE_SAVED = 3 -}; - /* Identification blocks */ enum IdentifyDeviceFlag : uint16_t { @@ -275,6 +121,9 @@ public: | (info[2] << 8) | (info[3] << 0); } + inline uint16_t getPackedASC(void) const { + return asc | (ascQualifier << 8); + } }; class Packet { @@ -283,12 +132,34 @@ public: uint8_t param[11]; uint8_t _reserved[4]; + inline void setTestUnitReady(void) { + util::clear(*this); + + //command = ATAPI_TEST_UNIT_READY; + } + inline void setRequestSense(uint8_t additionalLength = 0) { + util::clear(*this); + + command = ATAPI_REQUEST_SENSE; + param[3] = sizeof(SenseData) + additionalLength; + } inline void setStartStopUnit(ATAPIStartStopMode mode) { util::clear(*this); command = ATAPI_START_STOP_UNIT; param[3] = mode; } + inline void setModeSense( + ATAPIModePage page, size_t length, + ATAPIModePageType type = MODE_PAGE_TYPE_CURRENT + ) { + util::clear(*this); + + command = ATAPI_MODE_SENSE; + param[1] = (page & 0x3f) | (type << 6); + param[6] = (length >> 8) & 0xff; + param[7] = (length >> 0) & 0xff; + } inline void setRead(uint32_t lba, size_t count) { util::clear(*this); @@ -309,23 +180,6 @@ public: param[1] = (value >> 8) & 0xff; param[2] = (value >> 0) & 0xff; } - inline void setRequestSense(uint8_t additionalLength = 0) { - util::clear(*this); - - command = ATAPI_REQUEST_SENSE; - param[3] = sizeof(SenseData) + additionalLength; - } - inline void setModeSense( - ATAPIModePage page, size_t length, - ATAPIModePageType type = MODE_PAGE_TYPE_CURRENT - ) { - util::clear(*this); - - command = ATAPI_MODE_SENSE; - param[1] = (page & 0x3f) | (type << 6); - param[6] = (length >> 8) & 0xff; - param[7] = (length >> 0) & 0xff; - } }; /* Device class */ @@ -334,12 +188,12 @@ enum DeviceError { NO_ERROR = 0, UNSUPPORTED_OP = 1, NO_DRIVE = 2, - STATUS_TIMEOUT = 3, - DRIVE_ERROR = 4, - DISC_ERROR = 5, - DISC_CHANGED = 6, - INCOMPLETE_DATA = 7, - CHECKSUM_MISMATCH = 8 + NOT_YET_READY = 3, + STATUS_TIMEOUT = 4, + CHECKSUM_MISMATCH = 5, + DRIVE_ERROR = 6, + DISC_ERROR = 7, + DISC_CHANGED = 8 }; enum DeviceFlag { @@ -357,46 +211,53 @@ enum DeviceFlag { class Device { private: - inline uint8_t _read(CS0Register reg) { + inline uint8_t _read(CS0Register reg) const { return uint8_t(SYS573_IDE_CS0_BASE[reg] & 0xff); } - inline void _write(CS0Register reg, uint8_t value) { + inline void _write(CS0Register reg, uint8_t value) const { SYS573_IDE_CS0_BASE[reg] = value; } - inline uint8_t _read(CS1Register reg) { + inline uint8_t _read(CS1Register reg) const { return uint8_t(SYS573_IDE_CS1_BASE[reg] & 0xff); } - inline void _write(CS1Register reg, uint8_t value) { + inline void _write(CS1Register reg, uint8_t value) const { SYS573_IDE_CS1_BASE[reg] = value; } - DeviceError _waitForStatus( - uint8_t mask, uint8_t value, int timeout = 0, bool ignoreErrors = false - ); - DeviceError _select( - uint8_t devSelFlags, int timeout = 0, bool ignoreErrors = false - ); - DeviceError _setLBA(uint64_t lba, size_t count, int timeout = 0); - DeviceError _detectDrive(void); + inline void _select(uint8_t selFlags) const { + if (flags & DEVICE_SECONDARY) + _write(CS0_DEVICE_SEL, selFlags | CS0_DEVICE_SEL_SECONDARY); + else + _write(CS0_DEVICE_SEL, selFlags | CS0_DEVICE_SEL_PRIMARY); + } + inline void _setCylinder(uint16_t value) const { + _write(CS0_CYLINDER_L, (value >> 0) & 0xff); + _write(CS0_CYLINDER_H, (value >> 8) & 0xff); + } + inline uint16_t _getCylinder(void) const { + return _read(CS0_CYLINDER_L) | (_read(CS0_CYLINDER_H) << 8); + } - DeviceError _readPIO( - void *data, size_t length, int timeout = 0, bool ignoreErrors = false - ); - DeviceError _writePIO( - const void *data, size_t length, int timeout = 0, - bool ignoreErrors = false - ); - DeviceError _readDMA( - void *data, size_t length, int timeout = 0, bool ignoreErrors = false - ); - DeviceError _writeDMA( - const void *data, size_t length, int timeout = 0, - bool ignoreErrors = false - ); + void _readPIO(void *data, size_t length) const; + void _writePIO(const void *data, size_t length) const; + bool _readDMA(void *data, size_t length) const; + bool _writeDMA(const void *data, size_t length) const; - DeviceError _ideReadWrite( + DeviceError _waitForIdle( + bool drdy = false, int timeout = 0, bool ignoreError = false + ); + DeviceError _waitForDRQ(int timeout = 0, bool ignoreError = false); + void _handleError(void); + void _handleTimeout(void); + DeviceError _resetDrive(void); + + DeviceError _ataSetLBA(uint64_t lba, size_t count, int timeout = 0); + DeviceError _ataTransfer( uintptr_t ptr, uint64_t lba, size_t count, bool write ); + + DeviceError _atapiRequestSense(void); + DeviceError _atapiPacket(const Packet &packet, size_t dataLength = 0); DeviceError _atapiRead(uintptr_t ptr, uint32_t lba, size_t count); public: @@ -407,13 +268,16 @@ public: #endif uint64_t capacity; - inline Device(uint32_t flags) - : flags(flags), capacity(0) {} + uint8_t lastStatusReg, lastErrorReg; + SenseData lastSenseData; inline size_t getSectorSize(void) const { return (flags & DEVICE_ATAPI) ? ATAPI_SECTOR_SIZE : ATA_SECTOR_SIZE; } - inline bool isPointerAligned(const void *ptr) { + inline size_t getPacketSize(void) const { + return (flags & DEVICE_HAS_PACKET16) ? 16 : 12; + } + inline bool isPointerAligned(const void *ptr) const { // DMA transfers require 4-byte alignment, while PIO transfers require // 2-byte alignment. #if 0 @@ -423,13 +287,14 @@ public: #endif } + Device(uint32_t flags); DeviceError enumerate(void); - DeviceError atapiPacket(const Packet &packet, size_t transferLength = 0); - DeviceError atapiPoll(void); + DeviceError poll(void); DeviceError readData(void *data, uint64_t lba, size_t count); DeviceError writeData(const void *data, uint64_t lba, size_t count); DeviceError goIdle(bool standby = false); + DeviceError startStopUnit(ATAPIStartStopMode mode); DeviceError flushCache(void); }; diff --git a/src/common/idedefs.hpp b/src/common/idedefs.hpp new file mode 100644 index 0000000..b2f5e22 --- /dev/null +++ b/src/common/idedefs.hpp @@ -0,0 +1,238 @@ + +#pragma once + +#include + +namespace ide { + +/* Register definitions */ + +enum CS0Register { + CS0_DATA = 0, + CS0_ERROR = 1, + CS0_FEATURES = 1, + CS0_COUNT = 2, + CS0_SECTOR = 3, + CS0_CYLINDER_L = 4, + CS0_CYLINDER_H = 5, + CS0_DEVICE_SEL = 6, + CS0_STATUS = 7, + CS0_COMMAND = 7 +}; + +enum CS1Register { + CS1_ALT_STATUS = 6, + CS1_DEVICE_CTRL = 6 +}; + +enum CS0StatusFlag : uint8_t { + CS0_STATUS_ERR = 1 << 0, // Error (ATA) + CS0_STATUS_CHK = 1 << 0, // Check condition (ATAPI) + CS0_STATUS_DRQ = 1 << 3, // Data request + CS0_STATUS_DSC = 1 << 4, // Device seek complete (ATA) + CS0_STATUS_SERV = 1 << 4, // Service (ATAPI) + CS0_STATUS_DF = 1 << 5, // Device fault + CS0_STATUS_DRDY = 1 << 6, // Device ready + CS0_STATUS_BSY = 1 << 7 // Busy +}; + +enum CS0DeviceSelectFlag : uint8_t { + CS0_DEVICE_SEL_PRIMARY = 10 << 4, + CS0_DEVICE_SEL_SECONDARY = 11 << 4, + CS0_DEVICE_SEL_LBA = 1 << 6 +}; + +enum CS1DeviceControlFlag : uint8_t { + CS1_DEVICE_CTRL_IEN = 1 << 1, // Interrupt enable + CS1_DEVICE_CTRL_SRST = 1 << 2, // Software reset + CS1_DEVICE_CTRL_HOB = 1 << 7 // High-order bit (LBA48) +}; + +/* ATA command definitions */ + +enum ATACommand : uint8_t { + ATA_NOP = 0x00, + ATA_DEVICE_RESET = 0x08, + ATA_READ_SECTORS = 0x20, + ATA_READ_SECTORS_EXT = 0x24, + ATA_READ_DMA_EXT = 0x25, + ATA_READ_DMA_QUEUED_EXT = 0x26, + ATA_WRITE_SECTORS = 0x30, + ATA_WRITE_SECTORS_EXT = 0x34, + ATA_WRITE_DMA_EXT = 0x35, + ATA_WRITE_DMA_QUEUED_EXT = 0x36, + ATA_SEEK = 0x70, + ATA_EXECUTE_DIAGNOSTIC = 0x90, + ATA_PACKET = 0xa0, + ATA_IDENTIFY_PACKET = 0xa1, + ATA_SERVICE = 0xa2, + ATA_DEVICE_CONFIG = 0xb1, + ATA_ERASE_SECTORS = 0xc0, + ATA_READ_DMA_QUEUED = 0xc7, + ATA_READ_DMA = 0xc8, + ATA_WRITE_DMA = 0xca, + ATA_WRITE_DMA_QUEUED = 0xcc, + ATA_STANDBY_IMMEDIATE = 0xe0, + ATA_IDLE_IMMEDIATE = 0xe1, + ATA_STANDBY = 0xe2, + ATA_IDLE = 0xe3, + ATA_CHECK_POWER_MODE = 0xe5, + ATA_SLEEP = 0xe6, + ATA_FLUSH_CACHE = 0xe7, + ATA_FLUSH_CACHE_EXT = 0xea, + ATA_IDENTIFY = 0xec, + ATA_SET_FEATURES = 0xef +}; + +enum ATAFeature : uint8_t { + FEATURE_8BIT_DATA = 0x01, + FEATURE_WRITE_CACHE = 0x02, + FEATURE_TRANSFER_MODE = 0x03, + FEATURE_APM = 0x05, + FEATURE_AAM = 0x42, + FEATURE_RELEASE_IRQ = 0x5d, + FEATURE_SERVICE_IRQ = 0x5e, + FEATURE_DISABLE = 0x80 +}; + +enum ATATransferModeFlag : uint8_t { + TRANSFER_MODE_PIO_DEFAULT = 0 << 3, + TRANSFER_MODE_PIO = 1 << 3, + TRANSFER_MODE_DMA = 1 << 5, + TRANSFER_MODE_UDMA = 1 << 6 +}; + +/* ATAPI command definitions */ + +enum ATAPICommand : uint8_t { + ATAPI_TEST_UNIT_READY = 0x00, + ATAPI_REQUEST_SENSE = 0x03, + ATAPI_INQUIRY = 0x12, + ATAPI_START_STOP_UNIT = 0x1b, + ATAPI_PREVENT_REMOVAL = 0x1e, + ATAPI_READ_CAPACITY = 0x25, + ATAPI_READ10 = 0x28, + ATAPI_SEEK = 0x2b, + ATAPI_READ_SUBCHANNEL = 0x42, + ATAPI_READ_TOC = 0x43, + ATAPI_READ_HEADER = 0x44, + ATAPI_PLAY_AUDIO = 0x45, + ATAPI_PLAY_AUDIO_MSF = 0x47, + ATAPI_PAUSE_RESUME = 0x4b, + ATAPI_STOP = 0x4e, + ATAPI_MODE_SELECT = 0x55, + ATAPI_MODE_SENSE = 0x5a, + ATAPI_LOAD_UNLOAD_CD = 0xa6, + ATAPI_READ12 = 0xa8, + ATAPI_READ_CD_MSF = 0xb9, + ATAPI_SCAN = 0xba, + ATAPI_SET_CD_SPEED = 0xbb, + ATAPI_MECHANISM_STATUS = 0xbd, + ATAPI_READ_CD = 0xbe +}; + +enum ATAPIModePage : uint8_t { + MODE_PAGE_ERROR_RECOVERY = 0x01, + MODE_PAGE_CDROM = 0x0d, + MODE_PAGE_CDROM_AUDIO = 0x0e, + MODE_PAGE_CDROM_CAPABILITIES = 0x2a, + MODE_PAGE_ALL = 0x3f +}; + +enum ATAPIModePageType : uint8_t { + MODE_PAGE_TYPE_CURRENT = 0, + MODE_PAGE_TYPE_CHANGEABLE = 1, + MODE_PAGE_TYPE_DEFAULT = 2, + MODE_PAGE_TYPE_SAVED = 3 +}; + +enum ATAPIStartStopMode : uint8_t { + START_STOP_MODE_STOP_DISC = 0, + START_STOP_MODE_START_DISC = 1, + START_STOP_MODE_OPEN_TRAY = 2, + START_STOP_MODE_CLOSE_TRAY = 3 +}; + +/* ATAPI sense keys */ + +enum ATAPISenseKey : uint8_t { + SENSE_KEY_NO_SENSE = 0x0, + SENSE_KEY_RECOVERED_ERROR = 0x1, + SENSE_KEY_NOT_READY = 0x2, + SENSE_KEY_MEDIUM_ERROR = 0x3, + SENSE_KEY_HARDWARE_ERROR = 0x4, + SENSE_KEY_ILLEGAL_REQUEST = 0x5, + SENSE_KEY_UNIT_ATTENTION = 0x6, + SENSE_KEY_DATA_PROTECT = 0x7, + SENSE_KEY_BLANK_CHECK = 0x8, + SENSE_KEY_ABORTED_COMMAND = 0xb, + SENSE_KEY_MISCOMPARE = 0xe +}; + +enum ATAPISenseQualifier : uint16_t { + ASC_NO_SENSE_INFO = 0x00 | (0x00 << 8), // "NO ADDITIONAL SENSE INFORMATION" + ASC_PLAY_IN_PROGRESS = 0x00 | (0x11 << 8), // "PLAY OPERATION IN PROGRESS" + ASC_PLAY_PAUSED = 0x00 | (0x12 << 8), // "PLAY OPERATION PAUSED" + ASC_PLAY_COMPLETED = 0x00 | (0x13 << 8), // "PLAY OPERATION SUCCESSFULLY COMPLETED" + ASC_PLAY_ERROR = 0x00 | (0x14 << 8), // "PLAY OPERATION STOPPED DUE TO ERROR" + ASC_NO_AUDIO_STATUS = 0x00 | (0x15 << 8), // "NO CURRENT AUDIO STATUS TO RETURN" + ASC_MECHANICAL_ERROR = 0x01 | (0x00 << 8), // "MECHANICAL POSITIONING OR CHANGER ERROR" + ASC_NO_SEEK_COMPLETE = 0x02 | (0x00 << 8), // "NO SEEK COMPLETE" + ASC_NOT_READY = 0x04 | (0x00 << 8), // "LOGICAL DRIVE NOT READY - CAUSE NOT REPORTABLE" + ASC_NOT_READY_IN_PROGRESS = 0x04 | (0x01 << 8), // "LOGICAL DRIVE NOT READY - IN PROGRESS OF BECOMING READY" + ASC_NOT_READY_INIT_REQ = 0x04 | (0x02 << 8), // "LOGICAL DRIVE NOT READY - INITIALIZING COMMAND REQUIRED" + ASC_NOT_READY_MANUAL_REQ = 0x04 | (0x03 << 8), // "LOGICAL DRIVE NOT READY - MANUAL INTERVENTION REQUIRED" + ASC_LOAD_EJECT_FAILED = 0x05 | (0x01 << 8), // "MEDIA LOAD - EJECT FAILED" + ASC_NO_REFERENCE_POSITION = 0x06 | (0x00 << 8), // "NO REFERENCE POSITION FOUND" + ASC_TRACK_FOLLOW_ERROR = 0x09 | (0x00 << 8), // "TRACK FOLLOWING ERROR" + ASC_TRACK_SERVO_FAILURE = 0x09 | (0x01 << 8), // "TRACKING SERVO FAILURE" + ASC_FOCUS_SERVO_FAILURE = 0x09 | (0x02 << 8), // "FOCUS SERVO FAILURE" + ASC_SPINDLE_SERVO_FAILURE = 0x09 | (0x03 << 8), // "SPINDLE SERVO FAILURE" + ASC_UNRECOVERED_READ_ERROR = 0x11 | (0x00 << 8), // "UNRECOVERED READ ERROR" + ASC_CIRC_UNRECOVERED_ERROR = 0x11 | (0x06 << 8), // "CIRC UNRECOVERED ERROR" + ASC_POSITIONING_ERROR = 0x15 | (0x00 << 8), // "RANDOM POSITIONING ERROR" + ASC_MECHANICAL_ERROR_2 = 0x15 | (0x01 << 8), // "MECHANICAL POSITIONING OR CHANGER ERROR" + ASC_POSITIONING_ERROR_2 = 0x15 | (0x02 << 8), // "POSITIONING ERROR DETECTED BY READ OF MEDIUM" + ASC_REC_DATA_NO_ECC = 0x17 | (0x00 << 8), // "RECOVERED DATA WITH NO ERROR CORRECTION APPLIED" + ASC_REC_DATA_RETRIES = 0x17 | (0x01 << 8), // "RECOVERED DATA WITH RETRIES" + ASC_REC_DATA_POS_OFFSET = 0x17 | (0x02 << 8), // "RECOVERED DATA WITH POSITIVE HEAD OFFSET" + ASC_REC_DATA_NEG_OFFSET = 0x17 | (0x03 << 8), // "RECOVERED DATA WITH NEGATIVE HEAD OFFSET" + ASC_REC_DATA_RETRIES_CIRC = 0x17 | (0x04 << 8), // "RECOVERED DATA WITH RETRIES AND/OR CIRC APPLIED" + ASC_REC_DATA_PREV_SECTOR = 0x17 | (0x05 << 8), // "RECOVERED DATA USING PREVIOUS SECTOR ID" + ASC_REC_DATA_ECC = 0x18 | (0x00 << 8), // "RECOVERED DATA WITH ERROR CORRECTION APPLIED" + ASC_REC_DATA_ECC_RETRIES = 0x18 | (0x01 << 8), // "RECOVERED DATA WITH ERROR CORRECTION & RETRIES APPLIED" + ASC_REC_DATA_REALLOCATED = 0x18 | (0x02 << 8), // "RECOVERED DATA - THE DATA WAS AUTO-REALLOCATED" + ASC_REC_DATA_CIRC = 0x18 | (0x03 << 8), // "RECOVERED DATA WITH CIRC" + ASC_REC_DATA_L_EC = 0x18 | (0x04 << 8), // "RECOVERED DATA WITH L-EC" + ASC_PARAM_LENGTH_ERROR = 0x1a | (0x00 << 8), // "PARAMETER LIST LENGTH ERROR" + ASC_INVALID_COMMAND = 0x20 | (0x00 << 8), // "INVALID COMMAND OPERATION CODE" + ASC_LBA_OUT_OF_RANGE = 0x21 | (0x00 << 8), // "LOGICAL BLOCK ADDRESS OUT OF RANGE" + ASC_INVALID_PACKET_FIELD = 0x24 | (0x00 << 8), // "INVALID FIELD IN COMMAND PACKET" + ASC_INVALID_PARAM_FIELD = 0x26 | (0x00 << 8), // "INVALID FIELD IN PARAMETER LIST" + ASC_PARAM_NOT_SUPPORTED = 0x26 | (0x01 << 8), // "PARAMETER NOT SUPPORTED" + ASC_PARAM_VALUE_INVALID = 0x26 | (0x02 << 8), // "PARAMETER VALUE INVALID" + ASC_NOT_READY_TO_READY = 0x28 | (0x00 << 8), // "NOT READY TO READY TRANSITION, MEDIUM MAY HAVE CHANGED" + ASC_RESET_OCCURRED = 0x29 | (0x00 << 8), // "POWER ON, RESET OR BUS DEVICE RESET OCCURRED" + ASC_PARAMS_CHANGED = 0x2a | (0x00 << 8), // "PARAMETERS CHANGED" + ASC_MODE_PARAMS_CHANGED = 0x2a | (0x01 << 8), // "MODE PARAMETERS CHANGED" + ASC_INCOMPATIBLE_MEDIUM = 0x30 | (0x00 << 8), // "INCOMPATIBLE MEDIUM INSTALLED" + ASC_UNKNOWN_FORMAT = 0x30 | (0x01 << 8), // "CANNOT READ MEDIUM - UNKNOWN FORMAT" + ASC_INCOMPATIBLE_FORMAT = 0x30 | (0x02 << 8), // "CANNOT READ MEDIUM - INCOMPATIBLE FORMAT" + ASC_SAVING_NOT_SUPPORTED = 0x39 | (0x00 << 8), // "SAVING PARAMETERS NOT SUPPORTED" + ASC_MEDIUM_NOT_PRESENT = 0x3a | (0x00 << 8), // "MEDIUM NOT PRESENT" + ASC_CONDITIONS_CHANGED = 0x3f | (0x00 << 8), // "ATAPI CD-ROM DRIVE OPERATING CONDITIONS HAVE CHANGED" + ASC_MICROCODE_CHANGED = 0x3f | (0x01 << 8), // "MICROCODE HAS BEEN CHANGED" + ASC_INTERNAL_DRIVE_FAILURE = 0x44 | (0x00 << 8), // "INTERNAL ATAPI CD-ROM DRIVE FAILURE" + ASC_OVERLAP_ATTEMPTED = 0x4e | (0x00 << 8), // "OVERLAPPED COMMANDS ATTEMPTED" + ASC_LOAD_EJECT_FAILED_2 = 0x53 | (0x00 << 8), // "MEDIA LOAD OR EJECT FAILED" + ASC_REMOVAL_PREVENTED = 0x53 | (0x02 << 8), // "MEDIUM REMOVAL PREVENTED" + ASC_UNABLE_TO_RECOVER_TOC = 0x57 | (0x00 << 8), // "UNABLE TO RECOVER TABLE OF CONTENTS" + ASC_OPERATOR_REQUEST = 0x5a | (0x00 << 8), // "OPERATOR REQUEST OR STATE CHANGE INPUT (UNSPECIFIED)" + ASC_REMOVAL_REQUEST = 0x5a | (0x01 << 8), // "OPERATOR MEDIUM REMOVAL REQUEST" + ASC_END_OF_USER_AREA = 0x63 | (0x00 << 8), // "END OF USER AREA ENCOUNTERED ON THIS TRACK" + ASC_ILLEGAL_TRACK_MODE = 0x64 | (0x00 << 8), // "ILLEGAL MODE FOR THIS TRACK" + ASC_PLAY_ABORTED = 0xb9 | (0x00 << 8), // "PLAY OPERATION ABORTED" + ASC_LOSS_OF_STREAMING = 0xbf | (0x00 << 8) // "LOSS OF STREAMING" +}; + +} diff --git a/src/common/rom.cpp b/src/common/rom.cpp index 321efdd..3d82183 100644 --- a/src/common/rom.cpp +++ b/src/common/rom.cpp @@ -3,6 +3,7 @@ #include #include "common/io.hpp" #include "common/rom.hpp" +#include "common/romdrivers.hpp" #include "common/util.hpp" #include "ps1/registers.h" #include "ps1/registers573.h" @@ -165,29 +166,6 @@ uint32_t FlashRegion::zipCRC32( /* Flash-specific functions */ -enum JEDECCommand : uint16_t { - _JEDEC_RESET = 0xf0f0, - _JEDEC_HANDSHAKE1 = 0xaaaa, - _JEDEC_HANDSHAKE2 = 0x5555, - _JEDEC_GET_ID = 0x9090, - _JEDEC_WRITE_BYTE = 0xa0a0, - _JEDEC_ERASE_HANDSHAKE = 0x8080, - _JEDEC_ERASE_CHIP = 0x1010, - _JEDEC_ERASE_SECTOR = 0x3030 -}; - -enum IntelCommand : uint16_t { - _INTEL_RESET = 0xffff, - _INTEL_GET_ID = 0x9090, - _INTEL_WRITE_BYTE = 0x4040, - _INTEL_ERASE_SECTOR1 = 0x2020, - _INTEL_ERASE_SECTOR2 = 0xd0d0, - _INTEL_GET_STATUS = 0x7070, - _INTEL_CLEAR_STATUS = 0x5050, - _INTEL_SUSPEND = 0xb0b0, - _INTEL_RESUME = 0xd0d0 -}; - enum FlashIdentifier : uint16_t { _ID_AM29F016 = 0x01 | (0xad << 8), _ID_AM29F040 = 0x01 | (0xa4 << 8), @@ -234,16 +212,16 @@ uint32_t FlashRegion::getJEDECID(void) const { auto _ptr = reinterpret_cast(ptr); - _ptr[0x000] = _JEDEC_RESET; - _ptr[0x000] = _INTEL_RESET; - _ptr[0x555] = _JEDEC_HANDSHAKE1; - _ptr[0x2aa] = _JEDEC_HANDSHAKE2; - _ptr[0x555] = _JEDEC_GET_ID; // Same as _INTEL_GET_ID + _ptr[0x000] = JEDEC_RESET; + _ptr[0x000] = INTEL_RESET; + _ptr[0x555] = JEDEC_HANDSHAKE1; + _ptr[0x2aa] = JEDEC_HANDSHAKE2; + _ptr[0x555] = JEDEC_GET_ID; // Same as INTEL_GET_ID uint32_t id = _ptr[0] | (_ptr[1] << 16); - _ptr[0x000] = _JEDEC_RESET; - _ptr[0x000] = _INTEL_RESET; + _ptr[0x000] = JEDEC_RESET; + _ptr[0x000] = INTEL_RESET; return id; } @@ -301,356 +279,6 @@ const FlashRegion pcmcia[2]{ { SYS573_BANK_PCMCIA2, 0x4000000, io::JAMMA_PCMCIA_CD2 } }; -/* Data common to all chip drivers */ - -static constexpr int _FLASH_WRITE_TIMEOUT = 100000; -static constexpr int _FLASH_ERASE_TIMEOUT = 20000000; - -const char *const DRIVER_ERROR_NAMES[]{ - "NO_ERROR", - "UNSUPPORTED_OP", - "CHIP_TIMEOUT", - "CHIP_ERROR", - "VERIFY_MISMATCH", - "WRITE_PROTECTED" -}; - -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 and an 8-bit bus. -static const ChipSize _STANDARD_CHIP_SIZE{ - .chipLength = 2 * 0x200000, - .eraseSectorLength = 2 * 0x10000 -}; - -static const ChipSize _ALT_CHIP_SIZE{ - .chipLength = 2 * 0x80000, - .eraseSectorLength = 2 * 0x10000 -}; - -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(_region.ptr + offset * 2); - ptr[0] = value & 0xff; - ptr[1] = value >> 8; -} - -void RTCDriver::eraseSector(uint32_t offset) { - auto ptr = reinterpret_cast(_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(_region.ptr + offset * 2); - - uint16_t actualValue = (ptr[0] & 0xff) | ((ptr[1] & 0xff) << 8); - - if (value != actualValue) { - LOG( - "mismatch @ 0x%08x, exp=0x%02x, got=0x%04x", offset, value, - actualValue - ); - return VERIFY_MISMATCH; - } - - return NO_ERROR; -} - -DriverError RTCDriver::flushErase(uint32_t offset) { - return flushWrite(offset, 0); -} - -const ChipSize &RTCDriver::getChipSize(void) const { - return _RTC_CHIP_SIZE; -} - -/* AMD AM29F016/017 (Fujitsu MBM29F016A/017A) driver */ - -enum JEDECStatusFlag : uint16_t { - _JEDEC_STATUS_ERASE_TOGGLE = 1 << 2, - _JEDEC_STATUS_ERASE_START = 1 << 3, - _JEDEC_STATUS_ERROR = 1 << 5, - _JEDEC_STATUS_TOGGLE = 1 << 6, - _JEDEC_STATUS_POLL_BIT = 1 << 7 -}; - -DriverError AM29F016Driver::_flush( - uint32_t offset, uint16_t value, int timeout -) { - volatile uint16_t *ptr = _region.getRawPtr(offset & ~1); - - int shift = (offset & 1) * 8; - uint8_t byte = (value >> shift) & 0xff; - - uint8_t status, diff; - - for (; timeout > 0; timeout--) { - status = (*ptr >> shift) & 0xff; - diff = status ^ byte; - - if (!(diff & _JEDEC_STATUS_POLL_BIT)) - return NO_ERROR; - if (status & _JEDEC_STATUS_ERROR) - break; - } - - // If the error flag was set, make sure an error actually occurred. - status = (*ptr >> shift) & 0xff; - diff = status ^ byte; - - if (!(diff & _JEDEC_STATUS_POLL_BIT)) - return NO_ERROR; - - *ptr = _JEDEC_RESET; - - if (status & _JEDEC_STATUS_ERROR) { - LOG("error @ 0x%08x, stat=0x%02x", offset, status); - return CHIP_ERROR; - } else { - LOG("timeout @ 0x%08x, stat=0x%02x", offset, status); - return CHIP_TIMEOUT; - } -} - -void AM29F016Driver::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; - ptr[0x2aa] = _JEDEC_HANDSHAKE2; - ptr[0x555] = _JEDEC_WRITE_BYTE; - ptr[offset] = value; -} - -void AM29F016Driver::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; - ptr[0x2aa] = _JEDEC_HANDSHAKE2; - ptr[0x555] = _JEDEC_ERASE_HANDSHAKE; - ptr[0x555] = _JEDEC_HANDSHAKE1; - ptr[0x2aa] = _JEDEC_HANDSHAKE2; - ptr[offset] = _JEDEC_ERASE_SECTOR; -} - -void AM29F016Driver::eraseChip(uint32_t offset) { - volatile uint16_t *ptr = _region.getRawPtr(offset, true); - - ptr[0x000] = _JEDEC_RESET; - ptr[0x555] = _JEDEC_HANDSHAKE1; - ptr[0x2aa] = _JEDEC_HANDSHAKE2; - ptr[0x555] = _JEDEC_ERASE_HANDSHAKE; - ptr[0x555] = _JEDEC_HANDSHAKE1; - ptr[0x2aa] = _JEDEC_HANDSHAKE2; - ptr[0x555] = _JEDEC_ERASE_CHIP; -} - -DriverError AM29F016Driver::flushWrite( - uint32_t offset, uint16_t value -) { - auto error = _flush(offset, value, _FLASH_WRITE_TIMEOUT); - - if (error) - return error; - - return _flush(offset + 1, value, _FLASH_WRITE_TIMEOUT); -} - -DriverError AM29F016Driver::flushErase(uint32_t offset) { - auto error = _flush(offset, 0xffff, _FLASH_ERASE_TIMEOUT); - - if (error) - return error; - - return _flush(offset + 1, 0xffff, _FLASH_ERASE_TIMEOUT); -} - -const ChipSize &AM29F016Driver::getChipSize(void) const { - return _STANDARD_CHIP_SIZE; -} - -/* AMD AM29F040 (Fujitsu MBM29F040A) driver */ - -// Konami's drivers handle this chip pretty much identically to the MBM29F016A, -// but using 0x5555/0x2aaa as command addresses instead of 0x555/0x2aa. - -void AM29F040Driver::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; - ptr[0x2aaa] = _JEDEC_HANDSHAKE2; - ptr[0x5555] = _JEDEC_WRITE_BYTE; - ptr[offset] = value; -} - -void AM29F040Driver::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; - ptr[0x2aaa] = _JEDEC_HANDSHAKE2; - ptr[0x5555] = _JEDEC_ERASE_HANDSHAKE; - ptr[0x5555] = _JEDEC_HANDSHAKE1; - ptr[0x2aaa] = _JEDEC_HANDSHAKE2; - ptr[offset] = _JEDEC_ERASE_SECTOR; -} - -void AM29F040Driver::eraseChip(uint32_t offset) { - volatile uint16_t *ptr = _region.getRawPtr(offset, true); - - ptr[0x0005] = _JEDEC_RESET; - ptr[0x5555] = _JEDEC_HANDSHAKE1; - ptr[0x2aaa] = _JEDEC_HANDSHAKE2; - ptr[0x5555] = _JEDEC_ERASE_HANDSHAKE; - ptr[0x5555] = _JEDEC_HANDSHAKE1; - ptr[0x2aaa] = _JEDEC_HANDSHAKE2; - ptr[0x5555] = _JEDEC_ERASE_CHIP; -} - -const ChipSize &AM29F040Driver::getChipSize(void) const { - return _ALT_CHIP_SIZE; -} - -/* Intel 28F016S5 (Sharp LH28F016S) driver */ - -enum IntelStatusFlag : uint16_t { - _INTEL_STATUS_DPS = 1 << 1, - _INTEL_STATUS_BWSS = 1 << 2, - _INTEL_STATUS_VPPS = 1 << 3, - _INTEL_STATUS_BWSLBS = 1 << 4, - _INTEL_STATUS_ECLBS = 1 << 5, - _INTEL_STATUS_ESS = 1 << 6, - _INTEL_STATUS_WSMS = 1 << 7 -}; - -DriverError Intel28F016S5Driver::_flush(uint32_t offset, int timeout) { - volatile uint16_t *ptr = _region.getRawPtr(offset & ~1); - - int shift = (offset & 1) * 8; - uint8_t status = 0; - - // Not required as all write/erase commands already put the chip into status - // reading mode. - //*ptr = _INTEL_GET_STATUS; - - for (; timeout > 0; timeout--) { - status = (*ptr >> shift) & 0xff; - - if (!(status & _INTEL_STATUS_WSMS)) - continue; - - *ptr = _INTEL_RESET; - - // The datasheet suggests only checking the error flags after WSMS = 1. - if (status & (_INTEL_STATUS_DPS | _INTEL_STATUS_VPPS)) { - *ptr = _INTEL_CLEAR_STATUS; - - LOG("locked @ 0x%08x, stat=0x%02x", offset, status); - return WRITE_PROTECTED; - } - if (status & (_INTEL_STATUS_BWSLBS | _INTEL_STATUS_ECLBS)) { - *ptr = _INTEL_CLEAR_STATUS; - - LOG("error @ 0x%08x, stat=0x%02x", offset, status); - return CHIP_ERROR; - } - - return NO_ERROR; - } - - *ptr = _INTEL_RESET; - - LOG("timeout @ 0x%08x, stat=0x%02x", offset, status); - return CHIP_TIMEOUT; -} - -void Intel28F016S5Driver::write(uint32_t offset, uint16_t value) { - volatile uint16_t *ptr = _region.getRawPtr(offset); - - *ptr = _INTEL_RESET; - *ptr = _INTEL_CLEAR_STATUS; - *ptr = _INTEL_WRITE_BYTE; - *ptr = value; -} - -void Intel28F016S5Driver::eraseSector(uint32_t offset) { - volatile uint16_t *ptr = _region.getRawPtr(offset); - - *ptr = _INTEL_RESET; - *ptr = _INTEL_ERASE_SECTOR1; - *ptr = _INTEL_ERASE_SECTOR2; -} - -DriverError Intel28F016S5Driver::flushWrite( - uint32_t offset, uint16_t value -) { - auto error = _flush(offset, _FLASH_WRITE_TIMEOUT); - - if (error) - return error; - - return _flush(offset + 1, _FLASH_WRITE_TIMEOUT); -} - -DriverError Intel28F016S5Driver::flushErase(uint32_t offset) { - auto error = _flush(offset, _FLASH_ERASE_TIMEOUT); - - if (error) - return error; - - return _flush(offset + 1, _FLASH_ERASE_TIMEOUT); -} - -const ChipSize &Intel28F016S5Driver::getChipSize(void) const { - return _STANDARD_CHIP_SIZE; -} - -/* Intel 28F640J5 driver */ - -static const ChipSize _28F640J5_CHIP_SIZE{ - .chipLength = 0x800000, - .eraseSectorLength = 0x20000 -}; - -DriverError Intel28F640J5Driver::flushWrite( - uint32_t offset, uint16_t value -) { - return _flush(offset, _FLASH_WRITE_TIMEOUT); -} - -DriverError Intel28F640J5Driver::flushErase(uint32_t offset) { - return _flush(offset, _FLASH_ERASE_TIMEOUT); -} - -const ChipSize &Intel28F640J5Driver::getChipSize(void) const { - return _28F640J5_CHIP_SIZE; -} - /* BIOS ROM headers */ static const ShellInfo _SHELL_VERSIONS[]{ diff --git a/src/common/rom.hpp b/src/common/rom.hpp index c6a8f23..122713d 100644 --- a/src/common/rom.hpp +++ b/src/common/rom.hpp @@ -76,116 +76,6 @@ extern const BIOSRegion bios; extern const RTCRegion rtc; extern const FlashRegion flash, pcmcia[2]; -/* Chip drivers */ - -enum DriverError { - NO_ERROR = 0, - UNSUPPORTED_OP = 1, - CHIP_TIMEOUT = 2, - CHIP_ERROR = 3, - VERIFY_MISMATCH = 4, - WRITE_PROTECTED = 5 -}; - -struct ChipSize { -public: - size_t chipLength, eraseSectorLength; -}; - -class Driver { -protected: - const Region &_region; - -public: - inline Driver(const Region ®ion) - : _region(region) {} - - // 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 DriverError flushWrite(uint32_t offset, uint16_t value) { - return UNSUPPORTED_OP; - } - virtual DriverError flushErase(uint32_t offset) { - return UNSUPPORTED_OP; - } - virtual const ChipSize &getChipSize(void) const; -}; - -class RTCDriver : public Driver { -public: - inline RTCDriver(const RTCRegion ®ion) - : 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 AM29F016Driver : public Driver { -protected: - DriverError _flush(uint32_t offset, uint16_t value, int timeout); - -public: - inline AM29F016Driver(const FlashRegion ®ion) - : Driver(region) {} - - 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 AM29F040Driver : public AM29F016Driver { -public: - inline AM29F040Driver(const FlashRegion ®ion) - : AM29F016Driver(region) {} - - void write(uint32_t offset, uint16_t value); - void eraseSector(uint32_t offset); - void eraseChip(uint32_t offset); - const ChipSize &getChipSize(void) const; -}; - -class Intel28F016S5Driver : public Driver { -protected: - DriverError _flush(uint32_t offset, int timeout); - -public: - inline Intel28F016S5Driver(const FlashRegion ®ion) - : Driver(region) {} - - void write(uint32_t offset, uint16_t value); - void eraseSector(uint32_t offset); - DriverError flushWrite(uint32_t offset, uint16_t value); - DriverError flushErase(uint32_t offset); - const ChipSize &getChipSize(void) const; -}; - -class Intel28F640J5Driver : public Intel28F016S5Driver { -public: - inline Intel28F640J5Driver(const FlashRegion ®ion) - : Intel28F016S5Driver(region) {} - - DriverError flushWrite(uint32_t offset, uint16_t value); - DriverError flushErase(uint32_t offset); - const ChipSize &getChipSize(void) const; -}; - -extern const char *const DRIVER_ERROR_NAMES[]; - -static inline const char *getErrorString(DriverError error) { - return DRIVER_ERROR_NAMES[error]; -} - /* BIOS ROM headers */ struct [[gnu::packed]] SonyKernelHeader { diff --git a/src/common/romdrivers.cpp b/src/common/romdrivers.cpp new file mode 100644 index 0000000..e5a6971 --- /dev/null +++ b/src/common/romdrivers.cpp @@ -0,0 +1,336 @@ + +#include +#include +#include "common/rom.hpp" +#include "common/romdrivers.hpp" + +namespace rom { + +/* Data common to all chip drivers */ + +static constexpr int _FLASH_WRITE_TIMEOUT = 10000000; +static constexpr int _FLASH_ERASE_TIMEOUT = 20000000; + +const char *const DRIVER_ERROR_NAMES[]{ + "NO_ERROR", + "UNSUPPORTED_OP", + "CHIP_TIMEOUT", + "CHIP_ERROR", + "VERIFY_MISMATCH", + "WRITE_PROTECTED" +}; + +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 and an 8-bit bus. +static const ChipSize _STANDARD_CHIP_SIZE{ + .chipLength = 2 * 0x200000, + .eraseSectorLength = 2 * 0x10000 +}; + +static const ChipSize _ALT_CHIP_SIZE{ + .chipLength = 2 * 0x80000, + .eraseSectorLength = 2 * 0x10000 +}; + +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(_region.ptr + offset * 2); + ptr[0] = value & 0xff; + ptr[1] = value >> 8; +} + +void RTCDriver::eraseSector(uint32_t offset) { + auto ptr = reinterpret_cast(_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(_region.ptr + offset * 2); + + uint16_t actualValue = (ptr[0] & 0xff) | ((ptr[1] & 0xff) << 8); + + if (value != actualValue) { + LOG("ptr=0x%06x, exp=0x%02x, got=0x%04x", offset, value, actualValue); + return VERIFY_MISMATCH; + } + + return NO_ERROR; +} + +DriverError RTCDriver::flushErase(uint32_t offset) { + return flushWrite(offset, 0); +} + +const ChipSize &RTCDriver::getChipSize(void) const { + return _RTC_CHIP_SIZE; +} + +/* AMD AM29F016/017 (Fujitsu MBM29F016A/017A) driver */ + +DriverError AM29F016Driver::_flush( + uint32_t offset, uint16_t value, int timeout +) { + volatile uint16_t *ptr = _region.getRawPtr(offset & ~1); + + int shift = (offset & 1) * 8; + uint8_t byte = (value >> shift) & 0xff; + + uint8_t status, diff; + + for (; timeout > 0; timeout--) { + status = (*ptr >> shift) & 0xff; + diff = status ^ byte; + + if (!(diff & JEDEC_STATUS_POLL_BIT)) + return NO_ERROR; + if (status & JEDEC_STATUS_ERROR) + break; + } + + // If the error flag was set, make sure an error actually occurred. + status = (*ptr >> shift) & 0xff; + diff = status ^ byte; + + if (!(diff & JEDEC_STATUS_POLL_BIT)) + return NO_ERROR; + + *ptr = JEDEC_RESET; + + if (status & JEDEC_STATUS_ERROR) { + LOG("JEDEC error, ptr=0x%06x, st=0x%02x", offset, status); + return CHIP_ERROR; + } else { + LOG("JEDEC timeout, ptr=0x%06x, st=0x%02x", offset, status); + return CHIP_TIMEOUT; + } +} + +void AM29F016Driver::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; + ptr[0x2aa] = JEDEC_HANDSHAKE2; + ptr[0x555] = JEDEC_WRITE_BYTE; + ptr[offset] = value; +} + +void AM29F016Driver::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; + ptr[0x2aa] = JEDEC_HANDSHAKE2; + ptr[0x555] = JEDEC_ERASE_HANDSHAKE; + ptr[0x555] = JEDEC_HANDSHAKE1; + ptr[0x2aa] = JEDEC_HANDSHAKE2; + ptr[offset] = JEDEC_ERASE_SECTOR; +} + +void AM29F016Driver::eraseChip(uint32_t offset) { + volatile uint16_t *ptr = _region.getRawPtr(offset, true); + + ptr[0x000] = JEDEC_RESET; + ptr[0x555] = JEDEC_HANDSHAKE1; + ptr[0x2aa] = JEDEC_HANDSHAKE2; + ptr[0x555] = JEDEC_ERASE_HANDSHAKE; + ptr[0x555] = JEDEC_HANDSHAKE1; + ptr[0x2aa] = JEDEC_HANDSHAKE2; + ptr[0x555] = JEDEC_ERASE_CHIP; +} + +DriverError AM29F016Driver::flushWrite( + uint32_t offset, uint16_t value +) { + auto error = _flush(offset, value, _FLASH_WRITE_TIMEOUT); + + if (error) + return error; + + return _flush(offset + 1, value, _FLASH_WRITE_TIMEOUT); +} + +DriverError AM29F016Driver::flushErase(uint32_t offset) { + auto error = _flush(offset, 0xffff, _FLASH_ERASE_TIMEOUT); + + if (error) + return error; + + return _flush(offset + 1, 0xffff, _FLASH_ERASE_TIMEOUT); +} + +const ChipSize &AM29F016Driver::getChipSize(void) const { + return _STANDARD_CHIP_SIZE; +} + +/* AMD AM29F040 (Fujitsu MBM29F040A) driver */ + +// Konami's drivers handle this chip pretty much identically to the MBM29F016A, +// but using 0x5555/0x2aaa as command addresses instead of 0x555/0x2aa. + +void AM29F040Driver::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; + ptr[0x2aaa] = JEDEC_HANDSHAKE2; + ptr[0x5555] = JEDEC_WRITE_BYTE; + ptr[offset] = value; +} + +void AM29F040Driver::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; + ptr[0x2aaa] = JEDEC_HANDSHAKE2; + ptr[0x5555] = JEDEC_ERASE_HANDSHAKE; + ptr[0x5555] = JEDEC_HANDSHAKE1; + ptr[0x2aaa] = JEDEC_HANDSHAKE2; + ptr[offset] = JEDEC_ERASE_SECTOR; +} + +void AM29F040Driver::eraseChip(uint32_t offset) { + volatile uint16_t *ptr = _region.getRawPtr(offset, true); + + ptr[0x0005] = JEDEC_RESET; + ptr[0x5555] = JEDEC_HANDSHAKE1; + ptr[0x2aaa] = JEDEC_HANDSHAKE2; + ptr[0x5555] = JEDEC_ERASE_HANDSHAKE; + ptr[0x5555] = JEDEC_HANDSHAKE1; + ptr[0x2aaa] = JEDEC_HANDSHAKE2; + ptr[0x5555] = JEDEC_ERASE_CHIP; +} + +const ChipSize &AM29F040Driver::getChipSize(void) const { + return _ALT_CHIP_SIZE; +} + +/* Intel 28F016S5 (Sharp LH28F016S) driver */ + +DriverError Intel28F016S5Driver::_flush(uint32_t offset, int timeout) { + volatile uint16_t *ptr = _region.getRawPtr(offset & ~1); + + int shift = (offset & 1) * 8; + uint8_t status = 0; + + *ptr = INTEL_GET_STATUS; + + for (; timeout > 0; timeout--) { + status = (*ptr >> shift) & 0xff; + + if (!(status & INTEL_STATUS_WSMS)) + continue; + + *ptr = INTEL_RESET; + + // The datasheet suggests only checking the error flags after WSMS = 1. + if (status & (INTEL_STATUS_DPS | INTEL_STATUS_VPPS)) { + *ptr = INTEL_CLEAR_STATUS; + + LOG("Intel WP, ptr=0x%06x, st=0x%02x", offset, status); + return WRITE_PROTECTED; + } + if (status & (INTEL_STATUS_BWSLBS | INTEL_STATUS_ECLBS)) { + *ptr = INTEL_CLEAR_STATUS; + + LOG("Intel error, ptr=0x%06x, st=0x%02x", offset, status); + return CHIP_ERROR; + } + + return NO_ERROR; + } + + *ptr = INTEL_RESET; + + LOG("Intel timeout, ptr=0x%06x, st=0x%02x", offset, status); + return CHIP_TIMEOUT; +} + +void Intel28F016S5Driver::write(uint32_t offset, uint16_t value) { + volatile uint16_t *ptr = _region.getRawPtr(offset); + + *ptr = INTEL_RESET; + *ptr = INTEL_CLEAR_STATUS; + *ptr = INTEL_WRITE_BYTE; + *ptr = value; +} + +void Intel28F016S5Driver::eraseSector(uint32_t offset) { + volatile uint16_t *ptr = _region.getRawPtr(offset); + + *ptr = INTEL_RESET; + *ptr = INTEL_ERASE_SECTOR1; + *ptr = INTEL_ERASE_SECTOR2; +} + +DriverError Intel28F016S5Driver::flushWrite( + uint32_t offset, uint16_t value +) { + auto error = _flush(offset, _FLASH_WRITE_TIMEOUT); + + if (error) + return error; + + return _flush(offset + 1, _FLASH_WRITE_TIMEOUT); +} + +DriverError Intel28F016S5Driver::flushErase(uint32_t offset) { + auto error = _flush(offset, _FLASH_ERASE_TIMEOUT); + + if (error) + return error; + + return _flush(offset + 1, _FLASH_ERASE_TIMEOUT); +} + +const ChipSize &Intel28F016S5Driver::getChipSize(void) const { + return _STANDARD_CHIP_SIZE; +} + +/* Intel 28F640J5 driver */ + +static const ChipSize _28F640J5_CHIP_SIZE{ + .chipLength = 0x800000, + .eraseSectorLength = 0x20000 +}; + +DriverError Intel28F640J5Driver::flushWrite( + uint32_t offset, uint16_t value +) { + return _flush(offset, _FLASH_WRITE_TIMEOUT); +} + +DriverError Intel28F640J5Driver::flushErase(uint32_t offset) { + return _flush(offset, _FLASH_ERASE_TIMEOUT); +} + +const ChipSize &Intel28F640J5Driver::getChipSize(void) const { + return _28F640J5_CHIP_SIZE; +} + +} diff --git a/src/common/romdrivers.hpp b/src/common/romdrivers.hpp new file mode 100644 index 0000000..97fb8a2 --- /dev/null +++ b/src/common/romdrivers.hpp @@ -0,0 +1,161 @@ + +#pragma once + +#include +#include +#include "common/rom.hpp" + +namespace rom { + +/* Chip drivers */ + +enum JEDECCommand : uint16_t { + JEDEC_RESET = 0xf0f0, + JEDEC_HANDSHAKE1 = 0xaaaa, + JEDEC_HANDSHAKE2 = 0x5555, + JEDEC_GET_ID = 0x9090, + JEDEC_WRITE_BYTE = 0xa0a0, + JEDEC_ERASE_HANDSHAKE = 0x8080, + JEDEC_ERASE_CHIP = 0x1010, + JEDEC_ERASE_SECTOR = 0x3030 +}; + +enum JEDECStatusFlag : uint16_t { + JEDEC_STATUS_ERASE_TOGGLE = 1 << 2, + JEDEC_STATUS_ERASE_START = 1 << 3, + JEDEC_STATUS_ERROR = 1 << 5, + JEDEC_STATUS_TOGGLE = 1 << 6, + JEDEC_STATUS_POLL_BIT = 1 << 7 +}; + +enum IntelCommand : uint16_t { + INTEL_RESET = 0xffff, + INTEL_GET_ID = 0x9090, + INTEL_WRITE_BYTE = 0x4040, + INTEL_ERASE_SECTOR1 = 0x2020, + INTEL_ERASE_SECTOR2 = 0xd0d0, + INTEL_GET_STATUS = 0x7070, + INTEL_CLEAR_STATUS = 0x5050, + INTEL_SUSPEND = 0xb0b0, + INTEL_RESUME = 0xd0d0 +}; + +enum IntelStatusFlag : uint16_t { + INTEL_STATUS_DPS = 1 << 1, + INTEL_STATUS_BWSS = 1 << 2, + INTEL_STATUS_VPPS = 1 << 3, + INTEL_STATUS_BWSLBS = 1 << 4, + INTEL_STATUS_ECLBS = 1 << 5, + INTEL_STATUS_ESS = 1 << 6, + INTEL_STATUS_WSMS = 1 << 7 +}; + +enum DriverError { + NO_ERROR = 0, + UNSUPPORTED_OP = 1, + CHIP_TIMEOUT = 2, + CHIP_ERROR = 3, + VERIFY_MISMATCH = 4, + WRITE_PROTECTED = 5 +}; + +struct ChipSize { +public: + size_t chipLength, eraseSectorLength; +}; + +class Driver { +protected: + const Region &_region; + +public: + inline Driver(const Region ®ion) + : _region(region) {} + + // 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 DriverError flushWrite(uint32_t offset, uint16_t value) { + return UNSUPPORTED_OP; + } + virtual DriverError flushErase(uint32_t offset) { + return UNSUPPORTED_OP; + } + virtual const ChipSize &getChipSize(void) const; +}; + +class RTCDriver : public Driver { +public: + inline RTCDriver(const RTCRegion ®ion) + : 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 AM29F016Driver : public Driver { +protected: + DriverError _flush(uint32_t offset, uint16_t value, int timeout); + +public: + inline AM29F016Driver(const FlashRegion ®ion) + : Driver(region) {} + + 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 AM29F040Driver : public AM29F016Driver { +public: + inline AM29F040Driver(const FlashRegion ®ion) + : AM29F016Driver(region) {} + + void write(uint32_t offset, uint16_t value); + void eraseSector(uint32_t offset); + void eraseChip(uint32_t offset); + const ChipSize &getChipSize(void) const; +}; + +class Intel28F016S5Driver : public Driver { +protected: + DriverError _flush(uint32_t offset, int timeout); + +public: + inline Intel28F016S5Driver(const FlashRegion ®ion) + : Driver(region) {} + + void write(uint32_t offset, uint16_t value); + void eraseSector(uint32_t offset); + DriverError flushWrite(uint32_t offset, uint16_t value); + DriverError flushErase(uint32_t offset); + const ChipSize &getChipSize(void) const; +}; + +class Intel28F640J5Driver : public Intel28F016S5Driver { +public: + inline Intel28F640J5Driver(const FlashRegion ®ion) + : Intel28F016S5Driver(region) {} + + DriverError flushWrite(uint32_t offset, uint16_t value); + DriverError flushErase(uint32_t offset); + const ChipSize &getChipSize(void) const; +}; + +extern const char *const DRIVER_ERROR_NAMES[]; + +static inline const char *getErrorString(DriverError error) { + return DRIVER_ERROR_NAMES[error]; +} + +} diff --git a/src/main/app/app.cpp b/src/main/app/app.cpp index 3150b1b..444eb25 100644 --- a/src/main/app/app.cpp +++ b/src/main/app/app.cpp @@ -8,6 +8,7 @@ #include "common/filemisc.hpp" #include "common/filezip.hpp" #include "common/gpu.hpp" +#include "common/ide.hpp" #include "common/io.hpp" #include "common/spu.hpp" #include "common/util.hpp" @@ -65,7 +66,7 @@ WorkerStatusType WorkerStatus::setStatus(WorkerStatusType value) { /* Filesystem manager class */ FileIOManager::FileIOManager(void) -: _resourceFile(nullptr) { +: _resourceFile(nullptr), resourcePtr(nullptr), resourceLength(0) { __builtin_memset(ide, 0, sizeof(ide)); vfs.mount("resource:", &resource); @@ -83,6 +84,9 @@ void FileIOManager::initIDE(void) { auto &dev = ide::devices[i]; name[3] = i + '0'; + if (!(dev.flags & ide::DEVICE_READY)) + continue; + // Note that calling vfs.mount() multiple times will *not* update any // already mounted device, so if two hard drives or CD-ROMs are present // the hdd:/cdrom: prefix will be assigned to the first one. @@ -95,7 +99,7 @@ void FileIOManager::initIDE(void) { } ide[i] = iso; - vfs.mount("cdrom:", iso, true); + vfs.mount("cdrom:", iso); } else { auto fat = new file::FATProvider(); @@ -105,7 +109,7 @@ void FileIOManager::initIDE(void) { } ide[i] = fat; - vfs.mount("hdd:", fat, true); + vfs.mount("hdd:", fat); } vfs.mount(name, ide[i], true); @@ -113,35 +117,44 @@ void FileIOManager::initIDE(void) { } void FileIOManager::closeIDE(void) { - for (size_t i = 0; i < util::countOf(ide::devices); i++) { - if (!ide[i]) - continue; + char name[6]{ "ide#:" }; - ide[i]->close(); - delete ide[i]; - ide[i] = nullptr; + for (size_t i = 0; i < util::countOf(ide::devices); i++) { + if (ide[i]) { + ide[i]->close(); + delete ide[i]; + ide[i] = nullptr; + } + + name[3] = i + '0'; + vfs.unmount(name); } } bool FileIOManager::loadResourceFile(const char *path) { closeResourceFile(); - _resourceFile = vfs.openFile(path, file::READ); + if (path) + _resourceFile = vfs.openFile(path, file::READ); - if (!_resourceFile) - return false; + // Fall back to the default in-memory resource archive in case of failure. + if (_resourceFile) { + if (resource.init(_resourceFile)) + return true; + } - resource.close(); - return resource.init(_resourceFile); + resource.init(resourcePtr, resourceLength); + return false; } void FileIOManager::closeResourceFile(void) { - if (!_resourceFile) - return; + resource.close(); - _resourceFile->close(); - delete _resourceFile; - _resourceFile = nullptr; + if (_resourceFile) { + _resourceFile->close(); + delete _resourceFile; + _resourceFile = nullptr; + } } void FileIOManager::close(void) { @@ -320,9 +333,11 @@ void App::_interruptHandler(void) { LOG("build " VERSION_STRING " (" __DATE__ " " __TIME__ ")"); LOG("(C) 2022-2024 spicyjpeg"); - _ctx.screenData = this; + _ctx.screenData = this; + _fileIO.resourcePtr = resourcePtr; + _fileIO.resourceLength = resourceLength; - _fileIO.resource.init(resourcePtr, resourceLength); + _fileIO.loadResourceFile(nullptr); _loadResources(); char dateString[24]; diff --git a/src/main/app/app.hpp b/src/main/app/app.hpp index 8c3223a..b6a5d98 100644 --- a/src/main/app/app.hpp +++ b/src/main/app/app.hpp @@ -54,6 +54,9 @@ private: file::File *_resourceFile; public: + const void *resourcePtr; + size_t resourceLength; + file::Provider *ide[util::countOf(ide::devices)]; file::ZIPProvider resource; #ifdef ENABLE_PCDRV @@ -219,6 +222,7 @@ private: // miscworkers.cpp bool _ideInitWorker(void); + bool _fileInitWorker(void); bool _executableWorker(void); bool _atapiEjectWorker(void); bool _rebootWorker(void); diff --git a/src/main/app/miscworkers.cpp b/src/main/app/miscworkers.cpp index 69334f1..d93e593 100644 --- a/src/main/app/miscworkers.cpp +++ b/src/main/app/miscworkers.cpp @@ -4,6 +4,7 @@ #include "common/defs.hpp" #include "common/file.hpp" #include "common/ide.hpp" +#include "common/idedefs.hpp" #include "common/io.hpp" #include "common/util.hpp" #include "main/app/app.hpp" @@ -20,29 +21,37 @@ static const char *const _AUTOBOOT_PATHS[][2]{ }; bool App::_ideInitWorker(void) { - _workerStatus.update(0, 4, WSTR("App.ideInitWorker.initDrives")); - for (size_t i = 0; i < util::countOf(ide::devices); i++) { auto &dev = ide::devices[i]; - // Spin down all drives by default. if (dev.flags & ide::DEVICE_READY) continue; - if (dev.enumerate()) - dev.goIdle(); + + _workerStatus.update( + i, util::countOf(ide::devices), WSTR("App.ideInitWorker.init") + ); + dev.enumerate(); } - _workerStatus.update(1, 4, WSTR("App.ideInitWorker.initFileIO")); + return _fileInitWorker(); +} + +bool App::_fileInitWorker(void) { + _workerStatus.update(0, 4, WSTR("App.fileInitWorker.unmount")); + _fileIO.closeResourceFile(); + _fileIO.close(); + + _workerStatus.update(1, 4, WSTR("App.fileInitWorker.mount")); _fileIO.initIDE(); - _workerStatus.update(2, 4, WSTR("App.ideInitWorker.loadResources")); + _workerStatus.update(2, 4, WSTR("App.fileInitWorker.loadResources")); if (_fileIO.loadResourceFile(EXTERNAL_DATA_DIR "/resource.zip")) _loadResources(); #ifdef ENABLE_AUTOBOOT // Only try to autoboot if DIP switch 1 is on. if (io::getDIPSwitch(0)) { - _workerStatus.update(3, 4, WSTR("App.ideInitWorker.autoboot")); + _workerStatus.update(3, 4, WSTR("App.fileInitWorker.autoboot")); for (auto path : _AUTOBOOT_PATHS) { file::FileInfo info; @@ -193,10 +202,8 @@ bool App::_atapiEjectWorker(void) { if (!(dev.flags & ide::DEVICE_ATAPI)) continue; - ide::Packet packet; - - packet.setStartStopUnit(ide::START_STOP_MODE_OPEN_TRAY); - auto error = ide::devices[0].atapiPacket(packet); + auto error = + ide::devices[0].startStopUnit(ide::START_STOP_MODE_OPEN_TRAY); if (error) { _messageScreen.setMessage( diff --git a/src/main/app/modals.cpp b/src/main/app/modals.cpp index 91e1824..f565809 100644 --- a/src/main/app/modals.cpp +++ b/src/main/app/modals.cpp @@ -165,17 +165,15 @@ void FilePickerScreen::setMessage( } void FilePickerScreen::reloadAndShow(ui::Context &ctx) { - // Check if any disc has been changed and reload all filesystems if - // necessary. + // Check if any drive has reported a disc change and reload all filesystems + // if necessary. for (auto &dev : ide::devices) { - if (!(dev.flags & ide::DEVICE_ATAPI)) - continue; - if (!dev.atapiPoll()) + if (!dev.poll()) continue; APP->_messageScreen.previousScreens[MESSAGE_ERROR] = this; - APP->_runWorker(&App::_ideInitWorker, *this, false, true); + APP->_runWorker(&App::_fileInitWorker, *this, false, true); return; } diff --git a/src/main/app/romworkers.cpp b/src/main/app/romworkers.cpp index 70e5d34..798d753 100644 --- a/src/main/app/romworkers.cpp +++ b/src/main/app/romworkers.cpp @@ -5,6 +5,7 @@ #include "common/defs.hpp" #include "common/file.hpp" #include "common/rom.hpp" +#include "common/romdrivers.hpp" #include "common/util.hpp" #include "main/app/app.hpp" #include "main/app/romactions.hpp" diff --git a/src/main/app/tests.cpp b/src/main/app/tests.cpp index 6f5f03f..eba62c6 100644 --- a/src/main/app/tests.cpp +++ b/src/main/app/tests.cpp @@ -235,8 +235,10 @@ void AudioTestScreen::update(ui::Context &ctx) { if (ctx.buttons.pressed(ui::BTN_START)) { if (ctx.buttons.held(ui::BTN_LEFT) || ctx.buttons.held(ui::BTN_RIGHT)) { +#if 0 io::setMiscOutput(io::MISC_AMP_ENABLE, false); io::setMiscOutput(io::MISC_CDDA_ENABLE, false); +#endif ctx.show(APP->_testMenuScreen, true, true); } else {