Rewrite IDE driver completely, fix VFS and flash driver

This commit is contained in:
spicyjpeg 2024-06-02 01:45:15 +02:00
parent db5eaabe57
commit ef46aad5b0
No known key found for this signature in database
GPG Key ID: 5CC87404C01DF393
15 changed files with 1299 additions and 1066 deletions

View File

@ -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

View File

@ -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",

View File

@ -2,6 +2,7 @@
#include <stddef.h>
#include <stdint.h>
#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<uint16_t>(data);
auto ptr = reinterpret_cast<uint16_t *>(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<uint16_t>(data);
auto ptr = reinterpret_cast<const uint16_t *>(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<uint32_t>(data);
DMA_MADR(DMA_PIO) = reinterpret_cast<uint32_t>(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<uint32_t>(data);
DMA_MADR(DMA_PIO) = reinterpret_cast<uint32_t>(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)
);
_write(CS0_COMMAND, ATA_DEVICE_RESET);
// Only check for errors *after* BSY is cleared.
if (!(status & CS0_STATUS_BSY)) {
if ((status & CS0_STATUS_ERR) && !ignoreError) {
_handleError();
return DRIVE_ERROR;
}
if ((status & mask) == value)
if ((status & CS0_STATUS_DRDY) || !drdy)
return NO_ERROR;
}
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_COMMAND, ATA_DEVICE_RESET);
}
_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::_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<uint16_t>(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);
auto error = _waitForIdle(true);
if (error)
return error;
auto ptr = reinterpret_cast<uint16_t *>(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));
for (; length; length -= 2)
*(ptr++) = SYS573_IDE_CS0_BASE[CS0_DATA];
return NO_ERROR;
}
DeviceError Device::_writePIO(
const void *data, size_t length, int timeout, bool ignoreErrors
) {
util::assertAligned<uint16_t>(data);
auto error = _waitForStatus(
CS0_STATUS_DRQ | CS0_STATUS_BSY, CS0_STATUS_DRQ, timeout
);
auto error = _waitForIdle(true);
if (error)
return error;
}
auto ptr = reinterpret_cast<const uint16_t *>(data);
for (; length; length -= 2)
SYS573_IDE_CS0_BASE[CS0_DATA] = *(ptr++);
_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::_readDMA(
void *data, size_t length, int timeout, bool ignoreErrors
) {
length /= 4;
util::assertAligned<uint32_t>(data);
auto error = _waitForStatus(
CS0_STATUS_DRQ | CS0_STATUS_BSY, CS0_STATUS_DRQ, timeout
);
if (error)
return error;
DMA_MADR(DMA_PIO) = reinterpret_cast<uint32_t>(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;
}
return NO_ERROR;
}
DeviceError Device::_writeDMA(
const void *data, size_t length, int timeout, bool ignoreErrors
) {
length /= 4;
util::assertAligned<uint32_t>(data);
auto error = _waitForStatus(
CS0_STATUS_DRQ | CS0_STATUS_BSY, CS0_STATUS_DRQ, timeout
);
if (error)
return error;
DMA_MADR(DMA_PIO) = reinterpret_cast<uint32_t>(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--) {
for (size_t i = chunkLength; i; i--, ptr += ATA_SECTOR_SIZE) {
auto error = _waitForDRQ();
if (error)
return error;
if (write)
error = _writePIO(
reinterpret_cast<const void *>(ptr), ATA_SECTOR_SIZE
);
_writePIO(reinterpret_cast<const void *>(ptr), ATA_SECTOR_SIZE);
else
error = _readPIO(
reinterpret_cast<void *>(ptr), ATA_SECTOR_SIZE
);
if (error)
return error;
ptr += ATA_SECTOR_SIZE;
_readPIO(reinterpret_cast<void *>(ptr), ATA_SECTOR_SIZE);
}
error = _waitForStatus(
CS0_STATUS_DRDY | CS0_STATUS_BSY, CS0_STATUS_DRDY
);
if (error)
return error;
lba += chunkLength;
count -= chunkLength;
}
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<void *>(ptr), ATAPI_SECTOR_SIZE))
return atapiPoll();
auto error = _waitForDRQ();
ptr += ATAPI_SECTOR_SIZE;
if (error)
return error;
size_t chunkLength = _getCylinder();
_readPIO(reinterpret_cast<void *>(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,18 +586,26 @@ 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))
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)
== IDENTIFY_DEV_ATAPI_TYPE_CDROM
@ -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;
@ -484,113 +633,39 @@ DeviceError Device::enumerate(void) {
_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 (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;
if (flags & DEVICE_ATAPI) {
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<uintptr_t>(data), static_cast<uint32_t>(lba), count
);
else
return _ideReadWrite(
return _ataTransfer(
reinterpret_cast<uintptr_t>(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<uintptr_t>(data), lba, count, true);
return _ataTransfer(reinterpret_cast<uintptr_t>(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 (standby)
_write(CS0_COMMAND, ATA_STANDBY_IMMEDIATE);
else
_write(CS0_COMMAND, ATA_IDLE_IMMEDIATE);
return _waitForStatus(CS0_STATUS_BSY, 0);
_write(CS0_COMMAND, standby ? ATA_STANDBY_IMMEDIATE : ATA_IDLE_IMMEDIATE);
return _waitForIdle();
}
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();
}
}

View File

@ -3,169 +3,15 @@
#include <stddef.h>
#include <stdint.h>
#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 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);
};

238
src/common/idedefs.hpp Normal file
View File

@ -0,0 +1,238 @@
#pragma once
#include <stdint.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 (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"
};
}

View File

@ -3,6 +3,7 @@
#include <stdint.h>
#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<volatile uint16_t *>(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<volatile uint16_t *>(_region.ptr + offset * 2);
ptr[0] = value & 0xff;
ptr[1] = value >> 8;
}
void RTCDriver::eraseSector(uint32_t offset) {
auto ptr = reinterpret_cast<void *>(_region.ptr);
__builtin_memset(ptr, 0, _region.regionLength * 2);
}
void RTCDriver::eraseChip(uint32_t offset) {
eraseSector(offset);
}
DriverError RTCDriver::flushWrite(uint32_t offset, uint16_t value) {
auto ptr = reinterpret_cast<volatile uint16_t *>(_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[]{

View File

@ -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 &region)
: _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 &region)
: Driver(region) {}
void write(uint32_t offset, uint16_t value);
void eraseSector(uint32_t offset);
void eraseChip(uint32_t offset);
DriverError flushWrite(uint32_t offset, uint16_t value);
DriverError flushErase(uint32_t offset);
const ChipSize &getChipSize(void) const;
};
class AM29F016Driver : public Driver {
protected:
DriverError _flush(uint32_t offset, uint16_t value, int timeout);
public:
inline AM29F016Driver(const FlashRegion &region)
: 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 &region)
: 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 &region)
: 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 &region)
: 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 {

336
src/common/romdrivers.cpp Normal file
View File

@ -0,0 +1,336 @@
#include <stddef.h>
#include <stdint.h>
#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<volatile uint16_t *>(_region.ptr + offset * 2);
ptr[0] = value & 0xff;
ptr[1] = value >> 8;
}
void RTCDriver::eraseSector(uint32_t offset) {
auto ptr = reinterpret_cast<void *>(_region.ptr);
__builtin_memset(ptr, 0, _region.regionLength * 2);
}
void RTCDriver::eraseChip(uint32_t offset) {
eraseSector(offset);
}
DriverError RTCDriver::flushWrite(uint32_t offset, uint16_t value) {
auto ptr = reinterpret_cast<volatile uint16_t *>(_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;
}
}

161
src/common/romdrivers.hpp Normal file
View File

@ -0,0 +1,161 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#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 &region)
: _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 &region)
: Driver(region) {}
void write(uint32_t offset, uint16_t value);
void eraseSector(uint32_t offset);
void eraseChip(uint32_t offset);
DriverError flushWrite(uint32_t offset, uint16_t value);
DriverError flushErase(uint32_t offset);
const ChipSize &getChipSize(void) const;
};
class AM29F016Driver : public Driver {
protected:
DriverError _flush(uint32_t offset, uint16_t value, int timeout);
public:
inline AM29F016Driver(const FlashRegion &region)
: 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 &region)
: 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 &region)
: 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 &region)
: 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];
}
}

View File

@ -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,36 +117,45 @@ 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#:" };
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();
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();
if (_resourceFile) {
_resourceFile->close();
delete _resourceFile;
_resourceFile = nullptr;
}
}
void FileIOManager::close(void) {
vfs.close();
@ -321,8 +334,10 @@ void App::_interruptHandler(void) {
LOG("(C) 2022-2024 spicyjpeg");
_ctx.screenData = this;
_fileIO.resourcePtr = resourcePtr;
_fileIO.resourceLength = resourceLength;
_fileIO.resource.init(resourcePtr, resourceLength);
_fileIO.loadResourceFile(nullptr);
_loadResources();
char dateString[24];

View File

@ -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);

View File

@ -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(

View File

@ -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;
}

View File

@ -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"

View File

@ -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 {