From marcj@nando.net Wed Mar 1 17:51:03 1995 From: marcj@nando.net (MarcJ) Newsgroups: comp.os.msdos.programmer Subject: CD-ROM FAQ Date: 19 Feb 1995 23:18:50 -0500 Organization: NandO -- The News & Observer online service NNTP-Posting-Host: parsifal.nando.net 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). ;