| Каталог | Индекс раздела |
| Назад | Оглавление | Вперед |
8. Принтер
Принтер является, пожалуй, самым "неудобным" периферийным устройством ПЭВМ. Это "неудобство" заключается в том, что IBM-совместимые машины комплектуются принтерами разных производителей, которые подчас отличаются друг от друга по управлению. В качестве стандарта для IBM-совместимых машин принят принтер Epson. Принтеры других фирм ориентированы на этот стандарт в той или иной степени. Во всяком случае, можно с уверенностью утверждать, что вывод символа на принтер всегда выполняется одинаково. Что же касается управления спецификациями печати, то здесь гарантировать точное совпадение нельзя, к сожалению, не гарантируется и одинаковая установка разрядов байта состояния. Мы в нашем описании и в наших программах ориентируемся на стандарт Epson.
8.1. Порты принтера
DOS может работать с тремя параллельными принтерами, именуемыми LPT1, LPT2, LPT3. Каждый принтер имеет по три порта: порт вывода (базовый порт), порт состояния и порт управления. Адреса портов строго не фиксированы. В области данных BIOS по адресам 0040:0008, 0040:000A, 0040:000C содержатся адреса базовых портов для LPT1, LPT2, LPT3 соответственно. Адрес порта состояния - на 1 больше базового, порта управления - еще на 1 больше.
Самая первая операция, которую мы рассмотрим для принтера, - определение его состояния. Разряды байта, считываемого из порта состояния принтера, интерпретируются следующим образом:
| 0 | - 2 - не используются, обычно установлены в 1; |
| 3 | - ошибка принтера - нет/есть (0/1); |
| 4 | - принтер подключен/не подключен (1/0); |
| 5 | - бумага есть/нет (0/1); |
| 6 | - принтер выводит очередной символ/готов (0/1); |
| 7 | - принтер занят/свободен (0/1). |
Программа примера 8.1 предлагает проверить байт состояния при некоторых, наиболее вероятных состояниях принтера. Для сравнения программа выдает байт стандарта Epson.
/*== ПРИМЕР 8.1 ==*/
/*============== Получение статуса принтера ==============*/
#include <dos.h>
main() {
union REGS rr;
int dataport,statusport,ctrlport; /* Номера портов */
unsigned char stat; /* Байт статуса */
int i;
/* Определение адресов портов принтера */
dataport=peek(0x40,8);
statusport=dataport+1;
ctrlport=statusport+1;
printf("Порты LPT1 = %03X, %03X, %03X\n",
dataport,statusport,ctrlport);
/* Проверка состояний */
printf("\nУстановите: принтер выключен. ");
printf("Нажмите любую клавишу\n");
getch();
stat=inportb(statusport);
printf("Состояние принтера - ");
for (i=7; i>=0; i--)
if ((stat>>i)&1) printf("1"); else printf("0");
printf("\nEpson состояние - 11110111\n");
printf("\nУстановите: принтер offline. ");
printf("Нажмите любую клавишу\n");
getch();
stat=inportb(statusport);
printf("Состояние принтера - ");
for (i=7; i>=0; i--)
if ((stat>>i)&1) printf("1"); else printf("0");
printf("\nEpson состояние - 01010111\n");
printf("\nУстановите: нет бумаги. ");
printf("Нажмите любую клавишу\n");
getch();
stat=inportb(statusport);
printf("Состояние принтера - ");
for (i=7; i>=0; i--)
if ((stat>>i)&1) printf("1"); else printf("0");
printf("\nEpson состояние - 01110111\n");
printf("\nУстановите: принтер готов. ");
printf("Нажмите любую клавишу\n");
getch();
stat=inportb(statusport);
printf("Состояние принтера - ");
for (i=7; i>=0; i--)
if ((stat>>i)&1) printf("1"); else printf("0");
printf("\nEpson состояние - 11011111\n");
}
Разряды байта, передаваемого в порт управления, интерпретируются следующим образом:
| 0 | - устанавливается в 1 при передаче байта данных (строб вывода); |
| 1 | - установка его в 1 вызывает автоматический перевод строки после возврата каретки; |
| 2 | - нормально установлен в 0, устанавливается в 1 при инициализации порта принтера; |
| 3 | - устанавливается в 1 при выборе принтера; |
| 4 | - разрешает (1) прерывание принтера, используется только в программе спуллинга печати; |
| 5 - 6 | - не используются устанавливаются в 0. |
/*== ПРИМЕР 8.2 ==*/
/*=============== Печать символа на принтере =============*/
#include <dos.h>
main() {
int p1,p2,p3,i; /* Номера портов */
unsigned char stat; /* Байт состояния */
/* Символы для печати (10 - перевод строки) */
char a[]={24,'a','b','c','d','e','f','g','h','i','j',10};
/* Опеределение портов */
p1=peek(0x40,8); p2=p1+1; p3=p2+1;
/* Инициализация принтера */
outportb(p3,0x08); delay(50); outportb(p3,0x0c);
/* Проверка состояния */
stat=inport(p2);
if ((stat&0x20)!=0) {
printf("Ошибка принтера. Байт состояния = %02x\n",stat);
exit();
}
for(i=0;i<12;i++) {
/* Ожидание готовности */
while ((stat&0x80)==0) stat=inport(p2);
/* Символ в базовый порт */
outportb(p1,a[i]);
/* Строб */
outportb(p3,0x0d); outportb(p3,0x0c);
/* Проверка состояния */
stat=inport(p2);
if ((stat&0x20)!=0) {
printf("Ошибка принтера. Байт состояния = %02x\n",stat);
exit();
}
}
}
8.2. Прерывание BIOS
В BIOS принтер обслуживается прерыванием 0x17, имеющим функции:
| 0 | - вывод символа; |
| 1 | - инициализация; |
| 2 | - чтение состояния. |
Программные примеры 8.3 и 8.4 аналогичны по выполняемым действиям двум первым, но используют прерывание 0x17.
/*== ПРИМЕР 8.3 ==*/
/*============== Получение статуса принтера ==============*/
#include <dos.h>
main() {
union REGS rr;
int i;
/* Проверка состояний */
printf("\nУстановите: принтер выключен. ");
printf("Нажмите любую клавишу\n");
getch();
rr.x.dx=0; /* Номер принтера - LPT1 */
rr.h.ah=2; /* Функция 2 */
int86(0x17,&rr,&rr);
printf("Состояние принтера - ");
for (i=7; i>=0; i--)
if ((rr.h.ah>>i)&1) printf("1"); else printf("0");
printf("\n\nУстановите: принтер offline. ");
printf("Нажмите любую клавишу\n");
getch();
rr.x.dx=0; rr.h.ah=2; int86(0x17,&rr,&rr);
printf("Состояние принтера - ");
for (i=7; i>=0; i--)
if ((rr.h.ah>>i)&1) printf("1"); else printf("0");
printf("\n\nУстановите: нет бумаги. ");
printf("Нажмите любую клавишу\n");
getch();
rr.x.dx=0; rr.h.ah=2; int86(0x17,&rr,&rr);
printf("Состояние принтера - ");
for (i=7; i>=0; i--)
if ((rr.h.ah>>i)&1) printf("1"); else printf("0");
printf("\n\nУстановите: принтер готов. ");
printf("Нажмите любую клавишу\n");
getch();
rr.x.dx=0; rr.h.ah=2; int86(0x17,&rr,&rr);
printf("Состояние принтера - ");
for (i=7; i>=0; i--)
if ((rr.h.ah>>i)&1) printf("1"); else printf("0");
}
/*== ПРИМЕР 8.4 ==*/
/*=============== Печать символа на принтере =============*/
#include <dos.h>
main() {
union REGS rr;
unsigned char stat; /* Байт состояния */
/* Символы для печати */
char a[]={24,'a','b','c','d','e','f','g','h','i','j',10};
int i;
/* Инициализация принтера */
rr.x.dx=0; /* Номер принтера - LPT1 */
rr.h.ah=1; /* Функция 1 */
int86(0x17,&rr,&rr);
stat=rr.h.ah;
/* Проверка состояния */
if ((stat&0x08)!=0) {
printf("Ошибка принтера. Байт состояния = %02x\n",stat);
exit();
}
for(i=0;i<12;i++) {
/* Ожидание готовности */
while ((stat&0x80)==0) {
rr.x.dx=0; /* Номер принтера - LPT1 */
rr.h.ah=2; /* Функция 2 */
int86(0x17,&rr,&rr);
stat=rr.h.ah;
}
/* Вывод символа */
rr.x.dx=0; /* Номер принтера - LPT1 */
rr.h.ah=0; /* Функция 0 */
rr.h.al=a[i];
int86(0x17,&rr,&rr);
/* Проверка состояния */
if ((rr.h.ah&0x08)!=0) {
printf("Ошибка принтера. Байт состояния = %02x\n",stat);
exit();
}
}
}
8.3. Функции DOS
В DOS имеется функция 5, которая интегрирует в себе операции по управлению, выводу и анализу состояния. Те же действия программируются при помощи функции DOS следующим образом (пример 8.5):
/*== ПРИМЕР 8.5 ==*/
/*=============== Печать символа на принтере =============*/
#include <dos.h>
main() {
union REGS rr;
/* Символы для печати */
char a[]={24,'a','b','c','d','e','f','g','h','i ','j',10};
int i;
for(i=0;i<12;i++) {
rr.h.ah=5; /* Функция 5 DOS */
rr.h.dl=a[i];
intdos(&rr,&rr);
}
}
Кроме того, с принтером в DOS связан стандартный файл печати stdprn. Для вывода на принтер можно использовать функцию файлового вывода 0x40, указывая для нее номер файла - 4.
8.4. Управление спецификациями печати
Выше мы уже встретились с управляющими символами (24,10). Некоторые коды или последовательности кодов интерпретируются принтером не как коды символов, подлежащих отображению на бумаге, а как управляющие коды. Они используются для выполнения принтером специальных действий и управления спецификациями печати. Наиболее интересный из этих кодов - код 0x1B (27) или Esc. Появление этого кода интерпретируется принтером как начало целой управляющей последовательности. (Esc-последовательности принтера в отличие от клавиатуры и терминала обрабатываются не драйвером ANSI, а аппаратурой принтера). Следующий за Esc код задает тип действия, далее могут следовать еще несколько байт, количество которых зависит от действия. В примере 8.6 продемонстрированы некоторые режимы печати, устанавливаемые при помощи управляющих кодов и Esc-последовательностей. (Этот и следующие примеры не исчерпывают возможностей управления принтером при помощи управляющих кодов и Esc-последовательностей. Для получения полного представления о них следует обратиться к техническому описанию принтера.) Обратите внимание на последовательность 27, 64, названную в комментариях инициализацией. Не следует путать эту инициализацию с инициализацией порта, о которой шла речь выше. Esc-последовательность инициализации обеспечивает установку всех спецификаций печати в те значения, которые они имеют по умолчанию. Для выдачи управляющих последовательностей (как и печатаемых символов) можно использовать средства BIOS, DOS, а также любые средства, имеющиеся в применяемом языке программирования. Мы в этом и следующих примерах применяем функцию Турбо-Си putc.
/*== ПРИМЕР 8.6 ==*/
/*============ Управляющие коды режимов печати ===========*/
#include <stdio.h>
#define prt(x) putc(x,stdprn);
main() {
/* Инициализация */__ _.prt(27); prt(64);
/* Обычный режим */
fprintf(stdprn,"Default mode\n");
/* Режим двойной ширины */
prt(14);
fprintf(stdprn,"Set double width mode\n");
prt(20);
fprintf(stdprn,"Close double width mode\n");
/* Режим плотной печати */
prt(15);
fprintf(stdprn,"Set empassed mode\n");
prt(18);
fprintf(stdprn,"Close empassed mode\n");
/* Режим подчеркивания */
prt(27); prt(0x2d); prt(1);
fprintf(stdprn,"Set underline mode\n");
prt(27); prt(0x2d); prt(0);
fprintf(stdprn,"Close underline mode\n");
/* Режим двойной жирности */
prt(27); prt(0x45);
fprintf(stdprn,"Set double strike mode\n");
prt(27); prt(0x46);
fprintf(stdprn,"Close double strike mode\n");
/* Режим верхних индексов печати */
prt(27); prt(0x53); prt(0);
fprintf(stdprn,"Set superscript mode\n");
prt(27); prt(0x54);
fprintf(stdprn,"Close superscript mode\n");
/* Режим нижних индексов печати */
prt(27); prt(0x53); prt(1);
fprintf(stdprn,"Set subscript mode\n");
prt(27); prt(0x54);
fprintf(stdprn,"Close subscript mode\n");
}
Отдельный программный пример иллюстрирует интересную возможность формирования пользователем собственных печатных символов. Образы выводимых символов хранятся в ПЗУ принтера, но в принтере есть еще и ОЗУ, в которое могут быть загружены образы, созданные пользователем. Образ формируется на сетке высотой 8 и шириной 11 точек. В памяти образ представляется в двоичном виде, причем один байт описывает один столбец образа. Сформировав двоичный образ символа, следует записать его в ОЗУ, для чего используется Esc-последовательность вида:
Программа следующего примера использует технику, часто применяемую в драйверах русского текста (образы букв кириллицы необязательно есть в ПЗУ принтера). Программа загружает в ОЗУ принтера образ перевернутой буквы "a" под номером 40 и перехватывает вектор прерывания 0x17. Хотя в main-функции используются для печати функции Турбо-Си, все операции печати сводятся к этому прерыванию. Обработчик прерывания прежде всего анализирует функцию прерывания. Для функций 1 и 2 он вызывает старый обработчик. Для функции 0 (вывод символа) анализируется код символа в регистре AL. Если это код "a", то обработчик переключает принтер на ОЗУ, выводит код 40 и переключает принтер обратно на ПЗУ. Любой другой код обработчик выводит из ПЗУ.
/*== ПРИМЕР 8.7 ==*/
/*=================== Загружаемый шрифт ==================*/
#include <dos.h>
#include <stdio.h>
void interrupt (* old17)();
void interrupt h17();
int p1,p2,p3; /* Номера портов принтера */
#define prt(x) putc(x,stdprn)
#define byte unsigned char
/*==== main ====*/
main() {
char *st[]={ /* Текст для печати */
"He's a real Nowhere Man,",
"Sitting in his Nowhere Land,",
"Making all his nowhere plan for nobody." ;
byte esct[]= { /* Esc-последовательность */
27, 38, 0, /* Начало загрузки шрифта */
40, 40, /* Номера начального и конечного кодов */
0x8a, /* Ширина символа - 0a и сдвиг 80 */
/* Образ символа */
32,28,34,42,42,42,42,42,16,0,0 };
int i;
prt(27); prt(64); /* Инициализация принтера */
/* Выдача Esc-последовательности */
for (i=0; i<17; prt(esct[i++]));
p1=peek(0x40,8); p2=p1+1; p3=p2+1; /* Опр.портов */
/* Подключение к вектору 17 */
*old17=getvect(0x17); setvect(0x17,h17);
/* Печать текста */
for(i=0;i<3; fprintf(stdprn,"%s\n",st[i++]));
setvect(0x17,old17); /* Восстановление вектора */
prt(27); prt(64); /* Инициализация принтера */
}
/*==== Обработчик 17-го прерывания ====*/
void interrupt h17() {
if (_AH==0) { /* Вывод символа */
if (_AL=='a') { /* Символ "a" - особая обработка */
/* Esc-послед. переключения на шрифт ОЗУ. */
ownchar(27); ownchar(0x25); ownchar(1);
/* Печать символа ОЗУ с кодом 40 */
ownchar(40);
/* Esc-послед. переключения на шрифт ПЗУ. */
ownchar(27); ownchar(0x25); ownchar(0);
}
else ownchar(_AL); /* Печать символа не "a". */
}
else (*old17)(); /* Старый обработчик */
}
/*==== Печать одного символа ====*/
ownchar(byte f) {
byte stb; /* Байт состояния */
/* Ожидание готовности */
stb=0; while ((stb&0x80)==0) stb=inport(p2);
outportb(p1,f); /* Вывод символа */
outportb(p3,0x0d); outportb(p3,0x0c); /* Строб */
}
Напрашивается мысль о том, что матричный принтер можно заставить выводить на печать не только символы, но и любые изображения, и такая возможность действительно имеется - это использование принтера в графическом режиме. При печати в графическом режиме за один проход каретки печатается строка (полоса) высотой 8 точек. Таким образом, графическое изображение составляется из таких строк-полос. Размер битового образа одной строки в памяти (в байтах) равен ширине строки (в точках). Каждый байт образа описывает один столбик изображения высотой в 8 и шириной в 1 точку (старший разряд байта соответствует верхней точке изображения). Для выдачи образа на печать следует выдать на принтер такую Esc-последовательность:
Параметрами функции prtgraph являются экранные координаты левого верхнего и правого нижнего углов того окна экрана, копию которого следует вывести на печать, и код цвета, которым построено изображение. Поскольку изображение будет выводиться построчно, следует обеспечить, чтобы строки на печати располагались плотно, без промежутков между ними, этому удовлетворяет расстояние между строками 25/216 дюйма, которое устанавливается специальной Esc-последовательностью. Определяется ширина окна, размер битового образа будет равен этой ширине, в соответствии с этим выделяется память для размещения битового образа, заодно размер образа заносится в третий и четвертый байты Esc-последовательности установки графического режима. Формирование образа происходит построчно: последовательно считываются (функцией 0x0D прерывания 0x10, см. раздел 9.7) точки первой строки экранного обpаза; x-координата внутри окна опеределяет номер байта печатного образа, y-координата - номер формируемого разряда в этом байте. Если точка на экране имеет заданный цвет, то в соответствующий разряд заносится 1. (Мы считываем образ по строкам, и это соответствует представлению графической информации в видеоадаптере, но вы можете организовать и считывание образа по вертикали.) Когда очередные 8 строк экранного образа будут считаны и преобразованы в образ печати, информация выводится на принтер - вначале заголовок Esc-последовательности, а затем сам образ, после чего начинается формирование следующей строки образа для печати. Поскольку высота заданного окна не обязательно кратна 8, после перебо- ра всех экранных строк может остаться невыведенным на печать образ печатной строки, высота которого менее 8 точек. Этот образ выводится после выхода из цикла.
/*== ПРИМЕР 8.8 ==*/
/*========== Работа принтера в графическом режиме ========*/
#include <dos.h>
#include <math.h>
#include <stdio.h>
#include <alloc.h>
#define prt(x) putc(x,stdprn);
union REGS rr;
main() {
int col=4; /* Цвет - красный */
paint(col); /* Построение картинки */
getch();
prtgraph(1,1,80,80,col); /* Вывод на принтер */
}
/* Построение картинки (из окружностей) */
paint(int col) {
int x,y,fy,fx,k;
/* Установка графического режима */
rr.x.ax=0x0010; int86(0x10,&rr,&rr);
/* Построение контуров */
for (x=-24; x<=24; x++) {
y=(int)sqrt((double)(576-x*x));
point(x+32,32-y,col); point(x+32,32+y,col);
}
for (x=-12; x<=12; x++) {
y=(int)sqrt((double)(144-x*x));
point(x+20,32+y,col); point(x+44,32-y,col);
}
for (x=-4; x<=4; x++) {
y=(int)sqrt((double)(16-x*x));
point(x+20,32-y,col); point(x+20,32+y,col);
point(x+44,32+y,col); point(x+44,32-y,col);
}
/* Закрашивание */
for (fx=-24; fx<=24; fx++) {
fy=(int)sqrt((double)(576-fx*fx)); x=fx+32;
for (k=0, y=32-fy; y<=32+fy; y++) {
rr.x.dx=y; rr.x.cx=x; rr.h.bh=0;
rr.h.ah=0x0d; int86(0x10,&rr,&rr);
if ((rr.h.al==col)&&((y!=32)||(x==32))) k=1-k;
if (k) point(x,y,col);
}
}
}
/* Вывод одной графической точки */
point(int x, int y, int c) {
rr.x.dx=y; rr.x.cx=x; rr.h.bh=0; rr.h.al=c;
rr.h.ah=0x0c; int86(0x10,&rr,&rr);
}
/*----------------------------------------------------*/
/* Графическая копия экрана.
(x1,y1),(x2,y2) - координаты окна, c - цвет */
prtgraph(int x1, int y1, int x2, int y2, int c) {
char *str; /* Графический образ строки для печати */
char *s;
int strsize; /* Размер образа */
char esc[] = /* Начало Esc-послед.графической печати */
{ 27,42,0,0,0 };
int x,y; /* Экранные координаты */
int bit; /* Счетчик разрядов в образе */
int i;
/* Инициализация принтера */
prt(27); prt(64);
/* Установка расстояния между строк */
prt(27); prt(51); prt(25);
/* Выделение памяти для образа */
strsize=x2-x1+1; str=malloc(strsize);
for (s=str, i=0; i<strsize; i++, s++) *s=0;
/* Запись размера образа в Esc-послед. */
esc[3]=strsize%256; esc[4]=strsize/256;
/* Перебор строк */
for (bit=7, y=y1; y<=y2; y++) {
/* Перебор точек в строке */
for (s=str, x=x1; x<=x2; x++, s++) {
/* Чтение точки */
rr.x.dx=y; rr.x.cx=x; rr.h.bh=0;
rr.h.ah=0x0d; int86(0x10,&rr,&rr);
/* Если цвет точки совпадает -
заносится 1 в соответствующий разряд образа */
if (rr.h.al==c) *s|=(1<<bit);
}
if (--bit<0) {
/* Если сформированы 8 разрядов образа, то:
выводится начало Esc-последовательности, */
for(i=0;i<5;i++) prt(esc[i]);
/* выводится образ и обнуляется, */
for (i=0, s=str; i<strsize; i++,s++) {
prt(*s); *s=0;
}
prt(10); /* перевод строки */
bit=7;
}
}
/* Если не весь образ выведен - вывод остатка */
if (bit<7) {
for (i=0; i<5; i++) prt(esc[i]);
for (i=0, s=str; i<strsize; i++,s++) prt(*s);
prt(10);
}
/* Инициализация принтера */
prt(27); prt(64);
}
| Каталог | Индекс раздела |
| Назад | Оглавление | Вперед |