Implement IDE HDD driver, clean up I/O code

This commit is contained in:
spicyjpeg 2023-06-12 07:19:32 +02:00
parent 3b9680f434
commit 284280689f
No known key found for this signature in database
GPG Key ID: 5CC87404C01DF393
15 changed files with 673 additions and 82 deletions

View File

@ -174,13 +174,34 @@ void App::_qrCodeWorker(void) {
_dummyWorker();
}
void App::_rebootWorker(void) {
int startTime = _ctx->time;
int duration = _ctx->gpuCtx.refreshRate * 3;
int elapsed;
// Stop clearing the watchdog for a few seconds.
_allowWatchdogClear = false;
do {
elapsed = _ctx->time - startTime;
_workerStatus.update(elapsed, duration, WSTR("App.rebootWorker.reboot"));
delayMicroseconds(10000);
} while (elapsed < duration);
// If for some reason the watchdog fails to reboot the system, fall back to
// a soft reboot.
softReset();
}
/* Misc. functions */
void App::_interruptHandler(void) {
if (acknowledgeInterrupt(IRQ_VBLANK)) {
_ctx->tick();
io::clearWatchdog();
if (_allowWatchdogClear)
io::clearWatchdog();
if (gpu::isIdle())
switchThread(nullptr);
}

View File

@ -96,6 +96,8 @@ private:
cart::Parser *_parser;
const cart::DBEntry *_identified;
bool _allowWatchdogClear;
void _unloadCartData(void);
void _setupWorker(void (App::* func)(void));
void _setupInterrupts(void);
@ -104,12 +106,14 @@ private:
void _cartDetectWorker(void);
void _cartUnlockWorker(void);
void _qrCodeWorker(void);
void _rebootWorker(void);
void _interruptHandler(void);
public:
inline App(void)
: _driver(nullptr), _parser(nullptr), _identified(nullptr) {
: _driver(nullptr), _parser(nullptr), _identified(nullptr),
_allowWatchdogClear(true) {
_workerStack = new uint8_t[WORKER_STACK_SIZE];
}
inline ~App(void) {

View File

@ -40,7 +40,7 @@ void WarningScreen::show(ui::Context &ctx, bool goBack) {
_numButtons = 1;
#ifdef NDEBUG
_cooldownTimer = ctx.time + WARNING_COOLDOWN * 60;
_cooldownTimer = ctx.time + ctx.gpuCtx.refreshRate * WARNING_COOLDOWN;
#else
_cooldownTimer = 0;
#endif

View File

@ -18,7 +18,8 @@ static constexpr int _DMA_TIMEOUT = 10000;
size_t upload(const RectWH &rect, const void *data, bool wait) {
size_t length = (rect.w * rect.h) / 2;
//assert(!(length % _DMA_CHUNK_SIZE));
util::assertAligned<uint32_t>(data);
assert(!(length % _DMA_CHUNK_SIZE));
length = (length + _DMA_CHUNK_SIZE - 1) / _DMA_CHUNK_SIZE;
if (!waitForDMATransfer(DMA_GPU, _DMA_TIMEOUT))

View File

@ -2,58 +2,376 @@
#include <stddef.h>
#include <stdint.h>
#include "ide.hpp"
#include "io.hpp"
#include "util.hpp"
#include "ps1/registers.h"
#include "ps1/system.h"
#include "vendor/diskio.h"
namespace ide {
static constexpr int _STATUS_TIMEOUT = 100000;
static constexpr int _RESET_STATUS_TIMEOUT = 3000000;
static constexpr int _DMA_TIMEOUT = 10000;
/* Utilities */
void _copyString(char *output, const uint16_t *input, size_t length) {
// The strings in the identification block are byte-swapped and padded with
// spaces. To make them printable, any span of consecutive space characters
// at the end is replaced with null bytes.
bool isPadding = true;
output += length;
input += length / 2;
for (; length; length -= 2) {
uint16_t packed = *(--input);
char a = packed & 0xff, b = packed >> 8;
if (isPadding && __builtin_isspace(a))
a = 0;
else
isPadding = false;
if (isPadding && __builtin_isspace(b))
b = 0;
else
isPadding = false;
*(--output) = a;
*(--output) = b;
}
}
/* Device class */
Device devices[2]{
(DEV_PRIMARY), (DEV_SECONDARY)
};
Device devices[2]{ (DEVICE_PRIMARY), (DEVICE_SECONDARY) };
void Device::_setLBA(uint64_t lba, uint16_t count) {
if (flags & DEVICE_HAS_LBA48) {
//assert(lba < (1ULL << 48));
//assert(count <= (1 << 16));
_select(CS0_DEVICE_SEL_LBA);
_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));
}
_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);
}
DeviceError Device::_waitForStatus(uint8_t mask, uint8_t value, int timeout) {
for (; timeout > 0; timeout--) {
uint8_t status = _read(CS0_STATUS);
if (status & CS0_STATUS_ERR) {
LOG("IDE error, stat=0x%02x, err=0x%02x", _read(CS0_STATUS), _read(CS0_ERROR));
return DRIVE_ERROR;
}
if ((status & mask) == value)
return NO_ERROR;
delayMicroseconds(1);
}
LOG("IDE timeout, stat=0x%02x, err=0x%02x", _read(CS0_STATUS), _read(CS0_ERROR));
return STATUS_TIMEOUT;
}
DeviceError Device::_command(uint8_t cmd, bool drdy) {
DeviceError error;
if (drdy)
error = _waitForStatus(
CS0_STATUS_BSY | CS0_STATUS_DRDY, CS0_STATUS_DRDY, _STATUS_TIMEOUT
);
else
error = _waitForStatus(CS0_STATUS_BSY, 0, _STATUS_TIMEOUT);
if (error)
return error;
_write(CS0_COMMAND, cmd);
return _waitForStatus(CS0_STATUS_BSY, 0, _STATUS_TIMEOUT);
}
DeviceError Device::_transferPIO(void *data, size_t length, bool write) {
util::assertAligned<uint16_t>(data);
auto error = _waitForStatus(CS0_STATUS_DRQ, CS0_STATUS_DRQ, _STATUS_TIMEOUT);
if (error)
return error;
auto ptr = reinterpret_cast<uint16_t *>(data);
if (write) {
for (; length; length -= 2)
SYS573_IDE_CS0_BASE[CS0_DATA] = *(ptr++);
} else {
for (; length; length -= 2)
*(ptr++) = SYS573_IDE_CS0_BASE[CS0_DATA];
}
return NO_ERROR;
}
// FIXME: DMA transfers are completely broken currently
DeviceError Device::_transferDMA(void *data, size_t length, bool write) {
length /= 4;
util::assertAligned<uint32_t>(data);
auto error = _waitForStatus(CS0_STATUS_DRQ, CS0_STATUS_DRQ, _STATUS_TIMEOUT);
if (error)
return error;
uint32_t flags = DMA_CHCR_MODE_BURST | DMA_CHCR_ENABLE | DMA_CHCR_TRIGGER;
flags |= write ? DMA_CHCR_WRITE : DMA_CHCR_READ;
BIU_DEV0_ADDR = reinterpret_cast<uint32_t>(SYS573_IDE_CS0_BASE) & 0x1fffffff;
DMA_MADR(DMA_PIO) = reinterpret_cast<uint32_t>(data);
DMA_BCR (DMA_PIO) = length;
DMA_CHCR(DMA_PIO) = flags;
if (!waitForDMATransfer(DMA_PIO, _DMA_TIMEOUT)) {
LOG("DMA transfer timeout");
return INCOMPLETE_DATA;
}
BIU_DEV0_ADDR = DEV0_BASE & 0x1fffffff;
return NO_ERROR;
}
DeviceError Device::enumerate(void) {
flags &= DEVICE_PRIMARY | DEVICE_SECONDARY;
_write(CS1_DEVICE_CTRL, CS1_DEVICE_CTRL_IEN | CS1_DEVICE_CTRL_SRST);
delayMicroseconds(5000);
_write(CS1_DEVICE_CTRL, CS1_DEVICE_CTRL_IEN);
delayMicroseconds(5000);
auto error = _waitForStatus(CS0_STATUS_BSY, 0, _RESET_STATUS_TIMEOUT);
if (error)
return error;
// Check whether the ATAPI signature is present. Note that ATAPI drives will
// not assert DRDY until the first command is issued.
// FIXME: some drives may not provide the signature immediately
IdentifyBlock block;
_select(0);
if ((_read(CS0_CYLINDER_L) == 0x14) && (_read(CS0_CYLINDER_H) == 0xeb)) {
flags |= DEVICE_ATAPI;
error = _command(ATA_IDENTIFY_PACKET, false);
if (error)
return error;
error = _transferPIO(&block, sizeof(IdentifyBlock));
if (error)
return error;
if (
(block.deviceFlags & IDENTIFY_DEV_ATAPI_TYPE_BITMASK)
== IDENTIFY_DEV_ATAPI_TYPE_CDROM
)
flags |= DEVICE_READ_ONLY | DEVICE_CDROM;
if (
(block.deviceFlags & IDENTIFY_DEV_PACKET_LENGTH_BITMASK)
== IDENTIFY_DEV_PACKET_LENGTH_16
)
flags |= DEVICE_HAS_PACKET16;
} else {
error = _command(ATA_IDENTIFY);
if (error)
return error;
error = _transferPIO(&block, sizeof(IdentifyBlock));
if (error)
return error;
if (block.commandSetFlags[1] & (1 << 10)) {
flags |= DEVICE_HAS_LBA48;
capacity = block.getSectorCountExt();
} else {
capacity = block.getSectorCount();
}
if (block.commandSetFlags[1] & (1 << 12))
flags |= DEVICE_HAS_FLUSH;
}
_copyString(model, block.model, sizeof(model));
_copyString(revision, block.revision, sizeof(revision));
_copyString(serialNumber, block.serialNumber, sizeof(serialNumber));
LOG("%s: %s", (flags & DEVICE_PRIMARY) ? "primary" : "secondary", model);
// Find out the fastest PIO transfer mode supported and enable it.
int mode = 1;
if (block.timingValidityFlags & (1 << 1)) {
if (block.pioModeFlags & (1 << 1))
mode = 4;
else if (block.pioModeFlags & (1 << 0))
mode = 3;
}
_write(CS0_FEATURES, FEATURE_TRANSFER_MODE);
_write(CS0_COUNT, (1 << 3) | mode);
error = _command(ATA_SET_FEATURES, false);
if (error)
return error;
LOG("done, stat=0x%02x", _read(CS0_STATUS));
flags |= DEVICE_READY;
return NO_ERROR;
}
DeviceError Device::_ideReadWrite(
uint32_t ptr, uint64_t lba, size_t count, bool write
) {
if (flags & DEVICE_ATAPI)
return UNSUPPORTED_OP;
uint8_t cmd;
size_t maxLength;
if (flags & DEVICE_HAS_LBA48) {
cmd = write ? ATA_WRITE_SECTORS_EXT : ATA_READ_SECTORS_EXT;
maxLength = 1 << 16;
} else {
cmd = write ? ATA_WRITE_SECTORS : ATA_READ_SECTORS;
maxLength = 1 << 8;
}
while (count) {
size_t length = util::min(count, maxLength);
_setLBA(lba, count);
auto error = _command(cmd);
if (error)
return error;
//error = _transferDMA(reinterpret_cast<void *>(ptr), length, write);
error = _transferPIO(reinterpret_cast<void *>(ptr), length, write);
if (error)
return error;
error = _waitForStatus(
CS0_STATUS_BSY | CS0_STATUS_DRDY, CS0_STATUS_DRDY, _STATUS_TIMEOUT
);
if (error)
return error;
ptr += length;
count -= length;
}
return NO_ERROR;
}
DeviceError Device::flushCache(void) {
if (!(flags & DEVICE_HAS_FLUSH))
return NO_ERROR;
//return UNSUPPORTED_OP;
_select(CS0_DEVICE_SEL_LBA);
return _command(
(flags & DEVICE_HAS_LBA48) ? ATA_FLUSH_CACHE_EXT : ATA_FLUSH_CACHE
);
}
/* FatFs API glue */
extern "C" DSTATUS disk_initialize(uint8_t drive) {
return RES_NOTRDY;
if (!devices[drive].enumerate())
return RES_NOTRDY;
return disk_status(drive);
}
extern "C" DSTATUS disk_status(uint8_t drive) {
return RES_NOTRDY;
auto &dev = devices[drive];
uint32_t flags = 0;
if (dev.flags & DEVICE_READY)
flags |= STA_NOINIT;
if (!dev.capacity)
flags |= STA_NODISK;
if (dev.flags & DEVICE_READ_ONLY)
flags |= STA_PROTECT;
return flags;
}
extern "C" DRESULT disk_read(uint8_t drive, uint8_t *data, uint64_t lba, size_t length) {
return RES_NOTRDY;
extern "C" DRESULT disk_read(
uint8_t drive, uint8_t *data, LBA_t lba, size_t count
) {
auto &dev = devices[drive];
if (!(dev.flags & DEVICE_READY))
return RES_NOTRDY;
if (dev.ideRead(data, lba, count))
return RES_ERROR;
return RES_OK;
}
extern "C" DRESULT disk_write(uint8_t drive, const uint8_t *data, uint64_t lba, size_t length) {
return RES_NOTRDY;
extern "C" DRESULT disk_write(
uint8_t drive, const uint8_t *data, LBA_t lba, size_t count
) {
auto &dev = devices[drive];
if (!(dev.flags & DEVICE_READY))
return RES_NOTRDY;
if (dev.flags & DEVICE_READ_ONLY)
return RES_WRPRT;
if (dev.ideWrite(data, lba, count))
return RES_ERROR;
return RES_OK;
}
extern "C" DRESULT disk_ioctl(uint8_t drive, uint8_t cmd, void *buff) {
extern "C" DRESULT disk_ioctl(uint8_t drive, uint8_t cmd, void *data) {
auto &dev = devices[drive];
if (!(dev.flags & DEVICE_READY))
return RES_NOTRDY;
switch (cmd) {
case CTRL_SYNC:
return RES_NOTRDY;
return dev.flushCache() ? RES_ERROR : RES_OK;
case GET_SECTOR_COUNT:
return RES_NOTRDY;
__builtin_memcpy(data, &dev.capacity, sizeof(LBA_t));
return RES_OK;
case GET_SECTOR_SIZE:
return RES_NOTRDY;
case GET_BLOCK_SIZE:
return RES_NOTRDY;
case CTRL_TRIM:
return RES_NOTRDY;
//case GET_BLOCK_SIZE:
*reinterpret_cast<uint16_t *>(data) = (dev.flags & DEVICE_ATAPI)
? ATAPI_SECTOR_SIZE : ATA_SECTOR_SIZE;
return RES_OK;
default:
return RES_ERROR;
return RES_PARERR;
}
}
extern "C" uint32_t get_fattime(void) {
return 0;
return io::getRTCTime();
}
}

View File

@ -1,8 +1,10 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include "ps1/registers.h"
#include "util.hpp"
namespace ide {
@ -16,7 +18,7 @@ enum CS0Register {
CS0_SECTOR = 3,
CS0_CYLINDER_L = 4,
CS0_CYLINDER_H = 5,
CS0_DRIVE_SEL = 6,
CS0_DEVICE_SEL = 6,
CS0_STATUS = 7,
CS0_COMMAND = 7
};
@ -26,41 +28,80 @@ enum CS1Register {
CS1_DEVICE_CTRL = 6
};
enum StatusFlag : uint8_t {
STATUS_ERR = 1 << 0, // Error
STATUS_DRQ = 1 << 3, // Data request
STATUS_DSC = 1 << 4, // Ready (ATA) / service (ATAPI)
STATUS_DF = 1 << 5, // Device fault
STATUS_DRDY = 1 << 6, // Device ready (ATA only)
STATUS_BSY = 1 << 7 // Busy
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
};
/* IDE protocol definitions */
enum IDECommand : uint_least8_t {
IDE_NOP = 0x00,
IDE_READ_PIO = 0x20,
IDE_WRITE_PIO = 0x30,
IDE_EXECUTE_DIAG = 0x90,
IDE_PACKET = 0xa0, // ATAPI only
IDE_ATAPI_IDENTIFY = 0xa1, // ATAPI only
IDE_ERASE = 0xc0,
IDE_READ_DMA = 0xc8,
IDE_WRITE_DMA = 0xca,
IDE_IDENTIFY = 0xec, // ATA only
IDE_SET_FEATURES = 0xef
enum CS0DeviceSelectFlag : uint8_t {
CS0_DEVICE_SEL_PRIMARY = 10 << 4,
CS0_DEVICE_SEL_SECONDARY = 11 << 4,
CS0_DEVICE_SEL_LBA = 1 << 6
};
enum IDEFeature : uint8_t {
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_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_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,
@ -108,36 +149,162 @@ enum ATAPIStartStopMode : uint8_t {
START_STOP_MODE_CLOSE_TRAY = 0x3
};
/* Identification blocks */
enum IdentifyDeviceFlag : uint16_t {
IDENTIFY_DEV_PACKET_LENGTH_BITMASK = 3 << 0,
IDENTIFY_DEV_PACKET_LENGTH_12 = 0 << 0,
IDENTIFY_DEV_PACKET_LENGTH_16 = 1 << 0,
IDENTIFY_DEV_DRQ_TYPE_BITMASK = 3 << 5,
IDENTIFY_DEV_DRQ_TYPE_SLOW = 0 << 5,
IDENTIFY_DEV_DRQ_TYPE_INTERRUPT = 1 << 5,
IDENTIFY_DEV_DRQ_TYPE_FAST = 2 << 5,
IDENTIFY_DEV_REMOVABLE = 1 << 7,
IDENTIFY_DEV_ATAPI_TYPE_BITMASK = 31 << 8,
IDENTIFY_DEV_ATAPI_TYPE_CDROM = 5 << 8,
IDENTIFY_DEV_ATAPI = 1 << 15
};
enum IdentifyCapabilitiesFlag : uint16_t {
IDENTIFY_CAP_FLAG_DMA = 1 << 8,
IDENTIFY_CAP_FLAG_LBA = 1 << 9,
IDENTIFY_CAP_FLAG_IORDY_DISABLE = 1 << 10,
IDENTIFY_CAP_FLAG_IORDY = 1 << 11,
IDENTIFY_CAP_FLAG_ATAPI_OVERLAP = 1 << 13,
IDENTIFY_CAP_FLAG_COMMAND_QUEUE = 1 << 14,
IDENTIFY_CAP_FLAG_DMA_INTERLEAVE = 1 << 15
};
struct IdentifyBlock {
public:
uint16_t deviceFlags; // 0
uint16_t _reserved[9];
uint16_t serialNumber[10]; // 10-19
uint16_t _reserved2[3];
uint16_t revision[4]; // 23-26
uint16_t model[20]; // 27-46
uint16_t _reserved3[2];
uint16_t capabilities; // 49
uint16_t _reserved4[3];
uint16_t timingValidityFlags; // 53
uint16_t _reserved5[5];
uint16_t multiSectorSettings; // 59
uint16_t sectorCount[2]; // 60-61
uint16_t _reserved6;
uint16_t dmaModeFlags; // 63
uint16_t pioModeFlags; // 64
uint16_t cycleTimings[4]; // 65-68
uint16_t _reserved7[2];
uint16_t atapiBusReleaseTime; // 71
uint16_t atapiServiceTime; // 72
uint16_t _reserved8[2];
uint16_t queueDepth; // 75
uint16_t _reserved9[4];
uint16_t versionMajor; // 80
uint16_t versionMinor; // 81
uint16_t commandSetFlags[7]; // 82-88
uint16_t secureEraseTimings[2]; // 89-90
uint16_t currentAPMValue; // 91
uint16_t _reserved10;
uint16_t resetResult; // 93
uint16_t currentAAMValue; // 94
uint16_t streamSettings[5]; // 95-99
uint16_t sectorCountExt[4]; // 100-103
uint16_t _reserved11[23];
uint16_t removableStatusFlags; // 127
uint16_t securityStatus; // 128
uint16_t _reserved12[31];
uint16_t cfPowerMode; // 160
uint16_t _reserved13[15];
uint16_t mediaSerialNumber[30]; // 176-205
uint16_t _reserved99[49];
uint16_t checksum; // 255
inline uint32_t getSectorCount(void) {
return sectorCount[0] | (sectorCount[1] << 16);
}
inline uint64_t getSectorCountExt(void) {
return 0
| (uint64_t(sectorCountExt[0]) << 0)
| (uint64_t(sectorCountExt[1]) << 16)
| (uint64_t(sectorCountExt[2]) << 32)
| (uint64_t(sectorCountExt[3]) << 48);
}
};
/* Device class */
enum DeviceError {
NO_ERROR = 0,
UNSUPPORTED_OP = 1,
STATUS_TIMEOUT = 2,
DRIVE_ERROR = 3,
INCOMPLETE_DATA = 4
};
enum DeviceFlag {
DEV_IS_ATAPI = 1 << 0,
DEV_LBA_LENGTH_48 = 1 << 1, // Device supports 48-bit LBA addressing
DEV_PACKET_LENGTH_16 = 1 << 2, // Device requires 16-byte ATAPI packets
DEV_PRIMARY = 0 << 4,
DEV_SECONDARY = 1 << 4
DEVICE_PRIMARY = 0 << 0,
DEVICE_SECONDARY = 1 << 0,
DEVICE_READY = 1 << 1,
DEVICE_READ_ONLY = 1 << 2,
DEVICE_ATAPI = 1 << 3,
DEVICE_CDROM = 1 << 4,
DEVICE_HAS_TRIM = 1 << 5, // Device supports TRIM/sector erasing
DEVICE_HAS_FLUSH = 1 << 6, // Device supports cache flushing
DEVICE_HAS_LBA48 = 1 << 7, // Device supports 48-bit LBA addressing
DEVICE_HAS_PACKET16 = 1 << 8 // Device requires 16-byte ATAPI packets
};
class Device {
private:
inline uint8_t _readCS0(CS0Register reg) {
inline uint8_t _read(CS0Register reg) {
return uint8_t(SYS573_IDE_CS0_BASE[reg] & 0xff);
}
inline void _writeCS0(CS0Register reg, uint8_t value) {
inline void _write(CS0Register reg, uint8_t value) {
SYS573_IDE_CS0_BASE[reg] = value;
}
inline uint8_t _readCS1(CS0Register reg) {
inline uint8_t _read(CS1Register reg) {
return uint8_t(SYS573_IDE_CS1_BASE[reg] & 0xff);
}
inline void _writeCS1(CS0Register reg, uint8_t value) {
inline void _write(CS1Register reg, uint8_t value) {
SYS573_IDE_CS1_BASE[reg] = value;
}
inline void _select(uint8_t flags) {
if (flags & DEVICE_SECONDARY)
_write(CS0_DEVICE_SEL, flags | CS0_DEVICE_SEL_SECONDARY);
else
_write(CS0_DEVICE_SEL, flags | CS0_DEVICE_SEL_PRIMARY);
}
void _setLBA(uint64_t lba, uint16_t count);
DeviceError _waitForStatus(uint8_t mask, uint8_t value, int timeout);
DeviceError _command(uint8_t cmd, bool drdy = true);
DeviceError _transferPIO(void *data, size_t length, bool write = false);
DeviceError _transferDMA(void *data, size_t length, bool write = false);
DeviceError _ideReadWrite(
uint32_t ptr, uint64_t lba, size_t count, bool write
);
public:
uint32_t flags;
char model[40], revision[8], serialNumber[20];
uint64_t capacity;
inline Device(uint32_t flags)
: flags(flags) {}
: flags(flags), capacity(0) {}
inline DeviceError ideRead(void *data, uint64_t lba, size_t count) {
return _ideReadWrite(reinterpret_cast<uint32_t>(data), lba, count, false);
}
inline DeviceError ideWrite(const void *data, uint64_t lba, size_t count) {
return _ideReadWrite(reinterpret_cast<uint32_t>(data), lba, count, true);
}
DeviceError enumerate(void);
DeviceError flushCache(void);
};
extern Device devices[2];

View File

@ -15,7 +15,7 @@ void init(void) {
_cartOutputReg = 0;
_miscOutputReg = 0x0107;
BIU_DEV0_ADDR = 0x1f000000;
BIU_DEV0_ADDR = DEV0_BASE & 0x1fffffff;
BIU_DEV0_CTRL = 0
| (7 << 0) // Write delay
| (4 << 4) // Read delay
@ -59,6 +59,32 @@ uint32_t getJAMMAInputs(void) {
return inputs ^ 0x1fffffff;
}
uint32_t getRTCTime(void) {
SYS573_RTC_CTRL |= SYS573_RTC_CTRL_READ;
int year = SYS573_RTC_YEAR, month = SYS573_RTC_MONTH, day = SYS573_RTC_DAY;
int hour = SYS573_RTC_HOUR, min = SYS573_RTC_MINUTE, sec = SYS573_RTC_SECOND;
year = (year & 15) + 10 * ((year >> 4) & 15); // 0-99
month = (month & 15) + 10 * ((month >> 4) & 1); // 1-12
day = (day & 15) + 10 * ((day >> 4) & 3); // 1-31
hour = (hour & 15) + 10 * ((hour >> 4) & 3); // 0-23
min = (min & 15) + 10 * ((min >> 4) & 7); // 0-59
sec = (sec & 15) + 10 * ((sec >> 4) & 7); // 0-59
// Return all values packed into a FAT/MS-DOS-style bitfield. Assume the
// year is always in 1995-2094 range.
int _year = (year >= 95) ? (year + 1900 - 1980) : (year + 2000 - 1980);
return 0
| (_year << 25)
| (month << 21)
| (day << 16)
| (hour << 11)
| (min << 5)
| (sec >> 1);
}
/* Digital I/O board driver */
static void _writeBitstreamLSB(const uint8_t *data, size_t length) {

View File

@ -82,12 +82,12 @@ static inline uint32_t getDIPSwitches(void) {
}
static inline bool getCartInsertionStatus(void) {
return (SIO_STAT(1) >> 7) & 1;
return (SIO_STAT(1) / SIO_STAT_DSR) & 1;
}
static inline bool getCartSerialStatus(void) {
SIO_CTRL(1) |= 1 << 5;
return (SIO_STAT(1) >> 8) & 1;
SIO_CTRL(1) |= SIO_CTRL_RTS;
return (SIO_STAT(1) / SIO_STAT_CTS) & 1;
}
/* Bitbanged I/O */
@ -99,7 +99,7 @@ static inline bool getCartInput(CartInputPin pin) {
}
static inline bool getCartSDA(void) {
return (SYS573_MISC_IN >> 2) & 1;
return (SYS573_MISC_IN / SYS573_MISC_IN_CART_SDA) & 1;
}
static inline void setCartOutput(CartOutputPin pin, bool value) {
@ -151,6 +151,7 @@ static inline void setDIO1Wire(bool value) {
void init(void);
uint32_t getJAMMAInputs(void);
uint32_t getRTCTime(void);
bool loadBitstream(const uint8_t *data, size_t length);
void initKonamiBitstream(void);

View File

@ -4,6 +4,7 @@
#include "app/app.hpp"
#include "ps1/gpucmd.h"
#include "ps1/system.h"
#include "vendor/ff.h"
#include "asset.hpp"
#include "defs.hpp"
#include "gpu.hpp"
@ -23,8 +24,11 @@ int main(int argc, const char **argv) {
int width = 320, height = 240;
const void *ptr = nullptr;
size_t length = 0;
const void *resPtr = nullptr;
size_t resLength = 0;
char mountPath[16];
strcpy(mountPath, "1:");
#ifdef ENABLE_ARGV
for (; argc > 0; argc--) {
@ -47,6 +51,10 @@ int main(int argc, const char **argv) {
util::logger.enableSyslog = true;
break;
case "mount"_h:
__builtin_strncpy(mountPath, &arg[6], sizeof(mountPath));
break;
case "screen.width"_h:
width = int(strtol(&arg[13], nullptr, 0));
break;
@ -58,13 +66,13 @@ int main(int argc, const char **argv) {
// Allow the default assets to be overridden by passing a pointer to
// an in-memory ZIP file as a command-line argument.
case "resources.ptr"_h:
ptr = reinterpret_cast<const void *>(
resPtr = reinterpret_cast<const void *>(
strtol(&arg[14], nullptr, 16)
);
break;
case "resources.length"_h:
length = size_t(strtol(&arg[17], nullptr, 16));
resLength = size_t(strtol(&arg[17], nullptr, 16));
break;
}
}
@ -79,10 +87,20 @@ int main(int argc, const char **argv) {
LOG("build " VERSION_STRING " (" __DATE__ " " __TIME__ ")");
LOG("(C) 2022-2023 spicyjpeg");
io::clearWatchdog();
FATFS fat;
auto error = f_mount(&fat, mountPath, true);
if (error)
LOG("FAT init error, code=%d", error);
io::clearWatchdog();
asset::AssetLoader loader;
if (ptr && length)
loader.openMemory(ptr, length);
if (resPtr && resLength)
loader.openMemory(resPtr, resLength);
if (!loader.ready)
loader.openFAT("1:/cart_tool/resources.zip");
//if (!loader.ready)

View File

@ -85,7 +85,7 @@ typedef enum {
} SIOStatusFlag;
typedef enum {
SIO_MODE_BAUD_MASK = 3 << 0,
SIO_MODE_BAUD_BITMASK = 3 << 0,
SIO_MODE_BAUD_DIV1 = 1 << 0,
SIO_MODE_BAUD_DIV16 = 2 << 0,
SIO_MODE_BAUD_DIV64 = 3 << 0,
@ -191,12 +191,12 @@ typedef enum {
} DMACHCRFlag;
typedef enum {
DMA_DICR_CH_MODE_MASK = 0x7f << 0,
DMA_DICR_BUS_ERROR = 1 << 15,
DMA_DICR_CH_ENABLE_MASK = 0x7f << 16,
DMA_DICR_IRQ_ENABLE = 1 << 23,
DMA_DICR_CH_STAT_MASK = 0x7f << 24,
DMA_DICR_IRQ = 1 << 31
DMA_DICR_CH_MODE_BITMASK = 0x7f << 0,
DMA_DICR_BUS_ERROR = 1 << 15,
DMA_DICR_CH_ENABLE_BITMASK = 0x7f << 16,
DMA_DICR_IRQ_ENABLE = 1 << 23,
DMA_DICR_CH_STAT_BITMASK = 0x7f << 24,
DMA_DICR_IRQ = 1 << 31
} DMADICRFlag;
#define DMA_DICR_CH_MODE(dma) (1 << ((dma) + 0))
@ -468,6 +468,25 @@ typedef enum {
#define SYS573_RTC_BASE _ADDR16(DEV0_BASE | 0x620000)
#define SYS573_IO_BASE _ADDR16(DEV0_BASE | 0x640000)
/* System 573 RTC */
typedef enum {
SYS573_RTC_CTRL_CAL_BITMASK = 31 << 0,
SYS573_RTC_CTRL_CAL_POSITIVE = 0 << 5,
SYS573_RTC_CTRL_CAL_NEGATIVE = 1 << 5,
SYS573_RTC_CTRL_READ = 1 << 6,
SYS573_RTC_CTRL_WRITE = 1 << 7
} Sys573RTCControlFlag;
#define SYS573_RTC_CTRL _MMIO16(DEV0_BASE | 0x623ff0)
#define SYS573_RTC_SECOND _MMIO16(DEV0_BASE | 0x623ff2)
#define SYS573_RTC_MINUTE _MMIO16(DEV0_BASE | 0x623ff4)
#define SYS573_RTC_HOUR _MMIO16(DEV0_BASE | 0x623ff6)
#define SYS573_RTC_WEEKDAY _MMIO16(DEV0_BASE | 0x623ff8)
#define SYS573_RTC_DAY _MMIO16(DEV0_BASE | 0x623ffa)
#define SYS573_RTC_MONTH _MMIO16(DEV0_BASE | 0x623ffc)
#define SYS573_RTC_YEAR _MMIO16(DEV0_BASE | 0x623ffe)
/* System 573 analog I/O board */
#define SYS573A_LIGHTS_A _MMIO16(DEV0_BASE | 0x640080)

View File

@ -20,9 +20,10 @@
#include "ps1/registers.h"
#include "ps1/system.h"
#define BIOS_API_TABLE ((VoidFunction *) 0x80000200)
#define BIOS_BP_VECTOR ((uint32_t *) 0x80000040)
#define BIOS_EXC_VECTOR ((uint32_t *) 0x80000080)
#define BIOS_ENTRY_POINT ((VoidFunction) 0xbfc00000)
#define BIOS_API_TABLE ((VoidFunction *) 0x80000200)
#define BIOS_BP_VECTOR ((uint32_t *) 0x80000040)
#define BIOS_EXC_VECTOR ((uint32_t *) 0x80000080)
/* Internal state */
@ -45,7 +46,7 @@ void installExceptionHandler(void) {
IRQ_MASK = 0;
IRQ_STAT = 0;
DMA_DPCR = 0;
DMA_DICR = DMA_DICR_CH_STAT_MASK;
DMA_DICR = DMA_DICR_CH_STAT_BITMASK;
// Ensure interrupts and the GTE are enabled at the COP0 side.
uint32_t sr = SR_IEc | SR_Im2 | SR_CU0 | SR_CU2;
@ -85,6 +86,10 @@ void flushCache(void) {
setInterruptMask(mask);
}
void softReset(void) {
BIOS_ENTRY_POINT();
}
/* IRQ acknowledgement and blocking delay */
void delayMicroseconds(int us) {

View File

@ -142,6 +142,11 @@ void setInterruptHandler(ArgFunction func, void *arg);
*/
void flushCache(void);
/**
* @brief Jumps to the entry point in the BIOS. This function does not return.
*/
void softReset(void);
/**
* @brief Blocks for (roughly) the specified number of microseconds. This
* function does not rely on a hardware timer, so interrupts may throw off

View File

@ -112,6 +112,7 @@ void resetAllChannels(void) {
size_t upload(uint32_t ramOffset, const void *data, size_t length, bool wait) {
length /= 4;
util::assertAligned<uint32_t>(data);
assert(!(length % _DMA_CHUNK_SIZE));
length = (length + _DMA_CHUNK_SIZE - 1) / _DMA_CHUNK_SIZE;

View File

@ -1,6 +1,7 @@
#pragma once
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include "ps1/system.h"
@ -54,6 +55,10 @@ static inline uint32_t swapEndian(uint32_t value) {
return value;
}
template<typename T, typename X> inline void assertAligned(X *ptr) {
assert(!(reinterpret_cast<uintptr_t>(ptr) % alignof(T)));
}
template<typename T, typename X> static constexpr inline T forcedCast(X item) {
return reinterpret_cast<T>(reinterpret_cast<void *>(item));
}

View File

@ -30,7 +30,7 @@
#define FF_MAX_SS 4096
#define FF_LBA64 1
#define FF_MIN_GPT 0x10000000
#define FF_USE_TRIM 1
#define FF_USE_TRIM 0
#define FF_FS_TINY 0
#define FF_FS_EXFAT 1