From: notaz Date: Sun, 6 Oct 2013 18:08:07 +0000 (+0300) Subject: cd: switch to CD drive emu code from genplus X-Git-Tag: v1.91~16 X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=274fcc35aa20e9777a8e09630a94088757384329;p=picodrive.git cd: switch to CD drive emu code from genplus same license, much cleaner code --- diff --git a/pico/cart.c b/pico/cart.c index 6a835b63..a5c563d6 100644 --- a/pico/cart.c +++ b/pico/cart.c @@ -157,7 +157,7 @@ zip_failed: if (f == NULL) goto cso_failed; -#ifndef __EPOC32__ +#ifdef __GP2X__ /* we use our own buffering */ setvbuf(f, NULL, _IONBF, 0); #endif @@ -227,7 +227,7 @@ cso_failed: strncpy(file->ext, ext, sizeof(file->ext) - 1); fseek(f, 0, SEEK_SET); -#ifndef __EPOC32__ // makes things worse on Symbian +#ifdef __GP2X__ if (file->size > 0x400000) /* we use our own buffering */ setvbuf(f, NULL, _IONBF, 0); diff --git a/pico/cd/LC89510.h b/pico/cd/LC89510.h deleted file mode 100644 index d641ebb3..00000000 --- a/pico/cd/LC89510.h +++ /dev/null @@ -1,43 +0,0 @@ -/*********************************************************** - * * - * This source was taken from the Gens project * - * Written by Stéphane Dallongeville * - * Copyright (c) 2002 by Stéphane Dallongeville * - * Modified/adapted for PicoDrive by notaz, 2007 * - * * - ***********************************************************/ - -#ifndef _LC89510_H -#define _LC89510_H - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct -{ -// unsigned short Fader; // 34 -// unsigned short Control; // 36 -// unsigned short Cur_Comm;// unused - - // "Receive status" - unsigned short Status; - unsigned short Minute; - unsigned short Seconde; - unsigned short Frame; - unsigned char Ext; - unsigned char pad[3]; -} CDD; - - -PICO_INTERNAL void CDD_Export_Status(void); -PICO_INTERNAL void CDD_Import_Command(void); - -void CDD_Reset(void); - -#ifdef __cplusplus -}; -#endif - -#endif - diff --git a/pico/cd/buffering.c b/pico/cd/buffering.c deleted file mode 100644 index 84203369..00000000 --- a/pico/cd/buffering.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Buffering handling - * (C) notaz, 2007,2008 - * - * This work is licensed under the terms of MAME license. - * See COPYING file in the top-level directory. - */ - -#include "../pico_int.h" -#include "../cd/cue.h" - -int PicoCDBuffers = 0; -static unsigned char *cd_buffer = NULL; -static int prev_lba = 0x80000000; - -static int hits, reads; - -#undef dprintf -#define dprintf(...) - -void PicoCDBufferInit(void) -{ - void *tmp = NULL; - - prev_lba = 0x80000000; - hits = reads = 0; - - if (PicoCDBuffers <= 1) { - PicoCDBuffers = 0; - return; /* buffering off */ - } - - /* try alloc'ing until we succeed */ - while (PicoCDBuffers > 0) - { - tmp = realloc(cd_buffer, PicoCDBuffers * 2048 + 304); - if (tmp != NULL) break; - PicoCDBuffers >>= 1; - } - - if (PicoCDBuffers <= 0) return; /* buffering became off */ - - cd_buffer = tmp; -} - - -void PicoCDBufferFree(void) -{ - if (cd_buffer) { - free(cd_buffer); - cd_buffer = NULL; - } - if (reads) - elprintf(EL_STATUS, "CD buffer hits: %i/%i (%i%%)\n", hits, reads, hits * 100 / reads); -} - - -void PicoCDBufferFlush(void) -{ - prev_lba = 0x80000000; -} - - -/* this is was a try to fight slow SD access of GP2X */ -PICO_INTERNAL void PicoCDBufferRead(void *dest, int lba) -{ - int is_bin, offs, read_len, moved = 0; - reads++; - - is_bin = Pico_mcd->TOC.Tracks[0].ftype == CT_BIN; - - if (PicoCDBuffers <= 0) - { - /* no buffering */ - int where_seek = is_bin ? (lba * 2352 + 16) : (lba << 11); - pm_seek(Pico_mcd->TOC.Tracks[0].F, where_seek, SEEK_SET); - pm_read(dest, 2048, Pico_mcd->TOC.Tracks[0].F); - return; - } - - /* hit? */ - offs = lba - prev_lba; - if (offs >= 0 && offs < PicoCDBuffers) - { - hits++; - if (offs == 0) dprintf("CD buffer seek to old %i -> %i\n", prev_lba, lba); - memcpy32(dest, (int *)(cd_buffer + offs*2048), 2048/4); - return; - } - - if (prev_lba + PicoCDBuffers != lba) - { - int where_seek = is_bin ? (lba * 2352 + 16) : (lba << 11); - dprintf("CD buffer seek %i -> %i\n", prev_lba, lba); - pm_seek(Pico_mcd->TOC.Tracks[0].F, where_seek, SEEK_SET); - } - - dprintf("CD buffer miss %i -> %i\n", prev_lba, lba); - - if (lba < prev_lba && prev_lba - lba < PicoCDBuffers) - { - read_len = prev_lba - lba; - dprintf("CD buffer move=%i, read_len=%i", PicoCDBuffers - read_len, read_len); - memmove(cd_buffer + read_len*2048, cd_buffer, (PicoCDBuffers - read_len)*2048); - moved = 1; - } - else - { - read_len = PicoCDBuffers; - } - - if (PicoMessage != NULL && read_len >= 512) - { - PicoMessage("Buffering data..."); - } - - if (is_bin) - { - int i = 0; -#ifdef _PSP_FW_VERSION - int bufs = (read_len*2048) / (2048+304); - pm_read(cd_buffer, bufs*(2048+304), Pico_mcd->TOC.Tracks[0].F); - for (i = 1; i < bufs; i++) - // should really use memmove here, but my memcpy32 implementation is also suitable here - memcpy32((int *)(cd_buffer + i*2048), (int *)(cd_buffer + i*(2048+304)), 2048/4); -#endif - for (; i < read_len - 1; i++) - { - pm_read(cd_buffer + i*2048, 2048 + 304, Pico_mcd->TOC.Tracks[0].F); - // pm_seek(Pico_mcd->TOC.Tracks[0].F, 304, SEEK_CUR); // seeking is slower, in PSP case even more - } - // further data might be moved, do not overwrite - pm_read(cd_buffer + i*2048, 2048, Pico_mcd->TOC.Tracks[0].F); - pm_seek(Pico_mcd->TOC.Tracks[0].F, 304, SEEK_CUR); - } - else - { - pm_read(cd_buffer, read_len*2048, Pico_mcd->TOC.Tracks[0].F); - } - memcpy32(dest, (int *) cd_buffer, 2048/4); - prev_lba = lba; - - if (moved) - { - /* file pointer must point to the same data in file, as would-be data after our buffer */ - int where_seek; - lba += PicoCDBuffers; - where_seek = is_bin ? (lba * 2352 + 16) : (lba << 11); - pm_seek(Pico_mcd->TOC.Tracks[0].F, where_seek, SEEK_SET); - } -} - diff --git a/pico/cd/cd_file.c b/pico/cd/cd_file.c deleted file mode 100644 index 43bbb5d8..00000000 --- a/pico/cd/cd_file.c +++ /dev/null @@ -1,402 +0,0 @@ -/*********************************************************** - * * - * This source was taken from the Gens project * - * Written by Stéphane Dallongeville * - * Copyright (c) 2002 by Stéphane Dallongeville * - * Modified/adapted for PicoDrive by notaz, 2007 * - * * - ***********************************************************/ - -#include "../pico_int.h" -#include "cd_file.h" -#include "cue.h" - -//#define cdprintf(f,...) printf(f "\n",##__VA_ARGS__) // tmp - -static void to_upper(char *d, const char *s) -{ - for (; *s != 0; d++, s++) { - if ('a' <= *s && *s <= 'z') - *d = *s - 'a' + 'A'; - else - *d = *s; - } -} - -static int audio_track_mp3(const char *fname, int index) -{ - _scd_track *Tracks = Pico_mcd->TOC.Tracks; - FILE *tmp_file; - int fs, ret; - - tmp_file = fopen(fname, "rb"); - if (tmp_file == NULL) - return -1; - - ret = fseek(tmp_file, 0, SEEK_END); - fs = ftell(tmp_file); // used to calculate length - fseek(tmp_file, 0, SEEK_SET); - -#ifdef _PSP_FW_VERSION - // some systems (like PSP) can't have many open files at a time, - // so we work with their names instead. - fclose(tmp_file); - tmp_file = (void *) strdup(fname); -#endif - Tracks[index].KBtps = (short) mp3_get_bitrate(tmp_file, fs); - Tracks[index].KBtps >>= 3; - if (ret != 0 || Tracks[index].KBtps <= 0) - { - elprintf(EL_STATUS, "track %2i: mp3 bitrate %i", index+1, Tracks[index].KBtps); -#ifdef _PSP_FW_VERSION - free(tmp_file); -#else - fclose(tmp_file); -#endif - return -1; - } - - Tracks[index].F = tmp_file; - - // MP3 File - Tracks[index].ftype = CT_MP3; - fs *= 75; - fs /= Tracks[index].KBtps * 1000; - Tracks[index].Length = fs; - Tracks[index].Offset = 0; - - return 0; -} - -PICO_INTERNAL int Load_CD_Image(const char *cd_img_name, cd_img_type type) -{ - int i, j, num_track, Cur_LBA, index, ret; - int iso_name_len, missed, cd_img_sectors; - _scd_track *Tracks = Pico_mcd->TOC.Tracks; - char tmp_name[256], tmp_ext[10], tmp_ext_u[10]; - cue_data_t *cue_data = NULL; - pm_file *pmf; - static const char *exts[] = { - "%02d.mp3", " %02d.mp3", "-%02d.mp3", "_%02d.mp3", " - %02d.mp3", - "%d.mp3", " %d.mp3", "-%d.mp3", "_%d.mp3", " - %d.mp3", - }; - - if (PicoCDLoadProgressCB != NULL) - PicoCDLoadProgressCB(cd_img_name, 1); - - Unload_ISO(); - - /* is this a .cue? */ - cue_data = cue_parse(cd_img_name); - if (cue_data != NULL) { - cd_img_name = cue_data->tracks[1].fname; - Tracks[0].ftype = cue_data->tracks[1].type; - } - else - Tracks[0].ftype = type == CIT_BIN ? CT_BIN : CT_ISO; - - Tracks[0].F = pmf = pm_open(cd_img_name); - if (Tracks[0].F == NULL) - { - Tracks[0].ftype = 0; - Tracks[0].Length = 0; - if (cue_data != NULL) - cue_destroy(cue_data); - return -1; - } - - if (Tracks[0].ftype == CT_ISO) - cd_img_sectors = pmf->size >>= 11; // size in sectors - else cd_img_sectors = pmf->size /= 2352; - Tracks[0].Offset = 0; - - Tracks[0].MSF.M = 0; // minutes - Tracks[0].MSF.S = 2; // seconds - Tracks[0].MSF.F = 0; // frames - - elprintf(EL_STATUS, "Track 1: %02d:%02d:%02d %9i DATA %s", - Tracks[0].MSF.M, Tracks[0].MSF.S, Tracks[0].MSF.F, - Tracks[0].Length, cd_img_name); - - Cur_LBA = Tracks[0].Length = cd_img_sectors; - - if (cue_data != NULL) - { - if (cue_data->tracks[2].fname == NULL) { // NULL means track2 is in same file as track1 - Cur_LBA = Tracks[0].Length = cue_data->tracks[2].sector_offset; - } - i = 100 / cue_data->track_count+1; - for (num_track = 2; num_track <= cue_data->track_count; num_track++) - { - if (PicoCDLoadProgressCB != NULL) - PicoCDLoadProgressCB(cd_img_name, i * num_track); - index = num_track - 1; - Cur_LBA += cue_data->tracks[num_track].pregap; - if (cue_data->tracks[num_track].type == CT_MP3) { - ret = audio_track_mp3(cue_data->tracks[num_track].fname, index); - if (ret != 0) break; - } - else - { - Tracks[index].ftype = cue_data->tracks[num_track].type; - if (cue_data->tracks[num_track].fname != NULL) - { - pm_file *pmfn = pm_open(cue_data->tracks[num_track].fname); - if (pmfn != NULL) - { - // addume raw, ignore header for wav.. - Tracks[index].F = pmfn; - Tracks[index].Length = pmfn->size / 2352; - Tracks[index].Offset = cue_data->tracks[num_track].sector_offset; - } - else - { - elprintf(EL_STATUS, "track %2i (%s): can't determine length", - num_track, cue_data->tracks[num_track].fname); - Tracks[index].Length = 2*75; - Tracks[index].Offset = 0; - } - } - else - { - if (num_track < cue_data->track_count) - Tracks[index].Length = cue_data->tracks[num_track+1].sector_offset - - cue_data->tracks[num_track].sector_offset; - else - Tracks[index].Length = cd_img_sectors - cue_data->tracks[num_track].sector_offset; - Tracks[index].Offset = cue_data->tracks[num_track].sector_offset; - } - } - - if (cue_data->tracks[num_track].sector_xlength != 0) - // overriden by custom cue command - Tracks[index].Length = cue_data->tracks[num_track].sector_xlength; - - LBA_to_MSF(Cur_LBA, &Tracks[index].MSF); - Cur_LBA += Tracks[index].Length; - - elprintf(EL_STATUS, "Track %2i: %02d:%02d:%02d %9i AUDIO %s", num_track, Tracks[index].MSF.M, - Tracks[index].MSF.S, Tracks[index].MSF.F, Tracks[index].Length, - cue_data->tracks[num_track].fname); - } - cue_destroy(cue_data); - goto finish; - } - - /* mp3 track autosearch, Gens-like */ - iso_name_len = strlen(cd_img_name); - if (iso_name_len >= sizeof(tmp_name)) - iso_name_len = sizeof(tmp_name) - 1; - - for (num_track = 2, i = 0, missed = 0; i < 100 && missed < 4; i++) - { - if (PicoCDLoadProgressCB != NULL && i > 1) - PicoCDLoadProgressCB(cd_img_name, i + (100-i)*missed/4); - - for (j = 0; j < sizeof(exts)/sizeof(char *); j++) - { - int ext_len; - char *p; - - index = num_track - 1; - - sprintf(tmp_ext, exts[j], i); - ext_len = strlen(tmp_ext); - to_upper(tmp_ext_u, tmp_ext); - - memcpy(tmp_name, cd_img_name, iso_name_len + 1); - p = tmp_name + iso_name_len - 4; - - strcpy(p, tmp_ext); - ret = audio_track_mp3(tmp_name, index); - if (ret != 0) { - strcpy(p, tmp_ext_u); - ret = audio_track_mp3(tmp_name, index); - } - - if (ret != 0 && i > 1 && iso_name_len > ext_len) { - p = tmp_name + iso_name_len - ext_len; - strcpy(p, tmp_ext); - ret = audio_track_mp3(tmp_name, index); - if (ret != 0) { - strcpy(p, tmp_ext_u); - ret = audio_track_mp3(tmp_name, index); - } - } - - if (ret == 0) - { - LBA_to_MSF(Cur_LBA, &Tracks[index].MSF); - Cur_LBA += Tracks[index].Length; - - elprintf(EL_STATUS, "Track %2i: %02d:%02d:%02d %9i AUDIO - %s", num_track, Tracks[index].MSF.M, - Tracks[index].MSF.S, Tracks[index].MSF.F, Tracks[index].Length, tmp_name); - - num_track++; - missed = 0; - break; - } - } - if (ret != 0 && i > 1) missed++; - } - -finish: - Pico_mcd->TOC.Last_Track = num_track - 1; - - index = num_track - 1; - - LBA_to_MSF(Cur_LBA, &Tracks[index].MSF); - - elprintf(EL_STATUS, "End CD - %02d:%02d:%02d\n", Tracks[index].MSF.M, - Tracks[index].MSF.S, Tracks[index].MSF.F); - - if (PicoCDLoadProgressCB != NULL) - PicoCDLoadProgressCB(cd_img_name, 100); - - return 0; -} - - -PICO_INTERNAL void Unload_ISO(void) -{ - int i; - - if (Pico_mcd == NULL) return; - - if (Pico_mcd->TOC.Tracks[0].F) pm_close(Pico_mcd->TOC.Tracks[0].F); - - for(i = 1; i < 100; i++) - { - if (Pico_mcd->TOC.Tracks[i].F != NULL) - { - if (Pico_mcd->TOC.Tracks[i].ftype == CT_MP3) -#ifdef _PSP_FW_VERSION - free(Pico_mcd->TOC.Tracks[i].F); -#else - fclose(Pico_mcd->TOC.Tracks[i].F); -#endif - else - pm_close(Pico_mcd->TOC.Tracks[i].F); - } - } - memset(Pico_mcd->TOC.Tracks, 0, sizeof(Pico_mcd->TOC.Tracks)); -} - -#if 1*0 - -PICO_INTERNAL int FILE_Read_One_LBA_CDC(void) -{ - if (Pico_mcd->s68k_regs[0x36] & 1) // DATA - { - if (Pico_mcd->TOC.Tracks[0].F == NULL) return -1; - - // moved below.. - //fseek(Pico_mcd->TOC.Tracks[0].F, where_read, SEEK_SET); - //fread(cp_buf, 1, 2048, Pico_mcd->TOC.Tracks[0].F); - - cdprintf("Read file CDC 1 data sector :\n"); - } - else // AUDIO - { - cdprintf("Read file CDC 1 audio sector :\n"); - } - - // Update CDC stuff - - CDC_Update_Header(); - - if (Pico_mcd->s68k_regs[0x36] & 1) // DATA track - { - if (Pico_mcd->cdc.CTRL.B.B0 & 0x80) // DECEN = decoding enable - { - if (Pico_mcd->cdc.CTRL.B.B0 & 0x04) // WRRQ : this bit enable write to buffer - { - int where_read = 0; - - // CAUTION : lookahead bit not implemented - - if (Pico_mcd->scd.Cur_LBA < 0) - where_read = 0; - else if (Pico_mcd->scd.Cur_LBA >= Pico_mcd->TOC.Tracks[0].Length) - where_read = Pico_mcd->TOC.Tracks[0].Length - 1; - else where_read = Pico_mcd->scd.Cur_LBA; - - Pico_mcd->scd.Cur_LBA++; - - Pico_mcd->cdc.WA.N = (Pico_mcd->cdc.WA.N + 2352) & 0x7FFF; // add one sector to WA - Pico_mcd->cdc.PT.N = (Pico_mcd->cdc.PT.N + 2352) & 0x7FFF; - - *(unsigned int *)(Pico_mcd->cdc.Buffer + Pico_mcd->cdc.PT.N) = Pico_mcd->cdc.HEAD.N; - //memcpy(&Pico_mcd->cdc.Buffer[Pico_mcd->cdc.PT.N + 4], cp_buf, 2048); - - //pm_seek(Pico_mcd->TOC.Tracks[0].F, where_read, SEEK_SET); - //pm_read(Pico_mcd->cdc.Buffer + Pico_mcd->cdc.PT.N + 4, 2048, Pico_mcd->TOC.Tracks[0].F); - PicoCDBufferRead(Pico_mcd->cdc.Buffer + Pico_mcd->cdc.PT.N + 4, where_read); - - cdprintf("Read -> WA = %d Buffer[%d] =", Pico_mcd->cdc.WA.N, Pico_mcd->cdc.PT.N & 0x3FFF); - cdprintf("Header 1 = %.2X %.2X %.2X %.2X", Pico_mcd->cdc.HEAD.B.B0, - Pico_mcd->cdc.HEAD.B.B1, Pico_mcd->cdc.HEAD.B.B2, Pico_mcd->cdc.HEAD.B.B3); - cdprintf("Header 2 = %.2X %.2X %.2X %.2X --- %.2X %.2X\n\n", - Pico_mcd->cdc.Buffer[(Pico_mcd->cdc.PT.N + 0) & 0x3FFF], - Pico_mcd->cdc.Buffer[(Pico_mcd->cdc.PT.N + 1) & 0x3FFF], - Pico_mcd->cdc.Buffer[(Pico_mcd->cdc.PT.N + 2) & 0x3FFF], - Pico_mcd->cdc.Buffer[(Pico_mcd->cdc.PT.N + 3) & 0x3FFF], - Pico_mcd->cdc.Buffer[(Pico_mcd->cdc.PT.N + 4) & 0x3FFF], - Pico_mcd->cdc.Buffer[(Pico_mcd->cdc.PT.N + 5) & 0x3FFF]); - } - - } - } - else // music track - { - Pico_mcd->scd.Cur_LBA++; - - Pico_mcd->cdc.WA.N = (Pico_mcd->cdc.WA.N + 2352) & 0x7FFF; // add one sector to WA - Pico_mcd->cdc.PT.N = (Pico_mcd->cdc.PT.N + 2352) & 0x7FFF; - - if (Pico_mcd->cdc.CTRL.B.B0 & 0x80) // DECEN = decoding enable - { - if (Pico_mcd->cdc.CTRL.B.B0 & 0x04) // WRRQ : this bit enable write to buffer - { - // CAUTION : lookahead bit not implemented - - // this is pretty rough, but oh well - not much depends on this anyway - memcpy(&Pico_mcd->cdc.Buffer[Pico_mcd->cdc.PT.N], cdda_out_buffer, 2352); - } - } - } - - if (Pico_mcd->cdc.CTRL.B.B0 & 0x80) // DECEN = decoding enable - { - Pico_mcd->cdc.STAT.B.B0 = 0x80; - - if (Pico_mcd->cdc.CTRL.B.B0 & 0x10) // determine form bit form sub header ? - { - Pico_mcd->cdc.STAT.B.B2 = Pico_mcd->cdc.CTRL.B.B1 & 0x08; - } - else - { - Pico_mcd->cdc.STAT.B.B2 = Pico_mcd->cdc.CTRL.B.B1 & 0x0C; - } - - if (Pico_mcd->cdc.CTRL.B.B0 & 0x02) Pico_mcd->cdc.STAT.B.B3 = 0x20; // ECC done - else Pico_mcd->cdc.STAT.B.B3 = 0x00; // ECC not done - - if (Pico_mcd->cdc.IFCTRL & 0x20) - { - if (Pico_mcd->s68k_regs[0x33] & (1<<5)) - { - elprintf(EL_INTS, "cdc dec irq 5"); - SekInterruptS68k(5); - } - - Pico_mcd->cdc.IFSTAT &= ~0x20; // DEC interrupt happen - Pico_mcd->cdc.Decode_Reg_Read = 0; // Reset read after DEC int - } - } - - - return 0; -} - -#endif diff --git a/pico/cd/cd_file.h b/pico/cd/cd_file.h deleted file mode 100644 index f9bb8eac..00000000 --- a/pico/cd/cd_file.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef _CD_FILE_H -#define _CD_FILE_H - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum -{ - CIT_NOT_CD = 0, - CIT_ISO, - CIT_BIN, - CIT_CUE -} -cd_img_type; - - -PICO_INTERNAL int Load_CD_Image(const char *iso_name, cd_img_type type); -PICO_INTERNAL void Unload_ISO(void); -PICO_INTERNAL int FILE_Read_One_LBA_CDC(void); - - -#ifdef __cplusplus -}; -#endif - -#endif diff --git a/pico/cd/cd_image.c b/pico/cd/cd_image.c new file mode 100644 index 00000000..97c8f3f0 --- /dev/null +++ b/pico/cd/cd_image.c @@ -0,0 +1,266 @@ +/* + * CD image handler + * (C) notaz, 2007,2013 + * + * This work is licensed under the terms of MAME license. + * See COPYING file in the top-level directory. + */ + +#include "../pico_int.h" +#include "genplus_macros.h" +#include "cdd.h" +#include "cue.h" + +static int handle_mp3(const char *fname, int index) +{ + track_t *track = &cdd.toc.tracks[index]; + FILE *tmp_file; + int kBps; + int fs, ret; + + tmp_file = fopen(fname, "rb"); + if (tmp_file == NULL) + return -1; + + ret = fseek(tmp_file, 0, SEEK_END); + fs = ftell(tmp_file); + fseek(tmp_file, 0, SEEK_SET); + +#ifdef _PSP_FW_VERSION + // some systems (like PSP) can't have many open files at a time, + // so we work with their names instead. + fclose(tmp_file); + tmp_file = (void *) strdup(fname); +#endif + + kBps = mp3_get_bitrate(tmp_file, fs) / 8; + if (ret != 0 || kBps <= 0) + { + elprintf(EL_STATUS, "track %2i: mp3 bitrate %i", index+1, kBps); +#ifdef _PSP_FW_VERSION + free(tmp_file); +#else + fclose(tmp_file); +#endif + return -1; + } + + track->fd = tmp_file; + track->offset = 0; + + fs *= 75; + fs /= kBps * 1000; + return fs; +} + +static void to_upper(char *d, const char *s) +{ + for (; *s != 0; d++, s++) { + if ('a' <= *s && *s <= 'z') + *d = *s - 'a' + 'A'; + else + *d = *s; + } +} + +// cdd.c uses lba - 150 +static void sprintf_lba(char *buf, size_t size, int lba) +{ + lba += 150; + snprintf(buf, size, "%02d:%02d:%02d", lba / 60 / 75, + (lba / 75) % 60, lba % 75); +} + +int load_cd_image(const char *cd_img_name, int *type) +{ + static const char *exts[] = { + "%02d.mp3", " %02d.mp3", "-%02d.mp3", "_%02d.mp3", " - %02d.mp3", + "%d.mp3", " %d.mp3", "-%d.mp3", "_%d.mp3", " - %d.mp3", + }; + int i, j, n, lba, index, length, ret; + int iso_name_len, missed, cd_img_sectors; + char tmp_name[256], tmp_ext[10], tmp_ext_u[10]; + track_t *tracks = cdd.toc.tracks; + cue_data_t *cue_data = NULL; + pm_file *pmf; + + if (PicoCDLoadProgressCB != NULL) + PicoCDLoadProgressCB(cd_img_name, 1); + + Pico_mcd->cdda_type = CT_UNKNOWN; + + /* is this a .cue? */ + cue_data = cue_parse(cd_img_name); + if (cue_data != NULL) { + cd_img_name = cue_data->tracks[1].fname; + *type = cue_data->tracks[1].type; + } + + pmf = pm_open(cd_img_name); + if (pmf == NULL) + { + if (cue_data != NULL) + cue_destroy(cue_data); + return -1; + } + tracks[0].fd = pmf; + + if (*type == CT_ISO) + cd_img_sectors = pmf->size >>= 11; // size in sectors + else cd_img_sectors = pmf->size /= 2352; + + // cdd.c operates with lba - 150 + tracks[0].start = 0; + tracks[0].end = cd_img_sectors; + tracks[0].offset = 0; + + sprintf_lba(tmp_ext, sizeof(tmp_ext), 0); + elprintf(EL_STATUS, "Track 1: %s %9i DATA %s", + tmp_ext, tracks[0].end, cd_img_name); + + lba = cd_img_sectors; + + if (cue_data != NULL) + { + if (cue_data->tracks[2].fname == NULL) { + // NULL fname means track2 is in same file as track1 + lba = tracks[0].end = cue_data->tracks[2].sector_offset; + } + i = 100 / cue_data->track_count + 1; // progress display + + for (n = 2; n <= cue_data->track_count; n++) + { + if (PicoCDLoadProgressCB != NULL) + PicoCDLoadProgressCB(cd_img_name, i * n); + + index = n - 1; + lba += cue_data->tracks[n].pregap; + if (cue_data->tracks[n].type == CT_MP3) { + ret = handle_mp3(cue_data->tracks[n].fname, index); + if (ret < 0) + break; + length = ret; + } + else if (cue_data->tracks[n].fname != NULL) + { + pm_file *f = pm_open(cue_data->tracks[n].fname); + if (f != NULL) + { + // assume raw, ignore header for wav.. + tracks[index].fd = f; + tracks[index].offset = cue_data->tracks[n].sector_offset; + length = f->size / 2352; + } + else + { + elprintf(EL_STATUS, "track %2i (%s): can't determine length", + n, cue_data->tracks[n].fname); + tracks[index].offset = 0; + length = 2*75; + } + } + else + { + if (n < cue_data->track_count) + length = cue_data->tracks[n+1].sector_offset - + cue_data->tracks[n].sector_offset; + else + length = cd_img_sectors - cue_data->tracks[n].sector_offset; + tracks[index].offset = cue_data->tracks[n].sector_offset; + } + + if (cue_data->tracks[n].sector_xlength != 0) + // overriden by custom cue command + length = cue_data->tracks[n].sector_xlength; + + Pico_mcd->cdda_type = cue_data->tracks[n].type; + + tracks[index].start = lba; + lba += length; + tracks[index].end = lba; + + sprintf_lba(tmp_ext, sizeof(tmp_ext), tracks[index].start); + elprintf(EL_STATUS, "Track %2i: %s %9i AUDIO %s", + n, tmp_ext, length, cue_data->tracks[n].fname); + } + cue_destroy(cue_data); + goto finish; + } + + /* mp3 track autosearch, Gens-like */ + iso_name_len = strlen(cd_img_name); + if (iso_name_len >= sizeof(tmp_name)) + iso_name_len = sizeof(tmp_name) - 1; + + for (n = 2, i = 0, missed = 0; i < 100 && missed < 4; i++) + { + if (PicoCDLoadProgressCB != NULL && i > 1) + PicoCDLoadProgressCB(cd_img_name, i + (100-i)*missed/4); + + for (j = 0; j < sizeof(exts)/sizeof(char *); j++) + { + int ext_len; + char *p; + + index = n - 1; + + snprintf(tmp_ext, sizeof(tmp_ext), exts[j], i); + ext_len = strlen(tmp_ext); + to_upper(tmp_ext_u, tmp_ext); + + memcpy(tmp_name, cd_img_name, iso_name_len + 1); + p = tmp_name + iso_name_len - 4; + + strcpy(p, tmp_ext); + ret = handle_mp3(tmp_name, index); + if (ret <= 0) { + strcpy(p, tmp_ext_u); + ret = handle_mp3(tmp_name, index); + } + + if (ret <= 0 && i > 1 && iso_name_len > ext_len) { + p = tmp_name + iso_name_len - ext_len; + strcpy(p, tmp_ext); + ret = handle_mp3(tmp_name, index); + if (ret <= 0) { + strcpy(p, tmp_ext_u); + ret = handle_mp3(tmp_name, index); + } + } + + if (ret > 0) + { + length = ret; + tracks[index].start = lba; + lba += length; + tracks[index].end = lba; + + Pico_mcd->cdda_type = CT_MP3; + + sprintf_lba(tmp_ext, sizeof(tmp_ext), tracks[index].start); + elprintf(EL_STATUS, "Track %2i: %s %9i AUDIO - %s", + n, tmp_ext, length, tmp_name); + + n++; + missed = 0; + break; + } + } + if (ret <= 0 && i > 1) + missed++; + } + +finish: + cdd.toc.last = n - 1; + cdd.toc.end = lba; + + sprintf_lba(tmp_ext, sizeof(tmp_ext), cdd.toc.end); + elprintf(EL_STATUS, "End CD - %s\n", tmp_ext); + + if (PicoCDLoadProgressCB != NULL) + PicoCDLoadProgressCB(cd_img_name, 100); + + return 0; +} + +// vim:shiftwidth=2:ts=2:expandtab diff --git a/pico/cd/cd_sys.c b/pico/cd/cd_sys.c deleted file mode 100644 index f7cd7b5e..00000000 --- a/pico/cd/cd_sys.c +++ /dev/null @@ -1,933 +0,0 @@ -/*********************************************************** - * * - * This source file was taken from the Gens project * - * Written by Stéphane Dallongeville * - * Copyright (c) 2002 by Stéphane Dallongeville * - * Modified/adapted for PicoDrive by notaz, 2007 * - * * - ***********************************************************/ - -#include - -#include "../pico_int.h" -#include "cd_sys.h" -#include "cd_file.h" - -#define DEBUG_CD - -#define TRAY_OPEN 0x0500 // TRAY OPEN CDD status -#define NOCD 0x0000 // CD removed CDD status -#define STOPPED 0x0900 // STOPPED CDD status (happen after stop or close tray command) -#define READY 0x0400 // READY CDD status (also used for seeking) -#define FAST_FOW 0x0300 // FAST FORWARD track CDD status -#define FAST_REV 0x10300 // FAST REVERSE track CDD status -#define PLAYING 0x0100 // PLAYING audio track CDD status - -//#undef cdprintf -//#define cdprintf(x, ...) elprintf(EL_STATUS, x, ##__VA_ARGS__) - -#define CDC_Update_Header() - -static int CD_Present = 0; - - -#define CHECK_TRAY_OPEN \ -if (Pico_mcd->scd.Status_CDD == TRAY_OPEN) \ -{ \ - Pico_mcd->cdd.Status = Pico_mcd->scd.Status_CDD; \ - \ - Pico_mcd->cdd.Minute = 0; \ - Pico_mcd->cdd.Seconde = 0; \ - Pico_mcd->cdd.Frame = 0; \ - Pico_mcd->cdd.Ext = 0; \ - \ - Pico_mcd->scd.CDD_Complete = 1; \ - \ - return 2; \ -} - - -#define CHECK_CD_PRESENT \ -if (!CD_Present) \ -{ \ - Pico_mcd->scd.Status_CDD = NOCD; \ - Pico_mcd->cdd.Status = Pico_mcd->scd.Status_CDD; \ - \ - Pico_mcd->cdd.Minute = 0; \ - Pico_mcd->cdd.Seconde = 0; \ - Pico_mcd->cdd.Frame = 0; \ - Pico_mcd->cdd.Ext = 0; \ - \ - Pico_mcd->scd.CDD_Complete = 1; \ - \ - return 3; \ -} - - -static int MSF_to_LBA(_msf *MSF) -{ - return (MSF->M * 60 * 75) + (MSF->S * 75) + MSF->F - 150; -} - - -PICO_INTERNAL void LBA_to_MSF(int lba, _msf *MSF) -{ - if (lba < -150) lba = 0; - else lba += 150; - MSF->M = lba / (60 * 75); - MSF->S = (lba / 75) % 60; - MSF->F = lba % 75; -} - - -static unsigned int MSF_to_Track(_msf *MSF) -{ - int i, Start, Cur; - - Start = (MSF->M << 16) + (MSF->S << 8) + MSF->F; - - for(i = 1; i <= (Pico_mcd->TOC.Last_Track + 1); i++) - { - Cur = Pico_mcd->TOC.Tracks[i - 1].MSF.M << 16; - Cur += Pico_mcd->TOC.Tracks[i - 1].MSF.S << 8; - Cur += Pico_mcd->TOC.Tracks[i - 1].MSF.F; - - if (Cur > Start) break; - } - - --i; - - if (i > Pico_mcd->TOC.Last_Track) return 100; - else if (i < 1) i = 1; - - return (unsigned) i; -} - - -static unsigned int LBA_to_Track(int lba) -{ - _msf MSF; - - LBA_to_MSF(lba, &MSF); - return MSF_to_Track(&MSF); -} - - -static void Track_to_MSF(int track, _msf *MSF) -{ - if (track < 1) track = 1; - else if (track > Pico_mcd->TOC.Last_Track) track = Pico_mcd->TOC.Last_Track; - - MSF->M = Pico_mcd->TOC.Tracks[track - 1].MSF.M; - MSF->S = Pico_mcd->TOC.Tracks[track - 1].MSF.S; - MSF->F = Pico_mcd->TOC.Tracks[track - 1].MSF.F; -} - - -PICO_INTERNAL int Track_to_LBA(int track) -{ - _msf MSF; - - Track_to_MSF(track, &MSF); - return MSF_to_LBA(&MSF); -} - - -PICO_INTERNAL void Check_CD_Command(void) -{ - cdprintf("CHECK CD COMMAND"); - - // Check CDC - if (Pico_mcd->scd.Status_CDC & 1) // CDC is reading data ... - { - cdprintf("Got a read command"); - - // DATA ? - if (Pico_mcd->scd.Cur_Track == 1) { - Pico_mcd->s68k_regs[0x36] |= 0x01; - - if (Pico_mcd->scd.File_Add_Delay == 0) - { - unsigned char header[4]; - _msf MSF; - - LBA_to_MSF(Pico_mcd->scd.Cur_LBA, &MSF); - - header[0] = INT_TO_BCDB(MSF.M); - header[1] = INT_TO_BCDB(MSF.S); - header[2] = INT_TO_BCDB(MSF.F); - header[3] = 0x01; - - //FILE_Read_One_LBA_CDC(); - Pico_mcd->scd.Cur_LBA += - cdc_decoder_update(header); - } - else Pico_mcd->scd.File_Add_Delay--; - } - else { - Pico_mcd->s68k_regs[0x36] &= ~0x01; // AUDIO - unsigned char header[4] = { 0, }; - cdc_decoder_update(header); - } - } - - // Check CDD - if (Pico_mcd->scd.CDD_Complete) - { - Pico_mcd->scd.CDD_Complete = 0; - - CDD_Export_Status(); - } - - if (Pico_mcd->scd.Status_CDD == FAST_FOW) - { - Pico_mcd->scd.Cur_LBA += 10; - CDC_Update_Header(); - - } - else if (Pico_mcd->scd.Status_CDD == FAST_REV) - { - Pico_mcd->scd.Cur_LBA -= 10; - if (Pico_mcd->scd.Cur_LBA < -150) Pico_mcd->scd.Cur_LBA = -150; - CDC_Update_Header(); - } -} - - -PICO_INTERNAL int Init_CD_Driver(void) -{ - return 0; -} - - -PICO_INTERNAL void End_CD_Driver(void) -{ - Unload_ISO(); -} - - -PICO_INTERNAL void Reset_CD(void) -{ - Pico_mcd->scd.Cur_Track = 0; - Pico_mcd->scd.Cur_LBA = -150; - Pico_mcd->scd.Status_CDC &= ~1; - if (Pico_mcd->scd.Status_CDD != TRAY_OPEN) - Pico_mcd->scd.Status_CDD = CD_Present ? READY : NOCD; - Pico_mcd->scd.CDD_Complete = 0; - Pico_mcd->scd.File_Add_Delay = 0; -} - - -int Insert_CD(const char *cdimg_name, int type) -{ - int ret = 1; - - CD_Present = 0; - - if (cdimg_name != NULL && type != CIT_NOT_CD) - { - ret = Load_CD_Image(cdimg_name, type); - if (ret == 0) { - CD_Present = 1; - - if (Pico_mcd->scd.Status_CDD == TRAY_OPEN) - { - if (Pico_mcd->bios[0x122 ^ 1] == '2') - Close_Tray_CDD_cC(); - // else bios will issue it - } - else - { - Pico_mcd->scd.Status_CDD = READY; - } - } - } - - if (Pico_mcd->scd.Status_CDD != TRAY_OPEN && !CD_Present) - Pico_mcd->scd.Status_CDD = NOCD; - - return ret; -} - - -int Stop_CD(void) -{ - int ret = CD_Present; - - Unload_ISO(); - CD_Present = 0; - - return ret; -} - - -/* -PICO_INTERNAL void Change_CD(void) -{ - if (Pico_mcd->scd.Status_CDD == TRAY_OPEN) Close_Tray_CDD_cC(); - else Open_Tray_CDD_cD(); -} -*/ - -PICO_INTERNAL int Get_Status_CDD_c0(void) -{ - cdprintf("Status command : Cur LBA = %d", Pico_mcd->scd.Cur_LBA); - - // Clear immediat status - if ((Pico_mcd->cdd.Status & 0x0F00) == 0x0200) - Pico_mcd->cdd.Status = (Pico_mcd->scd.Status_CDD & 0xFF00) | (Pico_mcd->cdd.Status & 0x00FF); - else if ((Pico_mcd->cdd.Status & 0x0F00) == 0x0700) - Pico_mcd->cdd.Status = (Pico_mcd->scd.Status_CDD & 0xFF00) | (Pico_mcd->cdd.Status & 0x00FF); - else if ((Pico_mcd->cdd.Status & 0x0F00) == 0x0E00) - Pico_mcd->cdd.Status = (Pico_mcd->scd.Status_CDD & 0xFF00) | (Pico_mcd->cdd.Status & 0x00FF); - - Pico_mcd->scd.CDD_Complete = 1; - - return 0; -} - - -PICO_INTERNAL int Stop_CDD_c1(void) -{ - CHECK_TRAY_OPEN - - Pico_mcd->scd.Status_CDC &= ~1; // Stop CDC read - - if (CD_Present) Pico_mcd->scd.Status_CDD = STOPPED; - else Pico_mcd->scd.Status_CDD = NOCD; - Pico_mcd->cdd.Status = 0x0000; - - Pico_mcd->s68k_regs[0x36] |= 0x01; // Data bit set because stopped - - Pico_mcd->cdd.Minute = 0; - Pico_mcd->cdd.Seconde = 0; - Pico_mcd->cdd.Frame = 0; - Pico_mcd->cdd.Ext = 0; - - Pico_mcd->scd.CDD_Complete = 1; - - return 0; -} - - -PICO_INTERNAL int Get_Pos_CDD_c20(void) -{ - _msf MSF; - - cdprintf("command 200 : Cur LBA = %d", Pico_mcd->scd.Cur_LBA); - - CHECK_TRAY_OPEN - - Pico_mcd->cdd.Status &= 0xFF; - if (!CD_Present) - { - Pico_mcd->scd.Status_CDD = NOCD; - Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD; - } -// else if (!(CDC.CTRL.B.B0 & 0x80)) Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD; - Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD; - - cdprintf("Status CDD = %.4X Status = %.4X", Pico_mcd->scd.Status_CDD, Pico_mcd->cdd.Status); - - LBA_to_MSF(Pico_mcd->scd.Cur_LBA, &MSF); - - Pico_mcd->cdd.Minute = INT_TO_BCDW(MSF.M); - Pico_mcd->cdd.Seconde = INT_TO_BCDW(MSF.S); - Pico_mcd->cdd.Frame = INT_TO_BCDW(MSF.F); - Pico_mcd->cdd.Ext = 0; - - Pico_mcd->scd.CDD_Complete = 1; - - return 0; -} - - -PICO_INTERNAL int Get_Track_Pos_CDD_c21(void) -{ - int elapsed_time; - _msf MSF; - - cdprintf("command 201 : Cur LBA = %d", Pico_mcd->scd.Cur_LBA); - - CHECK_TRAY_OPEN - - Pico_mcd->cdd.Status &= 0xFF; - if (!CD_Present) - { - Pico_mcd->scd.Status_CDD = NOCD; - Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD; - } -// else if (!(CDC.CTRL.B.B0 & 0x80)) Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD; - Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD; - - elapsed_time = Pico_mcd->scd.Cur_LBA - Track_to_LBA(LBA_to_Track(Pico_mcd->scd.Cur_LBA)); - LBA_to_MSF(elapsed_time - 150, &MSF); - - cdprintf(" elapsed = %d", elapsed_time); - - Pico_mcd->cdd.Minute = INT_TO_BCDW(MSF.M); - Pico_mcd->cdd.Seconde = INT_TO_BCDW(MSF.S); - Pico_mcd->cdd.Frame = INT_TO_BCDW(MSF.F); - Pico_mcd->cdd.Ext = 0; - - Pico_mcd->scd.CDD_Complete = 1; - - return 0; -} - - -PICO_INTERNAL int Get_Current_Track_CDD_c22(void) -{ - cdprintf("Status CDD = %.4X Status = %.4X", Pico_mcd->scd.Status_CDD, Pico_mcd->cdd.Status); - - CHECK_TRAY_OPEN - - Pico_mcd->cdd.Status &= 0xFF; - if (!CD_Present) - { - Pico_mcd->scd.Status_CDD = NOCD; - Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD; - } -// else if (!(CDC.CTRL.B.B0 & 0x80)) Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD; - Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD; - - Pico_mcd->scd.Cur_Track = LBA_to_Track(Pico_mcd->scd.Cur_LBA); - - if (Pico_mcd->scd.Cur_Track == 100) Pico_mcd->cdd.Minute = 0x0A02; - else Pico_mcd->cdd.Minute = INT_TO_BCDW(Pico_mcd->scd.Cur_Track); - Pico_mcd->cdd.Seconde = 0; - Pico_mcd->cdd.Frame = 0; - Pico_mcd->cdd.Ext = 0; - - Pico_mcd->scd.CDD_Complete = 1; - - return 0; -} - - -PICO_INTERNAL int Get_Total_Lenght_CDD_c23(void) -{ - CHECK_TRAY_OPEN - - Pico_mcd->cdd.Status &= 0xFF; - if (!CD_Present) - { - Pico_mcd->scd.Status_CDD = NOCD; - Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD; - } -// else if (!(CDC.CTRL.B.B0 & 0x80)) Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD; - Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD; - - Pico_mcd->cdd.Minute = INT_TO_BCDW(Pico_mcd->TOC.Tracks[Pico_mcd->TOC.Last_Track].MSF.M); - Pico_mcd->cdd.Seconde = INT_TO_BCDW(Pico_mcd->TOC.Tracks[Pico_mcd->TOC.Last_Track].MSF.S); - Pico_mcd->cdd.Frame = INT_TO_BCDW(Pico_mcd->TOC.Tracks[Pico_mcd->TOC.Last_Track].MSF.F); - Pico_mcd->cdd.Ext = 0; - - Pico_mcd->scd.CDD_Complete = 1; - - return 0; -} - - -PICO_INTERNAL int Get_First_Last_Track_CDD_c24(void) -{ - CHECK_TRAY_OPEN - - Pico_mcd->cdd.Status &= 0xFF; - if (!CD_Present) - { - Pico_mcd->scd.Status_CDD = NOCD; - } -// else if (!(CDC.CTRL.B.B0 & 0x80)) Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD; - Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD; - - Pico_mcd->cdd.Minute = INT_TO_BCDW(1); - Pico_mcd->cdd.Seconde = INT_TO_BCDW(Pico_mcd->TOC.Last_Track); - Pico_mcd->cdd.Frame = 0; - Pico_mcd->cdd.Ext = 0; - - Pico_mcd->scd.CDD_Complete = 1; - - return 0; -} - - -PICO_INTERNAL int Get_Track_Adr_CDD_c25(void) -{ - int track_number; - - CHECK_TRAY_OPEN - - // track number in TC4 & TC5 - - track_number = (Pico_mcd->s68k_regs[0x38+10+4] & 0xF) * 10 + (Pico_mcd->s68k_regs[0x38+10+5] & 0xF); - - Pico_mcd->cdd.Status &= 0xFF; - if (!CD_Present) - { - Pico_mcd->scd.Status_CDD = NOCD; - Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD; - } -// else if (!(CDC.CTRL.B.B0 & 0x80)) Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD; - Pico_mcd->cdd.Status |= Pico_mcd->scd.Status_CDD; - - if (track_number > Pico_mcd->TOC.Last_Track) track_number = Pico_mcd->TOC.Last_Track; - else if (track_number < 1) track_number = 1; - - Pico_mcd->cdd.Minute = INT_TO_BCDW(Pico_mcd->TOC.Tracks[track_number - 1].MSF.M); - Pico_mcd->cdd.Seconde = INT_TO_BCDW(Pico_mcd->TOC.Tracks[track_number - 1].MSF.S); - Pico_mcd->cdd.Frame = INT_TO_BCDW(Pico_mcd->TOC.Tracks[track_number - 1].MSF.F); - Pico_mcd->cdd.Ext = track_number % 10; - - if (track_number == 1) Pico_mcd->cdd.Frame |= 0x0800; // data track - - Pico_mcd->scd.CDD_Complete = 1; - return 0; -} - - -PICO_INTERNAL int Play_CDD_c3(void) -{ - _msf MSF; - int delay, new_lba; - - CHECK_TRAY_OPEN - CHECK_CD_PRESENT - - // MSF of the track to play in TC buffer - - MSF.M = (Pico_mcd->s68k_regs[0x38+10+2] & 0xF) * 10 + (Pico_mcd->s68k_regs[0x38+10+3] & 0xF); - MSF.S = (Pico_mcd->s68k_regs[0x38+10+4] & 0xF) * 10 + (Pico_mcd->s68k_regs[0x38+10+5] & 0xF); - MSF.F = (Pico_mcd->s68k_regs[0x38+10+6] & 0xF) * 10 + (Pico_mcd->s68k_regs[0x38+10+7] & 0xF); - - Pico_mcd->scd.Cur_Track = MSF_to_Track(&MSF); - - new_lba = MSF_to_LBA(&MSF); - delay = new_lba - Pico_mcd->scd.Cur_LBA; - if (delay < 0) delay = -delay; - delay >>= 12; - - if (Pico_mcd->scd.Cur_LBA > 0 && delay < 13) - // based on genplus GX - delay = 13; - - Pico_mcd->scd.Cur_LBA = new_lba; - CDC_Update_Header(); - - cdprintf("Read : Cur LBA = %d, M=%d, S=%d, F=%d", Pico_mcd->scd.Cur_LBA, MSF.M, MSF.S, MSF.F); - - if (Pico_mcd->scd.Status_CDD != PLAYING) delay += 20; - - Pico_mcd->scd.Status_CDD = PLAYING; - Pico_mcd->cdd.Status = 0x0102; -// Pico_mcd->cdd.Status = COMM_OK; - - if (Pico_mcd->scd.File_Add_Delay == 0) Pico_mcd->scd.File_Add_Delay = delay; - - if (Pico_mcd->scd.Cur_Track == 1) - { - Pico_mcd->s68k_regs[0x36] |= 0x01; // DATA - } - else - { - Pico_mcd->s68k_regs[0x36] &= ~0x01; // AUDIO - cdda_start_play(); - } - - if (Pico_mcd->scd.Cur_Track == 100) Pico_mcd->cdd.Minute = 0x0A02; - else Pico_mcd->cdd.Minute = INT_TO_BCDW(Pico_mcd->scd.Cur_Track); - Pico_mcd->cdd.Seconde = 0; - Pico_mcd->cdd.Frame = 0; - Pico_mcd->cdd.Ext = 0; - - Pico_mcd->scd.Status_CDC |= 1; // Read data with CDC - - Pico_mcd->scd.CDD_Complete = 1; - return 0; -} - - -PICO_INTERNAL int Seek_CDD_c4(void) -{ - _msf MSF; - - CHECK_TRAY_OPEN - CHECK_CD_PRESENT - - // MSF to seek in TC buffer - - MSF.M = (Pico_mcd->s68k_regs[0x38+10+2] & 0xF) * 10 + (Pico_mcd->s68k_regs[0x38+10+3] & 0xF); - MSF.S = (Pico_mcd->s68k_regs[0x38+10+4] & 0xF) * 10 + (Pico_mcd->s68k_regs[0x38+10+5] & 0xF); - MSF.F = (Pico_mcd->s68k_regs[0x38+10+6] & 0xF) * 10 + (Pico_mcd->s68k_regs[0x38+10+7] & 0xF); - - Pico_mcd->scd.Cur_Track = MSF_to_Track(&MSF); - Pico_mcd->scd.Cur_LBA = MSF_to_LBA(&MSF); - CDC_Update_Header(); - - Pico_mcd->scd.Status_CDC &= ~1; // Stop CDC read - - Pico_mcd->scd.Status_CDD = READY; - Pico_mcd->cdd.Status = 0x0200; - - // DATA ? - if (Pico_mcd->scd.Cur_Track == 1) - Pico_mcd->s68k_regs[0x36] |= 0x01; - else Pico_mcd->s68k_regs[0x36] &= ~0x01; // AUDIO - - Pico_mcd->cdd.Minute = 0; - Pico_mcd->cdd.Seconde = 0; - Pico_mcd->cdd.Frame = 0; - Pico_mcd->cdd.Ext = 0; - - Pico_mcd->scd.CDD_Complete = 1; - - return 0; -} - - -PICO_INTERNAL int Pause_CDD_c6(void) -{ - CHECK_TRAY_OPEN - CHECK_CD_PRESENT - - Pico_mcd->scd.Status_CDC &= ~1; // Stop CDC read to start a new one if raw data - - Pico_mcd->scd.Status_CDD = READY; - Pico_mcd->cdd.Status = Pico_mcd->scd.Status_CDD; - - Pico_mcd->s68k_regs[0x36] |= 0x01; // Data bit set because stopped - - Pico_mcd->cdd.Minute = 0; - Pico_mcd->cdd.Seconde = 0; - Pico_mcd->cdd.Frame = 0; - Pico_mcd->cdd.Ext = 0; - - Pico_mcd->scd.CDD_Complete = 1; - - return 0; -} - - -PICO_INTERNAL int Resume_CDD_c7(void) -{ - CHECK_TRAY_OPEN - CHECK_CD_PRESENT - - Pico_mcd->scd.Cur_Track = LBA_to_Track(Pico_mcd->scd.Cur_LBA); - -#ifdef DEBUG_CD - { - _msf MSF; - LBA_to_MSF(Pico_mcd->scd.Cur_LBA, &MSF); - cdprintf("Resume read : Cur LBA = %d, M=%d, S=%d, F=%d", Pico_mcd->scd.Cur_LBA, MSF.M, MSF.S, MSF.F); - } -#endif - - Pico_mcd->scd.Status_CDD = PLAYING; - Pico_mcd->cdd.Status = 0x0102; - - if (Pico_mcd->scd.Cur_Track == 1) - { - Pico_mcd->s68k_regs[0x36] |= 0x01; // DATA - } - else - { - Pico_mcd->s68k_regs[0x36] &= ~0x01; // AUDIO - cdda_start_play(); - } - - if (Pico_mcd->scd.Cur_Track == 100) Pico_mcd->cdd.Minute = 0x0A02; - else Pico_mcd->cdd.Minute = INT_TO_BCDW(Pico_mcd->scd.Cur_Track); - Pico_mcd->cdd.Seconde = 0; - Pico_mcd->cdd.Frame = 0; - Pico_mcd->cdd.Ext = 0; - - Pico_mcd->scd.Status_CDC |= 1; // Read data with CDC - - Pico_mcd->scd.CDD_Complete = 1; - return 0; -} - - -PICO_INTERNAL int Fast_Foward_CDD_c8(void) -{ - CHECK_TRAY_OPEN - CHECK_CD_PRESENT - - Pico_mcd->scd.Status_CDC &= ~1; // Stop CDC read - - Pico_mcd->scd.Status_CDD = FAST_FOW; - Pico_mcd->cdd.Status = Pico_mcd->scd.Status_CDD | 2; - - Pico_mcd->cdd.Minute = INT_TO_BCDW(Pico_mcd->scd.Cur_Track); - Pico_mcd->cdd.Seconde = 0; - Pico_mcd->cdd.Frame = 0; - Pico_mcd->cdd.Ext = 0; - - Pico_mcd->scd.CDD_Complete = 1; - - return 0; -} - - -PICO_INTERNAL int Fast_Rewind_CDD_c9(void) -{ - CHECK_TRAY_OPEN - CHECK_CD_PRESENT - - Pico_mcd->scd.Status_CDC &= ~1; // Stop CDC read - - Pico_mcd->scd.Status_CDD = FAST_REV; - Pico_mcd->cdd.Status = Pico_mcd->scd.Status_CDD | 2; - - Pico_mcd->cdd.Minute = INT_TO_BCDW(Pico_mcd->scd.Cur_Track); - Pico_mcd->cdd.Seconde = 0; - Pico_mcd->cdd.Frame = 0; - Pico_mcd->cdd.Ext = 0; - - Pico_mcd->scd.CDD_Complete = 1; - - return 0; -} - - -PICO_INTERNAL int Close_Tray_CDD_cC(void) -{ - Pico_mcd->scd.Status_CDC &= ~1; // Stop CDC read - - elprintf(EL_STATUS, "tray close\n"); - - if (PicoMCDcloseTray != NULL) - PicoMCDcloseTray(); - - Pico_mcd->scd.Status_CDD = CD_Present ? STOPPED : NOCD; - Pico_mcd->cdd.Status = 0x0000; - - Pico_mcd->cdd.Minute = 0; - Pico_mcd->cdd.Seconde = 0; - Pico_mcd->cdd.Frame = 0; - Pico_mcd->cdd.Ext = 0; - - Pico_mcd->scd.CDD_Complete = 1; - - return 0; -} - - -PICO_INTERNAL int Open_Tray_CDD_cD(void) -{ - CHECK_TRAY_OPEN - - Pico_mcd->scd.Status_CDC &= ~1; // Stop CDC read - - elprintf(EL_STATUS, "tray open\n"); - - Unload_ISO(); - CD_Present = 0; - - if (PicoMCDopenTray != NULL) - PicoMCDopenTray(); - - Pico_mcd->scd.Status_CDD = TRAY_OPEN; - Pico_mcd->cdd.Status = 0x0E00; - - Pico_mcd->cdd.Minute = 0; - Pico_mcd->cdd.Seconde = 0; - Pico_mcd->cdd.Frame = 0; - Pico_mcd->cdd.Ext = 0; - - Pico_mcd->scd.CDD_Complete = 1; - - return 0; -} - - -PICO_INTERNAL int CDD_cA(void) -{ - CHECK_TRAY_OPEN - CHECK_CD_PRESENT - - Pico_mcd->scd.Status_CDC &= ~1; - - Pico_mcd->scd.Status_CDD = READY; - Pico_mcd->cdd.Status = Pico_mcd->scd.Status_CDD; - - Pico_mcd->cdd.Minute = 0; - Pico_mcd->cdd.Seconde = INT_TO_BCDW(1); - Pico_mcd->cdd.Frame = INT_TO_BCDW(1); - Pico_mcd->cdd.Ext = 0; - - Pico_mcd->scd.CDD_Complete = 1; - - return 0; -} - - -PICO_INTERNAL int CDD_Def(void) -{ - Pico_mcd->cdd.Status = Pico_mcd->scd.Status_CDD; - - Pico_mcd->cdd.Minute = 0; - Pico_mcd->cdd.Seconde = 0; - Pico_mcd->cdd.Frame = 0; - Pico_mcd->cdd.Ext = 0; - - return 0; -} - - -static int bswapwrite(int a, unsigned short d) -{ - *(unsigned short *)(Pico_mcd->s68k_regs + a) = (d>>8)|(d<<8); - return d + (d >> 8); -} - -PICO_INTERNAL void CDD_Export_Status(void) -{ - unsigned int csum; - - csum = bswapwrite( 0x38+0, Pico_mcd->cdd.Status); - csum += bswapwrite( 0x38+2, Pico_mcd->cdd.Minute); - csum += bswapwrite( 0x38+4, Pico_mcd->cdd.Seconde); - csum += bswapwrite( 0x38+6, Pico_mcd->cdd.Frame); - Pico_mcd->s68k_regs[0x38+8] = Pico_mcd->cdd.Ext; - csum += Pico_mcd->cdd.Ext; - Pico_mcd->s68k_regs[0x38+9] = ~csum & 0xf; - - Pico_mcd->s68k_regs[0x37] &= 3; // CDD.Control - - if (Pico_mcd->s68k_regs[0x33] & PCDS_IEN4) - { - elprintf(EL_INTS, "cdd export irq 4"); - SekInterruptS68k(4); - } - -// cdprintf("CDD exported status\n"); - cdprintf("out: Status=%.4X, Minute=%.4X, Second=%.4X, Frame=%.4X Checksum=%.4X", - (Pico_mcd->s68k_regs[0x38+0] << 8) | Pico_mcd->s68k_regs[0x38+1], - (Pico_mcd->s68k_regs[0x38+2] << 8) | Pico_mcd->s68k_regs[0x38+3], - (Pico_mcd->s68k_regs[0x38+4] << 8) | Pico_mcd->s68k_regs[0x38+5], - (Pico_mcd->s68k_regs[0x38+6] << 8) | Pico_mcd->s68k_regs[0x38+7], - (Pico_mcd->s68k_regs[0x38+8] << 8) | Pico_mcd->s68k_regs[0x38+9]); -} - - -PICO_INTERNAL void CDD_Import_Command(void) -{ -// cdprintf("CDD importing command\n"); - cdprintf("in: Command=%.4X, Minute=%.4X, Second=%.4X, Frame=%.4X Checksum=%.4X", - (Pico_mcd->s68k_regs[0x38+10+0] << 8) | Pico_mcd->s68k_regs[0x38+10+1], - (Pico_mcd->s68k_regs[0x38+10+2] << 8) | Pico_mcd->s68k_regs[0x38+10+3], - (Pico_mcd->s68k_regs[0x38+10+4] << 8) | Pico_mcd->s68k_regs[0x38+10+5], - (Pico_mcd->s68k_regs[0x38+10+6] << 8) | Pico_mcd->s68k_regs[0x38+10+7], - (Pico_mcd->s68k_regs[0x38+10+8] << 8) | Pico_mcd->s68k_regs[0x38+10+9]); - - switch (Pico_mcd->s68k_regs[0x38+10+0]) - { - case 0x0: // STATUS (?) - Get_Status_CDD_c0(); - break; - - case 0x1: // STOP ALL (?) - Stop_CDD_c1(); - break; - - case 0x2: // GET TOC INFORMATIONS - switch(Pico_mcd->s68k_regs[0x38+10+3]) - { - case 0x0: // get current position (MSF format) - Pico_mcd->cdd.Status = (Pico_mcd->cdd.Status & 0xFF00); - Get_Pos_CDD_c20(); - break; - - case 0x1: // get elapsed time of current track played/scanned (relative MSF format) - Pico_mcd->cdd.Status = (Pico_mcd->cdd.Status & 0xFF00) | 1; - Get_Track_Pos_CDD_c21(); - break; - - case 0x2: // get current track in RS2-RS3 - Pico_mcd->cdd.Status = (Pico_mcd->cdd.Status & 0xFF00) | 2; - Get_Current_Track_CDD_c22(); - break; - - case 0x3: // get total length (MSF format) - Pico_mcd->cdd.Status = (Pico_mcd->cdd.Status & 0xFF00) | 3; - Get_Total_Lenght_CDD_c23(); - break; - - case 0x4: // first & last track number - Pico_mcd->cdd.Status = (Pico_mcd->cdd.Status & 0xFF00) | 4; - Get_First_Last_Track_CDD_c24(); - break; - - case 0x5: // get track addresse (MSF format) - Pico_mcd->cdd.Status = (Pico_mcd->cdd.Status & 0xFF00) | 5; - Get_Track_Adr_CDD_c25(); - break; - - default : // invalid, then we return status - Pico_mcd->cdd.Status = (Pico_mcd->cdd.Status & 0xFF00) | 0xF; - Get_Status_CDD_c0(); - break; - } - break; - - case 0x3: // READ - Play_CDD_c3(); - break; - - case 0x4: // SEEK - Seek_CDD_c4(); - break; - - case 0x6: // PAUSE/STOP - Pause_CDD_c6(); - break; - - case 0x7: // RESUME - Resume_CDD_c7(); - break; - - case 0x8: // FAST FOWARD - Fast_Foward_CDD_c8(); - break; - - case 0x9: // FAST REWIND - Fast_Rewind_CDD_c9(); - break; - - case 0xA: // RECOVER INITIAL STATE (?) - CDD_cA(); - break; - - case 0xC: // CLOSE TRAY - Close_Tray_CDD_cC(); - break; - - case 0xD: // OPEN TRAY - Open_Tray_CDD_cD(); - break; - - default: // UNKNOWN - CDD_Def(); - break; - } -} - -void CDD_Reset(void) -{ - // Reseting CDD - - memset(Pico_mcd->s68k_regs+0x34, 0, 2*2); // CDD.Fader, CDD.Control - Pico_mcd->cdd.Status = 0; - Pico_mcd->cdd.Minute = 0; - Pico_mcd->cdd.Seconde = 0; - Pico_mcd->cdd.Frame = 0; - Pico_mcd->cdd.Ext = 0; - - // clear receive status and transfer command - memset(Pico_mcd->s68k_regs+0x38, 0, 20); - Pico_mcd->s68k_regs[0x38+9] = 0xF; // Default checksum -} - - diff --git a/pico/cd/cd_sys.h b/pico/cd/cd_sys.h deleted file mode 100644 index 6291c2cd..00000000 --- a/pico/cd/cd_sys.h +++ /dev/null @@ -1,109 +0,0 @@ -/*********************************************************** - * * - * This source was taken from the Gens project * - * Written by Stéphane Dallongeville * - * Copyright (c) 2002 by Stéphane Dallongeville * - * Modified/adapted for PicoDrive by notaz, 2007 * - * * - ***********************************************************/ - -#ifndef _CD_SYS_H -#define _CD_SYS_H - -#include "cd_file.h" - -#ifdef __cplusplus -extern "C" { -#endif - - -#define INT_TO_BCDB(c) \ -((c) > 99)?(0x99):((((c) / 10) << 4) + ((c) % 10)); - -#define INT_TO_BCDW(c) \ -((c) > 99)?(0x0909):((((c) / 10) << 8) + ((c) % 10)); - -#define BCDB_TO_INT(c) \ -(((c) >> 4) * 10) + ((c) & 0xF); - -#define BCDW_TO_INT(c) \ -(((c) >> 8) * 10) + ((c) & 0xF); - - -typedef struct -{ - unsigned char M; - unsigned char S; - unsigned char F; -} _msf; - -typedef struct -{ - _msf MSF; - // - char ftype; // cue_track_type - void *F; - int Length; - int Offset; // sector offset, when single file is used for multiple virtual tracks - short KBtps; // kbytes per sec for mp3s (bitrate / 1000 / 8) - short pad; -} _scd_track; - -typedef struct -{ -// unsigned char First_Track; // always 1 - _scd_track Tracks[100]; - unsigned int Last_Track; -} _scd_toc; - -typedef struct { - unsigned int Status_CDD; - unsigned int Status_CDC; - int Cur_LBA; - unsigned int Cur_Track; - int File_Add_Delay; - char CDD_Complete; - int pad[6]; -} _scd; - - -PICO_INTERNAL void LBA_to_MSF(int lba, _msf *MSF); -PICO_INTERNAL int Track_to_LBA(int track); - -// moved to pico.h -// int Insert_CD(char *iso_name, int is_bin); -// void Stop_CD(void); - -PICO_INTERNAL void Check_CD_Command(void); - -PICO_INTERNAL int Init_CD_Driver(void); -PICO_INTERNAL void End_CD_Driver(void); -PICO_INTERNAL void Reset_CD(void); - -PICO_INTERNAL int Get_Status_CDD_c0(void); -PICO_INTERNAL int Stop_CDD_c1(void); -PICO_INTERNAL int Get_Pos_CDD_c20(void); -PICO_INTERNAL int Get_Track_Pos_CDD_c21(void); -PICO_INTERNAL int Get_Current_Track_CDD_c22(void); -PICO_INTERNAL int Get_Total_Lenght_CDD_c23(void); -PICO_INTERNAL int Get_First_Last_Track_CDD_c24(void); -PICO_INTERNAL int Get_Track_Adr_CDD_c25(void); -PICO_INTERNAL int Play_CDD_c3(void); -PICO_INTERNAL int Seek_CDD_c4(void); -PICO_INTERNAL int Pause_CDD_c6(void); -PICO_INTERNAL int Resume_CDD_c7(void); -PICO_INTERNAL int Fast_Foward_CDD_c8(void); -PICO_INTERNAL int Fast_Rewind_CDD_c9(void); -PICO_INTERNAL int CDD_cA(void); -PICO_INTERNAL int Close_Tray_CDD_cC(void); -PICO_INTERNAL int Open_Tray_CDD_cD(void); - -PICO_INTERNAL int CDD_Def(void); - - -#ifdef __cplusplus -}; -#endif - -#endif - diff --git a/pico/cd/cdc.c b/pico/cd/cdc.c index aa1ded97..8b47b3dd 100644 --- a/pico/cd/cdc.c +++ b/pico/cd/cdc.c @@ -341,19 +341,6 @@ update_dma: Pico_mcd->s68k_regs[0x0b] = dma_addr; } -// tmp -static void cdd_read_data(uint8 *dst) -{ - int lba = Pico_mcd->scd.Cur_LBA; - - /* only read DATA track sectors */ - if (0 <= lba && lba < Pico_mcd->TOC.Tracks[0].Length) - { - /* read sector data (Mode 1 = 2048 bytes) */ - PicoCDBufferRead(dst, lba); - } -} - void cdc_dma_update(void) { /* end of DMA transfer ? */ diff --git a/pico/cd/cdd.c b/pico/cd/cdd.c new file mode 100644 index 00000000..58a60536 --- /dev/null +++ b/pico/cd/cdd.c @@ -0,0 +1,1328 @@ +/*************************************************************************************** + * Genesis Plus + * CD drive processor & CD-DA fader + * + * Copyright (C) 2012-2013 Eke-Eke (Genesis Plus GX) + * + * Redistribution and use of this code or any derivative works are permitted + * provided that the following conditions are met: + * + * - Redistributions may not be sold, nor may they be used in a commercial + * product or activity. + * + * - Redistributions that are modified from the original source must include the + * complete source code, including the source code for all components used by a + * binary built from the modified sources. However, as a special exception, the + * source code distributed need not include anything that is normally distributed + * (in either source or binary form) with the major components (compiler, kernel, + * and so on) of the operating system on which the executable runs, unless that + * component itself accompanies the executable. + * + * - Redistributions must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************************/ + +#include "../pico_int.h" +#include "genplus_macros.h" +#include "cue.h" +#include "cdd.h" + +#ifdef USE_LIBTREMOR +#define SUPPORTED_EXT 20 +#else +#define SUPPORTED_EXT 10 +#endif + +cdd_t cdd; + +/* BCD conversion lookup tables */ +static const uint8 lut_BCD_8[100] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, +}; + +static const uint16 lut_BCD_16[100] = +{ + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, + 0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, 0x0108, 0x0109, + 0x0200, 0x0201, 0x0202, 0x0203, 0x0204, 0x0205, 0x0206, 0x0207, 0x0208, 0x0209, + 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, 0x0308, 0x0309, + 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409, + 0x0500, 0x0501, 0x0502, 0x0503, 0x0504, 0x0505, 0x0506, 0x0507, 0x0508, 0x0509, + 0x0600, 0x0601, 0x0602, 0x0603, 0x0604, 0x0605, 0x0606, 0x0607, 0x0608, 0x0609, + 0x0700, 0x0701, 0x0702, 0x0703, 0x0704, 0x0705, 0x0706, 0x0707, 0x0708, 0x0709, + 0x0800, 0x0801, 0x0802, 0x0803, 0x0804, 0x0805, 0x0806, 0x0807, 0x0808, 0x0809, + 0x0900, 0x0901, 0x0902, 0x0903, 0x0904, 0x0905, 0x0906, 0x0907, 0x0908, 0x0909, +}; + +/* pre-build TOC */ +static const uint16 toc_snatcher[21] = +{ + 56014, 495, 10120, 20555, 1580, 5417, 12502, 16090, 6553, 9681, + 8148, 20228, 8622, 6142, 5858, 1287, 7424, 3535, 31697, 2485, + 31380 +}; + +static const uint16 toc_lunar[52] = +{ + 5422, 1057, 7932, 5401, 6380, 6592, 5862, 5937, 5478, 5870, + 6673, 6613, 6429, 4996, 4977, 5657, 3720, 5892, 3140, 3263, + 6351, 5187, 3249, 1464, 1596, 1750, 1751, 6599, 4578, 5205, + 1550, 1827, 2328, 1346, 1569, 1613, 7199, 4928, 1656, 2549, + 1875, 3901, 1850, 2399, 2028, 1724, 4889, 14551, 1184, 2132, + 685, 3167 +}; + +static const uint32 toc_shadow[15] = +{ + 10226, 70054, 11100, 12532, 12444, 11923, 10059, 10167, 10138, 13792, + 11637, 2547, 2521, 3856, 900 +}; + +static const uint32 toc_dungeon[13] = +{ + 2250, 22950, 16350, 24900, 13875, 19950, 13800, 15375, 17400, 17100, + 3325, 6825, 25275 +}; + +static const uint32 toc_ffight[26] = +{ + 11994, 9742, 10136, 9685, 9553, 14588, 9430, 8721, 9975, 9764, + 9704, 12796, 585, 754, 951, 624, 9047, 1068, 817, 9191, 1024, + 14562, 10320, 8627, 3795, 3047 +}; + +static const uint32 toc_ffightj[29] = +{ + 11994, 9752, 10119, 9690, 9567, 14575, 9431, 8731, 9965, 9763, + 9716, 12791, 579, 751, 958, 630, 9050, 1052, 825, 9193, 1026, + 14553, 9834, 10542, 1699, 1792, 1781, 3783, 3052 +}; + +/* supported WAVE file header (16-bit stereo samples @44.1kHz) */ +static const unsigned char waveHeader[32] = +{ + 0x57,0x41,0x56,0x45,0x66,0x6d,0x74,0x20,0x10,0x00,0x00,0x00,0x01,0x00,0x02,0x00, + 0x44,0xac,0x00,0x00,0x10,0xb1,0x02,0x00,0x04,0x00,0x10,0x00,0x64,0x61,0x74,0x61 +}; + +#ifdef USE_LIBTREMOR +#ifdef DISABLE_MANY_OGG_OPEN_FILES +static void ogg_free(int i) +{ + /* clear OGG file descriptor to prevent file from being closed */ + cdd.toc.tracks[i].vf.datasource = NULL; + + /* close VORBIS file structure */ + ov_clear(&cdd.toc.tracks[i].vf); + + /* indicates that the track is a seekable VORBIS file */ + cdd.toc.tracks[i].vf.seekable = 1; + + /* reset file reading position */ + fseek(cdd.toc.tracks[i].fd, 0, SEEK_SET); +} +#endif +#endif + +void cdd_reset(void) +{ + /* reset cycle counter */ + cdd.cycles = 0; + + /* reset drive access latency */ + cdd.latency = 0; + + /* reset track index */ + cdd.index = 0; + + /* reset logical block address */ + cdd.lba = 0; + + /* reset status */ + cdd.status = cdd.loaded ? CD_STOP : NO_DISC; + + /* reset CD-DA fader (full volume) */ + cdd.volume = 0x400; + + /* clear CD-DA output */ + cdd.audio[0] = cdd.audio[1] = 0; +} + +/* FIXME: use cdd_read_audio() instead */ +static void cdd_change_track(int index, int lba) +{ + int i, base, lba_offset, lb_len; + + for (i = index; i > 0; i--) + if (cdd.toc.tracks[i].fd != NULL) + break; + + Pico_mcd->cdda_stream = cdd.toc.tracks[i].fd; + base = cdd.toc.tracks[index].offset; + lba_offset = lba - cdd.toc.tracks[index].start; + lb_len = cdd.toc.tracks[index].end - cdd.toc.tracks[index].start; + + elprintf(EL_CD, "play #%d lba %d base %d", index, lba, base); + + cdda_start_play(base, lba_offset, lb_len); +} + +int cdd_context_save(uint8 *state) +{ + int bufferptr = 0; + + save_param(&cdd.cycles, sizeof(cdd.cycles)); + save_param(&cdd.latency, sizeof(cdd.latency)); + save_param(&cdd.index, sizeof(cdd.index)); + save_param(&cdd.lba, sizeof(cdd.lba)); + save_param(&cdd.scanOffset, sizeof(cdd.scanOffset)); + save_param(&cdd.volume, sizeof(cdd.volume)); + save_param(&cdd.status, sizeof(cdd.status)); + + return bufferptr; +} + +int cdd_context_load(uint8 *state) +{ + int lba; + int bufferptr = 0; + +#ifdef USE_LIBTREMOR +#ifdef DISABLE_MANY_OGG_OPEN_FILES + /* close previous track VORBIS file structure to save memory */ + if (cdd.toc.tracks[cdd.index].vf.datasource) + { + ogg_free(cdd.index); + } +#endif +#endif + + load_param(&cdd.cycles, sizeof(cdd.cycles)); + load_param(&cdd.latency, sizeof(cdd.latency)); + load_param(&cdd.index, sizeof(cdd.index)); + load_param(&cdd.lba, sizeof(cdd.lba)); + load_param(&cdd.scanOffset, sizeof(cdd.scanOffset)); + load_param(&cdd.volume, sizeof(cdd.volume)); + load_param(&cdd.status, sizeof(cdd.status)); + + /* adjust current LBA within track limit */ + lba = cdd.lba; + if (lba < cdd.toc.tracks[cdd.index].start) + { + lba = cdd.toc.tracks[cdd.index].start; + } + + /* seek to current track position */ + if (!cdd.index) + { + /* DATA track */ + if (cdd.toc.tracks[0].fd) + { + pm_seek(cdd.toc.tracks[0].fd, lba * cdd.sectorSize, SEEK_SET); + } + } +#ifdef USE_LIBTREMOR + else if (cdd.toc.tracks[cdd.index].vf.seekable) + { +#ifdef DISABLE_MANY_OGG_OPEN_FILES + /* VORBIS file need to be opened first */ + ov_open(cdd.toc.tracks[cdd.index].fd,&cdd.toc.tracks[cdd.index].vf,0,0); +#endif + /* VORBIS AUDIO track */ + ov_pcm_seek(&cdd.toc.tracks[cdd.index].vf, (lba - cdd.toc.tracks[cdd.index].start) * 588 - cdd.toc.tracks[cdd.index].offset); + } +#endif +#if 0 + else if (cdd.toc.tracks[cdd.index].fd) + { + /* PCM AUDIO track */ + fseek(cdd.toc.tracks[cdd.index].fd, (lba * 2352) - cdd.toc.tracks[cdd.index].offset, SEEK_SET); + } +#else + else + { + cdd_change_track(cdd.index, lba); + } +#endif + + return bufferptr; +} + +int cdd_context_load_old(uint8 *state) +{ + memcpy(&cdd.lba, state + 8, sizeof(cdd.lba)); + return 12 * 4; +} + +int cdd_load(const char *filename, int type) +{ + char header[0x210]; + int ret; + + /* first unmount any loaded disc */ + cdd_unload(); + + /* genplus parses cue here, in PD we use our own parser */ + ret = load_cd_image(filename, &type); + if (ret != 0) + return ret; + + /* read first 16 bytes */ + pm_read(header, 0x10, cdd.toc.tracks[0].fd); + + /* look for valid CD image ID string */ + if (memcmp("SEGADISCSYSTEM", header, 14)) + { + /* if not found, read next 16 bytes */ + pm_read(header, 0x10, cdd.toc.tracks[0].fd); + + /* look again for valid CD image ID string */ + if (memcmp("SEGADISCSYSTEM", header, 14)) + { + elprintf(EL_STATUS|EL_ANOMALY, "cd: bad cd image?"); + /* assume bin without security code */ + } + + /* BIN format (2352 bytes data blocks) */ + cdd.sectorSize = 2352; + } + else + { + /* ISO format (2048 bytes data blocks) */ + cdd.sectorSize = 2048; + } + + ret = (type == CT_BIN) ? 2352 : 2048; + if (ret != cdd.sectorSize) + elprintf(EL_STATUS|EL_ANOMALY, "cd: type detection mismatch"); + + /* read CD image header + security code */ + pm_read(header + 0x10, 0x200, cdd.toc.tracks[0].fd); + + /* Simulate audio tracks if none found */ + if (cdd.toc.last == 1) + { + /* Some games require exact TOC infos */ + if (strstr(header + 0x180,"T-95035") != NULL) + { + /* Snatcher */ + cdd.toc.last = cdd.toc.end = 0; + do + { + cdd.toc.tracks[cdd.toc.last].start = cdd.toc.end; + cdd.toc.tracks[cdd.toc.last].end = cdd.toc.tracks[cdd.toc.last].start + toc_snatcher[cdd.toc.last]; + cdd.toc.end = cdd.toc.tracks[cdd.toc.last].end; + cdd.toc.last++; + } + while (cdd.toc.last < 21); + } + else if (strstr(header + 0x180,"T-127015") != NULL) + { + /* Lunar - The Silver Star */ + cdd.toc.last = cdd.toc.end = 0; + do + { + cdd.toc.tracks[cdd.toc.last].start = cdd.toc.end; + cdd.toc.tracks[cdd.toc.last].end = cdd.toc.tracks[cdd.toc.last].start + toc_lunar[cdd.toc.last]; + cdd.toc.end = cdd.toc.tracks[cdd.toc.last].end; + cdd.toc.last++; + } + while (cdd.toc.last < 52); + } + else if (strstr(header + 0x180,"T-113045") != NULL) + { + /* Shadow of the Beast II */ + cdd.toc.last = cdd.toc.end = 0; + do + { + cdd.toc.tracks[cdd.toc.last].start = cdd.toc.end; + cdd.toc.tracks[cdd.toc.last].end = cdd.toc.tracks[cdd.toc.last].start + toc_shadow[cdd.toc.last]; + cdd.toc.end = cdd.toc.tracks[cdd.toc.last].end; + cdd.toc.last++; + } + while (cdd.toc.last < 15); + } + else if (strstr(header + 0x180,"T-143025") != NULL) + { + /* Dungeon Explorer */ + cdd.toc.last = cdd.toc.end = 0; + do + { + cdd.toc.tracks[cdd.toc.last].start = cdd.toc.end; + cdd.toc.tracks[cdd.toc.last].end = cdd.toc.tracks[cdd.toc.last].start + toc_dungeon[cdd.toc.last]; + cdd.toc.end = cdd.toc.tracks[cdd.toc.last].end; + cdd.toc.last++; + } + while (cdd.toc.last < 13); + } + else if (strstr(header + 0x180,"MK-4410") != NULL) + { + /* Final Fight CD (USA, Europe) */ + cdd.toc.last = cdd.toc.end = 0; + do + { + cdd.toc.tracks[cdd.toc.last].start = cdd.toc.end; + cdd.toc.tracks[cdd.toc.last].end = cdd.toc.tracks[cdd.toc.last].start + toc_ffight[cdd.toc.last]; + cdd.toc.end = cdd.toc.tracks[cdd.toc.last].end; + cdd.toc.last++; + } + while (cdd.toc.last < 26); + } + else if (strstr(header + 0x180,"G-6013") != NULL) + { + /* Final Fight CD (Japan) */ + cdd.toc.last = cdd.toc.end = 0; + do + { + cdd.toc.tracks[cdd.toc.last].start = cdd.toc.end; + cdd.toc.tracks[cdd.toc.last].end = cdd.toc.tracks[cdd.toc.last].start + toc_ffightj[cdd.toc.last]; + cdd.toc.end = cdd.toc.tracks[cdd.toc.last].end; + cdd.toc.last++; + } + while (cdd.toc.last < 29); + } +#if 0 + else + { + /* default TOC (99 tracks & 2s per audio tracks) */ + do + { + cdd.toc.tracks[cdd.toc.last].start = cdd.toc.end + 2*75; + cdd.toc.tracks[cdd.toc.last].end = cdd.toc.tracks[cdd.toc.last].start + 2*75; + cdd.toc.end = cdd.toc.tracks[cdd.toc.last].end; + cdd.toc.last++; + } + while ((cdd.toc.last < 99) && (cdd.toc.end < 56*60*75)); + } +#endif + } + + /* Lead-out */ + cdd.toc.tracks[cdd.toc.last].start = cdd.toc.end; + + /* CD loaded */ + cdd.loaded = 1; + return 0; +} + +int cdd_unload(void) +{ + int was_loaded = cdd.loaded; + + if (cdd.loaded) + { + int i; + + /* close CD tracks */ + if (cdd.toc.tracks[0].fd) + { + pm_close(cdd.toc.tracks[0].fd); + cdd.toc.tracks[0].fd = NULL; + } + + for (i = 1; i < cdd.toc.last; i++) + { +#ifdef USE_LIBTREMOR + if (cdd.toc.tracks[i].vf.datasource) + { + /* close VORBIS file (if still opened) */ + ov_clear(&cdd.toc.tracks[i].vf); + } + else +#endif + if (cdd.toc.tracks[i].fd) + { + /* close file */ + if (Pico_mcd->cdda_type == CT_MP3) + fclose(cdd.toc.tracks[i].fd); + else + pm_close(cdd.toc.tracks[0].fd); + + /* detect single file images */ + if (cdd.toc.tracks[i+1].fd == cdd.toc.tracks[i].fd) + { + /* exit loop */ + i = cdd.toc.last; + } + } + } + + /* CD unloaded */ + cdd.loaded = 0; + } + + /* reset TOC */ + memset(&cdd.toc, 0x00, sizeof(cdd.toc)); + + /* unknown CD image file format */ + cdd.sectorSize = 0; + + return was_loaded; +} + +void cdd_read_data(uint8 *dst) +{ + /* only read DATA track sectors */ + if ((cdd.lba >= 0) && (cdd.lba < cdd.toc.tracks[0].end)) + { + /* BIN format ? */ + if (cdd.sectorSize == 2352) + { + /* skip 16-byte header */ + pm_seek(cdd.toc.tracks[0].fd, cdd.lba * 2352 + 16, SEEK_SET); + } + + /* read sector data (Mode 1 = 2048 bytes) */ + pm_read(dst, 2048, cdd.toc.tracks[0].fd); + } +} + +#if 0 +void cdd_read_audio(unsigned int samples) +{ + /* previous audio outputs */ + int16 l = cdd.audio[0]; + int16 r = cdd.audio[1]; + + /* get number of internal clocks (samples) needed */ + samples = blip_clocks_needed(blip[0], samples); + + /* audio track playing ? */ + if (!Pico_mcd->regs[0x36>>1].byte.h && cdd.toc.tracks[cdd.index].fd) + { + int i, mul, delta; + + /* current CD-DA fader volume */ + int curVol = cdd.volume; + + /* CD-DA fader volume setup (0-1024) */ + int endVol = Pico_mcd->regs[0x34>>1].w >> 4; + + /* read samples from current block */ +#ifdef USE_LIBTREMOR + if (cdd.toc.tracks[cdd.index].vf.datasource) + { + int len, done = 0; + int16 *ptr = (int16 *) (cdc.ram); + samples = samples * 4; + while (done < samples) + { + len = ov_read(&cdd.toc.tracks[cdd.index].vf, (char *)(cdc.ram + done), samples - done, 0); + if (len <= 0) + { + done = samples; + break; + } + done += len; + } + samples = done / 4; + + /* process 16-bit (host-endian) stereo samples */ + for (i=0; i endVol) + { + /* fade-out */ + curVol--; + } + else if (!curVol) + { + /* audio will remain muted until next setup */ + break; + } + } + } + else +#endif + { +#ifdef LSB_FIRST + int16 *ptr = (int16 *) (cdc.ram); +#else + uint8 *ptr = cdc.ram; +#endif + fread(cdc.ram, 1, samples * 4, cdd.toc.tracks[cdd.index].fd); + + /* process 16-bit (little-endian) stereo samples */ + for (i=0; i endVol) + { + /* fade-out */ + curVol--; + } + else if (!curVol) + { + /* audio will remain muted until next setup */ + break; + } + } + } + + /* save current CD-DA fader volume */ + cdd.volume = curVol; + + /* save last audio output for next frame */ + cdd.audio[0] = l; + cdd.audio[1] = r; + } + else + { + /* no audio output */ + if (l) blip_add_delta_fast(blip[0], 0, -l); + if (r) blip_add_delta_fast(blip[1], 0, -r); + + /* save audio output for next frame */ + cdd.audio[0] = 0; + cdd.audio[1] = 0; + } + + /* end of Blip Buffer timeframe */ + blip_end_frame(blip[0], samples); + blip_end_frame(blip[1], samples); +} +#endif + + +void cdd_update(void) +{ +#ifdef LOG_CDD + error("LBA = %d (track n°%d)(latency=%d)\n", cdd.lba, cdd.index, cdd.latency); +#endif + + /* seeking disc */ + if (cdd.status == CD_SEEK) + { + /* drive latency */ + if (cdd.latency > 0) + { + cdd.latency--; + return; + } + + /* drive is ready */ + cdd.status = CD_READY; + } + + /* reading disc */ + else if (cdd.status == CD_PLAY) + { + /* drive latency */ + if (cdd.latency > 0) + { + cdd.latency--; + return; + } + + /* track type */ + if (!cdd.index) + { + /* DATA sector header (CD-ROM Mode 1) */ + uint8 header[4]; + uint32 msf = cdd.lba + 150; + header[0] = lut_BCD_8[(msf / 75) / 60]; + header[1] = lut_BCD_8[(msf / 75) % 60]; + header[2] = lut_BCD_8[(msf % 75)]; + header[3] = 0x01; + + /* data track sector read is controlled by CDC */ + cdd.lba += cdc_decoder_update(header); + } + else if (cdd.index < cdd.toc.last) + { + uint8 header[4] = { 0, }; + + /* check against audio track start index */ + if (cdd.lba >= cdd.toc.tracks[cdd.index].start) + { + /* audio track playing */ + Pico_mcd->regs[0x36>>1].byte.h = 0x00; + } + + /* audio blocks are still sent to CDC as well as CD DAC/Fader */ + cdc_decoder_update(header); + + /* next audio block is automatically read */ + cdd.lba++; + } + else + { + /* end of disc */ + cdd.status = CD_END; + return; + } + + /* check end of current track */ + if (cdd.lba >= cdd.toc.tracks[cdd.index].end) + { +#ifdef USE_LIBTREMOR +#ifdef DISABLE_MANY_OGG_OPEN_FILES + /* close previous track VORBIS file structure to save memory */ + if (cdd.toc.tracks[cdd.index].vf.datasource) + { + ogg_free(cdd.index); + } +#endif +#endif + /* play next track */ + cdd.index++; + + /* PAUSE between tracks */ + Pico_mcd->regs[0x36>>1].byte.h = 0x01; + + /* seek to next audio track start */ +#ifdef USE_LIBTREMOR + if (cdd.toc.tracks[cdd.index].vf.seekable) + { +#ifdef DISABLE_MANY_OGG_OPEN_FILES + /* VORBIS file need to be opened first */ + ov_open(cdd.toc.tracks[cdd.index].fd,&cdd.toc.tracks[cdd.index].vf,0,0); +#endif + ov_pcm_seek(&cdd.toc.tracks[cdd.index].vf, -cdd.toc.tracks[cdd.index].offset); + } + else +#endif +#if 0 + if (cdd.toc.tracks[cdd.index].fd) + { + fseek(cdd.toc.tracks[cdd.index].fd, (cdd.toc.tracks[cdd.index].start * 2352) - cdd.toc.tracks[cdd.index].offset, SEEK_SET); + } +#else + { + cdd_change_track(cdd.index, cdd.lba); + } +#endif + } + } + + /* scanning disc */ + else if (cdd.status == CD_SCAN) + { + /* fast-forward or fast-rewind */ + cdd.lba += cdd.scanOffset; + + /* check current track limits */ + if (cdd.lba >= cdd.toc.tracks[cdd.index].end) + { +#ifdef USE_LIBTREMOR +#ifdef DISABLE_MANY_OGG_OPEN_FILES + /* close previous track VORBIS file structure to save memory */ + if (cdd.toc.tracks[cdd.index].vf.datasource) + { + ogg_free(cdd.index); + } +#endif +#endif + /* next track */ + cdd.index++; + + /* skip directly to track start position */ + cdd.lba = cdd.toc.tracks[cdd.index].start; + + /* AUDIO track playing ? */ + if (cdd.status == CD_PLAY) + { + Pico_mcd->regs[0x36>>1].byte.h = 0x00; + } + } + else if (cdd.lba < cdd.toc.tracks[cdd.index].start) + { +#ifdef USE_LIBTREMOR +#ifdef DISABLE_MANY_OGG_OPEN_FILES + /* close previous track VORBIS file structure to save memory */ + if (cdd.toc.tracks[cdd.index].vf.datasource) + { + ogg_free(cdd.index); + } +#endif +#endif + + /* previous track */ + cdd.index--; + + /* skip directly to track end position */ + cdd.lba = cdd.toc.tracks[cdd.index].end; + } + + /* check disc limits */ + if (cdd.index < 0) + { + cdd.index = 0; + cdd.lba = 0; + } + else if (cdd.index >= cdd.toc.last) + { + /* no AUDIO track playing */ + Pico_mcd->regs[0x36>>1].byte.h = 0x01; + + /* end of disc */ + cdd.index = cdd.toc.last; + cdd.lba = cdd.toc.end; + cdd.status = CD_END; + return; + } + + /* seek to current block */ + if (!cdd.index) + { + /* no AUDIO track playing */ + Pico_mcd->regs[0x36>>1].byte.h = 0x01; + + /* DATA track */ + pm_seek(cdd.toc.tracks[0].fd, cdd.lba * cdd.sectorSize, SEEK_SET); + } +#ifdef USE_LIBTREMOR + else if (cdd.toc.tracks[cdd.index].vf.seekable) + { +#ifdef DISABLE_MANY_OGG_OPEN_FILES + /* check if a new track is being played */ + if (!cdd.toc.tracks[cdd.index].vf.datasource) + { + /* VORBIS file need to be opened first */ + ov_open(cdd.toc.tracks[cdd.index].fd,&cdd.toc.tracks[cdd.index].vf,0,0); + } +#endif + /* VORBIS AUDIO track */ + ov_pcm_seek(&cdd.toc.tracks[cdd.index].vf, (cdd.lba - cdd.toc.tracks[cdd.index].start) * 588 - cdd.toc.tracks[cdd.index].offset); + } +#endif +#if 0 + else if (cdd.toc.tracks[cdd.index].fd) + { + /* PCM AUDIO track */ + fseek(cdd.toc.tracks[cdd.index].fd, (cdd.lba * 2352) - cdd.toc.tracks[cdd.index].offset, SEEK_SET); + } +#else + else + { + cdd_change_track(cdd.index, cdd.lba); + } +#endif + } +} + +#define set_reg16(r, v) { \ + uint16 _v = v; \ + Pico_mcd->s68k_regs[(r)] = _v >> 8; \ + Pico_mcd->s68k_regs[(r)+1] = _v; \ +} + +void cdd_process(void) +{ + /* Process CDD command */ + switch (Pico_mcd->regs[0x42>>1].byte.h & 0x0f) + { + case 0x00: /* Drive Status */ + { + /* RS1-RS8 normally unchanged */ + Pico_mcd->regs[0x38>>1].byte.h = cdd.status; + + /* unless RS1 indicated invalid track infos */ + if (Pico_mcd->regs[0x38>>1].byte.l == 0x0f) + { + /* and SEEK has ended */ + if (cdd.status != CD_SEEK) + { + /* then return valid track infos, e.g current track number in RS2-RS3 (fixes Lunar - The Silver Star) */ + Pico_mcd->regs[0x38>>1].byte.l = 0x02; + set_reg16(0x3a, (cdd.index < cdd.toc.last) ? lut_BCD_16[cdd.index + 1] : 0x0A0A); + } + } + break; + } + + case 0x01: /* Stop Drive */ + { + /* update status */ + cdd.status = cdd.loaded ? CD_STOP : NO_DISC; + + /* no audio track playing */ + Pico_mcd->regs[0x36>>1].byte.h = 0x01; + + /* RS1-RS8 ignored, expects 0x0 ("no disc" ?) in RS0 once */ + set_reg16(0x38, 0x0000); + set_reg16(0x3a, 0x0000); + set_reg16(0x3c, 0x0000); + set_reg16(0x3e, 0x0000); + set_reg16(0x40, 0x000f); + return; + } + + case 0x02: /* Read TOC */ + { + /* Infos automatically retrieved by CDD processor from Q-Channel */ + /* commands 0x00-0x02 (current block) and 0x03-0x05 (Lead-In) */ + switch (Pico_mcd->regs[0x44>>1].byte.l) + { + case 0x00: /* Current Absolute Time (MM:SS:FF) */ + { + int lba = cdd.lba + 150; + set_reg16(0x38, cdd.status << 8); + set_reg16(0x3a, lut_BCD_16[(lba/75)/60]); + set_reg16(0x3c, lut_BCD_16[(lba/75)%60]); + set_reg16(0x3e, lut_BCD_16[(lba%75)]); + Pico_mcd->regs[0x40>>1].byte.h = cdd.index ? 0x00 : 0x04; /* Current block flags in RS8 (bit0 = mute status, bit1: pre-emphasis status, bit2: track type) */ + break; + } + + case 0x01: /* Current Track Relative Time (MM:SS:FF) */ + { + int lba = cdd.lba - cdd.toc.tracks[cdd.index].start; + set_reg16(0x38, (cdd.status << 8) | 0x01); + set_reg16(0x3a, lut_BCD_16[(lba/75)/60]); + set_reg16(0x3c, lut_BCD_16[(lba/75)%60]); + set_reg16(0x3e, lut_BCD_16[(lba%75)]); + Pico_mcd->regs[0x40>>1].byte.h = cdd.index ? 0x00 : 0x04; /* Current block flags in RS8 (bit0 = mute status, bit1: pre-emphasis status, bit2: track type) */ + break; + } + + case 0x02: /* Current Track Number */ + { + set_reg16(0x38, (cdd.status << 8) | 0x02); + set_reg16(0x3a, (cdd.index < cdd.toc.last) ? lut_BCD_16[cdd.index + 1] : 0x0A0A); + set_reg16(0x3c, 0x0000); + set_reg16(0x3e, 0x0000); /* Disk Control Code (?) in RS6 */ + Pico_mcd->regs[0x40>>1].byte.h = 0x00; + break; + } + + case 0x03: /* Total length (MM:SS:FF) */ + { + int lba = cdd.toc.end + 150; + set_reg16(0x38, (cdd.status << 8) | 0x03); + set_reg16(0x3a, lut_BCD_16[(lba/75)/60]); + set_reg16(0x3c, lut_BCD_16[(lba/75)%60]); + set_reg16(0x3e, lut_BCD_16[(lba%75)]); + Pico_mcd->regs[0x40>>1].byte.h = 0x00; + break; + } + + case 0x04: /* First & Last Track Numbers */ + { + set_reg16(0x38, (cdd.status << 8) | 0x04); + set_reg16(0x3a, 0x0001); + set_reg16(0x3c, lut_BCD_16[cdd.toc.last]); + set_reg16(0x3e, 0x0000); /* Drive Version (?) in RS6-RS7 */ + Pico_mcd->regs[0x40>>1].byte.h = 0x00; /* Lead-In flags in RS8 (bit0 = mute status, bit1: pre-emphasis status, bit2: track type) */ + break; + } + + case 0x05: /* Track Start Time (MM:SS:FF) */ + { + int track = Pico_mcd->regs[0x46>>1].byte.h * 10 + Pico_mcd->regs[0x46>>1].byte.l; + int lba = cdd.toc.tracks[track-1].start + 150; + set_reg16(0x38, (cdd.status << 8) | 0x05); + set_reg16(0x3a, lut_BCD_16[(lba/75)/60]); + set_reg16(0x3c, lut_BCD_16[(lba/75)%60]); + set_reg16(0x3e, lut_BCD_16[(lba%75)]); + Pico_mcd->regs[0x40>>1].byte.h = track % 10; /* Track Number (low digit) */ + if (track == 1) + { + /* RS6 bit 3 is set for the first (DATA) track */ + Pico_mcd->regs[0x3e>>1].byte.h |= 0x08; + } + break; + } + + default: + { +#ifdef LOG_ERROR + error("Unknown CDD Command %02X (%X)\n", Pico_mcd->regs[0x44>>1].byte.l, s68k.pc); +#endif + return; + } + } + break; + } + + case 0x03: /* Play */ + { + /* reset track index */ + int index = 0; + + /* new LBA position */ + int lba = ((Pico_mcd->regs[0x44>>1].byte.h * 10 + Pico_mcd->regs[0x44>>1].byte.l) * 60 + + (Pico_mcd->regs[0x46>>1].byte.h * 10 + Pico_mcd->regs[0x46>>1].byte.l)) * 75 + + (Pico_mcd->regs[0x48>>1].byte.h * 10 + Pico_mcd->regs[0x48>>1].byte.l) - 150; + + /* CD drive latency */ + if (!cdd.latency) + { + /* Fixes a few games hanging during intro because they expect data to be read with some delay */ + /* Radical Rex needs at least one interrupt delay */ + /* Wolf Team games (Anet Futatabi, Cobra Command, Road Avenger & Time Gal) need at least 6 interrupts delay */ + /* Space Adventure Cobra (2nd morgue scene) needs at least 13 interrupts delay (incl. seek time, so 6 is OK) */ + /* Jeopardy & ESPN Sunday Night NFL are picky about this as well: 10 interrupts delay (+ seek time) seems OK */ + cdd.latency = 10; + } + + /* CD drive seek time */ + /* max. seek time = 1.5 s = 1.5 x 75 = 112.5 CDD interrupts (rounded to 120) for 270000 sectors max on disc. */ + /* Note: This is only a rough approximation since, on real hardware, seek time is much likely not linear and */ + /* latency much larger than above value, but this model works fine for Sonic CD (track 26 playback needs to */ + /* be enough delayed to start in sync with intro sequence, as compared with real hardware recording). */ + if (lba > cdd.lba) + { + cdd.latency += (((lba - cdd.lba) * 120) / 270000); + } + else + { + cdd.latency += (((cdd.lba - lba) * 120) / 270000); + } + + /* update current LBA */ + cdd.lba = lba; + + /* get track index */ + while ((cdd.toc.tracks[index].end <= lba) && (index < cdd.toc.last)) index++; + +#ifdef USE_LIBTREMOR +#ifdef DISABLE_MANY_OGG_OPEN_FILES + /* check if track index has changed */ + if (index != cdd.index) + { + /* close previous track VORBIS file structure to save memory */ + if (cdd.toc.tracks[cdd.index].vf.datasource) + { + ogg_free(cdd.index); + } + + /* open current track VORBIS file */ + if (cdd.toc.tracks[index].vf.seekable) + { + ov_open(cdd.toc.tracks[index].fd,&cdd.toc.tracks[index].vf,0,0); + } + } +#endif +#endif + + /* update current track index */ + cdd.index = index; + + /* stay within track limits when seeking files */ + if (lba < cdd.toc.tracks[index].start) + { + lba = cdd.toc.tracks[index].start; + } + + /* seek to current block */ + if (!index) + { + /* DATA track */ + pm_seek(cdd.toc.tracks[0].fd, lba * cdd.sectorSize, SEEK_SET); + } +#ifdef USE_LIBTREMOR + else if (cdd.toc.tracks[index].vf.seekable) + { + /* VORBIS AUDIO track */ + ov_pcm_seek(&cdd.toc.tracks[index].vf, (lba - cdd.toc.tracks[index].start) * 588 - cdd.toc.tracks[index].offset); + } +#endif +#if 0 + else if (cdd.toc.tracks[index].fd) + { + /* PCM AUDIO track */ + fseek(cdd.toc.tracks[index].fd, (lba * 2352) - cdd.toc.tracks[index].offset, SEEK_SET); + } +#else + else + { + cdd_change_track(index, lba); + } +#endif + + /* no audio track playing (yet) */ + Pico_mcd->regs[0x36>>1].byte.h = 0x01; + + /* update status */ + cdd.status = CD_PLAY; + + /* return track index in RS2-RS3 */ + set_reg16(0x38, (CD_PLAY << 8) | 0x02); + set_reg16(0x3a, (cdd.index < cdd.toc.last) ? lut_BCD_16[index + 1] : 0x0A0A); + set_reg16(0x3c, 0x0000); + set_reg16(0x3e, 0x0000); + Pico_mcd->regs[0x40>>1].byte.h = 0x00; + break; + } + + case 0x04: /* Seek */ + { + /* reset track index */ + int index = 0; + + /* new LBA position */ + int lba = ((Pico_mcd->regs[0x44>>1].byte.h * 10 + Pico_mcd->regs[0x44>>1].byte.l) * 60 + + (Pico_mcd->regs[0x46>>1].byte.h * 10 + Pico_mcd->regs[0x46>>1].byte.l)) * 75 + + (Pico_mcd->regs[0x48>>1].byte.h * 10 + Pico_mcd->regs[0x48>>1].byte.l) - 150; + + /* CD drive seek time */ + /* We are using similar linear model as above, although still not exactly accurate, */ + /* it works fine for Switch/Panic! intro (Switch needs at least 30 interrupts while */ + /* seeking from 00:05:63 to 24:03:19, Panic! when seeking from 00:05:60 to 24:06:07) */ + if (lba > cdd.lba) + { + cdd.latency = ((lba - cdd.lba) * 120) / 270000; + } + else + { + cdd.latency = ((cdd.lba - lba) * 120) / 270000; + } + + /* update current LBA */ + cdd.lba = lba; + + /* get current track index */ + while ((cdd.toc.tracks[index].end <= lba) && (index < cdd.toc.last)) index++; + +#ifdef USE_LIBTREMOR +#ifdef DISABLE_MANY_OGG_OPEN_FILES + /* check if track index has changed */ + if (index != cdd.index) + { + /* close previous track VORBIS file structure to save memory */ + if (cdd.toc.tracks[cdd.index].vf.datasource) + { + ogg_free(cdd.index); + } + + /* open current track VORBIS file */ + if (cdd.toc.tracks[index].vf.seekable) + { + ov_open(cdd.toc.tracks[index].fd,&cdd.toc.tracks[index].vf,0,0); + } + } +#endif +#endif + + /* update current track index */ + cdd.index = index; + + /* stay within track limits */ + if (lba < cdd.toc.tracks[index].start) + { + lba = cdd.toc.tracks[index].start; + } + + /* seek to current block */ + if (!index) + { + /* DATA track */ + pm_seek(cdd.toc.tracks[0].fd, lba * cdd.sectorSize, SEEK_SET); + } +#ifdef USE_LIBTREMOR + else if (cdd.toc.tracks[index].vf.seekable) + { + /* VORBIS AUDIO track */ + ov_pcm_seek(&cdd.toc.tracks[index].vf, (lba - cdd.toc.tracks[index].start) * 588 - cdd.toc.tracks[index].offset); + } +#endif +#if 0 + else if (cdd.toc.tracks[index].fd) + { + /* PCM AUDIO track */ + fseek(cdd.toc.tracks[index].fd, (lba * 2352) - cdd.toc.tracks[index].offset, SEEK_SET); + } +#endif + + /* no audio track playing */ + Pico_mcd->regs[0x36>>1].byte.h = 0x01; + + /* update status */ + cdd.status = CD_SEEK; + + /* unknown RS1-RS8 values (returning 0xF in RS1 invalidates track infos in RS2-RS8 and fixes Final Fight CD intro when seek time is emulated) */ + set_reg16(0x38, (CD_SEEK << 8) | 0x0f); + set_reg16(0x3a, 0x0000); + set_reg16(0x3c, 0x0000); + set_reg16(0x3e, 0x0000); + set_reg16(0x40, ~(CD_SEEK + 0xf) & 0x0f); + return; + } + + case 0x06: /* Pause */ + { + /* no audio track playing */ + Pico_mcd->regs[0x36>>1].byte.h = 0x01; + + /* update status (RS1-RS8 unchanged) */ + cdd.status = Pico_mcd->regs[0x38>>1].byte.h = CD_READY; + break; + } + + case 0x07: /* Resume */ + { + /* update status (RS1-RS8 unchanged) */ + cdd.status = Pico_mcd->regs[0x38>>1].byte.h = CD_PLAY; + break; + } + + case 0x08: /* Forward Scan */ + { + /* reset scanning direction / speed */ + cdd.scanOffset = CD_SCAN_SPEED; + + /* update status (RS1-RS8 unchanged) */ + cdd.status = Pico_mcd->regs[0x38>>1].byte.h = CD_SCAN; + break; + } + + case 0x09: /* Rewind Scan */ + { + /* reset scanning direction / speed */ + cdd.scanOffset = -CD_SCAN_SPEED; + + /* update status (RS1-RS8 unchanged) */ + cdd.status = Pico_mcd->regs[0x38>>1].byte.h = CD_SCAN; + break; + } + + + case 0x0a: /* N-Track Jump Control ? (usually sent before CD_SEEK or CD_PLAY commands) */ + { + /* TC3 corresponds to seek direction (00=forward, FF=reverse) */ + /* TC4-TC7 are related to seek length (4x4 bits i.e parameter values are between -65535 and +65535) */ + /* Maybe related to number of auto-sequenced track jumps/moves for CD DSP (cf. CXD2500BQ datasheet) */ + /* also see US Patent nr. 5222054 for a detailled description of seeking operation using Track Jump */ + + /* no audio track playing */ + Pico_mcd->regs[0x36>>1].byte.h = 0x01; + + /* update status (RS1-RS8 unchanged) */ + cdd.status = Pico_mcd->regs[0x38>>1].byte.h = CD_READY; + break; + } + + case 0x0c: /* Close Tray */ + { + /* no audio track playing */ + Pico_mcd->regs[0x36>>1].byte.h = 0x01; + + /* update status */ + cdd.status = cdd.loaded ? CD_STOP : NO_DISC; + + /* RS1-RS8 ignored, expects 0x0 ("no disc" ?) in RS0 once */ + set_reg16(0x38, 0x0000); + set_reg16(0x3a, 0x0000); + set_reg16(0x3c, 0x0000); + set_reg16(0x3e, 0x0000); + set_reg16(0x40, 0x000f); + + if (PicoMCDcloseTray) + PicoMCDcloseTray(); + return; + } + + case 0x0d: /* Open Tray */ + { + /* no audio track playing */ + Pico_mcd->regs[0x36>>1].byte.h = 0x01; + + /* update status (RS1-RS8 ignored) */ + cdd.status = CD_OPEN; + set_reg16(0x38, CD_OPEN << 8); + set_reg16(0x3a, 0x0000); + set_reg16(0x3c, 0x0000); + set_reg16(0x3e, 0x0000); + set_reg16(0x40, ~CD_OPEN & 0x0f); + + if (PicoMCDopenTray) + PicoMCDopenTray(); + return; + } + + default: /* Unknown command */ +#ifdef LOG_CDD + error("Unknown CDD Command !!!\n"); +#endif + Pico_mcd->regs[0x38>>1].byte.h = cdd.status; + break; + } + + /* only compute checksum when necessary */ + Pico_mcd->regs[0x40>>1].byte.l = + ~(Pico_mcd->regs[0x38>>1].byte.h + Pico_mcd->regs[0x38>>1].byte.l + + Pico_mcd->regs[0x3a>>1].byte.h + Pico_mcd->regs[0x3a>>1].byte.l + + Pico_mcd->regs[0x3c>>1].byte.h + Pico_mcd->regs[0x3c>>1].byte.l + + Pico_mcd->regs[0x3e>>1].byte.h + Pico_mcd->regs[0x3e>>1].byte.l + + Pico_mcd->regs[0x40>>1].byte.h) & 0x0f; +} + +// vim:shiftwidth=2:ts=2:expandtab diff --git a/pico/cd/cdd.h b/pico/cd/cdd.h new file mode 100644 index 00000000..4789cdb4 --- /dev/null +++ b/pico/cd/cdd.h @@ -0,0 +1,98 @@ +/*************************************************************************************** + * Genesis Plus + * CD drive processor & CD-DA fader + * + * Copyright (C) 2012-2013 Eke-Eke (Genesis Plus GX) + * + * Redistribution and use of this code or any derivative works are permitted + * provided that the following conditions are met: + * + * - Redistributions may not be sold, nor may they be used in a commercial + * product or activity. + * + * - Redistributions that are modified from the original source must include the + * complete source code, including the source code for all components used by a + * binary built from the modified sources. However, as a special exception, the + * source code distributed need not include anything that is normally distributed + * (in either source or binary form) with the major components (compiler, kernel, + * and so on) of the operating system on which the executable runs, unless that + * component itself accompanies the executable. + * + * - Redistributions must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************************/ +#ifndef _HW_CDD_ +#define _HW_CDD_ + +#ifdef USE_LIBTREMOR +#include "tremor/ivorbisfile.h" +#endif + +/* CDD status */ +#define NO_DISC 0x00 +#define CD_PLAY 0x01 +#define CD_SEEK 0x02 +#define CD_SCAN 0x03 +#define CD_READY 0x04 +#define CD_OPEN 0x05 /* similar to 0x0E ? */ +#define CD_STOP 0x09 +#define CD_END 0x0C + +/* CD blocks scanning speed */ +#define CD_SCAN_SPEED 30 + +#define CD_MAX_TRACKS 100 + +/* CD track */ +typedef struct +{ + void *fd; +#ifdef USE_LIBTREMOR + OggVorbis_File vf; +#endif + int offset; + int start; + int end; +} track_t; + +/* CD TOC */ +typedef struct +{ + int end; + int last; + track_t tracks[CD_MAX_TRACKS]; +} toc_t; + +/* CDD hardware */ +typedef struct +{ + uint32 cycles; + uint32 latency; + int loaded; + int index; + int lba; + int scanOffset; + int volume; + uint8 status; + uint16 sectorSize; + toc_t toc; + int16 audio[2]; +} cdd_t; + +extern cdd_t cdd; + +#endif diff --git a/pico/cd/cue.c b/pico/cd/cue.c index a038ccf7..78c6b0f5 100644 --- a/pico/cd/cue.c +++ b/pico/cd/cue.c @@ -242,6 +242,8 @@ file_ok: data->tracks[count].type = CT_MP3; else if (strcasecmp(ext, "wav") == 0) data->tracks[count].type = CT_WAV; + else if (strcasecmp(ext, "bin") == 0) + data->tracks[count].type = CT_BIN; else { elprintf(EL_STATUS, "unhandled audio format: \"%s\"", data->tracks[count].fname); diff --git a/pico/cd/mcd.c b/pico/cd/mcd.c index 33553613..cad03e9f 100644 --- a/pico/cd/mcd.c +++ b/pico/cd/mcd.c @@ -22,13 +22,10 @@ void (*PicoMCDcloseTray)(void) = NULL; PICO_INTERNAL void PicoInitMCD(void) { SekInitS68k(); - Init_CD_Driver(); - gfx_init(); } PICO_INTERNAL void PicoExitMCD(void) { - End_CD_Driver(); } PICO_INTERNAL void PicoPowerMCD(void) @@ -45,9 +42,11 @@ PICO_INTERNAL void PicoPowerMCD(void) memset(Pico_mcd->s68k_regs, 0, sizeof(Pico_mcd->s68k_regs)); memset(&Pico_mcd->pcm, 0, sizeof(Pico_mcd->pcm)); memset(&Pico_mcd->m, 0, sizeof(Pico_mcd->m)); + Pico_mcd->s68k_regs[0x38+9] = 0x0f; // default checksum cdc_init(); - Reset_CD(); + cdd_reset(); + gfx_init(); // cold reset state (tested) Pico_mcd->m.state_flags = PCD_ST_S68K_RST; @@ -62,7 +61,7 @@ void pcd_soft_reset(void) Pico_mcd->m.s68k_pend_ints = 0; cdc_reset(); - CDD_Reset(); + cdd_reset(); #ifdef _ASM_CD_MEMORY_C //PicoMemResetCDdecode(1); // don't have to call this in 2M mode #endif @@ -135,7 +134,20 @@ unsigned int pcd_cycles_m68k_to_s68k(unsigned int c) static void pcd_cdc_event(unsigned int now) { // 75Hz CDC update - Check_CD_Command(); + cdd_update(); + + /* check if a new CDD command has been processed */ + if (!(Pico_mcd->s68k_regs[0x4b] & 0xf0)) + { + /* reset CDD command wait flag */ + Pico_mcd->s68k_regs[0x4b] = 0xf0; + + if (Pico_mcd->s68k_regs[0x33] & PCDS_IEN4) { + elprintf(EL_INTS|EL_CD, "s68k: cdd irq 4"); + SekInterruptS68k(4); + } + } + pcd_event_schedule(now, PCD_EVENT_CDC, 12500000/75); } diff --git a/pico/cd/memory.c b/pico/cd/memory.c index d3a2927e..7d838fc9 100644 --- a/pico/cd/memory.c +++ b/pico/cd/memory.c @@ -407,8 +407,11 @@ void s68k_reg_write8(u32 a, u32 d) elprintf(EL_CDREGS|EL_CD, "s68k irq mask: %02x", d); d &= 0x7e; if ((d ^ Pico_mcd->s68k_regs[0x33]) & d & PCDS_IEN4) { - if (Pico_mcd->s68k_regs[0x37] & 4) - CDD_Export_Status(); + // XXX: emulate pending irq instead? + if (Pico_mcd->s68k_regs[0x37] & 4) { + elprintf(EL_INTS, "cdd export irq 4 (unmask)"); + SekInterruptS68k(4); + } } break; case 0x34: // fader @@ -418,15 +421,21 @@ void s68k_reg_write8(u32 a, u32 d) return; // d/m bit is unsetable case 0x37: { u32 d_old = Pico_mcd->s68k_regs[0x37]; - Pico_mcd->s68k_regs[0x37] = d&7; + Pico_mcd->s68k_regs[0x37] = d & 7; if ((d&4) && !(d_old&4)) { - CDD_Export_Status(); + // ?? + pcd_event_schedule_s68k(PCD_EVENT_CDC, 12500000/75); + + if (Pico_mcd->s68k_regs[0x33] & PCDS_IEN4) { + elprintf(EL_INTS, "cdd export irq 4"); + SekInterruptS68k(4); + } } return; } case 0x4b: Pico_mcd->s68k_regs[a] = (u8) d; - CDD_Import_Command(); + cdd_process(); return; case 0x58: return; diff --git a/pico/media.c b/pico/media.c index 904693f9..3ba45324 100644 --- a/pico/media.c +++ b/pico/media.c @@ -198,7 +198,7 @@ enum media_type_e PicoLoadMedia(const char *filename, { const char *rom_fname = filename; enum media_type_e media_type; - cd_img_type cd_img_type = CIT_NOT_CD; + enum cd_img_type cd_img_type = CIT_NOT_CD; unsigned char *rom_data = NULL; unsigned int rom_size = 0; pm_file *rom = NULL; @@ -210,7 +210,7 @@ enum media_type_e PicoLoadMedia(const char *filename, goto out; if ((PicoAHW & PAHW_MCD) && Pico_mcd != NULL) - Stop_CD(); + cdd_unload(); PicoCartUnload(); PicoAHW = 0; PicoQuirks = 0; @@ -291,7 +291,7 @@ enum media_type_e PicoLoadMedia(const char *filename, // insert CD if it was detected if (cd_img_type != CIT_NOT_CD) { - ret = Insert_CD(filename, cd_img_type); + ret = cdd_load(filename, cd_img_type); if (ret != 0) { PicoCartUnload(); media_type = PM_BAD_CD; diff --git a/pico/pico.h b/pico/pico.h index 41ba7fb0..d5416727 100644 --- a/pico/pico.h +++ b/pico/pico.h @@ -102,7 +102,6 @@ void PicoGetInternal(pint_t which, pint_ret_t *ret); // cd/mcd.c extern void (*PicoMCDopenTray)(void); extern void (*PicoMCDcloseTray)(void); -extern int PicoCDBuffers; // pico.c #define XPCM_BUFFER_SIZE (320+160) @@ -128,14 +127,9 @@ void *PicoTmpStateSave(void); void PicoTmpStateRestore(void *data); extern void (*PicoStateProgressCB)(const char *str); -// cd/buffering.c -void PicoCDBufferInit(void); -void PicoCDBufferFree(void); -void PicoCDBufferFlush(void); - -// cd/cd_sys.c -int Insert_CD(const char *cdimg_name, int type); -int Stop_CD(void); // unloads CD, returns 1 if there was cd loaded +// cd/cdd.c +int cdd_load(const char *filename, int type); +int cdd_unload(void); // Cart.c typedef enum @@ -244,6 +238,15 @@ enum media_type_e { PM_MARK3, PM_CD, }; + +enum cd_img_type +{ + CIT_NOT_CD = 0, + CIT_ISO, + CIT_BIN, + CIT_CUE +}; + enum media_type_e PicoLoadMedia(const char *filename, const char *carthw_cfg_fname, const char *(*get_bios_filename)(int *region, const char *cd_fname), diff --git a/pico/pico_int.h b/pico/pico_int.h index d4d6d7de..76c4812e 100644 --- a/pico/pico_int.h +++ b/pico/pico_int.h @@ -374,9 +374,6 @@ struct PicoSRAM }; // MCD -#include "cd/cd_sys.h" -#include "cd/LC89510.h" - #define PCM_MIXBUF_LEN ((12500000 / 384) / 50 + 1) struct mcd_pcm @@ -448,9 +445,8 @@ typedef struct unsigned char bram[0x2000]; // 110200: 8K struct mcd_misc m; // 112200: misc struct mcd_pcm pcm; // 112240: - _scd_toc TOC; // not to be saved - CDD cdd; - _scd scd; + void *cdda_stream; + int cdda_type; int pcm_mixbuf[PCM_MIXBUF_LEN * 2]; int pcm_mixpos; char pcm_mixbuf_dirty; @@ -628,6 +624,19 @@ void cdc_reg_w(unsigned char data); unsigned char cdc_reg_r(void); unsigned short cdc_host_r(void); +// cd/cdd.c +void cdd_reset(void); +int cdd_context_save(unsigned char *state); +int cdd_context_load(unsigned char *state); +int cdd_context_load_old(unsigned char *state); +void cdd_read_data(unsigned char *dst); +void cdd_read_audio(unsigned int samples); +void cdd_update(void); +void cdd_process(void); + +// cd/cd_image.c +int load_cd_image(const char *cd_img_name, int *type); + // cd/gfx.c void gfx_init(void); void gfx_start(unsigned int base); @@ -727,13 +736,14 @@ PICO_INTERNAL int SekInterruptS68k(int irq); void SekInterruptClearS68k(int irq); // sound/sound.c -PICO_INTERNAL void cdda_start_play(); extern short cdda_out_buffer[2*1152]; extern int PsndLen_exc_cnt; extern int PsndLen_exc_add; extern int timer_a_next_oflow, timer_a_step; // in z80 cycles extern int timer_b_next_oflow, timer_b_step; +void cdda_start_play(int lba_base, int lba_offset, int lb_len); + void ym2612_sync_timers(int z80_cycles, int mode_old, int mode_new); void ym2612_pack_state(void); void ym2612_unpack_state(void); @@ -786,9 +796,6 @@ PICO_INTERNAL void z80_exit(void); PICO_INTERNAL_ASM void wram_2M_to_1M(unsigned char *m); PICO_INTERNAL_ASM void wram_1M_to_2M(unsigned char *m); -// cd/buffering.c -PICO_INTERNAL void PicoCDBufferRead(void *dest, int lba); - // sound/sound.c PICO_INTERNAL void PsndReset(void); PICO_INTERNAL void PsndDoDAC(int line_to); diff --git a/pico/sound/sound.c b/pico/sound/sound.c index b12afc3c..69c1be03 100644 --- a/pico/sound/sound.c +++ b/pico/sound/sound.c @@ -132,8 +132,6 @@ void PsndRerate(int preserve_state) // feed it back it's own registers, just like after loading state memcpy(YM2612GetRegs(), state, 0x204); ym2612_unpack_state(); - if ((PicoAHW & PAHW_MCD) && !(Pico_mcd->s68k_regs[0x36] & 1) && (Pico_mcd->scd.Status_CDC & 1)) - cdda_start_play(); } if (preserve_state) memcpy(state, sn76496_regs, 28*4); // remember old state @@ -191,23 +189,19 @@ PICO_INTERNAL void PsndDoDAC(int line_to) } // cdda -static pm_file *cdda_stream = NULL; - static void cdda_raw_update(int *buffer, int length) { int ret, cdda_bytes, mult = 1; - if (cdda_stream == NULL) - return; cdda_bytes = length*4; if (PsndRate <= 22050 + 100) mult = 2; if (PsndRate < 22050 - 100) mult = 4; cdda_bytes *= mult; - ret = pm_read(cdda_out_buffer, cdda_bytes, cdda_stream); + ret = pm_read(cdda_out_buffer, cdda_bytes, Pico_mcd->cdda_stream); if (ret < cdda_bytes) { memset((char *)cdda_out_buffer + ret, 0, cdda_bytes - ret); - cdda_stream = NULL; + Pico_mcd->cdda_stream = NULL; return; } @@ -219,51 +213,24 @@ static void cdda_raw_update(int *buffer, int length) } } -PICO_INTERNAL void cdda_start_play(void) +void cdda_start_play(int lba_base, int lba_offset, int lb_len) { - int lba_offset, index, lba_length, i; - - elprintf(EL_STATUS, "cdda play track #%i", Pico_mcd->scd.Cur_Track); - - index = Pico_mcd->scd.Cur_Track - 1; - - lba_offset = Pico_mcd->scd.Cur_LBA - Track_to_LBA(index + 1); - if (lba_offset < 0) lba_offset = 0; - lba_offset += Pico_mcd->TOC.Tracks[index].Offset; - - // find the actual file for this track - for (i = index; i > 0; i--) - if (Pico_mcd->TOC.Tracks[i].F != NULL) break; - - if (Pico_mcd->TOC.Tracks[i].F == NULL) { - elprintf(EL_STATUS|EL_ANOMALY, "no track?!"); - return; - } - - if (Pico_mcd->TOC.Tracks[i].ftype == CT_MP3) + if (Pico_mcd->cdda_type == CT_MP3) { int pos1024 = 0; - lba_length = Pico_mcd->TOC.Tracks[i].Length; - for (i++; i < Pico_mcd->TOC.Last_Track; i++) { - if (Pico_mcd->TOC.Tracks[i].F != NULL) break; - lba_length += Pico_mcd->TOC.Tracks[i].Length; - } - if (lba_offset) - pos1024 = lba_offset * 1024 / lba_length; + pos1024 = lba_offset * 1024 / lb_len; - mp3_start_play(Pico_mcd->TOC.Tracks[index].F, pos1024); + mp3_start_play(Pico_mcd->cdda_stream, pos1024); return; } - cdda_stream = Pico_mcd->TOC.Tracks[i].F; - PicoCDBufferFlush(); // buffering relies on fp not being touched - pm_seek(cdda_stream, lba_offset * 2352, SEEK_SET); - if (Pico_mcd->TOC.Tracks[i].ftype == CT_WAV) + pm_seek(Pico_mcd->cdda_stream, (lba_base + lba_offset) * 2352, SEEK_SET); + if (Pico_mcd->cdda_type == CT_WAV) { // skip headers, assume it's 44kHz stereo uncompressed - pm_seek(cdda_stream, 44, SEEK_CUR); + pm_seek(Pico_mcd->cdda_stream, 44, SEEK_CUR); } } @@ -330,13 +297,12 @@ static int PsndRender(int offset, int length) // CD: CDDA audio // CD mode, cdda enabled, not data track, CDC is reading - if ((PicoAHW & PAHW_MCD) && (PicoOpt & POPT_EN_MCD_CDDA) && - !(Pico_mcd->s68k_regs[0x36] & 1) && (Pico_mcd->scd.Status_CDC & 1)) + if ((PicoAHW & PAHW_MCD) && (PicoOpt & POPT_EN_MCD_CDDA) + && Pico_mcd->cdda_stream != NULL + && !(Pico_mcd->s68k_regs[0x36] & 1)) { // note: only 44, 22 and 11 kHz supported, with forced stereo - int index = Pico_mcd->scd.Cur_Track - 1; - - if (Pico_mcd->TOC.Tracks[index].ftype == CT_MP3) + if (Pico_mcd->cdda_type == CT_MP3) mp3_update(buf32, length, stereo); else cdda_raw_update(buf32, length); diff --git a/pico/state.c b/pico/state.c index 94cefade..089957eb 100644 --- a/pico/state.c +++ b/pico/state.c @@ -149,8 +149,8 @@ typedef enum { CHUNK_GA_REGS, CHUNK_PCM, CHUNK_CDC, // old - CHUNK_CDD, // 20 - CHUNK_SCD, + CHUNK_CDD, // 20 old + CHUNK_SCD, // old CHUNK_RC, // old CHUNK_MISC_CD, // @@ -177,6 +177,7 @@ typedef enum { CHUNK_CD_EVT = 50, CHUNK_CD_GFX, CHUNK_CD_CDC, + CHUNK_CD_CDD, // CHUNK_DEFAULT_COUNT, CHUNK_CARTHW_ = CHUNK_CARTHW, // 64 (defined in PicoInt) @@ -317,8 +318,6 @@ static int state_save(void *file) CHECKED_WRITE_BUFF(CHUNK_BRAM, Pico_mcd->bram); CHECKED_WRITE_BUFF(CHUNK_GA_REGS, Pico_mcd->s68k_regs); // GA regs, not CPU regs CHECKED_WRITE_BUFF(CHUNK_PCM, Pico_mcd->pcm); - CHECKED_WRITE_BUFF(CHUNK_CDD, Pico_mcd->cdd); - CHECKED_WRITE_BUFF(CHUNK_SCD, Pico_mcd->scd); CHECKED_WRITE_BUFF(CHUNK_MISC_CD, Pico_mcd->m); memset(buff, 0, 0x40); memcpy(buff, pcd_event_times, sizeof(pcd_event_times)); @@ -328,6 +327,8 @@ static int state_save(void *file) CHECKED_WRITE(CHUNK_CD_GFX, len, buf2); len = cdc_context_save(buf2); CHECKED_WRITE(CHUNK_CD_CDC, len, buf2); + len = cdd_context_save(buf2); + CHECKED_WRITE(CHUNK_CD_CDD, len, buf2); if (Pico_mcd->s68k_regs[3] & 4) // convert back wram_2M_to_1M(Pico_mcd->word_ram2M); @@ -498,8 +499,6 @@ static int state_load(void *file) case CHUNK_BRAM: CHECKED_READ_BUFF(Pico_mcd->bram); break; case CHUNK_GA_REGS: CHECKED_READ_BUFF(Pico_mcd->s68k_regs); break; case CHUNK_PCM: CHECKED_READ_BUFF(Pico_mcd->pcm); break; - case CHUNK_CDD: CHECKED_READ_BUFF(Pico_mcd->cdd); break; - case CHUNK_SCD: CHECKED_READ_BUFF(Pico_mcd->scd); break; case CHUNK_MISC_CD: CHECKED_READ_BUFF(Pico_mcd->m); break; case CHUNK_CD_EVT: @@ -517,12 +516,22 @@ static int state_load(void *file) len_check = cdc_context_load(buf); break; + case CHUNK_CD_CDD: + CHECKED_READ_LIM(buf); + len_check = cdd_context_load(buf); + break; + // old, to be removed: case CHUNK_CDC: CHECKED_READ_LIM(buf); cdc_context_load_old(buf); break; + case CHUNK_SCD: + CHECKED_READ_LIM(buf); + cdd_context_load_old(buf); + break; + // 32x stuff #ifndef NO_32X case CHUNK_MSH2: @@ -596,9 +605,6 @@ readend: { SekCycleAimS68k = SekCycleCntS68k; pcd_state_loaded(); - - if (!(Pico_mcd->s68k_regs[0x36] & 1) && (Pico_mcd->scd.Status_CDC & 1)) - cdda_start_play(); } retval = 0; diff --git a/platform/base_readme.txt b/platform/base_readme.txt index 175bcc1b..d2f7e96c 100644 --- a/platform/base_readme.txt +++ b/platform/base_readme.txt @@ -520,9 +520,6 @@ Homepage: http://www.mame.net/ Eke CD graphics processor and CD controller implementation (from Genesis Plus GX) - -Stephane Dallongeville -Gens, MD/Mega CD/32X emulator. Some Sega CD code is based on this emu. #ifdef PSP people @ ps2dev.org forums / PSPSDK crew @@ -537,6 +534,7 @@ Additional thanks * Charles MacDonald (http://cgfm2.emuviews.com/) for old but still very useful info about genesis hardware. * Steve Snake for all that he has done for Genesis emulation scene. +* Stephane Dallongeville for writing Gens and making it open source. * Tasco Deluxe for his reverse engineering work on SVP and some mappers. * Bart Trzynadlowski for his SSFII and 68000 docs. * Haze for his research (http://haze.mameworld.info). diff --git a/platform/common/common.mak b/platform/common/common.mak index c651bcad..fb59ecf3 100644 --- a/platform/common/common.mak +++ b/platform/common/common.mak @@ -93,9 +93,9 @@ DEFINES += NO_SMS endif # CD SRCS_COMMON += $(R)pico/cd/mcd.c $(R)pico/cd/memory.c $(R)pico/cd/sek.c \ - $(R)pico/cd/cdc.c $(R)pico/cd/cd_sys.c $(R)pico/cd/cd_file.c \ + $(R)pico/cd/cdc.c $(R)pico/cd/cdd.c $(R)pico/cd/cd_image.c \ $(R)pico/cd/cue.c $(R)pico/cd/gfx.c $(R)pico/cd/gfx_dma.c \ - $(R)pico/cd/misc.c $(R)pico/cd/pcm.c $(R)pico/cd/buffering.c + $(R)pico/cd/misc.c $(R)pico/cd/pcm.c # 32X ifneq "$(no_32x)" "1" SRCS_COMMON += $(R)pico/32x/32x.c $(R)pico/32x/memory.c $(R)pico/32x/draw.c \ diff --git a/platform/common/config_file.c b/platform/common/config_file.c index 324c993e..97369cb4 100644 --- a/platform/common/config_file.c +++ b/platform/common/config_file.c @@ -308,11 +308,6 @@ static int custom_read(menu_entry *me, const char *var, const char *val) return 0; return 1; - case MA_CDOPT_READAHEAD: - if (strcasecmp(var, "ReadAhead buffer") != 0) return 0; - PicoCDBuffers = atoi(val) / 2; - return 1; - case MA_32XOPT_MSH2_CYCLES: currentConfig.msh2_khz = atoi(val); Pico32xSetClocks(currentConfig.msh2_khz * 1000, 0); diff --git a/platform/common/emu.c b/platform/common/emu.c index c66c637c..18f63f2c 100644 --- a/platform/common/emu.c +++ b/platform/common/emu.c @@ -530,19 +530,20 @@ out: int emu_swap_cd(const char *fname) { - cd_img_type cd_type; + enum cd_img_type cd_type; int ret = -1; cd_type = PicoCdCheck(fname, NULL); if (cd_type != CIT_NOT_CD) - ret = Insert_CD(fname, cd_type); + ret = cdd_load(fname, cd_type); if (ret != 0) { menu_update_msg("Load failed, invalid CD image?"); return 0; } strncpy(rom_fname_loaded, fname, sizeof(rom_fname_loaded)-1); - rom_fname_loaded[sizeof(rom_fname_loaded)-1] = 0; + rom_fname_loaded[sizeof(rom_fname_loaded) - 1] = 0; + return 1; } @@ -606,7 +607,6 @@ void emu_set_defconfig(void) PsndRate = currentConfig.s_PsndRate; PicoRegionOverride = currentConfig.s_PicoRegion; PicoAutoRgnOrder = currentConfig.s_PicoAutoRgnOrder; - PicoCDBuffers = currentConfig.s_PicoCDBuffers; } int emu_read_config(const char *rom_fname, int no_defaults) @@ -1336,10 +1336,6 @@ void emu_loop(void) PicoLoopPrepare(); - // prepare CD buffer - if (PicoAHW & PAHW_MCD) - PicoCDBufferInit(); - plat_video_loop_prepare(); emu_loop_prep(); pemu_sound_start(); @@ -1501,10 +1497,4 @@ void emu_loop(void) pemu_loop_end(); emu_sound_stop(); - - // pemu_loop_end() might want to do 1 frame for bg image, - // so free CD buffer here - if (PicoAHW & PAHW_MCD) - PicoCDBufferFree(); } - diff --git a/platform/common/menu_pico.c b/platform/common/menu_pico.c index 372fad91..0f04bc41 100644 --- a/platform/common/menu_pico.c +++ b/platform/common/menu_pico.c @@ -395,31 +395,6 @@ static int menu_loop_keyconfig(int id, int keys) // ------------ SCD options menu ------------ -static const char *mgn_cdopt_ra(int id, int *offs) -{ - *offs = -5; - if (PicoCDBuffers <= 0) - return " OFF"; - sprintf(static_buff, "%5iK", PicoCDBuffers * 2); - return static_buff; -} - -static int mh_cdopt_ra(int id, int keys) -{ - if (keys & PBTN_LEFT) { - PicoCDBuffers >>= 1; - if (PicoCDBuffers < 2) - PicoCDBuffers = 0; - } else { - if (PicoCDBuffers <= 0) - PicoCDBuffers = 1; - PicoCDBuffers <<= 1; - if (PicoCDBuffers > 8*1024) - PicoCDBuffers = 8*1024; // 16M - } - return 0; -} - static const char h_cdleds[] = "Show power/CD LEDs of emulated console"; static const char h_cdda[] = "Play audio tracks from mp3s/wavs/bins"; static const char h_cdpcm[] = "Emulate PCM audio chip for effects/voices/music"; @@ -435,7 +410,6 @@ static menu_entry e_menu_cd_options[] = mee_onoff_h("CD LEDs", MA_CDOPT_LEDS, currentConfig.EmuOpt, EOPT_EN_CD_LEDS, h_cdleds), mee_onoff_h("CDDA audio", MA_CDOPT_CDDA, PicoOpt, POPT_EN_MCD_CDDA, h_cdda), mee_onoff_h("PCM audio", MA_CDOPT_PCM, PicoOpt, POPT_EN_MCD_PCM, h_cdpcm), - mee_cust ("ReadAhead buffer", MA_CDOPT_READAHEAD, mh_cdopt_ra, mgn_cdopt_ra), mee_onoff_h("SaveRAM cart", MA_CDOPT_SAVERAM, PicoOpt, POPT_EN_MCD_RAMCART, h_srcart), mee_onoff_h("Scale/Rot. fx (slow)", MA_CDOPT_SCALEROT_CHIP, PicoOpt, POPT_EN_MCD_GFX, h_scfx), mee_end, @@ -1045,7 +1019,7 @@ static int main_menu_handler(int id, int keys) break; case MA_MAIN_CHANGE_CD: if (PicoAHW & PAHW_MCD) { - if (!Stop_CD()) + if (!cdd_unload()) menu_loop_tray(); return 1; } diff --git a/platform/libretro.c b/platform/libretro.c index f59cf672..ee15d9c8 100644 --- a/platform/libretro.c +++ b/platform/libretro.c @@ -500,7 +500,7 @@ static unsigned int disk_get_image_index(void) static bool disk_set_image_index(unsigned int index) { - cd_img_type cd_type; + enum cd_img_type cd_type; int ret; if (index >= sizeof(disks) / sizeof(disks[0])) @@ -521,7 +521,7 @@ static bool disk_set_image_index(unsigned int index) ret = -1; cd_type = PicoCdCheck(disks[index].fname, NULL); if (cd_type != CIT_NOT_CD) - ret = Insert_CD(disks[index].fname, cd_type); + ret = cdd_load(disks[index].fname, cd_type); if (ret != 0) { lprintf("Load failed, invalid CD image?\n"); return 0; @@ -882,7 +882,6 @@ void retro_init(void) #endif PsndRate = 44100; PicoAutoRgnOrder = 0x184; // US, EU, JP - PicoCDBuffers = 0; vout_width = 320; vout_height = 240;