Однажды ко мне обратились с вопросом: «Как отключить в DOS-сессии управление программой с помощью манипулятора мышь?» Недолго думая, я ответил: «Нужно убрать соответствующий флажок в свойствах окна». Через минуту меня попросили уточнить, какой именно флажок нужно снять. Пришлось подойти, взять управление в свои руки и убедиться, что в настройках сессии DOS действительно отсутствует возможность отключения этого манипулятора. Более того, я не нашел возможности отключить загрузку драйвера мыши и через конфигурационные файлы autoexec. nt и config. nt. Ну нет так нет, вопрос не являлся принципиальным. Однако отступать без боя не хотелось.
Итак, задача поставлена. Как отключить мышь в DOS-сессии? Для начала вспомним, каким образом DOS-программа ею пользуется. Первый вариант — взаимодействие через порты. Этот уровень является самым низким и трудно реализуемым, поэтому на практике применяется редко. Большинство программ используют второй вариант — работу через прерывание.
В контексте нашего повествования прерыванием будет подпрограмма, поддерживающая функционирование оборудования и предоставляющая некоторый сервис для работы с ним. Адрес подпрограммы называется вектором прерывания и располагается в заранее определенных разработчиками вычислительной системы ячейках памяти. В результате пользовательские программы, заинтересованные в доступе к соответствующему оборудованию, получат нужный им сервис от прерывания. Сама подпрограмма именуется обработчиком прерывания и обычно устанавливается при загрузке драйвера устройства.
Если какой-нибудь программе нужно воспользоваться мышью, она обращается к прерыванию с номером 33. При этом через регистры процессора передается номер требуемой функции — получение или координат указателя, или состояния кнопок. Кроме того, одна из функций служит для того, чтобы ответить на запрос программы о наличии такого манипулятора, как мышь. Результат возвращается вызывающей программе также через регистры процессора.
Чистая система DOS не имеет установленного драйвера мыши, поэтому после обращения к прерыванию происходит возврат без модификации содержимого регистров процессора и программа осознает отсутствие поддержки мыши. В DOS-сессии, реализованной в системе Windows, загрузка драйвера происходит автоматически, и способа повлиять на данный процесс я не обнаружил. Остается только разработать программу, имитирующую отсутствие обработчика прерывания.
Опять-таки реализовать такой план можно по-разному. Первое, что приходит в голову, это написать собственный обработчик прерывания, содержащий одну-единственную полезную команду — возврат из прерывания IRET. Затем нужно сделать обработчик резидентным (т. е. зарезервировать область памяти и разместить там нашу подпрограмму) и перевести на него вектор 3316-го прерывания. Даже если не учитывать тот факт, что данный программный модуль будет отнимать такой дорогой с точки зрения DOS ресурс, как оперативная память, одно только перечисление подготовительных мероприятий, необходимых для его функционирования, заставляет шевелить мозгами. Ведь единственное, что нужно для реализации идеи, — наличие в доступной области памяти команды IRET, машинный код которой — CF16. И она наверняка там уже есть, поскольку существуют обработчики других прерываний, а каждое прерывание обязано завершаться этой командой. Для проверки воспользуемся отладчиком Debug, имеющимся в каждой версии Windows. В соответствии со спецификацией Intel вектор 2116-го прерывания должен располагаться в ячейке с адресом [0000:008416].
C:DEBUG
— D 0:84 L 4
0000:0080 25 02 49 03
— S 349:225 L FF CF
0349:024B
—
Итак, мы убедились, что уже в пределах первых FF16=255 байтов от начала обработчика 2116-го прерывания находится нужная нам команда IRET. Более того, мы получили ее адрес: [034916:024B16]. Остается всего ничего — записать данный адрес вместо вектора прерывания 3316, ответственного за работу с мышью, и тогда программы, запускаемые в окне этой сессии DOS, перестанут обнаруживать мышь.
Недостаток такого подхода — очевидное неудобство регулярного отключения мыши столь нетривиальным способом. Несомненно, придется написать программу, автоматически выполняющую все эти манипуляции. Более того, при разработке программы нельзя делать никаких предположений относительно адреса команды IRET в обработчике 2116-го прерывания, ведь он может изменяться от одной версии DOS или Windows к другой, а наша программа должна быть универсальной.
Приступим к работе, не забывая об этих дополнительных требованиях. Какой язык программирования мы выберем? Вопрос риторический. Естественно, ассемблер! А какое средство разработки и какой компилятор будем применять? Над этими вопросами можно долго размышлять, но вследствие простоты задачи предлагаю взять тот инструмент, который всегда под рукой, — знакомый вам отладчик Debug. Программа будет состоять из одного сегмента, что позволит сохранить ее в формате COM-файла. Поэтому адреса инструкций должны брать отсчет с адреса 10016. Ниже приведен полный листинг программы (листинг 1).
Определять вектор прерывания, читая непосредственно таблицу прерываний, как это было проделано выше, будет не совсем корректным подходом. Лучше применить для этого специальный сервис DOS — функцию 3516 программного прерывания 2116. Результат будет возвращен в двух регистрах процессора: сегмент — в регистре ES, смещение — в регистре BX. Далее в цикле осуществляется побайтовый просмотр сегмента до тех пор, пока не будет встречена ячейка с кодом инструкции IRET. И наконец, последнее: установка адреса найденной ячейки в качестве вектора 3316-го прерывания с помощью функции 2516 программного прерывания 2116.
Теперь осталось только выполнить компилирование программы в машинный код и проверить ее работоспособность. Воспользуемся тем приятным фактом, что программа Debug умеет обрабатывать команды, поступающие из потока стандартного ввода. Для этого подготовим текстовый файл (листинг 2). Пустые строки в нем имеют принципиальное значение.
Как видим, текст программы предваряется командой «A 100», по которой отладчик переходит в режим восприятия ассемблерных инструкций и размещения их в текущем сегменте, начиная со смещения 10016. Пустая строка, завершающая текст программы, переводит отладчик в командный режим. Чтобы сохранить программу в файл, нужно в регистрах BX и CX записать ее размер (2016=32 байта), присвоить файлу имя (вполне подойдет nomouse.com) и выполнить запись. Команда Q завершает работу отладчика.
Если приведенный выше текст сохранить в файл с именем nomouse.asm и из командной строки выполнить
C:>debug
то в текущем каталоге появится исполняемый файл nomouse.com. Программа готова к использованию!
Работа программы проверялась в среде сессии DOS операционной системы Microsoft Windows XP и в режиме эмуляции MS-DOS с помощью программы DOSBox 0.72 на широко известном файловом менеджере Volkov Commander 4.01. В обоих случаях после выполнения nomouse.com файловый менеджер благополучно забывал о существовании мыши (рис. 1 и 2). Успешно завершились испытания и на FoxPro 2.0 for DOS.
Таким образом, с использованием лишь подручных средств, начальных знаний ассемблера и способов взаимодействия программ с аппаратурой компьютера мы смогли создать работоспособную и достаточно универсальную программу, выполняющую вполне определенную функцию.
Существуют, как минимум, два способа практического применения этого замечательного 32-байтового продукта программистской мысли. В первом пользователь запускает программу сознательно, в здравом уме и твердой памяти, чтобы отключить функционирование мыши в консольных приложениях, которые по той или иной причине неправильно с ней работают. Такое случается с программами, написанными в далеком прошлом на FoxPro или Clipper, но содержащими ошибку в реализации пользовательского интерфейса, исправить которую за истечением срока давности уже некому. Во втором случае программа запускается без ведома пользователя, например из autoexec.nt. Для любителя DOS-игрушек поиск «пропавшей» мыши может стать настоящей первоапрельской головоломкой. Но важно не забывать, что в каждой шутке главное — знать меру, чтобы вместе с озадаченным приятелем или коллегой посмеяться над удавшимся розыгрышем!
Об адресах и векторах прерываний
Сессия DOS, реализованная в операционных системах Microsoft Windows, так же как и операционная система MS-DOS, выполняется в режиме имитации работы процессора Intel 8086 с реализованной в нем 20-разрядной адресацией. Основной особенностью этого режима является то, что доступная оперативная память делится на сегменты длиной по 64 Кбайт. Адрес ячейки оперативной памяти состоит из двух частей: 16-разрядного номера сегмента (сегменты могут накладываться друг на друга) и 16-разрядного смещения в нем. Обычно это записывается в виде [SEG:OFF]. Архитектура компьютеров на базе процессоров Intel построена таким образом, что начиная с адреса [0000:0000] следует таблица векторов прерываний. Векторы прерываний размещаются в таблице последовательно в порядке увеличения их номеров. Вектор каждого прерывания занимает 4 байта: сначала идут 2 байта смещения, потом 2 байта номера сегмента. Кроме того, нужно учитывать, что при хранении в памяти 16-разрядных чисел сначала идет младший байт, а затем старший.