2010-11-05 05:55:33 +01:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2002-2010 The DOSBox Team
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* $Id: dos_mscdex.cpp,v 1.59 2009-04-16 12:28:30 qbix79 Exp $ */
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include "regs.h"
|
|
|
|
#include "callback.h"
|
|
|
|
#include "dos_system.h"
|
|
|
|
#include "dos_inc.h"
|
|
|
|
#include "setup.h"
|
|
|
|
#include "support.h"
|
|
|
|
#include "bios_disk.h"
|
|
|
|
#include "cpu.h"
|
|
|
|
|
|
|
|
#include "cdrom.h"
|
|
|
|
|
|
|
|
#define MSCDEX_LOG LOG(LOG_MISC,LOG_ERROR)
|
|
|
|
//#define MSCDEX_LOG
|
|
|
|
|
|
|
|
#define MSCDEX_VERSION_HIGH 2
|
|
|
|
#define MSCDEX_VERSION_LOW 23
|
|
|
|
#define MSCDEX_MAX_DRIVES 8
|
|
|
|
|
|
|
|
// Error Codes
|
|
|
|
#define MSCDEX_ERROR_INVALID_FUNCTION 1
|
|
|
|
#define MSCDEX_ERROR_BAD_FORMAT 11
|
|
|
|
#define MSCDEX_ERROR_UNKNOWN_DRIVE 15
|
|
|
|
#define MSCDEX_ERROR_DRIVE_NOT_READY 21
|
|
|
|
|
|
|
|
// Request Status
|
|
|
|
#define REQUEST_STATUS_DONE 0x0100
|
|
|
|
#define REQUEST_STATUS_ERROR 0x8000
|
|
|
|
|
|
|
|
// Use cdrom Interface
|
|
|
|
int useCdromInterface = CDROM_USE_SDL;
|
|
|
|
int forceCD = -1;
|
|
|
|
|
|
|
|
static Bitu MSCDEX_Strategy_Handler(void);
|
|
|
|
static Bitu MSCDEX_Interrupt_Handler(void);
|
|
|
|
|
|
|
|
class DOS_DeviceHeader:public MemStruct {
|
|
|
|
public:
|
|
|
|
DOS_DeviceHeader(PhysPt ptr) { pt = ptr; };
|
|
|
|
|
|
|
|
void SetNextDeviceHeader (RealPt ptr) { sSave(sDeviceHeader,nextDeviceHeader,ptr); };
|
|
|
|
RealPt GetNextDeviceHeader (void) { return sGet(sDeviceHeader,nextDeviceHeader); };
|
|
|
|
void SetAttribute (Bit16u atr) { sSave(sDeviceHeader,devAttributes,atr); };
|
|
|
|
void SetDriveLetter (Bit8u letter) { sSave(sDeviceHeader,driveLetter,letter); };
|
|
|
|
void SetNumSubUnits (Bit8u num) { sSave(sDeviceHeader,numSubUnits,num); };
|
|
|
|
Bit8u GetNumSubUnits (void) { return sGet(sDeviceHeader,numSubUnits); };
|
|
|
|
void SetName (char const* _name) { MEM_BlockWrite(pt+offsetof(sDeviceHeader,name),_name,8); };
|
|
|
|
void SetInterrupt (Bit16u ofs) { sSave(sDeviceHeader,interrupt,ofs); };
|
|
|
|
void SetStrategy (Bit16u ofs) { sSave(sDeviceHeader,strategy,ofs); };
|
|
|
|
|
|
|
|
public:
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma pack(1)
|
|
|
|
#endif
|
|
|
|
struct sDeviceHeader{
|
|
|
|
RealPt nextDeviceHeader;
|
|
|
|
Bit16u devAttributes;
|
|
|
|
Bit16u strategy;
|
|
|
|
Bit16u interrupt;
|
|
|
|
Bit8u name[8];
|
|
|
|
Bit16u wReserved;
|
|
|
|
Bit8u driveLetter;
|
|
|
|
Bit8u numSubUnits;
|
|
|
|
} GCC_ATTRIBUTE(packed) TDeviceHeader;
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma pack()
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
class CMscdex {
|
|
|
|
public:
|
|
|
|
CMscdex (void);
|
|
|
|
~CMscdex (void);
|
|
|
|
|
|
|
|
Bit16u GetVersion (void) { return (MSCDEX_VERSION_HIGH<<8)+MSCDEX_VERSION_LOW; };
|
|
|
|
Bit16u GetNumDrives (void) { return numDrives; };
|
|
|
|
Bit16u GetFirstDrive (void) { return dinfo[0].drive; };
|
|
|
|
Bit8u GetSubUnit (Bit16u _drive);
|
|
|
|
bool GetUPC (Bit8u subUnit, Bit8u& attr, char* upc);
|
|
|
|
|
|
|
|
void InitNewMedia (Bit8u subUnit);
|
|
|
|
bool PlayAudioSector (Bit8u subUnit, Bit32u start, Bit32u length);
|
|
|
|
bool PlayAudioMSF (Bit8u subUnit, Bit32u start, Bit32u length);
|
|
|
|
bool StopAudio (Bit8u subUnit);
|
|
|
|
bool GetAudioStatus (Bit8u subUnit, bool& playing, bool& pause, TMSF& start, TMSF& end);
|
|
|
|
|
|
|
|
bool GetSubChannelData (Bit8u subUnit, Bit8u& attr, Bit8u& track, Bit8u &index, TMSF& rel, TMSF& abs);
|
|
|
|
|
|
|
|
int RemoveDrive (Bit16u _drive);
|
|
|
|
int AddDrive (Bit16u _drive, char* physicalPath, Bit8u& subUnit);
|
|
|
|
bool HasDrive (Bit16u drive);
|
|
|
|
void ReplaceDrive (CDROM_Interface* newCdrom, Bit8u subUnit);
|
|
|
|
void GetDrives (PhysPt data);
|
|
|
|
void GetDriverInfo (PhysPt data);
|
|
|
|
bool GetVolumeName (Bit8u subUnit, char* name);
|
|
|
|
bool GetCopyrightName (Bit16u drive, PhysPt data);
|
|
|
|
bool GetAbstractName (Bit16u drive, PhysPt data);
|
|
|
|
bool GetDocumentationName(Bit16u drive, PhysPt data);
|
|
|
|
bool GetDirectoryEntry (Bit16u drive, bool copyFlag, PhysPt pathname, PhysPt buffer, Bit16u& error);
|
|
|
|
bool ReadVTOC (Bit16u drive, Bit16u volume, PhysPt data, Bit16u& error);
|
|
|
|
bool ReadSectors (Bit16u drive, Bit32u sector, Bit16u num, PhysPt data);
|
|
|
|
bool ReadSectors (Bit8u subUnit, bool raw, Bit32u sector, Bit16u num, PhysPt data);
|
|
|
|
bool ReadSectorsMSF (Bit8u subUnit, bool raw, Bit32u sector, Bit16u num, PhysPt data);
|
|
|
|
bool SendDriverRequest (Bit16u drive, PhysPt data);
|
|
|
|
bool IsValidDrive (Bit16u drive);
|
|
|
|
bool GetCDInfo (Bit8u subUnit, Bit8u& tr1, Bit8u& tr2, TMSF& leadOut);
|
|
|
|
Bit32u GetVolumeSize (Bit8u subUnit);
|
|
|
|
bool GetTrackInfo (Bit8u subUnit, Bit8u track, Bit8u& attr, TMSF& start);
|
|
|
|
Bit16u GetStatusWord (Bit8u subUnit,Bit16u status);
|
|
|
|
bool GetCurrentPos (Bit8u subUnit, TMSF& pos);
|
|
|
|
Bit32u GetDeviceStatus (Bit8u subUnit);
|
|
|
|
bool GetMediaStatus (Bit8u subUnit, Bit8u& status);
|
|
|
|
bool LoadUnloadMedia (Bit8u subUnit, bool unload);
|
|
|
|
bool ResumeAudio (Bit8u subUnit);
|
|
|
|
bool GetMediaStatus (Bit8u subUnit, bool& media, bool& changed, bool& trayOpen);
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
PhysPt GetDefaultBuffer (void);
|
|
|
|
PhysPt GetTempBuffer (void);
|
|
|
|
|
|
|
|
Bit16u numDrives;
|
|
|
|
|
|
|
|
typedef struct SDriveInfo {
|
|
|
|
Bit8u drive; // drive letter in dosbox
|
|
|
|
Bit8u physDrive; // drive letter in system
|
|
|
|
bool audioPlay; // audio playing active
|
|
|
|
bool audioPaused; // audio playing paused
|
|
|
|
Bit32u audioStart; // StartLoc for resume
|
|
|
|
Bit32u audioEnd; // EndLoc for resume
|
|
|
|
bool locked; // drive locked ?
|
|
|
|
bool lastResult; // last operation success ?
|
|
|
|
Bit32u volumeSize; // for media change
|
|
|
|
TCtrl audioCtrl; // audio channel control
|
|
|
|
} TDriveInfo;
|
|
|
|
|
|
|
|
Bit16u defaultBufSeg;
|
|
|
|
TDriveInfo dinfo[MSCDEX_MAX_DRIVES];
|
|
|
|
CDROM_Interface* cdrom[MSCDEX_MAX_DRIVES];
|
|
|
|
|
|
|
|
public:
|
|
|
|
Bit16u rootDriverHeaderSeg;
|
|
|
|
|
|
|
|
bool ChannelControl (Bit8u subUnit, TCtrl ctrl);
|
|
|
|
bool GetChannelControl (Bit8u subUnit, TCtrl& ctrl);
|
|
|
|
};
|
|
|
|
|
|
|
|
CMscdex::CMscdex(void) {
|
|
|
|
numDrives = 0;
|
|
|
|
rootDriverHeaderSeg = 0;
|
|
|
|
defaultBufSeg = 0;
|
|
|
|
|
|
|
|
memset(dinfo,0,sizeof(dinfo));
|
|
|
|
for (Bit32u i=0; i<MSCDEX_MAX_DRIVES; i++) cdrom[i] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
CMscdex::~CMscdex(void) {
|
|
|
|
defaultBufSeg = 0;
|
|
|
|
for (Bit16u i=0; i<GetNumDrives(); i++) {
|
|
|
|
delete (cdrom)[i];
|
|
|
|
cdrom[i] = 0;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
void CMscdex::GetDrives(PhysPt data)
|
|
|
|
{
|
|
|
|
for (Bit16u i=0; i<GetNumDrives(); i++) mem_writeb(data+i,dinfo[i].drive);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CMscdex::IsValidDrive(Bit16u _drive)
|
|
|
|
{
|
|
|
|
_drive &= 0xff; //Only lowerpart (Ultimate domain)
|
|
|
|
for (Bit16u i=0; i<GetNumDrives(); i++) if (dinfo[i].drive==_drive) return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Bit8u CMscdex::GetSubUnit(Bit16u _drive)
|
|
|
|
{
|
|
|
|
_drive &= 0xff; //Only lowerpart (Ultimate domain)
|
|
|
|
for (Bit16u i=0; i<GetNumDrives(); i++) if (dinfo[i].drive==_drive) return (Bit8u)i;
|
|
|
|
return 0xff;
|
|
|
|
}
|
|
|
|
|
|
|
|
int CMscdex::RemoveDrive(Bit16u _drive)
|
|
|
|
{
|
|
|
|
Bit16u idx = MSCDEX_MAX_DRIVES;
|
|
|
|
for (Bit16u i=0; i<GetNumDrives(); i++) {
|
|
|
|
if (dinfo[i].drive == _drive) {
|
|
|
|
idx = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (idx == MSCDEX_MAX_DRIVES || (idx!=0 && idx!=GetNumDrives()-1)) return 0;
|
|
|
|
delete (cdrom)[idx];
|
|
|
|
if (idx==0) {
|
|
|
|
for (Bit16u i=0; i<GetNumDrives(); i++) {
|
|
|
|
if (i == MSCDEX_MAX_DRIVES-1) {
|
|
|
|
cdrom[i] = 0;
|
|
|
|
memset(&dinfo[i],0,sizeof(TDriveInfo));
|
|
|
|
} else {
|
|
|
|
dinfo[i] = dinfo[i+1];
|
|
|
|
cdrom[i] = cdrom[i+1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
cdrom[idx] = 0;
|
|
|
|
memset(&dinfo[idx],0,sizeof(TDriveInfo));
|
|
|
|
}
|
|
|
|
numDrives--;
|
|
|
|
|
|
|
|
if (GetNumDrives() == 0) {
|
|
|
|
DOS_DeviceHeader devHeader(PhysMake(rootDriverHeaderSeg,0));
|
|
|
|
Bit16u off = sizeof(DOS_DeviceHeader::sDeviceHeader);
|
|
|
|
devHeader.SetStrategy(off+4); // point to the RETF (To deactivate MSCDEX)
|
|
|
|
devHeader.SetInterrupt(off+4); // point to the RETF (To deactivate MSCDEX)
|
|
|
|
devHeader.SetDriveLetter(0);
|
|
|
|
} else if (idx==0) {
|
|
|
|
DOS_DeviceHeader devHeader(PhysMake(rootDriverHeaderSeg,0));
|
|
|
|
devHeader.SetDriveLetter(GetFirstDrive()+1);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int CMscdex::AddDrive(Bit16u _drive, char* physicalPath, Bit8u& subUnit)
|
|
|
|
{
|
|
|
|
subUnit = 0;
|
|
|
|
if ((Bitu)GetNumDrives()+1>=MSCDEX_MAX_DRIVES) return 4;
|
|
|
|
if (GetNumDrives()) {
|
|
|
|
// Error check, driveletter have to be in a row
|
|
|
|
if (dinfo[0].drive-1!=_drive && dinfo[numDrives-1].drive+1!=_drive)
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
// Set return type to ok
|
|
|
|
int result = 0;
|
|
|
|
// Get Mounttype and init needed cdrom interface
|
|
|
|
switch (CDROM_GetMountType(physicalPath,forceCD)) {
|
|
|
|
case 0x00: {
|
|
|
|
LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: Mounting physical cdrom: %s" ,physicalPath);
|
|
|
|
#if defined (WIN32)
|
|
|
|
// Check OS
|
|
|
|
OSVERSIONINFO osi;
|
|
|
|
osi.dwOSVersionInfoSize = sizeof(osi);
|
|
|
|
GetVersionEx(&osi);
|
|
|
|
if ((osi.dwPlatformId==VER_PLATFORM_WIN32_NT) && (osi.dwMajorVersion>4)) {
|
|
|
|
// only WIN NT/200/XP
|
|
|
|
if (useCdromInterface==CDROM_USE_IOCTL_DIO) {
|
|
|
|
cdrom[numDrives] = new CDROM_Interface_Ioctl(CDROM_Interface_Ioctl::CDIOCTL_CDA_DIO);
|
|
|
|
LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: IOCTL Interface.");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (useCdromInterface==CDROM_USE_IOCTL_DX) {
|
|
|
|
cdrom[numDrives] = new CDROM_Interface_Ioctl(CDROM_Interface_Ioctl::CDIOCTL_CDA_DX);
|
|
|
|
LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: IOCTL Interface (digital audio extraction).");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (useCdromInterface==CDROM_USE_IOCTL_MCI) {
|
|
|
|
cdrom[numDrives] = new CDROM_Interface_Ioctl(CDROM_Interface_Ioctl::CDIOCTL_CDA_MCI);
|
|
|
|
LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: IOCTL Interface (media control interface).");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (useCdromInterface==CDROM_USE_ASPI) {
|
|
|
|
// all Wins - ASPI
|
|
|
|
cdrom[numDrives] = new CDROM_Interface_Aspi();
|
|
|
|
LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: ASPI Interface.");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined (LINUX) || defined(OS2)
|
|
|
|
// Always use IOCTL in Linux or OS/2
|
|
|
|
cdrom[numDrives] = new CDROM_Interface_Ioctl();
|
|
|
|
LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: IOCTL Interface.");
|
|
|
|
#else
|
|
|
|
// Default case windows and other oses
|
|
|
|
cdrom[numDrives] = new CDROM_Interface_SDL();
|
|
|
|
LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: SDL Interface.");
|
|
|
|
#endif
|
|
|
|
} break;
|
|
|
|
case 0x01: // iso cdrom interface
|
|
|
|
LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: Mounting iso file as cdrom: %s", physicalPath);
|
|
|
|
cdrom[numDrives] = new CDROM_Interface_Image((Bit8u)numDrives);
|
|
|
|
break;
|
|
|
|
case 0x02: // fake cdrom interface (directories)
|
|
|
|
cdrom[numDrives] = new CDROM_Interface_Fake;
|
|
|
|
LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: Mounting directory as cdrom: %s",physicalPath);
|
|
|
|
LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: You wont have full MSCDEX support !");
|
|
|
|
result = 5;
|
|
|
|
break;
|
|
|
|
default : // weird result
|
|
|
|
return 6;
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!cdrom[numDrives]->SetDevice(physicalPath,forceCD)) {
|
|
|
|
// delete cdrom[numDrives] ; mount seems to delete it
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (rootDriverHeaderSeg==0) {
|
|
|
|
|
|
|
|
Bit16u driverSize = sizeof(DOS_DeviceHeader::sDeviceHeader) + 10; // 10 = Bytes for 3 callbacks
|
|
|
|
|
|
|
|
// Create Device Header
|
|
|
|
Bit16u seg = DOS_GetMemory(driverSize/16+((driverSize%16)>0));
|
|
|
|
DOS_DeviceHeader devHeader(PhysMake(seg,0));
|
|
|
|
devHeader.SetNextDeviceHeader (0xFFFFFFFF);
|
|
|
|
devHeader.SetAttribute(0xc800);
|
|
|
|
devHeader.SetDriveLetter (_drive+1);
|
|
|
|
devHeader.SetNumSubUnits (1);
|
|
|
|
devHeader.SetName ("MSCD001 ");
|
|
|
|
|
|
|
|
//Link it in the device chain
|
|
|
|
Bit32u start = dos_infoblock.GetDeviceChain();
|
|
|
|
Bit16u segm = (Bit16u)(start>>16);
|
|
|
|
Bit16u offm = (Bit16u)(start&0xFFFF);
|
|
|
|
while(start != 0xFFFFFFFF) {
|
|
|
|
segm = (Bit16u)(start>>16);
|
|
|
|
offm = (Bit16u)(start&0xFFFF);
|
|
|
|
start = real_readd(segm,offm);
|
|
|
|
}
|
|
|
|
real_writed(segm,offm,seg<<16);
|
|
|
|
|
|
|
|
// Create Callback Strategy
|
|
|
|
Bit16u off = sizeof(DOS_DeviceHeader::sDeviceHeader);
|
|
|
|
Bit16u call_strategy=(Bit16u)CALLBACK_Allocate();
|
|
|
|
CallBack_Handlers[call_strategy]=MSCDEX_Strategy_Handler;
|
|
|
|
real_writeb(seg,off+0,(Bit8u)0xFE); //GRP 4
|
|
|
|
real_writeb(seg,off+1,(Bit8u)0x38); //Extra Callback instruction
|
|
|
|
real_writew(seg,off+2,call_strategy); //The immediate word
|
|
|
|
real_writeb(seg,off+4,(Bit8u)0xCB); //A RETF Instruction
|
|
|
|
devHeader.SetStrategy(off);
|
|
|
|
|
|
|
|
// Create Callback Interrupt
|
|
|
|
off += 5;
|
|
|
|
Bit16u call_interrupt=(Bit16u)CALLBACK_Allocate();
|
|
|
|
CallBack_Handlers[call_interrupt]=MSCDEX_Interrupt_Handler;
|
|
|
|
real_writeb(seg,off+0,(Bit8u)0xFE); //GRP 4
|
|
|
|
real_writeb(seg,off+1,(Bit8u)0x38); //Extra Callback instruction
|
|
|
|
real_writew(seg,off+2,call_interrupt); //The immediate word
|
|
|
|
real_writeb(seg,off+4,(Bit8u)0xCB); //A RETF Instruction
|
|
|
|
devHeader.SetInterrupt(off);
|
|
|
|
|
|
|
|
rootDriverHeaderSeg = seg;
|
|
|
|
|
|
|
|
} else if (GetNumDrives() == 0) {
|
|
|
|
DOS_DeviceHeader devHeader(PhysMake(rootDriverHeaderSeg,0));
|
|
|
|
Bit16u off = sizeof(DOS_DeviceHeader::sDeviceHeader);
|
|
|
|
devHeader.SetDriveLetter(_drive+1);
|
|
|
|
devHeader.SetStrategy(off);
|
|
|
|
devHeader.SetInterrupt(off+5);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set drive
|
|
|
|
DOS_DeviceHeader devHeader(PhysMake(rootDriverHeaderSeg,0));
|
|
|
|
devHeader.SetNumSubUnits(devHeader.GetNumSubUnits()+1);
|
|
|
|
|
|
|
|
if (dinfo[0].drive-1==_drive) {
|
|
|
|
CDROM_Interface *_cdrom = cdrom[numDrives];
|
|
|
|
CDROM_Interface_Image *_cdimg = CDROM_Interface_Image::images[numDrives];
|
|
|
|
for (Bit16u i=GetNumDrives(); i>0; i--) {
|
|
|
|
dinfo[i] = dinfo[i-1];
|
|
|
|
cdrom[i] = cdrom[i-1];
|
|
|
|
CDROM_Interface_Image::images[i] = CDROM_Interface_Image::images[i-1];
|
|
|
|
}
|
|
|
|
cdrom[0] = _cdrom;
|
|
|
|
CDROM_Interface_Image::images[0] = _cdimg;
|
|
|
|
dinfo[0].drive = (Bit8u)_drive;
|
|
|
|
dinfo[0].physDrive = (Bit8u)toupper(physicalPath[0]);
|
|
|
|
subUnit = 0;
|
|
|
|
} else {
|
|
|
|
dinfo[numDrives].drive = (Bit8u)_drive;
|
|
|
|
dinfo[numDrives].physDrive = (Bit8u)toupper(physicalPath[0]);
|
|
|
|
subUnit = (Bit8u)numDrives;
|
|
|
|
}
|
|
|
|
numDrives++;
|
|
|
|
// init channel control
|
|
|
|
for (Bit8u chan=0;chan<4;chan++) {
|
|
|
|
dinfo[subUnit].audioCtrl.out[chan]=chan;
|
|
|
|
dinfo[subUnit].audioCtrl.vol[chan]=0xff;
|
|
|
|
}
|
|
|
|
// stop audio
|
|
|
|
StopAudio(subUnit);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CMscdex::HasDrive(Bit16u drive) {
|
|
|
|
return (GetSubUnit(drive) != 0xff);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CMscdex::ReplaceDrive(CDROM_Interface* newCdrom, Bit8u subUnit) {
|
|
|
|
delete cdrom[subUnit];
|
|
|
|
cdrom[subUnit] = newCdrom;
|
|
|
|
StopAudio(subUnit);
|
|
|
|
}
|
|
|
|
|
|
|
|
PhysPt CMscdex::GetDefaultBuffer(void) {
|
|
|
|
if (defaultBufSeg==0) {
|
|
|
|
Bit16u size = (2352*2+15)/16;
|
|
|
|
defaultBufSeg = DOS_GetMemory(size);
|
|
|
|
};
|
|
|
|
return PhysMake(defaultBufSeg,2352);
|
|
|
|
}
|
|
|
|
|
|
|
|
PhysPt CMscdex::GetTempBuffer(void) {
|
|
|
|
if (defaultBufSeg==0) {
|
|
|
|
Bit16u size = (2352*2+15)/16;
|
|
|
|
defaultBufSeg = DOS_GetMemory(size);
|
|
|
|
};
|
|
|
|
return PhysMake(defaultBufSeg,0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CMscdex::GetDriverInfo (PhysPt data) {
|
|
|
|
for (Bit16u i=0; i<GetNumDrives(); i++) {
|
|
|
|
mem_writeb(data ,(Bit8u)i); // subunit
|
|
|
|
mem_writed(data+1,RealMake(rootDriverHeaderSeg,0));
|
|
|
|
data+=5;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CMscdex::GetCDInfo(Bit8u subUnit, Bit8u& tr1, Bit8u& tr2, TMSF& leadOut)
|
|
|
|
{
|
|
|
|
if (subUnit>=numDrives) return false;
|
|
|
|
int tr1i,tr2i;
|
|
|
|
// Assume Media change
|
|
|
|
cdrom[subUnit]->InitNewMedia();
|
|
|
|
dinfo[subUnit].lastResult = cdrom[subUnit]->GetAudioTracks(tr1i,tr2i,leadOut);
|
|
|
|
if (!dinfo[subUnit].lastResult) {
|
|
|
|
tr1 = tr2 = 0;
|
|
|
|
memset(&leadOut,0,sizeof(leadOut));
|
|
|
|
} else {
|
|
|
|
tr1 = (Bit8u) tr1i;
|
|
|
|
tr2 = (Bit8u) tr2i;
|
|
|
|
}
|
|
|
|
return dinfo[subUnit].lastResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CMscdex::GetTrackInfo(Bit8u subUnit, Bit8u track, Bit8u& attr, TMSF& start)
|
|
|
|
{
|
|
|
|
if (subUnit>=numDrives) return false;
|
|
|
|
dinfo[subUnit].lastResult = cdrom[subUnit]->GetAudioTrackInfo(track,start,attr);
|
|
|
|
if (!dinfo[subUnit].lastResult) {
|
|
|
|
attr = 0;
|
|
|
|
memset(&start,0,sizeof(start));
|
|
|
|
};
|
|
|
|
return dinfo[subUnit].lastResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CMscdex::PlayAudioSector(Bit8u subUnit, Bit32u sector, Bit32u length)
|
|
|
|
{
|
|
|
|
if (subUnit>=numDrives) return false;
|
|
|
|
// If value from last stop is used, this is meant as a resume
|
|
|
|
// better start using resume command
|
|
|
|
if (dinfo[subUnit].audioPaused && (sector==dinfo[subUnit].audioStart) && (dinfo[subUnit].audioEnd!=0)) {
|
|
|
|
dinfo[subUnit].lastResult = cdrom[subUnit]->PauseAudio(true);
|
|
|
|
} else
|
|
|
|
dinfo[subUnit].lastResult = cdrom[subUnit]->PlayAudioSector(sector,length);
|
|
|
|
|
|
|
|
if (dinfo[subUnit].lastResult) {
|
|
|
|
dinfo[subUnit].audioPlay = true;
|
|
|
|
dinfo[subUnit].audioPaused = false;
|
|
|
|
dinfo[subUnit].audioStart = sector;
|
|
|
|
dinfo[subUnit].audioEnd = length;
|
|
|
|
};
|
|
|
|
return dinfo[subUnit].lastResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CMscdex::PlayAudioMSF(Bit8u subUnit, Bit32u start, Bit32u length)
|
|
|
|
{
|
|
|
|
if (subUnit>=numDrives) return false;
|
|
|
|
Bit8u min = (Bit8u)(start>>16) & 0xFF;
|
|
|
|
Bit8u sec = (Bit8u)(start>> 8) & 0xFF;
|
|
|
|
Bit8u fr = (Bit8u)(start>> 0) & 0xFF;
|
|
|
|
Bit32u sector = min*60*75+sec*75+fr - 150;
|
|
|
|
return dinfo[subUnit].lastResult = PlayAudioSector(subUnit,sector,length);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CMscdex::GetSubChannelData(Bit8u subUnit, Bit8u& attr, Bit8u& track, Bit8u &index, TMSF& rel, TMSF& abs)
|
|
|
|
{
|
|
|
|
if (subUnit>=numDrives) return false;
|
|
|
|
dinfo[subUnit].lastResult = cdrom[subUnit]->GetAudioSub(attr,track,index,rel,abs);
|
|
|
|
if (!dinfo[subUnit].lastResult) {
|
|
|
|
attr = track = index = 0;
|
|
|
|
memset(&rel,0,sizeof(rel));
|
|
|
|
memset(&abs,0,sizeof(abs));
|
|
|
|
};
|
|
|
|
return dinfo[subUnit].lastResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CMscdex::GetAudioStatus(Bit8u subUnit, bool& playing, bool& pause, TMSF& start, TMSF& end)
|
|
|
|
{
|
|
|
|
if (subUnit>=numDrives) return false;
|
|
|
|
dinfo[subUnit].lastResult = cdrom[subUnit]->GetAudioStatus(playing,pause);
|
|
|
|
if (dinfo[subUnit].lastResult) {
|
|
|
|
// Start
|
|
|
|
Bit32u addr = dinfo[subUnit].audioStart + 150;
|
|
|
|
start.fr = (Bit8u)(addr%75); addr/=75;
|
|
|
|
start.sec = (Bit8u)(addr%60);
|
|
|
|
start.min = (Bit8u)(addr/60);
|
|
|
|
// End
|
|
|
|
addr = dinfo[subUnit].audioEnd + 150;
|
|
|
|
end.fr = (Bit8u)(addr%75); addr/=75;
|
|
|
|
end.sec = (Bit8u)(addr%60);
|
|
|
|
end.min = (Bit8u)(addr/60);
|
|
|
|
} else {
|
|
|
|
playing = false;
|
|
|
|
pause = false;
|
|
|
|
memset(&start,0,sizeof(start));
|
|
|
|
memset(&end,0,sizeof(end));
|
|
|
|
};
|
|
|
|
|
|
|
|
return dinfo[subUnit].lastResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CMscdex::StopAudio(Bit8u subUnit)
|
|
|
|
{
|
|
|
|
if (subUnit>=numDrives) return false;
|
|
|
|
if (dinfo[subUnit].audioPlay) dinfo[subUnit].lastResult = cdrom[subUnit]->PauseAudio(false);
|
|
|
|
else dinfo[subUnit].lastResult = cdrom[subUnit]->StopAudio();
|
|
|
|
|
|
|
|
if (dinfo[subUnit].lastResult) {
|
|
|
|
if (dinfo[subUnit].audioPlay) {
|
|
|
|
TMSF pos;
|
|
|
|
GetCurrentPos(subUnit,pos);
|
|
|
|
dinfo[subUnit].audioStart = pos.min*60*75+pos.sec*75+pos.fr - 150;
|
|
|
|
dinfo[subUnit].audioPaused = true;
|
|
|
|
} else {
|
|
|
|
dinfo[subUnit].audioPaused = false;
|
|
|
|
dinfo[subUnit].audioStart = 0;
|
|
|
|
dinfo[subUnit].audioEnd = 0;
|
|
|
|
};
|
|
|
|
dinfo[subUnit].audioPlay = false;
|
|
|
|
};
|
|
|
|
return dinfo[subUnit].lastResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CMscdex::ResumeAudio(Bit8u subUnit) {
|
|
|
|
if (subUnit>=numDrives) return false;
|
|
|
|
return dinfo[subUnit].lastResult = PlayAudioSector(subUnit,dinfo[subUnit].audioStart,dinfo[subUnit].audioEnd);
|
|
|
|
}
|
|
|
|
|
|
|
|
Bit32u CMscdex::GetVolumeSize(Bit8u subUnit) {
|
|
|
|
if (subUnit>=numDrives) return false;
|
|
|
|
Bit8u tr1,tr2;
|
|
|
|
TMSF leadOut;
|
|
|
|
dinfo[subUnit].lastResult = GetCDInfo(subUnit,tr1,tr2,leadOut);
|
|
|
|
if (dinfo[subUnit].lastResult) return (leadOut.min*60*75)+(leadOut.sec*75)+leadOut.fr;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CMscdex::ReadVTOC(Bit16u drive, Bit16u volume, PhysPt data, Bit16u& error) {
|
|
|
|
Bit8u subunit = GetSubUnit(drive);
|
|
|
|
/* if (subunit>=numDrives) {
|
|
|
|
error=MSCDEX_ERROR_UNKNOWN_DRIVE;
|
|
|
|
return false;
|
|
|
|
} */
|
|
|
|
if (!ReadSectors(subunit,false,16+volume,1,data)) {
|
|
|
|
error=MSCDEX_ERROR_DRIVE_NOT_READY;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
char id[5];
|
|
|
|
MEM_BlockRead(data + 1, id, 5);
|
|
|
|
if (strncmp("CD001",id, 5)!=0) {
|
|
|
|
error = MSCDEX_ERROR_BAD_FORMAT;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
Bit8u type = mem_readb(data);
|
|
|
|
error = (type == 1) ? 1 : (type == 0xFF) ? 0xFF : 0;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CMscdex::GetVolumeName(Bit8u subUnit, char* data) {
|
|
|
|
if (subUnit>=numDrives) return false;
|
|
|
|
Bit16u drive = dinfo[subUnit].drive;
|
|
|
|
|
|
|
|
Bit16u error;
|
|
|
|
bool success = false;
|
|
|
|
PhysPt ptoc = GetTempBuffer();
|
|
|
|
success = ReadVTOC(drive,0x00,ptoc,error);
|
|
|
|
if (success) {
|
|
|
|
MEM_StrCopy(ptoc+40,data,31);
|
|
|
|
data[31] = 0;
|
|
|
|
rtrim(data);
|
|
|
|
};
|
|
|
|
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CMscdex::GetCopyrightName(Bit16u drive, PhysPt data) {
|
|
|
|
Bit16u error;
|
|
|
|
bool success = false;
|
|
|
|
PhysPt ptoc = GetTempBuffer();
|
|
|
|
success = ReadVTOC(drive,0x00,ptoc,error);
|
|
|
|
if (success) {
|
|
|
|
MEM_BlockCopy(data,ptoc+702,37);
|
|
|
|
mem_writeb(data+37,0);
|
|
|
|
};
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CMscdex::GetAbstractName(Bit16u drive, PhysPt data) {
|
|
|
|
Bit16u error;
|
|
|
|
bool success = false;
|
|
|
|
PhysPt ptoc = GetTempBuffer();
|
|
|
|
success = ReadVTOC(drive,0x00,ptoc,error);
|
|
|
|
if (success) {
|
|
|
|
MEM_BlockCopy(data,ptoc+739,37);
|
|
|
|
mem_writeb(data+37,0);
|
|
|
|
};
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CMscdex::GetDocumentationName(Bit16u drive, PhysPt data) {
|
|
|
|
Bit16u error;
|
|
|
|
bool success = false;
|
|
|
|
PhysPt ptoc = GetTempBuffer();
|
|
|
|
success = ReadVTOC(drive,0x00,ptoc,error);
|
|
|
|
if (success) {
|
|
|
|
MEM_BlockCopy(data,ptoc+776,37);
|
|
|
|
mem_writeb(data+37,0);
|
|
|
|
};
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CMscdex::GetUPC(Bit8u subUnit, Bit8u& attr, char* upc)
|
|
|
|
{
|
|
|
|
if (subUnit>=numDrives) return false;
|
|
|
|
return dinfo[subUnit].lastResult = cdrom[subUnit]->GetUPC(attr,&upc[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CMscdex::ReadSectors(Bit8u subUnit, bool raw, Bit32u sector, Bit16u num, PhysPt data) {
|
|
|
|
if (subUnit>=numDrives) return false;
|
|
|
|
if ((4*num*2048+5) < CPU_Cycles) CPU_Cycles -= 4*num*2048;
|
|
|
|
else CPU_Cycles = 5;
|
|
|
|
dinfo[subUnit].lastResult = cdrom[subUnit]->ReadSectors(data,raw,sector,num);
|
|
|
|
return dinfo[subUnit].lastResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CMscdex::ReadSectorsMSF(Bit8u subUnit, bool raw, Bit32u start, Bit16u num, PhysPt data) {
|
|
|
|
if (subUnit>=numDrives) return false;
|
|
|
|
Bit8u min = (Bit8u)(start>>16) & 0xFF;
|
|
|
|
Bit8u sec = (Bit8u)(start>> 8) & 0xFF;
|
|
|
|
Bit8u fr = (Bit8u)(start>> 0) & 0xFF;
|
|
|
|
Bit32u sector = min*60*75+sec*75+fr - 150;
|
|
|
|
return ReadSectors(subUnit,raw,sector,num,data);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Called from INT 2F
|
|
|
|
bool CMscdex::ReadSectors(Bit16u drive, Bit32u sector, Bit16u num, PhysPt data) {
|
|
|
|
return ReadSectors(GetSubUnit(drive),false,sector,num,data);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CMscdex::GetDirectoryEntry(Bit16u drive, bool copyFlag, PhysPt pathname, PhysPt buffer, Bit16u& error)
|
|
|
|
{
|
|
|
|
char volumeID[6] = {0};
|
|
|
|
char searchName[256];
|
|
|
|
char entryName[256];
|
|
|
|
bool foundComplete = false;
|
|
|
|
bool foundName;
|
|
|
|
bool nextPart = true;
|
|
|
|
char* useName = 0;
|
|
|
|
Bitu entryLength,nameLength;
|
|
|
|
// clear error
|
|
|
|
error = 0;
|
|
|
|
MEM_StrCopy(pathname+1,searchName,mem_readb(pathname));
|
|
|
|
upcase(searchName);
|
|
|
|
char* searchPos = searchName;
|
|
|
|
|
|
|
|
//strip of tailing . (XCOM APOCALYPSE)
|
|
|
|
size_t searchlen = strlen(searchName);
|
|
|
|
if (searchlen > 1 && strcmp(searchName,".."))
|
|
|
|
if (searchName[searchlen-1] =='.') searchName[searchlen-1] = 0;
|
|
|
|
|
|
|
|
//LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Get DirEntry : Find : %s",searchName);
|
|
|
|
// read vtoc
|
|
|
|
PhysPt defBuffer = GetDefaultBuffer();
|
|
|
|
if (!ReadSectors(GetSubUnit(drive),false,16,1,defBuffer)) return false;
|
|
|
|
// TODO: has to be iso 9960
|
|
|
|
MEM_StrCopy(defBuffer+1,volumeID,5); volumeID[5] = 0;
|
|
|
|
bool iso = (strcmp("CD001",volumeID)==0);
|
|
|
|
if (!iso) E_Exit("MSCDEX: GetDirEntry: Not an ISO 9960 CD.");
|
|
|
|
// get directory position
|
|
|
|
Bitu dirEntrySector = mem_readd(defBuffer+156+2);
|
|
|
|
Bits dirSize = mem_readd(defBuffer+156+10);
|
|
|
|
Bitu index;
|
|
|
|
while (dirSize>0) {
|
|
|
|
index = 0;
|
|
|
|
if (!ReadSectors(GetSubUnit(drive),false,dirEntrySector,1,defBuffer)) return false;
|
|
|
|
// Get string part
|
|
|
|
foundName = false;
|
|
|
|
if (nextPart) {
|
|
|
|
if (searchPos) {
|
|
|
|
useName = searchPos;
|
|
|
|
searchPos = strchr(searchPos,'\\');
|
|
|
|
}
|
|
|
|
if (searchPos) { *searchPos = 0; searchPos++; }
|
|
|
|
else foundComplete = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
|
|
|
entryLength = mem_readb(defBuffer+index);
|
|
|
|
if (entryLength==0) break;
|
|
|
|
nameLength = mem_readb(defBuffer+index+32);
|
|
|
|
MEM_StrCopy(defBuffer+index+33,entryName,nameLength);
|
|
|
|
if (strcmp(entryName,useName)==0) {
|
|
|
|
//LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Get DirEntry : Found : %s",useName);
|
|
|
|
foundName = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* Xcom Apocalipse searches for MUSIC. and expects to find MUSIC;1
|
|
|
|
* All Files on the CDROM are of the kind blah;1
|
|
|
|
*/
|
|
|
|
char* longername = strchr(entryName,';');
|
|
|
|
if(longername) {
|
|
|
|
*longername = 0;
|
|
|
|
if (strcmp(entryName,useName)==0) {
|
|
|
|
//LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Get DirEntry : Found : %s",useName);
|
|
|
|
foundName = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
index += entryLength;
|
|
|
|
} while (index+33<=2048);
|
|
|
|
|
|
|
|
if (foundName) {
|
|
|
|
if (foundComplete) {
|
|
|
|
if (copyFlag) {
|
|
|
|
LOG(LOG_MISC,LOG_WARN)("MSCDEX: GetDirEntry: Copyflag structure not entirely accurate maybe");
|
|
|
|
Bit8u readBuf[256];
|
|
|
|
Bit8u writeBuf[256];
|
|
|
|
if (entryLength > 256)
|
|
|
|
return false;
|
|
|
|
MEM_BlockRead( defBuffer+index, readBuf, entryLength );
|
|
|
|
writeBuf[0] = readBuf[1]; // 00h BYTE length of XAR in Logical Block Numbers
|
|
|
|
memcpy( &writeBuf[1], &readBuf[0x2], 4); // 01h DWORD Logical Block Number of file start
|
|
|
|
writeBuf[5] = 0;writeBuf[6] = 8; // 05h WORD size of disk in logical blocks
|
|
|
|
memcpy( &writeBuf[7], &readBuf[0xa], 4); // 07h DWORD file length in bytes
|
|
|
|
memcpy( &writeBuf[0xb], &readBuf[0x12], 7); // 0bh DWORD date and time
|
|
|
|
writeBuf[0x12] = readBuf[0x19]; // 12h BYTE bit flags
|
|
|
|
writeBuf[0x13] = readBuf[0x1a]; // 13h BYTE interleave size
|
|
|
|
writeBuf[0x14] = readBuf[0x1b]; // 14h BYTE interleave skip factor
|
|
|
|
memcpy( &writeBuf[0x15], &readBuf[0x1c], 2); // 15h WORD volume set sequence number
|
|
|
|
writeBuf[0x17] = readBuf[0x20];
|
|
|
|
memcpy( &writeBuf[0x18], &readBuf[21], readBuf[0x20] <= 38 ? readBuf[0x20] : 38 );
|
|
|
|
MEM_BlockWrite( buffer, writeBuf, 0x18 + 40 );
|
|
|
|
} else {
|
|
|
|
// Direct copy
|
|
|
|
MEM_BlockCopy(buffer,defBuffer+index,entryLength);
|
|
|
|
}
|
|
|
|
error = iso ? 1:0;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// change directory
|
|
|
|
dirEntrySector = mem_readd(defBuffer+index+2);
|
|
|
|
dirSize = mem_readd(defBuffer+index+10);
|
|
|
|
nextPart = true;
|
|
|
|
} else {
|
|
|
|
// continue search in next sector
|
|
|
|
dirSize -= 2048;
|
|
|
|
dirEntrySector++;
|
|
|
|
nextPart = false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
error = 2; // file not found
|
|
|
|
return false; // not found
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CMscdex::GetCurrentPos(Bit8u subUnit, TMSF& pos)
|
|
|
|
{
|
|
|
|
if (subUnit>=numDrives) return false;
|
|
|
|
TMSF rel;
|
|
|
|
Bit8u attr,track,index;
|
|
|
|
dinfo[subUnit].lastResult = GetSubChannelData(subUnit, attr, track, index, rel, pos);
|
|
|
|
if (!dinfo[subUnit].lastResult) memset(&pos,0,sizeof(pos));
|
|
|
|
return dinfo[subUnit].lastResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CMscdex::GetMediaStatus(Bit8u subUnit, bool& media, bool& changed, bool& trayOpen)
|
|
|
|
{
|
|
|
|
if (subUnit>=numDrives) return false;
|
|
|
|
dinfo[subUnit].lastResult = cdrom[subUnit]->GetMediaTrayStatus(media,changed,trayOpen);
|
|
|
|
return dinfo[subUnit].lastResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
Bit32u CMscdex::GetDeviceStatus(Bit8u subUnit)
|
|
|
|
{
|
|
|
|
if (subUnit>=numDrives) return false;
|
|
|
|
bool media,changed,trayOpen;
|
|
|
|
|
|
|
|
dinfo[subUnit].lastResult = GetMediaStatus(subUnit,media,changed,trayOpen);
|
|
|
|
Bit32u status = (trayOpen << 0) | // Drive is open ?
|
|
|
|
(dinfo[subUnit].locked << 1) | // Drive is locked ?
|
|
|
|
(1<<2) | // raw + cooked sectors
|
|
|
|
(1<<4) | // Can read sudio
|
|
|
|
(1<<8) | // Can control audio
|
|
|
|
(1<<9) | // Red book & HSG
|
|
|
|
((!media) << 11); // Drive is empty ?
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CMscdex::GetMediaStatus(Bit8u subUnit, Bit8u& status)
|
|
|
|
{
|
|
|
|
if (subUnit>=numDrives) return false;
|
|
|
|
/* bool media,changed,open,result;
|
|
|
|
result = GetMediaStatus(subUnit,media,changed,open);
|
|
|
|
status = changed ? 0xFF : 0x01;
|
|
|
|
return result; */
|
|
|
|
status = getSwapRequest() ? 0xFF : 0x01;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CMscdex::LoadUnloadMedia(Bit8u subUnit, bool unload)
|
|
|
|
{
|
|
|
|
if (subUnit>=numDrives) return false;
|
|
|
|
dinfo[subUnit].lastResult = cdrom[subUnit]->LoadUnloadMedia(unload);
|
|
|
|
return dinfo[subUnit].lastResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CMscdex::SendDriverRequest(Bit16u drive, PhysPt data)
|
|
|
|
{
|
|
|
|
Bit8u subUnit = GetSubUnit(drive);
|
|
|
|
if (subUnit>=numDrives) return false;
|
|
|
|
// Get SubUnit
|
|
|
|
mem_writeb(data+1,subUnit);
|
|
|
|
// Call Strategy / Interrupt
|
|
|
|
MSCDEX_Strategy_Handler();
|
|
|
|
MSCDEX_Interrupt_Handler();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Bit16u CMscdex::GetStatusWord(Bit8u subUnit,Bit16u status)
|
|
|
|
{
|
|
|
|
if (subUnit>=numDrives) return REQUEST_STATUS_ERROR | 0x02; // error : Drive not ready
|
|
|
|
|
|
|
|
if (dinfo[subUnit].lastResult) status |= REQUEST_STATUS_DONE; // ok
|
|
|
|
else status |= REQUEST_STATUS_ERROR;
|
|
|
|
|
|
|
|
if (dinfo[subUnit].audioPlay) {
|
|
|
|
// Check if audio is still playing....
|
|
|
|
TMSF start,end;
|
|
|
|
bool playing,pause;
|
|
|
|
if (GetAudioStatus(subUnit,playing,pause,start,end)) {
|
|
|
|
dinfo[subUnit].audioPlay = playing;
|
|
|
|
} else
|
|
|
|
dinfo[subUnit].audioPlay = false;
|
|
|
|
|
|
|
|
status |= (dinfo[subUnit].audioPlay<<9);
|
|
|
|
}
|
|
|
|
dinfo[subUnit].lastResult = true;
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CMscdex::InitNewMedia(Bit8u subUnit) {
|
|
|
|
if (subUnit<numDrives) {
|
|
|
|
// Reopen new media
|
|
|
|
cdrom[subUnit]->InitNewMedia();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CMscdex::ChannelControl(Bit8u subUnit, TCtrl ctrl) {
|
|
|
|
if (subUnit>=numDrives) return false;
|
|
|
|
// adjust strange channel mapping
|
|
|
|
if (ctrl.out[0]>1) ctrl.out[0]=0;
|
|
|
|
if (ctrl.out[1]>1) ctrl.out[1]=1;
|
|
|
|
dinfo[subUnit].audioCtrl=ctrl;
|
|
|
|
cdrom[subUnit]->ChannelControl(ctrl);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CMscdex::GetChannelControl(Bit8u subUnit, TCtrl& ctrl) {
|
|
|
|
if (subUnit>=numDrives) return false;
|
|
|
|
ctrl=dinfo[subUnit].audioCtrl;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static CMscdex* mscdex = 0;
|
|
|
|
static PhysPt curReqheaderPtr = 0;
|
|
|
|
|
|
|
|
static Bit16u MSCDEX_IOCTL_Input(PhysPt buffer,Bit8u drive_unit) {
|
|
|
|
Bitu ioctl_fct = mem_readb(buffer);
|
|
|
|
MSCDEX_LOG("MSCDEX: IOCTL INPUT Subfunction %02X",ioctl_fct);
|
|
|
|
switch (ioctl_fct) {
|
|
|
|
case 0x00 : /* Get Device Header address */
|
|
|
|
mem_writed(buffer+1,RealMake(mscdex->rootDriverHeaderSeg,0));
|
|
|
|
break;
|
|
|
|
case 0x01 :{/* Get current position */
|
|
|
|
TMSF pos;
|
|
|
|
mscdex->GetCurrentPos(drive_unit,pos);
|
|
|
|
Bit8u addr_mode = mem_readb(buffer+1);
|
|
|
|
if (addr_mode==0) { // HSG
|
|
|
|
Bit32u frames=MSF_TO_FRAMES(pos.min, pos.sec, pos.fr);
|
|
|
|
if (frames<150) MSCDEX_LOG("MSCDEX: Get position: invalid position %d:%d:%d", pos.min, pos.sec, pos.fr);
|
|
|
|
else frames-=150;
|
|
|
|
mem_writed(buffer+2,frames);
|
|
|
|
} else if (addr_mode==1) { // Red book
|
|
|
|
mem_writeb(buffer+2,pos.fr);
|
|
|
|
mem_writeb(buffer+3,pos.sec);
|
|
|
|
mem_writeb(buffer+4,pos.min);
|
|
|
|
mem_writeb(buffer+5,0x00);
|
|
|
|
} else {
|
|
|
|
MSCDEX_LOG("MSCDEX: Get position: invalid address mode %x",addr_mode);
|
|
|
|
return 0x03; // invalid function
|
|
|
|
}
|
|
|
|
}break;
|
|
|
|
case 0x04 : /* Audio Channel control */
|
|
|
|
TCtrl ctrl;
|
|
|
|
if (!mscdex->GetChannelControl(drive_unit,ctrl)) return 0x01;
|
|
|
|
for (Bit8u chan=0;chan<4;chan++) {
|
|
|
|
mem_writeb(buffer+chan*2+1,ctrl.out[chan]);
|
|
|
|
mem_writeb(buffer+chan*2+2,ctrl.vol[chan]);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x06 : /* Get Device status */
|
|
|
|
mem_writed(buffer+1,mscdex->GetDeviceStatus(drive_unit));
|
|
|
|
break;
|
|
|
|
case 0x07 : /* Get sector size */
|
|
|
|
if (mem_readb(buffer+1)==0) mem_writed(buffer+2,2048);
|
|
|
|
else if (mem_readb(buffer+1)==1) mem_writed(buffer+2,2352);
|
|
|
|
else return 0x03; // invalid function
|
|
|
|
break;
|
|
|
|
case 0x08 : /* Get size of current volume */
|
|
|
|
mem_writed(buffer+1,mscdex->GetVolumeSize(drive_unit));
|
|
|
|
break;
|
|
|
|
case 0x09 : /* Media change ? */
|
|
|
|
Bit8u status;
|
|
|
|
if (!mscdex->GetMediaStatus(drive_unit,status)) {
|
|
|
|
status = 0; // state unknown
|
|
|
|
}
|
|
|
|
mem_writeb(buffer+1,status);
|
|
|
|
break;
|
|
|
|
case 0x0A : /* Get Audio Disk info */
|
|
|
|
Bit8u tr1,tr2; TMSF leadOut;
|
|
|
|
if (!mscdex->GetCDInfo(drive_unit,tr1,tr2,leadOut)) return 0x05;
|
|
|
|
mem_writeb(buffer+1,tr1);
|
|
|
|
mem_writeb(buffer+2,tr2);
|
|
|
|
mem_writeb(buffer+3,leadOut.fr);
|
|
|
|
mem_writeb(buffer+4,leadOut.sec);
|
|
|
|
mem_writeb(buffer+5,leadOut.min);
|
|
|
|
mem_writeb(buffer+6,0x00);
|
|
|
|
break;
|
|
|
|
case 0x0B :{/* Audio Track Info */
|
|
|
|
Bit8u attr; TMSF start;
|
|
|
|
Bit8u track = mem_readb(buffer+1);
|
|
|
|
mscdex->GetTrackInfo(drive_unit,track,attr,start);
|
|
|
|
mem_writeb(buffer+2,start.fr);
|
|
|
|
mem_writeb(buffer+3,start.sec);
|
|
|
|
mem_writeb(buffer+4,start.min);
|
|
|
|
mem_writeb(buffer+5,0x00);
|
|
|
|
mem_writeb(buffer+6,attr);
|
|
|
|
break; };
|
|
|
|
case 0x0C :{/* Get Audio Sub Channel data */
|
|
|
|
Bit8u attr,track,index;
|
|
|
|
TMSF abs,rel;
|
|
|
|
mscdex->GetSubChannelData(drive_unit,attr,track,index,rel,abs);
|
|
|
|
mem_writeb(buffer+1,attr);
|
|
|
|
mem_writeb(buffer+2,track);
|
|
|
|
mem_writeb(buffer+3,index);
|
|
|
|
mem_writeb(buffer+4,rel.min);
|
|
|
|
mem_writeb(buffer+5,rel.sec);
|
|
|
|
mem_writeb(buffer+6,rel.fr);
|
|
|
|
mem_writeb(buffer+7,0x00);
|
|
|
|
mem_writeb(buffer+8,abs.min);
|
|
|
|
mem_writeb(buffer+9,abs.sec);
|
|
|
|
mem_writeb(buffer+10,abs.fr);
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
case 0x0E :{ /* Get UPC */
|
|
|
|
Bit8u attr; char upc[8];
|
|
|
|
mscdex->GetUPC(drive_unit,attr,&upc[0]);
|
|
|
|
mem_writeb(buffer+1,attr);
|
|
|
|
for (int i=0; i<7; i++) mem_writeb(buffer+2+i,upc[i]);
|
|
|
|
mem_writeb(buffer+9,0x00);
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
case 0x0F :{ /* Get Audio Status */
|
|
|
|
bool playing,pause;
|
|
|
|
TMSF resStart,resEnd;
|
|
|
|
mscdex->GetAudioStatus(drive_unit,playing,pause,resStart,resEnd);
|
|
|
|
mem_writeb(buffer+1,pause);
|
|
|
|
mem_writeb(buffer+3,resStart.min);
|
|
|
|
mem_writeb(buffer+4,resStart.sec);
|
|
|
|
mem_writeb(buffer+5,resStart.fr);
|
|
|
|
mem_writeb(buffer+6,0x00);
|
|
|
|
mem_writeb(buffer+7,resEnd.min);
|
|
|
|
mem_writeb(buffer+8,resEnd.sec);
|
|
|
|
mem_writeb(buffer+9,resEnd.fr);
|
|
|
|
mem_writeb(buffer+10,0x00);
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
default : LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Unsupported IOCTL INPUT Subfunction %02X",ioctl_fct);
|
|
|
|
return 0x03; // invalid function
|
|
|
|
}
|
|
|
|
return 0x00; // success
|
|
|
|
}
|
|
|
|
|
|
|
|
static Bit16u MSCDEX_IOCTL_Optput(PhysPt buffer,Bit8u drive_unit) {
|
|
|
|
Bitu ioctl_fct = mem_readb(buffer);
|
|
|
|
// MSCDEX_LOG("MSCDEX: IOCTL OUTPUT Subfunction %02X",ioctl_fct);
|
|
|
|
switch (ioctl_fct) {
|
|
|
|
case 0x00 : // Unload /eject media
|
|
|
|
if (!mscdex->LoadUnloadMedia(drive_unit,true)) return 0x02;
|
|
|
|
break;
|
|
|
|
case 0x03: //Audio Channel control
|
|
|
|
TCtrl ctrl;
|
|
|
|
for (Bit8u chan=0;chan<4;chan++) {
|
|
|
|
ctrl.out[chan]=mem_readb(buffer+chan*2+1);
|
|
|
|
ctrl.vol[chan]=mem_readb(buffer+chan*2+2);
|
|
|
|
}
|
|
|
|
if (!mscdex->ChannelControl(drive_unit,ctrl)) return 0x01;
|
|
|
|
break;
|
|
|
|
case 0x01 : // (un)Lock door
|
|
|
|
// do nothing -> report as success
|
|
|
|
break;
|
|
|
|
case 0x02 : // Reset Drive
|
|
|
|
LOG(LOG_MISC,LOG_WARN)("cdromDrive reset");
|
|
|
|
if (!mscdex->StopAudio(drive_unit)) return 0x02;
|
|
|
|
break;
|
|
|
|
case 0x05 : // load media
|
|
|
|
if (!mscdex->LoadUnloadMedia(drive_unit,false)) return 0x02;
|
|
|
|
default : LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Unsupported IOCTL OUTPUT Subfunction %02X",ioctl_fct);
|
|
|
|
return 0x03; // invalid function
|
|
|
|
}
|
|
|
|
return 0x00; // success
|
|
|
|
}
|
|
|
|
|
|
|
|
static Bitu MSCDEX_Strategy_Handler(void) {
|
|
|
|
curReqheaderPtr = PhysMake(SegValue(es),reg_bx);
|
|
|
|
// MSCDEX_LOG("MSCDEX: Device Strategy Routine called, request header at %x",curReqheaderPtr);
|
|
|
|
return CBRET_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Bitu MSCDEX_Interrupt_Handler(void) {
|
|
|
|
if (curReqheaderPtr==0) {
|
|
|
|
MSCDEX_LOG("MSCDEX: invalid call to interrupt handler");
|
|
|
|
return CBRET_NONE;
|
|
|
|
}
|
|
|
|
Bit8u subUnit = mem_readb(curReqheaderPtr+1);
|
|
|
|
Bit8u funcNr = mem_readb(curReqheaderPtr+2);
|
|
|
|
Bit16u errcode = 0;
|
|
|
|
PhysPt buffer = 0;
|
|
|
|
|
|
|
|
MSCDEX_LOG("MSCDEX: Driver Function %02X",funcNr);
|
|
|
|
|
|
|
|
if ((funcNr==0x03) || (funcNr==0x0c) || (funcNr==0x80) || (funcNr==0x82)) {
|
|
|
|
buffer = PhysMake(mem_readw(curReqheaderPtr+0x10),mem_readw(curReqheaderPtr+0x0E));
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (funcNr) {
|
|
|
|
case 0x03 : { /* IOCTL INPUT */
|
|
|
|
Bit16u error=MSCDEX_IOCTL_Input(buffer,subUnit);
|
|
|
|
if (error) errcode = error;
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
case 0x0C : { /* IOCTL OUTPUT */
|
|
|
|
Bit16u error=MSCDEX_IOCTL_Optput(buffer,subUnit);
|
|
|
|
if (error) errcode = error;
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
case 0x0D : // device open
|
|
|
|
case 0x0E : // device close - dont care :)
|
|
|
|
break;
|
|
|
|
case 0x80 : // Read long
|
|
|
|
case 0x82 : { // Read long prefetch -> both the same here :)
|
|
|
|
Bit32u start = mem_readd(curReqheaderPtr+0x14);
|
|
|
|
Bit16u len = mem_readw(curReqheaderPtr+0x12);
|
|
|
|
bool raw = (mem_readb(curReqheaderPtr+0x18)==1);
|
|
|
|
if (mem_readb(curReqheaderPtr+0x0D)==0x00) // HSG
|
|
|
|
mscdex->ReadSectors(subUnit,raw,start,len,buffer);
|
|
|
|
else
|
|
|
|
mscdex->ReadSectorsMSF(subUnit,raw,start,len,buffer);
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
case 0x83 : // Seek - dont care :)
|
|
|
|
break;
|
|
|
|
case 0x84 : { /* Play Audio Sectors */
|
|
|
|
Bit32u start = mem_readd(curReqheaderPtr+0x0E);
|
|
|
|
Bit32u len = mem_readd(curReqheaderPtr+0x12);
|
|
|
|
if (mem_readb(curReqheaderPtr+0x0D)==0x00) // HSG
|
|
|
|
mscdex->PlayAudioSector(subUnit,start,len);
|
|
|
|
else // RED BOOK
|
|
|
|
mscdex->PlayAudioMSF(subUnit,start,len);
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
case 0x85 : /* Stop Audio */
|
|
|
|
mscdex->StopAudio(subUnit);
|
|
|
|
break;
|
|
|
|
case 0x88 : /* Resume Audio */
|
|
|
|
mscdex->ResumeAudio(subUnit);
|
|
|
|
break;
|
|
|
|
default : LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Unsupported Driver Request %02X",funcNr);
|
|
|
|
break;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
// Set Statusword
|
|
|
|
mem_writew(curReqheaderPtr+3,mscdex->GetStatusWord(subUnit,errcode));
|
|
|
|
MSCDEX_LOG("MSCDEX: Status : %04X",mem_readw(curReqheaderPtr+3));
|
|
|
|
return CBRET_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool MSCDEX_Handler(void) {
|
|
|
|
if(reg_ah == 0x11) {
|
|
|
|
if(reg_al == 0x00) {
|
|
|
|
PhysPt check = PhysMake(SegValue(ss),reg_sp);
|
|
|
|
if(mem_readw(check+6) == 0xDADA) {
|
|
|
|
//MSCDEX sets word on stack to ADAD if it DADA on entry.
|
|
|
|
mem_writew(check+6,0xADAD);
|
|
|
|
}
|
|
|
|
reg_al = 0xff;
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
LOG(LOG_MISC,LOG_ERROR)("NETWORK REDIRECTOR USED!!!");
|
|
|
|
reg_ax = 0x49;//NETWERK SOFTWARE NOT INSTALLED
|
|
|
|
CALLBACK_SCF(true);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (reg_ah!=0x15) return false; // not handled here, continue chain
|
|
|
|
|
|
|
|
PhysPt data = PhysMake(SegValue(es),reg_bx);
|
|
|
|
MSCDEX_LOG("MSCDEX: INT 2F %04X BX= %04X CX=%04X",reg_ax,reg_bx,reg_cx);
|
|
|
|
switch (reg_ax) {
|
|
|
|
|
|
|
|
case 0x1500: /* Install check */
|
|
|
|
reg_bx = mscdex->GetNumDrives();
|
|
|
|
if (reg_bx>0) reg_cx = mscdex->GetFirstDrive();
|
|
|
|
reg_al = 0xff;
|
|
|
|
return true;
|
|
|
|
case 0x1501: /* Get cdrom driver info */
|
|
|
|
mscdex->GetDriverInfo(data);
|
|
|
|
return true;
|
|
|
|
case 0x1502: /* Get Copyright filename */
|
|
|
|
if (mscdex->GetCopyrightName(reg_cx,data)) {
|
|
|
|
CALLBACK_SCF(false);
|
|
|
|
} else {
|
|
|
|
reg_ax = MSCDEX_ERROR_UNKNOWN_DRIVE;
|
|
|
|
CALLBACK_SCF(true);
|
|
|
|
};
|
|
|
|
return true;
|
|
|
|
case 0x1503: /* Get Abstract filename */
|
|
|
|
if (mscdex->GetAbstractName(reg_cx,data)) {
|
|
|
|
CALLBACK_SCF(false);
|
|
|
|
} else {
|
|
|
|
reg_ax = MSCDEX_ERROR_UNKNOWN_DRIVE;
|
|
|
|
CALLBACK_SCF(true);
|
|
|
|
};
|
|
|
|
return true;
|
|
|
|
case 0x1504: /* Get Documentation filename */
|
|
|
|
if (mscdex->GetDocumentationName(reg_cx,data)) {
|
|
|
|
CALLBACK_SCF(false);
|
|
|
|
} else {
|
|
|
|
reg_ax = MSCDEX_ERROR_UNKNOWN_DRIVE;
|
|
|
|
CALLBACK_SCF(true);
|
|
|
|
};
|
|
|
|
return true;
|
|
|
|
case 0x1505: { // read vtoc
|
|
|
|
Bit16u error = 0;
|
|
|
|
if (mscdex->ReadVTOC(reg_cx,reg_dx,data,error)) {
|
|
|
|
// reg_ax = error; // return code
|
|
|
|
CALLBACK_SCF(false);
|
|
|
|
} else {
|
|
|
|
reg_ax = error;
|
|
|
|
CALLBACK_SCF(true);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
return true;
|
|
|
|
case 0x1508: { // read sectors
|
|
|
|
Bit32u sector = (reg_si<<16)+reg_di;
|
|
|
|
if (mscdex->ReadSectors(reg_cx,sector,reg_dx,data)) {
|
|
|
|
reg_ax = 0;
|
|
|
|
CALLBACK_SCF(false);
|
|
|
|
} else {
|
|
|
|
// possibly: MSCDEX_ERROR_DRIVE_NOT_READY if sector is beyond total length
|
|
|
|
reg_ax = MSCDEX_ERROR_UNKNOWN_DRIVE;
|
|
|
|
CALLBACK_SCF(true);
|
|
|
|
};
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
case 0x1509: // write sectors - not supported
|
|
|
|
reg_ax = MSCDEX_ERROR_INVALID_FUNCTION;
|
|
|
|
CALLBACK_SCF(true);
|
|
|
|
return true;
|
|
|
|
case 0x150B: /* Valid CDROM drive ? */
|
|
|
|
reg_ax = (mscdex->IsValidDrive(reg_cx) ? 0x5ad8 : 0x0000);
|
|
|
|
reg_bx = 0xADAD;
|
|
|
|
return true;
|
|
|
|
case 0x150C: /* Get MSCDEX Version */
|
|
|
|
reg_bx = mscdex->GetVersion();
|
|
|
|
return true;
|
|
|
|
case 0x150D: /* Get drives */
|
|
|
|
mscdex->GetDrives(data);
|
|
|
|
return true;
|
|
|
|
case 0x150E: /* Get/Set Volume Descriptor Preference */
|
|
|
|
if (mscdex->IsValidDrive(reg_cx)) {
|
|
|
|
if (reg_bx == 0) {
|
|
|
|
// get preference
|
|
|
|
reg_dx = 0x100; // preference?
|
|
|
|
CALLBACK_SCF(false);
|
|
|
|
} else if (reg_bx == 1) {
|
|
|
|
// set preference
|
|
|
|
if (reg_dh == 1) {
|
|
|
|
// valid
|
|
|
|
CALLBACK_SCF(false);
|
|
|
|
} else {
|
|
|
|
reg_ax = MSCDEX_ERROR_INVALID_FUNCTION;
|
|
|
|
CALLBACK_SCF(true);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
reg_ax = MSCDEX_ERROR_INVALID_FUNCTION;
|
|
|
|
CALLBACK_SCF(true);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
reg_ax = MSCDEX_ERROR_UNKNOWN_DRIVE;
|
|
|
|
CALLBACK_SCF(true);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
case 0x150F: { // Get directory entry
|
|
|
|
Bit16u error;
|
|
|
|
bool success = mscdex->GetDirectoryEntry(reg_cl,reg_ch&1,data,PhysMake(reg_si,reg_di),error);
|
|
|
|
reg_ax = error;
|
|
|
|
CALLBACK_SCF(!success);
|
|
|
|
}; return true;
|
|
|
|
case 0x1510: /* Device driver request */
|
|
|
|
if (mscdex->SendDriverRequest(reg_cx,data)) {
|
|
|
|
CALLBACK_SCF(false);
|
|
|
|
} else {
|
|
|
|
reg_ax = MSCDEX_ERROR_UNKNOWN_DRIVE;
|
|
|
|
CALLBACK_SCF(true);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Unknwon call : %04X",reg_ax);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
class device_MSCDEX : public DOS_Device {
|
|
|
|
public:
|
|
|
|
device_MSCDEX() { SetName("MSCD001"); }
|
|
|
|
bool Read (Bit8u * /*data*/,Bit16u * /*size*/) { return false;}
|
|
|
|
bool Write(Bit8u * /*data*/,Bit16u * /*size*/) {
|
|
|
|
LOG(LOG_ALL,LOG_NORMAL)("Write to mscdex device");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool Seek(Bit32u * /*pos*/,Bit32u /*type*/){return false;}
|
|
|
|
bool Close(){return false;}
|
|
|
|
Bit16u GetInformation(void){return 0xc880;}
|
|
|
|
bool ReadFromControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode);
|
|
|
|
bool WriteToControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode);
|
|
|
|
private:
|
|
|
|
// Bit8u cache;
|
|
|
|
};
|
|
|
|
|
|
|
|
bool device_MSCDEX::ReadFromControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode) {
|
|
|
|
if (MSCDEX_IOCTL_Input(bufptr,0)==0) {
|
|
|
|
*retcode=size;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool device_MSCDEX::WriteToControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode) {
|
|
|
|
if (MSCDEX_IOCTL_Optput(bufptr,0)==0) {
|
|
|
|
*retcode=size;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int MSCDEX_AddDrive(char driveLetter, const char* physicalPath, Bit8u& subUnit)
|
|
|
|
{
|
|
|
|
int result = mscdex->AddDrive(driveLetter-'A',(char*)physicalPath,subUnit);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
int MSCDEX_RemoveDrive(char driveLetter)
|
|
|
|
{
|
|
|
|
if(!mscdex) return 0;
|
|
|
|
return mscdex->RemoveDrive(driveLetter-'A');
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MSCDEX_HasDrive(char driveLetter)
|
|
|
|
{
|
|
|
|
return mscdex->HasDrive(driveLetter-'A');
|
|
|
|
}
|
|
|
|
|
|
|
|
void MSCDEX_ReplaceDrive(CDROM_Interface* cdrom, Bit8u subUnit)
|
|
|
|
{
|
|
|
|
mscdex->ReplaceDrive(cdrom, subUnit);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MSCDEX_GetVolumeName(Bit8u subUnit, char* name)
|
|
|
|
{
|
|
|
|
return mscdex->GetVolumeName(subUnit,name);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MSCDEX_HasMediaChanged(Bit8u subUnit)
|
|
|
|
{
|
|
|
|
static TMSF leadOut[MSCDEX_MAX_DRIVES];
|
|
|
|
|
|
|
|
TMSF leadnew;
|
|
|
|
Bit8u tr1,tr2;
|
|
|
|
if (mscdex->GetCDInfo(subUnit,tr1,tr2,leadnew)) {
|
|
|
|
bool changed = (leadOut[subUnit].min!=leadnew.min) || (leadOut[subUnit].sec!=leadnew.sec) || (leadOut[subUnit].fr!=leadnew.fr);
|
|
|
|
if (changed) {
|
|
|
|
leadOut[subUnit].min = leadnew.min;
|
|
|
|
leadOut[subUnit].sec = leadnew.sec;
|
|
|
|
leadOut[subUnit].fr = leadnew.fr;
|
|
|
|
mscdex->InitNewMedia(subUnit);
|
|
|
|
}
|
|
|
|
return changed;
|
|
|
|
};
|
|
|
|
if (subUnit<MSCDEX_MAX_DRIVES) {
|
|
|
|
leadOut[subUnit].min = 0;
|
|
|
|
leadOut[subUnit].sec = 0;
|
|
|
|
leadOut[subUnit].fr = 0;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MSCDEX_SetCDInterface(int intNr, int numCD) {
|
|
|
|
useCdromInterface = intNr;
|
|
|
|
forceCD = numCD;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MSCDEX_ShutDown(Section* /*sec*/) {
|
|
|
|
delete mscdex;
|
|
|
|
mscdex = 0;
|
|
|
|
curReqheaderPtr = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MSCDEX_Init(Section* sec) {
|
|
|
|
// AddDestroy func
|
|
|
|
sec->AddDestroyFunction(&MSCDEX_ShutDown);
|
|
|
|
/* Register the mscdex device */
|
|
|
|
DOS_Device * newdev = new device_MSCDEX();
|
|
|
|
DOS_AddDevice(newdev);
|
|
|
|
curReqheaderPtr = 0;
|
|
|
|
/* Add Multiplexer */
|
|
|
|
DOS_AddMultiplexHandler(MSCDEX_Handler);
|
|
|
|
/* Create MSCDEX */
|
|
|
|
mscdex = new CMscdex;
|
|
|
|
}
|