From ea46d05aed0343c20d8fdfaa0e67d54d51e8e2a0 Mon Sep 17 00:00:00 2001 From: "John Wilbert M. Villamor" Date: Fri, 22 Nov 2019 09:11:12 +0800 Subject: Added long awaited CD-ROM library and loads of fixes, see changelog for details --- libpsn00b/libpsxcd/_cd_control.s | 109 +++++++ libpsn00b/libpsxcd/cdgetsector.s | 53 +++ libpsn00b/libpsxcd/cdmix.s | 34 ++ libpsn00b/libpsxcd/cdsearchfile.c | 663 ++++++++++++++++++++++++++++++++++++++ libpsn00b/libpsxcd/makefile | 38 +++ libpsn00b/libpsxcd/psxcd.c | 319 ++++++++++++++++++ libpsn00b/libpsxcd/psxcd_asm.s | 520 ++++++++++++++++++++++++++++++ libpsn00b/libpsxcd/readme.txt | 48 +++ 8 files changed, 1784 insertions(+) create mode 100644 libpsn00b/libpsxcd/_cd_control.s create mode 100644 libpsn00b/libpsxcd/cdgetsector.s create mode 100644 libpsn00b/libpsxcd/cdmix.s create mode 100644 libpsn00b/libpsxcd/cdsearchfile.c create mode 100644 libpsn00b/libpsxcd/makefile create mode 100644 libpsn00b/libpsxcd/psxcd.c create mode 100644 libpsn00b/libpsxcd/psxcd_asm.s create mode 100644 libpsn00b/libpsxcd/readme.txt (limited to 'libpsn00b/libpsxcd') diff --git a/libpsn00b/libpsxcd/_cd_control.s b/libpsn00b/libpsxcd/_cd_control.s new file mode 100644 index 0000000..7f69266 --- /dev/null +++ b/libpsn00b/libpsxcd/_cd_control.s @@ -0,0 +1,109 @@ +.set noreorder + +.include "hwregs_a.h" + +.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 + + 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) + +.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) + + jr $ra + nop + \ No newline at end of file diff --git a/libpsn00b/libpsxcd/cdgetsector.s b/libpsn00b/libpsxcd/cdgetsector.s new file mode 100644 index 0000000..70039bf --- /dev/null +++ b/libpsn00b/libpsxcd/cdgetsector.s @@ -0,0 +1,53 @@ +.set noreorder + +.include "hwregs_a.h" + +.section .text + +.global CdGetSector +.type CdGetSector, @function +CdGetSector: + + lui $a2, IOBASE + +.Lwait_fifo: + lbu $v0, CD_REG0($a2) + nop + andi $v0, 0x40 + beqz $v0, .Lwait_fifo + nop + + lui $v0, 0x1 + srl $a1, 2 + or $v0, $a1 + sw $a0, D3_MADR($a2) # Set DMA base address and transfer length + sw $v0, D3_BCR($a2) + + lui $v0, 0x1100 # Start DMA transfer + sw $v0, D3_CHCR($a2) + nop + nop +.Ldma_wait: + lw $v0, D3_CHCR($a2) + nop + srl $v0, 24 + andi $v0, 0x1 + bnez $v0, .Ldma_wait + nop + +# Not stable +# sb $0 , CD_REG0($a2) +#.Lflush_fifo: # Read out any remaining bytes in the buffer +# lbu $v1, CD_REG0($a2) +# li $v0, 0x40 +# and $v1, $v0 +# beqz $v1, .Lend_flush +# nop +# lbu $v0, CD_REG2($a2) +# b .Lflush_fifo +# nop +#.Lend_flush: + + jr $ra + li $v0, 1 + diff --git a/libpsn00b/libpsxcd/cdmix.s b/libpsn00b/libpsxcd/cdmix.s new file mode 100644 index 0000000..745fb65 --- /dev/null +++ b/libpsn00b/libpsxcd/cdmix.s @@ -0,0 +1,34 @@ +.set noreorder + +.include "hwregs_a.h" + +.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/libpsxcd/cdsearchfile.c b/libpsn00b/libpsxcd/cdsearchfile.c new file mode 100644 index 0000000..60e4377 --- /dev/null +++ b/libpsn00b/libpsxcd/cdsearchfile.c @@ -0,0 +1,663 @@ +#include +#include +#include +#include +#include +#include "psxcd.h" + +// Uncommend to enable debug output +//#define DEBUG + +#pragma pack(push, 1) + +/// Structure of a double-endian unsigned short word +typedef struct ISO_USHORT_PAIR +{ + unsigned short lsb; /// LSB format 16-bit word + unsigned short msb; /// MSB format 16-bit word +} ISO_USHORT_PAIR; + +/// Structure of a double-endian unsigned int word +typedef struct ISO_UINT_PAIR +{ + unsigned int lsb; /// LSB format 32-bit word + unsigned int msb; /// MSB format 32-bit word +} ISO_UINT_PAIR; + +/// ISO descriptor header structure +typedef struct ISO_DESCRIPTOR_HEADER +{ + unsigned char type; /// Volume descriptor type (1 is descriptor, 255 is descriptor terminator) + char id[5]; /// Volume descriptor ID (always CD001) + unsigned short version; /// Volume descriptor version (always 0x01) +} ISO_DESCRIPTOR_HEADER; + +/// Structure of a date stamp for ISO_DIR_ENTRY structure +typedef struct ISO_DATESTAMP +{ + unsigned char year; /// number of years since 1900 + unsigned char month; /// month, where 1=January, 2=February, etc. + unsigned char day; /// day of month, in the range from 1 to 31 + unsigned char hour; /// hour, in the range from 0 to 23 + unsigned char minute; /// minute, in the range from 0 to 59 + unsigned char second; /// Second, in the range from 0 to 59 + unsigned char GMToffs; /// Greenwich Mean Time offset +} ISO_DATESTAMP; + +/// Structure of an ISO path table entry (specifically for the cd::IsoReader class) +typedef struct ISO_PATHTABLE_ENTRY +{ + unsigned char nameLength; /// Name length (or 1 for the root directory) + unsigned char extLength; /// Number of sectors in extended attribute record + unsigned int dirOffs; /// Number of the first sector in the directory, as a double word + unsigned short dirLevel; /// Index of the directory record's parent directory + /// If nameLength is odd numbered, a padding byte will be present after the identifier text. +} ISO_PATHTABLE_ENTRY; + +typedef struct ISO_DIR_ENTRY +{ + unsigned char entryLength; // Directory entry length (variable, use for parsing through entries) + unsigned char extLength; // Extended entry data length (always 0) + ISO_UINT_PAIR entryOffs; // Points to the LBA of the file/directory entry + ISO_UINT_PAIR entrySize; // Size of the file/directory entry + ISO_DATESTAMP entryDate; // Date & time stamp of entry + unsigned char flags; // File flags (0x02 for directories, 0x00 for files) + unsigned char fileUnitSize; // Unit size (usually 0 even with Form 2 files such as STR/XA) + unsigned char interleaveGapSize; // Interleave gap size (usually 0 even with Form 2 files such as STR/XA) + ISO_USHORT_PAIR volSeqNum; // Volume sequence number (always 1) + unsigned char identifierLen; // Identifier (file/directory name) length in bytes +} ISO_DIR_ENTRY; + +typedef struct ISO_ROOTDIR_HEADER +{ + unsigned char entryLength; // Always 34 bytes + unsigned char extLength; // Always 0 + ISO_UINT_PAIR entryOffs; // Should point to LBA 22 + ISO_UINT_PAIR entrySize; // Size of entry extent + ISO_DATESTAMP entryDate; // Record date and time + unsigned char flags; // File flags + unsigned char fileUnitSize; + unsigned char interleaveGapSize; + ISO_USHORT_PAIR volSeqNum; + unsigned char identifierLen; // 0x01 + unsigned char identifier; // 0x00 +} ISO_ROOTDIR_HEADER; + +// ISO descriptor structure +typedef struct ISO_DESCRIPTOR +{ + + // ISO descriptor header + ISO_DESCRIPTOR_HEADER header; + // System ID (always PLAYSTATION) + char systemID[32]; + // Volume ID (or label, can be blank or anything) + char volumeID[32]; + // Unused null bytes + unsigned char pad2[8]; + // Size of volume in sector units + ISO_UINT_PAIR volumeSize; + // Unused null bytes + unsigned char pad3[32]; + // Number of discs in this volume set (always 1 for single volume) + ISO_USHORT_PAIR volumeSetSize; + // Number of this disc in volume set (always 1 for single volume) + ISO_USHORT_PAIR volumeSeqNumber; + // Size of sector in bytes (always 2048 bytes) + ISO_USHORT_PAIR sectorSize; + // Path table size in bytes (applies to all the path tables) + ISO_UINT_PAIR pathTableSize; + // LBA to Type-L path table + unsigned int pathTable1Offs; + // LBA to optional Type-L path table (usually a copy of the primary path table) + unsigned int pathTable2Offs; + // LBA to Type-L path table but with MSB format values + unsigned int pathTable1MSBoffs; + // LBA to optional Type-L path table but with MSB format values (usually a copy of the main path table) + unsigned int pathTable2MSBoffs; + // Directory entry for the root directory (similar to a directory entry) + ISO_ROOTDIR_HEADER rootDirRecord; + // Volume set identifier (can be blank or anything) + char volumeSetIdentifier[128]; + // Publisher identifier (can be blank or anything) + char publisherIdentifier[128]; + // Data preparer identifier (can be blank or anything) + char dataPreparerIdentifier[128]; + // Application identifier (always PLAYSTATION) + char applicationIdentifier[128]; + // Copyright file in the file system identifier (can be blank or anything) + char copyrightFileIdentifier[37]; + // Abstract file in the file system identifier (can be blank or anything) + char abstractFileIdentifier[37]; + // Bibliographical file identifier in the file system (can be blank or anything) + char bibliographicFilelIdentifier[37]; + // Volume create date (in text format YYYYMMDDHHMMSSMMGG) + char volumeCreateDate[17]; + // Volume modify date (in text format YYYYMMDDHHMMSSMMGG) + char volumeModifyDate[17]; + // Volume expiry date (in text format YYYYMMDDHHMMSSMMGG) + char volumeExpiryDate[17]; + // Volume effective date (in text format YYYYMMDDHHMMSSMMGG) + char volumeEffeciveDate[17]; + // File structure version (always 1) + unsigned char fileStructVersion; + // Padding + unsigned char dummy0; + // Application specific data (says CD-XA001 at [141], the rest are null bytes) + unsigned char appData[512]; + // Padding + unsigned char pad4[653]; + +} ISO_DESCRIPTOR; + +// Leave non-aligned structure packing +#pragma pack(pop) + +extern char _cd_media_changed; +int _cd_iso_last_dir_lba; + +u_char _cd_iso_descriptor_buff[2048]; +u_char *_cd_iso_pathtable_buff=NULL; +u_char *_cd_iso_directory_buff=NULL; +int _cd_iso_directory_len; + +int _CdReadIsoDescriptor(int session_offs) +{ + int i; + CdlLOC loc; + ISO_DESCRIPTOR *descriptor; + + // Seek to volume descriptor location + CdIntToPos(16+session_offs, &loc); + if( !CdControl(CdlSetloc, (u_char*)&loc, 0) ) + { +#ifdef DEBUG + printf("psxcd: Could not set seek destination.\n"); +#endif + return -1; + } + + // Read volume descriptor + CdRead(1, (u_int*)_cd_iso_descriptor_buff, CdlModeSpeed); + if( CdReadSync(0, 0) ) + { +#ifdef DEBUG + printf("psxcd: Error reading ISO volume descriptor.\n"); +#endif + return -1; + } + + + // Verify if volume descriptor is present + descriptor = (ISO_DESCRIPTOR*)_cd_iso_descriptor_buff; + if( strncmp("CD001", descriptor->header.id, 5) ) + { +#ifdef DEBUG + printf("psxcd: Disc does not have a ISO9660 file system.\n"); +#endif + return -1; + } + +#ifdef DEBUG + printf("psxcd_dbg: Path table LBA = %d\n", descriptor->pathTable1Offs); + printf("psxcd_dbg: Path table len = %d\n", descriptor->pathTableSize.lsb); +#endif + + // Allocate path table buffer + i = ((2047+descriptor->pathTableSize.lsb)>>11)<<11; + if( _cd_iso_pathtable_buff ) + { + free(_cd_iso_pathtable_buff); + } + _cd_iso_pathtable_buff = (u_char*)malloc(i); + +#ifdef DEBUG + printf("psxcd_dbg: Allocated %d bytes for path table.\n", i); +#endif + + // Read path table + CdIntToPos(descriptor->pathTable1Offs, &loc); + CdControl(CdlSetloc, (u_char*)&loc, 0); + CdRead(i>>11, (u_int*)_cd_iso_pathtable_buff, CdlModeSpeed); + if( CdReadSync(0, 0) ) + { +#ifdef DEBUG + printf("psxcd: Error reading ISO path table.\n"); +#endif + return -1; + } + + _cd_iso_last_dir_lba = 0; + + return 0; +} + +int _CdReadIsoDirectory(int lba) +{ + int i; + CdlLOC loc; + ISO_DIR_ENTRY *direntry; + + if( lba == _cd_iso_last_dir_lba ) + { + return 0; + } + + CdIntToPos(lba, &loc); + i = CdPosToInt(&loc); +#ifdef DEBUG + printf("psxcd_dbg: Seek to sector %d\n", i); +#endif + if( !CdControl(CdlSetloc, (u_char*)&loc, 0) ) + { +#ifdef DEBUG + printf("psxcd: Could not set seek destination.\n"); +#endif + return -1; + } + + if( _cd_iso_directory_buff ) + { + free(_cd_iso_directory_buff); + } + + // Read first sector of directory record + _cd_iso_directory_buff = (u_char*)malloc(2048); + CdRead(1, (u_int*)_cd_iso_directory_buff, CdlModeSpeed); + if( CdReadSync(0, 0) ) + { +#ifdef DEBUG + printf("psxcd: Error reading initial directory record.\n"); +#endif + return -1; + } + + direntry = (ISO_DIR_ENTRY*)_cd_iso_directory_buff; + _cd_iso_directory_len = direntry->entrySize.lsb; + +#ifdef DEBUG + printf("psxcd_dbg: Location of directory record = %d\n", direntry->entryOffs.lsb); + printf("psxcd_dbg: Size of directory record = %d\n", _cd_iso_directory_len); +#endif + + if( _cd_iso_directory_len > 2048 ) + { + if( !CdControl(CdlSetloc, (u_char*)&loc, 0) ) + { +#ifdef DEBUG + printf("psxcd: Could not set seek destination.\n"); +#endif + return -1; + } + + free(_cd_iso_directory_buff); + i = ((2047+_cd_iso_directory_len)>>11)<<11; + _cd_iso_directory_buff = (u_char*)malloc(i); +#ifdef DEBUG + printf("psxcd_dbg: Allocated %d bytes for directory record.\n", i); +#endif + + CdRead(i>>11, (u_int*)_cd_iso_directory_buff, CdlModeSpeed); + if( CdReadSync(0, 0) ) + { +#ifdef DEBUG + printf("psxcd: Error reading initial directory record.\n"); +#endif + return -1; + } + } + + _cd_iso_last_dir_lba = lba; + + return 0; +} + +#ifdef DEBUG + +void dump_directory(void) +{ + int i; + int dir_pos; + ISO_DIR_ENTRY *dir_entry; + char namebuff[16]; + + printf("psxcd_dbg: Cached directory record contents:\n"); + + i = 0; + dir_pos = 0; + while(1) + { + dir_entry = (ISO_DIR_ENTRY*)(_cd_iso_directory_buff+dir_pos); + + strncpy(namebuff, + _cd_iso_directory_buff+dir_pos+sizeof(ISO_DIR_ENTRY), dir_entry->identifierLen); + + printf("P:%d L:%d %s\n", dir_pos, dir_entry->identifierLen, namebuff); + + dir_pos += dir_entry->entryLength; + i++; + + // Check if padding is reached (end of record sector) + if( _cd_iso_directory_buff[dir_pos] == 0 ) + { + // Snap it to next sector + dir_pos = ((dir_pos+2047)>>11)<<11; + + // Break if exceeds length of directory buffer (end) + if( dir_pos >= _cd_iso_directory_len ) + { + break; + } + } + } + + printf("--\n"); + +} + +void dump_pathtable(void) +{ + u_char *tbl_pos; + ISO_PATHTABLE_ENTRY *tbl_entry; + ISO_DESCRIPTOR *descriptor; + char namebuff[16]; + + printf("psxcd_dbg: Path table entries:\n"); + + descriptor = (ISO_DESCRIPTOR*)_cd_iso_descriptor_buff; + + tbl_pos = _cd_iso_pathtable_buff; + tbl_entry = (ISO_PATHTABLE_ENTRY*)tbl_pos; + + while( (int)(tbl_pos-_cd_iso_pathtable_buff) < + descriptor->pathTableSize.lsb ) + { + strncpy(namebuff, + tbl_pos+sizeof(ISO_PATHTABLE_ENTRY), + tbl_entry->nameLength); + + printf("psxcd_dbg: %s\n", namebuff); + + // Advance to next entry + tbl_pos += sizeof(ISO_PATHTABLE_ENTRY) + +(2*((tbl_entry->nameLength+1)/2)); + + tbl_entry = (ISO_PATHTABLE_ENTRY*)tbl_pos; + } + +} + +#endif + +int get_pathtable_entry(int entry, ISO_PATHTABLE_ENTRY *tbl, char *namebuff) +{ + int i; + u_char *tbl_pos; + ISO_PATHTABLE_ENTRY *tbl_entry; + ISO_DESCRIPTOR *descriptor; + + descriptor = (ISO_DESCRIPTOR*)_cd_iso_descriptor_buff; + + tbl_pos = _cd_iso_pathtable_buff; + tbl_entry = (ISO_PATHTABLE_ENTRY*)tbl_pos; + + i = 0; + while( (int)(tbl_pos-_cd_iso_pathtable_buff) < + descriptor->pathTableSize.lsb ) + { + if( i == (entry-1) ) + { + if( namebuff ) + { + strncpy(namebuff, + tbl_pos+sizeof(ISO_PATHTABLE_ENTRY), + tbl_entry->nameLength); + } + + if( tbl ) + { + *tbl = *tbl_entry; + } + + return 0; + } + + // Advance to next entry + tbl_pos += sizeof(ISO_PATHTABLE_ENTRY) + +(2*((tbl_entry->nameLength+1)/2)); + + tbl_entry = (ISO_PATHTABLE_ENTRY*)tbl_pos; + i++; + } + + if( entry <= 0 ) + { + return i+1; + } + + return -1; +} + +char *resolve_pathtable_path(int entry, char *rbuff) +{ + char namebuff[16]; + ISO_PATHTABLE_ENTRY tbl_entry; + + *rbuff = 0; + + do + { + if( get_pathtable_entry(entry, &tbl_entry, namebuff) ) + { + return NULL; + } + + rbuff -= tbl_entry.nameLength; + memcpy(rbuff, namebuff, tbl_entry.nameLength); + rbuff--; + *rbuff = '\\'; + + // Parse to the parent + entry = tbl_entry.dirLevel; + + } while( entry > 1 ); + + return rbuff; +} + +int find_dir_entry(const char *name, ISO_DIR_ENTRY *dirent) +{ + int i; + int dir_pos; + ISO_DIR_ENTRY *dir_entry; + char namebuff[16]; + +#ifdef DEBUG + printf("psxcd_dbg: Locating file %s.\n", name); +#endif + + i = 0; + dir_pos = 0; + while(dir_pos < _cd_iso_directory_len) + { + dir_entry = (ISO_DIR_ENTRY*)(_cd_iso_directory_buff+dir_pos); + + if( !(dir_entry->flags & 0x2) ) + { + strncpy(namebuff, + _cd_iso_directory_buff+dir_pos+sizeof(ISO_DIR_ENTRY), + dir_entry->identifierLen); + + if( strcmp(namebuff, name) == 0 ) + { + *dirent = *dir_entry; + return 0; + } + } + + dir_pos += dir_entry->entryLength; + i++; + + // Check if padding is reached (end of record sector) + if( _cd_iso_directory_buff[dir_pos] == 0 ) + { + // Snap it to next sector + dir_pos = ((dir_pos+2047)>>11)<<11; + + } + } + + return -1; +} + +char *get_pathname(char *path, const char *filename) +{ + char *c; + c = strrchr(filename, '\\'); + + if(( c == filename ) || ( !c )) + { + path[0] = '\\'; + path[1] = 0; + return NULL; + } + + strncpy(path, filename, (int)(c-filename)); + return path; +} + +char *get_filename(char *name, const char *filename) +{ + char *c; + c = strrchr(filename, '\\'); + + if(( c == filename ) || ( !c )) + { + strcpy(name, filename+1); + return name; + } + + c++; + strcpy(name, c); + return name; +} + +CdlFILE *CdSearchFile(CdlFILE *fp, const char *filename) +{ + int i,j,found_dir,num_dirs; + int dir_len; + char tpath_rbuff[128]; + char search_path[128]; + char *rbuff; + ISO_PATHTABLE_ENTRY tbl_entry; + ISO_DIR_ENTRY dir_entry; + + // Read ISO descriptor if changed flag is set + if( _cd_media_changed ) + { + // Read ISO descriptor and path table + if( _CdReadIsoDescriptor(0) ) + { +#ifdef DEBUG + printf("psxcd: Could not read ISO file system.\n"); +#endif + return NULL; + } +#ifdef DEBUG + printf("psxcd: ISO file system cache updated.\n"); +#endif + _cd_media_changed = 0; + } + + // Get number of directories in path table + num_dirs = get_pathtable_entry(0, NULL, NULL); + +#ifdef DEBUG + printf("psxcd_dbg: Directories in path table: %d\n", num_dirs); + + rbuff = resolve_pathtable_path(num_dirs-1, tpath_rbuff+127); + + if( !rbuff ) + { + printf("psxcd_dbg: Could not resolve path.\n"); + } + else + { + printf("psxcd_dbg: Longest path: %s|\n", rbuff); + } +#endif + + if( get_pathname(search_path, filename) ) + { +#ifdef DEBUG + printf("psxcd_dbg: Search path = %s|\n", search_path); +#endif + } + + // Search the pathtable for a matching path + found_dir = 0; + for(i=1; iname, filename); + + // Add version number if not specified + if( !strchr(fp->name, ';') ) + { + strcat(fp->name, ";1"); + } + +#ifdef DEBUG + dump_directory(); +#endif + + if( find_dir_entry(fp->name, &dir_entry) ) + { +#ifdef DEBUG + printf("psxcd: Could not find file.\n"); +#endif + return NULL; + } + +#ifdef DEBUG + printf("psxcd_dbg: Located file at LBA %d.\n", dir_entry.entryOffs.lsb); +#endif + + CdIntToPos(dir_entry.entryOffs.lsb, &fp->loc); + fp->size = dir_entry.entrySize.lsb; + + return fp; +} diff --git a/libpsn00b/libpsxcd/makefile b/libpsn00b/libpsxcd/makefile new file mode 100644 index 0000000..87466b3 --- /dev/null +++ b/libpsn00b/libpsxcd/makefile @@ -0,0 +1,38 @@ +# Run using make (Linux) or gmake (BSD) +# Part of the PSn00bSDK Project +# 2019 Lameguy64 / Meido-Tek Productions + +include ../common.mk + +TARGET = ../libpsxcd.a + +CFILES = $(notdir $(wildcard ./*.c)) +AFILES = $(notdir $(wildcard ./*.s)) +OFILES = $(addprefix build/,$(CFILES:.c=.o) $(AFILES:.s=.o)) + +INCLUDE = -I../include + +CFLAGS = -g -msoft-float -fno-builtin -fdata-sections -ffunction-sections -Wa,--strip-local-absolute +AFLAGS = -g -msoft-float -Wa,--strip-local-absolute + +CC = $(PREFIX)gcc +AS = $(PREFIX)as +AR = $(PREFIX)ar +RANLIB = $(PREFIX)ranlib + +all: $(TARGET) + +$(TARGET): $(OFILES) + $(AR) cr $(TARGET) $(OFILES) + $(RANLIB) $(TARGET) + +build/%.o: %.c + @mkdir -p $(dir $@) + $(CC) -O2 $(CFLAGS) $(INCLUDE) -c $< -o $@ + +build/%.o: %.s + @mkdir -p $(dir $@) + $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@ + +clean: + rm -Rf build $(TARGET) diff --git a/libpsn00b/libpsxcd/psxcd.c b/libpsn00b/libpsxcd/psxcd.c new file mode 100644 index 0000000..5be9eec --- /dev/null +++ b/libpsn00b/libpsxcd/psxcd.c @@ -0,0 +1,319 @@ +#include +#include "psxcd.h" + +extern volatile char _cd_ack_wait; +extern volatile unsigned char _cd_last_int; +extern volatile unsigned char _cd_last_mode; +extern volatile unsigned char _cd_status; +extern volatile CdlCB _cd_callback_int1_data; +volatile unsigned char *_cd_result_ptr; + +extern volatile char _cd_media_changed; + +void _cd_init(void); +void _cd_control(unsigned char com, unsigned char *param, int plen); +void _cd_wait_ack(void); +void _cd_wait(void); + +int CdInit(int mode) +{ + // 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); + printf("psxcd: Init Ok!\n"); + } + else + { + printf("psxcd: Error initializing. Bad disc/drive or no disc inserted.\n"); + } + + return 1; +} + +int CdControl(unsigned char com, unsigned char *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(); + + return 1; +} + +int CdControlB(unsigned char com, unsigned char *param, unsigned char *result) +{ + if( !CdControl(com, param, result) ) + { + return 0; + } + + CdSync(0, 0); + return 1; +} + +int CdControlF(unsigned char com, unsigned char *param) +{ + int param_len=0; + + // Command specific parameters + switch(com) + { + case CdlSetloc: + param_len = 3; + break; + case CdlPlay: + if( param ) + { + param_len = 1; + } + break; + case CdlSetfilter: + param_len = 2; + break; + case CdlSetmode: + param_len = 1; + break; + case CdlSetsession: + param_len = 1; + break; + case CdlTest: + param_len = 1; + break; + case CdlGetTD: + param_len = 1; + } + + // Issue Setloc if parameters are specified on CdlReadN and CdlReadS + if( ( com == CdlReadN ) || ( com == CdlReadS ) ) + { + if( param ) + { + _cd_control(CdlSetloc, param, 3); + } + } + + // 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) +{ + u_char 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; iminute = itob((i/75)/60); + p->second = itob((i/75)%60); + p->sector = itob(i%75); + + return p; + +} + +int CdPosToInt(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 unsigned int *_cd_read_addr; +volatile unsigned char _cd_read_result[8]; +volatile unsigned int _cd_read_oldcb; +volatile unsigned int _cd_read_sector_sz; +volatile CdlCB _cd_read_cb; + +// Sector callback +static void _CdReadReadyCallback(int status, unsigned char *result) +{ + 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>>2; + + // 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, unsigned int *buf, int mode) +{ + // Set sectors to read count + _cd_sector_count = sectors; + _cd_read_addr = buf; + + // Determine sector based on mode flags + if( mode & CdlModeSize0 ) + { + _cd_read_sector_sz = 2328; + } + else if( mode & CdlModeSize1 ) + { + _cd_read_sector_sz = 2340; + } + else + { + _cd_read_sector_sz = 2048; + } + + // Set readt callback + _cd_read_oldcb = CdReadyCallback(_CdReadReadyCallback); + + // Set specified mode + CdControl(CdlSetmode, (unsigned char*)&mode, 0); + + // Begin reading sectors + CdControl(CdlReadN, 0, (unsigned char*)_cd_read_result); + + return 0; +} + +int CdReadSync(int mode, unsigned char *result) +{ + if( mode ) + { + if( CdSync(1, 0) == CdlDiskError ) + { + return -1; + } + return _cd_sector_count; + } + + while(_cd_sector_count > 0); + if( CdSync(0, result) != CdlComplete ) + { + return -1; + } + + return 0; +} + +unsigned int 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/libpsxcd/psxcd_asm.s b/libpsn00b/libpsxcd/psxcd_asm.s new file mode 100644 index 0000000..7fe1c1d --- /dev/null +++ b/libpsn00b/libpsxcd/psxcd_asm.s @@ -0,0 +1,520 @@ +.set noreorder + +.include "hwregs_a.h" + +.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, 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) + + li $v0, 0x1325 + sw $v0, COM_DELAY($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, DPCR($a3) + li $v1, 0xB000 + or $v0, $v1 + sw $v0, DPCR($a3) + + jal ExitCriticalSection + nop + + #li $a0, 0x01 # GetStat + #jal _cd_control + #move $a2, $0 + #jal _cd_wait + #nop + + #li $a0, 0x0a # Init + #jal _cd_control + #move $a2, $0 + #jal _cd_wait + #nop + + #li $a0, 0x0c # Demute + #jal _cd_control + #move $a2, $0 + #jal _cd_wait + #nop + + #la $a0, _cd_init_msg + #jal printf + #addiu $sp, -16 + #addiu $sp, 16 + + 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 # Print out 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) # 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) + + 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 + +.Lread_futher_result: + + lbu $v0, CD_REG0($a3) + nop + andi $v0, 0x20 + beqz $v0, .Lno_result + nop + + lbu $v0, CD_REG1($a3) + lbu $v1, CD_REG0($a3) + sb $v0, 0($a0) + andi $v1, 0x20 + bnez $v1, .Lread_futher_result + addiu $a0, 1 + +.Lno_result: + + 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 + + jal EnterCriticalSection + nop + + lw $a0, 4($sp) + nop + sw $a0, 0($v1) + + jal ExitCriticalSection + nop + + 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) + + jal EnterCriticalSection + nop + + lw $a0, 4($sp) + nop + sw $a0, 0($v1) + + jal ExitCriticalSection + nop + + 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) + + jal EnterCriticalSection + nop + + lw $a0, 4($sp) + nop + sw $a0, 0($v1) + + jal ExitCriticalSection + nop + + lw $ra, 0($sp) + lw $v0, 8($sp) + jr $ra + addiu $sp, 12 + + +.section .data + +.global psxcd_credits +.type psxcd_credits, @object +psxgpu_credits: + .ascii "psxcd library programs by Lameguy64\n" + .asciiz "2019 PSn00bSDK Project / Meido-Tek Productions\n" + +_cd_init_msg: +.asciiz "psxcd: Init OK\n" +.align 4 + +.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 + +.comm _cd_media_changed, 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/libpsxcd/readme.txt b/libpsn00b/libpsxcd/readme.txt new file mode 100644 index 0000000..a85fab5 --- /dev/null +++ b/libpsn00b/libpsxcd/readme.txt @@ -0,0 +1,48 @@ +PSX CD-ROM library, part of PSn00bSDK +2019 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(). + + 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. + + +Library developer(s): + + Lameguy64 + + +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. + + * Original functions for querying directory contents in the file + system not yet implemented. \ No newline at end of file -- cgit v1.2.3