(*                FPCD - Enhanced CD-ROM Audio control unit, version 1.0

                         written in 26.12.2008 by Laaca
                             Laaca@seznam.cz
                             http://www.laaca.borec.cz

                         based on TMTCD v1.41 by Andrew Luzhin
                             andy21@windoms.sitek.net
                             http://windoms.sitek.net/~andy21

                         and CDAUDIO unit for Turbo pascal by Yuval Melamed

Language:            Freepascal, DOS (GO32V2) version, 1.x.x and 2.x.x series
Environment:         IBM PC i80386+ (MsDos) 32 bit Protected Mode.
                     MSCDEX Version 2.10+

Description: This unit provides a number of routines to work with
             audio CD.

             Don't find it strange that not all functions work
             (correct) on your CD-ROM... The problems is your device
             driver (and not this unit, I hope so ;-).                      *)

UNIT FPCD;
Interface
Type
  TMSF = Record
           Frm, Sec, Min, Trk : Byte;
         end;
  DiscRec = Record
    Length : TMSF;
    Track  : Array[1..99] of TMSF;
  end;

Const
  _CDDrivesNumber  : Integer = 0;                       {Number of CD drives}
  _CDCurrentDrv    : Integer = 0;                          {Current CD Drive}
  _CDFirstDrive    : Integer = 0;             {First CD drive, 0 for A: etc.}
  _CDListDrives    : string[20] = '';    {List of all CD drives}
  _MSCDEXVersion   : Word    = 0;        {High - major, Low  - minor version}


Var
  _MSCDEXInstalled : Boolean absolute _CDDrivesNumber;
  Request          : array[$00..$15] of Byte;
  TBuff            : array[$00..$0A] of Byte;

//////////////// TMSF Functions ////////////////
Function  HSG      (T:TMSF) : LongInt;
Function  RedBook(HSG :LongInt):TMSF;
Procedure HSG2RED  (HSG :LongInt; Var T:TMSF);
Procedure AddTMSF  (Var Dest : TMSF; Src : TMSF);
Procedure SubTMSF  (Var Dest : TMSF; Src : TMSF);
Function  EqTMSF   (T1, T2 : TMSF) : Boolean;
Function  NEqTMSF  (T1, T2 : TMSF) : Boolean;
Function  AddTMSFf (T1, T2 : TMSF) : TMSF;
Function  SubTMSFf (T1, T2 : TMSF) : TMSF;

{OVERLOAD +   = AddTMSFf;
OVERLOAD -   = SubTMSFf;
OVERLOAD +:= = AddTMSF;
OVERLOAD -:= = SubTMSF;
OVERLOAD =   = EqTMSF;
OVERLOAD <>  = NEqTMSF;}

///////////// Internal procedures //////////////
Procedure _CDInit;
Procedure _Stop;
Procedure PlayRequest;
Procedure InputRequest;
Procedure OutputRequest;
Procedure ReqQSubAudChnInfo;
Function  GetDriverName : String;


///////////////// CD commands //////////////////
Function  ChangeDrive (D:Word) : Boolean;
Procedure PauseAudio;
Procedure ResumeAudio;
Procedure CDStop;
Procedure EjectDoor;
Procedure InsertDoor;
Procedure LockDoor;
Procedure UnlockDoor;
Procedure SetVolume   (Vol    : Byte);
Procedure SeekPos     (SPos   : TMSF);
Procedure PlayRange   (From, Till   : TMSF);
Procedure PlayAmount  (From, Amount : TMSF);
Procedure PlayFrom    (From   : TMSF);
Procedure PlayTrack   (TrkNum : Byte);
Procedure PlaySingle  (TrkNum : Byte);
Procedure PlayReverse (Skip   : TMSF);
Procedure PlayForward (Skip   : TMSF);
Procedure PlayPrevTrk;
Procedure PlayNextTrk;
Procedure CDPrevTrack;
Procedure CDNextTrack;
Procedure ResetDrive;

/////////// Different status functions /////////
Function  AudVolume    : Byte;
Function  DeviceStatus : Word;
Function  MediaChanged : Byte;
Function  DATADisc     : Boolean;
Function  DoorOpen     : Boolean;
Function  Locked       : Boolean;
Function  NoDisc       : Boolean;
Function  DrvBusy      : Boolean;
Function  InPause      : Boolean;
Function  Writable     : Boolean;
Function  SuppAudVid   : Boolean;
Function  SuppAudChn   : Boolean;

////////////// Disc information ////////////////
Procedure GetDiscLen  (Var DLen : TMSF);
Procedure CurDiscPos  (Var DPos : TMSF);
Procedure CurTrkPos   (Var TPos : TMSF);
Procedure GetDiscRem  (Var DRem : TMSF);
Procedure GetTrkRem   (Var TRem : TMSF);
Procedure DiscTime    (Var Dest : TMSF);
Procedure TrkTime     (Var Dest : TMSF);
Procedure GetDiscInfo (Var Info : DiscRec);
Procedure GetTrkLoc   (Var TLoc : TMSF; Trk : Byte);  {Trk field = 1 if data}
Procedure GetTrkLen   (Var TLen : TMSF; Trk : Byte);  { and 0 if audio track}

Procedure AllStat1(Var _Open,_Locked,_NoDisc,_Busy,_Paused:Boolean);    {*}
Procedure AllStat2(Var DPos,TPos,DLen: TMSF; Var DATA:Boolean);         {*}

/////////////////////////////////////////////////////////////////////////////
{.$Define CorrectHSG} // HSG sector= minute*4500 + second*75 + frame - 150 //
/////////////////////////////////////////////////////////////////////////////

Implementation
Uses Go32;
Var
  __HSG            : LongInt;
  __Trk            : Byte;
  PFromT, PCountT  : TMSF;
  Regs             : TRealRegs;
  RmSegment1,
  RmSegment2       : Word;
  RmSelector1,
  RmSelector2      : Word;
  OldExit          : Pointer;

Function HSG(T:TMSF) :LongInt;
begin
HSG:=T.Min*4500+T.Sec*75+T.Frm-{$IfDef CorrectHSG}150{$Else}120{$EndIf};
end;

Procedure HSG2RED(HSG :LongInt; Var T:TMSF);
Begin
inc(HSG,{$IfDef CorrectHSG}150{$Else}140{$EndIf});
T.Frm:=HSG Mod 75;
HSG:=HSG Div 75;
T.Sec:=HSG Mod 60;
T.Min:=HSG Div 60;
End;

Function RedBook(HSG :LongInt):TMSF;
var a:TMSF;
Begin
inc(HSG,{$IfDef CorrectHSG}150{$Else}140{$EndIf});
A.Frm:=HSG Mod 75;
HSG:=HSG Div 75;
A.Sec:=HSG Mod 60;
A.Min:=HSG Div 60;
RedBook:=A;
End;

procedure AddTMSF(var Dest : TMSF; Src : TMSF);
Begin
  __HSG:=HSG(Dest)+HSG(Src);
  HSG2RED(__HSG,Dest);
End;

procedure SubTMSF(var Dest : TMSF; Src : TMSF);
Begin
  If HSG(Dest)>HSG(Src) Then
    __HSG:=HSG(Dest)-HSG(Src)
    Else __HSG:=HSG(Src)-HSG(Dest);
  HSG2RED(__HSG,Dest);
End;

function EqTMSF(T1, T2 : TMSF):Boolean;
begin
EqTMSF:=(T1.Trk=T2.Trk) and (T1.Min=T2.Min) and
        (T1.Sec=T2.Sec) and (T1.Frm=T2.Frm);
end;

function NEqTMSF(T1, T2 : TMSF):Boolean;
begin
NeqTMSF:=not(EqTMSF(T1,T2));
end;

function AddTMSFf(T1, T2 : TMSF):TMSF;
begin
AddTMSFf:=RedBook(HSG(T1)+HSG(T2));
end;

function SubTMSFf(T1, T2 : TMSF):TMSF;
begin
  If HSG(T1)>HSG(T2) Then SubTMSFf:=RedBook(HSG(T1)-HSG(T2))
                     Else SubTMSFf:=RedBook(HSG(T2)-HSG(T1));
end;

Procedure ClearRMregs(var r:TRealregs);
begin
Fillchar(r,sizeof(Trealregs),0)
end;

Procedure xGlobalDosAlloc(var segment,selector: word; Size: LongInt);
var Long: dword;
begin
Long:=Global_DOS_Alloc(Size);
segment:=Hi(Long);
selector:=Lo(Long);
end;


Procedure _CDInit;
var a:byte;
Begin
xGlobalDOSAlloc(RmSegment1,RMselector1,SizeOf(Request));
xGlobalDOSAlloc(RmSegment2,RMselector2,SizeOf(TBuff));
ClearRMregs(Regs);
Regs.AX:=$1500;
RealIntr($2F,Regs);
_CDDrivesNumber:=Regs.bx;
_CDFirstDrive:=Regs.cx;
_CDCurrentDrv:=Regs.cx;
ClearRmRegs(Regs);
Regs.AX:=$150C;
RealIntr($2F,Regs);
_MSCDEXVersion:=Regs.bx;
ClearRMregs(regs);
Regs.AX:=$150D;
Regs.ES:=RmSegment1;
Regs.BX:=0;
RealIntr($2F,Regs);
DOSmemGet(RmSegment1,0,Request,sizeof(request));
for a:=1 to _cddrivesnumber do
   _CDlistdrives:=_CDlistdrives+char(Request[a]+64);
end;


procedure ReqQSubAudChnInfo;
var a,b:byte;
begin
TBuff[0]:=$0C;
InputRequest;
a:=TBuff[2];
b:=a and $F;
a:=(a div 16)*10+b;
__Trk:=a;
end;


procedure PlayRequest;
Begin
  ClearRmRegs(Regs);
  DOSmemPut(RmSegment1,0,Request,sizeOf(Request));
  Regs.AX:=$1510;
  Regs.ES:=RmSegment1;
  Regs.CX:=_CDCurrentDrv;
  Mem[RmSegment1:2]:=$84;
  Mem[RmSegment1:$0D]:=0;
  RealIntr($2F,Regs);
  DOSmemGet(RMSegment1,0,Request,sizeOf(Request));
End;

procedure InputRequest;
Begin
  ClearRmRegs(Regs);
  DOSmemPut(RMSegment1,0,Request,sizeof(Request));
  DOSmemPut(RMSegment2,0,TBuff,sizeof(TBuff));
  Regs.AX:=$1510;
  Regs.ES:=RmSegment1;
  Regs.CX:=_CDCurrentDrv;
  Mem[RmSegment1:2]:=3;
  MemW[RmSegment1:$0E]:=0;
  MemW[RmSegment1:$10]:=RmSegment2;
  RealIntr($2F,Regs);
  DOSmemGet(RMSegment1,0,Request,sizeof(Request));
  DOSmemGet(RMSegment2,0,TBuff,sizeof(TBuff));
End;

procedure OutputRequest;
Begin
  ClearRmRegs(Regs);
  DOSmemPut(RMSegment1,0,Request,sizeof(Request));
  DOSmemPut(RMSegment2,0,TBuff,sizeof(TBuff));
  Regs.AX:=$1510;
  Regs.ES:=RmSegment1;
  Regs.CX:=_CDCurrentDrv;

  Mem[RmSegment1:2]:=$C;
  MemW[RmSegment1:$0E]:=0;
  MemW[RmSegment1:$10]:=RmSegment2;

  RealIntr($2F,Regs);
  DOSmemGet(RMSegment1,0,Request,sizeof(Request));
  DOSmemGet(RMSegment2,0,TBuff,sizeof(TBuff));
End;

procedure PauseAudio;
Begin
  ClearRmRegs(Regs);
  DOSmemPut(RMSegment1,0,Request,sizeof(Request));
  Regs.AX:=$1510;
  Regs.ES:=RmSegment1;
  Regs.CX:=_CDCurrentDrv;
  Mem[RmSegment1:2]:=$85;
  RealIntr($2F,Regs);
  DOSmemGet(RMSegment1,0,Request,sizeof(Request));
End;

procedure ResumeAudio;
Begin
  ClearRmRegs(Regs);
  DOSmemPut(RMSegment1,0,Request,sizeof(Request));
  Regs.AX:=$1510;
  Regs.ES:=RmSegment1;
  Regs.CX:=_CDCurrentDrv;
  Mem[RmSegment1:2]:=$88;
  RealIntr($2F,Regs);
  DOSmemGet(RMSegment1,0,Request,sizeof(Request));
End;

function DeviceStatus : Word;
begin
TBuff[0]:=$6;
InputRequest;
DeviceStatus:=+TBuff[1]+TBuff[2]*256;
end;


procedure EjectDoor;
var b:boolean;
begin
if not Locked then
   begin
   PauseAudio;
   TBuff[0]:=0;
   OutputRequest;
   end;
end;

function DoorOpen : Boolean;
begin
DoorOpen:=Odd(DeviceStatus);
end;


function Locked : Boolean;
begin
Locked:=(not(DeviceStatus) and 2)<>0;
end;


function NoDisc : Boolean;
begin
NoDisc:=(DeviceStatus and 2048)<>0;
end;


function DrvBusy : Boolean;
begin
DeviceStatus;
DrvBusy:=(Request[4] and 2)<>0;
end;

function InPause : Boolean;
begin
TBuff[0]:=$7;
InputRequest;
InPause:=boolean(TBuff[1]);
end;

function AudVolume : Byte;
begin
TBuff[0]:=4;
InputRequest;
AudVolume:=TBuff[2];
end;

function Writable : Boolean;
begin
Writable:=(DeviceStatus and 8)<>0;
end;

function SuppAudVid : Boolean;
begin
SuppAudVid:=(DeviceStatus and 16)<>0;
end;


function SuppAudChn : Boolean;
begin
SuppAudChn:=((DeviceStatus shr 8) and 1)<>0;
end;


procedure InsertDoor;
begin
TBuff[0]:=5;
OutputRequest;
end;

procedure LockDoor;
begin
TBuff[0]:=$1;TBuff[1]:=$1;
OutputRequest;
end;

procedure UnlockDoor;
begin
TBuff[0]:=$1;TBuff[1]:=0;
OutputRequest;
end;


procedure SetVolume(Vol : Byte);
var a:word;
begin
TBuff[0]:=3;
a:=vol*256;
TBuff[1]:=Lo(a);TBuff[2]:=Hi(a);
inc(a);
TBuff[3]:=Lo(a);TBuff[4]:=Hi(a);
inc(a);
TBuff[5]:=Lo(a);TBuff[6]:=Hi(a);
inc(a);
TBuff[7]:=Lo(a);TBuff[8]:=Hi(a);
OutputRequest;
end;

procedure GetDiscLen(var DLen : TMSF);
begin
TBuff[0]:=$A;
InputRequest;
DLen.trk:=TBuff[2];
DLen.frm:=TBuff[3];
DLen.sec:=TBuff[4];
DLen.min:=TBuff[5];
end;

procedure GetTrkLoc(var TLoc : TMSF; Trk : Byte);
begin
TBuff[0]:=$B;
TBuff[1]:=trk;
InputRequest;
TLoc.frm:=TBuff[2];
TLoc.sec:=TBuff[3];
TLoc.min:=TBuff[4];
TLoc.trk:=(TBuff[6] and 64) shr 6;
End;

procedure GetTrkLen(var TLen : TMSF; Trk : Byte);
var
  TLoc : TMSF;
begin
  GetTrkLoc(TLen, Trk);
  GetDiscLen(TLoc);
  if Trk < TLoc.Trk then
    GetTrkLoc(TLoc, Trk+1);
  SubTMSF(TLen, TLoc);
end;


procedure GetDiscInfo(var Info : DiscRec);
var
  Trk : Byte;
begin
  GetDiscLen(Info.Length);
  for Trk := 1 to Info.Length.Trk do
    GetTrkLen(Info.Track[Trk], Trk);
end;

procedure CurDiscPos(var DPos : TMSF );
begin
ReqQSubAudChnInfo;
DPos.trk:=__trk;
DPos.min:=TBuff[8];
DPos.sec:=TBuff[9];
DPos.frm:=TBuff[10];
end;

procedure CurTrkPos(var TPos : TMSF );
begin
ReqQSubAudChnInfo;
TPos.trk:=__trk;
TPos.min:=TBuff[4];
TPos.sec:=TBuff[5];
TPos.frm:=TBuff[6];
End;

procedure GetDiscRem(var DRem : TMSF);
var
  DLen : TMSF;
begin
  GetDiscLen(DLen);
  CurDiscPos(DRem);
  SubTMSF(DRem, DLen);
end;

procedure GetTrkRem(var TRem : TMSF);
var
  TLen : TMSF;
begin
  GetTrkLen(TLen, TRem.Trk);
  CurTrkPos(TRem);
  SubTMSF(TRem, TLen);
end;

procedure DiscTime(var Dest : TMSF);
var
  TLoc : TMSF;
begin
  GetTrkLoc(TLoc, Dest.Trk);
  AddTMSF(Dest, TLoc);
  Dest.Trk := 0;
end;

procedure TrkTime(var Dest : TMSF);
var
  Upper, Lower, Middle : Byte;
  Disc, Trk : TMSF;
begin
  Dest.Trk := 0;
  GetDiscLen(Disc);
  GetTrkLoc(Trk, Disc.Trk);
  if LongInt(Dest) >= (LongInt(Trk) and $FFFFFF) then
    Dest.Trk := Disc.Trk;
  if (Disc.Trk > 1) and (Dest.Trk = 0) then
  begin
    GetTrkLoc(Trk, 2);
    if Longint(Dest) < (Longint(Trk) and $FFFFFF) then
    begin
      Dest.Trk := 1;
      GetTrkLoc(Trk, 1);
    end;
  end;
  if Dest.Trk = 0 then
  begin
    Lower := 2;
    Upper := Disc.Trk - 1;
    while Dest.Trk <> Middle do
    begin
      Middle := (Lower + Upper) shr 1;
      GetTrkLoc(Trk, Middle + 1);
      if Longint(Dest) > (Longint(Trk) and $FFFFFF) then
      begin
        Lower := Middle + 1;
        Continue;
      end;
      GetTrkLoc(Trk, Middle);
      if Longint(Dest) < (Longint(Trk) and $FFFFFF) then
        Upper := Middle - 1
      else
        Dest.Trk := Middle;
    end;
  end;
  SubTMSF(Dest, Trk);
end;

procedure PlayFrom(From : TMSF);
var a,b:longint;
begin
  PauseAudio;
  GetDiscLen(PCountT);
  if From.Trk <> 0 then DiscTime(From);
  a:=HSG(From);
  Move(a,Request[$0E],4);
  a:=HSG(PCountT);
  b:=HSG(From);
  a:=a-b;
  Move(a,Request[$12],4);
  PlayRequest;
end;

procedure PlayRange(From, Till : TMSF);
var a:longint;
begin
  PauseAudio;
  if From.Trk <> 0 then DiscTime(From);
  if Till.Trk <> 0 then DiscTime(Till);
  a:=HSG(From);
  Move(a,Request[$0E],4);
  a:=HSG(PCountT) - HSG(From);
  Move(a,Request[$12],4);
  PlayRequest;
end;

procedure PlayAmount(From, Amount : TMSF);
var a:longint;
begin
  PauseAudio;
  if From.Trk <> 0 then DiscTime(From);
  a:=HSG(From);
  Move(a,Request[$0E],4);
  a:=HSG(Amount);
  Move(a,Request[$12],4);
  PlayRequest;
end;

procedure PlayTrack(TrkNum : Byte);
begin
  GetTrkLoc(PFromT, TrkNum);
  if PFromT.Trk = 0 then PlayFrom(PFromT);
end;

procedure PlaySingle(TrkNum : Byte);
begin
  GetTrkLoc(PFromT, TrkNum);
  GetTrkLen(PCountT, TrkNum);
  if PFromT.Trk = 0 then PlayAmount(PFromT, PCountT);
end;

procedure PlayPrevTrk;
Var PCount:TMSF;
begin
  GetDiscLen(PCount);
  CurTrkPos(PFromT);
  if PFromT.Trk = 1 then
    PFromT.Trk := PCountT.Trk
  else
    Dec(PFromT.Trk);
  GetTrkLoc(PFromT, PFromT.Trk);
  if PFromT.Trk = 0 then PlayFrom(PFromT);
end;

procedure PlayNextTrk;
begin
  GetDiscLen(PCountT);
  CurTrkPos(PFromT);
  if PFromT.Trk = PCountT.Trk then
    PFromT.Trk := 1
  else
    Inc(PFromT.Trk);
  GetTrkLoc(PFromT, PFromT.Trk);
  if PFromT.Trk = 0 then PlayFrom(PFromT);
end;

procedure PlayReverse(Skip : TMSF);
begin
  CurDiscPos(PFromT);
  SubTMSF(PFromT, Skip);
  PFromT.Trk := 0;
  PlayFrom(PFromT);
end;

procedure PlayForward(Skip : TMSF);
begin
  CurDiscPos(PFromT);
  AddTMSF(PFromT, Skip);
  PFromT.Trk := 0;
  PlayFrom(PFromT);
end;

Function DATADisc:Boolean;
var a:byte;
begin
TBuff[0]:=$A;
InputRequest;
if TBuff[2]<>1 then Exit(false);
TBuff[0]:=$B;
TBuff[1]:=$1;
InputRequest;
DATADisc:=(TBuff[6] and 64)<>0;
End;

procedure ResetDrive;
begin
TBuff[0]:=2;
OutputRequest;
end;

Function MediaChanged : Byte;
{  1  :  Media not changed            }
{  0  :  Don't Know                   }
{ -1  :  Media changed.               }
{ N.B. Usually works incorrectly ;-)  }
begin
TBuff[0]:=9;
OutputRequest;
MediaChanged:=TBuff[1];
end;


Function GetDriverName : String;     {I never use this procedure, but}
Var                                  {if you want to work with MSCDEX}
   Cdntemp : Array[1..18] Of Byte;   {through Int 21h it will be usefull}
   Count : Byte;
   Cdstemp : String[8];
   _sg:word;
   _of:word;

Begin
  ClearRmRegs(Regs);

  DOSmemPut(RMSegment1,0,Request,sizeOf(Request));
  Regs.AX:=$1501;
  Regs.ES:=RmSegment1;
  Regs.CX:=_CDCurrentDrv;
  Mem[RmSegment1:2]:=$84;
  Mem[RmSegment1:$0D]:=0;
  RealIntr($2F,Regs);

  DosMemGet(RmSegment1,0,Request,sizeOf(Request));

  _sg:=Request[3]+Request[4]*256;
  _of:=Request[1]+Request[2]*256;

  for count:=1 to 18 do CDNtemp[count]:=Mem[_sg:_of+count-1];

  Count := 1;
  Repeat
     CDStemp[Count] := CHR(CDNTemp[10+Count]);
     Inc(Count);
  Until (Count > 8) Or (Cdntemp[10+Count]=32);
  CDSTemp[0] := CHR(Count-1);
  GetDriverName := CDSTemp;
End;

Procedure CDStop;
Var T:TMSF;
Begin
  CurDiscPos(T);              {This stuff is necessary because, some drivers}
  GetTrkLoc(T,T.Trk);         {doesn't process stop (double pause) procedure}
  SeekPos(T);                 {correctly}
  PauseAudio;
  If InPause Then PauseAudio;
End;

{Procedure for my CD player.}
Procedure AllStat1(Var _Open,_Locked,_NoDisc,_Busy,_Paused:Boolean);
var w:word;
begin
w:=DeviceStatus;
_Open:=(w and 1)<>0;
_Locked:=(w and 2)<>0;
_NoDisc:=(w and 2048)<>0;
_Busy:=(Request[4] and 2)<>0;
TBuff[0]:=$F;
InputRequest;
_Paused:=TBuff[1]<>0;
end;

{Procedure for my CD player.}
Procedure AllStat2(Var DPos,TPos,DLen: TMSF; Var DATA:Boolean);
begin
ReqQSubAudChnInfo;
DPos.min:=TBuff[8];
DPos.sec:=TBuff[9];
DPos.frm:=TBuff[10];

TPos.min:=TBuff[4];
TPos.sec:=TBuff[5];
TPos.frm:=TBuff[6];

TBuff[0]:=$A;
InputRequest;
DLen.trk:=TBuff[2];
DLen.frm:=TBuff[3];
DLen.sec:=TBuff[4];
DLen.min:=TBuff[5];

TBuff[0]:=$B;
TBuff[1]:=__Trk;
InputRequest;
DATA:=(TBuff[6] and 64)<>0;
DPos.trk:=__trk;
TPos.trk:=__trk;
end;

Function  ChangeDrive (D:Word):Boolean; {0=A:,1=B: etc.}
Begin
  ClearRmRegs(Regs);
  Regs.AX:=$150B;
  Regs.CX:=D;
  RealIntr($2F,Regs);
  If Regs.AX <> 0 Then
   Begin
    _CDCurrentDrv:=D;
    ChangeDrive:=True;
   End Else ChangeDrive:=False;
End;


procedure SeekPos(SPos : TMSF);
var a:longint;
Begin
  PauseAudio;
  if SPos.Trk <> 0 then GetTrkLoc(SPos, SPos.Trk);
  ClearRmRegs(Regs);
  Regs.AX:=$1510;
  Regs.ES:=RmSegment1;
  Regs.CX:=_CDCurrentDrv;
  Mem[RmSegment1:2]:=$83;
  Mem[RmSegment1:$0D]:=0;
  a:=HSG(SPos);
  DOSmemPut(RMSegment1,$14,a,4);
  RealIntr($2F,Regs);
end;

procedure CDPrevTrack;
Var PCount:TMSF;
begin
  CurTrkPos(PFromT);
  if PFromT.Trk = 1 then
  begin
    GetDiscLen(PCount);
    Move(PCount,Request[$12],4);
    PFromT.Trk := PCountT.Trk;
  end
  else
    Dec(PFromT.Trk);
  GetTrkLoc(PFromT, PFromT.Trk);
  if PFromT.Trk = 0 then
   begin
    If DrvBusy Then PlayFrom(PFromT)
               Else SeekPos(PFromT)
   end;
end;

procedure CDNextTrack;
begin
  GetDiscLen(PCountT);
  CurTrkPos(PFromT);
  if PFromT.Trk = PCountT.Trk then
    PFromT.Trk := 1
  else
    Inc(PFromT.Trk);
  GetTrkLoc(PFromT, PFromT.Trk);
  if PFromT.Trk = 0 then
   begin
    If DrvBusy Then PlayFrom(PFromT)
               Else SeekPos(PFromT)
   end;
end;



 procedure xGlobalDosFree(var selector:word);
 begin
  Global_DOS_Free(selector);
 end;

/////////////////////////////////////////////////////////////////////////////
Procedure _Stop;
Begin
 If RmSegment1<>0 Then xGlobalDOSfree(RmSelector1);
 If RmSegment2<>0 Then xGlobalDOSfree(RmSelector2);
End;

Begin
  _cdinit;

End.
