14.3 (I) Шинные сигналы и процесс обмена данными
==997
Типичная шина микрокомпьютера имеет 50...100 сигнальных линий, по которым передаются адрес, данные и контрольные сигналы. PC104/ISA - пример, подходящий для небольших вычислительных устройств. Состоит из 53 сигнальных линий и 8 линий питания/земли. Вместо того, чтобы вывалить их на читателя разом, рассмотрим принципы построения шины, начиная с сигналов, необходимых для простейших операций обмена данными, а расширять этот набор будем по мере необходимости. Чтобы сделать материал интереснее, в него, как и прежде, добавлены полезные примеры: в данном случае - организации взаимодействия.
Линии шины PC104 проходят с одной платы на другую по 64-выводному этажерочному соединителю 2 ряда по 32 вывода, выходящему с платы на обе стороны. На верхней стороне видны его гнёзда, а на нижнюю выведены штыри. Две сочленённые платы показаны на рис. 14.7 : сверху - ЦПУ, снизу - АЦП.
Рис. 14.7 Процессорная плата стандарта PC104 ( сверху ) с платой быстрого АЦП. Процессор фирмы Diamond Systems имеет последовательные и параллельный порты, USB, видео, клавиатурный, дисковый и сетевой интерфейс. Периферийный модуль фирмы Chase Scientific включает два 14-разрядных АЦП со скоростью 10 Msps , цифровые порты и память. Сквозная шина PC104 проходит по нижней правой стороне платы и видна как два набора гнёзд 2×32 для 8-разрядной шины и стоящего рядом 16-разрядного расширения на 2×20 контактов. А всего получается 104 вывода
14.3.1 Основные сигналы шины: адрес, данные, строб
Чтобы передать данные по разделяемой шине, требуется выставить данные, указать приёмник и обозначить момент, когда данные действительны. Таким образом, минимальный набор линий включает данные ( DATA ), адрес ( ADDRESS ) и какую-то линию стробирования ( STROBE ). Линии данных обычно выделяются по ширине машинного слова. В 8-разрядной PC104 15 есть только 8 линий данных ( «D7...D0» ). Таким образом, за один раз можно передать только один байт, а для 16-разрядного слова придётся использовать два цикла обмена. Число адресных линий определяется количеством адресуемых устройств. Если через одну шину идут данные от периферии и от памяти ( это обычная ситуация ), то адресных линий может быть 16 или 32 ( 64K или 4G адресуемого пространства ). Шины, занятые только вводом-выводом могут ограничиться и 8 линиями ( 256 устройств ). PC104/ISA общается и с периферией, и с памятью, поэтому имеет 20 линий ADDRESS ( «A19...A0» ) или 1M адресного пространства.
[* для обмена с пространством ввода-вывода используются 16 младших линий «A15...A0» - 64K адресов, а в ISA от них остаётся только 10 ( «A9...A0» ) - 1K адресов ].
==998
Наконец, данные при передаче синхронизируются дополнительными стробирующими сигналами. Делать это можно двумя способами: отдельными линиями для чтения ( READ ) и записи ( WRITE ) или с одним стробом «DS» ( DATA STROBE ) и линией выбора операции «R/W'» ( READ/WRITE' ). PC104 использует первую схему 16 с активными низкими сигналами «IOR'», «IOW'», «MEMR'», «MEMW'». Стробов в два раза больше, чтобы развести по разным адресным пространствам операции ввода-вывода и обмена с памятью.
[* Адресные пространства разные, потому что именно так и задумано при проектировании процессора: для обмена с РАЗНЫМИ пространствами в системе команд есть РАЗНЫЕ инструкции. Противоположный подход продвигала фирма Motorola. Её процессоры имели «плоское» адресное пространство объёмом 32 разряда, где с точки зрения программиста периферийные устройства ничем не отличались от памяти, т.е. не требовали специальных действий и специальных команд. Достоинства и недостатки есть у обоих подходов, но унификация при обращении к единому ( «плоскому» ) адресному пространству в итоге перевесила ].
Эти три группы - DATA, ADDRESS и стробы - всё, что нужно для организации простого обмена на шине. Но в PC104 требуется ещё один сигнал - «AEN» ( ADDRESS ENABLE ). Он позволяет отличить обычный цикл от «прямого доступа к памяти» - ПДП ( DMA ). ПДП рассматривается в §14.3.10 , а всё, что надо знать сейчас: «AEN» НИЗКИЙ для нормального цикла и «AEN» ВЫСОКИЙ для цикла ПДП. На данный момент есть 33 сигнала шины: «A19...A0», «D7...D0», «IOR'», «IOW'», «MEMR'», «MEMW'», «AEN». Посмотрим, как все они работают.
14.3.2 Программный ввод-вывод: вывод данных
Самым простым методом обмена данными является «программный ввод-вывод»: данные передаются программой с использованием ассемблерных мнемоник «IN», «OUT». Направление действия команд «IN» и «OUT» является одной из немногих вещей, с которой согласны все производители компьютеров: «IN» означает в ЦПУ, «OUT» - из ЦПУ [* «процессоро-центричный» взгляд, так же организована шина USB и, наверно, любая шина, где роли абонентов сильно различаются ( один спрашивающий, много отвечающих )] . Полный цикл вывода данных «OUT» ( или записи в память ) очень прост и логичен ( рис. 14.8 ). Адрес приёмника выставляется процессором на линиях «A15...A0», данные на «D7...D0», после чего ЦПУ переводит строб «IOW'» в НИЗКОЕ состояние, сообщая приёмнику, что данные готовы. Для 8-разрядной PC104/ISA время гарантированного установления адреса - не менее чем за 100 ns перед «IOW'», а данные будут удерживаться до конца НИЗКОГО состояния «IOW'» и не менее чем 25 ns после установления на «IOW'» ВЫСОКОГО уровня. Чтобы поучаствовать в обмене, периферийное устройство должно смотреть на адресные линии. Обнаружив собственный адрес, устройство должно защёлкнуть состояние линий данных по заднему ( восходящему ) фронту «IOW'». Собственно, всё.
Рис. 14.8 Цикл записи ( WRITE ) в периферийное устройство. Время в наносекундах. Отметим, что такие диаграммы редко рисуют с соблюдением масштаба
14.3.2.A Пример: 8-разрядный регистр
==999
Описанная логика показана на блок-схеме 14.9 . Прямоугольник с надписью «adr_decode» выдаёт ВЫСОКИЙ уровень на линии «adr_match», когда на «A15...A0» появляется назначенный блоку адрес. [* На схеме имена написаны без подчёркивания, но подобной формы следует избегать. Современные системы разработки софта и аппаратуры крайне негативно относятся к пробелам в именах переменных или путях к файлам, поспринимая такие пробелы как разделители параметров. С подчёркиванием вы не попадёте «в непонятное» ] . ВЫСОКИЙ «adr_match» разрешает прохождение НИЗКОГО состояния «IOW'» через вентиль. Задний фронт «IOW'» защёлкивает данные с «D7...D0» в 8-разрядном регистре D-типа. Процесс можно описать короче: «данные защёлкиваются в регистре по сигналу записи, одобренному адресной логикой». Отметим, что для записи в регистр используется задний фронт «IOW'». Это обязательное условие, потому что на переднем ( спадающем ) фронте данных на линии ещё нет, а вот на заднем их правильность гарантируется. Фактически имеем время установления ≥474 ns и время удержания ≥25 ns относительно восходящего фронта. На схеме есть альтернативный участок для набора сигналов «направление-строб» фирмы Motorola ( «R/W'» и «DS'» ) вместо «интеловской» пары «IOW'», «IOR'».
Рис. 14.9 Программный вывод: данные, выставленные процессором на линиях «D7...D0» стробируются во внешнем устройстве импульсом записи, если адрес устройства совпадает с комбинацией на линиях «A15...A0». Сигнал «AEN» для простоты опущен ( см. рис. 14.10 )
Ассемблерный код, обслуживающий такой интерфейс, возмутительно прост. Если требуется передать байт, лежащий в регистре AL , достаточно одной команды:
OUT 3F8h, AL ;(send to port adr = 3F8 hex)
Процессор при этом действует в соответствии с описанием к рис. 14.8 : выдаёт нужный адрес ( 3F8h ) 17 на линии «A9...A0», переводит «IOW'» в НИЗКОЕ состояние, выдаёт содержимое AL на линии «D7...D0» и, наконец, деактивирует сигнал «IOW'» ( переводит в ВЫСОКОЕ состояние ) и снимает адрес вместе с данными. Далее начинается выполнение следующей инструкции.
14.3.2.B Пример: векторный графический дисплей
Более сложный и интересный пример показан на рис. 14.10 . Пара 16-разрядных цифро-аналоговых преобразователей, формирующих изображение на векторном дисплее, подключается к 8-разрядной шине PC104. Такую схему можно подключать к осциллографической приставке высокого разрешения ( 64K×64K ! ), которая принимает на входе аналоговые напряжения «x», «y» и рисует соответствующую точку на экране, засвечивая позицию в зависимости от уровня на входе «z».
Рис. 14.10 Подключение двух 16-разрядных ЦАПов к 8-разрядной шине PC104
ЦАПы AD660 ( Analog Devices ) имеют пару регистров шириной один байт, управляемых входами разрешения загрузке «LBE'», «HBE'» и тактируемых от линии «WR'». 8-разрядное содержимое передаётся в 16-разрядный регистр, который отвечает за уровень выходного напряжения ( рис. 14.11 ). Такая двухстадийная схема предохраняет от ложных изменений на аналоговом выходе. С ней можно переслать 16-разрядное число побайтно, а затем разом выдать его на преобразователь. Схема, конечно, чуть сложнее, чем простая запись одного 8-разрядного значения в выходной порт ( рис. 14.9 ).
Рис. 14.11 Блок-схема и временная диаграмма 16-разрядного ЦАПа AD660 с 8-разрядной шиной данных
==1000
В данном случае используется блок адресов длиной 8 байт с 3F8h по 3FFh ( двоичное представление 11 1111 1xxx ), где 3F8h ( 11 1111 1000 ) используется в качестве базового. 8-входовой элемент «И-НЕ» фиксирует факт попадания в заданный диапазон адресов ( с учётом НИЗКОГО состояния «AEN» ) 18 . Выход «8И-НЕ» разрешает работу дешифратора 1-на-8 ’138 ( §10.3.3.D ), который обслуживает две младшие адресные линии «A1» и «A0», выбирая «LBE'» или «HBE'» одной из двух соседних микросхем при обращении к адресам 3F8h, 3F9h, 3FAh и 3FBh . Загруженные данные передаются в выходные регистры ЦАПов в момент выполнения записи по адресу 3FCh 19 : в этот момент активируется сигнал «LDAC» ( LoadDAC ), и стартует формирование задержанного сигнала «z». Последний разрешает отображение точки на векторном дисплее, а задержка нужна для полного установления аналоговых координат с ЦАПов. Отметим, что при обращении к адресу 3FCh состояние линий данных игнорируется.
[* Данный приём называется операцией «фиктивной записи». Для её проведения требуется выполнить команду «OUT 3FCh , AL», причём содержимое AL никак не используется и может быть любым, т.к. нам нужен только строб, одобренный адресной логикой. Понятно, «фиктивную запись» можно проводить по адресу, который и в самом деле будет игнорировать пришедшие данные, т.е. вы должны понимать с какой аппаратурой работаете. В общем случае речь может идти и об операции «фиктивного чтения» ( «IN AL, 3FCh » ), которая выглядит и работает аналогично, только строб будет «IOR'», а «мусор» на линиях данных запишется в AL . Данные на шине не обязаны быть или не быть, впрочем как и не обязаны быть мусором, просто программе требуется, чтобы процессор выдал на шину строб нужному устройству, а данные можно просто выбросить ].
На практике разумнее всего упаковать всю логику, включая адресный дешифратор, в программируемую логическую микросхему ( см. рис. 14.12 и Часть 11 ). Можно даже добавить пару перемычек, чтобы можно было выбрать адрес.
Рис. 14.12 Вся логика схемы 14.10 легко помещается в мелкую PLD 20V8. Логические уравнения построены в предположении, что управляющие сигналы имеют активный НИЗКИЙ уровень. Данный факт отражается в заголовочном файле
14.3.3 Программируемый векторный дисплей
Передача данных через описанный интерфейс несложна. Что делать, показывает программа 14.3 . Ей нужны адрес первой пары «x,y» и число выводимых точек. Код можно оформить в виде процедуры, а указанные сведения ей можно передавать при вызове. Адреса начала массива «x» ( адрес первого значения ) кладётся в регистр SI , а «y» - в DI . Количество выводимых точек в CX . Программа в цикле пересылает последовательные значения «x,y» в порт 3F8h и 3FAh . 16-разрядные указатели точек в массиве увеличиваются [* на 2 , т.к. величины «x» и «y» 2-байтовые ] , а счётчик циклов уменьшается и проверяется на равенство нулю ( такое состояние возникает, когда выведена последняя точка ). Наконец, указатель и счётчик реинициализируются, после чего процесс начинается сначала. Величины «x» и «y» - 2-байтовые ( 16 разрядные ), и код выбирает их одной операцией «MOV» с участием 16-разрядного регистра AX , а наружу передаёт в двух последовательных циклах записи через 8-разрядную шину. Процессоры x86 сохраняют многобайтные значения в соседних ячейках памяти в порядке от младшего-к-старшему [* LSB - в памяти с наименьшим адресом, MSB - в памяти с наибольшим ] 20 . Начальный адрес всегда чётный. Данные условия определяют схемную реализацию сигналов «LBE» и «HBE».
Program 14.3
;routine to drive 16-bit xy DAC port INIT: MOV SI, xpoint ;initialize x pointer MOV DI, ypoint ;initialize y pointer MOV CX, npoint ;initialize counter RASTER: MOV AX, [SI] ;get x word (2 bytes) OUT 3F8H, AX ;send out (2 byte xfers) ADD SI, 2 ;advance x word pointer MOV AX, [DI] ;get y word (2 bytes) OUT 3FAH, AX ;send out (2 byte xfers) ADD DI, 2 ;advance y word pointer OUT 3FCH, AL ;load x and y to DAC DEC CX ;decrement counter JNZ RASTER ;not done, send more JMP INIT ;done, start over}
==1001
Несколько важных замечаний. Будучи однажды запущенной, программа продолжает выводить координаты «x,y» на дисплей бесконечно. В нормальной жизни стоит, наверно, проверять состояние клавиатуры на предмет взаимодействия с оператором. Кроме того, должна существовать возможность прервать цикл через заданное время или по «прерыванию», о котором скоро пойдёт речь. Такого сорта регенеративные дисплеи [* , т.е. требующие возобновления постоянно исчезающего изображения; бывают ещё индикаторы типа «электронных чернил», изображение на которых не пропадает само собой, а требует принудительного изменения ] не позволяют заниматься серьёзными расчётами, пока программа вывода изображения находится в активном режиме. Гораздо лучше схема, в которой индикатор обновляет изображение из своей внутренней памяти, не отнимая время центрального процессора. Так или иначе, но, если требуется устройство для получения точной копии изображения на фотографии, предложенная схема решит задачу 21 .
14.3.4 Программный ввод-вывод: чтение данных
Рис. 14.13 Цикл ввода-вывода: операция чтения ( READ ). Время в наносекундах
Обратное направление обмена данными - чтение ( «IN» ) - выглядит аналогично. Абонент смотрит на линии «A9...A0», и, если их состояние ( вместе с НИЗКИМ уровнем «AEN» ) соответствует его собственному адресу, выдаёт на шину данные при низком уровне на линии «IOR'» ( рис. 14.13 ). На рис. 14.14 показана замечательно простая блок-схема, выполняющая данную задачу. Интерфейс позволяет процессору считывать данные, защёлкнутые в ИМС ’374 - регистре D-типа с третьим состоянием выходов 22 . Тактовая линия и входы находятся в ведении периферийного устройства, поэтому через такой шлюз можно передавать любые цифровые данные, например, отсчёты АЦП. Линия, подписанная «ADR=200h», идёт с адресного дешифратора. В таком качестве может выступать PLD или несколько вентилей жёсткой логики, которые выдают ВЫСОКИЙ уровень, когда «A9...A0» имеют состояние «10 0000 0000». Конкретная реализация схемы остаётся в качестве задачи читателю.
Рис. 14.14 Интерфейс программного чтения. Внешний абонент выводит данные на шину при активном уровне на «IOR'», если состояние адресных линий в этот момент совпадает с его адресом. ’374 - восьмиканальный D-триггер с третьим состоянием выходов
В ходе исполнения инструкции «IN AL, 200h » ЦПУ выдаёт на линии «A9...A0» адрес \(200_{16}\) ( шестнадцатиричное число 200 , которое иногда пишут как 0x200 ), выдерживает театральную паузу и активирует на 530 ns сигнал «IOR'». По заднему фронту «IOR'» процессор фиксирует состояние линий «D7...D0», после чего снимает адрес с «A9...A0». Периферийное устройство обязано выставить данные на «D7...D0» не позднее, чем за 26 ns до заднего фронта «IOR'». Это очень мягкие требования, т.к. запрос выставляется за 504 ns до дедлайна. С учётом типичного времени распространения 10 ns указанные 504 ns кажутся вечностью ( и однозначно определяют эпоху рождения шины - 1981 - времена, когда были иные представления о быстродействии ).
14.3.4.A Сигналы шины: двунаправленные и не очень
В примерах выше была представлена работа двунаправленных линий. При записи их состояние контролировал процессор, а при чтении - периферийное устройство. Обе стороны должны использовать для линий «D7...D0» микросхемы с Z-состоянием. Остальными сигналами - «IOW'», «IOR'» и «A9...A0» - управляет только ЦПУ через обычные логические вентили с двумя состояниями. Такое решение типично для шин передачи данных: двунаправленный канал для самих данных и однонаправленные линии управления, которыми манипулирует только центральный процессор ( точнее, логика управления шиной ). Описанная схема обеспечивает жёсткий контроль над протоколом и временными соотношениями, чтобы не допускать аварийных событий – «столкновений на шине» - в двунаправленной части интерфейса.
==1002
В PC104 двунаправленными являются только линии DATA. ADDRESS, «AEN» и стробы READ/WRITE - однонаправленные, но, следует заметить, что такое положение вещей скорее исключение в мире сложных компьютерных систем, допускающих существование нескольких задатчиков на шине. В таких системах, очевидно, практически все сигналы являются двунаправленными.
14.3.5 Программный ввод-вывод: регистр состояния
В последнем примере компьютер может запросить данные в любое время. Это, конечно, замечательно, но как узнать, что данные обновились ? В некоторых ситуациях можно просто читать данные через равные промежутки времени, например, на регулярной основе запускать ( командой «OUT» ) цикл преобразования в АЦП, а спустя какое-то время считывать результат ( командой «IN» ). Простому наблюдению за процессом этого будет достаточно. Но возможен вариант, когда периферийное устройство имеет собственный взгляд на рабочее расписание, и было бы удобно, если бы оно могло доложить новости, не дожидаясь следующего сеанса связи [* и не заставляя ЦПУ грузить шину запросами ] .
Классический пример - клавиатура. С одной стороны нельзя пропускать нажатия клавиш, т.е. процессор должен получить каждый скан-код и, желательно, с минимальными задержками. С быстрыми устройствами, вроде дисковых накопителей и сетевых интерфейсов, ситуация ещё сложнее. Поток данных может исчисляться многими мегабайтами в секунду и следовать без задержек. Проблема решается тремя способами: с помощью регистра состояния, через прерывания или через прямой доступ к памяти. Начнём с простейшего - регистра состояния - на примере клавиатурного интерфейса ( рис. 14.15 ).
Рис. 14.15 Клавиатурный интерфейс с битом состояния. В скобках указаны сигналы PC104
В данном случае «ASCII_keyboard» - простейшее устройство, которое выдаёт код нажатой клавиши на линии «KQ7...KQ0» в сопровождении стробирующего импульса «STB» 23 . «STB» ( “strobe” ) используется для защёлкивания 8-разрядного кода символа в регистре. Выходы регистра имеют Z-состояние и позволяют подключаться к линиям DATA напрямую. Сигнал «KBDATA_SEL'» приходит со схемы декодирования адреса и переключается в НИЗКОЕ состояние, когда линии «A9...A0» принимают нужное состояние ( в сопровождении НИЗКОГО уровня на «AEN» ).
==1003
Новым элементом схемы является триггер ’74, который устанавливается с появлением нового кода клавиши и сбрасывается при считывании выходного регистра. Это 1-разрядный регистр состояния : ВЫСОКИЙ уровень означает появление нового скан-кода. Процессор может запросить статус, выполнив операцию «IN» по адресу KBDFLAG_SEL . Для передачи требуется только одна линия, которая в данном случае подключена через Z-буфер ’125 к «D7». НИКОГДА не пытайтесь подключить обычный логический выход ( с двумя активными состояниями ) к двунаправленной линии. НИКОГДА ! Линия разрешения выхода подходит к элементу ’125 сбоку и имеет активное НИЗКОЕ состояние ( о чём говорит символ инверсии на входе ).
Схему можно реализовать на стандартной логике согласно схеме, а можно засунуть в небольшую PLD, например, XC2C32, XC9536, ATF2500, Mach4032 или даже 22V10.
14.3.5.A Пример программы: клавиатурный терминал
Теперь компьютер может узнать, есть ли новые данные. Как это сделать показывает листинг 14.4 . Процедура получает символ с клавиатуры, адрес которой KBDATA ( должным программным стилем является объявление где-то в начале программы числа, на которое настроен адресный декодер периферии, в виде удобного для восприятия имени, например KBDATA_SEL ). Для каждого принятого символа создаётся «эхо», т.е. его копия отправляется на дисплей ( по адресу OUTBYTE ). После отрисовки полной строки, на следующую выводится символ приглашения ( prompt ). Протокол такого вида хорошо знаком людям, работающим с компьютерами.
Program 14.4
;keyboard handler -- uses flags KBDATA equ ***H ;put kbd data port adr here KBFLAG equ ***H ;a different port for kbd flag KBMASK equ 80H ;kbd flag mask OUTBYTE equ ***H ;put disp port adr here OUTFLAG equ ***H ;another for disp port flag OUTMASK equ ***H ;disp port busy mask charbuf DB 100 dup(0) ;allocates buffer of 100 bytes INIT: MOV BP, offset charbuf ;init char buf pntr KFCHK: IN AL, KBFLAG ;read kbd flag AND AL, KBMASK ;mask unused bits JZ KFCHK ;flag not set -- no new data IN AL, KBDATA ;flag set -- get new kbd byte MOV [BP], AL ;store it in line buffer INC BP ;and advance pointer CALL TYPEIT ;echo last char to display CMP AL, 0DH ;was it carriage return (0Dh)? JNZ KFCHK ;if not, get next char LINE: o ;if so, do something with line o ;keep at it o ;don't quit now o ;done at last! MOV AL, '*' CALL TYPEIT ;type a "prompt” -- asterisk JMP INIT ;get another line ;routine to type character ;types and preserves AL TYPEIT: MOV AH, AL ;save the char in AH PCHK: IN AL, OUTFLAG ;check printer busy? AND AL, OUTMASK ;printer flag mask JNZ PCHK ;if busy check again MOV AL, AH ;restore char to AL OUT OUTBYTE, AL ;type it RET ;and return
Программа начинается с инициализации указателя на буфер символов. Т.е. в BP загружается адрес блока памяти [* адрес первого ( младшего ) байта в блоке ] , отведённого под буфер. Обратите внимание, что нельзя просто написать «MOV BP, charbuf», потому что в результате в BP окажется содержимое из памяти с адресом charbuf , т.е. из начала буфера, а не нужный адрес этой памяти. В ассемблере x86 надо использовать модификатор «offset» перед именем переменной, чтобы сообщить, что нужен именно адрес. [* В ассемблере «отводить» память приходится программисту, который должен знать её объём и границы ( это обычное дело для микроконтроллеров ). При написании программ на языке высокого уровня программист просто выбирает модель памяти ( она влияет на величину адресного пространства, т.е. длину указателей, и формат машинных инструкций вызова и переходов ) и вид приложения, сообщая, тем самым, компилятору в каких условиях будет работать программа. Инициализацию указателей и учёт границ ведёт уже компилятор. Именно такими делами занят «стартовый» код ] . Дальше программа читает ( «IN» ) состояние клавиатуры, выделяет один старший бит ( «AND» с константой KBMASK , т.е. 80h ) - маскИрует его и проверяет на равенство байта нулю. Нуль означает, что интересующий бит сброшен [* а неинтересующие были сброшены в результате наложения маски ( X AND 0 = 0 ) ] , и программа возвращается в начало цикла. При обнаружении ненулевого значения проводится считывание данных из порта клавиатуры в буфер ( при этом сбрасывается бит состояния ), далее указатель буфера BP увеличивается, и вызывается программа, выводящая символ на экран. Наконец, проводится проверка на наличие завершающего символа – «возврата каретки» ( CR ). Если символ отличается, выполняется возврат на начало цикла, если совпадает, управление передаётся программе разбора строки, после чего на экран выводится приглашение и процесс повторяется.
Для вывода символа на экран используется подпрограмма, потому что даже такая простая операция требует маскирования и проверки некоторого количества флагов. Сперва выводимый символ сохраняется в AH , затем считывается и проверяется признак занятости дисплея. Ненулевое значение свидетельствует о занятости и требует повторной проверки. После снятия признака занятости символ возвращается в AL и выводится в дисплейный порт, а сама процедура возвращает управление.
Некоторые замечания по программе.
- Можно обойтись без маскирования бита состояния клавиатуры, т.к. он располагается в MSB ( куда его поместил разработчик схемы ), а MSB - знаковый разряд и может проверяться специальной инструкцией «JNS KFCHK». Но этот приём работает только с MSB, т.е. слишком специализирован.
- Должная программная практика требует дать коду «возврата каретки» ( 0Dh ) и знаку приглашения символические имена, как это сделано в случае KBMASK .
- Разбор строки тоже стоит оформить в виде подпрограммы.
- Если линия будет обрабатываться слишком долго, символы, набираемые на клавиатуре, будут теряться. Задача имеет изящное решение - использование прерываний .
- Клавиатурные и дисплейные обработчики нужны столь часто, что операционные системы имеют собственные утилиты, доступ к которым выполняется через «программное прерывание» ( software interrupt ). Оно будет рассмотрено позднее. В результате оказывается, что вся описанная программа на самом деле просто не нужна.
14.3.5.B Общие сведения о битах состояния
Пример интерфейса клавиатуры иллюстрирует концепцию обработки состояния, но при этом столь прост, что может создать неправильное представление о вопросе. В реальных периферийных устройствах бывает достаточно много флагов, индицирующих самые разные события. Скажем, в сетевом интерфейсе Ethernet обычно есть отдельный бит, сообщающий об успешной отправке пакета или, тут как повезёт, об аварийных событиях, возникших в ходе передачи. В качестве довольно необычного примера можно указать интерфейсную ИМС фирмы Microchip ENC28J60. В ней есть 56-разрядный регистр состояния передачи. Бит 20 сообщает об ошибке CRC при передаче. Согласно документации, «контрольная сумма в составе переданного пакета, не соответствует таковой, полученной внутренним генератором CRC» 24 . [* Эта смешная формулировка сообщает, что в момент передачи возникла «коллизия» - вклинился другой передатчик, пакет испорчен, и его необходимо передать заново ] .
==1004
Для периферийных устройств умеренной сложности обычной практикой является упаковка всех флагов состояния в одно слово, чтобы процессор мог получить полную картину, выполнив одну команду «IN». Иногда полезно иметь специальный бит, объединяющий все возможные ошибочные ситуации, и отвести под него MSB. Тогда простая проверка знака сообщит, были ли какие-нибудь проблемы. Если ошибки имеются, можно конкретизировать причину, проверяя отдельные разряды слова состояния ( операция «AND» с нужной маской ). Возможно, в сложном интерфейсе будет полезно иметь признаки ошибок и событий, которые не сбрасываются автоматически, как это происходит в примере, но требуют принудительного сброса через команду «OUT». [* Обычная практика современного «чипостроения»: информационные биты, например, «появление нового символа» сбрасываются при обращении автоматически, а биты ошибок защёлкиваются и требуют прямого сброса ] .
Упражнение 14.1
В предложенном интерфейсе клавиатуры нет возможности узнать, что произошла потеря нажатия клавиши. Измените схему, добавив второй флаг - LOST_DATA . Он должен заводиться на «D6» того же порта состояния KBFLAG и устанавливаться, если до момента считывания предыдущего скан-кода была нажата какая-то клавиша.
Упражнение 14.2
Добавьте в программу 14.4 кусок, проверяющий пропуск нажатий клавиш. При обнаружении потерянных данных должна вызываться подпрограмма с именем LOST . В нормальном состоянии всё должно работать как и прежде.
14.3.6 Программный ввод-вывод: регистры управления
==1005
Итак, имеем бит ( или набор битов ) в регистре состояния, который информирует ЦПУ о ходе процесса ( если его опрашивать ). Можно инвертировать ситуацию: ЦПУ может пересылать быт ( или коллекцию битов ) в периферийное устройство, побуждая последнее к какому-либо действию. Такой бит зовётся командным и располагается в регистре управления. Например, так можно инициализировать позиционную систему, заставляя её переместиться в точку, координаты которой записаны в пару регистров в ходе предшествующих циклов вывода ( «OUT» ) данных. Или, возвращаясь к упомянутому контроллеру Ethernet, процессор должен передать в ИМС начальный и конечный адрес буфера, подлежащего отправке, потом установить командный бит ( а именно, бит 3 ) в регистре ECON1 . Такая последовательность открывает слив, инициируя отправку пакета с очередным котиком в сеть. ИМС выполняет запрос процессора указанным способом ( в данном случае с использованием DMA для увеличения скорости, см. §14.3.10 и через регистр состояния рапортует о результате, если ЦПУ таковой запросит. ( Можно включить более агрессивный режим - прерывание ).
==1005
15 Спецификация PC104/ISA описывает и 8- и 16-разрядную шину. В книге для простоты используется только первая. <-
16 Эту схему исторически использует Intel. Motorola предложила селектор «R/W'» и строб «DS». <-
17 Авторы надеются, что читатель извинит их за неудачный выбор адреса: в IBM PC 3F8h - адрес по умолчанию для асинхронного порта. <-
18 На «AEN» устанавливается ВЫСОКИЙ уровень в момент обмена через ПДП ( §14.3.10 ), поэтому для обычного цикла ввода-вывода он должен быть НИЗКИМ. <-
19 На самом деле LDAC формируется при записи по любому адресу из диапазона 3FCh...3FFh , потому что 3-входовой элемент «И-НЕ» игнорирует состояние линий «A1» и «A0». Такой способ называется «ленивое декодирование» . <-
20 Известный как “little-endian” ( чтобы отличить его от варианта “big-endian”, в котором байты от старшего к младшему располагаются в порядке роста адресов памяти [* т.е. MSB в ячейке памяти с наименьшим адресом, а LSB в памяти с наибольшим ] , см. §14.8.2.A ). Это очень известная «детская» ловушка. <-
21 В более сложном варианте можно формировать сигнал «z» программно . Для него требуется отдельный адрес, например 3FDh , с триггером, состояние которого надо сначала устанавливать, а после некоторой задержки сбрасывать. Ширина импульса и задержки могут задаваться таймерами в ЦПУ. А можно добавить программируемый генератор импульса в саму схему, задавая временные соотношения через 8-разрядный регистр. <-
22 Именно с учётом возможной работы с общими ( разделяемыми ) шинами данных многие ИМС снабжаются Z-состоянием выхода, управляемого с вывода «OE'» . <-
23 В этой клавиатуре отсутствует интеллект, свойственный современным моделям, в которых есть собственный процессор, сканирующий матрицу, преобразующий координаты нажатых клавиш в скан-код и передающий его в компьютер по шине USB. Зато отсутствие интеллекта позволяет проиллюстрировать учебный материал. <-
24 В описании попадаются довольно интересные места. Вот, например, такая загадочная фраза: «бит 27 ( “Transmit Excessive Defer” ) означает, что пакет задержан более чем на 24.287 битового промежутка». <-