Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых
[Назад] [Содеожание] [Дальше]

Аппаратное обеспечение персонального компьютера

© Александр Фролов, Григорий Фролов
Том 33, М.: Диалог-МИФИ, 1997, 304 стр.

9 Устройство чтения CD-ROM

  • Драйвер устройства чтения CD-ROM
  • Расширение MSCDEX
  • Функции MSCDEX
  • Работа через драйвер CD-ROM
  • Команды драйвера CD-ROM
  • Программа CDINFO
  • Программа CDPLAY
  • Устройство чтения компакт-дисков давно стало необходимым атрибутом современных персональных компьютеров. Компакт-диски, вмещающие до 650 Мбайт информации, как нельзя лучше подходят для дистрибутивов программ, мультимедийных энциклопедий, видеодисков и других приложений, связанных с хранением и обработкой значительных объемов информации.

    Не останавливаясь подробно на описании различных форматов компакт-дисков, которое само по себе может составить предмет для отдельной книги, мы рассмотрим средства MS-DOS, предназначенные для работы с компакт-дисками формата ISO-9960 и со звуковыми компакт-дисками стандарта Redbook. Первые из них чаще всего применяются для хранения программ и компьютерных баз данных, вторые – для записи звуковой информации. О том, как работать со звуковыми дисками в среде операционной системы Microsoft Windows, мы рассказывали в 15 томе «Библиотеки системного программиста», который называется «Мультимедиа для Windows».

    Драйвер устройства чтения CD-ROM

    Для работы с устройством чтения CD-ROM в среде операционных систем MS-DOS и Microsoft Windows 95 вы должны установить в файле CONFIG.SYS специальный драйвер. Этот драйвер обычно поставляется вместе с устройством и программой установки на дискете.

    Вот пример строки файла CONFIG.SYS, в которой загружается драйвер устройства чтения CD-ROM:

    
    device=c:\cd\cpqidecd.sys /d:idecd01
    

    Здесь мы передаем драйверу параметр /d:idecd01, который задает имя устройства чтения CD-ROM.

    Заметим, что устройство чтения CD-ROM имеет символьный драйвер, несмотря на то что оно является дисковым и, казалось бы, для него должен применяться блочный драйвер (подробнее о типах драйверов вы можете прочитать в 18 томе “Библиотеки системного программиста”, который называется “MS-DOS для программиста»). На самом деле устройство чтения CD-ROM не похоже на обычное дисковое устройство. Операционная система MS-DOS работает с ним как с сетевым устройством через интерфейс Network Redirector.

    Расширение MSCDEX

    Вторая компонента, необходимая для работы в среде MS-DOS с устройством чтения CD-ROM – программа Microsoft CD-ROM Extention, которая находится в файле MSCDEX.EXE. Этот файл входит в комплект MS-DOS и иногда поставляется вместе с устройством чтения CD-ROM на той же дискете, что и драйвер. Для того чтобы избежать несовместимости, мы рекомендуем всегда использовать ту программу MSCDEX.EXE, что устанавливается на диск вместе с MS-DOS.

    Программа MSCDEX.EXE подключается в файле AUTOEXEC.BAT операционной системы MS-DOS следующим образом:

    
    c:\dos\mscdex /d:idecd01
    

    Обратите внимание, что значение параметра /d должно совпадать со значением аналогичного параметра для драйвера.

    В среде операционной системы Microsoft Windows 95 программа MSCDEX.EXE не нужна, так как все выполняемые ей функции встроены в операционную систему.

    Что же касается Microsoft Windows NT, то в ней только часть функций расширения MSCDEX.EXE моделируется для виртуальной машины DOS. Поэтому не все программы, исходные тексты которых приведены в этой главе, будут там правильно работать.

    Функции MSCDEX

    В этом разделе мы приведем краткое описание основных функций программного интерфейса расширения MSCDEX.EXE, доступные в среде MS-DOS.

    Все функции расширения MSCDEX.EXE вызываются через мультиплексное прерывание INT 2Fh. При этом в регситр AH записывается значение 15h, а в регистр AL – код функции.

    Определение количества устройств CD-ROM

    Сегодня все большее количество компьютеров оснащается сразу несколькими устойствами чтения CD-ROM. С помощью функции 00h можно определить количество устройств чтения CD-ROM, имеющихся в системе, номер первого устройства CD-ROM, а также проверить, установлена ли программа MSCDEX:

    Регистры на входе:

    AX = 1500h;

    Регистры на выходе:

    BX = количество устройств чтения CD-ROM, установленных в системе;

    CX = номер первого устройства чтения CD-ROM. Значение 0 соответствует устройству A:, 1 – B: и так далее

    Заметим, что буквенные обозначения устройств не обязательно должны идти последовательно, начинаясь со значения, которое функция 00h возвращает в регистре CX. Если вам нужно определить обозначения всех устройств чтения CD-ROM, следует воспользоваться функцией 150Dh, о которой мы расскажем ниже в этом разделе.

    Получение списка устройств CD-ROM

    С помощью функции 01h вы можете получить список структур CD_ROM_Driver_Desc, описывающих установленные устройства CD-ROM:

    Регистры на входе:

    AX = 1501h;

    ES:BX = адрес буфера, в который будет скопирован список устройств CD-ROM

    Регистры на выходе:

    Не используются

    Первый байт такой структуры содержит номер устройства (unit nubmer), следом за которым идут четыре байта адреса заголовка драйвера, обслуживающего данное устройство:

    
    typedef struct _CD_ROM_Driver_Desc
    {
      unsigned char cSubUnit;
      unsigned long dwDevHeader;
    } CD_ROM_Driver_Desc;
    

    Перед вызовом этой функции необходимо подготовить буфер достаточного размера. Размер буфера нетрудно определить, узнав предварительно количество устройств CD-ROM, установленных в системе, с помощью функции 00h. Для каждого устройства в буфере требуется пять байт оперативной памяти.

    Получение имени файла прав собственности

    Функция 02h записывает в буфер имя файла, содержащего права собственности на компакт-диск, установленный в устройстве чтения CD-ROM:

    Регистры на входе:

    AX = 1502h;

    ES:BX = адрес буфера размером 38 байт

    CX = номер устройства чтения CD-ROM

    Регистры на выходе:

    CY = 1 при ошибке в номере устройства чтения CD-ROM;

    AX = код ошибки

    Функция может вернуть в выходном буфере пустую строку, состоящую из одного нулевого байта.

    Получение имени файла резюме

    Функция 03h записывает в буфер имя файла, содержащего резюме компакт-диска, установленного в устройстве чтения CD-ROM:

    Регистры на входе:

    AX = 1503h;

    ES:BX = адрес буфера размером 38 байт

    CX = номер устройства чтения CD-ROM

    Регистры на выходе:

    CY = 1 при ошибке в номере устройства чтения CD-ROM;

    AX = код ошибки

    Функция может вернуть в выходном буфере пустую строку, состоящую из одного нулевого байта.

    Получение имени файла библиографической документации

    Функция 04h записывает в буфер имя файла, содержащего библиографическую документацию компакт-диска, установленного в устройстве чтения CD-ROM:

    Регистры на входе:

    AX = 1504h;

    ES:BX = адрес буфера размером 38 байт

    CX = номер устройства чтения CD-ROM

    Регистры на выходе:

    CY = 1 при ошибке в номере устройства чтения CD-ROM;

    AX = код ошибки

    Функция может вернуть в выходном буфере пустую строку, состоящую из одного нулевого байта.

    Чтение сектора оглавления компакт-диска

    При помощи функции 05h вы можете найти все оглавления тома Volume Descriptor:

    Регистры на входе:

    AX = 1505h;

    ES:BX = адрес буфера размером 2048 байт;

    CX = номер устройства чтения CD-ROM;

    DX = номер дескриптора тома (0 – первый, 1 – второй и так далее)

    Регистры на выходе:

    CY = 0, если не было ошибок. При этом содержимое регистра AX определяет тип прочитанного дескриптора:

    AX= 1, стандартный дескриптор тома;

    AX = 0FFh, завершающий дескриптор;

    AX = 0, дескриптор другого типа;

    CY = 1 при ошибке в номере устройства чтения CD-ROM. При этом регистр AX содержит код ошибки

    Чтение сектора по абсолютному адресу

    Функция 08h предназначена для прямого чтения секторов компакт-диска и напоминает прерывание INT 25h опреационной системы MS-DOS:

    Регистры на входе:

    AX = 1508h;

    ES:BX = адрес буфера, в который будут прочитаны данные;

    CX = номер устройства чтения CD-ROM;

    DX = количество секторов, которые нужно прочитать;

    SI:DI = номер начального сектора

    Регистры на выходе:

    CY = 1 при ошибке в номере устройства чтения CD-ROM;

    AL = код ошибки

    Проверка устройства чтения CD-ROM

    При помощи функции 0Bh вы можете проверить, является диск устройством чтения CD-ROM, доступ к которому возможен через функции MSCDEX:

    Регистры на входе:

    AX = 150Bh;

    CX = номер устройства чтения CD-ROM

    Регистры на выходе:

    BX = ADADh, если программа MSCDEX установлена;

    AX = признак, является ли диск устройством чтения CD-ROM:

    если содержимое AX не равно нулю, то диск – устройство CD-ROM;

    если содержимое AX равно нулю, доступ к данному устройству с помощью функций MSCDEX невозможен

    Определение версии MSCDEX

    Функция 0Ch предназначена для определения версии установленной программы MSCDEX:

    Регистры на входе:

    AX = 150Ch;

    Регистры на выходе:

    BH = старший номер верии MSCDEX;

    BL = младший номер версии MSCDEX

    Определение обозначения устройств чтения CD-ROM

    При помощи функции 0Dh вы можете заполнить массив номерами установленных в системе устройств чтения CD-ROM:

    Регистры на входе:

    AX = 150Dh;

    ES:BX = адрес массива, в который будут записаны обозначения устройств CD-ROM

    Регистры на выходе:

    Не используются

    Размер массива должен быть равен количеству установленных в системе устройств чтения CD-ROM, которое можно определить с помощью функции 00h.

    Вызов драйвера CD-ROM

    Функция 10h предназначена для прямого вызова драйвера устройства чтения CD-ROM:

    Регистры на входе:

    AX = 1510h;

    CX = номер устройства чтения CD-ROM;

    ES:BX = адрес предварительно заполненного заголовка запроса драйвера

    Регистры на выходе:

    Не используются

    Перед вызовом этой функции вы должны подготовить заголовок запроса.

    Вызывая драйвер CD-ROM, вы можете выполнять такие операции, как проигрывание звуковых дорожек, извлечение компакт-диска, получение информации об устройстве, компакт-диске и дорожках и так далее.

    Работа через драйвер CD-ROM

    Перед тем как приступить к чтению этого раздела, мы рекомендуем вам обратиться к 6 главе 18 тома “Библиотеки системного программиста”, которая называется “Драйверы”. В ней мы привели минимум сведений, которые необходимы для создания собственных драйверов устройств, а также для работы с уже имеющимися драйверами.

    Напомним, что после загрузки драйвер становится частью операционной системы. Прикладные программы не вызывают драйвер напрямую, а пользуются для вызова драйвера функциями операционной системы.

    Внутри драйвера есть две функции, одна из которых называется программой стратегии, другая – программой прерывания.

    Задача программы стратегии – сохранение в области данных драйвера адреса заголовка запроса, который подготавливается для драйвера операционной системой и выполняется программой прерывания.

    Как найти адреса этих программ?

    Они есть в заголовке драйвера, который, однако, тоже еще нужно найти. Операционная система MS-DOS не имеет в своем составе документированных средств для поиска заголовков драйверов. В 18 томе “Библиотеки системного программиста” мы описали, как это можно сделать с применением недокументированной векторной таблицы связи MS-DOS.

    Однако для вызова драйвера устройства чтения CD-ROM вам не потребуются недокументированные средства, а также прямые вызовы программ стратегии и прерывания. С помощью описанной выше функции 10h расширения MSCDEX.EXE вы можете передавать драйверу заголовки запросов вполне документированным способом.

    Заголовок запроса

    Структура и размер заголовка запроса зависит от кода выполняемой команды, однако начальная часть заголовка всегда одна и та же. Для удобства формирования заголовка запроса мы подготовили структуру ReqHdr, соответствующую начальной части заголовка запроса, а также структуры для всех основных команд.

    Определение структуры ReqHdr приведено ниже:

    
    typedef unsigned char BYTE;
    typedef unsigned int  WORD;
    typedef unsigned long DWORD;
    
    #pragma pack(1)
    typedef struct _ReqHdr
    {
      BYTE bSize;        // размер заголовка запроса в байтах
      BYTE bSubUnit;     // номер устройства subunit
      BYTE bCmd;         // код команды
      WORD wStatus;      // слово состояния
      BYTE bReserved[8]; // зарезервировано
    } ReqHdr;
    

    Для удобства мы также определили типы BYTE, WORD и DWORD, которыми будем пользоваться в этой главе.

    Поле bSize должно содержать общий размер заголовка запроса, который складывается из размера структуры ReqHdr и размера дополнительной структуры, формат которой зависит от кода команды.

    В поле bSubUnit необходиом занести номер устройства, обслуживаемого данным драйвером. Этот номер нетрудно определить с помощью функции 01h расширения MSCDEX.EXE.

    В поле bCmd необходимо записать код команды, которую должен выполнить драйвер. Коды и описание команд мы приведем ниже.

    После выполнения команды драйвер записывает в поле wStatus слово состояния, по которому можно судить о результате выполнения.

    Формат слова состояния:

    Поле

    Описание

    0-7

    Код ошибки (если в слове состояния установлен бит 15)

    8

    Выполнение команды завершено

    9

    Устройство занято

    10-14

    Зарезервировано

    15

    При выполнении команды произошла ошибка

    Если команда выполнилась (с ошибкой или без ошибки), в слове состояния установлен бит 8.

    При возникновении ошибки также устанавливается бит 15. При этом в поле 0-7 находится код ошибки.

    Список кодов ошибок приведен ниже:

    Код ошибки

    Описание

    00h

    Защита записи

    01h

    Неизвестное устройство

    02h

    Устройство не готово

    03h

    Неизвестная команда

    04h

    Ошибка циклической контрольной суммы CRC

    05h

    Неправильная длина структуры запроа

    06h

    Ошибка при поиске

    07h

    Неизвестный носитель данных

    08h

    Сектор не найден

    09h

    В принтере нет бумаги

    0Ah

    Ошиба при записи

    0Bh

    Ошибка при чтении

    0Ch

    Общая ошибка

    0Dh

    Зарезервировано

    0Eh

    Зарезервировано

    0Fh

    Неправильная смена диска

    Здесь приведены коды ошибок не только для устройства чтения CD-ROM, но и для других устройств (например, для принтера).

    Команды драйвера CD-ROM

    В этом разделе мы расскажем о командах драйвера CD-ROM. Заметим, что в рамках одной команды может выполняться несколько функций. Код функции при этом записывается в расширение загловка запроса.

    Инициализация

    Команда инициализации вызывается из MS-DOS только один раз.

    Ниже мы привели формат заголовка запроса для этой команды:

    
    // ---------------
    // Код команды 0
    // ---------------
    #pragma pack(1)
    typedef struct _Init
    {
      ReqHdr rh;              
      BYTE   bNumberOfUnits;
      DWORD  lpEndAddress;
      DWORD  lpAddressOfBPB;
      BYTE   bNumberOfBlockDevice;
    } Init;
    

    После выполнения команды поля структуры заполняются драйвером следующим образом:

    Поле

    Описание

    rh.wStatus

    Слово состояния

    bNumberOfUnits

    Количество устройств, обслуживаемых драйвером. Равно 0

    lpEndAddress

    Конечный адрес резидентной порции драйвера в оперативной памяти

    lpAddressOfBPB

    Указатель на символ = в строке файла CONFIG.SYS, с помощью которой загружен драйвер CD-ROM. Может быть использован для анализа параметров драйвера

    bNumberOfBlockDevice

    Равно 0

    Чтение IOCTL Input

    При помощи команды IOCTL Input программа может получить от драйвера самую разную информацию, начиная от адреса заголовка драйвера и заканчивая информацией о дорожках диска.

    Формат заголовка запроса:

    
    // ---------------
    // Код команды 3
    // ---------------
    #pragma pack(1)
    typedef struct _IOCTL_Input
    {             
      ReqHdr rh;
      BYTE   bMediaDescriptor;
      DWORD  lpTransferAddress;
      WORD   wDataSize;
      WORD   wStartSector;
      DWORD  lpVolID;
    } IOCTL_Input;
    

    Заполнение полей заголовка запроса:

    Поле

    Описание

    rh.wStatus

    После вызова драйвера содержит слово состояния

    bMediaDescriptor

    Байт описания среды носителя данных

    lpTransferAddress

    Адрес буфера

    wDataSize

    Размер буфера

    wStartSector

    Номер начального сектора

    lpVolID

    Указатель на идентификатор тома, если при выполнении команды возникла ошибка с кодом 0Fh

    Команда IOCTL Input может выполнять много функций. Перед вызовом драйвера вы должны подготовить заголовок функции, указав в одном из его полей код выполняемой функции. Адрес и размер этой структуры необходимо записать в поля lpTransferAddress и wDataSize, соответственно.

    Рассмотрим форматы заголовков различных функций, выполняемых в рамках команды IOCTL Input.

    Определение адреса заголовка драйвера CD-ROM

    
    // ---------------
    // Код функции 0
    // ---------------
    #pragma pack(1)
    typedef struct _RAddr
    {             
      BYTE   bFunctionCode;
      DWORD  lpDeviceHeader;
    } RAddr;
    

    Поле

    Описание

    bFunctionCode

    Код функции

    lpDeviceHeader

    Адрес заголовка драйвера CD-ROM

    Зная адрес заголовка драйвера, вы можете определить имя драйвера и его атрибуты. Подробности об атрибутах драйверов вы найдете в 18 томе «Библиотеки системного програмиста».

    Определение положения головки

    
    // ---------------
    // Код функции 1
    // ---------------
    #pragma pack(1)
    typedef struct _HeadLocation
    {             
      BYTE   bFunctionCode;
      BYTE   bAddressMode;
      DWORD  lpHeadLocation;
    } HeadLocation;
    

    Поле

    Описание

    bFunctionCode

    Код функции

    bAddressMode

    Режим адресации:

    0 – HSG;

    1 – Redbook;

    2 – 255 – зарезервировано

    lpHeadLocation

    Положении головки. Значение зависит от режима адресации

    Здесь необходимо сделать замечание относительно режимов адресации.

    По умолчанию устройство чтения CD-ROM находится в режиме адресации HSG, описанный в стандарте High Sierra. При этом в качестве адреса указывается логический номер блока.

    Другой режим адресации описан в стандарте Redbook. В нем адрес представляет собой набор из трех значений: минуты (MIN), секунды (SEC), фреймы (FRAME). Каждое значение занимает один байт, причем в младшем байте хранится значение FRAME, в следующем байте - значение SEC, и в последнем, третьем байте, - значение MIN.

    С помощью следующей формулы вы можете преобразовать адрес из формата Redbook в формат HSG:

    
    SECTOR = MIN * 60 * 75 + SEC * 75 + FRAME - 150
    

    Получение информации о звуковых каналах

    
    // ---------------
    // Код функции 4
    // ---------------
    #pragma pack(1)
    typedef struct _ChanInfo
    {             
      BYTE   bFunctionCode;
      BYTE   bInpChannel0;
      BYTE   bVolControl0;
      BYTE   bInpChannel1;
      BYTE   bVolControl1;
      BYTE   bInpChannel2;
      BYTE   bVolControl2;
      BYTE   bInpChannel3;
      BYTE   bVolControl3;
    } ChanInfo;
    

    Поле

    Описание

    bFunctionCode

    Код функции

    bInpChannel0

    Номер входного канала, присвоенного выходному каналу 0. По умолчанию равно 0

    bVolControl0

    Уровень громкости для канала 0. По умолчанию равно 0FFh

    bInpChannel1

    Номер входного канала, присвоенного выходному каналу 1. По умолчанию равно 1

    bVolControl1

    Уровень громкости для канала 1. По умолчанию равно 0FFh

    bInpChannel2

    Номер входного канала, присвоенного выходному каналу 2. По умолчанию равно 2

    bVolControl2

    Уровень громкости для канала 2. По умолчанию равно 0FFh

    bInpChannel3

    Номер входного канала, присвоенного выходному каналу 3. По умолчанию равно 3

    bVolControl3

    Уровень громкости для канала 3. По умолчанию равно 0FFh

    Чтение данных из устройства

    
    // ---------------
    // Код функции 5
    // ---------------
    #pragma pack(1)
    typedef struct _DriveBytes
    {             
      BYTE   bFunctionCode;
      BYTE   bNumBytes;
      BYTE   bReadBuff[128];
    } DriveBytes;
    

    Поле

    Описание

    bFunctionCode

    Код функции

    bNumBytes

    Количество байт, которые необходимо прочитать из устройства

    bReadBuff

    Буфер для чтения размером 128 байт. После выполнения команды в него записывается информация, которая зависит от типа устройства чтения CD-ROM

    Определение состояния устройства

    
    // ---------------
    // Код функции 6
    // ---------------
    #pragma pack(1)
    typedef struct _DeviceStatus
    {             
      BYTE   bFunctionCode;
      DWORD  dwDeviceParameters;
    } DeviceStatus;
    

    Поле

    Описание

    bFunctionCode

    Код функции

    dwDeviceParameters

    32-разрядное слово состояния устройства

    Описания отдельных бит слова состояния приведено ниже:

    Бит

    Описание

    0

    1 – устройство открыто, в него можно вставлять диск;

    0 – устройство закрыто

    1

    0 – устройство заблокировано, из него нельзя извлечь диск;

    1 – устройство разблокировано

    2

    0 – устройство может работать только в режиме Cooked Reading;

    1 – дополнительно может использоваться режим Raw Reading

    3

    0 – устройстов может только читать компакт-диски;

    1 – устройство может читать и записывать компакт-диски

    4

    0 – устройство может работать только с дорожками, содержащими данные:

    1 – дополнительно устройство может проигрывать звуковые дорожки или дорожки с видео

    5

    0 – устройство не способно работать с чередованием данных;

    1 – устройство работает с чередованием данных в стандарте ISO‑9660

    6

    Зарезервировано

    7

    0 – предварительная выборка не применяется;

    1 – устройство способно выполнять запросы на предварительную выборку

    8

    0 – устройство не работает со звуковыми каналами;

    1 – устройство может работать со звуковыми каналами

    9

    0 – устройство может работать в режиме адресации HSG;

    1 – дополнительно можно использовать режим адресации Redbook

    10

    Зарезервировано

    11

    0 – в устройстве находится компакт-диск;

    1 – в устройстве нет компакт-диска

    12

    0 – устрйство не работает с подканалами R-W;

    1 – указанные каналы используются

    13-31

    Зарезервировано и равно нулю

    Немного о режимах Cooked и Raw.

    По умолчанию устройство чтения CD-ROM работает в режиме Cooked. Этот режим предполагает аппаратную обработку циклической контрольной суммы. Размер блока данных равен 2048 байт.

    В режиме Raw драйвер возвращает 2352 байта данных, в которые входят заголовок блока данных и контрольная сумма.

    Определение размера сектора

    
    // ---------------
    // Код функции 7
    // ---------------
    #pragma pack(1)
    typedef struct _SectorSize
    {             
      BYTE   bFunctionCode;
      BYTE   bReadMode;
      DWORD  dwSectorSize;
    } SectorSize;
    

    Поле

    Описание

    bFunctionCode

    Код функции

    bReadMode

    Режим чтения

    dwSectorSize

    Размер сектора. Для режима Cooked Reading он равен 2048 байт, для режима Raw Reading – 2352 байт

    Определение размера тома

    
    // ---------------
    // Код функции 8
    // ---------------
    #pragma pack(1)
    typedef struct _VolumeSize
    {             
      BYTE   bFunctionCode;
      DWORD  dwVolumeSize;
    } VolumeSize;
    

    Поле

    Описание

    bFunctionCode

    Код функции

    dwVolumeSize

    Размер тома

    Функция возвращает адрес дорожки Lead-out, преобразованное в численное значение по следующей формуле:

    
    frame + (sec * 75) + (min * 60 * 75)
    

    Это значение является адресом первого сектора, который располагается за самым последним адресуемым сектором диска.

    Проверка замены носителя данных

    
    // ---------------
    // Код функции 9
    // ---------------
    #pragma pack(1)
    typedef struct _MediaChange
    {             
      BYTE   bFunctionCode;
      BYTE   bMedia;
    } MediaChange;
    

    Поле

    Описание

    bFunctionCode

    Код функции

    bMedia

    1 – носитель не заменялся;

    0 – не известно, выполнялась замена носителя данных, или нет;

    0FFh – носитель заменялся

    Получение информации о компакт-диске

    
    // ---------------
    // Код функции 10
    // ---------------
    #pragma pack(1)
    typedef struct _DiskInfo
    {
      BYTE   bFunctionCode;
      BYTE   bLowest;
      BYTE   bHighest;
      DWORD  dwTotal;
    } DiskInfo;
    

    Поле

    Описание

    bFunctionCode

    Код функции

    bLowest

    Номер первой дорожки

    bHighest

    Номер последней дорожки

    dwTotal

    Адрес дорожки Lead-out

    Получение информации о дорожке компакт-диска

    
    // ---------------
    // Код функции 11
    // ---------------
    #pragma pack(1)
    typedef struct _TrackInfo
    {
      BYTE   bFunctionCode;
      BYTE   bTrack;
      DWORD  dwLoc;
      BYTE   bInfo;
    } TrackInfo;
    

    Поле

    Описание

    bFunctionCode

    Код функции

    bTrack

    Номер дорожки

    dwLoc

    Первый сектор дорожки

    bInfo

    Формат дорожки

    Старшая тетрада формата дорожки имеет следующий формат:

    Значение

    Описание

    00*0

    Два звуковых канала без предискажений

    00*1

    Два звуковых канала с предискажениями

    10*1

    Четыре звуковых канала без предискажений

    10*0

    Четыре звуковых канала с предискажениями

    01*0

    Дорожка с данными

    01*1

    Зарезервировано

    11**

    Зарезервировано

    **0*

    Копирование дорожки запрещено

    **1*

    Копирование дорожки разрешено

    Состояние бита, отмеченного символом *, значения не имеет.

    Младшая тетрада формата дорожки содержит тип режима ADR, описанного в Redbook.

    Получение информации о канале Q

    
    // ---------------
    // Код функции 12
    // ---------------
    #pragma pack(1)
    typedef struct _QInfo
    {
      BYTE   bFunctionCode;
      BYTE   bCtrlAndARD;
      BYTE   bTrackNumb;
      BYTE   bIndex;
      BYTE   bMin;
      BYTE   bSec;
      BYTE   bFrame;
      BYTE   bRunningTime;
      BYTE   bAminOrPmin;
      BYTE   bAsecOrPsec;
      BYTE   bAframeOrPframe;
    } QInfo;
    

    Поле

    Описание

    bFunctionCode

    Код функции

    bCtrlAndARD

    Формат дорожки

    bTrackNumb

    Номер дорожки

    bIndex

    Текущее время, которое прошло с начала проигрывания дорожки

    bMin

    Компонента MIN адреса дорожки

    bSec

    Компонента SEC адреса дорожки

    bFrame

    Компонента FRAME адреса дорожки

    bRunningTime

    Общее время проигрывания диска

    bAminOrPmin

    Значение AMIN или PMIN

    bAsecOrPsec

    Значение ASEC или PSEC

    bAframeOrPframe

    Значение AFRAME или PFRAME

    Получение информации о подканале

    
    // ---------------
    // Код функции 13
    // ---------------
    #pragma pack(1)
    typedef struct _QInfo
    {
      BYTE   bFunctionCode;
      DWORD  dwStartSector;
      DWORD  dwTransferAddress;
      DWORD  dwNumberOfSectors;
    } QInfo;
    

    Поле

    Описание

    bFunctionCode

    Код функции

    dwStartSector

    Адрес начального сектора

    dwTransferAddress

    Адрес в оперативной памяти, по которому будет скопирована информация из подканала

    dwNumberOfSectors

    Количество секторов, которые необходимо скопировать

    Получение штрих-кода изготовителя компакт-диска

    
    // ---------------
    // Код функции 14
    // ---------------
    #pragma pack(1)
    typedef struct _UPCCode
    {
      BYTE   bFunctionCode;
      BYTE   bCtrlAndARD;
      BYTE   bUPCCode[7];
      BYTE   bZero;
      BYTE   bAFrame;
    } UPCCode;
    

    Поле

    Описание

    bFunctionCode

    Код функции

    bCtrlAndARD

    Формат дорожек

    bUPCCode

    Штрих-код

    bZero

    Равно 0

    bAFrame

    Значение AFRAME

    Сброс входных буферов

    Команда освобождает все входные буферы и отменяет все запущенные команды.

    Формат заголовка запроса:

    
    // ---------------
    // Код команды 7
    // ---------------
    #pragma pack(1)
    typedef struct _Flush
    {             
      ReqHdr rh;
    } Flush;
    

    Заполнение полей заголовка запроса:

    Поле

    Описание

    rh.wStatus

    После вызова драйвера содержит слово состояния

    Запись IOCTL Output

    При помощи команды IOCTL Output программа может заставить драйвер выполнять различные операции, такие как управление механизмом извлечения компакт-диска.

    Формат заголовка запроса:

    
    // ---------------
    // Код команды 12
    // ---------------
    #pragma pack(1)
    typedef struct _IOCTL_Output
    {             
      ReqHdr rh;
      BYTE   bMediaDescriptor;
      DWORD  lpTransferAddress;
      WORD   wDataSize;
      WORD   wStartSector;
      DWORD  lpVolID;
    } IOCTL_Output;       
    

    Заполнение полей заголовка запроса:

    Поле

    Описание

    rh.wStatus

    После вызова драйвера содержит слово состояния

    bMediaDescriptor

    Байт описания среды носителя данных, должен быть равен нулю

    lpTransferAddress

    Адрес буфера

    wDataSize

    Размер буфера

    wStartSector

    Номер начального сектора, должен быть равен нулю

    lpVolID

    Указатель на идентификатор тома, если при выполнении команды возникла ошибка с кодом 0Fh

    Рассмотрим форматы заголовков различных функций, выполняемых в рамках команды IOCTL Output.

    Извлечение компакт-диска

    
    // ---------------
    // Код функции 0
    // ---------------
    #pragma pack(1)
    typedef struct _EjectDisk
    {
      BYTE   bFunctionCode;
    } EjectDisk;
    

    Поле

    Описание

    bFunctionCode

    Код функции

    Блокирование и разблокирование компакт-диска в устройстве

    
    // ---------------
    // Код функции 1
    // ---------------
    #pragma pack(1)
    typedef struct _LockDisk
    {
      BYTE   bFunctionCode;
      BYTE   bLock;
    } LockDisk;
    

    Поле

    Описание

    bFunctionCode

    Код функции

    bLock

    1 – блокирование компакт-диска;

    0 – разблокирование компакт-диска

    Сброс устройства чтения CD-ROM

    
    // ---------------
    // Код функции 2
    // ---------------
    #pragma pack(1)
    typedef struct _ResetDrive
    {
      BYTE   bFunctionCode;
    } ResetDrive;
    

    Поле

    Описание

    bFunctionCode

    Код функции

    Управление звуковыми каналами

    
    // ---------------
    // Код функции 3
    // ---------------
    #pragma pack(1)
    typedef struct _ChanControl
    {             
      BYTE   bFunctionCode;
      BYTE   bInpChannel0;
      BYTE   bVolControl0;
      BYTE   bInpChannel1;
      BYTE   bVolControl1;
      BYTE   bInpChannel2;
      BYTE   bVolControl2;
      BYTE   bInpChannel3;
      BYTE   bVolControl3;
    } ChanControl;
    

    Поле

    Описание

    bFunctionCode

    Код функции

    bInpChannel0

    Номер входного канала, присвоенного выходному каналу 0

    bVolControl0

    Уровень громкости для канала 0

    bInpChannel1

    Номер входного канала, присвоенного выходному каналу 1

    bVolControl1

    Уровень громкости для канала 1

    bInpChannel2

    Номер входного канала, присвоенного выходному каналу 2

    bVolControl2

    Уровень громкости для канала 2

    bInpChannel3

    Номер входного канала, присвоенного выходному каналу 3

    bVolControl3

    Уровень громкости для канала 3

    Запись в устройство управляющей строки

    
    // ---------------
    // Код функции 4
    // ---------------
    #pragma pack(1)
    typedef struct _DriveControlBytes
    {             
      BYTE bFunctionCode;
      BYTE bWriteBuff[...]; // размер зависит от устройства
    } DriveControlBytes;
    

    Поле

    Описание

    bFunctionCode

    Код функции

    bWriteBuff

    Буфер с управляющими данными, которые будут записаны в устройство чтения CD-ROM. Формат зависит от типа устройства

    Закрывание приемного устройства для компакт-диска

    
    // ---------------
    // Код функции 5
    // ---------------
    #pragma pack(1)
    typedef struct _CloseTray
    {
      BYTE   bFunctionCode;
    } CloseTray;
    

    Поле

    Описание

    bFunctionCode

    Код функции

    Открывание устройства

    Команда открывает устройство чтения CD-ROM, сообщая драйверу о том, что данное устройство будет использовано еще одной программой.

    Формат заголовка запроса:

    
    // ---------------
    // Код команды 13
    // ---------------
    #pragma pack(1)
    typedef struct _RsumePlay
    {             
      ReqHdr rh;
    } RsumePlay;
    

    Заполнение полей заголовка запроса:

    Поле

    Описание

    rh.wStatus

    После вызова драйвера содержит слово состояния

    Закрывание устройства

    Команда закрывает устройство чтения CD-ROM, открытое предыдущей командой.

    Формат заголовка запроса:

    
    // ---------------
    // Код команды 14
    // ---------------
    #pragma pack(1)
    typedef struct _RsumePlay
    {             
      ReqHdr rh;
    } RsumePlay;
    

    Заполнение полей заголовка запроса:

    Поле

    Описание

    rh.wStatus

    После вызова драйвера содержит слово состояния

    Чтение длинное

    При помощи команды длинного чтения программа может прочитать полное содержимое сектора компакт-диска, включая служебные области сектора.

    Формат заголовка запроса:

    
    // ---------------
    // Код команды 128
    // ---------------
    #pragma pack(1)
    typedef struct _ReadLong
    {             
      ReqHdr rh;
      BYTE   bAddressMode;
      DWORD  lpTransferAddress;
      WORD   wDataSize;
      WORD   wStartSector;
      BYTE   bDataReadMode;
      BYTE   bInterleaveSise;
      BYTE   bInterleaveSkip;
    } ReadLong;       
    

    Заполнение полей заголовка запроса:

    Поле

    Описание

    rh.wStatus

    После вызова драйвера содержит слово состояния

    bAddressMode

    Режим адресации:

    0 – режим HSG (по умолчанию);

    1 – режим Readbook;

    2-255 – зарезервировано

    lpTransferAddress

    Адрес буфера

    wDataSize

    Размер буфера

    wStartSector

    Номер начального сектора, должен быть равен нулю

    bDataReadMode

    Режим чтения данных:

    0 – режим Cooked;

    1 – режим Raw;

    2-255 – зарезервировано

    bInterleaveSize

    Количество блоков или секторов, которые записаны последовательно друг за другом

    bInterleaveSkip

    Фактор пропуска при чередовании: количество последовательно расположенных блоков или секторов, которые разделяют фрагменты файла с чередованием

    Чтение длинное с предварительной выборкой

    Функция аналогична предыдущей, но сразу после вызова она немедленно возвращает управление. Драйвер выполняет подготовительные действия для операции чтения, такие, например, как позиционирование головки, однако операция чтения не выполняется.

    Формат заголовка запроса:

    
    // ---------------
    // Код команды 130
    // ---------------
    #pragma pack(1)
    typedef struct _ReadLongPrefetch
    {             
      ReqHdr rh;
      BYTE   bAddressMode;
      DWORD  lpTransferAddress;
      WORD   wDataSize;
      WORD   wStartSector;
      BYTE   bDataReadMode;
      BYTE   bInterleaveSise;
      BYTE   bInterleaveSkip;
    } ReadLongPrefetch;       
    

    Заполнение полей заголовка запроса такое же, как и для предыдущей функции.

    Поиск

    Команда выполняет позиционирование головки. Она возвращает управление немедленно, не дожидаясь завершения процесса позиционирования.

    Формат заголовка запроса:

    
    // ---------------
    // Код команды 131
    // ---------------
    #pragma pack(1)
    typedef struct _Seek
    {             
      ReqHdr rh;
      BYTE   bAddressMode;
      DWORD  lpTransferAddress;
      WORD   wDataSize;
      WORD   wStartSector;
    } Seek;       
    

    Заполнение полей заголовка запроса:

    Поле

    Описание

    rh.wStatus

    После вызова драйвера содержит слово состояния

    bAddressMode

    Режим адресации:

    0 – режим HSG (по умолчанию);

    1 – режим Readbook;

    2-255 – зарезервировано

    lpTransferAddress

    Адрес буфера

    wDataSize

    Размер буфера

    wStartSector

    Номер начального сектора, должен быть равен нулю

    Проигрывание звуковой дорожки

    Команда запускает проигрывание звуковой дорожки начиная с указанного сектора.

    Формат заголовка запроса:

    
    // ---------------
    // Код команды 132
    // ---------------
    #pragma pack(1)
    typedef struct _Play
    {             
      ReqHdr rh;
      BYTE   bAddressMode;
      WORD   wStartSector;
      WORD   wNumberOfSectors;
    } Play;
    

    Заполнение полей заголовка запроса:

    Поле

    Описание

    rh.wStatus

    После вызова драйвера содержит слово состояния

    bAddressMode

    Режим адресации:

    0 – режим HSG (по умолчанию);

    1 – режим Readbook;

    2-255 – зарезервировано

    wStartSector

    Номер начального сектора

    wNumberOfSectors

    Количество секторов, которые нужно проиграть

    Остановка проигрывания звуковой дорожки

    Команда останавливает проигрывание звуковой дорожки.

    Формат заголовка запроса:

    
    // ---------------
    // Код команды 133
    // ---------------
    #pragma pack(1)
    typedef struct _StopPlay
    {             
      ReqHdr rh;
    } StopPlay;
    

    Заполнение полей заголовка запроса:

    Поле

    Описание

    rh.wStatus

    После вызова драйвера содержит слово состояния

    Возобновление проигрывания звуковой дорожки

    Команда останавливает проигрывание звуковой дорожки.

    Формат заголовка запроса:

    
    // ---------------
    // Код команды 136
    // ---------------
    #pragma pack(1)
    typedef struct _RsumePlay
    {             
      ReqHdr rh;
    } RsumePlay;
    

    Заполнение полей заголовка запроса:

    Поле

    Описание

    rh.wStatus

    После вызова драйвера содержит слово состояния

    Программа CDINFO

    Программа CDINFO вызывает как функции расширения MSCDEX.EXE, так и драйвер устройства чтения CD-ROM, получая различную информацию как о расширении, так и о компакт-диске.

    Вот пример листинга, полученного при работе программы CDINFO на виртуальной машине DOS в среде Microsoft Windows 95:

    
    CDINFO, (c) A. Frolov, 1997
    
    MSCDEX version: 2.95
    Found 1 CD Unit, start unit: G
    CD-ROM letters: G
    
    Status of CD Drive G: 00000390
    VolumeSize: 29460 blocks
    Tracks: (1 - 1) 8252
    track 1: location: 512, info: 41 * digital, copy prohibited *
    

    Здесь в компьютере установлено только одно устройство чтения CD‑ROM, в котором находился один цифровой компакт-диск.

    Ниже мы представили листинг, полученный при аналогичных условиях на компьютере, оборудованном двумя устройствами чтения CD-ROM. В первое устройство (H:) был вставлен звуковой диск, а во второе (I:) - комбинированный диск с одной цифровой и тремя звуковыми дорожками:

    
    CDINFO, (c) A. Frolov, 1997
    
    MSCDEX version: 2.21
    Found 2 CD Unit, start unit: H
    CD-ROM letters: H I 
    
    Status of CD Drive H: 00000390
    VolumeSize: 302375 blocks
    Tracks: (1 - 15) 2866
    track 1: location: 512, info: 01 * audio, copy prohibited *
    track 2: location: 79922, info: 01 * audio, copy prohibited *
    track 3: location: 466492, info: 01 * audio, copy prohibited *
    track 4: location: 788490, info: 01 * audio, copy prohibited *
    track 5: location: 1120281, info: 01 * audio, copy prohibited *
    track 6: location: 1453334, info: 01 * audio, copy prohibited *
    track 7: location: 1778979, info: 01 * audio, copy prohibited *
    track 8: location: 2042649, info: 01 * audio, copy prohibited *
    track 9: location: 2363412, info: 01 * audio, copy prohibited *
    track 10: location: 2565639, info: 01 * audio, copy prohibited *
    track 11: location: 2823479, info: 01 * audio, copy prohibited *
    track 12: location: 3094814, info: 01 * audio, copy prohibited *
    track 13: location: 3419404, info: 01 * audio, copy prohibited *
    track 14: location: 3740478, info: 01 * audio, copy prohibited *
    track 15: location: 4130306, info: 01 * audio, copy prohibited *
    
    Status of CD Drive I: 00000390
    VolumeSize: 278505 blocks
    Tracks: (1 - 4) 13598
    track 1: location: 512, info: 41 * digital, copy prohibited *
    track 2: location: 3282733, info: 01 * audio, copy prohibited *
    track 3: location: 3608079, info: 01 * audio, copy prohibited *
    track 4: location: 3801921, info: 01 * audio, copy prohibited *
    

    Заметим, что в среде виртуальной машины операционной системы Microsoft Windows NT эта программа показывает неверные результаты. Скорее всего это происходит из-за неправильной работы эмулятора расширения MSCDEX.

    Исходный текст программы CDINFO вы найдете в листинге 9.1.

    Листинг 9.1. Файл cdinfo\cdinfo.с

    
    // =====================================================
    // Прсмотр различной информации об устройствах
    // чтения CD-ROM и компакт-дисках 
    // с помощью интерфейса MSCDEX и обращением к драйверу
    // устройства чтения CD-ROM
    //
    // (C) Фролов А.В, 1997
    //
    // E-mail: frolov@glas.apc.org
    // WWW:    http://www.glasnet.ru/~frolov
    //            или
    //         http://www.dials.ccas.ru/frolov
    // =====================================================
    #include <stdio.h>
    #include <stdlib.h>
    #include <conio.h>
    #include <dos.h>
    #include <memory.h>
      
    typedef unsigned char BYTE;
    typedef unsigned int  WORD;
    typedef unsigned long DWORD;
    
    // Необходимо для обеспечения выравнивания 
    // полей структур на границу байта
    #pragma pack(1)
    
    // Заголовок запроса для обращения к драйверу
    typedef struct _ReqHdr
    {
      BYTE bSize;
      BYTE bSubUnit;
      BYTE bCmd;
      WORD wStatus;
      BYTE bReserved[8];    
    } ReqHdr;
      
    // Запрос IOCTL Input
    typedef struct _IOCTL_Input
    {             
      ReqHdr rh;
      BYTE   bMediaDescriptor;
      DWORD  lpTransferAddress;
      WORD   wDataSize;
      WORD   wStartSector;
      DWORD  lpVolID;
    } IOCTL_Input;       
    
    // Запрос на получение информации о компакт-диске
    typedef struct _DiskInfo
    {
      BYTE bControl;
      BYTE bLowest;
      BYTE bHighest;
      DWORD dwTotal;
    } DiskInfo;
    
    // Запрос на получение информации 
    // о дорожке компакт-диска
    typedef struct _TrackInfo
    {
      BYTE  bControl;
      BYTE  bTrack;
      DWORD dwLoc;
      BYTE  bInfo;
    } TrackInfo;
    
    // Запрос для определения текущего состояния
    // устройства чтения CD-ROM
    typedef struct _DeviceStatus
    {
      BYTE bControl;
      DWORD dwParam;
    } DeviceStatus;
    
    // Запрос для определения общего количества
    // секторов на компакт-диске
    typedef struct _VolumeSize
    {
      BYTE bControl;
      DWORD dwVolumeSize;
    } VolumeSize;
    
    #pragma pack()
    
    // Прототипы функций
    void GetCDLetters(BYTE *bLetters);
    void CallCDDriver(void *rh, int nCDUnit);
    int GetDiskInfo(int nCDUnit);
    int GetDeviceStatus(int nCDUnit);
    int GetVolumeSize(int nCDUnit);
    int GetTrackInfo(int iTrack, int nCDUnit);
    void delay(int ms);
    
    // Регистры для вызова функции int86
    union REGS rg;                      
    
    // Количество установленных устройств чтения CD-ROM
    int nCDUnits;
    
    // Номер первого устройства чтения CD-ROM
    int nCDStartUnit;
    
    // Слово состояния после вызова драйвера CD-ROM
    int iStatus;
    
    // Информация о компакт-диске
    DiskInfo     di;             
    
    // Состояние устройства чтения CD-ROM
    DeviceStatus ds;                     
    
    // Объем компакт-диска
    VolumeSize   vs;      
    
    // Информация о дорожке
    TrackInfo    ti;
    
    BYTE bLetters[26];
    
    // ---------------------------------------------------
    // main
    // Точка входа в программу
    // ---------------------------------------------------
    int main() 
    {
      int iRetry;
      int i, j;
      
      printf("CDINFO, (c) A. Frolov, 1997\n\n");
      
      // Проверяем, установлена ли программа MSCDEX
      rg.x.ax = 0x1500;
      rg.x.bx = 0;
      int86(0x2f, &rg, &rg);
    
      if(rg.x.bx == 0)
      {
        printf("MSCDEX is not installed\n");
        return -1;
      }
      
      else        
      {
        // Сохраняем общее количество устройств чтения CD-ROM
        nCDUnits = rg.x.bx;
        
        // Сохраняем номер первого такого устройства
        nCDStartUnit = rg.x.cx;
    
        // Определяем и отображаем вресию MSCDEX
        rg.x.ax = 0x150c;
        int86(0x2f, &rg, &rg);
        printf("MSCDEX version: %d.%d\n", rg.h.bh, rg.h.bl);
    
        // Отображаем количество найденных устройств чтения
        // CD-ROM и номер первого устройства
        printf("Found %d CD Unit, start unit: %c\n",
          nCDUnits, nCDStartUnit + 'A');
      }
    
      // Получаем массив номеров устройств чтения CD-ROM
      GetCDLetters(bLetters);
      
      // Отображаем обозначения всех устройств CD-ROM
      printf("CD-ROM letters: ");
      for(i = 0; i < nCDUnits; i++)
      {
        printf("%c ", bLetters[i] + 'A');  
      }
      printf("\n");
    
      // Цикл по всем устройствам чтения CD-ROM
      for(i = 0; i < nCDUnits; i++)
      {
        // Определяем и отображаем состояние устройства
        iStatus = GetDeviceStatus(bLetters[i]);
        if(iStatus != 0x0100)
        {
          printf("GetDeviceStatus status: %04.4X\n", iStatus);
          printf("GetDeviceStatus failed\n");
          exit(1);               
        }
        printf("\nStatus of CD Drive %c: %08.8X\n", 
          bLetters[i] + 'A', ds.dwParam);
    
        // Определяем и отображаем объем устройства
        iStatus = GetVolumeSize(bLetters[i]);
        if(iStatus != 0x0100)
        {
          printf("GetVolumeSize status: %04.4X\n", iStatus);
          printf("GetVolumeSize failed\n");
        }
        else
          printf("VolumeSize: %ld blocks\n", vs.dwVolumeSize);
    
        // Определяем и отображаем информацию о
        // компакт-диске
        iStatus = GetDiskInfo(bLetters[i]);
        
        // Делаем три попытки получения информации
        iRetry = 0;
        while(iStatus != 0x0100)
        {
          printf("GetDiskInfo status: %04.4X\n", iStatus);
        
          // Задержка длительностью 1 с
          delay(1000);
        
          iRetry++;
        
          if(iRetry == 3)
          {
            printf("GetDiskInfo failed\n");
            break;
          }
    
          iStatus = GetDiskInfo(bLetters[i]);
        }
      
        // Если удалось получить информацию о компакт-диске,
        // исследуем его дорожки
        if(iRetry != 3)
        {
          // Выводим номера дорожек
          printf("Tracks: (%d - %d) %d\n", 
            di.bLowest, di.bHighest, di.dwTotal);
    
          // Цикл по всем дорожкам диска
          for(j = di.bLowest; j <= di.bHighest; j++)
          {
            // Получаем информацию о дорожке и отображаем ее
            GetTrackInfo(j, bLetters[i]);
            printf("track %d: location: %ld, info: %02.2X",
              j, ti.dwLoc, ti.bInfo);
          
            // Определяем тип дорожки - звуковая дорожка
            // или дорожка с данными
            if(ti.bInfo & 0x40)
              printf(" * digital");
            else
              printf(" * audio");
    
            // Определяем, разрашено ли копирование дорожки
            if(ti.bInfo & 0x20)
              printf(", copy permitted *\n");
            else
              printf(", copy prohibited *\n");
          }
        }  
      }
      return 0;
    }
    
    // ---------------------------------------------------
    // GetDiskInfo
    // Получение информации о компакт-диске
    // ---------------------------------------------------
    int GetDiskInfo(int nCDUnit)
    {
      // Заголовок команды IOCTL Input
      IOCTL_Input cmd;
      
      // Очищаем заголовок
      memset(&cmd, 0, sizeof(IOCTL_Input ));
      
      // Заполняем заголовок
      cmd.rh.bSize    = 26;
      cmd.rh.bSubUnit = 0;
      cmd.rh.bCmd     = 3;
      
      cmd.bMediaDescriptor  = 0;
      cmd.lpTransferAddress = (DWORD)(void far *)&di;
      cmd.wDataSize         = 7;
      cmd.wStartSector      = 0;
      cmd.lpVolID           = (DWORD)(void far *)NULL;
      
      di.bControl = 10;
      
      // Вызываем драйвер
      CallCDDriver(&cmd, nCDUnit);
      
      return cmd.rh.wStatus;
    }  
    
    // ---------------------------------------------------
    // GetTrackInfo
    // Получение информации о дорожке компакт-диска
    // ---------------------------------------------------
    int GetTrackInfo(int iTrack, int nCDUnit)
    {
      IOCTL_Input cmd;
      
      memset(&cmd, 0, sizeof(IOCTL_Input ));
      
      cmd.rh.bSize    = 26;
      cmd.rh.bSubUnit = 0;
      cmd.rh.bCmd     = 3;
      
      cmd.bMediaDescriptor  = 0;
      cmd.lpTransferAddress = (DWORD)(void far *)&ti;
      cmd.wDataSize         = 7;
      cmd.wStartSector      = 0;
      cmd.lpVolID           = (DWORD)(void far *)NULL;
      
      ti.bControl = 11;
      ti.bTrack = iTrack;
      
      CallCDDriver(&cmd, nCDUnit);
      
      return cmd.rh.wStatus;
    }  
    
    // ---------------------------------------------------
    // GetDeviceStatus
    // Определение состояния устройства чтения CD-ROM
    // ---------------------------------------------------
    int GetDeviceStatus(int nCDUnit)
    {
      IOCTL_Input cmd;
      
      memset(&cmd, 0, sizeof(IOCTL_Input ));
      
      cmd.rh.bSize    = 26;
      cmd.rh.bSubUnit = 0;
      cmd.rh.bCmd     = 3;
      
      cmd.bMediaDescriptor  = 0;
      cmd.lpTransferAddress = (DWORD)(void far *)&ds;
      cmd.wDataSize         = 5;
      cmd.wStartSector      = 0;
      cmd.lpVolID           = (DWORD)(void far *)NULL;
      
      ds.bControl = 6;
      
      CallCDDriver(&cmd, nCDUnit);
      return cmd.rh.wStatus;
    }  
    
    // ---------------------------------------------------
    // GetVolumeSize
    // Определение объема компакт-диска
    // ---------------------------------------------------
    int GetVolumeSize(int nCDUnit)
    {
      IOCTL_Input cmd;
      
      memset(&cmd, 0, sizeof(IOCTL_Input ));
      
      cmd.rh.bSize    = 26;
      cmd.rh.bSubUnit = 0;
      cmd.rh.bCmd     = 3;
      
      cmd.bMediaDescriptor  = 0;
      cmd.lpTransferAddress = (DWORD)(void far *)&vs;
      cmd.wDataSize         = 5;
      cmd.wStartSector      = 0;
      cmd.lpVolID           = (DWORD)(void far *)NULL;
      
      vs.bControl = 8;
      
      CallCDDriver(&cmd, nCDUnit);
      return cmd.rh.wStatus;
    }  
    
    // ---------------------------------------------------
    // CallCDDriver
    // Вызов драйвера компакт-диска
    // ---------------------------------------------------
    void CallCDDriver(void *rh, int nCDUnit)
    {
      static union REGS rg;
      static struct SREGS srg;
      
      segread(&srg);
      rg.x.ax = 0x1510;
      rg.x.cx = nCDUnit;
      rg.x.bx = FP_OFF(rh);  
      
      int86x(0x2f, &rg, &rg, &srg);
    }
    
    // ---------------------------------------------------
    // GetCDLetters
    // Заполнение массива номерами установленных 
    // в системе устройств чтения компакт-диска
    // ---------------------------------------------------
    void GetCDLetters(BYTE *bLetters)
    {
      static union REGS rg;
      static struct SREGS srg;
      
      segread(&srg);
      rg.x.ax = 0x150d;
      rg.x.bx = FP_OFF(bLetters);  
      
      int86x(0x2f, &rg, &rg, &srg);
    }
    
    // ---------------------------------------------------
    // delay
    // Формирование задержки по таймеру
    // ---------------------------------------------------
    void delay(int ms) 
    {
      int ticks;    
      
      ticks = ms / 55;
      _asm 
      {
        push si
    
        mov  si, ticks
        mov  ah, 0
        int  1ah
    
        mov  bx, dx
        add  bx, si
    
    delay_loop:
    
        int  1ah
        cmp  dx, bx
        jne  delay_loop
    
        pop  si
      }
    }
    

    Программа CDPLAY

    Программа CDPLAY предназначена для проигрывания дорожек звуковых компакт-дисков. При запуске этой программы необходимо указать параметр – номер блока, с которого должно выполняться проигрывание.

    Ниже мы привели пример запуска программы, передав ей адрес 512:

    
    CDPLAY, (c) A. Frolov, 1997
    
    Track Red book: 512
    Track Sierra: 0
    MSCDEX version: 2.95
    Found 1 CD Unit, start unit: G
    CD-ROM letters: G
    Started. Press any key to stop and eject CD
    

    Этот адрес мы взяли из листинга, полученного программой CDINFO, описанной в предыдущем разделе. Он был пересчитан программой CDPLAY в формат Sierra, в результате чего программа запустила проигрывание самой первой звуковой дорожки диска.

    Если после запуска программы и начала проигрывания нажать любую клавишу, проигрывание будет остановлено, а компакт-диск - извлечен из устройства чтения CD-ROM.

    Исходный текст программы CDPLAY приведен в листинге 9.2.

    Листинг 9.2. Файл cdplay\cdplay.с

    
    // =====================================================
    // Проигрывание звуковых компакт-дисков
    //
    // (C) Фролов А.В, 1997
    //
    // E-mail: frolov@glas.apc.org
    // WWW:    http://www.glasnet.ru/~frolov
    //            или
    //         http://www.dials.ccas.ru/frolov
    // =====================================================
    #include <stdio.h>
    #include <stdlib.h>
    #include <conio.h>
    #include <dos.h>
    #include <memory.h>
      
    typedef unsigned char BYTE;
    typedef unsigned int  WORD;
    typedef unsigned long DWORD;
    
    // Необходимо для обеспечения выравнивания 
    // полей структур на границу байта
    #pragma pack(1)
    
    // Заголовок запроса для обращения к драйверу
    typedef struct _ReqHdr
    {
      BYTE bSize;
      BYTE bSubUnit;
      BYTE bCmd;
      WORD wStatus;
      BYTE bReserved[8];    
    } ReqHdr;
      
    typedef struct _PlayAudio
    {
      ReqHdr rh;
      BYTE bMode;
      DWORD dwLoc;
      DWORD dwSectorNum;
    } PlayAudio;
    
    // Запрос IOCTL Output
    typedef struct _IOCTL_Output
    {             
      ReqHdr rh;
      BYTE   bMediaDescriptor;
      DWORD  lpTransferAddress;
      WORD   wDataSize;
      WORD   wStartSector;
      DWORD  lpVolID;
    } IOCTL_Output;       
    
    // Запрос на извлечение компакт-диска
    typedef struct _EjectDisk
    {
      BYTE  bControl;
    } EjectDisk;
    
    #pragma pack()
    
    // Прототипы функций
    void GetCDLetters(BYTE *bLetters);
    void CallCDDriver(void *rh, int nCDUnit);
    int PlayAudioTrack(DWORD dwLoc, DWORD dwSectorNum, int nCDUnit);
    int StopAudio(int nCDUnit);
    int DeviceOpen(int nCDUnit);
    int DeviceClose(int nCDUnit);
    int EjectCD(int nCDUnit);
    DWORD Red2Sierra(DWORD dwRedLoc);
    
    // Регистры для вызова функции int86
    union REGS rg;                      
    
    // Количество установленных устройств чтения CD-ROM
    int nCDUnits;
    
    // Номер первого устройства чтения CD-ROM
    int nCDStartUnit;
    
    // Слово состояния после вызова драйвера CD-ROM
    int iStatus;
                                   
    // Массив номеров установленных устройств CD-ROM
    BYTE bLetters[26];
    
    // ---------------------------------------------------
    // main
    // Точка входа в программу
    // ---------------------------------------------------
    int main(int argc, char *argv[]) 
    {
      int i;
      
      DWORD dwStartTrack;
      
      printf("CDPLAY, (c) A. Frolov, 1997\n\n");
      
      dwStartTrack = 1;
      if(argc == 2)
      {
        dwStartTrack = atol(argv[1]);
        printf("Track Red book: %ld\n", dwStartTrack);
        
      }
      else
      {
        printf("Usage: CDPLAY <Red book sector address>\n");
        return -1;
      }
      
      // Преобразование адреса сектора в формат Sierra
      dwStartTrack = Red2Sierra(dwStartTrack);
      printf("Track Sierra: %ld\n", dwStartTrack);
      
      // Проверяем, установлена ли программа MSCDEX
      rg.x.ax = 0x1500;
      rg.x.bx = 0;
      int86(0x2f, &rg, &rg);
    
      if(rg.x.bx == 0)
      {
        printf("MSCDEX is not installed\n");
        return -1;
      }
      
      else        
      {
        // Сохраняем общее количество устройств чтения CD-ROM
        nCDUnits = rg.x.bx;
        
        // Сохраняем номер первого такого устройства
        nCDStartUnit = rg.x.cx;
    
        // Определяем и отображаем вресию MSCDEX
        rg.x.ax = 0x150c;
        int86(0x2f, &rg, &rg);
        printf("MSCDEX version: %d.%d\n", rg.h.bh, rg.h.bl);
    
        // Отображаем количество найденных устройств чтения
        // CD-ROM и номер первого устройства
        printf("Found %d CD Unit, start unit: %c\n",
          nCDUnits, nCDStartUnit + 'A');
      }
    
      // Получаем массив номеров устройств чтения CD-ROM
      GetCDLetters(bLetters);
      
      // Отображаем обозначения всех устройств CD-ROM
      printf("CD-ROM letters: ");
      for(i = 0; i < nCDUnits; i++)
      {
        printf("%c ", bLetters[i] + 'A');  
      }
      printf("\n");
    
      // Открываем устройство
      iStatus = DeviceOpen(bLetters[0]);
      if(iStatus & 0x8000)
      {
        printf("DeviceOpen status: %04.4X\n", iStatus);
        return -1;
      }
    
      // Запускаем проигрывание
      iStatus = 
        PlayAudioTrack(dwStartTrack, 0xffffffff, bLetters[0]);
      if(iStatus & 0x8000)
      {
        printf("PlayAudioTrack status: %04.4X\n", iStatus);
        return -1;
      }
      printf("Started. Press any key to stop and eject CD\n");
      
      // Ожидаем, пока пользователь не нажмет клавишу
      getch();
    
      // Останавливаем проигрывание
      iStatus = StopAudio(bLetters[0]);
      if(iStatus & 0x8000)
      {
        printf("StopAudio status: %04.4X\n", iStatus);
        return -1;
      }
    
      // Извлекаем диск
      iStatus = EjectCD(bLetters[0]);
      if(iStatus & 0x8000)
      {
        printf("EjectCD status: %04.4X\n", iStatus);
        return -1;
      }
    
      // Закрываем устройство
      iStatus = DeviceClose(bLetters[0]);
      if(iStatus & 0x8000)
      {
        printf("DeviceClose status: %04.4X\n", iStatus);
        return -1;
      }
      return 0;
    }
    
    // ---------------------------------------------------
    // PlayAudioTrack
    // Запуск проигрывания звукового компакт-диска
    // ---------------------------------------------------
    int PlayAudioTrack(DWORD dwLoc, DWORD dwSectorNum, int nCDUnit)
    {
      PlayAudio cmd;
      
      memset(&cmd, 0, sizeof(PlayAudio));
      
      cmd.rh.bSize    = 22;
      cmd.rh.bSubUnit = 0;
      cmd.rh.bCmd     = 132;
      
      cmd.bMode  = 0;
      cmd.dwLoc  = dwLoc;
      cmd.dwSectorNum  = dwSectorNum;
      
      CallCDDriver(&cmd, nCDUnit);
      return cmd.rh.wStatus;
    }  
    
    // ---------------------------------------------------
    // StopAudio
    // Остановка проигрывания звукового компакт-диска
    // ---------------------------------------------------
    int StopAudio(int nCDUnit)
    {
      ReqHdr cmd;
      
      memset(&cmd, 0, sizeof(ReqHdr));
      
      cmd.bSize    = 13;
      cmd.bSubUnit = 0;
      cmd.bCmd     = 133;
      
      CallCDDriver(&cmd, nCDUnit);
      return (cmd.wStatus);
    }  
    
    // ---------------------------------------------------
    // DeviceOpen
    // Открывание устройства
    // ---------------------------------------------------
    int DeviceOpen(int nCDUnit)
    {
      ReqHdr cmd;
      
      memset(&cmd, 0, sizeof(ReqHdr));
      
      cmd.bSize    = 13;
      cmd.bSubUnit = 0;
      cmd.bCmd     = 13;
      
      CallCDDriver(&cmd, nCDUnit);
      return (cmd.wStatus);
    }  
    
    
    // ---------------------------------------------------
    // DeviceClose
    // Закрывание устройства
    // ---------------------------------------------------
    int DeviceClose(int nCDUnit)
    {
      ReqHdr cmd;
      
      memset(&cmd, 0, sizeof(ReqHdr));
      
      cmd.bSize    = 13;
      cmd.bSubUnit = 0;
      cmd.bCmd     = 14;
      
      CallCDDriver(&cmd, nCDUnit);
      return (cmd.wStatus);
    }  
    
    // ---------------------------------------------------
    // EjectCD
    // Извлечение компакт-диска
    // ---------------------------------------------------
    int EjectCD(int nCDUnit)
    {
      IOCTL_Output cmd;
      EjectDisk ed;
      
      memset(&cmd, 0, sizeof(IOCTL_Output));
      
      cmd.rh.bSize    = 14;
      cmd.rh.bSubUnit = 0;
      cmd.rh.bCmd     = 12;
      
      cmd.bMediaDescriptor  = 0;
      cmd.lpTransferAddress = (DWORD)(void far *)&ed;
      cmd.wDataSize         = 1;
      cmd.wStartSector      = 0;
      cmd.lpVolID           = (DWORD)(void far *)NULL;
      
      ed.bControl = 0;
      
      CallCDDriver(&cmd, nCDUnit);
      return cmd.rh.wStatus;
    }  
    
    // ---------------------------------------------------
    // CallCDDriver
    // Вызов драйвера компакт-диска
    // ---------------------------------------------------
    void CallCDDriver(void *rh, int nCDUnit)
    {
      static union REGS rg;
      static struct SREGS srg;
      
      segread(&srg);
      rg.x.ax = 0x1510;
      rg.x.cx = nCDUnit;
      rg.x.bx = FP_OFF(rh);  
      
      int86x(0x2f, &rg, &rg, &srg);
    }
    
    // ---------------------------------------------------
    // GetCDLetters
    // Заполнение массива номерами установленных 
    // в системе устройств чтения компакт-диска
    // ---------------------------------------------------
    void GetCDLetters(BYTE *bLetters)
    {
      static union REGS rg;
      static struct SREGS srg;
      
      segread(&srg);
      rg.x.ax = 0x150d;
      rg.x.bx = FP_OFF(bLetters);  
      
      int86x(0x2f, &rg, &rg, &srg);
    }
    
    // ---------------------------------------------------
    // Преобразование адреса дорожки из формата Red book
    // в формат Sierra
    // ---------------------------------------------------
    DWORD Red2Sierra(DWORD dwRedLoc)
    {
      BYTE bMin, bSec, bFrame;
    
      bMin   = (BYTE)((dwRedLoc >> 16) & 0xff);
      bSec   = (BYTE)((dwRedLoc >> 8) & 0xff);
      bFrame = (BYTE)(dwRedLoc & 0xff);
      
      return (DWORD)bMin * 75 * 60 + (DWORD)bSec * 75 + 
        (DWORD)bFrame - 150;
    }
    
    [Назад] [Содеожание] [Дальше]