aboutsummaryrefslogtreecommitdiff
path: root/libpsn00b/psxcd/cdread.c
diff options
context:
space:
mode:
authorspicyjpeg <thatspicyjpeg@gmail.com>2022-11-17 22:20:38 +0100
committerspicyjpeg <thatspicyjpeg@gmail.com>2022-11-17 22:20:38 +0100
commitfc021dc6d6f06f8eff7edc72929faddd1e1f0d1b (patch)
treec088d292c6cc974854c2e7d3a4024c4951ef3602 /libpsn00b/psxcd/cdread.c
parent85d765f30595fe7f27c1b065c5a1934c3d389cef (diff)
downloadpsn00bsdk-fc021dc6d6f06f8eff7edc72929faddd1e1f0d1b.tar.gz
Refactor libpsxcd, add new CD-ROM APIs, fix SPU DMA read
Diffstat (limited to 'libpsn00b/psxcd/cdread.c')
-rw-r--r--libpsn00b/psxcd/cdread.c168
1 files changed, 168 insertions, 0 deletions
diff --git a/libpsn00b/psxcd/cdread.c b/libpsn00b/psxcd/cdread.c
new file mode 100644
index 0000000..d211a01
--- /dev/null
+++ b/libpsn00b/psxcd/cdread.c
@@ -0,0 +1,168 @@
+/*
+ * PSn00bSDK CD-ROM library (high-level reading API)
+ * (C) 2020-2022 Lameguy64, spicyjpeg - MPL licensed
+ *
+ * CdRead() and its related functions are separate from the "main" psxcd code
+ * since handling retries is fairly complicated. In particular the drive
+ * controller will not process any command properly for some time after a
+ * CdlPause command, so an external timer (the vblank counter) and manual
+ * polling are required to defer the next attempt.
+ */
+
+#include <stdint.h>
+#include <assert.h>
+#include <psxgpu.h>
+#include <psxapi.h>
+#include <psxcd.h>
+
+#define CD_READ_TIMEOUT 180
+#define CD_READ_COOLDOWN 60
+
+/* Internal globals */
+
+static CdlCB _read_callback = (CdlCB) 0;
+
+static int _total_sectors, _sector_size;
+static uint8_t _read_result[4];
+
+static volatile uint32_t *_read_addr;
+static volatile int _read_timeout, _pending_attempts, _pending_sectors;
+
+extern CdlCB _cd_override_callback;
+
+/* Private utilities and sector callback */
+
+static void _sector_callback(CdlIntrResult irq, uint8_t *result) {
+ if (irq == CdlDataReady) {
+ CdGetSector((void *) _read_addr, _sector_size);
+ _read_addr += _sector_size;
+
+ if (--_pending_sectors > 0) {
+ _read_timeout = VSync(-1) + CD_READ_TIMEOUT;
+ return;
+ }
+ }
+
+ // Stop reading if an error occurred or if no more sectors need to be read.
+ CdCommandF(CdlPause, 0, 0);
+
+ _cd_override_callback = (CdlCB) 0;
+ if (!_pending_attempts && _read_callback)
+ _read_callback(irq, result);
+
+ _read_timeout = VSync(-1) + CD_READ_COOLDOWN;
+}
+
+static int _poll_retry(void) {
+ if (!_pending_attempts) {
+ _sdk_log("CdRead() failed, too many attempts\n");
+
+ _pending_sectors = 0;
+ return -1;
+ }
+
+ //CdControlB(CdlPause, 0, 0);
+
+ _sdk_log("CdRead() failed, retrying (%d sectors pending)\n", _pending_sectors);
+ _pending_attempts--;
+
+ // Restart from the first sector that returned an error.
+ CdlLOC pos;
+ CdIntToPos(
+ CdPosToInt(CdLastPos()) + _total_sectors - _pending_sectors,
+ &pos
+ );
+
+ _read_timeout = VSync(-1) + CD_READ_TIMEOUT;
+ _total_sectors = _pending_sectors;
+
+ FastEnterCriticalSection();
+ _cd_override_callback = &_sector_callback;
+ FastExitCriticalSection();
+
+ if (CdCommand(CdlSetloc, (uint8_t *) &pos, 3, _read_result))
+ CdCommand(CdlReadN, 0, 0, _read_result);
+
+ return _pending_sectors;
+}
+
+/* Public API */
+
+int CdReadRetry(int sectors, uint32_t *buf, int mode, int attempts) {
+ if (CdReadSync(1, 0) > 0) {
+ _sdk_log("CdRead() failed, another read in progress (%d sectors pending)\n", _pending_sectors);
+ return 0;
+ }
+
+ _read_addr = buf;
+ _read_timeout = VSync(-1) + CD_READ_TIMEOUT;
+ _pending_attempts = attempts - 1;
+ _pending_sectors = sectors;
+ _total_sectors = sectors;
+ _sector_size = (mode & CdlModeSize) ? 585 : 512;
+
+ FastEnterCriticalSection();
+ _cd_override_callback = &_sector_callback;
+ FastExitCriticalSection();
+
+ uint8_t _mode = mode;
+ if (!CdCommand(CdlSetmode, &_mode, 1, 0))
+ return 0;
+ if (!CdCommand(CdlReadN, 0, 0, _read_result))
+ return 0;
+
+ return 1;
+}
+
+int CdRead(int sectors, uint32_t *buf, int mode) {
+ return CdReadRetry(sectors, buf, mode, 1);
+}
+
+void CdReadBreak(void) {
+ if (_pending_sectors > 0)
+ _pending_sectors = -1;
+}
+
+int CdReadSync(int mode, uint8_t *result) {
+ if (mode) {
+ if (_pending_sectors < 0)
+ return -2;
+ if (!_pending_sectors)
+ return 0;
+
+ if (VSync(-1) > _read_timeout)
+ return _poll_retry();
+ if (CdSync(1, 0) == CdlDiskError)
+ return -1;
+
+ return _pending_sectors;
+ }
+
+ while (_pending_sectors > 0) {
+ if (VSync(-1) > _read_timeout) {
+ if (_poll_retry() < 0)
+ return -1;
+ }
+
+ //if (CdSync(1, 0) == CdlDiskError)
+ //return -1;
+ }
+
+ CdlIntrResult status = CdSync(0, result);
+ if (_pending_sectors < 0)
+ return -2;
+ if (status != CdlComplete)
+ return -1;
+
+ return 0;
+}
+
+CdlCB CdReadCallback(CdlCB func) {
+ FastEnterCriticalSection();
+
+ CdlCB old_callback = _read_callback;
+ _read_callback = func;
+
+ FastExitCriticalSection();
+ return old_callback;
+}