Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Финогенов-основы_языка_ассемблера.doc
Скачиваний:
44
Добавлен:
17.09.2019
Размер:
3.35 Mб
Скачать

Глава 3

Команды и алгоритмы

115

pop AX iret . new_08 eudp

восстановим АХ

;Возврат в прерванную программу

Что же касается основной программы, то самое простое включить в нее (после завершения действий по инициализации обработчика преры­ваний) функцию DOS Olh ожидания ввода с клавиатуры:

mov int

AH,01h 2 Hi

В результате программа, дойдя до этих строк, остановится (фактически будет выполняться цикл опроса клавиатуры в ожидании нажатия клави­ши, включенный в состав программы реализации функции Olh DOS), а на экран непрерывной чередой будут выводиться символы коммерческого at (рис. 3.5). После нажатия на любую клавишу программа завершится.

FACUBRENT>p

Рис. 3.5. Вывод программы 3-3. , ч;

Приведенный пример позволяет обсудить чрезвычайно важный вопрос о взаимодействии обработчиков аппаратных прерываний с прерываемой программой и операционной системой. Особенностью аппаратных преры­ваний является то, что они могут возникнуть в любой момент времени и, соответственно, прервать выполняемую программу в любой точке. Текущая программа, разумеется, использует регистры, как общего назначения, так и сегментные. Если в обработчике прерывания мы разрушим содержимое хотя бы одного из регистров процессора, прерванная программа по мень­шей мере продолжит свое выполнение неправильным образом, а скорее всего произойдет зависание системы. Поэтому в любом обработчике аппа­ратных прерываний необходимо в самом его начале сохранить все регист­ры, которые будут использоваться в программе обработчика, а перед за­вершением обработчика (перед командой iret) восстановить их. Регистры, которые обработчиком не используются, сохранять не обязательно.

В нашем простом обработчике используется толь'ко один регистр АХ. Его мы и сохраняем в стеке первой же командой push AX, восстанавли­вая в самом конце обработчика командой pop AX.

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

:рвем выполнение системной функции на полпути, и начнем выпол-|нять ту же самую или даже другую функцию с начала, произойдет разру-хение системы, которая не предусматривает такое «вложенное» выпол-|нение своих программ. В настоящее время разработаны программные при-|емы, позволяющие эффективно обойти указанный запрет, однако ^использование их в программе драматически увеличивает ее сложность и |объем, и рассматривать эти приемы мы здесь не будем.

В нашем случае дело усугубляется тем, что прерывания от таймера не |только могут придти в тот момент, когда выполняется функция DOS, но f и неминуемо приходят только в такие моменты, так как мы остановили ^•программу с помощью вызова функции Olh прерывания 21U и, следова-|тельно, все время, пока наша программа ждет нажатия клавиши, в дей-|ствительности выполняются внутренние программы DOS. Именно поэто-мы отказались от использования в обработчике прерывания функции и выводим на экран символы с помощью прерывания BIOS. Выпол­нение функции BIOS «на фоне» DOS не так опасно. Надо только следить тем, чтобы наше прерывание BIOS в обработчике не оказалось вложен­ным в такое же прерывание BIOS в прерываемой программе.

Рассмотренный пример имеет существенный недостаток. Записав в век­тор прерываний 8 адрес нашего обработчика, мы затерли исходное содер­жимое вектора и тем самым ликвидировали (в логическом плане) исход­ный, системный обработчик. Практически это приведет к тому, что на вре­мя работы нашей программы остановятся системные часы, и если в системе есть какие-то другие программы, использующие прерывания от таймера, они перестанут работать должным образом. Ликвидировать указанный не­достаток очень просто: надо «сцепить» наш обработчик с системным так, чтобы в ответ на каждый сигнат прерывания активизировались последова­тельно оба обработчика. Рассмотрим методику сцепления обработчиков.

При инициализации прикладного обработчика, сцепляемого с сис­темным, следует точно так же, как и раньше, сохранить в программе ад­рес системного обработчика и поместить в вектор прерывания адрес при­кладного обработчика. Изменениям подвергнется только программа само-|' го обработчика, начало которой должно выглядеть следующим образом:

;Сцспление прикладного обработчика с системным

;для программы 3-3 new_08 proc

pushf

call CS:old 08

;Отправляем в стек слово флагов

;В системный обработчик

;Продолжение программы обработчика

cndp

iret new 08

Как будет работать такая программа' После того, как процессор вы-юлнит процедуру прерывания, в стеке прерванного процесса оказывают-три слова: слово флагов, а также двухсловный адрес возврата в пре­рванную программу (рис.3.6, нижние три слова стека).

116

Глава 3

IP2

1

>От команды

CS2

call CS:old OS

Флаги

<

— От команды pushf

IP1

CS1

Заполнено процессором > при выполнении процедуры

Флаги

Рис. 3.6. Стек прерванной программы в процессе выполнения прикладного обработчика прерываний.

CS1 — сегментный адрес прерванного процесса;

fP! — смещение точки возврата в прерванную программу;

CS2 — сегментный адрес прикладного обработчика;

IP2 — смещение точки возврата в прикладной обработчик.

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

Первая команда нашего обработчика pushf засылает в стек еще раз слово флагов, а команда дальнего вызова процедуры call cs:old_08 (где ячейка old_08 объявлена с помощью оператора dd двойным словом) в процессе передачи управления системному обработчику помещает в стек двухсловный адрес возврата на следующую команду прикладного обра­ботчика. В результате в стеке формируется трехсловная структура, которая нужна команде iret.

Системный обработчик, закончив обработку данного прерывания, завершается командой iret. Эта команда забирает из стека три верхние слова и осуществляет переход по адресу CS2:IP2, т.е. на продолжение при­кладного обработчика.

Завершающая команда нашего обработчика iret снимает со стека три верхних слова и передает управление по адресу CS1:IP1.

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

Обработчики программных прерываний

Программные прерывания вызываются командой int, операндом ко­торой служит номер вектора с адресом обработчика данного прерывания. Команда inl используется прежде всего, как стандартный механизм вызо­ва системных средств. Так, команда int 21h позволяет обратиться к много­численным функциям DOS, а команды int LOh, int 13h или hit 16h — к

117

Команды и алгоритмы

группам функций BIOS, отвечающим за управление теми или иными ап­паратными средствами компьютера. В этих случаях обработчики прерыва­ний представляют собой готовые системные программы, и в задачу про­граммиста входит только вызов требуемого программного средства' с по­мощью команды int с подходящим номером.

В некоторых специальных случаях, однако, программисту приходится писать собственный обработчик прерывания, которое уже обслуживается системой. Таким образом, например, осуществляется управление рези­дентными программами, которые для связи с внешним миром обычно используют прерывание 2FU. В каждой резидентной программе имеется собственный обработчик этого прерывания, который, выполнив свою долю действий, передает управление «предыдущему», адрес которого находил­ся ранее в векторе 2FU, и был сохранен обработчиком в своих полях дан­ных. Другой пример — перехват прерываний BIOS в обработчиках аппа­ратных прерываний с целью обнаружения моментов времени, когда ни одна из наличных программ не использует данное прерывание и, следова­тельно, сам обработчик может им воспользоваться.

Наконец, прикладной программист может воспользоваться одним из свободных векторов, написать собственный обработчик соответствующе­го прерывания и оставить его резидентным в памяти. После этого любые программы могут с помощью команды Lnt вызывать этот обработчик, ко-

|"торый, таким образом, становится резидентной программой общего

^пользования.

Резидентные программы

Большой класс программ, обеспечивающих функционирование вычис­лительной системы (драйверы устройств, оболочки DOS, русификаторы, интерактивные справочники и др.), должны постоянно находиться в памя­ти и мгновенно реагировать на запросы пользователя, или на какие-то со­бытия, происходящие в вычислительной системе. Такие программы носят названия программ, резидентных в памяти (Terminate and Stay Resident, TSR), или просто резидентных программ. Сделать резидентной можно как программу типа .СОМ, так и программу типа .ЕХЕ, однако поскольку ре­зидентная программа должна быть максимально компактной, чаще всего в качестве резидентных используют программы типа .СОМ.

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

При первом вызове программа загружается в память целиком и управ­ление передается секции инициализации, которая заполняет или моди­фицирует векторы прерываний, настраивает программу на конкретные условия работы (возможно, исходя из параметров, переданных програм­ме при се вызове) и с помощью прерывания DOS Int 211i с функцией 31h завершает программу, оставляя в памяти ее резидентную часть. Размер резидентной части программы (в параграфах) передается DOS в регистре DX. Указывать при этом сегментный адрес программы нет необходимое-

118