Contributor: TORSTEN FRANK
Unit CD_ROM;
Interface
Const
RED=1;
HSG=0;
GlobalError:Word = 0;
GlobalDriveCount:Byte = 0;
MSCDEXOk:Boolean = FALSE;
AudioCD:Boolean = FALSE;
Type
REDUnPack = Record
Frame:Byte;
Second:Byte;
Minute:Byte;
Unused:Byte;
End;
Var
GlobalDriveNo:Byte;
GlobalLeadOut:LongInt;
GlobalTrackMin:Byte;
GlobalTrackMax:Byte;
GlobalAktualTrack :Byte;
TOC:Array [1..99] Of LongInt;
Function RED2HSG(Minute, Second, Frame :LongInt) :LongInt;
Procedure HSG2RED(HSG :LongInt; var Minute, Second, Frame :LongInt);
Procedure Audio_Disk_Info(var TrackMin,TrackMax :Byte; var LeadOut :LongInt);
Procedure Audio_Track_Info(TrackNr :Byte; var TrackStart :LongInt; var CntlAdr
:Byte);Function Media_Changed :Byte;
Procedure Audio_Status(var AudioStatus :Word; var StartResume,EndPlay
:LongInt);Procedure Stop_Audio;
Procedure Pause_Audio;
Procedure Resume_Audio;
Procedure Play_Audio(StartSector,nFrames :LongInt);
Function Location_of_Head(AdressMode :Byte) :LongInt;
Function Device_Status :LongInt;
Procedure Audio_Channel_Info(var
ChOut0,Vol0,ChOut1,Vol1,ChOut2,Vol2,ChOut3,Vol3 :Byte);Procedure
Audio_Q_Channel_Info(var TrackNo,Min,Sec,Frame,Zero,AMin,ASec,AFrame :Byte);
Function Disk_Remain :LongInt;
Function Track_Remain :LongInt;
Function Get_UPC :String;
Procedure MSCDEX_Version(var HVersion, NVersion :Byte);
Function CDROM_Number(var DriveNo :Byte) :Byte;
Procedure MSCDEX_Init;
Procedure ReadTOC;
Procedure Calculate_Track(TrackNo :Byte; var StartSector,TrackLength,DiskEnd
:LongInt);Procedure Play_Track(TrackNo :Byte);
Procedure Play_Track_to_End(TrackNo :Byte);
Procedure Show_Track(TrackNo, Seconds :Byte);
Procedure Next_Track;
Procedure Last_Track;
Procedure Next_Track_to_End;
Procedure Last_Track_to_End;
Procedure Eject_Disk;
Procedure Close_Tray;
Procedure Lock_Door;
Procedure UnLock_Door;
Procedure Reset_Drive;
Procedure
Audio_Channel_Control(ChOut0,Vol0,ChOut1,Vol1,ChOut2,Vol2,ChOut3,Vol3
:Byte);Procedure Chk_Audio;Implementation
Const
TOCreaded : Boolean = FALSE;
Type
Ptr = Record
Ofs :Word;
Seg :Word;
End;
TDevName = Array [1..8] of Char;
TRequestHeader =
Record
Len :Byte;
SubUnit :Byte;
CommandCode :Byte;
Status :Word;
DevName :TDevName;
End;
TIOCtlOBufferStruct =
Record
Command:Byte;
Data0:Byte;
Data1:Byte;
Data2:Byte;
Data3:Byte;
Data4:Byte;
Data5:Byte;
Data6:Byte;
Data7:Byte;
End;
TIOCtlO =
Record
RequestHeader:TRequestHeader;
MediaDescriptor:Byte;
Buffer:Pointer;
BufferSize:Word;
StartSector:LongInt;
VolumePtr:LongInt;
End;
TIOCtlI =
Record
RequestHeader:TRequestHeader;
MediaDescriptor:Byte;
Buffer:Pointer;
BufferSize:Word;
StartSector:Word;
VolumePtr:LongInt;
End;
Var
IO:TIOCtlO;
II:TIOCtlI;
Function RED2HSG(Minute, Second, Frame :LongInt) :LongInt;
Begin
RED2HSG:=Minute*4500+Second*75+Frame;
End;
Procedure HSG2RED(HSG :LongInt; var Minute, Second, Frame :LongInt);
Begin
Frame:=HSG Mod 75;
HSG:=HSG Div 75;
Second:=HSG Mod 60;
Minute:=HSG Div 60;
End;
Procedure IOCTLin(IOCTL :Pointer; IOCTLLength :Word);
Var S,O :Word;
Begin
FillChar(II, SizeOf(II), 0);
With II do
Begin
RequestHeader.Len:=SizeOf(TIOCtlI);
RequestHeader.CommandCode:=3;
Buffer:=IOCTL;
BufferSize:=IOCTLLength;
StartSector:=0;
VolumePtr:=0;
End;
O:=Ofs(II);S:=Seg(II);
Asm
xor ch,ch;
mov cl,GlobalDriveNo;
mov ax,S;
mov es,ax;
mov bx,O;
mov ax,$1510;
int $2f;
End;
GlobalError:=II.RequestHeader.Status;
End;
Procedure IOCTLout(IOCTL :Pointer; IOCTLLength :Word);
Var S,O :Word;
Begin
FillChar(IO, SizeOf(IO), 0);
With IO do
Begin
RequestHeader.Len:=SizeOf(TIOCtlI);
RequestHeader.CommandCode:=12;
Buffer:=IOCTL;
BufferSize:=IOCTLLength;
StartSector:=0;
VolumePtr:=0;
End;
O:=Ofs(IO);
S:=Seg(IO);
Asm
xor ch,ch;
mov cl,GlobalDriveNo;
mov ax,S;
mov es,ax;
mov bx,O;
mov ax,$1510;
int $2f;
End;
GlobalError:=IO.RequestHeader.Status;
End;
Procedure Audio_Disk_Info(var TrackMin,TrackMax :Byte; var LeadOut :LongInt);
Type
TAudioDiskInfo =
Record
Command:Byte;
Min:Byte;
Max:Byte;
LeadOut:LongInt;
End;
Var
IOCTL:TAudioDiskInfo;
Begin
IOCTL.Command:=10;
IOCTLin(@IOCTL,SizeOf(IOCTL));
TrackMin:=IOCTL.Min;
TrackMax:=IOCTL.Max;
LeadOut:=IOCTL.LeadOut; (* RED *)
End;
Procedure Audio_Track_Info(TrackNr :Byte; var TrackStart :LongInt; var CntlAdr
:Byte);Type
TAudioTrackInfo = Record
Command:Byte;
TrackNr:Byte;
TrackStart :LongInt;
TrackCntl:Byte;
End;
Var
IOCTL :TAudioTrackInfo;
Begin
IOCTL.Command:=11;
IOCTL.TrackNr:=TrackNr;
IOCTLin(@IOCTL,SizeOf(IOCTL));
TrackStart:=IOCTL.TrackStart; (* RED *)
CntlAdr:=IOCTL.TrackCntl;
End;
Function Media_Changed :Byte;
Type
TMediaChanged =
Record
Command:Byte;
MediaChanged:Byte;
End;
Var IOCTL :TMediaChanged;
Begin
IOCTL.Command:=9;
IOCTLin(@IOCTL,SizeOf(IOCTL));
Media_Changed:=IOCTL.MediaChanged;
End;
Procedure Audio_Status(var AudioStatus :Word; var StartResume,EndPlay
:LongInt); Type
TAudioStatus =
Record
Command:Byte;
AudioStatus:Word;
StartResume:LongInt;
EndPlay:LongInt;
End;
Var
IOCTL :TAudioStatus;
Begin
If AudioCD Then
Begin
IOCTL.Command:=15;
IOCTLin(@IOCTL,SizeOf(IOCTL));
AudioStatus:=IOCTL.AudioStatus;
StartResume:=IOCTL.StartResume;
EndPlay:=IOCTL.EndPlay;
End;
End;
Procedure Halt_Audio;
Var RequestHeader :TRequestHeader; S,O :Word;
Begin
If AudioCD Then
Begin
RequestHeader.Len:=SizeOf(TRequestHeader);
RequestHeader.CommandCode:=133;
O:=Ofs(RequestHeader);
S:=Seg(RequestHeader);
Asm
xor ch,ch;
mov cl,GlobalDriveNo;
mov ax,S;
mov es,ax;
mov bx,O;
mov ax,$1510;
int $2f;
End;
GlobalError:=RequestHeader.Status;
End;
End;
Procedure Stop_Audio;
Var AudioStatus :Word; StartResume,EndPlay :LongInt;
Begin
If AudioCD Then
Begin
Halt_Audio;
Audio_Status(AudioStatus,StartResume,EndPlay);
If (AudioStatus AND 1)=1 Then
Halt_Audio;
End;
End;
Procedure Pause_Audio;
Var AudioStatus :Word; StartResume,EndPlay :LongInt;
Begin
If AudioCD Then
Begin
Audio_Status(AudioStatus,StartResume,EndPlay);
If (AudioStatus AND 1)=0 Then
Halt_Audio;
End;
End;
Procedure Resume_Audio;
Var RequestHeader :TRequestHeader; AudioStatus,S,O :Word;
StartResume,EndPlay :LongInt; Begin
If AudioCD Then
Begin
Audio_Status(AudioStatus,StartResume,EndPlay);
If (AudioStatus AND 1)=1 Then
Begin
RequestHeader.Len:=SizeOf(TRequestHeader);
RequestHeader.CommandCode:=136;
O:=Ofs(RequestHeader);
S:=Seg(RequestHeader);
Asm
xor ch,ch;
mov cl,GlobalDriveNo;
mov ax,S;
mov es,ax;
mov bx,O;
mov ax,$1510;
int $2f;
End;
GlobalError:=RequestHeader.Status;
End;
End;
End;
Procedure Play_Audio(StartSector,nFrames :LongInt);
Type
TPlayAudio =
Record
RequestHeader :TRequestHeader;
AdressMode :Byte;
StartSector :LongInt;
nFrames :LongInt;
End;
Var
PA :TPlayAudio; S,O :Word;
Begin
FillChar(PA, SizeOf(PA), 0);
PA.RequestHeader.Len:=SizeOf(TPlayAudio);
PA.RequestHeader.CommandCode:=132;
PA.AdressMode:=0; (* HSG *)
PA.StartSector:=StartSector;
PA.nFrames:=nFrames;
O:=Ofs(PA);
S:=Seg(PA);
Asm
xor ch,ch;
mov cl,GlobalDriveNo;
mov ax,S;
mov es,ax;
mov bx,O;
mov ax,$1510;
int $2f;
End;
GlobalError:=PA.RequestHeader.Status;
End;
Function Location_of_Head(AdressMode :Byte) :LongInt;
Type
TLocHead =
Record
Command :Byte;
AdressMode :Byte;
HeadPosition :LongInt;
End;
Var
IOCTL :TLocHead;
Begin
IOCTL.Command:=1;
IOCTL.AdressMode:=AdressMode; (* 0=HSG 1=RED *)
IOCTLin(@IOCTL,SizeOf(IOCTL));
Location_of_Head:=IOCTL.HeadPosition;
End;
Function Device_Status :LongInt;
Type
TDeviceStatus =
Record
Command :Byte;
Status :LongInt;
End;
Var IOCTL :TDeviceStatus;
Begin
IOCTL.Command:=6;
IOCTLin(@IOCTL,SizeOf(IOCTL));
Device_Status:=IOCTL.Status;
End;
Procedure Audio_Channel_Info(var
ChOut0,Vol0,ChOut1,Vol1,ChOut2,Vol2,ChOut3,Vol3 :Byte); Type
TAudioChannelInfo =
Record
Command :Byte;
ChOut0 :Byte;
Vol0 :Byte;
ChOut1 :Byte;
Vol1 :Byte;
ChOut2 :Byte;
Vol2 :Byte;
ChOut3 :Byte;
Vol3 :Byte;
End;
Var IOCTL :TAudioChannelInfo;
Begin
IOCTL.Command:=4;
IOCTLin(@IOCTL,SizeOf(IOCTL));
ChOut0:=IOCTL.ChOut0;
ChOut1:=IOCTL.ChOut1;
ChOut2:=IOCTL.ChOut2;
ChOut3:=IOCTL.ChOut3;
Vol0:=IOCTL.Vol0;
Vol1:=IOCTL.Vol1;
Vol2:=IOCTL.Vol2;
Vol3:=IOCTL.Vol3;
End;
Procedure Audio_Q_Channel_Info(var TrackNo,Min,Sec,Frame,Zero,AMin,ASec,AFrame
:Byte); Type
TAudioQChannelInfo =
Record
Command :Byte;
ADR :Byte;
TrackNo :Byte;
Point :Byte;
Min :Byte;
Sec :Byte;
Frame :Byte;
Zero :Byte;
AMin :Byte;
ASec :Byte;
AFrame :Byte;
End;
Var IOCTL :TAudioQChannelInfo; HTrack :Byte;
Begin
IOCTL.Command:=12;
IOCTL.ADR:=1;
IOCTL.Point:=0;
IOCTLin(@IOCTL,SizeOf(IOCTL));
HTrack:=(IOCTL.TrackNo AND $0F)+((IOCTL.TrackNo AND $F0) shr 4)*10;
TrackNo:=HTrack;
Min:=IOCTL.Min;
Sec:=IOCTL.Sec;
Frame:=IOCTL.Frame;
Zero:=IOCTL.Zero;
AMin:=IOCTL.AMin;
ASec:=IOCTL.ASec;
AFrame:=IOCTL.AFrame;
End;
Function Disk_Remain :LongInt;
Var StartSector,TrackLength,DiskEnd :LongInt;
TrackNo,Min,Sec,Frame,Zero,AMin,ASec,AFrame :Byte;
Begin
If NOT TOCreaded Then
ReadTOC;
If TOCreaded AND (Media_Changed=1) AND AudioCD Then
Begin
Audio_Q_Channel_Info(TrackNo,Min,Sec,Frame,Zero,AMin,ASec,AFrame);
Disk_Remain:=GlobalLeadOut-RED2HSG(AMin,ASec,AFrame);
GlobalAktualTrack:=TrackNo;
End;
End;
Function Track_Remain :LongInt;
Var StartSector,TrackLength,DiskEnd :LongInt;
TrackNo,Min,Sec,Frame,Zero,AMin,ASec,AFrame :Byte;
Begin
If NOT TOCreaded Then
ReadTOC;
If TOCreaded AND (Media_Changed=1) AND AudioCD Then
Begin
Audio_Q_Channel_Info(TrackNo,Min,Sec,Frame,Zero,AMin,ASec,AFrame);
Calculate_Track(TrackNo,StartSector,TrackLength,DiskEnd);
Track_Remain:=TrackLength-RED2HSG(Min,Sec,Frame);
GlobalAktualTrack:=TrackNo;
End;
End;
Function Get_UPC :String;
Type
TGetUPCAEN =
Record
Command:Byte;
ADR:Byte;
UPC:Array [0..6] Of Char;
Zero:Byte;
AFrame:Byte;
End;
Var IOCTL :TGetUPCAEN; HStr :String[7]; T :Byte;
Begin
IOCTL.Command:=14;
IOCTL.ADR:=1;
IOCTLin(@IOCTL,SizeOf(IOCTL));
HStr:='';
For T:=0 To 6 Do
HStr:=HStr+IOCTL.UPC[T];
Get_UPC:=HStr;
End;
Procedure MSCDEX_Version(var HVersion, NVersion :Byte);
Var HV,NV :Byte;
Begin
Asm
mov ax,$150C;
int $2F;
mov HV,bh;
mov NV,bh;
End;
HVersion:=HV;
NVersion:=NV;
End;
Function CDROM_Number(var DriveNo :Byte) :Byte;
Var Ha,Hb:Byte;
Begin
Asm
mov ax,$1500;
xor bx,bx;
xor cx,cx;
int $2F;
mov Ha,bl;
mov Hb,cl;
End;
CDROM_Number:=Ha;
DriveNo:=Hb;
End;
Procedure MSCDEX_Init;
Type
TInit =
Record
RequestHeader:TRequestHeader;
AnzahlDrv:Byte;
EndAdress:LongInt;
BPB:LongInt;
BlockDeviceNo:Byte;
End;
Var MSCDEXInit :TInit; S,O :Word;
Begin
MSCDEXInit.RequestHeader.Len:=SizeOf(MSCDEXInit);
MSCDEXInit.RequestHeader.CommandCode:=0;
MSCDEXInit.AnzahlDrv:=0;
MSCDEXInit.BlockDeviceNo:=0;
O:=Ofs(MSCDEXInit);
S:=Seg(MSCDEXInit);
Asm
xor ch,ch;
mov cl,GlobalDriveNo;
mov ax,S;
mov es,ax;
mov bx,O;
mov ax,$1510;
int $2f;
End;
GlobalError:=MSCDEXInit.RequestHeader.Status;
End;
Procedure Chk_Audio;
Begin
Play_Audio(0,10);
If (GlobalError AND $8002)=$8002 Then
AudioCD:=FALSE
Else
AudioCD:=TRUE;
Stop_Audio;
End;
Procedure ReadTOC;
Var Change,TrackMin,TrackMax,T,CntlADR :Byte; HTOC,LeadOut :LongInt; HErr
:Word; Begin
Change:=Media_Changed;
While (Change<>1) AND ((GlobalError AND $8000)=0) Do
Change:=Media_Changed;
Chk_Audio;
If ((GlobalError AND $8000)=0) AND AudioCD Then
Begin
Audio_Disk_Info(TrackMin,TrackMax,LeadOut);
If (GlobalError AND $8000)=0 Then
Begin
GlobalLeadOut:=RED2HSG(REDUnPack(LeadOut).Minute,
REDUnPack(LeadOut).Second,
REDUnPack(LeadOut).Frame);
GlobalTrackMin:=TrackMin;
GlobalTrackMax:=TrackMax;
TOCreaded:=TRUE;
For T:=TrackMin To TrackMax Do
Begin
Audio_Track_Info(T,HTOC,CntlADR);
TOC[T]:=RED2HSG(REDUnPack(HTOC).Minute,
REDUnPack(HTOC).Second,
REDUnPack(HTOC).Frame);
End;
End;
End;
End;
Procedure Calculate_Track(TrackNo :Byte; var StartSector,TrackLength,DiskEnd
:LongInt); Var HStartSector,HTrackLength :LongInt;
Begin
If (TOCreaded) AND (Media_Changed=1) AND AudioCD Then
Begin
HStartSector:=TOC[TrackNo]-TOC[GlobalTrackMin];
If TrackNo1) Then
ReadTOC;
If (TrackNo>=GlobalTrackMin) AND (TrackNo<=GlobalTrackMax) AND AudioCD
Then Begin
Status:=Device_Status;
If (GlobalError AND $0200)=$0200 Then
Pause_Audio;
Calculate_Track(TrackNo,StartSector,TrackLength,DiskEnd);
If (GlobalError AND $8000)=0 Then
Begin
Play_Audio(StartSector,TrackLength);
GlobalAktualTrack:=TrackNo;
End;
End;
End;
End;
Procedure Play_Track_to_End(TrackNo :Byte);
Var Status,StartSector,TrackLength,DiskEnd :LongInt;
Begin
Status:=Device_Status;
If (Status AND $0800)=$0000 Then
Begin
If (NOT TOCreaded) OR (Media_Changed<>1) Then
ReadTOC;
If (TrackNo>=GlobalTrackMin) AND (TrackNo<=GlobalTrackMax) AND AudioCD
Then Begin
Status:=Device_Status;
If (GlobalError AND $0200)=$0200 Then
Pause_Audio;
Calculate_Track(TrackNo,StartSector,TrackLength,DiskEnd);
If (GlobalError AND $8000)=0 Then
Begin
Play_Audio(StartSector,DiskEnd);
GlobalAktualTrack:=TrackNo;
End;
End;
End;
End;
Procedure Show_Track(TrackNo, Seconds :Byte);
Var Status,StartSector,TrackLength,DiskEnd :LongInt;
Begin
Status:=Device_Status;
If (Status AND $0800)=$0000 Then
Begin
If (NOT TOCreaded) OR (Media_Changed<>1) Then
Reset_Drive;
If (TrackNo>=GlobalTrackMin) AND (TrackNo<=GlobalTrackMax) AND AudioCD
Then Begin
Status:=Device_Status;
If (GlobalError AND $0200)=$0200 Then
Pause_Audio;
Calculate_Track(TrackNo,StartSector,TrackLength,DiskEnd);
If (GlobalError AND $8000)=0 Then
Begin
Play_Audio(StartSector,Seconds*75);
GlobalAktualTrack:=TrackNo;
End;
End;
End;
End;
Procedure Next_Track;
Begin
If (TOCreaded) AND (Media_Changed=1) AND AudioCD Then
If GlobalTrackMax>GlobalAktualTrack Then
Play_Track(GlobalAktualTrack+1)
Else
Play_Track(GlobalTrackMin);
End;
Procedure Last_Track;
Begin
If (TOCreaded) AND (Media_Changed=1) AND AudioCD Then
If GlobalTrackMinGlobalAktualTrack Then
Play_Track_to_End(GlobalAktualTrack+1)
Else
Play_Track_to_End(GlobalTrackMin);
End;
Procedure Last_Track_to_End;
Begin
If (TOCreaded) AND (Media_Changed=1) AND AudioCD Then
If GlobalTrackMin0 Then
MSCDEXOk:=TRUE;
End.