

                   Глава 6.  УСТАНАВЛИВАЕМЫЕ  ДРАЙВЕРЫ УСТРОЙСТВ


                   Зачем нужны драйверы устройств?
                   Установка драйверов устройств
                   Работа с драйвером в среде MS-DOS
                   Создание драйверов устройств
                   Пример драйвера виртуального диска
                   Заключение



            Основное требование, предъявляемое к любой вычислительной сис-
         теме,  заключается не только в способности вычислять, но и в спо-
         собности взаимодействовать с внешним миром через периферийные ус-
         тройства.  Без таковой способности компьютер становится не  более
         чем  бесполезной  "железякой".  Задача любой операционной системы
         состоит в обеспечении средств взаимодействия для прикладных прог-
         рамм и для нужд самой операционной системы.
            Для того, чтобы прикладная программа могла взаимодействовать с
         внешним  устройством,  операционная  система должна удовлетворять
         двум основным требованиям. Во-первых, должен существовать опреде-
         ленный  интерфейс между прикладной программой и операционной сис-
         темой.  Этот интерфейс должен быть достаточно гибким, чтобы  при-
         кладная программа могла точно определить свои действия при работе
         с требуемым устройством.  Во-вторых, операционная система обязана
         уметь передавать и принимать данные от устройства и управлять его
         работой.  Такой интерфейс в MS-DOS обеспечивается так называемыми
         драйверами устройств.
            В то время как операционные системы больших ЭВМ  и  миникомпь-
         ютеров  традиционно  обладают широкими возможностями по поддержке
         устройств,  микрокомпьютеры довольно бедны в этой области. Обычно
         они имеют средства поддержки основных дисковых накопителей,  сис-
         темного терминала, печатающего устройства и, возможно, какого-ли-
         бо дополнительного устройства. Все что поддерживается сверх этого
         уровня можно рассматривать как приятную неожиданность.  В  старых
         операционных  системах,  включая MS-DOS версии 1.0,  обеспечивать
         поддержку дополнительных устройств после покупки ОС было довольно
         затруднительно.  Операционная система не содержала функциональных
         запросов прикладного уровня для нестандартных  устройств  и  сами
         драйверы были глубоко запрятаны в BIOS (базовая система ввода-вы-
         вода).  Добавление или изменение  драйвера  устройства  требовало
         корректировки исходных кодов BIOS (при  их  наличии,  разумеется),
         повторного  ассемблирования и  копирования полученных кодов на за-
         грузочную дорожку системного диска.  Очень часто  для  выполнения
         указанных операций не было даже соответствующих утилит. Более то-
         го,  такие компьютеры как IBM PC не позволяли и  этого,  т.к.  их
         BIOS записана в ПЗУ (постоянном запоминающем устройстве). Измене-
         ние содержимого ПЗУ требует  наличия  специального  программатора
         (устройства,  которое  записывает  информацию  в  программируемое
         ПЗУ),  а он имеется далеко не у каждого.  И даже после всех  этих
         усилий  прикладная программа не имела никаких средств для общения
         с драйвером с помощью ОС.
            Все изменилось с выходом MS-DOS  версии  2.0. Вероятно,  самым
         значительным нововведением в операционных системах микрокомпьюте-
         ров с тех пор как появилась CP/M стало то,  что MS-DOS версии 2.0
         и выше обеспечивают не только возможность установки драйверов без

                                      - 6-2 -
         каких-либо мучений, но и стандартный расширяемый интерфейс, кото-
         рый дает программам возможность взаимодействовать с драйверами. В
         результате громадно возросло количество устройств, поддерживаемых
         MS-DOS   и  появились  драйверы  псевдоустройств,  обеспечивающие
         MS-DOS системы такими средствами как  RAM-диски,  высокоуровневые
         графические интерфейсы и т.п.
            Драйвер устройства в MS-DOS - это подпрограмма,  которая вызы-
         вается MS-DOS,  с одной стороны,  и взаимодействует с  конкретным
         устройством,  с другой.  Как посредник между системой и аппарату-
         рой,  драйвер устройства передает данные между программой и  уст-
         ройством.

                          Зачем нужны драйверы устройств?

            Драйверы устройств решают две основные задачи. Первая заключа-
         ется в обеспечении стандартного интерфейса со  всеми программами,
         желающими  использовать определенное устройство,  независимого от
         конкретных особенностей устройства.  Программа, выполняющая обра-
         ботку текста,  или электронная таблица,  производящая вычисления,
         может не заботиться о типе терминала,  подключенного  к  системе,
         выдавая простые команды типа "Отобразить символ" и "Получить сим-
         вол".  Все технические детали по пересылке символов берет на себя
         драйвер,  обеспечивая тем самым желанный для прикладной программы
         высокоуровневый интерфейс.  Замена терминала может вызвать замену
         драйвера,  но  при этом в прикладной программе не потребуется де-
         лать никаких изменений.  Драйверы дисководов должны  обеспечивать
         стандартный  интерфейс  для  всех используемых типов дисков,  при
         этом программа, осуществляющая ввод/вывод с диска, будет работать
         с дискетой любого формата,  с жестким диском, и даже с псевдодис-
         ком в ОЗУ,  не замечая никаких различий. Одним словом, первая за-
         дача  драйвера  состоит  в обеспечении независимого от устройства
         унифицированного интерфейса.
            Второе целевое  назначение  драйверов  устройств заключается в
         том,  что они для всех прикладных программ  обеспечивают  сервис,
         подобный библиотекам функций времени выполнения.  Любая программа
         освобождена не только от необходимости поддержки множества разно-
         форматных  устройств,  но  и от необходимости поддерживать вообще
         какие-либо форматы.  Все заботы по поддержке устройств  возложены
         на драйверы устройств.  В связи с тем, что все драйверы собраны в
         операционной системе, требуется лишь одна копия каждого драйвера.
         В результате этого программы,  написанные с использованием интер-
         фейса, предоставляемого MS-DOS, вообще не содержат в себе драйве-
         ров.
            В операционной системе MS-DOS версии 2.0 и выше драйверы могут
         быть добавлены для того,  чтобы заменить встроенные драйверы сис-
         темы. Если Вам не нравится как работает системный драйвер с конк-
         ретным устройством,  Вы можете написать свой собственный драйвер.
         Как подчеркивалось выше,  прикладные программы при этом ничего не
         заметят. Конечно  создание  драйвера  - не самое простое занятие,
         но, по крайней мере, такая возможность у Вас есть.
            Имея такое мощное средство обеспечения работы MS-DOS с различ-
         ными устройствами,  недолго представить себе драйверы, не поддер-
         живающие  реальных  устройств!  Другими  словами,  можно написать
         драйвер,который поддерживает несуществующее  устройство, например
         драйвер-эмулятор диска в ОЗУ.  Такие устройства получили название
         "виртуальные устройства",  а драйверы таких устройств,  соответс-
         твенно, "драйверы  виртуальных устройств" или просто "виртуальные

                                      - 6-3 -
         драйверы".
            Реальные или виртуальные устройства не ограничены, по сути де-
         ла,  только операциями ввода/вывода. На драйвер может быть возло-
         жена  любая  функция  по преобразованию данных.  Высокоскоростные
         процессоры для выполнения больших объемов вычислений  с плавающей
         точкой -  это  только  один из примеров устройства преобразования
         информации. Кроме того, драйверы могут программно эмулировать ре-
         альные устройства,  которые отсутствуют в конкретной системе, та-
         кие как часы или сопроцессор с плавающей точкой.

                      Когда использовать драйверы устройств?

            При каких  условиях некоторую функцию следует удалить из прог-
         раммы и перенести в драйвер?  Основное правило состоит в том, что
         если  какая-либо  функция  выполняет  ввод/вывод на на физическом
         уровне (т.е. работая непосредственно с аппаратурой), то эта функ-
         ция - прекрасный кандидат для переноса в драйвер. По самой приро-
         де семейства микропроцессоров 80x86 такие функции обычно содержат
         команды IN и/или OUT (включая INS или OUTS). Если система поддер-
         живает ввод/вывод,  отображенный на память,  доступ к  абсолютным
         адресам  памяти  также  может служить индикатором ввода/вывода на
         физическом уровне (чтение и запись векторов прерываний тоже явля-
         ется доступом к абсолютным адресам, но, конечно, предпочтительнее
         использовать функции MS-DOS "Получить вектор прерывания" и "Уста-
         новить  вектор  прерывания",  чем  использовать для этих же целей
         драйвер).
            Выделение программ-обработчиков  операций ввода/вывода в драй-
         вер устройства порождает четыре следствия :  это делает программы
         более  переместимыми,  делает  обработчики  операций ввода/вывода
         доступными для других программ, желающих работать с этим устройс-
         твом,  несколько увеличивает в размерах систему и замедляет время
         доступа к аппаратуре.  Некоторое увеличение размера памяти, зани-
         маемой  системой,  не  имеет большого значения,  а вот увеличение
         времени доступа может быть  критическим  фактором  для  некоторых
         приложений. Когда принимается решение о написании драйвера, необ-
         ходимо тщательно взвесить скоростные характеристики программы,  с
         одной стороны,  и  повышение совместимости программ и доступность
         драйвера, с другой стороны. Увеличение времени  доступа  за  счет
         накладных  расходов при каждом обращении к драйверу более заметно
         для устройств,  которые передают за один раз слово или байт  дан-
         ных. В драйверах, передающих за одно обращение целый блок данных,
         накладные расходы заметно уменьшаются.

                        MS-DOS - нереентерабельная система

            В связи с тем,  что обращения к драйверам осуществляет MS-DOS,
         на них накладываются такие же ограничения, как и на резидентные в
         памяти программы.  Так,  например, драйверы не могут пользоваться
         функциями MS-DOS (за исключением некоторых функций, которые могут
         использоваться при инициализации драйвера).  Это серьезно ограни-
         чивает свободу драйверов виртуальных устройств, созданных для до-
         полнительной обработки информации,  предназначенной для стандарт-
         ных драйверов.
            Так, например,  драйвер виртуального принтера, предназначенный
         для поддержки графических примитивов на  игольчатом  принтере  не
         может  использовать  стандартные функции MS-DOS для вывода симво-
         лов.  Драйвер виртуального принтера  должен  обеспечивать  полную

                                      - 6-4 -
         программную  поддержку  для  осуществления  физического вывода на
         принтер. Заметим, что драйвер, описанный в этом примере, именует-
         ся виртуальным, несмотря на то, что он работает с физическим уст-
         ройством.  Это объясняется тем, что драйвер предоставляет возмож-
         ности,   не   поддерживаемые   реальным  устройством,  такие  как
         выполнение графических операций на простом принтере.
            В связи с тем,  что MS-DOS нереентерабельна,  нельзя использо-
         вать программу DEBUG для отладки установленного драйвера. Для вы-
         полнения   собственных  операций  ввода/вывода  DEBUG  использует
         MS-DOS и если DEBUG использовать для отладки драйвера,  он испор-
         тит  переданную  драйверу  информацию,  делая невозможным возврат
         корректной информации в MS-DOS.  Один из  способов  обхода  этого
         препятствия  заключается в использовании любых имеющихся встроен-
         ных функций ввода/вывода (например,  функций BIOS) для вывода от-
         ладочной информации.  Более предпочтительный способ заключается в
         создании небольшой тестовой программы для проверки работы драйве-
         ра, которая передает драйверу тестовые данные и проверяет возвра-
         щаемую информацию.  Такая программа запускается  под  управлением
         отладчика обычным образом.  Конечно,  если устройство критично ко
         времени,  необходимо принять соответствующие меры, чтобы избежать
         какого-либо влияния на работу драйвера.

                           Установка драйверов устройств

            Как упоминалось ранее,  во времена, предшествующие MS-DOS вер-
         сии  2.0,  установка драйвера устройства означала изменение BIOS.
         Начиная с версии 2.0 появилась возможность устанавливать и  заме-
         нять драйверы в процессе начальной загрузки системы.
            Процесс начальной загрузки MS-DOS начинается со сброса  систе-
         мы.  Аппаратура  Вашей системы устанавливается в состояние сброса
         при включении питания компьютера.  Сразу после  сброса  процессор
         начинает выполнять команды,  находящиеся в самом конце его адрес-
         ного пространства.  Для процессора 80386 это команды, находящиеся
         по  шестнадцатиричному  адресу FFFFFFF0,  для процессора 80286 по
         адресу FFFFF0,  для процессора 8086 по адресу FFFF0. В любом слу-
         чае  по этим адресам находится ПЗУ,  содержащее начальный загруз-
         чик,  задача которого заключается в  загрузке  системной  области
         диска  в память.  Интересно отметить,  что возможности начального
         загрузчика постоянно росли.  Первый персональный компьютер  фирмы
         IBM  (IBM  PC)  мог загружаться только с дисковода "A".  Вместе с
         компьютером IBM PC XT появилась возможность загрузки  с  жесткого
         диска  и, видимо, недалек тот час, когда появится возможность се-
         тевой загрузки.
            Системная область  диска,  загружаемая в память начальным заг-
         рузчиком,  называется вторичным загрузчиком. В случае MS-DOS, ра-
         ботающей на IBM - совместимом компьютере, это самый первый сектор
         диска длиной 512 байт.  Такой маленький  размер  объясняется  тем
         фактом,  что BIOS находится в ПЗУ.  Вторичному загрузчику, в этом
         случае, для загрузки остальной части системы достаточно обратить-
         ся к BIOS,  которая всегда находится в ПЗУ. В системах, не содер-
         жащих BIOS в ПЗУ,  начальный загрузчик должен считывать  с  диска
         программу, способную обеспечить возможность вторичному загрузчику
         считать остальную часть системы.   В таких системах начальный за-
         грузчик должен считывать довольно большую часть диска.
            Сама MS-DOS загружается только после того,  как будет считан в
         память  вторичный загрузчик.  Именно по этой причине возможен за-
         пуск игр, не требующих для своей работы MS-DOS, или возможна заг-

                                      - 6-5 -
         рузка  других  операционных систем.  Собственно,  тип загружаемой
         системы зависит от того,  что именно считывается  с  загрузочного
         диска. При загрузке MS-DOS вторичный загрузчик предполагает нали-
         чие на диске корневого директория и,  как минимум, двух системных
         файлов. В связи с тем, что эти файлы скрытые, они не отображаются
         при выводе содержимого корневого  директория  (однако,  их  можно
         увидеть при помощи таких утилит,  как XTREE, Norton Utilities или
         SDIR).  Функции этих файлов одинаковы у  всех  поставщиков,  хотя
         имена могут различаться. Первый файл содержит ядро MS-DOS и обыч-
         но называется MSDOS.SYS или IBMDOS.COM  на  системах  фирмы  IBM.
         Другой  файл  содержит  интерфейс между MS-DOS и подсистемой вво-
         да-вывода и называется IO.SYS (Microsoft),  IBMBIO.COM (IBM)  или
         еще как-нибудь у других поставщиков. Вместе эти два файла состав-
         ляют операционную систему MS-DOS.  После того, как вторичный  за-
         грузчик находит и загружает эти файлы, начинается процесс инициа-
         лизации  MS-DOS.  Заметим,  что   на   IBM-совместимых   системах
         вторичный загрузчик считывает только файл IBMBIO.COM,  который, в
         свою очередь, загружает IBMDOS.COM.
            Как только  загружен интерфейсный файл (IO.SYS или его эквива-
         лент),  вторичный загрузчик передает управление процедуре инициа-
         лизации,  содержащейся в интерфейсном файле. Кроме этой процедуры
         интерфейсный файл содержит стандартные  драйверы,  которые  будут
         использоваться при инициализации и работе MS-DOS.
            Сама процедура инициализации заключается в распределении  час-
         тей MS-DOS в памяти, создании всех внутренних таблиц, рабочих об-
         ластей и т.п.,  и, наконец, инициализации всех устройств, связан-
         ных  с  системой.  Инициализация  устройств заключается в посылке
         команды INIT каждому из драйверов,  содержащихся  в  интерфейсном
         файле (мы обсудим команду INIT позже,  совместно с другими коман-
         дами для драйверов устройств). После инициализации устройств про-
         цедура  инициализации  заканчивает  создание  внутренних таблиц и
         система к этому моменту готова к работе. До окончательного завер-
         шения, однако, остается еще один шаг.
            В этой точке процедура инициализации проверяет  наличие  файла
         CONFIG.SYS.  Если указанный файл отсутствует, то MS-DOS загружает
         стандартный интерпретатор команд и передает ему  управление. Если
         же файл CONFIG.SYS найден,  то выполняется еще один шаг инициали-
         зации. На этом этапе Вам предоставляется возможность подключить к
         MS-DOS Ваши собственные драйверы устройств.

                                  Файл CONFIG.SYS

            Файл CONFIG.SYS это обычный  текстовый  файл,  который  должен
         быть расположен в корневом директории диска,  с которого происхо-
         дит загрузка системы (если этот файл находится не в корневом  ди-
         ректории,  то процедура инициализации предполагает, что он совсем
         отсутствует).  Файл CONFIG.SYS содержит  команды,  руководствуясь
         которыми  процедура  инициализации изменяет и/или дополняет стан-
         дартную конфигурацию MS-DOS.  Если этот файл доступен,  процедура
         инициализации  (но не COMMAND.COM - он еще не загружен) считывает
         его в память и обрабатывает строка за строкой.  Каждая строка со-
         держит одну команду конфигурации. На диаграмме 6-1 показана обра-
         ботка некоторых команд.  Наиболее важна для нас  команда  DEVICE,
         которая имеет следующий формат:

                DEVICE=[d:][path]filename[.ext][ parameters]


                                      - 6-6 -
            где (заключенные в квадратные скобки элементы не являются обя-
         зательными):
                d:         - идентификатор дисковода,
                path       - путь к драйверу,
                filename   - имя файла, содержащего драйвер,
                ext        - расширение имени файла,
                parameters - параметры для драйвера.

            Эта команда  задает  необходимость  установки нового драйвера.
         Программа драйвера,  содержащаяся в заданном драйвере,  похожа на
         обычную .COM программу,  но имеет некоторые специфические особен-
         ности, описываемые далее,  в разделе, посвященном написанию драй-
         веров.
            В общем случае,  драйвер представляет собой особую форму рези-
         дентной программы.  Когда в файле CONFIG.SYS встречается  команда
         DEVICE, соответствующий драйвер загружается в память и анализиру-
         ется. Заголовок драйвера содержит информацию о типе, имени, атри-
         бутах устройства и определяет точки входа в программу. После заг-
         рузки  драйвера  MS-DOS  обращается  к  драйверу с командой INIT.
         Драйвер выполняет инициализацию и возвращает  управление  MS-DOS,
         указывая адрес конца драйвера, т.е. адрес первого свободного бай-
         та памяти, непосредственно следующего за драйвером. На этом уста-
         новка драйвера заканчивается.
            Указание адреса  конца  драйвера  при  возвращении  управления
         MS-DOS после выполнения команды INIT подобно указанию размера па-
         мяти,  занимаемой программой, при вызове функции MS-DOS "Остаться
         резидентом". По возвращаемому адресу MS-DOS определяет расположе-
         ние свободной памяти. Если файл CONFIG.SYS содержит другие коман-
         ды  DEVICE,  следующий  драйвер загружается непосредственно после
         предыдущего.  После того, как обработка файла CONFIG.SYS законче-
         на,  загружается еще один драйвер - драйвер фиктивного устройства
         (NUL-драйвер).  Затем MS-DOS  завершает  инициализацию  загрузкой
         постоянной части COMMAND.COM или другой, определяемой пользовате-
         лем оболочки.
            При загрузке драйверов MS-DOS связывает их в цепочку, так что-
         бы каждый драйвер содержал ссылку на ранее  загруженный  драйвер.
         Цепочка драйверов начинается,  таким образом, с последнего загру-
         женного драйвера (NUL-драйвер) и заканчивается самым первым  заг-
         руженным  драйвером (обычно стандартный драйвер устройства COM2).
         Такая цепочка строится, используя первые два слова заголовка каж-
         дого драйвера.  Эти два слова содержат сегмент и смещение следую-
         щего в цепочке драйвера или, в случае последнего драйвера число -
         1 (шестнадцатиричное значение FFFF). Пример цепочки драйверов по-
         казан в листинге 6-6, приведенном в конце этой главы.
            Когда MS-DOS  требуется  обратиться  к определенному драйверу,
         она начинает поиск по цепочке драйверов (начиная  с NUL-драйвера)
         в порядке, обратном тому, в котором драйверы были загружены. Пос-
         ле того, как требуемый драйвер найден, MS-DOS обращается к нему с
         соответствующей командой. Последовательность поиска в цепочке при
         этом такова,  что если загружен пользовательский драйвер, имя ко-
         торого  совпадает с именем какого-либо стандартного драйвера (та-
         кого как CON,  AUX или PRN),  драйвер пользователя  будет  найден
         первым.  Это позволяет пользователю заменять стандартные драйверы
         (например, заменить стандартный CON-драйвер на ANSI.SYS CON-драй-
         вер).
            Стандартные драйверы в действительности загружаются и  инициа-
         лизируются  до того как файл CONFIG.SYS будет считан и обработан.

                                      - 6-7 -
           ┌───┐   ┌───┐
           │ 1 │   │ 2 │
           └─┬─┘   └─┬─┘
             └───┬───┘
                 
               ┌───┐
               │ 3 │
               └─┬─┘
                / \   Да
              <  4  >────────>┬<──────────────────────────────────────┐
                \ /                                                  │
                 │Нет        / \  Да                                  │
                 │         <  5  >───────────────────────────┐        │
                 │           \ /                                     │
                 │<───────────┘Нет                         ┌───┐      │
                                                          │ 7 │      │
               ┌───┐                                       └─┬─┘      │
               │ 6 │                                    Да  / \       │
               └─┬─┘                              ┌───────<  8  >     │
                 │                                         \ /       │
                 │                              ┌───┐        │Нет     │
                / \  Да                         │ 9 │                │
              <  12 >─────────┐                 └─┬─┘      ┌───┐      │
                \ /                                      │ 10│      │
                 │Нет       ┌───┐               ┌───┐      └─┬─┘      │
                           │ 13│               │ 11│        │        │
               ┌───┐        └─┬─┘               └─┬─┘                │
               │ 14│          │                   └───────────────────┘
               └─┬─┘          │
                 │            │
                 │<───────────┘
                 │
                 
               ┌───┐
               │ 15│
               └───┘

                   Блок-схема 6-1. Процесс инициализации MS-DOS.

            1 - Теплая загрузка (при нажатии клавиш Ctrl+Alt+Del)
            2 - Холодная загрузка (кнопка "СБРОС" или включение питания)
            3 - Загрузка системы
            4 - Файл CONFIG.SYS существует ?
            5 - Есть еще строки в CONFIG.SYS ?
            6 - Загрузка и запуск требуемого командного процессора (по
                умолчанию это COMMAND.COM)
            7 - Чтение строки из файла CONFIG.SYS
            8 - Это команда "DEVICE=" ?
            9 - Загрузка указанного в команде "DEVICE=" файла и подключение
                его к цепочке драйверов
           10 - Обработка какой-либо из команд "BREAK=","BUFFERS=","FILES="
                или "SHELL="
           11 - Обращение к драйверу с командой "INIT="
           12 - Файл AUTOEXEC.BAT существует ?
           13 - Выполнение всех обнаруженных в AUTOEXEC.BAT команд
           14 - Выполнение программ DATE и TIME
           15 - Выдача системного приглашения "A:>"

                                      - 6-8 -
         Это позволяет процедуре инициализации драйвера использовать неко-
         торые  функции MS-DOS для вывода сообщений или настройке драйвера
         на конкретную версию операционной системы.  Без всякой опаски мо-
         гут быть использованы функции MS-DOS с 01H по 0CH,  которые обес-
         печивают  работу  с  устройствами CON, PRN и AUX, а также функция
         30H ("Получить версию MS-DOS").  Вызовов,  относящихся к работе с
         файлами или управлением памятью,  следует избегать, так как  рас-
         пределение памяти полностью еще не завершено.
            После того, как файл CONFIG.SYS обработан и драйверы проиници-
         ализированы, стандартные драйверы устройств CON, PRN и AUX закры-
         ваются и заново открываются операционной системой для того, чтобы
         могла  произойти  замена  (если  таковая предусмотрена) указанных
         драйверов.  Начиная с этого  момента  используются  только  новые
         драйверы.
            Определенные драйверы не могут  быть  заменены  пользователем.
         Один из них - это драйвер пустого  (фиктивного)  устройства  NUL.
         Это  объясняется тем фактом,  что MS-DOS использует NUL-драйвер в
         качестве начала цепочки драйверов. Так как встроенный NUL-драйвер
         всегда определяет начало цепочки драйверов,  то первым всегда бу-
         дет найден встроенный NUL-драйвер.  Схематический пример  цепочки
         драйверов показан на рисунке 6-1.  Подробно назначение каждого из
         указанных полей будет объяснено позже.  Драйвер,  помеченный  как
         последний,  в действительности был первым устанавливаемым драйве-
         ром,  а драйвер, находящийся сразу после NUL-драйвера (в цепочке)
         устанавливался самым последним.
             ┌────────────╖         ┌────────────╖         ┌────────────╖
             │ Указатель  ║         │ Указатель  ║         │   Маркер   ║
             │ на первый  ╟────────>│    на      ╟────────>│ последнего ║
             │  драйвер   ║         │ следующий  ║         │  драйвера  ║
             │            ║         │  драйвер   ║         │   ( -1 )   ║
             ├────────────╢         ├────────────╢         ├────────────╢
             │  Атрибуты  ║         │  Атрибуты  ║         │  Атрибуты  ║
             ├────────────╢         ├────────────╢         ├────────────╢
             │ Указатель  ║         │ Указатель  ║         │ Указатель  ║
       ┌─────┤     на     ║   ┌─────┤     на     ║   ┌─────┤     на     ║
       │     │ СТРАТЕГИЙ  ║   │     │ СТРАТЕГИЙ  ║   │     │ СТРАТЕГИЙ  ║
       │     ├────────────╢   │     ├────────────╢   │     ├────────────╢
       │     │ Указатель  ║   │     │ Указатель  ║   │     │ Указатель  ║
       │  ┌──┤     на     ║   │  ┌──┤     на     ║   │  ┌──┤     на     ║
       │  │  │ ПРЕРЫВАНИЙ ║   │  │  │ ПРЕРЫВАНИЙ ║   │  │  │ ПРЕРЫВАНИЙ ║
       │  │  ├────────────╢   │  │  ├────────────╢   │  │  ├────────────╢
       │  │  │ Устройство ║   │  │  │  Имя или   ║   │  │  │  Имя или   ║
       │  │  │    NUL     ║   │  │  │   число    ║   │  │  │   число    ║
       │  │  │            ║   │  │  │ устройств  ║   │  │  │ устройств  ║
       └────>├────────────╢   └────>├────────────╢   └────>├────────────╢
          │  │ Программа  ║      │  │ Программа  ║      │  │ Программа  ║
          │  │ СТРАТЕГИЙ  ║      │  │ СТРАТЕГИЙ  ║      │  │ СТРАТЕГИЙ  ║
          │   \/\/\/\/\/\/       │   \/\/\/\/\/\/       │   \/\/\/\/\/\/
          │  /\/\/\/\/\/\/\      │  /\/\/\/\/\/\/\      │  /\/\/\/\/\/\/\
          └─>├────────────╢      └─>├────────────╢      └─>├────────────╢
             │ Программа  ║         │ Программа  ║         │ Программа  ║
             │ ПРЕРЫВАНИЙ ║         │ ПРЕРЫВАНИЙ ║         │ ПРЕРЫВАНИЙ ║
              \/\/\/\/\/\/           \/\/\/\/\/\/           \/\/\/\/\/\/
             /\/\/\/\/\/\/\         /\/\/\/\/\/\/\         /\/\/\/\/\/\/\
             ╘════════════╝         ╘════════════╝         ╘════════════╝
                     Рисунок 6-1. Цепочка драйверов устройств.
            Не только NUL-драйвер не может быть заменен. Драйверы, работа-

                                      - 6-9 -
         ющие с устройствами массовой памяти (например с  дисками),  также
         не  могут  быть  заменены.  Вы можете добавить драйверы для новых
         дисков,  но не удалить или заменить уже существующие. Это ограни-
         чение возникает по той причине, что имена драйверам дисковых уст-
         ройств (A,B,C и т.д.) назначает MS-DOS при  загрузке.  Невозможно
         присвоить  конкретному  дисководу уникальное имя,  соответственно
         нельзя и заменить его.

                      Использование команды ASSIGN для замены
                           драйверов дисковых устройств

            Тем не менее не расстраивайтесь, если Вас не удовлетворяет ра-
         бота существующих дисковых драйверов. Хотя их нельзя удалить, они
         могут быть "нейтрализованы". После того, как Вы написали (и прове-
         рили) новый драйвер,добавьте его в файл CONFIG.SYS. После  переза-
         грузки системы он будет включен в цепочку драйверов устройств. На-
         пример, если Вы имеете три дисковода,  новый драйвер  получит  имя
         "D".  Теперь  используйте  команду ASSIGN для переназначения любых
         обращений к старому драйверу на новый. Допустим, мы хотим заменить
         драйвер дисковода "A". Команда ASSIGN, при этом, будет иметь вид

                ASSIGN A = D

            MS-DOS переназначит  все  обращения  к драйверу "A" на драйвер
         "D",  включая абсолютный доступ к диску по прерываниям 25H и 26H.
         Если  Вы  написали  новый  драйвер для работы с тем же физическим
         дисководом, с которым работал старый драйвер, то описанной проце-
         дурой  Вы довольно эффективно заменили его на новый.  Если же Вам
         покажется, что старый драйвер все-таки лучше, Вы можете восстано-
         вить первоначальную конфигурацию,  введя команду ASSIGN без пара-
         метров.
        ┌─────────────────────────────────────────────────────────────────┐
        │    ПРЕДУПРЕЖДЕНИЕ : Когда НЕЛЬЗЯ использовать команду ASSIGN    │
        │                                                                 │
        │    Хотя  команда  ASSIGN  позволяет  Вам  заменять существующие │
        │ драйверы  дисков  на  новые,  это  не всегда разумно. Некоторые │
        │ команды,  такие  как  BACKUP  и  PRINT, или программы, подобные │
        │ Lotus 1-2-3 будут весьма удивлены, если их попросят работать  с │
        │ переопределенными дисками.  Другие команды,  такие как  FORMAT, │
        │ DISKCOPY или DISKCOMP, вообще игнорируют такие диски и работают │
        │ с настоящими логическими дисками.                               │
        └─────────────────────────────────────────────────────────────────┘

                             Типы драйверов устройств

            Существует два типа драйверов устройств,  именованные и неиме-
         нованные,  называемые соответственно драйверами  символьных  уст-
         ройств  и драйверами блоковых устройств.  Различие между ними го-
         раздо глубже,  чем способность иметь имя или заменяемость.  Кроме
         того,  что блоковые драйверы предназначены для поддержки дисковых
         устройств, предполагается,что один блоковый драйвер может поддер-
         живать  более  одного  дисковода.  Команды ввода/вывода для таких
         драйверов обеспечивают возможность доступа к  отдельным  секторам
         и,  если  не  задан  атрибут NONIBM (также известный как NONFAT),
         предполагается,  что драйвер должен поддерживать стандартную  для
         MS-DOS структуру диска, включая FAT (таблицу распределения диска)

                                      - 6-10 -
         и директории.
            Откровенно говоря,  названия "символьный" и "блоковый" не сов-
         сем точны,  так как символьный драйвер  тоже  может  поддерживать
         блоковый режим передачи данных.  Более того,  нельзя сказать, что
         символьные драйверы обеспечивают последовательный доступ, а блоч-
         ные драйверы обеспечивают прямой доступ,  так как можно спроекти-
         ровать символьный драйвер так, чтобы он поддерживал прямой доступ
         к устройству (если, конечно, он может работать в таком режиме.
            Оставив пока вопрос о том,  что же такое символьный драйвер  и
         что  такое  блоковый драйвер,  обсудим некоторые способы работы с
         драйверами устройств через MS-DOS. Это даст нам некоторые сообра-
         жения о том,  какой тип следует выбрать, если Вы желаете написать
         драйвер для какого-либо приложения.

                   Работа с драйверами устройств в среде MS-DOS

            Для прикладных  программ  MS-DOS  обеспечивает четыре основных
         метода доступа к внешним устройствам.  Каждый из них  удобен  для
         соответствующих  приложений и мы обсудим достоинства и недостатки
         каждого метода для того,  чтобы Вы могли выбрать метод,  наиболее
         удобный для Вашего приложения.  Мы не будем описывать детали каж-
         дого из функциональных вызовов, так как эту информацию можно най-
         ти в "MS-DOS Programmer's Reference Manual" ("MS-DOS. Руководство
         программиста.") фирмы Microsoft или другом аналогичном  руководс-
         тве. Следующий ниже список классифицирует эти четыре метода.

         * CP/M-ориентированные  функции для работы с такими устройствами,
           как консоль, принтер или вспомогательное устройство. Это истин-
           но символьные устройства. Функции, входящие в эту группу :

                CON: Функции 01H, 02H, и с 06H по 0CH
                PRN: Функция 05H
                AUX: Функции 03H и 04H

         * CP/M-ориентированные функции для работы с файлами с использова-
           нием FCB (блока управления файлами).  Этот  метод  также  может
           быть  использован  для доступа к символьным устройствам.  В эту
           группу входят функции :

             Открыть/Закрыть:                 Функции 0FH и 10H
             Читать/Писать Устройство/Файл:   Функции 14H и 15H
             Читать/Писать Файл:              Функции 21H, 22H, 27H и 28H

         * Функции MS-DOS-стиля для  работы  с  файлами  с  использованием
           описателей. Этот метод (аналогично FCB-методу) тоже можно ис-
           пользовать для работы с символьными устройствами.  Функции, ра-
           ботающие с использованием описателей файлов :

                Открыть/Закрыть:                 Функции 3DH и 3EH
                Читать/Писать Устройство/Файл:   Функции 3FH и 40H
                Управление Устройством:          Функция 44H
         * Функции прямого доступа к диску, выполняющие чтение и запись по
           абсолютным адресам.  Эти функции обеспечиваются отдельными пре-
           рываниями INT 25H (абсолютное чтение) и INT 26H (абсолютная за-
           пись).


                                      - 6-11 -
             Функции CP/M-стиля для работы с символьными устройствами

            CP/M-ориентированные функции предназначены,  в  основном,  для
         работы со стандартным устройством CON и предлагают возможности для
         буферизации, эхо-отображения, ожидания символов и проверки состоя-
         ния.  Поддержка устройств PRN и AUX более ограниченная,  но вполне
         достаточна для многих приложений. Для нестандартных устройств, од-
         нако,  необходимо использовать либо метод, использующий FCB (блоки
         управления файлами), либо метод на основе описателей файлов.

          Работа с устройством с использованием блоков управления файлами

            FCB-метод работы с устройствами имеет и достоинства  и  недос-
         татки. С одной стороны, FCB сложнее создавать и использовать, чем
         работать с  описателями файлов,  хотя использование макросредств и
         директив STRUC может весьма облегчить задачу построения блока  уп-
         равления  файлом.  С другой стороны,  FCB-метод позволяет програм-
         мисту непосредственно указывать номер записи в файле,  делая  воз-
         можным  прямой  доступ  к  файлам.  Функции  3FH  ("Читать") и 40H
         ("Писать"), работающие с описателями, позволяют осуществлять толь-
         ко последовательный доступ к файлам. Для выполнения прямого досту-
         па к файлам,  используя функции описателей ,  прикладная программа
         должна  обращаться  к функции 42H ("Передвинуть указатель файла").
         FCB-метод работы таких дополнительных действий не требует.

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

            Хотя прямой доступ очень нужен при работе  с  файлами,  он  не
         имеет большого значения при работе с  не  дисковыми  устройствами.
         При  работе  с  такими  устройствами  метод доступа,  использующий
         описатели,  намного проще в использовании и не требует от програм-
         миста  создания FCB.  Кроме того,  описатель-ориентированный метод
         доступа  (ДОМД)  поддерживает  IOCTL  (управление  вводом/выводом)
         функцию 44H. Как мы вскоре увидим, IOCTL-функция может быть исклю-
         чительно полезна для управления устройством.
            При использовании   ДОМД   (описатель-ориентированного   метода
         доступа) для работы с не дисковыми  устройствами,  программист  не
         ограничен пересылкой одного байта за один раз. За одно обращение к
         функциям ввода/вывода может быть переслано  с  устройства  или  на
         устройство до 64 Kбайт.  Как и при работе с дисками, использование
         этих функций для  не  дисковых  устройств  приводит  к  выполнению
         последовательной передачи данных. Используя, однако, IOCTL-функцию
         прямого управления,  можно задать устройству дополнительные  пара-
         метры.  Так, например, если и устройство и его драйвер установлены
         в режим прямого доступа,  можно использовать IOCTL-функцию для уп-
         равления  точками  отправления и назначения при пересылке данных в
         устройстве.
            Этот пример может помочь при  иллюстрации  потенциала  прямого
         управления вводом/выводом с устройством. Предположим, что некото-
         рая система имеет отображаемую на адресное пространство графичес-
         кую подсистему.  Данные из системной памяти в графическую пересы-
         лаются с использованием драйвера графического  адаптера.  По  той
         причине,  что этот адаптер не является устройством массовой памя-
         ти, драйвер для него должен быть символьным. Если ввод/вывод про-
         изводится с использованием только ДОМД,  нет никакого способа оп-
         ределить место в видео-памяти,  куда должны быть посланы  данные.

                                      - 6-12 -
         Если  же драйвер поддерживает IOCTL-функцию,  место в графической
         памяти можно определить через канал управления.

           Функция 44H - управление вводом/выводом для устройств (IOCTL)

            Как мы   упоминали,   не  все  устройства  поддерживают  вызов
         IOCTL-функции.  Те драйверы, которые обеспечивают управление вво-
         дом/выводом,   не   обязательно   поддерживают   все  возможности
         IOCTL-функции.  Тем не менее,  IOCTL  является  настолько  мощным
         средством  управления  работой  устройств,  что  понуждает многих
         программистов поближе познакомиться с его  возможностями.  Знание
         того,  что  можно сделать с помощью IOCTL,  несомненно определяет
         решение программиста о том,  какими функциональными особенностями
         наделить драйвер устройства.
            Функция управления вводом/выводом имеет три  основных  режима,
         которые определяются передаваемым в регистре AL кодом функции :

              - Конфигурация устройства (коды 0, 1 и в последних версиях
                MS-DOS, коды 8, 0BH, 0EH и 0FH);
              - Управление каналом ввода/вывода (коды с 2 по 5 и в MS-DOS
                версии 3.2, коды 0CH и 0DH);
              - Запрос статуса устройства (коды 6 и 7).

         Список кодов функций, поддерживаемых IOCTL, показан в таблице 6-1.
            Запрос статуса устройства возвращает либо индикатор готовности
         (0FFH) либо не готовности (0). В руководстве  программиста  фирма
         Microsoft предупреждает о том,  что код статуса может быть некор-
         ректным на момент возвращения  управления  вызывающей  программе.
         Вероятно, в руководстве имеется в виду будущая возможность  муль-
         тизадачности MS-DOS.  Можно только надеяться,  что когда появятся
         будущие версии, Microsoft найдет способ возвращать корректную ин-
         формацию. Как бы то ни было, до тех пор пока MS-DOS не стала мно-
         гозадачной, проблемы неточности статуса не должно существовать.
            Мы уже упоминали  возможности  канала  управления  устройством
         IOCTL.  Попросту говоря,  это средство пересылки буфера данных по
         вспомогательному каналу.  Механизм этого вызова идентичен  вызову
         функций ввода/вывода на основе ДОМД (функции 3FH и 40H),  за иск-
         лючением кодов  функций,  определяемых  содержимым  регистра  AX.
         Предназначены ли данные,  передаваемые по дополнительному каналу,
         для устройства или для самого драйвера - это дело разработчика.
            Не будьте, однако, ослеплены простотой этой функции и не восп-
         ринимайте ее как всего-лишь еще одну функцию ввода/вывода.  В со-
         ответствующем  приложении,  IOCTL может блестяще выступать в роли
         вторичного канала для взаимодействия с драйвером. Фирма Microsoft
         обеспечила  "запасную  дверь" для решения непредвиденных проблем.
         Они говорят - "Вам кажется,  что наш интерфейс с драйверами слиш-
         ком ограничен ? Должен быть более гибким ? Что же, попробуйте вот
         это." Такой подход является огромным шагом вперед по  сравнению с
         подходом "У нас нет этого, значит Вам это не нужно !", который не
         так давно был весьма распространен в среде  разработчиков систем.


                                      - 6-13 -
                                                         Таблица 6-1
                     Функции управления вводом/выводом (IOCTL)
         ──────┬────────┬──────────┬──────────────────────────────────────
           Код │ Версия │Примечание│           Назначение
          (AL=)│ MS-DOS │          │
         ──────┼────────┼──────────┼──────────────────────────────────────
           0:  │  2.0+  │ #1,#2    │Получить информацию устройства
           1:  │  2.0+  │          │Установить информацию устройства
           2:  │  2.0+  │ #3,#6    │Читать из управляющего канала СУ
           3:  │  2.0+  │ #3,#6    │Писать в управляющий канал СУ
           4:  │  2.0+  │ #3,#7    │Читать из управляющего канала БУ
           5:  │  2.0+  │ #3,#7    │Писать в управляющий канал БУ
           6:  │  2.0+  │ #1       │Получить входную информацию
           7:  │  2.0+  │ #1       │Получить выходную информацию
           8:  │  3.0+  │ #2       │БУ поддерживает смену носителя ?
           9:  │  3.2+  │          │БУ локальное или удаленное ?
           A:  │  3.2+  │          │Описатель локальный или удаленный ?
           B:  │  3.0+  │ #4       │Изменить счетчик попыток
           C:  │  3.3+  │ #5       │Запрос на переключение кодовых страниц
           D:  │  3.3+  │ #5       │Запрос IOCTL для блоковых устройств
           E:  │  3.3+  │ #5       │Получить имя логического диска
           F:  │  3.3+  │ #5       │Установить имя логического диска
         ──────┴────────┴──────────┴──────────────────────────────────────
         Примечание #1: Функция поддерживает как файлы, так и устройства.
         Примечание #2: Функция не поддерживает сетевую работу.
         Примечание #3: Функция разрешается  битом  14  словом  атрибутов
                        драйвера  и  поддержка определяется битом 14 слова
                        конфигурации.
         Примечание #4: Функция требует загрузки команды SHARE.
         Примечание #5: Функция разрешена битом 6 слова атрибутов драйвера.
         Примечание #6: СУ - символьное устройство.
         Примечание #7: БУ - блоковое устройство.

              Конфигурация с помощью команд управления вводом/выводом

            MS-DOS обеспечивает  выполнение команд конфигурации ("Получить
         или   Установить    информацию    устройства"),    поддерживаемых
         IOCTL-функцией. На рисунке 6-2 показано 16-битовое слово конфигу-
         рации, используемое функциями "Получить/Установить информацию ус-
         тройства" (коды 0 и 1). В текущих версиях MS-DOS могут быть опре-
         делены только младшие 8 бит этого слова.  Ниже описано назначение
         тех битов слова конфигурации,  которые имеют значение для драйве-
         ров устройств или влияют на способ обработки драйвером данных.

                                IOCTL БИТ 14: CTRL

            Бит CTRL устанавливается в 1 если драйвер  может  обрабатывать
         управляющие последовательности. Этот бит точно отражает состояние
         IOCTL бита в слове атрибутов драйвера устройства.  IOCTL-бит  ис-
         пользуется драйвером для оповещения MS-DOS о том, что драйвер бу-
         дет принимать управляющие последовательности.  Этот бит  применим
         как к файлам, так и к устройствам.



                                      - 6-14 -
          15  14  13  12  11  10  9   8   7   6   5   4   3   2   1   0
        ╔═══╤═══╤═══╤═══╤═══╤═══╤═══╤═══╤═══╤═══╤═══╤═══╤═══╤═══╤═══╤═══╗
        ║ R │ C │   │   │   │   │   │   │ I │ E │ B │ S │ I │ I │ I │ I ║
        ║ E │ T │   │   │   │   │   │   │ S │ O │ I │ P │ S │ S │ S │ S ║
        ║ S │ R │    R E S E R V E D    │ D │ F │ N │ E │ C │ N │ C │ C ║
        ║   │ L │   │   │   │   │   │   │ E │   │   │ C │ L │ U │ O │ I ║
        ║   │   │   │   │   │   │   │   │ V │   │   │ L │ K │ L │ T │ N ║
        ╚═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╝
         ЗНАЧЕНИЯ БИТОВ                     УСТРОЙСТВО
         CTRL =1 : Поддержка управляющего   EOF  =0 : Конец файла на входе
                   канала                   BIN  =1 : Работа в двоичном
         ISDEV=1 : Канал - это устройство             режиме
              =0 : Канал - это файл         SPECL=1 : Специальное устрой-
                                                      ство
                                            ISCLK=1 : Устройство "ЧАСЫ"
         ФАЙЛ                               ISNUL=1 : Устройство NUL
         После записи в канал биты с 0 по   ISCOT=1 : Консоль вывода
         5 - это номер блокового устр-ва    ISCIN=1 : Консоль ввода

                    Рисунок 6-2. Слово конфигурации устройства.

                                IOCTL БИТ 7: ISDEV

            Бит ISDEV равен 1 если канал (или описатель)  открыт  к  уст-
         ройству. Если канал открыт к файлу, то этот бит сбрасывается в 0.

                                 IOCTL БИТ 5: BIN

            Пятый бит конфигурации (BIN) определяет подготовленный или не-
         подготовленный режим работы драйвера.  Другими словами,  этот бит
         определяет будут ли данные проходить дополнительную обработку при
         передаче или MS-DOS будет просто передавать "сырую" двоичную  ин-
         формацию между устройством и прикладной программой.  Под дополни-
         тельной обработкой подразумевается обработка определенных  управ-
         ляющих  символов,  расширение  символов  табуляции,  проверка  на
         нажатие клавиш CTRL-BREAK и т.п.
            Более традиционными  для этих функций являются термины "двоич-
         ный режим" и "ASCII режим",  соответствующие неподготовленному  и
         подготовленному  режимам.  В  руководстве программиста для MS-DOS
         приводятся более детальные инструкции о том,  как проверить и ус-
         тановить пятый бит. Мы, в свою очередь, обсудим влияние этого би-
         та на работу символьных драйверов.  (Заметьте,  что, как показано
         на рис.6-2, этот бит не используется для блоковых драйверов).
            Если символьный драйвер находится в подготовленном  режиме (по
         умолчанию), данные передаются побайтно. Другими словами, одно об-
         ращение к драйверу приводит к передаче одного символа. Это проис-
         ходит вне зависимости от того,  какое количество байт затребовано
         прикладной программой при  обращении  к  MS-DOS.  Например,  если
         прикладной программе требуется вывести 128 байт на символьное ус-
         тройство,  а драйвер работает в подготовленном режиме,  то MS-DOS
         сделает  128 обращений к драйверу с функцией "ВЫВОД" или "ВЫВОД С
         ПРОВЕРКОЙ", передавая за один вызов один байт.
            Посимвольного ввода/вывода можно избежать,  переведя драйвер в
         неподготовленный режим.  Последний может быть установлен только с
         помощью IOCTL функции. В неподготовленном режиме количество пере-
         даваемых байт, заданное прикладной программой, используется также
         при обращении к драйверу.  Пользуясь тем же самым примером,  если
         прикладная программа требует вывода 128 байт на  символьное  уст-

                                      - 6-15 -
         ройство,  и драйвер работает в неподготовленном режиме, то MS-DOS
         сделает единственное обращение к драйверу с функцией  "ВЫВОД" или
         "ВЫВОД С ПРОВЕРКОЙ",  задавая количество передаваемых байт равным
         128.
                                IOCTL БИТ 4: SPECL
            Подобно биту CTRL,  бит SPECL в слове конфигурации точно отра-
         жает  состояние бита SPECL в слове атрибутов.  Будучи установлен-
         ным,  этот бит означает, что данный драйвер (который почти всегда
         является  драйвером  консоли) способен выполнять высокоскоростной
         вывод в двоичном режиме, используя прерывание INT 29H.
            Бит BIN,  определяющий  неподготовленный режим,  также требует
         разрешения высокоскоростного режима вывода, определяемого атрибу-
         том SPECL. Если установлены как бит слова конфигурации BIN, так и
         бит слова атрибутов SPECL, значит драйвер будет работать в режиме
         высокоскоростного вывода. Этот режим и бит атрибутов SPECL обсуж-
         даются более глубоко в разделе "Слове атрибутов заголовка драйве-
         ра".

                      Группа команд управления вводом/выводом

            Четыре IOCTL команды, появившиеся в MS-DOS версии 3.3 - коман-
         ды C,  D, E и F - являются необязательными и разрешены только при
         установленном бите 6 слова атрибутов драйвера. Группа подфункций,
         обеспечиваемых командами C и D, представляет собой довольно "раз-
         ношерстное"  собрание  весьма специфических функций.  Эта группа,
         как правило,  используется для поддержки  определенных,  заданных
         изготовителем  устройства функциональных особенностей,  таких как
         переключение фонтов в принтере,  форматирование диска и т.д. Если
         Вам кажется, что у Вас есть необходимость использовать эти коман-
         ды,  следует обратиться к руководству программиста, где представ-
         лена более подробная информация.
            Команды E и F  позволяют  прикладному  программисту  управлять
         назначением и освобождением логических дисков,  например так, как
         это делается командой SUBST. Команда E ("Получить имя логического
         диска") возвращает назначение, использованное при последнем обра-
         щении к реальному устройству,  а команда F ("Установить имя логи-
         ческого диска") используется для изменения назначенных имен логи-
         ческих дисков.

             Прямой доступ к диску через прерывания INT 25H и INT 25H

            С другой  стороны  спектра  от  доступа к устройству с помощью
         описателей файлов (ДОМД) лежат прерывания прямого доступа к диску
         :  "Чтение по абсолютному адресу" (INT 25H) и "Запись по абсолют-
         ному адресу" (INT 26H).  Согласно  названию,  прерывания  прямого
         доступа  к диску работают исключительно с блоковыми устройствами,
         например с дисками.  Задача этих прерываний заключается в обеспе-
         чении работы с дисками напрямую,  не используя файловую структуру
         MS-DOS. Это может быть полезно в двух случаях.
            В первом случае,  программисты могут считывать или  записывать
         отдельные  части  стандартного диска MS-DOS,  содержащие файл или
         структуру директория. Это часто требуется, когда часть диска ста-
         новится плохой и невозможно,  поэтому, использовать FCB-метод или
         ДОМД.  В этом случае можно использовать функции прямого доступа к
         диску  для  того,  чтобы попробовать восстановить все,  что может
         быть восстановлено. Кроме того, программы могут считывать и запи-
         сывать  таблицу распределения (FAT) или директории диска,  недос-
         тупные  другим  методам.  Такая способность  требуется  утилитам,

                                      - 6-16 -
         сортирующим директории, изменяющим атрибуты файлов и т.п.
            Во втором случае,  использование этих функций  может  потребо-
         ваться в случае, если диск вообще не содержит таблицы распределе-
         ния файлов или директориев.  Такой диск  может  быть  использован
         только  как диск данных.  Такая же ситуация может встретиться при
         чтении дисков,  записанных в другой операционной среде, такой как
         CP/M  или  UCSD-p система.  Во всех этих случаях параметры диска,
         возвращаемые системе драйвером, делают невозможным доступ к диску
         любым другим методом.  Любая попытка выполнения файловых операций
         ввода/вывода,  включая чтение директория, возвратит мусор или со-
         общение об ошибке ("Non-DOS Disk"). Если Вы желаете получить под-
         робную информацию о том,  как  MS-DOS  определяет  формат  диска,
         просмотрите  описание команды драйверу "Построить блок параметров
         BIOS" в руководстве программиста или ином, аналогичном документе.
            Возвращаясь к обеспечению прямого доступа к диску, следует за-
         метить, что INT 25H и INT26H не выполняют блокирование и деблоки-
         рование  данных.  Блокирование и деблокирование требуются,  когда
         размер физического сектора на диске отличается от  размера  логи-
         ческой  записи,  используемого системой.  При блокировании данных
         система собирает вместе достаточное количество записей для запол-
         нения физического сектора перед сохранением его на диске.  Дебло-
         кирование используется при чтении с диска,  т.к.  один физический
         сектор может содержать несколько записей. В последнем случае сис-
         тема считывает целый сектор и,  затем,  выбирает оттуда требуемые
         программе  записи.  Функции доступа к диску по абсолютным адресам
         считывают и записывают только целые секторы,  так что программист
         обязан знать размер сектора диска для того,  чтобы определить ко-
         личество считанных или записанных байтов.
            В связи с тем, что параметры, используемые этими прерываниями,
         передаются драйверу без какого-либо преобразования, операции чте-
         ния и записи передают блоки данных размером, кратным длине секто-
         ра диска.  Это отличает данный метод доступа  от  FCB-метода  или
         описатель-ориентированного метода доступа,  где ввод/вывод  опре-
         деляется в терминах логических блоков и записей,  а  MS-DOS  осу-
         ществляет  преобразование логических блоков в физические секторы.
            Последняя особенность функций прямого доступа к диску заключа-
         ется в том,  что они возвращаются из прерываний INT 25H и INT 26H
         при помощи команды RETF,  а не IRET,  оставляя при этом флаги  на
         стеке.  Поэтому,  после проверки корректности выполнения функции,
         Вы должны убрать флаги со стека.

                          Опция "Ввод/вывод с проверкой"

            При выполнении  операций  ввода/вывода  следует учитывать одну
         особенность,  влияющее на работу драйвера устройства. Эта особен-
         ность  вызывается  использованием опции "Ввод/вывод с проверкой",
         при помощи которой можно заставить драйвер  проверять  выполнение
         команд вывода, т.е. осуществлять считывание после записи. Эта оп-
         ция может устанавливаться или отменяться тремя способами :

                1.С командной строки MS-DOS пользователь  может  выполнить
                команды "VERIFY ON" или "VERIFY OFF" для того, чтобы соот-
                ветственно включить или выключить эту опцию.

                2.Для некоторых команд MS-DOS,  таких как COPY,  можно за-
                дать ключ /V, который включает опцию проверки на время вы-
                полнения команды.

                                      - 6-17 -
                3.Опция проверки может быть  включена  и  выключена  любой
                программой,  используя  функцию  MS-DOS  2H ("Включить или
                выключить опцию проверки").

                                      Выводы

            В этом разделе мы обсудили основные типы операций, которые мо-
         гут потребоваться от драйвера;  мы вплотную подошли  к  вопросам,
         связанным с разработкой драйверов.
            Обобщая все вышесказанное,  можно отметить следующее. Выполне-
         ние  основных  операций  ввода/вывода  всегда  производится через
         драйверы устройств.  Драйверы могут также  поддерживать  дополни-
         тельный канал ввода/вывода для управления устройством. Символьные
         драйверы могут передавать от 1 до 64 Kбайт за  одно  обращение  к
         драйверу.  Блоковые  драйверы  могут  передавать данные только по
         секторам,  т.к. преобразование секторов в записи и обратно выпол-
         няет  MS-DOS.  Как мы вкратце упоминали,  блоковые драйверы могут
         возвращать информацию об используемом ими в настоящий момент дис-
         ке.

                           Создание драйверов устройств

            Создание драйверов в любой операционной  системе  имеет  много
         преимуществ перед написанием обычных программ. Драйверы устройств
         должны следовать строго определенной структуре,  а если структура
         понятна, то остальное приложится.
            Базовая структура  драйвера  устройства  показана  на рис.6-3.
         Обязательно должны присутствовать три раздела драйвера -- ЗАГОЛО-
         ВОК ДРАЙВЕРА,  ПРОГРАММА СТРАТЕГИЙ и ПРОГРАММА ПРЕРЫВАНИЙ.  Прог-
         рамма ПРЕРЫВАНИЙ это не тоже самое,  что программа обработки пре-
         рываний,  которая  может присутствовать в качестве необязательной
         части работающего по прерываниям драйвера.  На самом деле,  прог-
         рамма ПРЕРЫВАНИЙ - это точка входа в драйвер для обработки  полу-
         чаемых от MS-DOS команд.


                               ┌─────────────────────────╖
                               │   Заголовок драйвера    ║
                               ├─────────────────────────╢
                               │ Область данных драйвера ║
                               ├─────────────────────────╢
                               │   Программа СТРАТЕГИЙ   ║
                               ├─────────────────────────╢
                               │         Вход в          ║
                               │   программу ПРЕРЫВАНИЙ  ║
                               ├─────────────────────────╢
                               │    Обработчик команд    ║
                               ├─────────────────────────╢
                               │   Программа обработки   ║
                               │       прерываний        ║
                               ├─────────────────────────╢
                               │ Процедура инициализации ║
                               ╘═════════════════════════╝

                     Рисунок 6-3. Структура драйвера в MS-DOS

                                      - 6-18 -
            В программе  6-1 представлен скелет драйвера устройства.  Хотя
         структура драйвера похожа на структуру .COM программы,  важно от-
         метить следующие отличия :

          1. Программа начинается с нулевого смещения, а не 100H.

          2. Образ программы начинается с директив определения  данных
             для заголовка драйвера.

          3. Программа  не  содержит  директивы  ASSUME  для стекового
             сегмента.

          4. Программа не содержит директивы END START.


         Листинг 6-1. Заголовок драйвера, программы СТРАТЕГИЙ и ПРЕРЫВАНИЙ
        ------------------------------------------------------------------

    DRIVER    SEGMENT PARA
              ASSUME  CS:DRIVER,DS:NOTHING,ES:NOTHING
              ORG     0
    START     EQU     $                       ; Начало драйвера
    ;
    ;******* ЗАГОЛОВОК ДРАЙВЕРА *******************************************
    ;
              dw      -1,-1            ; Указатель на следующий драйвер
              dw      ATTRIBUTE        ; Слово атрибутов
              dw      offset STRATEGY  ; Точка входа в программу STRATEGY
              dw      offset INTERRUPT ; Точка входа в программу INTERRUPT
              db      8 dup (?)        ; Количество устройств/поле имени
    ;
    ;******* РЕЗИДЕНТНАЯ ЧАСТЬ ДРАЙВЕРА ***********************************
    ;
    req_ptr   dd      ?                ; Указатель на заголовок запроса
       .
       .
       .
    ;
    ;******* ПРОГРАММА СТРАТЕГИИ ******************************************
    ;
    ; Сохранить адрес заголовка запроса для программы СТРАТЕГИЙ в REQ_PTR.
    ; На входе адрес заголовка запроса находится в регистрах ES:BX.
    ;
    STRATEGY  PROC    FAR
              mov     cs:word ptr [req_ptr],bx
              mov     cs:word ptr [req_ptr + 2],bx
              ret
    STRATEGY  ENDP
    ;
    ;******* ПРОГРАММА ПРЕРЫВАНИЙ *****************************************
    ;
    ; Обработать команду, находящуюся в заголовке запроса. Адрес заголовка
    ; запроса содержится в REQ_PTR в форме СМЕЩЕНИЕ:СЕГМЕНТ.
    ;
    INTERRUPT  PROC    FAR
              pusha                    ; Сохранить все регистры

                                      - 6-19 -
               lds     bx,cs:[req_ptr]  ; Получить адрес заголовка запроса
                .
                .
                .
     INTERRUPT  ENDP
                .
                .
                .
     DRIVER    ENDS
               END

     ---------------------------------------------------------------------

                                Заголовок драйвера

            Заголовок драйвера -- это блок данных длиной 18  байт, которым
         должен начинаться любой драйвер. Заголовок драйвера всегда должен
         располагаться начиная с нулевого смещения  в  сегменте  драйвера.
         При  загрузке драйвера MS-DOS считывает заголовок для того, чтобы
         определить тип драйвера и точки  входа  в  драйвер.  В  заголовке
         драйвера содержится четыре типа сведений, критичных для использо-
         вания драйвера системой :  ПОЛЕ СВЯЗИ,  СЛОВО АТРИБУТОВ,  ВЕКТОРА
         ТОЧЕК ВХОДА и ПОЛЕ ИМЕНИ/КОЛИЧЕСТВА.

                                    Поле связи

            Первые четыре байта заголовка драйвера это FAR указатель (сме-
         щение:сегмент) на следующий драйвер в цепочке (списке) драйверов.
         При создании драйвера эти байты устанавливаются равными FFFF:FFFF
         (-1).  При  загрузке  нового драйвера его адрес помещается в поле
         связи предыдущего драйвера.  Исключением являются файлы, содержа-
         щие  несколько  драйверов одновременно.  В этом случае первые два
         байта поля связи должны содержать смещение  заголовка  следующего
         драйвера.

                                  Слово атрибутов

            Следующее слово заголовка драйвера называется  словом  атрибу-
         тов.  Оно  содержит ряд однобитовых полей,  которые характеризуют
         тип и возможности драйвера.  На рис.6-4 показано  расположение  и
         значение  битов  в  слове  атрибутов.  Слово атрибутов для разных
         драйверов может иметь, например, следующее значение :

                Драйвер диска формата IBM - 0000H
                Стандартный драйвер консоли - 8003H
                Драйвер стандартного устройства (напр. PRN) - 8000H

         БИТ15: CHR.  Бит CHR должен быть сброшен в 0,  если драйвер пред-
         назначен  для  блоковых устройств,  и должен быть установлен в 1,
         если драйвер будет обслуживать символьное устройство  (см. раздел
         "Типы драйверов устройств").

         БИТ14: IOCTL.  Бит  IOCTL является необязательным.  Его установка
         информирует MS-DOS о том, что драйвер поддерживает средства кана-
         ла прямого управления. Если IOCTL бит установлен, то драйвер ОБЯ-
         ЗАН поддерживать команды 3 и 12 (IOCTL ввод и вывод), в противном

                                      - 6-20 -
         случае бит 14 должен быть сброшен. Указанные команды доступны при
         помощи подфункций 2 и 3 (для символьных устройств) или 4 и 5 (для
         блоковых устройств) функции MS-DOS 44H.
         БИТ13: NONIBM/OTB.  Для  блоковых  драйверов  этот бит называется
         также NONFAT бит. Будучи установленным, этот бит указывает на то,
         что блоковое устройство может не поддерживать стандартной для IBM
         /MS-DOS структуры диска. В этом случае обработка драйвером команд
         INIT и BUILD BPB будет происходить особым образом. Для символьных
         драйверов в MS-DOS версий 3.2 и более  поздних,  этот  бит  носит
         название  OTB  (Output  Until Busy) -- "Вывод пока не занято",  и
         указывает на то,  что драйвер поддерживает дополнительную команду
         9  (Output  Until Busy).  Эта команда полезна для символьных уст-
         ройств,  имеющих буфер большой емкости, таких как некоторые прин-
         теры. Символьные драйверы, используемые с MS-DOS версий 3.1 и ни-
         же, должны иметь этот бит сброшенным в 0.

         БИТ12: NETWORK. Этот бит является необязательным атрибутом, впер-
         вые определенный в MS-DOS версии 3.10. Интересно, что бит NETWORK
         не упоминался в последующей документации по MS-DOS версий 3.2 или
         3.3,  так что использование его в настоящее время оставляет неко-
         торые вопросы.  Это бит предназначен для информирования MS-DOS  о
         том, что драйвер обслуживает сетевое устройство. Сетевые устройс-
         тва помечаются как блоковые устройства  в  слове  атрибутов;  при
         этом делается допущение, что обслуживаемое сетевое устройство яв-
         ляется "окном" в сеть,  позволяя, таким образом, целиком перенап-
         равлять на обработку удаленному устройству системные вызовы.  Ко-
         нечно,  для  того,  чтобы  воспользоваться  услугами   сети   для
         указанного перенаправления, необходима поддержка соответствующего
         средства, такого как MS-NET.

         БИТ11: OCRM. Атрибут OCRM (Open/Close/Removable Media) появляется
         начиная с MS-DOS версии 3.0. Он может использоваться как для сим-
         вольных,  так и для блоковых драйверов. Этот бит является не обя-
         зательным,  хотя Microsoft рекомендует устанавливать его для всех
         новых драйверов. Поняв назначение этого атрибута, программист не-
         сомненно сможет определиться в его использовании (или не  исполь-
         зовании).

            Как для символьных,  так и для  блоковых  драйверов  установка
         этого  бита  означает  поддержку  драйвером  команд DEVICE OPEN и
         DEVICE CLOSE (команды 13 "Открыть устройство" и 14  "Закрыть  уст-
         ройство").  Блоковые  драйверы  с установленным битом OCRM должны
         также поддерживать команду CHECK FOR REMOVABLE MEDIA (команда 15,
         "Проверка замены носителя").
            Для блоковых устройств команды DEVICE OPEN и DEVICE CLOSE  вы-
         даются  только  в  режиме  совместного использования файлов (file
         sharing).  Этот режим включается после запуска команды SHARE.EXE.
         При установленном режиме совместного использования файлов, коман-
         да DEVICE OPEN выдается драйверу при вызове  функций  MS-DOS  0FH
         ("Открыть файл, используя FCB") или 3DH ("Открыть файл при помощи
         вызова  функций 10H ("Закрыть файл, используя FCB") или 3H ("Зак-
         рыть файл при помощи описателя").  Для  дисковых устройств коман-
         ды  DEVICE  OPEN  и  DEVICE CLOSE можно использовать для подсчета
         числа открытий определенного устройства, например, числа открытых
         файлов на диске.  Это может быть полезно при определении недопус-
         тимости смены дискеты в дисководе, если на момент замены носителя

                                      - 6-21 -
         оставались открытые файлы.
            Для символьных устройств команды DEVICE OPEN  и  DEVICE  CLOSE
         выдаются  всегда,  когда соответствующее устройство открывается и
         закрывается,  независимо от режима совместного использования фай-
         лов,  так что загрузка команды SHARE.EXE не требуется. При работе
         с устройствами могут быть использованы только функции  MS-DOS 3DH
         ("Открыть файл при помощи описателя") и 3H ("Закрыть файл при по-
         мощи описателя"),  так как FCB-метод не работает с  устройствами.
         Для символьных устройств команды DEVICE OPEN и DEVICE CLOSE могут
         быть использованы для предотвращения одновременного доступа к та-
         ким устройствам, как принтер или модем, а также для вызова проце-
         дур пред- и после обработки,  таких как процедуры настройки прин-
         тера или завершение сеанса связи для модема.
            Заметим, что устройства CON,  AUX и PRN открыты всегда, так как
         связаны с описателями 0,  1,  и 2 (STDIN,  STDOUT и STDERR --  все
         отображаются на устройство CON),  описателем 3 (STDAUX, отображае-
         мый на устройство AUX) и описателем  4  (STDPRN,  отображаемый  на
         устройство PRN).
            Команда CHECK  FOR REMOVABLE MEDIA выдается при вызове пользо-
         вателем функции MS-DOS 44H  ("Управление  работой  устройств")  с
         подкомандой номер 8.  Драйвер должен вернуть информацию о наличии
         сменного либо несменного носителя.
            Атрибут OCRM  (Open/Close/Removable  Media)  также учитывается
         при обработке драйвером команды BUILD BPB ("Построить блок  пара-
         метров  BIOS").  Сменный  носитель может содержать "идентификатор
         тома", одиннадцатисимвольное имя диска. Если устройство поддержи-
         вает сменный носитель,  имя тома должно быть определено и обрабо-
         тано драйвером. Подробнее об этом можно найти при описании коман-
         ды BUILD BIOS PARAMETER BLOCK.

         БИТЫ с 10 по 7 : Зарезервированы.
         ---------------------------------
         БИТ6: GIOCTL.  В MS-DOS версии 3.3 бит GIOCTL ("Группа команд уп-
         равления") устанавливается в 1 для индикации того,  что  блоковый
         или символьный драйвер поддерживает дополнительные подкоманды ко-
         мандой 19 (GENERIC I/O CONTROL REQUEST).  Если этот бит разрешает
         использование  команды 19,  драйвер должен также поддерживать ко-
         манды 23 и 24 (GET/SET LOGICAL DRIVE --  Получить/Установить  имя
         логического диска).
            При поддержке драйвером указанных команд,  программа пользова-
         теля  может  выдать команду GENERIC I/O CONTROL REQUEST с помощью
         функции 44H MS-DOS (подфункции 0CH и 0DH). Для блоковых драйверов
         команды  GET/SET  LOGICAL DRIVE могут быть выполнены вызовом под-
         функций 0H (GET LOGICAL DRIVE) и 0FH (SET LOGICAL  DRIVE) функции
         MS-DOS 44H. Для получения более подробной информации обратитесь к
         описанию функции 44H и описанию команд драйвера GENERIC  IOCTL  и
         GET/SET LOGICAL DRIVE.

         БИТ 5 : Зарезервирован.
         -----------------------

         БИТ4: SPECL. Бит SPECL является необязательным атрибутом, исполь-
         зуемым  только драйвером консоли,  и информирующим MS -DOS о том,
         что драйвер установил специальный обработчик INT 29H для выполне-
         ния  высокоскоростного  вывода на консоль (устройство CON).  Если
         этот бит установлен, то при необходимости быстрого вывода на кон-

                                      - 6-22 -
         соль MS-DOS выдает программное прерывание INT 29H,  передавая вы-
         водимый символ в регистр AL.  Режим быстрого вывода управляется и
         индицируется  битом 5 (режим двоичного вывода) в слове конфигура-
         ции. При выдаче прерывания INT 29H ожидается, что драйвер выведет
         переданный в регистре AL символ и вернет управление. Обычные про-
         цедуры ввода/вывода пропускаются. Как стандартный драйвер консоли
         MS-DOS,  так  и  заменяющий его драйвер ANSI.SYS поддерживают эту
         особенность.  Если используемый драйвер консоли поддерживает пре-
         рывание  INT  29H  (что  определяется  чтением слова конфигурации
         драйвера), то прикладная программа также может осуществлять быст-
         рый вывод на консоль, используя INT 29H.
            Заметим, что этот бит объявлен резервным в документации  IBM и
         вообще игнорируется в последней документации фирмы Microsoft. Оба
         этих факта говорят о том,  что поддержка бита SPECL в будущем  не
         гарантируется.

         БИТ3: CLOCK.  Бит  CLOCK  устанавливается на драйвере символьного
         устройства, имеющего имя "CLOCK$", для обозначения этого устройс-
         тва,  как устройства системного времени. Так как драйвер устройс-
         тва "Часы" практически всегда обеспечивается системой,  необходи-
         мость использования этого бита возникает довольно редко.
            Драйвер устройства "Часы" обычно  является  обычным  драйвером
         символьного  устройства  без  каких-либо дополнительных атрибутов
         (слово атрибутов 8008H).  Время считывается командой INPUT (ввод)
         и  устанавливается командой OUTPUT (вывод).  По любой из этих ко-
         манд всегда передается ровно 6 байт, имеющих следующее значение :

                # БАЙТА         РАЗМЕР        ЗНАЧЕНИЕ

                0, 1            16 бит        Количество дней с 1.1.1980г.
                2               8 бит         Минуты
                3               8 бит         Часы
                4               8 бит         Сотые доли секунды
                5               8 бит         Секунды

         БИТ2: NUL.  Бит NUL означает, что данный драйвер является драйве-
         ром устройства NUL. В связи с тем,  что NUL-драйвер не может быть
         заменен,  нет  никакой необходимости создавать драйвер устройства
         NUL.

         БИТЫ1и0: STDIN и STDOUT. Биты STDIN и STDOUT означают, что данный
         драйвер является соответственно драйвером стандартного устройства
         ввода и вывода. Для устройства CON, обслуживающего системную кла-
         виатуру и монитор, эти биты почти всегда определяются вместе. Ес-
         ли устанавливается новый драйвер консоли (такой как ANSI.SYS) для
         того,  чтобы добавить какие-либо новые возможности,  то оба  этих
         бита  должны  быть установленными.  Атрибуты STDIN и STDOUT могут
         быть установлены только на одном драйвере из всех активных  (дру-
         гие копии CON-драйвера тоже могут иметь эти атрибуты,  однако ак-
         тивным будет только последний установленный CON-драйвер).

                Вектора точек входа программ СТРАТЕГИЙ и ПРЕРЫВАНИЙ

            Следующие два слова заголовка драйвера содержат смещения прог-
         рамм  СТРАТЕГИЙ и ПРЕРЫВАНИЙ,  соответственно.  MS-DOS использует
         эту информацию совместно с сегментным адресом драйвера для  опре-
         деления точек входа в указанные программы. Сегментный адрес драй-
         вера система, конечно же, узнает при его загрузке.


                                      - 6-23 -
                          Поле имени/количества устройств

            Последние восемь байт заголовка драйвера  служат  двум  целям.
         Для  символьных драйверов это поле содержит ASCII имя устройства,
         дополненных справа пробелами. Например, для драйвера принтера это
         поле может содержать строку 'PRN '.
            Для блоковых устройств имеет значение только первый  байт.  Он
         показывает MS-DOS сколько отдельных устройств поддерживается дан-
         ным драйвером.  Такая возможность необходима потому,  что  многие
         контроллеры  поддерживают более одного физического дисковода. Так
         как остальные семь байт поля в этом случае не  используются,  там
         можно хранить имя устройства для поиска драйвера в памяти или для
         идентификации драйвера. Например, поле количества устройств драй-
         вера RAM-диска,  называемого RDISK (см. листинг 6-10), может быть
         определено как :

                UNIT_FIELD      DB       1, 'RDISK   '

                                Программа СТРАТЕГИЙ

            Следующий раздел  драйвера устройства  - это программа СТРАТЕ-
         ГИЙ.  В листинге 6-1 она занимает только три  строки  выполняемых
         кодов.  Единственное назначение программы СТРАТЕГИЙ заключается в
         сохранении адреса блока запроса для последующего его  использова-
         ния программой ПРЕРЫВАНИЙ.
            Что представляет собой блок запроса? Листинг  6-2 представляет
         структуру заголовка запроса. С него начинается любой блок запроса
         ввода/вывода к драйверу.  Для блока запроса может  иногда  требо-
         ваться  больше информации,  чем содержится в в заголовке запроса,
         поэтому заголовок содержит поле  длины  информации.  К  заголовку
         запроса мы еще вернемся,  а сейчас продолжим обсуждение программы
         СТРАТЕГИЙ.
                     Листинг 6-2. Структура заголовка запроса
         -----------------------------------------------------------------

         request     equ        ds:[bx]     ; базовый адрес заголовка
         reqhdr      struc                  ;       запроса.
         length      db         ?           ; длина блока запроса (байт).
         unit        db         ?           ; количество устройств.
         command     db         ?           ; код команды для драйвера.
         status      dw         ?           ; возвращаемое состояние.
                     db         8 dup (?)   ; резерв.
         reqhdr      ends

         -----------------------------------------------------------------

            Причина того,  что  программа СТРАТЕГИЙ обязана сохранять адрес
         заголовка запроса заключается в  том,  что  MS-DOS  выполняет  не
         единственное обращение к драйверу для выполнения определенной ко-
         манды. На самом деле,  система сначала делает предварительное об-
         ращение к драйверу для того,  чтобы информировать драйвер о  том,
         что  он должен сделать и затем делает повторное обращение для вы-
         полнения требуемых действий.
            Такое двухэтапное обращение к драйверу имеет смысл  при работе
         в MS-DOS какой-либо многозадачной системы.  В этом случае запросы
         к драйверу от разных задач могут выдаваться в любой момент време-
         ни.  Путем  выделения  в  драйвере самостоятельных частей анализа

                                      - 6-24 -
         запроса и выполнения запроса драйвер  может  принимать  множество
         запросов, одновременно удовлетворяя полученный ранее запрос.
            MS-DOS передает  программе СТРАТЕГИЙ адрес блока запроса в ре-
         гистрах ES:BX. Хотя программа СТРАТЕГИЙ должна сохранять сам блок
         запроса, большинство драйверов ограничивается сохранением его ад-
         реса. Это возможно из-за того, что MS-DOS в настоящее время вызы-
         вает программу ПРЕРЫВАНИЙ непосредственно после возврата управле-
         ния  от  программы  СТРАТЕГИЙ,  не  изменяя  информации  в  блоке
         запроса.  Следующий  пример демонстрирует фрагмент кода,  который
         сохраняет блок запроса, используя описанную методику :

                mov      cs:word ptr [req_ptr],bx
                mov      cs:word ptr [req_ptr + 2],es

            Однако, как  только  MS-DOS  станет многозадачной,  сохранение
         только указателя на блок запроса будет уже  недопустимо.  В  этом
         случае  программа  СТРАТЕГИЙ должна будет не только сохранять сам
         блок запроса но и,  возможно,  помещать блоки запросов в  очередь
         (если,  конечно, эту функцию не возьмет на себя MS-DOS). Впрочем,
         до тех пока этого не случилось, мы можем пользоваться более прос-
         тым способом сохранения адреса блока.
            Как программа ПРЕРЫВАНИЙ так и программа СТРАТЕГИЙ должны быть
         определены  в  драйвере  как FAR процедуры,  возвращая управление
         MS-DOS, соответственно,  командой RETF. В связи с тем, что MS-DOS
         вызывает  эти подпрограммы с помощью команды CALL типа FAR, любая
         иная команда возврата приведет либо к передаче управления по  не-
         верному адресу (RETN) либо к порче стека (IRET).

                               Программа ПРЕРЫВАНИЙ

            После того,  как  программа  СТРАТЕГИЙ  сохраняет указатель на
         блок запроса и возвращает управление,  MS-DOS вызывает  программу
         ПРЕРЫВАНИЙ  (называемую также точкой входа запроса в документации
         фирмы IBM по PC DOS). Собственно запрос к драйверу обрабатывается
         именно этой программой.
            Самое первое действие, которое должна выполнить программа ПРЕ-
         РЫВАНИЙ - это сохранить все регистры. На момент обращения к драй-
         веру устройства стек имеет емкость примерно в 20 слов. Сохранение
         всех регистров,  включая флаги,  требует 14 слов.  Если программе
         ПРЕРЫВАНИЙ требуется для работы более чем 6 слов стека, она долж-
         на установить свой собственный локальный стек.
            После сохранения текущего состояния процессора, программа ПРЕ-
         РЫВАНИЙ  должна  получить  блок  запроса,  сохраненный программой
         СТРАТЕГИЙ.  Если адрес этого блока был сохранен с помощью  приве-
         денных выше команд,  то получить адрес блока параметров можно ко-
         мандой LDS

               lds        bx,cs:[req_ptr]  ; получить адрес блока запроса

            Теперь, получив доступ к заголовку блока запроса,  можно начи-
         нать его обработку.  Первый шаг заключается  в  анализе  запроса.
         Доступ  к  нужным полям блока запроса будет значительно облегчен,
         если описана структура заголовка. Структура, которую мы использу-
         ем  в драйвере RDISK и которая определяет формат заголовка запро-
         са, показана в листинге 6-2.
            Если драйвер должен обслуживать блоковое устройство, то первый
         элемент заголовка запроса, который должен быть проверен, это поле

                                      - 6-25 -
         количества устройств (request.unit).  После проверки корректности
         поля request.unit,  программа ПРЕРЫВАНИЙ должна получить из блока
         запроса код команды (request.command),  которую требуется  выпол-
         нить.  Символьные драйверы могут обращаться сразу к коду команды,
         т.к.  каждый символьный драйвер поддерживает только одно устройс-
         тво.
            Определив код команды,  программа ПРЕРЫВАНИЙ  должна  передать
         управление соответствующему обработчику. В листинге 6-3, содержа-
         щем пример программы ПРЕРЫВАНИЙ, показан один из способов переда-
         чи управления требуемому обработчику, основанный на использовании
         таблицы переходов.  Таблица переходов представляет собой последо-
         вательность  смещений программ-обработчиков команд.  Для передачи
         управления определенному обработчику  необходимо  указать  индекс
         требуемой подпрограммы,  заданной своим смещением в таблице пере-
         ходов. Этот индекс (в нашем случае это код команды) преобразуется
         в  смещение  в  таблице,  после  чего выполняется косвенный вызов
         подпрограммы или переход на нее через таблицу переходов

                call       word ptr cs:jumptab[bx]  ; обработать команду

            В связи с тем,  что индекс (т.е. код команды) может быть боль-
         ше, чем максимальный из используемых кодов команд, программа ПРЕ-
         РЫВАНИЙ должна выполнять проверку индекса для  того,  чтобы  убе-
         диться в его правильности.  При этом,  вместо сравнения индекса с
         каким-либо заранее фиксированным значением,  программа ПРЕРЫВАНИЙ
         сравнивает код команды с максимально допустимым значением, храня-
         щимся в поле max_cmd :

                cmp        bl,[max_cmd]             ; команда допустима ?

            Для того, чтобы понять пользу хранения максимально допустимого
         значения  в  памяти,  взгляните  на  таблицу 6-2.  В этой таблице
         представлены команды,  поддерживаемые различными версиями MS-DOS.
         Максимальный  код  команды,  обеспечиваемый MS-DOS версий до 3.0,
         имеет значение 0CH. Однако, учитывая тот факт, что max_cmd распо-
         лагается  в памяти,  драйвер может модифицировать это значение во
         время инициализации,  позволяя, таким образом, использовать новые
         команды,  если  драйвер  загружен  под  управлением  новой версии
         MS-DOS.

















         
                                      - 6-26 -
                                                         Таблица 6-2
                         Команды для драйверов устройств

    ────────┬───────┬─────────┬─────────┬─────────┬──────────────────────
    Команда │ Версия│ Блоковые│ Симв-ные│ Атрибут │ Название команды
            │  DOS  │  устр-ва│  устр-ва│         │
    ────────┼───────┼─────────┼─────────┼─────────┼───────────────────────
      0:    │  2.0  │    +    │    +    │         │ INIT
      1:    │  2.0  │    +    │    -    │         │ MEDIA CHECK
      2:    │  2.0  │    +    │    -    │         │ BUILD BPB
      3:    │  2.0  │    +    │    +    │ 14:IOCTL│ INPUT IOCTL
      4:    │  2.0  │    +    │    +    │         │ INPUT
      5:    │  2.0  │    -    │    +    │         │ Nondestructive INPUT
      6:    │  2.0  │    -    │    +    │         │ INPUT STATUS
      7:    │  2.0  │    -    │    +    │         │ INPUT FLUSH
      8:    │  2.0  │    +    │    +    │         │ OUTPUT
      9:    │  2.0  │    +    │    +    │         │ OUTPUT with VERIFY
     10:    │  2.0  │    -    │    +    │         │ OUTPUT STATUS
     11:    │  2.0  │    -    │    +    │         │ OUTPUT FLUSH
     12:    │  2.0  │    +    │    +    │ 14:IOCTL│ OUTPUT IOCTL
     13:    │  3.0  │    +    │    +    │ 11:OCRM │ DEVICE OPEN
     14:    │  3.0  │    +    │    +    │ 11:OCRM │ DEVICE CLOSE
     15:    │  3.0  │    +    │    -    │ 11:OCRM │ REMOVABLE MEDIA
     16:    │  3.1  │    -    │    +    │ 13:OTB  │ OUTPUT until busy
     19:    │  3.2  │    +    │    +    │ 6:GIOCTL│ Generic IOCTL Request
     23:    │  3.2  │    +    │    -    │ 6:GIOCTL│ Get Logical Device
     24:    │  3.2  │    +    │    -    │ 6:GIOCTL│ Set Logical Device
     ───────┴───────┴─────────┴─────────┴─────────┴───────────────────────
      Примечание 1. В колонке "Версия DOS" указана самая ранняя версия
                    MS-DOS, начиная с которой поддерживается эта команда.
      Примечание 2. В колонке "Атрибут" указаны бит слова атрибутов
                    драйвера, разрешающий использование данной команды.
     ═════════════════════════════════════════════════════════════════════

            Последняя задача программы ПРЕРЫВАНИЙ после обработки  требуе-
         мой команды заключается в установке статуса возврата в блоке зап-
         роса.  В листинге 6-3 ожидается,  что обработчик  каждой  команды
         возвращает статус завершения в регистре AX. После возврата управ-
         ления от обработчика,  программа ПРЕРЫВАНИЙ записывает  статус  в
         поле слова состояния заголовка запроса (поле request.status). За-
         тем программа ПРЕРЫВАНИЙ устанавливает бит DONE  ("выполнено")  в
         слове  состояния и возвращает управление MS-DOS.  Так как возврат
         управления должен быть выполнен командой RETF, программа ПРЕРЫВА-
         НИЙ определяется как процедура типа FAR.

                     Листинг 6-3. Пример программы ПРЕРЫВАНИЙ.
         -----------------------------------------------------------------
     ;
     ; Определение битов слова состояния драйвера устройства
     ;
     ST_ERROR    equ        1000000000000000b       ; была ошибка
     ST_BUSY     equ        0000001000000000b       ; устройство занято
     ST_DONE     equ        0000000100000000b       ; команда выполнена
     ;
     ; Определение кодов ошибки при обработке команд
     ;
     WRITE_PROTECT          equ      0              ; защита от записи
     UNKNOWN_UNIT           equ      1              ; неопознано устройство
     NOT_READY              equ      2              ; устройство не готово

                                      - 6-27 -
     UNKNOWN_COMMAND        equ      3              ; команда не опознана
            .
            .
     ;
     ;********** Точка входа в программу ПРЕРЫВАНИЙ ************************
     ;
     INTERRUPT    proc       far
                 pusha                              ; сохраним все рабочие
                 push       ds                      ;   регистры
                 push       es
                 push       cs                      ; установим локальный
                 pop        ds                      ;   сегмент данных
                 les        di,[req_ptr]            ; получим адрес блока
                 mov        bl,es:[di].command      ;   запроса и код
                                                    ;   команды
     ;
     ; Установим заранее флаг ошибки (на случай, если команда будет
     ;   неопознана)
     ;
                 mov        ax,(ST_ERROR or UNKNOWN_COMMAND)
                 cmp        bl,[max_cmd]             ; эта команда
                 ja         exit                     ;   поддерживается ?
     ;
     ; Передадим управление соответствующему обработчику.  На входе каждый
     ; обработчик получает регистры CS и DS установленными на  сегмент
     ; DRIVER  и регистры ES:DI указывающими на блок запроса.  Свой статус
     ; обработчик должен вернуть в регистре AX.
     ;
                 xor        bh,bh                    ; превратим команду
                 shl        bx,1                     ;   в индекс
                 call       word ptr cs:jumptab[bx]  ; обработаем команду
     ;
     ; Запишем статус в слово состояния блока запроса
     ;
     exit:       push       cs
                 pop        ds
                 les        di,[req_ptr]            ; получим адрес блока
                 or         ax,ST_DONE              ; запроса, установим
                 mov        es:[di].status,ax       ; бит DONE и сохраним
                 pop        es                      ; статус
                 pop        ds                      ; восстановим контекст
                 popa
                 ret                                ; RETF
     INTERRUPT    endp
            .
            .
            .
     ;
     ;********** Таблица переходов на обработку команд *******************
     ;
     JUMPTAB     label      word
                 dw         offset INIT            ; 0 - Инициализация
                 dw         offset MEDIA_CHECK     ; 1 - Проверка носителя
                 dw         offset BUILD_BPB       ; 2 - Построить BPB
                  .
                  .
                  .

                                      - 6-28 -
                 dw         offset NO_COMMAND      ; 16
                 dw         offset GET_LOGICAL     ; 17 - Получить имя ЛУ
                 dw         offset SET_LOGICAL     ; 18 - Установить ЛУ
                  .
                  .
                  .

     ---------------------------------------------------------------------

            Слово состояния, показанное на рис.6-5, используется для инди-
         кации ошибок,  случившихся при выполнении какой-либо команды (бит
         ERROR -- ошибка) и для отображения состояния устройства по коман-
         дам  опроса  статуса и проверки смены носителя (бит BUSY -- заня-
         то).

        15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
       ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───╖
       │ E │   │   │   │   │   │ B │ D │   │   │   │   │   │   │   │   ║
       │ R │   │   │   │   │   │ U │ O │   │   │  КОД  ОШИБКИ  │   │   ║
       │ R │  ЗАРЕЗЕРВИРОВАНО  │ S │ N │   │  ЕСЛИ БИТ 15 РАВЕН 1  │   ║
       │ O │   │   │   │   │   │ Y │ E │   │   │   │   │   │   │   │   ║
       │ R │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   ║
       ╘═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╧═══╝
         Значение битов :

            ERROR = 1 : При обработке команды случилась ошибка.
                        Код ошибки находится в битах с 0 по 7.
            BUSY  = 1 : Устанавливается командами опроса состояния
                        и проверки смены носителя.
            DONE  = 1 : Команда выполнена. Устанавливается на выходе.
                 Рисунок 6-5. Слово состояния драйвера устройства

            Бит ERROR устанавливается, если возникла ошибка при выполнении
         какой-либо  команды  или  если  команда является недопустимой для
         данного драйвера.  При установленном бите ошибки  драйвер  обязан
         поместить соответствующий код ошибки в биты с 0 по 7 слова состо-
         яния.  Возможные ошибки и их коды перечислены в таблице 6-3.  Бит
         DONE  должен всегда устанавливаться драйвером перед возвратом уп-
         равления к MS-DOS.
                                                         Таблица 6-3
                          Коды ошибок драйверов устройств
         ────────────────────────────────────┬────────────────────────────
          Код          Ошибка                │Код        Ошибка
         ────────────────────────────────────┼────────────────────────────
           0  Запись на устройство запрещена │ 8  Сектор не обнаружен
           1  Неопознанное устройство        │ 9  Нет бумаги в принтере
           2  Устройство не готово           │ A  Ошибка при записи
           3  Команда не опознана            │ B  Ошибка при чтении
           4  Неверно переданы данные        │ C  Общая ошибка
           5  Неверна длина заголовка запроса│ D  Зарезервировано
           6  Ошибка при установке головки   │ E  Зарезервировано
           7  Неопознанный носитель данных   │ F  Недопустимая смена диска
         ────────────────────────────────────┴────────────────────────────
          Примечание 1.  Все коды ошибок представлены в виде  шестнадцати-
                         ричных значений.
          Примечание 2.  Код ошибки 0FH поддерживается только в MS-DOS вер-
                         сии 3.0 и более поздних.

                                      - 6-29 -

                            Команды драйверов устройств

            Заголовок запроса,  как правило,  содержит не всю  информацию,
         которая требуется для большинства команд. Команд, которые не тре-
         буют дополнительной информации,  довольно  мало  --  это  команды
         INPUN/OUTPUT STATUS , FLUSH OUTPUT, OPEN/CLOSE DEVICE и REMOVABLE
         MEDIA.  Все остальные команды требуют гораздо больше  информации,
         чем  содержится в заголовке запроса.  Для каждой из этих команд к
         заголовку запроса  добавляется  дополнительная  информация.  Поле
         request.length  заголовка  запроса содержит при этом общий размер
         блока запроса (в байтах).
            Для облегчения  доступа  к  различным элементам блока запроса,
         опять-таки,  могут быть использованы структуры.  В листинге  6-10
         (листинге  драйвера  RDISK,  приведенного в конце главы) показано
         определение структур для тех команд,  которые обрабатываются этим
         драйвером.  Заметьте, что нам не нужно определять все поля в каж-
         дом блоке,  т.к. различные запросы часто используют похожие блоки
         запросов. Это обстоятельство довольно удобно, т.к. MASM не позво-
         ляет использовать одно и тоже имя более  одного  раза,  даже  для
         различных структур.

                                   Команда INIT
      ╔══════════════════════════════════════════════════════════════════╗
      ║                                                                  ║
      ║  Команда INIT (0)                                                ║
      ║                                                                  ║
      ║  ────────────────────────────────         ┌───┐                  ║
      ║  +00 : 23                   Длина         │ X │ Блок. драйверы   ║
      ║  ────────────────────────────────         └───┘                  ║
      ║  +01 :                 Устройство         ┌───┐                  ║
      ║  ────────────────────────────────         │ X │ Симв. драйверы   ║
      ║  +02 : 00                 Команда         └───┘                  ║
      ║  ────────────────────────────────                                ║
      ║  +03 :                     Статус                                ║
      ║  ────────────────────────────────          ЧТЕНИЕ      ЗАПИСЬ    ║
      ║                   Зарезервировано                                ║
      ║  ───────────────────────────────────────── ┌───┐ ───── ┌───┐ ─── ║
      ║  +13 :       Количество устройств          │   │       │ X │     ║
      ║  ───────────────────────────────────────── ├───┤ ───── ├───┤ ─── ║
      ║  +14 :                Адрес конца          │   │       │ X │     ║
      ║  ───────────────────────────────────────── ├───┤ ───── ├───┤ ─── ║
      ║  +18 :          Команда/Адрес BPB          │ X │       │ X │     ║
      ║  ───────────────────────────────────────── ├───┤ ───── ├───┤ ─── ║
      ║  +22 :           Номер устройства          │ X │       │   │     ║
      ║  ───────────────────────────────────────── └───┘ ───── └───┘ ─── ║
      ║                                                                  ║
      ║  Адрес таблицы BPB возвращается только блоковыми драйверами.     ║
      ║  Номер устройства поддерживается начиная с DOS 3.10.             ║
      ║                                                                  ║
      ╚══════════════════════════════════════════════════════════════════╝

            Команда INIT  (инициализация) всегда является самой первой вы-
         зываемой командой и обрабатывается на этапе  установки  драйвера.

                                      - 6-30 -
         MS-DOS  выдает  эту команду для каждого драйвера только один раз.
         На  команду  INIT  возложена  ответственность  за  информирование
         MS-DOS об особых характеристиках драйвера и за выполнение необхо-
         димых действий по инициализации драйвера.  Последние  зависят  от
         типа устройства,  управляемого драйвером.  Возвращаемые драйвером
         характеристики также зависят от типа драйвера.
            Все драйверы  должны возвращать адрес последнего байта памяти,
         занимаемой драйвером и количество устройств,  управляемых драйве-
         ром.  Драйверы  символьных  устройств могут поддерживать не более
         одного устройства. Блоковые драйверы могут поддерживать несколько
         устройств (например, если в одном устройстве содержится несколько
         дисководов).  Кроме того,  драйвер может вернуть ноль в  качестве
         параметра  количества  поддерживаемых устройств,  для прекращения
         процесса инициализации.  Это может потребоваться,  к примеру, при
         обнаружении отсутствия устройства.  В такой ситуации драйвер дол-
         жен также установить адрес последнего используемого  байта равным
         CS:0 (текущий кодовый сегмент,  нулевое смещение) для того, чтобы
         MS-DOS могла использовать всю занимаемую драйвером память. В нор-
         мальной  ситуации адрес завершения представляет собой адрес (сег-
         мент и смещение) первого свободного после драйвера  байта памяти.
         MS-DOS  продолжает  загрузку  системы начиная со следующего после
         адреса завершения параграфа памяти (или начиная с адреса заверше-
         ния, если он приходится на границу параграфа).
            Третий параметр, определяемый командой INIT - это адрес табли-
         цы BPB. Этот указатель, возвращаемый MS-DOS командой INIT, предс-
         тавляет собой адрес таблицы,  которая сама представляет собой со-
         вокупность  указателей  на  блоки  параметров  BIOS.  Таблица BPB
         содержит по одному указателю на каждое устройство, поддерживаемое
         драйвером. Блок параметров BIOS (или, короче, BPB) это структура,
         которая определяет формат блокового устройства (см. рис.6-6). Так
         как  этот параметр имеет смысл только для блоковых устройств,  он
         не возвращается символьными  драйверами.  Однако  поле  указателя
         таблицы BPB в блоке запроса несет еще одну полезную нагрузку, ко-
         торая может быть использована обоими типами драйверов -  это поле
         содержит адрес командной строки драйвера. У нас еще будет возмож-
         ность подробнее обсудить назначение этого поля.
               СМЕЩЕНИЕ                СОДЕРЖАНИЕ                  РАЗМЕР
                (hex)
                        ┌───────────────────────────────────────╖
                  +0    │        Размер сектора в байтах        ║   Слово
                        ├───────────────────────────────────────╢
                  +2    │    Количество секторов в кластере     ║   Байт
                        ├───────────────────────────────────────╢
                  +3    │ Количество зарезервированных секторов ║   Слово
                        ├───────────────────────────────────────╢
                  +5    │         Количество таблиц FAT         ║   Байт
                        ├───────────────────────────────────────╢
                  +6    │    Количество элементов директория    ║   Слово
                        ├───────────────────────────────────────╢
                  +8    │    Количество логических секторов     ║   Слово
                        ├───────────────────────────────────────╢
                  +A    │          Описатель  носителя          ║   Байт
                        ├───────────────────────────────────────╢
                  +B    │    Количество секторов в одной FAT    ║   Слово
                        ╘═══════════════════════════════════════╝
                        Рисунок 6-6. Блок параметров BIOS

                                      - 6-31 -
            Последний параметр, уникальный для команды INIT, это номер ус-
         тройства.  Этот параметр,  который поддерживается только в MS-DOS
         версии 3.10 и более поздних,  используется для задания начального
         номера устройства.  К примеру, если драйвер должен управлять дис-
         ками C:  и D:,  содержимое этого поля будет равно 2 и  количество
         устройств будет равным двум. Если драйвер должен управлять только
         дисководом A:,  то номер устройства будет равен 0,  а  количество
         устройств 1. Эта возможность очень важна, так как она позволяет в
         конце концов заменять стандартные блоковые драйверы  на драйверы,
         устанавливаемые пользователем.
            Команда INIT является уникальной, так как из всех команд драй-
         вера,  она выполняется в среде, близкой к той, в которой выполня-
         ются  обычные программы.  В отличие от остальных команд,  команда
         INIT может использовать функции MS-DOS с 01H по 0CH и  30H.  Ука-
         занные функции позволяют драйверу выдать идентифицирующее сообще-
         ние во время установки и, если нужно, отобразить состояние конфи-
         гурации  драйвера.  Функция 30H ("Получить версию DOS") позволяет
         драйверу настроиться на определенную версию MS-DOS, что дает воз-
         можность разработчику писать драйверы, работающие с любой версией
         операционной системы.
            Другое сходство команды INIT  с  обычными  программами  MS-DOS
         заключается  в  том,  что  INIT  может прочитать командную строку
         драйвера и использовать ее для  конфигурации  драйвера.  Как  уже
         указывалось,  команда  DEVICE  в файле CONFIG.SYS имеет следующий
         формат :

                DEVICE=[d:][path]filename[.ext][ parameters]

            При обращении к драйверу с командой INIT  драйверу  передается
         адрес буфера,  содержащего текст командной строки. Этот адрес пе-
         редается в поле указателя таблицы BPB блока заголовка и указывает
         на первый после знака "=" символ командной строки.  Для получения
         необходимой информации процедура инициализации должна просмотреть
         командную строку,  пропустив спецификации файла, и обработать пе-
         реданные параметры.  Однако,  в отличие от стандартных  программ,
         команде INIT передается только адрес командной строки,  а не сама
         строка. Командную строку при этом можно только читать (и ни в ко-
         ем  случае не модифицировать).  Для блоковых драйверов это адрес,
         конечно же, должен будет перекрыт адресом таблицы BPB.
            MS-DOS обращается к драйверу с командой INIT  только  единожды
         во  время  загрузки системы,  поэтому код,  реализующий обработку
         этой команды после завершения последней,  будет бесполезно  зани-
         мать память.  Для того, чтобы минимизировать использование памяти
         драйвером,  можно располагать код команды INIT после предполагае-
         мого адреса завершения или отводить место,  занимаемое процедурой
         инициализации, для внутренних буферов драйвера (драйвер RDISK ис-
         пользует пространство, занимаемое командой INIT, как часть буфера
         памяти).  В  любом  случае  память будет заново использована либо
         MS-DOS либо драйвером.  Все остальные процедуры,  реализующие ос-
         тальные команды, должны располагаться до адреса завершения.


                                      - 6-32 -
                                Команда MEDIA CHECK
                               ~~~~~~~~~~~~~~~~~~~~~
       ╔══════════════════════════════════════════════════════════════════╗
       ║                                                                  ║
       ║ Команда MEDIA CHECK (1)                                          ║
       ║                                                                  ║
       ║ ────────────────────────────────         ┌───┐                   ║
       ║ +00 : 19                   Длина         │ X │ Блок. драйверы    ║
       ║ ────────────────────────────────         └───┘                   ║
       ║ +01 : номер           Устройство         ┌───┐                   ║
       ║ ────────────────────────────────         │   │ Симв. драйверы    ║
       ║ +02 : 01                 Команда         └───┘                   ║
       ║ ────────────────────────────────                                 ║
       ║ +03 :                     Статус                                 ║
       ║ ────────────────────────────────          ЧТЕНИЕ      ЗАПИСЬ     ║
       ║                  Зарезервировано                                 ║
       ║ ───────────────────────────────────────── ┌───┐ ───── ┌───┐ ──── ║
       ║ +13 :        Описатель  носителя          │ X │       │   │      ║
       ║ ───────────────────────────────────────── ├───┤ ───── ├───┤ ──── ║
       ║ +14 :         Состояние носителя          │   │       │ X │      ║
       ║ ───────────────────────────────────────── ├───┤ ───── ├───┤ ──── ║
       ║ +15 :           Адрес имени тома          │   │       │ X │      ║
       ║ ───────────────────────────────────────── └───┘ ───── └───┘ ──── ║
       ║                                                                  ║
       ║ Состояние носителя : (-1) - носитель заменен, 0 - носитель неоп- ║
       ║ ределен, 1 - носитель не изменялся.                              ║
       ║ Имя тома возвращается только,если : (a) DOS версии не ниже 3.00, ║
       ║ (b) установлен атрибут OCRM и (c) возвращаемый статус носителя   ║
       ║ равен (-1).                                                      ║
       ║                                                                  ║
       ╚══════════════════════════════════════════════════════════════════╝

            Команда MEDIA  CHECK  (Проверить  носитель) всегда выполняется
         блоковыми драйверами и никогда не используется для драйверов сим-
         вольных устройств. Эта команда используется MS-DOS для разрешения
         проблемы, которая может возникнуть при работе с устройствами, ис-
         пользующими сменный носитель  информации  :  носитель  (например,
         гибкий диск) может быть заменен.  При замене дискеты или ее экви-
         валента формат новой дискеты может отличаться от  предыдущего,  а
         уж содержимое новой дискеты несомненно будет другим.
            При замене дискеты MS-DOS должна настроиться на новую структу-
         ру диска : размер сектора, количество секторов и т.п. MS-DOS хра-
         нит  формат текущего диска в BPB и при смене носителя MS-DOS  по-
         требуется копия нового BPB.
            Даже если  дискета  заменена на имеющую тот же формат,  MS-DOS
         должна знать о том,  что замена диска произошла.  Каждый раз  при
         смене  носителя  директории и файлы новой дискеты наверняка будут
         отличаться от содержимого предыдущей дискеты, и MS-DOS должна бу-
         дет решать:  что делать с теми данными,  которые хранятся в буфе-
         рах, подготовленных для записи на предыдущий носитель.
            Для разрешения  всех  этих вопросов MS-DOS выдает драйверу ко-
         манду MEDIA CHECK,  спрашивая его о том, был ли заменен носитель.
         Драйвер должен вернуть на этот вопрос один из трех  ответов: "Да"
         (состояние  носителя  -1),  "Нет"  (состояние носителя 1) или "Не
         знаю" (состояние носителя 0).
            Важность этого вопроса  отражается  в  том  действии,  которое
         MS-DOS  предпринимает при получении ответа на него.  Если драйвер

                                      - 6-33 -
         отвечает "Нет,  носитель НЕ БЫЛ заменен", MS-DOS продолжает рабо-
         тать  так,  как и планировала, не проверяя, изменилось содержимое
         дискеты или нет.  Если драйвер отвечает "Да,  носитель БЫЛ  изме-
         нен", MS-DOS "выбрасывает" все хранящиеся  в буферах данные и за-
         прашивает у драйвера параметры  нового  носителя.  Наконец,  если
         драйвер отвечает что он сам не  знает  -  была  замена  или  нет,
         MS-DOS берет решение на себя. Если есть какие-либо данные, подго-
         товленные для записи на диск,  MS-DOS делает предположение о том,
         что это тот же самый диск.  В противном случае она делает предпо-
         ложение о том,  что произошла смена диска и  продолжает  работать
         так, как если бы драйвер вернул ответ "Носитель БЫЛ изменен".
            Для оказания помощи драйверу в решении вопроса о смене носите-
         ля MS-DOS передает драйверу текущий Media Descriptor  Byte  (байт
         описателя  носителя),  сокращенно MDB.  Этот байт входит в группу
         параметров,  называемую BPB (блок параметров BIOS), которая возв-
         ращается MS-DOS командами драйвера INIT и BUILD BPB. Каждому уни-
         кальному формату диска должен соответствовать свой описатель, хо-
         тя это и не всегда возможно (в разделе, описывающем команду BUILD
         BPB, этот вопрос обсуждается более подробно).
            Описатель носителя  хранится  в  первом  байте, находящемся на
         диске FAT (таблицы размещения файлов).  Кроме того,  младший байт
         значения  типа  диска  (см.  табл.11.5) представляет собой не что
         иное,  как MDB.  Подробнее о FAT и типах дисков Вы можете узнать,
         прочитав 11 главу.
            При решении вопроса о том,  была ли замена  носителя,  драйвер
         может использовать следующую логику :

           1. Если устройство не поддерживает возможность  смены  носителя
              (например,  если это жесткий диск или RAM-диск),  то драйвер
              должен ответить "Нет,  замены носителя не было". В противном
              случае переход к шагу 2.

           2. Фирма Microsoft утверждает, что на замену дискеты  требуется
              не менее двух секунд.  Принимая этот факт во внимание, драй-
              вер должен проверить системные часы и, если с момента преды-
              дущего  обращения к диску прошло менее двух секунд,  вернуть
              ответ "Нет,  замены носителя не было".  Конечно,  этот метод
              требует,чтобы драйвер всегда сохранял время обращения к дис-
              ку. Если прошло более двух секунд, то переход к шагу 3. Оче-
              видно,  что  если нет возможности считывать системное время,
              то данный шаг можно опустить.

           3. Иногда сам дисковод может иметь  возможность  информирования
              драйвера о происшедшей замене носителя.  Некоторые дисководы
              оборудованы электронной схемой, подающей сигнал, если дверца
              дисковода  открывалась с момента последнего обращения к дис-
              ку. Если драйвер обслуживает именно такой дисковод и послед-
              ний сообщает,  что дверца не открывалась,  то драйвер должен
              ответить "Нет,  замены носителя не было".  Если дверца  была
              открыта, то переход к шагу 4.
                 Бывают дисководы,  в которых двигатели включаются  только
              при  обращении  к дискете и выключаются,  выдержав некоторый
              временной интервал.  Если такой дисковод позволяет считывать
              состояние  двигателя и двигатель еще работает с момента пос-
              леднего обращения, то это позволяет сделать вывод о том, что
              дискета не заменялась и драйвер должен ответить "Нет, замены
              носителя не было".  Однако, встречаются дисководы, двигатели
              которых включаются в тот момент,  когда вставляется дискета,
              что может сделать результаты данной  проверки некорректными.

                                      - 6-34 -

           4. Драйвер должен прочесть с  диска описатель  носителя.  Если
              этот  MDB  отличается от переданного драйверу при вызове ко-
              манды MEDIA CHECK описателя,  то драйвер  должен  ответить
              "Да, была замена носителя". В противном случае переход к ша-
              гу 5.

           5. Драйвер должен прочесть с диска идентификатор тома. Если  он
              отличается  от  того,  который  хранится драйвером с момента
              последней команды BUILD BPB, то драйвер должен ответить "Да,
              была замена носителя". Иначе переход к шагу 6.

           6. Драйвер должен ответить "Не знаю, была ли замена носителя".

            Может случиться так, что невозможно реализовать некоторые эта-
         пы описанного алгоритма. Если по каким-либо причинам Вы не можете
         определить,  произошла ли замена дискеты, то лучшим ответом будет
         "Не знаю,  была ли замена носителя". Конкретный метод определения
         замены носителя будет зависеть как от особенностей дисковода, так
         и от квалификации программиста.
            Если драйвер работает с MS-DOS версии 3.0 или выше, то команда
         MEDIA CHECK может вернуть еще некоторую информацию. В том случае,
         когда  драйвер  поддерживает  команды  OPEN/CLOSE/REMOVABLE MEDIA
         (установлен бит 11 в слове атрибутов драйвера)  и  команда  MEDIA
         CHECK  собирается ответить "Да,  была замена носителя" (состояние
         носителя : -1), тогда драйвер обязан вернуть указатель на имя то-
         ма предыдущего диска (см.  главу 11 для получения сведений о фор-
         мате и расположении имени тома).  Если драйвер не знает имя  тома
         предыдущего диска (например, если обращение к команде MEDIA CHECK

                                 Команда BUILD BPB
       ╔══════════════════════════════════════════════════════════════════╗
       ║ Команда BUILD BIOS PARAMETER BLOCK (2)                           ║
       ║                                                                  ║
       ║ ────────────────────────────────         ┌───┐                   ║
       ║ +00 : 22                   Длина         │ X │ Блок. драйверы    ║
       ║ ────────────────────────────────         └───┘                   ║
       ║ +01 : номер           Устройство         ┌───┐                   ║
       ║ ────────────────────────────────         │   │ Симв. драйверы    ║
       ║ +02 : 02                 Команда         └───┘                   ║
       ║ ────────────────────────────────                                 ║
       ║ +03 :                     Статус                                 ║
       ║ ────────────────────────────────          ЧТЕНИЕ      ЗАПИСЬ     ║
       ║                  Зарезервировано                                 ║
       ║ ───────────────────────────────────────── ┌───┐ ───── ┌───┐ ──── ║
       ║ +13 :        Описатель  носителя          │ X │       │   │      ║
       ║ ───────────────────────────────────────── ├───┤ ───── ├───┤ ──── ║
       ║ +14 :           Указатель на FAT          │ X │       │   │      ║
       ║ ───────────────────────────────────────── ├───┤ ───── ├───┤ ──── ║
       ║ +18 :           Указатель на BPB          │   │       │ X │      ║
       ║ ───────────────────────────────────────── └───┘ ───── └───┘ ──── ║
       ║                                                                  ║
       ║ Поле по смещению 14 от начала блока запроса содержит указатель на║
       ║ FAT для IBM-стандартных устройств (бит 13 в слове атрибутов равен║
       ║ нулю)  или указатель на "мусор" для NONIBM/NONFAT устройств  (бит║
       ║ 13 слова атрибутов равен 1).                                     ║
       ╚══════════════════════════════════════════════════════════════════╝

                                      - 6-35 -
         происходит впервые), то драйвер должен вернуть указатель на стро-
         ку  "NO NAME"  оканчивающуюся нулевым байтом (т.е.  "NO",  пробел,
         "NAME", четыре пробела, ноль).
            Команда BUILD  BPB (построить блок параметров BIOS) всегда вы-
         полняется блоковыми драйверами  и  никогда  не  используется  для
         драйверов  символьных  устройств.  Во всех случаях,  когда MS-DOS
         проинформирована или решила сама, что носитель заменен, она долж-
         на получить параметры нового носителя. Выдавая команду BUILD BPB,
         MS-DOS просит драйвер вернуть указатель на блок  параметров BIOS,
         содержащий  новые  значения  (содержимое  полей  BPB  показано на
         рис.6-6).
            Существует важное различие между адресом BPB, возвращаемым ко-
         мандой BUILD BPB и указателем таблицы BPB,  возвращаемым командой
         INIT.  В то время,  как команда BUILD BPB возвращает указатель на
         сам блок параметров BIOS,  команда INIT возвращает адрес  таблицы
         указателей на BPB. Хотя различие между указателем и указателем на
         указатели очевидно, оно может быть источником ошибок.
            Подобно команде MEDIA CHECK, команда BUILD BPB может иметь де-
         ло с идентификатором тома.  В MS-DOS версии 3.0 и выше  драйверы,
         поддерживающие возможность замены носителя и имеющие атрибут OCRM
         (бит 11 слова атрибутов равен 1),  должны считывать  и  сохранять
         имя тома. Это имя позже будет возвращаться последующими обращени-
         ями к команде MEDIA CHECK.
            Получение команды BUILD BPB может восприниматься драйвером как
         заявление системы о том,  что по ее мнению произошла замена носи-
         теля. Если драйвер поддерживает счетчик количества "открываний" и
         "закрываний",  выполненных для устройства командами OPEN DEVICE и
         CLOSE DEVICE, то пришла пора обнулить его.

                          Получение блока параметров BIOS

             Не рассматривая механизма возврата BPB,  мы должны решить за-
         дачу  определения содержимого блока параметров BIOS.  Описываемые
         методы применимы не только к команде BUILD BPB,  но и  к  команде
         INIT.  В  простейшем случае драйвера устройства,  поддерживающего
         только один тип носителя (например драйвер RAM-диска), содержимое
         BPB может быть закодировано в теле самого драйвера.  К несчастью,
         при работе с реальными дисками, включая жесткие диски, не все так
         просто и драйвер обязан определять содержимое BPB.
            Как правило, BPB является частью блока начальной загрузки, как
         показано  на рис.6-7. В этом случае драйвер должен найти и прочи-
         тать этот блок,  выбрать оттуда блок параметров BIOS и возвратить
         адрес последнего. Практически во всех случаях блок начальной заг-
         рузки располагается в самом первом логическом секторе диска (т.е.
         сектора,  имеющего  номер  0).  Преобразование номера логического
         сектора в координаты физического сектора зависит от характеристик
         устройства и должно быть описано в документации по этому устройс-
         тву. Драйвер должен проверить структуру этого сектора, чтобы убе-
         диться, что он действительно содержит блок начальной загрузки.
            Если первый  логический  сектор  не содержит корректного блока
         начальной  загрузки,  например, как в дисках, отформатированных в
         MS-DOS версий до 2.0,то драйвер должен считать первый сектор таб-
         лицы размещения файлов (FAT).  К счастью,  MS-DOS версий  до  2.0
         поддерживали только  несколько форматов, каждый из которых опреде-
         лялся в первом секторе  FAT второго логического сектора диска. Са-
         мый первый байт первого  сектора  FAT  содержит байт описателя но-

                                      - 6-36 -
                  СМЕЩЕНИЕ              СОДЕРЖАНИЕ                РАЗМЕР
                   (hex)
                        ┌───────────────────────────────────────╖
                    +00 │  Команда перехода на код загрузчика   ║ 3 байта
                        ├───────────────────────────────────────╢
                    +03 │       Имя и версия изготовителя       ║ 8 байт
                  ╓─    ├───────────────────────────────────────╢
                  ║ +0B │        Размер сектора в байтах        ║ Слово
                  ║     ├───────────────────────────────────────╢
                  ║ +0D │    Количество секторов в кластере     ║ Байт
                  ║     ├───────────────────────────────────────╢
                  ║ +0E │ Количество зарезервированных секторов ║ Слово
         БЛОК     ║     ├───────────────────────────────────────╢
                  ║ +10 │         Количество таблиц FAT         ║ Байт
      ПАРАМЕТРОВ ─╢     ├───────────────────────────────────────╢
                  ║ +11 │    Количество элементов директория    ║ Слово
         BIOS     ║     ├───────────────────────────────────────╢
                  ║ +13 │    Количество логических секторов     ║ Слово
                  ║     ├───────────────────────────────────────╢
                  ║ +15 │          Описатель  носителя          ║ Байт
                  ║     ├───────────────────────────────────────╢
                  ║ +16 │    Количество секторов в одной FAT    ║ Слово
                  ╙─    ├───────────────────────────────────────╢
                    +18 │    Количество секторов на дорожке     ║ Слово
                        ├───────────────────────────────────────╢
                    +1A │   Количество головок чтения/записи    ║ Слово
                        ├───────────────────────────────────────╢
                    +1C │      Количество скрытых секторов      ║ Слово
                        ╘═══════════════════════════════════════╝

          Рисунок 6-7. Содержимое первых 30 байт блока начальной загрузки
          сителя, который можно использовать для определения соответству-
          ющего содержимого BPB,  возвращаемого к MS-DOS.  Версии  MS-DOS
          до 2.0 используют описатели 0FEH и 0FFH. В главе 11 представлен
          список различных значений типов дисков, из которых берется MDB.
            Выполняя этот процесс,  Вам следует помнить, что просто чтение
         диска не может гарантировать правильных  результатов.  Если  уст-
         ройство и  драйвер  поддерживают  несколько форматов (например, с
         различными размерами сектора),  то драйверу  может  потребоваться
         несколько попыток чтения с разными форматами для того,  чтобы об-
         наружить корректный формат. После того, как сформирован BPB и оп-
         ределен  формат данного диска драйвер,  поддерживающий устройство
         со сменным носителем  (имеющий  атрибут  OCRM),  обязан  получить
         идентификатор  тома данного диска. Найти его можно, обратившись к
         корневому директорию, как описано в главе 11.

            Вкратце, последовательность обработки команды BUILD BPB следу-
          ющая :

           1. Драйвер должен прочитать блок начальной загрузки (обычно на-
              ходящийся в первом логическом секторе диска - сектор  #0)  и
              проверить его на наличие блока параметров BIOS. Если BPB об-
              наружен, то переход к шагу 3, иначе переход к шагу 2.

           2. Драйвер должен прочитать первый сектор FAT для  того,  чтобы
              получить  байт описателя носителя.  Полагаясь на этот MDB,

                                      - 6-37 -
              драйвер должен сконструировать соответствующий BPB (см. гла-
              ву 11 о соответствии между MDB и BPB).

           3. Если устройство поддерживает замену носителя (установлен бит
              11 слова атрибутов), драйвер должен получить из  корневого
              директория идентификатор тома и сохранить его.

            Для выполнения  этого  алгоритма,  драйвер должен иметь буфера
         для хранения копии BPB и имени тома, а также буфер, предназначен-
         ный для считывания туда сектора с диска.
            Мы опустили  из  рассмотрения  параметры,  которые  передаются
         драйверу при обращении к нему с командой BUILD  BPB.  Игнорируйте
         их. Один из этих параметров - это описанный ранее описатель носи-
         теля,  который в данной ситуации не имеет никакого значения,  так
         как  данная команда возвращает MS-DOS новое его значение.  Второй
         параметр - это адрес буфера,  который либо не содержит ничего су-
         щественного (если бит 13, NONIBM атрибут, равен 1), либо содержит
         копию первого сектора FAT (если бит 13 сброшен). В последнем слу-
         чае,  т.е.  если там содержится FAT, этот буфер никоим образом не
         должен быть модифицирован,  а так как драйвер обязан  иметь  свой
         буфер,  куда будет считываться блок начальной загрузки, то на бу-
         фер,  передаваемый при вызове команды BUILD BPB можно не обращать
         внимания.
            Напоследок представляется важным отметить,  что в  отличие  от
         BPB  описатель  носителя не обеспечивает однозначного определения
         формата диска.  Однако,  MS-DOS версии 3.0 и выше не будут обнов-
         лять свои внутренние структуры, ассоциированные с данным дисково-
         дом,  до тех пор, пока байт описателя носителя не станет отличным
         от предыдущего MDB.  Даже несмотря на то, что MS-DOS версии 3.0 и
         выше не обращают внимание на действительное значение MDB, драйвер
         должен вернуть новый MDB при смене формата дискеты.
            Команды INPUT, OUTPUT и  OUTPUT & VERIFY (команды 4,  8 и 9  -
         "Ввод", "Вывод"  и  "Вывод  с проверкой",  соответственно) всегда
         требуются для всех драйверов.  При помощи этих команд выполняется
         передача данных между MS-DOS и устройством.
            Команды IOCTL INPUT и IOCTL OUTPUT (коды 3 и 12 - "Ввод команд
         управления" и "Вывод команд управления", соответственно) являются
         дополнительными,  требующимися только при установленном IOCTL ат-
         рибуте (бит 14 слова атрибутов драйвера). Эти команды применяются
         как с блоковыми так и с символьными драйверами и обеспечивают пе-
         редачу данных между MS-DOS и драйвером.
            Команда OUTPUT UNTIL BUSY (код 16 - "Вывод  пока  не  занято")
         является необязательной командой и используется исключительно для
         символьных драйверов,  имеющих атрибут NONIBM/OTB (бит  13).  Эта
         команда обеспечивает передачу данных от MS-DOS к устройству.  За-
         метьте также,  что эта команда не документирована в IBM Technical
         Reference Manual для PC-DOS версии 3.30.
            Команды OUTPUT  и  OUTPUT & VERIFY устанавливаются комбинацией
         пятого бита IOCTL (бит 5 - подготовленный/неподготовленный режим)
         и  опцией VERIFY.  Если установлен режим проверки,  то весь вывод
         данных обеспечивается командой OUTPUT & VERIFY. Если режим провер-
         ки не установлен, то используется обычная команда OUTPUT. Перевод
         драйвера в неподготовленный режим (при установке  IOCTL  бита  5)
         позволяет осуществлять многобайтные передачи.
            Существует комбинация режимов,  которая должна была бы вызвать
         использование команды OUTPUT UNTIL BUSY, но не делает этого. Эта
         комбинация включает режим без проверки (так что команда OUTPUT &

                                      - 6-38 -
                              Команды INPUT и OUTPUT
                             ~~~~~~~~~~~~~~~~~~~~~~~~
      ╔══════════════════════════════════════════════════════════════════╗
      ║                                                                  ║
      ║ Команды INPUT и OUTPUT (3,4,8,9,12,16)                           ║
      ║                                                                  ║
      ║ ────────────────────────────────         ┌───┐                   ║
      ║ +00 : 22                   Длина         │ X │ Блок. драйверы    ║
      ║ ────────────────────────────────         └───┘                   ║
      ║ +01 : номер           Устройство         ┌───┐                   ║
      ║ ────────────────────────────────         │ X │ Симв. драйверы    ║
      ║ +02 : команда            Команда         └───┘                   ║
      ║ ────────────────────────────────                                 ║
      ║ +03 :                     Статус     ┌──────────────────────────┐║
      ║ ────────────────────────────────     │  Команды :               │║
      ║                  Зарезервировано     │                          │║
      ║                                      │  03 : IOCTL INPUT        │║
      ║                                      │  04 : INPUT              │║
      ║                                      │  08 : OUTPUT             │║
      ║                                      │  09 : OUTPUT & VERIFY    │║
      ║                                      │  12 : IOCTL OUTPUT       │║
      ║                                      │  16 : OUTPUT UNTIL BUSY  │║
      ║                                      └──────────────────────────┘║
      ║                                                                  ║
      ║                                           ЧТЕНИЕ      ЗАПИСЬ     ║
      ║                                                                  ║
      ║ ───────────────────────────────────────── ┌───┐ ───── ┌───┐ ─────║
      ║ +13 :        Описатель  носителя          │ X │       │   │      ║
      ║ ───────────────────────────────────────── ├───┤ ───── ├───┤ ─────║
      ║ +14 :               Адрес буфера          │ X │       │   │      ║
      ║ ───────────────────────────────────────── ├───┤ ───── ├───┤ ─────║
      ║ +18 : Количество байтов/секторов          │ X │       │ X │      ║
      ║ ───────────────────────────────────────── ├───┤ ───── ├───┤ ─────║
      ║ +20 :           Начальный сектор          │ X │       │   │      ║
      ║ ───────────────────────────────────────── ├───┤ ───── ├───┤ ─────║
      ║ +22 :      Указатель на имя тома          │   │       │ X │      ║
      ║ ───────────────────────────────────────── └───┘ ───── └───┘ ─────║
      ║                                                                  ║
      ║ Поле  по смещению  18 от начала  блока запроса содержит на  входе║
      ║ требуемое количество байтов/секторов.  Драйвер должен поместить в║
      ║ это поле фактическое количество переданных секторов или байтов.  ║
      ║ Указатель на имя тома возвращается только в MS-DOS версии  3.00 и║
      ║ выше  при условии,  что возвращается ошибка  0FH -- "Недопустимая║
      ║ замена диска".                                                   ║
      ║                                                                  ║
      ╚══════════════════════════════════════════════════════════════════╝

         VERIFY не будет использоваться), неподготовленный режим (позволя-
         ющий многобайтные передачи) и устройство,  поддерживающее команду
         OUTPUT UNTIL BUSY.  Однако, при тестировании этого режима обнару-
         жилось,  что команда OUTPUT UNTIL BUSY не выдается никогда, чем и
         объясняется,  наверное,  почему  IBM опустила эту команду в своей
         документации.
            Все эти команды имеют общую структуру блока запроса,  но отли-
         чаются типом запрашиваемой операции ввода/вывода и типом драйвера
         устройства. Ниже перечислены основные параметры, используемые при
         вызове команд ввода/вывода :

                                      - 6-39 -

            * Сама команда определяет источник и  получатель  при передаче
              данных. Важно отметить, что операции управления вводом/выво-
              дом предназначены для передачи драйверу различных директив и
              управляющей информации,  а не для передачи данных непосредс-
              твенно на устройство. Приведем возможные комбинации источни-
              ка и получателя данных :

                КОМАНДЫ                        ИСТОЧНИК      ПОЛУЧАТЕЛЬ

                INPUT                          Устройство    Буфер
                OUTPUT                         Буфер         Устройство
                OUTPUT VERIFY                  Буфер         Устройство
                OUTPUT UNTIL BUSY              Буфер         Устройство
                IOCTL INPUT                    Драйвер       Буфер
                IOCTL OUTPUT                   Буфер         Драйвер

            * Адрес источника или получателя со стороны MS-DOS представля-
              ет собой адрес буфера,  который либо содержит данные для вы-
              вода (команды OUTPUT),  либо будет заполнен данными (команды
              INPUT).

            * "Количество байт/секторов" определяет сколько байт (для сим-
              вольных устройств и в командах IOCTL) или  секторов (команды
              INPUT, OUTPUT и OUTPUT&VERIFY) будет (или было) передано.

            * Только  для  блоковых драйверов задаются параметры "Устройс-
              тво" и "Начальный сектор", уточняющие местонахождение источ-
              ника (для INPUT) или получателя (для OUTPUT).

            * Параметр "Байт описателя носителя" (только для блоковых ус-
              тройств) может быть использован для определения формата дис-
              ка или того факта, что носитель был заменен.
            Как только драйвер определил источника и получателя, он выпол-
         няет  передачу  данных.  После выполнения передачи драйвер должен
         вернуть фактическое количество переданных  байтов  или  секторов.
         Даже если возникла ошибка и установлен индикатор ошибки в возвра-
         щаемом слове состояния, MS-DOS считает, что возвращаемое значение
         параметра  "Количество байтов/секторов" корректно.  Если драйверу
         не удалось обновить этот параметр, то возвращаемое значение будет
         таким  же  как  и  переданное драйверу на входе.  Вы должны также
         знать,  что даже если передача прошла успешно,  счетчик  все-таки
         может  иметь неверное значение.  Это происходит при возникновении
         т.н. "перекрытия".
            Перекрытие (для  блоковых  драйверов)  возникает в том случае,
         если передается большее количество байт, чем то которое можно ад-
         ресовать с помощью сегментного адреса буфера.  Приведем следующий
         пример. Пусть драйвер пересылает 64 сектора по 512 байт каждый из
         буфера начиная со смещения 8000H. Таким образом, общее количество
         байт, которые требуется передать, составит 32768 (8000H). Так как
         начальное  смещение в буфере равно 8002H,  то смещение последнего
         байта будет иметь заведомо некорректное значение 10002H. В подоб-
         ных случаях, т.е. при возникновении перекрытий, драйвер не должен
         пытаться передавать недостижимую порцию данных.
            Каждая команда  имеет свои особенные требования для выполнения
         передачи и отличаются возвращаемым значением статуса  и счетчика.
         Эти требования описаны ниже для каждой из команд.

                                      - 6-40 -

         КОМАНДЫ CTL INPUT и CTL OUTPUT (3 и 12).
            Это простейшие команды,  обычно требуемые только для  передачи
         данных самому  драйверу  (не  устройству) или получения данных от
         него.  Для MS-DOS эти данные не имеют никакого значения, и, более
         того,  могут  быть проигнорированы самим драйвером,  если ему так
         захочется.  Ответственность за обработку переданных таким образом
         данных лежит только на драйвере и прикладной программе.  Как пра-
         вило,  используются для изменения режимов работы  драйвера  и/или
         устройства,  хотя возможны и другие варианты. Важно только, чтобы
         драйвер не забывал правильно устанавливать  возвращаемое значение
         счетчика переданных байтов.

         КОМАНДЫ INPUT OUTPUT (4 и 8).
         Для большинства  символьных  драйверов  логика  обработки  команд
         INPUT и OUTPUT весьма незамысловата.  Если передача прошла успеш-
         но,  то устанавливается бит DONE в  слове  состояния  драйвера  и
         драйвер возвращает управление. Если возникла какая-либо проблема,
         то в  слово  состояния  записывается  код  соответствующей ошибки
         (см.табл.6-3), устанавливается счетчик и возвращается управление.

            Если символьное  устройство  не имеет готовых данных на момент
         выдачи команды INPUT,  драйвер может либо подождать  или  вернуть
         ошибку "Устройство не готово". При выводе данных, если устройство
         не может их принять,  драйвер также может вернуть эту ошибку. Од-
         нако ошибка "Устройство не готово" обычно используется для указа-
         ния того, что устройство выключено или по каким-либо причинам не-
         доступно.  Использование  этой  ошибки  всего  лишь для индикации
         неготовности данных является не слишком хорошим решением, так как
         получив ошибку "Устройство не готово",  MS-DOS запросто может вы-
         дать оператору запрос на вмешательство.
            Логика  работы  блоковых  драйверов  при  выполнении  операций
         ввода/вывода более сложная. Как правило, драйвер должен выполнять
         преобразование номера начального сектора в координаты физического
         сектора,  обычно состоящие из номера цилиндра  (дорожки),  номера
         головки  и номера физического сектора на дорожке.  Возможно,  что
         драйверу придется выполнить операцию перевода головки  чтения/за-
         писи  на соответствующую дорожку перед началом передачи и,  может
         быть, в процессе самой передачи секторов. Более подробно устройс-
         тво диска описано в главе 11.
            Более того, устройства, подобные дисководам, являются источни-
         ком множества ошибок (см.табл.6-3), таких как "Запись на устройс-
         тво запрещена",  "Неверно переданы данные (ошибка CRC)",  "Ошибка
         при установке головки", "Ошибка при чтении", "Ошибка при записи",
         и даже такая звучная как "Общая ошибка". Обычно при возникновении
         ошибки драйвер фиксирует код ошибки в слове состояния, устанавли-
         вает счетчик успешно переданных секторов и возвращает управление.
         Однако одна ошибка требует дальнейшего анализа и обработки  - это
         "Недопустимая смена диска".
            Ошибка "Недопустимая смена диска" воспринимается MS-DOS версии
         3.0 и выше и только в том случае, когда MS-DOS знает что она име-
         ет дело с устройством, поддерживающем замену носителя (установлен
         атрибут OCRM в слове атрибутов драйвера).  Отличие этой ошибки от
         остальных заключается в том,  что если драйвер информирует MS-DOS
         о недопустимой замене носителя,  MS-DOS  должна  знать  с  каким,
         собственно,  диском намеревался работать драйвер.  Эта информация
         определяется именем тома ожидаемого диска,  указатель на  которое

                                      - 6-41 -
         должен вернуть драйвер.  Как и в команде MEDIA CHECK,  если соот-
         ветствующее имя драйверу не известно,  он  должен  вернуть  адрес
         строки "NO NAME".
            Как узнает драйвер о недопустимой замене носителя?  Если драй-
         вер ведет счетчик количества открытий и закрытий,  выполненных на
         устройстве (командами 13 и 14) и  диск  заменяется  пользователем
         (что  определяется другим форматом,  другим описателем носителя и
         т.п.) при количестве открытий превышающем количество закрытий, то
         делается  вывод  о  том,  что замена носителя была недопустимой и
         формируется ошибка "Недопустимая смена диска".

         КОМАНДА OUTPUT & VERIFY (9).
         Команда OUTPUT & VERIFY(вывод с проверкой) применяется только для
         тех устройств,  в которых возможно считывание данных после их за-
         писи  на устройство для того,  чтобы убедиться в корректности вы-
         полненной операции.  Для таких устройств  (например  для  дисков)
         драйвер  должен  выводить данные (также как и по команде OUTPUT),
         считывать их обратно (так же как по INPUT) и  сравнить  считанные
         данные с теми,  которые были записаны. Если обнаружена ошибка, то
         драйверу следует не пытаться повторить  неудавшуюся  операцию,  а
         сообщить  об  этой  ситуации  MS-DOS,  вернув соответствующий код
         ошибки (см.табл.6-3) и количество успешно переданных  байтов/сек-
         торов.

            Как и   в   случае   команды    BUILD BPB,   обработка команды
         OUTPUT & VERIFY требует наличия у драйвера внутреннего буфера для
         считывания проверяемых данных.  Если устройство не позволяет счи-
         тывать данные обратно,  то данная команда  должна  обрабатываться
         также как и команда OUTPUT (команда 8).

         КОМАНДА OUTPUT UNTIL BUSY (16).
            Команда OUTPUT UNTIL BUSY представляет еще одну  разновидность
         команды OUTPUT.  Эта команда,  которая используется только с сим-
         вольными устройствами, драйверы которых имеют атрибут OTB (бит 13
         слова атрибутов),  позволяет программам передавать большие порции
         данных устройствам,  которые имеют внутренние буфера  (таких  как
         принтеры).  Драйвер такого устройства должен посылать данные либо
         до тех пор пока они не кончатся, либо пока устройство в состоянии
         их принять. Очень важно, чтобы такой драйвер корректно устанавли-
         вал счетчик переданных байтов, так чтобы MS-DOS знала какое коли-
         чество данных уже передано.  Обратите внимание,  что для этой ко-
         манды не  является  ошибочной  ситуация,  когда  передано  меньше
         данных, чем было запрошено.
            Команда NONDESTRUCTIVE INPUT WITHOUT WAIT (неразрушающее  счи-
         тывание  без  ожидания) требуется только для драйверов символьных
         устройств и не используется для блоковых драйверов.  Хотя эта ко-
         манда похожа на обычную символьную команду INPUT, она все же име-
         ет несколько заметных отличий :
            * Отсутствует буфер данных  и  счетчик  количества  переданных
              данных.  При  вызове  этой команды требуемое количество байт
              всегда равно 1 и если устройство  готово  предоставить  байт
              данных,  он  возвращается  в  поле  "Считанный из устройства
              байт" блока запроса.

            * Нет ожидания. Если устройство не готово предоставить очеред-

                                      - 6-42 -
                     Команда NONDESTRUCTIVE INPUT WITHOUT WAIT
                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      ╔═════════════════════════════════════════════════════════════════╗
      ║                                                                 ║
      ║ Команда NONDESTRUCTIVE INPUT WITHOUT WAIT (5)                   ║
      ║                                                                 ║
      ║ ────────────────────────────────         ┌───┐                  ║
      ║ +00 : 14                   Длина         │   │ Блок. драйверы   ║
      ║ ────────────────────────────────         └───┘                  ║
      ║ +01 :                 Устройство         ┌───┐                  ║
      ║ ────────────────────────────────         │ X │ Симв. драйверы   ║
      ║ +02 : 05                 Команда         └───┘                  ║
      ║ ────────────────────────────────                                ║
      ║ +03 :                     Статус                                ║
      ║ ────────────────────────────────          ЧТЕНИЕ      ЗАПИСЬ    ║
      ║                  Зарезервировано                                ║
      ║ ───────────────────────────────────────── ┌───┐ ───── ┌───┐ ────║
      ║ +13 :  Считанный из устр-ва байт          │   │       │ X │     ║
      ║ ───────────────────────────────────────── └───┘ ───── └───┘ ────║
      ║                                                                 ║
      ╚═════════════════════════════════════════════════════════════════╝
              ной символ, то драйвер должен установить бит BUSY в дополне-
              ние к биту DONE в слове состояния и  незамедлительно вернуть
              управление.

            * Чтение  неразрушающее.  Если  устройство  готово выдать байт
              данных, то драйвер обязан не только возвратить этот байт, но
              и  сохранить его для чтения последующей командой INPUT. Если
              вводимые данные помещаются драйвером в очередь (как в управ-
              ляемых прерываниями драйверах),  то возвращаемый этой коман-
              дой байт должен остаться в очереди.

            Эта команда  предназначена  для  того,  чтобы  MS-DOS   могла,
         во-первых, определить наличие данных, не используя команду INPUT,
         которая может привести к длительному ожиданию данных,  и, во-вто-
                        Команды STATUS и FLUSH INPUT/OUTPUT
                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         ╔══════════════════════════════════════════════════════════════╗
         ║Команды STATUS и FLUSH INPUT/OUTPUT (6,7,10,11)               ║
         ║                                                              ║
         ║────────────────────────────────         ┌───┐                ║
         ║+00 : 13                   Длина         │   │ Блок. драйверы ║
         ║────────────────────────────────         └───┘                ║
         ║+01 : номер           Устройство         ┌───┐                ║
         ║────────────────────────────────         │ X │ Симв. драйверы ║
         ║+02 : команда            Команда         └───┘                ║
         ║────────────────────────────────                              ║
         ║+03 :                     Статус       ┌────────────────────┐ ║
         ║────────────────────────────────       │ КОМАНДЫ :          │ ║
         ║                 Зарезервировано       │                    │ ║
         ║────────────────────────────────       │  6 : INPUT STATUS  │ ║
         ║                                       │  7 : INPUT FLUSH   │ ║
         ║                                       │ 10 : OUTPUT STATUS │ ║
         ║                                       │ 11 : OUTPUT FLUSH  │ ║
         ║                                       └────────────────────┘ ║
         ╚══════════════════════════════════════════════════════════════╝

                                      - 6-43 -
         рых,  анализировать следующий символ в буфере без изъятия его от-
         туда.
            Команды I/O STATUS и I/O FLUSH (команды 6,10 и 7,11, соответс-
         твенно) требуются только для символьных устройств и не  использу-
         ются блоковыми.
            Команды INPUT  STATUS и INPUT FLUSH имеют смысл только для тех
         драйверов символьных устройств,  которые поддерживают управляемые
         прерываниями очереди вводимых данных,  хотя эти команды могут вы-
         зываться для любого символьного драйвера.Команда INPUT STATUS ис-
         пользуется для индикации состояния очереди следующим образом:

            * Если очередь есть,  но она пуста,  то драйвер должен устано-
              вить  биты  DONE и BUSY в слове состояния и вернуть управле-
              ние.

            * Если в очереди есть символы,  доступные для чтения, то драй-
              вер должен установить бит DONE,  сбросить бит BUSY и вернуть
              управление.

            * Если очередь не поддерживается, то драйвер должен установить
              бит DONE,  сбросить бит BUSY в слове состояния и вернуть уп-
              равление. Это выглядит странным -  уведомлять MS-DOS о нали-
              чии символа, когда даже очереди нет.Объяснение заключается в
              том, что после такого ответа MS-DOS выдаст команду INPUT для
              считывания символа.  Если же этого не сделать, то MS-DOS бу-
              дет продолжать опрашивать  статус  ввода бесконечно, так как
              из-за отсутствия очереди статус всегда будет одним и тем же.

            Команда INPUT FLUSH  применяется для уведомления драйвера о не-
         обходимости удаления всех находящихся в данный момент  во входной
         очереди  символов.  После  очистки очереди (если таковая имеется)
         драйвер должен установить бит DONE и вернуть управление.  При об-
         работке этой команды не должно возникать никаких ошибок, по край-
         ней мере MS-DOS предполагает,  что эта команда всегда завершается
         успешно.
            Команда OUTPUT STATUS используется для проверки состояния  вы-
         ходной  очереди или устройства.  Если драйвер не поддерживает вы-
         ходную очередь, то следует, по возможности, вернуть состояние са-
         мого  устройства.  Состояние  определяется  битом BUSY ("занято")
         слова состояния (состояние "занято" означает что вывод задержива-
         ется).  Установив состояние, драйвер должен установить бит DONE и
         вернуть управление.
            Команда OUTPUT FLUSH предназначена для указания драйверу необ-
         ходимости удалить все находящиеся в выходной очереди символы (ес-
         ли очередь поддерживается) и, если это возможно, немедленно прек-
         ратить любые операции  вывода.  После  выполнения  этих  действий
         драйвер  должен  установить бит DONE в слове состояния драйвера и
         вернуть управление MS-DOS.
            Команды DEVICE OPEN и DEVICE CLOSE (команды 13 и  14) являются
         необязательными командами, поддерживаемые MS-DOS версий 3.0 и вы-
         ше, и используются только если драйвер имеет атрибут OCRM (бит 11
         слова  атрибутов драйвера равен 1).  Однако Microsoft рекомендует
         использовать эти команды во всех новых создаваемых драйверах.

                                      - 6-44 -
                    Команды DEVICE OPEN/CLOSE и REMOVABLE MEDIA
                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         ╔══════════════════════════════════════════════════════════════╗
         ║                                                              ║
         ║Команды DEVICE OPEN/CLOSE и REMOVABLE MEDIA (13,14,15)        ║
         ║                                                              ║
         ║────────────────────────────────         ┌───┐                ║
         ║+00 : 13                   Длина         │ X │ Блок. драйверы ║
         ║────────────────────────────────         └───┘                ║
         ║+01 : номер           Устройство         ┌───┐                ║
         ║────────────────────────────────         │   │ Симв. драйверы ║
         ║+02 : команда            Команда         └───┘                ║
         ║────────────────────────────────                              ║
         ║+03 :                     Статус       ┌────────────────────┐ ║
         ║────────────────────────────────       │ КОМАНДЫ :          │ ║
         ║                 Зарезервировано       │                    │ ║
         ║────────────────────────────────       │ 13 : DEVICE OPEN   │ ║
         ║                                       │ 14 : DEVICE CLOSE  │ ║
         ║                                       │ 15 : REMOVABLE     │ ║
         ║                                       │      MEDIA CHECK  │ ║
         ║                                       └────────────────────┘ ║
         ╚══════════════════════════════════════════════════════════════╝


            Команда REMOVABLE  MEDIA  (15) является дополнительной командой
         для блоковых драйверов в MS-DOS 3.0 и выше и  используется  только
         при наличии у драйвера атрибута OCRM (бит 11 слова атрибутов). Эта
         команда также рекомендуется к использованию  фирмой  Microsoft  во
         всех новых драйверах.

            Условия, при   которых   происходит   обращение   к   командам
         DEVICEOPEN и DEVICECLOSE,  описаны выше в подразделе "Слово атри-
         бутов", подзаголовок "БИТ 11 : OCRM".
            Для блоковых устройств со сменным носителем информации эти ко-
         манды  могут  использоваться для отслеживания количества открытых
         на устройстве файлов, позволяя, таким образом, обнаруживать ситу-
         ацию  недопустимой  замены носителя (которая возникает при замене
         диска, на котором еще имеются открытые файлы).
            Для символьных устройств эти команды могут  использоваться для
         предотвращения одновременного доступа различных программ к одному
         устройству (такому как принтер) или для  обеспечения возможностей
         перед и после обработки устройства (например, операций загрузки и
         сброса принтера).
            Команда REMOVABLEMEDIA  может  быть выдана прикладной програм-
         мой,  используя подфункцию "Проверка заменяемости носителя" функ-
         ции IOCTL (подфункция 08H функции 44H).  При вызове  этой функции
         прикладная программа должна задать номер интересующего ее  диско-
         вода.  Получив команду REMOVABLEMEDIA,  драйвер должен определить
         имеет ли упомянутое устройство возможность замены носителя и вер-
         нуть статус битом BUSY слова состояния драйвера.  Если устройство
         не поддерживает смены носителя, то драйвер должен установить  бит
         BUSY, в противном случае сбросить его.
            Команда GENERIC IOCTL (19) является  дополнительной  командой,
         поддерживаемой  MS-DOS начиная с версии 3.20.  Использование этой
         команды разрешается установленным в 1 атрибутом  GIOCTL  (бит  6)
         слова состояния.

                                      - 6-45 -
                               Команда GENERIC IOCTL
                              ~~~~~~~~~~~~~~~~~~~~~~~
         ╔══════════════════════════════════════════════════════════════╗
         ║                                                              ║
         ║  Команда GENERIC IOCTL (19)                                  ║
         ║                                                              ║
         ║  ────────────────────────────────  ┌───┐                     ║
         ║  +00 : 23                   Длина  │ X │ Блок. драйверы      ║
         ║  ────────────────────────────────  └───┘                     ║
         ║  +01 : номер           Устройство  ┌───┐                     ║
         ║  ────────────────────────────────  │   │ Симв. драйверы      ║
         ║  +02 : 19                 Команда  └───┘                     ║
         ║  ────────────────────────────────                            ║
         ║  +03 :                     Статус                            ║
         ║  ────────────────────────────────   ЧТЕНИЕ      ЗАПИСЬ       ║
         ║                   Зарезервировано                            ║
         ║  ────────────────────────────────── ┌───┐ ───── ┌───┐ ─────  ║
         ║  +13 :    Номер функции (старший)   │ X │       │   │        ║
         ║  ────────────────────────────────── ├───┤ ───── ├───┤ ─────  ║
         ║  +14 :    Номер функции (младший)   │ X │       │   │        ║
         ║  ────────────────────────────────── ├───┤ ───── ├───┤ ─────  ║
         ║  +15 :     Содержимое регистра SI   │ X │       │   │        ║
         ║  ────────────────────────────────── ├───┤ ───── ├───┤ ─────  ║
         ║  +17 :     Содержимое регистра DI   │ X │       │   │        ║
         ║  ────────────────────────────────── ├───┤ ───── ├───┤ ─────  ║
         ║  +19 :  Адрес блока IOCTL запроса   │ X │       │   │        ║
         ║  ────────────────────────────────── └───┘ ───── └───┘ ─────  ║
         ║                                                              ║
         ╚══════════════════════════════════════════════════════════════╝

            Название "Группа команд управления вводом/выводом"  не  совсем
         точно,  так  как эта команда используется при поддержке драйвером
         дополнительных возможностей.  Одна группа дополнительных  функций
         (доступ к которой обеспечивается IOCTL подфункцией 0CH) поддержи-
         вает  возможность  переключения  кодовых   страниц   (code   page
         switching),  средства  для  оперативной  реконфигурации драйвера.
         Другая большая группа функций (доступ  к  которой  обеспечивается
         IOCTL подфункцией 0DH) обеспечивает стандартный интерфейс для ап-
         паратурозависимых операций блоковых драйверов. Операции, входящие
         в данную группу, включают чтение, запись, верификацию, форматиро-
         вание целых дорожек,  чтение и модификацию блока параметров  BIOS
         (BPB).
            Расширенные возможности команды GENERIC IOCTL хорошо описаны в
         "MS-DOS Technical Reference Manual" ("MS-DOS.  Техническое описа-
         ние") в разделе,  описывающем функцию 44H MS-DOS.  В связи с тем,
         что эти функции предназначены в основном для поддержки оборудова-
         ния производителей, мы отсылаем читателей к упомянутому руководс-
         тву для получения более подробной информации.
            Команды GETLOGICALDEVICE и SETLOGICALDEVICE (23 и 24) являются
         дополнительными командами для блоковых драйверов и поддерживаются
         в MS-DOS начиная с версии 3.20.  Использование этих команд разре-
         шается  при наличии у драйвера атрибута GIOCTL (бит 6 слова атри-
         бутов) равного 1.

                                      - 6-46 -
                         Команды GET & SET LOGICAL DEVICE
                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         ╔════════════════════════════════════════════════════════════════╗
         ║                                                                ║
         ║Команда GET & SET LOGICAL DEVICE (23,24)                        ║
         ║                                                                ║
         ║────────────────────────────────         ┌───┐                  ║
         ║+00 : 21                   Длина         │ X │ Блок. драйверы   ║
         ║────────────────────────────────         └───┘                  ║
         ║+01 : номер           Устройство         ┌───┐                  ║
         ║────────────────────────────────         │   │ Симв. драйверы   ║
         ║+02 : команда            Команда         └───┘                  ║
         ║────────────────────────────────                                ║
         ║+03 :                     Статус     ┌─────────────────────────┐║
         ║────────────────────────────────     │ КОМАНДЫ :               │║
         ║                 Зарезервировано     │                         │║
         ║                                     │ 23 : GET LOGICAL DEVICE │║
         ║                                     │ 24 : SET LOGICAL DEVICE │║
         ║                                     └─────────────────────────┘║
         ║                                                                ║
         ║                                          ЧТЕНИЕ      ЗАПИСЬ    ║
         ║                                                                ║
         ║───────────────────────────────────────── ┌───┐ ───── ┌───┐ ────║
         ║+13 :      Ввод (код устройства)          │ X │       │   │     ║
         ║───────────────────────────────────────── ├───┤ ───── ├───┤ ────║
         ║+14 :                Код команды          │ X │       │   │     ║
         ║───────────────────────────────────────── ├───┤ ───── ├───┤ ────║
         ║+15 :                     Статус          │ X │       │   │     ║
         ║───────────────────────────────────────── ├───┤ ───── ├───┤ ────║
         ║+17 :            Зарезервировано          │   │       │ X │     ║
         ║───────────────────────────────────────── └───┘ ───── └───┘ ────║
         ║                                                                ║
         ╚════════════════════════════════════════════════════════════════╝
              Эти команды  используются  для  отслеживания  имени текущего
         диска для устройств, имеющих несколько логических дисков. Подобно
         команде GENERICIOCTL, команды GET/SET LOGICAL DEVICE доступны че-
         рез функцию 44H MS-DOS.  Подфункция 0H применяется для  получения
         текущего  имени  логического диска,  а функция 0FH для присвоения
         имени  нового  логического  диска.   Так   же   как   и   команда
         GENERICIOCTL, команды GET/SET LOGICAL DEVICE предназначены, в ос-
         новном, для поддержки устройств производителя (например, в случае
         драйвера  DRIVER.SYS,  для поддержки 3.5-дюймовых гибких дисков).
         Полное описание этих  команд  можно  найти  в  "MS-DOS  Technical
         Reference Manual" в разделе, описывающем функцию MS-DOS 44H, куда
         мы Вас и отсылаем.

                  Создание загрузочного файла драйвера устройства

            Выше уже упоминалось, что программа драйвера устройства похожа
         на обычную .COM программу.  Это утверждение тем более истинно при
         использовании описываемого метода создания .SYS  файла  драйвера.
         Заметьте, что нет никаких причин, кроме соглашений, для использо-
         вания расширения .SYS в файлах драйверов -  допустимы любые  рас-
         ширения.  В листинге 6-4 представлен диалог с системой при созда-
         нии драйвера "DRIVER".  Этот файл ассемблируется и линкуется  как
         обычная программа, после чего преобразуется в двоичный .SYS файл.
         Отметим,что отсутствие стека для драйвера является нормальным яв-

                                      - 6-47 -
         лением,  так  как  драйвер при работе использует собственный стек
         MS-DOS.

            В примере,  приведенном в листинге 6-4, создается также выход-
         ной  .LST  файл  ассемблера и выходной .MAP файл редактора связей
         (линкера). Конечно же, .OBJ и .EXE файлы могут быть удалены после
         создания .SYS файла.

                 Листинг 6-4. Процесс создания простого драйвера
         -----------------------------------------------------------------
         C> masm driver,driver,driver;

         Microsoft Macro Assembler Version 4.00
         Copyright Microsoft Corp 1981, 1983, 1984, 1985.
         All rights reserved.

           45976 Bytes symbol space free

              0 Warning Errors
              0 severe  Errors

         C> link driver,driver,driver;

         Microsoft 8086 Object linker
         Version 3.00 Copyright Microsoft Corp 1983, 1984, 1985

         Warning: no stack segment

         C> exe2bin driver driver.sys

         -----------------------------------------------------------------

                            Отладка драйверов устройств

            После того,  как драйвер установлен в системе, он уже не может
         быть отлажен с помощью MS-DOS (из-за проблемы реентерабельности).
         Однако отлаживать драйверы необходимо, так как подобно практичес-
         ки всем программам трудно ожидать от драйвера  правильной  работы
         после  первого запуска.  К решению задачи отладки драйверов можно
         подойти с трех сторон.
            Во-первых, разрабатывайте  драйвер по технологии "сверху вниз"
         - заставьте  работать основную часть программы, а затем добавляй-
         те более сложные блоки. Не пытайтесь сделать в первую очередь об-
         работчики IOCTL. Процедурами, правильной работы которых Вы должны
         добиться в первую очередь,  являются программы СТРАТЕГИЙ и ПРЕРЫ-
         ВАНИЙ, а также процедура инициализации INIT. В блоковых драйверах
         Вы  должны также добиться правильной работы команды MEDIACHECK и,
         если только Вы не установили NONIBM бит в слове атрибутов, коман-
         ды BUILDBPB. С помощью такого набора функций Вы, конечно, не смо-
         жете выполнять операции ввода/вывода,  однако  MS-DOS  сможет  по
         крайней мере успешно загрузить этот драйвер.
            Другой подход,  который может помочь в отладке драйверов, зак-
         лючается в использовании функций BIOS для вывода информации,  оп-
         ределяющей текущее состояние драйвера.  Знание места, до которого
         дошел драйвер, прежде чем аварийно завершиться очень помогает при
         отладке. Если у Вас нет ROM-BIOS, на который можно положиться, Вы
         можете  встроить в драйвер различные подпрограммы вывода.  Напри-

                                      - 6-48 -
         мер, отлаживая драйвер RDISK (приведенный в  конце  этой  главы),
         авторы  встроили в драйвер средства вывода на дисплей идентифици-
         рующего символа для каждой обрабатываемой команды ("I" для ПРЕРЫ-
         ВАНИЙ,  "S" для СТРАТЕГИЙ, "i" для INIT и т.д.). Это представляло
         действительную помощь когда драйвер загружался и был  доступен  с
         помощью  прерываний прямого доступа к диску,  но "сваливался" при
         попытке чтения директория диска. Взаимодействие между драйвером и
         системой  может  быть  одной  из самых сложных проблем  и,  к не-
         счастью, обычно может быть отлажено только после загрузки драйве-
         ра.
            Если Вы решили добавить отладочные команды к  Вашему драйверу,
         знайте,  что это вероятнее всего увеличит требуемую глубину стека
         и Вам,  возможно, придется использовать в драйвере локальный стек
         (если, конечно, Вы еще не сделали этого).
            При тестировании отдельных частей драйвера нет никакой необхо-
         димости отлаживать их после его загрузки.  Если Вам не жалко вре-
         мени,  потраченного на написание простой тестовой программы, соз-
         дающей  блоки запросов и передающей их драйверу для обработки, то
         Вы сможете использовать обычную программу DEBUG для отладки  тес-
         товой программы и самого драйвера. Это позволит Вам довести драй-
         вер до состояния,  при котором он уже может быть загружен,  после
         чего  использовать  другие способы отладки для исправления остав-
         шихся ошибок.
            При разработке  драйверов всегда пользуйтесь копией системного
         диска.  Ошибка в драйвере может привести к тому,  что система  не
         будет  загружаться или к разрушению каких-либо значимых данных на
         диске. По этим причинам Вам следует всегда иметь копию системного
         диска.

                Отображение списка загруженных в системе драйверов

            Очень часто при отладке драйверов полезно знать какие конкрет-
         но драйверы загружены в данный момент. На этот случай мы приводим
         текст небольшой программы, названной SD (SHOW DRIVERS -  показать
         драйверы). Примерный вид выводимой этой программой информации по-
         казан в листинге 6-5.
            Большинство отображаемых драйверов являются стандартными драй-
         верами MS-DOS,  эа исключением верхнего  драйвера  CON-устройства
         (драйвера консоли), который является драйвером ANSYI.SYS, и верх-
         него блокового драйвера, который является Bernulli Box драйвером.
         Нижний  блоковый  драйвер  является стандартным MS-DOS драйвером,
         поддерживающем одновременно один жесткий диск и два гибких.
            Колонка Attrib  содержит  слова  атрибутов драйверов,  колонка
         Address содержит начальный адрес каждого драйвера (взятый из поля
         связи  предыдущего в списке драйвера) и колонки STRAT и INTRP со-
         держат смещения программ СТРАТЕГИЙ и ПРЕРЫВАНИЙ от начала драйве-
         ра.  Исходный текст программы SD на языке ассемблера  приведен  в
         листинге  6-6.  Заметьте,  что  в программе SD используются файлы
         DRIVER.INC (листинг 6-7),  STDMAC.INC (листинг A-7, приложение A)
         и программа BIN2HEX файла STDLIB.LIB (листинг A-8, приложение A).


                                      - 6-49 -
          Листинг 6-5. Пример цепочки драйверов, выводимый программой SD
     --------------------------------------------------------------------
     SD-ShowDriv, Version 1.00, Copyright 1988 Kevin Jaeger
     Device         Type   Units   Attrib    Address       STRAT   INTRP
     -------------------------------------------------------------------
     NUL            Char    01      8004    0000:1898       1418    141E
     CON            Char    01      8013    08A9:0000       00A2    00AD
     --------      Block    02      0000    083D:0000       00A7    00B2
     CON            Char    01      8013    0070:0160       00A7    00B2
     AUX            Char    01      8000    0070:01F1       00A7    00B8
     PRN            Char    01      A000    0070:02A0       00A7    00C7
     CLOCK$         Char    01      8008    0070:034A       00A7    00DC
     --------      Block    03      0800    0870:0416       00A7    00E2
     COM1           Char    01      8000    0070:0203       00A7    00B8
     LPT1           Char    01      A000    0070:02B2       00A7    00C7
     LPT2           Char    01      A000    0070:0B13       00A7    00CD
     LPT3           Char    01      A000    0070:0B25       00A7    00D3
     COM2           Char    01      8000    0070:0B37       00A7    00BE
     <<< ------------------ End Of Driver List --------------------- >>>
     --------------------------------------------------------------------
                Листинг 6-6. Исходный текст программы SHOWDRIV.ASM
     ---------------------------------------------------------------------
     PAGE   60,132
     ; ************ SHOWDRIV *********************************************
     ;
     ; SHOWDRIV - Отображение списка загруженных драйверов MS-DOS
     ;
     ; ************ INCLUDES *********************************************
     ;
     INCLUDE        stdmac.inc
     INCLUDE        driver.inc
     ;
     ; ************ DGROUP (DATA) COMPONENT SEGMENTS *********************
     ;
     _DATA          SEGMENT BYTE PUBLIC 'DATA'
     _DATA          ENDS
     ;
     STACK          SEGMENT PARA STACK
                    dw      1024 dup (?)
     STACK          ENDS
     ;
     DGROUP         GROUP   _DATA, STACK
     ;
     ; ************ DATA STORAGE & TEMPLATES *****************************
     ;
     _DATA          SEGMENT
     ;
     ; параметры для поиска
     ;
     nuldev         db      'NUL     '              ; Имя NUL драйвера
     nulattr        dw      AT_CHR OR AT_NUL        ; Слово атрибутов
     ;
     ; Текстовые сообщения для вывода на дисплей. Формат :
     ;
     ; "Device         Type   Units   Attrib    Address     STRAT   INTRP"
     ; "-----------------------------------------------------------------"
     ; "xxxxxxxx       xxxx    xx      xxxx    xxxx:xxxx     xxxx    xxxx"

                                      - 6-50 -
     ; "<<< ------------------ End Of Driver List ------------------- >>>"
     ;
     $title db CR,LF
            db 'SD-ShowDriv, Version 1.00, Copyright 1988'
            db CR,LF,CR,LF
            db 'Device         Type   Units   Attrib    Address'
            db '       STRAT   INTRP'
            db CR,LF
            db '-----------------------------------------------'
            db '--------------------'
            db CR,LF,'$'
     $space db '    $'
     $block db '--------     Block    $'
     $char  db 'Char$'
     $colon db ':'
     $end   db CR,LF
            db '<<< ------------------ End Of Driver List -----'
            db '---------------- >>>'
     $crlf  db CR,LF,'$'
     ;
     ; Шаблон структуры
     ;
     devhead        STRUC                  ; Структура заголовка драйвера
            next    dd      ?              ; Указатель на следующего...
            attrib  dw      ?              ; Слово атрибутов
            strat   dw      ?              ; Смещение программы СТРАТЕГИЙ
            intrp   dw      ?              ; Смещение программы ПРЕРЫВАНИЙ
            dname   db      8 dup (?)      ; Имя/количество устройств
            term    db      ?              ; Конец заголовка драйвера
     devhead        ENDS
     ;
     _DATA          ENDS
     ;
     ; ************ ПРОГРАММА НАЧИНАЕТСЯ ЗДЕСЬ ***************************
     ;
     _TEXT  SEGMENT BYTE PUBLIC 'CODE'
            ASSUME  cs:_TEXT, ds:DGROUP, es:DGROUP, ss:DGROUP
     ;
            EXTRN   bin2hex:near           ; Шестнадцатиричный вывод
     main   PROC    FAR
            mov     ax,DGROUP              ; Установка сегмента данных
            mov     ds,ax
     ;
     ; Найдем NUL-драйвер с помощью поиска имени "NUL"
     ;
            cld
            mov     cx,0FFFEh              ; Счетчик для поиска
            xor     ax,ax                  ;
            mov     es,ax                  ; Начало поиска после таблицы
            mov     di,0400Hh              ;   прерываний
            mov     al,nuldev              ; Начинаем с поиска этой буквы
     search:
            repne   scasb                  ; Ищем пока не найдем
            jne     exit                   ; Не нашли...
     ;
            push    cx                     ; Возможно нашли...
            push    di                     ; Сохраним текущую позицию

                                      - 6-51 -
            mov     si,offset nuldev+1     ; Остаток строки "NUL     "
            mov     cx,7                   ; Длина остатка
            repe    cmpsb                  ; Сравним остаток строки
            jne     not_it                 ; Не совпадают...
     ;
            sub     di,(offset term - offset attrib) ; Выровняем указатель
            cmpsw                            ; Это атрибут NUL-драйвера ?
            jne     not_it                   ; Нет...
            add     sp,4                     ; Удаляем сохраненные DI и CX
            sub     di,(offset strat - offset next) ; Выравниваем указатель
            jmp     found_nul                ; Нашли заголовок NUL-драйвера!
     ;
     not_it:                                 ; Восстанавливаем позицию
            pop     di                       ;   и счетчик
            pop     cx
            jmp     short search
     ;
     ; Нашли заголовок NUL-драйвера. Теперь выводим всю цепочку
     ;
     found_nul:
            @DisStr $title                   ; Выводим название (титул)
     show_driver:
            call    ShowDeviceInfo           ; Отобразим заголовок драйвера
            cmp     word ptr es:[di],-1      ; Проверим на конец цепочки
            jne     done                     ; Если (-1) то на выход
            les     di,es:[di].next          ; Если не (-1) то на следующий
            jmp     short show_driver        ;   заголовок
     done:
            @DisStr $end                     ; Завершающее сообщение
            @DisStr $crlf
     ;
     exit:  mov     al,0                     ; Нормальное завершение
            @ExitToDOS                       ; Завершение программы
     main   ENDP
     ;
     ; ************ ShowDeviceInfo ****************************************
     ; Подпрограмма ShowDeviceInfo отображает блок, адресуемый по ES:DI,
     ; предполагая что это заголовок драйвера. Формат выводимой информации
     ; показан выше.
     ;
     ShowDeviceInfo PROC    NEAR
            test    es,[di].attrib,AT_CHR  ; Драйвер символьный или
            jnz     is_char                ;   блоковый ?
            @DisStr $block                 ; Блоковый (без имени)
            xor     ah,ah
            mov     al,es:[di].dname       ; Количество устройств
            jmp     short dis_units
     is_char:
            push    ds                     ; Сохраним DS
            push    es                     ; Выровняем сегменты
            pop     ds
            lea     si,es:[di].dname       ; SI = смещение имени
            mov     cx,8                   ; Длина имени
     show_name:
            lodsb                          ; Выводим по одному символу
            @DisChr al                     ;   за раз
            loop    show_name

                                      - 6-52 -
            pop     ds                     ; Восстанавливаем DS
            @DisStr @space
            @DisStr @char                  ; Выводим тип драйвера
            @DisStr @space
            mov     ax,1                   ; Только одно устройство
     ;
     dis_units:
            mov     ch,02                  ; Выводим количество устройств
            call    bin2hex
            @DisStr @space
            mov     ch,04                  ; Вывод числовых данных
            mov     ax,es:[di].attrib
            call    bin2hex                ; Выводим слово атрибутов
            @DisStr @space
     ;
            mov     ax,es
            call    bin2hex                ; Выводим сегментный адрес
            @DisChr $colon
            mov     ax,di
            call    bin2hex                ; Выводим смещение
            @DisStr @space
     ;
            mov     ax,es:[di].strat
            call    bin2hex                ; Выводим адрес СТРАТЕГИЙ
            @DisStr @space
     ;
            mov     ax,es:[di].intrp
            call    bin2hex                ; Выводим адрес ПРЕРЫВАНИЙ
            @DisStr @crlf
     ;
            ret
     ShowDeviceInfo ENDP
     ;
     ; ************ КОНЕЦ ПРОГРАММЫ, КОНЕЦ ФАЙЛА ************************
     ;
     _TEXT  ENDS
            END     main

     ---------------------------------------------------------------------


                           Листинг 6-7. Файл DRIVER.INC
     ---------------------------------------------------------------------
     ; ************ DRIVER.INC *******************************************
     ;
     ; Driver.Inc : Содержит определения и константы для использования при
     ; ассемблировании драйверов MS-DOS.
     ;
     ; ************ ОПРЕДЕЛЕНИЕ КОНСТАНТ, ИСПОЛЬЗУЕМЫХ В ДРАЙВЕРАХ *******
     ;
     ; Определение битов слова атрибутов драйвера :
     AT_CHR         EQU     1000000000000000b      ; Символьное устройство
     AT_IOCTL       EQU     0100000000000000b      ; Поддержка IOCTL
     AT_BUSY        EQU     0010000000000000b      ; Поддержка OTB
     AT_NOIBM       EQU     0010000000000000b      ; Не IBM устройство
     AT_NET         EQU     0001000000000000b      ; Сетевое устройство
     AT_OCRM        EQU     0000100000000000b      ; Поддержка OCRM

                                      - 6-53 -
     AT_GIOCTL      EQU     0000000001000000b      ; Поддержка GIOCTL
     AT_LOGICL      EQU     0000000001000000b      ; Get/Set Logical Dev
     AT_SPECL       EQU     0000000000010000b      ; Специальное устр-во
     AT_CLOCK       EQU     0000000000001000b      ; Устройство "ЧАСЫ"
     AT_NUL         EQU     0000000000000100b      ; Устройство NUL
     AT_STDOUT      EQU     0000000000000010b      ; Стандартные устр-ва
     AT_STDIN       EQU     0000000000000001b      ;   ввода и вывода
     ;
     ; Определение кодов ошибок драйверов устройств :
     WRITE_PROTECT          EQU      0
     UNKNOWN_UNIT           EQU      1
     NOT_READY              EQU      2
     UNKNOWN_UNIT           EQU      3
     CRC_ERROR              EQU      4
     BAD_REQUEST            EQU      5
     SEEK_ERROR             EQU      6
     UNKNOWN_MEDIA          EQU      7
     SECTOR_NOT_FOUND       EQU      8
     OUT_OF_PAPER           EQU      9
     WRITE_FAULT            EQU      0Ah
     READ_FAULT             EQU      0Bh
     GENERAL_FAILURE        EQU      0Ch
     INVALID_DISK_CHANGE    EQU      0Fh
     ;
     ; Статус, возвращаемый командой MEDIA CHECK :
     IsChanged      EQU     -1       ; носитель был заменен
     DontKnow       EQU     0        ; не известно была ли замена
     NotChanged     EQU     1        ; носитель был заменен
     ;
     ; ************ КОНЕЦ ФАЙЛА : DRIVER.INC *****************************

     ---------------------------------------------------------------------


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

            В конце этой главы, в листинге 6-10, мы приводим пример весьма
         упрощенного драйвера RAM-диска (т.е. драйвера виртуального диска,
         размещаемого в ОЗУ).  Несмотря на свою простоту,  драйвер на 100%
         работоспособен и может быть использован на любой  MS-DOS  системе
         начиная с версии 2.0 и выше. Драйвер RAM-диска, показанный в лис-
         тинге 6-10,  использует 360 Kбайт системной памяти  для  эмуляции
         стандартного пятидюймового дисковода.  Если Вы намерены использо-
         вать этот драйвер,  то Ваша система должна иметь по крайней  мере
         512 Kбайт памяти. Если Вы имеете меньше памяти или просто желаете
         иметь виртуальный диск меньших размеров,  то Вы  можете  изменить
         принимаемые  по  умолчанию  параметры,  которые  описаны в секции
         драйвера, помеченной как "Описание RAM-диска".
            Более элегантным решением изменения размеров RAM-диска являет-
         ся использование параметров командной строки.  Вспомните, что при
         входе  в  обработчик  команды  INIT  параметры  request.bpbtabo и
         request.bpbtabs содержат длинный указатель на  командную   строку
         драйвера. Эта строка может быть проверена на наличие переключате-
         лей и опций,  которые могут быть  использованы  для  конфигурации
         драйвера.  При  использовании  этого метода процедура INIT должна
         выполнить проверку,  скорректировать параметры в BPB и сегментный
         адрес завершения драйвера.

                                      - 6-54 -
            После того, как программа была обработана ассемблером и редак-
         тором связей,  переименуйте ее в RDISK.SYS.  Теперь создайте файл
         CONFIG.SYS (если,  конечно,  он еже не создан) и добавьте в  него
         командную строку :

                DEVICE=RDISK.SYS

            При первой  же перезагрузке драйвер будет установлен как драй-
         вер следующего по порядку дисковода (вероятно как драйвер  диско-
         вода C:,  если у Вас нет жесткого диска).  Ничего более для уста-
         новки драйвера RDISK не требуется.
            Доступ к RAM-диску возможен с помощью любых функций MS-DOS или
         программ,  за исключением команд DISKCOPY  и  DISKCOMP.  Обе  эти
         программы  ожидают  определенные  типы  дисков  и  не  работают с
         RAM-дисками.
            Драйвер RDISK,  приведенный в листинге 6-10,  содержит простой
         код, который может быть использован для отладки или  исследования
         драйверов. Он написан с использованием функций ввода/вывода уров-
         ня BIOS,  приведенных в листинге 6-8.  Для того, чтобы отладочный
         код располагался до адреса завершения драйвера,  RDISK включает в
         себя исходный текст файла BIOSIO.ASM (см.листинг  6-9).  Так  как
         библиотечные процедуры обычно добавляются редактором связей в ко-
         нец программы, их использование в драйверах устройств представля-
         ется проблематичным.
            Отладочный код может быть задействован путем включения  в файл
         RDISK оператора DEBUG EQU 1 или, при использовании Microsoft MASM
         версии 4 или более поздней,  указанием в командной  строке  опции
         /DDEBUG.
            Во время выполнения отладочный код использует ряд команд драй-
         вера в качестве индекса в таблице message_table.  Элементами таб-
         лицы message_table являются адреса  строк,  представляющих  имена
         команд,  находящихся  в  области  данных,  предшествующей таблице
         message_table.Эти текстовые строки отображаются с помощью аппара-
         турозависимой  процедуры  _biosprt.  В  драйвере  RDISK процедура
         _biosprt использует адаптер EGA с цветным монитором, что позволя-
         ет легко отличать отладочный текст от обычных сообщений MS-DOS.


                           Листинг 6-8. Файл BIOSIO.INC
     ----------------------------------------------------------------------
     ; ************ BIOSIO.INC ********************************************
     ;
     ; BiosIO.Inc содержит константы для использования процедур BIOS уровня
     ; находящихся в файле STDLIB.LIB
     ;
     ; Макрокоманда @Video для использования с видеопроцедурами
     ;
     @Video MACRO   function
            mov     ah,function
            int     10h
            ENDM
     ;
     ; ************ BIOS I/O Equates **************************************
     ;
     ; Эти определения поддерживают использование ввода/вывода уровня BIOS.
     ;
     ; Определения функций видеосервиса BIOS (INT 10H)

                                      - 6-55 -
     SET_CURSOR_POS EQU 02H ;; BH = страница, DH = строка, DL = колонка
     GET_CURSOR_POS EQU 03H ;; BH = страница; строка => DH, колонка => DL
     SET_PAGE       EQU 05H ;; AL => страница
     SCROLL_UP      EQU 06H ;; AL = #строк, BH => атрибут, C(x) = верхняя
     SCROLL_DOWN    EQU 07H ;;    левая, D(x) = нижняя правая,
                            ;;    (x)H = строка, (x)L = колонка
     READ_CHR_ATR   EQU 08H ;; BH = страница; атр. => AH, симв. => AL
     WRITE_CHR_ATR  EQU 09H ;; BH = страница, CX = 1, AL = симв., BL = атр.
     WRITE_CHAR     EQU 0AH ;; BH = страница, CX = 1, AL = симв., без атр.
     WRITE_TEXT     EQU 0EH ;; BH = страница, AL = символ
     GET_MODE       EQU 0FH ;; режим => AL, #колонок => AH, страница => BH
     ;
     ; Атрибуты символов при использовании адаптера EGA
     BLINK          EQU     10000000b
     BRIGHT         EQU     00001000b
     BLACK_F        EQU     00h
     BLUE_F         EQU     01h
     GREEN_F        EQU     02h
     CYAN_F         EQU     03h
     RED_F          EQU     04h
     MAGENTA_F      EQU     05h
     YELLOW_F       EQU     06h
     WHITE_F        EQU     07h
     BLACK_B        EQU     10h
     BLUE_B         EQU     10h
     GREEN_B        EQU     20h
     CYAN_B         EQU     30h
     RED_B          EQU     40h
     MAGENTA_B      EQU     50h
     YELLOW_B       EQU     60h
     WHITE_B        EQU     70h
     ;
     ; ************ КОНЕЦ ФАЙЛА BIOSIO.INC ********************************


                           Листинг 6-9. Файл BIOSIO.ASM
     ----------------------------------------------------------------------
     PAGE   60,132
     PUBLIC _biosprt
     ; ************ BIOSIO.ASM ********************************************
     ; BIOSIO: Содержит процедуры для выполнения ввода/вывода на
     ; уровне BIOS, используя стандартные вызовы BIOS. Эти процедуры
     ; предназначены для целей отладки.
     ;
     IFNDEF DEBUG           ; если не часть DEBUG, то должна быть часть
                            ; от LIBRARY, и должна включать наши
                            ; собственные определения
     ; ************ INCLUDES **********************************************
     ;
     INCLUD biosio.inc       ; BIOS I/O difinition
     ;
     ; ************ DGROUP (DATA) COMPONENT SEGMENTS **********************
     _DATA   SEGMENT BYTE PUBLIC 'DATA'
     _DATA   ENDS
     ;
     DGROUP  GROUP   _DATA
     ;

                                      - 6-56 -
     ;************* PROGRAM CODE STARTS HERE ******************************
     ;
     _TEXT   SEGMENT BYTE PUBLIC 'CODE'
             ASSUME  cs:_TEXT, ds:DGROUP, es:DGROUP, ss:DGROUP
     ENDIF
     ;
     ; Шаблон структуры, описывающей состояние стека для _BIODPRT
     bpframe STRUC
                    dw      ?                ; Старый BP
                    dw      ?                ; адрес возврата
             p1     dw      ?                ; параметр #1
             p2     dw      ?                ; параметр #2
             p3     dw      ?                ; параметр #3
             p4     dw      ?                ; параметр #4
     bpframe ENDS
     prtbase EQU    [bp]
     ;
     ; _BIOSPRT
     ; Эта подпрограмма выполняет вывод на экран на уровне BIOS и
     ; используется для отладки драйвера. Подпрограмма использует
     ; видеорежим 03h : 80*25 цветной текст
     ;
     ; Эквивалентный языку Си синтаксис вызова : biosprt(string,color)
     ;
     _biosprt        PROC    NEAR
             push    bp
             mov     bp,sp
             push    si
             push    cx
             push    bx
     ;
             @Video  GET_MODE                 ; Получить номер тек.страницы
             mov     si,word prt [prtbase.p1] ; адрес строки
             mov     bl,byte prt [prtbase.p2] ; атрибут
             mov     cx,1
     ;
     biosprtloop:
             lodsb                           ; Берем очередной символ
             or      al,al                   ; Строка завершается нулем
             jz      biosprtdone
             cmp     al,'$'                  ; или завершается "$"
             jz      biosprtdone
             push    ax
             mov     al,020h
             @Video  WRITE_CHR_ATR           ; Пробел с атрибутом
             pop     ax
             @Video  WRITE_TEXT              ; Символ в режиме TTY
             jmp     biosprtloop             ; Следующий символ
     ;
     biosprtdone
             pop     bx
             pop     cx
             pop     si
             pop     bp
             ret
     _boisprt        ENDP
     ;

                                      - 6-57 -
     IFNDEF  DEBUG                           ; если не включено как часть
     _TEXT   ENDS                            ; DEBUG, то потребуются наши
     ENDIF                                   ; собственные ENDS
     ;
     ; ************ КОНЕЦ ФАЙЛА BIOSIO.ASM ********************************
     ;       END            ; При использовании в библиотеке, удалите ";"

     ----------------------------------------------------------------------

                  Листинг 6-10. Исходный текст драйвера RAM-диска
     ----------------------------------------------------------------------
     PAGE 60,132
     ; ************ RDISK.ASM : MS-DOS ДРАЙВЕР RAM-ДИСКА ******************
     ;
     ; Этот файл содержит исходный текст простого MS-DOS драйвера RAM-диска
     ; эмулирующего 360K флоппи-диск.
     ;
     ; В этом примере демонстрируются основные принципы построения драйвера
     ; устройств, включая один из методов, который можно использовать для
     ; отладки драйверов. Для установки этого драйвера включите в файл
     ; CONFIG.SYS строку "DEVICE=RDISK.SYS"
     ;
     ; ============ ВСПОМОГАТЕЛЬНЫЕ ФАЙЛЫ ДЛЯ ДРАЙВЕРА ====================
     ;
     INCLUDE driver.inc                     ; Константы для MS-DOS драйвера
     IFDEF   DEBUG
     INCLUDE biosio.inc                     ; Определения для отладки
     ENDIF
     ;
     ; ============ КОНСТАНТЫ =============================================
     ;
     ; Ограничения,накладываемые версией MS-DOS на максимальный код команды
     ;
     CMD_PRE_30               EQU      00Ch  ;  до MS-DOS версии 3.00
     CMD_PRE_32               EQU      00Fh  ;  до MS-DOS версии 3.20
     CMD_32                   EQU      018h  ;  начиная с версии 3.20
     ;
     IFDEF           DEBUG
     CR                       EQU      0Ah   ; используются в отладочных
     LF                       EQU      0Dh   ;   сообщениях
     ENDIF
     ;
     PAGE
     ;
     ; ============ ШАБЛОНЫ СТРУКТУР ======================================
     ;
     request         EQU      es:[di]        ; указатель на блок запроса
     ;
     ; Структура заголовка запроса
     ;
     reqhdr          STRUC
                     rlength  db      ?              ; размер блока запроса
                     unit     db      ?              ; номер устройства
                     command  db      ?              ; код команды
                     status   dw      ?              ; возвращаемый статус
                              db      8 DUP (?)      ; зарезервировано
     reghdr          ENDS

                                      - 6-58 -
     ;
     ; Структура блока запроса для команды INIT
     ;
     inithdr         STRUC
                              db      (type reqhdr) DUP (?)
                     units    db      ?              ; количество устройств
                     endadro  dw      ?              ; смещение и сегмент
                     endadrs  dw      ?              ;   адреса завершения
                     bpbtabo  dw      ?              ; смещение и сегмент
                     bpbtabs  dw      ?              ;   таблицы BPB
                     devnum   db      ?              ; номер устройства
     inithdr         ENDS
     ;
     ; Структура блока запроса для команды MEDIA CHECK
     ;
     mchkhdr         STRUC
                              db       (type reqhdr) DUP (?)
                     mbd      db      ?             ; описатель носителя
                     chande   dw      ?             ; статус замены
                     volume   dd      ?             ; указатель на имя тома
     mchkhdr         ENDS
     ;
     ; Структура блока запроса для команды BUILD BPB
     ;
     bpbhdr          STRUC
                              db       (type reqhdr) DUP (?)
                              db      ?              ; описатель носителя
                              dd      ?              ; указатель на FAT
                     bpbptro  dw      ?              ; смещение BPB
                     bpbptrs  dw      ?              ; сегмент BPB
     bpbhdr          ENDS
     ;
     ; Структура блока запроса для команд чтения/записи
     ;
     iohdr           STRUC
                              db       (type reqhdr) DUP(?)
                              db      ?             ; описатель носителя
                     bufprt   dd      ?             ; адрес буфера
                     count    dw      ?             ; кол-во байт/секторов
                     start    dw      ?             ; # начального сектора
                     nuvol    dd      ?             ; адрес нов. имени тома
     iohdr           ENDS
     ;
     ; Структура блока параметров BIOS (BPB)
     ;
     bpbstrc         STRUC
                     bps      dw      ?      ; количество байтов в секторе
                     spau     db      ?      ; кол-во секторов в кластере
                     nrs      dw      ?      ; кол-во зарезервир. секторов
                     nft      db      ?      ; количество копий FAT
                     nde      dw      ?      ; кол-во элементов директория
                     nls      dw      ?      ; кол-во логических секторов
                     md       db      ?      ; байт описателя носителя
                     nfs      dw      ?      ; размер FAT в секторах
     bpbstrc         ENDS
     ;

                                      - 6-59 -
     PAGE
     ;
     ; ============= НАЧАЛО КОДА ДРАЙВЕРА =================================
     ;
     _TEXT           SEGMENT  BYTE    PUBLIC 'CODE'
                     ASSUME   CS:_TEXT, DS:_TEXT, ES:NOTHING
                     ORG      0
     ORIGIN          EQU      $
     ;
     ; ============= ЗАГОЛОВОК ДРАЙВЕРА ===================================
     ;
                     dw       -1,-1          ; указатель на след. драйвер
                     dw       AT_IOCTL OR AT_OCRM OR AT_NET
                     dw       offset STRATEGRY       ; смещение СТРАТЕГИЙ
                     dw       offset ПРЕРЫВАНИЙ       ; смещение ПРЕРЫВАНИЙ
                     db       1,'CDEVICE'            ; кол-во устройств/имя
     ;
     ; ============= ТАБЛИЦА АДРЕСОВ ОБРАБОТЧИКОВ КОМАНД ==================
     ;
     JUMPTAB        LABEL   WORD
                    dw      offset INIT             ; 0 - инициализация
                    dw      offset MEDIA_CHECK      ; 1 - проверка носителя
                    dw      offset BUILD_BPB        ; 2 - построить BPB
                    dw      offset IOCTL_INPUT      ; 3 - IOCTL ввод
                    dw      offset READ             ; 4 - ввод из устр-ва
                    dw      offset READ_NOWAIT      ; 5 - неразруш. ввод
                    dw      offset INPUT_STATUS     ; 6 - ввод статуса
                    dw      offset INPUT_FLUSH      ; 7 - сбросить ввод
                    dw      offset WRITE            ; 8 - вывод на устр-во
                    dw      offset WRITE_VERIFY     ; 9 - вывод с проверкой
                    dw      offset OUTPUT_STATUS    ; A - вывод статуса
                    dw      offset OUTPUT_FLUSH     ; B - сбросить вывод
                    dw      offset IOCTL_OUTPUT     ; C - вывод IOCTL
                    dw      offset DEVICE_OPEN      ; D - открыть устр-во
                    dw      offset DEVICE_CLOSE     ; E - закрыть устр-во
                    dw      offset REMOVABLE        ; F - носитель сменный?
                    dw      offset NO_COMMAND       ; 10
                    dw      offset NO_COMMAND       ; 11
                    dw      offset NO_COMMAND       ; 12
                    dw      offset GENERIC_IOCTL    ; 13 - Generic IOCTL
                    dw      offset NO_COMMAND       ; 14
                    dw      offset NO_COMMAND       ; 15
                    dw      offset NO_COMMAND       ; 16
                    dw      offset GET_LOGICAL      ; 17 - получить/устано-
                    dw      offset SET_LOGICAL      ; 18 - вить лог.устр-во
     ;
     ; ============ ОБЛАСТЬ ДАННЫХ ДРАЙВЕРА ===============================
     ;
     reg_ptr        dd       ?               ; адрес блока запроса
     max_cmd        db       CMD_PRE_30      ; максимально допустимый код
     ;                                       ;   команды
     save_ss        dw       ?               ; значение SS на входе
     save_sp        dw       ?               ; значение SP на входе
     ;

                                      - 6-60 -
     PAGE
     ;
     ; ============ ПРОГРАММА СТРАТЕГИЙ ====================================
     ;
     STRATEGY                PROC    FAR
                    mov     cs:word ptr [reg_ptr],bx
                    mov     cs:word ptr [reg_ptr+2],es
                    ret
     strategy               ENDP
     ;
     ; ============ ПРОГРАММА ПРЕРЫВАНИЙ ===================================
     ;
     INTERRUPT               PROC     FAR
                    push    ax               ; сохранить все рабочие
                    push    cx               ;   регистры
                    push    dx
                    push    bx
                    push    bp
                    push    si
                    push    di
                    push    ds
                    push    es
     ;
                    push    cs               ; определим локальный сегмент
                    pop     ds               ;   данных
     ;
                    mov     word ptr save_ss,ss      ; сохраним входное
                    mov     word ptr save_sp,sp      ; значение SS и SP
     ;
                    mov     bx,cs                    ; установим локальный
                    mov     ax,offset local_stack - 2        ; стек
                    mov     ss,bx
                    mov     sp,ax
     ;
                    les     di,[req_ptr]             ; получить адрес блока
                    mov     bl,request.command       ; запроса и команду
     ;
     ; установим заранее код ошибки на случай если команда неверная
     ;
                    mov     ax,(ST_ERROR OR UNKNOWN_COMMAND)
                    cmp     bl,[max_cmd]     ; команда поддерживается ?
                    ja      exit             ; нет - отвергаем ее
     ;
     ; Выдаем указанную команду на выполнение соответствующему обработчику.
     ; Каждый обработчик получает управление с CS и DS установленными на
     ; сегмент драйвера и ES:DI указывающем на блок запроса. Свой статус
     ; обработчики возвращают в регистре AX.
     ;
                    xor     bh,bh            ; BX - индекс в таблице
                    shl     bx,1             ;   команд
     IFDEF          DEBUG
                    call    print_command    ; выдаем имя обрабатываемой
     ENDIF                                   ;   команды
                    call    word ptr jumptab[bx]     ; вызываем обработчик
     ;
     ; Перешлем статус из регистра AX в слово состояния блока запроса
     ;
     exit:          push    cs               ; установка локального

                                      - 6-61 -
                    pop     ds               ;   сегмента данных
     ;
                    les     di,[req_ptr]     ; получим адрес блока запроса
                    or      ax,ST_DONE       ; установим бит DONE
                    mov     request.status,ax        ; сохраним статус
     ;
                    mov     ss,word ptr save_ss      ; восстановим значение
                    mov     sp,word ptr save_sp      ;   регистров SS:SP
     ;
                    pop     es               ; восстановим содержимое
                    pop     ds               ;   регистров
                    pop     di
                    pop     si
                    pop     bp
                    pop     bx
                    pop     dx
                    pop     cx
                    pop     ax
                    ret
     interrupt      ENDP
     ;
     PAGE
     ;
     ; ============ ОБРАБОТЧИКИ КОМАНД ====================================
     ;
     NO_COMAND      PROC    NEAR     ; неподдерживаемая команда
            ret                      ; возврат с ошибкой
     NO_COMMAND     ENDP
     ;
     MEDIA_CHECK    PROC    NEAR     ; 1 - проверка носителя
            mov     request.change,NotChanged
            xor     ax,ax
            ret
     MEDIA_CHECK    ENDP
     ;
     BUILD_BPB      PROC    NEAR     ; 2 - построить BPB
            mov     request.bpbptro,offset bpb
            mov     request.bpbptrs,cs
            xor     ax,ax
            ret
     BUILD_BPB      ENDP
     ;
     IOCTL_INPUT    PROC    NEAR     ; 3 - ввод IOCTL
            xor     ax,ax
            ret
     IOCTL_INPUT    ENDP
     ;
     READ           PROC    NEAR     ; 4 - ввод из устройства
            call    verify           ; проверка и установка параметров
            jc      rd_err           ; выход по ошибке
            les     di,request.bufptr   ; считываем в буфер
            rep     movsw            ; передача
            xor     ax,ax            ; нет ошибок
     rd_err:
            ret
     READ           ENDP
     ;

                                      - 6-62 -
     READ_NOWAIT    PROC    NEAR     ; 5 - неразрушающий ввод
            xor     ax,ax            ;     без ожидания
            ret
     READ_NOWAIT    ENDP
     ;
     INPUT_STATUS   PROC    NEAR     ; 6 - ввод статуса
            xor     ax,ax
            ret
     INPUT_STATUS   ENDP
     ;
     INPUT_FLUSH    PROC    NEAR     ; 7 - сбросить входную очередь
            xor     ax,ax
            ret
     INPUT_FLUSH    ENDP
     ;
     WRITE          PROC    NEAR     ; 8 - вывод на устройство
            call    verify           ; проверка и установка параметров
            jc      wr_err           ; выход при ошибке
            push    ds               ; сохраним сегмент "сектора"
            lds     si,request.bufptr   ; записываем из буфера
            pop     es               ; на диск
            xor     di,di            ; с нулевым смещением
            rep     movsw            ; передача
            xor     ax,ax            ; нет ошибок
     wr_err:
            ret
     WRITE          ENDP
     ;
     WRITE_VERIFY   PROC    NEAR     ; 9 - вывод с проверкой
            call    write
            ret
     WRITE_VERIFY   ENDP
     ;
     OUTPUT_STATUS  PROC    NEAR     ; A - вывод статуса
            xor     ax,ax
            ret
     OUTPUT_STATUS  ENDP
     ;
     OUTPUT_FLUSH   PROC    NEAR     ; B - сбросить выходную очередь
            xor     ax,ax
            ret
     OUTPUT_FLUSH   ENDP
     ;
     IOCTL_OUTPUT   PROC    NEAR     ; C - вывод IOCTL
            xor     ax,ax
            ret
     IOCTL_OUTPUT
     ;
     DEVICE_OPEN    PROC    NEAR     ; D - открыть устройство
            xor     ax,ax
            ret
     DEVICE_OPEN    ENDP
     ;
     DEVICE_CLOSE   PROC    NEAR     ; E - закрыть устройство
            xor     ax,ax
            ret
     DEVICE_CLOSE   ENDP

                                      - 6-63 -
     ;
     REMOVABLE      PROC    NEAR     ; F - носитель сменный ?
            mov     ax,ST_BUSY       ; нет !
            ret
     REMOVABLE      ENDP
     ;
     GENERIC_IOCTL  PROC    NEAR     ; 13 - групповой IOCTL запрос
            xor     ax,ax
            ret
     GENERIC_IOCTL  ENDP
     ;
     GET_LOGICAL    PROC    NEAR     ; 17 - получить имя логического
            xor     ax,ax            ;      диска
            ret
     GET_LOGICAL    ENDP
     ;
     SET_LOGICAL    PROC    NEAR     ; 18 - установить имя логического
            xor     ax,ax            ;      диска
            ret
     SET_LOGICAL    ENDP
     ;
     PAGE
     ; ------------ Подпрограммы обработки запросов -----------------------
     ; Эти подпрограммы вызываются для обработки параметров любого запроса
     ; на ввод/вывод.
     ; На входе :
     ;    ES:DI - содержит адрес блока запроса
     ; Действия :
     ;    Проверка параметра "номер сектора" на допустимость.
     ;    Преобразование этого параметра в "сегмент:смещение".
     ;    Выровнять счетчик для предотвращения "перекрытия".
     ; На выходе :
     ;    DS:SI - содержит адрес "сектора" в RAM-диске
     ;    ES:DI - содержит адрес блока запроса
     ;    CX - содержит количество передаваемых слов.
     ;
     verify PROC    NEAR
     ; проверим,что номера начального и конечного секторов лежат в пределах
     ; от 0 до N.
            mov     cx,request.start         ; сравним номер начального
            cmp     cx,bpb.nls               ;   сектора с количеством
            jae     out_of_range             ;   логических секторов
            add     cx,request.count         ; найдем номер конечного
            dec     cx                       ;   сектора и тоже сравним
            cmp     cx,bpb.nls               ; если номера секторов
            jb      in_range                 ;   нормальные то продолжим
     ; заданные секторы не содержатся на диске
     out_of_range:
            mov     ax,ST_ERROR OR SECTOR_NOT_FOUND
            mov     request.count,0          ; ничего не было передано
            stc                              ; возвращаемся с ошибкой
            ret
     ; вычислим сегментный адрес начального сектора
     in_range:
            mov     ax,bpb.bps               ; количество байт в секторе
            mov     cl,4                     ; разделим на 16 для получения
            shr     ax,cl                    ;   размера в параграфах

                                      - 6-64 -
            mul     request.start            ; смещение параграфа относи-
                                             ;   тельно начала диска
            add     ax,RPARA                 ; смещение параграфа относи-
            mov     dx,cs                    ;   тельно CS
            add     ax,dx                    ; абсолютное смещ. параграфа
            mov     si,ax                    ; сохраним сегмент в SI
     ; вычислим и проверим счетчик передаваемых данных
            mov     ax,bpb.bps               ; размер сектора в байтах
            mul     request.count            ; счетчик передачи в байтах
            cmp     dx,0                     ; проверим на корректность
            jne     out_of_range
     ; выровняем счетчик в AX для предотвращения перекрытия
            mov     cx,word ptr request.bufptr
            cmp     ax,0                     ; смещение = 0
            je      set_size
            neg     cx                       ; остаток = 64K - смещение
            cmp     cx,ax                    ;   буфера
            jae     set_size                 ; если остаток меньше счетчика,
            mov     ax,cx                    ;   то передаем только остаток
     ; установим количество передаваемых секторов и счетчик передачи
     set_size:
            mov     cx,ax                    ; счетчик передачи в байтах
            shr     cx,1                     ; преобразуем в счетчик слов
            div     bpb.bps                  ; (DX был 0) кол-во секторов
            mov     request.count,ax         ; сохраним счетчик передачи
     ; загрузим в DS:SI адрес блока в памяти
            mov     ds,si
            xor     si,si
     ; установим направление передачи и вернемся без ошибок
            cld
            clc
            ret
     verify ENDP
     ;
     IFDEF  DEBUG
     INCLUDE        biosio.asm
     PAGE
     ;
     ; ************ КОД И ДАННЫЕ ДЛЯ ОТЛАДКИ ******************************
     ;
     ; Отладочные сообщения
     ;
     NO_COMMAND_msg    db   'NO COMMAND',CR,LF,'$'
     INIT_msg          db   'INITialization',CR,LF,'$'
     MEDIA_CHECK_msg   db   'MEDIA Check',CR,LF,'$'
     BUILD_BPB_msg     db   'Build BIOS Parameter Block',CR,LF,'$'
     IOCTL_INPUT_msg   db   'IO Control Input',CR,LF,'$'
     READ_msg          db   'Input from Device',CR,LF,'$'
     READ_NOWAIT_msg   db   'Nondestructive Input no-wait',CR,LF,'$'
     INPUT_STATUS_msg  db   'Input Status',CR,LF,'$'
     INPUT_FLUSH_msg   db   'Flush Input Queue',CR,LF,'$'
     WRITE_msg         db   'Output to Device',CR,LF,'$'
     WRITE_VERIFY_msg  db   'Output with Verify',CR,LF,'$'
     OUTPUT_STATUS_msg db   'Output Status',CR,LF,'$'
     OUTPUT_FLUSH_msg  db   'Flush Output Queue',CR,LF,'$'
     IOCTL_OUTPUT_msg  db   'IO Control Output',CR,LF,'$'
     DEVICE_OPEN_msg   db   'Open a Device',CR,LF,'$'

                                      - 6-65 -
     DEVICE_CLOSE_msg  db   'Close a Device',CR,LF,'$'
     REMOVABLE_msg     db   'Is Media Removable',CR,LF,'$'
     GENERIC_IOCTL_msg db   'Generic IOCTL Request',CR,LF,'$'
     GET_LOGICAL_msg   db   'Get Logical Device',CR,LF,'$'
     SET_LOGICAL_msg   db   'Set Logical Device',CR,LF,'$'
     ;
     PAGE
     ;
     ; ============= ТАБЛИЦА АДРЕСОВ ОТЛАДОЧНЫХ СООБЩЕНИЙ =================
     ;
     message_table  LABEL   WORD
            dw      offset INIT_msg          ; 01 - инициализация
            dw      offset MEDIA_CHECK_msg   ; 02 - проверка носителя
            dw      offset BUILD_BPB_msg     ; 03 - построить BPB
            dw      offset IOCTL_INPUT_msg   ; 04 - ввод IOCTL
            dw      offset READ_msg          ; 05 - ввод из устройства
            dw      offset READ_NOWAIT_msg   ; 06 - неразруш. ввод без ожид.
            dw      offset INPUT_STATUS_msg  ; 07 - ввод статуса
            dw      offset INPUT_FLUSH_msg   ; 08 - сброс входной очереди
            dw      offset WRITE_msg         ; 09 - вывод на устройство
            dw      offset WRITE_VERIFY_msg  ; 10 - вывод с проверкой
            dw      offset OUTPUT_STATUS_msg ; 11 - вывод статуса
            dw      offset OUTPUT_FLUSH_msg  ; 12 - сброс выходной очереди
            dw      offset IOCTL_OUTPUT_msg  ; 13 - вывод IOCTL
            dw      offset DEVICE_OPEN_msg   ; 14 - открыть устройство
            dw      offset DEVICE_CLOSE_msg  ; 15 - закрыть устройство
            dw      offset REMOVABLE_msg     ; 16 - носитель сменный ?
            dw      offset NO_COMMAND_msg    ; 17 -
            dw      offset NO_COMMAND_msg    ; 18 -
            dw      offset NO_COMMAND_msg    ; 19 -
            dw      offset GENERIC_IOCTL_msg ; 20 - групповой IOCTL запрос
            dw      offset NO_COMMAND_msg    ; 21 -
            dw      offset NO_COMMAND_msg    ; 22 -
            dw      offset NO_COMMAND_msg    ; 23 -
            dw      offset GET_LOGICAL_msg   ; 24 - получить имя диска
            dw      offset SET_LOGICAL_msg   ; 25 - установить имя диска
     ;
     PAGE
     ; PRINT_COMMAND
     ;
     ; Эта процедура вызывает функцию BIOS для печати (_biosprt), передавая
     ; ей адрес строки, содержащей имя только что вызванной команды. При
     ; вызове этой процедуры удвоенный код команды передается в регистре BX.
     ; Все используемые регистры сохраняются.
     ;
     print_command  PROC    NEAR
            push    ax                       ; сохраним содержимое рег. AX
            mov     ax, BLUE_F OR BRIGHT OR BLACK_B  ; установим цвет
            push    ax
            mov     ax,word ptr message_table[bx]    ; адрес строки
            push    ax
            call    _biosprt                 ; вызываем процедуру BIOS
            add     sp,4                     ; очищаем стек от параметров
            pop     ax                       ; восстанавливаем AX и выходим
            ret
     print_command  ENDP
     ENDIF

                                      - 6-66 -
     ;
     PAGE
     ;
     ; ******* ВНУТРЕННИЙ СТЕК И КОНЕЦ ОПЕРАЦИОННОЙ ЧАСТИ ДРАЙВЕРА ********
     ;
            db      32 DUP ('stack   ')      ; внутренний стек глубиной
     local_stack    EQU     $                ;   256 байт
     ;
     bpb_tab        dw      offset bpb       ; указатель на BPB
     ;
     LAST_USED      EQU     $                ; адрес завершения
     ;
     ; ******* ХАРАКТЕРИСТИКИ RAM-ДИСКА, ПРИНИМАЕМЫЕ ПО УМОЛЧАНИЮ **********
     ;
     ; Параметры для 5-1/4" двустороннего двойной плотности диска с девятью
     ; секторами на дорожке.
     ;
     MTYPE   EQU    0FDh             ; байт описателя носителя
     TRACKS  EQU    40               ; 40 дорожек
     SECTORS EQU    9                ; 9 секторов на дорожке
     DSIZE   EQU    512              ; 512 байт в секторе
     SIDES   EQU    2                ; 2 стороны на диске
     ;
     FSECS   EQU    2                ; количество секторов в FAT
     DIREN   EQU    112              ; количество элементов директория
     DSECS   EQU    7                ; 7 секторов в директории
     CLSIZ   EQU    2                ; 2 сектора в кластере
     ;
     STOTAL  EQU    TRACKS*SECTORS*SIDES     ; всего секторов
     PTOTAL  EQU    (DSIZE/16)*STOTAL        ; всего параграфов
     ;
     ; ************ НАЧАЛО ОБЛАСТИ ДАННЫХ RAM-ДИСКА ***********************
     ;
     ; RAM-диск д.б. выровнен на границу параграфа
     ;
            IF      ($-ORIGIN) mod 16
            ORG     ($-ORIGIN) + 16 - (($-ORIGIN) mod 16)
            ENDIF
     RDISK  LABEL   BYTE             ; начало RAM-диска
     RPARA  EQU     ($-ORIGIN)/16    ; размер кода в параграфах
     ;
     ; ------------ Блок параметров BIOS ----------------------------------
     ;
            jmp     short boot       ; короткий JMP (2 байта)
            nop                      ; требуется для boot_record
            db      'IBM  3.1'       ; 8 байт имя и версия
     ;
     bpb    bpbstrc <DSIZE,CLSIZ,1,2,DIREN,STOTAL,MTYPE,FSECS>
            dw      SECTORS          ; количество секторов на дорожке
            dw      SIDES            ; количество головок чтения/записи
            dw      0                ; количество скрытых секторов
     boot:
            db      (DSIZE-30) DUP (?)       ; остаток boot_sector
     ;
     ; ------------ Таблицы размещения файлов (FAT) -----------------------
     ;                                       ; первые два элемента FAT
     FAT_1  db      MTYPE,0FFh,0FFh          ; нулевой остаток FAT

                                      - 6-67 -
            db      (DSIZE-3) DUP (0)
            db      ((FSECS-1) * DSIZE) DUP (0)
     FAT_2  db      MTYPE,0FFh,0FFh          ; первые два элемента FAT
            db      (DSIZE-3) DUP (0)        ; нулевой остаток FAT
            db      ((FSECS-1) * DSIZE) DUP (0)
     ;
     ; ------------ Сектора директория ------------------------------------
     ;
     DIREC  db      'RAM_DISK   '            ; имя тома (11 байт)
            db      08h                      ; VID
            db      10 DUP (?)               ; зарезервировано
            dw      0600h                    ; время 12:00:00 (полдень)
            dw      021h                     ; дата 1 января 1980 года
            dw      0                        ; начальный кластер 0
            dd      0                        ; размер файла 0
            db      (DSIZE-32) DUP (0)       ; нулевой остаток директория
            db      ((DSECS-1) * DSIZE) DUP (0)
     BUFFER LABEL   BYTE                     ; начало области данных
     ;
     ; ************ ПРОЦЕДУРА ИНИЦИАЛИЗАЦИИ *******************************
     ;
     INCLUDE        stdmac.inc
     ;
     ; ============ Область данных инициализации ==========================
     ;
     $signon        db      'RAM DISK Driver Version 1.00 Installed: Drive'
     $desig         db      'A'
     $crlf          db      0Dh,0Ah,'$'
     ;
     ; ============ Начало процедуры инициализации ========================
     ;
     INIT   PROC    NEAR             ; 00 - инициализация
     ;
     ; установим адрес завершения, количество устройств и указатель на
     ; таблицу BPB
     ;
            mov     request.endadro,0        ; адрес конца драйвера
            mov     request.endadrs,cs
            add     request.endadrs,(RPARA+PTOTAL)   ; последний параграф
            mov     request.units,1
            mov     request.bpbtabo,offset bpb_tab
            mov     request.bpbtabs,cs
            mov     al,$desig                ; скорректируем имя диска
            add     al,request.devnum
            mov     $desig,al
     ;
     ; вывод на экран идентификационной строки
            @DisStr $signon
     ;
     ; скорректируем значение "max_cmd" исходя из версии MS-DOS
            @GetDOSVersion                   ; получим номер версии MS-DOS
            cmp     al,3                     ; MS-DOS версии 3.00 и выше ?
            jb      init_done             ; нет - прекращаем инициализацию
            mov     [max_cmd],CMD_PRE_32     ; команды для MS-DOS 3.00
            cmp     ah,2                     ; MS-DOS версии 3.20 и выше ?
            jb      init_done             ; нет - прекращаем инициализацию
            mov     [max_cmd],CMD_32         ; команды для MS-DOS 3.20

                                      - 6-68 -
     ;
     init_done:
            xor     ax,ax                    ; нет проблем !
            ret
     INIT   ENDP
     ;
     ; ************ КОНЕЦ ДРАЙВЕРА. КОНЕЦ ФАЙЛА ***************************
     ;
     _TEXT  ENDS
            END

     ---------------------------------------------------------------------

                                    Заключение

            Теперь Вы готовы писать и устанавливать свои собственные драй-
         веры  устройств.  Руководствуйтесь  нашими  замечаниями и "MS-DOS
         Programmers Reference Manual" при возникновении каких-либо техни-
         ческих вопросов.
                                      ╔═══════════╗
                                      ║   Вывод   ╟─┐
                                      ║ программы ║ │
                                      ╚═╤═════════╝ │
                                        └───║───────┘
                         ╔══════════════════╬══════════════════╗
                         ║                  ║                  ║
                  ╔══════v══════╗    ╔══════v══════╗    ╔══════v══════╗
                  ║ Виртуальный ╟─┐  ║ Виртуальный ╟─┐  ║ Виртуальный ╟─┐
                  ║ дисплей #0  ║ │  ║ дисплей #1  ║ │  ║ дисплей #2  ║ │
                  ╚═╤═══════════╝ │  ╚═╤═══════════╝ │  ╚═╤═══════════╝ │
                    └────║────────┘    └────║────────┘    └────║────────┘
                         ╚═════════════════>║<═════════════════╝
                                            ║
                                      ╔═════v═════╗
                                      ║  Монитор  ╟─┐
                                      ║  Дисплей  ║ │
                                      ╚═╤═════════╝ │
                                        └───────────┘

                     Рисунок 6-8. Драйвер виртуальных экранов

            Полезными драйверами были  бы,  например,  драйвер  матричного
         принтера,  поддерживающий графические команды (такие как "нарисо-
         вать линию") и преобразующий их к требуемому  принтером  формату,
         или драйвер терминала, поддерживающий виртуальные экраны (см.рис.
         6-8). Такой драйвер терминала может иметь несколько буферов в па-
         мяти, хранящих копии экранной информации. Посылая команды драйве-
         ру по IOCTL-каналу,  можно указать драйверу какой виртуальный эк-
         ран  должен  быть  обновлен и какой виртуальный экран должен быть
         отображен на реальном экране.  Если  Вы  успешно  написали  такой
         драйвер, то Вы можете заменить им существующий драйвер консоли.
             Список идей,  которые можно было бы реализовать в  драйверах,
         практически бесконечен.  Вероятно,  у Вас уже появилось несколько
         своих,  которые Вы хотели бы реализовать. Если у Вас хватает спо-
         койствия  и  терпения то ничто не может помешать Вам в этом,  так
         что дерзайте!
