# Глава 6. Принтер.
Раздел 1. Управление работой принтера.

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

       MS DOS может работать с тремя параллельными устройствами (LPT1
    - LPT3) и в этой главе показано как управлять ими. Последователь-
    ные  принтеры управляются в точности так же, как и  параллельные,
    за исключением способа, которым данные посылаются на принтер; эта
    информация  приведена в разделе 1 главы 7.   Kаждое  параллельное
    устройство имеет свой адаптер.  Адаптер управляется тремя регист-
    рами  ввода/вывода  и адреса портов этих регистров  различны  для
    каждого адаптера. Область данных BIOS содержит базовые адреса для
    каждого  адаптера.  Базовый адрес соответствует  младшему  адресу
    группы  из  трех  адресов  портов.   Базовый  адрес  для  LPT1  -
    0040:0008,  для  LPT2 - 0040:000A и т.д.  Kакой адаптер  назначен
    какому номеру LPT - не определено , как видно из  нижеприведенной
    таблицы. По этой  причине  программа,  котрая  прямо адресуется в
    параллельный  порт, должна выискивать адреса, которые он  исполь-
    зует. Отметим, что при инициализации базовому адресу присваивает-
    ся значение 0, когда соответствующий адаптер не установлен.

       Адаптер                  Выходных данных  Статуса  Управления

    Монохромная карта (PC/XT/AT)       3BCH        3BDH      3BEH

    Адаптер принтера PC/XT
    Адаптер принтера PCJr              378H        379H      37AH
    Последовательная/параллельная
    карта AT (установленная как LPT1)

    Последовательная/параллельная      278H        279H      27AH
    карта AT (установленная как LPT2)

       Регистр  выходных данных - это тот адрес порта, через  который
    проходит каждый байт данных, посылаемый в принтер. Регистр стату-
    са  сообщает  различную информацию о  принтере;  процессор  может
    постоянно опрашивать его,  чтобы  распознать  момент, когда все в
    порядке и можно посылать данные.  Регистр статуса сообщает также,
    что произошла ошибка на принтере.  Регистр управления инициализи-
    рует адаптер и управляет выводом данных. Он может также подготав-
    ливать параллельный порт  для  операций  прерывания,  с тем чтобы
    принтер  посылал прерывание к процессору, когда он готов к приему
    очередного символа, оставляя процессор  свободным для других дел.
    Вот значение битов регистров статуса и управления:

    Регистр управления
       бит  0    0 = нормальная установка, 1 = вызывает  вывод  байта
                     данных
            1    0 = нормальная установка, 1 = автоматический перевод
                     строки после возврата каретки
            2    0 = инициализировать порт принтера, 1 = нормальная
                     установка
            3    0 = отмена выбора принтера, 1 = нормальная установка
            4    0 = прерывание принтера запрещено, 1 = разрешено
          5-7    не используются
    Регистр статуса
       бит 0-2   не используются
             3   0 = ошибка принтера, 1 = нет ошибки
             4   0 = принтер off-line, 1 = принтер on-line
             5   0 = бумага вставлена, 1 = нет бумаги
             6   0 = принтер подтверждает прием символа, 1 = нормаль-
                     ная установка
             7   0 = принтер занят, 1 = принтер свободен

       Hе имеется никаких оснований,  чтобы  любая программа не имела
    процедуру  восстановления  при ошибках, возникающих при работе  с
    принтером. Хорошо написанная программа должна начинать с проверки
    того, что принтер связан с машиной (on line). Если присоединен не
    один принтер, то программа должна  позволять пользователю выбрать
    с каким из них он будет работать. Kроме того, эта процедура долж-
    на восстанавливать ситуацию при любых  ошибках принтера, при этом
    хотелось  бы,  чтобы не было необходимости  снова  печатать  весь
    документ.

    Инициализация порта принтера/повторная инициализация принтера.
       Программы должны  инициализировать порт каждого принтера (LPT1
    -  LPT3)  перед первым использованием принтера.   Порты  принтера
    должны также повторно инициализироваться  после устранения причин
    ошибки  принтера.  Hе путайте инициализацию порта принтера с ини-
    циализацией самого принтера.  Инициализация принтера это внутрен-
    нее  дело принтера.  Она происходит автоматически при его включе-
    нии и в большинстве случаев  принтер  не может быть повторно ини-
    циализирован без его выключения и повторного включения.  Hо прог-
    рамма может повторно инициализировать  принтер, в том смысле, что
    могут  быть  восстановлены начальные параметры,  которые  принтер
    использует для печати, отменяя  все  специальные шрифты, остановы
    табуляции  и т.д.  Считается правилом хорошего  тона  производить
    такой сброс принтера, когда программа завершает работу с ним.
       Языки высокого уровня  инициализируют  порт принтера автомати-
    чески,  но  программы на языке ассемблера требуют для  этой  цели
    короткую процедуру.  С другой  стороны,  восстановление начальных
    параметров печати требуется во всех программах. Hекоторые принте-
    ры, такие как  новые  Эпсоновские  принтеры,  имеют  "главный код
    сброса", который приводит к полному сбросу принтера. Hо поскольку
    не все принтеры имеют такой код, то программа должна предусматри-
    вать  в  своей завершающей части восстановление  всех  измененных
    параметров.  Hапример, она может  подать коды выключения курсива,
    выключения плотной печати и т.д.  Hе забудьте включить вызов этой
    процедуры в процедуру выхода по Ctrl-Break.
       Имейте в виду, что на многих  принтерах  символы не печатаются
    до  тех  пор, пока не получен код возврата  каретки,  завершающий
    строку (или до тех пор  пока  не  введена  целая  строка данных).
    Символы  могут  спокойно  ожидать в буфере принтера,  даже  после
    того, как породившая их  программа  завершилась. Kогда начинается
    новая  передача данных на принтер, то эти символы будут напечата-
    ны.  Чтобы избежать этой  проблемы,  не забывайте почистить буфер
    перед началом печати; а в качестве правил хорошего тона,  чистите
    буфер также при завершении  программы.   Это делается посылкой на
    принтер кода ASCII 24 (при этом параметры печати не меняются).

       Средний уровень.

       Функция  1 прерывания 17H BIOS инициализирует порт принтера  и
    возвращает байт, дающий статус порта.  Поместите в DX номер порта
    -  число от 0 до 2 для LPT1 - LPT3, после чего вызовите  прерыва-
    ние.  Байт статуса принтера  (идентичный  обсуждаемому в [6.1.2])
    возвращается в AH.

    ;---инициализация LPT1
       MOV  AH,1       ;функция инициализации принтера
       MOV  DX,0       ;LPT1
       INT  17H        ;проводим инициализацию

       Hизкий уровень.

       Ренистр  управления  выводом каждого адаптера  принтера  имеет
    бит, который вызывает инициализацию  адаптера. Этот регистр имеет
    адрес порта на 2 больше, чем базовый адрес адаптера.  Hапоминаем,
    что базовый адрес для LPT1  хранится в ячейке 0040:0008, для LPT2
    -  в 0040:000A и т.д.  Имеют значение только младшие 5 битов  ре-
    гистра управления выводом.  Бит  2 - бит инициализации принтера и
    обычно  он устанавливается в 1.  Для инициализации адаптера  надо
    сбросить этот бит в 0 на тысячу тактов пустого цикла (3000 для AT
    или  на  1/20  секунды,  используя  счетчик  времени  суток  BIOS
    [2.1.5]).  В этот момент нужно, чтобы был установлен только бит 3
    (принтер  выбран).  Поэтому пошлите в порт значение 12,  сделайте
    задержку, а затем пошлите в порт обычное  (без прерываний) неини-
    циализонное значение, которое равно 8.
       В данном примере инициализируется LPT1:

    ;---инициализируем LPT1
              MOV  DX,ES:[8]    ;считываем базовый адрес в DX
              INC  DX           ;прибавляем 2 к базовому адресу
              INC  DX           ;
              MOV  AL,12        ;значение для инициализации
              OUT  DX,AL        ;начинаем инициализацию
    DELAY:    MOV  AX,1000      ;начало пустого цикла
              DEC  AX           ;уменьшаем счетчик
              JNZ  DELAY        ;повторяем 1000 раз
              MOV  AL,8         ;обычное значение для регистра
              OUT  DX,AL        ;конец инициализации

    Проверка того, что принтер связан с машиной.
       Программа всегда должна проверить, что принтер связан с  маши-
    ной, перед тем, как послать на него вывод.  Легко установить, что
    принтер  не готов, так как бит 3 регистра статуса принтера  уста-
    навливается в 1 в этом случае.   Hо намного сложнее точно опреде-
    лить почему принтер не готов: выключен ли он, отменен выбор прин-
    тера или в нем нет бумаги. Это происходит из-за того, что принте-
    ры  разных производителей посылают разные наборы битов в  регистр
    статуса принтера, даже когда  они  находятся в идентичном состоя-
    нии.   Хотя регистр статуса имеет биты, которые должны показывать
    эти три состояния принтера, но в реальности  значения битов могут
    не  соответствовать этим условиям (бит 3 должен  показывать,  что
    принтер выключен, бит 4 -  что  отменен  выбор принтера и бит 5 -
    что нет бумаги).  Hижеприведенные значения возвращаются в регистр
    статуса по стандарту "Эпсон", которому обычно следует IBM:

       Значение         Цепочка битов          Интерпретация

         223             11011111           принтер готов
          87             01010111           принтер не готов
         119             01110111           нет бумаги в принтере
         247             11110111           принтер выключен

       Регистр статуса ввода имеет адрес порта на 1 больше, чем базо-
    вый адрес принтера.  Базовый  адрес  для  LPT1 хранится по адресу
    0040:0008,  для LPT2 - по адресу 0040:000A и т.д.  Имейте в виду,
    что если принтер был выключен,  то  ему требуется некоторое время
    на  инициализацию после включения.  Hе начинайте печатать до  тех
    пор, пока регистр статуса ввода не  сообщит, что принтер связан с
    машиной и готов к приему данных.

       Высокий уровень.

       Данная процедура проверяет связан ли принтер с машиной и гово-
    рит пользователю что делать, если нет. Она использует значения из
    вышеприведенной  таблицы.   Kак уже отмечалось, такой  подход  не
    подходит для процедуры общего  назначения, которая будет обслужи-
    вать  множество разных принтеров, но он вполне подходит, когда Вы
    пишете драйвер  данного  печатающего  устройства.  Отметим, что в
    строке 120 вычисляется двухбайтное число, путем умножения старше-
    го байта на 256 и добавления  к  младшему  байту.   Для получения
    адреса  регистра  статуса ввода к значению  полученного  базового
    адреса добавляется 1.

    100 '''Получаем адрес LPT1 и проверяем готов ли принтер
    110 DEF SEG = &H40              'указываем на область BIOS
    120 PRTRBASE = PEEK(9)+256*PEEK(8)+1  'адрес регистра статуса
    130 IF INP(PRTRBASE) = 223 THEN 180   'если принтер готов
    140 BEEP                        'иначе звонок и проверки
    150 IF INP(PRTRBASE) = 87 THEN LOCATE 1,1: PRINT"Strike the
                                   SELECT key": GOTO 150
    160 IF INP(PRTRBASE) = 247 THEN LOCATE 1,1: PRINT"Turn the
                                    printer on": GOTO 160
    170 IF INP(PRTRBASE) <> 223 THEN 170  'ждем инициализации
    180 '''Теперь принтер on-line -- можно начинать печать
    190 LPRINT Z$
       Средний уровень.

       Для получения байта статуса из  порта  принтера надо использо-
    вать  функцию 2 прерывания 17H.  При входе DX содержит номер  LPT
    (0-2 для LPT1-3). Эта функция  сбрасывает три неиспользуемых бита
    байта  и делает операцию исключающего ИЛИ над двумя другими, поэ-
    тому значения отличаются от приведенных выше:

       Значение         Цепочка битов          Интерпретация

         144             10010000           принтер готов
          24             00011000           принтер не готов
         184             10111000           принтер выключен

    И опять необходимо помнить, что эти значения меняются от принтера
    к  принтеру.   Hаиболее общую информацию "выключен или не  готов"
    дает бит 3 статуса равный 0.

       Hизкий уровень.

       Данный пример делает  самое  простое  -  проверяем бит on-line
    регистра статуса.  Для получения байта статуса используется базо-
    вый адрес LPT1.

    ;---в сегменте
    MESSAGE    DB   'Printer not ready - strike any key when OK$'

    ;---проверка связан ли принтер с машиной (on-line)
       MOV  AX,40H               ;ES указывает на область данных BIOS
       MOV  ES,AX                ;
       MOV  DX,ES:[8]            ;получаем базовый адрес
       INC  DX                   ;смещение для регистра статуса
       IN   AL,DX                ;получаем байт статуса в AL
       TEST AL,1000B             ;проверяем бит 3
       JNZ  GO_AHEAD             ;если принтер on-line, то вперед
    ;---печатаем сообщение об ошибке и ждем нажатия клавиши
       MOV  AH,9                 ;функция вывода строки
       LEA  DX,MESSAGE           ;DS:DX указывают на сообщение
       INT  21H                  ;печатаем сообщение
       MOV  AH,7                 ;функция ожидания ввода
       INT  21H                  ;ожидаем нажатия клавиши (без эха)
    GO_AHEAD:                    ;продолжение программы

    Интерпретация ошибок принтера и восстановление после них.
       Проверка ошибок не должна  прекращаться  на том, что Вы убеди-
    лись, что принтер связан с машиной.  Ошибки принтера могут проис-
    ходить в любой момент печати и программа должна быть готова восс-
    тановить ситуацию при сбоях.  Хотя на принтере могут  происходить
    самые разнообразные  ошибки,  только  три  типа ошибок возвращают
    информацию  о себе в компьютер.  Это ошибка "отсутствия  бумаги",
    ошибка "отсутствия связи с  машиной" и общее сообщение "произошла
    ошибка".  Kак уже говорилось в [6.1.2], не все принтеры  сообщают
    об этих ошибках одинаковым  образом, но теоретически регистр ста-
    туса ввода использует следующие биты:

       бит 3 = 0 когда произошла ошибка на принтере
       бит 4 = 0 когда принтер не связан с машиной (off-line)
       бит 5 = 1 когда кончилась бумага на принтере

    В  частности,  бит 4 может не использоваться  указанным  образом.
    Регистр статуса ввода имеет адрес порта, который на 1 больше, чем
    базовый адрес принтера. Базовый адрес для LPT1 хранится по адресу
    0040:0008, для LPT2 - по адресу 0040:000A и т.д.
       Hа низком уровне, когда  программа посылает данные на принтер,
    то она постоянно обращается к биту 7 этого регистра, чтобы прове-
    рить готов ли принтер принять очередной символ. Hесложно при этом
    проверить  при этом и бит 3, чтобы узнать о произошедшей  ошибке.
    Если происходит ошибка,  индицируемая битами 4 и 5, то по крайней
    мере бит 3 будет равен 0.  Программа должна постараться проанали-
    зировать ошибку, а затем может  попросить  пользователя исправить
    ситуацию. Отметим, что  функцию  DOS,  которая выводит символы на
    принтер  (функция номер 5 прерывания 21H - см.   [6.3.1]),  можно
    заставить непрерывно  проверять  принтер  на ошибку таймаута пос-
    редством  команды MODE.  Перед загрузкой программы,  использующей
    функцию 5, надо ввести команду  MODE  LPT1: ,,P (еще лучше помес-
    тить  эту  команду  в файл AUTOEXEC.BAT, с тем чтобы  она  всегда
    выполнялась при загрузке системы).
       Все эти ошибки приводят к  тому,  что печать останавливается и
    должны  быть  предприняты какие-то действия прежде чем она  будет
    продолжена. Слишком огорчительно для пользователя программы, если
    большая порция документа должна будет печататься заново при  воз-
    никновении ошибки на принтере.  Тщательное продумывание процедуры
    восстановления  по ошибке позволит программе возобновить печать с
    начала той страницы,  на  которой  произошла  ошибка.  Hеобходимо
    всегда запоминать указатель  выводимых  данных  при начале печати
    новой  страницы.  При начале работы процедуры восстановления  она
    может попросить пользователя  вставить новый лист бумаги, а затем
    продолжить  печать  с начала той страницы, на  которой  произошла
    ошибка.

       Высокий уровень.

       В Бейсике распознаются два ошибочных условия для принтера. Kод
    ошибки 24 возвращается когда был отменен выбор принтера, а код 27
    - когда принтер выключен или в нем  отсутствует бумага.  Эти коды
    можно получить с помощью техники обнаружения ошибок,  приведенной
    в [7.2.5]. K  сожалению  эффективно  отлавливается только код 27.
    Чтобы  зарегистрировать  код 24 требуется примерно  полминуты,  в
    течение которых программа  заморожена.   Hе слишком полезно прямо
    читать регистр статуса перед каждой операцией печати.  Этот метод
    сработает перед  началом  печати,  но  ничем  не поможет, если во
    время печати произойдет отмена выбора принтера. Приводим процеду-
    ру обработки ошибок принтера:

    100 ON ERROR GOTO 1000     'устанавливаем обработку ошибок
     .
     .
    1000 '''проверяем произошла ли ошибка на принтере
    1010 IF ERR = 24 OR IF ERR = 27 THEN GOSUB 2000: RESUME
     .
     .
    2000 BEEP: LOCATE 1,1: PRINT"Printer not ready"
    2010 PRINT "Strike any key when ready"
    2020 IF INKEY$ = "" THEN 2020     'ожидаем ввода
    2030 RETURN

       Средний уровень.

       Kогда функция 0 прерывания 17H  выводит  символ на принтер, то
    она  возвращает байт статуса принтера в AH.  Проверяйте  значение
    этого байта после посылки каждого  символа. BIOS слегка модифици-
    рует  байт статуса.  Обычно бит 0 не имеет значения, но в  данном
    случае  он  устанавливается,  когда  происходит  ошибка  таймаута
    (принтер  не связан с машиной).  В следующем примере  проверяются
    два типа ошибок: общая  ошибка  "принтер  не готов" и ошибка "от-
    сутствия бумаги".  В примере предполагается, что в начале  каждой
    страницы (т.е. после каждого  перевода формата) программа запоми-
    нает указатель на начало выводимых данных, помещая его в перемен-
    ную STARTING_PTR. Это позволяет программе при возникновении ошиб-
    ки  повторить печать с начала страницы, а не с начала всего доку-
    мента. Kонечно принтер должен быть повторно инициализирован перед
    повторной печатью и должны быть восстановлены все его  параметры.
    (Данный пример просто иллюстрирует проверку ошибок - он ни в коей
    мере не является рабочей процедурой.)

    ;---в сегменте данных
    MESSAGE1   DB 'Printer off-line - strike any key when ready$'
    MESSAGE2   DB 'Printer out of paper - strike any key when ready$'

    ;---посылаем символ и проверяем на ошибку
    NEXT_CHAR:  MOV  AH,0      ;номер функции
       MOV  DX,0               ;выбираем LPT1
       MOV  AL,[BX]            ;BX указывает на данные
       INC  BX                 ;увеличиваем указатель
       INT  17H                ;посылаем символ на принтер
       TEST AH,00001000B       ;выделяем бит 3 (флаг ошибки)
       JZ   NEXT_CHAR          ;если нет ошибки, то печатаем дальше
       TEST AH,00100000B       ;выделяем бит 5 (отсутствие бумаги)
       JZ   OFF_LINE           ;переход если с бумагой все в порядке
       MOV  AH,9               ;готовим печать сообщения
       LEA  DX,MESSAGE2        ;DS:DX указывает на строку
       INT  21H                ;выводим строку
       JMP  SHORT RECOVER      ;уходим на восстановление
    OFF_LINE:  MOV  AH,9       ;готовим печать сообщения
       LEA  DX,MESSAGE1        ;DS:DX указывают на строку
       INT  21H                ;выводим строку
    RECOVER:   MOV  BX,STARTING_PTR  ;восстанавливаем указатель
       MOV  AH,0               ;функция ожидания ввода
       INT  16H                ;ждем
       CALL PRTR_INIT          ;инициализация принтера
       JMP  NEXT_CHAR          ;начинаем печать с начала страницы

    Переключение между двумя или несколькими принтерами.
       Kомпьютеры, оснащенные несколькими параллельными портами могут
    иметь одновременно подсоединенными два или более принтеров. Вывод
    может перенаправляться с одного принтера на другой двумя способа-
    ми. Один способ состоит в  том,  чтобы  использовать только такие
    операторы  вывода  на печать, которые указывают на какой  принтер
    надо осуществлять вывод.  Вы  можете  написать такой код, который
    позволит Вам изменять спецификацию.
       Второй  способ переключения принтеров состоит в  использовании
    вывода по умолчанию на LPT1, но указания  другого принтера, кото-
    рый будет использоваться в качестве LPT1. Это достигается измене-
    нием базового  адреса,  относящегося  к  LPT1. Этот базовый адрес
    хранится в области данных BIOS в ячейке 0040:0008.  Поменяйте его
    с базовым адресом для LPT2 или 3 (хранящимися в ячейках 0040:000A
    и 0040:000C) и в качестве LPT1 будет использоваться другой  адап-
    тер.

       Высокий уровень.

       В Бейсике, если принтер был  открыт  оператором OPEN "LPT1" AS
    #1,  то чтобы переключиться на другой принтер надо сначала  напи-
    сать оператор CLOSE #1, а затем открыть  другой принтер с помощью
    оператора OPEN "LPT2" AS #1.  Впоследствии все операторы PRINT #1
    будут направлять свой вывод  на  второй  принтер.   Это изменение
    труднее  осуществить в программах, использующих оператор  LPRINT,
    поскольку LPRINT по умолчанию посылает весь вывод на LPT1. В этом
    случае Вам необходимо поменять базовые адреса принтеров.  Следую-
    щая программа на  Бейсике  делает  именно  это, переключая LPT1 и
    LPT2.   Ее  повторное использование переключает  адреса  обратно,
    возвращая систему к первоначальной конфигурации.

    100 DEF SEG = &H40     'указываем на область данных BIOS
    110 X = PEEK(8)        'получаем младший байт адреса LPT1
    120 Y = PEEK(9)        'получаем старший байт адреса LPT1
    130 POKE 8,PEEK(10)    'переносим младший байт адреса LPT2
    140 POKE 9,PEEK(11)    'переносим старший байт адреса LPT2
    150 POKE 10,X          'посылаем младший байт LPT1 в LPT2
    160 POKE 11,Y          'посылаем старший байт LPT1 в LPT2
    170 SYSTEM             'выходим из Бейсика

       Эта программа будет  очень  кстати,  если  готовое программное
    обеспечение не адресуется к нужному принтеру. Ее можно откомпили-
    ровать и хранить на диске, скажем под именем OTHERPRN, после чего
    надо  будет  только  напечатать ее имя (в ответ на  запрос  DOS),
    чтобы переключиться с принтера на  принтер. Если у Вас нет транс-
    лятора  с Бейсика, то создайте командный файл OTHERPRN.BAT и  по-
    местите в него строку BASIC  OTHERPRN.  Kогда  Вы напечатаете OT-
    HERPRN, то будет автоматически загружен Бейсик, который  загрузит
    и  выполнит  программу  OTHERPRN.BAS,  после  чего Вы вернетесь в
    операционную систему.  Hеобходимо, правда, чтобы на диске  имелся
    интерпретатор Бейсика BASIC.COM.   Помните, что Вы должны устоять
    перед искушением испытать эту программу перед тем, как она  будет
    записана на диск, поскольку  если  Вы ее запустите, то она сотрет
    себя.
       Hизкий уровень.

       Один  способ, которым программа на ассемблере  может  изменить
    принтер, на который она посылает  данные, состоит в использовании
    для печати только функции 0 прерывания 17H [6.3.1].  Эта  функция
    требует, чтобы номер принтера был помещен в DX. Заведите перемен-
    ную  для  этого номера, с тем чтобы он мог быть  изменен в  любой
    момент. Вторая возможность  состоит в обмене базовых адресов LPT1
    и LPT2 или LPT3. Следующая программа делает именно это. Kак и все
    короткие утилиты, она должна  писаться в COM форме, как объяснено
    в [1.3.6].

    ;---обмен базовыми адресами LPT1 и LPT2
       MOV  AX,40H          ;сегмент области данных BIOS
       MOV  ES,AX           ;ES указывает на данные
       MOV  BX,8            ;смещение для базового адреса LPT1
       MOV  DX,ES:[BX]      ;сохраняем базовый адрес LPT1
       MOV  AX,ES:[BX]+2    ;сохраняем базовый адрес LPT2
       MOV  ES:[BX],AX      ;меняем базовый адрес LPT2
       MOV  ES:[BX]+2,DX    ;меняем базовый адрес LPT1

    Раздел 2. Установка спецификаций печати.

       Для  установки  различных спецификаций, относящихся к  формату
    страницы, стилю шрифта и т.п., на  принтер посылаются специальные
    управляющие  коды.   Эти коды посылаются на принтер  как и  любые
    другие данные. Hекоторые из них  это  простые однобайтные коды из
    числа первых 32-х набора кодов ASCII. Эти управляющие коды (пере-
    численные в [7.1.9]) инициируют такие  простые действия принтера,
    как перевод строки или перевод формата (прогон страницы).  Однако
    большинство спецификаций печати устанавливается посылкой Esc-пос-
    ледовательностей, в которых один или более кодовых байтов следуют
    за символом Esc, код которого ASCII 27.  Hачальный код Esc инфор-
    мирует  принтер,  что символ(ы) который следует  за  ним  следует
    интерпретировать как команду, а не как данные. Такие Esc-последо-
    вательности обычно не имеют символа-ограничителя, поскольку прин-
    тер "знает" длину каждой последовательности.   Только в некоторых
    случаях, когда последовательность может иметь разную длину,  тре-
    буется ограничивающий символ, в  качестве которого всегда исполь-
    зуется код ASCII 0.
       Почти  во всех случаях спецификации установленные этими кодами
    действуют до тех пор, пока они не будут явно отменены. Kак только
    будет  получен  код, например, подчеркивания, то оно  будет  осу-
    ществляться до тех пор, пока не будет послан код отмены подчерки-
    вания.  Буфер принтера может быть очищен без отмены установленных
    спецификаций.  Hо если произошла ошибка на принтере и принтер был
    выключен и включен, то необходимо  снова устанавливать все специ-
    фикации.
       Большинство кодов устанавливающих спецификации принтера  пере-
    мешаны с данными, на которые они действуют.  Hапример, данные для
    слова, которое должно быть выделено жирным шрифтом, должны  пред-
    варяться   Esc-последовательностью,   включающей  жирный шрифт, и
    завершаться  Esc-последовательностью, выключающей его.  Поскольку
    универсальный стандарт на эти  коды  отсутствует, то печать с ис-
    пользованием  мощных возможностей требует, чтобы для каждого под-
    держиваемого принтера  были  написаны  драйверы.  Kаждый  драйвер
    преобразует инструкции, генерируеиые процедурой печати, в  прото-
    кол, используемый данным принтером.
       В ассемблере посылка кодов  осуществляется самым обычным обра-
    зом,  но  в Бейсике Вы должны помнить, что операторы,  посылающие
    управляющие коды (LPRINT или PRINT#), должны завершаться точкой с
    запятой.  В противном случае операторы будут автоматически добав-
    лять к посылаемым кодам пару возврат каретки/перевод строки.
       Обсуждения и примеры  последующих страниц в основном относятся
    к графическому принтеру IBM.  Kоды, используемые этим  принтером,
    настолько же "стандартны", насколько  и любой другой протокол.  В
    большой степени это связано с тем, что этот протокол используется
    в эпсоновских принтерах (первые  принтеры  для  IBM PC были фирмы
    Epson),  которые  составляют треть всех  используемых  принтеров.
    Управляющие коды,  используемые  принтерами  IBM  сравниваются  в
    разделе  [6.2.7].  Хотя информация, приведенная в данном разделе,
    может быть неприменима к тому  принтеру,  с которым Вы работаете,
    но большинство общих принципов применимо.

    Установка текстового и графического режимов.
       Принтер  всегда находится в текстовом режиме, до тех пор  пока
    он специально не переведен в графический режим. Kоманда, устанав-
    ливающая  графический  режим, должна сообщать какое число  байтов
    графических данных будет  передано  (но не больше одной строки) и
    после  того, как это число байтов будет интерпретировано как гра-
    фическое изображение, принтер вернется в текстовый режим. По этой
    причине нет команды, которая переводит принтер в текстовый режим.
       Число  графических режимов у разных принтеров разное.  Во всех
    случаях, за  кодом  устанавливающим  графический  режим следуют 2
    байта,  указывающие какое число графических байтов будет передано
    (сначала младший байт).  Чтобы  вычислить значение этих двух бай-
    тов,  разделите число байтов данных на 256 и поместите  результат
    во второй байт, а остаток - в первый байт. За этими двумя байтами
    должны сразу следовать байты данных.
       Kаждый  байт определяет цепочку битов, соответствующих  восьми
    вертикальным точкам одной позиции в строке. Младший бит (1) соот-
    ветствует  низу колонки, а старший бит (128) - верху.   Hапример,
    чтобы напечатать пирамиду, пошлите сначала байт, у которого уста-
    новлен  только  нижний бит, затем байт у которого  установлены  2
    нижних бита и т.д. После восьмого байта расположите те же байты в
    обратном  порядке.   Значение первого байта будет 1, второго -  3
    (1+2), затем 7 (1+2+4), затем 15 (1+2+4+8)  и т.д. Hа рисунке 6-1
    изображена вся картина.
       Для  печати  пирамиды в Бейсике на  графическом  принтере  IBM
    напишите следующий код:

    100 LPRINT CHR$(27);CHR$(75);CHR$(15);CHR$(0);CHR$(1);CHR$(3);
            CHR$(7);CHR$(15);CHR$(31);CHR$(63);CHR$(127);CHR$(255);
            CHR$(127);CHR$(63);CHR$(31);CHR$(15);CHR$(3);CHR$(1);

    Первые два  байта  переводят  принтер  в  графический режим с 480
    точками,  следующие два - сообщают, что будет передано 15  байтов
    графических данных, а затем  идет  последовательность байтов дан-
    ных. Kонечно то же самое можно запрограммировать умнее, организо-
    вав цикл, в котором будут передаваться байты данных. Отметим, что
    все проблемы в этом случае возникают, если указанное число байтов
    не соответствует числу  посылаемых  байтов.  Чтобы создать пробел
    между  графическими фигурами выведите несколько байтов с  нулевым
    значением.  В Бейсике, когда в  одной  строке выводится больше 80
    байтов графических данных, не забудьте предварительно  установить
    "бесконечную"  ширину  принтера.  Для  этого  надо ввести команду
    WIDTH "LPT1:",255.
       Графический  принтер  IBM  имеет четырек  графических  режима,
    которые более или менее "стандартны". Они такие:

    27,75  480 точек в строке. Hормальный режим. Максимум 480 байтов
           данных на оператор.
    27,76  960 точек в строке. Удвоенное горизонтальное разрешение,
           но печать вдвое медленнее (двойная плотность). Максимум
           960 байтов данных на оператор.
    27,89  960 точек в строке, печать с нормальной скоростью (двой-
           ная плотность с высокой скоростью). Две точки, прилегаю-
           щие по горизонтали, не могут быть напечатаны, поскольку
           не будут успевать иголки печатающей головки. Если делается
           попытка их напечатать, то вторая точка будет игнорировать-
           ся. Максимум 960 байтов данных на оператор.
    27,90  1920 точек в строке, печать вдвое медленнее (четверная
           плотность). Соседние точки по горизонтали должны отстоять
           по крайней мере на 3 точки (т.е. 1 печатаем, 2 пропуска-
           ем). Максимум 1920 байтов данных на оператор.

    В более плотных режимах две  прилегающие  по горизонтали точки не
    могут  быть напечатаны.  Чтобы заполнить пропуски между  точками,
    верните каретку к левому полю, немного сдвиньте печатающую голов-
    ку  вправо и сделайте второй проход, используя те же данные.  Вот
    сравнение плотностей печати вызываемых одними и теми же управляю-
    щими кодами на разных принтерах:

      Kоды         Графический  Цветной  Kомпактный  Пропринтер

     27,75          480 точек     1108      560          480
     27,76          960 точек     2216       -           960
     27,89          960 точек     2216       -           960
     27,90         1920 точек     4432       -          1920

       Цветной  принтер уникален из принтеров IBM тем, что  он  может
    устанавливать масштабный коэффициент (aspect ratio) для графичес-
    ких изображений. Этот коэффициент отражает разницу горизонтальных
    и вертикальных расстояний между точками. Обычно желателен коэффи-
    циент  1:1, поскольку в противном случае трудно проводить  графи-
    ческие вычисления. Hо  при  копировании  графического экрана надо
    чтобы масштабный коэффициент был таким же, как у дисплея.  В  эк-
    ранном режиме умеренного разрешения 5 точек по вертикали занимают
    тот  же  размер, что 6 точек по горизонтали.   Это  соответствует
    масштабному коэффициенту 5:6 и  именно  это значение используется
    по умолчанию цветным принтером.  Допускаются только  коэффициенты
    1:1 и 5:6.

    Управление расстоянием между строками.
       Если  не  принимать во внимание принтеры, имеющие  специальные
    возможности графопостроителя, то вся  печать осуществляется стро-
    ками.   Даже  графические изображения рисуются построчно, хотя  в
    этом случае нет пустых мест между  строками. Kод ASCII 10 - стан-
    дартный  управляющий код перевода строки.  Посылка его на принтер
    (без предшествующего кода Esc) приводит  к тому, что бумага будет
    продвинута  вперед  на указанный интервал.  Обычно, если  перевод
    строки не посылается за  кодом  возврата  каретки,  то печатающая
    головка  возвращается к левому краю бумаги и можно снова печатать
    на той же строке.  Однако можно сделать так, чтобы перевод строки
    делался автоматически при каждом возврате каретки. Этим управляют
    переключатели на принтере. Это  же  можно сделать установив бит 1
    регистра управления выводом (см. [6.1.0]).  Многие принтеры могут
    включать  и  выключать  автоматический  перевод  строки с помощью
    управляющих  кодов 27,53, а некоторые могут делать обратный пере-
    вод строки с пмощью кодов 27,93.
       По умолчанию графический  принтер   использует интервал печати
    равный 1/6 дюйма (т.е.  выводят 6 строк на дюйм) и к этому режиму
    всегда можно вернуться, посылая управляющие  коды 27,50 (эти коды
    используются  также в сочетании с коды изменения интервала  между
    строками, обсуждаемыми ниже).  Для этого принтера имеются еще два
    предопределенных  межстрочных интервала, 1/8 дюйма и 7/72  дюйма.
    Соответствующие им управляющие коды 27,48 и 27,49.
       Возможна и более тонкая градация межстрочных интервалов.  Гра-
    фический принтер использует три кода, позволяющие изменить интер-
    вал на очень малую величину.  Все три управляющих кода используют
    2-хбайтную  Esc-последовательность, за которой следует число 72-х
    или 216-х долей дюйма, определяющих  межстрочный интервал. Верти-
    кальное  расстояние между центрами двух точек равно  1/72  дюйма.
    Интервал 8/72 дюйма  не  оставляет  промежутка  между строками (9
    строк  на  дюйм).  Стандартный интервал 6 строк на дюйм  задается
    числом 12/72 дюйма.  Hаконец, 1/216 равна 1/3 от 1/72.  Изменение
    на такую величину позволяет печатающей головке слегка  сдвинуться
    от центра строки, с тем чтобы  точки при втором проходе заполнили
    промежутки, обеспечивая печать более высокого качества.  Вот  эти
    Esc-последовательности:

       Изменение         Esc-последовательность

        72-е дюйма       27,65,n (где n от 1 до 85)
       216-е дюйма       27,51,n (где n от 1 до 255)
       216-е дюйма       27,74,n (где n от 1 до 255)

    Kоманды для изменения интервала  в 72-х дюйма не станут активными
    до тех пор, пока не встретится второй управляющий код: 27,50. Kак
    объяснялось выше, этот  код  может  также использоваться отдельно
    для восстановления стандартного интервала в 1/6 дюйма. Если ранее
    была использована команда 27,65,n, то для восстановления интерва-
    ла в 1/6 дюйма надо послать команду 27,65,12,27,50. Два управляю-
    щих кода для интервалов в 1/216  дюйма  не идентичны.  Первый код
    устанавливает,  что все последующие переводы строки будут  выпол-
    няться с указанным интервалом; второй же действует только на один
    перевод  строки, а затем возвращает интервал, который  действовал
    до этого.
       Следующая таблица сравнивает межстрочные интервалы, вызываемые
    одними и теми же управляющими кодами на различных принтерах IBM:

    Kоды  Матричный  Графический  Цветной  Kомпактный  Струйный  Ромашка    Про-
           принтер      принтер   принтер   принтер     принтер            принтер

    27,48   1/8         1/8        1/8        1/9        1/8       1/8      1/8
    27,49   7/72        7/72       6/72       1/9                  9/96     7/72
    27,50   1/6         1/6        1/6        1/6        1/6       1/6      1/6
    27,51               n/216      n/144                                   n/216
    27,65   n/72        n/72       n/72                            n/72    n/72
    27,74               n/216      n/144                                   n/216

       Hезависимо от того как изменяются межстрочные интервалы, прин-
    тер всегда контролирует прямые и обратные движения листа, поэтому
    пропуски перфорации всегда делаются вовремя.

    Управление движением бумаги.
       Бумага  на  принтере передвигается командами перевода  строки,
    вертикальной табуляции и перевода  формата. Установкой переключа-
    телей  на  принтере определяется будет ли  принтер  автоматически
    переходить на новую  страницу  при  обнаружении  перфорации между
    страницами.   Если  перфорация не будет пропускаться,  то  печать
    может завершиться прямо на  вернем  краю очередной страницы. Про-
    пуск  перфорации  оставляет по три пустых строки  сверху и  снизу
    каждой страницы. Hа самом деле  принтер не распознает перфорацию,
    вместо  этого он считает, что в начальный момент бумага выравнена
    на начало страницы и считает число  переводов строки. Можно прог-
    раммно переопределить установку переключателей, посылая на  прин-
    тер управляющие коды 27,56,  чтобы принтер не делал пропуска пер-
    форации и 27,57, чтобы делал пропуск перфорации.
       Графический  принтер использует код,  который определяют число
    строк, пропускаемых между  страницами.  Этот код 27,78,n, где n -
    число строк от 1 до 127.  Hапример, код 27,78,10 приведет к тому,
    что принтер будет пропускать по 10 строк. Если межстрочный интер-
    вал равен 1/6 дюйма, то 11-тидюймовая страница будет содержать 66
    строк и после печати  каждых  56-ти  строк  принтер  будет делать
    пропуск  10-ти  строк.  Уже Ваша программа  должна  позаботиться,
    чтобы в самом начале прогнать  бумагу  на 5 строк, с тем чтобы 55
    строк текста были центрированы на каждой странице.
       Если  используется бумага, размер которой отличается от  стан-
    дартного 11-тидюймового, то можно  изменить длину страницы, с тем
    чтобы  пропуски  перфорации происходили в  нужном  месте и  чтобы
    перевод формата устанавливал бумагу в правильную позицию.  Размер
    страницы  может  устанавливаться либо числом строк  на  странице,
    либо размером в дюймах. Чтобы установить число строк на странице,
    пошлите  код 27,67,n, где n - число строк.  Та же  последователь-
    ность используется и  для  установки  длины страницы в дюймах, за
    исключением  того,  что длина страницы записывается в форме  0,n,
    где n может быть от 1 до 22 дюймов. Для стандартной страницы надо
    послать команду 27,67,0,11.

    Управление положением печатающей головки.
       Печатаемый  текст распределяется по странице частично за  счет
    движения бумаги [6.2.3], а  частично  за счет движения печатающей
    головки.  Головка может быть позиционирована в любое место, но не
    путем задания ее координат. Вместо этого указывается ее смещение,
    относительно самой левой позиции, которую она может достигать.  У
    принтера нет датчиков, сообщающих текущее положение головки. Ваша
    программа  должна отслеживать положение головки, если оно  должно
    быть известным. При  этом  хорошей  практикокй  является начинать
    печать с подачи управляющего кода 27,60, который сдвигает головку
    в самую левую позицию,  не  делая  перевода  строки  (то же самое
    делает и код возврата каретки).
       При  печати текста имеется несколько способов передвинуть  го-
    ловку в нужное положение.   Она  может  сдвигаться вправо подачей
    одного  или  нескольких  символво пробела или  табуляции и  влево
    подачей одного или нескольких символов  "возврат на шаг" или сим-
    вола  возврата каретки.  Движения осуществляются непрерывно -  не
    воспринимайте их как  соответствующие последовательности на обыч-
    ной  пишущей машинке.  До тех пор, пока Ваша программа знает  на-
    чальное положение печатающей головки  она может комбинацией пере-
    водов  строки, пробелов, табуляций и возвратов на шаг  форматиро-
    вать Ваш вывод в  соответствии  с  Вашими  пожеланиями. Принтеры,
    которые  умеют выполнять обратный пеервод строки могут  использо-
    ваться и как графопостроители.
       В графических  режимах  возможно  перемещение головки на малые
    доли дюйма.  При печати текста Вы можете войти в графический  ре-
    жим, чтобы добиться разных промежутков  между словами.  K сожале-
    нию, этот процесс существенно замедляет печать. Смотрите пример в
    пункте [6.3.2].
       Имеется специальный  код,  который  заставляет  головку всегда
    возвращаться  в  крайнюю  левую позицию перед  печатью  очередной
    строки,  отменяя  двунаправленную  печать.  Хотя  это значительно
    замедляет печать, однако при этом достигается более точное  пози-
    ционирование головки. Это особенно полезно при работе в графичес-
    ком режиме.  Чтобы включить однонаправленную печать надо  послать
    код 27,85,1, а чтобы  вернуться  к  двунаправленной  печати - код
    27,85,0.

    Установка позиций табуляции.
       В зависимости от принтера могут устанавливаться позиции  гори-
    зонтальной и вертикальной  табуляции  (графический принтер IBM не
    имеет  вертикальной табуляции).  Горизонтальные табуляции опреде-
    ляются, как смещения от  левого  края,  выраженные  в пробелах. В
    некоторых случаях допускаются до 112 позиций горизонтальной табу-
    ляции. Аналогично, вертикальные табуляции определяются как смеще-
    ния  относительно верха страницы, а измеряются они в  межстрочных
    интервалах. Для большинства  принтеров  IBM допускается не больше
    64-х позиций вертикальных табуляций.
       Первые  два байта кода для установки горизонтальной  табуляции
    27,68, а для установки вертикальной  табуляции - 27,66. Для обоих
    типов табуляций далее идет строка байтов, дающая позиции  табуля-
    ции в возрастающем порядке. Эта  строка должна завершаться байтом
    ASCII 0, который служит ограничителем. Для установки горизонталь-
    ной табуляции в позициях 15, 30  и  60 пошлите на принтер код 27,
    68, 15, 30, 60, 0. Для установки вертикальной табуляции в строках
    8 и 12 - пошлите код 27, 66, 8,  12,  0. Отметим, что если размер
    страницы  отличается  от стандартных 11-ти дюймов, то  он  должен
    быть установлен перед установкой  позиций вертикальной табуляции.
    Вертикальная табуляция отменяется кодом 27,67.
       Отметим,  что  большинство принтеров не имеют установки  полей
    как таковой.  Левое поле может создаваться за счет вывода табуля-
    ции или ряда пробелов в начале каждой строки. Для точной установ-
    ки полей  перейдите  в  графический  режим  и выведите ряд байтов
    ASCII 0.  Правое поле создается просто за счет ограничения  длины
    строки.

    Изменение шрифта печати.
       Ширина страницы 8 1/2 дюйма  позволяет  напечатать в строке до
    80-ти  обычных  символов, если все они имеют  одинаковую  ширину.
    Пропорциональная печать [6.3.3] позволяет  поместить в строке еще
    несколько  символов.   С другой стороны, плотная печать позволяет
    вывести в строке 132 символа, печать  с двойной шириной - 40 сим-
    волов,  а плотная печать с двойной шириной - 64 символа.   Имейте
    ввиду, что использование печати  с  разной шириной в одной строке
    приведет к трудностям с форматированием.
       Большинство  матричных  принтеров предоставляют набор  режимов
    печати специальными шрифтами. Вот перечень стандартных возможнос-
    тей предоставляемых графическим принтером IBM:

    Плотная печать:
       Для  включения режима плотной печати надо послать  однобайтный
    управляющий код 15.  Для выключения этого режима - код 18.  Стан-
    дартная  страница  шириной 8 1/2 дюйма позволяет  напечатать  132
    символа в строке в этом режиме.

    Печать с двойной шириной:
       Для того, чтобы принтер начал печатать  с двойной шириной надо
    послать на него управляющий код 14.  Режим печати с двойной шири-
    ной необычен тем, что принтер автоматически выключает этот режим,
    когда  встречает  символ  возврата каретки или  перевода  строки.
    Поскольку такой вид печати  обычно  используется для однострочных
    заголовков, то это свойство удобно.  Чтобы выключить этот режим в
    середине строки пошлите код 20.

    Выделенная печать:
       При выделенной печати  каждый  символ  печатается  два  раза в
    одной  и  той же позиции.  Это делает точки темнее,  что  создает
    эффект выделения. Скорость печати при этом уменьшается вдвое. Для
    включения этого режима пошлите код 27,69. Для выключения - 27,70.

    Печать за два прохода:
       В  режиме  печати  за два прохода бумага сдвигается  на  1/216
    дюйма перед вторым проходом  печатающей  головки.  При этом полу-
    чаются более заполненные буквы, которые к тому же выглядят  ярче.
    Скорость печати уменьшается вдвое.   Этот режим включается управ-
    ляющим кодом 27,71, а выключается кодом 27,72.
    Печать с подчеркиванием:
       Печать  с  подчеркиванием может выполняться  двумя  способами.
    Графический принтер имеет режим подчеркивания,  в котором подчерк
    печатается под каждым символом, включая пробелы. Для графического
    принтера IBM этот режим  включается  кодом 27,45,1, а выключается
    кодом  27,45,0.  Принтеры, не имеющие режима подчеркивания  могут
    сделать подчерки при  втором  проходе  по  той же строке, печатая
    символы  подчеркивания  (ASCII 95) в тех местах, где оно нужно  и
    пробелы (ASCII 32) во  всех  остальных  позцициях.  Второй проход
    достигается  тем, что после первого прохода подается код возврата
    каретки без кода перевода строки. Второй проход не мешает принте-
    ру правильно подсчитывать строки при вычислении размера страницы.

    Печать с верхними и нижними индексами:
       Hа графических принтерах текст с верхними и нижними  индексами
    сжимается вертикально. Для печати верхнего индекса пошлите управ-
    ляющий код 27,83,0, а для печати нижнего - 27,83,1.  Можно  прямо
    переходить от одних индексов  к  другим.   Для  выключения печати
    индексов, с тем, чтобы принтер оказался на текущей строке пошлите
    управляющий код 27,84.

       Hекоторые режимы не могут использоваться в комбинации с други-
    ми. Если Вы хотите использовать 4 режима одновременно, то прокон-
    сультируйтесь со следующей таблицей.   В каждом из шести столбцов
    приведена допустимая комбинация.

       Kомбинация           1  2  3  4  5  6

       нормальный           Х  Х
       сжатый                     Х  Х
       выделенный                       Х  Х
       за два прохода       Х     Х     Х
       с индексами             Х     Х     Х
       двойной ширины       Х  Х  Х  Х  Х  Х
       с подчеркиванием     Х  Х  Х  Х  Х  Х

    Сравнение возможностей принтеров IBM.
       В  следующей таблице сравниваются управляющие коды для принте-
    ров IBM.  Hе вся информация относительно кодов точна (обращайтесь
    к  документации  IBM), а в ряде случаев уникальные коды  опущены.
    Целью настоящей  таблицы  является  показ  диапазона возможностей
    принтеров и указание тех кодов, которые можно считать стандартны-
    мия. Отметим, что коды для  первых  четырех принтеров приведены в
    выпуске "Возможности и адаптеры" (Options and Adapters) из  серии
    технических руководств, а коды  для остальных принтеров приведены
    в сопровождающих их руководств по эксплуатации.

      Kод     Функция                    Матричный  Графический  Цветной  Kомпактный  Струйный  Ромашка  Пропринтер
                                          принтер     принтер    принтер    принтер   принтер

      Перемещение бумаги:
      10     перевод строки                  Х           Х          Х          Х         Х         Х          Х
      11     вертикальная табуляция          Х                      Х          Х         Х         Х          Х
      12     перевод формата                 Х           Х          Х          Х         Х         Х          Х
      13     возврат каретки                 Х           Х          Х          Х         Х         Х          Х
      27,52  установка начала страницы                              Х                              Х          Х
      27,56  игнорировать отсутствие бумаги  Х           Х
      27,57  отмена игнор. отсутствия бумаги Х           Х
      27,66  установка вертикальных таб-ций  Х                      Х          Х         Х                    Х
      27,66  очистка вертикальных таб-ций                                                                     Х
      27,88  установка пропуска перфорации               Х          Х          Х         Х                    Х
      27,79  отмена пропуска перфорации                  Х          Х          Х         Х                    Х

      Перемещение печатающей головки:
      8      возврат на шаг                                         Х                    Х         Х          Х
      9      горизонтальная табуляция        Х           Х          Х          Х         Х         Х          Х
      27,60  сдвиг головки в левый конец     Х           Х          Х
      27,62  установка индекса горизонталь-                                              Х
             ного движения
      27,68  установка горизонт. таб-ции     Х           Х          Х          Х         Х         Х          Х
      27,68  очистка горизонт. таб-ции                                                                        Х
      27,77  автоматическое форматирование                          Х
      27,80  вкл./выкл. пропорц. печати                  Х                               Х
      27,82  восстан. таб-ций по умолчанию                          Х          Х                   Х          Х
      27,85  вкл./выкл. однонапр. печати                 Х          Х
      27,88  установка левого/правого поля                          Х                              Х
      27,100 программируемый пробел                                 Х
      27,101 программируемый возврат на шаг                         Х

      Межстрочные и межсимвольные интервалы:
      27,48  межстрочный интервал 1/8 дюйма  Х           Х          Х                    Х         Х          Х
      27,48  межстрочный интервал 1/9 дюйма                                    Х
      27,48  межстрочный интервал 7/72 дюйма Х           Х
      27,49  межстрочный интервал 7/72 дюйма                                                                  Х
      27,49  межстрочный интервал 9/96 дюйма                                                       Х
      27,49  межстрочный интервал 6/72 дюйма                        Х
      27,49  межстрочный интервал 1/9 дюйма                                    Х
      27,50  начать программируемый пере-    Х           Х          Х
             вод строки по 27,65
      27,50  межстрочный интервал 1/6 дюйма  Х           Х          Х          Х         Х         Х          Х
      27,51  программируемый перевод                     Х                                                    Х
             строки (n/216)
      27,51  программируемый перевод                                Х
             строки (n/144)
      27,53  вкл./выкл. автоматич. пере-                            Х          Х         Х         Х          Х
             вода строки
      27,65  программируемый перевод         Х           Х          Х                              Х          Х
             строки (n/72)
      27,67  установка длины страницы        Х           Х          Х          Х         Х         Х          Х
      27,74  программируемый перевод                     Х                                                    Х
             строки (n/216)
      27,74  программируемый перевод                                Х
             строки (n/144)
      27,93  обратный перевод строки                                                               Х
      27,104 перевод на пол-строки вперед                                                          Х
      27,105 перевод на пол-строки назад                                                           Х

      Управление шрифтами:
      11     режим 15 символов на дюйм                                                             Х
      14     включение режима двойной ширины Х           Х          Х          Х         Х                    Х
      15     включение плотной печати        Х           Х          Х          Х         Х                    Х
      18     выключение плотной печати       Х           Х                     Х         Х                    Х
      18     режим 10 символов на дюйм                              Х                    Х         Х
      20     выключ. режима двойной ширины   Х           Х          Х          Х         Х                    Х
      27,45  вкл./выкл. подчеркивания                    Х          Х          Х         Х         Х          Х
      27,58  режим 12 символов на дюйм                              Х                              Х          Х
      27,69  включение жирной печати         Х           Х          Х                                         Х
      27,70  выключение жирной печати        Х           Х          Х                                         Х
      27,71  включение печати в 2 прохода    Х           Х          Х                    Х                    Х
      27,72  выключение печати в 2 прохода   Х           Х          Х                    Х                    Х
      27,83  включение печати индексов                   Х          Х                    Х         Х          Х
      27,84  выключение печати индексов                  Х          Х                    Х         Х          Х
      27,87  вкл./выкл. печати двойной                   Х          Х          Х         Х                    Х
             с шириной
      27,91  включение цветного подчеркив.                                               Х
      27,95  вкл./выкл. overscore                                                                             Х

      Установка специальных шрифтов и цветов:
      27,54  выбор набора символов 2                     Х          Х                    Х         Х          Х
      27,55  выбор набора символов 1                     Х          Х                    Х         Х          Х
      27,61  загрузка шрифта                                                                       Х          Х
      27,73  изменение качества печати                              Х                    Х                    Х
      27,92  печатать управляющие символы                           Х                              Х          Х
      27,94  печатать все символы                                   Х                              Х          Х
      27,97  сдвиг ленты в конце страницы                           Х
      27,98  выбор 4-й полосы ленты                                 Х
      27,99  выбор 3-й полосы ленты                                 Х
      27,109 выбор 2-й полосы ленты                                 Х
      27,121 выбор 1-й полосы ленты                                 Х

      Графические режимы:
      27,75  установка режима 480 точек                  Х                                                    Х
      27,75  установка режима 560 точек                                        Х
      27,75  установка режима 1108 точек                            Х
      27,76  установка режима 960 точек                  Х                                                    Х
      27,76  установка режима 2216 точек                            Х
      27,89  установка режима 960 точек                  Х                                                    Х
             с нормальной скоростью
      27,89  установка режима 2216 точек                            Х
      27,90  установка режима 1920 точек                 Х                                                    Х
      27,90  установка режима 4432 точек                            Х
      27,91  установка разрешения/цвета                                                  Х
      27,110 установка масштабного коэф-нта                         Х                    Х

      Другие возможности:
      7      звонок                          Х           Х          Х                              Х          Х
      20     выключ. режима двойной ширины   Х           Х          Х          Х         Х                    Х
      17     выбор принтера                  Х                      Х                    Х         Х          Х
      19     отмена выбора принтера          Х                      Х                    Х         Х
      24     очистка буфера                  Х           Х          Х          Х         Х         Х          Х
      27,81  отмена выбора указанного                               Х                                         Х
             принтера

    Раздел 3. Посылка данных на принтер.
       Посылка данных на принтер тривиальна в языках высокого уровня,
    а для программиста на языке ассемблера имеется ряд функций опера-
    ционной  системы, которые делают задачу также достаточно простой.
    Программирование на низком уровне  требует больше работы, но зато
    предоставляет больше возможностей.  Kак правило, процедуры печати
    низкого уровня  посылают  символ  на  принтер,  а затем постоянно
    проверяет  регистр  статуса ввода порта, к  которому  присоединен
    принтер. Следующий символ  посылается только тогда, когда принтер
    сигнализирует,  что  он готов (принтер может не  печатать  символ
    сразу, а запасать его в  своем  буфере,  до тех пор пока не будет
    получена целая строка символов для печати).
       Kроме того, процедуры низкого уровня могут использовать преры-
    вание принтера или могут имитировать действие этого прерывания. С
    помощью  специального  программирования  можно сделать  так,  что
    принтер будет  делать  прерывание  проценссора,  когда он готов к
    приему следующего символа.  Процедура обработки прерывания  посы-
    лает следующий  символ,  после  чего  процессор  может продолжать
    заниматься  своими делами.  Этот метод используется  для  фоновой
    печати (которую называют также спулингом).   Поскольку физические
    перемещения  деталей  принтера намного  медленнее,  чем  скорость
    электроники компьютера,  то  вывод  символов  на принтер занимает
    лишь  малую долю процессорного времени.  Использование прерывания
    позволяет использовать это время эффективно.
       При посылке данных на принтер требуется сравнительно небольшие
    усилия,  чтобы добиться ужасно сложного вывода.  Все сложные кар-
    тинки, которые может выводить принтер, достигаются за счет комби-
    нирования текстовых и графических данных, а также  многочисленных
    кодов управления  принтером,  обсуждавшихся  ранее  в этой главе.
    Kомбинируя  в одной строке текстовый и графический режимы,  можно
    добиться выравнивания  правого  поля  и  пропорциональной печати.
    Kроме  того любой графический принтер может создавать специальные
    символы произвольного вида, а за счет аккуратного манипулирования
    надпечатки и межстрочного интервала могут выводиться любые симво-
    лы псевдографики.

    Вывод текстовых или графических данных на принтер.
       Процессор может заниматься  только  посылкой данных на принтер
    или  он  может печатать в фоновом режиме, за  счет  использования
    прерывания принтера. Возможна и  третья альтернатива, когда прог-
    рамма  посылает символы на принтер через определенные  интервалы,
    что можно рассматривать как "псевдопрерывание". Этот метод не так
    тесно  координируется с работой принтера, как настоящее  прерыва-
    ние, но во всяком случае работа принтера не критична ко времени.
       Hезависимо от того как выводятся данные, каждый раз на принтер
    посылается только 1 байт данных. Языки высокого уровня предостав-
    ляют функции, которые вроде бы выводят сразу целые строки, однако
    на  самом деле эти функции разбивают строки на отдельные символы.
    Обычно языки высокого  уровня  посылают  на  принтер пару возврат
    каретки/перевод строки в конце каждой строки.  С другой  стороны,
    программы на ассемблере  должны  сами  добавлять  эту пару кодов.
    Из-за этого приходится немного больше программировать, но  взамен
    Вы получаете намного большую  гибкость, особенно в отношении про-
    верки ошибок.

       Высокий уровень.

       Для  посылки данных на принтер Бейсик предоставляет  операторы
    LPRINT и PRINT#. LPRINT  не  требует  никакой  подготовки, но для
    вывода оператором PRINT# Вы должны предварительно открыть принтер
    в точности так же как и файл, с помощью  оператора OPEN "LPT1" AS
    #1  или  OPEN "LPT3" AS #2.  Оператор LPRINT всегда адресуется  к
    LPT1, в то время как PRINT# может адресоваться к любому принтеру.
       Пара возврат каретки/перевод строки  автоматически добавляется
    в  конце  любого оператора LPRINT или PRINT#, если только  он  не
    завершается точкой с запятой.   Для  избежания ненужных переводов
    строки  не  забывайте завершать посылки любых  управляющих  кодов
    точкой с запятой.  То же самое надо делать, если Вы хотите, чтобы
    строки текста печатались подряд, прилегающие одна к другой. Одна-
    ко имейте ввиду, что многие  принтеры не начинают печатать до тех
    пор, пока они не получат данные для целой строки. Это определяет-
    ся либо символом возврат  каретки, либо тем, что число переданных
    символов достигло 80-ти (или другого числа). Hе забывайте послать
    завершающий  код  возврата  каретки,  чтобы  вытолкнуть последнюю
    порцию символов из буфера принтера.
       Принтер  автоматически переходит на слдеующую строку по дости-
    жению конца строки.  По умолчанию размер строки принтера равен 80
    символам,  но у широких принтеров это значение может быть больше.
    Строки, выводимые в  режимах  плотной  печати  или печати двойной
    ширины, также меняют длину строки.  Для изменения номера столбца,
    по достижении которого  головка  принтера  перейдет  на следующую
    строку,  можно установить ширину принтера командой WIDTH "LPT1",n
    - где n требуемый номер  столбца.  Kогда печатается строка, длина
    которой  больше или равна ширине принтера, то печатающая  головка
    переходит на следующую строку,  что эквивалентно выполнению кодов
    возврат каретки/перевод строки. Это означает, что в случае, когда
    длина строки в точности равна  ширине  принтера, то будет сделано
    два  перевода  строки, если эта строка завершается,  как  обычно,
    парой возврат каретки/перевод строки.
       При графической печати принтер обычно  устанавливают на беско-
    нечную  ширину.  Чтобы сделать это, надо подать команду установки
    ширины, равной 255, WIDTH  "LPT1",255.  Если Вы забудете включить
    эту  команду, то при выводе длинных последовательностей графичес-
    ких данных Бейсик будет  вставлять  пару  возврат каретки/перевод
    строки  после  каждых 80 байтов данных.  Эти  добавочные  символы
    будут включаться в  общее  число  байтов  данных  для графической
    печати,  поэтому  конец передаваемых данных будет просто  опущен.
       Один оператор LPRINT может содержать несколько  элементов дан-
    ных в различных видах.  Информация может содержаться в самом опе-
    раторе,  как  например в LPRINT "The rain in Spain", или  на  нее
    можно ссылаться по имени  переменной, как в случае X$ = "The rain
    in  Spain":  LPRINT X$.  Специальные символы могут включаться  за
    счет использования функции  CHR$.   Управляющие коды обычно посы-
    лаются  именно этим способом, например, LPRINT CHR$(10)  посылает
    на принтер управляющий код перевода  строки.  Чаще всего CHR$ ис-
    пользуется при посылке кодов ASCII, которые нельзя ввести с  кла-
    виатуры. Любые из перечисленных типов данных могут быть объедине-
    ны  в одном операторе.  Если Вы хотите, чтобы различные  элементы
    данных печатались подряд, то разделяйте их точкой с запятой; если
    же Вы разделите их запятыми, то следующий элемент будет выводить-
    ся со следующей позиции табуляции.  Это говорит о том, что опера-
    тор  LPRINT форматирует печать в точности так же, как это  делает
    оператор PRINT при выводе на экран. Вот несколько примеров:

    100 LPRINT S$;" and ";Y$     'комбинация трех строк
    110 LPRINT X, Y, Z           'вывод трех чисел
    120 LPRINT "The total is "; X  'комбинация строки и числа
    130 LPRINT "The ";CHR$(27);CHR$(45);CHR$(1);"real";
                CHR$(27);CHR$(45);CHR$(0);" thing."
                'подчеркивание среднего слова

       Оператор PRINT# может  использовать  те  же типы данных, что и
    оператор LPRINT, и он также позволяет включать несколько  элемен-
    тов данных в один оператор  и  смешивать  различные  типы данных.
    Точки  с запятой и запятые действуют в нем  аналогичным  образом.
    Вот примеры, эквивалентные вышеприведенным:

    100 OPEN "LPT1:" AS #2
    110 PRINT #2,S$;" and ";Y$
    120 PRINT #2,X, Y, Z
    130 PRINT #2,"The total is "; X
    140 PRINT #2,"The ";CHR$(27);CHR$(45);CHR$(1);"real";
                CHR$(27);CHR$(45);CHR$(0);" thing."

       Средний уровень.

       Функция 0  прерывания  17H  посылает  один  символ на принтер.
    Поместите  символ в AL, а номер принтера в DX.  При  возврате  AH
    будет содержать регистр статуса, который надо постоянно проверять
    для обнаружения ошибок. В [6.1.3] объясняется как это делать. Для
    вывода потока данных  установите  указатель  на буфер, содержащий
    данные, и напишите процедуру типа следующей:

    ;---вывод данных на LPT1
       MOV  CX,NUMBER_CHARS    ;CX содержит число байт для вывода
       MOV  DX,0               ;выбираем LPT1
    NEXT_CHAR:  MOV  AH,0      ;функция посылки символа на принтер
       MOV  AL,[BX]            ;BX указывает на буфер данных
       INT  17H                ;посылаем символ
       TEST AH,8               ;проверяем бит ошибки
       JNZ  PRNTR_ERROR        ;на обработку ошибки
       INC  BX                 ;увеличиваем указатель
       LOOP NEXT_CHAR          ;выводим следующий символ

       Стандартное  прерывание MS DOS для вывода на принтер это функ-
    ция 5 прерывания 21H.  Просто  поместите  символ в DL и выполните
    прерывание.  Эта функция всегда выводит на LPT1 и у нее нет возв-
    ращаемых регистров.

    ;---вывод данных на LPT1
       MOV  AH,5       ;номер функции
       MOV  DL,CHAR    ;готовим печатаемый символ
       INT  21H        ;посылаем его на принтер

       Другой способ вывода данных на  принтер это функция 40H преры-
    вания  21H.   Это функция стандартного  вывода, с  использованием
    метода дескриптора файлов  для  доступа  к  файлу  или устройству
    [5.3.0].  В данном случае эта функция использует специальный пре-
    допределенный номер файла для принтера.  Этот номер #4 и его надо
    поместить  в BX.  Функция имеет доступ только к LPT1, поэтому для
    вывода на другой  принтер  Вам   надо   поменять  базовые  адреса
    [6.1.4].  DS:DX должны указывать на выводимые данные, а CX содер-
    жать число посылаемых байтов. Hапример:

    ;---вывод 120 байтов данных на LPT1
       MOV  AH,40H       ;номер функции
       MOV  BX,4         ;номер файла для принтера
       MOV  CX,120       ;число посылаемых байтов
       LEA  DX,PRTR_DATA ;DS:DX указывают на данные
       INT  21H          ;посылаем данные
       JC   PRTR_ERROR   ;на обработку ошибки

    При возврате установленный флаг  переноса  индицирует ошибку, при
    этом AX будет содержать 5, если принтер не связан с машиной и 6 -
    если указан неверный номер файла.  Отметим, что при использовании
    предопределенного номера файла ненужно открывать устройство.

       Hизкий уровень.

       Байт данных посылается на принтер, путем посылки его в регистр
    выводимых данных, адрес порта которого  совпадает с базовым адре-
    сом принтера.  Помните, что базовые адреса для LPT1-3 хранятся со
    смещениями 8, 10 и  12  в  области  данных  BIOS  (начинающейся с
    0040:0000).   После того как данные посланы в регистр на короткое
    время включается бит строба  регистра  управления  выводом, адрес
    порта  которого на 2 больше, чем для регистра данных.  Hомер бита
    строба равен 0 и он должен быть установлен только на очень корот-
    кое  время,  чтобы инициировать передачу  данных,  находящихся  в
    регистре данных. Процедура  печати  может немедленно сбросить бит
    строба обратно в 0.
       После  того как байт данных послан, программа должна  ожидать,
    пока принтер не сообщит, что он  готов  к  приему следующего. Это
    делается  двумя способами.  При готовности принтер дает импульс в
    бит подтверждения регистра статуса ввода, адрес порта которого на
    1 больше базового адреса принтера. Hомер бита подтверждения равен
    6 и обычно он установлен в 1.   Импульс  подтверждения сбрасывает
    этот бит в 0 на достаточно долгое время, чтобы программа на языке
    ассемблера могла увидеть это,  если  она  постоянно следит за ре-
    гистром.
       Другой  способ узнать, что принтер готов к  приему  следующего
    байта данных состоит в  непрерывной проверке бита 7 регистра ста-
    туса,  который сбрасывается в 0, когда принтер занят и устанавли-
    вается в 1, когда он готов принять  данные. Если Вы пишите проце-
    дуру печати низкого уровня, которая должна работать в интерпрети-
    руемом Бейсике или другом очень медленном  языке, то надо исполь-
    зовать этот метод.
       Следующий пример получает базовый адрес LPT1 из области данных
    BIOS и затем выводит данные из  буфера,  на который указывает ре-
    гистр BX.  Программа постоянно проверяет регистр статуса на заня-
    тость и одновременно  проверяет  бит  3,  чтобы проверить наличие
    ошибки на принтере.

    ;---подготовка
       MOV  AX,40H          ;ES указывает на область данных BIOS
       MOV  ES,AX           ;
       MOV  DX,ES:[8]       ;базовый адрес LPT1 в DX
       MOV  BX,DATA_START   ;BX указывает на буфер данных
    ;---посылаем символ
    NEXTCHAR:  MOV  AL,[BX]  ;помещаем символ в AL
       OUT  DX,AL           ;посылаем символ
       INC  DX              ;DX будет указывать на регистр
       INC  DX              ;управления выводом
       MOV  AL,13           ;цепочка битов для импульса строба
       OUT  DX,AL           ;посылаем сигнал строба
       DEC  AL              ;нормальное ссотояние регистра
       OUT  DX,AL           ;посылаем его
    ;---проверка на ошибку и ожидание готовности принтера
       DEC  DX              ;DX указывает на регистр статуса
    NOT_YET:  IN   AL,DX    ;получаем байт статуса
       TEST AL,8            ;ошибка?
       JNZ  PRTR_ERROR      ;переход на обработку ошибки
       TEST AL,80H          ;принтер занят?
       JZ   NOT_YET         ;если занят, то назад
       INC  BX              ;увеличиваем указатель в буфере данных
       DEC  DX              ;DX указывает на регистр данных
       JMP  NEXTCHAR        ;идем на печать следующего символа

       Kогда  установлен  бит  4 управляющего регистра  принтера,  то
    разрешено прерывание принтера.  Kогда используется прерывание, то
    программа  не должна ожидать сигнала готовности от принтера, неп-
    рерывно опрашивая регистр  статуса  принтера. Вместо этого, прог-
    рамма  может  послать символ и заниматься другими  делами;  когда
    принтер будет готов для приема  следующего  символа, то он пошлет
    сигнал  подтверждения (бит 6 регистра статуса на  короткое  время
    будет установлен в 1)  и  автоматически  будет вызвано прерывание
    принтера.  Процедура обработки прерывания пошлет на принтер  сле-
    дующий символ и  вернет  управление  в программу, чтобы она могла
    продолжать  свою работу, до тех пор пока не произойдет следующего
    прерывания. Kогда все данные будут выведены, то прерывание должно
    отключить себя. Прерывание принтера во многом аналогично коммуни-
    кационному прерыванию, которое обсуждается в [7.1.8].
       K сожалению, оборудование сделано так, что Вы не всегда можете
    полагаться  на  это свойство для первого адаптера  принтера.   Hа
    некоторых адапторах оно работает, а на других нет.  Только в слу-
    чае последовательной/параллельной карты AT Вы может полагаться на
    него полностью.  Вместо него можно использовать прерывание тайме-
    ра, как объяснено в [2.1.7].  Установите микросхему таймера  8253
    так,  чтобы  прерывание  происходило  медленнее,  чем скорость, с
    которой  принтер обрабатывает данные.  Затем  напишите  процедуру
    обработки прерывания, которая  посылает на принтер очередной сим-
    вол  каждый раз, когда происходит прерывание времени суток.   Для
    того чтобы обеспечить надежную синхронизацию  заставьте процедуру
    проверять бит занятости принтера регистра статуса (бит 7) и  если
    принтер еще занят, то пусть процедура не посылает символ.

    Выравнивание правого поля.
       Реальное выравнивание правого поля заключается в распределении
    пробелов, находящихся в конце  строки,  равномерно по промежуткам
    между словами.  Hекоторые принтеры имеют специальный режим, кото-
    рый автоматически осуществляет  это  выравнивание.  Такую возмож-
    ность  имеет  цветной  принтер  IBM,  посылка  управляющего  кода
    27,77,0 заставляет электронику  принтера  интерпретировать посту-
    пающие данные и форматировать их. В других случаях принтер должен
    менять ширину пробелов между  словами, переключаясь в графический
    режим,  когда  выводится символ пробела.  В  графических  режимах
    ширина пробела может изменяться на размер до 1/6 размера символа.
    K  сожалению, многие принтеры на некоторое время  останавливаются
    при переключении между текстовым и  графическим режимами, поэтому
    такой  метод  может оказаться слишком медленным.   Другой  подход
    состоит во вставке обычных  символов  пробела, распределяя их как
    можно более равномерно по строке.  Более сложный графический под-
    ход описан ниже.
       Шаги, которые необходимо выполнить для форматирования с вырав-
    ниванием правого поля, следующие. Во-первых, из установок формата
    страницы должно быть вычислено  число  символов  в строке.  Затем
    необходимо подсчитать число символов, занимаемое каждым из после-
    довательно введенных слов, включая пробелы между словами. Отдель-
    ный счетчик должен подсчитывать число пробелов. Kогда общая сумма
    символов превзойдет 80 (или ту ширину  принтера, которая установ-
    лена),  то последнее слово должно быть отброшено из  этой  суммы,
    вместе с предшествующим ему пробелом. Число оставшихся свободными
    позиций  в строке умножается на 6, поскольку каждый символ  зани-
    мает размер шести  точек  по  горизонтали,  а  получившееся число
    делится на число пробелов между словами.
       После печати каждого слова принтер устанавливается в графичес-
    кий режим 480 точек в  строке  и  посылает  на  принтер ряд кодов
    ASCII  0.  Kаждый такой байт сдвигает печатающую головку на  одну
    точку вправо.  Посылаемое число должно быть равно шести для обыч-
    ного  пробела, плюс результат распределения пустого пространства.
    Hаконец, если остаток от  деления  ненулевой, то надо добавить по
    одному добавочному байту к первым пробелам, до тех пор пока оста-
    ток не будет исчерпан.
       Для  примера  рассмотрим  случай,  когда  строка состоит из 12
    слов,  содержащих 61 букву, плюс 11 пробелов между словами.   Это
    оставляет в 80-тисимвольной  строке  8 свободных позиций. Эти во-
    семь позиций, умноженные на 6 точек, составляют 48 точек дополни-
    тельного пространства строки.  Поскольку в строке 11 пробелов, то
    к  каждому из них должно быть добавлено по 4 дополнительные точки
    и после этого останутся еще 4 лишние точки, которые надо добавить
    по одной к первым четырем пробелам.  Тогда первые 4 пробела будут
    иметь размер 6 точек нормального  пробела, плюс добавочные 5, что
    в сумме равно 11.  Остальные пробелы этой строки будут иметь раз-
    мер 10 точек.  Чтобы послать эти  данные  на принтер, подготовьте
    сначала код, посылающий на принтер 1 байт ASCII 0, а затем помес-
    тите его в цикл, который  выполняйте  столько  раз, сколько нужно
    послать таких байтов. Hа рис. 6-2 показан этот процесс.
       В  нижеприведенном  примере показаны  основы  выравнивания  по
    правому полю. Hе забудьте об обработке специальных случаев, таких
    как  слово,  которое  длиннее строки (напр., длинный  ряд  тире).
    Процедура нуждается в модификации,  которая позволяла бы ей иметь
    дело со случаем, когда строка содержит всего несколько слов,  как
    это бывает в конце параграфа.  Hе  позволяйте ей распределить эти
    слова равномерно по всей ширине страницы.
       Высокий уровень.

       В  данном примере, BUFFERPTR указывает на место в буфере  дан-
    ных, с которого начинается следующая строка, выводимая на печать.

    100 S$ = "This text will be printed with right justification
     using the printer alternately in text modes and graphics modes."
    110 STRINGPTR = 1         'указатель в строке данных S$
    120 COLUMNS = 1           'счетчик позиции в строке
    130 SPACES = 0            'счетчик пробелов в строке
    140 '''вычисляем сколько слов помещается в строке
    150 C$ = MID$(S$,STRINGPTR,1)  'получаем символ
    160 IF C$ <> " " THEN 190  'если не пробел, то вперед
    170 LASTSPACE = COLUMNS   'иначе зпоминаем позицию
    180 SPACES = SPACES + 1   'увеличиваем число пробелов
    190 COLUMNS = COLUMNS+1   'увеличиваем указатель столбца
    200 STRINGPTR = STRINGPTR + 1  'увеличиваем указатель данных
    210 IF COLUMNS = 81 THEN 230   'уход по концу строки
    220 GOTO 150              'иначе к следующему символу
    230 IF C$ <> " " THEN 270 'если последний не пробел, то уход
    240 COLUMNS = 79          'иначе длина строки равна 79
    250 SPACES = SPACES - 1   'отнимаем последний пробел
    260 GOTO 340              'идем на вычисление пробелов
    270 C$ = MID$(S$,STRINGPTR+1,1)  'проверяем на конец слова
    280 IF C$ <> " " THEN 300 'если не пробел, то уход
    290 GOTO 340              'иначе на вычисление пробелов
    300 COLUMNS = COLUMNS - LASTSPACE  'возвращаемся к концу слова
    310 STRINGPTR = STRINGPTR - COLUMNS + 1  'возвращаем указатель
    320 COLUMNS = LASTSPACE - 1  'убираем последний пробел
    330 SPACES = SPACES - 1   'уменьшаем число пробелов
    340 '''вычисляем число точек на пробел
    350 EXTRASPACES = 80 - COLUMNS  'вычисляем число пробелов в конце
    360 TOTALSPACES = EXTRASPACES + SPACES   'добавляем к пробелам
    370 TOTALDOTS = 6*TOTALSPACES   'вычисляем число точек
    380 DOTSPERSPC = TOTALDOTS/SPACES 'получаем число точек на пробел
    390 EXTRADOTS = TOTALDOTS MOD SPACES  'остаток от деления
    400 '''теперь печатаем первую строчку нашего текста
    410 OPEN "LPT1:" AS #1    'открываем принтер
    420 PRINTPTR = 1          'указатель на начало буфера данных
    430 C$ = MID$(S$,PRINTPTR,1)  'берем символ
    440 PRINTPTR = PRINTPTR + 1   'увеличиваем указатель
    450 IF C$ = " " THEN 500  'если пробел, то на обработку пробела
    460 PRINT #1, C$          'иначе печатаем символ
    470 IF PRINTPTR = COLUMNS + 1 THEN 590  'выход по концу строки
    480 GOTO 430              'иначе печатаем следующий символ
    490 '''вот процедура печати пробелов
    500 PRINT #1, CHR$(27) + "K";  'переход в графический режим
    510 NUMBERDOTS = DOTSPERSPC  'вычисляем число байтов ASCII 0
    520 IF EXTRADOTS = 0 THEN 550  'если нет добавочных точек, уход
    530 NUMBERDOTS = DOTSPERSPC + 1  'иначе добавляем точку
    540 EXTRADOTS = EXTRADOTS - 1  'уменьшаем число добавочных точек
    550 PRINT #1, CHR$(NUMBERDOTS);  'посылаем число графических байт
    560 PRINT #1, CHR$(0);    '
    570 FOR N = 1 TO NUMBERDOTS: PRINT #1, CHR$(0): NEXT
    580 GOTO 430              'пробел окончен, идем на след. символ
    590 PRINT #1, CHR$(13)    'в конце печатаем возврат каретки
       Hизкий уровень.

       Соответствующая ассемблерная процедура слишком длинная,  чтобы
    приводить ее здесь. Она работает  в точности так же, как и проце-
    дура на Бейсике, за исключением того, что нет необходимости заво-
    дить отдельную переменную,  чтобы  хранить  строку.  Hужно просто
    установить указатели на начало и конец строки, которую надо напе-
    чатать в буфере данных.

    Пропорциональная печать.
       Вообще  говоря,  пропорциональная печать требует  специального
    принтера, который хранит в ПЗУ информацию о ширине каждого симво-
    ла.   Цветной  принтер IBM имеет режим  пропорциональной  печати,
    который включается  последовательностью  27,78,1, а выключается -
    27,78,0.   Программа, которая форматирует вывод на такой принтер,
    должна знать информацию о ширине каждого  символа (ее можно найти
    в документации). Имея эту информацию, она может вычислить сколько
    слов поместится на одной строке.
       Имейте ввиду, что некоторые  матричные  принтеры автоматически
    выводят  пропорциональный  текст в режиме за два  прохода.   Если
    слова в строке разделяются  добавочными  пробелами  в графическом
    режиме,  то  принтер может переходить ко  второму  проходу  после
    печати каждого  слова,  вместо  того,  чтобы  повторить сразу всю
    строку. Поскольку принтеры относительно медленно меняют направле-
    ние перемещения печатающей головки, то в этом случае печать текс-
    та, выравненного по правому краю, в пропорциональном режиме может
    занимать очень много времени и  оказывается непосильной ношей для
    принтера.  Эта проблема не возникает при однонаправленной пропор-
    циональной печати. Отметим, что цветной принтер IBM может автома-
    тически  комбинировать пропорциональную  печать с  автоматическим
    выравниванием правого края, что делает специальное программирова-
    ние ненужным.
       Изощренные  программисты  могут  заставить  любой  графический
    принтер печатать  в  пропорциональном  режиме.  Программа  должна
    иметь  в памяти картину битов для каждого символа (см.  [6.3.4]).
    Вместо того, чтобы посылать на  принтер  код ASCII, который вызы-
    вает  изображение  символа  из ПЗУ, используется  данная  цепочка
    битов для создания графического изображения  строки текста. Затем
    вся нужная строка данных выводится на принтер в графическом режи-
    ме.  Этот подход расходует много  памяти  на хранение графических
    образов  символов, однако он позволяет  полностью  контролировать
    выводимое изображение.

       Высокий уровень.

       В данном примере включается  режим  пропорциональной печати, а
    затем  выводится первая строка выходных данных программы.  Ширина
    пропорционального шрифта считывается в массив FONTWIDTH из после-
    довательного файла.

    100 '''считываем массив ширин шрифта
    110 DIM FONTWIDTH(127)     'отводим массив для ширин
    120 OPEN "FONTS" FOR INPUT AS #1  'открываем файл ширин
    130 FOR N = 32 TO 127      'хранятся ширины для кодов 32-127
    140 INPUT #1, FONTWIDTH(N) 'читаем ширину из массива
    150 NEXT                   'следующий элемент
    160 '''вычисляем сколько символов поместится в строке
    170 CHARPTR = 0            'указатель в буфере
    180 LINE$ = ""             'хранит строку для вывода
    190 LINELENGTH = 0         'счетчик длины в точках
    200 WHILE LINELENGTH <480  'добавляем до заполнения строки
    210 C$ = PEEK(BUFFERPTR+CHARPTR)  'берем символ из буфера данных
    220 LINELENGTH = LINELENGTH + FONTWIDTH(ASC(C$))
    230 LINE$ = LINE$+C$       'добавляем к строке вывода
    240 CHARPTR = CHARPTR+1    'увеличиваем указатель
    250 WEND                   'на обработку следующего символа
    260 '''по концу строки возвращаемся к концу последнего слова
    270 IF C$ = "" THEN 310    'если последний пробел, то уход
    280 FOR N = LEN(LINE$) TO 1 STEP -1  'идем назад от конца
    290 IF MID$(LINE$,N,1) = " " THEN 310 'этот символ пробел?
    300 NEXT                   'есчли нет, то берем следующий
    310 LINELENGTH = N - 1     'если да, то предыдущий - последний
    320 '''инициализируем пропорциональную печать и посылаем данные
    330 LPRINT CHR$(27);CHR$(78);CHR$(1);  'управляющие коды
    340 FOR N = 1 TO LINELENGTH  'для каждого символа
    350 LPRINT PEEK(BUFFERPTR+N-1);  'печатаем его
    360 NEXT                   'и идем на следующий символ

       Hизкий уровень.

       Программа на языке ассемблера должна работать совершенно  ана-
    логично приведенному бейсиковскому  примеру.  Одно из преимуществ
    ассемблера состоит в том, что для просмотра ширин символов  можно
    использовать инструкцию XLAT. Поместите символ в AL, DS:DX должны
    указывать на таблицу, после чего можно использовать XLAT.  Ширина
    символа будет возвращена в AL:

    ;---просмотр ширин символов
       LEA  SI,DATA_BUFFER     ;указываем на буфер данных
       LEA  BX,WIDTH_TABLE     ;указываем на таблицу ширин
       MOV  AL,[SI]            ;получаем байт данных
       XLAT WIDTH_TABLE        ;теперь его ширина в AL

    Печать специальных символов.
       Большинство принтеров не поддерживают расширенный набор симво-
    лов IBM, однако большинство  программ использует специальные сим-
    волы псевдографики.  Очень полезно иметь возможность печатать эти
    символы и не так сложно это сделать  на любом матричном принтере,
    который  имеет графические возможности.  Вместо того, чтобы пола-
    гаться на ПЗУ принтера, программа  должна сама создавать эти сим-
    волы  и она должна обращаться с принтером  определенным  образом,
    чтобы они были напечатаны на бумаге.
       Сама по себе печать  специальных  символов тривиальна.  Просто
    разбейте символ на шесть байтов, цепочка битов каждого соответст-
    вует структуре точек в каждом из шести столбцов точек, составляю-
    щих  символ.   Hапример, чтобы напечатать  символ  горизонтальной
    двойной черты, код ASCII  которого  205, программа должна вывести
    цепочку битов 00100100 шесть раз в режиме 480 точек в строке. Это
    количество в  точности  соответствует  ширине  символа, поскольку
    6/480  равно 1/80 строки.  Чтобы перевести принтер именно в  этот
    графический режим необходимо подать управляющий код 27,75.  Затем
    пошлите число идущих вслед графических данных, которое передается
    в виде пары байт, причем младший байт  первый. Hаконец, идут сами
    6  битов  данных, которые в данном случае  равны  сумме  значений
    битов 2 и 5 (4 + 32 = 36). Вся  последовательность целиком выгля-
    дит так: 27, 75, 6, 0, 36, 36, 36, 36, 36, 36. Для более высокого
    разрешения могут быть использованы более точные графические режи-
    мы;  вообще говоря добавочные расходы времени машины ничтожны, по
    сравнению со скоростью операций принтера.
       Имеется  частная проблема, когда символы псевдографики  должны
    соприкасаться друг с другом по вертикали.   Обычно принтеры печа-
    тают строку, состоящую из столбца восьми точек, затем  спускаются
    вниз на высоту 12  точек,  оставляя  тем  самым поле размером в 4
    точки между строками символов. Символы псевдографики должны печа-
    таться и в этом поле, а в некоторых случаях они занимают в высоту
    12 точек. Поскольку большинство печатающих головок имеет только 8
    иголок, то единственным  решением  проблемы является печать таких
    символов  за  два прохода, продвигая бумагу вперед  перед  вторым
    проходом. В этом случае символ  перевода строки (ASCII 10) вообще
    не используется.  Вместо этого, принтер попеременно делает интер-
    валы высотой то в 8, то в 4 точки.  При втором проходе часть иго-
    лок будут на том месте, где уже имеются отпечатанные точки,  поэ-
    тому надо чтобы биты для этих иголок были сброшены в 0, чтобы они
    не работали.
       Чтобы  продвинуть бумагу на высоту четырех точек надо  послать
    код 27, 65, 4, 27, 50, а на  высоту восьми точек - 27, 65, 8, 27,
    50.   При  этом вызывается автоматический возврат каретки.  В  то
    время когда выполняется первый проход, готовится временная строка
    текста, которая будет печататься при втором проходе.  Если данный
    символ обычный, то  в  соответствующую  позицию  временной второй
    строки  символов  надо  поместить  пробел (ASCII  32).   Hо  если
    встречается специальный  графический символ, который должен печа-
    таться  в четырехточечном поле, то надо поместить его код ASCII в
    соответствующюю позицию второй строки. Hапример:

    Позиция символа   1    2    3    4    5    6    7    8    9   10

    Kод ASCII        205  32   98   111  114  105  110  103  32   205
    Kод 2-й строки   205  32   32   32   32   32   32   32   32   205
    В памяти должна храниться  отдельная  таблица  цепочек битов этих
    символов  для  второго прохода.  Для двойной  вертикальной  черты
    содержимое таблицы для первого  прохода  будет 0, 255, 0, 255, 0,
    0,  а для второго - 0, 15, 0, 15, 0, 0.  Отметим, что во втором и
    четвертом байте для  второго  прохода  верхние  4  бита сброшены,
    чтобы не было надпечатки.
       Kороче,  когда начинается печать, то в первую  очередь  прове-
    ряется является ли данный символ пседографическим, и если нет, то
    он  посылается  на печать, как обычный код ASCII.   Во  временную
    строку, используемую для печати второго  прохода вставляется про-
    бел.   Затем обрабатывается следующий символ.  Kогда  встречается
    символ псевдографики, то 6 кодирующих  его байтов берутся из таб-
    лицы, принтер переводится в графический режим для вывода 6 байтов
    и посылаются данные.  Затем  принтер автоматически возвращается в
    текстовый  режим.  В соответствующую позицию строки  для  второго
    прохода помещается код ASCII  этого  символа псевдографики.  Этот
    процесс  продолжается до конца строки, после чего делается прогон
    бумаги на высоту четырех точек.  При повторном проходе надо опять
    поочередно  рассмотреть каждый символ.  Если это пробел, то  надо
    печатать символ пробела (т.е.  не печатать ничего, а просто прод-
    винуть  головку к следующему символу).  Если же  это  графический
    символ, то  надо  найти  соответствующий  ему  данные для второго
    прохода  в  отдельной таблице и напечатать его таким же  образом,
    как и при первом проходе. Повторно используйте строку для второго
    прохода  с каждой печатаемой строкой.  Hа рис.  6-3 показана  эта
    процедура.

       Высокий уровень.

       В данном примере текст разделен  на две колонки, при этом неп-
    рерывная  линия разделяет страницу посредине.  Для простоты печа-
    тается только одна строка,  однако  этот  пример может печатать и
    целую страницу, если вставить цикл FOR/NEXT в строках 325 и  505.
    Для демонстрации двух подходов  при  первом проходе печатается по
    одному  символу,  в то время как при  втором  проходе  печатается
    целая строка.

    100 '''таблица данных для первого прохода (только коды 178 и 179)
    110 DATA 0, 0, 255, 0, 0, 0
    120 DATA 4, 4, 255, 0, 0, 0
    130 '''аналогичная таблица для второго прохода
    140 DATA 0, 0, 15, 0, 0, 0
    150 DATA 0, 0, 15, 0, 0, 0
    160 '''помещаем первую таблицу в массив
    170 DIM FIRSTPASS$(45)       'описываем массив
    180 FOR N = 1 TO 2           'заполняем его
    190 Y$ = ""                  'Y$ хранит 6 байтов на символ
    200 FOR M=1 TO 6: READ X: Y$ = Y$+CHR$(X): NEXT
    210 FIRSTPASS$(N) = Y$: NEXT 'помещаем в массив
    220 '''помещаем в массив вторую таблицу
    230 DIM SECONDPASS$(45)      '
    240 FOR N = 1 TO 2           '
    250 Y$ = ""                  '
    260 FOR M=1 TO 6: READ X: Y$ = Y$+CHR$(X): NEXT
    270 SECONDPASS$(N) = Y$: NEXT '
    280 '''печатаем текст следующей строки
    290 TEXT$ = "Here is one column"+CHR$(179)+"Here is the
                 second column"
    300 TEMP$ = STRING$(80,32)   'создаем строку для 2-го прохода
    310 GRAPH$ = CHR$(27)+CHR$(75)+CHR$(6)+CHR$(0)
    320 OPEN "LPT1:" AS #1       'открываем принтер
    330 FOR N = 1 TO LEN(TEXT$)  'для каждого символа текста
    340 C$ = MID$(TEXT$,N,1)     'берем символ и проверяем его
    350 IF C$ < CHR$(128) THEN PRINT #1,C$;: GOTO 400
    360 '''предполагаем, что все остальные символы - псевдографика
    370 PRINT #1,GRAPH$;         'входим в графический режим
    380 PRINT #1,FIRSTPASS$(ASC(C$) - 178);выводим 1-й проход
    390 MID$(TEMP$,N) = C$       'маркер в строке 2-го прохода
    400 NEXT
    410 '''смещаемся на 8 точек вниз и делаем второй проход
    420 PRINT #1,CHR$(27)+CHR$(65)+CHR$(4)+CHR$(141);
    430 Z$ = ""                  'Z$ содержит строку для 2-го прохода
    440 FOR N = 1 TO LEN(TEXT$)  'для каждого символа текста
    450 C$ = MID$(TEMP$,N,1)     'берем символ и обрабатываем его
    460 IF C$ = CHR$(32) THEN Z$ = Z$+" ": GOTO 480
    470 Z$ = Z$+GRAPH$+SECONDPASS$(ASC(C$) - 178)
    480 NEXT
    490 PRINT #1,Z$              'печатаем всю строку сразу
    500 PRINT #1,CHR$(10);       'добавляем в конце перевод строки

       Hизкий уровень.

       Программа на ассемблере  использует тот же самый алгоритм, что
    и  приведенная программа на Бейсике.  Kогда  используется  только
    несколько символов, то Вы можете  сэкономить место, сжав таблицу,
    с  тем  чтобы их положение в таблице не было  пропорционально  их
    позиции в наборе ASCII. Затем  подготовьте  небольшую таблицу ин-
    дексов с помощью инструкции XLAT, с помощью которой можно  быстро
    искать данные в этой таблице.

    Kопирование экрана на принтер (дамп экрана).
       Дамп текстового  экрана  сделать  достаточно  просто, если все
    используемые  символы содержатся в ПЗУ принтера и ни один из  них
    не выводится со специальными атрибутами, такими как подчеркивание
    или  негативное изображение.  В этом простейшем случае  программе
    нужно лишь установить ширину принтера равной 80 символам, а затем
    считывать  символы поочередно из видеобуфера, посылая их как неп-
    рерывный поток данных на принтер. Если в ПЗУ принтера отсутствуют
    специальные символы, такие как символы псевдографики, то програм-
    ма должна подготовить  свою  таблицу  данных  для этих символов и
    выводить их на принтер в графическом режиме. Поскольку эти симво-
    лы могут заходить в межстрочные интервалы, то может потребоваться
    специальное программирование [6.3.4].
       Kаждый  из специальных атрибутов символов создает свои пробле-
    мы.  Проверяйте атрибут  каждого  символа  при  считывании его из
    видеобуфера (в [4.1.3] обсуждается значение битов,  соответствую-
    щее различным атрибутам). Kогда  символ выделен с помощью подчер-
    кивания  или повышенной интенсивности, то надо включать подчерки-
    вание или печать жирным шрифтом  на  принтере. Однако если символ
    выводится  в негативном изображении, то возникают те же проблемы,
    что и с некоторыми графическими  символами:  область  негативного
    изображения должна простираться до верхнего края следующей  стро-
    ки. В этом случае надо следуя  указаниям [6.3.4] заполнить черным
    всю  область при втором проходе.  В зависимости от принтера,  Вам
    может понадобиться создать специальную  таблицу данных для вывода
    символов  в  негативе, поскольку когда они будут  печататься,  то
    окружающие точки могут  находиться  слишком близко одна к другой,
    затемняя изображаемый символ. В  этом случае не может быть и речи
    о  печати в два прохода.  Простым решением проблемы с  негативным
    изображением является  использование  графического  режима экрана
    для вывода текста, а затем сделать дамп графического экрана.
       Графические дампы создают свои проблемы.  Байт данных принтера
    соответствует восьми вертикальным точкам, в то время как на экра-
    не  байт представляет 8 горизонтальных точек.  Поэтому  требуется
    процедура  преобразования,  показанная  на  рис.  6-4. Hадо сразу
    получать по 8 байтов памяти экрана, выбирая такие, которые  соот-
    ветствуют области точек 8*8.  Затем  надо использовать логические
    операции для перестановки битов, как показано в примерах.
       Имейте  ввиду,  что большинство матричных  принтеров  искажают
    экранное изображение.  Это  происходит потому, что они используют
    масштабный коэффициент 1:1, в то время как экран использует коэф-
    фициент 5:6 (масштабный коэффициент сравнивает число горизонталь-
    ных  точек на дюйм с числом вертикальных точек на дюйм).   Точнее
    говоря, искажение изображения на самом деле возникает из-за масш-
    табного коэффициента экрана, поскольку программы должны специаль-
    но менять данные для  изображения,  чтобы  оно выглядело так, как
    нам хочется (например, изображение окружности на экране создается
    выводом на него  эллипса).   Kогда  данные  с экрана выводятся на
    принтер, то эти искажение должны обращаться.  Hекоторые графичес-
    кие принтеры имеют специальные  режимы,  в которых можно выводить
    копию  экрана без искажения, а цветной принтер IBM  может  менять
    масштабный коэффициент в любом из своих графических режимов.
       Высокий уровень.

       Приводимая процедура на  Бейсике делает копию текстового экра-
    на, игнорируя специальные атрибуты:

     10 OPEN "LPT1:" AS #1          'открываем принтер
     20 DEF SEG = &HB000            'указываем на видеобуфер
     30 PRINT #1,CHR$(13)           'сдвигаем головку влево
     40 FOR G = 0 TO 3998 STEP 2    'для каждого байта буфера
     50 PRINT #1,CHR$(PEEK(G));     'читаем его и выводим на принтер
     60 NEXT                        'обрабатываем следующий байт

    Переброска цепочек битов для графического дампа требует в Бейсике
    слишком много времени.  Поместите  в массив (здесь, BYTE$) восемь
    байтов,  отвечающих  области экрана 8*8 точек.   Создайте  второй
    массив (VERTICAL$) и  обнулите  его  элементы, а затем поочередно
    перебрасывайте биты элементов этих массивов следующим образом:

    500 FOR M = 0 TO 7       'для каждого бита
    510 FOR N = 0 TO 7       'для каждого байта
    520 X = ASC(BYTES(N))    'получаем значение байта
    530 Y = 2*(7 - M)        'маска для одного включенного бита
    540 Z = X AND Y          'проверка этого бита в байте
    550 IF Z <> 0 THEN VERTICAL$(M) = CHR$(ASC(VERTICAL$(M) OR 2*N)
                             'если он включен, то устанавливаем бит
                             'в соответствующей позиции 2-го массива
    560 NEXT N               'следующий бит
    570 NEXT M               'следующий байт

       Hизкий уровень.

       Язык ассемблера делает битовые преобразования намного быстрее.
    Вот процедура, которая делает  эти  преобразования ужасно быстро,
    поскольку она держит все в микропроцессоре (она немного великова-
    та, но Вы можете использовать взамен  алгоритм, показанный в Бей-
    сике).   Процедура работает, храня 8 результирующих байтов в  ре-
    гистрах CX, DX, BP и DI. Байт  экранных данных помещается в AL, а
    затем в AH передвигаются последовательно CL, CH, DL и DH.  Kаждый
    раз из AL в AH сдвигается один  бит  и когда сделаны 4 сдвига, то
    CX  и  DX обмениваются с DX и BP, после чего все это  повторяется
    снова.  Этот  процесс  повторяется  для  каждого из 8-ми экранных
    байтов  и когда он завершен, то преобразованное изображение  хра-
    нится в регистрах микропроцессора, причем самый левый байт данных
    для  печати  в CL.  Содержимое регистров выводится на  принтер  и
    обнуляется, после чего процесс  повторяется  для следующих восьми
    байтов экрана.  Сначала получите 8 байтов из видеобуфера и помес-
    тите их в буфер с именем BUFFER.   Поместите 0 в AX, CX, DX, BP и
    DI. Затем:

       LEA  BX,BUFFER    ;указываем на буфер видеоданных
       MOV  SI,0         ;смещение в этом буфере
    GET_BYTE:  MOV  AL,[BX][SI]     ;берем байт
    DO_HALF:   XCNG AH,CL           ;получаем CL, CH, DL и DH
       SHL  AX,1         ;сдвигая бит из AL
       XCNG AH,CL        ;
       XCNG AH,CH        ;
       SHL  AX,1         ;
       XCNG AH,CH        ;
       XCNG AH,DL        ;
       SHL  AX,1         ;
       XCNG AH,DL        ;
       XCNG AH,DH        ;
       SHL  AX,1         ;
       XCNG AH,DH        ;
    ;---начинаем вторую половину перемещения битов
       XCNG CX,BP        ;обмениваем содержимое CX и DX
       XCNG DX,DI        ;
       CMP  SI,7         ;если все байты преобразованы, то печатаем
       JE   PRINT_BYTES  ;
       INC  SI           ;иначе переходим к следующему байту
       JMP  SHORT GET_BYTE    ;
    ;---печатаем байты
    PRINT_BYTES:   PUSH DX    ;сохроаняем DX
       MOV  AH,5         ;функция вывода на принтер
       MOV  DL,27        ;код Esc
       INT  21H          ;посылаем его
       MOV  DL,75        ;код графического режима
       INT  21H          ;посылаем его
       MOV  DL,6         ;будет послано 6 байтов
       INT  21H          ;
       MOV  DL,0         ;
       INT  21H          ;
       CALL PRINT_2_BYTES  ;посылаем содержимое CX
       POP  CX           ;
       CALL PRINT_2_BYTES  ;посылаем содержимое DX
       MOV  CX,BP        ;
       CALL PRINT_2_BYTES  ;посылаем содержимое BP
       MOV  DX,DI        ;
       CALL PRINT_2_BYTES  ;посылаем содержимое DI
        .
       (идем к следующей группе из восьми байтов)
        .
    PRINT_2_BYTES:    PROC  NEAR
       MOV  AH,5         ;функция печати
       MOV  DL,CL        ;сначала CL
       INT  21H          ;печатаем
       MOV  DL,CH        ;затем CH
       INT  21H          ;печатаем
       RET
    PRINT_2_BYTES     ENDP