9 Устройство чтения CD-ROMУстройство чтения компакт-дисков давно стало необходимым атрибутом современных персональных компьютеров. Компакт-диски, вмещающие до 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:
Заметим, что буквенные обозначения устройств не обязательно должны идти последовательно, начинаясь со значения, которое функция 00h возвращает в регистре CX. Если вам нужно определить обозначения всех устройств чтения CD-ROM, следует воспользоваться функцией 150Dh, о которой мы расскажем ниже в этом разделе. Получение списка устройств CD-ROMС помощью функции 01h вы можете получить список структур CD_ROM_Driver_Desc, описывающих установленные устройства 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:
Функция может вернуть в выходном буфере пустую строку, состоящую из одного нулевого байта. Получение имени файла резюмеФункция 03h записывает в буфер имя файла, содержащего резюме компакт-диска, установленного в устройстве чтения CD-ROM:
Функция может вернуть в выходном буфере пустую строку, состоящую из одного нулевого байта. Получение имени файла библиографической документацииФункция 04h записывает в буфер имя файла, содержащего библиографическую документацию компакт-диска, установленного в устройстве чтения CD-ROM:
Функция может вернуть в выходном буфере пустую строку, состоящую из одного нулевого байта. Чтение сектора оглавления компакт-дискаПри помощи функции 05h вы можете найти все оглавления тома Volume Descriptor:
Чтение сектора по абсолютному адресуФункция 08h предназначена для прямого чтения секторов компакт-диска и напоминает прерывание INT 25h опреационной системы MS-DOS:
Проверка устройства чтения CD-ROMПри помощи функции 0Bh вы можете проверить, является диск устройством чтения CD-ROM, доступ к которому возможен через функции MSCDEX:
Определение версии MSCDEXФункция 0Ch предназначена для определения версии установленной программы MSCDEX:
Определение обозначения устройств чтения CD-ROMПри помощи функции 0Dh вы можете заполнить массив номерами установленных в системе устройств чтения CD-ROM:
Размер массива должен быть равен количеству установленных в системе устройств чтения CD-ROM, которое можно определить с помощью функции 00h. Вызов драйвера CD-ROMФункция 10h предназначена для прямого вызова драйвера устройства чтения CD-ROM:
Перед вызовом этой функции вы должны подготовить заголовок запроса. Вызывая драйвер 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 слово состояния, по которому можно судить о результате выполнения. Формат слова состояния:
Если команда выполнилась (с ошибкой или без ошибки), в слове состояния установлен бит 8. При возникновении ошибки также устанавливается бит 15. При этом в поле 0-7 находится код ошибки. Список кодов ошибок приведен ниже:
Здесь приведены коды ошибок не только для устройства чтения 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; После выполнения команды поля структуры заполняются драйвером следующим образом:
Чтение 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; Заполнение полей заголовка запроса:
Команда IOCTL Input может выполнять много функций. Перед вызовом драйвера вы должны подготовить заголовок функции, указав в одном из его полей код выполняемой функции. Адрес и размер этой структуры необходимо записать в поля lpTransferAddress и wDataSize, соответственно. Рассмотрим форматы заголовков различных функций, выполняемых в рамках команды IOCTL Input. Определение адреса заголовка драйвера CD-ROM// --------------- // Код функции 0 // --------------- #pragma pack(1) typedef struct _RAddr { BYTE bFunctionCode; DWORD lpDeviceHeader; } RAddr;
Зная адрес заголовка драйвера, вы можете определить имя драйвера и его атрибуты. Подробности об атрибутах драйверов вы найдете в 18 томе «Библиотеки системного програмиста». Определение положения головки// --------------- // Код функции 1 // --------------- #pragma pack(1) typedef struct _HeadLocation { BYTE bFunctionCode; BYTE bAddressMode; DWORD lpHeadLocation; } HeadLocation;
Здесь необходимо сделать замечание относительно режимов адресации. По умолчанию устройство чтения 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;
Чтение данных из устройства// --------------- // Код функции 5 // --------------- #pragma pack(1) typedef struct _DriveBytes { BYTE bFunctionCode; BYTE bNumBytes; BYTE bReadBuff[128]; } DriveBytes;
Определение состояния устройства// --------------- // Код функции 6 // --------------- #pragma pack(1) typedef struct _DeviceStatus { BYTE bFunctionCode; DWORD dwDeviceParameters; } DeviceStatus;
Описания отдельных бит слова состояния приведено ниже:
Немного о режимах Cooked и Raw. По умолчанию устройство чтения CD-ROM работает в режиме Cooked. Этот режим предполагает аппаратную обработку циклической контрольной суммы. Размер блока данных равен 2048 байт. В режиме Raw драйвер возвращает 2352 байта данных, в которые входят заголовок блока данных и контрольная сумма. Определение размера сектора// --------------- // Код функции 7 // --------------- #pragma pack(1) typedef struct _SectorSize { BYTE bFunctionCode; BYTE bReadMode; DWORD dwSectorSize; } SectorSize;
Определение размера тома// --------------- // Код функции 8 // --------------- #pragma pack(1) typedef struct _VolumeSize { BYTE bFunctionCode; DWORD dwVolumeSize; } VolumeSize;
Функция возвращает адрес дорожки Lead-out, преобразованное в численное значение по следующей формуле: frame + (sec * 75) + (min * 60 * 75) Это значение является адресом первого сектора, который располагается за самым последним адресуемым сектором диска. Проверка замены носителя данных// --------------- // Код функции 9 // --------------- #pragma pack(1) typedef struct _MediaChange { BYTE bFunctionCode; BYTE bMedia; } MediaChange;
Получение информации о компакт-диске// --------------- // Код функции 10 // --------------- #pragma pack(1) typedef struct _DiskInfo { BYTE bFunctionCode; BYTE bLowest; BYTE bHighest; DWORD dwTotal; } DiskInfo;
Получение информации о дорожке компакт-диска// --------------- // Код функции 11 // --------------- #pragma pack(1) typedef struct _TrackInfo { BYTE bFunctionCode; BYTE bTrack; DWORD dwLoc; BYTE bInfo; } TrackInfo;
Старшая тетрада формата дорожки имеет следующий формат:
Состояние бита, отмеченного символом *, значения не имеет. Младшая тетрада формата дорожки содержит тип режима 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;
Получение информации о подканале// --------------- // Код функции 13 // --------------- #pragma pack(1) typedef struct _QInfo { BYTE bFunctionCode; DWORD dwStartSector; DWORD dwTransferAddress; DWORD dwNumberOfSectors; } QInfo;
Получение штрих-кода изготовителя компакт-диска// --------------- // Код функции 14 // --------------- #pragma pack(1) typedef struct _UPCCode { BYTE bFunctionCode; BYTE bCtrlAndARD; BYTE bUPCCode[7]; BYTE bZero; BYTE bAFrame; } UPCCode;
Сброс входных буферовКоманда освобождает все входные буферы и отменяет все запущенные команды. Формат заголовка запроса: // --------------- // Код команды 7 // --------------- #pragma pack(1) typedef struct _Flush { ReqHdr rh; } Flush; Заполнение полей заголовка запроса:
Запись 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; Заполнение полей заголовка запроса:
Рассмотрим форматы заголовков различных функций, выполняемых в рамках команды IOCTL Output. Извлечение компакт-диска// --------------- // Код функции 0 // --------------- #pragma pack(1) typedef struct _EjectDisk { BYTE bFunctionCode; } EjectDisk;
Блокирование и разблокирование компакт-диска в устройстве// --------------- // Код функции 1 // --------------- #pragma pack(1) typedef struct _LockDisk { BYTE bFunctionCode; BYTE bLock; } LockDisk;
Сброс устройства чтения CD-ROM// --------------- // Код функции 2 // --------------- #pragma pack(1) typedef struct _ResetDrive { BYTE bFunctionCode; } ResetDrive;
Управление звуковыми каналами// --------------- // Код функции 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;
Запись в устройство управляющей строки// --------------- // Код функции 4 // --------------- #pragma pack(1) typedef struct _DriveControlBytes { BYTE bFunctionCode; BYTE bWriteBuff[...]; // размер зависит от устройства } DriveControlBytes;
Закрывание приемного устройства для компакт-диска// --------------- // Код функции 5 // --------------- #pragma pack(1) typedef struct _CloseTray { BYTE bFunctionCode; } CloseTray;
Открывание устройстваКоманда открывает устройство чтения CD-ROM, сообщая драйверу о том, что данное устройство будет использовано еще одной программой. Формат заголовка запроса: // --------------- // Код команды 13 // --------------- #pragma pack(1) typedef struct _RsumePlay { ReqHdr rh; } RsumePlay; Заполнение полей заголовка запроса:
Закрывание устройстваКоманда закрывает устройство чтения CD-ROM, открытое предыдущей командой. Формат заголовка запроса: // --------------- // Код команды 14 // --------------- #pragma pack(1) typedef struct _RsumePlay { ReqHdr rh; } RsumePlay; Заполнение полей заголовка запроса:
Чтение длинноеПри помощи команды длинного чтения программа может прочитать полное содержимое сектора компакт-диска, включая служебные области сектора. Формат заголовка запроса: // --------------- // Код команды 128 // --------------- #pragma pack(1) typedef struct _ReadLong { ReqHdr rh; BYTE bAddressMode; DWORD lpTransferAddress; WORD wDataSize; WORD wStartSector; BYTE bDataReadMode; BYTE bInterleaveSise; BYTE bInterleaveSkip; } ReadLong; Заполнение полей заголовка запроса:
Чтение длинное с предварительной выборкойФункция аналогична предыдущей, но сразу после вызова она немедленно возвращает управление. Драйвер выполняет подготовительные действия для операции чтения, такие, например, как позиционирование головки, однако операция чтения не выполняется. Формат заголовка запроса: // --------------- // Код команды 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; Заполнение полей заголовка запроса:
Проигрывание звуковой дорожкиКоманда запускает проигрывание звуковой дорожки начиная с указанного сектора. Формат заголовка запроса: // --------------- // Код команды 132 // --------------- #pragma pack(1) typedef struct _Play { ReqHdr rh; BYTE bAddressMode; WORD wStartSector; WORD wNumberOfSectors; } Play; Заполнение полей заголовка запроса:
Остановка проигрывания звуковой дорожкиКоманда останавливает проигрывание звуковой дорожки. Формат заголовка запроса: // --------------- // Код команды 133 // --------------- #pragma pack(1) typedef struct _StopPlay { ReqHdr rh; } StopPlay; Заполнение полей заголовка запроса:
Возобновление проигрывания звуковой дорожкиКоманда останавливает проигрывание звуковой дорожки. Формат заголовка запроса: // --------------- // Код команды 136 // --------------- #pragma pack(1) typedef struct _RsumePlay { ReqHdr rh; } RsumePlay; Заполнение полей заголовка запроса:
Программа 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; } |