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

Модемы и факс-модемы. Программирование для MS-DOS и Windows.

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

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

6.4. Программа CHATINT

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

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

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

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

Листинг 6.1. Файл 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

Исходные тексты программы CHATINT включают в себя несколько модулей на языке Си и один модуль на языке ассемблера. Ниже перечислены названия исходных файлов программы:

Имена файловСодержит
CHATINT.CГлавная функция программы
MODEM.CПередача данных модему через COM-порт
TIMER.CРеализация временных задержек
CONF.CЧтение и обработка файла конфигурации
SEND_COMM.CПередача команд модему
TOOLS.CНабор функций для работы с модулем UART.ASM
UART.ASMОбработчик прерываний и процедуры низкого уровня

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

Теперь приведем сами исходные тексты программы. Основной модуль программы называется CHATINT.C (см. листинг 6.2). В зависимости от параметра программы этот модуль вызывает функцию Call - для вызова удаленного модема - или функцию Answer - для ответа на приходящие звонки.

Если программа CHATINT запущена без параметров, управление передается функции Hello. Функция Hello отображает на экране справочную информацию. Затем программа завершается.

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

Функции Call, Answer и Shutdown, выполняют все действия по программированию COM-порта, контроллера прерываний и модема. Данные функции определены в модуле MODEM.C. Исходные тексты модуля MODEM.C приведены в листинге 6.4.

Листинг 6.2. Файл CHATINT.C

//=======================================================
// Основной модуль коммуникационной программы
//=======================================================

#include 
#include 
#include 
#include 
#include 

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

//=======================================================
// Функция Hello
//=======================================================
void Hello(void) 
{
	printf("Неправильно задан параметр программы \n"
				"CHATINT n, где n = 1 режим вызова, "
				"n = 0 режим ответа\n");

	exit(0);
}

// Основная процедура
void main( int argc, char *argv[] ) {

	// Программа должна вызываться  параметром 1 или 0
	if( argc < 2 )
		Hello();

	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();
}

В файле CONF.C определена функция GetConfig, считывающая файл конфигурации SETUP.CFG и заполняющая соответствующими значениями глобальные переменные (см. листинг 6.3).

Функция GetConfig открывает файл конфигурации SETUP.CFG и начинает считывать его содержимое по одной строке. Каждое первое слово из строки сравнивается с ключевыми словами, приведенными в следующей таблице. В случае совпадения заполняется соответствующая глобальная переменная. Глобальные переменные определены в файле MODEM.C (см. листинг 6.4).

Ключевое словоГлобальная переменнаяОписание
DroplinedroplineКоманда модему для разрыва связи с удаленным модемом
InitializeinitializeКоманда инициализации модема
AutoAnswerautoanswerКоманда инициализации модема для работы в режиме автоответа на приходящие звонки
DialNumberdialNumberНомер удаленного модема
DialPrefixdialPrefixПрефикс для команды набора номера
DialSuffixdialSuffixСуффикс для команды набора номера
DevicedeviceНомер COM-порта, к которому подключен модем
CharDelaychardelayЗадержка между отдельными символами, передаваемыми модему
DialTimeoutdialTimeoutИнтервал времени, за который модем должен набрать номер и установить связь с удаленным модемом
ModemTimeoutmodemTimeoutИнтервал времени, за который модем должен ответить на передаваемые ему команды
TimeoutAnsweranswerTimeoutВремя, отведенное на установку связи с удаленным модемом
SpeedspeedСкорость обмена данными через COM-порт

По достижении конца файла SETUP.CFG файл закрывается, и функция возвращает управление.

Листинг 6.3. Файл CONF.C

#include 
#include 
#include 
#include 

#include "conf.h"

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

	FILE *CfgFile;
	char LineBuf[256];

	// Глобальные переменные, заполняемые функцией GetConfig
	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;

//=============================================================
// Функция GetConfig
//=============================================================
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("AutoAnswer"))  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);
}

//=====================================================
// Функция Token
//=====================================================
void Token( char *string ) {

	char *next;

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

//=====================================================
// Функция Number
//=====================================================
void Number( unsigned *num ) {

	char buf[80];

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

В файле MODEM.C, представленном на листинге 6.4, определены основные функции высокого уровня для работы с модемом:

ФункцияНазначение
CallОпределяет работу программы в режиме вызова удаленного модема
AnswerОпределяет работу программы в режиме ответа на приходящие звонки
ShutdownВыполняет завершение сеанса связи
ExchangeПоддерживает диалог пользователя и удаленного модема.

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

Листинг 6.4. Файл MODEM.C

#include #include #include #include #include #include #include "uart.h" #include "common.h" #include "modem.h" #include "sendcmd.h" #include "timer.h" #include "tools.h" #include "conf.h" // Номер используемого порта в формате COMn, где n от 1 до 4 char *device = "COM3"; // Продолжительность ожидания соединения unsigned dialTimeout = 12; // Задержка при передаче между символами unsigned chardelay = 0; // Таймаут на получение ответа от модема unsigned modemTimeout = 3; // Продолжительность ожидания звонка unsigned answerTimeout; // Скорость обмена данными unsigned speed = 2400; char initialize[80]; // команда инициализации char dropline[80]; // команда повесить трубку char autoanswer[80]; // ответ на вызов в режиме автоответа char dialPrefix[80]; // префикс номера char dialSuffix[80]; // суффикс номера char dialNumber[80]; // телефонный номер // Прототип функции Exchange 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( RCountPending() > 0 ) { SRead(str, i = RCountPending(), 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( ); flag = 0; break; } if( key == '\r' ) putch( '\n' ); putch(key); SWrite( &key, 1); } // Если получены данные от модема, отображаем их на экране if( RCountPending() > 0 ) { Delay(100); SRead(str, i = RCountPending(), 0); str[i] = '\0'; for( j = 0; j < i; j++ ) putch( str[j] ); } } }

Следующий модуль - SENDCMD.C определяет функцию SendStr, которая используется для передачи модему AT-команд. Исходные тексты файла SENDCMD.C приведены в листинге 6.5.

Листинг 6.5. Файл SENDCMD.C

#include 
#include 

#include "common.h"
#include "modem.h"
#include "sendcmd.h"
#include "timer.h"
#include "tools.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, представленный в листинге 6.6, содержит определения функций для работы с модулем UART.ASM.

Листинг 6.6. Файл TOOLS.C

#include 
#include 
#include 
#include 
#include 
#include 
#include 

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

unsigned 				port_active = FALSE;
static unsigned 	current_baud;
static unsigned 	hangup_needed = TRUE;

#define  STOPBIT  1

/**
*.Name     OpenLine
*
*.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-порт
	SelectPort(value);

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

	// Устанавливаем новый обработчик прерываний COM-порта
	InstallCom();

	// Программируем текущий COM-порт
	// скорость, связь через модем, не проверяем четность
	// один стоповый бит
	OpenCom(baud, 'M', 'N', STOPBIT);

	// Запоминаем скорость
	current_baud = baud;

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

	port_active = TRUE;

	return( 0 );
}

/**
*.Name    SRead
*
*.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 = RCountPending();

		// Если в буфере ессть необходимое количество символов
		if (pending >= wanted) {

			unsigned int i;

			// Считывааем из буфера нужное число символов
			for (i = 0; i < wanted; i++)
				*buffer++ = (char) ReceiveCom();

			return pending;
		}

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

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

			Delay(0);

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

/**
*.Name     SWrite
*
*.Descr    Функция записывает заданное число символов
*          в буфер передатчика асинхронного порта.
*
*.Proto    int SWrite(char *data, unsigned len)
*
*.Params   char *data - указатель на буфер данных
*
*          unsigned len - число символов, которое надо записать
*                         в буфер передатчика
*
*.Return   количество символов записанных в буфер передатчика
**/
int SWrite(char *data, unsigned int len) {

	unsigned int i;

	hangup_needed = TRUE;

	// Записываем входные данные в буфер передатчика
	// асинхронного порта
	for (i = 0; i < len; i++)
		SendCom(*data++);

	return len;
}

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

	BreakCom();
}

/**
*.Name         CloseLine
*
*.Descr        Функция восстанавливает старые значение
*              векторов прерываний и запрещает прерывания
*              от COM-порта.
*
*.Proto        void CloseLine(void)
*
*.Params       Не используются.
*
*.Return       Не используется.
**/
void CloseLine(void) {

	int far *stats;

	port_active = FALSE;

	// Отменяем сигнал DTR
	DtrOff();

	// Запрещаем прерывания от COM-порта
	CloseCom();

	// Восстанавливаем вектора прерываний
	RestoreCom();
}

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

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

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

Листинг 6.7. Файл TIMER.C

#include 
#include 
#include 

#include "timer.h"

/**
*.Name    Sleep
*
*.Descr   Функция приостанавливает выполнение
*         программы на заданное число секунд.
*
*.Proto   void Sleep(time_t interval)
*
*.Params  time_t interval - время задержки в секундах
*
*.Return  Не используется
**/
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
*
*.Descr   Функция приостанавливает выполнение
*         программы на заданное число милисекунд.
*
*.Proto   void Delay(int milliseconds)
*
*.Params  time_t interval - время задержки в милисекундах
*
*.Return  Не используется
**/
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;
	}
}

Включаемый файл COMMON.H содержит макроопределения нескольких функций, предназначенных для работы со строками. Исходный текст файла COMMON.H содержится в листинге 6.8.

Листинг 6.8. Файл 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 (см. листинг 6.9) содержит макроопределения OPERATOR и STRIP, используемые в модуле CONF.C, а также описание функции GetConfig.

Листинг 6.9. Файл CONF.H

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

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

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

Опишем функции, определенные в модуле UART.ASM.

ФункцияНазначение
SelectPortОпределяет, с каким COM-портом мы в дальнейшем будем работать. Единственный параметр функции должен содержать номер COM-порта.
SaveComСохраняет адрес старого обработчика прерываний от COM-порта. Функция не имеет параметров и не возвращает никакого значения.
RestoreCom Восстанавливает адрес старого обработчика прерываний от COM-порта, ранее сохраненного функцией SaveCom.
InstallCom Устанавливает новый обработчик прерываний от COM-порта. Она должна вызываться, после того как адрес старого обработчика прерываний сохранен с помощью функции SaveCom. Функция возвращает единицу при успешной установке обработчика или ноль в случае ошибки.
OpenComИнициализирует регистры асинхронного адаптера. Первый параметр функции baud определяет скорость обмена данными через COM-порт. Второй параметр - device задает тип устройства связи: Для модемов он должен содержать код символа 'M', а для нуль-модема - 'D'. Третий параметр parity - управляет проверкой на четность. Если он содержит код символа 'N' - проверка не производится, 'O' - выполняется проверка по нечетности, 'E' - проверка по четности, 'S' - бит четности всегда сброшен и 'M' - бит четности установлен. Последний параметр - stop_bits - задает количество стоповых битов.
CloseComЗапрещает прерывания от COM-порта. После ее вызова обмен данными через COM-порт прекращается.
DtrOn Устанавливает сигнал DTR
DtrOffСбрасывает сигнал DTR
RCountПроверяет состояние буфера приемника. Младший байт значения, возвращаемого функцией, определяет количество байтов в буфере приемника, а старший - общий размер буфера приемника.
ReceiveComЧитает один символ из буфера приемника и возвращает его значение.
SCountВозвращает в младшем байте число свободных байтов в буфере передатчика, а в старшем - общий размер буфера передатчика.
SendComПозволяет записать один символ в буфер передатчика. Единственный параметр функции должен содержать код передаваемого символа.
BreakComПереводит передающую линию в состояние BREAK.
ComErrorsВозвращает указатель на массив счетчиков ошибок. Во включаемом файле UART.H определен набор констант для доступа к отдельным полям этого массива.
Листинг 6.10. Файл UART.ASM

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

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

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

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

_DATA  ENDS

COM_TEXT    SEGMENT PARA public 'CODE'

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

	public   _SelectPort
	public   _SaveCom
	public   _InstallCom
	public   _RestoreCom
	public   _OpenCom
	public   _CloseCom
	public   _DtrOn
	public   _DtrOff
	public   _RCount
	public   _SCount
	public   _ReceiveCom
	public   _SendCom
	public   _BreakCom
	public   _ComErrors

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

_SelectPort   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

_SelectPort   ENDP

;=======================================================
; Сохранение текущего вектора COM прерывания
_SaveCom	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

_SaveCom	ENDP

;=======================================================
; InstallCom: установить активный порт
;
; Возвращает в регистре ax - 1 при успешной установке
; и 0 в случае ошибки
;
_InstallCom   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

_InstallCom   ENDP

;=======================================================
; Восстановление векторов прерываний
;
_RestoreCom   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

_RestoreCom   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
;
_OpenCom   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

_OpenCom	ENDP

;=======================================================
; Запрещаем прерывания от асинхронного порта
_CloseCom	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

_CloseCom	ENDP

;=======================================================
; Снимаем сигнал DTR
_DtrOff	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

_DtrOff	ENDP

;=======================================================
; Устанавливаем сигнал DTR
_DtrOn 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

_DtrOn ENDP

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

_RCount	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

_RCount	ENDP

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

_ReceiveCom	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

_ReceiveCom	ENDP

;=======================================================
; Функция возвращает в регистре ax число свободных байт в
; буфере передатчика, а в регистре dx общий размер буфера 
; передатчика
_SCount	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

_SCount	ENDP

;=======================================================
; Поместить символ в буфер передатчика
;  [bp+6] - символ
_SendCom	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

_SendCom	ENDP

;=======================================================
; S_local
;
_SendLocal 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
_SendLocal ENDP

;=======================================================
; Передаем удаленному модему сигнал "BREAK"
;
_BreakCom	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

_BreakCom	ENDP

;=======================================================
; Возвращаем в dx:ax указатель на счетчики ошибок
;
_ComErrors 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
_ComErrors ENDP

;=======================================================
; Заполняем счетчики ошибок
;
SetErr   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

SetErr   ENDP

;=======================================================
; Протокол модема для передачи данных
;
ModemProtocol 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

ModemProtocol 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    ; SAVE si
	mov   si,OFFSET DGROUP:AREA3
	jmp   short handle_int

;=======================================================
; Обработчик прерываний от COM4
;
int_hndlr4 PROC  FAR

	push  si    ; SAVE 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,6
	je    LSTAT_int

	; Обнаружено состояние "BREAK" или произошла ошибка
	cmp   al,0
	je    MSTAT_int

	; Завершаем обработку прерываний
	jmp   FAR PTR exit_handler

LSTAT_int:

	; Считываем регистр сотояния линии и вызываем функцию
	; set_err, которая определит причину прерывания
	mov   dx,LSR[si]
	in    al,dx
	call  SetErr
	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  ModemProtocol

	; Передаем очередной символ из буфера передатчика
	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

Включаемый файл SENDCMD.H, представленный в листинге 6.11, содержит описания функций ExpectStr и SendStr. Эти функции были нами определены в модуле SENDCMD.C.

Листинг 6.11. Файл SENDCMD.H

unsigned		ExpectStr(char *Search, unsigned int Timeout);
void				SendStr(char *str);

Включаемый файл MODEM.H (см. листинг 6.13) содержит описание функций Call, Answer, SlowWrite и ShutDown, определенных в модуле MODEM.C.

Листинг 6.12. Файл MODEM.H

int		Call( void );
int		Answer( void );
void		SlowWrite( char *s, int len);
void		ShutDown( void );

extern char *device;

Включаемый файл MODEM.H (см. листинг 6.14) содержит описание функций Sleep и Delay, определенных в модуле TIMER.C, и предназначенных для организации временных задержек.

Листинг 6.13. Файл TIMER.H

void Sleep(time_t interval); void Delay(int milliseconds);

Включаемый файл TOOLS.H (см. листинг 6.15) содержит описание функций, определенных в модуле TOOLS.C.

Листинг 6.14. Файл 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(void);

void CloseLine(void);

Включаемый файл UART.H (см. листинг 6.14) содержит описание функций, определенных в модуле UART.ASM и констант COM_E, используемых для определения причины ошибок COM-порта.

Листинг 6.15. Файл UART.H

void far SelectPort(int);
void far SaveCom(void);
void far RestoreCom(void);
int  far InstallCom(void);
void far OpenCom( int, int, int, int );
void far CloseCom(void); 
void far DtrOff(void);
void far DtrOn(void);
long far RCount(void);

// Макроопределение RCountSize возвращает
// общий размер буфера приемника
#define RCountSize() ((int)(RCount() >> 16))

// Макроопределение RCountPending возвращает
// число байтов в буфере приемника
#define RCountPending() ((int)RCount())

int  far ReceiveCom(void);
long far SCount(void);

// Макроопределение SCountSize возвращает
// общий размер буфера приемника
#define SCountSize() ((int)(SCount() >> 16))

// Макроопределение SCountFree возвращает
// число байтов в буфере приемника
#define SCountFree() ((int)SCount())

void far SendCom(int);
void far BreakCom(void);
int  far *far ComErrors(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
[Назад] [Содеожание] [Дальше]