Аппаратное обеспечение персонального компьютера© Александр Фролов, Григорий ФроловТом 33, М.: Диалог-МИФИ, 1997, 304 стр. |
Устройство чтения компакт-дисков давно стало необходимым атрибутом современных персональных компьютеров. Компакт-диски, вмещающие до 650 Мбайт информации, как нельзя лучше подходят для дистрибутивов программ, мультимедийных энциклопедий, видеодисков и других приложений, связанных с хранением и обработкой значительных объемов информации.
Не останавливаясь подробно на описании различных форматов компакт-дисков, которое само по себе может составить предмет для отдельной книги, мы рассмотрим средства MS-DOS, предназначенные для работы с компакт-дисками формата ISO-9960 и со звуковыми компакт-дисками стандарта Redbook. Первые из них чаще всего применяются для хранения программ и компьютерных баз данных, вторые – для записи звуковой информации. О том, как работать со звуковыми дисками в среде операционной системы Microsoft Windows, мы рассказывали в 15 томе «Библиотеки системного программиста», который называется «Мультимедиа для Windows».
Для работы с устройством чтения 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.
Вторая компонента, необходимая для работы в среде 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.EXE, доступные в среде MS-DOS.
Все функции расширения MSCDEX.EXE вызываются через мультиплексное прерывание INT 2Fh. При этом в регситр AH записывается значение 15h, а в регистр AL – код функции.
Сегодня все большее количество компьютеров оснащается сразу несколькими устойствами чтения 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, о которой мы расскажем ниже в этом разделе.
С помощью функции 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 = код ошибки |
При помощи функции 0Bh вы можете проверить, является диск устройством чтения CD-ROM, доступ к которому возможен через функции MSCDEX:
Регистры на входе: |
AX = 150Bh; CX = номер устройства чтения CD-ROM |
Регистры на выходе: |
BX = ADADh, если программа MSCDEX установлена; AX = признак, является ли диск устройством чтения CD-ROM: если содержимое AX не равно нулю, то диск – устройство CD-ROM; если содержимое AX равно нулю, доступ к данному устройству с помощью функций MSCDEX невозможен |
Функция 0Ch предназначена для определения версии установленной программы MSCDEX:
Регистры на входе: |
AX = 150Ch; |
Регистры на выходе: |
BH = старший номер верии MSCDEX; BL = младший номер версии MSCDEX |
При помощи функции 0Dh вы можете заполнить массив номерами установленных в системе устройств чтения CD-ROM:
Регистры на входе: |
AX = 150Dh; ES:BX = адрес массива, в который будут записаны обозначения устройств CD-ROM |
Регистры на выходе: |
Не используются |
Размер массива должен быть равен количеству установленных в системе устройств чтения CD-ROM, которое можно определить с помощью функции 00h.
Функция 10h предназначена для прямого вызова драйвера устройства чтения CD-ROM:
Регистры на входе: |
AX = 1510h; CX = номер устройства чтения CD-ROM; ES:BX = адрес предварительно заполненного заголовка запроса драйвера |
Регистры на выходе: |
Не используются |
Перед вызовом этой функции вы должны подготовить заголовок запроса.
Вызывая драйвер 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. Заметим, что в рамках одной команды может выполняться несколько функций. Код функции при этом записывается в расширение загловка запроса.
Команда инициализации вызывается из 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 программа может получить от драйвера самую разную информацию, начиная от адреса заголовка драйвера и заканчивая информацией о дорожках диска.
Формат заголовка запроса:
// --------------- // Код команды 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.
// --------------- // Код функции 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.
// --------------- // Код функции 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 программа может заставить драйвер выполнять различные операции, такие как управление механизмом извлечения компакт-диска.
Формат заголовка запроса:
// --------------- // Код команды 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 – разблокирование компакт-диска |
// --------------- // Код функции 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 вызывает как функции расширения 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 предназначена для проигрывания дорожек звуковых компакт-дисков. При запуске этой программы необходимо указать параметр – номер блока, с которого должно выполняться проигрывание.
Ниже мы привели пример запуска программы, передав ей адрес 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; }