На главную
Подписка
Новости










Главная / MS-DOS / MS-DOS. РУКОВОДСТВО РАЗРАБОТЧИКА / Глава 8 / Пример программы Сделать домашней страницей Добавить в избранное Написать писмо

НАЗАД СОДЕРЖАНИЕ ВПЕРЁД

Пример программы

Мы описали аппаратные средства последовательного порта, указали, какие действия необходимо выполнить для программирования порта в целях организации эффективного управляемого прерываниями ввода/вывода. Осталось только объединить отдельные части, для того, чтобы показать, каким образом создается завершенная коммуникационная программа. Мы делаем это в листинге 8-1, который содержит основную коммуникационную программу, написанную на Microsoft C версии 5.0.


            Листинг 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); /* Успешно вставленный элемент */
         }
         _____________________________________________________________

НАЗАД СОДЕРЖАНИЕ ВПЕРЁД

Hosted by uCoz