mirror of
https://github.com/spicyjpeg/573in1.git
synced 2025-01-22 19:52:05 +01:00
Implement IDE HDD driver, clean up I/O code
This commit is contained in:
parent
3b9680f434
commit
284280689f
@ -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);
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
360
src/ide.cpp
360
src/ide.cpp
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
235
src/ide.hpp
235
src/ide.hpp
@ -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];
|
||||
|
28
src/io.cpp
28
src/io.cpp
@ -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) {
|
||||
|
@ -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);
|
||||
|
30
src/main.cpp
30
src/main.cpp
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
2
src/vendor/vendorconfig.h
vendored
2
src/vendor/vendorconfig.h
vendored
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user