Методическое руководство к курсовой работе по дисциплине Assembler ibm pc содержание

Вид материалаРуководство

Содержание


10. Недостатки рассмотренного подхода построения резидентных программ и возможные пути их преодоления. 24 Prg6.
Второй этап
Третий этап
Посылка команды EOI на соответствующий контроллер.
Int 21h, Фнкция 35h. Получение вектора прерывания.
Int 21h, Фнкция 25h. Установка вектора прерывания.
Комментарий к программе
Prg1. Прикладной обработчик ошибки деления new_0h
Endp main
Ctrl-C. Функции ввода/вывода в своём большинстве проверяют код 03h в кольцевом буфере клавиатуры и при его обнаружении вызывают
Prg3. Запрет прерываний по Ctrl-C и Ctrl-Break (первый вариант)
Proc new_09h
Endp new_09h Proc new_1Bh ;Новый обработчик прерывания по Ctrl-Break iret Endp new_1Bh Proc main
Endp main End main ;Конец программы/точка входа
Prg4. Запрет прерываний по Ctrl-C и Ctrl-Break (второй вариант)
Endp new_1Bh Proc main
Endp main End main
TSR – программами
End) на начало процедуры resident
Prg5_res. Пассивный резидент с использованием прикладного программного обработчика прерывания с номером 61h
...
Полное содержание
Подобный материал:
  1   2   3   4   5   6   7   8   9

НГТУ

кафедра «Вычислительная техника»

Разработка прикладных обработчиков прерываний и резидентных программ

в MS-DOS

методическое руководство к курсовой работе по дисциплине Assembler IBM PC

Содержание


1. Система прерываний 2

2. Процедура обслуживания прерывания 3

3. Контроллер прерываний и его программирование 4

4. Прикладные обработчики программных прерываний 6

Prg1. Прикладной обработчик ошибки деления new_0h 9


5. Особенности обработки аппаратных прерываний 10

6. КМОП – часы и системный таймер 11

Prg2. Прикладной обработчик прерывания DOS 1Ch. Выводит на экран

реальное время из CMOP - часов. 11

7. Клавиатура и системный обработчик 09h 14

8. Особенности обработки прерываний по Ctrl-C и Ctrl-Break 16

Дополнительно: об особенностях взаимодействия клавиатурного и

системного контроллеров прерываний и учёта значений сегментных

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

Prg3. Запрет прерываний по Ctrl-C и Ctrl-Break (первый вариант) 17

Prg4. Запрет прерываний по Ctrl-C и Ctrl-Break (второй вариант) 19

9. Резидентные программы и их организация 21

Prg5_res. Пассивный резидент с использованием прикладного программного

обработчика прерывания Int 61h 22

Prg5_test. Тестовая задача для проверки работоспособности резидентной

программы Prg5_res 24

^ 10. Недостатки рассмотренного подхода построения резидентных

программ и возможные пути их преодоления. 24

Prg6. Резидентная программа, активизируемая нажатием клавиши "серый плюс", выводит на экран время системного таймера. Программа защищена от повторной установки и может быть выгружена командой с клавиатуры. 26

11. Нереентерабельность MS-DOS и пути её преодоления в

обработчиках аппаратных прерываний 31

Prg7. Резидентная программа, активизируемая нажатием клавиш Alt-A.

Выводит на экран системное время функцией DOS 40h с учётом анализа занятости DOS. 35

Prg8. Тестовая программа для исследования работы prg7 38

12. Индивидуальные задания 41

13. Литература 42

1. Система прерываний

Система прерываний любого ПК является его важнейшей частью, позволяющей быстро реагировать на события, требующие неотложной обработки процессором, например, сигналы от периферийных устройств (клавиатура, принтер, мышь, и т. п.). Прерывание заставляет процессор временно прекратить выполнение текущей программы (по завершению текущей программы) и перейти на выполнение процедуры обработки прерывания ISR (interrupt service routine), которая считается более важной. Исполнение прерванной программы возобновляется после окончания обработки прерывания.

Прерывания могут быть как внешними, так и внутренними. Внешние прерывания информируют процессор о внешних событиях, а внутренние – обнаруживаются процессором при исполнении программы. Существует два источника внешних прерываний и два – внутренних. Внутренние делятся на программные (результат выполнения программой команды int n, где n – номер или тип вектора прерывания) и исключения – внештатные ситуации, возникающие при исполнении текущей программы, такие, например, как деления на 0 при исполнении команды деления, попытка выполнить несуществующую команду и т. п.

Рассмотрим, более подробно, аппаратные прерывания, схема обработки которых в компьютере представлена на рис. 1. Сигналы аппаратных прерываний поступают в процессор не непосредственно, а через систему из двух контроллеров прерываний, один из которых является ведущим, а второй - ведомым. Каждый контроллер имеет 8 входов для сигналов-запросов на обслуживание от периферийных устройств Irqi. Выходным сигналом INTR контроллер формирует для процессора запрос на обслуживание прерывания, а по входу INTA – разрешение, после получения которого, контроллер по шине данных передаёт код номера вектора прерывания, соответствующий Irqi. Два контроллера (аналогичные типу I-8259A) используются для увеличения числа внешних сигналов для обработки и входят в состав так называемых интегрированных шинных комплексов.

К
Irq0

Irq1

Irq8, Irq9, Irq10, Irq11, Irq12, Irq13, Irq14, Irq15


Irq3

Irq4 Для ведущего контроллера эти

Irq5 запросы имеют равный приоритет

Irq6

Irq7
аждый контроллер выполняет функцию арбитра на основе правила: меньший номер – больший приоритет. С учётом того, что сигнал прерывания INT ведомого контроллера заведён на вход Irq2 ведущего, приоритеты в обработке запросов, при их одновременном поступлении, будут иметь следующий вид (режим полной вложенности – Fully Nested Mode).

Номер вектора прерывания образуется путём сложения базового номера, записанного в одном из регистров контроллера, с номером входной линии, по которой поступил запрос. Базовые номера записываются в контроллер автоматически в процессе начальной загрузки компьютеров. Таким образом, номера векторов, закреплённых за аппаратными прерываниями, лежат в диапазонах: 8h – 0Fh (ведущий) и 70h – 77h (ведомый).

Прерывание по входу INTR воспринимается процессором лишь при флаге IF=1. Флаг IF устанавливается командой Sti и блокируется (маскируются) командой Cli – запретить аппаратные прерывания. Обе эти команды никак не влияют на вход прерывания NMI (немаскируемое прерывание), впрочем, как и на все внутренние, в том числе и программные, реализуемые командой Int n. Обычно вход NMI зарезервирован за контролем напряжения источника питания или за ошибками при обращении к памяти. В общей системе прерываний компьютера любое внутренне прерывание (за исключением прерывания пошаговой работы Int 1) имеет более высокий приоритет, чем внешние прерывания.

Приоритеты прерываний в порядке их убывания:
  • прерывание из-за ошибки деления Int 0,
  • программное прерывание Int n, в том числе прерывание в контрольной точке Int 3,
  • прерывание при переполнении Int 4,
  • немаскируемое прерывание NMI,
  • аппаратные прерывания,
  • прерывание пошаговой работы Int 1,





2. Процедура обслуживания прерывания

Процессор, получив по линии INTR сигнал прерывания, выполняет последовательность стандартных действий, которую можно назвать процедурой прерывания. Рассматриваемая процедура верна и для всех типов прерывания. Процессор различает 256 различных векторов прерываний, для хранения которых отведён первый КБайт памяти компьютера (см. рис. 2). Вектор прерывания это 32 – битный адресный указатель CS:IP соответствующего обработчика. Процедура обслуживания прерывания условно можно разбить на 3 этапа. На первом этапе совершается прекращение исполнения текущей программы и переход к выполнению программы обработчика ISR. Отличительным признаком (в сравнении с обычным переходом от программы к подпрограмме) является не только сохранение флагов текущей программы, но и последующее обнуление флагов управления TF и IF, чтобы не допустить сбой в процессе перехода.

^ Второй этап – выполнение самой процедуры обработчика ISR

; сохранение используемых в процедуре регистров,

sti – разрешение аппаратных прерываний,

;… – собственно команды обработчика,

cli – запрещение аппаратных прерываний,

; – восстановление регистров,

^ Третий этап – самый простой и реализуется командой IRET:

pop ip

pop cs

popf

Важное замечание. Благодаря тому, что флаги автоматически сохраняются и восстанавливаются, а также тому, что прерывания от устройства обрабатываются, только если установлен флаг IF=1, программисту не нужно самому выполнять sti внутри ISR, чтобы после её окончания разрешить обработку новых прерываний. Выполнение sti внутри ISR необходимо только в том случае, когда есть желание разрешить прерывания в процессе выполнения ISR.






3. Контроллер прерываний и его программирование

Чтобы написать программу обработки прерывания от какого-либо устройства, необходимо представлять особенности функционирования контроллера прерываний, логическая структура которого изображена на рис. 3.

Сигнал запроса Irq1 от устройства (номер 1 закреплён за клавиатурой) поступает на вход регистра запросов IRR и устанавливает в «1» соответствующий бит этого регистра. Далее на пути сигнала стоит регистр маски IMR. Значение 0 в бите маски разрешает прохождение сигнала, значение 1 – запрещает. Пройдя через маску, сигнал поступает на схему приоритетов PR. Эта схема выполняет свою функцию выбора наиболее приоритетного запроса лишь, когда 2 и более прерываний накладывается друг на друга (Irq0 – максимальный уровень приоритета, Irq7 – минимальный). Пройдя через схему анализа приоритетов, сигнал запроса поступает на соответствующий вход регистра обслуживания запросов ISR, а также на вход INTR процессора МП. Поступление данного сигнала на вход регистра ISR выполняет функцию разрешения установки в “1” соответствующего бита регистра сигналом INTA от процессора. Микропроцессор воспринимает поступление сигнала INT (если флаг разрешения аппаратных прерываний IF=1) и вырабатывает ответный сигнал INTA, выполняющего 2 функции:

– устанавливает в «1» соответствующий бит в регистре ISR,

– сбрасывает аналогичный бит «0» в регистре IRR; с этого момента запрос на прерывание переводится в разряд обслуживаемых.

Действие МП при реализации процедуры обслуживания рассмотрено выше. Здесь отметим лишь, что МП одновременно с формированием сигнала INTA, сбрасывает флаги IF и TF, запрещая тем самым все аппаратные прерывания на уровне процессора. Установленный в «1» сигналом INTA бит регистра ISR воздействует на схему анализа приоритетов, блокируя все запросы с текущим или более низким приоритетом. Сброс бита обслуживания в регистре ISR осуществляется специальной командой EOI (End of Interrupt). Код команды EOI – 20h. Если прерывание возникло от ведомого контроллера, то данная команда должна подаваться на оба контроллера. Исходя из изложенного, можно предложить следующую структуру обработчика прерывания, вызванного запросом Irqi (прошу извинение за некоторое повторение).





Irqi —→ Начало процедуры обслуживания прерывания.

Запрещены все прерывания, т. к. флаг IF сброшен в 0 процессором.

Сохранение регистров.

Sti – разрешение аппаратных прерываний с более высоким приоритетом

Содержательная часть обработчика.

Cli – запрет аппаратных прерываний

^ Посылка команды EOI на соответствующий контроллер.

Восстановление регистров.

Iret – завершение процедуры и разрешение прерываний (если до этой процедуры флаг IF был установлен).

Прежде чем перейти к рассмотрению вопросов программирования контроллера, отметим, что для каждого прерывания существует три уровня запрета прерываний:
  • На уровне процессора (команда Cli).
  • На уровне контроллера. Замаскирован соответствующий бит в регистре маски или уровень запроса не превышает уровень запроса, принятого к обслуживанию.
  • На уровне конкретного устройства (запрет на выработку сигнала обслуживания).

Обычно программирование контроллера прерываний в прикладных программах сводится к посылке сигнала EOI и маскированию (или раз маскированию) прерываний от отдельных устройств, так как инициализацию контроллеров производит система BIOS. Однако иногда это необходимо делать, например, при использовании прикладной программой защищённого режима (меняются базовые вектора). Процедура инициализации контроллеров заключается в посылке 4-х слов инициализации ICW 1 – 4 (Initialization Control Word) в строгой последовательности друг за другом.

Тип ICW Код приказа Порт Назначение

Ведущий

ICW1 11h 20h Сопряжение из двух контроллеров

ICW2 08h 21h Задание базового вектора 08h

ICW3 04h 21h Вход Irq2 соединён с выходом ведомого контр.

ICW4 01h 21h Режим i80x86

Тип ICW Код приказа Порт Назначение

Ведомый

ICW1 11h 0A0h Сопряжение из двух контроллеров

ICW2 70h 0A1h Задание базового вектора 70h

ICW3 02h 0A1h Выход ведомого контр.соединён с входом Irq2

ведущего контр.

ICW4 01h 0A1h Режим i80x86

Ниже приведён фрагмент программы инициализации ведущего контроллера.



cli ;Запрет аппаратных прерываний

mov dx,20h ;Установка адреса первого порта ведущего контроллера

mov al,11h ;Установка ICW1

out dx, al ;Посылка ICW1

jmp $+2 ;Задержка для восприятия контроллером посылаемой команды

inc dx

mov al,08h ;ICW2

out dx,al

jmp $+2

mov al,04h ;ICW3

out dx,al

jmp $+2

mov al,01h ;ICW4

out dx,al

В такой же последовательности производится инициализация ведущего контроллера.

После инициализации контроллеров производится установка масок прерываний, значения которых зависят от конкретной конфигурации компьютера. Как правило, это маска B8h для ведущего контроллера и 0DDh – для ведомого. Ниже приводится пример запрета прерываний от клавиатуры путём маскирования соответствующего бита в регистре маски.

in al,21h ;Прочитать текущую маску

or al,02h ;Замаскировать бит 1

out 21h,al ;Установить новую маску



in al,21h ; Прочитать текущую маску

and al,0FDh ;Сбросить (раз маскировать) бит 1

out 21h,al ;Установить маску

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

Чтение регистра IRR ведущего контроллера

mov al,0Ah ;Код приказа чтения IRR

out 20h,al

jmp $+2

in al,20h ;IRR→al

Чтение регистра ISR ведомого контроллера

mov al,0Bh ;Код приказа чтения ISR

out 0A0h,al

jmp $+2

in al,0A0h ;ISR→al

4. Прикладные обработчики программных прерываний

Использование механизма программных прерываний для вызова системных функций DOS и BIOS широко применялось при написании программ лабораторного практикума. Если прерывание определить как временное прекращение основной программы, то команды Call (обращение к процедуре) и Int (обращение к системному обработчику DOS или BIOS) реализуют этот процесс в принципе одинаково. Только передача управления через команду Int n требует знание номера вектора программного прерывания, применение команды Call –знание адреса (имени) вызываемой процедуры. Более того, к обработчику прерывания можно обратиться с использованием команды Call. Так, например, команду Int n (пусть n=3) можно заменить следующими двумя командами (предварительно, конечно, инициализируется один из дополнительных сегментных регистров на область памяти с таблицей векторов прерываний):

mov ax,00h

mov es,ax

pushf ;Сохранить флаги

call [dword es:4*3h] ;Межсегментный переход к процедуре обработки

Использование дополнительной команды pushf связано с особенностями действия команды iret - выхода из процедуры прерывания.

Эквивалент команды Iret: pop ip

pop cs

popf ;Восстановить флаги

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

Первый случай, использующийся также при создании резидентных программ (см. дальше), требует знание свободных векторов. Как правило, это векторы 60h…66h, 78h…7Fh и ряд других, зарезервированных системой для дальнейшего развития. Чтобы посмотреть, какие векторы из таблицы векторов свободны на вашем компьютере, запустите из командной строки Turbo Debugger, откройте окно CPU, а в нём подокно памяти, инициировав предварительно сегментный регистр ES значением 0000h. Это позволит вам просмотреть первый килобайт памяти на предмет выявления ячеек с нулевым содержанием.

Второй случай отличается большим разнообразием в построении прикладных обработчиков прерываний, начиная от полной подмены ими системных обработчиков до их включения в программный код собственного обработчика в качестве составной части. Это так называемый случай сцепления векторов, форма реализации которого также многообразны. Отметим здесь два «граничных».
  1. Программный код прикладного обработчика new_handler начинается с выполнения системного:

Proc new_handler

Pushf ;Сохраним флаги для команды iret

Call [dword old_handler] ;В системный обработчик с возвратом

… ;Прикладная обработка

iret

Endp new_handler
  1. Программный код прикладного обработчика new_handler заканчивается выполнения системного:

Proc new_handler

… ;Прикладная обработка. Здесь нет необходимости

;помещать в стек флаги

jmp [dword old_handler] ;В системный обработчик без возврата

Endp new_handler

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

В качестве примера, рассмотрим разработку собственного программного обработчика ошибки от деления на нуль, полностью подменяющего системный (идеология обработчика заимствована из литературы [2]. Название существующего системного обработчика «деление на нуль» (вектор с номером 0 – команда int 0h) служит источником заблуждения всякий раз, когда после исполнения команд div или idiv вызывается это прерывание. В этом случае результат деления (частное) просто не укладывается в отведённый ему формат (регистры al или ax). Конечно, это же произойдёт и при делении на ноль. Например, исполнение следующего кода вызовет прерывание “деление на ноль”:

mov ax,100h ;делимое

mov bl,1 ;делитель

div bl ;результат: ah=quot (ax/bl), ah=rem(ax/bl)

В данном случае результат 100h/1=100h (256d) не укладывается в формат регистра al.

Останова в выполнении некоторых программ желательно избегать. В этом случае решение вопроса может быть найдено только в замене системного обработчика Int 0 на пользовательский new_0h. Применим в новом обработчике следующее правило - появление ошибки деления (переполнения) устанавливает частное в ноль. Однако реализация собственного обработчика ошибки деления наталкивается на определённые трудности, связанные с тем, что если в процессорах 8086/186 генерация данного прерывания помещает в стек адрес следующей за Div (Idiv)команды (как это и положено в процедурах прерывания, см. рис. 2), то в старших моделях процессоров в стек помещается адрес самой команды. Следовательно, чтобы перейти в последнем случае (при возникновении ошибки деления) к следующей после Div (Idiv) команде, необходимо модифицировать значение смещения этой команды в стеке. При этом надо иметь в виду, что команда Div (Idiv) имеет длину 2 или 4 байта, в зависимости от типа делителя. Если делитель находится в регистре, длина команды 2 байта, в ячейке памяти – 4 байта. Очевидно, обработчик должен анализировать код команды Div (Idiv), если поле md второго байта равно 11, то делитель находится в регистре, в противном случае – в ячейке памяти. После выяснения данного факта, обработчик new_0h должен увеличить значение смещения в стеке на 2 или 4 байта. В этом случае команда завершения обработчика iret возвратит исполнение программы к команде следующей после Div (Idiv).

Приведённая далее по тексту программа, использует стандартную структуру из двух частей. Первая часть программного кода представлена новым прикладным обработчиком new_0h, вторая – собственно программой для исследования операции деления, а также системные средства DOS для перехвата системного вектора Int 0h в процессе исполнения программы и замены его своим new_0h. По окончанию программы надо восстановить status quo, т. е. восстановить системный вектор Int 0h.

^ Int 21h, Фнкция 35h. Получение вектора прерывания.

Вызов: AH=35h, AL = номер вектора прерывания.

Возврат: ES:BX = двухсловный адрес (Seg:Offset) обработчика прерываний.

^ Int 21h, Фнкция 25h. Установка вектора прерывания.

Позволяет заполнить вектор прерывания адресом обработчика прерываний.

Вызов: AH=25h, AL = номер вектора прерывания.

Возврат: DS:BX = двухсловный адрес обработчика прерываний.

Конкретное использование указанных системных средств показано в программе prg1

^ Комментарий к программе

Прежде всего, в выделенной двухсловной ячейке памяти old_0h сохраняется полный адрес (Seg:Offset) системного вектора int 0h (адрес системной процедуры обработки ошибки деления), чтобы по завершению программы можно было восстановить таблицу векторов в исходное состояние. Прочитать вектор, конечно, можно прямым обращением к памяти (вектор 0h занимает 4 байта с адресом 0000h:0000h), однако лучше использовать функцию DOS 35h. После сохранения системного вектора 0h, на его помещаем адрес нашего обработчика new_0h с помощью функции DOS 25h. Применение этой функции предполагает занесение адреса нового обработчика (Seg:Offset) в регистры DS:DX. Обратите внимание, что в EXE- программах регистр DS настроен на сегмент данных, в то время как программный код с обработчиком расположен в кодовом сегменте с адресом в регистре CS. Изменённое значение регистра DS надо обязательно восстановить по завершению функции 25h. Рассматриваемая область программирования по разработке обработчиков прерываний и резидентных программ в особенности, требует предельного внимания программиста к значениям сегментных регистров. Мне приходилось не раз восстанавливать разрушенную систему компьютера, пока это для меня не стало обязательным правилом.

Перед завершением программы необходимо с помощью всё той же функции DOS 25h восстановить исходный вектор, который хранился в ячейке памяти old_0h.

Программный код нового обработчика можно поместить и в конце программы (после директивы Endp main). Правила здесь действуют те же, что и при размещении подпрограмм в ваших прежних программах.

^ Prg1. Прикладной обработчик ошибки деления new_0h

%TITLE "prg1"

IDEAL

MODEL small

P486N

STACK 256

crlf equ 10,13

DATASEG

;Поля данных программы и место для хранения перехватываемых векторов

mes1 DB crlf,"Выполняем деление с ошибкой 'деление на ноль'$"

mes2 DB crlf,'Программа успешно завершена $'

old_0h DD 0 ;Ячейка для хранения двухсловного адреса системного

;вектора Int 0h

CODESEG

Proc new_0h

;Обработчик ошибки деления. Частное обнуляем и устанавливаем регистр IP на адрес следующей

;после div (idiv) команды

sti ;Разрешить аппаратные прерывания

push ds ;Сохраним регистры

push si

;Перейдём к адресации стека через регистр bp с целью извлечения адреса команды div (idiv) после

;ошибки деления на нуль

push bp ;)*

mov bp,sp

;Область стека во время выполнения обработчика прерывания new_int0

;Адресация через Содержимое Значение

;регистр bp стека указателя стека

;bp+0 bp sp после выполнения )*

;bp+2 si

;bp+4 ds

;bp+6 ip команды div sp после прерывания

;bp+8 cs команды div

;bp+10 flags

;bp+12 **** sp до прерывания

mov ds,[bp+8] ;ds -сегмент div

mov si,[bp+6] ;si -смещение div

lodsw ;ax<-{ds:si}

;Анализ второго байта машинного кода команды div для выяснения характера делителя: регистр

;(тогда поле md=11 и число байт команды равно 2) или память (тогда поле md < 11 и число байт

;команды равно 4). По окончанию анализа, регистр устанавливаем на адрес следующей после div ;команды.

and ah,0C0h ;Выделим поле md во втором байте

cmp ah,0C0h ;md=11?

je L0 ;Да, команда div из 2-х байт

add [word bp+6],2 ;Нет, команда div из 4-х байт

L0: add [word bp+6],2

xor ax,ax ;Установим частное (регистр ах) в нуль

pop bp

pop si

pop ds

iret ;Возврат из прерывания

Endp new_0h

;Главная процедура

Proc main

mov ax,@data

mov ds,ax

;Получим значение старого вектора int 0h и сохраним его в ячейке old_0h, используя функцию

;35h int 21h

mov ax,3500h ;ah -функция, al - номер вектора

int 21h

mov [word old_0h],bx ;offset int 0h

mov [word old_0h+2],es ;segment int 0h

;Установим новый обработчик функцией 25h int 21h. Адрес обработчика должен находится в

;регистрах DS:DX

mov ax,2500h ;ah -функция, al - номер вектора

mov dx,offset new_0h

push ds ;сохраним ds

mov bx,seg new_0h

mov ds,bx

int 21h

pop ds ;восстановим ds

;Исследуемая программа с командой div (idiv)

mov ah,09h

mov dx,offset mes1 ;Сообщение о начале исполнения программы

int 21h

mov ax,100h ;Делимое

xor bx,bx ;Нулевой делитель

div bx ;Выполнение деления на нуль

;Продолжим выполнение программы с обнулённым частным благодаря действию нового обработчика

mov ah,09h

mov dx,offset mes2 ;Сообщение об успешном завершении программы

int 21h

;Восстановим старый вектор int0 функцией 25h int 21h

mov ax,2500h ;ah -функция, al - номер вектора

lds dx,[old_0h] ;ds:dx - указатель адреса старого обработчика

int 21h

Exit: mov ax,4C00h ;Функция DOS 4Сh:выход из программы

int 21h ;Вызов DOS. Останов

Endp main

END main ;Конец программы/точка входа

5. Особенности обработки аппаратных прерываний

Прикладные обработчики аппаратных прерываний строятся по тем же правилам, что и программные, но имеют и свои особенности. Во-первых, аппаратные прерывания относятся к типу асинхронных прерываний, то есть прерываний способных прервать текущую программу в произвольный момент времени. Это ведь не момент встречи в программе с командой Int n, которую мы помещаем в нужную точку программы, полностью подготовившись к передаче управления обработчику. Данное обстоятельство определяет первое отличие.
  1. Обработчик должен начинаться командами сохранения не только используемых им регистров общего назначения, но и всех сегментных регистров, за исключением регистра кода CS. Дело в том, что аппаратный обработчик может прервать текущую программу в тот момент, когда последняя обратилась к какой-либо сервисной функции DOS, а какие там использовались сегментные регистры, никто не знает.
  2. Метод полной подмены аппаратного системного обработчика прикладным неприемлем в подавляющем большинстве случаев, так как мы, допустим, не можем (не должны во всяком случае) блокировать управление компьютером от клавиатуры или системного таймера. Поэтому прикладные обработчики аппаратных прерываний используют различного вида сцепления с системными обработчиками.
  3. Та часть общей процедуры прикладного обработчика, которая минует в ходе своего исполнения системный обработчик, непременно должна включать команду конца прерывания EOI перед заключительной командой Iret (см. П. 3).

6. КМОП – часы и системный таймер

Персональные компьютеры оснащаются КМОП – микросхемой с батарейным питанием, которая выполняет две функции: хранит информацию о конфигурации компьютера и ведёт отсчёт реального времени. КМОП –часы работают независимо от того, включён или выключен компьютер. Синхронизация часов реального времени осуществляется внутренним кварцевым генератором, частота которого подобрана так, что сигналы на его выходе (после схемы пересчёта) следуют с интервалом в одну секунду. Эти сигналы и используются для отсчёта текущего времени календаря. Одновременно с этим КМОП –часы формируют на своём выходе периодические сигналы с программируемой частотой, которые поступают на линию Irq8 ведомого контроллера прерываний (вектор 70h). Для чтения или изменения показаний часов реального времени используется прерывание BIOS 1Ah. Это прерывание позволяет не только прочитать или установить КМОП –часы, но и «завести будильник». Когда будильник «зазвенит», будет вызвано прерывание int 4Ah (вызывается BIOS через посредство обработчика с вектором 70H, то есть входит в состав данного обработчика). Если пользователь заменит это прерывание своим (прикладным), то он будет активизирован в заданный момент времени, независимо от того, чем занят в это время компьютер. КМОП –часы будут выдавать прерывание int 4Ah каждые сутки в заданное время до тех пор, пока с помощью функции BIOS 07h прерывания int 1Ah будильник не будет выключен (информация об указанных системных функциях приведена в книге [8], стр. 627).

Кроме часов реального времени компьютер содержит системный таймер, который работает при включённом питании. Таймер вырабатывает сигналы с частотой 18,206 Гц (точное значение 1 193 180/65536 раз в сек) и подключён к линии запроса Irq0 ведущего контроллера (вектор 08h). В процессе начальной загрузки компьютера программа BIOS считывает показания КМОП –часов (часы, минуты и секунды) и, преобразовав их в число секунд, истекших от начала текущих суток, умножает эту величину на 18,206, чтобы получить текущее время, выраженное в числе тактов таймера. Эта величина заносится в ячейку с адресом 40h:6Ch. В дальнейшем, эта величина увеличивается с выработкой каждого выходного сигнала таймера. Содержимое этой ячейки памяти анализируется функцией DOS 2Ch прерывания Int 21h, использующееся для получения текущего времени. Данное прерывание содержит и другие полезные функции, связанные с работой системного таймера. Структурная схема таймера и его программирование подробно описано в литературе [ и ].

Рассмотрим построение прикладного аппаратного обработчика прерываний DOS Int 1Ch, входящего в состав системного обработчика BIOS 08h (см. рис.4). Прерывание DOS Int 1Ch служит для перехвата тактов системного таймера (18,206 Гц), не нарушая его работу, и содержит лишь одну команду Iret. Прикладная программа может установить собственный обработчик прерывания new_1Ch.

В качестве имитации вычислительной работы основной программы примем процедуру вывода на экран строк текста в цикле с задержкой. Данная программа будет прерываться 18,206 раз в сек нашим обработчиком new_1Ch, который будет выводить на экран текущее реальное время КМОП –часов, используя для этого функцию BIOS 02h прерывания 1Ah.

Prg2. Прикладной обработчик прерывания DOS 1Ch. Выводит на экран реальное время из CMOP - часов.

IDEAL

MODEL small

P486N

STACK 256

DATASEG

;Поля данных программы и место для хранения перехватываемых векторов

string DB 10,13,'*****0123456789*****$'

old_1ch DD 0 ;Вектор (адрес) старого обработчика 1Ch





CODESEG

Proc new_1Ch

;Обработчик прерывания 1Ch. Перехватывает такты системного таймера и выводит на экран

;реальное время из CMOP- часов c помощью функции BIOS 02h прерывания 1Ah

pusha

push ds

push es

push cs ;Значение сегментного регистра ds получено из

pop ds ;обработчика BIOS, сделаем его равным cs

;Функция 02h int1Ah выводит время CMOP- часов в двоично-десятичном формате (код BCD).

;Возврат: CH -часы, CL- минуты, DH - секунды, CF=1, если часы не идут или находятся в процессе ;обновления

mov ah,02h

int 21h

jc end_out ;Если часы заняты - в следующий раз

mov al,ch ;Возьмём часы

;Программа преобразования двухразрядного BCD - кода в ASCII-строку

;Вход: AL- BCD – код, Выход: AL-младшая ASCII-цифра, AH-старшая ASCII-цифра

call bcd_asc

mov [cs:clock],ah ;Выводится на экран сначала старшая

mov [cs:clock+2],al ;цифра, затем младшая!

mov al,cl ;Возьмём минуты

call bcd_asc

mov [cs:clock+6],ah

mov [cs:clock+8],al

mov al,dh ;Возьмём секунды

call bcd_asc

mov [cs:clock+12],ah

mov [cs:clock+14],al

;Выведем в видеобуфер дисплея

mov cx,clock_len ;Число выводимых элементов

mov ax,0B800h ;Установим сегментный адрес видеобуфера

mov es,ax

mov di,[cs:locate] ;es:di- адрес видеопамяти

mov si,offset clock ;ds:si- адрес выводимой строки. Для этой строки (адреса)

cld ;выше было реализовано соотношение ds=cs

rep movsb ;Отобразить на экране

end_out: pop es ;Сохраним используемые регистры

pop ds

popa

iret ;Передадим управление аппаратному обработчику 08h

;Процедура преобразования BCD-кода из регистра al в ASCII-символы

Proc bcd_asc

mov ah,al

and al,0Fh ;Оставим младшие четыре бита в al

shr ah,4 ;Логически сдвинем старшие биты на место младших

or ax,3030h ;Преобразуем в ASCII- символы

ret

Endp bcd_asc

;Поля данных обработчика new_1Ch

;Строка записи времени в формате 00:00:00. Каждый символ строки представляется двумя байтами:

;кодом символа и атрибутом цвета

clock DB 2 DUP(20h,1Fh),":",1Fh ;1Fh- белый по синему

DB 2 DUP(20h,1Fh),":",1Fh,2 DUP(20h,1Fh)

clock_len = $-clock

locate DW 160 ;Позиция точки вывода на экране

Endp new_1Ch

;Основная программа

Proc main

mov ax,@data

mov ds,ax

;Получим и сохраним адрес старого обработчика 1Сh с помощью функции DOS 35h int 21h

mov ax,351Ch;AL=1Ch- номер вектора

int 21h

mov [word old_1Ch],bx ;offset вектора 1Ch

mov [word old_1Ch+2],es ;segment вектора 1Ch

;Установим новый обработчик new_1Ch функцией DOS 25h int 21h. Адрес обработчика должен

;быть в DS:DX

mov ax,251Ch ;AL=1Ch- номер вектора

mov dx,offset new_1Ch

push ds ;Сохраним ds

mov bx,seg new_1Ch

mov ds,bx

int 21h

pop ds ;Восстановим ds

;Собственно программа пользователя. Выводит на экран строки текста в цикле с задержкой

mov cx,10 ;Счётчик числа выводов строки

out_str: mov ah,09h

mov dx,offset string

int 21h

;Задержка, включающая два вложенных цикла

push cx ;Сохраним счётчик выводов

mov cx,1000 ;Параметр задержки

L2: push cx

xor cx,cx

L1: loop L1

pop cx

loop L2

pop cx

loop out_str

;Восстановим вектор 1Сh в таблице векторов функцией DOS 25h int 21h

mov ax,251Ch ;AL=1Ch- номер вектора

lds dx,[old_1CH] ;ds:dx - вектор 1Ch

int 21h

Exit: mov ax,4C00h ;Выход в DOS

int 21h