Маленькие тайны ZX-Lprint III
(С) Юрий Пехов, Ярославль,1995.
В последнее время распространенной моделью Spectrum-coBMecTM-мых компьютеров (во всяком случае в Московском регионе) стал "Пен-тагон-128", оснащенный интерфейсом принтера ZX-Lprint III, который позволяет подключать к нему различные типы принтеров как с параллельным, так и с последовательным интерфейсами.
Однако, при использовании этого интерфейса возникает ряд проблем. Пока речь идет о резидентном BASIC'е - т.е. выполнении команд LLIST, LPRINT, СОРУ - особых трудностей нет. Но когда нужно печатать что-то из машинного кода, в особенности графику, возникает потребность в чем-то более простом и изящном, чем управление принтером посредством управления интерфейсом. Кроме того, в процессе печати при нажатии BREAK управление автоматически передается анализатору ошибок резидентного BASIC'а, что не всегда желательно.
ЧИТАТЕЛЬ - Ч И.Т А Т Е Л Ю
С390
С1
El
3E
3F
18
DC
7E
E6
CA
C478: 00 10 96 C3 11 A9 C3 13 :35
С398
07
C5
06
F8
4F
ЗА
76
C4
E8
C480: B4 C3 16 BE C3 00 00 00 :52
СЗАО
АО
B1
32
76
C4
CI
23
18
1С
СЗА8
АА
7E
E6
07
07
07
07
C5
5A
Для демонстрации работы проце
СЗВО
06
C7
18
E8
7E
E6
01
C5
6A
дуры можно воспользоваться Бей
СЗВ8
06
BF
OF
OF
18
DE
7E
32
04
сик -программой (автостарт со 2
СЗСО
78
C4
23
7E
32
77
C4
23
F0
строки):
СЗС8
18
89
F5
D9
F1
E5
6F
26
65
C3D0
00
29
29
29
ED
5B
36
5C
E8
1 GO ТО 10
C3D8
19
44
4D
C5
ЗА
77
C4
87
06
2 BORDER 7: PAPER 7: INK 0: С
СЗЕО
5F
87
83
16
FF
14
D6
08
13
LEAR 49999: RANDOMIZE USR 15619:
СЗЕ8
30
FB
C6
08
F5
ЗА
78
C4
OF
REM : LOAD "print6"CODE
C3F0
5F
F1
CI
D5
C5
FE
00
CC
28
3 RANDOMIZE USR 15619: REM ;
C3F8
ОС
C4
47
3E
08
90
32
75
4F
LOAD "font6"CODE 64600
С400
С4
CD
4E
C4
54
5D
CI
CD
A6
4 FOR a=32 TO 127: POKE 50309
С408
20
C4
18
02
AF
C9
ЗА
77
F3
-32+a,a: NEXT a: POKE 50405,0: P
С410
С4
3C
32
77
C4
D1
CD
5D
3C
ОКЕ 23606,88: POKE 23607,251
С418
С4
ЗА
76
C4
77
El
D9
C9
0E
10 POKE 50296,0: POKE 50297,0
С420
1А
C5
F5
OA
4F
ЗА
75
C4
84
20 FOR a=l TO 9: RANDOMIZE USR
С428
47
F1
C5
CB
3F
10
FC
CI
CO
50000: NEXT a
С430
А7
CB
11
CB
17
10
FA
77
DA
С438
23
71
2B
24
14
CI
03
ЗА
F1
Точке входа WRITE соответству
С440
74
C4
3D
32
74
C4
20
D8
DB
ет адрес 50003, поскольку это де
С448
ЗЕ
08
32
74
C4
C9
7B
E6
E6
монстрационный вариант. Если Вы
С450
18
C6
40
67
7B
E6
07
OF
10
захотите получить процедуру печа
С458
0F
OF
82
6F
C9
7B
E6
18
6D
ти отдельно, удалите строки 3 и 4
С460
CB
3F
CB
3F
CB
3F
F6
58
90
из листинга на ассемблере.
С468
67
7B
E6
07
B7
IF
IF
IF
OF
С470
IF
82
6F
C9
08
00
38
00
4D
* * *
Среди программного обеспечения, так или иначе работающего с принтером, нечасто встречаются программы, поддерживающие ZX-LPRINT III, а уж тем более последовательный протокол обмена. Здесь я не имею в виду программы на BASIC'е или, к примеру, пакет DEVPAC, печать из которого производится путём вывода через стандартный поток.
Большинство программ (по крайней мере из тех, которые попадались мне) ориентированы на использование КР580ВВ55 с самым разнообразным расположением в адресном пространстве, или на параллельный порт, входящий в состав AY-3-8910/12, (MicroEd, например) . Все эти способы имеют один общий недостаток - они недоступны простому "непаяющему" пользователю, коих, по моим наблюдениям, становится все больше. Кроме того, коммерческая ценность таких программ снижается из-за отсутствия аппаратной поддержки. В качестве примера можно назвать очень симпатичную программу Screen Manager (распространяемую, кстати, ИНФОРКОМом), которая практически неизвестна широкому кругу пользователей.
Сложившуюся ситуацию можно, по-видимому, объяснить недостатком информации о работе ZX-Lprint III.
Не вдаваясь слишком глубоко в схемотехнику, попробую вкратце сформулировать главные принципы работы интерфейса.
Интерфейс состоит из трех основных частей :
- дешифратора адреса;
- теневого ПЗУ;
- портов ввода/вывода.
Как известно, в стандартном ПЗУ Sinclair'82 имеются драйверы для поддержки устройства под названием ZX-Printer, порт которого расположен по адресу #FB. При выполнении команд вывода на печать из BASIC'а происходит опрос этого порта на предмет готовности принтера. Ввиду особенностей построения (а точнее практического отсутствия) дешифратора адреса в базовой модели ZX-Spectrum системе абсолютно безразлично - обращаетесь вы по адресу #FB или по адресу #7В. Однако это не безразлично для дешифратора адреса интерфейса ZX-Lprint III, который, встретив команду IN A,(#FB), подставляет на место стандартного - теневое ПЗУ, а при появлении команды IN А,(#7В), возвращает на место ПЗУ Sinclair. А так как ПЗУ Sinclair'82 (и его наиболее удачные доработки) известны своим постоянством в отношении адресов точек входа в основные подпрограммы, программа в теневом ПЗУ может однозначно определить, какая команда была вызвана из BASIC 'а и выполнить соответствующие действия, в том числе и инициализацию интерфейса по команде LPRINT [CR].
2 Зак. 334
Теневое ПЗУ содержит набор программ для управления некоторыми типами принтеров, собственный интерпретатор команд, процедуры интерфейса с пользователем, управляющую программу и драйверы Centronics и RSJ-232C. При своей работе управляющая программа активно использует буфер принтера в котором организует массив собственных системных переменных и, кроме того, пересылает туда часть процедур, которые затем оттуда вызывает. Связано это с тем, что вызов подпрограмм из ПЗУ Sinclair, которые активно используются, например, для контроля нажатия BREAK, вывода на экран и для других целей, возможен только из ОЗУ, так как эти подпрограммы не предусматривают способов возврата в теневое ПЗУ.
Порты ввода/вывода расположены по адресу #FB (#7В) и, за исключением линии STROBE, особенностей не имеют. Сигнал STROBE формируется D-триггером из сигналов А7, WR, IORQout. В отличие от TR-DOS, здесь порты доступны вне зависимости от того, какое ПЗУ активно - Sinclair или Lpriyit. Следует лишь помнить, что команда IN А,(#FB) активизирует теневое ПЗУ.
Имея такую информацию об особенностях схемы интерфейса и вооружившись распечаткой дизассемблера ПЗУ ZX-Lprint III, можно приступить к решению проблемы адаптации программ. Задачу стоящую перед нами, можно сформулировать следующим образом: создать семейство драйвербв для управления принтерами по протоколам Centronics и RS-232C через аппаратуру интерфейса ZX-Lprint III, не пользуясь концепцией потоков и каналов (поскольку это не всегда удобно), не пользуясь управляющими кодами интерфейса (поскольку это всегда неудобно) и максимально используя процедуры из теневого ПЗУ. При этом не должно иреть значения, был ли инициализирован интерфейс ZX-Lprint III командой LPRINT [CR] или нет.
ЧИТАТЕЛЬ - ЧИТАТЕЛЮ
Начнем с рассмотрения некоторых подпрограмм, находящихся в теневом ПЗУ. Так, вывод информации на принтер по протоколу Centronics производит процедура, расположенная по адресу #00D0. Правда, при работе интерфейса она переносится в буфер принтера и вызывается оттуда. В листинге 1 приводится эта процедура с учетом реального рабочего адреса. Следует иметь в виду, что выполнение этой процедуры начинается при активном основном ПЗУ и запрещенных прерываниях.
ЧИТАТЕЛЬ-ЧИТАТЕЛЮ
Листинг 1.
|
5В6Е |
F5 |
PUSH AF | ||
|
5B6F |
CD |
54 |
1F |
CALL #1F54 |
|
5В72 |
D2 |
00 |
0D |
OP NC,#0D00 |
|
5В75 |
DB |
7В |
IN A,(#7B) | |
|
5В77 5В79 5В7В |
Е6 20 F1 |
80 F4 |
AND #80 OR NZ,#5B6F POP AF | |
|
5В7С |
D3 |
FB |
OUT (#FB),A | |
|
5В7Е 5В80 5В82 |
D3 D3 С9 |
7В FB |
OUT (#7B),A OUT (#FB),A RET |
сохранение на стеке байта предназначенного к печати ;вызов подпрограммы BREAK_ ;TEST из основного ПЗУ ;выход в BASIC с сообщением ;BREAK-cont repeats ;чтение порта (не переключая ;ПЗУ)
;выделение бита готовности ;ожидание готовности ;восстановление байта предназначенного для печати ;запись байта в выходной ;регистр
;установка сигнала STROBE ;снятие сигнала STROBE ;возврат в вызывающую ;подпрограмму
Как можно видеть из листинга 1, работа по протоколу Centronics осуществляется из ОЗУ. В ПЗУ Lprint нет фрагментов для вывода по этому протоколу, которые можно было бы корректно вызывать из ОЗУ. Поэтому единственным способом работы по протоколу Centronics будет размещение подобного драйвера в произвольном месте ОЗУ. Примерный вид такого драйвера приведен в листинге 2.
Листинг 2.
Драйвер для печати по протоколу Centronics через аппаратуру ZX-Lprint III
(с) 1994 SUNNY TRAM Research
SEND DI
PUSH AF ;сохранение байта на стеке
OUT (#FB),А ;вывод байта WAIT CALL BREAK ;здесь следует вызвать Подпрограмму,
;которая проконтролировала бы нажатие ;клавиши BREAK и соответствующим ;образом на это отрёагировала
ЧИТАТЕЛЬ-ЧИТАТЕЛЮ
;вызов необязательной подпрограммы, ;которая каким-либо способом (текстом ;или цветом BORDER) информирует о ;неготовности принтера ;чтение порта
;проверка бита готовности принтера ;ожидание готовности ;вызывается для ликвидации следов ;работ'ы процедуры P_NORDY ;снятие со стека выводимого байта ;вывод байта вместе со стробом ;снятие сигнала STROBE
Следует только внимательно следить за стеком при вызове подпрограмм опроса BREAK и вывода сообщений с тем, чтобы не нарушать количества PUSH и РбР и не утерять необходимую информацию.
Теперь рассмотрим более сложный и интересный случай - связь с принтером через последовательный порт. Интерфейс ZX-LprintIII поддерживает передачу по протоколу DTR с передачей 8 бит, 1 стартовый и 2 стоповых бита, без контроля четности. В качестве выхода TxD используется линия D7 параллельного порта, а вход DSR соответствует линии D6 порта #FB.
В отличие от протокола Centronics, процедуры, поддерживающие последовательный обмен, вызываются непосредственно из теневого ПЗУ. Их работа рассматривается в листинге 3
Листинг 3.
; подпрограмма вывода одного байта в последовательный порт ; вызывается при активном теневом ПЗУ и запрещенных ; прерываниях
|
07ВВ |
С5 |
PUSH |
BC |
;сохранение на стеке | |
|
07ВС |
Е5 |
PUSH |
HL |
;регистров,которые могут | |
|
07BD |
F5 |
PUSH |
AF |
;быть коррумпированы | |
|
07ВЕ |
ЕЕ |
FF |
XOR |
#FF |
;подготовка байта к передаче |
|
07С0 |
F5 |
PUSH |
AF |
;и его сохранение на стеке | |
|
07С1 |
CD |
F9 OE |
CALL |
#06F9 |
;это не ошибка - ПЗУ Lprint |
занимает адреса #0000 - #07FF и поэтому когда оно активно нет разницы между обращением по адресу #06F9, #0EF9 или, скажем, #2EF9 (в пределах адресной области основного ПЗУ, разумеется). Почему адресация в теневом ПЗУ выполнена таким образом, что реальные адреса перемежаются со смещенными на 2 килобайта, мне, к сожалению, неведомо. Тем не менее нас это обстоятельство смущать не должно и, в дальнейшем, в листинге дизассемблера в области кодов я буду указывать содержимое ПЗУ а в мнемониках - реальные значения адреса. По адресу #06F9 в теневом ПЗУ содержится подпрограмма проверки нажатия BREAK.
|
07С4 |
D2 |
4С 05 |
JP |
NC,#054C |
;переход если BREAK |
|
07С7 |
DB |
FB |
IN |
A,(#FB) |
;чтение порта готовности |
|
07С9 |
СВ |
77 |
BIT |
6,A |
;не выключая теневое ПЗУ |
|
07СВ |
28 |
F4 |
JR |
Z,#07C1 |
;ожидание готовности |
|
07CD |
06 |
08 |
LD |
B, 8 |
;загрузка счетчика бит |
ЧИТАТЕЛЬ-ЧИТАТЕЛЮ
|
07CF |
ЗЕ |
FF |
LD |
А, #FF |
;вывод стартового бита | ||
|
07D1 |
D3 |
FB |
OUT |
(#FB),А | |||
|
07D3 |
F1 |
POP |
AF |
;инвертированный байт | |||
|
07D4 |
CD |
ЕЕ |
OF |
CALL |
#07ЕЕ |
;задержка | |
|
07D7 |
CB |
OF |
RRC |
А |
последовательная передача | ||
|
07D9 |
D3 |
FB |
OUT |
(#FB),А |
;всех восьми бит | ||
|
07DB |
10 |
F7 |
DJNZ |
#07D4 | |||
|
07DD |
CD |
EE |
OF |
CALL |
#07ЕЕ |
;задержка | |
|
07Е0 |
ЗЕ |
00 |
LD |
А, 0 |
;передача |
двух стоповых бит | |
|
07Е2 |
D3 |
FB |
OUT |
(#FB),А | |||
|
07Е4 |
CD |
EE |
OF |
CALL |
#07ЕЕ |
;задержка | |
|
07Е7 |
CD |
EE |
OF |
CALL |
#07ЕЕ |
;задержка | |
|
07ЕА |
F1 |
POP |
AF |
;восстановление регистров | |||
|
07ЕВ |
El |
POP |
HL | ||||
|
07ЧС |
CI |
POP |
ВС | ||||
|
07 ED |
C9 |
RET | |||||
|
Листинг 4 | |||||||
|
подпрограмма |
задержки |
для последовательного протокола | |||||
|
к 07ЕЕ |
F5 |
PUSH |
AF |
сохранение аккумулятора | |||
|
07EF |
2A |
FC |
5B |
LD |
HL,(*5BFC) | ||
|
07F2 |
2B |
DEC |
HL | ||||
|
07F3 |
7C |
LD |
А, Н | ||||
|
07F4 |
B5 |
OR |
L | ||||
|
07F5 |
20 |
FB |
JR |
NZ,#07F2 | |||
|
07F7 |
F1 |
POP |
AF | ||||
|
07F8 |
C9 |
RET | |||||
|
При своей работе |
процедура |
из листинга 3 вызывает подпрограмму за- | |||||
|
держки из |
теневого ПЗУ, работа которой |
рассматривается в листинге 4. | |||||
|
Как можно |
видеть |
подпрограмма (листинг 4) использует в работе | |||||
|
двухбайтное число из |
системной переменной интерфейса с адресом #5BFC. | ||||||
|
Число это |
определяет |
скорость |
передачи |
интерфейса и изменяется управ- | |||
|
ляющей программой Lprint в зависимости |
от значения системной перемен- | ||||||
|
ной с адресом |
23728. |
Взаимосвязь значения в переменной 23728, скорос- | |||||
|
ти передачи и |
числа |
по адресу |
#5BFC показана в таблице 1. (Внутренняя | ||||
|
таблица величин |
задержки для разных |
скоростей |
содержится в теневом | ||||
|
ПЗУ по адресу |
#0502) | ||||||
|
Таблица 1. | |||||||
|
Содержимое |
Скорость передачи |
[ (бод) |
Содержимое адреса | ||||
|
адреса : |
23728 |
#5BFC | |||||
|
1 |
75 |
#0714 | |||||
|
2 |
110 |
#04D6 | |||||
|
3 |
150 |
#038С | |||||
|
4 |
300 |
#01С0 | |||||
|
5 |
600 |
#00D8 | |||||
|
6 |
1200 |
#006D | |||||
|
7 |
2400 |
#0033 | |||||
|
8 |
4800 |
#0017 | |||||
|
9 |
9600 |
#000А | |||||
ЧИТАТЕЛЬ-ЧИТАТЕЛЮ
Листинг 5.
|
06F9 |
ЗЕ |
7F |
LD |
A, |
#7F |
;опрос клавиши SPACE и если |
|
06FB |
DB |
FE |
IN |
A, |
#FE |
;она не нажата Ьыход с |
|
06FD |
1F |
RRA |
установленным флагом Carry | |||
|
06FE |
D8 |
RET |
С | |||
|
06FF |
ЗЕ |
FE |
LD |
A, |
#FE |
;если нажаты вместе Space |
|
•0701 |
DB |
FE |
IN |
A, |
#FE |
;и Caps Shift - выход со |
|
0703 |
1F |
RRA |
;сброшенным флагом Carry | |||
|
0704 |
С9 |
RET |
Опрос клавиши BREAK производит подпрограмма с адресом #06F9, она приведена в листинге 5.
Если в процессе печати была нажата клавиша BREAK, управление передается программе в теневом ПЗУ с адресом #054С, которая работает весьма любопытно (эта процедура приводится в листинге 6).
Листинг 6.
активизируется основное ПЗУ в котором по этому адресу
054С А 7 AND А
054D DB 7В IN А,(#7В)
054F FB EI
; содержится как раз процедура обработки BREAK пакета работы ; с магнитофоном
|
DI | |
|
PUSH |
AF |
|
IN |
A, #FB |
|
POP |
AF |
|
CALL |
#07BB |
|
IN |
A, #7B |
|
EI | |
|
RET |
SEND
;сохранение байта на стеке ;включение теневого ПЗУ
;вызов программы передачи байта ;включение основного ПЗУ
Следует лишь перед началом работы поместить по адресу #5BFC число из таблицы скоростей передачи, сделать это достаточно один раз.
2. Вы пишите (или адаптируете) программу, в которой нежелателен выход в BASIC потому, что разрушена BASIC-система или это снизило бы
Учитывая специфику организации программной поддержки последовательной передачи в ПЗУ ZX-Lprint, можно утверждать, что в зависимости от ситуации, есть три способа пocfpoeния внешнего драйвера. Рассмотрим эти три случая:
1. Вы пишете некоммерческую программу и не боитесь "вываливания" в BASIC при нажатии BREAK во время печати, при этом сама BASIC-система не разрушена и с адресом #5BFC не производится никаких манипуляций, или Вы на 100% уверены в адекватности реакции Вашего BREAK-перехват-чика на нажатие BREAK во время печати и, опять же, с адресом #5BFC не производится никаких манипуляций.
В этом случае можно использовать максимально короткий драйвер, приведенный в листинге 7.
Листинг 7.
ЧИТАТЕЛЬ - ЧИТАТЕЛЮ
коммерческую ценность программы. Возможна ситуация, когда единственной проверкой на BREAK является проверка во время пёчати. При этом, как и в первом случае, буфер принтера не занят. Рассмотрим такую ситуацию на примере адаптации к последовательному протоколу передачи через интерфейс ZX-Lprint программы Screen Manager 2.1 (автор С.Хан-цис). В листинге 8 приведен дизассемблер процедуры печати этой программы .
Листинг 8.
С5 F5
DD СВ 16
20 01 JR
2F CPL
; передаваемого байта в случае необходимости ADD1 4 7 LD В,А сохранение аккумулятора
ADD2 CD ЕЕ AD CALL #ADEE ;проверка на BREAK,
; идентичная процедуре на листинге 5
ADD5 D4 0D В1 CALL NC,#B10D ;подпрограмма которая
; запрашивает подтверждение прерывания печати, и в случае ; положительного ответа корректно выходит из программы
ADC8 ADC9 ADCA ADCE ADD О
PUSH ВС PUSH AF BIT 1,(IX+16) NZ,#ADD1
сохранение регистров ;на стеке
;проверка установленного ;типа интерфейса (IFSP ;или Centronics и инверсия
4Е
|
; печати |
не |
коррумпируя |
стек | ||
|
ADD 8 |
DB |
5F |
IN |
A,(#5F) |
;ожидание готовности |
|
ADDA |
17 |
RLA |
;принтера | ||
|
ADDB |
38 |
F5 |
JR |
С,#ADD2 | |
|
ADDD |
78 |
LD |
А,В |
;восстановление байта | |
|
ADDE |
D3 |
3F |
OUT |
(#3F),A |
;передача байта через |
|
ADEO |
AF |
XOR |
A |
;порт КР580ВВ55 | |
|
ADEl |
D3 |
5F |
OUT |
(#5F),A | |
|
ADE3 |
06 |
63 |
LD |
B,#63 | |
|
ADE5 |
10 |
FE |
DJNZ |
#ADE5 | |
|
ADE7 |
3E |
01 |
LD |
A, 1 | |
|
ADE9 |
D3 |
5F |
OUT |
(#5F),A | |
|
ADEB |
F1 |
POP |
AF |
;восстановление | |
|
ADEC |
CI |
POP |
ВС |
;регистров и | |
|
ADED |
C9 |
RET |
;возврат | ||
|
В листинге |
9.показано, как |
будет выглядеть этот фрагмент nocj | |||
менений.
Листинг 9.
|
ADC8 |
F3 |
DI | |||
|
ADC9 |
F5 |
PUSH |
AF |
сохранение аккумулятора | |
|
ADCA |
CD |
D2 AD |
CALL |
#ADD2 |
;подпрограмма печати |
|
ADCD |
DB |
7B |
IN |
A, #7B |
;включение основного ПЗУ |
|
ADCF |
F1 |
POP |
AF |
;восстановление аккумуля | |
|
ADD0 |
FB |
EI |
тора и прерываний | ||
|
ADD1 |
C9 |
RET |
;возврат | ||
|
ADD2 |
C5 |
PUSH |
ВС |
сохранение регистров | |
|
ADD3 |
E5 |
PUSH |
HL | ||
|
ADD 4 |
F5 |
PUSH |
AF | ||
|
ADD 5 |
EE |
FF |
XOR |
#FF |
;подготовка байта к передаче |
|
ADD7 |
F5 |
PUSH |
AF |
;и его сохранение на стеке |
ЧИТАТЕЛЬ-ЧИТАТЕЛЮ
;включение основного ПЗУ ;проверка на BREAK,
ADD8 DB 7В IN А,#7В
ADDA CD EE AD CALL #ADEE
; идентичная процедуре на листинге 5
ADDD D4 OD B1 CALL NC,#B10D подпрограмма которая
; запрашивает подтверждение прерывания печати, и в случае ; положительного ответа корректно выходит из программы печати не коррумпируя стек
IN A,(#FB) BIT 6,А JR Z,#ADD8 JP #07CD см. Листинг 3 )
ADEO DB FB ADE2 СВ 77 ADE4 28 F4 ADE6 СЗ CD 07 ; теневое ПЗУ (
;чтение порта готовности ;и включение теневого ПЗУ ;ожидание готовности ;переход на программу в
Перед использованием процедуры здесь также следует записать по адресу #5BFC число #000А (для скорости 9600 бод). Разумеется, можно изменить вышеприведенную программу для работы по параллельному протоколу.
3. Самый неприятный случай, когда буфер принтера уже чем-то занят, например, системными переменными BASIC-128. В этой ситуации единственным выходом будет создание где-то в ОЗУ немного измененной копии программ из Листингов 3-5. Я надеюсь, что из предыдущих рассуждений ясно, как это сделать. Правда, такой драйвер получится достаточно длинным, но надо же чем-то платить за удовольствие подключить принтер всего тремя проводами.
Хочется верить в то, что всего вышесказанного достаточно для адаптации любой программы к работе с интерфейсом ZX-Lprint III.