From marcj@nando.net Wed Mar  1 17:51:03 1995



Subject: CD-ROM FAQ

Date: 19 Feb 1995 23:18:50 -0500




In recognition of a number of questions I've seen recently asking 

questions on programming CD-ROMs...


CD-ROM programming FAQ Version 1.00


Copyright (C) 1995 by Marcus W. Johnson. All rights reserved. This

article is not in the public domain, but it may be redistributed so

long as this notice, the acknowledgments, and the information on

obtaining the latest copy of this list are retained and no fee is

charged. The code fragments may be used freely; credit would be

polite.


------- Table of Contents --------------------------------------------


Section 0 - Availability

 0.01. How can I get the latest copy of this FAQ?

Section 1 - MSCDEX Status

 1.01. How do I know if MSCDEX is installed?

 1.02. How do I determine the MSCDEX version?

Section 2 - CD-ROM Existence

 2.01. How many CD-ROMs are present?

 2.02. Which drives are CD-ROMs?

 2.03. How do I get the name of the CD-ROM device driver?

Section 3 - Drive Interface

 3.01. How do I open the door?

 3.02. How do I close the door?

 3.03. How do I unlock the door?

 3.04. How do I lock the door?

 3.05. How do I reset the drive?

 3.06. How do I get drive status?

Section 4 - Drive Capacity

 4.01. What sector size is supported?

 4.02. How many sectors are on the disk?

 4.03. How much data is on the disk?

Section 5 - Volume Table of Contents

 5.01. How do I get the abstract file name?

 5.02. How do I get the bibliography file name?

 5.03. How do I get the copyright file name?

 5.04. How do I read the Volume Table of Contents (VTOC)?

Section 6 - Audio

 6.01. How do I find out how many tracks are on a CD?

 6.02. What are Red Book and HSG formats?

 6.03. How can I determine where a particular track starts?

 6.04. How do I play audio?

 6.05. How do I pause audio playback?

 6.06. How do I resume audio playback?


======================================================================

Section 0 - Administration

----------------------------------------------------------------------

0.01. How can I get the latest copy of this FAQ?


      The FAQ is published monthly in comp.os.msdos.programming and

      alt.msdos.programmer.

----------------------------------------------------------------------

0.02. Where did this information come from?


      Ralf Brown's interrupt list

      "MS-DOS Extensions", by Ray Duncan, Microsoft Press

      My personal research for "PC-Programmer's Guide to Low-Level

      Functions and Interrupts", Sams

      The mention of particular books or programs must not be construed

      to reflect unfavorably on any that are not mentioned.

----------------------------------------------------------------------

0.03. How accurate is this information?


      I have personally tested the code fragments in this FAQ, and

      they appear to work as advertised, but there is no warranty on

      the code or on the techniques described in this article.

      As testing may not have been perfect, and machines and

      configurations vary, it is possible that the fragments will not

      work for you.

      Please send corrections to marcj@nando.net.

======================================================================

Section 1 - MSCDEX Status

======================================================================

1.01. How do I know if MSCDEX is installed?


      Call the MSCDEX installation check function. Here's code that

      performs the check:


      mov  AX,0DADAH

      push AX

      mov  AX,01100H

      int  2FH

      pop  BX

      cmp  BX,0ADADH

      jne  not_installed

      cmp  AL,0FFH

      jne  not_installed

      ;

      ; MSCDEX is installed

      ;

----------------------------------------------------------------------

1.02. How do I determine the MSCDEX version?


      Call the MSCDEX version check function. Here's code that gets

      the version:


      mov  AX,150CH

      int  2FH

      ;

      ; BH holds the major version

      ; BL holds the minor version

      ; Prior to MSCDEX version 2.0, the version returned is 0.0 (BX =

      ; 0)

      ;

======================================================================

Section 2 - CD-ROM Existence

======================================================================

2.01. How many CD-ROMs are present?


      Ask MSCDEX. Here's code that gives the count of CD-ROMs

      installed and the drive letter of the first one:


      mov  AX,1500H

      xor  BX,BX

      int  2FH

      ;

      ; BX will hold the number of CD-ROMs

      ; CL will hold the first CD-ROM's drive; 0 = A:, 1 = B:, and so

      ; on

      ;


      A problem with this method, BTW, is that it conflicts with DOS

      4.0's GRAPHICS.COM.

----------------------------------------------------------------------

2.02. Which drives are CD-ROMs?


      There are two ways to find out. Both ways require MSCDEX version

      2 (see question 1.02, How do I determine the MSCDEX version?).

      The first way gives a list of all CD-ROM drives; the second

      verifies whether a specific drive is a CD-ROM.


      Method 1: (get list of CD-ROMs)

      This method requires a block of memory; the size, in bytes, must

      be at least the number of drives returned by function 1500H (see

      question 2.01, How many CD-ROMs are present?).


      mov  AX,150DH

      les  BX,LetterArray

      int.2FH

      ;

      ; each byte in LetterArray will contain a drive value (0 = A:, 1

      ; = B:, etc.)

      ;


      Method 2: (is a specified drive a CD-ROM?)


      mov  AX,150BH

      mov  CX,Drive  ; 0 = A:, 1 = B:, and so on

      int  2FH

      or   AX,AX

      jz   not_cd_rom

      cmp  BX,0ADADH

      jne  not_cd_rom

      ;

      ; the drive is a CD-ROM

      ;

----------------------------------------------------------------------

2.03. How do I get the name of the CD-ROM device driver?


      First, you need to know how many CD-ROMs you have (see question

      2.01, How many CD-ROMs are present?). You need a block of memory

      whose size, in bytes, is 5 times the number of CD-ROMs present.

      This code will fill that array:


      mov  AX,1501H

      les  BX,DriverArray

      int  2FH


      Each 5-byte element in the array consists of the drive's subunit

      number (a CD-ROM device driver may support several drives as

      subunits), followed by the address of the drive's device driver.

      The filename is 10 bytes into the device driver. The filename is

      at most 8 bytes long, and if less than 8 bytes, is terminated by

      a space (20H).

======================================================================

Section 3 - Drive Interface

======================================================================

3.01. How do I open the door?


      First, you need the name of the device driver (see question 2.03,

      How do I get the name of the CD-ROM device driver?). Open the

      file for read/write and obtain the file handle (DOS function 3DH

      will suffice).


      Once you have the file handle, you need a one byte block of

      memory. Call DOS IOCTL function 4403H, as shown here:


      mov  BX,FileHandle

      mov  Command,0

      lds  DX,Command

      mov  CX,1

      mov  AX,4403H

      int  21H

      jc   error

      cmp  AX,1

      jne  write_error

      ;

      ; door should be open

      ;


      On error (carry set), AX will hold an error code: 0001H (invalid

      function), 0005H (access denied), 0006H (invalid handle), or

      000DH (invalid data).

----------------------------------------------------------------------

3.02. How do I close the door?


      First, you need the name of the device driver (see question 2.03,

      How do I get the name of the CD-ROM device driver?). Open the

      file for read/write and obtain the file handle (DOS function 3DH

      will suffice).


      Once you have the file handle, you need a one byte block of

      memory. Call DOS IOCTL function 4403H, as shown here:


      mov  BX,FileHandle

      mov  Command,5

      lds  DX,Command

      mov  CX,1

      mov  AX,4403H

      int  21H

      jc   error

      cmp  AX,1

      jne  write_error

      ;

      ; door should be closed

      ;


      On error (carry set), AX will hold an error code: 0001H (invalid

      function), 0005H (access denied), 0006H (invalid handle), or

      000DH (invalid data).


      The drive should be reset after closing the door before

      accessing the drive (see question 3.05, How do I reset the

      drive?).

----------------------------------------------------------------------

3.03. How do I unlock the door?


      First, you need the name of the device driver (see question 2.03,

      How do I get the name of the CD-ROM device driver?). Open the

      file for read/write and obtain the file handle (DOS function 3DH

      will suffice).


      Once you have the file handle, you need a two-byte block of

      memory. Call DOS IOCTL function 4403H, as shown here:


      mov  BX,FileHandle

      mov  Command,1

      mov  Command+1,0

      lds  DX,Command

      mov  CX,2

      mov  AX,4403H

      int  21H

      jc   error

      cmp  AX,2

      jne  write_error

      ;

      ; door should be unlocked

      ;


      On error (carry set), AX will hold an error code: 0001H (invalid

      function), 0005H (access denied), 0006H (invalid handle), or

      000DH (invalid data).


      The drive should be reset after unlocking the door before

      accessing the drive (see question 3.05, How do I reset the

      drive?).

----------------------------------------------------------------------

3.04. How do I lock the door?


      First, you need the name of the device driver (see question 2.03,

      How do I get the name of the CD-ROM device driver?). Open the

      file for read/write and obtain the file handle (DOS function 3DH

      will suffice).


      Once you have the file handle, you need a two-byte block of

      memory. Call DOS IOCTL function 4403H, as shown here:


      mov  BX,FileHandle

      mov  Command,1

      mov  Command+1,1

      lds  DX,Command

      mov  CX,2

      mov  AX,4403H

      int  21H

      jc   error

      cmp  AX,2

      jne  write_error

      ;

      ; door should be locked

      ;


      On error (carry set), AX will hold an error code: 0001H (invalid

      function), 0005H (access denied), 0006H (invalid handle), or

      000DH (invalid data).


      The drive should be reset after locking the door before

      accessing the drive (see question 3.05, How do I reset the

      drive?).

----------------------------------------------------------------------

3.05. How do I reset the drive?


      First, you need the name of the device driver (see question 2.03,

      How do I get the name of the CD-ROM device driver?). Open the

      file for read/write and obtain the file handle (DOS function 3DH

      will suffice).


      Once you have the file handle, you need a one-byte block of

      memory. Call DOS IOCTL function 4403H, as shown here:


      mov  BX,FileHandle

      mov  Command,2

      lds  DX,Command

      mov  CX,1

      mov  AX,4403H

      int  21H

      jc   error

      cmp  AX,1

      jne  write_error

      ;

      ; drive should be reset

      ;


      On error (carry set), AX will hold an error code: 0001H (invalid

      function), 0005H (access denied), 0006H (invalid handle), or

      000DH (invalid data).

----------------------------------------------------------------------

3.06. How do I get drive status?


      First, you need the name of the device driver (see question 2.03,

      How do I get the name of the CD-ROM device driver?). Open the

      file for read/write and obtain the file handle (DOS function 3DH

      will suffice).


      Once you have the file handle, you need a five-byte block of

      memory. Call DOS IOCTL function 4402H, as shown here:


      mov  BX,FileHandle

      mov  Command,6

      lds  DX,Command

      mov  CX,5

      mov  AX,4402H

      int  21H

      jc   error

      cmp  AX,5

      jne  read_error

      ;

      ; The word at offset 1 of the five-byte block of memory contains

      ; status

      ; bit 10 is set if audio is playing

      ; bit  9 is set if Red Book and HSG addressing are both

      ;                  supported

      ; bit  8 is set if audio channel control is supported

      ; bit  7 is set if prefetch requests are supported

      ; bit  5 is set if interleaving is supported

      ; bit  4 is set if audio/video track playback is supported

      ; bit  3 is set if the CD-ROM is writable

      ; bit  2 is set if raw and cooked read is supported

      ; bit  1 is set if the door is unlocked

      ; bit  0 is set if the door is open

      ;


      On error (carry set), AX will hold an error code: 0001H (invalid

      function), 0005H (access denied), 0006H (invalid handle), or

      000DH (invalid data).


      The drive should be reset after checking drive status before

      accessing the drive (see question 3.05, How do I reset the

      drive?).

======================================================================

Section 4 - Drive Capacity

======================================================================

4.01. What sector size is supported?


      First, you need the name of the device driver (see question 2.03,

      How do I get the name of the CD-ROM device driver?). Open the

      file for read/write and obtain the file handle (DOS function 3DH

      will suffice).


      Once you have the file handle, you need a four-byte block of

      memory. Call DOS IOCTL function 4402H, as shown here:


      mov  BX,FileHandle

      mov  Command,7

      lds  DX,Command

      mov  CX,4

      mov  AX,4402H

      int  21H

      jc   error

      cmp  AX,4

      jne  read_error

      ;

      ; The byte at offset 1 of the four-byte block of memory contains

      ; raw/cooked status (0 = cooked, 1 = raw)

      ; The word at offset 2 of the four-byte block of memory contains

      ; the sector size


      On error (carry set), AX will hold an error code: 0001H (invalid

      function), 0005H (access denied), 0006H (invalid handle), or

      000DH (invalid data).


      The drive should be reset after getting the sector size before

      accessing the drive (see question 3.05, How do I reset the

      drive?).

----------------------------------------------------------------------

4.02. How many sectors are on the disk?


      First, you need the name of the device driver (see question 2.03,

      How do I get the name of the CD-ROM device driver?). Open the

      file for read/write and obtain the file handle (DOS function 3DH

      will suffice).


      Once you have the file handle, you need a five-byte block of

      memory. Call DOS IOCTL function 4402H, as shown here:


      mov  BX,FileHandle

      mov  Command,8

      lds  DX,Command

      mov  CX,5

      mov  AX,4402H

      int  21H

      jc   error

      cmp  AX,5

      jne  read_error

      ;

      ; The dword at offset 1 of the five-byte block of memory

      ; contains the number of sectors


      On error (carry set), AX will hold an error code: 0001H (invalid

      function), 0005H (access denied), 0006H (invalid handle), or

      000DH (invalid data).


      The drive should be reset after getting the number of sectors

      before accessing the drive (see question 3.05, How do I reset

      the drive?).

----------------------------------------------------------------------

4.03. How much data is on the disk?


      See question 4.01, What sector size is supported?, and question

      4.02, How many sectors are on the disk?. Take the product of the

      two values returned. The conventional DOS functions don't work

      reliably.

======================================================================

Section 5 - Volume Table of Contents

======================================================================

5.01. How do I get the abstract file name?


      You need a 38-byte block of memory to hold the abstract file

      name. This code will fill that block:


      les  BX,Buffer

      mov  CX,Drive.; must be in format 0 = A:, 1 = B:, etc.

      mov  AX,1503H

      int  2FH

      jc   error

      ;

      ; buffer is filled with the abstract file name.

      ;


      The file name is nul-terminated.


      The drive should be reset after getting the abstract file name

      before accessing the drive (see question 3.05, How do I reset

      the drive?).

----------------------------------------------------------------------

5.02. How do I get the bibliography file name?


      You need a 38-byte block of memory to hold the bibliography file

      name. This code will fill that block:


      les  BX,Buffer

      mov  CX,Drive.; must be in format 0 = A:, 1 = B:, etc.

      mov  AX,1504H

      int  2FH

      jc   error

      ;

      ; buffer is filled with the bibliography file name.

      ;


      The file name is nul-terminated.


      The drive should be reset after getting the bibliography file

      name before accessing the drive (see question 3.05, How do I

      reset the drive?).

----------------------------------------------------------------------

5.03. How do I get the copyright file name?


      You need a 38-byte block of memory to hold the copyright file

      name. This code will fill that block:


      les  BX,Buffer

      mov  CX,Drive.; must be in format 0 = A:, 1 = B:, etc.

      mov  AX,1502H

      int  2FH

      jc   error

      ;

      ; buffer is filled with the copyright file name.

      ;


      The file name is nul-terminated.


      The drive should be reset after getting the copyright file name

      before accessing the drive (see question 3.05, How do I reset

      the drive?).

----------------------------------------------------------------------

5.04. How do I read the Volume Table of Contents (VTOC)?


      The VTOC is read in 2048-byte blocks. This code fills a VTOC

      block:


      les  BX,Buffer

      mov  CX,Drive       ; must be in format 0 = A:, 1 = B:, etc.

      mov  DX,BlockNumber ; 0 for the first block

      mov  AX,1505H

      int  2FH

      jc   error

      ;

      ; block is filled

      ;

      ; AX contains the descriptor type for this block:

      ;  0001H = standard volume descriptor

      ;  00FFH = volume descriptor terminator

      ;


      On error, AX will hold an error value: 000FH (invalid drive) or

      0015H (drive not ready).

======================================================================

Section 6 - Audio

======================================================================

6.01. How do I find out how many tracks are on a CD?


      First, you need the name of the device driver (see question

      2.03, How do I get the name of the CD-ROM device driver?). Open

      the file for read/write and obtain the file handle (DOS function

      3DH will suffice).


      Once you have the file handle, you need a seven-byte block of

      memory. Call DOS IOCTL function 4402H, as shown here:


      mov  BX,FileHandle

      mov  Command,0AH

      lds  DX,Command

      mov  CX,7

      mov  AX,4402H

      int  21H

      jc   error

      cmp  AX,7

      jne  read_error

      ;

      ; The byte at offset 1 of the seven-byte block of memory is the

      ; number of the first track

      ; The byte at offset 2 of the seven-byte block of memory is the

      ; number of the last track

      ; The dword at offset 4 of the seven-byte block of memory is the

      ; start address of the first track in Red Book format


      On error (carry set), AX will hold an error code: 0001H (invalid

      function), 0005H (access denied), 0006H (invalid handle), or

      000DH (invalid data).

----------------------------------------------------------------------

6.02. What are Red Book and HSG formats?


      Both are ways of encoding frame information. An audio frame is

      1/75 second of audio. HSG encodes frame information into a

      double word: minute multiplied by 4500, plus second multiplied

      by 75, plus frame, minus 150. Red Book encodes frame information

      into a four-byte data structure:

       Byte 0: frame number

       Byte 1: second

       Byte 2: minute

       Byte 3: unused

----------------------------------------------------------------------

6.03. How can I determine where a particular track starts?


      First, you need the name of the device driver (see question

      2.03, How do I get the name of the CD-ROM device driver?). Open

      the file for read/write and obtain the file handle (DOS function

      3DH will suffice).


      Once you have the file handle, you need an eight-byte block of

      memory. Call DOS IOCTL function 4402H, as shown here:


      mov  BX,FileHandle

      mov  Command,0BH

      mov  Command+1,TrackNumber

      lds  DX,Command

      mov  CX,8

      mov  AX,4402H

      int  21H

      jc   error

      cmp  AX,8

      jne  read_error

      ;

      ; The dword at offset 2 of the eight-byte block of memory is the

      ; start address of the specified track in Red Book format

      ; The word at offset 6 of the eight-byte block of memory is the

      ; track control information. Bits 15-12 are used:

      ; 0xxx: Two audio channels, no pre-emphasis, digital copy not

      ;       permitted

      ; 1xxx: Two audio channels, with pre-emphasis, digital copy not

      ;       permitted

      ; 2xxx: Two audio channels, no pre-emphasis, digital copy

      ;       permitted

      ; 3xxx: Two audio channels, with pre-emphasis, digital copy

      ;       permitted

      ; 4xxx: Data track, digital copy not permitted

      ; 6xxx: Data track, digital copy permitted

      ; 8xxx: Four audio channels, no pre-emphasis, digital copy not

      ;       permitted

      ; 9xxx: Four audio channels, with pre-emphasis, digital copy not

      ;       permitted

      ; Axxx: Four audio channels, no pre-emphasis, digital copy

      ;       permitted

      ; Bxxx: Four audio channels, with pre-emphasis, digital copy

      ;       permitted


      On error (carry set), AX will hold an error code: 0001H (invalid

      function), 0005H (access denied), 0006H (invalid handle), or

      000DH (invalid data).

----------------------------------------------------------------------

6.04. How do I play audio?


      For starters, you need MSCDEX Version 2.1 or greater (see

      question 1.02, How do I determine the MSCDEX version?).


      You also need the subunit number for the drive containing the

      audio CD (see question 2.03, How do I get the name of the CD-ROM

      device driver?)


      You also need to know what frame you want to start with (see

      question 6.03, How can I determine where a particular track

      starts?), and how many frames you want to play.


      Now, you need a 22-byte block of memory. Write 22 (16H) to the

      first byte. Write the subunit number to the second byte. Write

      84H to the third byte. Write 0 to the byte at offset 0DH (this

      sets up HSG addressing). Convert the starting frame number to

      HSG format and write the 4-byte result to the dword at offset

      0EH. Finally, write the frame count to the dword at offset 12H.


      To play the CD as instructed, execute this code:


      les  BX,Buffer

      mov  CX,Drive.; must be in format 0 = A:, 1 = B:, etc.

      mov  AX,1510H

      int  2FH

      ;

      ; status is in the word at offset 3 of the buffer. Look for bit

      ; 8 set (done), and watch out for bit 15 set (error).

      ;

----------------------------------------------------------------------

6.05. How do I pause audio playback?


      For starters, you need MSCDEX Version 2.1 or greater (see

      question 1.02, How do I determine the MSCDEX version?).


      You also need the subunit number for the drive containing the

      audio CD (see question 2.03, How do I get the name of the CD-ROM

      device driver?)


      Now, you need a 13-byte block of memory. Write 13 (0DH) to the

      first byte. Write the subunit number to the second byte. Write

      85H to the third byte.


      To pause the CD, execute this code:


      les  BX,Buffer

      mov  CX,Drive.; must be in format 0 = A:, 1 = B:, etc.

      mov  AX,1510H

      int  2FH

      ;

      ; status is in the word at offset 3 of the buffer. Look for bit

      ; 8 set (done), and watch out for bit 15 set (error).

      ;

----------------------------------------------------------------------

6.06. How do I resume audio playback?


      For starters, you need MSCDEX Version 2.1 or greater (see

      question 1.02, How do I determine the MSCDEX version?).


      You also need the subunit number for the drive containing the

      audio CD (see question 2.03, How do I get the name of the CD-ROM

      device driver?)


      Now, you need a 13-byte block of memory. Write 13 (0DH) to the

      first byte. Write the subunit number to the second byte. Write

      88H to the third byte.


      To resume, execute this code:


      les  BX,Buffer

      mov  CX,Drive.; must be in format 0 = A:, 1 = B:, etc.

      mov  AX,1510H

      int  2FH

      ;

      ; status is in the word at offset 3 of the buffer. Look for bit

      ; 8 set (done), and watch out for bit 15 set (error).

      ;




