режим включается управ- ляющим кодом 27,71, а выключается кодом 27,72. Печать с подчеркиванием: Печать с подчеркиванием может выполняться двумя способами. Графический принтер имеет режим подчеркивания, в котором подчерк печатается под каждым символом, включая пробелы. Для графического принтера IBM этот режим включается кодом 27,45,1, а выключается кодом 27,45,0. Принтеры, не имеющие режима подчеркивания могут сделать подчерки при втором проходе по той же строке, печатая символы подчеркивания (ASCII 95) в тех местах, где оно нужно и пробелы (ASCII 32) во всех остальных позцициях. Второй проход достигается тем, что после первого прохода подается код возврата каретки без кода перевода строки. Второй проход не мешает принте- ру правильно подсчитывать строки при вычислении размера страницы. Печать с верхними и нижними индексами: На графических принтерах текст с верхними и нижними индексами сжимается вертикально. Для печати верхнего индекса пошлите управ- ляющий код 27,83,0, а для печати нижнего - 27,83,1. Можно прямо переходить от одних индексов к другим. Для выключения печати индексов, с тем, чтобы принтер оказался на текущей строке пошлите управляющий код 27,84. Некоторые режимы не могут использоваться в комбинации с други- ми. Если Вы хотите использовать 4 режима одновременно, то прокон- сультируйтесь со следующей таблицей. В каждом из шести столбцов приведена допустимая комбинация. Комбинация 1 2 3 4 5 6 нормальный Х Х сжатый Х Х выделенный Х Х за два прохода Х Х Х с индексами Х Х Х двойной ширины Х Х Х Х Х Х с подчеркиванием Х Х Х Х Х Х 6.2.7 Сравнение возможностей принтеров IBM. В следующей таблице сравниваются управляющие коды для принте- ров IBM. Не вся информация относительно кодов точна (обращайтесь к документации IBM), а в ряде случаев уникальные коды опущены. Целью настоящей таблицы является показ диапазона возможностей принтеров и указание тех кодов, которые можно считать стандартны- мия. Отметим, что коды для первых четырех принтеров приведены в выпуске "Возможности и адаптеры" (Options and Adapters) из серии технических руководств, а коды для остальных принтеров приведены в сопровождающих их руководств по эксплуатации. Код Функция Матричный Графический Цветной Компактный Струйный Ромашка Пропринтер принтер принтер принтер принтер принтер Перемещение бумаги: 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. Посылка данных на принтер. Посылка данных на принтер тривиальна в языках высокого уровня, а для программиста на языке ассемблера имеется ряд функций опера- ционной системы, которые делают задачу также достаточно простой. Программирование на низком уровне требует больше работы, но зато предоставляет больше возможностей. Как правило, процедуры печати низкого уровня посылают символ на принтер, а затем постоянно проверяет регистр статуса ввода порта, к которому присоединен принтер. Следующий символ посылается только тогда, когда принтер сигнализирует, что он готов (принтер может не печатать символ сразу, а запасать его в своем буфере, до тех пор пока не будет получена целая строка символов для печати). Кроме того, процедуры низкого уровня могут использовать преры- вание принтера или могут имитировать действие этого прерывания. С помощью специального программирования можно сделать так, что принтер будет делать прерывание проценссора, когда он готов к приему следующего символа. Процедура обработки прерывания посы- лает следующий символ, после чего процессор может продолжать заниматься своими делами. Этот метод используется для фоновой печати (которую называют также спулингом). Поскольку физические перемещения деталей принтера намного медленнее, чем скорость электроники компьютера, то вывод символов на принтер занимает лишь малую долю процессорного времени. Использование прерывания позволяет использовать это время эффективно. При посылке данных на принтер требуется сравнительно небольшие усилия, чтобы добиться ужасно сложного вывода. Все сложные кар- тинки, которые может выводить принтер, достигаются за счет комби- нирования текстовых и графических данных, а также многочисленных кодов управления принтером, обсуждавшихся ранее в этой главе. Комбинируя в одной строке текстовый и графический режимы, можно добиться выравнивания правого поля и пропорциональной печати. Кроме того любой графический принтер может создавать специальные символы произвольного вида, а за счет аккуратного манипулирования надпечатки и межстрочного интервала могут выводиться любые симво- лы псевдографики. 6.3.1 Вывод текстовых или графических данных на принтер. Процессор может заниматься только посылкой данных на принтер или он может печатать в фоновом режиме, за счет использования прерывания принтера. Возможна и третья альтернатива, когда прог- рамма посылает символы на принтер через определенные интервалы, что можно рассматривать как "псевдопрерывание". Этот метод не так тесно координируется с работой принтера, как настоящее прерыва- ние, но во всяком случае работа принтера не критична ко времени. Независимо от того как выводятся данные, каждый раз на принтер посылается только 1 байт данных. Языки высокого уровня предостав- ляют функции, которые вроде бы выводят сразу целые строки, однако на самом деле эти функции разбивают строки на отдельные символы. Обычно языки высокого уровня посылают на принтер пару возврат каретки/перевод строки в конце каждой строки. С другой стороны, программы на ассемблере должны сами добавлять эту пару кодов. Из-за этого приходится немного больше программировать, но взамен Вы получаете намного большую гибкость, особенно в отношении про- верки ошибок. Высокий уровень. Для посылки данных на принтер Бейсик предоставляет операторы LPRINT и PRINT#. LPRINT не требует никакой подготовки, но для вывода оператором PRINT# Вы должны предварительно открыть принтер в точности так же как и файл, с помощью оператора OPEN "LPT1" AS #1 или OPEN "LPT3" AS #2. Оператор LPRINT всегда адресуется к LPT1, в то время как PRINT# может адресоваться к любому принтеру. Пара возврат каретки/перевод строки автоматически добавляется в конце любого оператора LPRINT или PRINT#, если только он не завершается точкой с запятой. Для избежания ненужных переводов строки не забывайте завершать посылки любых управляющих кодов точкой с запятой. То же самое надо делать, если Вы хотите, чтобы строки текста печатались подряд, прилегающие одна к другой. Одна- ко имейте ввиду, что многие принтеры не начинают печатать до тех пор, пока они не получат данные для целой строки. Это определяет- ся либо символом возврат каретки, либо тем, что число переданных символов достигло 80-ти (или другого числа). Не забывайте послать завершающий код возврата каретки, чтобы вытолкнуть последнюю порцию символов из буфера принтера. Принтер автоматически переходит на слдеующую строку по дости- жению конца строки. По умолчанию размер строки принтера равен 80 символам, но у широких принтеров это значение может быть больше. Строки, выводимые в режимах плотной печати или печати двойной ширины, также меняют длину строки. Для изменения номера столбца, по достижении которого головка принтера перейдет на следующую строку, можно установить ширину принтера командой WIDTH "LPT1",n - где n требуемый номер столбца. Когда печатается строка, длина которой больше или равна ширине принтера, то печатающая головка переходит на следующую строку, что эквивалентно выполнению кодов возврат каретки/перевод строки. Это означает, что в случае, когда длина строки в точности равна ширине принтера, то будет сделано два перевода строки, если эта строка завершается, как обычно, парой возврат каретки/перевод строки. При графической печати принтер обычно устанавливают на беско- нечную ширину. Чтобы сделать это, надо подать команду установки ширины, равной 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 содер- жать число посылаемых байтов. Например: ;---вывод 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 - если указан неверный номер файла. Отметим, что при использовании предопределенного номера файла ненужно открывать устройство. Низкий уровень. Байт данных посылается на принтер, путем посылки его в регистр выводимых данных, адрес порта которого совпадает с базовым адре- сом принтера. Помните, что базовые адреса для LPT1-3 хранятся со смещениями 8, 10 и 12 в области данных BIOS (начинающейся с 0040:0000). После того как данные посланы в регистр на короткое время включается бит строба регистра управления выводом, адрес порта которого на 2 больше, чем для регистра данных. Номер бита строба равен 0 и он должен быть установлен только на очень корот- кое время, чтобы инициировать передачу данных, находящихся в регистре данных. Процедура печати может немедленно сбросить бит строба обратно в 0. После того как байт данных послан, программа должна ожидать, пока принтер не сообщит, что он готов к приему следующего. Это делается двумя способами. При готовности принтер дает импульс в бит подтверждения регистра статуса ввода, адрес порта которого на 1 больше базового адреса принтера. Номер бита подтверждения равен 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 ;идем на печать следующего символа Когда установлен бит 4 управляющего регистра принтера, то разрешено прерывание принтера. Когда используется прерывание, то программа не должна ожидать сигнала готовности от принтера, неп- рерывно опрашивая регистр статуса принтера. Вместо этого, прог- рамма может послать символ и заниматься другими делами; когда принтер будет готов для приема следующего символа, то он пошлет сигнал подтверждения (бит 6 регистра статуса на короткое время будет установлен в 1) и автоматически будет вызвано прерывание принтера. Процедура обработки прерывания пошлет на принтер сле- дующий символ и вернет управление в программу, чтобы она могла продолжать свою работу, до тех пор пока не произойдет следующего прерывания. Когда все данные будут выведены, то прерывание должно отключить себя. Прерывание принтера во многом аналогично коммуни- кационному прерыванию, которое обсуждается в [7.1.8]. К сожалению, оборудование сделано так, что Вы не всегда можете полагаться на это свойство для первого адаптера принтера. На некоторых адапторах оно работает, а на других нет. Только в слу- чае последовательной/параллельной карты AT Вы может полагаться на него полностью. Вместо него можно использовать прерывание тайме- ра, как объяснено в [2.1.7]. Установите микросхему таймера 8253 так, чтобы прерывание происходило медленнее, чем скорость, с которой принтер обрабатывает данные. Затем напишите процедуру обработки прерывания, которая посылает на принтер очередной сим- вол каждый раз, когда происходит прерывание времени суток. Для того чтобы обеспечить надежную синхронизацию заставьте процедуру проверять бит занятости принтера регистра статуса (бит 7) и если принтер еще занят, то пусть процедура не посылает символ. 6.3.2 Выравнивание правого поля. Реальное выравнивание правого поля заключается в распределении пробелов, находящихся в конце строки, равномерно по промежуткам между словами. Некоторые принтеры имеют специальный режим, кото- рый автоматически осуществляет это выравнивание. Такую возмож- ность имеет цветной принтер IBM, посылка управляющего кода 27,77,0 заставляет электронику принтера интерпретировать посту- пающие данные и форматировать их. В других случаях принтер должен менять ширину пробелов между словами, переключаясь в графический режим, когда выводится символ пробела. В графических режимах ширина пробела может изменяться на размер до 1/6 размера символа. К сожалению, многие принтеры на некоторое время останавливаются при переключении между текстовым и графическим режимами, поэтому такой метод может оказаться слишком медленным. Другой подход состоит во вставке обычных символов пробела, распределяя их как можно более равномерно по строке. Более сложный графический под- ход описан ниже. Шаги, которые необходимо выполнить для форматирования с вырав- ниванием правого поля, следующие. Во-первых, из установок формата страницы должно быть вычислено число символов в строке. Затем необходимо подсчитать число символов, занимаемое каждым из после- довательно введенных слов, включая пробелы между словами. Отдель- ный счетчик должен подсчитывать число пробелов. Когда общая сумма символов превзойдет 80 (или ту ширину принтера, которая установ- лена), то последнее слово должно быть отброшено из этой суммы, вместе с предшествующим ему пробелом. Число оставшихся свободными позиций в строке умножается на 6, поскольку каждый символ зани- мает размер шести точек по горизонтали, а получившееся число делится на число пробелов между словами. После печати каждого слова принтер устанавливается в графичес- кий режим 480 точек в строке и посылает на принтер ряд кодов ASCII 0. Каждый такой байт сдвигает печатающую головку на одну точку вправо. Посылаемое число должно быть равно шести для обыч- ного пробела, плюс результат распределения пустого пространства. Наконец, если остаток от деления ненулевой, то надо добавить по одному добавочному байту к первым пробелам, до тех пор пока оста- ток не будет исчерпан. Для примера рассмотрим случай, когда строка состоит из 12 слов, содержащих 61 букву, плюс 11 пробелов между словами. Это оставляет в 80-тисимвольной строке 8 свободных позиций. Эти во- семь позиций, умноженные на 6 точек, составляют 48 точек дополни- тельного пространства строки. Поскольку в строке 11 пробелов, то к каждому из них должно быть добавлено по 4 дополнительные точки и после этого останутся еще 4 лишние точки, которые надо добавить по одной к первым четырем пробелам. Тогда первые 4 пробела будут иметь размер 6 точек нормального пробела, плюс добавочные 5, что в сумме равно 11. Остальные пробелы этой строки будут иметь раз- мер 10 точек. Чтобы послать эти данные на принтер, подготовьте сначала код, посылающий на принтер 1 байт ASCII 0, а затем помес- тите его в цикл, который выполняйте столько раз, сколько нужно послать таких байтов. На рис. 6-2 показан этот процесс. В нижеприведенном примере показаны основы выравнивания по правому полю. Не забудьте об обработке специальных случаев, таких как слово, которое длиннее строки (напр., длинный ряд тире). Процедура нуждается в модификации, которая позволяла бы ей иметь дело со случаем, когда строка содержит всего несколько слов, как это бывает в конце параграфа. Не позволяйте ей распределить эти слова равномерно по всей ширине страницы. Высокий уровень. В данном примере, 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) 'в конце печатаем возврат каретки Низкий уровень. Соответствующая ассемблерная процедура слишком длинная, чтобы приводить ее здесь. Она работает в точности так же, как и проце- дура на Бейсике, за исключением того, что нет необходимости заво- дить отдельную переменную, чтобы хранить строку. Нужно просто установить указатели на начало и конец строки, которую надо напе- чатать в буфере данных. 6.3.3 Пропорциональная печать. Вообще говоря, пропорциональная печать требует специального принтера, который хранит в ПЗУ информацию о ширине каждого симво- ла. Цветной принтер 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 'и идем на следующий символ Низкий уровень. Программа на языке ассемблера должна работать совершенно ана- логично приведенному бейсиковскому примеру. Одно из преимуществ ассемблера состоит в том, что для просмотра ширин символов можно использовать инструкцию XLAT. Поместите символ в AL, DS:DX должны указывать на таблицу, после чего можно использовать XLAT. Ширина символа будет возвращена в AL: ;---просмотр ширин символов LEA SI,DATA_BUFFER ;указываем на буфер данных LEA BX,WIDTH_TABLE ;указываем на таблицу ширин MOV AL,[SI] ;получаем байт данных XLAT WIDTH_TABLE ;теперь его ширина в AL 6.3.4 Печать специальных символов. Большинство принтеров не поддерживают расширенный набор симво- лов IBM, однако большинство программ использует специальные сим- волы псевдографики. Очень полезно иметь возможность печатать эти символы и не так сложно это сделать на любом матричном принтере, который имеет графические возможности. Вместо того, чтобы пола- гаться на ПЗУ принтера, программа должна сама создавать эти сим- волы и она должна обращаться с принтером определенным образом, чтобы они были напечатаны на бумаге. Сама по себе печать специальных символов тривиальна. Просто разбейте символ на шесть байтов, цепочка битов каждого соответст- вует структуре точек в каждом из шести столбцов точек, составляю- щих символ. Например, чтобы напечатать символ горизонтальной двойной черты, код ASCII которого 205, программа должна вывести цепочку битов 00100100 шесть раз в режиме 480 точек в строке. Это количество в точности соответствует ширине символа, поскольку 6/480 равно 1/80 строки. Чтобы перевести принтер именно в этот графический режим необходимо подать управляющий код 27,75. Затем пошлите число идущих вслед графических данных, которое передается в виде пары байт, причем младший байт первый. Наконец, идут сами 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). Но если встречается специальный графический символ, который должен печа- таться в четырехточечном поле, т