diff options
| author | spicyjpeg <thatspicyjpeg@gmail.com> | 2022-11-17 22:20:38 +0100 |
|---|---|---|
| committer | spicyjpeg <thatspicyjpeg@gmail.com> | 2022-11-17 22:20:38 +0100 |
| commit | fc021dc6d6f06f8eff7edc72929faddd1e1f0d1b (patch) | |
| tree | c088d292c6cc974854c2e7d3a4024c4951ef3602 /libpsn00b/psxcd | |
| parent | 85d765f30595fe7f27c1b065c5a1934c3d389cef (diff) | |
| download | psn00bsdk-fc021dc6d6f06f8eff7edc72929faddd1e1f0d1b.tar.gz | |
Refactor libpsxcd, add new CD-ROM APIs, fix SPU DMA read
Diffstat (limited to 'libpsn00b/psxcd')
| -rw-r--r-- | libpsn00b/psxcd/_cd_control.s | 144 | ||||
| -rw-r--r-- | libpsn00b/psxcd/cdmix.s | 34 | ||||
| -rw-r--r-- | libpsn00b/psxcd/cdread.c | 168 | ||||
| -rw-r--r-- | libpsn00b/psxcd/common.c | 435 | ||||
| -rw-r--r-- | libpsn00b/psxcd/getsector.c | 51 | ||||
| -rw-r--r-- | libpsn00b/psxcd/isofs.c | 72 | ||||
| -rw-r--r-- | libpsn00b/psxcd/misc.c | 151 | ||||
| -rw-r--r-- | libpsn00b/psxcd/psxcd.c | 371 | ||||
| -rw-r--r-- | libpsn00b/psxcd/psxcd_asm.s | 487 | ||||
| -rw-r--r-- | libpsn00b/psxcd/readme.txt | 40 |
10 files changed, 800 insertions, 1153 deletions
diff --git a/libpsn00b/psxcd/_cd_control.s b/libpsn00b/psxcd/_cd_control.s deleted file mode 100644 index 5fa336a..0000000 --- a/libpsn00b/psxcd/_cd_control.s +++ /dev/null @@ -1,144 +0,0 @@ -.set noreorder - -.include "hwregs_a.inc" - -.section .text - -# -# Issues command and parameter bytes to CD controller directly -# -.global _cd_control -.type _cd_control, @function -_cd_control: - - # a0 - command value - # a1 - pointer to parameters - # a2 - length of parameters - - addiu $sp, -16 - sw $ra, 0($sp) - sw $a0, 4($sp) - sw $a1, 8($sp) - sw $a2, 12($sp) - -# move $a3, $a2 # Debug -# move $a2, $a1 -# move $a1, $a0 -# la $a0, _cd_control_msg -# jal printf -# addiu $sp, -16 -# addiu $sp, 16 - - lw $a0, 4($sp) - lw $a1, 8($sp) - lw $a2, 12($sp) - - li $v0, 1 # Set acknowledge wait flag - la $v1, _cd_ack_wait - sb $v0, 0($v1) - - # Commands that have a 'completion' interrupt (CDIRQ2) - - beq $a0, 0x07, .Lset_complete # CdlStandby - nop - beq $a0, 0x08, .Lset_complete # CdlStop - nop - beq $a0, 0x09, .Lset_complete # CdlPause - nop - beq $a0, 0x0A, .Lset_complete # CdlInit - nop - beq $a0, 0x12, .Lset_complete # CdlSetsession - nop - beq $a0, 0x15, .Lset_complete # CdlSeekL - nop - beq $a0, 0x16, .Lset_complete # CdlSeekP - nop - beq $a0, 0x1A, .Lset_complete # GetID - nop - beq $a0, 0x1D, .Lset_complete # GetQ - nop - - la $v1, _cd_complt_wait # Set wait complete flag - sb $0 , 0($v1) - - b .Lno_complete - nop - -.Lset_complete: - - la $v1, _cd_complt_wait # Set wait complete flag - sb $v0, 0($v1) - -.Lno_complete: - - bne $a0, 0x0E, .Lnot_mode - lbu $v0, 0($a1) - la $v1, _cd_last_mode - sb $v0, 0($v1) - -.Lnot_mode: - - la $v1, _cd_last_int # Clear last IRQ value - sb $0 , 0($v1) - - la $v1, _cd_last_cmd # Save command as last command - sb $a0, 0($v1) - - lui $v1, IOBASE - -.Lbusy_wait: - lbu $v0, CD_REG0($v1) - nop - andi $v0, 0x80 - bnez $v0, .Lbusy_wait - nop - - li $v0, 1 # Clear parameter FIFO (in case it wasn't cleared) - sb $v0, CD_REG0($v1) - li $v0, 0x40 - sb $v0, CD_REG3($v1) - -#.Lflush_response: # Flush response FIFO -# lbu $v0, CD_REG1($v1) -# nop -# lbu $v0, CD_REG0($v1) -# nop -# andi $v0, 0x20 -# beqz $v0, .Lflush_response -# nop - -.Lcmd_wait: # Wait for CD to become ready for commands - lbu $v0, CD_REG0($v1) - nop - andi $v0, 0x80 - bnez $v0, .Lcmd_wait - nop - - sb $0 , CD_REG0($v1) - - beqz $a2, .Lno_params - nop - -.Lfeed_params: # Feed parameters to parameter FIFO - lbu $v0, 0($a1) - addi $a2, -1 - sb $v0, CD_REG2($v1) - bgtz $a2, .Lfeed_params - addiu $a1, 1 - -.Lno_params: - - sb $0 , CD_REG0($v1) # Feed command value - sb $a0, CD_REG1($v1) - - lw $ra, 0($sp) - addiu $sp, 16 - jr $ra - nop - - -#.section .data - -#_cd_control_msg: -# .asciiz "CdControl(%d, %x, %d)\n" -
\ No newline at end of file diff --git a/libpsn00b/psxcd/cdmix.s b/libpsn00b/psxcd/cdmix.s deleted file mode 100644 index 40cd181..0000000 --- a/libpsn00b/psxcd/cdmix.s +++ /dev/null @@ -1,34 +0,0 @@ -.set noreorder - -.include "hwregs_a.inc" - -.section .text - - -.global CdMix -.type CdMix, @function -CdMix: - - lui $a3, IOBASE - - lbu $t0, 0($a0) - lbu $t1, 1($a0) - lbu $t2, 2($a0) - lbu $t3, 3($a0) - - li $v0, 2 - sb $v0, CD_REG0($a3) - sb $t0, CD_REG2($a3) - sb $t1, CD_REG3($a3) - - li $v0, 3 - sb $v0, CD_REG0($a3) - sb $t2, CD_REG1($a3) - sb $t3, CD_REG2($a3) - - li $v0, 0x20 - sb $v0, CD_REG3($a3) - - jr $ra - li $v0, 1 -
\ No newline at end of file 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; +} diff --git a/libpsn00b/psxcd/common.c b/libpsn00b/psxcd/common.c new file mode 100644 index 0000000..6b20df2 --- /dev/null +++ b/libpsn00b/psxcd/common.c @@ -0,0 +1,435 @@ +/* + * PSn00bSDK CD-ROM library (common functions) + * (C) 2020-2022 Lameguy64, spicyjpeg - MPL licensed + */ + +#include <stdint.h> +#include <assert.h> +#include <psxetc.h> +#include <psxapi.h> +#include <psxcd.h> +#include <hwregs_c.h> + +#define CD_ACK_TIMEOUT 0x100000 +#define CD_SYNC_TIMEOUT 0x100000 + +/* Internal globals */ + +static CdlCB _ready_callback = (CdlCB) 0; +static CdlCB _sync_callback = (CdlCB) 0; +static CdlCB _pause_callback = (CdlCB) 0; + +static uint8_t *_result_ptr; +static uint8_t _last_command, _last_mode; +static CdlLOC _last_pos; + +static volatile uint8_t _last_status, _last_irq, _last_error; +static volatile uint8_t _ack_pending, _sync_pending; + +// These globals are accessed by other parts of the library. +CdlCB _cd_override_callback; +volatile int _cd_media_changed; + +/* Command metadata */ + +// The original implementation of CdControlF() and the IRQ handler used a +// ridiculous number of branches to account for command-specific behavior. +// Storing that information as an array of bitfields is far more efficient. +typedef enum { + PARAM_1 = 1, + PARAM_2 = 2, + PARAM_3 = 3, + STATUS = 1 << 2, // First byte of CdlAcknowledge response is status + C_STATUS = 1 << 3, // First byte of CdlComplete response is status + BLOCKING = 1 << 4, // Command triggers CdlComplete interrupt + OPTIONAL = 1 << 5, // Parameter is optional for the command + SETLOC = 1 << 6 // Parameter shall be sent as a separate CdlSetloc +} CommandFlags; + +// https://problemkaputt.de/psx-spx.htm#cdromcontrollercommandsummary +static const uint8_t _command_flags[] = { + 0, + STATUS, // CdlNop + STATUS | PARAM_3, // CdlSetloc + STATUS | OPTIONAL | PARAM_1, // CdlPlay + //STATUS | SETLOC, // CdlPlay + STATUS, // CdlForward + STATUS, // CdlBackward + STATUS | SETLOC, // CdlReadN + STATUS | C_STATUS | BLOCKING, // CdlStandby + STATUS | C_STATUS | BLOCKING, // CdlStop + STATUS | C_STATUS | BLOCKING, // CdlPause + STATUS | C_STATUS | BLOCKING, // CdlInit + STATUS, // CdlMute + STATUS, // CdlDemute + STATUS | PARAM_2, // CdlSetfilter + STATUS | PARAM_1, // CdlSetmode + STATUS, // CdlGetparam + 0, // CdlGetlocL + 0, // CdlGetlocP + STATUS | C_STATUS | BLOCKING | PARAM_1, // CdlSetsession + STATUS, // CdlGetTN + STATUS | PARAM_1, // CdlGetTD + STATUS | C_STATUS | BLOCKING | SETLOC, // CdlSeekL + STATUS | C_STATUS | BLOCKING | SETLOC, // CdlSeekP + 0, + 0, + PARAM_1, // CdlTest + STATUS | C_STATUS | BLOCKING, // CdlGetID + STATUS | SETLOC, // CdlReadS + STATUS, // CdlReset + STATUS | BLOCKING | PARAM_2, // CdlGetQ + STATUS | C_STATUS | BLOCKING // CdlReadTOC +}; + +/* Private interrupt handler */ + +static void _update_status(uint8_t status) { + uint8_t last = _last_status; + _last_status = status; + + if (!(last & CdlStatError) && (status & CdlStatError)) + _sdk_log("drive error, status=0x%02x, code=0x%02x\n", _last_status, _last_error); + + if (!(last & CdlStatShellOpen) && (status & CdlStatShellOpen)) { + _sdk_log("shell opened, invalidating cache\n"); + _cd_media_changed = 1; + } +} + +static void _cd_irq_handler(void) { + CD_REG(0) = 1; + CdlIntrResult irq = CD_REG(3) & 7; + + if (irq == CdlDataReady) { + // TODO: are the first 4 accesses really needed, or was this just + // Sony's (dumb) way to flush the KUSEG write queue? We definitely + // don't need to do that since we're using KSEG1. + CD_REG(0) = 0; + CD_REG(0); + CD_REG(3) = 0; + CD_REG(3); + CD_REG(0) = 0; + CD_REG(3) = 0x80; // Request data + } + + CD_REG(0) = 1; + CD_REG(3) = 0x1f; // Acknowledge all IRQs + CD_REG(3) = 0x40; // Reset parameter buffer + + //while (CD_REG(0) & (1 << 5)) + //CD_REG(1); + for (int i = 0; i < 50; i++) + __asm__ volatile(""); + + if (!irq || (irq > CdlDiskError)) + return; + + // Fetch the result from the drive if a buffer was previously set up. The + // first byte is always read as it contains the drive status for most + // commands. + uint8_t first_byte = CD_REG(1); + CdlCB callback; + + if (_result_ptr) { + _result_ptr[0] = first_byte; + + for (int i = 1; (CD_REG(0) & 0x20) && (i < 8); i++) + _result_ptr[i] = CD_REG(1); + } + + switch (irq) { + case CdlDataReady: + // CdRead() can override any callback set using CdReadyCallback() + // by setting _cd_override_callback. + callback = _cd_override_callback; + if (!callback) + callback = _ready_callback; + + _update_status(first_byte); + break; + + case CdlComplete: + _sync_pending = 0; + callback = _sync_callback; + + if (_last_command <= CdlReadTOC) { + if (_command_flags[_last_command] & C_STATUS) + _update_status(first_byte); + } + break; + + case CdlAcknowledge: + _ack_pending = 0; + callback = (CdlCB) 0; + + if (_last_command <= CdlReadTOC) { + if (_command_flags[_last_command] & STATUS) + _update_status(first_byte); + } + break; + + case CdlDataEnd: + callback = _pause_callback; + + _update_status(first_byte); + break; + + case CdlDiskError: + _last_error = CD_REG(1); + callback = _ready_callback; + + if (_ack_pending || _sync_pending) { + if (_sync_callback) + _sync_callback(irq, _result_ptr); + + _ack_pending = 0; + _sync_pending = 0; + } + + _update_status(first_byte); + break; + } + + if (callback) + callback(irq, _result_ptr); + + _last_command = 0; + _last_irq = irq; + _result_ptr = (uint8_t *) 0; +} + +/* Initialization */ + +int CdInit(void) { + EnterCriticalSection(); + InterruptCallback(IRQ_CD, &_cd_irq_handler); + ExitCriticalSection(); + + CD_DELAY_SIZE = 0x00020943; + + CD_REG(0) = 1; + CD_REG(3) = 0x1f; // Acknowledge all IRQs + CD_REG(2) = 0x1f; // Enable all IRQs + CD_REG(0) = 0; + CD_REG(3) = 0x00; // Clear any pending request + + CdlATV mix = { 0x80, 0x00, 0x80, 0x00 }; + CdMix(&mix); + + DMA_DPCR |= 0x0000b000; // Enable DMA3 + DMA_CHCR(DMA_CD) = 0x00000000; // Stop DMA3 + + _last_mode = 0; + _ack_pending = 0; + _sync_pending = 0; + + _cd_override_callback = (CdlCB) 0; + _cd_media_changed = 1; + + // Initialize the drive. + CdCommand(CdlNop, 0, 0, 0); + CdCommand(CdlInit, 0, 0, 0); + + if (CdSync(0, 0) == CdlDiskError) { + _sdk_log("setup error, bad disc/drive or no disc inserted\n"); + return 0; + } + + CdCommand(CdlDemute, 0, 0, 0); + _sdk_log("setup done\n"); + return 1; +} + +/* Low-level command API */ + +int CdCommandF(CdlCommand cmd, const void *param, int length) { + const uint8_t *_param = (const uint8_t *) param; + + _last_command = (uint8_t) cmd; + _ack_pending = 1; + + if (cmd <= CdlReadTOC) { + if (_command_flags[cmd] & BLOCKING) + _sync_pending = 1; + + // Keep track of the last mode and seek location set (so retries can be + // attempted). + + if (cmd == CdlSetloc) { + _last_pos.minute = _param[0]; + _last_pos.second = _param[1]; + _last_pos.sector = _param[2]; + } else if (cmd == CdlSetmode) { + _last_mode = _param[0]; + } + } + + // Request a command FIFO write. + while (CD_REG(0) & 0x80) + __asm__ volatile(""); + + CD_REG(0) = 1; + CD_REG(3) = 0x40; // Reset parameter buffer + + //while (CD_REG(0) & (1 << 5)) + //CD_REG(1); + for (int i = 0; i < 50; i++) + __asm__ volatile(""); + + // Wait for the FIFO to become ready, then send the parameters followed by + // the command index. + while (CD_REG(0) & (1 << 7)) + __asm__ volatile(""); + + CD_REG(0) = 0; + for (; length; length--) + CD_REG(2) = *(_param++); + + CD_REG(0) = 0; + CD_REG(1) = (uint8_t) cmd; + return 1; +} + +int CdCommand(CdlCommand cmd, const void *param, int length, uint8_t *result) { + /*if (_ack_pending) { + _sdk_log("CdCommand(0x%02x) failed, drive busy\n", cmd); + return 0; + }*/ + + _result_ptr = result; + CdCommandF(cmd, param, length); + + // Wait for the command to be acknowledged. + for (int i = CD_ACK_TIMEOUT; i; i--) { + if (!_ack_pending) + return 1; + } + + _sdk_log("CdCommand(0x%02x) failed, acknowledge timeout\n", cmd); + return 0; +} + +/* High-level command API */ + +int CdControlF(CdlCommand cmd, const void *param) { + // Assume no parameters need to be passed if the command is unknown. + uint8_t flags = (cmd <= CdlReadTOC) ? _command_flags[cmd] : 0; + int length; + + if (flags & OPTIONAL) { + // The command may optionally take a parameter. + length = param ? (flags & 3) : 0; + } else if (flags & SETLOC) { + // The command takes no parameter, but the API allows specifying a + // location to be sent to the drive as a separate CdlSetloc command. + length = 0; + if (param) + CdCommandF(CdlSetloc, param, 3); + } else { + // The command takes a mandatory parameter or no parameter. + length = flags & 3; + if (length && !param) + return -1; + } + + return CdCommandF(cmd, param, length); +} + +int CdControl(CdlCommand cmd, const void *param, uint8_t *result) { + /*if (_ack_pending) { + _sdk_log("CdControl(0x%02x) failed, drive busy\n", cmd); + return 0; + }*/ + + _result_ptr = result; + CdControlF(cmd, param); + + // Wait for the command to be acknowledged. + for (int i = CD_ACK_TIMEOUT; i; i--) { + if (!_ack_pending) + return 1; + } + + _sdk_log("CdControl(0x%02x) failed, acknowledge timeout\n", cmd); + return 0; +} + +int CdControlB(CdlCommand cmd, const void *param, uint8_t *result) { + int error = CdControl(cmd, param, result); + if (error != 1) + return error; + + error = CdSync(0, 0); + return (error == CdlDiskError) ? 0 : 1; +} + +/* Other APIs and callback setters */ + +CdlIntrResult CdSync(int mode, uint8_t *result) { + if (mode) { + if (_sync_pending) + return CdlNoIntr; + + if (result) + *result = _last_status; + if (_last_irq == CdlAcknowledge) + return CdlComplete; + + return _last_irq; + } + + for (int i = CD_SYNC_TIMEOUT; i; i--) { + if (!_sync_pending) + return CdSync(1, result); // :P + } + + _sdk_log("CdSync() timeout\n"); + return -1; +} + +CdlCommand CdLastCom(void) { + return _last_command; +} + +const CdlLOC *CdLastPos(void) { + return &_last_pos; +} + +int CdMode(void) { + return _last_mode; +} + +int CdStatus(void) { + return _last_status; +} + +CdlCB CdReadyCallback(CdlCB func) { + FastEnterCriticalSection(); + + CdlCB old_callback = _ready_callback; + _ready_callback = func; + + FastExitCriticalSection(); + return old_callback; +} + +CdlCB CdSyncCallback(CdlCB func) { + FastEnterCriticalSection(); + + CdlCB old_callback = _sync_callback; + _sync_callback = func; + + FastExitCriticalSection(); + return old_callback; +} + +CdlCB CdAutoPauseCallback(CdlCB func) { + FastEnterCriticalSection(); + + CdlCB old_callback = _pause_callback; + _pause_callback = func; + + FastExitCriticalSection(); + return old_callback; +} diff --git a/libpsn00b/psxcd/getsector.c b/libpsn00b/psxcd/getsector.c deleted file mode 100644 index a214d7a..0000000 --- a/libpsn00b/psxcd/getsector.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * PSn00bSDK CD drive library (sector DMA API) - * (C) 2022 spicyjpeg - MPL licensed - */ - -#include <stdint.h> -#include <assert.h> -#include <psxcd.h> -#include <hwregs_c.h> - -#define DATA_SYNC_TIMEOUT 0x100000 - -/* DMA transfer functions */ - -int CdGetSector(void *madr, int size) { - //while (!(CD_STAT & (1 << 6))) - //__asm__ volatile(""); - - DMA_MADR(3) = (uint32_t) madr; - DMA_BCR(3) = size | (1 << 16); - DMA_CHCR(3) = 0x11000000; - - while (DMA_CHCR(3) & (1 << 24)) - __asm__ volatile(""); - - return 1; -} - -int CdGetSector2(void *madr, int size) { - //while (!(CD_STAT & (1 << 6))) - //__asm__ volatile(""); - - DMA_MADR(3) = (uint32_t) madr; - DMA_BCR(3) = size | (1 << 16); - DMA_CHCR(3) = 0x11400100; // Transfer 1 word every 16 CPU cycles - - return 1; -} - -int CdDataSync(int mode) { - if (mode) - return (DMA_CHCR(3) >> 24) & 1; - - for (int i = DATA_SYNC_TIMEOUT; i; i--) { - if (!(DMA_CHCR(3) & (1 << 24))) - return 0; - } - - _sdk_log("CdDataSync() timeout\n"); - return -1; -} diff --git a/libpsn00b/psxcd/isofs.c b/libpsn00b/psxcd/isofs.c index e00ddeb..0ac782b 100644 --- a/libpsn00b/psxcd/isofs.c +++ b/libpsn00b/psxcd/isofs.c @@ -11,6 +11,7 @@ #include <psxcd.h> #include "isofs.h" +#define CD_READ_ATTEMPTS 3 #define DEFAULT_PATH_SEP '\\' #define IS_PATH_SEP(ch) (((ch) == '/') || ((ch) == '\\')) @@ -21,7 +22,7 @@ typedef struct _CdlDIR_INT uint8_t *_dir; } CdlDIR_INT; -extern int _cd_media_changed; +extern volatile int _cd_media_changed; static int _cd_iso_last_dir_lba; @@ -77,7 +78,7 @@ static int _CdReadIsoDescriptor(int session_offs) _sdk_log("Read sectors.\n"); // Read volume descriptor - CdRead(1, (uint32_t*)_cd_iso_descriptor_buff, CdlModeSpeed); + CdReadRetry(1, (uint32_t*)_cd_iso_descriptor_buff, CdlModeSpeed, CD_READ_ATTEMPTS); if( CdReadSync(0, 0) ) { @@ -115,7 +116,7 @@ static int _CdReadIsoDescriptor(int session_offs) // Read path table CdIntToPos(descriptor->pathTable1Offs, &loc); CdControl(CdlSetloc, (uint8_t*)&loc, 0); - CdRead(i>>11, (uint32_t*)_cd_iso_pathtable_buff, CdlModeSpeed); + CdReadRetry(i>>11, (uint32_t*)_cd_iso_pathtable_buff, CdlModeSpeed, CD_READ_ATTEMPTS); if( CdReadSync(0, 0) ) { _sdk_log("Error reading ISO path table.\n"); @@ -163,7 +164,7 @@ static int _CdReadIsoDirectory(int lba) // Read first sector of directory record _cd_iso_directory_buff = (uint8_t*)malloc(2048); - CdRead(1, (uint32_t*)_cd_iso_directory_buff, CdlModeSpeed); + CdReadRetry(1, (uint32_t*)_cd_iso_directory_buff, CdlModeSpeed, CD_READ_ATTEMPTS); if( CdReadSync(0, 0) ) { _sdk_log("Error reading initial directory record.\n"); @@ -194,7 +195,7 @@ static int _CdReadIsoDirectory(int lba) _sdk_log("Allocated %d bytes for directory record.\n", i); - CdRead(i>>11, (uint32_t*)_cd_iso_directory_buff, CdlModeSpeed); + CdReadRetry(i>>11, (uint32_t*)_cd_iso_directory_buff, CdlModeSpeed, CD_READ_ATTEMPTS); if( CdReadSync(0, 0) ) { _sdk_log("Error reading remaining directory record.\n"); @@ -485,13 +486,13 @@ CdlFILE *CdSearchFile(CdlFILE *fp, const char *filename) } else { - _sdk_log("Longest path: %s|\n", rbuff); + _sdk_log("Longest path: %s\n", rbuff); } #endif if( get_pathname(search_path, filename) ) { - _sdk_log("Search path = %s|\n", search_path); + _sdk_log("Search path = %s\n", search_path); } // Search the pathtable for a matching path @@ -499,7 +500,7 @@ CdlFILE *CdSearchFile(CdlFILE *fp, const char *filename) for(i=1; i<num_dirs; i++) { rbuff = resolve_pathtable_path(i, tpath_rbuff+127); - _sdk_log("Found = %s|\n", rbuff); + _sdk_log("Found = %s\n", rbuff); if( rbuff ) { @@ -580,7 +581,7 @@ CdlDIR *CdOpenDir(const char* path) for( i=1; i<num_dirs; i++ ) { rbuff = resolve_pathtable_path( i, tpath_rbuff+127 ); - _sdk_log( "Found = %s|\n", rbuff ); + _sdk_log( "Found = %s\n", rbuff ); if( rbuff ) { @@ -666,11 +667,9 @@ int CdReadDir(CdlDIR *dir, CdlFILE* file) file->size = dir_entry->entrySize.lsb; - _sdk_log("dir_entry->entryLength = %d, ", dir_entry->entryLength); - d_dir->_pos += dir_entry->entryLength; - _sdk_log("d_dir->_pos = %d\n", d_dir->_pos); + _sdk_log("dir_entry->entryLength = %d, d_dir->_pos = %d\n", dir_entry->entryLength, d_dir->_pos); // Check if padding is reached (end of record sector) if( d_dir->_dir[d_dir->_pos] == 0 ) @@ -697,34 +696,29 @@ int CdIsoError() return _cd_iso_error; } -int CdGetVolumeLabel(char* label) +int CdGetVolumeLabel(char *label) { - int i; + int i, length = 31; ISO_DESCRIPTOR* descriptor; - + if( _CdReadIsoDescriptor(0) ) - { return -1; - } - + descriptor = (ISO_DESCRIPTOR*)_cd_iso_descriptor_buff; - - i = 0; - for( i=0; (descriptor->volumeID[i]!=0x20)&&(i<32); i++ ) - { - label[i] = descriptor->volumeID[i]; - } - - label[i] = 0x00; - - return 0; + + while (descriptor->volumeID[length] == 0x20) + length--; + + length++; + memcpy(label, descriptor->volumeID, length); + label[length] = 0x00; + + return length; } // Session load routine -void _cd_control(unsigned char com, unsigned char *param, int plen); - static volatile unsigned int _ready_oldcb; static volatile int _ses_scanfound; @@ -733,7 +727,7 @@ static volatile int _ses_scancomplete; //static volatile char _ses_scan_resultbuff[8]; static volatile char *_ses_scanbuff; -static void _scan_callback(int status, unsigned char *result) +static void _scan_callback(CdlIntrResult status, unsigned char *result) { if( status == CdlDataReady ) { @@ -743,7 +737,7 @@ static void _scan_callback(int status, unsigned char *result) { if( strncmp((const char*)_ses_scanbuff+1, "CD001", 5) == 0 ) { - _cd_control(CdlPause, 0, 0); + CdControlF(CdlPause, 0); _ses_scancomplete = 1; _ses_scanfound = 1; return; @@ -752,7 +746,7 @@ static void _scan_callback(int status, unsigned char *result) _ses_scancount++; if( _ses_scancount >= 512 ) { - _cd_control(CdlPause, 0, 0); + CdControlF(CdlPause, 0); _ses_scancomplete = 1; return; } @@ -760,7 +754,7 @@ static void _scan_callback(int status, unsigned char *result) if( status == CdlDiskError ) { - _cd_control(CdlPause, 0, 0); + CdControlF(CdlPause, 0); _ses_scancomplete = 1; } } @@ -768,7 +762,7 @@ static void _scan_callback(int status, unsigned char *result) int CdLoadSession(int session) { CdlLOC *loc; - unsigned int ready_oldcb; + CdlCB ready_oldcb; char scanbuff[2048]; char resultbuff[16]; int i; @@ -791,9 +785,7 @@ int CdLoadSession(int session) } // Set search routine callback - EnterCriticalSection(); ready_oldcb = CdReadyCallback(_scan_callback); - ExitCriticalSection(); _ses_scanfound = 0; _ses_scancount = 0; @@ -810,26 +802,20 @@ int CdLoadSession(int session) // Wait until scan complete while(!_ses_scancomplete); - EnterCriticalSection(); CdReadyCallback((void*)_ready_oldcb); - ExitCriticalSection(); if( !_ses_scanfound ) { _sdk_log("CdLoadSession(): Did not find volume descriptor.\n"); _cd_iso_error = CdlIsoInvalidFs; - EnterCriticalSection(); CdReadyCallback((CdlCB)ready_oldcb); - ExitCriticalSection(); return -1; } // Restore old callback if any - EnterCriticalSection(); CdReadyCallback((CdlCB)ready_oldcb); - ExitCriticalSection(); // Wait until CD-ROM has completely stopped reading, to get a consistent // fix of the CD-ROM pickup's current location diff --git a/libpsn00b/psxcd/misc.c b/libpsn00b/psxcd/misc.c new file mode 100644 index 0000000..8fd2a4d --- /dev/null +++ b/libpsn00b/psxcd/misc.c @@ -0,0 +1,151 @@ +/* + * PSn00bSDK CD-ROM library (misc. functions) + * (C) 2020-2022 Lameguy64, spicyjpeg - MPL licensed + */ + +#include <stdint.h> +#include <string.h> +#include <assert.h> +#include <psxetc.h> +#include <psxcd.h> +#include <hwregs_c.h> + +#define DATA_SYNC_TIMEOUT 0x100000 + +/* Private types */ + +typedef struct { + uint8_t status, first_track, last_track; +} TrackInfo; + +/* Sector DMA transfer functions */ + +int CdGetSector(void *madr, int size) { + //while (!(CD_REG(0) & (1 << 6))) + //__asm__ volatile(""); + + DMA_MADR(DMA_CD) = (uint32_t) madr; + DMA_BCR(DMA_CD) = size | (1 << 16); + DMA_CHCR(DMA_CD) = 0x11000000; + + while (DMA_CHCR(DMA_CD) & (1 << 24)) + __asm__ volatile(""); + + return 1; +} + +int CdGetSector2(void *madr, int size) { + //while (!(CD_REG(0) & (1 << 6))) + //__asm__ volatile(""); + + DMA_MADR(DMA_CD) = (uint32_t) madr; + DMA_BCR(DMA_CD) = size | (1 << 16); + DMA_CHCR(DMA_CD) = 0x11400100; // Transfer 1 word every 16 CPU cycles + + return 1; +} + +int CdDataSync(int mode) { + if (mode) + return (DMA_CHCR(DMA_CD) >> 24) & 1; + + for (int i = DATA_SYNC_TIMEOUT; i; i--) { + if (!(DMA_CHCR(DMA_CD) & (1 << 24))) + return 0; + } + + _sdk_log("CdDataSync() timeout\n"); + return -1; +} + +/* LBA/MSF conversion */ + +CdlLOC *CdIntToPos(int i, CdlLOC *p) { + i += 150; + + p->minute = itob(i / (75 * 60)); + p->second = itob((i / 75) % 60); + p->sector = itob(i % 75); + return p; +} + +int CdPosToInt(const CdlLOC *p) { + return ( + (btoi(p->minute) * (75 * 60)) + + (btoi(p->second) * 75) + + btoi(p->sector) + ) - 150; +} + +/* Misc. functions */ + +int CdGetToc(CdlLOC *toc) { + TrackInfo track_info; + + if (!CdCommand(CdlGetTN, 0, 0, (uint8_t *) &track_info)) + return 0; + if (CdSync(1, 0) != CdlComplete) + return 0; + + int first = btoi(track_info.first_track); + int tracks = btoi(track_info.last_track) + 1 - first; + //assert(first == 1); + + for (int i = 0; i < tracks; i++) { + uint8_t track = itob(first + i); + + if (!CdCommand(CdlGetTD, &track, 1, (uint8_t *) &toc[i])) + return 0; + if (CdSync(1, 0) != CdlComplete) + return 0; + + toc[i].sector = 0; + toc[i].track = track; + } + + return tracks; +} + +CdlRegionCode CdGetRegion(void) { + uint8_t param = 0x22; + uint8_t result[16]; + + // Test command 0x22 is unsupported in firmware version C0, which was used + // exclusively in the SCPH-1000 Japanese model. It's thus safe to assume + // that the console is Japanese if the command returns a valid error. + // https://psx-spx.consoledev.net/cdromdrive/#19h22h-int3for-europe + memset(result, 0, 16); + + if (!CdCommand(CdlTest, ¶m, 1, result)) { + _sdk_log("failed to probe drive region\n"); + return (result[1] == 0x10) ? CdlRegionSCEI : CdlRegionUnknown; + } + + _sdk_log("drive region: %s\n", result); + + if (!strcmp(result, "for Japan")) + return CdlRegionSCEI; + if (!strcmp(result, "for U/C")) + return CdlRegionSCEA; + if (!strcmp(result, "for Europe")) + return CdlRegionSCEE; + if (!strcmp(result, "for NETNA") || !strcmp(result, "for NETEU")) + return CdlRegionSCEW; + if (!strcmp(result, "for US/AEP")) + return CdlRegionDebug; + + return CdlRegionUnknown; +} + +int CdMix(const CdlATV *vol) { + CD_REG(0) = 2; + CD_REG(2) = vol->val0; + CD_REG(3) = vol->val1; + + CD_REG(0) = 3; + CD_REG(1) = vol->val2; + CD_REG(2) = vol->val3; + + CD_REG(3) = 0x20; // Unmute XA, apply volume changes + return 1; +} diff --git a/libpsn00b/psxcd/psxcd.c b/libpsn00b/psxcd/psxcd.c deleted file mode 100644 index 9392d30..0000000 --- a/libpsn00b/psxcd/psxcd.c +++ /dev/null @@ -1,371 +0,0 @@ -#include <stdint.h> -#include <assert.h> -#include <psxgpu.h> -#include <psxapi.h> -#include <psxcd.h> - -#define READ_TIMEOUT 600 // 10 seconds for NTSC - -extern volatile char _cd_ack_wait; -extern volatile uint8_t _cd_last_int; -extern volatile uint8_t _cd_last_mode; -extern volatile uint8_t _cd_status; -extern volatile CdlCB _cd_callback_int1_data; - -volatile uint8_t *_cd_result_ptr; - -// For read retry -volatile CdlLOC _cd_last_setloc; -volatile uint32_t *_cd_last_read_addr; -volatile int _cd_last_sector_count; - -int _cd_media_changed; - -void _cd_init(void); -void _cd_control(unsigned char com, const void *param, int plen); -void _cd_wait_ack(void); -void _cd_wait(void); - -int CdInit(void) { - // Sets up CD-ROM hardware and low-level subsystem - _cd_init(); - - // So CD ISO file system component will update the ISO descriptor - _cd_media_changed = 1; - - // Issue commands to initialize the CD-ROM hardware - CdControl(CdlNop, 0, 0); - CdControl(CdlInit, 0, 0); - - if(CdSync(0, 0) != CdlDiskError) { - CdControl(CdlDemute, 0, 0); - _sdk_log("setup done\n"); - } else { - _sdk_log("setup error, bad disc/drive or no disc inserted\n"); - } - - return 1; -} - -int CdControl(unsigned char com, const void *param, unsigned char *result) -{ - // Don't issue command if ack is not received yet - if( _cd_ack_wait ) - { - return 0; - } - - _cd_result_ptr = result; - - CdControlF(com, param); - _cd_wait_ack(); - - // Set media changed flag if lid had been opened - if( (CdStatus()&0x10) ) - { - _cd_media_changed = 1; - } - - return 1; -} - -int CdControlB(unsigned char com, const void *param, unsigned char *result) -{ - if( !CdControl(com, param, result) ) - { - return 0; - } - - CdSync(0, 0); - return 1; -} - -int CdControlF(unsigned char com, const void *param) -{ - int param_len=0; - - // Command specific parameters - switch(com) - { - case CdlSetloc: - param_len = 3; - _cd_last_setloc = *((CdlLOC*)param); - break; - case CdlPlay: - if( param ) - { - param_len = 1; - } - break; - case CdlSetfilter: - param_len = 2; - break; - case CdlSetmode: - case CdlSetsession: - case CdlTest: - case CdlGetTD: - param_len = 1; - break; - case CdlReadN: - case CdlReadS: - case CdlSeekL: - case CdlSeekP: - if( param ) - { - _cd_control(CdlSetloc, param, 3); - _cd_last_setloc = *((CdlLOC*)param); - } - break; - } - - // Issue CD command - _cd_control(com, param, param_len); - - return 1; -} - -int CdSync(int mode, unsigned char *result) -{ - int cdirq; - - if( mode ) - { - if( result ) - { - *result = _cd_status; - } - - cdirq = _cd_last_int; - if( cdirq == CdlAcknowledge ) - { - cdirq = CdlComplete; - } - return cdirq; - } - - _cd_wait(); - - if( result ) - { - *result = _cd_status; - } - - cdirq = _cd_last_int; - if( cdirq == CdlAcknowledge ) - { - cdirq = CdlComplete; - } - - return cdirq; -} - -int CdGetToc(CdlLOC *toc) -{ - uint8_t track_info[8]; - int i,tracks; - - // Get number of tracks - if( !CdControl(CdlGetTN, 0, track_info) ) - { - return 0; - } - - if( CdSync(1, 0) != CdlComplete ) - { - return 0; - } - - tracks = 1+(btoi(track_info[2])-btoi(track_info[1])); - - // Get track positions - for(i=0; i<tracks; i++) - { - int t = itob(1+i); - if( !CdControl(CdlGetTD, (uint8_t*)&t, (uint8_t*)&toc[i]) ) - { - return 0; - } - if( CdSync(1, 0) != CdlComplete ) - { - return 0; - } - toc[i].sector = 0; - toc[i].track = 1+i; - } - - return tracks; -} - -CdlLOC *CdIntToPos(int i, CdlLOC *p) { - - i += 150; - - p->minute = itob((i/75)/60); - p->second = itob((i/75)%60); - p->sector = itob(i%75); - - return p; - -} - -int CdPosToInt(const CdlLOC *p) -{ - return ((75*(btoi(p->minute)*60))+(75*btoi(p->second))+btoi(p->sector))-150; -} - -int CdStatus(void) -{ - return _cd_status; -} - -int CdMode(void) -{ - return _cd_last_mode; -} - - -// CD data read routines -volatile int _cd_sector_count = 0; -volatile uint32_t *_cd_read_addr; -volatile uint8_t _cd_read_result[8]; -volatile uint32_t _cd_read_oldcb; -volatile uint32_t _cd_read_sector_sz; -volatile uint32_t _cd_read_counter; - - - -volatile CdlCB _cd_read_cb; - -// Sector callback -static void _CdReadReadyCallback(int status, unsigned char *result) -{ - _cd_read_counter = VSync(-1); - - if( status == CdlDataReady ) - { - // Fetch sector from CD controller - CdGetSector((void*)_cd_read_addr, _cd_read_sector_sz); - - // Increment destination address - _cd_read_addr += _cd_read_sector_sz; - - // Subtract sector count - _cd_sector_count--; - } - - // End reading with pause command when sector count reaches zero - // or when an error occurs - if( ( _cd_sector_count <= 0 ) || ( status == CdlDiskError ) ) - { - // Stop reading - _cd_control(CdlPause, 0, 0); - - // Revert previous ready callback - _cd_callback_int1_data = (CdlCB)_cd_read_oldcb; - - // Execute read completion callback - if( _cd_read_cb ) - { - _cd_read_cb(status, result); - } - } -} - -int CdRead(int sectors, uint32_t *buf, int mode) -{ - // Set sectors to read count - _cd_sector_count = sectors; - _cd_last_sector_count = sectors; - _cd_last_read_addr = buf; - _cd_read_addr = buf; - - // Determine sector based on mode flags - if( mode & CdlModeSize ) - _cd_read_sector_sz = 2340 / 4; - else - _cd_read_sector_sz = 2048 / 4; - - _cd_read_counter = VSync(-1); - - // Set read callback - EnterCriticalSection(); - _cd_read_oldcb = CdReadyCallback(_CdReadReadyCallback); - ExitCriticalSection(); - - // Set specified mode - CdControl(CdlSetmode, (uint8_t*)&mode, 0); - - // Begin reading sectors - CdControl(CdlReadN, 0, (uint8_t*)_cd_read_result); - - return 0; -} - -static void CdDoRetry() -{ - int cb; - - _sdk_log("retrying read...\n"); - - // Stop reading - CdControl(CdlPause, 0, 0); - CdSync(0, 0); - - // Reset parameters for retrying read operation - _cd_sector_count = _cd_last_sector_count; - _cd_read_addr = _cd_last_read_addr; - - // Reset timeout - _cd_read_counter = VSync(-1); - - EnterCriticalSection(); - CdReadyCallback(_CdReadReadyCallback); - ExitCriticalSection(); - - // Retry read - CdControl(CdlSetloc, (void*)&_cd_last_setloc, 0); - CdControl(CdlReadN, 0, (uint8_t*)_cd_read_result); -} - -int CdReadSync(int mode, uint8_t *result) -{ - if( (VSync(-1)-_cd_read_counter) > READ_TIMEOUT ) - { - CdDoRetry(); - } - - if( mode ) - { - if( CdSync(1, 0) == CdlDiskError ) - { - return -1; - } - return _cd_sector_count; - } - - while(_cd_sector_count > 0) - { - if( (VSync(-1)-_cd_read_counter) > READ_TIMEOUT ) - { - CdDoRetry(); - } - } - - if( CdSync(0, result) != CdlComplete ) - { - return -1; - } - - return 0; -} - -uint32_t CdReadCallback(CdlCB func) -{ - unsigned int old_func; - - old_func = (unsigned int)_cd_read_cb; - - _cd_read_cb = func; - - return old_func; -} diff --git a/libpsn00b/psxcd/psxcd_asm.s b/libpsn00b/psxcd/psxcd_asm.s deleted file mode 100644 index c0a5312..0000000 --- a/libpsn00b/psxcd/psxcd_asm.s +++ /dev/null @@ -1,487 +0,0 @@ -.set noreorder - -.include "hwregs_a.inc" - -.section .text - -.global _cd_init -.type _cd_init, @function -_cd_init: - - addiu $sp, -4 - sw $ra, 0($sp) - - jal EnterCriticalSection - nop - - lui $a3, IOBASE # Acknowledge all CD IRQs - - li $v0, 0x20943 # Set CD-ROM Delay/Size and common delay - sw $v0, CD_DELAY_SIZE($a3) - li $v0, 0x1325 - sw $v0, COM_DELAY_CFG($a3) - - li $v0, 1 - sb $v0, CD_REG0($a3) - li $v0, 0x1f - sb $v0, CD_REG3($a3) - sb $v0, CD_REG2($a3) # Enable all IRQs - - sb $0 , CD_REG0($a3) - sb $0 , CD_REG3($a3) - - la $a1, _cd_handler - jal InterruptCallback - li $a0, 2 - - li $v0, 2 # Set CD left volume - sb $v0, CD_REG0($a3) - li $v0, 0x80 - sb $v0, CD_REG2($a3) - - li $v0, 3 # Set CD right volume - sb $v0, CD_REG0($a3) - li $v0, 0x80 - sb $v0, CD_REG1($a3) - - li $v0, 0x20 # Apply volume - sb $v0, CD_REG3($a3) - - # Clear a bunch of stats - la $v0, _cd_ack_wait - sb $0 , 0($v0) - la $v0, _cd_complt_wait - sb $0 , 0($v0) - la $v0, _cd_last_cmd - sb $0 , 0($v0) - la $v0, _cd_last_mode - sb $0 , 0($v0) - la $v0, _cd_last_int - sb $0 , 0($v0) - - la $v0, _cd_result_ptr - sw $0 , 0($v0) - - # Clear callback hooks - la $v0, _cd_callback_int1_data - sw $0 , 0($v0) - la $v0, _cd_callback_int4 - sw $0 , 0($v0) - - la $v0, _cd_read_cb - sw $0 , 0($v0) - - lw $v0, DMA_DPCR($a3) - li $v1, 0xB000 - or $v0, $v1 - sw $v0, DMA_DPCR($a3) - - jal ExitCriticalSection - nop - - lw $ra, 0($sp) - addiu $sp, 4 - jr $ra - nop - - -.global _cd_wait -.type _cd_wait, @function -_cd_wait: - la $v0, _cd_ack_wait - lbu $v0, 0($v0) - nop - bnez $v0, _cd_wait - nop -.Lcomplete: - la $v0, _cd_complt_wait - lbu $v0, 0($v0) - nop - bnez $v0, .Lcomplete - nop - jr $ra - nop - - -.global _cd_wait_ack -.type _cd_wait_ack, @function -_cd_wait_ack: - la $v0, _cd_ack_wait - lbu $v0, 0($v0) - nop - bnez $v0, _cd_wait_ack - nop - jr $ra - nop - - -.global _cd_wait_complt -.type _cd_wait_complt, @function -_cd_wait_complt: - la $v0, _cd_complt_wait - lbu $v0, 0($v0) - nop - bnez $v0, _cd_wait_complt - nop - jr $ra - nop - - -.type _cd_handler, @function -_cd_handler: - - addiu $sp, -4 - sw $ra, 0($sp) - - lui $a3, IOBASE # Get IRQ number - li $v0, 1 - sb $v0, CD_REG0($a3) - - lbu $v0, CD_REG3($a3) - nop - andi $v0, 0x7 - - la $v1, _cd_last_int # Save last IRQ value - sb $v0, 0($v1) - - bne $v0, 0x1, .Lno_data - nop - - sb $0 , CD_REG0($a3) - lbu $v1, CD_REG0($a3) - sb $0 , CD_REG3($a3) - lbu $v1, CD_REG3($a3) - - sb $0 , CD_REG0($a3) # Load data FIFO on INT1 - li $v1, 0x80 - sb $v1, CD_REG3($a3) - -.Lno_data: - - li $v1, 1 - sb $v1, CD_REG0($a3) # Clear CD interrupt and parameter FIFO - li $v1, 0x5f - sb $v1, CD_REG3($a3) - li $v1, 0x40 - sb $v1, CD_REG3($a3) - - li $v1, 0 # Delay when clearing parameter FIFO - sw $v1, 0($0) - li $v1, 1 - sw $v1, 0($0) - li $v1, 2 - sw $v1, 0($0) - li $v1, 3 - sw $v1, 0($0) - - la $v1, _cd_last_int - lbu $v0, 0($v1) - - beq $v0, 0x1, .Lirq_1 # Data ready - nop - beq $v0, 0x2, .Lirq_2 # Command finish - nop - beq $v0, 0x3, .Lirq_3 # Acknowledge - nop - beq $v0, 0x4, .Lirq_4 # Data/track end - nop - beq $v0, 0x5, .Lirq_5 # Error - nop - - b .Lirq_misc - nop - -.Lirq_1: # Data ready - - jal _cd_fetch_result - nop - - la $v0, _cd_callback_int1_data - lw $v0, 0($v0) - nop - beqz $v0, .Lirq_misc - nop - - la $a0, _cd_last_int - lbu $a0, 0($a0) - la $a1, _cd_result_ptr - lw $a1, 0($a1) - - jalr $v0 - addiu $sp, -16 - addiu $sp, 16 - - b .Lirq_misc - nop - -.Lirq_2: # Command complete - - jal _cd_fetch_result - nop - - la $v0, _cd_complt_wait - sb $0 , 0($v0) - - la $v0, _cd_sync_cb - lw $v0, 0($v0) - nop - beqz $v0, .Lirq_misc - nop - - la $a0, _cd_last_int - lbu $a0, 0($a0) - la $a1, _cd_result_ptr - lw $a1, 0($a1) - jalr $v0 - addiu $sp, -16 - addiu $sp, 16 - - b .Lirq_misc - nop - -.Lirq_3: # Command acknowledge - - jal _cd_fetch_result - nop - - la $v0, _cd_ack_wait - sb $0 , 0($v0) - - b .Lirq_misc - nop - -.Lirq_4: - - jal _cd_fetch_result - nop - - la $v0, _cd_callback_int4 - lw $v0, 0($v0) - nop - beqz $v0, .Lirq_misc - nop - - jalr $v0 - addiu $sp, -16 - addiu $sp, 16 - - b .Lirq_misc - nop - -.Lirq_5: # Error - - jal _cd_fetch_result - nop - - la $v0, _cd_complt_wait - lbu $v0, 0($v0) - nop - beqz $v0, .Lno_complete - nop - - la $v0, _cd_sync_cb - lw $v0, 0($v0) - nop - beqz $v0, .Lno_complete - nop - - li $a0, 0x05 # CdlDiskError - la $a1, _cd_result_ptr - lw $a1, 0($a1) - jalr $v0 - addiu $sp, -16 - addiu $sp, 16 - -.Lno_complete: - - la $v0, _cd_complt_wait - sb $0 , 0($v0) - la $v0, _cd_ack_wait - sb $0 , 0($v0) - - la $v0, _cd_callback_int1_data - lw $v0, 0($v0) - nop - beqz $v0, .Lirq_misc - nop - - li $a0, 0x05 # CdlDiskError - la $a1, _cd_result_ptr - lw $a1, 0($a1) - - jalr $v0 - addiu $sp, -16 - addiu $sp, 16 - -.Lirq_misc: - - lw $ra, 0($sp) - addiu $sp, 4 - jr $ra - nop - - -_cd_fetch_result: - - lui $a3, IOBASE - - la $a0, _cd_status - lbu $v0, CD_REG1($a3) - - la $v1, _cd_last_int - lbu $v1, 0($v1) - nop - beq $v1, 0x2, .Lirq2_checks - nop - - # IRQ3 checks - - la $v1, _cd_last_cmd - lbu $v1, 0($v1) - nop - beq $v1, 0x10, .Lskip_status - nop - beq $v1, 0x11, .Lskip_status - nop - - b .Lwrite_status - nop - - # IRQ2 checks - -.Lirq2_checks: - - la $v1, _cd_last_cmd - lbu $v1, 0($v1) - nop - beq $v1, 0x1D, .Lskip_status - nop - -.Lwrite_status: - - sb $v0, 0($a0) - -.Lskip_status: - - la $a0, _cd_result_ptr - lw $a0, 0($a0) - nop - beqz $a0, .Lno_result - nop - sb $v0, 0($a0) - addiu $a0, 1 - - move $a1, $0 -.Lread_futher_result: - - lbu $v0, CD_REG0($a3) - nop - andi $v0, 0x20 - beqz $v0, .Lno_result - addu $a1, 1 - bge $a1, 7, .Lno_result # timeout (locks up here on PSIO) - nop - lbu $v0, CD_REG1($a3) - lbu $v1, CD_REG0($a3) - sb $v0, 0($a0) - - andi $v1, 0x20 # when performing a CD-ROM read - bnez $v1, .Lread_futher_result - addiu $a0, 1 - -.Lno_result: - -# lbu $v0, CD_REG0($a3) # Flush response FIFO -# nop -# andi $v0, 0x20 -# bnez $v0, .Lno_result -# lbu $v0, CD_REG1($a3) - - jr $ra - nop - - -.global CdAutoPauseCallback -.type CdAutoPauseCallback, @function -CdAutoPauseCallback: - - addiu $sp, -8 - sw $ra, 0($sp) - sw $a0, 4($sp) - - la $v1, _cd_callback_int4 - lw $v0, 0($v1) - - la $v1, _cd_callback_int4 - - lw $a0, 4($sp) - nop - sw $a0, 0($v1) - - lw $ra, 0($sp) - addiu $sp, 8 - jr $ra - nop - - -.global CdReadyCallback -.type CdReadyCallback, @function -CdReadyCallback: - - addiu $sp, -12 - sw $ra, 0($sp) - sw $a0, 4($sp) - - la $v1, _cd_callback_int1_data - lw $v0, 0($v1) - - la $v1, _cd_callback_int1_data - sw $v0, 8($sp) - - lw $a0, 4($sp) - nop - sw $a0, 0($v1) - - lw $ra, 0($sp) - lw $v0, 8($sp) - jr $ra - addiu $sp, 12 - - -.global CdSyncCallback -.type CdSyncCallback, @function -CdSyncCallback: - addiu $sp, -12 - sw $ra, 0($sp) - sw $a0, 4($sp) - - la $v1, _cd_sync_cb - lw $v0, 0($v1) - - la $v1, _cd_sync_cb - sw $v0, 8($sp) - - lw $a0, 4($sp) - nop - sw $a0, 0($v1) - - lw $ra, 0($sp) - lw $v0, 8($sp) - jr $ra - addiu $sp, 12 - - -.section .data - -.comm _cd_last_cmd, 1, 1 -.comm _cd_last_mode, 1, 1 -.comm _cd_ack_wait, 1, 1 -.comm _cd_complt_wait, 1, 1 - -.comm _cd_status, 1, 1 -.comm _cd_last_int, 1, 1 - -# Callback hooks -.comm _cd_callback_int1_data, 4, 4 # Data IRQ callback -.comm _cd_callback_int4, 4, 4 # Autopause callback -.comm _cd_sync_cb, 4, 4 diff --git a/libpsn00b/psxcd/readme.txt b/libpsn00b/psxcd/readme.txt index 64643ed..dcb40b6 100644 --- a/libpsn00b/psxcd/readme.txt +++ b/libpsn00b/psxcd/readme.txt @@ -1,45 +1,39 @@ PSX CD-ROM library, part of PSn00bSDK -2020 Lameguy64 / Meido-Tek Productions +2020-2022 Lameguy64 / Meido-Tek Productions Licensed under Mozilla Public License - Open source implementation of the long awaited CD-ROM library that -provides greater functionality than the BIOS CD-ROM subsystem. Supports -pretty much all features of the CD-ROM hardware such as CD data read, -CD Audio and XA audio playback. Data streaming should also be possible -with the use of CdlReadN/CdlReadS commands and CdReadyCallback(). +Open source implementation of the long awaited CD-ROM library that provides +greater functionality than the BIOS CD-ROM subsystem. Supports pretty much all +features of the CD-ROM hardware such as CD data read, CD Audio and XA audio +playback, with the exception of the St*() APIs for .STR playback (but manual +streaming using CdReadyCallback() is still supported). - Library also includes an ISO9660 file system parser for locating -files within the CD-ROM file system. Unlike the file system parser in -the official libraries libpsxcd can parse directories containing any -number of files. Rock-ridge and Joliet extensions are not supported -however. - - Be aware that the CD-ROM library might have some loose ends as it -is still a work in progress, but should work flawlessly in most use -cases. +An ISO9660 file system driver for locating files within the CD-ROM is also +included. Unlike the ISO9660 parser in the official libraries, libpsxcd can +parse directories containing any number of files. Currently no ISO9660 +extensions are supported. +Be aware that the CD-ROM library might have some loose ends as it is still a +work in progress, but should work flawlessly in most use cases. Library developer(s): - Lameguy64 - + Lameguy64 (ISO9660 driver, initial implementation of low-level functions) + spicyjpeg (C rewrite) Library header(s): psxcd.h - Todo list: * Command query mechanism so that more than 2 CD-ROM commands can easily be issued in callbacks. Official library probably does this. - + * Helper functions for handling disc changes (CdDiskReady and CdGetDiskType) are not yet implemented. - - * CdReadBreak() not yet implemented. - + * Data streaming functions (prefixed with St*) not yet implemented. Would require devising a PSn00bSDK equivalent of the STR file - format.
\ No newline at end of file + format. |
