home *** CD-ROM | disk | FTP | other *** search
/ PC-Test Pro / PCTESTPRO.iso / cdrom / ctcdread / entp / ctcdread.pas < prev   
Encoding:
Pascal/Delphi Source File  |  1994-09-21  |  44.5 KB  |  1,007 lines

  1. PROGRAM CTCDREAD;
  2.  
  3. {CD-ROM Audio Dump V1.1, U. Rohbeck, 13-Juli-94
  4.            V1.2, U. Rohbeck, 15-Juli-94  * -SONY-
  5.            V1.3, U. Rohbeck, 11-Sep-94   * bug -> ReadCDDA10,
  6.                            bug -> reset of CDRtype,
  7.                            cancel BREAK-function
  8.            V1.4, U. Rohbeck, 19-Sep-94   *RequestSense, TestUnitReady
  9.                            addModeSense[NEC],addModeSelect [NEC]
  10.            V1.5, U. Rohbeck, 22-Sep-94    * -l LO-area, bug -> LBN}
  11.  
  12. {literature
  13.   Kai Schwitzke: Das VOC- und das WAVE-Dateiformat, c't 01/93 - S.213
  14.   Dr. Bernd Steinbrink: Entwirrung der diversen CD-ROM-Formate, c't 02/93 - S.178
  15.   Small Computer System Interface X3.131-199x, X3T9.2375 Revision 10k,17-Mar-93, Working Draft (SCSI-2)
  16.   Ulf Rohbeck: ASPI-Bibliothek für Assembler, Turbo-Pascal und C, c't 12/93 - S.212}
  17.  
  18. USES CRT,DOS,ASPIxDOS;
  19.  
  20. CONST
  21.  CDRsupport= 4;
  22.  CDRtypeLST: ARRAY[1..CDRsupport] of STRING[16] = ('TOSHIBA','HITACHI',
  23.                            'NEC','SONY');
  24. TYPE
  25.  InquireDataFormat = Record
  26.     ConfigPara: ARRAY[0..7] of BYTE;
  27.     VendorID  : ARRAY[1..8] of char;
  28.     ProductID : ARRAY[1..16] of char;
  29.     ProductRev: ARRAY[1..4] of char;
  30.  END;
  31.  SenseDataFormat = Record
  32.    ValErrorCode : BYTE;    {VAL|ErrorCode}
  33.    rsvd1        : BYTE;    {reserved|0}
  34.    SenseKey     : BYTE;    {Sense Key}
  35.    InfoBytes    : LongInt; {Information Bytes MSB..LSB}
  36.    ASL          : BYTE;    {Additional Sense Length}
  37.    CSIB         : LongInt; {Command Specific Information Bytes MSB..LSB}
  38.    ASC          : BYTE;    {Additional Sense Code}
  39.    ASCQ         : BYTE;    {Additional Sense Code Qualifer}
  40.    rsvd2        : LongInt; {reserved|0}
  41.  END;
  42.  
  43.  AbsCDRaddrFormat = Record
  44.     MSF_rsvd    : Byte;
  45.     MSF_MM      : Byte;
  46.     MSF_SS      : Byte;
  47.     MSF_DD      : Byte;
  48.  END;
  49.  TOCTrackDescriptor = Record
  50.     rsvd01    : Byte;
  51.     AdrControl: Byte;
  52.     TrackNum  : Byte;
  53.     rsvd02    : Byte;
  54.     AbsCDRaddr: AbsCDRaddrFormat;
  55.  END;
  56.  ReadTOCDataFormat = Record
  57.     TOCdataLEN: Word;
  58.     firstTrack: Byte;
  59.     lastTrack : Byte;
  60.     TOCtd     : ARRAY[1..99] of TOCTrackDescriptor;
  61.  END;
  62.  SubChannelDataHeader = Record
  63.     rsvd        : Byte;
  64.     AudioStatus : Byte;
  65.     SubCDataLEN : Array[0..1] of Byte;
  66.  END;
  67.  TrackISRCDataFormat = Record
  68.     SubCHDR   : SubChannelDataHeader;
  69.     SubCDFcode: Byte;   {Sub Channel Data Format Code}
  70.     AdrControl: Byte;
  71.     TNO       : Byte;   {Track Number}
  72.     rsvd      : Byte;
  73.     TCVal     : Byte;
  74.     TrackISRC : Array[0..14] of Byte; {Track International-Standard-Recording-Code}
  75.  END;
  76.  CDROM_CurrentPositionDataFormat = Record
  77.     SubCHDR   : SubChannelDataHeader;
  78.     SubCDFcode: Byte;   {Sub Channel Data Format Code}
  79.     AdrControl: Byte;
  80.     TNO       : Byte;   {Track Number}
  81.     INO       : Byte;   {Index Number}
  82.     AbsCDRaddr: AbsCDRaddrFormat; {Absolute CD-ROM Address}
  83.     TrackRaddr: AbsCDRaddrFormat; {Track Relative CD-ROM Address}
  84.  END;
  85.  MediaCatalogNumberDataFormat = Record
  86.     SubCHDR   : SubChannelDataHeader;
  87.     SubCDFcode: Byte;   {Sub Channel Data Format Code}
  88.     rsvd      : Array[0..2] of Byte;
  89.     MCVal     : Byte;
  90.     MCN       : Array[0..14] of Byte; {Media Catalog Number (UPC/Bar Code}
  91.  END;
  92.  ReadSubQDataFormat = Record
  93.     SubCHDR   : SubChannelDataHeader;
  94.     SubCDFcode: Byte;  {Sub Channel Data Format Code}
  95.     AdrControl: Byte;
  96.     TNO       : Byte;  {Track Number}
  97.     INO       : Byte;  {Index Number}
  98.     AbsCDRaddr: AbsCDRaddrFormat; {Absolute CD-ROM Address}
  99.     TrackRaddr: AbsCDRaddrFormat; {Track Relative CD-ROM Address}
  100.     MCVal     : Byte;
  101.     MCN       : Array[0..14] of Byte; {Media Catalog Number (UPC/Bar Code}
  102.     TCVal     : Byte;
  103.     TrackISRC : Array[0..14] of Byte; {Track International-Standard-Recording-Code}
  104.  END;
  105.  _WAVEhdr = RECORD
  106.     MainDesc  : LongInt;
  107.     lenFile   : LongInt;
  108.     FileType  : LongInt;
  109.     SubDesc   : LongInt;
  110.     lenSubDesc: LongInt;
  111.     Format    : Word;
  112.     Mode      : Word;
  113.     SampleFreq: LongInt;
  114.     BytePerSec: LongInt;
  115.     BytePerSam: Word;
  116.     BitPerSam : Word;
  117.     DataDesc  : LongInt;
  118.     lenData   : LongInt;
  119.  END;
  120.  _ASPI_01 = _ASPI_SRB_GetDeviceType;
  121.  _ASPI_02 = _ASPI_SRB_ExecuteSCSI_IORequest;
  122.  
  123. CONST
  124.  PVer = 1.5;
  125.  PName= 'CTCDREAD';
  126.  ModeSelectData : ARRAY[0..11] of BYTE = (
  127.            $00, {Density Code}
  128.            $00,    {Medium Type}
  129.            $00, {Device Specific Parameter}
  130.            $08, {Block Descriptor Length}
  131.            $82, {Density Code=CDDA transfer over SCSI support mode}
  132.        $00,$00,$00, {Number of Blocks}
  133.            $00, {reserved}
  134.        $00,$09,$30);{Block Length = 2352}
  135.  
  136. VAR
  137.  ReadCDDA    : FUNCTION(LBA:LongInt;TransLEN:Byte;PtrBuf:pointer;AllocLEN:WORD):BYTE;
  138.  Option      : BYTE;
  139.  Os          : String;
  140.  ReqDataBuf  : SenseDataFormat;
  141.  ModeDataBuf : ARRAY[0..11] of Byte;
  142.  SRR         : BYTE; {[NEC] mode of disc rotation speed}
  143.  SubCData01  : CDROM_CurrentPositionDataFormat;
  144.  SubCData02  : MediaCatalogNumberDataFormat;
  145.  SubCData03  : TrackISRCDataFormat;
  146.  INQBuf      : InquireDataFormat;
  147.  ReadTOCdata : ReadTOCDataFormat;
  148.  AudioDataBuf: ARRAY[0..2351] of Byte;
  149.  startTrack, endTrack       : BYTE;
  150.  currMSF, startMSF, endMSF  : AbsCDRaddrFormat;
  151.  startLBN, endLBN           : LongInt;
  152.  InstHost    : BYTE; {installed Hostadapter}
  153.  NumHost     : BYTE; {number of Hostadapters}
  154.  InstCDROMdev: BOOLEAN; {installed CD-ROM device}
  155.  PDevTyp     : BYTE; {peripheral device type}
  156.  WaveHDR     : _WAVEhdr;
  157.  WaveFile    : FILE;
  158.  FileName    : PathStr;
  159.  CDRtype     : BYTE;
  160.  WaveFileName: NameStr;
  161.  WaveFilePath: DirStr;
  162.  WaveFileExt : ExtStr;
  163.  CurrentPath : DirStr;
  164.  Ix          : BYTE;
  165.  PSD         : ^SenseDataFormat;
  166.  VC1,VC2     : Integer;
  167.  CurrLine    : BYTE;
  168.  ISRCcode    : LongInt;
  169.  Umleitung   : BOOLEAN;
  170.  CON         : TEXT;
  171.  
  172. FUNCTION HexOut(HexVal:BYTE):STRING;
  173. CONST
  174.   KonvTab: STRING[16] = ('0123456789ABCDEF');
  175. BEGIN
  176.   HexOut:='0x'+KonvTab[(HexVal SHR 4)+1]+KonvTab[(HexVal AND $0F)+1];
  177. END;
  178.  
  179. PROCEDURE TestUmleitung (VAR umleitung:BOOLEAN);
  180. BEGIN
  181.  Umleitung:= mem[prefixseg:$19]<> 1;
  182.  IF Umleitung THEN BEGIN
  183.   assignCRT(CON);
  184.   rewrite(CON);
  185.   assign(Output,'');
  186.   rewrite(Output);
  187.  END;
  188. END;
  189. {------------------------------------------------------}
  190. PROCEDURE ErrorDetect(ErrorText:STRING);
  191. BEGIN
  192.  WRITELN('+++ error ExecuteSCSI_IORequest');
  193.  WRITE('    -> Status      : ',HexOut(_ASPI_02(SRB_ParBlock.pbASPIsrb^)._.Status)+'  <');
  194.  CASE _ASPI_02(SRB_ParBlock.pbASPIsrb^)._.Status of
  195.   $00: WRITE('SCSI request in progress');
  196.   $01: WRITE('SCSI request completed without error');
  197.   $02: WRITE('SCSI request aborted by host');
  198.   $04: WRITE('SCSI request completed with error');
  199.   $80: WRITE('Invalid SCSI request');
  200.   $81: WRITE('Invalid Host Adapter Number');
  201.   $82: WRITE('SCSI device not installed');
  202.  END;
  203.  WRITELN('>');
  204.  WRITE('    -> HostAdapStat: ',HexOut(_ASPI_02(SRB_ParBlock.pbASPIsrb^).HostAdapStat)+'  <');
  205.  CASE _ASPI_02(SRB_ParBlock.pbASPIsrb^).HostAdapStat of
  206.   $00: WRITE('Host Adapter did not detect any error');
  207.   $11: WRITE('Selection timeout');
  208.   $12: WRITE('Data overrun/underrun');
  209.   $13: WRITE('Unexpected Bus Free');
  210.   $14: WRITE('Target bus phase sequence Failure');
  211.  END;
  212.  WRITELN('>');
  213.  WRITE('    -> TargetStat  : ',HexOut(_ASPI_02(SRB_ParBlock.pbASPIsrb^).TargetStat)+'  <');
  214.  CASE _ASPI_02(SRB_ParBlock.pbASPIsrb^).TargetStat of
  215.   $00: WRITE('No Target Status');
  216.   $02: WRITE('Check Condition Status');
  217.   $08: WRITE('Specified Target/LUN is busy');
  218.   $18: WRITE('Reservation conflict');
  219.  END;
  220.  WRITELN('>');
  221.  IF _ASPI_02(SRB_ParBlock.pbASPIsrb^).TargetStat = 02 THEN
  222.  BEGIN
  223.   IF _ASPI_02(SRB_ParBlock.pbASPIsrb^).SenseAllocLen > 0 THEN
  224.   BEGIN
  225.    {pointer sense data}
  226.    PSD:=PTR(SEG(_ASPI_02(SRB_ParBlock.pbASPIsrb^).SCSI_CDB),
  227.         OFS(_ASPI_02(SRB_ParBlock.pbASPIsrb^).SCSI_CDB)+_ASPI_02(SRB_ParBlock.pbASPIsrb^).SCSI_CDBLen);
  228.    WRITE('       -> SenseKey : ',HexOut((PSD^.SenseKey) AND $0F)+'  <');
  229.    CASE ((PSD^.SenseKey) AND $0F) of
  230.     $00: WRITE('NO SENSE');
  231.     $01: WRITE('RECOVERED ERROR');
  232.     $02: WRITE('NOT READY');
  233.     $03: WRITE('MEDIUM ERROR');
  234.     $04: WRITE('HARDWARE ERROR');
  235.     $05: WRITE('ILLEGAL REQUEST');
  236.     $06: WRITE('UNIT ATTENTION');
  237.     $0B: WRITE('ABORTED COMMAND');
  238.    END;
  239.    WRITELN('>');
  240.    WRITELN('       -> ASC|ASCQ : ',HexOut(PSD^.ASC)+'|'+HexOut(PSD^.ASCQ));
  241.   END;
  242.  END;
  243.  IF Length(ErrorText)>0 THEN
  244.  WRITELN('    -> SCSIfunction: ',ErrorText);
  245.  HALT;
  246. END;
  247.  
  248. {------------------------------------------------------}
  249. FUNCTION MSFtoLBN(MSF:AbsCDRaddrFormat):LongInt;
  250. BEGIN
  251.  MSFtoLBN:=MSF.MSF_MM*LongInt($1194)+MSF.MSF_SS*75+MSF.MSF_DD-150;
  252. END;
  253. PROCEDURE LBNtoMSF(LBN:LongInt;var MSF:AbsCDRaddrFormat);
  254. VAR
  255.  LBA:LongInt;
  256. BEGIN
  257.  LBA:=LBN+150;
  258.  MSF.MSF_MM:=(LBA DIV $1194);
  259.  MSF.MSF_SS:=(LBA-((LBA DIV $1194)*longint($1194))) DIV 75;
  260.  MSF.MSF_DD:=(LBA MOD 75);
  261. END;
  262. {------------------------------------------------------}
  263. FUNCTION LBNtoMSFstr(LBN:LongInt):STRING;
  264. VAR
  265.  MSF   : AbsCDRaddrFormat;
  266.  
  267. FUNCTION INTtoSTR(DezVAL:BYTE;OSize:BYTE):STRING;
  268. VAR
  269.  DezStr: STRING[8];
  270. BEGIN
  271.  STR(DezVAL:OSize,DezStr);
  272.  WHILE Pos(' ',DezStr) > 0 do DezStr[Pos(' ',DezStr)] := '0';
  273.  INTtoSTR:=DezStr;
  274. END;
  275.  
  276. BEGIN
  277.  LBNtoMSF(LBN,MSF);
  278.  LBNtoMSFstr:=INTtoSTR(MSF.MSF_MM,2)+':'+INTtoStr(MSF.MSF_SS,2)+'.'+INTtoStr(MSF.MSF_DD,2);
  279. END;
  280. {------------------------------------------------------------------------------}
  281. FUNCTION MSFSTRtoMSF(MSFstr:STRING;var MSF:AbsCDRaddrFormat):BOOLEAN;
  282. VAR
  283.  VCmm,VCss,VCdd: Integer;
  284. BEGIN
  285.  VAL(COPY(MSFstr,1,POS(':',MSFstr)-1),MSF.MSF_MM,VCmm);
  286.  VAL(COPY(MSFstr,POS(':',MSFstr)+1,POS('.',MSFstr)-POS(':',MSFstr)-1),MSF.MSF_SS,VCss);
  287.  VAL(COPY(MSFstr,POS('.',MSFstr)+1,LENGTH(MSFstr)-POS('.',MSFstr)),MSF.MSF_DD,VCdd);
  288.  IF (VCmm OR VCss OR VCss) <> 0 THEN MSFSTRtoMSF:=FALSE
  289.   ELSE MSFSTRtoMSF:=TRUE;
  290. END;
  291. {------------------------------------------------------------------------------}
  292. FUNCTION MSFSTRtoLBN(MSFstr:STRING;var LBN:LongInt):BOOLEAN;
  293. VAR
  294.  MSF: AbsCDRaddrFormat;
  295. BEGIN
  296.  IF MSFSTRtoMSF(MSFstr,MSF) THEN BEGIN
  297.   LBN:=MSFtoLBN(MSF);
  298.   MSFSTRtoLBN:=TRUE;
  299.  END ELSE MSFSTRtoLBN:=FALSE;
  300. END;
  301. {;*******************************************************************************
  302.  ;*                     T e s t U n i t R e a d y                               *
  303.  ;*      TestUnitReady           --                                             *
  304.  ;*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*
  305.  ;* parameter:                                                                  *
  306.  ;* none                                                                        *
  307.  ;* result:                                                                     *
  308.  ;* AL:     status of SRB                                                       *
  309.  ;*         FF: Error_no_unused_SRB                                             *
  310.  ;*******************************************************************************}
  311. FUNCTION TestUnitReady:BYTE;
  312. CONST
  313.  OpC:Array[0..5] of BYTE =($0,0,0,0,0,0);
  314. BEGIN
  315.  IF AllocSRBExecute(@OpC,6) THEN BEGIN
  316.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^)._.SCSIReqFlags:=$00;
  317.   TestUnitReady:=ExecuteSCSI_IORequest;
  318.  END;
  319. END;
  320. {;*******************************************************************************
  321.  ;*                        R e q u e s t S e n s e                              *
  322.  ;*      RequestSense            PtrBuf,AllocLEN                                *
  323.  ;*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*
  324.  ;* parameter:                                                                  *
  325.  ;* ReqDataBuf: pointer of buffer request data                                  *
  326.  ;* AllocLEN  : specifies number of bytes that initiator has allocated for data *
  327.  ;* result:                                                                     *
  328.  ;* AL:     status of SRB                                                       *
  329.  ;*         FF: Error_no_unused_SRB                                             *
  330.  ;*******************************************************************************}
  331. FUNCTION RequestSense(ReqDataBuf:pointer;AllocLEN:BYTE):BYTE;
  332. CONST
  333.  OpC:Array[0..5] of BYTE =($3,0,0,0,0,0);
  334. BEGIN
  335.  IF AllocSRBExecute(@OpC,6) THEN BEGIN
  336.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^)._.SCSIReqFlags:=$08;
  337.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataBufPtrOfs:=Ofs(ReqDataBuf^);
  338.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataBufPtrSeg:=Seg(ReqDataBuf^);
  339.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataAllocLen:=AllocLEN;
  340.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[4]:=AllocLEN;
  341.   RequestSense:=ExecuteSCSI_IORequest;
  342.  END;
  343. END;
  344. {;*******************************************************************************
  345.  ;*                               I n q u i r e                                 *
  346.  ;*      Inquire                 PtrBuf,AllocLEN                                *
  347.  ;*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*
  348.  ;* parameter:                                                                  *
  349.  ;* PtrBuf  : pointer of buffer data                                            *
  350.  ;* AllocLEN: specifies the number of bytes that the initiator has allocated    *
  351.  ;*           for data                                                          *
  352.  ;* result:                                                                     *
  353.  ;* AL:     status of SRB                                                       *
  354.  ;*         FF: Error_no_unused_SRB                                             *
  355.  ;*******************************************************************************}
  356. FUNCTION Inquire(INQBuf:pointer;AllocLEN:BYTE):BYTE;
  357. CONST
  358.  OpC:Array[0..5] of BYTE =($12,0,0,0,0,0);
  359. BEGIN
  360.  IF AllocSRBExecute(@OpC,6) THEN BEGIN
  361.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^)._.SCSIReqFlags:=$00;
  362.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataBufPtrOfs:=Ofs(InqBuf^);
  363.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataBufPtrSeg:=Seg(InqBuf^);
  364.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataAllocLen:=AllocLEN;
  365.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[4]:=AllocLEN;
  366.   Inquire:=ExecuteSCSI_IORequest;
  367.  END;
  368. END;
  369. {;*******************************************************************************
  370.  ;*                         P l a y A u d i o M S F                             *
  371.  ;*      PlayAudioMSF            MSFstart,MSFend                                *
  372.  ;*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*
  373.  ;* parameter:                                                                  *
  374.  ;* MSFstart: specifies the absolute MSF address at which the audio playback    *
  375.  ;*           operation shall begin                                             *
  376.  ;* MSFend  : specifies the absolute MSF address at which the audio playback    *
  377.  ;*           operation shall end.                                              *
  378.  ;* result:                                                                     *
  379.  ;* AL:     status of SRB                                                       *
  380.  ;*         FF: Error_no_unused_SRB                                             *
  381.  ;*******************************************************************************}
  382. FUNCTION PlayAudioMSF(MSFstart,MSFend:AbsCDRaddrFormat):BYTE;
  383. CONST
  384.  OpC:Array[0..9] of BYTE =($47,0,0,0,0,0,0,0,0,0);
  385. BEGIN
  386.  IF AllocSRBExecute(@OpC,10) THEN BEGIN
  387.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^)._.SCSIReqFlags:=$18;
  388.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[3]:=MSFstart.MSF_MM;
  389.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[4]:=MSFstart.MSF_SS;
  390.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[5]:=MSFstart.MSF_DD;
  391.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[6]:=MSFend.MSF_MM;
  392.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[7]:=MSFend.MSF_SS;
  393.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[8]:=MSFend.MSF_DD;
  394.   PlayAudioMSF:=ExecuteSCSI_IORequest;
  395.  END;
  396. END;
  397. {;*******************************************************************************
  398.  ;*                           M o d e S e l e c t                               *
  399.  ;*      ModeSelect              PtrBuf,AllocLEN                                *
  400.  ;*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*
  401.  ;* parameter:                                                                  *
  402.  ;* PtrBuf  : pointer of buffer data                                            *
  403.  ;* AllocLEN: specifies the number of bytes that the initiator has allocated    *
  404.  ;*           for data                                                          *
  405.  ;* result:                                                                     *
  406.  ;* AL:     status of SRB                                                       *
  407.  ;*         FF: Error_no_unused_SRB                                             *
  408.  ;*******************************************************************************}
  409. FUNCTION ModeSelect(ModeDataBuf:pointer;AllocLEN:BYTE):BYTE;
  410. CONST
  411.  OpC:Array[0..5] of BYTE =($15,0,0,0,0,0);
  412. BEGIN
  413.  IF AllocSRBExecute(@OpC,6) THEN BEGIN
  414.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^)._.SCSIReqFlags:=$10;
  415.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataBufPtrOfs:=Ofs(ModeDataBuf^);
  416.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataBufPtrSeg:=Seg(ModeDataBuf^);
  417.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataAllocLen:=AllocLEN;
  418.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[4]:=AllocLEN;
  419.   ModeSelect:=ExecuteSCSI_IORequest;
  420.  END;
  421. END;
  422. {;*******************************************************************************
  423.  ;*                        a d d M o d e S e l e c t (NEC)                      *
  424.  ;*      addModeSelect            PtrBuf,AllocLEN                               *
  425.  ;*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*
  426.  ;* parameter:                                                                  *
  427.  ;* PtrBuf  : pointer of buffer data                                            *
  428.  ;* AllocLEN: specifies the number of bytes that the initiator has allocated    *
  429.  ;*           for data                                                          *
  430.  ;* result:                                                                     *
  431.  ;* AL:     status of SRB                                                       *
  432.  ;*         FF: Error_no_unused_SRB                                             *
  433.  ;*******************************************************************************}
  434. FUNCTION addModeSelect(addModeDataBuf:pointer;AllocLEN:BYTE):BYTE;
  435. CONST
  436.  OpC:Array[0..9] of BYTE =($C5,$10,0,0,0,0,0,0,0,0);
  437. BEGIN
  438.  IF AllocSRBExecute(@OpC,10) THEN BEGIN
  439.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^)._.SCSIReqFlags:=$10;
  440.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataBufPtrOfs:=Ofs(addModeDataBuf^);
  441.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataBufPtrSeg:=Seg(addModeDataBuf^);
  442.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataAllocLen:=AllocLEN;
  443.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[8]:=AllocLEN;
  444.   addModeSelect:=ExecuteSCSI_IORequest;
  445.  END;
  446. END;
  447. {;*******************************************************************************
  448.  ;*                           M o d e S e n s e                                 *
  449.  ;*      ModeSense               PtrBuf,AllocLEN                                *
  450.  ;*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*
  451.  ;* parameter:                                                                  *
  452.  ;* PtrBuf  : pointer of buffer data                                            *
  453.  ;* AllocLEN: specifies number of bytes that initiator has allocated for data   *
  454.  ;* result:                                                                     *
  455.  ;* AL:     status of SRB                                                       *
  456.  ;*         FF: Error_no_unused_SRB                                             *
  457.  ;*******************************************************************************}
  458. FUNCTION ModeSense(ModeDataBuf:pointer;AllocLEN:BYTE):BYTE;
  459. CONST
  460.  OpC:Array[0..5] of BYTE =($1A,0,0,0,0,0);
  461. BEGIN
  462.  IF AllocSRBExecute(@OpC,6) THEN BEGIN
  463.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^)._.SCSIReqFlags:=$08;
  464.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataBufPtrOfs:=Ofs(ModeDataBuf^);
  465.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataBufPtrSeg:=Seg(ModeDataBuf^);
  466.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataAllocLen:=AllocLEN;
  467.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[4]:=AllocLEN;
  468.   ModeSense:=ExecuteSCSI_IORequest;
  469.  END;
  470. END;
  471. {;*******************************************************************************
  472.  ;*                         a d d M o d e S e n s e (NEC)                       *
  473.  ;*      addModeSense             PtrBuf,AllocLEN                               *
  474.  ;*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*
  475.  ;* parameter:                                                                  *
  476.  ;* PtrBuf  : pointer of buffer data                                            *
  477.  ;* AllocLEN: specifies number of bytes that initiator has allocated for data   *
  478.  ;* result:                                                                     *
  479.  ;* AL:     status of SRB                                                       *
  480.  ;*         FF: Error_no_unused_SRB                                             *
  481.  ;*******************************************************************************}
  482. FUNCTION addModeSense(ModeDataBuf:pointer;AllocLEN:BYTE):BYTE;
  483. CONST
  484.  OpC:Array[0..9] of BYTE =($CA,$08,$0F,0,0,0,0,0,0,0);
  485. BEGIN
  486.  IF AllocSRBExecute(@OpC,10) THEN BEGIN
  487.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^)._.SCSIReqFlags:=$08;
  488.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataBufPtrOfs:=Ofs(ModeDataBuf^);
  489.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataBufPtrSeg:=Seg(ModeDataBuf^);
  490.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataAllocLen:=AllocLEN;
  491.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[8]:=AllocLEN;
  492.   addModeSense:=ExecuteSCSI_IORequest;
  493.  END;
  494. END;
  495. {;*******************************************************************************
  496.  ;*                              R e a d  ( 6 )                                 *
  497.  ;*      Read6                   LBA,TransLEN,PtrBuf,AllocLEN                   *
  498.  ;*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*
  499.  ;* parameter:                                                                  *
  500.  ;* LBA     : the logical block address at which the read operation shall begin *
  501.  ;* TransLEN: specifies the number of logical blocks to be transfered           *
  502.  ;* PtrBuf  : pointer of buffer data                                            *
  503.  ;* AllocLEN: specifies number of bytes that initiator has allocated for data   *
  504.  ;* result:                                                                     *
  505.  ;* AL:     status of SRB                                                       *
  506.  ;*         FF: Error_no_unused_SRB                                             *
  507.  ;*******************************************************************************}
  508. FUNCTION Read6(LBA:LongInt;TransLEN:Byte;PtrBuf:pointer;AllocLEN:WORD):BYTE;far;
  509. CONST
  510.  OpC:Array[0..5] of BYTE =($08,0,0,0,0,0);
  511. BEGIN
  512.  IF AllocSRBExecute(@OpC,6) THEN BEGIN
  513.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^)._.SCSIReqFlags:=$08;
  514.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataBufPtrOfs:=Ofs(PtrBuf^);
  515.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataBufPtrSeg:=Seg(PtrBuf^);
  516.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataAllocLen:=AllocLEN;
  517.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[1]:=LO(LBA SHR 16);
  518.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[2]:=HI(LBA AND $FFFF);
  519.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[3]:=LO(LBA AND $FFFF);
  520.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[4]:=TransLEN;
  521.   Read6:=ExecuteSCSI_IORequest;
  522.  END;
  523. END;
  524. {;*******************************************************************************
  525.  ;*                              R e a d C D D A (12)                           *
  526.  ;*      ReadCDDA12              LBA,TransLEN,PtrBuf,AllocLEN                   *
  527.  ;*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*
  528.  ;* parameter:                                                                  *
  529.  ;* LBA     : the logical block address at which the read operation shall begin *
  530.  ;* TransLEN: specifies the number of logical blocks to be transfered           *
  531.  ;* PtrBuf  : pointer of buffer data                                            *
  532.  ;* AllocLEN: specifies number of bytes that initiator has allocated for data   *
  533.  ;* result:                                                                     *
  534.  ;* AL:     status of SRB                                                       *
  535.  ;*         FF: Error_no_unused_SRB                                             *
  536.  ;*******************************************************************************}
  537. FUNCTION ReadCDDA12(LBA:LongInt;TransLEN:Byte;PtrBuf:pointer;AllocLEN:WORD):BYTE;far;
  538. CONST
  539.  OpC:Array[0..11] of BYTE =($D8,0,0,0,0,0,0,0,0,0,0,0);
  540. BEGIN
  541.  IF AllocSRBExecute(@OpC,12) THEN BEGIN
  542.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^)._.SCSIReqFlags:=$08;
  543.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataBufPtrOfs:=Ofs(PtrBuf^);
  544.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataBufPtrSeg:=Seg(PtrBuf^);
  545.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataAllocLen:=AllocLEN;
  546.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[3]:=LO(LBA SHR 16);
  547.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[4]:=HI(LBA AND $FFFF);
  548.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[5]:=LO(LBA AND $FFFF);
  549.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[9]:=TransLEN;
  550.   ReadCDDA12:=ExecuteSCSI_IORequest;
  551.  END;
  552. END;
  553. {;*******************************************************************************
  554.  ;*                              R e a d C D D A (10)                           *
  555.  ;*      ReadCDDA10              LBA,TransLEN,PtrBuf,AllocLEN                   *
  556.  ;*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*
  557.  ;* parameter:                                                                  *
  558.  ;* LBA     : the logical block address at which the read operation shall begin *
  559.  ;* TransLEN: specifies the number of logical blocks to be transfered           *
  560.  ;* PtrBuf  : pointer of buffer data                                            *
  561.  ;* AllocLEN: specifies number of bytes that initiator has allocated for data   *
  562.  ;* result:                                                                     *
  563.  ;* AL:     status of SRB                                                       *
  564.  ;*         FF: Error_no_unused_SRB                                             *
  565.  ;*******************************************************************************}
  566. FUNCTION ReadCDDA10(LBA:LongInt;TransLEN:Byte;PtrBuf:pointer;AllocLEN:WORD):BYTE;far;
  567. CONST
  568.  OpC:Array[0..9] of BYTE =($D4,0,0,0,0,0,0,0,0,0);
  569. BEGIN
  570.  IF AllocSRBExecute(@OpC,10) THEN BEGIN
  571.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^)._.SCSIReqFlags:=$08;
  572.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataBufPtrOfs:=Ofs(PtrBuf^);
  573.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataBufPtrSeg:=Seg(PtrBuf^);
  574.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataAllocLen:=AllocLEN;
  575.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[3]:=LO(LBA SHR 16);
  576.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[4]:=HI(LBA AND $FFFF);
  577.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[5]:=LO(LBA AND $FFFF);
  578.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[8]:=TransLEN;
  579.   ReadCDDA10:=ExecuteSCSI_IORequest;
  580.  END;
  581. END;
  582. {;*******************************************************************************
  583.  ;*                              R e a d  T O C                                 *
  584.  ;*      ReadTOC                 Track,PtrBuf,AllocLEN                          *
  585.  ;*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*
  586.  ;* parameter:                                                                  *
  587.  ;* STrack  : specifies the starting track that the logical block address       *
  588.  ;*           format should be used for the CD-ROM address field                *
  589.  ;* PtrBuf  : pointer of buffer data                                            *
  590.  ;* AllocLEN: specifies number of bytes that initiator has allocated for data   *
  591.  ;* result:                                                                     *
  592.  ;* AL:     status of SRB                                                       *
  593.  ;*         FF: Error_no_unused_SRB                                             *
  594.  ;*******************************************************************************}
  595. FUNCTION ReadTOC(STrack:Byte;TOCdataBuf:pointer;AllocLEN:WORD):BYTE;
  596. CONST
  597.  OpC:Array[0..9] of BYTE =($43,2,0,0,0,0,0,0,0,0);
  598. BEGIN
  599.  IF AllocSRBExecute(@OpC,10) THEN BEGIN
  600.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^)._.SCSIReqFlags:=$08;
  601.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataBufPtrOfs:=Ofs(TOCdataBuf^);
  602.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataBufPtrSeg:=Seg(TOCdataBuf^);
  603.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataAllocLen:=AllocLEN;
  604.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[6]:=STrack;
  605.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[7]:=hi(AllocLEN);
  606.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[8]:=lo(AllocLEN);
  607.   ReadTOC:=ExecuteSCSI_IORequest;
  608.  END;
  609. END;
  610. {;*******************************************************************************
  611.  ;*                              R e a d  S u b C                               *
  612.  ;*      ReadSubC               STrack,SubCDF,PtrBuf,AllocLEN                   *
  613.  ;*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*
  614.  ;* parameter:                                                                  *
  615.  ;* STrack  : specifies the starting track that the logical block address       *
  616.  ;*           format should be used for the CD-ROM address field                *
  617.  ;* SubCDF  : specifies the format of returned sub-channel data                 *
  618.  ;* PtrBuf  : pointer of buffer data                                            *
  619.  ;* AllocLEN: specifies number of bytes that initiator has allocated for data   *
  620.  ;* result:                                                                     *
  621.  ;* AL:     status of SRB                                                       *
  622.  ;*         FF: Error_no_unused_SRB                                             *
  623.  ;*******************************************************************************}
  624. FUNCTION ReadSubC(STrack,SubCDF:Byte;SubCdataBuf:pointer;AllocLEN:WORD):BYTE;
  625. CONST
  626.  OpC:Array[0..9] of BYTE =($42,2,$40,0,0,0,0,0,0,0);
  627. BEGIN
  628.  IF AllocSRBExecute(@OpC,10) THEN BEGIN
  629.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^)._.SCSIReqFlags:=$08;
  630.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataBufPtrOfs:=Ofs(SubCdataBuf^);
  631.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataBufPtrSeg:=Seg(SubCdataBuf^);
  632.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).DataAllocLen:=AllocLEN;
  633.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[3]:=SubCDF;
  634.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[6]:=STrack;
  635.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[7]:=hi(AllocLEN);
  636.   _ASPI_02 (SRB_ParBlock.pbASPIsrb^).SCSI_CDB[8]:=lo(AllocLEN);
  637.   ReadSubC:=ExecuteSCSI_IORequest;
  638.  END;
  639. END;
  640.  
  641. BEGIN { MAIN }
  642.  ClrScr;
  643.  TestUmleitung(Umleitung);
  644.  WRITELN;
  645.  WRITELN(PName,' version ',PVer:4:2,' U. Rohbeck, 22/09/94');
  646.  { no parameter, display help text }
  647.  IF ParamCount = 0 THEN BEGIN
  648.   WRITELN('syntax: ',PNAME,' [option] WaveFile[.WAV]');
  649.   WRITELN('  -l                   track listing of CD');
  650.   WRITELN('  -d:<type>            device select');
  651.   WRITELN('     <type>: HITACHI');
  652.   WRITELN('             NEC');
  653.   WRITELN('             SONY');
  654.   WRITELN('             TOSHIBA (default)');
  655.   WRITELN('  -mp                  mode: play audio');
  656.   WRITELN('  -mw                  mode: write to file in WAVE format');
  657.   WRITELN('  -fT[=[start][,end]]  format: send whole track to file');
  658.   WRITELN('  -fM[=[start][,end]]  format: send sector using MSF format MM:SS.DD');
  659.   WRITELN('  -w                   watching audio');
  660.   HALT;
  661.  END;
  662.  { search ASPI manager }
  663.  IF NOT (InitSCSIMgr) THEN BEGIN
  664.   WRITELN('+++ no SCSIMgr installed');
  665.   HALT;
  666.  END;
  667.  { get number of hostadapters }
  668.  GetInstHostAdapters(InstHost,NumHost);
  669.  SRB_ParBlock.pbHostAdapNum:=0;
  670.  { find first CD-ROM drive }
  671.  WHILE ((NumHost>0) AND (NOT InstCDROMdev)) DO BEGIN
  672.   IF ((InstHost SHR SRB_ParBlock.pbHostAdapNum) AND $01)=01 THEN BEGIN
  673.    {get device information}
  674.    InstCDROMdev:=FALSE;
  675.    SRB_ParBlock.pbTargetID:=0;
  676.    SRB_ParBlock.pbLUN:=0;
  677.    { scan SCSI ID for CD-ROM (01) }
  678.    WHILE (SRB_ParBlock.pbTargetID<8) AND (NOT InstCDROMdev) DO BEGIN
  679.     IF GetDeviceType=01 THEN BEGIN
  680.      IF _ASPI_01 (SRB_ParBlock.pbASPIsrb^).PDevTyp=$05 THEN BEGIN
  681.       InstCDROMdev:=TRUE;
  682.       IF Inquire(@InqBuf,SizeOf(InqBuf)) = $0FF THEN ErrorDetect('Inquire(@InqBuf,SizeOf(InqBuf))') ELSE BEGIN
  683.        IF SRB_ParBlock.pbASPIstatus = $01 THEN BEGIN
  684.     WRITELN(InqBuf.VendorID,' ',InqBuf.ProductID,' CD-DA Audio Dump');
  685.     CDRtype:=1;
  686.     { check if CD-ROM is supported }
  687.     WHILE (CDRtype<=CDRsupport) AND (COPY(InqBuf.VendorID,1,length(CDRtypeLST[CDRtype]))<>CDRtypeLST[CDRtype])
  688.      DO INC(CDRtype);
  689.     IF CDRtype > CDRsupport THEN BEGIN
  690.      WRITELN('+++ warning: device type <',OS,'> not supported');
  691.      CDRtype:=1; {default: TOSHIBA}
  692.     END;
  693.        END ELSE ErrorDetect('');
  694.        IF RequestSense(@ReqDataBuf,sizeof(ReqDataBuf)) <> 01 THEN ErrorDetect('RequestSense(@ReqDataBuf,sizeof(ReqDataBuf))');
  695.        IF TestUnitReady <> 01 THEN ErrorDetect('TestUnitReady');
  696.       END;
  697.      END;
  698.     END;
  699.     IF NOT InstCDROMdev THEN
  700.       INC(SRB_ParBlock.pbTargetID);
  701.    END; { WHILE }
  702.    DEC(NumHost);
  703.   END;
  704.  END; { WHILE }
  705.  WRITELN;
  706.  IF NOT InstCDROMdev THEN BEGIN
  707.   WRITELN('+++ error: no CD-ROM device installed');
  708.   HALT;
  709.  END;
  710.  { read TOC }
  711.  IF ReadTOC(00,@ReadTOCdata,sizeof(ReadTOCdata))<>01 THEN ErrorDetect('ReadTOC(00,@ReadTOCdata,sizeof(ReadTOCdata))');
  712.  FileName:='';
  713.  Option:=0;
  714.  Ix:=1;
  715.  { check command line }
  716.  WHILE Ix<=ParamCount DO BEGIN
  717.   Os:= ParamStr(Ix);
  718.   IF (Os[1]= '-') OR (Os[1]='/') THEN BEGIN
  719.    CASE Os[2] of
  720.     'l','L':BEGIN { -l: Track listing of CD }
  721.          Option:=Option OR $80;
  722.         END;
  723.     'd','D':BEGIN { -d: Drive Type }
  724.          OS:=COPY(OS,POS(':',OS)+1,LENGTH(OS)-POS(':',OS)+1);
  725.          FOR VC1:=1 TO LENGTH(OS) DO OS[VC1]:=UpCase(OS[VC1]);
  726.          CDRtype:=1;    {TOSHIBA, default}
  727.          WHILE (CDRtype<=CDRsupport) AND (OS<>CDRtypeLST[CDRtype]) DO
  728.           INC(CDRtype);
  729.          IF CDRtype > CDRsupport THEN BEGIN
  730.           WRITELN('+++ device type <',OS,'> not supported');
  731.           HALT;
  732.          END ELSE WRITELN('set device to: ',CDRtypeLST[CDRtype]);
  733.         END;
  734.     'f','F':BEGIN { -fx.. format }
  735.              CASE Os[3] of
  736.           't','T':BEGIN { -fT: send whole Track to file }
  737.                        Option:=(Option AND $FE) OR $1;
  738.                        startTrack:=0;
  739.                        VC1:=0;
  740.                        endTrack  :=0;
  741.                VC2:=0;
  742.                IF POS('=',Os)>0 THEN BEGIN
  743.                         IF POS(',',Os)>0 THEN BEGIN
  744.              VAL(COPY(Os,POS('=',Os)+1,POS(',',Os)-POS('=',Os)-1),
  745.                                   startTrack,VC1);
  746.              VAL(COPY(Os,POS(',',Os)+1,LENGTH(Os)-POS(',',Os)+1),
  747.                   endTrack,VC2);
  748.                         END ELSE VAL(COPY(Os,POS('=',Os)+1,LENGTH(Os)-POS('=',Os)+1),
  749.                                           startTrack,VC1);
  750.                         IF (VC1 OR VC2)<>0 THEN BEGIN
  751.                          WRITELN('+++ illegal track number format');
  752.                          HALT;
  753.             END;
  754.                        END;
  755.                IF startTrack=0 THEN StartTrack:=ReadTOCdata.firstTrack;
  756.                IF endTrack=0 THEN endTrack:=ReadTOCdata.lastTrack;
  757.                startLBN:=MSFtoLBN(ReadTOCdata.TOCtd[startTrack].AbsCDRaddr);
  758.                endLBN:=MSFtoLBN(ReadTOCdata.TOCtd[endTrack+1].AbsCDRaddr)-1;
  759.               END;
  760.           'm','M':BEGIN { -fM: send sector using MSF format MM:SS:DD }
  761.                Option:=(Option AND $FE) OR $1;
  762.                startLBN:=0;
  763.                VC1:=0;
  764.                endLBN:=0;
  765.                VC2:=0;
  766.                IF POS('=',Os)>0 THEN BEGIN
  767.             IF POS(',',Os)>0 THEN BEGIN
  768.              IF POS(',',Os)-1 = POS('=',Os) THEN MSFSTRtoLBN('00:02.00',startLBN)
  769.               ELSE BEGIN
  770.               IF NOT MSFSTRtoLBN(COPY(Os,POS('=',Os)+1,POS(',',Os)-POS('=',Os)-1),startLBN)
  771.                THEN VC1:=$FF;
  772.              END;
  773.              IF NOT MSFSTRtoLBN(COPY(Os,POS(',',Os)+1,LENGTH(Os)-POS(',',Os)+1),endLBN)
  774.               THEN VC1:=$FF;
  775.             END ELSE BEGIN
  776.              IF NOT MSFSTRtoLBN(COPY(Os,POS('=',Os)+1,LENGTH(Os)-POS('=',Os)-1),startLBN)
  777.               THEN VC1:=$FF;
  778.             END;
  779.                END;
  780.                IF (VC1 OR VC2)<>0 THEN BEGIN
  781.             WRITELN('+++ illegal MSF format');
  782.             HALT;
  783.                END;
  784.                IF (startLBN=0) OR
  785.               (startLBN<MSFtoLBN(ReadTOCdata.TOCtd[ReadTOCdata.firstTrack].AbsCDRaddr))
  786.                THEN
  787.             startLBN:=MSFtoLBN(ReadTOCdata.TOCtd[ReadTOCdata.firstTrack].AbsCDRaddr);
  788.                IF (endLBN=0) OR
  789.               (endLBN>MSFtoLBN(ReadTOCdata.TOCtd[ReadTOCdata.lastTrack+1].AbsCDRaddr)-1)
  790.                THEN
  791.             endLBN:=MSFtoLBN(ReadTOCdata.TOCtd[ReadTOCdata.lastTrack+1].AbsCDRaddr)-1;
  792.               END; { -fM }
  793.          END; { Case -fx }
  794.         END; { -f }
  795.     'm','M':BEGIN { -mx mode select, play or write }
  796.          CASE Os[3] of
  797.           'p','P': Option:=(Option AND $CF) OR $10; { -mP play audio }
  798.           'w','W': Option:=(Option AND $CF) OR $20; { -mW write WAV  }
  799.          END;
  800.         END;
  801.     'w','W':Option:=(Option AND $BF) OR $40; { -w watching audio }
  802.    END; { CASE }
  803.   END ELSE FileName:=ParamStr(Ix);
  804.   INC(Ix);
  805.  END; (* WHILE *)
  806.  IF ((Option AND $10)<>0) AND ((Option AND $01)=0) THEN BEGIN
  807.   WRITELN('+++ format parameter required');
  808.   HALT;
  809.  END;
  810.  { -l, display CD ROM Track list }
  811.  IF (Option AND $80) <> 0 THEN BEGIN
  812.   WITH ReadTOCdata DO BEGIN
  813.    WRITELN('*** TRACK LISTING OF CD ***');
  814.    WRITELN;
  815.    WRITELN('Start track: ',firstTrack:2);
  816.    WRITELN('Last  track: ',lastTrack:2);
  817.    WRITELN('Total time : ',TOCtd[ReadTOCdata.lastTrack+1].AbsCDRaddr.MSF_MM,' minutes ',
  818.         TOCtd[ReadTOCdata.lastTrack+1].AbsCDRaddr.MSF_SS,' seconds ',
  819.         TOCtd[ReadTOCdata.lastTrack+1].AbsCDRaddr.MSF_DD,' frames (75th/s)');
  820.    IF ReadSubC(startTrack,$02,@SubCData02,sizeof(SubCData02)) <> 01
  821.     THEN ErrorDetect('ReadSubC(startTrack,$02,@SubCData02,sizeof(SubCData02))');
  822.    WRITE('UPC/BarCode: ');
  823.    IF (subCData02.MCVAL AND $80) = 0 THEN WRITELN('*** not available ***')
  824.     ELSE BEGIN
  825.     FOR Ix:=0 TO 4 DO WRITE(SubCData02.MCN[Ix]);
  826.     WRITE(' ');
  827.     FOR Ix:=5 TO 7 DO WRITE(SubCData02.MCN[Ix]);
  828.     WRITE('-');
  829.     FOR Ix:=8 TO 10 DO WRITE(SubCData02.MCN[Ix]);
  830.     WRITE('-');
  831.     FOR Ix:=11 TO 11 DO WRITE(SubCData02.MCN[Ix]);
  832.     WRITE(' ');
  833.     FOR Ix:=12 TO 14 DO WRITE(SubCData02.MCN[Ix]);
  834.    END;
  835.    WRITELN;
  836.    WRITELN;
  837.    WRITELN('====================================================================');
  838.    WRITELN('|     |       | MSF_start |              | Digital Copy | Audio    |');
  839.    WRITELN('| No. | Type  | time      | Pre-Emphasis | Permitted    | Channels |');
  840.    WRITELN('|-----+-------+-----------+--------------+--------------+----------|');
  841.    FOR Ix:=firstTrack TO lastTrack DO BEGIN
  842.     WRITE('| ',Ix:2,'  |');
  843.     IF (TOCtd[Ix].AdrControl AND $04) <> 0 THEN WRITE(' data ')
  844.      ELSE WRITE(' audio');
  845.     WRITE(' | ',LBNtoMSFstr(MSFtoLBN(TOCtd[Ix].AbsCDRaddr)),'  |');
  846.     IF (TOCtd[Ix].AdrControl AND $02) = 0 THEN WRITE('    without   |')
  847.      ELSE WRITE('    with      |');
  848.     IF (TOCtd[Ix].AdrControl AND $01) <> 0 THEN WRITE('      yes     |')
  849.      ELSE WRITE('      no      |');
  850.     IF (TOCtd[Ix].AdrControl AND $08) = 0 THEN WRITELN('     2    |')
  851.      ELSE WRITELN('     4    |');
  852.    END;
  853.    WRITE('| LO  |      ');
  854.    WRITE(' | ',LBNtoMSFstr(MSFtoLBN(TOCtd[Ix+1].AbsCDRaddr)),'  |');
  855.    WRITELN('              |              |          |');
  856.  
  857.    WRITELN('====================================================================');
  858.    WRITELN;
  859.   END;
  860.  END;
  861.  IF (Option AND $30) <> 0 THEN BEGIN
  862.   IF ((Option AND $01)=0) OR (startLBN > endLBN) THEN BEGIN
  863.    WRITELN('+++ error format parameter detected');
  864.    HALT;
  865.   END;
  866.   Ix:=ReadTOCdata.firstTrack;
  867.   WHILE ((Ix<(ReadTOCdata.lastTrack)+1) AND (MSFtoLBN(ReadTOCdata.TOCtd[IX].AbsCDRaddr) < startLBN))
  868.    DO INC(Ix);
  869.   IF (ReadTOCdata.TOCtd[IX].AdrControl AND $04) <> 0 THEN BEGIN
  870.    WRITELN('+++ error: data track detected');
  871.    HALT;
  872.   END;
  873.   WHILE ((Ix<(ReadTOCdata.lastTrack)+1) AND (MSFtoLBN(ReadTOCdata.TOCtd[IX].AbsCDRaddr) < endLBN))
  874.    DO BEGIN
  875.    IF (ReadTOCdata.TOCtd[IX].AdrControl AND $04) <> 0 THEN BEGIN
  876.     WRITELN('+++ error: data track detected');
  877.     HALT;
  878.    END;
  879.    INC(Ix);
  880.   END;
  881.  END;
  882.  {play audio}
  883.  IF (Option AND $10) <> 0 THEN BEGIN
  884.   LBNtoMSF(startLBN,startMSF);
  885.   LBNtoMSF(endLBN,endMSF);
  886.   IF PlayAudioMSF(startMSF,endMSF) <> 01 THEN ErrorDetect('PlayAudioMSF(startMSF,endMSF)');
  887.   { watch audio }
  888.   IF (Option AND $40) <> 0 THEN BEGIN
  889.    LBNtoMSF($00,SubCData01.AbsCDRaddr);
  890.    WRITELN('... watching audio');
  891.    WRITELN;
  892.    WRITELN('Country Code :       --');
  893.    WRITELN('Owner Code   :      ---');
  894.    WRITELN('year of recording: 19xx');
  895.    WRITELN;
  896.    WRITE('position: ');
  897.    CurrLine:=WHEREY;
  898.    WHILE (MSFtoLBN(SubCData01.AbsCDRaddr)) <= endLBN DO BEGIN
  899.     IF (SubCData01.TNO<>SubCData03.TNO)    THEN BEGIN
  900.      IF ReadSubC(SubCData01.TNO,03,@SubCData03,sizeof(SubCData03)) <> 01
  901.       THEN ErrorDetect('ReadSubC(SubCData01.TNO,03,@SubCData03,sizeof(SubCData03))');
  902.      IF (SubCData03.TCVAL AND $80) <>0 THEN BEGIN
  903.       WITH SubCData03 DO BEGIN
  904.        ISRCcode:=0;
  905.        FOR Ix:=0 TO 7 DO ISRCcode:=(ISRCcode SHL 4) OR (TrackISRC[Ix] AND $0F);
  906.        GOTOXY(22,CurrLine-4);
  907.        WRITE(CHR($30+((ISRCcode SHR 26) AND $3F))+
  908.        CHR($30+((ISRCcode SHR 20) AND $3F)));
  909.        GOTOXY(21,CurrLine-3);
  910.        WRITE(CHR($30+((ISRCcode SHR 14) AND $3F))+
  911.         CHR($30+((ISRCcode SHR 8) AND $3F))+
  912.         CHR($30+((ISRCcode SHR 2) AND $3F)));
  913.        GOTOXY(20,CurrLine-2);
  914.        WRITE('19',TrackISRC[8]*10+TrackISRC[9]);
  915.       END;
  916.      END;
  917.     END;
  918.     ReadSubC(00,01,@SubCData01,sizeof(SubCData01));
  919.     GotoXY(12,CurrLine);
  920.     WRITE('Track: #',SubCData01.TNO);
  921.     WRITE('  abs: ',LBNtoMSFstr(MSFtoLBN(SubCData01.AbsCDRaddr)));
  922.     WRITE('  rel: ',LBNtoMSFstr(MSFtoLBN(SubCData01.TrackRaddr)));
  923.    END;
  924.   END;
  925.  END;
  926.  {write to file in WAVE-format}
  927.  IF (Option AND $20) <> 0 THEN BEGIN
  928.   IF FileName='' THEN BEGIN
  929.    WRITELN('+++ no valid filename');
  930.    HALT;
  931.   END;
  932.   GetDir($0,CurrentPath);
  933.   IF CurrentPath[Length(CurrentPath)]<>'\' THEN CurrentPath:=CurrentPath+'\';
  934.   FSPLIT(FileName,WaveFilePath,WaveFileName,WaveFileExt);
  935.   IF WaveFilePath='' THEN WaveFilePath:=CurrentPath;
  936.   IF WaveFilePath[Length(WaveFilePath)]<>'\' THEN WaveFilePath:=WaveFilePath+'\';
  937.   IF WaveFileExt='' THEN WaveFileExt:='.WAV';
  938.   CASE CDRtype of
  939.    $01:BEGIN {TOSHIBA}
  940.     { read CD-ROM parameter table }
  941.     IF ModeSense(@ModeDataBuf,sizeof(ModeDataBuf)) <> 01 THEN ErrorDetect('ModeSense(@ModeDataBuf,sizeof(ModeDataBuf))');
  942.     IF ModeSelect(@ModeSelectData,sizeof(ModeSelectData)) <> 01
  943.     THEN ErrorDetect('ModeSelect(@ModeSelectData,sizeof(ModeSelectData))');
  944.      ReadCDDA:=Read6;
  945.        END;
  946.    $02:ReadCDDA:=ReadCDDA12; {HITACHI}
  947.    $03:BEGIN {NEC}
  948.     IF addModeSense(@ModeDataBuf,sizeof(ModeDataBuf)) <> 01
  949.     THEN ErrorDetect('addModeSense(@ModeDataBuf,sizeof(ModeDataBuf))');
  950.     SRR:=ModeDataBuf[6] AND $20; {mode of disc rotation speed}
  951.     {set speed=normal}
  952.     ModeDataBuf[6]:=ModeDataBuf[6] OR $20;
  953.     IF addModeSelect(@ModeDataBuf,sizeof(ModeDataBuf)) <> 01
  954.     THEN ErrorDetect('addModeSelect(@ModeDataBuf,sizeof(ModeDataBuf))');
  955.     ReadCDDA:=ReadCDDA10;
  956.        END;
  957.    $04:ReadCDDA:=ReadCDDA12; {SONY}
  958.   END;
  959.   ASSIGN(WaveFile,WaveFilePath+WaveFileName+WaveFileExt);
  960.   REWRITE(WaveFile,1);
  961.   {initialize WAVEheader}
  962.   WAVEhdr.MainDesc:=$46464952; {'RIFF'}
  963.   WAVEhdr.FileType:=$45564157; {'WAVE'}
  964.   WAVEhdr.SubDesc:=$20746D66;  {'fmt '}
  965.   WAVEhdr.lenSubDesc:=$10;
  966.   WAVEhdr.Format:=$01;    {PCM}
  967.   WAVEhdr.Mode:=$02;        {Stereo}
  968.   WAVEhdr.SampleFreq:=$0AC44;    {44.1 KHz}
  969.   WAVEhdr.BytePerSec:=$2B110;    {16Bit-Stereo-Sound}
  970.   WAVEhdr.BytePerSam:=$04;    {16Bit-Stereo-Sound}
  971.   WAVEhdr.BitPerSam:=$10;    {16-Bit}
  972.   WAVEhdr.DataDesc:=$61746164;    {'data'}
  973.   WAVEhdr.lenData:=longInt((endLBN+1)-startLBN)*$930;
  974.   WAVEhdr.lenFile:=sizeof(WAVEhdr)-8+WAVEhdr.lenData;
  975.   WRITELN('total bytes requested: ',WAVEhdr.lenFile,' Byte');
  976.   IF DiskFree(ORD(WaveFilePath[1])-$40)< WAVEhdr.lenFile THEN BEGIN
  977.    WRITELN('+++ not enough space available on target drive');
  978.    HALT;
  979.   END;
  980.   BLOCKWRITE(WaveFile,WAVEhdr,sizeof(WAVEhdr));
  981.   WRITELN;
  982.   WHILE startLBN <= endLBN DO BEGIN
  983.    IF READCDDA(startLBN,1,@AudioDataBuf,sizeof(AudioDataBuf))<>01
  984.     THEN ErrorDetect('READCDDA(startLBN,1,@AudioDataBuf,sizeof(AudioDataBuf))');
  985.    BLOCKWRITE(WaveFile,AudioDataBuf,sizeof(AudioDataBuf));
  986.    GotoXY(1,WhereY);
  987.    WRITE('MSF: ',LBNtoMSFstr(startLBN));
  988.    INC(startLBN);
  989.   END;
  990.   CLOSE(WaveFile);
  991.   CASE CDRtype of
  992.    $01:BEGIN {TOSHIBA}
  993.     ModeDataBuf[0]:=0;
  994.     IF ModeSelect(@ModeDataBuf,sizeof(ModeDataBuf)) <> 01
  995.     THEN ErrorDetect('ModeSelect(@ModeDataBuf,sizeof(ModeDataBuf))');
  996.        END;
  997.    $03:BEGIN {NEC}
  998.     {set speed=saved}
  999.     ModeDataBuf[6]:=ModeDataBuf[6] OR (SRR AND $20);
  1000.     IF addModeSelect(@ModeDataBuf,sizeof(ModeDataBuf)) <> 01
  1001.     THEN ErrorDetect('addModeSelect(@ModeDataBuf,sizeof(ModeDataBuf))');
  1002.        END;
  1003.   END;
  1004.   WRITELN;
  1005.  END;
  1006. END.
  1007.