7.Каналы и потоки
Перемещение информации между программой и периферийными
устройствами управляется с помощью каналов и потоков. Желательно
представлять канал как физическое устройство, получающее информацию
(телевизор, принтер), или выдающее ее (клавиатура), а поток как
дорожку, по которой данные должны перемещаться к каналу или от
него.
В ZX Spectrum без ZX интерфейса 1 различаются 4 канала. Они
обозначаются единичными литерами:
S - Выходной канал. Данные, пересылаемые к этому каналу,
высвечиваются в верхней части экрана.
K - Канал I/O (ввода/вывода), обслуживает клавиатуру и
нижнюю часть экрана.
P - Выходной канал, данные посылаются на принтер.
R - Выходной канал, используемый только редактором для
ввода в буфер редактора данных, считанных с
клавиатуры.
Данные в каналы пересылаются подключением к ним потоков. Их мы
имеем в распоряжении 16 (0...15). После инициализации системы
следует подключение потоков 0 и 1 к каналу "K", потока 2 к "S",
потока 3 к "P". Канал "R" недоступен из ZX BASIC.
Инструкции BASIC PRINT, LPRINT, INPUT, LIST, LLIST делают
возможным доступ ко всем этим каналам, полностью скрывая их
существование. В действительности необходимы только две из них:
INPUT и LIST, а также существующая система каналов и потоков.
Каждая из этих инструкций может быть использована для пересылки
информации любым потоком. Для этой цели необходимо лишь символом
#K сигнализировать, каким каналом мы хотели бы воспользоваться.
Инструкция LLIST аналогична LIST #3, LIST это то же самое, что и
LLIST #2. Можно также писать LIST #0 или LLIST #0, но это не очень
практично, так как система не позволяет выводить туда более 22
экранных операторов и часто сама очищает эту область. Также и
команда:
PRINT #1;"Это нижняя часть экрана"
вызовет печать в нижней части экрана, также как и INPUT, зато
PRINT #3;"У тебя даже есть принтер"
перешлет данный текст на принтер. Сам PRINT равнозначен PRINT #2.
Иногда может быть выгодно использование
INPUT #2;"Сообщи свое имя":#0;A$
что вызовет вывод директивы не в нижней части экрана, а в верхней.
#0 перед A$ необходим, т.к. канал "S"является только выходным
каналом и из него нельзя читать.
В распоряжении пользователя остаются потоки с номерами от 4 до
15. Перед их использованием необходимо соответствующие потоки
подключить к каналам, которые должны их обслуживать. Для этого
служит инструкция OPEN #K;"L", где #K является выражением, дающим в
результате число между 4 и 15, а L - алфавитно-цифровым выражением,
дающим в результате одну литеру, идентифицирующую канал (в "голом"
ZX Spectrum должны быть K, S или P). Например, после команды
OPEN#6;"P" инструкция LIST #6 будет действовать как LLIST. Также и
PRINT #6 может быть использовано как LPRINT.
После использования данного потока перед подключением его к
другому каналу обязательно его отключение. Для этого служит команда
CLOSE #K. Хорошее правило рекомендует закрывать каналы (отключать
от них потоки) сразу же после их использования. Это может быть
важно для периферийных устройств (это позволит им, к примеру,
выключать контролируемые ими устройства). Потоки от 0 до 3
автоматически подключены к своим каналам и их закрытие
нецелесообразно, так как система подключит их опять. Попытки
подключения их к другим каналам могут иметь неопределенные
последствия с крахом системы включительно. Нельзя также закрывать
потоки, которые не подчинены ни к какому каналу (см. раздел "Ошибки
системы").
"Голый" ZX Spectrum не дает возможности оценить достоинства
такой системы перемещения информации (особенно когда не возникает
необходимости совместной работы с уровня ZX BASIC с нетиповыми
периферийными устройствами). Выявляются они только после
подключения ZX интерфейса 1, при работе с микродрайвом или в
компьютерной сети.
Важнейшей пользой, приносимой системой каналов и потоков,
является ее большая гибкость, определяемая легкостью управления
новыми каналами. На практике это позволяет значительно упростить
конструкции интерфейсов путем размещения в памяти компьютера
необходимых программ, обслуживающих данные устройства (что также
снижает расходы). Включение собственных каналов в систему BASIC
требует тогда только двух обычно очень простых и коротких
дополнительных процедур в машинном коде. Правда теоретически для
этой цели могло бы хватить инструкций IN и OUT, но в случае
устройств, требующих подачи сигналов с интервалом от нескольких
микросекунд до нескольких десятков микросекунд это так и остается
теорией.
Для пользователей данного устройства также немаловажным
является удобство написания программ. Значительно проще
использовать PRINT #K или INPUT #K, чем каждый раз дописывать в ZX
BASIC процедуры, готовящие данные, а затем высылающие их по одному
знаку.
Перед тем, как будут объяснены способы организации собственных
каналов, мы должны знать, как Spectrum хранит необходимую
информацию о них и о потоках, а также как он ими пользуется.
В области памяти, начинающейся с адреса, хранимой системной
переменной CHANS (#5C4F(23631)) до PROG-1 размещены базовые данные
о каналах. Они имеют стандартный формат. Описание каждого канала
занимает 5 байт и имеет форму:
------------------------------------------------------
| Адрес | Размер | Значение |
|----------------------------------------------------|
| X | 2 байта| Адрес выводной процедуры канала |
| X+2 | 2 байта| Адрес вводной процедуры канала |
| X+4 | 1 байт | Код литеры канала |
------------------------------------------------------
Процедура вывода будет вызываться с кодом очередного символа в
регистре A. Процедуры ввода, чтобы беспрепятственно
взаимодействовать с ZX BASIC, должны поставлять коды очередных
знаков, распознаваемых системой и сигнализировать доступность
данных установкой указателя C. Отсутствие входных данных должно
сигнализироваться обнулением указателя C (Carry - перенос) и Z
(Zero - нуль).
Часто случается, что данное устройство является односторонним,
тогда как адрес процедуры обработки некорректной операции выдает
положение (обычно в ROM) процедуры:
RST 0008
DEFB код
Код соответствующего сообщения об ошибке (содержимое ячейки,
следующей за RST 8) выбирается как параметр.
После инициализации системы область информации о каналах
занимает 20 байт плюс один, содержащий указатель конца области
(#80(128)). Они содержат:
--------------------------------------------------------------
| Адрес | |
|------------------------------------------------------------|
| CHANS | Адрес процедуры, пишущей в нижней части экрана |
| +2 | Адрес процедуры, считывающей данные с клавиатуры|
| +4 | "К" идентификатор канала |
|------------------------------------------------------------|
| +5 | Адрес процедуры, пишущей в верхней части экрана |
| +7 | Адрес процедуры, сигнализирующей ошибку |
| +9 | "S" идентификатор канала |
|------------------------------------------------------------|
| +10 | Адрес процедуры, вводящей считанные данные в |
| | буфер редактора |
| +12 | Адрес процедуры, сигнализирующей ошибку |
| +14 | "R" идентификатор канала |
|------------------------------------------------------------|
| +15 | Адрес процедуры, обслуживающей принтер |
| +17 | Адрес процедуры, сигнализирующей ошибку |
| +19 | "P" идентификатор канала |
|------------------------------------------------------------|
| +20 | #80(128) - указатель конца области |
|------------------------------------------------------------|
| +21 | Начало области PROG |
--------------------------------------------------------------
Как видно, в этой области нет места для размещения данных о
новых каналах. Из существующих для модификации подойти может лишь
"P", так как остальные автоматически открываются системой. Это не
является наилучшим способом, так как позволяет определить только
один дополнительный канал, а также делает невозможным одновременное
использование принтера. Выгоднее сдвинуть весь блок PROG-1 до
STEKEND на соответствующее число байт вместе с модификацией
системных переменных. Проще всего это делается системной процедурой
MAKE_ROM. Менее элегантно, но столь же действенно, размещение
информации о новых каналах где-нибудь в области системных
переменных на 38 байтах, начиная с STRMS #5C10 (23568). Для каждого
потока предназначается 2 байта. Адрес 5 байт, описывающих данный
канал, имеет вид CHANS+X-1, где X - содержимое двух байт, связанных
с данным потоком. Описание потока с номером K размещается с адреса
STRMS+6+2*K.
В момент подключения потока к каналу этим ячейкам
присваивается соответствующие значения. Сколько раз в программе
появится инструкция INPUT #K или PRINT #K, столько раз на основе
данных из таблицы, STRMS назначается адрес соответствующей
процедуры и помещается в системной переменной CURCHL #5C51 (23633).
Далее, в случае пишущей инструкции , очередные символы загружаются
в накопитель и вызывается эта процедура. В случае чтения из
накопителя выбираются очередные знаки, если они доступны (выставлен
указатель C). Неактивные потоки помечаются нулем в соответствующем
месте таблицы STRMS.
Как видно, эта система действительно очень эластична и
подключение новых каналов не трудоемко. Проблема только в том, что
инструкции OPEN и CLOSE работают только лишь со стандартными
идентификаторами "K", "S" и "P".Подключение и отключение потоков от
новых каналов (модификация данных в STRMS, а также переменной
CURCHL) должны быть выполнены программой. Это требует пересылки
дополнительных сигналов, инициализирующих данное устройство или
информирующих его о завершении сеанса работы.
В конце неприятная новость для обладателей ZX интерфейса 1:
после подключения этого устройства к Spectrum изменяются форматы
хранения данных о каналах и потоках и приведенные выше соображения
не удается применить.