summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/machine/stvcd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/machine/stvcd.c')
-rw-r--r--src/mame/machine/stvcd.c1856
1 files changed, 1856 insertions, 0 deletions
diff --git a/src/mame/machine/stvcd.c b/src/mame/machine/stvcd.c
new file mode 100644
index 00000000000..58548adf9e8
--- /dev/null
+++ b/src/mame/machine/stvcd.c
@@ -0,0 +1,1856 @@
+/***************************************************************************
+
+ machine/stvcd.c - Sega Saturn and ST-V CD-ROM handling
+
+ Rewritten (again) 2007 by R. Belmont.
+
+ Status: All known discs at least load their executable, and many load
+ some data files successfully, but there are other problems.
+
+ Information sources:
+ - Tyranid's document
+ - A commented disassembly I made of the Saturn BIOS's CD code
+ - Yabuse's cs2.c
+ - The ISO/IEC "Yellow Book" CD-ROM standard, 1995 version
+
+ Address is mostly in terms of FAD (Frame ADdress).
+ FAD is absolute number of frames from the start of the disc.
+ In other words, FAD = LBA + 150; FAD is the same units as
+ LBA except it counts starting at absolute zero instead of
+ the first sector (00:02:00 in MSF format).
+
+***************************************************************************/
+
+#include "driver.h"
+#ifdef MESS
+#include "devices/chd_cd.h"
+#endif
+#include "cdrom.h"
+#include "stvcd.h"
+#include <stdio.h>
+
+// super-verbose
+#if 0
+#define CDROM_LOG(x) printf x;
+#else
+#define CDROM_LOG(x)
+#endif
+
+static cdrom_file *cdrom = (cdrom_file *)NULL;
+
+static void cd_readTOC(void);
+static void cd_readblock(UINT32 fad, UINT8 *dat);
+static void cd_playdata(void);
+
+#define MAX_FILTERS (24)
+#define MAX_BLOCKS (200)
+
+typedef struct
+{
+ UINT8 flags; // iso9660 flags
+ UINT32 length; // length of file
+ UINT32 firstfad; // first sector of file
+ UINT8 name[128];
+} direntryT;
+
+typedef struct
+{
+ UINT8 mode;
+ UINT8 chan;
+ UINT8 smmask;
+ UINT8 cimask;
+ UINT8 fid;
+ UINT8 smval;
+ UINT8 cival;
+ UINT8 condtrue;
+ UINT8 condfalse;
+ UINT32 fad;
+ UINT32 range;
+} filterT;
+
+typedef struct
+{
+ INT32 size; // size of block
+ INT32 FAD; // FAD on disc
+ UINT8 data[CD_MAX_SECTOR_DATA];
+ UINT8 chan; // channel
+ UINT8 fnum; // file number
+ UINT8 subm; // subchannel mode
+ UINT8 cinf; // coding information
+} blockT;
+
+typedef struct
+{
+ INT32 size;
+ blockT *blocks[MAX_BLOCKS];
+ UINT8 bnum[MAX_BLOCKS];
+ UINT8 numblks;
+} partitionT;
+
+// 16-bit transfer types
+typedef enum
+{
+ XFERTYPE_INVALID,
+ XFERTYPE_TOC,
+ XFERTYPE_FILEINFO_1,
+ XFERTYPE_FILEINFO_254
+} transT;
+
+// 32-bit transfer types
+typedef enum
+{
+ XFERTYPE32_INVALID,
+ XFERTYPE32_GETSECTOR,
+ XFERTYPE32_GETDELETESECTOR
+} trans32T;
+
+// local variables
+static emu_timer *sector_timer;
+static partitionT partitions[MAX_FILTERS];
+static partitionT *transpart;
+
+static blockT blocks[MAX_BLOCKS];
+static blockT curblock;
+
+static UINT8 tocbuf[102*4];
+static UINT8 finfbuf[256];
+static UINT8 onesectorstored;
+
+static INT32 sectlenin, sectlenout;
+
+static UINT8 lastbuf, playtype;
+
+static transT xfertype;
+static trans32T xfertype32;
+static UINT32 xfercount, calcsize;
+static UINT32 xferoffs, xfersect, xfersectpos, xfersectnum, xferdnum;
+
+static filterT filters[MAX_FILTERS];
+static filterT *cddevice;
+static int cddevicenum;
+
+static UINT16 cr1, cr2, cr3, cr4;
+static UINT16 hirqmask, hirqreg;
+static UINT16 cd_stat;
+static UINT32 cd_curfad = 0;
+static UINT32 in_buffer = 0; // amount of data in the buffer
+static int oddframe = 0;
+static UINT32 fadstoplay = 0;
+static int buffull, sectorstore, freeblocks;
+
+// iso9660 utilities
+static void read_new_dir(UINT32 fileno);
+static void make_dir_current(UINT32 fad);
+
+static direntryT curroot; // root entry of current filesystem
+static direntryT *curdir; // current directory
+static int numfiles; // # of entries in current directory
+static int firstfile; // first non-directory file
+
+// HIRQ definitions
+#define CMOK 0x0001 // command ok / ready for new command
+#define DRDY 0x0002 // drive ready
+#define CSCT 0x0004 // sector ready (?)
+#define BFUL 0x0008 // buffer full
+#define PEND 0x0010 // command pending
+#define DCHG 0x0020 // disc change / tray open
+#define ESEL 0x0040 // soft reset, end of blah
+#define EHST 0x0080 //
+#define ECPY 0x0100 //
+#define EFLS 0x0200 // stop execution of cd block filesystem
+#define SCDQ 0x0400 // subcode Q renewal complete
+#define MPED 0x0800 // MPEG
+#define MPCM 0x1000 // MPEG
+#define MPST 0x2000 // MPEG
+
+// CD status (hi byte of CR1) definitions:
+// (these defines are shifted up 8)
+#define CD_STAT_BUSY 0x0000 // status change in progress
+#define CD_STAT_PAUSE 0x0100 // CD block paused (temporary stop)
+#define CD_STAT_STANDBY 0x0200 // CD drive stopped
+#define CD_STAT_PLAY 0x0300 // CD play in progress
+#define CD_STAT_SEEK 0x0400 // drive seeking
+#define CD_STAT_SCAN 0x0500 // drive scanning
+#define CD_STAT_OPEN 0x0600 // tray is open
+#define CD_STAT_NODISC 0x0700 // no disc present
+#define CD_STAT_RETRY 0x0800 // read retry in progress
+#define CD_STAT_ERROR 0x0900 // read data error occured
+#define CD_STAT_FATAL 0x0a00 // fatal error (hard reset required)
+#define CD_STAT_PERI 0x2000 // periodic response if set, else command response
+#define CD_STAT_TRANS 0x4000 // data transfer request if set
+#define CD_STAT_WAIT 0x8000 // waiting for command if set, else executed immediately
+#define CD_STAT_REJECT 0xff00 // ultra-fatal error.
+
+static TIMER_CALLBACK( sector_cb )
+{
+ if (fadstoplay)
+ {
+ cd_playdata();
+ }
+ else
+ {
+ hirqreg |= SCDQ;
+ }
+
+ cd_stat |= CD_STAT_PERI;
+ cr1 = cd_stat;
+ cr2 = 0x4101;
+ cr3 = (cd_curfad>>16)&0xff;
+ cr4 = cd_curfad;
+
+ timer_adjust(sector_timer, ATTOTIME_IN_HZ(150), 0, attotime_zero);
+}
+
+// global functions
+void stvcd_reset(void)
+{
+ INT32 i, j;
+
+ hirqmask = 0xffff;
+ hirqreg = 0xffff;
+ cr1 = 'C';
+ cr2 = ('D'<<8) | 'B';
+ cr3 = ('L'<<8) | 'O';
+ cr4 = ('C'<<8) | 'K';
+ cd_stat = CD_STAT_PAUSE;
+
+ if (curdir != (direntryT *)NULL)
+ free((void *)curdir);
+ curdir = (direntryT *)NULL; // no directory yet
+
+ xfertype = XFERTYPE_INVALID;
+ xfertype32 = XFERTYPE32_INVALID;
+
+ // reset flag vars
+ buffull = sectorstore = 0;
+
+ freeblocks = 200;
+
+ sectlenin = sectlenout = 2048;
+
+ lastbuf = 0xff;
+
+ // reset buffer partitions
+ for (i = 0; i < MAX_FILTERS; i++)
+ {
+ partitions[i].size = -1;
+ partitions[i].numblks = 0;
+
+ for (j = 0; j < MAX_BLOCKS; j++)
+ {
+ partitions[i].blocks[j] = (blockT *)NULL;
+ partitions[i].bnum[j] = 0xff;
+ }
+ }
+
+ // reset blocks
+ for (i = 0; i < MAX_BLOCKS; i++)
+ {
+ blocks[i].size = -1;
+ memset(&blocks[i].data, 0, CD_MAX_SECTOR_DATA);
+ }
+
+ // open device
+ if (cdrom)
+ {
+ cdrom_close(cdrom);
+ cdrom = (cdrom_file *)NULL;
+ }
+
+ #ifdef MESS
+ cdrom = mess_cd_get_cdrom_file_by_number(0);
+ #else
+ cdrom = cdrom_open(get_disk_handle(0));
+ #endif
+
+ if (cdrom)
+ {
+ CDROM_LOG(("Opened CD-ROM successfully, reading root directory\n"))
+ read_new_dir(0xffffff); // read root directory
+ }
+ else
+ {
+ cd_stat = CD_STAT_OPEN;
+ }
+
+ sector_timer = timer_alloc(sector_cb);
+ timer_adjust(sector_timer, ATTOTIME_IN_HZ(150), 0, attotime_zero); // 150 sectors / second = 300kBytes/second
+}
+
+static blockT *cd_alloc_block(UINT8 *blknum)
+{
+ INT32 i;
+
+ // search the 200 available blocks for a free one
+ for (i = 0; i < 200; i++)
+ {
+ if (blocks[i].size == -1)
+ {
+ freeblocks--;
+ if (freeblocks <= 0)
+ {
+ buffull = 1;
+ }
+
+ blocks[i].size = sectlenin;
+ *blknum = i;
+
+ CDROM_LOG(("CD: allocating block %d, size %x\n", i, sectlenin))
+
+ return &blocks[i];
+ }
+ }
+
+ buffull = 1;
+ return (blockT *)NULL;
+}
+
+static void cd_free_block(blockT *blktofree)
+{
+ INT32 i;
+
+ CDROM_LOG(("cd_free_block: %x\n", (UINT32)blktofree))
+
+ for (i = 0; i < 200; i++)
+ {
+ if (&blocks[i] == blktofree)
+ {
+ CDROM_LOG(("CD: freeing block %d\n", i))
+ }
+ }
+
+ blktofree->size = -1;
+ freeblocks++;
+ buffull = 0;
+ hirqreg &= ~BFUL;
+}
+
+static void cd_getsectoroffsetnum(UINT32 bufnum, UINT32 *sectoffs, UINT32 *sectnum)
+{
+ if (*sectoffs == 0xffff)
+ {
+ // last sector
+ CDROM_LOG(("CD: Don't know how to handle offset ffff\n"))
+ }
+ else if (*sectnum == 0xffff)
+ {
+ *sectnum = partitions[bufnum].numblks - *sectoffs;
+ }
+}
+
+static void cd_defragblocks(partitionT *part)
+{
+ UINT32 i, j;
+ blockT *temp;
+ UINT8 temp2;
+
+ for (i = 0; i < (MAX_BLOCKS-1); i++)
+ {
+ for (j = i+1; j < MAX_BLOCKS; j++)
+ {
+ if ((part->blocks[i] == (blockT *)NULL) && (part->blocks[j] != (blockT *)NULL))
+ {
+ temp = part->blocks[i];
+ part->blocks[i] = part->blocks[j];
+ part->blocks[j] = temp;
+
+ temp2 = part->bnum[i];
+ part->bnum[i] = part->bnum[j];
+ part->bnum[j] = temp2;
+ }
+ }
+ }
+}
+
+static UINT16 cd_readWord(UINT32 addr)
+{
+ UINT16 rv;
+
+ switch (addr & 0xffff)
+ {
+ case 0x0008: // read HIRQ register
+ case 0x000a:
+ rv = hirqreg;
+
+ rv &= ~DCHG; // always clear bit 6 (tray open)
+
+ if (buffull) rv |= BFUL; else rv &= ~BFUL;
+ if (sectorstore) rv |= CSCT; else rv &= ~CSCT;
+
+ hirqreg = rv;
+
+// CDROM_LOG(("RW HIRQ: %04x (PC=%x)\n", rv, activecpu_get_pc()))
+
+ return rv;
+ break;
+
+ case 0x000c:
+ case 0x000e:
+ CDROM_LOG(("RW HIRM: %04x\n", hirqmask))
+ return hirqmask;
+ break;
+
+ case 0x0018:
+ case 0x001a:
+// CDROM_LOG(("RW CR1: %04x\n", cr1))
+ return cr1;
+ break;
+
+ case 0x001c:
+ case 0x001e:
+// CDROM_LOG(("RW CR2: %04x\n", cr2))
+ return cr2;
+ break;
+
+ case 0x0020:
+ case 0x0022:
+// CDROM_LOG(("RW CR3: %04x\n", cr3))
+ return cr3;
+ break;
+
+ case 0x0024:
+ case 0x0026:
+// CDROM_LOG(("RW CR4: %04x\n", cr4))
+ return cr4;
+ break;
+
+ case 0x8000:
+ rv = 0xffff;
+ switch (xfertype)
+ {
+ case XFERTYPE_TOC:
+ rv = tocbuf[xfercount]<<8 | tocbuf[xfercount+1];
+ xfercount += 2;
+ xferdnum += 2;
+
+ if (xfercount > 102*4)
+ {
+ xfercount = 0;
+ xfertype = XFERTYPE_INVALID;
+ }
+ break;
+
+ case XFERTYPE_FILEINFO_1:
+ rv = finfbuf[xfercount]<<8 | finfbuf[xfercount+1];
+ xfercount += 2;
+ xferdnum += 2;
+
+ if (xfercount > 6*2)
+ {
+ xfercount = 0;
+ xfertype = XFERTYPE_INVALID;
+ }
+ break;
+
+ default:
+ CDROM_LOG(("STVCD: Unhandled xfer type %d\n", (int)xfertype))
+ rv = 0xffff;
+ break;
+ }
+
+ return rv;
+ break;
+
+ default:
+ CDROM_LOG(("CD: RW %08x (PC=%x)\n", addr, activecpu_get_pc()))
+ return 0xffff;
+ break;
+ }
+
+ return 0xffff;
+}
+
+static UINT32 cd_readLong(UINT32 addr)
+{
+ UINT32 rv = 0;
+
+ switch (addr & 0xffff)
+ {
+ case 0x8000:
+ switch (xfertype32)
+ {
+ case XFERTYPE32_GETSECTOR:
+ case XFERTYPE32_GETDELETESECTOR:
+ // make sure we have sectors left
+ if (xfersect < xfersectnum)
+ {
+ // get next longword
+ rv = transpart->blocks[xfersectpos+xfersect]->data[xferoffs]<<24 |
+ transpart->blocks[xfersectpos+xfersect]->data[xferoffs + 1]<<16 |
+ transpart->blocks[xfersectpos+xfersect]->data[xferoffs + 2]<<8 |
+ transpart->blocks[xfersectpos+xfersect]->data[xferoffs + 3];
+
+ xferdnum += 4;
+ xferoffs += 4;
+
+ // did we run out of sector?
+ if (xferoffs >= transpart->blocks[xfersect]->size)
+ {
+ CDROM_LOG(("CD: finished xfer of block %d of %d\n", xfersect+1, xfersectnum))
+
+ xferoffs = 0;
+ xfersect++;
+ }
+ }
+ else // sectors are done, kill 'em all if we can
+ {
+ if (xfertype32 == XFERTYPE32_GETDELETESECTOR)
+ {
+ INT32 i;
+
+ CDROM_LOG(("Killing sectors in done\n"))
+
+ // deallocate the blocks
+ for (i = xfersectpos; i < xfersectpos+xfersectnum; i++)
+ {
+ cd_free_block(transpart->blocks[i]);
+ transpart->blocks[i] = (blockT *)NULL;
+ transpart->bnum[i] = 0xff;
+ }
+
+ // defrag what's left
+ cd_defragblocks(transpart);
+
+ // clean up our state
+ transpart->size -= xferdnum;
+ transpart->numblks -= xfersectnum;
+
+ xfertype32 = XFERTYPE32_INVALID;
+ }
+ }
+ break;
+
+ default:
+ CDROM_LOG(("CD: unhandled 32-bit transfer type\n"))
+ break;
+ }
+
+ return rv;
+ break;
+
+ default:
+ CDROM_LOG(("CD: RL %08x (PC=%x)\n", addr, activecpu_get_pc()))
+ return 0xffff;
+ break;
+ }
+}
+
+static void cd_writeWord(UINT32 addr, UINT16 data)
+{
+ UINT32 temp;
+
+ switch(addr & 0xffff)
+ {
+ case 0x0008:
+ case 0x000a:
+// CDROM_LOG(("WW HIRQ: %04x & %04x => %04x (PC %x)\n", hirqreg, data, hirqreg & data, activecpu_get_pc()))
+ hirqreg &= data;
+ return;
+ case 0x000c:
+ case 0x000e:
+ CDROM_LOG(("WW HIRM: %04x => %04x\n", hirqmask, data))
+ hirqmask = data;
+ return;
+ case 0x0018:
+ case 0x001a:
+// CDROM_LOG(("WW CR1: %04x\n", data))
+ cr1 = data;
+ cd_stat &= ~CD_STAT_PERI;
+ break;
+ case 0x001c:
+ case 0x001e:
+// CDROM_LOG(("WW CR2: %04x\n", data))
+ cr2 = data;
+ break;
+ case 0x0020:
+ case 0x0022:
+// CDROM_LOG(("WW CR3: %04x\n", data))
+ cr3 = data;
+ break;
+ case 0x0024:
+ case 0x0026:
+// CDROM_LOG(("WW CR4: %04x\n", data))
+ cr4 = data;
+// CDROM_LOG(("CD: command exec %02x %02x %02x %02x %02x (stat %04x)\n", hirqreg, cr1, cr2, cr3, cr4, cd_stat))
+
+ if (!cdrom)
+ {
+ cd_stat = CD_STAT_OPEN;
+ cr1 = cd_stat | 0xff;
+ cr2 = 0xffff;
+ cr3 = 0xffff;
+ cr4 = 0xffff;
+ return;
+ }
+
+ switch (cr1 & 0xff00)
+ {
+ case 0x0000:
+ CDROM_LOG(("CD: Get Status (PC=%x)\n", activecpu_get_pc()))
+
+ // values taken from a US saturn with a disc in and the lid closed
+ hirqreg |= CMOK;
+ cr1 = cd_stat;
+ cr2 = 0x4101;
+ cr3 = 0x100 | (cd_curfad>>16);
+ cr4 = cd_curfad;
+ CDROM_LOG((" = %04x %04x %04x %04x %04x\n", hirqreg, cr1, cr2, cr3, cr4))
+ break;
+
+ case 0x0100:
+ CDROM_LOG(("CD: Get Hardware Info (PC=%x)\n", activecpu_get_pc()))
+ hirqreg |= CMOK;
+ cr1 = cd_stat;
+ cr2 = 0x0201;
+ cr3 = 0x0000;
+ cr4 = 0x0400;
+ break;
+
+ case 0x200: // Get TOC
+ CDROM_LOG(("CD: Get TOC (PC=%x)\n", activecpu_get_pc()))
+ cd_readTOC();
+ cd_stat = CD_STAT_TRANS|CD_STAT_PAUSE;
+ cr2 = 102*2; // TOC length in words (102 entries @ 2 words/4bytes each)
+ cr3 = 0;
+ cr4 = 0;
+ xferdnum = 0;
+ hirqreg |= (CMOK|DRDY);
+ break;
+
+ case 0x0300: // get session info (lower byte = session # to get?)
+ // bios is interested in returns in cr3 and cr4
+ // cr3 should be data track #
+ // cr4 must be > 1 and < 100 or bios gets angry.
+ CDROM_LOG(("CD: Get Session Info (PC=%x)\n", activecpu_get_pc()))
+ cd_readTOC();
+ switch (cr1 & 0xff)
+ {
+ case 0: // get total session info / disc end
+ cr2 = 0;
+ cr3 = 0x0100 | tocbuf[(101*4)+1];
+ cr4 = tocbuf[(101*4)+2]<<8 | tocbuf[(101*4)+3];
+ break;
+
+ case 1: // get total session info / disc start
+ cr2 = 0;
+ cr3 = 0x0100; // sessions in high byte, session start in lower
+ cr4 = 0;
+ break;
+
+ default:
+ mame_printf_error("CD: Unknown request to Get Session Info %x\n", cr1 & 0xff);
+ break;
+ }
+ cd_stat = CD_STAT_PAUSE;
+ cr1 = cd_stat << 8;
+ hirqreg |= (CMOK);
+ break;
+
+ case 0x400: // initialize CD system
+ // CR1 & 1 = reset software
+ // CR1 & 2 = decode RW subcode
+ // CR1 & 4 = don't confirm mode 2 subheader
+ // CR1 & 8 = retry reading mode 2 sectors
+ // CR1 & 10 = force single-speed
+ CDROM_LOG(("CD: Initialize CD system (PC=%x)\n", activecpu_get_pc()))
+ hirqreg |= (CMOK|DRDY|ESEL);
+ cd_stat = CD_STAT_PAUSE;
+ cd_curfad = 150;
+ in_buffer = 0;
+ buffull = 0;
+ cr2 = 0x4101;
+ cr3 = 0x100 | ((cd_curfad>>16)&0xff);
+ cr4 = cd_curfad;
+ break;
+
+ case 0x0600: // end data transfer
+ // returns # of bytes transfered (24 bits) in
+ // low byte of cr1 (MSB) and cr2 (middle byte, LSB)
+ CDROM_LOG(("CD: End data transfer (PC=%x) (%d bytes xfer'd)\n", activecpu_get_pc(), xferdnum))
+
+ // clear the "transfer" flag
+ cd_stat &= ~CD_STAT_TRANS;
+
+ if (xferdnum)
+ {
+ cr1 = (cd_stat) | ((xferdnum>>17) & 0xff);
+ cr2 = (xferdnum>>1)&0xffff;
+ cr3 = 0;
+ cr4 = 0;
+ }
+ else
+ {
+ cr1 = (cd_stat) | (0xff); // is this right?
+ cr2 = 0xffff;
+ cr3 = 0;
+ cr4 = 0;
+ }
+
+ // try to clean up any transfers still in progress
+ switch (xfertype32)
+ {
+ case XFERTYPE32_GETSECTOR:
+ hirqreg |= EHST;
+ break;
+
+ case XFERTYPE32_GETDELETESECTOR:
+ if (transpart->size > 0)
+ {
+ INT32 i;
+
+ xfertype32 = XFERTYPE32_INVALID;
+
+ // deallocate the blocks
+ for (i = xfersectpos; i < xfersectpos+xfersectnum; i++)
+ {
+ cd_free_block(transpart->blocks[i]);
+ transpart->blocks[i] = (blockT *)NULL;
+ transpart->bnum[i] = 0xff;
+ }
+
+ // defrag what's left
+ cd_defragblocks(transpart);
+
+ // clean up our state
+ transpart->size -= xferdnum;
+ transpart->numblks -= xfersectnum;
+
+ if (freeblocks == 200)
+ {
+ onesectorstored = 0;
+ }
+
+ hirqreg |= EHST;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+
+ // hack for the bootloader
+ cd_stat |= CD_STAT_PERI;
+ cr1 = cd_stat;
+
+ // and kick the CD if there's more to read
+ cd_playdata();
+
+ xferdnum = 0;
+ hirqreg |= CMOK;
+
+ CDROM_LOG((" = %04x %04x %04x %04x %04x\n", hirqreg, cr1, cr2, cr3, cr4))
+ break;
+
+ case 0x1000: // Play Disk. FAD is in lowest 7 bits of cr1 and all of cr2.
+ CDROM_LOG(("CD: Play Disk (PC=%x)\n", activecpu_get_pc()))
+ cd_stat = CD_STAT_PLAY; //|0x80; // set "cd-rom" bit?
+ cd_curfad = ((cr1&0xff)<<16) | cr2;
+ fadstoplay = ((cr3&0xff)<<16) | cr4;
+
+ if (cd_curfad & 0x800000)
+ {
+ if (cd_curfad != 0xffffff)
+ {
+ // fad mode
+ cd_curfad &= 0xfffff;
+ fadstoplay &= 0xfffff;
+ }
+ }
+ else
+ {
+ // track mode
+ mame_printf_error("CD: Play Disk track mode, not yet implemented\n");
+ }
+
+ CDROM_LOG(("CD: Play Disk: start %x length %x\n", cd_curfad, fadstoplay))
+
+ cr2 = 0x4101; // ctrl/adr in hi byte, track # in low byte
+ cr3 = (0x100) | ((cd_curfad>>16)&0xff); // index of subcode in hi byte, frame address
+ cr4 = cd_curfad & 0xffff;
+ hirqreg |= (CMOK|DRDY);
+ oddframe = 0;
+ in_buffer = 0;
+
+ playtype = 0;
+
+ // and do the disc I/O
+ // make sure it doesn't come in too early
+ timer_adjust(sector_timer, attotime_never, 0, attotime_never);
+ timer_adjust(sector_timer, ATTOTIME_IN_HZ(150), 0, attotime_zero); // 150 sectors / second = 300kBytes/second
+ break;
+
+ case 0x1100: // disk seek
+ CDROM_LOG(("CD: Disk seek (PC=%x)\n", activecpu_get_pc()))
+ if (cr1 & 0x80)
+ {
+ temp = (cr1&0x7f)<<16; // get FAD to seek to
+ temp |= cr2;
+
+ if (temp == 0xffffff)
+ {
+ cd_stat = CD_STAT_PAUSE;
+ }
+ else
+ {
+ CDROM_LOG(("CD: not clear how to handle FAD seek\n"))
+ cd_curfad = temp;
+ }
+ }
+ else
+ {
+ // is it a valid track?
+ if (cr2 >> 8)
+ {
+ cd_stat = CD_STAT_PAUSE;
+ // (index is cr2 low byte)
+ }
+ else
+ {
+ cd_stat = CD_STAT_STANDBY;
+ cd_curfad = 0xffffffff;
+ }
+ }
+
+
+ hirqreg |= CMOK;
+ cr1 = cd_stat;
+ cr2 = 0x4101;
+ cr3 = (cd_curfad>>16)&0xff;
+ cr4 = cd_curfad;
+ break;
+
+ case 0x3000: // Set CD Device connection
+ {
+ UINT8 parm;
+
+ // get operation
+ parm = cr3>>8;
+
+ CDROM_LOG(("CD: Set CD Device Connection (PC=%x) filter # %x\n", activecpu_get_pc(), parm))
+
+ cddevicenum = parm;
+
+ if (parm == 0xff)
+ {
+ cddevice = (filterT *)NULL;
+ }
+ else
+ {
+ if (parm < 0x24)
+ {
+ cddevice = &filters[(cr3>>8)];
+ }
+ }
+
+ hirqreg |= (CMOK|ESEL);
+ }
+ break;
+
+ case 0x4000: // Set Filter Range
+ // cr1 low + cr2 = FAD0, cr3 low + cr4 = FAD1
+ // cr3 hi = filter num.
+ {
+ UINT8 fnum = (cr3>>8)&0xff;
+
+ CDROM_LOG(("CD: Set Filter Range (PC=%x)\n", activecpu_get_pc()))
+
+ filters[fnum].fad = ((cr1 & 0xff)<<16) | cr2;
+ filters[fnum].range = ((cr3 & 0xff)<<16) | cr4;
+
+ hirqreg |= (CMOK|ESEL|DRDY);
+ cr2 = 0x4101; // ctrl/adr in hi byte, track # in low byte
+ cr3 = 0x0100|((cd_curfad>>16)&0xff);
+ cr4 = (cd_curfad & 0xffff);
+ }
+ break;
+
+ case 0x4200: // Set Filter Subheader conditions
+ {
+ UINT8 fnum = (cr3>>8)&0xff;
+
+ CDROM_LOG(("CD: Set Filter Subheader conditions (PC=%x) %x => chan %x masks %x fid %x vals %x\n", activecpu_get_pc(), fnum, cr1&0xff, cr2, cr3&0xff, cr4))
+
+ filters[fnum].chan = cr1 & 0xff;
+ filters[fnum].smmask = (cr2>>8)&0xff;
+ filters[fnum].cimask = cr2&0xff;
+ filters[fnum].fid = cr3&0xff;
+ filters[fnum].smval = (cr4>>8)&0xff;
+ filters[fnum].cival = cr4&0xff;
+
+ hirqreg |= (CMOK|ESEL);
+ cr2 = 0x4101; // ctrl/adr in hi byte, track # in low byte
+ cr3 = 0x0100|((cd_curfad>>16)&0xff);
+ cr4 = (cd_curfad & 0xffff);
+ }
+ break;
+
+ case 0x4400: // Set Filter Mode
+ {
+ UINT8 fnum = (cr3>>8)&0xff;
+ UINT8 mode = (cr1 & 0xff);
+
+ // initialize filter?
+ if (mode & 0x80)
+ {
+ memset(&filters[fnum], 0, sizeof(filterT));
+ }
+ else
+ {
+ filters[fnum].mode = mode;
+ }
+
+ CDROM_LOG(("CD: Set Filter Mode (PC=%x) filt %x mode %x\n", activecpu_get_pc(), fnum, mode))
+ hirqreg |= (CMOK|ESEL|DRDY);
+ cr2 = 0x4101; // ctrl/adr in hi byte, track # in low byte
+ cr3 = 0x0100|((cd_curfad>>16)&0xff);
+ cr4 = (cd_curfad & 0xffff);
+ }
+ break;
+
+ case 0x4600: // Set Filter Connection
+ {
+ UINT8 fnum = (cr3>>8)&0xff;
+
+ CDROM_LOG(("CD: Set Filter Connection (PC=%x) %x => mode %x parm %04x\n", activecpu_get_pc(), fnum, cr1 & 0xf, cr2))
+
+ // set true condition?
+ if (cr1 & 1)
+ {
+ filters[fnum].condtrue = (cr2>>8)&0xff;
+ }
+ else if (cr1 & 2) // set false condition
+ {
+ filters[fnum].condfalse = cr2&0xff;
+ }
+
+ hirqreg |= (CMOK|ESEL);
+ cr2 = 0x4101; // ctrl/adr in hi byte, track # in low byte
+ cr3 = 0x0100|((cd_curfad>>16)&0xff);
+ cr4 = (cd_curfad & 0xffff);
+ }
+ break;
+
+ case 0x4800: // Reset Selector
+ CDROM_LOG(("CD: Reset Selector (PC=%x)\n", activecpu_get_pc()))
+ hirqreg |= (CMOK|ESEL|DRDY);
+ cr2 = 0x4101; // ctrl/adr in hi byte, track # in low byte
+ cr3 = 0x0100|((cd_curfad>>16)&0xff);
+ cr4 = (cd_curfad & 0xffff);
+ break;
+
+ case 0x5000: // get Buffer Size
+ cr1 = cd_stat;
+ cr2 = freeblocks;
+ if (cr2 > 200) cr2 = 200; // ???
+
+ cr3 = 0x1800;
+ cr4 = 200;
+ CDROM_LOG(("CD: Get Buffer Size = %d (PC=%x)\n", cr2, activecpu_get_pc()))
+ hirqreg |= (CMOK|ESEL|DRDY); // DRDY is probably wrong
+ break;
+
+ case 0x5100: // get # sectors used in a buffer
+ {
+ UINT32 bufnum = cr3>>8;
+
+ // is the partition empty?
+ if (partitions[bufnum].size == -1)
+ {
+ cr4 = 0;
+ }
+ else
+ {
+ cr4 = partitions[bufnum].numblks;
+ }
+
+ CDROM_LOG(("CD: Get Sector Number (PC=%x) (bufno %d) = %d blocks\n", activecpu_get_pc(), bufnum, cr4))
+
+ cr2 = 0;
+ cr3 = 0;
+ hirqreg |= (CMOK|DRDY);
+ }
+ break;
+
+ case 0x5200: // calculate acutal size
+ {
+ UINT32 bufnum = cr3>>8;
+ UINT32 sectoffs = cr2;
+ UINT32 numsect = cr4;
+
+ CDROM_LOG(("CD: Calculate actual size: buf %x offs %x numsect %x (PC=%x)\n", bufnum, sectoffs, numsect, activecpu_get_pc()))
+
+ calcsize = 0;
+ if (partitions[bufnum].size != -1)
+ {
+ INT32 i;
+
+ for (i = 0; i < numsect; i++)
+ {
+ if (partitions[bufnum].blocks[sectoffs+i])
+ {
+ calcsize += (partitions[bufnum].blocks[sectoffs+i]->size / 2);
+ }
+ }
+ }
+
+ hirqreg |= (CMOK|ESEL);
+ cr1 = cd_stat;
+ cr2 = 0x4101; // CTRL/track
+ cr3 = (cd_curfad>>16)&0xff;
+ cr4 = (cd_curfad & 0xffff);
+ }
+ break;
+
+ case 0x5300: // get actual block size
+ CDROM_LOG(("CD: Get actual block size (PC=%x)\n", activecpu_get_pc()))
+ hirqreg |= (CMOK|ESEL);
+ cr1 = cd_stat | ((calcsize>>16)&0xff);
+ cr2 = (calcsize & 0xffff);
+ cr3 = 0;
+ cr4 = 0;
+ break;
+
+ case 0x6000: // set sector length
+ CDROM_LOG(("CD: Set sector length (PC=%x)\n", activecpu_get_pc()))
+ hirqreg |= (CMOK|ESEL|EFLS|SCDQ|DRDY);
+
+ switch (cr1 & 0xff)
+ {
+ case 0:
+ sectlenin = 2048;
+ break;
+ case 1:
+ sectlenin = 2336;
+ break;
+ case 2:
+ sectlenin = 2340;
+ break;
+ case 3:
+ sectlenin = 2352;
+ break;
+ }
+
+ switch ((cr2>>8) & 0xff)
+ {
+ case 0:
+ sectlenout = 2048;
+ break;
+ case 1:
+ sectlenout = 2336;
+ break;
+ case 2:
+ sectlenout = 2340;
+ break;
+ case 3:
+ sectlenout = 2352;
+ break;
+ }
+ break;
+
+ case 0x6100: // get sector data
+ {
+ UINT32 sectnum = cr4;
+ UINT32 sectofs = cr2;
+ UINT32 bufnum = cr3>>8;
+
+ CDROM_LOG(("CD: Get sector data (PC=%x) (SN %d SO %d BN %d)\n", activecpu_get_pc(), sectnum, sectofs, bufnum))
+
+ if (bufnum >= MAX_FILTERS)
+ {
+ CDROM_LOG(("CD: invalid buffer number\n"));
+ cd_stat = 0xff; // ERROR
+ hirqreg |= (CMOK|EHST);
+ return;
+ }
+
+ if (partitions[bufnum].numblks == 0)
+ {
+ CDROM_LOG(("CD: buffer is empty\n"))
+ cd_stat = 0xff; // ERROR
+ hirqreg |= (CMOK|EHST);
+ return;
+ }
+
+ cd_getsectoroffsetnum(bufnum, &sectofs, &sectnum);
+
+ xfertype32 = XFERTYPE32_GETSECTOR;
+ xferoffs = 0;
+ xfersect = 0;
+ xferdnum = 0;
+ xfersectpos = sectofs;
+ xfersectnum = sectnum;
+ transpart = &partitions[bufnum];
+
+ cd_stat |= CD_STAT_TRANS;
+ hirqreg |= (CMOK|EHST|DRDY);
+ }
+ break;
+
+ case 0x6200: // delete sector data
+ {
+ UINT32 sectnum = cr4;
+ UINT32 sectofs = cr2;
+ UINT32 bufnum = cr3>>8;
+ INT32 i;
+
+ CDROM_LOG(("CD: Delete sector data (PC=%x) (SN %d SO %d BN %d)\n", activecpu_get_pc(), sectnum, sectofs, bufnum))
+
+ if (bufnum >= MAX_FILTERS)
+ {
+ CDROM_LOG(("CD: invalid buffer number\n"))
+ cd_stat = 0xff; // ERROR
+ hirqreg |= (CMOK|EHST);
+ return;
+ }
+
+ if (partitions[bufnum].numblks == 0)
+ {
+ CDROM_LOG(("CD: buffer is empty\n"))
+ cd_stat = 0xff; // ERROR
+ hirqreg |= (CMOK|EHST);
+ return;
+ }
+
+ cd_getsectoroffsetnum(bufnum, &sectofs, &sectnum);
+
+ for (i = sectofs; i < (sectofs + sectnum); i++)
+ {
+ partitions[bufnum].size -= partitions[bufnum].blocks[i]->size;
+ cd_free_block(partitions[bufnum].blocks[i]);
+ partitions[bufnum].blocks[i] = (blockT *)NULL;
+ partitions[bufnum].bnum[i] = 0xff;
+ }
+
+ cd_defragblocks(&partitions[bufnum]);
+
+ partitions[bufnum].numblks -= sectnum;
+
+ cd_stat &= ~CD_STAT_TRANS;
+ hirqreg |= (CMOK|EHST);
+ }
+ break;
+
+ case 0x6300: // get then delete sector data
+ {
+ UINT32 sectnum = cr4;
+ UINT32 sectofs = cr2;
+ UINT32 bufnum = cr3>>8;
+
+ CDROM_LOG(("CD: Get and delete sector data (PC=%x) (SN %d SO %d BN %d)\n", activecpu_get_pc(), sectnum, sectofs, bufnum))
+
+ if (bufnum >= MAX_FILTERS)
+ {
+ CDROM_LOG(("CD: invalid buffer number\n"))
+ cd_stat = 0xff; // ERROR
+ hirqreg |= (CMOK|EHST);
+ return;
+ }
+
+ if (partitions[bufnum].numblks == 0)
+ {
+ CDROM_LOG(("CD: buffer is empty\n"))
+ cd_stat = 0xff; // ERROR
+ hirqreg |= (CMOK|EHST);
+ return;
+ }
+
+ cd_getsectoroffsetnum(bufnum, &sectofs, &sectnum);
+
+ xfertype32 = XFERTYPE32_GETDELETESECTOR;
+ xferoffs = 0;
+ xfersect = 0;
+ xferdnum = 0;
+ xfersectpos = sectofs;
+ xfersectnum = sectnum;
+ transpart = &partitions[bufnum];
+
+ cd_stat &= ~CD_STAT_TRANS;
+ hirqreg |= (CMOK|EHST|DRDY);
+ }
+ break;
+
+ case 0x6700: // get copy error
+ CDROM_LOG(("CD: Get copy error (PC=%x)\n", activecpu_get_pc()))
+ hirqreg |= (CMOK|ESEL|EFLS|SCDQ|DRDY);
+ break;
+
+ case 0x7000: // change directory
+ CDROM_LOG(("CD: Change Directory (PC=%x)\n", activecpu_get_pc()))
+ hirqreg |= (CMOK|EFLS);
+
+ temp = (cr3&0xff)<<16;
+ temp |= cr4;
+ read_new_dir(temp);
+ break;
+
+ case 0x7100: // Read directory entry
+ CDROM_LOG(("CD: Read Directory Entry (PC=%x)\n", activecpu_get_pc()))
+ hirqreg |= (CMOK|DRDY);
+
+ temp = (cr3&0xff)<<16;
+ temp |= cr4;
+ cr2 = 0x4101; // CTRL/track
+ cr3 = (curdir[temp].firstfad>>16)&0xff;
+ cr4 = (curdir[temp].firstfad&0xffff);
+ break;
+
+ case 0x7200: // Get file system scope
+ CDROM_LOG(("CD: Get file system scope(PC=%x)\n", activecpu_get_pc()))
+ hirqreg |= (CMOK|DRDY);
+ cr2 = numfiles; // # of files in directory
+ cr3 = 0x0100; // report directory held
+ cr4 = firstfile; // first file id
+ break;
+
+ case 0x7300: // Get File Info
+ CDROM_LOG(("CD: Get File Info (PC=%x)\n", activecpu_get_pc()))
+ cd_stat |= CD_STAT_TRANS;
+ cd_stat &= 0xff00; // clear top byte of return value
+ hirqreg |= (CMOK|DRDY);
+
+ temp = (cr3&0xff)<<16;
+ temp |= cr4;
+
+ if (temp == 0xffffff) // special
+ {
+ xfertype = XFERTYPE_FILEINFO_254;
+ xfercount = 0;
+
+ cr1 = cd_stat;
+ cr2 = 0x5f4;
+ cr3 = 0;
+ cr4 = 0;
+ }
+ else
+ {
+ cr1 = cd_stat;
+ cr2 = 6; // 6 words for single file
+ // first 4 bytes = FAD address
+ // second 4 bytes = length
+ // last 4 bytes:
+ // - unit size
+ // - gap size
+ // - file #
+ // attributes flags
+
+ cr3 = cr4 = 0;
+
+ // first 4 bytes = FAD
+ finfbuf[0] = (curdir[temp].firstfad>>24)&0xff;
+ finfbuf[1] = (curdir[temp].firstfad>>16)&0xff;
+ finfbuf[2] = (curdir[temp].firstfad>>8)&0xff;
+ finfbuf[3] = (curdir[temp].firstfad&0xff);
+ // second 4 bytes = length of file
+ finfbuf[4] = (curdir[temp].length>>24)&0xff;
+ finfbuf[5] = (curdir[temp].length>>16)&0xff;
+ finfbuf[6] = (curdir[temp].length>>8)&0xff;
+ finfbuf[7] = (curdir[temp].length&0xff);
+ finfbuf[8] = 0x00;
+ finfbuf[9] = 0x00;
+ finfbuf[10] = temp;
+ finfbuf[11] = curdir[temp].flags;
+
+ xfertype = XFERTYPE_FILEINFO_1;
+ xfercount = 0;
+ }
+ CDROM_LOG((" = %04x %04x %04x %04x %04x\n", hirqreg, cr1, cr2, cr3, cr4))
+ break;
+
+ case 0x7400: // Read File
+ CDROM_LOG(("CD: Read File (PC=%x)\n", activecpu_get_pc()))
+ temp = (cr3&0xff)<<16;
+ temp |= cr4;
+
+ cd_stat = CD_STAT_PLAY|0x80; // set "cd-rom" bit
+ cr2 = 0x4101; // CTRL/track
+ cr3 = (curdir[temp].firstfad>>16)&0xff;
+ cr4 = (curdir[temp].firstfad&0xffff);
+
+ cd_curfad = curdir[temp].firstfad;
+ if (curdir[temp].length / 2048)
+ {
+ fadstoplay = curdir[temp].length/2048;
+ fadstoplay++;
+ }
+ else
+ {
+ fadstoplay = curdir[temp].length/2048;
+ }
+
+ hirqreg |= (CMOK|DRDY);
+ oddframe = 0;
+ in_buffer = 0;
+
+ playtype = 1;
+
+ // and do the disc I/O
+// timer_adjust(sector_timer, ATTOTIME_IN_HZ(150), 0, attotime_zero); // 150 sectors / second = 300kBytes/second
+ break;
+
+ case 0x7500:
+ CDROM_LOG(("CD: Abort File (PC=%x)\n", activecpu_get_pc()))
+ // bios expects "2bc" mask to work against this
+ hirqreg |= (CMOK|EFLS|EHST|ESEL|DCHG|PEND|BFUL|CSCT|DRDY);
+ cd_stat = CD_STAT_PERI|CD_STAT_PAUSE; // force to pause
+ break;
+
+ case 0xe000: // appears to be copy protection check. needs only to return OK.
+ CDROM_LOG(("CD: Verify copy protection (PC=%x)\n", activecpu_get_pc()))
+ cd_stat = CD_STAT_PAUSE;
+ cr1 = cd_stat; // necessary to pass
+ cr2 = 0x4;
+// hirqreg |= (CMOK|EFLS|CSCT);
+ sectorstore = 1;
+ hirqreg = 0xfc5;
+ break;
+
+ case 0xe100: // get disc region
+ CDROM_LOG(("CD: Get disc region (PC=%x)\n", activecpu_get_pc()))
+ cd_stat = CD_STAT_PAUSE;
+ cr1 = cd_stat; // necessary to pass
+ cr2 = 0x4; // (must return this value to pass bios checks)
+ hirqreg |= (CMOK);
+ break;
+
+ default:
+ CDROM_LOG(("CD: Unknown command %04x (PC=%x)\n", cr1, activecpu_get_pc()))
+ hirqreg |= (CMOK);
+ break;
+ }
+// CDROM_LOG(("ret: %04x %04x %04x %04x %04x\n", hirqreg, cr1, cr2, cr3, cr4))
+ break;
+ default:
+ CDROM_LOG(("CD: WW %08x %04x (PC=%x)\n", addr, data, activecpu_get_pc()))
+ break;
+ }
+}
+
+READ32_HANDLER( stvcd_r )
+{
+ UINT32 rv = 0;
+
+ offset <<= 2;
+
+ switch (offset)
+ {
+ case 0x90008:
+ case 0x9000a:
+ case 0x9000c:
+ case 0x9000e:
+ case 0x90018:
+ case 0x9001a:
+ case 0x9001c:
+ case 0x9001e:
+ case 0x90020:
+ case 0x90022:
+ case 0x90024:
+ case 0x90026:
+ rv = cd_readWord(offset);
+ return rv<<16;
+ break;
+
+ case 0x98000:
+ case 0x18000:
+ if (mem_mask == 0)
+ {
+ rv = cd_readLong(offset);
+ }
+ else if (mem_mask == 0x0000ffff)
+ {
+ rv = cd_readWord(offset)<<16;
+ }
+ else if (mem_mask == 0xffff0000)
+ {
+ rv = cd_readWord(offset);
+ }
+ else
+ {
+ mame_printf_error("CD: Unknown data buffer read @ mask = %08x\n", mem_mask);
+ }
+
+ break;
+
+ default:
+ CDROM_LOG(("Unknown CD read @ %x\n", offset))
+ break;
+ }
+
+ return rv;
+}
+
+WRITE32_HANDLER( stvcd_w )
+{
+ offset <<= 2;
+
+ switch (offset)
+ {
+ case 0x90008:
+ case 0x9000a:
+ case 0x9000c:
+ case 0x9000e:
+ case 0x90018:
+ case 0x9001a:
+ case 0x9001c:
+ case 0x9001e:
+ case 0x90020:
+ case 0x90022:
+ case 0x90024:
+ case 0x90026:
+ cd_writeWord(offset, data>>16);
+ break;
+
+ default:
+ CDROM_LOG(("Unknown CD write %x @ %x\n", data, offset))
+ break;
+ }
+}
+
+// iso9660 parsing
+static void read_new_dir(UINT32 fileno)
+{
+ int foundpd, i;
+ UINT32 cfad, dirfad;
+ UINT8 sect[2048];
+
+ if (fileno == 0xffffff)
+ {
+ cfad = 166; // first sector of directory as per iso9660 specs
+
+ foundpd = 0; // search for primary vol. desc
+ while ((!foundpd) && (cfad < 200))
+ {
+ memset(sect, 0, 2048);
+ cd_readblock(cfad++, sect);
+
+ if ((sect[1] == 'C') && (sect[2] == 'D') && (sect[3] == '0') && (sect[4] == '0') && (sect[5] == '1'))
+ {
+ switch (sect[0])
+ {
+ case 0: // boot record
+ break;
+
+ case 1: // primary vol. desc
+ foundpd = 1;
+ break;
+
+ case 2: // secondary vol desc
+ break;
+
+ case 3: // vol. section descriptor
+ break;
+
+ case 0xff:
+ cfad = 200;
+ break;
+ }
+ }
+ }
+
+ // got primary vol. desc.
+ if (foundpd)
+ {
+ dirfad = sect[140] | (sect[141]<<8) | (sect[142]<<16) | (sect[143]<<24);
+ dirfad += 150;
+
+ // parse root entry
+ curroot.firstfad = sect[158] | (sect[159]<<8) | (sect[160]<<16) | (sect[161]<<24);
+ curroot.firstfad += 150;
+ curroot.length = sect[166] | (sect[167]<<8) | (sect[168]<<16) | (sect[169]<<24);
+ curroot.flags = sect[181];
+ for (i = 0; i < sect[188]; i++)
+ {
+ curroot.name[i] = sect[189+i];
+ }
+ curroot.name[i] = '\0'; // terminate
+
+ // easy to fix, but make sure we *need* to first
+ if (curroot.length > 14336)
+ {
+ mame_printf_error("ERROR: root directory too big (%d)\n", curroot.length);
+ }
+
+ // done with all that, read the root directory now
+ make_dir_current(curroot.firstfad);
+ }
+ }
+ else
+ {
+ if (curdir[fileno].length > 14336)
+ {
+ mame_printf_error("ERROR: new directory too big (%d)!\n", curdir[fileno].length);
+ }
+ make_dir_current(curdir[fileno].firstfad);
+ }
+}
+
+// makes the directory pointed to by FAD current
+static void make_dir_current(UINT32 fad)
+{
+ int i;
+ UINT32 nextent, numentries;
+ UINT8 sect[14336];
+ direntryT *curentry;
+
+ memset(sect, 0, 14336);
+ for (i = 0; i < (curroot.length/2048); i++)
+ {
+ cd_readblock(fad+i, &sect[2048*i]);
+ }
+
+ nextent = 0;
+ numentries = 0;
+ while (nextent < 14336)
+ {
+ if (sect[nextent])
+ {
+ nextent += sect[nextent];
+ numentries++;
+ }
+ else
+ {
+ nextent = 14336;
+ }
+ }
+
+ if (curdir != (direntryT *)NULL)
+ {
+ free((void *)curdir);
+ }
+
+ curdir = (direntryT *)malloc_or_die(sizeof(direntryT)*numentries);
+ curentry = curdir;
+ numfiles = numentries;
+
+ nextent = 0;
+ while (numentries)
+ {
+ curentry->firstfad = sect[nextent+2] | (sect[nextent+3]<<8) | (sect[nextent+4]<<16) | (sect[nextent+5]<<24);
+ curentry->firstfad += 150;
+ curentry->length = sect[nextent+10] | (sect[nextent+11]<<8) | (sect[nextent+12]<<16) | (sect[nextent+13]<<24);
+ curentry->flags = sect[nextent+25];
+ for (i = 0; i < sect[nextent+32]; i++)
+ {
+ curentry->name[i] = sect[nextent+33+i];
+ }
+ curentry->name[i] = '\0'; // terminate
+
+ nextent += sect[nextent];
+ curentry++;
+ numentries--;
+ }
+
+ for (i = 0; i < numfiles; i++)
+ {
+ if (!(curdir[i].flags & 0x02))
+ {
+ firstfile = i;
+ i = numfiles;
+ }
+ }
+}
+
+static void cd_readTOC(void)
+{
+ int i, ntrks, toclen, tocptr, fad;
+
+ xfertype = XFERTYPE_TOC;
+ xfercount = 0;
+
+ if (cdrom)
+ {
+ ntrks = cdrom_get_last_track(cdrom);
+ }
+ else
+ {
+ ntrks = 0;
+ }
+
+ toclen = (4 * ntrks); // toclen header entry
+
+ // data format for Saturn TOC:
+ // no header.
+ // 4 bytes per track
+ // top nibble of first byte is CTRL info
+ // low nibble is ADR
+ // next 3 bytes are FAD address (LBA + 150)
+ // there are always 99 track entries (0-98)
+ // unused tracks are ffffffff.
+ // entries 99-101 are metadata
+
+ tocptr = 0; // starting point of toc entries
+
+ for (i = 0; i < ntrks; i++)
+ {
+ if (cdrom)
+ {
+ tocbuf[tocptr] = cdrom_get_adr_control(cdrom, i)<<4 | 0x01;
+ }
+
+ if (cdrom)
+ {
+ fad = cdrom_get_track_start(cdrom, i) + 150;
+ }
+ else
+ {
+ fad = 150;
+ }
+
+ tocbuf[tocptr+1] = (fad>>16)&0xff;
+ tocbuf[tocptr+2] = (fad>>8)&0xff;
+ tocbuf[tocptr+3] = fad&0xff;
+
+ tocptr += 4;
+ }
+
+ // fill in the rest
+ for ( ; i < 99; i++)
+ {
+ tocbuf[tocptr] = 0xff;
+ tocbuf[tocptr+1] = 0xff;
+ tocbuf[tocptr+2] = 0xff;
+ tocbuf[tocptr+3] = 0xff;
+
+ tocptr += 4;
+ }
+
+ // tracks 99-101 are special metadata
+ // $$$FIXME: what to do with the address info for these?
+ tocptr = 99 * 4;
+ tocbuf[tocptr] = tocbuf[0]; // get ctrl/adr from first track
+ tocbuf[tocptr+1] = 1; // first track's track #
+ tocbuf[tocptr+2] = 0;
+ tocbuf[tocptr+3] = 0;
+
+ tocbuf[tocptr+4] = tocbuf[(ntrks-1)*4]; // ditto for last track
+ tocbuf[tocptr+5] = ntrks; // last track's track #
+ tocbuf[tocptr+6] = 0;
+ tocbuf[tocptr+7] = 0;
+
+ // get total disc length (start of lead-out)
+ fad = cdrom_get_track_start(cdrom, 0xaa) + 150;
+
+ tocbuf[tocptr+8] = tocbuf[0];
+ tocbuf[tocptr+9] = (fad>>16)&0xff;
+ tocbuf[tocptr+10] = (fad>>8)&0xff;
+ tocbuf[tocptr+11] = fad&0xff;
+}
+
+static partitionT *cd_filterdata(filterT *flt, int trktype)
+{
+ int match = 1, keepgoing = 2;
+ partitionT *filterprt = (partitionT *)NULL;
+
+ CDROM_LOG(("cd_filterdata, trktype %d\n", trktype))
+
+ // loop on the filters
+ do
+ {
+ if ((trktype != CD_TRACK_AUDIO) && (curblock.data[15] == 2))
+ {
+ if (flt->mode & 1) // file number
+ {
+ if (curblock.fnum != flt->fid)
+ {
+ logerror("fnum reject\n");
+ match = 0;
+ }
+ }
+
+ if (flt->mode & 2) // channel number
+ {
+ mame_printf_error("STVCD: unimplemented channel number match\n");
+ }
+
+ if (flt->mode & 4) // sub mode
+ {
+ mame_printf_error("STVCD: unimplemented sub mode match\n");
+ }
+
+ if (flt->mode & 8) // coding information
+ {
+ mame_printf_error("STVCD: unimplemented coding information match\n");
+ }
+
+ if (flt->mode & 0x10) // reverse subheader conditions
+ {
+ match ^= 1;
+ }
+ }
+
+ // FAD range check?
+ if (flt->mode & 0x40)
+ {
+ if ((cd_curfad < flt->fad) || (cd_curfad > (flt->fad + flt->range)))
+ {
+ logerror("curfad reject\n");
+ match = 0;
+ }
+ }
+
+ if (match)
+ {
+ lastbuf = flt->condtrue;
+ filterprt = &partitions[lastbuf];
+ // we're done
+ keepgoing = 0;
+ }
+ else
+ {
+ lastbuf = flt->condfalse;
+
+ // reject sector if no match on either connector
+ if ((lastbuf == 0xff) || (keepgoing < 2))
+ {
+ return (partitionT *)NULL;
+ }
+
+ // try again using the filter that was on the "false" connector
+ flt = &filters[lastbuf];
+
+ // and exit if we fail
+ keepgoing--;
+ }
+ } while (keepgoing);
+
+ // try to allocate a block
+ filterprt->blocks[filterprt->numblks] = cd_alloc_block(&filterprt->bnum[filterprt->numblks]);
+
+ // did the allocation succeed?
+ if (filterprt->blocks[filterprt->numblks] == (blockT *)NULL)
+ {
+ return (partitionT *)NULL;
+ }
+
+ // copy working block to the newly allocated one
+ memcpy(filterprt->blocks[filterprt->numblks], &curblock, sizeof(blockT));
+
+ // and massage the data format a bit
+ switch (curblock.size)
+ {
+ case 2048: // user data
+ if (curblock.data[15] == 2)
+ {
+ // mode 2
+ memcpy(&filterprt->blocks[filterprt->numblks]->data[0], &curblock.data[24], curblock.size);
+ }
+ else
+ {
+ // mode 1
+ memcpy(&filterprt->blocks[filterprt->numblks]->data[0], &curblock.data[16], curblock.size);
+ }
+ break;
+
+ case 2324: // Mode 2 Form 2 data
+ memcpy(&filterprt->blocks[filterprt->numblks]->data[0], &curblock.data[24], curblock.size);
+ break;
+
+ case 2336: // Mode 2 Form 2 skip sync/header
+ memcpy(&filterprt->blocks[filterprt->numblks]->data[0], &curblock.data[16], curblock.size);
+ break;
+
+ case 2340: // Mode 2 Form 2 skip sync only
+ memcpy(&filterprt->blocks[filterprt->numblks]->data[0], &curblock.data[12], curblock.size);
+ break;
+
+ case 2352: // want all data, it's already done, so don't do it again :)
+ break;
+ }
+
+ // update the status of the partition
+ if (filterprt->size == -1)
+ {
+ filterprt->size = 0;
+ }
+ filterprt->size += filterprt->blocks[filterprt->numblks]->size;
+ filterprt->numblks++;
+
+ return filterprt;
+}
+
+// read a single sector off the CD, applying the current filter(s) as necessary
+static partitionT *cd_read_filtered_sector(INT32 fad)
+{
+ int trktype;
+
+ if ((cddevice != NULL) && (!buffull))
+ {
+ // find out the track's type
+ trktype = cdrom_get_track_type(cdrom, cdrom_get_track(cdrom, fad-150));
+
+ // now get a raw 2352 byte sector
+ cdrom_read_data(cdrom, fad-150, curblock.data, CD_TRACK_RAW_DONTCARE);
+ cr3 = 0x100 | (fad>>16); // update cr3/4 with the current fad
+ cr4 = fad;
+ curblock.size = sectlenin;
+ curblock.FAD = fad;
+
+ // if track is Mode 2, get the subheader values
+ if ((trktype != CD_TRACK_AUDIO) && (curblock.data[15] == 2))
+ {
+ curblock.chan = curblock.data[17];
+ curblock.fnum = curblock.data[16];
+ curblock.subm = curblock.data[18];
+ curblock.cinf = curblock.data[19];
+
+ // if it's Form 2, the length is actually 2324 bytes
+ if (curblock.subm & 0x20)
+ {
+ curblock.size = 2324;
+ }
+ }
+
+ return cd_filterdata(cddevice, trktype);
+ }
+
+ return (partitionT *)NULL;
+}
+
+// loads in data set up by a CD-block PLAY command
+static void cd_playdata(void)
+{
+ if ((cd_stat & 0x0f00) == CD_STAT_PLAY)
+ {
+ if (fadstoplay)
+ {
+ logerror("STVCD: Reading FAD %d\n", cd_curfad);
+
+ if (cdrom)
+ {
+ partitionT *playpart;
+
+ playpart = cd_read_filtered_sector(cd_curfad);
+
+ cd_curfad++;
+ fadstoplay--;
+
+ hirqreg |= CSCT;
+
+ if (!fadstoplay)
+ {
+ CDROM_LOG(("cd_playdata: playback ended\n"))
+ cd_stat = CD_STAT_PAUSE;
+
+ hirqreg |= PEND;
+
+ if (playtype == 1)
+ {
+ CDROM_LOG(("cd_playdata: setting EFLS\n"))
+ hirqreg |= EFLS;
+ }
+ }
+ }
+ }
+ }
+}
+
+// loads a single sector off the CD, anywhere from FAD 150 on up
+static void cd_readblock(UINT32 fad, UINT8 *dat)
+{
+ if (cdrom)
+ {
+ cdrom_read_data(cdrom, fad-150, dat, CD_TRACK_MODE1);
+ }
+}
+
+