Методическое руководство к курсовой работе по дисциплине Assembler ibm pc содержание
Вид материала | Руководство |
- Учебно-методическое пособие по выполнению курсовой работы по дисциплине «Комплексный, 276.48kb.
- Дейт К. Д27 Руководство по реляционной субд db2/ Пер с англ и предисл. М. Р. Когаловского, 4309.37kb.
- Методическое пособие по дисциплине «информатика» (2семестр), 838.3kb.
- Методическое пособие по курсовой работе Для студентов Современной Гуманитарной Академии, 63.65kb.
- Пособие предназначено для студентов, выполняющих курсовые работы по дисциплине «Направления, 589.33kb.
- Руководство к выполнению курсовой работы по дисциплине «Финансы предприятий», 244.13kb.
- Методические указания к курсовой работе по дисциплине «Материаловедение и ткм», 699.8kb.
- Пояснительная записка к курсовой работе на тему: «Активный полосовой фильтр» по дисциплине, 342.06kb.
- Всвязи с имеющими место случаями невыполнения требований задания к курсовой работе, 112.75kb.
- Курс. 01;Мпк. 01;3 методическое пособие по курсовой работе методика воспитательной, 230.31kb.
НГТУ
кафедра «Вычислительная техника»
Разработка прикладных обработчиков прерываний и резидентных программ
в 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. Это позволит вам просмотреть первый килобайт памяти на предмет выявления ячеек с нулевым содержанием.
Второй случай отличается большим разнообразием в построении прикладных обработчиков прерываний, начиная от полной подмены ими системных обработчиков до их включения в программный код собственного обработчика в качестве составной части. Это так называемый случай сцепления векторов, форма реализации которого также многообразны. Отметим здесь два «граничных».
- Программный код прикладного обработчика new_handler начинается с выполнения системного:
Proc new_handler
Pushf ;Сохраним флаги для команды iret
Call [dword old_handler] ;В системный обработчик с возвратом
… ;Прикладная обработка
iret
Endp new_handler
- Программный код прикладного обработчика 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, которую мы помещаем в нужную точку программы, полностью подготовившись к передаче управления обработчику. Данное обстоятельство определяет первое отличие.
- Обработчик должен начинаться командами сохранения не только используемых им регистров общего назначения, но и всех сегментных регистров, за исключением регистра кода CS. Дело в том, что аппаратный обработчик может прервать текущую программу в тот момент, когда последняя обратилась к какой-либо сервисной функции DOS, а какие там использовались сегментные регистры, никто не знает.
- Метод полной подмены аппаратного системного обработчика прикладным неприемлем в подавляющем большинстве случаев, так как мы, допустим, не можем (не должны во всяком случае) блокировать управление компьютером от клавиатуры или системного таймера. Поэтому прикладные обработчики аппаратных прерываний используют различного вида сцепления с системными обработчиками.
- Та часть общей процедуры прикладного обработчика, которая минует в ходе своего исполнения системный обработчик, непременно должна включать команду конца прерывания 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