СЕКРЕТЫ "КОРВЕТА"
Система прерываний
Кого и зачем нужно прерывать? Вообразите себе большого Начальника, который сидит в своем кабинете и решает разные дела. Так как у Начальника, пусть даже большого, имеется только одна голова и он не может одновременно разбираться больше, чем с одним делом, у него есть Секретарша, которая выясняет, что за посетители приходят, а их сообщения сортирует в порядке важности для Начальника.
Конечно же, периодически Начальник говорит своей Секретарше, какие дела на текущий момент самые важные, какие могут подождать, а какие и вовсе недостойны отвлекать его внимание.
Может так случиться, что дело попалось сверхсрочное и в то же время малозависящее от других текущих проблем. Тогда Начальник вызывает Секретаршу и приказывает ей никаких посетителей не впускать, по телефону не соединять. Теперь можно спокойно, не прерываясь по пустякам, решать свое сверхважное дело.
Но в другой раз дело оказалось таким, что без постоянной связи с внешним миром его не решить: все время надо реагировать на изменяющуюся обстановку. Начальник приказывает Секретарше: по такому-то Поводу впускать и соединять немедленно, по этакому - пусть в очереди посидят, а остальным - не сметь прерывать мою работу!
Теперь назовем действующие лица своими именами: большой Начальник - это микропроцессор, Секретарша, как многие уже догадались, - контроллер прерываний, а посетители и телефонные звонки - не что иное, как запросы прерываний. Изложим подобную же историю в терминах компьютерной технологии.
Представьте себе, что программа работает с несколькими периферийными устройствами, например поддерживает обмен с другими машинами через локальную сеть и выводит какие-то данные на принтер. Если бы не было возможности прерывания, процессору пришлось бы постоянно опрашивать все эти устройства: нет ли запроса на передачу, нет ли готовности принять данные? Все равно что телефон без звонка: пока трубку не поднимешь, до тех пор не узнаешь - хочет ли кто передать сообщение, или линия свободна.
Очевидно, такая "обеспокоенность" процессора состоянием периферии была бы очень накладной и по времени, и по объему памяти, занимаемому опрашивающей программой Поэтому разработчики микропроцессора мудро предусмотрели возможность подать на одну из сорока его ножек сигнал прерывания.
Но, имея лишь один обезличенный сигнал прерывания, микропроцессору трудно разобраться - какое устройство его отвлекло и насколько важно для работы прерывание именно этого устройства для работы. Пришлось бы опять занудно опрашивать сех подчиненных на тему: "Кто сказал мяу?". А одновременное прерывание от нескольких устройств было бы вообще неразрешимой задачей.
Теперь вполне понятно, зачем микропроцессору понадобилась Секретарша в виде БИС ВН59, на которой и выполнен контроллер прерываний. Совершенно аналогичная микросхема под названием "8259" применяется в импортной аппаратуре, совместимой с IBM РС/ХТ/АТ.
Пять "секретарских" обязанностей контроллера прерываний
Во-первых, контроллер прерываний увеличивает число независимых запросов прерывания. В ПК 80 используется только одна микросхема - Секретарша, поэтому количество таких запросов ограничено восемью. В таблице1 перечислены источники запросов прерывания всех восьми уровней. Для любознательных добавим, что на каждый вход этой микросхемы можно подать выход точно такой же БИС. Это называется вторым каскадом. Несложно подсчитать, что число запросов при этом увеличилось бы до 15..64! Но конструкторы, видимо, не поленились прикинуть размеры электронной платы и стоимость машины при введении второго каскада прерываний и решили, что ежели "по-умному", то и восьми уровней вполне достаточно. С этим можно согласиться, но было бы очень полезно иметь еще и отсутствующее в данной машине прерывание от клавиатуры.
Во-вторых, контроллер прерываний сортирует запросы прерывания по задаваемым программно приоритетам. Это означает, что запрос, имеющий более высокий приоритет, обрабатывается в первую очередь. Такая ситуация возникает, когда два и более запроса приходят одновременно или очередной запрос приходит во время обработки предыдущего.
Приоритеты прерываний во многом сходны с приоритетами арифметических операций. Если в арифметическом выражении имеется более чем одна операция (например, сложение и деление), то сначала выполняется более приоритетная (в нашем примере - деление). Имеется даже аналог скобок, которые, как известно, повышают до максимума приоритет любой арифметической операции. Этот аналог будет описан чуть позже под названием "спецмаскирование". В таблице 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 типа программируемых операций, которые, в свою очередь, разделены на подтипы:
-
Операции маскирования:
- обычное маскирование запросов,
- специальное маскирование.
-
Операции чтения-опроса:
- чтение внутренних регистров;
- опрос на прерывание.
-
Операции окончания прерываний:
- обычное окончание;
- специальное окончание.
-
Операции установки приоритетов:
- произвольный сдвиг приоритетов;
- циклический сдвиг приоритетов.
Перечисленные операции довольно тесно переплетены между собой как аппаратно (они используют одни и те же порты), так и функционально (несколько операций могут быть объединены в одной команде). Поэтому следует хотя бы однократно познакомиться со всем набором команд контроллера прерываний. Большинство из них практически не применяются, хотя могут очень элегантно решать некоторые программистские противоречия.
Рисунок 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А)
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-разрядный адрес.
Микросхема | Адрес | Программируемый узел | Биты | Функция и доступность чтения-записи |
---|---|---|---|---|
КР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 |
Имена конфигураций и константы системного реестра |
Адреса ППЗУ |
Адреса ОЗУ |
Адреса ГЗУ |
Адреса УВВ |
Область УВВ | ||||
---|---|---|---|---|---|---|---|---|---|
Страница клавиатуры |
Страница служебных регистров |
Страница периферийных регистров |
Страница АЦЗУ |
||||||
TBS_80 | 00H | 0-37FF | 4000-FFFF | 3800-3FFF | 3800 | 3A00 | 3B00 | 3C00 | |
NDOS | 14H | 0-1FFF | 2000-F7FF | F800-FFFF | F800 | FA00 | FB00 | FC00 | |
ROMB_1 | 18H | 0-3FFF | 4000-F7FF | F800-FFFF | F800 | FA00 | FB00 | FC00 | |
ODOSA | 1CH | 0-F7FF | F800-FFFF | F800 | FA00 | FB00 | FC00 | ||
ROMB_2 | 20H | 0-37FF | 4000-BFFF | C000-FFFF | 3800-3FFF | 3800 | 2A00 | 3B00 | 3C00 |
DOSG_1 | 3CH | 0-3FFF и 8000-FDFF | 4000-7FFF | FE00-FFFF | FF00 | FE00 | |||
BASIC | 40H | 0-5FFF | 6000-F7FF | F800-FFFF | F800 | FA00 | FB00 | FC00 | |
DOSA | 5CH | 0-FDFF | FE00-FFFF | FF00 | FE00 | ||||
BASG | 60H | 0-5FFF | 6000-BEFF | C000-FFFF | FE00-FFFF | BF00 | |||
DOSG_2 | 6CH | 0-BEFF | C000-FFFF | BF00-BFFF | BF00 |
Примечание:
- Конфигурации перечислены в порядке возрастания константы системного регистра.
- Вса адреса даны в НЕХ форма.
- Для страниц области УВВ показаны только начальные (базовые) адресе.
- Базовые конфигурации:
BASIC - для Бейсике ППЗУ; DOSA - для операционной системы МикроДОС; ODOSA - для операционной системы СР/М-8О. - Сокращения:
ОЗУ - оперативное запоминающее устройство; ППЗУ - постоянное перепрограммируемое запоминающее устройство; ГЗУ - графическое запоминающее устройство; УВВ - устройство ввода-вывода.