С. А. Никитин, (г. Осташков)

СЕКРЕТЫ "КОРВЕТА"

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

Кого и зачем нужно прерывать? Вообразите себе большого Начальника, который сидит в своем кабинете и решает разные дела. Так как у Начальника, пусть даже большого, имеется только одна голова и он не может одновременно разбираться больше, чем с одним делом, у него есть Секретарша, которая выясняет, что за посетители приходят, а их сообщения сортирует в порядке важности для Начальника.

Конечно же, периодически Начальник говорит своей Секретарше, какие дела на текущий момент самые важные, какие могут подождать, а какие и вовсе недостойны отвлекать его внимание.

Может так случиться, что дело попалось сверхсрочное и в то же время малозависящее от других текущих проблем. Тогда Начальник вызывает Секретаршу и приказывает ей никаких посетителей не впускать, по телефону не соединять. Теперь можно спокойно, не прерываясь по пустякам, решать свое сверхважное дело.

Но в другой раз дело оказалось таким, что без постоянной связи с внешним миром его не решить: все время надо реагировать на изменяющуюся обстановку. Начальник приказывает Секретарше: по такому-то Поводу впускать и соединять немедленно, по этакому - пусть в очереди посидят, а остальным - не сметь прерывать мою работу!

Теперь назовем действующие лица своими именами: большой Начальник - это микропроцессор, Секретарша, как многие уже догадались, - контроллер прерываний, а посетители и телефонные звонки - не что иное, как запросы прерываний. Изложим подобную же историю в терминах компьютерной технологии.

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

Очевидно, такая "обеспокоенность" процессора состоянием периферии была бы очень накладной и по времени, и по объему памяти, занимаемому опрашивающей программой Поэтому разработчики микропроцессора мудро предусмотрели возможность подать на одну из сорока его ножек сигнал прерывания.

Но, имея лишь один обезличенный сигнал прерывания, микропроцессору трудно разобраться - какое устройство его отвлекло и насколько важно для работы прерывание именно этого устройства для работы. Пришлось бы опять занудно опрашивать сех подчиненных на тему: "Кто сказал мяу?". А одновременное прерывание от нескольких устройств было бы вообще неразрешимой задачей.

Теперь вполне понятно, зачем микропроцессору понадобилась Секретарша в виде БИС ВН59, на которой и выполнен контроллер прерываний. Совершенно аналогичная микросхема под названием "8259" применяется в импортной аппаратуре, совместимой с IBM РС/ХТ/АТ.

Пять "секретарских" обязанностей контроллера прерываний

Во-первых, контроллер прерываний увеличивает число независимых запросов прерывания. В ПК 80 используется только одна микросхема - Секретарша, поэтому количество таких запросов ограничено восемью. В таблице1 перечислены источники запросов прерывания всех восьми уровней. Для любознательных добавим, что на каждый вход этой микросхемы можно подать выход точно такой же БИС. Это называется вторым каскадом. Несложно подсчитать, что число запросов при этом увеличилось бы до 15..64! Но конструкторы, видимо, не поленились прикинуть размеры электронной платы и стоимость машины при введении второго каскада прерываний и решили, что ежели "по-умному", то и восьми уровней вполне достаточно. С этим можно согласиться, но было бы очень полезно иметь еще и отсутствующее в данной машине прерывание от клавиатуры.

Во-вторых, контроллер прерываний сортирует запросы прерывания по задаваемым программно приоритетам. Это означает, что запрос, имеющий более высокий приоритет, обрабатывается в первую очередь. Такая ситуация возникает, когда два и более запроса приходят одновременно или очередной запрос приходит во время обработки предыдущего.

Приоритеты прерываний во многом сходны с приоритетами арифметических операций. Если в арифметическом выражении имеется более чем одна операция (например, сложение и деление), то сначала выполняется более приоритетная (в нашем примере - деление). Имеется даже аналог скобок, которые, как известно, повышают до максимума приоритет любой арифметической операции. Этот аналог будет описан чуть позже под названием "спецмаскирование". В таблице 1 уровни прерываний перечислены по убыванию приоритета так, как они устанавливаются при стандартной инициализации (т.е. при включении или перезагрузке машины). Программист при необходимости может изменять приоритеты.

Таблица 1. Источники запросов прерывания
Уровень Источник прерывания Комментарии
0Адаптер расширения системыЛюбое внешнее устройство, подключенное к этому адаптеру, может генерировать запрос прерывания
1Последовательный адаптерПрерывание по окончании приема адаптером очередного байта
2Последовательный адаптерПрерывание по окончании передачи адаптером очередного байта
3Адаптер локальной сетиПрерывание, свидетельствующее, что по сети пришел очередной байт
4Генератор кадровой разверткиПрерывание с периодом 20 миллисекунд (т.е. 50 раз в секунду)
5Канал 2 таймераПрерывание с периодом, заданным программистом
6Адаптер принтераПрерывание, свидетельствующее, что принтер готов принимать данные
7Контроллер НГМДПрерывание, свидетельствующее о выключении мотора НГМД

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

Когда ПК 80 включают в сеть, программа инициализации ППЗУ программирует контроллер прерываний таким образом, что прерывания всех уровней, кроме третьего, задерживаются. Следовательно, только сообщение, пришедшее по локальной сети, может оторвать микропроцессор от работы. Это и понятно, ведь встроенные в ППЗУ программы могут обработать только прерывание от этого адаптера. Программы, использующие в своей работе другие устройства, должны корректно перепрограммировать контроллер (что и делает операционная система, если она сразу загружается с диска).

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

И наконец, в-пятых, в любой момент контроллер-Секретарша может предоставить микропроцессору отчет о состоянии своих внутренних регистров. Называется это режимом чтения и опроса. Подобный "маленький стриптиз" позволяет процессору узнать, были ли прерывания и какое из них имело наивысший приоритет в то время, когда прерывания программно запрещены (командой DI). Одно из применений режима чтения - это превращение входа прерывания в обыкновенный входной порт (ниже описано, как это используется на практике в базовых программах ППЗУ).

Что значит - обработать прерывание?

Конечно, "решения" по запросу должен принимать микропроцессор, это ведь он Начальник. Но привести его к правильному решению - задача чисто секретарская.

При поступлении запроса или запросов контроллер прерываний сначала решает две проблемы - "можно ли пропустить" и "кого первым", а затем сообщает процессору о том, что запрос "ждет". Микропроцессор может отреагировать сразу, закончив очередную команду, а может (в случае действия команды DI) - только после разрешения прерываний командой EI. "Услышав" контроллер, первым делом он выдаст ему сигнал подтверждения: "вас слышу". Только теперь контроллер прерываний посылает процессору три байта кодов - обычную команду CALL (0CDH) с адресом нужной подпрограммы.

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

Дальше идут более очевидные события: микропроцессор выполняет подпрограмму, указанную контроллером прерываний, и, возвратившись из нее (как из обычной подпрограммы - по команде RET), продолжает прерванную работу. В конце подпрограммы обязательно должны быть команды, сообщающие контроллеру прерываний о том, что запрос обработан и его можно "снять с повестки дня". Если этого не сделать, контроллер будет считать, что процессор все еще занят обработкой запроса, и не пропустит к нему другие прерывания равного или более низкого уровня.

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

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

Этими действиями исчерпывается работа контроллера и микропроцессора по обслуживанию запроса прерывания. Пришла пора рассмотреть другую сторону системы - источники запросов.

Краткий обзор источников прерывания

Самыми традиционными источниками сигналов прерывания являются медленные внешние устройства и системный таймер.

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

Уровень 0. Когда вы еще только достали машину из коробки, про уровень 0 можно сказать лишь то, что один из штырьков большого разъема на левом боку системного блока соединен с входной ножкой микросхемы контроллера прерываний. Число и функции устройств, которые могут пользоваться входом этого прерывания, ограничены только фантазией разработчика подключаемой аппаратуры. Естественно, что любая подобная конструкция должна быть поддержана программно, и запрос прерывания, если он разрешен, должен иметь соответствующую процедуру обслуживания. Принципы ее разработки будут изложены далее.

Зашитая в ПЗУ программа инициализации компьютера вообще использует вход 0 уровня как обычный входной порт. Дело в том, что, по соглашению, если используется внешнее ПЗУ, подключаемое к портам расширения системы, то она узнает об этом, проверяя, имеется ли перемычка между битом CONTROL и входом прерывания 0 уровня. Для этого программа, запретив прерывания, устанавливает бит CONTROL и в режиме чтения анализирует, есть ли запрос уровня 0.

Уровни 1 и 2. Эти прерывания генерируют, соответственно, приемник и передатчик последовательного адаптера.

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

Уровень 3. К этому входу подключен приемник локальной сети. Поскольку адаптер ЛС является не чем иным, как немного упрощенным последовательным адаптером, это прерывание должно истолковываться совершенно аналогично 1 уровню: по локальной сети пришел и подготовлен к считыванию очередной байт информации.

Уровень 4. Источник этого прерывания - видеоконтроллер. Ход электронного луча в кинескопе дисплея управляется компьютером. Каждые 20 миллисекунд (или 50 раз в секунду) генератор кадровой развертки начинает очередной ход луча по экрану из левого верхнего угла. Иногда это называют "выводом видеобланка". Перед самым началом этого процесса видеоконтроллер посылает контроллеру прерываний свой сигнал.

Как ни покажется странным для новичка, дисплейное прерывание, пожалуй, самое ходовое в ПК 80. Прерывание 4 уровня используется не столько по прямому назначению - для согласования вывода на экран с частотой кадров, сколько в качестве фиксированного системного прерывания. Большинство операционных систем (СР/М, МикроДОС, Корнет), обрабатывая этот запрос, опрашивают клавиатуру. Это и есть цена отсутствия у контроллера клавиатуры собственной линии прерывания.

Уровень 5. Источник запроса - канал 2 микросхемы таймера. Говоря официальным языком, это прерывание системного таймера, но, поскольку таймер не умеет делать ничего, кроме отсчета временных интервалов, можно назвать этот запрос прерывания проще и понятней - "будильник".

В отличие от видеоконтроллера (запрос 4 уровня), который, как часы с кукушкой, посылает свой сигнал всегда через одно и то же время, таймер может по заданию микропроцессора отмерять различные интервалы. Сосчитав до нуля, он вежливо сообщает контроллеру прерываний: "заданное время истекло, извольте разбудить Начальника". Разбуженный процессор "идет" в соответствующую процедуру и делает работу, которая была подготовлена для выполнения именно в этот момент времени.

Уровень 6. Прерывание от принтера. Если вы знакомы с работой адаптера печатающего устройства, то никаких неясностей быть не может, так как данный запрос напрямую связан с сигналом BUSYP.

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

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

Уровень 7. Редко используемое прерывание от контроллера НГМД. Этот контроллер имеет схему (одновибратор), которая включает мотор дисковода на 3 секунды после установки в единицу бита 5 в регистре управления. Так вот, инвертированный сигнал этого самого одновибратора и является запросом прерывания 7 уровня. Таким образом, запрос поступает в тот момент, когда выключается мотор дисковода. При включении же мотора запрос сбрасывается. Чуть позже будет рассмотрена программа на Бейсике-ПЗУ, иллюстрирующая прерывание этого уровня.

Единственное известное автору применение этого запроса не имеет никакого отношения к прерыванию. С его помощью программа инициализации ПЗУ проверяет наличие контроллера НГМД в данной машине, а следовательно, надо ли передавать управление загрузчику ОС или сразу перейти в Бейсик-ПЗУ. Этот способ не показывает наличие или отсутствие дисковода (т.е. сам НГМД может и отсутствовать), а говорит лишь о наличии контроллера. Зато это хороший прием, позволяющий программно отличить ПК 8020 от 8010.

Программирование системы прерываний

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

Однако, изучив программирование системы прерываний на низком уровне, легко повторить то же самое и на высоком. Даже в Бейсике с помощью РЕЕК и РОКЕ можно сделать любую работу по программированию микросхемы контроллера. Но не торопитесь воспользоваться этим, не имея уверенности, что система программирования не изменит ваших установок. Если возникла необходимость манипулировать прерываниями, лучше всего создать подпрограмму на ассемблере, а потом "подшить" ее к откомпилированной основной программе. В системе Паскаль МТ+ для этого будет удобен встроенный миниассемблер, при этом полезно создать небольшую библиотеку процедур, программирующих систему прерываний.

Почти не найдется причин для того, чтобы, программируя на Бейсике, ломать себе голову о прерываниях, но один полезный совет для пользователей компьютеров, связанных локальной сетью, тут бесспорен. Чтобы защититься от прерываний по локальной сети, которые могут случайно нарушить работу Бейсика-ПЗУ, необходимо дать директиву POKE &HFB29,&HFF. Тогда контроллер не пропустит запросов к процессору, и можно работать спокойно Для восстановления "сетевого слуха" директива такая. POKE &HFB29,&HF7. Этими же командами полезно начинать и заканчивать большинство Бейсик-программ, если недопустим их случайный сбой. Бейсик указанную установку никогда не отменяет, чего нельзя сказать, если вы попытаетесь запретить прерывания командой микропроцессора DI.

Средний уровень. Ни ПЗУ, ни операционные системы типа СР/М не имеют каких-либо доступных пользователю функций, программирующих контроллер прерываний. Но это чаще всего и не нужно. Важно, что они инициализируют систему прерываний оптимальным образом и создают таблицы переходов по запросам. Для подавляющего большинства случаев этого достаточно. Надо лишь разобраться в том, как пользоваться этими таблицами, что совершенно несложно.

Таблица стартовых переходов по прерываниям. ПЗУ и операционная система создают совершенно одинаковые, полностью совместимые таблицы стартовых переходов по прерываниям. Такая "типовая" таблица всегда находится в самом конце системной страницы по фиксированному адресу F7E0H и имеет формат 4*8 байт. Структура ее такова: три байта под команду перехода, четвертый "пустой". Пример:

            ...
    0F7XX:  JMP addr
            DS 1
            ...

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

    10 CLS: PCLS: PRINT ТАВ (16)"Таблица прерываний": PRINT
    20 DIM D(3)
    30 FOR I=0 TO 7 ' Для каждой строки таблицы
    40 LOCATE 12,1+3: PRINT "0F7";HEX$(&HE0+I*4);": ";
    50 FOR B=0 TO 3 ' Для каждого байта строки
    60 D(B)=PEEK(&HF7E0+I*4+B)
    70 NEXT В ' Следующий байт
    80 IF D(0)=&HC3 THEN PRINT "JMP ";HEX$(D(2)*256+D(1));:GOTO 110
    90 PRINT "DB ";
    100 FOR B=0 TO 2:PRINT HEX$(D(B));", ";:NEXT:PRINT HEX$(D(3));
    110 PRINT TAB(42) Уровень";I
    120 NEXT I ' Следующая строка

В DDT нужно дать команду LF7E0,F7FF [ВК]. Но здесь таблица получится не такая наглядная, так как деассемблер DDT "не знает" ее структуры и воспринимает пустые строки как последовательность команд (скорее всего, вы увидите NOP или RST 7). Теперь вы воочию убедились, что исследуемая система поддерживает всего одно-два прерывания.

В случае Бейсика-ПЗУ - это локальная сеть. ПЗУ, кроме программы инициализации, никаких программно доступных функций, программирующих контроллер, не имеет. Но ПЗУ имеет монитор локальной сети (тесно связанный с Бейсиком), запускающийся по запросу прерывания 3 уровня. Другие уровни прерываний программами ПЗУ не поддерживаются (если не считать загрузчика из внешнего ПЗУ, который использует запрос 0 уровня как дополнительный входной порт, о чем уже говорилось).

Стандартные версии операционных систем СР/М и МикроДОС поддерживают только прерывание, которое исходит от видеоконтроллера 50 раз в секунду. По каждому запросу ОС обрабатывает изменения в таблице цвета (LUT) и опрашивает клавиатуру. Сетевые версии имеют также процедуру, обслуживающую запрос локальной сети. Запрос системного таймера используется очень редко.

Таблица векторов прерываний. В отличие от ПЗУ, системная страница, организуемая ОС СР/М, кроме описанной, имеет таблицу векторов перехода. Откуда она взялась и зачем нужна?

Дело в том, что операционная система выполняет простейшую предобработку прерываний. Адрес в рассмотренной выше таблице переходов указывает на секцию команд этой предобработки. Обычно она сводится к запрещению прерываний, сохранению регистровых пар HL и PSW в стеке и переключению карты памяти в конфигурацию ODOSA (системная константа 1СН). Затем процедура передает управление подпрограмме, обрабатывающей прерывание. Этот переход организован косвенно, по вектору из описываемой таблицы. Вот типичная предобработка i-того прерывания:

            ...
    0F7XX:  JMP INT_i   ; Фрагмент таблицы переходов по прерыванию
            ...         ; Секция команд предобработки
    INTJ:   DI          ; Запретить прерывания
            PUSH H      ; Сохранить в стеке HL
            PUSH PSW    ; Сохранить в стеке PSW
            MVI A,ODOSA ; Нормализовать карту (1СН)
            STA SYSREG  ; памяти в ODOSA (0FA7FH)
            LHLD VTRAPi ; Взять вектор перехода из таблицы
            PCHL        ; Перейти по вектору

Структура таблицы векторов прерываний проще, чем предыдущей. Это простая последовательность двухбайтовых слов. Но элементов в этой таблице не восемь, а двенадцать - 8 векторов аппаратных прерываний и 4 так называемых псевдопрерываний (или программных прерываний). Псевдопрерывания на самом деле никакие не прерывания. Просто так организуют переход к некоторым системным процедурам (в частности, опроса клавиатуры), которые по смыслу аналогичны аппаратным прерываниям. Важное отличие: в нужном месте программы всегда имеется явный вызов подпрограммы псевдопрерывания, тогда как подпрограмма, обрабатывающая аппаратное прерывание, ниоткуда явно не вызывается (процессор попадает в нее по аппаратным сигналам контроллера прерываний).

Вот как выглядит таблица векторов прерываний, базовый адрес которой также фиксирован на системной странице (0F7C8H):

    VTRAPO: DW DOINTO   ; 0F7C8 Вектор прерывания 0 уровня
    VTRAP1: DW DOINT1   ; 0F7CA
    VTRAP2: DW DOINT2   ; 0F7CC
    VTRAP3: DW DOINT3   ; 0F7CE ...
    VTRAP4: DW DOINT4   ; 0F7D0
    VTRAP5: DW DOINT5   ; 0F7D2
    VTRAP6: DW DOINT6   ; 0F7D4
    VTRAP7: DW DOINT7   ; 0F7D6 Вектор прерывания 7 уровня
    VTRAP8: DW AUXI0    ; 0F7D8 Вектор псевдопрерывания 8 уровня
    VTRAP9: DW AUXI1    ; 0F7DA ...
    VTRAP10:DW AUXI2    ; 0F7DC
    VTRAP11:DW AUXI3    ; 0F7DE Вектор псевдопрерывания 11 уровня

Это изображение таблицы, так сказать, в общем виде. Что представляет собой таблица векторов в ваших конкретных системах, покажет следующая программа:

    10 CLS: PRINT "Таблица векторов прерывании": PRINT
    20 FOR A=&HF7C8 ТО &HF7DF STEP 2 ' Для каждого элемента таблицы
    30 PRINT НЕХ$(А);"; ' вывести адрес
    40 PRINT НЕХ$(РЕЕК(А+1)); ' вывести старший байт
    50 PRINT НЕХ$(РЕЕК(А)) ' вывести младший байт
    60 NEXT А ' следующий элемент

Не удивляйтесь, что в Бейсике-ПЗУ таблица пустая. Об этой особенности прерываний ПЗУ будет сказано немного позже. Теперь же, после знакомства с системными таблицами, можно рассмотреть самый важный в этом разделе вопрос - как организовать собственную реакцию на прерывание.

Как организовать собственную реакцию на прерывание. Если ваша программа будет выполняться в среде ОС СР/М (или совместимых с ней), то почти все действия сведутся к манипуляциям с адресами в таблице векторов. Возможны два способа подключения к прерываниям:

1. Ваша новая процедура выполняет какую-то специфическую работу, после чего управление передается существующей ("старой") процедуре обработки прерывания.

2. Ваша новая процедура полностью заменяет существующую, вызывать которую либо уже нет смысла, либо недопустимо.

Принципиальная разница состоит в окончании вашей процедуры: в первом случае достаточно (восстановив регистры ВС, DE и SP) просто перейти по запомненному "старому" вектору, все остальное сделает ОС при выходе из обработки. Во втором случае ваша процедура должна сама восстановить все регистры, сообщить контроллеру об окончании прерывания, восстановить конфигурацию памяти, разрешить прерывания и вернуться по команде RET. Вообще-то, возможен и третий вариант, вернее немного видоизмененный второй - по окончании обработки передать управление секции команд, завершающей прерывание. Но, во-первых, ваша программа будет "привязана" к конкретной версии ОС (малоприятная перспектива), а во-вторых, "овчинка выделки не стоит", так как легче повторить этот крохотный командный фрагмент в заменяющей процедуре.

Действия по замене вектора прерываний в обоих случаях одинаковы. Вот корректный алгоритм этой работы, иллюстрируемый на ассемблере, для прерывания номер i:

            DI              ; Запретить прерывания
            LHLD VTRAP0+2*i ; Прочитать вектор из таблицы
            SHLD COPVTR+2*i ; и запомнить его в переменной-копии
            LXI Н,NEWVTi    ; Вектор собственной процедуры
            SHLD VTRAP0+2*i ; записать в таблицу
            EI              ; Разрешить прерывания

Примечание. Выбранная ассемблерная реализация, конечно, далеко не единственно возможная, но к преимуществу этого способа следует отнести то, что задействована всего одна регистровая пара. Адресация переменной-копии подсказывает, что это таблица копий; в более простом случае, когда такая копия одна, достаточно было бы указать ассемблеру лишь имя COPVTR.

Теперь обсудим саму процедуру обработки запроса для обоих способов подключения к прерыванию. В первом случае (более простом и предпочтительном) корректными будут следующие действия:

    NEWVTi:                 ; "Добавка" к процедуре обработки i-того запроса
            DI              ; Запретить прерывания
            PUSH ...        ; При необходимости сохранить
            ...             ; регистры, кроме PSW и HL
            ...             ; ...
            ...             ; (тело процедуры)
            ...             ; ...
            POP ...         ; Восстановить сохраненные регистры
            LHLD COPVTR+2*i ; Вспомнить "старый" вектор из копии
            PCHL            ; Перейти в "старую" процедуру

Главное достоинство этого варианта - полная независимость как от версии ОС, так и от аппаратных средств. Если все же необходимо полностью заменить процедуру обработки, то возвращаться придется самостоятельно. Вот проверенный вариант:

    NEWVTi:                 ; Новая процедура обработки i-того запроса
            DI              ; Запретить прерывания
            PUSH ...        ; При необходимости сохранить
            ...             ; регистры, кроме PSW и HL
            ...             ; ...
            ...             ; (тело процедуры)
            ...             ; ...
            POP ...         ; Восстановить сохраненные регистры
            LDA SYSCOPY     ; Восстановить из копии (0F703)
            STA SYSREG      ; конфигурацию памяти (0FA7F)
            MVI А,ЕО1       ; Сообщить контроллеру о том, (20Н)
            STA EOIREG      ; что прерывание обработано (0FB28)
            POP PSW         ; Восстановить регистры, 
            POP HL          ; сохраненные в предобработке
            EI              ; Разрешить прерывания
            RET             ; Вернуться в подпрограмму

Строки, программирующие контроллер напрямую, будут подробно объяснены в следующем разделе.

Одной из самых важных причин для подключения к прерыванию является дополнение процедуры, обрабатывающей запрос 4 уровня. После обновления таблицы векторов, 50 раз в секунду (если не запрещены прерывания) ваша процедура будет получать управление. Таким способом можно, например, проверять нажатие каких-то клавиш, которые в вашей программе являются управляющими (подобно Ctrl-C в Бейсике или Ctrl-P в ОС СР/М), а также обновлять какую-то индикацию на экране (счет, время, координаты и т.п.) или показания системных часов. Например, ваша процедура может подсчитывать время, за которое пользователь не произвел никаких действий, и, если оно превысит какую-то критическую величину (минут, допустим, пять-десять), программа очищает экран, чтобы не изнашивались катоды и люминесцентное покрытие дисплея. Кстати, сделать это можно двумя способами: либо сохраняя информацию в ОЗУ и заполняя экран пробелами, либо, что более элегантно, перепрограммируя таблицу LUT и не трогая с места ни единого байта. Пример этот не взят с потолка - так работает Norton Commander, который выводит при этом изображение "звездного неба", т.е. редкие неяркие точки, чтобы пользователь не подумал, что дисплей сломался или выключен Не видно причин не пользоваться этим приемом и в других программах - телевизор штука дорогая. Однако не забудьте предусмотреть восстановление картинки при первом же нажатии любой клавиши (это тоже должно быть заложено в "добавку" к процедуре).

Как отключиться от прерывания? Не менее важно корректно отключиться от прерывания, восстановив работоспособность системы в первоначальном виде. Здесь возможны два способа:

1. Если необходимость в отключении от прерывания может возникнуть только по окончании программы, то специально ничего делать не надо. Завершите программу "теплым стартом" (JMP 0), который полностью реинициализирует систему прерываний.

2. Если не исключено, что потребуется восстановление исходных векторов по ходу программы, то необходимо предусмотреть следующие несложные действия:

            DI              ; Запретить прерывания
            LHLD COPVTR+2*i ; Прочитать из копии первоначальный
            SHLD VTRAP0+2*i ; вектор и поместить его в таблицу
            EI              ; Разрешить прерывания

Ограничения на процедуру обработки прерывания. Способ подключения к прерыванию путем изменения вектора очень удобен в большинстве ситуаций, так как немалую часть работы берет на себя операционная система. Однако у всякой палки два конца - необходимо помнить, что традиционные операционные системы ПК 8020 весьма ограниченно работают с прерываниями. ОС СР/М, в частности, требует от процедуры обработки прерывания следующего:

1. Должны быть сохранены регистры DE, ВС и SP. Как уже указывалось, HL и PSW сохраняет процедура предобработки. Не лишним будет предварительно убедиться с помощью деассемблера, что в вашей версии ОС дело обстоит именно так.

2. Внутри процедуры нельзя вызывать функции BDOS и BIOS.

3. Нужно запретить прерывания, так как в большинстве версий не разрешаются вложенные запросы.

Могут быть и еще какие-то ограничения, о чем необходимо справиться в описании конкретной версии, установленной на вашем компьютере.

Подключение к прерыванию в системе ПЗУ. Программы ПЗУ, как уже говорилось, тоже инициализируют систему прерываний и поддерживают запрос приемника локальной сети. Но системная страница, организуемая ПЗУ, не имеет таблицы векторов, поэтому работать надо прямо с адресами в таблице перехода по прерываниям. Естественно, что системная предобработка запросов в этом случае не проводится. Следует помнить, что шаг таблицы переходов - не 2 байта, а 4, причем адрес начинается со второго байта. В первый байт элемента таблицы следует всегда записывать код команды JMP (0СЗН), так как вы уже могли убедиться, что система делает это только для одного уровня запроса, остальные - пусты.

Классикой подключения к прерываниям в Бейсике-ПЗУ является программа "BASIC Vocabulary" (BASVOC.COM и BASVOC.BIN), По содержанию это почти бесполезный и педагогически "серый" Бейсик-справочник. Но программистское решение весьма интересно, так как очень изящно используется страничная организация памяти ПК 80. Справочная система загружена в те страницы ОЗУ, которые перекрываются ПЗУ (а это целых 24 килобайта), поэтому пользователь работает в Бейсике-ПЗУ, не испытывая никаких ограничений. Но в старших адресах ОЗУ имеется маленькая процедура, опрашивающая клавиатуру и передающая управление справочной системе, если нажата клавиша [ПРФ/esc]. Как вы уже, наверное, догадались, вызывает эту процедуру прерывание 4-го уровня.

Эти идеи могут лечь в основу множества самых разных программ, когда требуется разместить данные или команды, не используя для этого ОЗУ Бейсика. Возможны и обратные варианты, например версия СР/М, которая временно "подключает" пользователя к Бейсику ПЗУ при нажатии какой-либо управляющей клавиши.

Коварства ПЗУ-Бейсика. Дело в том, что Бейсик-ПЗУ создавался без особых мыслей о расширении системы. Наверное, это было бы правильно, если бы школьные компьютеры совершенствовались мировыми темпами. Но поскольку мы "застряли на месте" и нет возможности оснастить все РМУ дисководом или массово применять кассеты ПЗУ, приходится программно совершенствовать то, что заложено в машину изначально.

Опыт показал, что Бейсик-ПЗУ не поддерживает на системной странице копии нечитаемых регистров (системный, цветовой, LUT). Но самое печальное, что при этом он даже не запрещает прерывания, переключая карту памяти. Следовательно, нельзя размещать процедуру, отрабатывающую прерывание выше адреса 0С000Н или ниже адреса 8000Н, иначе ваши коды будут перекрыты видеопамятью ГЗУ. Но не переживайте сильно - в basic-буфере остается еще около 20 килобайт, это немало. Выяснить, в какой конфигурации очутилась ваша процедура, можно, прочитав ячейку 6000Н. Обнаружив нестандартную конфигурацию, лучше немедленно сбросить запрос и ретироваться, иначе вас подстерегают непредсказуемые неприятности. Ценой долгих экспериментов можно научиться что-то делать на фоне бейсиковского рисования, но закраска все равно всегда выводит процедуры из строя.

Другая хитрость заключается в том, что программа инициализации ПЗУ устанавливает контроллер прерываний для чтения таблицы переходов с адреса 2Е0Н, т. е. в ПЗУ, куда вы не сможете записать свои JMP'ы и адреса. Поэтому необходимо реинициализировать контроллер (как описано далее), но поместить таблицу не на привычную для СР/М системную страницу 0F700H, а опять же ниже 0С000Н.

И это еще не все: Бейсик читает клавиатуру после каждой команды, не используя для этого системное прерывание. Учитывая то, что интерпретатор "зашит" ПЗУ, последнее практически означает, что программист не в силах устранить бейсиковский контроль за клавиатурой. Например, невозможно отключить клавишу [СТОП], чтобы защитить программу от неуклюжих действий новичка.

Если после всех этих "НО" вы не передумали иметь дело с прерываниями в ПЗУ-Бейсике, в добрый путь. Тем более что большинство "граблей", на которые можно наступить, вам показаны.

Обратные варианты (например, версия СР/М, которая временно "подключает" пользователя к Бейсику-ПЗУ при нажатии какой-либо управляющей клавиши), наверное, возможны, но затруднительны, так как требуют безукоризненного знания программного интерфейса интерпретатора. Однако соблазнительно, например, получить доступ к "зашитым" арифметическим процедурам Бейсика, ведь в этом он силен.

Итак, на среднем уровне доступ к системе прерываний наиболее удобен для программиста. Однако для понимания "души" этой системы и уверенного программирования в любых ситуациях, на любом уровне, необходимо спуститься еще на одну ступеньку - к портам и регистрам контроллера прерываний.

Низкий уровень. Специальные команды процессора. Конечно, первое, что следует упомянуть в программировании системы прерываний на низком уровне, - это команды микропроцессора DI (запретить прерывания), EI (разрешить прерывания) и HLT (останов до прерывания). Все остальные действия возможны лишь путем программирования БИС контроллера прерываний ВН59.

В семействе ПК 80 не используются так называемые немаскируемые прерывания, т. е. прерывания, которые нельзя так или иначе запретить. Такие запросы, как правило, применяются для реагирования на совершенно неотложные ситуации, например, падение напряжения питания или деление на ноль. Как всегда, об отсутствии какой-либо возможности можно пожалеть, но не следует забывать и о том, что немаскируемые прерывания - всегда клубок сложных программных противоречий. В нашем же компьютере этих проблем никогда не возникает, ибо есть возможность полностью запретить все прерывания.

Общим правилом при использовании команд DI и EI является сведение к минимуму времени, когда прерывания запрещены. Не менее важен анализ фрагментов программ, выход из которых возможен только по прерыванию. Конечно, бывают случаи, когда программисты преднамеренно организуют "зависание" системы (например, в СР/М v.2.2 имеется такой фокус), но чаще это происходит вопреки чьей-то воле. Поэтому надо крайне осмотрительно пользоваться командой HLT и отказаться от структуры "вечного" цикла.

Программирование БИС контроллера прерываний. Микросхема контроллера представлена в адресном пространстве процессора двумя байтовыми портами. Их относительные адреса идут подряд: 28Н и 29Н. Старший байт адреса, как обычно, определяется конфигурацией памяти, и для самых "ходовых" из них - BASIC и ODOSA -абсолютные адреса равны 0FB28H и 0FB29H. На самом деле контроллер имеет регистров больше, чем два байта, но доступ к ним определяется, во-первых, очередностью обращения, а во-вторых, состоянием битов-идентификаторов.

Когда мы говорим об очередности обращения, мы имеем в виду то, что порты по разному воспринимают данные, записанные в них при инициализации и после нее Биты-идентификаторы - это разряды 4 и 3 порта 28Н, сообщающие контроллеру наши желания: хотим ли мы переустановить контроллер, или даем текущую команду, а если команду, то какого типа.

Режим инициализации. Логично начать с этого режима, хотя для программы это всегда будет переустановка, ведь при включении машины в сеть программа ПЗУ уже инициализировала микросхему.

Начинать надо всегда с записи в порт 28Н, при этом бит 4 должен быть установлен -это и есть признак инициализации Следом записывается байт в порт 29Н. При этом контроллер понимает наши данные так, как это показано на рисунке 1.

Порты контроллера прерываний при инициализации
Рисунок 1. Порты контроллера прерываний при инициализации

Единичка в разряде 1 порта 28Н сообщает контроллеру, что мы используем только одну микросхему, а не каскад. Естественно, что в ПК 80 этот бит всегда будет именно таким.

Надеюсь, понятно, почему для адреса таблицы переходов контроллер требует всего 11 разрядов (А15-А5). Конечно же потому, что младшие биты адреса начала таблицы всегда нулевые, а для каждого уровня запроса контроллер заменяет эти нули смещением от начала таблицы. Смещение зависит от формата таблицы, который задается вторым разрядом порта 28Н. Причем формат 8 байт требует для адреса таблицы всего лишь десять разрядов, а пятый бит в этом случае игнорируется.

Таким образом, контроллер прерываний, вычисляя адрес перехода по запросу, берет записанный программой базовый адрес и прибавляет к нему смещение:

    Адрес := А15...А5...00000 + (4 * Номер_запроса)

или

    Адрес := А15...А6...000000 + (8 * Номер_запроса)

Итак, записав в порты контроллера 2 байта, мы сообщили ему всю необходимую для работы информацию: использование одной микросхемы, формат и базовый адрес таблицы переходов.

Командный режим. Контроллер ВН59 имеет 4 типа программируемых операций, которые, в свою очередь, разделены на подтипы:

  1. Операции маскирования:
    • обычное маскирование запросов,
    • специальное маскирование.
  2. Операции чтения-опроса:
    • чтение внутренних регистров;
    • опрос на прерывание.
  3. Операции окончания прерываний:
    • обычное окончание;
    • специальное окончание.
  4. Операции установки приоритетов:
    • произвольный сдвиг приоритетов;
    • циклический сдвиг приоритетов.

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

Регистр маскирования
Рисунок 2. Регистр маскирования

Обычное маскирование запросов

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

После записи двух байтов инициализации порт 29Н контроллера воспринимает любую записанную в него информацию как маску блокируемых запросов и называется поэтому регистром маскирования (РМ). Этот регистр произвольно доступен как для записи, так и для чтения. Замаскированный запрос скрыт от процессора. Восемь уровней прерываний соответствуют восьми разрядам регистра. Если бит установлен - прерывание данного уровня задерживается контроллером. Если бит обнулен - прерывание пропускается к микропроцессору (с учетом приоритета).

Запись в регистр маскирования - первое действие после инициализации контроллера. Причем сделать это надо до разрешения прерываний командой EI, так как регистр маскирования вначале обнулен и к процессору могут пройти нежелательные прерывания. В дальнейшем можно блокировать таким образом различные уровни запросов сколь угодно часто и в любых сочетаниях. Также ничем не ограничена возможность чтения регистра. Благодаря возможности простого чтения всегда предпочтительнее работать только с необходимыми в данный момент разрядами регистра, не изменяя остальные. Как это делается, подробно изложено в описании логических операций микропроцессора.

Для иллюстрации задайте Бейсику-ПЗУ следующую программу:

    10 CLS: PRINT "Исходное состояние РМ: ";
    30 GOSUB 100 ' Вывод состояния регистра
    40 PRINT ТАВ(7) "Смена маски: ";: POKE &HFB29,&B11110000
    50 GOSUB 100: PRINT "А сейчас система зависнет..."
    60 POKE &HFB29,0: PRINT "Эта строка никогда не напечатается..."
    70 END
    100 PRINT BIN$(PEEK(&HFB29)): ВЕЕР: PRINT: RETURN

Почему возникло зависание? Записав в регистр маскирования маску "0", мы разрешили прохождение к микропроцессору всех запросов. Однако в предыдущих параграфах было показано, что система ПЗУ не имеет ни векторов, ни процедур обработки для большинства прерываний. Напротив, ОС СР/М v.2.2 имеет все адреса переходов, а также пред- и постобработку (хотя сами процедуры обработки чаще всего отсутствуют). Благодаря этому в СР/М безопасно обнулять регистр маскирования, хотя и нежелательно.

Крохотную ассемблерную программку из четырех команд можно набрать, работая с утилитами DDT.COM или SID.COM. Вот протокол работы, отображаемый на экране дисплея (справа - пояснения):

    A>DDT               Вызываем утилиту
    DDT VERS 1.4        Заставка и Промпт (#) DDT
    #А100               Вставить ассемблерные
    0100 EI             команды с адреса 100Н
    0101 MVI A,0        Записать 0
    0103 STA FB29       в регистр маскирования
    0106 JMP 0          "Теплый" старт ОС
    0109 
    #G100               Запуск фрагмента

Несмотря на то что эта программа записывает в регистр маскирования "0", т.е. пропускает все прерывания, зависания не происходит и мы благополучно возвращаемся в ОС. Напоминаю, что "теплый" старт полностью восстанавливает систему прерываний, в том числе и маски.

Специальное маскирование

Обрабатываемые запросы блокируют прохождение к процессору прерываний равного или низшего уровня. Обычный путь снятия этого блока - команда окончания прерывания. Но может возникнуть ситуация, когда необходимо пропустить блокированный запрос, не заканчивая обработки предыдущего. В подобных случаях необходимо дать контроллеру команду специального маскирования (рисунок Зг). В регистре маскирования при этом устанавливают биты тех уровней, через которые должно "перепрыгнуть" пропускаемое прерывание. Команда 48Н (рисунок Зд) отменяет режим спецмаскирования.

Регистр ввода и спецмаскирования
Рисунок З. Регистр ввода и спецмаскирования

Чтение внутренних регистров

Контроллер предоставляет возможность прочитать свои внутренние регистры - регистр запросов (РЗ) и регистр обработанных запросов (РОЗ).

Бит РЗ устанавливается по приходу соответствующего сигнала от источника прерывания. О незамаскированном запросе с наивысшим приоритетом сообщается микропроцессору. Когда же тот подтвердит получение сигнала, бит из РЗ перезаписывается в РОЗ, а в РЗ сбрасывается. Теперь, пока процессор не сообщит об окончании обработки запроса, соответствующий бит РОЗ будет установлен, а это блокирует другие равные или низшие запросы.

Читая РЗ и РОЗ, программа может контролировать всю эту аппаратную "кухню" и при необходимости реагировать тем или иным образом. Например, программа инициализации ПЗУ выясняет, читая седьмой бит РОЗ, имеется ли в системе контроллер дисковода, и, если логическое включение мотора НГМД не обнуляет этот запрос, передает управление Бейсику-ПЗУ, минуя загрузчик ОС. Познакомьтесь с этим фрагментом, деассемблировав ПЗУ с адреса 048DH по 049СН.

Та же программа, читая РОЗ, проверяет наличие перемычки между битом CONTROL порта 32Н и входом прерывания 0 уровня. Бит CONTROL сначала обнуляется, а затем устанавливается. По реакции на эти переключения нулевого разряда РОЗ делается вывод о наличии или отсутствии перемычки, а следовательно, внешнего ПЗУ. Данный фрагмент можно просмотреть в адресах 04BFH...04D3H.

Если регистр маскирования имеет собственный порт и читается тривиально, то перед прочтением внутренних регистров необходимо дать контроллеру соответствующую команду. Команды, предшествующие чтению внутренних регистров, показаны на рисунках За, 36. Эти константы надо записать в порт 28Н, а затем прочитать из него же.

Режимы чтения РЗ и РОЗ отменяются только подачей в порт 28Н новой команды и не зависят от числа прочтений регистров.

Попробуйте прочитать РОЗ контроллера в Бейсике-ПЗУ:

    10 CLS: PRINT "Регистр маски:"; ТАВ(15); BIN$(PEEK(&HFB29))
    20 POKE &HFB28,&H0A ' устанавливаем режим чтения РОЗ
    30 PRINT "Р О 3:"; ТАВ(15); BIN$(PEEK(&HFB28)): END

Полученные результаты следует понимать так: в обработке контроллером находятся запросы 4 и 7 уровней, но так, как эти уровни маскированы, в данный момент прерывания не доходят до процессора.

Следующая программа для Бейсика-ПЗУ ПК 8020 очень наглядно иллюстрирует запрос прерывания от одновибратора НГМД. Напомню, что это устройство включает мотор дисковода на 3 секунды, при этом сигнал запроса 7 уровня сбрасывается. Одновременно с выключением мотора запрос прерывания устанавливается. Подключен ли дисковод к компьютеру, для программы несущественно, так как она взаимодействует с контроллером НГМД, который встроен в системный блок и по таким "пустякам", как включение мотора, не проверяет состояние накопителя. Подробности, касающиеся НГМД, здесь не рассматриваются. Эта программа лишь позволяет в динамике наблюдать изменения 7 бита РОЗ в зависимости от включения или выключения мотора НГМД.

    10 CLS: PRINT "Иллюстрация к прерыванию от дисковода""
    20 ROZ=&HFB28               ' Адрес РОЗ
    30 RUD=&HFB39               ' Адрес регистра управления НГМД
    40 POKE RUD,3               ' Сброс битов включения моторов
    50 POKE ROZ,&H0A            ' Режим чтения РОЗ
    60 PRINT BIN$(PEEK(ROZ))    ' Чтение и печать РОЗ
    70 POKE RUD,&H21            ' Включение на 3 с мотора НГМД А
    80 FOR Z=0 TO 11
    90 NR=PEEK(ROZ)             ' Очередное прочтение РОЗ
    100 T=8-LEN(BIN$(NR))       ' Вычисление табуляции для вывода
    110 PRINT TAB(T); BIN$(NR)  ' Вывод на экран
    120 FOR Q=0 ТО 300: NEXT    ' Пауза
    130 NEXT Z
    140 END

Опрос на прерывание

У обычного режима работы системы прерываний - обслуживания по запросу - есть альтернатива: обслуживание по результатам опроса. В этом режиме запросы обрабатываются по инициативе программы, поэтому прерывания запрещены командой микропроцессора DI. Режим этот напоминает уже описанный выше телефон без звонка, однако в некоторых ситуациях он может быть полезен.

В определенные программистом моменты времени процессор опрашивает контроллер на предмет прерывания. Для этого в порт 28Н записывается команда опроса (рисунок Зв). Теперь можно прочитать этот же порт. Полученный байт имеет структуру, изображенную на рисунке 4.

Структура данных при опроса на прерывание
Рисунок 4. Структура данных при опроса на прерывание

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

В отличие от чтения РЗ и РОЗ, режим опроса действует только для однократного прочтения порта. Для того чтобы еще раз опросить контроллер, необходимо снова записать команду и лишь затем считать результат.

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

Операции окончания прерываний

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

Если явно не указать об окончании прерывания, контроллер не пропустит к микропроцессору прерываний, низших или равных по приоритету обработанному. Обращение к регистру окончания прерываний (РОП) - через порт 28Н. РОП выбирается сброшенными битами 4 и 3 (рисунок 5).

Регистр окончания и сдвига приоритетов
Рисунок 5. Регистр окончания и сдвига приоритетов

Самый традиционный способ объявить прерывание оконченным - записать в РОП константу 20Н, что указывает на обычное окончание и просто обнуляет соответствующий бит РОЗ (рисунок 5а). Пожалуй, это самая частая команда контроллеру, так как ею заканчиваются практически все процедуры обслуживания запросов.

Возможно и специальное окончание прерывания, когда обслуживается одно прерывание, а сбрасывается бит РОЗ другого уровня (рисунок 5б). При этом младшие три бита команды (В2-В0) указывают номер уровня сбрасываемого запроса. В обоих случаях приоритеты остаются неизменными, что чаще всего и требуется.

Операции установки приоритетов

Ранее были перечислены источники прерываний в порядке убывания приоритетов. Такой порядок устанавливается по умолчанию, так как не требует никаких явных действий. Однако приоритеты запросов можно программно изменять. Это очень сильный прием, особенно в системах, работающих в реальном времени со специальными периферийными устройствами, но в стандартном ПК 80 ему трудно найти достойное применение, кроме как в играх, рассчитанных на двоих партнеров. Следует отметить, что приоритеты не могут меняться беспорядочно, так как относительное расположение входов прерываний задано аппаратно. Поэтому речь идет не о вольном манипулировании, а лишь о сдвигах приоритетного кольца. Причем все команды задают лишь низший уровень, остальные уровни определяются порядком расположения в кольце Таким образом, возможны только восемь вариантов (рисунок 6а-з).

Варианты распределения уровней приоритета
Рисунок 6. Варианты распределения уровней приоритета

Контроллер ВН59 предоставляет три способа сдвинуть приоритетное кольцо. Перечислим их кратко:

1. Произвольный сдвиг приоритетов. В порт 28Н записывается команда, изображенная на рисунке 5д, при этом разряды В2-В0 выбирают низший уровень приоритета.

2. Произвольный сдвиг приоритетов с окончанием прерывания (команда на рисунке 5г). Ничем не отличается от предыдущего по действию на приоритеты, но одновременно осуществляет обычное окончание прерывания, сбрасывая в РОЗ бит обслуженного уровня.

3. Циклический сдвиг приоритетов с окончанием прерывания (рисунок 5в). Обычное окончание прерывания, при этом обработанный уровень автоматически попадает на дно приоритетного "колодца".

Мост к семейству IBM PC

Первым делом необходимо выяснить одну важную деталь. В большинстве руководств no IBM PC авторы относят к прерываниям и то, что мы выше называли аппаратными прерываниями, и так называемые программные или псевдопрерывания. Здесь, скорее всего, играет роль обычная психологическая инерция, когда неудачная (но устоявшаяся) терминология сводит "под одну крышу" принципиально различные понятия. Дело в том, что программные "прерывания" ничего не прерывают! Это просто специфический вызов подпрограмм. Отличие команды INT от команды CALL состоит лишь в том, что требуется указывать не физический адрес, а лишь номер (полная аналогия с командой RST для нашего микропроцессора). Но не более того. Нет главных признаков истинного прерывания - непредсказуемости и неявного вызова, ведь в процедуру обработки программного "прерывания" можно попасть только одним способом - явно вызвав ее. Аналогично организованы псевдопрерывания СР/М, о чем шла речь раньше, тот же принцип положен в метод "общей точки входа". Процедуры же, обслуживающие настоящие прерывания, ниоткуда не вызываются. Микропроцессор попадает в них принципиально другим способом - по аппаратным управляющим кодам контроллера прерываний.

Система обработки истинных аппаратных прерываний в PC решается абсолютно так же, как в ПК 80. Более того, на низком уровне программирования контроллера вы найдете отличия только в физической адресации двух портов микросхемы 8259. Остальное - совершенно аналогично. Но кроме обычных аппаратных микропроцессоры PC имеют еще два типа прерываний: внутренние и немаскируемые.

Системный таймер

Добрая хозяйка всегда одновременно готовит несколько блюд. Конечно, она может судить об их готовности методами "на глазок" и "по вкусу", но согласитесь, процесс станет более размеренным и цивилизованным, если на кухне появится маленький полезный прибор - таймер. Он подскажет своим сигналом о том, когда надо что-то выключить, а что-то посолить.

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

Разговор о таймере совсем не случайно начат лишь после описания системы прерываний. Очень важно, чтобы не просто отсчитывалось время, но и была возможность оперативно узнать о том, что заданный интервал истек. Для этого в роли звонка будильника таймер использует собственный канал прерывания.

Иногда системный таймер называют "часами", но это не так. Английское слово "timer" действительно означает устройство, обрабатывающее время. Но часы-то по-английски называются "clock". Не стоило бы вдаваться в дебри лингвистики, если бы в них не был отражен важный для нас факт - со временем можно работать двумя альтернативными способами. Часы отсчитывают время по возрастающей и практически не предполагают окончания счета, ведь календарь, представляется как бы старшими разрядами времени, не имеет обозримого предела. Таймер же, напротив, считает по убывающей, и счет этот заведомо должен окончиться на нуле.

В схему компьютера, в принципе, могут входить и системные часы, но пока это принято лишь в более дорогих и сложных моделях, а в дешевых "персоналках" (в том числе и в ПК 80, и в IBM PC) часы делаются программно, кстати, на основе сигналов от системного таймера.

TODO: Тут должны быть таблицы

Таблица 2. Функции микропроцессора ПК "Корвет" (КР580ВМ80А)

Таблица 3. Коды команд микропроцессора "Корвет"
0 1 2 3 4 5 6 7 8 9 A B C D E F
0 NOP LXI
BC,dW
STAX
[BC]
INX
BC
INR
B
DCR
B
MVI
B,dB
RLC DAD
BC
LDAX
[BC]
DCX
BC
INR
C
DCR
C
MVI
C,dB
RRC
1 LXI
DE,dW
STAX
[DE]
INX
DE
INR
D
DCR
D
MVI
D,dB
RAL DAD
DE
LDAX
[DE]
DCX
DE
INR
E
DCR
E
MVI
E,dB
RAR
2 LXI
HL,dW
SHLD
Adr
INX
HL
INR
H
DCR
H
MVI
H,dB
DAA DAD
HL
LHLD
Adr
DCX
HL
INR
L
DCR
L
MVI
L,dB
CMA
3 LXI
SP,dW
STA
Adr
INX
SP
INR
M
DCR
M
MVI
M,dB
STC DAD
SP
LDA
Adr
DCX
SP
INR
A
DCR
A
MVI
A,dB
CMC
4 MOV
B,B
MOV
B,C
MOV
B,D
MOV
B,E
MOV
B,H
MOV
B,L
MOV
B,M
MOV
B,A
MOV
C,B
MOV
C,C
MOV
C,D
MOV
C,E
MOV
C,H
MOV
C,L
MOV
C,M
MOV
C,A
5 MOV
D,B
MOV
D,C
MOV
D,D
MOV
D,E
MOV
D,H
MOV
D,L
MOV
D,M
MOV
D,A
MOV
E,B
MOV
E,C
MOV
E,D
MOV
E,E
MOV
E,H
MOV
E,L
MOV
E,M
MOV
E,A
6 MOV
H,B
MOV
H,C
MOV
H,D
MOV
H,E
MOV
H,H
MOV
H,L
MOV
H,M
MOV
H,A
MOV
L,B
MOV
L,C
MOV
L,D
MOV
L,E
MOV
L,H
MOV
L,L
MOV
L,M
MOV
L,A
7 MOV
M,B
MOV
M,C
MOV
M,D
MOV
M,E
MOV
M,H
MOV
M,L
HLT MOV
M,A
MOV
A,B
MOV
A,C
MOV
A,D
MOV
A,E
MOV
A,H
MOV
A,L
MOV
A,M
MOV
A,A
8 ADD
B
ADD
C
ADD
D
ADD
E
ADD
H
ADD
L
ADD
M
ADD
A
ADC
B
ADC
C
ADC
D
ADC
E
ADC
H
ADC
L
ADC
M
ADC
A
9 SUB
B
SUB
C
SUB
D
SUB
E
SUB
H
SUB
L
SUB
M
SUB
A
SBB
B
SBB
C
SBB
D
SBB
E
SBB
H
SBB
L
SBB
M
SBB
A
A ANA
B
ANA
C
ANA
D
ANA
E
ANA
H
ANA
L
ANA
M
ANA
A
XRA
B
XRA
C
XRA
D
XRA
E
XRA
H
XRA
L
XRA
M
XRA
A
B ORA
B
ORA
C
ORA
D
ORA
E
ORA
H
ORA
L
ORA
M
ORA
A
CMP
B
CMP
C
CMP
D
CMP
E
CMP
H
CMP
L
CMP
M
CMP
A
C RNZ POP
BC
JNZ
Adr
JMP
Adr
CNZ
Adr
PUSH
BC
ADI
dB
RST
0
RZ RET JZ
Adr
CZ
Adr
CALL
Adr
ACI
dB
RST
1
D RNC POP
DE
JNC
Adr
OUT
dB
CNC
Adr
PUSH
DE
SUI
dB
RST
2
RC JC
Adr
IN
dB
CC
Adr
SBI
dB
RST
3
E RPO POP
HL
JPO
Adr
XTHL CPO
Adr
PUSH
HL
ANI
dB
RST
4
RPE PCHL JPE
Adr
XCHG CPE
Adr
XRI
dB
RST
5
F RP POP
PSW
JP
Adr
DI CP
Adr
PUSH
PSW
ORI
dB
RST
6
RM SPHL JM
Adr
EI CM
Adr
CPI
dB
RST
7

Примечание.
По вертикали берется старший HEX pas ряд коде по горизонтали - младший.
Регистровые пары обозначены полными именами (ВС, DE, HL).
В квадратных скобках [..] указана косвенная регистровая адресация.
dB = 8-разрядный операнд; dW = 16-разрядный операнд; Adr = 16-разрядный адрес.

Таблица 4. Карта памяти страницы периферийных портов области УВВ
Микросхема Адрес Программируемый узел Биты Функция и доступность чтения-записи
КР580ВИ53 00H Регистр счета канала 0 0-7 Выбор частоты сигнала звукогенератора
01H Регистр счета канала 1 0-7 Выбор скорости передачи через последовательный интерфейс
02H Регистр счета канала 2 0-7 Выбор задержки для генерации системных прерываний
03H Регистр счета канала 2 0-7 Выбор счетчика чтения/загрузки байтов режима БИС
КР580ВВ55А
ДПА-3
08H Порт A 0-7 Порты A, B и C ДПА-3 полностью отданы в распоряжение пользователя и выведены на разъем интерфейса расширения системы
09H Порт B 0-7
0AH Порт C 0-7
0BH Порт управляющего слова 0-7 Режим выбирается пользователем
КР580ВВ51А 10H Регистр данных 0-7 Обмен данными через пользовательский интерфейс
11H Регистр состояния 0-7 Чтение флагов тактирования* и ошибок
11H Регистр управления 0-7 Запись команд адаптеру последовательного интерфейса
КР1818ВГ93 18H Регистр команд 0-7 Запись команд контроллеру НГМД
18H Регистр состояния 0-7 Чтение флагов тактирования* и ошибок
19H Регистр дорожки 0-7 Чтение номера текущей дорожки ГМД
1AH Регистр сектора 0-7 Запись номера искомого сектора ГМД
1BH Регистр данных 0-7 Обмен данными через контроллер НГМД
КР580ВВ51А 20H Регистр данных 0-7 Обмен данными через адаптер локальной сети
21H Регистр управления 0-7 Запись команд адаптеру локальной сети
21H Регистр состояния 0-7 Чтение флагов тактирования* и ошибок
КР580ВН59 28H Регистр окончания прерывания 0*
0-7
"0" - идентификатор регистра окончания прерывания
Выбор конца прерывания и уровня низшего приоритета
28H Регистр опроса, чтения и спецпрерываний 3
0-1
2
6-8
"1" - идентификатор регистров
Чтение векторных регистров*
Выбор режима опроса
Управление спецпрерываниями
29H Регистр запрета прерываний 0-7 Биты, установленные в "1", запрещают XXXXXXXXXX* уровня прерываний (доступен для чтения и записи)
КР580ВВ55А
Порты
A, B, C
ДПА-2
30H Регистр данных принтера 0-7 Запись байтов данных для вывод на печать
31H --- 0-7 Не используется (резерв)
32H Регистры выходных сигналов к периферийным устройствам 0-1
2
3
4-5
7
Регистр записи адаптера НКМЛ
Регистр управления мотором НКМЛ ("1" - включен)
Регистр разрешения звукогенератора ("1" - включен)
Регистр управления адаптером принтера
Бит CONTROL (выведены на разъем расширения системы)
33H Порт управляющего слова 0-7 При инициализации режим выбирается константой 82H
КР580ВВ55А
Порты
A, B, C
ДПА-1
38H Регистр выходных сигналов от периферийных устройств 0
2
3
4-7
Регистр чтения адаптера НКМЛ
Регистр чтения состояния принтера
Регистр чтения состояния атрибута АЦЗУ
Регистр чтения номера рабочего места
39H Регистр выбора управления НГМД 0-3
4
5
6
7
Выбор дисковода (0001 - A, 0010 - B)
Выбор стороны диска
Запуск дисковода на 3 секунды(0 => 1)
Выбор плотности записи ("0" - двойная)
Выбор диаметра диска ("0" - пятидюймовый
3AH Регистры управления отображением и выбора экранов ГЗУ 0-1
2
3
4-5
6-7
Выбор экрана ГЗУ (0-3), отображаемого дисплеем
Выбор знакогенератора ("0" - основной)
Выбор ширины символов ("0" - нормальная)
Управление битом инверсии
Выбор экрана ГЗУ (0-3), подключенного к процессору
3BH Порт управляющего слова 0-7 При инициализации режим выбирается константой 90H
* - не удалось разобрать что написано на скане а оригинального журнала нет в наличии. В случае появления будет исправлено.
Таблица 5. Карта конфигураций доступной микропроцессору памяти
Имена
конфигураций
и константы
системного
реестра
Адреса
ППЗУ
Адреса
ОЗУ
Адреса
ГЗУ
Адреса
УВВ
Область УВВ
Страница
клавиатуры
Страница
служебных
регистров
Страница
периферийных
регистров
Страница
АЦЗУ
TBS_8000H0-37FF4000-FFFF 3800-3FFF38003A003B003C00
NDOS 14H0-1FFF2000-F7FF F800-FFFFF800FA00FB00FC00
ROMB_118H0-3FFF4000-F7FF F800-FFFFF800FA00FB00FC00
ODOSA 1CH 0-F7FF F800-FFFFF800FA00FB00FC00
ROMB_220H0-37FF4000-BFFFC000-FFFF3800-3FFF38002A003B003C00
DOSG_13CH 0-3FFF и
8000-FDFF
4000-7FFFFE00-FFFF FF00FE00
BASIC 40H0-5FFF6000-F7FF F800-FFFFF800FA00FB00FC00
DOSA 5CH 0-FDFF FE00-FFFF FF00FE00
BASG 60H0-5FFF6000-BEFFC000-FFFFFE00-FFFF BF00
DOSG_26CH 0-BEFF C000-FFFFBF00-BFFF BF00

Примечание:

  1. Конфигурации перечислены в порядке возрастания константы системного регистра.
  2. Вса адреса даны в НЕХ форма.
  3. Для страниц области УВВ показаны только начальные (базовые) адресе.
  4. Базовые конфигурации:
    BASIC - для Бейсике ППЗУ;
    DOSA - для операционной системы МикроДОС;
    ODOSA - для операционной системы СР/М-8О.
  5. Сокращения:
    ОЗУ - оперативное запоминающее устройство;
    ППЗУ - постоянное перепрограммируемое запоминающее устройство;
    ГЗУ - графическое запоминающее устройство;
    УВВ - устройство ввода-вывода.