Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Программирование модемов

© Александр Фролов, Григорий Фролов
Том 4, М.: Диалог-МИФИ, 1993, 236 стр.

[Назад] [Содеожание] [Дальше]

5.4. Коммуникационная программа, использующая прерывания

Программа, представленная в предыдущей главе, имеет один большой недостаток: она должна постоянно производить опрос регистра состояния линии, с тем чтобы определить момент, когда от модема поступит очередной символ. В результате становится трудной, а иногда невозможной обработка поступающих символов. Например, если вы сразу отображаете символы, получаемые от COM-порта, на экране, то при использовании для этого функции putch() отдельные символы могут быть потеряны. Дело в том, что функция putch() работает слишком медленно и на скоростях 2400 бод и выше модем может успеть передать в COM-порт несколько новых символов, в то время как функция putch() еще не вывела на экран ни одного символа. В этом случае происходит ошибка переполнения входного буфера микросхемы UART (см. бит D2 регистра состояния линии).

Таким образом, имеет смысл организовать прием и передачу символов модему в фоновом режиме, используя прерывания по окончании приема и передачи символа.

Если ваша коммуникационная программа будет использовать прерывания, можно организовать буфер принимаемых и передаваемых данных. Обработчик прерываний должен проанализировать причину прерывания и либо передать в COM-порт очередной символ из буфера передатчика (если прерывание произошло в результате передачи очередного символа), либо считать поступивший символ из регистра данных и записать его в буфер приемника (если прерывание произошло в результате приема от модема очередного символа).

В этом случае процесс обмена идет в фоновом режиме и процессор может спокойно заниматься обработкой принимаемых и передаваемых символов. Если программе понадобится передать данные модему, она может просто записать их в буфер передатчика. Для приема данных она должна считать их из буфера приемника.

Принципы использования прерываний

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

Прерывания могут вырабатываться асинхронным адаптером в следующих случаях:

  • изменилось состояние линии приемника: произошло переполнение приемника, произошла ошибка четности или синхронизации, линия перешла в состояние BREAK;
  • данные приняты и доступны для чтения через регистр данных;
  • регистр передатчика пуст;
  • изменилось состояние модема: изменилось состояние линий CTS, RI, DCD, DSR.

Вы можете отдельно запрещать или разрешать эти прерывания. Для этого необходимо установить соответствующие биты в регистре управления прерываниями.

Как приходит прерывание от COM-порта? Как мы указывали ранее, каждому COM-порту соответствует, кроме базового адреса его регистров, линия IRQ (см. главы "Последовательный асинхронный адаптер" и "COM-порт и номера IRQ"):

COM-порт IRQ Номер прерывания
COM1 IRQ4 INT 0Ch
COM2 IRQ3 INT 0Bh
COM3 IRQ4 INT 0Ch
COM4 IRQ3 INT 0Bh

Заметим, что в данной таблице представлен только один возможный вариант соответствия номеру COM-порта линии IRQ. Некоторые платы асинхронных адаптеров и некоторые внутренние модемы имеют отдельно перемычки для выбора номера COM-порта (адреса базового регистра) и номера линии IRQ.

Что представляет из себя обработчик прерываний асинхронного адаптера? После вызова обработчика прерываний он должен:

Разрешить обработку прерываний

Необходимо выполнить команду sti, для того чтобы разрешить обработку прерываний с более высоким приоритетом, чем прерывание от асинхронного адаптера.

Определить причину прерывания

Для этого следует считать содержимое регистра идентификации прерываня. Состояние битов D1 D2 определяют причину прерывания:

Биты D2 D1 Причина прерывания
00 прерывание по линии состояния;
01 буфер передатчика пуст;
10 данные приняты;
11 изменилось состояние модема.

В зависимости от того, какое произошло прерывание, его надо соответствующим образом обработать.

Произошло прерывание по линии состояния

Считать регистр состояния линии и конкретизировать причину прерывания (данное прерывание сбрасывается после чтения регистра состояния линии). Если это необходимо, подать основной программе сигнал о произошедшей ошибке с целью ее устранения. Например, в случае определения на линии сигнала BREAK (удаленный модем повесил трубку), надо попытаться возобновить связь.

Прерывание по принятию данных

Очередной символ принят, и его можно считать через регистр данных. Прерывание сбрасывается после чтения регистра данных. Принятый байт необходимо записать в приемный буфер программы, из которого впоследствии его прочитает основная программа. Буфер приемника удобно организовать в виде очереди.

Буфер передатчика пуст

Прерывание происходит в случае, если буфер передатчика пуст и можно передать COM-порту очередной символ. Можно организовать буфер передатчика программы, в который программа будет записывать данные, предназначенные для передачи через COM-порт. В этом случае, когда придет прерывание, надо считать очередной символ из буфера передатчика программы и записать его в регистр данных. Прерывание сбрасывается после записи очередного символа в регистр данных UART. Если нет данных для передачи (программный буфер передатчика пуст), можно запретить это прерывание через регистр управления прерываниями.

Изменилось состояние модема

Прерывание происходит при изменении состояния входных линий CTS, RI, DCD, DSR. Состояние этих линий можно определить, считав регистр состояния модема. Это прерывание используется для обнаружения звонка на телефонной линии. Прерывание автоматически сбрасывается после чтения регистра состояния модема.

Считать регистр идентификации прерывания

Может случиться, что одновременно произойдет несколько прерываний. В этом случае бит D0 регистра идентификации прерываний равен единице. Тогда перед завершением обработки прерывания необходимо обработать следующее прерывание в соответствии с состоянием битов D1, D2. Так следует поступать до тех пор, пока не будут обработаны все прерывания (бит D0 не станет равен нулю).

Обработать конец прерывания

Передать контроллеру прерываний команду обработки конца прерывания. Для этого посылается в порт с адресом 20h команда конца прерывания:

mov   al,20h
out   20h,al

Закончить обработку прерывания

Теперь можно закончить обработку прерывания, выполнив команду iret.

Итак, мы завершили рассмотрение обработчика прерываний. Теперь нам осталось изучить примерный порядок работы коммуникационной программы с использованием прерываний.

Установить обработчик прерываний

Необходимо установить обработчик прерываний, изменив соответствующий элемент таблицы векторов прерываний. Адрес старого обработчика сохраняется в глобальных переменных.

Инициализация COM-порта

Сначала надо перевести в неактивное состояние линии DTR и RTS. Затем сбросить регистр состояния линии, регистр состояния модема и регистр данных.

После того как мы сбросили регистры UART, можно приступить к инициализации COM-порта. Во время инициализации задается формат данных - длина слова, количество стоповых битов, наличие контроля по четности и скорость обмена.

Последним шагом в инициализации регистров UART является установка регистра управления прерываниями, в который записывается соответствующее значение. Например, чтобы разрешить генерацию прерываний при приеме очередного символа, надо записать значение 01h в регистр управления прерываниями:

// устанавливаем регистр управления прерываниями

   outp(port_adr+ICR, 1);   // ICR - адрес регистра
                  // управления прерываниями

На этом этап инициализации регистров UART можно считать законченным. Теперь COM-порт подготовлен для обмена через него данными с модемом, но модем пока еще не будет воспринимать данные от компьютера. Чтобы перевести его в рабочее состояние, надо передать ему сигналы DTR и RTS. В ответ на эти сигналы модем должен вернуть компьютеру сигналы DSR и CTS.

Инициализация контроллера прерывний

Для того чтобы прерывания от асинхронного адаптера выполнялись, необходимо разрешить прерывание по соответствующей линии IRQ через регистр маски прерываний контроллера прерываний:

// считываем состояние регистра маски прерываний 
mov   dx,21h
in   dx,al

// разрешаем прерывания от порта COM1
and   al,11101111b

// записываем новое значение в регистр маски прерываний
out   dx,al

Инициализация модема и установление связи

После установки обработчика прерываний и инициализации регистров COM-порта и контроллера прерываний можно передавать модему AT-команды и принимать от него ответ на них. При этом данные можно считывать (записывать) из COM-порта через буфер обработчика прерываний.

Обмен данными с удаленным модемом

Установив связь с удаленным модемом, оба модема переходят в режим обмена данными. Теперь можно начинать передавать и принимать данные.

Передача и прием данных от удаленного модема осуществляются так же, как передача модему команд и прием от него сообщениий.

Завершение программы

Для завершения коммуникационной программы, использующей прерывания, необходимо сбросить сигналы DTR и RTS и запретить через контроллер прерываний прерывания от COM-порта:

// считываем состояние регистра маски прерываний 
mov   dx,21h
in   dx,al

// запрещаем прерывания от порта COM1
or   al,00010000b

// записываем новое значение в регистр маски прерываний
out   dx,al

Затем нужно восстановить старый вектор обработчика прерываний.

Коммуникационная программа CHAT

В этой главе мы приведем исходный текст коммуникационной программы CHAT. В отличие от программы CHAT_S данная программа использует для работы с асинхронным адаптером прерывания.

При помощи этой программы можно связаться с удаленным модемом, передавать и принимать от него данные в формате ASCII. Например, вы можете позвонить на станцию BBS и прочитать почтовые сообщения. Передачу и прием файлов программа не поддерживает, иначе пример занимал бы слишком много места.

Данная телекоммуникационная программа может работать в двух режимах - активном, когда она сама производит вызов удаленного модема, и пассивном, когда программа находится в режиме ожидания звонка от удаленного модема. Для работы программы в активном режиме необходимо запустить ее с параметром "1", для пассивного режима - "0".

Большинство параметров программы, таких, как AT-команды инициализации, телефонный номер, скорость обмена и номер COM-порта, можно настроить через файл конфигурации setup.cfg. Образец этого файла представлен ниже:

// строка инициализации для режима активного вызова

Initialize ATS0=0Q0E0M1V1X4&C1&D2

// команда, которая переводит модем в командный режим и 
// кладет трубку

Dropline  \d\d+++\d\dATH0\n\r\d

// строка инициализации для режима ожидания звонка

AutoAnswer ATS0=1Q0E0M1V1X4&C1&D2

// префикс телефонного номера

DialPrefix \r\pATDP

// суффикс телефонного номера

DialSuffix

// телефонный номер

DialNumber 1135810

// номер COM-порта в формате COMn, где n - номер порта

Device COM3

// время, отведенное на установку связи с удаленным модемом

DialTimeout 30
TimeoutAnswer 30

// временная задержка между сиимволами при передаче

CharDelay 0

// время реакции модема на команды

ModemTimeout 3

// скорость обмена данными

Speed 2400

Программа состоит из следующих модулей:

CHAT.C      // главная процедура программы
MODEM.C      // передача данных модему через COM-порт
TIMER.C      // реализация временных задержек
CONF.C      // чтение файла конфигурации
SEND_COMM.C   // передача команд модему
TOOLS.C      // набор функций для работы с модулем UART.ASM
UART.ASM      // обработчик прерываний и процедуры низкого уровня

Теперь приведем сами исходные тексты программы. Основной модуль программы называется CHAT.C. В зависимости от параметра программы этот модуль вызывает функцию call() - режим вызова удаленного модема - или функцию answer() - режим ответа на приходящие звонки. Обе функции, call() и answer(), выполняют все действия по программированию COM-порта, контроллера прерываний и модема. Данные функции определены в модуле MODEM.C.

После окончания связи вызывается функция shutdown(), которая опускает телефонную трубку и отключает обработчик прерываний.

// CHAT.C
// основной модуль коммуникационной программы

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>

#include "mod_link.h"
#include "common.h"
#include "modem.h"
#include "timer.h"
#include "tools.h"


void hello(void) {

   printf("неправильно задан параметр программы \n"
          "mod_link n, где n = 1 вызов, n = 0 ответ\n");
   exit(0);
}

// основная процедура

void main( int argc, char *argv[] ) {

   // программа должна вызываться  параметром 1 или 0
   // 1 - это активный режим, когда программа сама вызывает
   // удаленный модем

   // 0 - программа находится в режиме автоответа на
   // приходящий вызов

   if( argc < 2 )
      hello();

0   if( strcmp( argv[1], "0") && strcmp( argv[1], "1" ))
      hello();

   if( !strcmp(  argv[1], "1" ) )

      // если программа запущена с параметром "1", вызывается
      // функция call() из модуля MODEM.C, выполняющая вызов
      // удаленного модема

      call();

   else

      // если программа запущена с параметром "0", вызывается
      // функция answer(), переключающая модем в режим автоответа

      answer();

   // освобождаем телефон

   shutdown();
}

Данный модуль определяет функции высокого уровня для работы с модемом:

  • call() - режим вызова удаленного модема,
  • answer() - режим ответа на приходящие звонки,
  • shutdown() - завершение сеанса связи,
  • exchange() - диалог пользователя и удаленного модема.

Эти функции вызывают модули более низкого уровня: SEND_COMM.C, TOOLS.C.

// MODEM.C
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>

#include "uart.h"
#include "common.h"
#include "modem.h"
#include "mod_link.h"
#include "timer.h"
#include "tools.h"
#include "conf.h"


char *device = "COM3";  // номер используемого порта в формате
                        // COMn, где n от 1 до 4

unsigned  dialTimeout = 12,   // продолжительность ожидания соединения
          chardelay = 0,      // задержка при передаче между символами
          modemTimeout = 3,   // таймаут на получение ответа от модема
          answerTimeout;      // ппродолжительность ожидания звонка


unsigned  speed = 2400;     // скорость обмена данными


char initialize[80];    // команда инициализации
char dropline[80];      // команда повесить трубку
char autoanswer[80];    // ответ на вызов в режиме автоответа

char dialPrefix[80];  // префикс номера
char dialSuffix[80];  // суффикс номера
char dialNumber[80];  // телефонный номер


static boolean    getmodem( const char *brand);
static boolean    sendlist( char **list, int timeout, int lasttimeout);
static boolean    sendalt( char *string, int timeout);

void exchange( void );


// call
// вызов удаленного пользователя

int call() {

   char str[80], buf[80];
   char *exp;
   int i,j;

   // определяем параметры связи (считываем файл конфигурации)

   getconfig();

   // устанавливаем обработчик прерываний и инициализируем
   // регистры UART и контроллера прерываний

   if (openline(device, speed))
      return FALSE;

   // очищаем приемный буфер

   while (sread(buf,1,0));

   printf("инициализируем модем\n\n");

   // передаем модему строку инициализации
   // (строка инициализации определяется ключевым словом Initialize
   // в файле конфигурации setup.cfg)

   sendstr( initialize );

   // ожидаем ответа модема

   sleep(modemTimeout);

   // считываем и отображаем на экране ответное сообщение модема

   if( r_count_pending() > 0 ) {

      sread(str, i = r_count_pending(), 0);
      str[i] = '\0';

      for( j = 0; j < i; j++ )
         putch( str[j] );
   }

   // передаем модему команду набора номера

   strcpy(buf, dialPrefix);
   strcat(buf, dialNumber);
   strcat(buf, dialSuffix);

   printf( "набираем номер\n\n");

   sendstr( buf );

   printf( "ожидаем соединение\n\n");

   // производим обмен данными с удаленным модемом,
   // пока не нажата клавиша ESC

   exchange();

   return(0);
}

// answer
// отвечает на звонок

int answer( void ) {

   char c;

   // определяем параметры связи

   getconfig();

   // устанавливаем обработчик прерываний и инициализируем
   // регистры UART и контроллера прерываний

   if (openline(device, speed))
      exit(-2);

   // очищаем приемный буфер

   while (sread(&c ,1,0));

   printf("инициализируем модем\n\n");

   // передаем модему строку инициализации
   // (строка инициализации определяется ключевым словом Autoanswer
   // в файле конфигурации setup.cfg)

   sendstr( autoanswer );
   sleep(modemTimeout);

   printf("ожидаем звонок\n");

   // производим обмен данными с удаленным модемом,
   // пока не нажата клавиша ESC

   exchange();

   return(0);
}


// shutdown
// функция кладет телефонную трубку

void shutdown( void ) {

   printf("\n\nсвязь окончена, освобождаем телефон\n");

   // передаем команду положить трубку

   sendstr( dropline );

   // восстанавливаем старый обработчик прерываний

   closeline();
}


// slowwrite
// передать символ модему с задержкой, определяемой ключевым
// словом CharDelay в файле конфигурации

void slowwrite( char *s, int len) {

   swrite( s , len );
   if (chardelay > 0) delay(chardelay);
}


// exchange
// функция выполняет диалог пользователя и удаленного модема

void exchange( void ) {

   int flag = 1;

   while(flag) {

      unsigned char str[80];
      unsigned char key;
      unsigned i,j;

      // если пользователь нажал на клавиатуру, получаем код
      // нажатого символа и передаем его модему

      if( kbhit() ) {
         key =  getch();

         // по нажатию клавиши Esc выходим из программы

         if( key == 27 ) {
            ssendbrk( 3 );
            flag = 0;
            break;
         }

         if( key == '\r' ) putch( '\n' );

         putch(key);

         swrite( &key, 1);
      }

      // если получены данные от модема, отображаем их на экране

      if( r_count_pending() > 0 ) {

         delay(100);

         sread(str, i = r_count_pending(), 0);
         str[i] = '\0';

         for( j = 0; j < i; j++ )
            putch( str[j] );
      }
   }
}

Модуль CONF.C определяет функцию getconfig(), считывающую файл конфигурации setup.cfg и записывающую считанные значения в глобальные переменные.

Конфигурационный файл может содержать следующие команды:

Initialize [строка инициализации для режима активного вызова]

Dropline [команда для освобождения телефона]

AutoAnswer [строка инициализации для режима ожидания звонка]

DialPrefix [ префикс телефонного номера]

DialSuffix [суффикс телефонного номера]

DialNumber [телефонный номер]

Device [номер COM-порта]

DialTimeout [время, отведенное на установку связи с удаленным модемом]

TimeoutAnswer [время, отведенное на установку связи с удаленным модемом]

CharDelay [временная задержка между символами при передаче]

ModemTimeout [время реакции модема на команды]

Speed [скорость обмена данными]

Итак, исходный текст модуля CONF.C:

// CONF.C

#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "conf.h"

void Number( unsigned *num );
void Token( char *string );


   FILE *CfgFile;
   char LineBuf[256];


   extern char initialize[80];
   extern char dropline[80];
   extern char autoanswer[80];

   extern char dialPrefix[80];
   extern char dialSuffix[80];
   extern char dialNumber[80];

   extern unsigned   chardelay;
   extern unsigned   dialTimeout;
   extern unsigned   modemTimeout;
   extern unsigned   answerTimeout;

   extern unsigned   speed;

   extern char *device;

/**
*.Name         getconfig
*
*.Title        Считывает файл setup.cfg.
*
*.Descr        Эта функция считывает данные из файла setup.cfg
*
*.Proto        void getconfig(void)
*
*.Params       не используется
*
*.Return       не используется
**/
void getconfig(void) {

   CfgFile=fopen("setup.cfg","r");
   if(CfgFile == NULL) {
      cprintf("\r\nОтсутствует файл SETUP.CFG.");
      exit(-1);
   }

   // заполняем глобальные переменные

   for(;;) {
      fgets(LineBuf,255,CfgFile);
      if(feof(CfgFile)) break;

      STRIP(LineBuf);

      if(OPERATOR("Dropline"))         Token( dropline );
      else if(OPERATOR("Initialize"))  Token( initialize );
      else if(OPERATOR("AutoAnswerr")) Token( autoanswer );

      else if(OPERATOR("DialNumber"))  Token( dialNumber );
      else if(OPERATOR("DialPrefix"))  Token( dialPrefix );
      else if(OPERATOR("DialSuffix"))  Token( dialSuffix );
      else if(OPERATOR("Device"))      Token( device );


      else if(OPERATOR("CharDelay"))      Number( &chardelay );
      else if(OPERATOR("DialTimeout"))    Number( &dialTimeout );
      else if(OPERATOR("ModemTimeout"))   Number( &modemTimeout );

      else if(OPERATOR("TimeoutAnswer"))  Number( &answerTimeout );
      else if(OPERATOR("Speed"))          Number( &speed );
   }

   fclose(CfgFile);
}


void Token( char *string ) {

   char *next;

   next = strcpy( string, strchr( LineBuf, ' ' ) + 1 );
   if(next == NULL) string[0] = '\0';
}

void Number( unsigned *num ) {

   char buf[80];

   strcpy( buf, strchr( LineBuf, ' ' ) + 1 );
   *num = atoi( buf );
}

Следующий модуль - SEND_COMM.C определяет функцию sendstr(), которая используется для передачи модему AT-команд.

// SEND_COMM.C

#include <time.h>

#include "common.h"
#include "modem.h"
#include "get_word.h"
#include "timer.h"
#include "tools.h"
#include "total.h"


// writestr
// обработка управляющих символов
static unsigned writestr(register char *s)  {

   register char last = '\0';
   int no_CR  = FALSE;
   unsigned char digit;

   while (*s) {
      if (last == '\\') {
         last = *s;

         switch (*s) {

         // задержка на две секунды

            case 'd':
            case 'D':
               sleep(2);
               break;

         // не передавать символ возврата каретки 
         // в конце строки

            case 'c':
            case 'C':
               no_CR = TRUE;
               break;

         // передать символ возврата каретки

            case 'r':
            case 'R':
               slowwrite("\r", 1);
               break;

         // передать символ возврата каретки

            case 'n':
            case 'N':
               slowwrite("\n", 1);
               break;

         // задержка 500 миллисекунд

            case 'p':
            case 'P':
               delay(500);
               break;

            default:
               slowwrite(s, 1);
               last = '\0';
         }
      }
      else if (*s != '\\')
         slowwrite(s, 1);

      else
         last = *s;
      s++;
   }

   return no_CR;
}



// sendstr
// послать строку в буфер передатчика,
// при этом обрабатываются следующие управляющие символы:

// d (D) - задержка на две секунды
// c (C) - не передавать символ возврата каретки в конце строки
// r (R) - передать символ возврата каретки
// n (N) - передать символ перевода строки
// p (P) - задержка 500 миллисекунд

void sendstr(char *str) {

   if(!equal(str,"")) {
      if(!writestr(str))
         slowwrite("\r", 1);
   }
   else
      slowwrite("\r", 1);

   return;
}

Модуль TOOLS.C содержит определения функций для работы с модулем UART.ASM.

// TOOLS.C
//

#include <assert.h>
#include <fcntl.h>
#include <io.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "common.h"
#include "tools.h"
#include "uart.h"
#include "timer.h"


unsigned port_active = FALSE;

static unsigned current_baud;

#define  STOPBIT  1

static unsigned hangup_needed = TRUE;


/**
*.Name         openline
*
*.Title        Устанавливает текущий COM-порт.
*
*.Descr        Эта функция устанавливает текущий асинхронный
*              порт, с которым в дальнейшем будет происходить обмен.
*
*.Proto        int openline(char *name, unsigned baud)
*
*.Params       char *name - номер COM-порта
*              unsigned baud - скорость обмена данными
*
*.Return       не используется
**/

int openline(char *name, unsigned baud) {

   int   value;

   // если порт уже активен, закрываем его

   if (port_active)
      closeline();

   if (sscanf(name, "COM%d", &value) != 1) {
      exit(-1);
   }

   // выбираем текущий COM-порт

   select_port(value);

   // сохраняем адрес старого обработчика прерываний COM-порта

   save_com();

   // устанавливаем новый обработчик прерываний COM-порта

   install_com();


   // программируем текущий COM-порт
   // скорость, связь через модем, не проверяем четность
   // один стоповый бит

   open_com(baud, 'M', 'N', STOPBIT);

   // запоминаем скорость

   current_baud = baud;

   // устанавливаем линию DTR в активное состояние
   // (компьютер готов к обмену данными)

   dtr_on();

   port_active = TRUE;

   return( 0 );
}


/**
*.Name         sread
*
*.Title        Читает символы из COM-порта.
*
*.Descr        Эта функция читает заданное число символов
*              из буфера приемника асинхронного порта.
*
*.Proto        unsigned sread(char *buffer, unsigned wanted, unsigned timeout)
*
*.Params       char *buffer - указатель на буфер в который будут записаны символы
*                             из буфера приемника
*              unsigned wanted - число символов, которое надо прочитать
*                                из буфера приемника
*              unsigned timeout - время, отведенное на чтение символов
*
*.Return       количество символов, прочитанных  из буфера приемника
**/

unsigned  sread(char *buffer, unsigned wanted, unsigned timeout) {

   time_t start;
   hangup_needed = TRUE;

   // определяем начальное время

   start = time(nil(time_t));

   for(;;) {

      unsigned int pending;

      // определяем число символов в буфере приемника

      pending = r_count_pending();

      // если в буфере есть необходимое количество символов

      if (pending >= wanted) {

         unsigned int i;

         // считываем из буфера нужное число символов

         for (i = 0; i < wanted; i++)
            *buffer++ = (char) receive_com();

         return pending;
      }

      // если в буфере приемника меньше символов, чем заказано
      // для чтения, проверяем, не иcтекло ли отведенное для чтения
      // время

      else {

         time_t   now     = time(nil(time_t));
         time_t   elapsed = now - start;

         delay(0);

         if (elapsed >= (long) timeout)
            return pending;
      }
   }
}


/**
*.Name         swrite
*
*.Title        Запись символов в COM-порт.
*
*.Descr        Эта функция записывает заданное число символов
*              в буфер передатчика асинхронного порта.
*
*.Proto        int swrite(char *data, unsigned len)
*
*.Params       char *data - указатель на буфер данных
*              unsigned len - число символов, которое надо записать
*                                в буфер передатчика
*
*.Return       количество символов, записанных в буфер передатчика
*
*.Sample       comlib.c
**/

int swrite(char *data, unsigned int len) {

   unsigned int i;

   hangup_needed = TRUE;

   // записываем входные данные в буфер передатчика
   // асинхронного порта

   for (i = 0; i < len; i++)
      send_com(*data++);

   return len;
}


/**
*.Name         ssendbrk
*
*.Title        Передает сигнал BREAK удаленному модему.
*
*.Proto        void ssendbrk()
*
*.Params       Не используются.
*
*.Return       Не используется.
*
*.Sample       comlib.c
**/

void ssendbrk(void)  {

   break_com();
}


/**
*.Name         closeline
*
*.Title        Закрывает COM-порт.
*
*.Descr        Эта функция восстанавливает старые значения
*              векторов прерываний и запрещает прерывания
*              от COM-порта.
*
*.Proto        void closeline(void)
*
*.Params       Не используются.
*
*.Return       Не используется.
*
*.Sample       comlib.c
**/

void closeline(void) {

   int far *stats;

   port_active = FALSE;

   // отменяем сигнал DTR

   dtr_off();

   // запрещаем прерывания от COM-порта

   close_com();

   // восстанавливаем векторы прерываний

   restore_com();
}

Вспомогательный модуль TIMER.C содержит определения функций sleep() и delay(). Эти функции используются в программе для организации временных задержек.

// TIMER.C
// определены функции sleep и delay, выполняющие временные задержки

#include <time.h>
#include <sys/timeb.h>

#include "timer.h"


/**
*.Name         sleep
*
*.Title        Приостанавливает выполнение программы
*
*.Descr        Эта функция приостанавливает выполнение
*              программы на заданное число секунд.
*
*.Proto        void sleep(time_t interval)
*
*.Params       time_t interval - время задержки в секундах
*
*.Return       не используется
*
*.Sample       timer.c
**/

void sleep(time_t interval) {

   time_t start;

   start = time((time_t *)NULL);

   // ожидаем, пока пройдет time_t секунд

   while ((time((time_t *)NULL) - start) < interval)
      delay(1000);
}



/**
*.Name         delay
*
*.Title        Приостанавливает выполнение программы
*
*.Descr        Эта функция приостанавливает выполнение
*              программы на заданное число миллисекунд.
*
*.Proto        void delay(int milliseconds)
*
*.Params       time_t interval - время задержки в миллисекундах
*
*.Return       не используется
*
*.Sample       timer.c
**/

void   delay   (int milliseconds) {

   struct timeb t;
   time_t seconds;
   unsigned last;

   if (milliseconds == 0)
      return;

   // определяем текущее время

   ftime(&t);

   last = t.millitm;
   seconds = t.time;

   // ожидаем milliseconds миллисекунд

   while( milliseconds > 0) {

      int count;

      // задержка
      for ( count = 0; count < 2000; count ++);

      // определяем текущее время

      ftime(&t);

      if (t.time == seconds)
         milliseconds -= (t.millitm - last);

      else
         milliseconds -= 1000 * (int) (t.time - seconds) -
                                      (last - t.millitm);

      last = t.millitm;
      seconds = t.time;
   }
}

Модуль UART.ASM - это основной модуль программы CHAT. Он содержит обработчик прерываний от COM-порта и функции низкого уровня для работы с ним.

Обработчик прерываний имеет два буфера - буфер приемника и буфер передатчика. Через эти буферы осуществляется обмен данными между программой и обработчиком прерываний. Буферы выполнены в виде очереди.

;
; UART.ASM
; модуль управления модемом и COM-портом нижнего уровня
;

;  определяем размеры буфера приемника и передатчика

R_SIZE   EQU   2048     ; размер приемного буфера
S_SIZE   EQU   500      ; размер буфера передатчика


; номера обработчиков прерываний

INT_COM1 EQU   0Ch      ; COM1
INT_COM2 EQU   0Bh      ; COM2
INT_COM3 EQU   0Ch      ; COM3
INT_COM4 EQU   0Bh      ; COM4


; порты контроллера прерываний 8259

OCR   EQU   20H      ; управляющий регистр 8259
IMR   EQU   21H      ; регистр маски прерываний 8259


; константы для управления контроллером прерываний

E_IRQ4   EQU   00010000B
D_IRQ4   EQU   11101111B
EOI4     EQU   01100100B

E_IRQ3   EQU   00001000B
D_IRQ3   EQU   11110111B
EOI3     EQU   01100011B

;
; область переменных BIOS
;

; адреса базовых регистров последовательных асинхронных адаптеров

BIOS_VAR  SEGMENT AT 40H

   rs232_base DW  4 DUP(?)

BIOS_VAR  ENDS


;
; таблица для каждого COM-порта
;

SP_TAB      STRUC

   port     DB ?  ; 1, 2, 3 или 4


; параметры для этого уровня прерываний

   int_com  DB ?  ; номер прерывания
   e_irq    DB ?
   d_irq    DB ?
   eoi      DB ?


; обработчики прерываний для этого уровня

   int_hndlr   DW ?  ; смещение обработчика прерываний
   old_com_off DW ?  ; смещение старого обработчика прерываний
   old_com_seg DW ?  ; сегмент старого обработчика прерываний

; параметры COM-порта

   installed      DB ?  ; установлен ли порт на этом компьютере? (1=да,0=нет)
   baud_rate      DW ?
   device_conn    DB ?  ; M(Модем), D(Нуль-модем)
   parity         DB ?  ; N(ONE), O(DD), E(VEN), S(PACE), M(ARK)
   stop_bits      DB ?  ; 1, 2

; счетчики ошибок

   error_block DW 8 DUP(?)

; порты 8250

   DATREG   DW ?  ; регистр данных
   IER      DW ?  ; регистр управления прерываниями
   IIR      DW ?  ; регистр идентификации прерывания
   LCR      DW ?  ; регистр управления линией
   MCR      DW ?  ; регистр управления модемом
   LSR      DW ?  ; регистр состояния линии
   MSR      DW ?  ; регистр состояния модема

   DLL      EQU   DATREG ; младший регистр делителя
   DLH      EQU   IER    ; старший регистр делителя

; указатели буферов FIFO

   ; индекс первого символа в буфере передатчика

   start_s_data DW ?

   ; индекс первого свободного элемента буфера передатчика

   end_s_data   DW ?

   ; индекс первого символа в буфере приемника

   start_r_data DW ?

   ; индекс первого свободного элемента буфера приемника

   end_r_data   DW ?


; счетчики количества символов в буферах

   size_s_data  DW ?  ; число символов в буфере передатчика
   size_r_data  DW ?  ; число символов в буфере приемника


; буфера

   send_buf       DB    S_SIZE DUP(?)  ; буфер передатчика
   reciave_buf    DB    R_SIZE DUP(?)  ; буфер приемника

SP_TAB      ENDS


EFRAME   EQU   error_block+6  ; ошибка синхронизации
EPARITY  EQU   error_block+8  ; ошибка четности
EOVFLOW  EQU   error_block    ; произошло переполнение буфера
EDSR     EQU   error_block+12 ; модем не ответил сигналом DSR

EOVRUN   EQU   error_block+2  ; ошибка переполнения
EBREAK   EQU   error_block+4  ; обнаружен запрос на прерывание

EXMIT    EQU   error_block+10 ; ошибка при передаче
ECTS     EQU   error_block+14 ; модем не ответил сигналом CTS



;
;
DGROUP   GROUP _DATA

_DATA SEGMENT public 'DATA'


   DIV50    DW    2304

; текущий номер области данных порта

   CURRENT_AREA   DW AREA1

; область данных для каждого порта

   AREA1 SP_TAB   <1,INT_COM1,E_IRQ4,D_IRQ4,EOI4>     ; область данных COM1
   AREA2 SP_TAB   <2,INT_COM2,E_IRQ3,D_IRQ3,EOI3>     ; область данных COM2
   AREA3 SP_TAB   <3,INT_COM3,E_IRQ4,D_IRQ4,EOI4>     ; область данных COM3
   AREA4 SP_TAB   <4,INT_COM4,E_IRQ3,D_IRQ3,EOI3>     ; область данных COM4

_DATA  ENDS



COM_TEXT    SEGMENT PARA public 'CODE'

   ASSUME   cs:COM_TEXT,ds:DGROUP,es:NOTHING

   public   _select_port
   public   _save_com
   public   _install_com
   public   _restore_com
   public   _open_com
   public   _close_com
   public   _dtr_on
   public   _dtr_off
   public   _r_count
   public   _s_count
   public   _receive_com
   public   _send_com
   public   _break_com
   public   _com_errors



;
; выбор активного порта
;  [bp+6] - номер порта

_select_port   PROC FAR

   push  bp
   mov   bp, sp
   mov   ax, [bp+6]  ;получаем в ax аргумент функции

   cmp   al,1     ; установлен порт 1?
   je    port1    ; да

   cmp   al,2     ; установлен порт 2?
   je    port2    ; да

   cmp   al,3     ; установлен порт 3?
   je    port3    ; да

   cmp   al,4     ; установлен порт 4?
   je    port4    ; да

   jmp set_carrent_area


port1:
   mov   ax,OFFSET DGROUP:AREA1  ; выбираем область данных COM1
   jmp   short set_carrent_area

port2:
   mov   ax,OFFSET DGROUP:AREA2  ; выбираем область данных COM2
   jmp   short set_carrent_area

port3:
   mov   ax,OFFSET DGROUP:AREA3  ; выбираем область данных COM3
   jmp   short set_carrent_area

port4:
   mov   ax,OFFSET DGROUP:AREA4  ; выбираем область данных COM4

set_carrent_area:

   ; записываем в переменной CURRENT_AREA смещение
   ; текущей области данных

   mov   CURRENT_AREA,ax

   mov sp,bp
   pop bp
   ret

_select_port   ENDP


;
; сохранение текущего вектора прерывания COM-порта
;
_save_com   PROC FAR

   push bp
   mov bp,sp
   push si

   ; записываем в si указатель на текущую область данных

   mov   si,CURRENT_AREA
   push  es

   mov   AREA1.int_hndlr,OFFSET int_hndlr1
   mov   AREA2.int_hndlr,OFFSET int_hndlr2
   mov   AREA3.int_hndlr,OFFSET int_hndlr3
   mov   AREA4.int_hndlr,OFFSET int_hndlr4

; сохраняем старый вектор прерывания

   mov   ah,35H
   mov   al,int_com[si] ; номер прерывания
   int   21h

; записываем в переменные old_com_off и old_com_seg
; соответственно сегмент и смещение старого вектора прерывания

   mov   old_com_off[si],bx
   mov   bx,es
   mov   old_com_seg[si],bx

   pop   es

   pop si
   mov sp,bp
   pop bp

   ret

_save_com   ENDP


;
; install_com: установить активный порт
;
; возвращает в регистре ax - 1 при успешной установке
; и 0 в случае ошибки
;
_install_com   PROC FAR

   push bp
   mov bp,sp
   push si

   mov   si,CURRENT_AREA

   push  es

   cmp   installed[si],1
   jne   go_install
   jmp   alredy_ok

; очищаем счетчики ошибок

go_install:

   mov   WORD PTR EOVFLOW[si],0  ; переполнение буфера передатчика
   mov   WORD PTR EOVRUN[si],0   ; ошибка переполнения при приеме
   mov   WORD PTR EBREAK[si],0   ; обнаружен запрос на прерывание
   mov   WORD PTR EFRAME[si],0   ; ошибка синхронизации
   mov   WORD PTR EPARITY[si],0  ; ошибка четности
   mov   WORD PTR EXMIT[si],0    ; ошибка при передаче
   mov   WORD PTR EDSR[si],0     ; не получен сигнал DSR
   mov   WORD PTR ECTS[si],0     ; не получен сигнал CTS


; определяем базовый адрес используемого COM порта

   mov   bx,BIOS_VAR
   mov   es,bx

   ASSUME   es:BIOS_VAR

   cmp   port[si],1  ; порт 1?
   je    adr_3F8

   cmp   port[si],2  ; порт 2?
   je    adr_2F8

   cmp   port[si],3  ; порт 3?
   je    adr_3E8

   cmp   port[si],4  ; порт 4?
   je    adr_2E8

   int   20H

adr_3F8:
   mov   ax,3F8H
   jmp   cmp_bios


adr_2F8:
   mov ax,2F8H
   jmp   cmp_bios

adr_3E8:
   cmp   rs232_base+4,0
   je    adr_3E8_A

   mov   ax,rs232_base+4
   jmp   cmp_bios

adr_3E8_A:
   mov   ax,3E8H
   mov   rs232_base+4,ax
   jmp   cmp_bios


adr_2E8:
   cmp   rs232_base+6,0
   je    adr_2E8_A

   mov   ax,rs232_base+6
   jmp   cmp_bios

adr_2E8_A:
   mov   ax,2E8H
   mov rs232_base+6,ax

; проверяем, определена ли соответствующая переменная
; BIOS

cmp_bios:
   cmp   ax,rs232_base
   je    set_reg_adr

   cmp   ax,rs232_base+2
   je    set_reg_adr

   cmp   ax,rs232_base+4
   je    set_reg_adr

   cmp   ax,rs232_base+6
   jne   bad_exit


set_reg_adr:

   mov   bx,DATREG
   mov   cx,7

set_next_reg_adr:

   mov   WORD PTR [si][bx],ax
   inc   ax
   add   bx,2
   loop  set_next_reg_adr

; устанавливаем вектор прерывания на наш обработчик

   mov   AREA1.int_hndlr,OFFSET int_hndlr1
   mov   AREA2.int_hndlr,OFFSET int_hndlr2
   mov   AREA3.int_hndlr,OFFSET int_hndlr3
   mov   AREA4.int_hndlr,OFFSET int_hndlr4

   mov   ah,25H
   mov   al,int_com[si] ; номер прерывания
   mov   dx,OFFSET DGROUP:int_hndlr[si]
   push  ds
   push  cs
   pop   ds
   int   21h

   pop   ds

   ; поднимаем флаг - порт установлен

alredy_ok:
   mov   installed[si],1
   pop   es

   ; возвращаем 1

   mov ax,1
   pop si
   mov sp,bp
   pop bp
   ret

; порт не установлен

bad_exit:
   mov installed[si],0
   pop   es

   ; возвращаем 0

   mov ax,0
   pop si
   mov sp,bp
   pop bp
   ret

_install_com   ENDP


;
; восстановление векторов прерываний
;
_restore_com   PROC FAR

   push bp
   mov bp,sp
   push si

; отмечаем COM-порт как неактивный

   mov   si,CURRENT_AREA
   mov   installed[si],0

; восстанавливаем вектор прерывания

   mov   ah,25H
   mov   al,int_com[si]
   mov   dx,old_com_off[si]
   mov   bx,old_com_seg[si]
   push  ds
   mov   ds,bx
   int   21h

   pop   ds

   pop   si
   mov   sp,bp
   pop   bp
   ret

_restore_com   ENDP


;
; открыть COM порт
;
; сброс буферов передатчика и приемника,
; инициализация регистров UART 8250
; разрешение прерываний от UART 8250
; (программирование контроллера прерываний)
;
; [bp+6] = скорость обмена
; [bp+8] = способ соединения -  M(Модем), D(Нуль-модем)
; [bp+10] = четность - N(ONE), O(DD), E(VEN), S(PACE), M(ARK)
; [bp+12] = число стоповых бит 1, 2
;
_open_com   PROC FAR

   push bp
   mov bp,sp
   push si

   mov   si,CURRENT_AREA

 ; запрещаем прерывания

   cli
   mov   ax,[bp+6]
   mov   baud_rate[si],ax
   mov   bh,[bp+8]
   mov   device_conn[si],bh
   mov   bl,[bp+10]
   mov   parity[si],bl
   mov   ch,[bp+12]
   mov   stop_bits[si],CH


; сбрасываем буферы и указатели

   mov   start_s_data[si],0
   mov   end_s_data[si],0
   mov   start_r_data[si],0
   mov   end_r_data[si],0
   mov   size_s_data[si],0
   mov   size_r_data[si],0

   ; проверяем, установлен ли уже обработчик прерываний

   test  installed[si],1
   jnz   reset_uart
   jmp   exit_open

reset_uart:

; устанавливаем регистры UART 8250

; сбрасываем регистр управления модемом

   mov   al,0
   mov   dx,MCR[si]
   out   dx,al
   jmp   $+2

   ; сбрасываем регистр состояния линии

   mov   dx,LSR[si]
   in    al,dx
   jmp   $+2

   ; сбрасываем регистр данных

   mov   dx,DATREG[si]
   in    al,dx
   jmp   $+2

   ; сбрасываем регистр состояния модема

   mov   dx,MSR[si]
   in    al,dx

   ; определяем делитель частоты тактового генератора

   mov   ax,50
   mul   DIV50
   div   baud_rate[si]
   mov   bx,ax

   ; переключаем регистр данных и регистр управления прерываниями
   ; для ввода делителя частоты тактового генератора

   mov   dx,LCR[si]
   mov   al,80H
   out   dx,al
   jmp   $+2

   ; вводим младший байт делителя частоты тактового генератора

   mov   dx,WORD PTR DLL[si]
   mov   al,bl
   out   dx,al
   jmp   $+2

   ; вводим старший байт делителя частоты тактового генератора

   mov   dx,WORD PTR DLH[si]
   mov   al,bh
   out   dx,al
   jmp   $+2

; определяем четность и число стоповых битов

   mov   al,03H
   cmp   parity[si],'O'
   jne   next1
   mov   al,0ah
   jmp   short next3

next1:
   cmp   parity[si],'E'
   jne   next2
   mov   al,1ah
   jmp   short next3

next2:
   cmp   parity[si],'M'
   jne   next3
   mov   al,2ah

next3:
   test  stop_bits[si],2
   jz stop1
   or al,4

stop1:
   mov   dx,LCR[si]
   out   dx,al

; разрешаем прерывания для 8259 и 8250

   ; устанавливаем регистр маски прерываний так, чтобы
   ; разрешить прерывания от асинхронного порта

   in    al,IMR
   and   al,d_irq[si]
   out   IMR,al

   ; разрешаем генерацию прерываний при готовности принимаемых
   ; данных, по состоянию BREAK и по ошибке

   mov   dx,IER[si]
   mov   al,5
   out   dx,al
   jmp   $+2

   ; устанавливаем DTR, RTS, OUT2

   mov   dx,MCR[si]
   mov   al,0bh
   out   dx,al

exit_open:

   sti

   pop si
   mov sp,bp
   pop bp
   ret

_open_com   ENDP


;
; запрещаем прерывания от асинхронного порта
;
_close_com   PROC FAR
   push bp
   mov bp,sp
   push si

   mov   si,CURRENT_AREA
   test  installed[si],1
   jz    exit_close

; запрещаем прерывания UART 8250

   mov   dx,IER[si]
   mov   al,0
   out   dx,al

; маскируем прерывания от UART

   mov   dx,IMR
   in    al,dx
   or    al,e_irq[si]
   jmp   $+2
   out   dx,al

exit_close:

   pop si
   mov sp,bp
   pop bp
   ret

_close_com   ENDP


;
; снимаем сигнал DTR
;

_dtr_off   PROC FAR

   push bp
   mov bp,sp
   push si

   pushf
   push  ax
   push  dx
   push  si

   mov   si,CURRENT_AREA
   test  installed[si],1
   jz    exit_dtr_off

   ; устанавливаем регистр управления модемом,
   ; сбрасываем сигналы DTR и RTS

   mov   dx,MCR[si]
   mov   al,08H
   out   dx,al

exit_dtr_off:

   pop   si
   pop   dx
   pop   ax
   popf

   pop si
   mov sp,bp
   pop bp

   ret

_dtr_off   ENDP


;
; устанавливаем сигнал DTR
;

_dtr_on PROC FAR

   push bp
   mov bp,sp
   push si

   pushf
   push  ax
   push  dx
   push  si
   mov   si,CURRENT_AREA
   test  installed[si],1
   jz    exit_dtr_on

   ; устанавливаем регистр управления модемом,
   ; устанавливаем сигналы DTR, RTS, OUT2

   mov   dx,MCR[si]
   mov   al,0bh
   out   dx,al

exit_dtr_on:

   pop   si
   pop   dx
   pop   ax
   popf

   pop si
   mov sp,bp
   pop bp

   ret

_dtr_on ENDP


;
; возвращаем в регистре ax число байт в регистре приемника,
; а в регистре dx общий размер буфера приемника
;

_r_count   PROC FAR
   push bp
   mov bp,sp
   push si

   pushf
   push  si
   mov   si,CURRENT_AREA

   mov   ax,0
   mov   dx,R_SIZE

   test  installed[si],1
   jz    exit_r_count

   ; записываем в регистр ax число символов в буфере приемника

   mov   ax,size_r_data[si]

exit_r_count:

   pop   si
   popf

   pop si
   mov sp,bp
   pop bp
   ret

_r_count   ENDP


;
; получаем очередной символ из буфера приемника,
; полученный символ удаляется из буфера
;

_receive_com   PROC FAR

   push bp
   mov bp,sp
   push si

   pushf
   push  bx
   push  si

   mov   si,CURRENT_AREA
   mov   ax,-1

   test  installed[si],1
   jz    exit_receive_com

   ; возвращаемся, если буфер приемника пуст

   cmp   size_r_data[si],0
   je    exit_receive_com

   mov   ah,0
   mov   bx,start_r_data[si]
   mov   al,reciave_buf[si][bx]


   cmp   parity[si],'N'
   je no_parity

   ; если производится поверка на четность, то маскируем старший бит

   and   al,7FH

no_parity:

   inc   bx
   cmp   bx,R_SIZE
   jb    rec_ptr_no_max
   mov   bx,0

rec_ptr_no_max:

   mov   start_r_data[si],bx
   dec   size_r_data[si]

exit_receive_com:

   pop   si
   pop   bx
   popf

   pop si
   mov sp,bp
   pop bp

   ret

_receive_com   ENDP



;
; функция возвращает в регистре ax число свободных байт в
; буфере передатчика, а в регистре dx общий размер буфера передатчика
;
_s_count   PROC FAR

   push bp
   mov bp,sp
   push si

   pushf
   push  si

   mov   si,CURRENT_AREA
   mov   ax,0
   mov   dx,S_SIZE

   test  installed[si],1
   jz    exit_s_count

   mov   ax,S_SIZE
   sub   ax,size_s_data[si]

exit_s_count:

   pop   si
   popf

   pop si
   mov sp,bp
   pop bp

   ret

_s_count   ENDP



;
; поместить символ в буфер передатчика
;  [bp+6] - символ
;
_send_com   PROC FAR

   push bp
   mov bp,sp
   push si

   mov al,[bp+6]

   pushf
   push  ax
   push  bx
   push  dx
   push  si

   mov   si,CURRENT_AREA

   test  installed[si],1
   jz    exit_send_com

   cmp   size_s_data[si],S_SIZE
   jl    no_s_EOVFLOW

   ; произошло переполнение буфера передатчика

   inc   WORD PTR EOVFLOW[si]
   jmp   short exit_send_com

no_s_EOVFLOW:

   mov   bx,end_s_data[si]
   mov   send_buf[si][bx],al
   inc   bx
   cmp   bx,S_SIZE
   jl    no_send_ptr_max
   mov   bx,0

no_send_ptr_max:

   mov   end_s_data[si],bx
   inc   size_s_data[si]

   ; считываем регистр управления прерываниями

   mov   dx,IER[si]
   in    al,dx

   ; завершаем функцию, если разрешены прерывания после передачи байта

   test  al,2
   jnz   exit_send_com

   ; разрешаем прерывания после передачи байта, после приема байта,
   ; при обнаружении состояния BREAK и при возникновении ошибки

   mov   al,7
   out   dx,al

exit_send_com:

   pop   si
   pop   dx
   pop   bx
   pop   ax
   popf

   pop si
   mov sp,bp
   pop bp

   ret

_send_com   ENDP


;
; S_local
;
_send_local PROC FAR
   push bp
   mov bp,sp
   push si

   mov al,[bp+6]
   pushf
   push  ax
   push  bx
   push  si
   mov   si,CURRENT_AREA
   test  installed[si],1
   jz    SLX

   cli
   cmp   size_r_data[si],R_SIZE
   jb    L13A
   inc   WORD PTR EOVFLOW[si]
   jmp   short L14

L13A:
   mov   bx,end_r_data[si]
   mov   reciave_buf[si][bx],al
   inc   bx
   cmp   bx,R_SIZE
   jl    L13
   mov   bx,0

L13:
   mov   end_r_data[si],bx
   inc   size_r_data[si]

L14:
   sti

SLX:
   pop   si
   pop   bx
   pop   ax
   popf
   pop si
   mov sp,bp
   pop bp
   ret
_send_local ENDP



;
; передаем удаленному модему сигнал BREAK
;

_break_com   PROC FAR

   push bp
   mov bp,sp
   push si

   pushf
   push  ax
   push  cx
   push  dx

   mov   si,CURRENT_AREA
   test  installed[si],1
   jz    exit_break_com

   ; передаем сигнал BREAK

   mov   dx,LCR[si]
   in    al,dx
   jmp   $+2
   or    al,40h
   out   dx,al

   mov   cx,0C000h

do_BREAK:
   loop   do_BREAK

   and   al,0BFh
   out   dx,al

exit_break_com:

   pop   dx
   pop   cx
   pop   ax
   popf

   pop si
   mov sp,bp
   pop bp

   ret

_break_com   ENDP



;
; возвращаем в dx:ax указатель на счетчики ошибок
;
_com_errors PROC FAR

   push  bp

   mov   bp,sp
   mov   ax,OFFSET DGROUP:CURRENT_AREA
   add   ax,error_block
   mov   dx,ds
   mov   sp,bp

   pop   bp

   ret
_com_errors ENDP


;
; заполняем счетчики ошибок
;

set_err   PROC  NEAR

   test  al,2
   jz    test1
   inc   WORD PTR EOVRUN[si]

test1:
   test  al,4
   jz    test2
   inc   WORD PTR EPARITY[si]

test2:
   test  al,8
   jz    test3
   inc   WORD PTR EFRAME[si]

test3:
   test  al,16
   jz    exit_set_err
   inc   WORD PTR EBREAK[si]

exit_set_err:

   ret

set_err   ENDP


;
; протокол модема для передачи данных
;

modem_protocol PROC NEAR

   cmp   device_conn[si],'M'
   jne   no_modem

; устанавливаем сигналы DTR, RTS и OUT2

   mov   dx,MCR[si]
   mov   al,00001011B
   out   dx,al
   jmp   $+2

; ожидаем, пока модем ответит о готовности сигналом DSR

   mov   cx,1000
   mov   dx,MSR[si]

wait_dsr:
   in    al,dx
   test  al,20H
   jnz   test_cts
   loop  wait_dsr

   ; модем не ответил сигналом DSR

   inc   WORD PTR EDSR[si]
   jmp   short no_modem

test_cts:

; ожидаем, пока модем ответит о готовности сигналом CTS

   mov   cx,1000

wait_cts:
   in    al,dx
   test  al,10H
   jnz   test_lcr
   loop  wait_cts

   ; модем не ответил сигналом CTS

   inc   WORD PTR ECTS[si]

test_lcr:
no_modem:

   ; проверяем, пуст ли регистр хранения передатчика

   mov   dx,LSR[si]
   in    al,dx
   test  al,20H
   jnz   s_reg_empty

   ; ошибка при передаче

   inc   WORD PTR EXMIT[si]

s_reg_empty:

   ret

modem_protocol ENDP


;
; обработчик прерываний от COM1
;

int_hndlr1 PROC  FAR

   push  si
   mov   si,OFFSET DGROUP:AREA1
   jmp   short handle_int

;
; обработчик прерываний от COM2
;

int_hndlr2 PROC  FAR

   push  si
   mov   si,OFFSET DGROUP:AREA2
   jmp   short handle_int

;
; обработчик прерываний от COM3
;

int_hndlr3 PROC  FAR

   push  si
   mov   si,OFFSET DGROUP:AREA3
   jmp   short handle_int

;
; обработчик прерываний от COM4
;

int_hndlr4 PROC  FAR

   push  si
   mov   si,OFFSET DGROUP:AREA4


;
; обработчик прерываний
;

handle_int:

   push  ax
   push  bx
   push  cx
   push  dx
   push  bp
   push  di
   push  ds
   push  es

   mov   ax,DGROUP
   mov   ds,ax

next_pr:

; передаем контроллеру прерываний команду конца обработки
; прерывания

   mov   dx,OCR
   mov   al,eoi[si]
   out   dx,al

next_inter:

   ; считываем значение регистра идентификации прерывания

   mov   dx,IIR[si]
   in    al,dx

   ; определяем причину прерывания

   ; данные приняты и доступны для чтения

   cmp   al,4
   je    RX_int

   ; буфер передатчика пуст

   cmp   al,2
   je    TX_int

   ; изменилось состояние линий CTS, RI, DCD, DSR

   cmp   al,0
   je    MSTAT_int

   ; обнаружено сотояние BREAK или произошла ошибка

   cmp   al,6
   je    LSTAT_int

   ; завершаем обработку прерываний

   jmp   FAR PTR exit_handler

LSTAT_int:

   ; считываем регистр сотояния линии и вызываем функцию
   ; set_err, которая определит причину прерывания

   mov   dx,LSR[si]
   in    al,dx
   call  set_err
   jmp   next_inter

MSTAT_int:

   ; считываем регистр состояния модема

   mov   dx,MSR[si]
   in    al,dx
   jmp   next_inter

TX_int:

   ; смотрим, есть ли данные для передачи модему

   cmp   size_s_data[si],0

   jg    have_data_for_send

; если буфер передатчика пуст, переустанавливаем регистр
; управления прерываниями

   mov   dx,IER[si]
   mov   al,5
   out   dx,al
   jmp   next_inter

have_data_for_send:

   ; передаем символ модему в соответствии с состоянием
   ; линий RS-232-С

   call  modem_protocol

   ; передаем очередной символ из буфера передатчика

   mov   bx,start_s_data[si]
   mov   al,send_buf[si][bx]
   mov   dx,DATREG[si]
   out   dx,al

   inc   bx
   cmp   bx,S_SIZE
   jb    ptr_no_max
   mov   bx,0

ptr_no_max:

   mov   start_s_data[si],bx
   dec   size_s_data[si]
   jmp   next_inter


; данные приняты и доступны для чтения
RX_int:

   ; считываем принятый байт из регистра данных UART

   mov   dx,DATREG[si]
   in    al,dx

   cmp   size_r_data[si],R_SIZE
   jl    no_r_EOVFLOW

   ; буфер приемника переполнен, увеличиваем соответствующий
   ; счетчик ошибок

   inc   WORD PTR EOVFLOW[si]
   jmp   next_inter

no_r_EOVFLOW:

   mov   bx,end_r_data[si]
   mov   reciave_buf[si][bx],al
   inc   size_r_data[si]
   inc   bx

   cmp   bx,R_SIZE
   jb    no_max_r_ptr
   mov   bx,0

no_max_r_ptr:

   mov   end_r_data[si],bx

   jmp   next_inter

exit_handler:

   mov   al,20h
   out   20h,al

   pop   es
   pop   ds
   pop   di
   pop   bp
   pop   dx
   pop   cx
   pop   bx
   pop   ax

   pop   si
   iret

int_hndlr4 ENDP
int_hndlr3 ENDP
int_hndlr2 ENDP
int_hndlr1 ENDP

COM_TEXT ENDS

END

Теперь мы приведем исходные тексты включаемых файлов, используемых нашей программой.

// COMMON.H

#define equal(a,b)               (!strcmp(a,b))
#define equali(a,b)              (!stricmp(a,b))
#define equalni(a,b,n)           (!strnicmp(a,b,n))
#define equaln(a,b,n)            (!strncmp(a,b,n))

#define nil(type)   ((type *)NULL)


#define      boolean      unsigned
#define      TRUE      1
#define      FALSE      0

Файл CONF.H

#define OPERATOR(x) !strncmp(LineBuf,(x),strlen((x)))
#define STRIP(x) (x)[strlen(x)-1] = 0;

Файл GET_WORD.H

unsigned   expectstr(char *Search, unsigned int Timeout);
void      sendstr(char *str);

Файл MOD_LINK.H

#define  CONN_INITIALIZE   1
#define  CONN_CALLUP       2
#define  CONN_HOTMODEM     3

#define  CONN_ANSWER       4

#define  CONN_LOGIN        5
#define  CONN_HOTLOGIN     6

#define  CONN_PROTOCOL     7
#define  CONN_SERVER       8
#define  CONN_CLIENT       9
#define  CONN_TERMINATE    10
#define  CONN_DROPLINE     11
#define  CONN_EXIT         12

#define  CONN_STATE        char

Файл MODEM.H

int call( void );
int answer( void );

void slowwrite( char *s, int len);
void shutdown( void );

extern char *device;

Файл TIMER.H

void sleep(time_t interval);
void delay(int milliseconds);

Файл TOOLS.H

extern unsigned port_active;

int openline(char *name, unsigned baud);

unsigned int sread(char *buffer,
                          unsigned int wanted,
                          unsigned int timeout);

int swrite(char *data, unsigned int len);

void ssendbrk(unsigned int duration);

void closeline(void);

Файл TOTAL.H

#define WHITESPACE " \t\n\r"

#define equal(a,b)               (!strcmp(a,b))
#define equali(a,b)              (!stricmp(a,b))
#define equalni(a,b,n)           (!strnicmp(a,b,n))
#define equaln(a,b,n)            (!strncmp(a,b,n))


#define nil(type)               ((type *)NULL)

Файл UART.H

/**
*.Name         select_port
*
*.Title        Выбираем используемый COM-порт
*
*.Descr        Эта функция определяет, с каким COM-портом
*              мы в дальнейшем будем работать.
*
*.Proto        void far select_port(int port)
*
*.Params       int port - номер порта (COM1..COM4)
*
*.Return       не используется
*
*.Sample       comm.asm
**/

void far select_port(int);


/**
*.Name         save_com
*
*.Title        Сохраняем старый вектор прерывания COM-порта
*
*.Descr        Эта функция сохраняет адрес старого обработчика
*              прерываний от COM-порта.
*
*.Proto        void far save_com(void)
*
*.Params       не используется
*
*.Return       не используется
**/

void far save_com(void);


/**
*.Name         restore_com
*
*.Title        Восстанавливаем старый вектор прерывания COM-порта
*
*.Descr        Эта функция восстанавливает адрес старого обработчика
*              прерываний от COM-порта.
*
*.Proto        void far restore_com(void)
*
*.Params       не используется
*
*.Return       не используется
**/
void far restore_com(void);


/**
*.Name         install_com
*
*.Title        Устанавливаем свой обработчик прерывания COM-порта
*
*.Descr        Эта функция устанавливает новый обработчик
*              прерываний от COM-порта.
*
*.Proto        int far install_com(void)
*
*.Params       не используется
*
*.Return       возвращает 1 при успешной установке
*              и 0 в случае ошибки
**/
int far install_com(void);


/**
*.Name         install_com
*
*.Title        Устанавливаем свой обработчик прерывания COM-порта
*
*.Descr        Эта функция устанавливает новый обработчик
*              прерываний от COM-порта.
*
*.Proto        void far open_com( int baud, int device,
*                                 int parity, int stop_bits )
*
*.Params       int baud       - скорость обмена
*              int device     - тип устройства связи: M - модем
*                               D - нуль-модем
*              int parity     - проверка на четность: N - нет проверки
*                               на четность, O - проверка по нечетности,
*                               E - проверка по четности, S - Space,
*                               M - Mark
*              int stop_bits  - количество стоповых бит
*
*.Return       не используется
**/
void far open_com( int, int, int, int );


/**
*.Name         close_com
*
*.Title        Запрещаем прерывания от асинхронного порта COM-порта
*
*.Descr        Эта функция запрещает прерывания от COM-порта
*
*.Proto        void far close_com(void)
*
*.Params       не используется
*
*.Return       не используется
**/
void far close_com(void);  /* close com port */


/**
*.Name         dtr_off
*
*.Title        Сбрасываем сигнал DTR
*
*.Proto        void far dtr_off(void)
*
*.Params       не используется
*
*.Return       не используется
**/
void far dtr_off(void);


/**
*.Name         dtr_on
*
*.Title        Устанавливаем сигнал DTR
*
*.Proto        void far dtr_on(void)
*
*.Params       не используется
*
*.Return       не используется
**/
void far dtr_on(void);


/**
*.Name         r_count
*
*.Title        Определяем состояние буфера приемника
*
*.Proto        long far r_count(void)
*
*.Params       не используется
*
*.Return       в младшем байте - число байт в буфере приемника,
*              а в старшем - общий размер буфера приемника
*
**/
long far r_count(void);

// макроопределение r_count_size возвращает
// общий размер буфера приемника

#define r_count_size() ((int)(r_count() >> 16))

// макроопределение r_count_pending возвращает
// число байт в буфере приемника

#define r_count_pending() ((int)r_count())


/**
*.Name         receive_com
*
*.Title        Получить один символ из буфера приемника
*
*.Proto        int far receive_com(void)
*
*.Params       не используется
*
*.Return       очередной символ из буфера приемника
**/
int far receive_com(void);


/**
*.Name         s_count
*
*.Title        Определяем состояние буфера приемника
*
*.Proto        long far s_count(void)
*
*.Params       не используется
*
*.Return       в младшем байте - число свободных байт в буфере передатчика,
*              а в старшем - общий размер буфера передатчика
*
**/
long far s_count(void);

// макроопределение s_count_size возвращает
// общий размер буфера приемника

#define s_count_size() ((int)(s_count() >> 16))

// макроопределение s_count_pending возвращает
// число байт в буфере приемника

#define s_count_free() ((int)s_count())


/**
*.Name         send_com
*
*.Title        Передать один символ в буфер передатчика
*
*.Proto        void far send_com(int ch)
*
*.Params       int ch - передаваемый символ
*
*.Return       не используется
**/
void far send_com(int);


/**
*.Name         break_com
*
*.Title        Передать сигнал BREAK удаленному модему
*
*.Proto        void far break_com(void)
*
*.Params       не используется
*
*.Return       не используется
**/
void far break_com(void);

/**
*.Name         com_errors
*
*.Title        Возвращает указатель на массив счетчиков ошибок
*
*.Proto        int far *far com_errors(void)
*
*.Params       не используется
*
*.Return       не используется
**/
int far *far com_errors(void);


#define COM_EOVFLOW 0   // переполнен буфер
#define COM_EOVRUN  1   // ошибка переполнения при приеме
#define COM_EBREAK  2   // обнаружен запрос на прерывание
#define COM_EFRAME  3   // ошибка синхронизации
#define COM_EPARITY 4   // ошибка четности
#define COM_EXMIT   5   // ошибка при передаче
#define COM_EDSR    6   // не получен сигнал dsr
#define COM_ECTS    7   // не получен сигнал cts
[Назад] [Содеожание] [Дальше]