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).
      ;


---

