Задача программы стратегии - запомнить этот адрес внутри тела драйвера для дальнейшего использования или организовать очередь запросов обслуживания.
Сразу после вызова программы стратегии DOS вызывает программу обработки прерываний, определив ее адрес из поля interrupt заголовка драйвера.
Программа обработки прерывания извлекает только что записанный программой стратегии адрес заголовка запроса и выполняет ту функцию, номер которой записан в запросе. Номер функции находится в заголовке запроса.
Результаты выполнения функции программа прерывания записывает в специально отведенные поля заголовка запроса, и на этом процедура обращения DOS к драйверу завершается.
Формат заголовка запроса будет приведен ниже, а сейчас покажем, как в заголовке драйвера задаются смещения программ стратегии и прерывания:
strateg DW strateg_proc
interrupt DW interrupt_proc
Последнее поле заголовка драйвера dev_name имеет различную интерпретацию для символьных и блочных устройств.
Для символьных устройств в этом поле должно располагаться выровненное по левому краю и дополненное до восьми символов пробелами имя устройства. Это имя будет использоваться для обращения к драйверу. Если Вы собираетесь заменить драйвер стандартного символьного устройства DOS на свой, Вы должны записать имя устройства заглавными буквами:
dev_nameDB 'AUX '
Для блочных устройств первый байт поля dev_name содержит количество устройств, обслуживаемых данным драйвером, остальные семь байтов не используются:
dev_name DB 1
DB 7 dup(?)
Таким образом, мы выяснили, что драйвер содержит в самом начале заголовок, и где-то дальше должны располагаться программы стратегии и прерывания. (Не следует путать программу прерывания драйвера с программой обслуживания аппаратных или программных прерываний. Хотя программа прерывания драйвера немного похожа на обработчик программных прерываний, назначение этой программы и механизм ее использования совершенно другой).
Что еще может находиться в программе-драйвере?
Это могут быть области данных, используемые драйвером, и подпрограммы, вызываемые программами стратегии и прерывания. Иногда стандартные драйверы переназначают на себя некоторые вектора прерываний, и тогда они содержат в себе обработчики этих прерываний. В области памяти, отведенной операционной системой драйверу, может располагаться стек драйвера, если размер системного стека недостаточен.
На длину драйвера накладывается такое же ограничение, как и на длину COM-программ - 64 килобайта, то есть один сегмент.
Рассмотрим теперь более подробно механизм взаимодействия драйвера и операционной системы.
После загрузки драйвер становится как бы частью операционной системы. Все обращения к драйверу DOS выполняет с использованием заголовка драйвера. Для примера приведем вид заголовка символьного драйвера, выполняющего только простейшие функции:
next DD 0FFFFFFFFhattrib DW 8000hstrateg DW strateg_procinterrupt DW interrupt_procdev_name DB 'TESTDRV 'Это символьный драйвер (старший бит поля attrib равен 1), исходный текст содержит только один драйвер (поле next содержит значение 0FFFFFFFFh), имя устройства, которое нужно будет использовать при обращении к драйверу - TESTDRV. Имя устройства не должно совпадать с именем файла, содержащего символьный драйвер, иначе Вы не сможете обратиться к файлу, например, для его переименования - DOS будет работать не с файлом, а с устройством.
Как уже было сказано, перед обращением к драйверу DOS подготавливает заголовок запроса в своей области данных и вызывает программу стратегии, извлекая ее смещение из заголовка драйвера. Программа стратегии обычно очень проста, так как ее задача - запомнить адрес заголовка запроса в области памяти драйвера. Область для хранения адреса заголовка запроса может быть определена следующим образом:
req_off DW ?req_seg DW ?Тогда программа стратегии должна записать содержимое регистра ES в поле req_seg, а регистра BX - в поле req_off:
strateg_proc: mov cs:req_off,bx mov cs:req_seg,es retДрайвер состоит из одного сегмента кодов, поэтому для адресации данных используется сегментный регистр CS.
Запрос операционной системы к драйверу соcтоит из заголовка, имеющего фиксированный формат и длину 13 байт, и переменной части, размер и формат которой зависит от выполняемой функции.
Приведем формат заголовка запроса:
(0) 1 | size | Длина запроса в байтах (длина заголовка запроса плюс длина переменной части запроса) |
(+1) 1 | unit | Номер устройства (используется для блочных устройств, указывает, с каким именно устройством, обслуживаемым драйвером, будет работать операционная система) |
(+2) 1 | cmd | Код команды, которую требуется выполнить (может иметь значение от 0 до 18h) |
(+3) 2 | status | Слово состояния устройства, заполняется драйвером перед возвратом управления операционной системе |
(+5) 8 | reserved | Зарезервировано |
После вызова программы стратегии DOS передает управление программе прерывания (без параметров). Задача программы прерывания - выполнить команду, код которой находится в поле cmd заголовка запроса. Если драйвер блочного устройства обслуживает несколько логических устройств, то в поле unit находится номер устройства, для которого необходимо выполнить команду.
В зависимости от выполняемой команды запрос может содержать другую информацию, необходимую для выполнения команды.
Как результаты выполнения команды возвращаются DOS?
Данные (или адреса данных), полученные драйвером от физического устройства ввода/вывода, помещаются в область переменной части запроса. Кроме того, драйвер должен установить слово соcтояния устройства status в заголовке запроса в соответствии с результатами выполнения команды.
Приведем формат слова состояния устройства:
Бит | Назначение |
0-7 | Код ошибки устройства (если команда выполнена с ошибкой и драйвер установил признак ошибки (бит 15) в единицу, в это поле он должен записать код ошибки). |
8 | Команда выполнена. Этот бит всегда устанавливается драйвером перед тем, как он возвращает управление операционной системе. |
9 | Занято. Этот бит устанавливается обработчиком команды, когда физическое устройство занято выполнением предыдущей операции и поэтому не может выполнить требуемую команду. Этот бит используется также для передачи такой информации, как "буфер клавиатуры не пуст", "среда носителя данных заменяемая" (в команде проверки возможности замены среды носителя данных). |
10-14 | Зарезервировано. |
15 | Признак ошибки. Устанавливается драйвером, когда он не может обработать запрос или произошла физическая либо логическая ошибка при обработке правильного запроса. Биты 0-7 при этом должны содержать код ошибки. |
Приведем таблицу возможных кодов ошибок:
Код | Описание |
0 | Нарушение защиты от записи. Была предпринята попытка записи информации на защищенное от записи устройство. |
1 | Неизвестное устройство. |
2 | Устройство не готово. |
3 | Неизвестная команда. Затребованная команда не поддерживается драйвером. |
4 | Ошибка CRC. При выполнении команды обнаружена ошибка циклического кода проверки. |
5 | Неправильная длина запроса. Поле длины в заголовке запроса содержит неверное значение. |
6 | Ошибка при поиске дорожки (дорожка не найдена). |
7 | Неизвестный носитель данных. |
8 | Сектор не найден. |
9 | Нет бумаги в принтере. |
0Ah | Ошибка записи. |
0Bh | Ошибка чтения. |
0Ch | Общая ошибка. |
0Dh | Зарезервировано. |
0Eh | Зарезервировано. |
0Fh | Неразрешенная замена диска (только для DOS версии 3.0 и более поздних версий). |
Общая схема действий программы прерывания драйвера такова:
Эта функция выполняется только один раз при загрузке драйвера и подключении его к операционной системе.
Функция инициализации должна поддерживаться любым драйвером, так как она сообщает операционной системе сведения, необходимые DOS для правильного подключения и использования драйвера.
Приведем формат запроса для команды инициализации:
(0) 13 | header | Заголовок запроса. |
(+13) 1 | n_units | Количество устройств, обслуживаемых драйвером. Это поле заполняется только блочным драйвером. |
(+14) 4 | end_addr | Конечный FAR-адрес резидентной части кода драйвера. В это поле драйвер записывает адрес байта памяти, следующего за той частью кода драйвера, которая должна стать резидентной. |
(+18) 4 | parm | FAR-адрес строки параметров инициализации драйвера из файла CONFIG.SYS. Эта строка содержит все, что находится в строке файла после команды 'DEVICE=', она заканчивается символами перевода строки и возврата каретки 0Ah, 0Dh. При возврате драйвер блочного устройства должен записать в это поле адрес массива указателей на блоки параметров BIOSBIOS (BPB), по одному указателю на каждое устройство, обслуживаемое драйвером. |
(+22) 1 | drive | Номер устройства. Для версии DOS 3.0 и более поздних версий в это поле при загрузке драйвера операционная система заносит номер, назначенный устройству, обслуживаемому драйвером. Например, для устройства А: это 0, для B: - 1 и т.д. |
При инициализации драйвер символьного устройства сохраняет в своей внутренней области данных параметры инициализации, используя адрес parm. Если параметры содержат числовые величины, программа инициализации может произвести их перекодировку и сохранить значения в двоичном формате.