Глава 16. Загружаемые драйверы.

Я вступил в аллею, под

покров шумящих дубов, и с которьм благоговением углуб­лялся в мрак ее.

Н.М. Карамзин Остров Борнгопьм.

Файл IO.SYS содержит в себе драйверы стандартных устройств. Таких устройств как минимум 5. Мы уже говорили о них, однако, рассмотрим еще раз. Это экран, кла­виатура, последовательный порт, стандартное устройство вывода сообщений об ошиб­ках и принтер. Наряду со стандартными драйверами в MS DOS предусмотрена воз­можность, создавать свои загружаемые драйверы. Имядрайверауказывается в файле CONFIG.SYS после строки DEVICE=. Наверное, Вам приходилось сталкиваться с та­кими драйверами.

Загружаемые драйверы обладают рядом преимуществ по сравнению с обычными TSR-программами. Перечислю эти преимущества:

1. Загружаемые драйверы после загрузки становятся неотъемлемой частью опера­ционной системы. Она выделяет им место в системной области и "знает" об их суще­ствовании.

2. Загружаемые драйверы попадают в память раньше, чем обычные программы и COMMAND.COM. Поэтому они раньше, чем другие программы, могут перехватить те или иные векторы прерываний, раньше выделить для себя память. Это может быть существенным, например, при борьбе с вирусами.

3. Интерфейс с загружаемыми драйверами можно осуществлять как посредством прерываний, так и посредством имени. Имя драйвера становится известным операци­онной системе после загрузки наряду с PRN, CONh т.д. Появляется возможность "на­учить" операционную систему работе с нестандартными устройствами.

4. Если имя устройства будет совпадать со стандартным именем, то Ваш драйвер заменит стандартныйдрайвер, содержащийся в IO.SYS. В примере, приведенном ниже, наш драйвер заменяет стандартный, обслуживающий устройство CON. Таким обра­зом, весь вывод на экран, производимый с помощью функций DOS, будет идти только

через наш драйвер.

Рассмотрим краткую теорию загружаемых драйверов. Более подробное описание Вы найдете в книгах [11,23].

DOS управляет различными внешними устройствами, обращаясь к соответствую­щим драйверам. Каждый драйвер содержит имя устройства, которым он управляет. MS DOS находит нужный драйвер, просматривая список установленных драйверов, как стандартных, так и установленных в CONFIG.SYS. Первым в списке находится драйвер с именем NULL и содержит указатель на следующий драйвер. Точно так же другие драйверы содержат указатели на следующие. Указатель в последнем драйвере содержит -1, что означает конец списка. При установке нового драйвера он ставится сразу после NULL. Таким образом, стандартные драйверы DOS оказываются в спискепоследними, и DOS обращается к ним только после просмотра всего списка, если в нем не найдено соответствующего имени. Возникает очень простой механизм замены стандартного драйвера на новый.

Драйвер должен состоять из трех частей:

1. Заголовок устройства. Помещается в начале драйвера и выглядит следующим образом.

DWORD Указатель на следующий драйвер в файле. Обычно -1, если драйвер пос­ледний или единственный.

WORD Атрибут.

WORD Указатель (смещение) на программу стратегии (см. ниже). WORD Указатель (смещение) на программу прерывания (см. ниже). 8-BYTE Имя драйвера. Для того чтобы драйвер "откликался" на имя, длина его должна составлять восемь байт. Существует два типадрайверов:

- драйвер символьного устройства,

- драйвер блокового устройства.

Символьное устройство осуществляет последовательный ввод-вывод. Такими уст­ройствами являются консоль (CON), последовательный порт (AUX), принтер (PRN).

Блоковые устройства включают все дисководы в системе. Они могут осуществ­лять выборочный ввод/вывод блоков данных, обычно равных физическому размеру сектора. Все они имеют свои номера и идентифицируются буквами А, В, С и т.д. Один драйвер блокового устройства может отвечать за один или несколько логически связ­ных дисководов. Предположим, данный драйвер отвечает за четыре дисковода. Это значит, что для него отведено четыре номера 0-3 и он занимает четыре буквы. Если он первый в списке драйверов блочных устройств, то эти буквы будут А, В, С, D. Следу­ющему блочному драйверу будут отведены буквы, начиная с Е. Блочный драйвер не будет загружаться, если использован весь латинский алфавит (последняя буква Z). Драйверы символьных устройств могут отвечать только за одно устройство. Типич­ным примером драйвера блокового устройства является драйвер электронного диска.

В следующих двух таблицах разбирается структура атрибута устройства.

Символьное устройство.

Бит

Состояние

Значение

0

І

Консоль ввода

І

І

Консоль вывода

2

І

Нулевое устройство

З

І

Часы

4-5

 

Зарезервировано (нуль)

б

І

Поддерживает функции 3.2

7-І0

 

Зарезервировано (нуль)

І

Понимает открыть/закрыть

 

12

Зарезервировано (нуль)

 

І

Поддерживает режим: вывод пока не занят

 

І4 І

Поддерживает управляющие

 

последовательности

 

 

15 І Символьное устройство

Блоковые устройства.

Бит

Состояние

Значение

О

 

Зарезервировано (ноль)

І

І

Поддерживает 32-битную адресацию секторов

2-5

 

Зарезервировано (ноль)

б

І

Поддерживает функции 3.2 и функции общего ввода/вывода

7-І0

 

Зарезервировано (нуль)

И

І

Понимает открыть/закрыть

І2

 

Зарезервировано (ноль)

ІЗ

І

Определяет диск проверкой первого байта FAT

І4

І

Поддерживает управляющие последовательности

І5

О

Блоковое устройство

Посмотрите на Рис. 16.1, как заполнено поле атрибута.

2. Процедура стратегии. Служит для передачи драйверу команд и данных, а также для получения от драйвера кода возврата и данных. В процедуру стратегии через пару регистров ES:BX драйверу передается адрес запроса (см. ниже). В запросе содержит­ся командадрайверу и необходимыедля выполнения команды данные. Сюдаже драй­вер заносит код возврата и данные, передаваемые драйвером системе. Процедура стра­тегии должна быть оформлена как процедура типа FAR.

Процедура стратегии предусмотрена разработчиками операционной системы для

дальнейшего использования в мультипрограммной среде. Процедура в нашей ситуа­ции предельно проста - она передает ссылку на запрос системы для процедуры преры­вания (см. Рис. 16.1).

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

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

Длина

Значение

BYTE

Длина в байтах заголовка (число 13).

BYTE

Код устройства, к которому обращена операция (если

 

драйвер обслуживает несколько устройств).

BYTE

Код операции (см. ниже).

WORD

Слово состояния. Заполняется процедурой прерывания.

8 BYTE

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

3. Процедура прерывания анализирует передаваемый ей запрос, выполняет команду, код которой содержится в запросе, и выставляет в слове состояния результат выпол­ненной операции. Фактически процедура прерывания является второй точкой входа. К ней DOS обращается уже после обращения к процедуре стратегии.

4. Кроме того, в теле драйвера могут храниться различные переменные (адрес за­головка запроса и др.), располагаться процедуры, к которым происходит обращение из процедуры прерывания.

Рассмотрим теперь все команды, выполняемые драйвером, и их краткую характе­ристику. Более подробные сведения можно найти в [11].

Команда инициализации (INI). Код 0. Выполняется всегда после загрузки драйве­ра (обязательно) и только один раз. Процедуру инициализации можно использовать для переустановки векторов прерываний или вывода сообщений. Должна возвращать конечный адрес резидентной части (по смещению от начала заголовка запроса. Таким образом, сама процедура инициализации может быть исключена из резидент­ной части драйвера. Процедура инициализации - и только она - может использовать для работы функции DOS.

Команда проверки диска (CHECK MEDIA). Код 1. Используется только с блоко­выми устройствами. Проверяет, не менялся ли диск в дисководе.

Команда построения ВРВ (блок параметров BIOS) (BUILDBPB). Код 2. Исполь­зуется только с блоковыми устройствами. Вызывается каждый раз после сообщения о

смене диска. Драйвер должен возвратить указатель на ВРВ.

УВВ чтение. Код 3 (IOCTL_INPUT). Эта команда используется драйверомдля воз­вращения управляющей информации об устройстве (например, состояние принтера).

Возвращается 44Н-Й функцией DOS и редко используется программами.

Чтение для блоковыхи символьныхустройств (INPUT). Код 4. Драйвер считывает

данные с устройства и передает их операционной системе.

Процедура не буферизованного чтения (READ_NO_WAIT). Код операции - 5. Используется в драйверах символьных устройств. Фактически данные в операцион­ную систему не передаются. DOS лишь получает сигнал о том, есть данные для вво­да или нет.

Процедура проверки состояния (INPUT_STATUS). Код операции 6. Применима только для символьного устройства. От предыдущей операции она отличается тем, что проверяет не наличие символов для ввода, а готовность самого устройства.

Процедура сброса или очистки ввода (INPUTFLUSH). Код операции 7. Применя­ется для символьных устройств, чтобы удалять из буфера ввода все вводимые ранее символы.

Запись для блоковых и символьных устройств (OUTPUT). Код операции 8. Пред­писывает драйверу произвести запись указанных данных на внешнее устройство.

Запись с проверкой (VERIFY OUTPUT). Код9. Командааналогичнапредыдущей, НО если установлен переключатель VERIFY ON, то после записи осуществляется счи­тывание данных и их проверка. Разумеется, для принтеров и экранов такая команда не применима.

Команда состояния вывода (OUTPUTJSTATUS). Код 10. Эта команда заставляет драйвер проверить состояние устройства, которое используется для вывода. Не при­менима для устройства, осуществляющего ввод.

Команда очистки вывода (OUTPUT_FLUSH). Код 11 .Эта функция используется для сброса входной очереди на символьных устройствах. Драйвер осуществляет сброс, выставляет состояния и возвращает управление.

УВВ запись (IOCTL_OUTPUT). Код 12. Данные передаются драйверу, но не уп­равляемому устройству. Разумеется, программа должна знать, какая информация мо­жет быть передана.

Процедуры закрытия и открытия (CLOSE и OPEN). Коды -13 и 14. Процедуры вызываются DOS, если бит 11 в атрибуте заголовка установлен в 1. Процедуры пред­назначены для того, чтобы информировать драйвер о текущей активности файлов. Драйвер может вести счет открытым устройствам. В случае символьного устройства при вызове процедуры открытия драйвер может послать инициализирующую после­довательность.

: Процедура проверки типа диска (REMOVE MEDIA). Код операции -15. Функция вызывается если бит 11 атрибута заголовка драйвера равен 1. Некоторым утилитам иногда необходимо знать, с каким диском они работают (с флоппи или винчестером).

Вывод пока не занято (OUT_UN_BUSY). Код 16. Команда для символьного уст­ройства. Действует, если установлен, бит 13 в слове атрибутов в заголовке устройства. Удобна для работы с принтерами.

Процедура общего УВВ. Код -19. Функции общего УВВ дают расширенное сред­ство управления

Процедуры установки/получения карты логического дисковода. Коды операций 23 и 24. Эти функции вызываются MS DOS, только если установлен бит 6 в слове

атрибута заголовка устройства.

Блоковые устройства читают и записывают сектора, символьные устройства чита­ют и записывают байты. По завершению ввода/выводадрайвер должен выставить слово-состояние и сообщить количество успешно переданных байт. В нижеприведенном примере МЫ не сообщаем счетчику, сколько байт выведено на экран, т.к. полагаем, что выведены все переданные байты.

Поле статуса.

Биты

Смысл поля

0-7

Коды ошибок. Работает только тогда, когда бит 15 равен 1.

8

Бит устанавливается в 1, когда драйвер закончил работу.

9

Устанавливается в 1, когда драйвер занят.

10-14

Резерв (нуль).

15

Устанавливается в 1, когда в работе драйвера произошла ошибка.

Кодыошибокдрайвера.

Код

Вид ошибки

0

Попытка записи на защищенный от записи носитель.

І

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

2

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

З

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

4

Ошибка в контрольной сумме.

5

Плохая структура запросадрайвера.

6

Ошибка носителя.

У

Неизвестный носитель.

8

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

9

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

Загружаемые драйверы строятся как СОМ-программы, однако ORG 1 ООН в нача­ле программы должно отсутствовать, так как при загрузке драйвера РЗРне строится. Но при трансляции текста не обязательно доводить дело до СОМ-формата - ЕХЕ-фор-мат так же будет понят системой (т.е. возможна, например, наряду с device = и

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

роватьегобезвсякихизмененийипроверитьнапрактике,каконработает.Данный драйвер заменит стандартный драйвер, обслуживающий устройство CON (т.е. по умол­чанию вывод на дисплей). Причем драйвер будет перехватывать не только те вызовы, которые идутчерез команды COPY, ТУРЕили посредством открытия вязыке высоко­го уровня файла с именем 'CON', но и, разумеется, символьный вывод посредством стандартных функций DOS.

Отметим некоторые особенности представленного драйвера, на которые следует

обратить внимание.

1. В процедуре инициализации мы указываем адрес конца драйвера так, чтобы сама процедура в резидентную часть драйвера не вошла. Это вполне естественно, т.к. эта процедура исполняется всего один раз.

2. Вывод на экран осуществляется посредством прерывания ЮН (BIOS). Это дос­таточно очевидно, потому что вызов соответствующих функций DOS привел бы к вызову драйвером самого себя, не говоря уже о свойстве неинтерабельности DOS.

3. Весь вывод осуществляется посредством функции OUTPUT. Однако при сим­вольном выводе система вызывает и другие функции. Для этих функций у нас стоит всего одна команда JMP QUIT- мы сообщаем системе, что данная функция выполнена успешно. Функции, где стоит JMP EXIT, будут возвращать в систему сообщение об

ошибке.

4. В концетаблицы вызовов функций стоит 8 DUP(EXIT),T.e. восемь адресов EXIT. Дело в том, что функция OUT_UN_BUSY имеет номер 16, тогда как номер последней

функции 24.

5. Мы не используем системный стек, а организуем свой. В целях безопасности

рекомендуется поступать таким способом.

6. Наш драйвер будет работать только на вывод, команда DOS типа COPY CON А.ТХТ не будет работать. Для корректной работы драйвера следовало бы добавить процедуру ввода (INPUT).

7. Не будут работать и некоторые другие функции, традиционно обрабатываемые символьным выводом DOS. К таким, в частности, относится звуковой сигнал при вы­воде символа с кодом 7. Почему? Да просто мы это не предусмотрели. Поработайте над этим сами.

CODE SEGMENT

DRIV PROC FAR

ASSUME CS:CODE ; заголовок драйвера (устройства)

DD -1 ATRIBUT      DW 1010100000000010В

DW STRATEGY

DW INTERRUPT

DB 'CON '    ;имя,  дополненное пробелами

;до 8 байт

/процедура STRATEGY стратегии

PROC F

FAR

MOV

MOV

CS :

CS :

вх,вх

ES,ES

RETF

REQ _ВХ

LABEL

DWORD 7

DW

DW

STRATEGY

ENDP

;здесь  хранятся регистры

_SS _SP _AX ; наш

STAC

DW DW DW

стек

DW DW ? ? ?

100

DUP(?)

прерывания

INTERRUPT   PROC FAR

свой стек

MOV MOV MOV MOV MOV MOV MOV

;сохраняем все PUSHF

PUSH

PUSH

PUSH

PUSH

PUSH

PUSH

PUSH ;определяем

LDS

XOR

MOV

SHL

JMP ;таблица

CS:_AX,AX CS:_SS,SS

CS:_SP,SP AX,CS SS, AX

SP,OFFSET STAC AX,CS:_AX

регистры

DS BX SI AX

DX

cx

DI

функцию драйвера SI,REQ

внувн

BL,DS:[SI+2] BX, 1

CS:[TABLE+BX]

переходов

TABLE DW INI

DW CHECK_MEDIA

DW BUILD_BPB

DW IOCTL_INPUT

DW INPUT

DW READ_NO_WAIT

DW INPUT_STATUS

DW INPUT_FLUSH

DW OUTPUT

DW VERIFY OUTPUT

DW OUTPUT_STATUS

DW OUTPUT_FLUSH

DW IOCTL_OUTPUT

DW OPEN

DW CLOSE

DW

DW OUT_UN_BUSY

DW 8 DUP(EXIT)

CHECK_MEDIA: JMP EXIT

BUILD_BPB: JMP EXIT

IOCTL_INPUT: JMP QUIT

INPUT: 1      JMP QUIT

READ_NO_WAIT: JMP QUIT

INPUT_STATUS: JMP QUIT

JMP QUIT

STROKA DB       'Нажмите любую клавишу'

NUM_STR DB      0 /Счетчик числа строк

_СХ DW ?

/процедура вывода  текста из   буфера запроса

OUTPUT:

PUSH ES /определяем,   где курсор

MOV АН,3

MOV ВН, О

INT 10H

CMP DH,24

JNZ D6

MOV АН,2

MOV DH,23

MOV  DL, 0

int юн

D6:

MOV AX, DS MOV ES,AX

LDS DI,ES: [SI+14]   ; DS : DI на буфер

MOV CX,ES:[SI+18]    /количество передаваемых байт / цикл  вывода текста L00:

. MOV CS:_CX,CX /если конец  строки,   то  следует  перейти к следующей

CMP

JNZ D4 DEC DI

10-4072

INC CS:_CX JMP    SHORT D5

D4:

CMP BYTE PTR DS: [DI] ,13   /признак конца

;строки?

JNZ Dl

D5:

INC CS:NUM_STR CMP DH,23

JNZ D2 ;сдвигаем экран вверх MOV АН,Об XOR СХ,СХ MOV DH,23 MOV DL,7 9 MOV AL,1 MOV BH,07H INT 10H

/курсор на начало  строки 23 MOV ВН,ОН MOV DH,23 MOV DL, 0 MOV AH,2 INT 10H JMP D3

D2:

/курсор на начало  следующей строки MOV DL,0

INC DH MOV AH,2

INT 10H JMP D3

Dl:

MOV AL, DS : [DI]

CMP AL,10        /символ конца строки /не печатать

JZ

D3

 

/печатаем

символ

MOV

ВН,

0

MOV

СХ,

1

MOV

BL,

7

MOV

АН,

09Н

INT

ЮН

 

;курсор к следующей позиции INC DL MOV АН, 2

INT 10H

D3:

;переходим к следующему символу

INC DI

CMP    CS:NUM_STR, 23

JNZ D7

MOV    CS:NUM_S TR,0

;выводим подсказку   внизу экрана PUSH ВХ PUSH DI

MOV AX,0B800H

MOV ES,AX

MOV BX,3840

MOV    DI,OFFSET STROKA

MOV CX,21

MOV    AH, 13

LO01:

MOV    AL,CS:[DI] MOV    ES: [BX] , AL

MOV

INC DI

ADD    BX,2

LOOP L001 ;ждем нажатия клавиши

MOV   АН, О

INT 16H

MOV CX,21

MOV BX,3840 ;чистим нижнюю строку экрана D9:

WORD PTR ES:[BX],0720H

D7 :

MOV

ADD

LOOP

POP POP

MOV

DEC

JZ

JMP

BX,2

D9 DI BX

CX,CS:

CX D8 LOO

CX

10*

D8:

POP ES

JMP QUIT

VERIFY_OUTPUT: OUTPUT_STATUS: OUTPUT_FLUSH: IOCTLJ3UTPUT:

OPEN: CLOSE:

REMOVE_ME DIA: OUT UN BUSY:

JMP QUIT JMP QUIT

JMP QUIT JMP QUIT JMP QUIT JMP QUIT

JMP EXIT

JMP QUIT

EXIT:

OR      WORD PTR DS: [SI+3] , 8003Н /установка

;8003H =  10000000В ОЗН - неизвестная команда. QUIT:

OR     WORD PTR DS:  [SI+3],0100H    /установка бита

,-ОЮОН =   00000001 00000000 POP DI

POP CX POP DX POP AX POP SI POP BX POP DS POPF

MOV SS,CS:'_SS MOV SP,CS:_SP RETF

END_:

/функция инициализация INI :

/устанавливаем в заголовке запроса адрес конца /резидентной части драйвера

MOV WORD PTR DS:  [ SI+14 ], OFFSET END_

MOV        DS:  [SI + 10H] , CS

PUSH CS

POP DS /выводим сообщение  о  загрузке драйвера MOV АН, 9

MOV STR

INT 21H

JMP QUIT

/битов ошибки

/конец работы

STR DB is INTERRUPT ENDP

DRIV ENDP

CODE ENDS END

Puc. 16.1. Пример простого драйвера, заменяющего устройство CON.