DOSBox-X
|
00001 /* 00002 * Copyright (C) 2002-2020 The DOSBox Team 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation; either version 2 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License along 00015 * with this program; if not, write to the Free Software Foundation, Inc., 00016 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00017 */ 00018 00019 00020 #include <string.h> 00021 #include <ctype.h> 00022 #include "regs.h" 00023 #include "callback.h" 00024 #include "dos_system.h" 00025 #include "dos_inc.h" 00026 #include "setup.h" 00027 #include "support.h" 00028 #include "bios_disk.h" 00029 #include "cpu.h" 00030 00031 #include "cdrom.h" 00032 00033 #define MSCDEX_LOG LOG(LOG_MISC, LOG_DEBUG) 00034 #define MSCDEX_LOG_ERROR LOG(LOG_MISC, LOG_ERROR) 00035 00036 #define MSCDEX_VERSION_HIGH 2 00037 #define MSCDEX_VERSION_LOW 23 00038 #define MSCDEX_MAX_DRIVES 16 00039 00040 // Error Codes 00041 #define MSCDEX_ERROR_INVALID_FUNCTION 1 00042 #define MSCDEX_ERROR_BAD_FORMAT 11 00043 #define MSCDEX_ERROR_UNKNOWN_DRIVE 15 00044 #define MSCDEX_ERROR_DRIVE_NOT_READY 21 00045 00046 // Request Status 00047 #define REQUEST_STATUS_DONE 0x0100 00048 #define REQUEST_STATUS_ERROR 0x8000 00049 00050 // Use cdrom Interface 00051 int useCdromInterface = CDROM_USE_SDL; 00052 int forceCD = -1; 00053 00054 static Bitu MSCDEX_Strategy_Handler(void); 00055 static Bitu MSCDEX_Interrupt_Handler(void); 00056 00057 class DOS_DeviceHeader:public MemStruct { 00058 public: 00059 DOS_DeviceHeader(PhysPt ptr) { pt = ptr; }; 00060 00061 void SetNextDeviceHeader (RealPt ptr) { sSave(sDeviceHeader,nextDeviceHeader,ptr); }; 00062 RealPt GetNextDeviceHeader (void) { return sGet(sDeviceHeader,nextDeviceHeader); }; 00063 void SetAttribute (Bit16u atr) { sSave(sDeviceHeader,devAttributes,atr); }; 00064 void SetDriveLetter (Bit8u letter) { sSave(sDeviceHeader,driveLetter,letter); }; 00065 void SetNumSubUnits (Bit8u num) { sSave(sDeviceHeader,numSubUnits,num); }; 00066 Bit8u GetNumSubUnits (void) { return sGet(sDeviceHeader,numSubUnits); }; 00067 void SetName (char const* _name) { MEM_BlockWrite(pt+offsetof(sDeviceHeader,name),_name,8); }; 00068 void SetInterrupt (Bit16u ofs) { sSave(sDeviceHeader,interrupt,ofs); }; 00069 void SetStrategy (Bit16u ofs) { sSave(sDeviceHeader,strategy,ofs); }; 00070 00071 #ifdef _MSC_VER 00072 #pragma pack(1) 00073 #endif 00074 struct sDeviceHeader{ 00075 RealPt nextDeviceHeader; 00076 Bit16u devAttributes; 00077 Bit16u strategy; 00078 Bit16u interrupt; 00079 Bit8u name[8]; 00080 Bit16u wReserved; 00081 Bit8u driveLetter; 00082 Bit8u numSubUnits; 00083 } GCC_ATTRIBUTE(packed) TDeviceHeader = {}; 00084 #ifdef _MSC_VER 00085 #pragma pack() 00086 #endif 00087 }; 00088 00089 class CMscdex { 00090 public: 00091 CMscdex (void); 00092 ~CMscdex (void); 00093 00094 Bit16u GetVersion (void) { return (MSCDEX_VERSION_HIGH<<8)+MSCDEX_VERSION_LOW; }; 00095 Bit16u GetNumDrives (void) { return numDrives; }; 00096 Bit16u GetFirstDrive (void) { return dinfo[0].drive; }; 00097 Bit8u GetSubUnit (Bit16u _drive); 00098 bool GetUPC (Bit8u subUnit, Bit8u& attr, char* upc); 00099 00100 void InitNewMedia (Bit8u subUnit); 00101 bool PlayAudioSector (Bit8u subUnit, Bit32u sector, Bit32u length); 00102 bool PlayAudioMSF (Bit8u subUnit, Bit32u start, Bit32u length); 00103 bool StopAudio (Bit8u subUnit); 00104 bool GetAudioStatus (Bit8u subUnit, bool& playing, bool& pause, TMSF& start, TMSF& end); 00105 00106 bool GetSubChannelData (Bit8u subUnit, Bit8u& attr, Bit8u& track, Bit8u &index, TMSF& rel, TMSF& abs); 00107 00108 int RemoveDrive (Bit16u _drive); 00109 int AddDrive (Bit16u _drive, char* physicalPath, Bit8u& subUnit); 00110 bool HasDrive (Bit16u drive); 00111 void ReplaceDrive (CDROM_Interface* newCdrom, Bit8u subUnit); 00112 void GetDrives (PhysPt data); 00113 void GetDriverInfo (PhysPt data); 00114 bool GetVolumeName (Bit8u subUnit, char* data); 00115 bool GetFileName (Bit16u drive, Bit16u pos, PhysPt data); 00116 bool GetDirectoryEntry (Bit16u drive, bool copyFlag, PhysPt pathname, PhysPt buffer, Bit16u& error); 00117 bool ReadVTOC (Bit16u drive, Bit16u volume, PhysPt data, Bit16u& offset, Bit16u& error); 00118 bool ReadSectors (Bit16u drive, Bit32u sector, Bit16u num, PhysPt data); 00119 bool ReadSectors (Bit8u subUnit, bool raw, Bit32u sector, Bit16u num, PhysPt data); 00120 bool ReadSectorsMSF (Bit8u subUnit, bool raw, Bit32u start, Bit16u num, PhysPt data); 00121 bool SendDriverRequest (Bit16u drive, PhysPt data); 00122 bool IsValidDrive (Bit16u _drive); 00123 bool GetCDInfo (Bit8u subUnit, Bit8u& tr1, Bit8u& tr2, TMSF& leadOut); 00124 Bit32u GetVolumeSize (Bit8u subUnit); 00125 bool GetTrackInfo (Bit8u subUnit, Bit8u track, Bit8u& attr, TMSF& start); 00126 Bit16u GetStatusWord (Bit8u subUnit,Bit16u status); 00127 bool GetCurrentPos (Bit8u subUnit, TMSF& pos); 00128 Bit32u GetDeviceStatus (Bit8u subUnit); 00129 bool GetMediaStatus (Bit8u subUnit, Bit8u& status); 00130 bool LoadUnloadMedia (Bit8u subUnit, bool unload); 00131 bool ResumeAudio (Bit8u subUnit); 00132 bool GetMediaStatus (Bit8u subUnit, bool& media, bool& changed, bool& trayOpen); 00133 00134 PhysPt GetDefaultBuffer (void); 00135 PhysPt GetTempBuffer (void); 00136 00137 void SaveState( std::ostream& stream ); 00138 void LoadState( std::istream& stream ); 00139 00140 Bit16u numDrives = 0; 00141 00142 typedef struct SDriveInfo { 00143 Bit8u drive; // drive letter in dosbox 00144 Bit8u physDrive; // drive letter in system 00145 bool audioPlay; // audio playing active 00146 bool audioPaused; // audio playing paused 00147 Bit32u audioStart; // StartLoc for resume 00148 Bit32u audioEnd; // EndLoc for resume 00149 bool locked; // drive locked ? 00150 bool lastResult; // last operation success ? 00151 Bit32u volumeSize; // for media change 00152 TCtrl audioCtrl; // audio channel control 00153 } TDriveInfo; 00154 00155 Bit16u defaultBufSeg = 0; 00156 TDriveInfo dinfo[MSCDEX_MAX_DRIVES]; 00157 CDROM_Interface* cdrom[MSCDEX_MAX_DRIVES]; 00158 Bit16u rootDriverHeaderSeg = 0; 00159 00160 bool ChannelControl (Bit8u subUnit, TCtrl ctrl); 00161 bool GetChannelControl (Bit8u subUnit, TCtrl& ctrl); 00162 }; 00163 00164 CMscdex::CMscdex(void) { 00165 memset(dinfo,0,sizeof(dinfo)); 00166 for (Bit32u i=0; i<MSCDEX_MAX_DRIVES; i++) cdrom[i] = 0; 00167 } 00168 00169 CMscdex::~CMscdex(void) { 00170 defaultBufSeg = 0; 00171 for (Bit16u i=0; i<GetNumDrives(); i++) { 00172 delete cdrom[i]; 00173 cdrom[i] = 0; 00174 } 00175 } 00176 00177 void CMscdex::GetDrives(PhysPt data) 00178 { 00179 for (Bit16u i=0; i<GetNumDrives(); i++) mem_writeb(data+i,dinfo[i].drive); 00180 } 00181 00182 bool CMscdex::IsValidDrive(Bit16u _drive) 00183 { 00184 _drive &= 0xff; //Only lowerpart (Ultimate domain) 00185 for (Bit16u i=0; i<GetNumDrives(); i++) if (dinfo[i].drive==_drive) return true; 00186 return false; 00187 } 00188 00189 Bit8u CMscdex::GetSubUnit(Bit16u _drive) 00190 { 00191 _drive &= 0xff; //Only lowerpart (Ultimate domain) 00192 for (Bit16u i=0; i<GetNumDrives(); i++) if (dinfo[i].drive==_drive) return (Bit8u)i; 00193 return 0xff; 00194 } 00195 00196 int CMscdex::RemoveDrive(Bit16u _drive) 00197 { 00198 Bit16u idx = MSCDEX_MAX_DRIVES; 00199 for (Bit16u i=0; i<GetNumDrives(); i++) { 00200 if (dinfo[i].drive == _drive) { 00201 idx = i; 00202 break; 00203 } 00204 } 00205 00206 if (idx == MSCDEX_MAX_DRIVES || (idx!=0 && idx!=GetNumDrives()-1)) return 0; 00207 delete cdrom[idx]; 00208 if (idx==0) { 00209 for (Bit16u i=0; i<GetNumDrives(); i++) { 00210 if (i == MSCDEX_MAX_DRIVES-1) { 00211 cdrom[i] = 0; 00212 memset(&dinfo[i],0,sizeof(TDriveInfo)); 00213 } else { 00214 dinfo[i] = dinfo[i+1]; 00215 cdrom[i] = cdrom[i+1]; 00216 } 00217 } 00218 } else { 00219 cdrom[idx] = 0; 00220 memset(&dinfo[idx],0,sizeof(TDriveInfo)); 00221 } 00222 numDrives--; 00223 00224 if (GetNumDrives() == 0) { 00225 DOS_DeviceHeader devHeader(PhysMake(rootDriverHeaderSeg,0)); 00226 Bit16u off = sizeof(DOS_DeviceHeader::sDeviceHeader); 00227 devHeader.SetStrategy(off+4); // point to the RETF (To deactivate MSCDEX) 00228 devHeader.SetInterrupt(off+4); // point to the RETF (To deactivate MSCDEX) 00229 devHeader.SetDriveLetter(0); 00230 } else if (idx==0) { 00231 DOS_DeviceHeader devHeader(PhysMake(rootDriverHeaderSeg,0)); 00232 devHeader.SetDriveLetter(GetFirstDrive()+1); 00233 } 00234 return 1; 00235 } 00236 00237 int CMscdex::AddDrive(Bit16u _drive, char* physicalPath, Bit8u& subUnit) 00238 { 00239 subUnit = 0; 00240 if ((Bitu)GetNumDrives()+1>=MSCDEX_MAX_DRIVES) return 4; 00241 if (GetNumDrives()) { 00242 // Error check, driveletter have to be in a row 00243 if (dinfo[0].drive-1!=_drive && dinfo[numDrives-1].drive+1!=_drive) 00244 return 1; 00245 } 00246 // Set return type to ok 00247 int result = 0; 00248 // Get Mounttype and init needed cdrom interface 00249 switch (CDROM_GetMountType(physicalPath,forceCD)) { 00250 case 0x00: { 00251 LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: Mounting physical cdrom: %s" ,physicalPath); 00252 #if defined (WIN32) 00253 // Check OS 00254 OSVERSIONINFO osi; 00255 osi.dwOSVersionInfoSize = sizeof(osi); 00256 GetVersionEx(&osi); 00257 if ((osi.dwPlatformId==VER_PLATFORM_WIN32_NT) && (osi.dwMajorVersion>4)) { 00258 // only WIN NT/200/XP 00259 if (useCdromInterface==CDROM_USE_IOCTL_DIO) { 00260 // cdrom[numDrives] = new CDROM_Interface_Ioctl(CDROM_Interface_Ioctl::CDIOCTL_CDA_DIO); 00261 // LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: IOCTL Interface."); 00262 // break; 00263 } 00264 if (useCdromInterface==CDROM_USE_IOCTL_DX) { 00265 // cdrom[numDrives] = new CDROM_Interface_Ioctl(CDROM_Interface_Ioctl::CDIOCTL_CDA_DX); 00266 // LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: IOCTL Interface (digital audio extraction)."); 00267 // break; 00268 } 00269 if (useCdromInterface==CDROM_USE_IOCTL_MCI) { 00270 // cdrom[numDrives] = new CDROM_Interface_Ioctl(CDROM_Interface_Ioctl::CDIOCTL_CDA_MCI); 00271 // LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: IOCTL Interface (media control interface)."); 00272 // break; 00273 } 00274 } 00275 if (useCdromInterface==CDROM_USE_ASPI) { 00276 // all Wins - ASPI 00277 // cdrom[numDrives] = new CDROM_Interface_Aspi(); 00278 // LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: ASPI Interface."); 00279 // break; 00280 } 00281 #endif 00282 #if defined (LINUX) || defined(OS2) 00283 // Always use IOCTL in Linux or OS/2 00284 cdrom[numDrives] = new CDROM_Interface_Ioctl(); 00285 LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: IOCTL Interface."); 00286 #else 00287 // Default case windows and other oses 00288 cdrom[numDrives] = new CDROM_Interface_SDL(); 00289 LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: SDL Interface."); 00290 #endif 00291 } break; 00292 case 0x01: // iso cdrom interface 00293 LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: Mounting iso file as cdrom: %s", physicalPath); 00294 cdrom[numDrives] = new CDROM_Interface_Image((Bit8u)numDrives); 00295 break; 00296 case 0x02: // fake cdrom interface (directories) 00297 cdrom[numDrives] = new CDROM_Interface_Fake; 00298 LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: Mounting directory as cdrom: %s",physicalPath); 00299 LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: You wont have full MSCDEX support !"); 00300 result = 5; 00301 break; 00302 default : // weird result 00303 return 6; 00304 } 00305 00306 if (!cdrom[numDrives]->SetDevice(physicalPath,forceCD)) { 00307 // delete cdrom[numDrives] ; mount seems to delete it 00308 return 3; 00309 } 00310 00311 00312 if (rootDriverHeaderSeg==0) { 00313 00314 Bit16u driverSize = sizeof(DOS_DeviceHeader::sDeviceHeader) + 10; // 10 = Bytes for 3 callbacks 00315 00316 // Create Device Header 00317 Bit16u seg = DOS_GetMemory(driverSize/16u+((driverSize%16u)>0u),"MSCDEX device header"); 00318 DOS_DeviceHeader devHeader(PhysMake(seg,0u)); 00319 devHeader.SetNextDeviceHeader (0xFFFFFFFFul); 00320 devHeader.SetAttribute(0xc800u); 00321 devHeader.SetDriveLetter (_drive+1u); 00322 devHeader.SetNumSubUnits (1u); 00323 devHeader.SetName ("MSCD001 "); 00324 00325 //Link it in the device chain 00326 Bit32u start = dos_infoblock.GetDeviceChain(); 00327 Bit16u segm = (Bit16u)(start>>16ul); 00328 Bit16u offm = (Bit16u)(start&0xFFFFu); 00329 while(start != 0xFFFFFFFFul) { 00330 segm = (Bit16u)(start>>16u); 00331 offm = (Bit16u)(start&0xFFFFu); 00332 start = real_readd(segm,offm); 00333 } 00334 real_writed(segm,offm,(unsigned int)seg<<16u); 00335 00336 // Create Callback Strategy 00337 Bit16u off = sizeof(DOS_DeviceHeader::sDeviceHeader); 00338 Bit16u call_strategy=CALLBACK_Allocate(); 00339 CallBack_Handlers[call_strategy]=MSCDEX_Strategy_Handler; 00340 real_writeb(seg,off+0,(Bit8u)0xFE); //GRP 4 00341 real_writeb(seg,off+1,(Bit8u)0x38); //Extra Callback instruction 00342 real_writew(seg,off+2,call_strategy); //The immediate word 00343 real_writeb(seg,off+4,(Bit8u)0xCB); //A RETF Instruction 00344 devHeader.SetStrategy(off); 00345 00346 // Create Callback Interrupt 00347 off += 5; 00348 Bit16u call_interrupt=CALLBACK_Allocate(); 00349 CallBack_Handlers[call_interrupt]=MSCDEX_Interrupt_Handler; 00350 real_writeb(seg,off+0,(Bit8u)0xFE); //GRP 4 00351 real_writeb(seg,off+1,(Bit8u)0x38); //Extra Callback instruction 00352 real_writew(seg,off+2,call_interrupt); //The immediate word 00353 real_writeb(seg,off+4,(Bit8u)0xCB); //A RETF Instruction 00354 devHeader.SetInterrupt(off); 00355 00356 rootDriverHeaderSeg = seg; 00357 00358 } else if (GetNumDrives() == 0) { 00359 DOS_DeviceHeader devHeader(PhysMake(rootDriverHeaderSeg,0)); 00360 Bit16u off = sizeof(DOS_DeviceHeader::sDeviceHeader); 00361 devHeader.SetDriveLetter(_drive+1); 00362 devHeader.SetStrategy(off); 00363 devHeader.SetInterrupt(off+5); 00364 } 00365 00366 // Set drive 00367 DOS_DeviceHeader devHeader(PhysMake(rootDriverHeaderSeg,0)); 00368 devHeader.SetNumSubUnits(devHeader.GetNumSubUnits()+1); 00369 00370 if (dinfo[0].drive-1==_drive) { 00371 CDROM_Interface *_cdrom = cdrom[numDrives]; 00372 CDROM_Interface_Image *_cdimg = CDROM_Interface_Image::images[numDrives]; 00373 for (Bit16u i=GetNumDrives(); i>0; i--) { 00374 dinfo[i] = dinfo[i-1]; 00375 cdrom[i] = cdrom[i-1]; 00376 CDROM_Interface_Image::images[i] = CDROM_Interface_Image::images[i-1]; 00377 } 00378 cdrom[0] = _cdrom; 00379 CDROM_Interface_Image::images[0] = _cdimg; 00380 dinfo[0].drive = (Bit8u)_drive; 00381 dinfo[0].physDrive = (Bit8u)toupper(physicalPath[0]); 00382 subUnit = 0; 00383 } else { 00384 dinfo[numDrives].drive = (Bit8u)_drive; 00385 dinfo[numDrives].physDrive = (Bit8u)toupper(physicalPath[0]); 00386 subUnit = (Bit8u)numDrives; 00387 } 00388 numDrives++; 00389 // init channel control 00390 for (Bit8u chan=0;chan<4;chan++) { 00391 dinfo[subUnit].audioCtrl.out[chan]=chan; 00392 dinfo[subUnit].audioCtrl.vol[chan]=0xff; 00393 } 00394 // stop audio 00395 StopAudio(subUnit); 00396 return result; 00397 } 00398 00399 bool CMscdex::HasDrive(Bit16u drive) { 00400 return (GetSubUnit(drive) != 0xff); 00401 } 00402 00403 void CMscdex::ReplaceDrive(CDROM_Interface* newCdrom, Bit8u subUnit) { 00404 if (cdrom[subUnit] != NULL) { 00405 StopAudio(subUnit); 00406 delete cdrom[subUnit]; 00407 } 00408 cdrom[subUnit] = newCdrom; 00409 } 00410 00411 PhysPt CMscdex::GetDefaultBuffer(void) { 00412 if (defaultBufSeg==0) { 00413 Bit16u size = (2352*2+15)/16; 00414 defaultBufSeg = DOS_GetMemory(size,"MSCDEX default buffer"); 00415 } 00416 return PhysMake(defaultBufSeg,2352); 00417 } 00418 00419 PhysPt CMscdex::GetTempBuffer(void) { 00420 if (defaultBufSeg==0) { 00421 Bit16u size = (2352*2+15)/16; 00422 defaultBufSeg = DOS_GetMemory(size,"MSCDEX temp buffer"); 00423 } 00424 return PhysMake(defaultBufSeg,0); 00425 } 00426 00427 void CMscdex::GetDriverInfo (PhysPt data) { 00428 for (Bit16u i=0; i<GetNumDrives(); i++) { 00429 mem_writeb(data ,(Bit8u)i); // subunit 00430 mem_writed(data+1,RealMake(rootDriverHeaderSeg,0)); 00431 data+=5; 00432 } 00433 } 00434 00435 bool CMscdex::GetCDInfo(Bit8u subUnit, Bit8u& tr1, Bit8u& tr2, TMSF& leadOut) { 00436 if (subUnit>=numDrives) return false; 00437 int tr1i,tr2i; 00438 // Assume Media change 00439 cdrom[subUnit]->InitNewMedia(); 00440 dinfo[subUnit].lastResult = cdrom[subUnit]->GetAudioTracks(tr1i,tr2i,leadOut); 00441 if (!dinfo[subUnit].lastResult) { 00442 tr1 = tr2 = 0; 00443 memset(&leadOut,0,sizeof(leadOut)); 00444 } else { 00445 tr1 = (Bit8u) tr1i; 00446 tr2 = (Bit8u) tr2i; 00447 } 00448 return dinfo[subUnit].lastResult; 00449 } 00450 00451 bool CMscdex::GetTrackInfo(Bit8u subUnit, Bit8u track, Bit8u& attr, TMSF& start) { 00452 if (subUnit>=numDrives) return false; 00453 dinfo[subUnit].lastResult = cdrom[subUnit]->GetAudioTrackInfo(track,start,attr); 00454 if (!dinfo[subUnit].lastResult) { 00455 attr = 0; 00456 memset(&start,0,sizeof(start)); 00457 } 00458 return dinfo[subUnit].lastResult; 00459 } 00460 00461 bool CMscdex::PlayAudioSector(Bit8u subUnit, Bit32u sector, Bit32u length) { 00462 if (subUnit>=numDrives) return false; 00463 // If value from last stop is used, this is meant as a resume 00464 // better start using resume command 00465 if (dinfo[subUnit].audioPaused && (sector==dinfo[subUnit].audioStart) && (dinfo[subUnit].audioEnd!=0)) { 00466 dinfo[subUnit].lastResult = cdrom[subUnit]->PauseAudio(true); 00467 } else 00468 dinfo[subUnit].lastResult = cdrom[subUnit]->PlayAudioSector(sector,length); 00469 00470 if (dinfo[subUnit].lastResult) { 00471 dinfo[subUnit].audioPlay = true; 00472 dinfo[subUnit].audioPaused = false; 00473 dinfo[subUnit].audioStart = sector; 00474 dinfo[subUnit].audioEnd = length; 00475 } 00476 return dinfo[subUnit].lastResult; 00477 } 00478 00479 bool CMscdex::PlayAudioMSF(Bit8u subUnit, Bit32u start, Bit32u length) { 00480 if (subUnit>=numDrives) return false; 00481 Bit8u min = (Bit8u)(start>>16) & 0xFF; 00482 Bit8u sec = (Bit8u)(start>> 8) & 0xFF; 00483 Bit8u fr = (Bit8u)(start>> 0) & 0xFF; 00484 Bit32u sector = min*60u*75u+sec*75u+fr - 150u; 00485 return dinfo[subUnit].lastResult = PlayAudioSector(subUnit,sector,length); 00486 } 00487 00488 bool CMscdex::GetSubChannelData(Bit8u subUnit, Bit8u& attr, Bit8u& track, Bit8u &index, TMSF& rel, TMSF& abs) { 00489 if (subUnit>=numDrives) return false; 00490 dinfo[subUnit].lastResult = cdrom[subUnit]->GetAudioSub(attr,track,index,rel,abs); 00491 if (!dinfo[subUnit].lastResult) { 00492 attr = track = index = 0; 00493 memset(&rel,0,sizeof(rel)); 00494 memset(&abs,0,sizeof(abs)); 00495 } 00496 return dinfo[subUnit].lastResult; 00497 } 00498 00499 bool CMscdex::GetAudioStatus(Bit8u subUnit, bool& playing, bool& pause, TMSF& start, TMSF& end) { 00500 if (subUnit>=numDrives) return false; 00501 dinfo[subUnit].lastResult = cdrom[subUnit]->GetAudioStatus(playing,pause); 00502 if (dinfo[subUnit].lastResult) { 00503 if (playing) { 00504 // Start 00505 Bit32u addr = dinfo[subUnit].audioStart + 150; 00506 start.fr = (Bit8u)(addr%75); addr/=75; 00507 start.sec = (Bit8u)(addr%60); 00508 start.min = (Bit8u)(addr/60); 00509 // End 00510 addr = dinfo[subUnit].audioEnd + 150; 00511 end.fr = (Bit8u)(addr%75); addr/=75; 00512 end.sec = (Bit8u)(addr%60); 00513 end.min = (Bit8u)(addr/60); 00514 } else { 00515 memset(&start,0,sizeof(start)); 00516 memset(&end,0,sizeof(end)); 00517 } 00518 } else { 00519 playing = false; 00520 pause = false; 00521 memset(&start,0,sizeof(start)); 00522 memset(&end,0,sizeof(end)); 00523 } 00524 00525 return dinfo[subUnit].lastResult; 00526 } 00527 00528 bool CMscdex::StopAudio(Bit8u subUnit) { 00529 if (subUnit>=numDrives) return false; 00530 if (dinfo[subUnit].audioPlay) { 00531 // Check if audio is still playing.... 00532 TMSF start,end; 00533 bool playing,pause; 00534 if (GetAudioStatus(subUnit,playing,pause,start,end)) 00535 dinfo[subUnit].audioPlay = playing; 00536 else 00537 dinfo[subUnit].audioPlay = false; 00538 } 00539 if (dinfo[subUnit].audioPlay) 00540 dinfo[subUnit].lastResult = cdrom[subUnit]->PauseAudio(false); 00541 else 00542 dinfo[subUnit].lastResult = cdrom[subUnit]->StopAudio(); 00543 00544 if (dinfo[subUnit].lastResult) { 00545 if (dinfo[subUnit].audioPlay) { 00546 TMSF pos; 00547 GetCurrentPos(subUnit,pos); 00548 dinfo[subUnit].audioStart = pos.min*60u*75u+pos.sec*75u+pos.fr - 150u; 00549 dinfo[subUnit].audioPaused = true; 00550 } else { 00551 dinfo[subUnit].audioPaused = false; 00552 dinfo[subUnit].audioStart = 0; 00553 dinfo[subUnit].audioEnd = 0; 00554 } 00555 dinfo[subUnit].audioPlay = false; 00556 } 00557 return dinfo[subUnit].lastResult; 00558 } 00559 00560 bool CMscdex::ResumeAudio(Bit8u subUnit) { 00561 if (subUnit>=numDrives) return false; 00562 return dinfo[subUnit].lastResult = PlayAudioSector(subUnit,dinfo[subUnit].audioStart,dinfo[subUnit].audioEnd); 00563 } 00564 00565 Bit32u CMscdex::GetVolumeSize(Bit8u subUnit) { 00566 if (subUnit>=numDrives) return false; 00567 Bit8u tr1,tr2; 00568 TMSF leadOut; 00569 dinfo[subUnit].lastResult = GetCDInfo(subUnit,tr1,tr2,leadOut); 00570 if (dinfo[subUnit].lastResult) return (leadOut.min*60u*75u)+(leadOut.sec*75u)+leadOut.fr; 00571 return 0; 00572 } 00573 00574 bool CMscdex::ReadVTOC(Bit16u drive, Bit16u volume, PhysPt data, Bit16u& offset, Bit16u& error) { 00575 Bit8u subunit = GetSubUnit(drive); 00576 /* if (subunit>=numDrives) { 00577 error=MSCDEX_ERROR_UNKNOWN_DRIVE; 00578 return false; 00579 } */ 00580 if (!ReadSectors(subunit,false,16u+volume,1u,data)) { 00581 error=MSCDEX_ERROR_DRIVE_NOT_READY; 00582 return false; 00583 } 00584 char id[5]; 00585 MEM_BlockRead(data + 1, id, 5); 00586 if (strncmp("CD001", id, 5)==0) offset = 0; 00587 else { 00588 MEM_BlockRead(data + 9, id, 5); 00589 if (strncmp("CDROM", id, 5)==0) offset = 8; 00590 else { 00591 error = MSCDEX_ERROR_BAD_FORMAT; 00592 return false; 00593 } 00594 } 00595 Bit8u type = mem_readb(data + offset); 00596 error = (type == 1) ? 1 : (type == 0xFF) ? 0xFF : 0; 00597 return true; 00598 } 00599 00600 bool CMscdex::GetVolumeName(Bit8u subUnit, char* data) { 00601 if (subUnit>=numDrives) return false; 00602 Bit16u drive = dinfo[subUnit].drive; 00603 00604 Bit16u offset = 0, error; 00605 bool success = false; 00606 PhysPt ptoc = GetTempBuffer(); 00607 success = ReadVTOC(drive,0x00,ptoc,offset,error); 00608 if (success) { 00609 MEM_StrCopy(ptoc+offset+40,data,31); 00610 data[31] = 0; 00611 rtrim(data); 00612 } 00613 00614 return success; 00615 } 00616 00617 bool CMscdex::GetFileName(Bit16u drive, Bit16u pos, PhysPt data) { 00618 Bit16u offset = 0, error; 00619 bool success = false; 00620 PhysPt ptoc = GetTempBuffer(); 00621 success = ReadVTOC(drive,0x00,ptoc,offset,error); 00622 if (success) { 00623 Bit8u len; 00624 for (len=0;len<37;len++) { 00625 Bit8u c=mem_readb(ptoc+offset+pos+len); 00626 if (c==0 || c==0x20) break; 00627 } 00628 MEM_BlockCopy(data,ptoc+offset+pos,len); 00629 mem_writeb(data+len,0); 00630 } 00631 return success; 00632 } 00633 00634 bool CMscdex::GetUPC(Bit8u subUnit, Bit8u& attr, char* upc) 00635 { 00636 if (subUnit>=numDrives) return false; 00637 return dinfo[subUnit].lastResult = cdrom[subUnit]->GetUPC(attr,&upc[0]); 00638 } 00639 00640 bool CMscdex::ReadSectors(Bit8u subUnit, bool raw, Bit32u sector, Bit16u num, PhysPt data) { 00641 if (subUnit>=numDrives) return false; 00642 if ((cpu_cycles_count_t)(4u*num*2048u+5u) < CPU_Cycles) CPU_Cycles -= cpu_cycles_count_t(4u*num*2048u); 00643 else CPU_Cycles = 5u; 00644 dinfo[subUnit].lastResult = cdrom[subUnit]->ReadSectors(data,raw,sector,num); 00645 return dinfo[subUnit].lastResult; 00646 } 00647 00648 bool CMscdex::ReadSectorsMSF(Bit8u subUnit, bool raw, Bit32u start, Bit16u num, PhysPt data) { 00649 if (subUnit>=numDrives) return false; 00650 Bit8u min = (Bit8u)(start>>16) & 0xFF; 00651 Bit8u sec = (Bit8u)(start>> 8) & 0xFF; 00652 Bit8u fr = (Bit8u)(start>> 0) & 0xFF; 00653 Bit32u sector = min*60u*75u+sec*75u+fr - 150u; 00654 return ReadSectors(subUnit,raw,sector,num,data); 00655 } 00656 00657 // Called from INT 2F 00658 bool CMscdex::ReadSectors(Bit16u drive, Bit32u sector, Bit16u num, PhysPt data) { 00659 return ReadSectors(GetSubUnit(drive),false,sector,num,data); 00660 } 00661 00662 bool CMscdex::GetDirectoryEntry(Bit16u drive, bool copyFlag, PhysPt pathname, PhysPt buffer, Bit16u& error) { 00663 char volumeID[6] = {0}; 00664 char searchName[256]; 00665 char entryName[256]; 00666 bool foundComplete = false; 00667 bool nextPart = true; 00668 const char* useName = NULL; 00669 Bit8u entryLength, nameLength; 00670 // clear error 00671 error = 0; 00672 MEM_StrCopy(pathname+1,searchName,mem_readb(pathname)); 00673 upcase(searchName); 00674 char* searchPos = searchName; 00675 00676 //strip of tailing . (XCOM APOCALYPSE) 00677 size_t searchlen = strlen(searchName); 00678 if (searchlen > 1 && strcmp(searchName,"..")) 00679 if (searchName[searchlen-1] =='.') searchName[searchlen-1] = 0; 00680 00681 //LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Get DirEntry : Find : %s",searchName); 00682 // read vtoc 00683 PhysPt defBuffer = GetDefaultBuffer(); 00684 if (!ReadSectors(GetSubUnit(drive),false,16,1,defBuffer)) return false; 00685 MEM_StrCopy(defBuffer+1,volumeID,5); volumeID[5] = 0; 00686 bool iso = (strcmp("CD001",volumeID)==0); 00687 if (!iso) { 00688 MEM_StrCopy(defBuffer+9,volumeID,5); 00689 if (strcmp("CDROM",volumeID)!=0) E_Exit("MSCDEX: GetDirEntry: Not an ISO 9660 or HSF CD."); 00690 } 00691 Bit16u offset = iso ? 156:180; 00692 // get directory position 00693 Bit32u dirEntrySector = mem_readd(defBuffer+offset+2); 00694 Bits dirSize = (Bit32s)mem_readd(defBuffer+offset+10); 00695 while (dirSize>0) { 00696 Bit16u index = 0; 00697 if (!ReadSectors(GetSubUnit(drive),false,dirEntrySector,1,defBuffer)) return false; 00698 // Get string part 00699 bool foundName = false; 00700 if (nextPart) { 00701 if (searchPos) { 00702 useName = searchPos; 00703 searchPos = strchr(searchPos,'\\'); 00704 } 00705 if (searchPos) { *searchPos = 0; searchPos++; } 00706 else foundComplete = true; 00707 } 00708 00709 do { 00710 entryLength = mem_readb(defBuffer+index); 00711 if (entryLength==0) break; 00712 if (mem_readb(defBuffer + index + (iso?0x19:0x18) ) & 4) { 00713 // skip associated files 00714 index += entryLength; 00715 continue; 00716 } 00717 nameLength = mem_readb(defBuffer+index+32); 00718 MEM_StrCopy(defBuffer+index+33,entryName,nameLength); 00719 // strip separator and file version number 00720 char* separator = strchr(entryName,';'); 00721 if (separator) *separator = 0; 00722 // strip trailing period 00723 size_t entrylen = strlen(entryName); 00724 if (entrylen>0 && entryName[entrylen-1]=='.') entryName[entrylen-1] = 0; 00725 00726 if (strcmp(entryName,useName)==0) { 00727 //LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Get DirEntry : Found : %s",useName); 00728 foundName = true; 00729 break; 00730 } 00731 index += entryLength; 00732 } while (index+33<=2048); 00733 00734 if (foundName) { 00735 if (foundComplete) { 00736 if (copyFlag) { 00737 LOG(LOG_MISC,LOG_WARN)("MSCDEX: GetDirEntry: Copyflag structure not entirely accurate maybe"); 00738 Bit8u readBuf[256]; 00739 Bit8u writeBuf[256]; 00740 MEM_BlockRead( defBuffer+index, readBuf, entryLength ); 00741 writeBuf[0] = readBuf[1]; // 00h BYTE length of XAR in Logical Block Numbers 00742 memcpy( &writeBuf[1], &readBuf[0x2], 4); // 01h DWORD Logical Block Number of file start 00743 writeBuf[5] = 0;writeBuf[6] = 8; // 05h WORD size of disk in logical blocks 00744 memcpy( &writeBuf[7], &readBuf[0xa], 4); // 07h DWORD file length in bytes 00745 memcpy( &writeBuf[0xb], &readBuf[0x12], 6); // 0bh BYTEs date and time 00746 writeBuf[0x11] = iso ? readBuf[0x18]:0; // 11h BYTE time zone 00747 writeBuf[0x12] = readBuf[iso ? 0x19:0x18]; // 12h BYTE bit flags 00748 writeBuf[0x13] = readBuf[0x1a]; // 13h BYTE interleave size 00749 writeBuf[0x14] = readBuf[0x1b]; // 14h BYTE interleave skip factor 00750 memcpy( &writeBuf[0x15], &readBuf[0x1c], 2); // 15h WORD volume set sequence number 00751 writeBuf[0x17] = readBuf[0x20]; 00752 memcpy( &writeBuf[0x18], &readBuf[21], readBuf[0x20] <= 38 ? readBuf[0x20] : 38 ); 00753 MEM_BlockWrite( buffer, writeBuf, 0x18 + 40 ); 00754 } else { 00755 // Direct copy 00756 MEM_BlockCopy(buffer,defBuffer+index,entryLength); 00757 } 00758 error = iso ? 1:0; 00759 return true; 00760 } 00761 // change directory 00762 dirEntrySector = mem_readd(defBuffer+index+2); 00763 dirSize = (Bit32s)mem_readd(defBuffer+index+10); 00764 nextPart = true; 00765 } else { 00766 // continue search in next sector 00767 dirSize -= 2048; 00768 dirEntrySector++; 00769 nextPart = false; 00770 } 00771 } 00772 error = 2; // file not found 00773 return false; // not found 00774 } 00775 00776 bool CMscdex::GetCurrentPos(Bit8u subUnit, TMSF& pos) { 00777 if (subUnit>=numDrives) return false; 00778 TMSF rel; 00779 Bit8u attr,track,index; 00780 dinfo[subUnit].lastResult = GetSubChannelData(subUnit, attr, track, index, rel, pos); 00781 if (!dinfo[subUnit].lastResult) memset(&pos,0,sizeof(pos)); 00782 return dinfo[subUnit].lastResult; 00783 } 00784 00785 bool CMscdex::GetMediaStatus(Bit8u subUnit, bool& media, bool& changed, bool& trayOpen) { 00786 if (subUnit>=numDrives) return false; 00787 dinfo[subUnit].lastResult = cdrom[subUnit]->GetMediaTrayStatus(media,changed,trayOpen); 00788 return dinfo[subUnit].lastResult; 00789 } 00790 00791 Bit32u CMscdex::GetDeviceStatus(Bit8u subUnit) { 00792 if (subUnit>=numDrives) return false; 00793 bool media,changed,trayOpen; 00794 00795 dinfo[subUnit].lastResult = GetMediaStatus(subUnit,media,changed,trayOpen); 00796 if (dinfo[subUnit].audioPlay) { 00797 // Check if audio is still playing.... 00798 TMSF start,end; 00799 bool playing,pause; 00800 if (GetAudioStatus(subUnit,playing,pause,start,end)) 00801 dinfo[subUnit].audioPlay = playing; 00802 else 00803 dinfo[subUnit].audioPlay = false; 00804 } 00805 00806 Bit32u status = ((trayOpen?1u:0u) << 0u) | // Drive is open ? 00807 ((dinfo[subUnit].locked?1u:0u) << 1u) | // Drive is locked ? 00808 (1u<<2u) | // raw + cooked sectors 00809 (1u<<4u) | // Can read sudio 00810 (1u<<8u) | // Can control audio 00811 (1u<<9u) | // Red book & HSG 00812 ((dinfo[subUnit].audioPlay?1u:0u) << 10u) | // Audio is playing ? 00813 ((media?0u:1u) << 11u); // Drive is empty ? 00814 return status; 00815 } 00816 00817 bool CMscdex::GetMediaStatus(Bit8u subUnit, Bit8u& status) { 00818 if (subUnit>=numDrives) return false; 00819 /* bool media,changed,open,result; 00820 result = GetMediaStatus(subUnit,media,changed,open); 00821 status = changed ? 0xFF : 0x01; 00822 return result; */ 00823 status = getSwapRequest() ? 0xFF : 0x01; 00824 return true; 00825 } 00826 00827 bool CMscdex::LoadUnloadMedia(Bit8u subUnit, bool unload) { 00828 if (subUnit>=numDrives) return false; 00829 dinfo[subUnit].lastResult = cdrom[subUnit]->LoadUnloadMedia(unload); 00830 return dinfo[subUnit].lastResult; 00831 } 00832 00833 bool CMscdex::SendDriverRequest(Bit16u drive, PhysPt data) { 00834 Bit8u subUnit = GetSubUnit(drive); 00835 if (subUnit>=numDrives) return false; 00836 // Get SubUnit 00837 mem_writeb(data+1,subUnit); 00838 // Call Strategy / Interrupt 00839 MSCDEX_Strategy_Handler(); 00840 MSCDEX_Interrupt_Handler(); 00841 return true; 00842 } 00843 00844 Bit16u CMscdex::GetStatusWord(Bit8u subUnit,Bit16u status) { 00845 if (subUnit>=numDrives) return REQUEST_STATUS_ERROR | 0x02; // error : Drive not ready 00846 00847 if (dinfo[subUnit].lastResult) status |= REQUEST_STATUS_DONE; // ok 00848 else status |= REQUEST_STATUS_ERROR; 00849 00850 if (dinfo[subUnit].audioPlay) { 00851 // Check if audio is still playing.... 00852 TMSF start,end; 00853 bool playing,pause; 00854 if (GetAudioStatus(subUnit,playing,pause,start,end)) { 00855 dinfo[subUnit].audioPlay = playing; 00856 } else 00857 dinfo[subUnit].audioPlay = false; 00858 00859 status |= (dinfo[subUnit].audioPlay<<9); 00860 } 00861 dinfo[subUnit].lastResult = true; 00862 return status; 00863 } 00864 00865 void CMscdex::InitNewMedia(Bit8u subUnit) { 00866 if (subUnit<numDrives) { 00867 // Reopen new media 00868 cdrom[subUnit]->InitNewMedia(); 00869 } 00870 } 00871 00872 bool CMscdex::ChannelControl(Bit8u subUnit, TCtrl ctrl) { 00873 if (subUnit>=numDrives) return false; 00874 // adjust strange channel mapping 00875 if (ctrl.out[0]>1) ctrl.out[0]=0; 00876 if (ctrl.out[1]>1) ctrl.out[1]=1; 00877 dinfo[subUnit].audioCtrl=ctrl; 00878 cdrom[subUnit]->ChannelControl(ctrl); 00879 return true; 00880 } 00881 00882 bool CMscdex::GetChannelControl(Bit8u subUnit, TCtrl& ctrl) { 00883 if (subUnit>=numDrives) return false; 00884 ctrl=dinfo[subUnit].audioCtrl; 00885 return true; 00886 } 00887 00888 static CMscdex* mscdex = 0; 00889 static PhysPt curReqheaderPtr = 0; 00890 00891 bool GetMSCDEXDrive(unsigned char drive_letter,CDROM_Interface **_cdrom) { 00892 Bitu i; 00893 00894 if (mscdex == NULL) { 00895 if (_cdrom) *_cdrom = NULL; 00896 return false; 00897 } 00898 00899 for (i=0;i < MSCDEX_MAX_DRIVES;i++) { 00900 if (mscdex->cdrom[i] == NULL) continue; 00901 if (mscdex->dinfo[i].drive == drive_letter) { 00902 if (_cdrom) *_cdrom = mscdex->cdrom[i]; 00903 return true; 00904 } 00905 } 00906 00907 return false; 00908 } 00909 00910 static Bit16u MSCDEX_IOCTL_Input(PhysPt buffer,Bit8u drive_unit) { 00911 Bit8u ioctl_fct = mem_readb(buffer); 00912 MSCDEX_LOG("MSCDEX: IOCTL INPUT Subfunction %02X",(int)ioctl_fct); 00913 switch (ioctl_fct) { 00914 case 0x00 : /* Get Device Header address */ 00915 mem_writed(buffer+1,RealMake(mscdex->rootDriverHeaderSeg,0)); 00916 break; 00917 case 0x01 :{/* Get current position */ 00918 TMSF pos; 00919 mscdex->GetCurrentPos(drive_unit,pos); 00920 Bit8u addr_mode = mem_readb(buffer+1); 00921 if (addr_mode==0) { // HSG 00922 Bit32u frames=MSF_TO_FRAMES(pos.min, pos.sec, pos.fr); 00923 if (frames<150) MSCDEX_LOG_ERROR("MSCDEX: Get position: invalid position %d:%d:%d", pos.min, pos.sec, pos.fr); 00924 else frames-=150; 00925 mem_writed(buffer+2,frames); 00926 } else if (addr_mode==1) { // Red book 00927 mem_writeb(buffer+2,pos.fr); 00928 mem_writeb(buffer+3,pos.sec); 00929 mem_writeb(buffer+4,pos.min); 00930 mem_writeb(buffer+5,0x00); 00931 } else { 00932 MSCDEX_LOG_ERROR("MSCDEX: Get position: invalid address mode %x",addr_mode); 00933 return 0x03; // invalid function 00934 } 00935 }break; 00936 case 0x04 : /* Audio Channel control */ 00937 TCtrl ctrl; 00938 if (!mscdex->GetChannelControl(drive_unit,ctrl)) return 0x01; 00939 for (Bit8u chan=0;chan<4;chan++) { 00940 mem_writeb(buffer+chan*2u+1u,ctrl.out[chan]); 00941 mem_writeb(buffer+chan*2u+2u,ctrl.vol[chan]); 00942 } 00943 break; 00944 case 0x06 : /* Get Device status */ 00945 mem_writed(buffer+1,mscdex->GetDeviceStatus(drive_unit)); 00946 break; 00947 case 0x07 : /* Get sector size */ 00948 if (mem_readb(buffer+1)==0) mem_writew(buffer+2,2048); 00949 else if (mem_readb(buffer+1)==1) mem_writew(buffer+2,2352); 00950 else return 0x03; // invalid function 00951 break; 00952 case 0x08 : /* Get size of current volume */ 00953 mem_writed(buffer+1,mscdex->GetVolumeSize(drive_unit)); 00954 break; 00955 case 0x09 : /* Media change ? */ 00956 Bit8u status; 00957 if (!mscdex->GetMediaStatus(drive_unit,status)) { 00958 status = 0; // state unknown 00959 } 00960 mem_writeb(buffer+1,status); 00961 break; 00962 case 0x0A : /* Get Audio Disk info */ 00963 Bit8u tr1,tr2; TMSF leadOut; 00964 if (!mscdex->GetCDInfo(drive_unit,tr1,tr2,leadOut)) return 0x05; 00965 mem_writeb(buffer+1,tr1); 00966 mem_writeb(buffer+2,tr2); 00967 mem_writeb(buffer+3,leadOut.fr); 00968 mem_writeb(buffer+4,leadOut.sec); 00969 mem_writeb(buffer+5,leadOut.min); 00970 mem_writeb(buffer+6,0x00); 00971 break; 00972 case 0x0B :{/* Audio Track Info */ 00973 Bit8u attr; TMSF start; 00974 Bit8u track = mem_readb(buffer+1); 00975 mscdex->GetTrackInfo(drive_unit,track,attr,start); 00976 mem_writeb(buffer+2,start.fr); 00977 mem_writeb(buffer+3,start.sec); 00978 mem_writeb(buffer+4,start.min); 00979 mem_writeb(buffer+5,0x00); 00980 mem_writeb(buffer+6,attr); 00981 break; } 00982 case 0x0C :{/* Get Audio Sub Channel data */ 00983 Bit8u attr,track,index; 00984 TMSF abs,rel; 00985 mscdex->GetSubChannelData(drive_unit,attr,track,index,rel,abs); 00986 mem_writeb(buffer+1,attr); 00987 mem_writeb(buffer+2,track); 00988 mem_writeb(buffer+3,index); 00989 mem_writeb(buffer+4,rel.min); 00990 mem_writeb(buffer+5,rel.sec); 00991 mem_writeb(buffer+6,rel.fr); 00992 mem_writeb(buffer+7,0x00); 00993 mem_writeb(buffer+8,abs.min); 00994 mem_writeb(buffer+9,abs.sec); 00995 mem_writeb(buffer+10,abs.fr); 00996 break; 00997 } 00998 case 0x0E :{ /* Get UPC */ 00999 Bit8u attr; char upc[8]; 01000 mscdex->GetUPC(drive_unit,attr,&upc[0]); 01001 mem_writeb(buffer+1u,attr); 01002 for (unsigned int i=0; i<7; i++) mem_writeb(buffer+2u+i,(unsigned char)upc[i]); 01003 mem_writeb(buffer+9,0x00u); 01004 break; 01005 } 01006 case 0x0F :{ /* Get Audio Status */ 01007 bool playing,pause; 01008 TMSF resStart,resEnd; 01009 mscdex->GetAudioStatus(drive_unit,playing,pause,resStart,resEnd); 01010 mem_writeb(buffer+1u,pause); 01011 mem_writeb(buffer+3u,resStart.min); 01012 mem_writeb(buffer+4u,resStart.sec); 01013 mem_writeb(buffer+5u,resStart.fr); 01014 mem_writeb(buffer+6u,0x00u); 01015 mem_writeb(buffer+7u,resEnd.min); 01016 mem_writeb(buffer+8u,resEnd.sec); 01017 mem_writeb(buffer+9u,resEnd.fr); 01018 mem_writeb(buffer+10u,0x00u); 01019 break; 01020 } 01021 default : LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Unsupported IOCTL INPUT Subfunction %02X",(int)ioctl_fct); 01022 return 0x03; // invalid function 01023 } 01024 return 0x00; // success 01025 } 01026 01027 static Bit16u MSCDEX_IOCTL_Optput(PhysPt buffer,Bit8u drive_unit) { 01028 Bit8u ioctl_fct = mem_readb(buffer); 01029 // MSCDEX_LOG("MSCDEX: IOCTL OUTPUT Subfunction %02X",ioctl_fct); 01030 switch (ioctl_fct) { 01031 case 0x00 : // Unload /eject media 01032 if (!mscdex->LoadUnloadMedia(drive_unit,true)) return 0x02; 01033 break; 01034 case 0x03: //Audio Channel control 01035 TCtrl ctrl; 01036 for (Bit8u chan=0;chan<4;chan++) { 01037 ctrl.out[chan]=mem_readb(buffer+chan*2u+1u); 01038 ctrl.vol[chan]=mem_readb(buffer+chan*2u+2u); 01039 } 01040 if (!mscdex->ChannelControl(drive_unit,ctrl)) return 0x01; 01041 break; 01042 case 0x01 : // (un)Lock door 01043 // do nothing -> report as success 01044 break; 01045 case 0x02 : // Reset Drive 01046 LOG(LOG_MISC,LOG_WARN)("cdromDrive reset"); 01047 if (!mscdex->StopAudio(drive_unit)) return 0x02; 01048 break; 01049 case 0x05 : // load media 01050 if (!mscdex->LoadUnloadMedia(drive_unit,false)) return 0x02; 01051 break; 01052 default : LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Unsupported IOCTL OUTPUT Subfunction %02X",(int)ioctl_fct); 01053 return 0x03; // invalid function 01054 } 01055 return 0x00; // success 01056 } 01057 01058 static Bitu MSCDEX_Strategy_Handler(void) { 01059 curReqheaderPtr = PhysMake(SegValue(es),reg_bx); 01060 // MSCDEX_LOG("MSCDEX: Device Strategy Routine called, request header at %x",curReqheaderPtr); 01061 return CBRET_NONE; 01062 } 01063 01064 static Bitu MSCDEX_Interrupt_Handler(void) { 01065 if (curReqheaderPtr==0) { 01066 MSCDEX_LOG_ERROR("MSCDEX: invalid call to interrupt handler"); 01067 return CBRET_NONE; 01068 } 01069 Bit8u subUnit = mem_readb(curReqheaderPtr+1); 01070 Bit8u funcNr = mem_readb(curReqheaderPtr+2); 01071 Bit16u errcode = 0; 01072 PhysPt buffer = 0; 01073 01074 MSCDEX_LOG("MSCDEX: Driver Function %02X",funcNr); 01075 01076 if ((funcNr==0x03) || (funcNr==0x0c) || (funcNr==0x80) || (funcNr==0x82)) { 01077 buffer = PhysMake(mem_readw(curReqheaderPtr+0x10),mem_readw(curReqheaderPtr+0x0E)); 01078 } 01079 01080 switch (funcNr) { 01081 case 0x03 : { /* IOCTL INPUT */ 01082 Bit16u error=MSCDEX_IOCTL_Input(buffer,subUnit); 01083 if (error) errcode = error; 01084 break; 01085 } 01086 case 0x0C : { /* IOCTL OUTPUT */ 01087 Bit16u error=MSCDEX_IOCTL_Optput(buffer,subUnit); 01088 if (error) errcode = error; 01089 break; 01090 } 01091 case 0x0D : // device open 01092 case 0x0E : // device close - dont care :) 01093 break; 01094 case 0x80 : // Read long 01095 case 0x82 : { // Read long prefetch -> both the same here :) 01096 Bit32u start = mem_readd(curReqheaderPtr+0x14); 01097 Bit16u len = mem_readw(curReqheaderPtr+0x12); 01098 bool raw = (mem_readb(curReqheaderPtr+0x18)==1); 01099 if (mem_readb(curReqheaderPtr+0x0D)==0x00) // HSG 01100 mscdex->ReadSectors(subUnit,raw,start,len,buffer); 01101 else 01102 mscdex->ReadSectorsMSF(subUnit,raw,start,len,buffer); 01103 break; 01104 } 01105 case 0x83 : // Seek - dont care :) 01106 break; 01107 case 0x84 : { /* Play Audio Sectors */ 01108 Bit32u start = mem_readd(curReqheaderPtr+0x0E); 01109 Bit32u len = mem_readd(curReqheaderPtr+0x12); 01110 if (mem_readb(curReqheaderPtr+0x0D)==0x00) // HSG 01111 mscdex->PlayAudioSector(subUnit,start,len); 01112 else // RED BOOK 01113 mscdex->PlayAudioMSF(subUnit,start,len); 01114 break; 01115 } 01116 case 0x85 : /* Stop Audio */ 01117 mscdex->StopAudio(subUnit); 01118 break; 01119 case 0x88 : /* Resume Audio */ 01120 mscdex->ResumeAudio(subUnit); 01121 break; 01122 default : MSCDEX_LOG_ERROR("Unsupported Driver Request %02X",funcNr); 01123 break; 01124 01125 } 01126 01127 // Set Statusword 01128 mem_writew(curReqheaderPtr+3,mscdex->GetStatusWord(subUnit,errcode)); 01129 MSCDEX_LOG("MSCDEX: Status : %04X",mem_readw(curReqheaderPtr+3)); 01130 return CBRET_NONE; 01131 } 01132 01133 static bool MSCDEX_Handler(void) { 01134 if(reg_ah == 0x11) { 01135 if(reg_al == 0x00) { 01136 if (mscdex->rootDriverHeaderSeg==0) return false; 01137 PhysPt check = PhysMake(SegValue(ss),reg_sp); 01138 if(mem_readw(check+6) == 0xDADA) { 01139 //MSCDEX sets word on stack to ADAD if it DADA on entry. 01140 mem_writew(check+6,0xADAD); 01141 } 01142 reg_al = 0xff; 01143 return true; 01144 } else { 01145 LOG(LOG_MISC,LOG_ERROR)("NETWORK REDIRECTOR USED!!!"); 01146 reg_ax = 0x49;//NETWERK SOFTWARE NOT INSTALLED 01147 CALLBACK_SCF(true); 01148 return true; 01149 } 01150 } 01151 01152 if (reg_ah!=0x15) return false; // not handled here, continue chain 01153 if (mscdex->rootDriverHeaderSeg==0) return false; // not handled if MSCDEX not installed 01154 01155 PhysPt data = PhysMake(SegValue(es),reg_bx); 01156 MSCDEX_LOG("MSCDEX: INT 2F AX=%04X BX=%04X CX=%04X",reg_ax,reg_bx,reg_cx); 01157 CALLBACK_SCF(false); // carry flag cleared for all functions (undocumented); only set on error 01158 switch (reg_ax) { 01159 case 0x1500: /* Install check */ 01160 reg_bx = mscdex->GetNumDrives(); 01161 if (reg_bx>0) reg_cx = mscdex->GetFirstDrive(); 01162 reg_al = 0xff; 01163 break; 01164 case 0x1501: /* Get cdrom driver info */ 01165 mscdex->GetDriverInfo(data); 01166 break; 01167 case 0x1502: /* Get Copyright filename */ 01168 case 0x1503: /* Get Abstract filename */ 01169 case 0x1504: /* Get Documentation filename */ 01170 if (!mscdex->GetFileName(reg_cx,702+(reg_al-2)*37,data)) { 01171 reg_ax = MSCDEX_ERROR_UNKNOWN_DRIVE; 01172 CALLBACK_SCF(true); 01173 } 01174 break; 01175 case 0x1505: { // read vtoc 01176 Bit16u offset = 0, error = 0; 01177 bool success = mscdex->ReadVTOC(reg_cx,reg_dx,data,offset,error); 01178 reg_ax = error; 01179 if (!success) CALLBACK_SCF(true); 01180 } 01181 break; 01182 case 0x1506: /* Debugging on */ 01183 case 0x1507: /* Debugging off */ 01184 // not functional in production MSCDEX 01185 break; 01186 case 0x1508: { // read sectors 01187 Bit32u sector = ((Bit32u)reg_si << 16u) + (Bit32u)reg_di; 01188 if (mscdex->ReadSectors(reg_cx,sector,reg_dx,data)) { 01189 reg_ax = 0; 01190 } else { 01191 // possibly: MSCDEX_ERROR_DRIVE_NOT_READY if sector is beyond total length 01192 reg_ax = MSCDEX_ERROR_UNKNOWN_DRIVE; 01193 CALLBACK_SCF(true); 01194 } 01195 } 01196 break; 01197 case 0x1509: // write sectors - not supported 01198 reg_ax = MSCDEX_ERROR_INVALID_FUNCTION; 01199 CALLBACK_SCF(true); 01200 break; 01201 case 0x150A: /* Reserved */ 01202 break; 01203 case 0x150B: /* Valid CDROM drive ? */ 01204 reg_ax = (mscdex->IsValidDrive(reg_cx) ? 0x5ad8 : 0x0000); 01205 reg_bx = 0xADAD; 01206 break; 01207 case 0x150C: /* Get MSCDEX Version */ 01208 reg_bx = mscdex->GetVersion(); 01209 break; 01210 case 0x150D: /* Get drives */ 01211 mscdex->GetDrives(data); 01212 break; 01213 case 0x150E: /* Get/Set Volume Descriptor Preference */ 01214 if (mscdex->IsValidDrive(reg_cx)) { 01215 if (reg_bx == 0) { 01216 // get preference 01217 reg_dx = 0x100; // preference? 01218 } else if (reg_bx == 1) { 01219 // set preference 01220 if (reg_dh != 1) { 01221 reg_ax = MSCDEX_ERROR_INVALID_FUNCTION; 01222 CALLBACK_SCF(true); 01223 } 01224 } else { 01225 reg_ax = MSCDEX_ERROR_INVALID_FUNCTION; 01226 CALLBACK_SCF(true); 01227 } 01228 } else { 01229 reg_ax = MSCDEX_ERROR_UNKNOWN_DRIVE; 01230 CALLBACK_SCF(true); 01231 } 01232 break; 01233 case 0x150F: { // Get directory entry 01234 Bit16u error; 01235 bool success = mscdex->GetDirectoryEntry(reg_cl,reg_ch&1,data,PhysMake(reg_si,reg_di),error); 01236 reg_ax = error; 01237 if (!success) CALLBACK_SCF(true); 01238 } 01239 break; 01240 case 0x1510: /* Device driver request */ 01241 if (!mscdex->SendDriverRequest(reg_cx,data)) { 01242 reg_ax = MSCDEX_ERROR_UNKNOWN_DRIVE; 01243 CALLBACK_SCF(true); 01244 } 01245 break; 01246 default: LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Unknown call : %04X",reg_ax); 01247 reg_ax = MSCDEX_ERROR_INVALID_FUNCTION; 01248 CALLBACK_SCF(true); 01249 break; 01250 } 01251 return true; 01252 } 01253 01254 class device_MSCDEX : public DOS_Device { 01255 public: 01256 device_MSCDEX() { SetName("MSCD001"); } 01257 bool Read (Bit8u * /*data*/,Bit16u * /*size*/) { return false;} 01258 bool Write(const Bit8u * /*data*/,Bit16u * /*size*/) { 01259 LOG(LOG_ALL,LOG_NORMAL)("Write to mscdex device"); 01260 return false; 01261 } 01262 bool Seek(Bit32u * /*pos*/,Bit32u /*type*/){return false;} 01263 bool Close(){return false;} 01264 Bit16u GetInformation(void){return 0xc880;} 01265 bool ReadFromControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode); 01266 bool WriteToControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode); 01267 // private: 01268 // Bit8u cache; 01269 }; 01270 01271 bool device_MSCDEX::ReadFromControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode) { 01272 if (MSCDEX_IOCTL_Input(bufptr,0)==0) { 01273 *retcode=size; 01274 return true; 01275 } 01276 return false; 01277 } 01278 01279 bool device_MSCDEX::WriteToControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode) { 01280 if (MSCDEX_IOCTL_Optput(bufptr,0)==0) { 01281 *retcode=size; 01282 return true; 01283 } 01284 return false; 01285 } 01286 01287 int MSCDEX_AddDrive(char driveLetter, const char* physicalPath, Bit8u& subUnit) 01288 { 01289 int result = mscdex->AddDrive(driveLetter-'A',(char*)physicalPath,subUnit); 01290 return result; 01291 } 01292 01293 int MSCDEX_RemoveDrive(char driveLetter) 01294 { 01295 if(!mscdex) return 0; 01296 return mscdex->RemoveDrive(driveLetter-'A'); 01297 } 01298 01299 bool MSCDEX_HasDrive(char driveLetter) 01300 { 01301 return mscdex->HasDrive(driveLetter-'A'); 01302 } 01303 01304 void MSCDEX_ReplaceDrive(CDROM_Interface* cdrom, Bit8u subUnit) 01305 { 01306 mscdex->ReplaceDrive(cdrom, subUnit); 01307 } 01308 01309 Bit8u MSCDEX_GetSubUnit(char driveLetter) 01310 { 01311 return mscdex->GetSubUnit(driveLetter-'A'); 01312 } 01313 01314 bool MSCDEX_GetVolumeName(Bit8u subUnit, char* name) 01315 { 01316 return mscdex->GetVolumeName(subUnit,name); 01317 } 01318 01319 bool MSCDEX_HasMediaChanged(Bit8u subUnit) 01320 { 01321 static TMSF leadOut[MSCDEX_MAX_DRIVES]; 01322 01323 TMSF leadnew; 01324 Bit8u tr1,tr2; 01325 if (mscdex->GetCDInfo(subUnit,tr1,tr2,leadnew)) { 01326 bool changed = (leadOut[subUnit].min!=leadnew.min) || (leadOut[subUnit].sec!=leadnew.sec) || (leadOut[subUnit].fr!=leadnew.fr); 01327 if (changed) { 01328 leadOut[subUnit].min = leadnew.min; 01329 leadOut[subUnit].sec = leadnew.sec; 01330 leadOut[subUnit].fr = leadnew.fr; 01331 mscdex->InitNewMedia(subUnit); 01332 } 01333 return changed; 01334 } 01335 if (subUnit<MSCDEX_MAX_DRIVES) { 01336 leadOut[subUnit].min = 0; 01337 leadOut[subUnit].sec = 0; 01338 leadOut[subUnit].fr = 0; 01339 } 01340 return true; 01341 } 01342 01343 void MSCDEX_SetCDInterface(int intNr, int numCD) { 01344 useCdromInterface = intNr; 01345 forceCD = numCD; 01346 } 01347 01348 void MSCDEX_ShutDown(Section* /*sec*/) { 01349 if (mscdex != NULL) { 01350 delete mscdex; 01351 mscdex = NULL; 01352 } 01353 01354 curReqheaderPtr = 0; 01355 } 01356 01357 /* HACK: The IDE emulation is messily tied into calling MSCDEX.EXE! 01358 * We cannot shut down the mscdex object when booting into a guest OS! 01359 * Need to fix this, this is backwards! */ 01360 void MSCDEX_DOS_ShutDown(Section* /*sec*/) { 01361 curReqheaderPtr = 0; 01362 } 01363 01364 void MSCDEX_Startup(Section* sec) { 01365 (void)sec;//UNUSED 01366 if (mscdex == NULL) { 01367 LOG(LOG_MISC,LOG_DEBUG)("Allocating MSCDEX.EXE emulation"); 01368 01369 /* Register the mscdex device */ 01370 DOS_Device * newdev = new device_MSCDEX(); 01371 DOS_AddDevice(newdev); 01372 curReqheaderPtr = 0; 01373 /* Add Multiplexer */ 01374 DOS_AddMultiplexHandler(MSCDEX_Handler); 01375 /* Create MSCDEX */ 01376 mscdex = new CMscdex; 01377 } 01378 } 01379 01380 void MSCDEX_Init() { 01381 LOG(LOG_MISC,LOG_DEBUG)("Initializing MSCDEX.EXE emulation"); 01382 01383 AddExitFunction(AddExitFunctionFuncPair(MSCDEX_ShutDown)); 01384 01385 /* in any event that the DOS kernel is shutdown or abruptly wiped from memory */ 01386 AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(MSCDEX_ShutDown)); 01387 AddVMEventFunction(VM_EVENT_DOS_EXIT_BEGIN,AddVMEventFunctionFuncPair(MSCDEX_DOS_ShutDown)); 01388 } 01389 01390 void CMscdex::SaveState( std::ostream& stream ) 01391 { 01392 // - pure data 01393 WRITE_POD( &defaultBufSeg, defaultBufSeg ); 01394 WRITE_POD( &rootDriverHeaderSeg, rootDriverHeaderSeg ); 01395 } 01396 01397 void CMscdex::LoadState( std::istream& stream ) 01398 { 01399 // - pure data 01400 READ_POD( &defaultBufSeg, defaultBufSeg ); 01401 READ_POD( &rootDriverHeaderSeg, rootDriverHeaderSeg ); 01402 } 01403 01404 extern bool dos_kernel_disabled; 01405 01406 void POD_Save_DOS_Mscdex( std::ostream& stream ) 01407 { 01408 if (!dos_kernel_disabled) { 01409 for (Bit8u drive_unit=0; drive_unit<mscdex->GetNumDrives(); drive_unit++) { 01410 TMSF pos, start, end; 01411 bool playing, pause; 01412 01413 mscdex->GetAudioStatus(drive_unit, playing, pause, start, end); 01414 mscdex->GetCurrentPos(drive_unit,pos); 01415 01416 01417 WRITE_POD( &playing, playing ); 01418 WRITE_POD( &pause, pause ); 01419 WRITE_POD( &pos, pos ); 01420 WRITE_POD( &start, start ); 01421 WRITE_POD( &end, end ); 01422 } 01423 01424 mscdex->SaveState(stream); 01425 } 01426 } 01427 01428 01429 void POD_Load_DOS_Mscdex( std::istream& stream ) 01430 { 01431 if (!dos_kernel_disabled) { 01432 for (Bit8u drive_unit=0; drive_unit<mscdex->GetNumDrives(); drive_unit++) { 01433 TMSF pos, start, end; 01434 Bit32u msf_time, play_len; 01435 bool playing, pause; 01436 01437 01438 READ_POD( &playing, playing ); 01439 READ_POD( &pause, pause ); 01440 READ_POD( &pos, pos ); 01441 READ_POD( &start, start ); 01442 READ_POD( &end, end ); 01443 01444 01445 // end = total play time (GetAudioStatus adds +150) 01446 // pos = current play cursor 01447 // start = start play cursor 01448 play_len = end.min * 75 * 60 + ( end.sec * 75 ) + end.fr - 150; 01449 play_len -= ( pos.min - start.min ) * 75 * 60 + ( pos.sec - start.sec ) * 75 + ( pos.fr - start.fr ); 01450 msf_time = ( pos.min << 16 ) + ( pos.sec << 8 ) + ( pos.fr ); 01451 01452 01453 // first play, then simulate pause 01454 mscdex->StopAudio(drive_unit); 01455 01456 if( playing ) mscdex->PlayAudioMSF(drive_unit, msf_time, play_len); 01457 if( pause ) mscdex->PlayAudioMSF(drive_unit, msf_time, 0); 01458 } 01459 01460 mscdex->LoadState(stream); 01461 } 01462 }