Страница 103 из 124
Что в этом примере нового, так это — триггер, который устанавливается в единичное состояние, когда клавиша некоторого символа нажата и сбрасывается, когда символ считывается компьютером. Фактически это — одноразрядный регистр состояния, высокий уровень выходного сигнала которого означает возможность считать очередной символ, низкий уровень — отсутствие таковой. Компьютер может опросить бит состояния, выполнив команду ввода данных IN с другого адреса рассматриваемого устройства, дешифрованного как KBFLAG SEL' (с помощью логических вентилей, дешифраторов или каких-либо других элементов).
Для того чтобы описать состояние этого устройства, требуется только один бит, поэтому схема сопряжения управлялась только битом старшего разряда, в данном случае с помощью буфера с тремя выходными состояниями типа `125. (Никогда не подключайте двунаправленные линии к выходам устройств, специально для этого не предназначенным!) Линия, подходящая на рисунке к изображению буфера снизу, управляет трехстабильным выходным сигналом, разрешая его своим низким уровнем, что обозначено кружком.
Клавиатура терминала: пример программы. Итак, теперь у компьютера имеется возможность узнать, когда готовы очередные данные. Программа 10.4 показывает как.
Эта программа предназначена для того, чтобы считывать символы с клавиатуры терминала, адрес порта данных которого — KBDATA (хорошим стилем программирования является определение действительного кода адреса порта, который соответствует адресу, определяемому аппаратным образом, как KBDATA SEL и т. п. — несколькими операторами, располагаемыми в начале программы, как показано); каждый символ отображается (режим «эхо») на экране дисплея компьютера (адрес порта = OUTBYTE). Когда набирается целая строка, программа передает управление блоку обработки строки, функционирование которого определяется символьным содержанием строки. Когда программа готова к обработке другой строки, она печатает звездочку. Если у вас есть какой-то опыт в работе на компьютере, программа покажется вам достаточно очевидной.
Программа начинается с инициализации указателя буфера символов, осуществляемой пересылкой адреса буфера в адресный регистр ВР. Отметим, что мы не можем записать
MOV BP,charbuf
так как такая команда загрузит содержимое, а не адрес; в языке ассемблера процессора 8086 для обозначения адреса ячейки памяти используют слово offset перед идентификатором этой ячейки. После этого программа с помощью команды IN считывает бит состояния клавиатуры, логически умножает его на 80Н, чтобы оставить только бит состояния (это называется «маскированием») и результат сравнивает с нулем. Нуль означает, что бит не равен единице и программа выполняет цикл. Когда обнаружен ненулевой бит состояния, программа считывает данные из порта данных клавиатуры (при этом обнуляется бит состояния триггера) и последовательно запоминает их в буфере строки, инкрементируя указатель (ВР) и вызывая подпрограмму, которая отображает символ на экран. Наконец, программа проверяет, не оканчивается ли строка символом возврата каретки: если этого нет, то управление передается циклу повторной проверки бита состояния клавиатуры; если последний символ является символом перевода строки (CR), программа передает управление обработчику строки, после чего печатает звездочку и начинает все заново.
Даже простая подпрограмма, используемая для вывода символа на дисплей, требует нескольких операций проверки флага (бита) состояния и маскирования. Сначала подпрограмма запоминает байт в регистре АН, затем считывает и маскирует флаг занятости экрана дисплея. Ненулевой флаг означает, что дисплей занят и надо продолжить проверку этого флага; в противном случае подпрограмма заносит символ в регистр AL, пересылает этот символ в порт данных дисплея и завершается.
Несколько замечаний по программе: а) поскольку бит старшего разряда (который мы аппаратным образом определили как бит флага) является знаковым, стадию маскирования флага клавиатуры можно опустить; при этом можно использовать команду JPL KFCHK. Эта уловка, однако, срабатывает только при проверке старшего разряда и поэтому весьма специфична; б) продолжая практику хорошего стиля программирования, символ возврата каретки (0DH) и звездочки можно было бы определить как константы, подобно KBMASK; в) блок обработки строки может быть оформлен в качестве подпрограммы; г) в том случае, если процедура обработки строки слишком длинна, могут быть потеряны символы; это соображение приводит к использованию более изящного механизма прерывания, который мы вскоре рассмотрим; д) программы обслуживания клавиатуры и дисплея используются настолько часто, что PC имеет встроенные программы такого рода, вызываемые посредством программных прерываний (рассмотрим их позже); таким образом, наша программа вообще не нужна!
Обобщение битов состояний. Пример с клавиатурой иллюстрирует протокол обработки бита состояния; однако протокол настолько прост, что у вас может создаться неправильное представление об этом предмете. На самом деле интерфейс внешнего устройства несколько сложнее, как правило он предусматривает несколько флагов для фиксации различных условий. Например, в интерфейсе ленточного магнитофона вам, как правило, надо иметь следующие биты состояний: начало ленты, конец катушки, ошибка четности, движение ленты и т. д. Традиционная процедура заключается в том, чтобы свести все биты состояний в один байт или слово так, чтобы сразу считать все биты из регистра состояний с помощью команды ввода данных IN. Обычно надо назначить бит, показывающий наличие любой из ошибочных ситуаций, старшему разряду слова состояний с тем, чтобы простая проверка знака сигнализировала бы о наличии какой-нибудь ошибки; если это обнаруживается, можно проверять отдельные разряды слова (накладывая маску с помощью логической функции «И») для выявления конкретной ошибки. Более того, в сложных интерфейсах, возможно, не требуется, чтобы биты состояния сбрасывались «автоматически», как это происходило с единственным битом в нашем примере; вместо этого можно воспользоваться командой вывода данных, каждый бит которых сбрасывает соответствующий флаг.
Упражнение 10.3. У нашего интерфейса клавиатуры нет средства, позволяющего компьютеру определить, был ли пропущен символ. Измените схему так, чтобы она использовала два бита состояний: «готовность символа» (это у нас уже есть) и «потеря данных». Флаг «потеря данных» должен устанавливаться на линии D6 того же порта состояний, что и «готовность символа»; флаг «потеря данных» должен становиться равным единице, если клавиша была нажата до того, как предыдущий символ был считан компьютером, в остальных случаях он должен быть равен нулю.
Упражнение 10.4. Дополните программу 10.4 блоком проверки потерянных данных. Это должно осуществляться вызовом подпрограммы с именем LOST в тех случаях, когда флаг «потеря данных» установлен равным единице; в остальных случаях программа должна работать как и раньше.
10.09. Прерывания
Только что проиллюстрированное использование флагов состояний является одним из трех способов, используемых внешним устройством для того, чтобы «намекнуть» компьютеру на необходимость выполнения каких-то действий. Хотя во многих простых случаях этого вполне достаточно, имеется серьезный недостаток в том, что внешнее устройство не может само объявить о необходимости выполнения каких-то действий — оно должно ждать до тех пор, пока ЦП не опросит его посредством считывания содержимого регистра состояния командой IN. Устройствам, которым требуется быстродействие (такие, как диски или другие, чья работа предусматривает ввод-вывод в реальном масштабе времени), необходим частый опрос флагов состояний, и в компьютерной системе с несколькими подобными устройствами ЦП вскоре обнаружит себя проводящим основное время за проверкой флагов состояний, как в последнем примере.