/***************************************************************************
machine/stvcd.c - Sega Saturn and ST-V CD-ROM handling
Another tilt at the windmill in 2011 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 "emu.h"
#include "imagedev/chd_cd.h"
#include "cdrom.h"
#include "stvcd.h"
#include "sound/cdda.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)
#define MAX_DIR_SIZE (128*1024)
#define CD_SPEED 75*1 /* TODO: should be x2 */
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 timer_device *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;
static int cur_track;
// iso9660 utilities
static void read_new_dir(running_machine &machine, UINT32 fileno);
static void make_dir_current(running_machine &machine, 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.
TIMER_DEVICE_CALLBACK( stv_sector_cb )
{
if (fadstoplay)
{
cd_playdata();
}
else
{
hirqreg |= SCDQ;
}
cd_stat |= CD_STAT_PERI;
cr1 = cd_stat;
if (cur_track == 0xff)
{
cr2 = 0xffff;
}
else
{
cr2 = cdrom_get_adr_control(cdrom, cur_track)<<8 | cur_track;
}
cr3 = (cd_curfad>>16)&0xff;
cr4 = cd_curfad;
timer.adjust(attotime::from_hz(CD_SPEED));
}
// global functions
void stvcd_reset(running_machine &machine)
{
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;
cur_track = 0xff;
if (curdir != (direntryT *)NULL)
auto_free(machine, 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 = cd_get_cdrom_file(machine.device( "cdrom" ));
#else
cdrom = cdrom_open(get_disk_handle(machine, "cdrom"));
#endif
cdda_set_cdrom( machine.device("cdda"), cdrom );
if (cdrom)
{
CDROM_LOG(("Opened CD-ROM successfully, reading root directory\n"))
read_new_dir(machine, 0xffffff); // read root directory
}
else
{
cd_stat = CD_STAT_OPEN;
}
sector_timer = machine.device<timer_device>("sector_timer");
sector_timer->adjust(attotime::from_hz(CD_SPEED)); // 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)(FPTR)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\n", rv))
return rv;
case 0x000c:
case 0x000e:
CDROM_LOG(("RW HIRM: %04x\n", hirqmask))
return hirqmask;
case 0x0018:
case 0x001a:
// CDROM_LOG(("RW CR1: %04x\n", cr1))
return cr1;
case 0x001c:
case 0x001e:
// CDROM_LOG(("RW CR2: %04x\n", cr2))
return cr2;
case 0x0020:
case 0x0022:
// CDROM_LOG(("RW CR3: %04x\n", cr3))
return cr3;
case 0x0024:
case 0x0026:
// CDROM_LOG(("RW CR4: %04x\n", cr4))
return cr4;
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;
case XFERTYPE_FILEINFO_254:
CDROM_LOG(("STVCD: Unhandled xfer type 254\n"))
break;
default:
CDROM_LOG(("STVCD: Unhandled xfer type %d\n", (int)xfertype))
rv = 0;
break;
}
return rv;
default:
CDROM_LOG(("CD: RW %08x\n", addr))
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;
default:
CDROM_LOG(("CD: RL %08x\n", addr))
return 0xffff;
}
}
static void cd_writeWord(running_machine &machine, UINT32 addr, UINT16 data)
{
UINT32 temp;
switch(addr & 0xffff)
{
case 0x0008:
case 0x000a:
// CDROM_LOG(("%s:WW HIRQ: %04x & %04x => %04x\n", machine.describe_context(), hirqreg, data, hirqreg & data))
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;
if(cr1 != 0 && 0)
printf("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(("%s:CD: Get Status\n", machine.describe_context()))
hirqreg |= CMOK;
cr1 = cd_stat;
if (cur_track == 0xff)
{
cr2 = 0xffff;
}
else
{
cr2 = cdrom_get_adr_control(cdrom, cur_track)<<8 | cur_track;
}
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(("%s:CD: Get Hardware Info\n", machine.describe_context()))
hirqreg |= CMOK;
cr1 = cd_stat;
cr2 = 0x0201;
cr3 = 0x0000;
cr4 = 0x0400;
break;
case 0x200: // Get TOC
CDROM_LOG(("%s:CD: Get TOC\n", machine.describe_context()))
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(("%s:CD: Get Session Info\n", machine.describe_context()))
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;
cr2 = 0;
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(("%s:CD: Initialize CD system\n", machine.describe_context()))
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(("%s:CD: End data transfer (%d bytes xfer'd)\n", machine.describe_context(), xferdnum))
// clear the "transfer" flag
cd_stat &= ~CD_STAT_TRANS;
// hack for the bootloader (TODO: Falcom Classics doesn't want this!)
cd_stat |= CD_STAT_PERI;
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;
}
// 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 Disc. FAD is in lowest 7 bits of cr1 and all of cr2.
CDROM_LOG(("%s:CD: Play Disc\n", machine.describe_context()))
cd_stat = CD_STAT_PLAY;
if (!(cr3 & 0x8000)) // preserve current position if bit 7 set
{
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;
}
printf("fad mode\n");
cur_track = cdrom_get_track(cdrom, cd_curfad-150);
}
else
{
// track mode
cur_track = cd_curfad>>8;
printf("track mode %d\n",cur_track);
cd_curfad = cdrom_get_track_start(cdrom, cur_track-1);
fadstoplay = cdrom_get_track_start(cdrom, cur_track) - cd_curfad;
}
}
else // play until the end of the disc
{
fadstoplay = cdrom_get_track_start(cdrom, 0xaa) + 150;
printf("track mode %08x %08x\n",cd_curfad,fadstoplay);
}
CDROM_LOG(("CD: Play Disc: start %x length %x\n", cd_curfad, fadstoplay))
cr2 = cdrom_get_adr_control(cdrom, cur_track)<<8 | cur_track;
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
if (cdrom_get_track_type(cdrom, cur_track-1) == CD_TRACK_AUDIO)
{
cdda_pause_audio( machine.device( "cdda" ), 0 );
cdda_start_audio( machine.device( "cdda" ), cd_curfad, fadstoplay );
}
else
{
sector_timer->reset();
sector_timer->adjust(attotime::from_hz(CD_SPEED)); // 150 sectors / second = 300kBytes/second
}
break;
case 0x1100: // disc seek
CDROM_LOG(("%s:CD: Disc seek\n", machine.describe_context()))
//printf("%08x %08x %08x %08x\n",cr1,cr2,cr3,cr4);
if (cr1 & 0x80)
{
temp = (cr1&0xff)<<16; // get FAD to seek to
temp |= cr2;
//cd_curfad = temp;
if (temp == 0xffffff)
{
cd_stat = CD_STAT_PAUSE;
cdda_pause_audio( machine.device( "cdda" ), 1 );
}
else
printf("disc seek with params %04x %04x",cr1,cr2);
cr3 = (temp>>16)&0xff;
cr4 = temp;
}
else
{
// is it a valid track?
if (cr2 >> 8)
{
cd_stat = CD_STAT_PAUSE;
cur_track = cr2>>8;;
cd_curfad = cdrom_get_track_start(cdrom, cur_track-1);
cdda_pause_audio( machine.device( "cdda" ), 1 );
// (index is cr2 low byte)
}
else // error!
{
cd_stat = CD_STAT_STANDBY;
cd_curfad = 0xffffffff;
cur_track = 0xff;
cdda_stop_audio( machine.device( "cdda" ) ); //stop any pending CD-DA
}
cr3 = (cd_curfad>>16)&0xff;
cr4 = cd_curfad;
}
hirqreg |= CMOK;
cr1 = cd_stat;
cr2 = cdrom_get_adr_control(cdrom, cur_track)<<8 | cur_track;
break;
case 0x3000: // Set CD Device connection
{
UINT8 parm;
// get operation
parm = cr3>>8;
CDROM_LOG(("%s:CD: Set CD Device Connection filter # %x\n", machine.describe_context(), 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(("%s:CD: Set Filter Range\n", machine.describe_context()))
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(("%s:CD: Set Filter Subheader conditions %x => chan %x masks %x fid %x vals %x\n", machine.describe_context(), 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(("%s:CD: Set Filter Mode filt %x mode %x\n", machine.describe_context(), 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(("%s:CD: Set Filter Connection %x => mode %x parm %04x\n", machine.describe_context(), 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(("%s:CD: Reset Selector\n", machine.describe_context()))
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\n", cr2))
hirqreg |= (CMOK|ESEL|DRDY); // DRDY is probably wrong
break;
case 0x5100: // get # sectors used in a buffer
{
UINT32 bufnum = cr3>>8;
/* TODO: Akumajou Dracula X reads 0 there, why? */
// is the partition empty?
if (partitions[bufnum].size == -1)
{
cr4 = 0;
}
else
{
cr4 = partitions[bufnum].numblks;
}
CDROM_LOG(("%s:CD: Get Sector Number (bufno %d) = %d blocks\n", machine.describe_context(), 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(("%s:CD: Calculate actual size: buf %x offs %x numsect %x\n", machine.describe_context(), bufnum, sectoffs, numsect))
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(("%s:CD: Get actual block size\n", machine.describe_context()))
hirqreg |= (CMOK|ESEL);
cr1 = cd_stat | ((calcsize>>16)&0xff);
cr2 = (calcsize & 0xffff);
cr3 = 0;
cr4 = 0;
break;
case 0x6000: // set sector length
CDROM_LOG(("%s:CD: Set sector length\n", machine.describe_context()))
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(("%s:CD: Get sector data (SN %d SO %d BN %d)\n", machine.describe_context(), 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, §ofs, §num);
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(("%s:CD: Delete sector data (SN %d SO %d BN %d)\n", machine.describe_context(), 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, §ofs, §num);
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(("%s:CD: Get and delete sector data (SN %d SO %d BN %d)\n", machine.describe_context(), 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, §ofs, §num);
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(("%s:CD: Get copy error\n", machine.describe_context()))
hirqreg |= (CMOK|ESEL|EFLS|SCDQ|DRDY);
break;
case 0x7000: // change directory
CDROM_LOG(("%s:CD: Change Directory\n", machine.describe_context()))
hirqreg |= (CMOK|EFLS);
temp = (cr3&0xff)<<16;
temp |= cr4;
#if 0
if(temp == 0xfffff8) /* TODO: Falcom Classics */
temp = 0;
#endif
read_new_dir(machine, temp);
break;
case 0x7100: // Read directory entry
CDROM_LOG(("%s:CD: Read Directory Entry\n", machine.describe_context()))
hirqreg |= (CMOK|EFLS);
temp = (cr3&0xff)<<16;
temp |= cr4;
#if 0
if(temp == 0xfffff8) /* TODO: Falcom Classics */
temp = 0;
#endif
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\n"))
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(("%s:CD: Get File Info\n", machine.describe_context()))
cd_stat |= CD_STAT_TRANS;
cd_stat &= 0xff00; // clear top byte of return value
hirqreg |= (CMOK|DRDY);
temp = (cr3&0xff)<<16;
temp |= cr4;
#if 0
if(temp == 0xfffff8) /* TODO: Falcom Classics */
temp = 0;
#endif
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(("%s:CD: Read File\n", machine.describe_context()))
temp = (cr3&0xff)<<16;
temp |= cr4;
cd_stat = CD_STAT_PLAY|0x80; // set "cd-rom" bit
cur_track = cdrom_get_track(cdrom, curdir[temp].firstfad-150);
cr2 = cdrom_get_adr_control(cdrom, cur_track)<<8 | cur_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
// sector_timer->adjust(attotime::from_hz(CD_SPEED)); // 150 sectors / second = 300kBytes/second
break;
case 0x7500:
CDROM_LOG(("%s:CD: Abort File\n", machine.describe_context()))
// 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(("%s:CD: Verify copy protection\n", machine.describe_context()))
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(("%s:CD: Get disc region\n", machine.describe_context()))
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\n", cr1))
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\n", addr, data))
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;
case 0x98000:
case 0x18000:
if (mem_mask == 0xffffffff)
{
rv = cd_readLong(offset);
}
else if (mem_mask == 0xffff0000)
{
rv = cd_readWord(offset)<<16;
}
else if (mem_mask == 0x0000ffff)
{
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(space->machine(), offset, data>>16);
break;
default:
CDROM_LOG(("Unknown CD write %x @ %x\n", data, offset))
break;
}
}
// iso9660 parsing
static void read_new_dir(running_machine &machine, 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 > MAX_DIR_SIZE)
{
mame_printf_error("ERROR: root directory too big (%d)\n", curroot.length);
}
// done with all that, read the root directory now
make_dir_current(machine, curroot.firstfad);
}
}
else
{
if (curdir[fileno].length > MAX_DIR_SIZE)
{
mame_printf_error("ERROR: new directory too big (%d)!\n", curdir[fileno].length);
}
make_dir_current(machine, curdir[fileno].firstfad);
}
}
// makes the directory pointed to by FAD current
static void make_dir_current(running_machine &machine, UINT32 fad)
{
int i;
UINT32 nextent, numentries;
UINT8 sect[MAX_DIR_SIZE];
direntryT *curentry;
memset(sect, 0, MAX_DIR_SIZE);
for (i = 0; i < (curroot.length/2048); i++)
{
cd_readblock(fad+i, §[2048*i]);
}
nextent = 0;
numentries = 0;
while (nextent < MAX_DIR_SIZE)
{
if (sect[nextent])
{
nextent += sect[nextent];
numentries++;
}
else
{
nextent = MAX_DIR_SIZE;
}
}
if (curdir != (direntryT *)NULL)
{
auto_free(machine, curdir);
}
curdir = auto_alloc_array(machine, 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;
}
}
}
void stvcd_exit(running_machine& machine)
{
if (curdir != (direntryT *)NULL)
{
auto_free(machine, curdir);
curdir = (direntryT *)NULL;
}
if (cdrom)
{
#ifndef MESS
cdrom_close(cdrom);
#endif
cdrom = (cdrom_file *)NULL;
}
}
static void cd_readTOC(void)
{
int i, ntrks, tocptr, fad;
xfertype = XFERTYPE_TOC;
xfercount = 0;
if (cdrom)
{
ntrks = cdrom_get_last_track(cdrom);
}
else
{
ntrks = 0;
}
// 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;
}
else
{
tocbuf[tocptr] = 0xff;
}
if (cdrom)
{
fad = cdrom_get_track_start(cdrom, i) + 150;
tocbuf[tocptr+1] = (fad>>16)&0xff;
tocbuf[tocptr+2] = (fad>>8)&0xff;
tocbuf[tocptr+3] = fad&0xff;
}
else
{
tocbuf[tocptr+1] = 0xff;
tocbuf[tocptr+2] = 0xff;
tocbuf[tocptr+3] = 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)
{
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);
}
}