Листинг 8-1. Коммуникационная программа на Microsoft C 5.0
------------------------------------------------------------------
/*
* Имя файла: SERIO.C
* Цель: Иллюстрация программирования
* последовательного порта в систе-
* мах MS-DOS. Эта версия разрабо-
* тана на персональном компьтере
* IBM PC-AT с последовательным
* адаптером фирмы IBM.
* Использовалась операционная
* система DOS 3.1.
* Автор: Наба Баркакати, март 1988
* Язык: Microsoft C 5.0
* Модель памяти: Большой емкости
* Транслировать/компоновать: CL /AL /Gs serio.c
*/
/*----------------------------------------------------------*/
#include
#include
#include
#include
#include
#define TRUE 1
#define FALSE 0
#define EOS '\0'
#define CONTROL(x) (x-0x40)
#define ESC_KEY CONTROL('[')
/* Определить коммуникационные параметры */
#define COM_PARAMS (_COM_CHR8 | _COM_STOP1 | \
_COM_NOPARITY |_COM_1200)
/* Определить размеры приемного и передающего буферов */
#define RXQSIZE 512
#define TXQSIZE 512
/* Определения для программируемого контроллера
* прерываний 8259
*/
#define P8259_0 0x20 /* регистр управления прерыванием */
#define P8259_1 0x21 /* регистр маски прерывания */
#define END_OF_INT 0x20 /* не определенный EIO */
/* Определить коды ASCII XON и XOFF */
#define XON_ASCII (0x11)
#define XOFF_ASCII (0x13)
/* Обратиться к области данных BIOS по адресу 400h */
#define BIOS_DATA ((int far *)(0x400000L))
/* Адресом коммуникационного порта является короткое целое
* 'comport'. Эта переменная инициализируется чтением из
* области данных BIOS на сегменте 0х40.
*/
#define IER (comport + 1) /* регистр разрешения прерываний */
#define IIR (comport + 2) /* идентификация прерывания */
#define LCR (comport + 3) /* регистр управления регистром */
#define MCR (comport + 4) /* регистр управления модемом */
#define LSR (comport + 5) /* регистр состояния линии */
#define MSR (comport + 6) /* регистр состояния модема */
/* Коды разрешения отдельных прерываний */
#define RDAINT 1
#define THREINT 2
#define RLSINT 4
#define MSINT 8
/* Значение регистра управления модемом */
#define MCRALL 15 /* (DTR, RTS, OUT1 и OUT2 = 1) */
#define MCROFF 0 /* все сброшено */
/* Значение регистра разрешения прерывания для его
* включения/выключения
*/
#define IERALL (RDAINT+THREINT+RLSINT+MSINT)
#define IEROFF 0
/* Несколько масок для сброса прерываний */
#define THEREOFF 0xfd
/* Номера идентификатора прерываний */
#define MDMSTATUS 0
#define TXREGEMPTY 2
#define RXDATAREADY 4
#define RLINESTATUS 6
/* Флаги управления потоком XON/XOFF */
#define XON_RCVD 1
#define XOFF_RCVD 0
#define XON_SENT 1
#define XOFF_SENT 0
/* Высший и низший коэффициенты для триггера xon-xoff */
#define HI_TRIGGER(x) (3*x/4)
#define LO_TRIGGER(x) (x/4)
/* Функция получения нулевого бита целого */
#define bit0(i) (i & 0x0001)
/* Макрокоманда для включения прерывания, "номер разрешения
* прерывания" которого "i", в противном случае оно запрещает-
* ся. Например, прерывание THRE запрещается, если XOFF полу-
* чен от удаленной системы.
*/
#define turnon_int(i,j) \
if(((j=inp(IER))&i)==0)outp(IER,(j|i))
#define report_error(s) fprintf(stderr,s)
typedef struct QTYPE /* структура данных для очереди */
{
int count;
int front;
int rear;
int mexsize;
char *data;
} QTYPE;
static char rxbuf[RXQSIZE], txbuf[TXQSIZE];
static QTYPE rcvq = {0, }-1, -1, RXQSIZE, rxbuf},
trmq = {0, }-1, -1, TXQSIZE, txbuf};
/* Признаки общего состояния */
int s_linestatus, s_modemstatus;
static QTYPE *txq = &trmq, *rxq = &rcvq;
static short comport=0,
enable_xonxoff = 1,
rcvd_xonxoff = XON_RCVD,
sent_xonxoff = XON_SENT,
send_xon = FALSE,
send_xoff = FALSE,
int_number = 12;
int_enable_mask = 0xef,
int_disable_mask = 0x10;
/* Прототипы функций */
int s_sendchar(int);
int s_rcvchar(void);
int s_setup(short, unsigned);
int s_cleanup(void);
char *q_getfrom( QTYPE *, char *);
int q_puton( QTYPE *, char *);
void interrupt far s_inthndlr(void);
static void s_rda(void);
static void s_trmty(void);
static void (interrupt far *old_handler)();
/*----------------------------------------------------------*/
main(int argc, char **argv)
{
int ch, port_number = 0;
/* Получить номер порта из командной строки */
if(argc > 1) port_number = atoi(argv[1]) - 1;
printf("\nSERIO -- Последовательный ввод/вывод \
c параметрами 1200,8,N,1 через порт COM%d\n", port_number+1);
printf("\nСвязь ...\n");
/* Сначала установить последовательный порт */
s_setup(port_number, COM_PARAMS);
/* Последующий бесконечный цикл имитирует терминал.
* Клавиша Escape служить для общего сброса и возврата.
*/
while (TRUE)
{
if ((ch = s_rcvchar()) != -1) putch(ch);
if ( kbhit() != 0)
{
ch = getch();
if (ch == ESC_KEY)
{
s_cleanup();
return;
}
else
s_sendchar(ch);
} /* конец проверки kbhit() */
}
}
/*---------------------------------------------------------*/
/* s _ i n t h n d l r
* Обработчик всех прерываний последовательного порта.
*/
void interrupt far s_inthndlr(void)
{
int c;
registr int int_id, intmask;
/* Немедленно разрешить прерывания */
_enable();
while (TRUE)
{
/* Считать регистр идентификации прерывания , IIR */
int_id = inp(IIR);
if (bit0(int_id) == 1)
{
/* Если бит 0 - 1, то прерывания не ожидаются. Направить прог-
* раммируемому контроллеру прерываний сигнал "конец прерыва-
* ния" и вернуться.
*/
outp(P8259_0, END_OF_INT);
return;
}
if (int_id >= RXDATAREADY)
turnon_int(THREINT,intmask);
/* Обработать прерывание в соответствии с идентификатором.
* Следующий перечень составлен в соответствии с возрастанием
* приоритета.
*/
switch (int_id)
{
case MDMSTATUS: /* считать состояние модема */
s_modemstatus = inp(MSR);
break;
case TXREGEMPTY: s_trmty();
break;
case RXDATAREADY: s_rda();
break;
case RLINESTATUS: /* читать состояние линии */
s_linestatus = inp(LSR);
break;
/* пропустить, если идентификатор не является одним из
* перечисленных */
}
}
}
/*---------------------------------------------------------*/
/* s _ r d a
* Обработать прерывание "доступны данные для приема"
*/
static void s_rda(void)
{
registr int intmask;
char c;
/* читать из коммуникационного порта */
c = inp(comport);
if(enable_xonxoff) {
if (c == XON_ASCII) {
rcvd_xonxoff = XON_RCVD;
/* Включить прерывание THRE, если оно выключено. */
turnon_int(THREINT,intmask);
return;
}
if(c == XOFF_ASCII) {
rcvd_xonxoff = XOFF_RCVD;
/* Сбросить прерывания THRE */
intmask = inp(IER);
if (intmask & THREINT)
outp(IER, intmask & THREOFF);
return;
}
}
q_puton(rxq, &c);
/* Проверить, заполнена ли почти очередь (75%) */
if(enable_xonxoff) {
if(rxq->count >= HI_TRIGGER(RXQSIZE) &&
sent_xonxoff != XOFF_SENT ) {
/* Установить флаг для направления XOFF */
send_xoff = TRUE;
/* Включить прерывания THRE так, чтобы послать XOFF */
turnon_int(THREINT,intmask);
}
}
}
/*---------------------------------------------------------*/
/* s _ t r m t y
* Обработать прерывание "регистр хранения передачи
* свободен"
*/
static void s_trmty(void)
{
char c;
registr int ierval;
if (send_xoff == TRUE) {
outp(comport, XOFF_ASCII);
send_off = FALSE;
sent_xonxoff = XOFF_SENT;
return;
}
if (send_xon == TRUE) {
outp(comport, XON_ASCII);
send_xon = FALSE;
sent_xonxoff = XON_SENT;
return;
}
/* Поместить символ в регистр хранения передачи */
if( q_getfrom(txq, &c) != NULL){
outp(comport, c);
return;
}
/* Нечего посылать -- сбросить прерывания THRE */
ierval = inp(IER);
if (ierval & THREINT) outp(IER, ierval & THREOFF);
}
/*---------------------------------------------------------*/
/* s _ s e t u p
* Установить все для связи.
* Вернуть 1, если установка прошла успешно, в противном
* случае вернуть 0.
*/
int s_setup(short port_number, unsigned commparams)
{
int intmask;
if (port_number < 0 || port_number > 1)
report_error("Неверный номер порта!\n");
/* Получить базовый адрес последовательного порта из
* области данных BIOS */
comport = *(BIOS_DATA + port_number);
if (comport == 0)
{
report_error("BIOS не может найти порт!\n");
return(0);
}
/* Установить маски для программируемого контроллера
* прерываний 8259A. Для разрешения прерывания порта
* эта маска логически умножается с маской регистра
* в 21h. Для запрещения, логически сложить маску
* запрещения с маской регистра. Номер прерывания
* определяется как 8 + уровень IRQ прерывания.
* Коммуникационный порт 1 имеет IRQ 4, порт 2 имеет
* IRQ 3.
*/
if (port_number == 0)
{
int_enable_mask = 0xef;
int_disable_mask = 0x10;
int_number = 12;
}
if (port_number == 1)
{
int_enable_mask = 0xf7;
int_disable_mask = 8;
int_number = 11;
}
/* Получить номер старого прерывания и сохранить его. */
old_handler = _dos_getvect(int_number);
/* Установить новый обработчик с именем s_inthndlr.
* Запретить прерывания при смене обработчика.
*/
_disable();
_dos_setvect(int_number, s_inthndlr);
_enable();
/* Установить коммуникационные параметры */
_bios_serialcom(_COM_INIT, port_number, commparams);
/* Инициализировать флаги XON/XOFF */
rcvd_xonxoff = XON_RCVD;
if (sent_xonxoff == XOFF_SENT)
send_xon = TRUE;
else
send_xon = FALSE;
send_xoff = FALSE;
/* Включить прерывания коммуникационного порта и
* установка 8259A.
*/
_disable();
/* Установить регистр управления модемом (порт = MCR) */
outp(MCR, MCRALL);
/* Разрешить все прерывания последовательной карты */
outp(IER, IERALL);
/* Считать регистр маски прерывания 8259A и записать его
* обратно после логического умножения с _int_enable_mask.
*/
intmask = inp(P8259_1) & int_enable_mask;
outp(P8259_1, intmask);
_enable();
return(1);
}
/*---------------------------------------------------------*/
/* s _ c l e a n u p
/* Очистить после сеанса связи. Сбросить все прерывания. */
int s_cleanup(void)
{
int intmask;
/* Выключить прерывания последовательной карты */
_disable();
/* Первым сбросить регистр разрешения прерывания порта */
outp(IER, IEROFF);
/* Сбросить все биты регистра управления модемом */
outp(MCR, MCROFF);
/* Затем запретить распознавание контроллером 8259A
* прерываний последовательного порта.
*/
intmask = inp(P8259 _1) | int_disable_mask;
outp(P8259_1, intmask);
/* Восстановить первоначальный вектор прерывания */
_dos_setvect(int_number, old_handler);
/* Снова разрешить прерывания */
_enable();
}
/*---------------------------------------------------------*/
/* s _ s e n d c h a r
* Поместить символ в очередь на передачу. Вернуть 1, если
* все в порядке, в противном случае вернуть 0.
*/
int s_sendchar(int ch)
{
int retval, intmask;
_disable();
retval = q_puton(txq, (char *)&ch);
_enable();
/* Включить прерываниеTHRE в том случае, если оно выключено
* и не получен XOFF.
*/
if (rcvd_xonxoff != XOFF_RCVD)
turnon_int(THREINT,intmask);
return(retval);
}
/*---------------------------------------------------------*/
/* s _ r c v c h a r
* Вернуть символ из очереди на прием.
* Вернуть 1, если очередь пуста.
*/
int s_rcvchar(void)
{
int ch, intmask;
/* Если ранее был послан сигнал XOFF, то мы должны направить
* XON.
*/
if(enable_xonxoff)
{
if(rxq->count <= LO_TRIGGER(RXQSIZE) &&
sent_xonxoff != XON_SENT )
{
send_xon = TRUE;
turnon_int(THREINT,intmask);
}
}
_disable();
if (q_getfrom(rxq, (chr *)&ch) == NULL)
{
_enable();
return(-1);
}
else
{
_enable();
return(ch);
}
}
/*--------------------------------------------------------*/
/* q _ g e t f r o m
* Копировать следующий элемент данных в очередь в
* определенное местоположение. Также вернуть указатель
* на этот элемент.
*/
char *q_getfrom( QTYPE *queue, char *data)
{
char *current;
current = NULL;
if(queue->front == -1) return(current);
/* В противном случае искать данные */
current = &(queue->data[queue->front]);
*data = *current;
queue->count--;
if(queue->count == 0)
{
/* Очередь пуста. Сбросить начало, конец и счет */
queue->front = queue->rear = -1;
return(current);
}
/* Увеличить начальный индекс и проверить на циклический
* переход.
*/
if(queue->front == queue->maxsize-1)
queue->front = 0;
else
queue->front++;
return(current);
}
/*--------------------------------------------------------*/
/* q _ p u t o n
* Поместить элемент данных в очередь.
*/
int q_puton(QTYPE *queue, char *data)
{
/* Сначала проверить степень заполнения очереди.
* Если она полна, то вернуть 0.
*/
if(queue->count == queue->maxsize) return(0);
/* В противном случае установить на конец и провести
* проверку на циклический переход.
*/
if(queue->rear == queue->maxsize-1)
queue->rear = 0;
else
queue->rear++;
/* Сохранить символ в очереди */
queue->data[queue->rear] = *data;
queue->count++;
if(queue->front == -1) queue->front = 0;
return(1); /* Успешно вставленный элемент */
}
_____________________________________________________________
|