Программа 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#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).
Ключевое слово |
Глобальная переменная |
Описание |
Dropline |
dropline |
Команда модему для разрыва связи с удаленным модемом |
Initialize |
initialize |
Команда инициализации модема |
AutoAnswer |
autoanswer |
Команда инициализации модема для работы в режиме автоответа на приходящие звонки |
DialNumber |
dialNumber |
Номер удаленного модема |
DialPrefix |
dialPrefix |
Префикс для команды набора номера |
DialSuffix |
dialSuffix |
Суффикс для команды набора номера |
Device |
device |
Номер COM-порта, к которому подключен модем |
CharDelay |
chardelay |
Задержка между отдельными символами, передаваемыми модему |
DialTimeout |
dialTimeout |
Интервал времени, за который модем должен набрать номер и установить связь с удаленным модемом |
ModemTimeout |
modemTimeout |
Интервал времени, за который модем должен ответить на передаваемые ему команды |
TimeoutAnswer |
answerTimeout |
Время, отведенное на установку связи с удаленным модемом |
Speed |
speed |
Скорость обмена данными через COM-порт |
По достижении конца файла SETUP.CFG файл закрывается, и функция возвращает управление.
Листинг 6.3. Файл 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];
// Глобальные переменные, заполняемые функцией 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 |
Поддерживает диалог пользователя и удаленного модема. |
Листинг 6.4. Файл MODEM.C
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#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 <string.h>
#include <time.h>
#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 <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;
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 <time.h>
#include <stdio.h>
#include <sys/timeb.h>
#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