Коммуникационная программа CHAT
Коммуникационная программа CHAT
В этой главе мы приведем исходный текст коммуникационной программы CHAT. В отличие от программы CHAT_S данная программа использует для работы с асинхронным адаптером прерывания.
При помощи этой программы можно связаться с удаленным модемом, передавать и принимать от него данные в формате ASCII. Например, вы можете позвонить на станцию BBS и прочитать почтовые сообщения. Передачу и прием файлов программа не поддерживает, иначе пример занимал бы слишком много места.
Данная телекоммуникационная программа может работать в двух режимах - активном, когда она сама производит вызов удаленного модема, и пассивном, когда программа находится в режиме ожидания звонка от удаленного модема. Для работы программы в активном режиме необходимо запустить ее с параметром "1", для пассивного режима - "0".
Большинство параметров программы, таких, как AT-команды инициализации, телефонный номер, скорость обмена и номер COM-порта, можно настроить через файл конфигурации setup.cfg. Образец этого файла представлен ниже:
// строка инициализации для режима активного вызова
Initialize ATS0=0Q0E0M1V1X4&C1&D2
// команда, которая переводит модем в командный режим и // кладет трубку
Dropline \d\d+++\d\dATH0\n\r\d
// строка инициализации для режима ожидания звонка
AutoAnswer ATS0=1Q0E0M1V1X4&C1&D2
// префикс телефонного номера
DialPrefix \r\pATDP
// суффикс телефонного номера
DialSuffix
// телефонный номер
DialNumber 1135810
// номер COM-порта в формате COMn, где n - номер порта
Device COM3
// время, отведенное на установку связи с удаленным модемом
DialTimeout 30 TimeoutAnswer 30
// временная задержка между сиимволами при передаче
CharDelay 0
// время реакции модема на команды
ModemTimeout 3
// скорость обмена данными
Speed 2400
Программа состоит из следующих модулей:
CHAT.C // главная процедура программы MODEM.C // передача данных модему через COM-порт TIMER.C // реализация временных задержек CONF.C // чтение файла конфигурации SEND_COMM.C // передача команд модему TOOLS.C // набор функций для работы с модулем UART.ASM UART.ASM // обработчик прерываний и процедуры низкого уровня
Теперь приведем сами исходные тексты программы. Основной модуль программы называется CHAT.C. В зависимости от параметра программы этот модуль вызывает функцию call() - режим вызова удаленного модема - или функцию answer() - режим ответа на приходящие звонки. Обе функции, call() и answer(), выполняют все действия по программированию COM-порта, контроллера прерываний и модема. Данные функции определены в модуле MODEM.C.
После окончания связи вызывается функция shutdown(), которая опускает телефонную трубку и отключает обработчик прерываний.
// CHAT.C // основной модуль коммуникационной программы
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <sys/types.h>
#include "mod_link.h" #include "common.h" #include "modem.h" #include "timer.h" #include "tools.h"
void hello(void) {
printf("неправильно задан параметр программы \n" "mod_link n, где n = 1 вызов, n = 0 ответ\n"); exit(0); }
// основная процедура
void main( int argc, char *argv[] ) {
// программа должна вызываться параметром 1 или 0 // 1 - это активный режим, когда программа сама вызывает // удаленный модем
// 0 - программа находится в режиме автоответа на // приходящий вызов
if( argc < 2 ) hello();
0 if( strcmp( argv[1], "0") && strcmp( argv[1], "1" )) hello();
if( !strcmp( argv[1], "1" ) )
// если программа запущена с параметром "1", вызывается // функция call() из модуля MODEM.C, выполняющая вызов // удаленного модема
call();
else
// если программа запущена с параметром "0", вызывается // функция answer(), переключающая модем в режим автоответа
answer();
// освобождаем телефон
shutdown(); }
Данный модуль определяет функции высокого уровня для работы с модемом:
- call() - режим вызова удаленного модема,
- answer() - режим ответа на приходящие звонки,
- shutdown() - завершение сеанса связи,
- exchange() - диалог пользователя и удаленного модема.
Эти функции вызывают модули более низкого уровня: SEND_COMM.C, TOOLS.C.
// MODEM.C //
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <sys/types.h>
#include "uart.h" #include "common.h" #include "modem.h" #include "mod_link.h" #include "timer.h" #include "tools.h" #include "conf.h"
char *device = "COM3"; // номер используемого порта в формате // COMn, где n от 1 до 4
unsigned dialTimeout = 12, // продолжительность ожидания соединения chardelay = 0, // задержка при передаче между символами modemTimeout = 3, // таймаут на получение ответа от модема answerTimeout; // ппродолжительность ожидания звонка
unsigned speed = 2400; // скорость обмена данными
char initialize[80]; // команда инициализации char dropline[80]; // команда повесить трубку char autoanswer[80]; // ответ на вызов в режиме автоответа
char dialPrefix[80]; // префикс номера char dialSuffix[80]; // суффикс номера char dialNumber[80]; // телефонный номер
static boolean getmodem( const char *brand); static boolean sendlist( char **list, int timeout, int lasttimeout); static boolean sendalt( char *string, int timeout);
void exchange( void );
// call // вызов удаленного пользователя
int call() {
char str[80], buf[80]; char *exp; int i,j;
// определяем параметры связи (считываем файл конфигурации)
getconfig();
// устанавливаем обработчик прерываний и инициализируем // регистры UART и контроллера прерываний
if (openline(device, speed)) return FALSE;
// очищаем приемный буфер
while (sread(buf,1,0));
printf("инициализируем модем\n\n");
// передаем модему строку инициализации // (строка инициализации определяется ключевым словом Initialize // в файле конфигурации setup.cfg)
sendstr( initialize );
// ожидаем ответа модема
sleep(modemTimeout);
// считываем и отображаем на экране ответное сообщение модема
if( r_count_pending() > 0 ) {
sread(str, i = r_count_pending(), 0); str[i] = '\0';
for( j = 0; j < i; j++ ) putch( str[j] ); }
// передаем модему команду набора номера
strcpy(buf, dialPrefix); strcat(buf, dialNumber); strcat(buf, dialSuffix);
printf( "набираем номер\n\n");
sendstr( buf );
printf( "ожидаем соединение\n\n");
// производим обмен данными с удаленным модемом, // пока не нажата клавиша ESC
exchange();
return(0); }
// answer // отвечает на звонок
int answer( void ) {
char c;
// определяем параметры связи
getconfig();
// устанавливаем обработчик прерываний и инициализируем // регистры UART и контроллера прерываний
if (openline(device, speed)) exit(-2);
// очищаем приемный буфер
while (sread(&c ,1,0));
printf("инициализируем модем\n\n");
// передаем модему строку инициализации // ( строка инициализации определяется ключевым словом Autoanswer // в файле конфигурации setup.cfg)
sendstr( autoanswer ); sleep(modemTimeout);
printf("ожидаем звонок\n");
// производим обмен данными с удаленным модемом, // пока не нажата клавиша ESC
exchange();
return(0); }
// shutdown // функция кладет телефонную трубку
void shutdown( void ) {
printf("\n\nсвязь окончена, освобождаем телефон\n");
// передаем команду положить трубку
sendstr( dropline );
// восстанавливаем старый обработчик прерываний
closeline(); }
// slowwrite // передать символ модему с задержкой, определяемой ключевым // словом CharDelay в файле конфигурации
void slowwrite( char *s, int len) {
swrite( s , len ); if (chardelay > 0) delay(chardelay); }
// exchange // функция выполняет диалог пользователя и удаленного модема
void exchange( void ) {
int flag = 1;
while(flag) {
unsigned char str[80]; unsigned char key; unsigned i,j;
// если пользователь нажал на клавиатуру, получаем код // нажатого символа и передаем его модему
if( kbhit() ) { key = getch();
// по нажатию клавиши Esc выходим из программы
if( key == 27 ) { ssendbrk( 3 ); flag = 0; break; }
if( key == '\r' ) putch( '\n' );
putch(key);
swrite( &key, 1); }
// если получены данные от модема, отображаем их на экране
if( r_count_pending() > 0 ) {
delay(100);
sread(str, i = r_count_pending(), 0); str[i] = '\0';
for( j = 0; j < i; j++ ) putch( str[j] ); } } }
Модуль CONF.C определяет функцию getconfig(), считывающую файл конфигурации setup.cfg и записывающую считанные значения в глобальные переменные.
Конфигурационный файл может содержать следующие команды:
Initialize [строка инициализации для режима активного вызова]
Dropline [команда для освобождения телефона]
AutoAnswer [строка инициализации для режима ожидания звонка]
DialPrefix [ префикс телефонного номера]
DialSuffix [суффикс телефонного номера]
DialNumber [телефонный номер]
Device [номер COM-порта]
DialTimeout [время, отведенное на установку связи с удаленным модемом]
TimeoutAnswer [время, отведенное на установку связи с удаленным модемом]
CharDelay [временная задержка между символами при передаче]
ModemTimeout [время реакции модема на команды]
Speed [скорость обмена данными]
Итак, исходный текст модуля CONF.C:
// CONF.C
#include <conio.h> #include <stdio.h> #include <stdlib.h> #include <string.h>
#include "conf.h"
void Number( unsigned *num ); void Token( char *string );
FILE *CfgFile; char LineBuf[256];
extern char initialize[80]; extern char dropline[80]; extern char autoanswer[80];
extern char dialPrefix[80]; extern char dialSuffix[80]; extern char dialNumber[80];
extern unsigned chardelay; extern unsigned dialTimeout; extern unsigned modemTimeout; extern unsigned answerTimeout;
extern unsigned speed;
extern char *device;
/** *.Name getconfig * *.Title Считывает файл setup.cfg. * *.Descr Эта функция считывает данные из файла setup.cfg * *.Proto void getconfig(void) * *.Params не используется * *.Return не используется **/ void getconfig(void) {
CfgFile=fopen("setup.cfg","r"); if(CfgFile == NULL) { cprintf("\r\nОтсутствует файл SETUP.CFG."); exit(-1); }
// заполняем глобальные переменные
for(;;) { fgets(LineBuf,255,CfgFile); if(feof(CfgFile)) break;
STRIP(LineBuf);
if(OPERATOR("Dropline")) Token( dropline ); else if(OPERATOR("Initialize")) Token( initialize ); else if(OPERATOR("AutoAnswerr")) Token( autoanswer );
else if(OPERATOR("DialNumber")) Token( dialNumber ); else if(OPERATOR("DialPrefix")) Token( dialPrefix ); else if(OPERATOR("DialSuffix")) Token( dialSuffix ); else if(OPERATOR("Device")) Token( device );
else if(OPERATOR("CharDelay")) Number( &chardelay ); else if(OPERATOR("DialTimeout")) Number( &dialTimeout ); else if(OPERATOR("ModemTimeout")) Number( &modemTimeout );
else if(OPERATOR("TimeoutAnswer")) Number( &answerTimeout ); else if(OPERATOR("Speed")) Number( &speed ); }
fclose(CfgFile); }
void Token( char *string ) {
char *next;
next = strcpy( string, strchr( LineBuf, ' ' ) + 1 ); if(next == NULL) string[0] = '\0'; }
void Number( unsigned *num ) {
char buf[80];
strcpy( buf, strchr( LineBuf, ' ' ) + 1 ); *num = atoi( buf ); }
Следующий модуль - SEND_COMM.C определяет функцию sendstr(), которая используется для передачи модему AT-команд.
// SEND_COMM.C
#include <time.h>
#include "common.h" #include "modem.h" #include "get_word.h" #include "timer.h" #include "tools.h" #include "total.h"
// writestr // обработка управляющих символов static unsigned writestr(register char *s) {
register char last = '\0'; int no_CR = FALSE; unsigned char digit;
while (*s) { if (last == '\\') { last = *s;
switch (*s) {
// задержка на две секунды
case 'd': case 'D': sleep(2); break;
// не передавать символ возврата каретки // в конце строки
case 'c': case 'C': no_CR = TRUE; break;
// передать символ возврата каретки
case 'r': case 'R': slowwrite("\r", 1); break;
// передать символ возврата каретки
case 'n': case 'N': slowwrite("\n", 1); break;
// задержка 500 миллисекунд
case 'p': case 'P': delay(500); break;
default: slowwrite(s, 1); last = '\0'; } } else if (*s != '\\') slowwrite(s, 1);
else last = *s; s++; }
return no_CR; }
// sendstr // послать строку в буфер передатчика, // при этом обрабатываются следующие управляющие символы:
// d (D) - задержка на две секунды // c (C) - не передавать символ возврата каретки в конце строки // r (R) - передать символ возврата каретки // n (N) - передать символ перевода строки // p (P) - задержка 500 миллисекунд
void sendstr(char *str) {
if(!equal(str,"")) { if(!writestr(str)) slowwrite("\r", 1); } else slowwrite("\r", 1);
return; }
Модуль TOOLS.C содержит определения функций для работы с модулем UART.ASM.
// TOOLS.C //
#include <assert.h> #include <fcntl.h> #include <io.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h>
#include "common.h" #include "tools.h" #include "uart.h" #include "timer.h"
unsigned port_active = FALSE;
static unsigned current_baud;
#define STOPBIT 1
static unsigned hangup_needed = TRUE;
/** *.Name openline * *.Title Устанавливает текущий COM-порт. * *.Descr Эта функция устанавливает текущий асинхронный * порт, с которым в дальнейшем будет происходить обмен. * *.Proto int openline(char *name, unsigned baud) * *.Params char *name - номер COM-порта * unsigned baud - скорость обмена данными * *.Return не используется **/
int openline(char *name, unsigned baud) {
int value;
// если порт уже активен, закрываем его
if (port_active) closeline();
if (sscanf(name, "COM%d", &value) != 1) { exit(-1); }
// выбираем текущий COM-порт
select_port(value);
// сохраняем адрес старого обработчика прерываний COM-порта
save_com();
// устанавливаем новый обработчик прерываний COM-порта
install_com();
// программируем текущий COM-порт // скорость, связь через модем, не проверяем четность // один стоповый бит
open_com(baud, 'M', 'N', STOPBIT);
// запоминаем скорость
current_baud = baud;
// устанавливаем линию DTR в активное состояние // (компьютер готов к обмену данными)
dtr_on();
port_active = TRUE;
return( 0 ); }
/** *.Name sread * *.Title Читает символы из COM-порта. * *. Descr Эта функция читает заданное число символов * из буфера приемника асинхронного порта. * *.Proto unsigned sread(char *buffer, unsigned wanted, unsigned timeout) * *.Params char *buffer - указатель на буфер в который будут записаны символы * из буфера приемника * unsigned wanted - число символов, которое надо прочитать * из буфера приемника * unsigned timeout - время, отведенное на чтение символов * *.Return количество символов, прочитанных из буфера приемника **/
unsigned sread(char *buffer, unsigned wanted, unsigned timeout) {
time_t start; hangup_needed = TRUE;
// определяем начальное время
start = time(nil(time_t));
for(;;) {
unsigned int pending;
// определяем число символов в буфере приемника
pending = r_count_pending();
// если в буфере есть необходимое количество символов
if (pending >= wanted) {
unsigned int i;
// считываем из буфера нужное число символов
for (i = 0; i < wanted; i++) *buffer++ = (char) receive_com();
return pending; }
// если в буфере приемника меньше символов, чем заказано // для чтения, проверяем, не иcтекло ли отведенное для чтения // время
else {
time_t now = time(nil(time_t)); time_t elapsed = now - start;
delay(0);
if (elapsed >= (long) timeout) return pending; } } }
/** *.Name swrite * *.Title Запись символов в COM-порт. * *.Descr Эта функция записывает заданное число символов * в буфер передатчика асинхронного порта. * *.Proto int swrite(char *data, unsigned len) * *.Params char *data - указатель на буфер данных * unsigned len - число символов, которое надо записать * в буфер передатчика * *.Return количество символов, записанных в буфер передатчика * *.Sample comlib.c **/
int swrite(char *data, unsigned int len) {
unsigned int i;
hangup_needed = TRUE;
// записываем входные данные в буфер передатчика // асинхронного порта
for (i = 0; i < len; i++) send_com(*data++);
return len; }
/** *.Name ssendbrk * *. Title Передает сигнал BREAK удаленному модему. * *.Proto void ssendbrk() * *.Params Не используются. * *.Return Не используется. * *.Sample comlib.c **/
void ssendbrk(void) {
break_com(); }
/** *.Name closeline * *.Title Закрывает COM-порт. * *.Descr Эта функция восстанавливает старые значения * векторов прерываний и запрещает прерывания * от COM-порта. * *.Proto void closeline(void) * *.Params Не используются. * *.Return Не используется. * *.Sample comlib.c **/
void closeline(void) {
int far *stats;
port_active = FALSE;
// отменяем сигнал DTR
dtr_off();
// запрещаем прерывания от COM-порта
close_com();
// восстанавливаем векторы прерываний
restore_com(); }
Вспомогательный модуль TIMER.C содержит определения функций sleep() и delay(). Эти функции используются в программе для организации временных задержек.
// TIMER.C // определены функции sleep и delay, выполняющие временные задержки
#include <time.h> #include <sys/timeb.h>
#include "timer.h"
/** *.Name sleep * *.Title Приостанавливает выполнение программы * *.Descr Эта функция приостанавливает выполнение * программы на заданное число секунд. * *.Proto void sleep(time_t interval) * *.Params time_t interval - время задержки в секундах * *.Return не используется * *.Sample timer.c **/
void sleep(time_t interval) {
time_t start;
start = time((time_t *)NULL);
// ожидаем, пока пройдет time_t секунд
while ((time((time_t *)NULL) - start) < interval) delay(1000); }
/** *.Name delay * *.Title Приостанавливает выполнение программы * *.Descr Эта функция приостанавливает выполнение * программы на заданное число миллисекунд. * *.Proto void delay(int milliseconds) * *.Params time_t interval - время задержки в миллисекундах * *.Return не используется * *.Sample timer.c **/
void delay (int milliseconds) {
struct timeb t; time_t seconds; unsigned last;
if (milliseconds == 0) return;
// определяем текущее время
ftime(&t);
last = t.millitm; seconds = t.time;
// ожидаем milliseconds миллисекунд
while( milliseconds > 0) {
int count;
// задержка for ( count = 0; count < 2000; count ++);
// определяем текущее время
ftime(&t);
if (t.time == seconds) milliseconds -= (t.millitm - last);
else milliseconds -= 1000 * (int) (t.time - seconds) - (last - t.millitm);
last = t.millitm; seconds = t.time; } }
Модуль UART.ASM - это основной модуль программы CHAT. Он содержит обработчик прерываний от COM-порта и функции низкого уровня для работы с ним.
Обработчик прерываний имеет два буфера - буфер приемника и буфер передатчика. Через эти буферы осуществляется обмен данными между программой и обработчиком прерываний. Буферы выполнены в виде очереди.
; ; UART.ASM ; модуль управления модемом и COM-портом нижнего уровня ;
; определяем размеры буфера приемника и передатчика
R_SIZE EQU 2048 ; размер приемного буфера S_SIZE EQU 500 ; размер буфера передатчика
; номера обработчиков прерываний
INT_COM1 EQU 0Ch ; COM1 INT_COM2 EQU 0Bh ; COM2 INT_COM3 EQU 0Ch ; COM3 INT_COM4 EQU 0Bh ; COM4
; порты контроллера прерываний 8259
OCR EQU 20H ; управляющий регистр 8259 IMR EQU 21H ; регистр маски прерываний 8259
; константы для управления контроллером прерываний
E_IRQ4 EQU 00010000B D_IRQ4 EQU 11101111B EOI4 EQU 01100100B
E_IRQ3 EQU 00001000B D_IRQ3 EQU 11110111B EOI3 EQU 01100011B
; ; область переменных BIOS ;
; адреса базовых регистров последовательных асинхронных адаптеров
BIOS_VAR SEGMENT AT 40H
rs232_base DW 4 DUP(?)
BIOS_VAR ENDS
; ; таблица для каждого COM-порта ;
SP_TAB STRUC
port DB ? ; 1, 2, 3 или 4
; параметры для этого уровня прерываний
int_com DB ? ; номер прерывания e_irq DB ? d_irq DB ? eoi DB ?
; обработчики прерываний для этого уровня
int_hndlr DW ? ; смещение обработчика прерываний old_com_off DW ? ; смещение старого обработчика прерываний old_com_seg DW ? ; сегмент старого обработчика прерываний
; параметры COM-порта
installed DB ? ; установлен ли порт на этом компьютере? (1=да,0=нет) baud_rate DW ? device_conn DB ? ; M(Модем), D(Нуль-модем) parity DB ? ; N(ONE), O(DD), E(VEN), S(PACE), M(ARK) stop_bits DB ? ; 1, 2
; счетчики ошибок
error_block DW 8 DUP(?)
; порты 8250
DATREG DW ? ; регистр данных IER DW ? ; регистр управления прерываниями IIR DW ? ; регистр идентификации прерывания LCR DW ? ; регистр управления линией MCR DW ? ; регистр управления модемом LSR DW ? ; регистр состояния линии MSR DW ? ; регистр состояния модема
DLL EQU DATREG ; младший регистр делителя DLH EQU IER ; старший регистр делителя
; указатели буферов FIFO
; индекс первого символа в буфере передатчика
start_s_data DW ?
; индекс первого свободного элемента буфера передатчика
end_s_data DW ?
; индекс первого символа в буфере приемника
start_r_data DW ?
; индекс первого свободного элемента буфера приемника
end_r_data DW ?
; счетчики количества символов в буферах
size_s_data DW ? ; число символов в буфере передатчика size_r_data DW ? ; число символов в буфере приемника
; буфера
send_buf DB S_SIZE DUP(?) ; буфер передатчика reciave_buf DB R_SIZE DUP(?) ; буфер приемника
SP_TAB ENDS
EFRAME EQU error_block+6 ; ошибка синхронизации EPARITY EQU error_block+8 ; ошибка четности EOVFLOW EQU error_block ; произошло переполнение буфера EDSR EQU error_block+12 ; модем не ответил сигналом DSR
EOVRUN EQU error_block+2 ; ошибка переполнения EBREAK EQU error_block+4 ; обнаружен запрос на прерывание
EXMIT EQU error_block+10 ; ошибка при передаче ECTS EQU error_block+14 ; модем не ответил сигналом CTS
; ; DGROUP GROUP _DATA
_DATA SEGMENT public 'DATA'
DIV50 DW 2304
; текущий номер области данных порта
CURRENT_AREA DW AREA1
; область данных для каждого порта
AREA1 SP_TAB <1,INT_COM1,E_IRQ4,D_IRQ4,EOI4> ; область данных COM1 AREA2 SP_TAB <2,INT_COM2,E_IRQ3,D_IRQ3,EOI3> ; область данных COM2 AREA3 SP_TAB <3,INT_COM3,E_IRQ4,D_IRQ4,EOI4> ; область данных COM3 AREA4 SP_TAB <4,INT_COM4,E_IRQ3,D_IRQ3,EOI3> ; область данных COM4
_DATA ENDS
COM_TEXT SEGMENT PARA public 'CODE'
ASSUME cs:COM_TEXT,ds:DGROUP,es:NOTHING
public _select_port public _save_com public _install_com public _restore_com public _open_com public _close_com public _dtr_on public _dtr_off public _r_count public _s_count public _receive_com public _send_com public _break_com public _com_errors
; ; выбор активного порта ; [bp+6] - номер порта
_select_port PROC FAR
push bp mov bp, sp mov ax, [bp+6] ;получаем в ax аргумент функции
cmp al,1 ; установлен порт 1? je port1 ; да
cmp al,2 ; установлен порт 2? je port2 ; да
cmp al,3 ; установлен порт 3? je port3 ; да
cmp al,4 ; установлен порт 4? je port4 ; да
jmp set_carrent_area
port1: mov ax,OFFSET DGROUP:AREA1 ; выбираем область данных COM1 jmp short set_carrent_area
port2: mov ax,OFFSET DGROUP:AREA2 ; выбираем область данных COM2 jmp short set_carrent_area
port3: mov ax,OFFSET DGROUP:AREA3 ; выбираем область данных COM3 jmp short set_carrent_area
port4: mov ax,OFFSET DGROUP:AREA4 ; выбираем область данных COM4
set_carrent_area:
; записываем в переменной CURRENT_AREA смещение ; текущей области данных
mov CURRENT_AREA,ax
mov sp,bp pop bp ret
_select_port ENDP
; ; сохранение текущего вектора прерывания COM-порта ; _save_com PROC FAR
push bp mov bp,sp push si
; записываем в si указатель на текущую область данных
mov si,CURRENT_AREA push es
mov AREA1.int_hndlr,OFFSET int_hndlr1 mov AREA2.int_hndlr,OFFSET int_hndlr2 mov AREA3.int_hndlr,OFFSET int_hndlr3 mov AREA4.int_hndlr,OFFSET int_hndlr4
; сохраняем старый вектор прерывания
mov ah,35H mov al,int_com[si] ; номер прерывания int 21h
; записываем в переменные old_com_off и old_com_seg ; соответственно сегмент и смещение старого вектора прерывания
mov old_com_off[si],bx mov bx,es mov old_com_seg[si],bx
pop es
pop si mov sp,bp pop bp
ret
_save_com ENDP
; ; install_com: установить активный порт ; ; возвращает в регистре ax - 1 при успешной установке ; и 0 в случае ошибки ; _install_com PROC FAR
push bp mov bp,sp push si
mov si,CURRENT_AREA
push es
cmp installed[si],1 jne go_install jmp alredy_ok
; очищаем счетчики ошибок
go_install:
mov WORD PTR EOVFLOW[si],0 ; переполнение буфера передатчика mov WORD PTR EOVRUN[si],0 ; ошибка переполнения при приеме mov WORD PTR EBREAK[si],0 ; обнаружен запрос на прерывание mov WORD PTR EFRAME[si],0 ; ошибка синхронизации mov WORD PTR EPARITY[si],0 ; ошибка четности mov WORD PTR EXMIT[si],0 ; ошибка при передаче mov WORD PTR EDSR[si],0 ; не получен сигнал DSR mov WORD PTR ECTS[si],0 ; не получен сигнал CTS
; определяем базовый адрес используемого COM порта
mov bx,BIOS_VAR mov es,bx
ASSUME es:BIOS_VAR
cmp port[si],1 ; порт 1? je adr_3F8
cmp port[si],2 ; порт 2? je adr_2F8
cmp port[si],3 ; порт 3? je adr_3E8
cmp port[si],4 ; порт 4? je adr_2E8
int 20H
adr_3F8: mov ax,3F8H jmp cmp_bios
adr_2F8: mov ax,2F8H jmp cmp_bios
adr_3E8: cmp rs232_base+4,0 je adr_3E8_A
mov ax,rs232_base+4 jmp cmp_bios
adr_3E8_A: mov ax,3E8H mov rs232_base+4,ax jmp cmp_bios
adr_2E8: cmp rs232_base+6,0 je adr_2E8_A
mov ax,rs232_base+6 jmp cmp_bios
adr_2E8_A: mov ax,2E8H mov rs232_base+6,ax
; проверяем, определена ли соответствующая переменная ; BIOS
cmp_bios: cmp ax,rs232_base je set_reg_adr
cmp ax,rs232_base+2 je set_reg_adr
cmp ax,rs232_base+4 je set_reg_adr
cmp ax,rs232_base+6 jne bad_exit
set_reg_adr:
mov bx,DATREG mov cx,7
set_next_reg_adr:
mov WORD PTR [si][bx],ax inc ax add bx,2 loop set_next_reg_adr
; устанавливаем вектор прерывания на наш обработчик
mov AREA1.int_hndlr,OFFSET int_hndlr1 mov AREA2.int_hndlr,OFFSET int_hndlr2 mov AREA3.int_hndlr,OFFSET int_hndlr3 mov AREA4.int_hndlr,OFFSET int_hndlr4
mov ah,25H mov al,int_com[si] ; номер прерывания mov dx,OFFSET DGROUP:int_hndlr[si] push ds push cs pop ds int 21h
pop ds
; поднимаем флаг - порт установлен
alredy_ok: mov installed[si],1 pop es
; возвращаем 1
mov ax,1 pop si mov sp,bp pop bp ret
; порт не установлен
bad_exit: mov installed[si],0 pop es
; возвращаем 0
mov ax,0 pop si mov sp,bp pop bp ret
_install_com ENDP
; ; восстановление векторов прерываний ; _restore_com PROC FAR
push bp mov bp,sp push si
; отмечаем COM-порт как неактивный
mov si,CURRENT_AREA mov installed[si],0
; восстанавливаем вектор прерывания
mov ah,25H mov al,int_com[si] mov dx,old_com_off[si] mov bx,old_com_seg[si] push ds mov ds,bx int 21h
pop ds
pop si mov sp,bp pop bp ret
_restore_com ENDP
; ; открыть COM порт ; ; сброс буферов передатчика и приемника, ; инициализация регистров UART 8250 ; разрешение прерываний от UART 8250 ; (программирование контроллера прерываний) ; ; [bp+6] = скорость обмена ; [bp+8] = способ соединения - M(Модем), D(Нуль-модем) ; [bp+10] = четность - N(ONE), O(DD), E(VEN), S(PACE), M(ARK) ; [bp+12] = число стоповых бит 1, 2 ; _open_com PROC FAR
push bp mov bp,sp push si
mov si,CURRENT_AREA
; запрещаем прерывания
cli mov ax,[bp+6] mov baud_rate[si],ax mov bh,[bp+8] mov device_conn[si],bh mov bl,[bp+10] mov parity[si],bl mov ch,[bp+12] mov stop_bits[si],CH
; сбрасываем буферы и указатели
mov start_s_data[si],0 mov end_s_data[si],0 mov start_r_data[si],0 mov end_r_data[si],0 mov size_s_data[si],0 mov size_r_data[si],0
; проверяем, установлен ли уже обработчик прерываний
test installed[si],1 jnz reset_uart jmp exit_open
reset_uart:
; устанавливаем регистры UART 8250
; сбрасываем регистр управления модемом
mov al,0 mov dx,MCR[si] out dx,al jmp $+2
; сбрасываем регистр состояния линии
mov dx,LSR[si] in al,dx jmp $+2
; сбрасываем регистр данных
mov dx,DATREG[si] in al,dx jmp $+2
; сбрасываем регистр состояния модема
mov dx,MSR[si] in al,dx
; определяем делитель частоты тактового генератора
mov ax,50 mul DIV50 div baud_rate[si] mov bx,ax
; переключаем регистр данных и регистр управления прерываниями ; для ввода делителя частоты тактового генератора
mov dx,LCR[si] mov al,80H out dx,al jmp $+2
; вводим младший байт делителя частоты тактового генератора
mov dx,WORD PTR DLL[si] mov al,bl out dx,al jmp $+2
; вводим старший байт делителя частоты тактового генератора
mov dx,WORD PTR DLH[si] mov al,bh out dx,al jmp $+2
; определяем четность и число стоповых битов
mov al,03H cmp parity[si],'O' jne next1 mov al,0ah jmp short next3
next1: cmp parity[si],'E' jne next2 mov al,1ah jmp short next3
next2: cmp parity[si],'M' jne next3 mov al,2ah
next3: test stop_bits[si],2 jz stop1 or al,4
stop1: mov dx,LCR[si] out dx,al
; разрешаем прерывания для 8259 и 8250
; устанавливаем регистр маски прерываний так, чтобы ; разрешить прерывания от асинхронного порта
in al,IMR and al,d_irq[si] out IMR,al
; разрешаем генерацию прерываний при готовности принимаемых ; данных, по состоянию BREAK и по ошибке
mov dx,IER[si] mov al,5 out dx,al jmp $+2
; устанавливаем DTR, RTS, OUT2
mov dx,MCR[si] mov al,0bh out dx,al
exit_open:
sti
pop si mov sp,bp pop bp ret
_open_com ENDP
; ; запрещаем прерывания от асинхронного порта ; _close_com PROC FAR push bp mov bp,sp push si
mov si,CURRENT_AREA test installed[si],1 jz exit_close
; запрещаем прерывания UART 8250
mov dx,IER[si] mov al,0 out dx,al
; маскируем прерывания от UART
mov dx,IMR in al,dx or al,e_irq[si] jmp $+2 out dx,al
exit_close:
pop si mov sp,bp pop bp ret
_close_com ENDP
; ; снимаем сигнал DTR ;
_dtr_off PROC FAR
push bp mov bp,sp push si
pushf push ax push dx push si
mov si,CURRENT_AREA test installed[si],1 jz exit_dtr_off
; устанавливаем регистр управления модемом, ; сбрасываем сигналы DTR и RTS
mov dx,MCR[si] mov al,08H out dx,al
exit_dtr_off:
pop si pop dx pop ax popf
pop si mov sp,bp pop bp
ret
_dtr_off ENDP
; ; устанавливаем сигнал DTR ;
_dtr_on PROC FAR
push bp mov bp,sp push si
pushf push ax push dx push si mov si,CURRENT_AREA test installed[si],1 jz exit_dtr_on
; устанавливаем регистр управления модемом, ; устанавливаем сигналы DTR, RTS, OUT2
mov dx,MCR[si] mov al,0bh out dx,al
exit_dtr_on:
pop si pop dx pop ax popf
pop si mov sp,bp pop bp
ret
_dtr_on ENDP
; ; возвращаем в регистре ax число байт в регистре приемника, ; а в регистре dx общий размер буфера приемника ;
_r_count PROC FAR push bp mov bp,sp push si
pushf push si mov si,CURRENT_AREA
mov ax,0 mov dx,R_SIZE
test installed[si],1 jz exit_r_count
; записываем в регистр ax число символов в буфере приемника
mov ax,size_r_data[si]
exit_r_count:
pop si popf
pop si mov sp,bp pop bp ret
_r_count ENDP
; ; получаем очередной символ из буфера приемника, ; полученный символ удаляется из буфера ;
_receive_com PROC FAR
push bp mov bp,sp push si
pushf push bx push si
mov si,CURRENT_AREA mov ax,-1
test installed[si],1 jz exit_receive_com
; возвращаемся, если буфер приемника пуст
cmp size_r_data[si],0 je exit_receive_com
mov ah,0 mov bx,start_r_data[si] mov al,reciave_buf[si][bx]
cmp parity[si],'N' je no_parity
; если производится поверка на четность, то маскируем старший бит
and al,7FH
no_parity:
inc bx cmp bx,R_SIZE jb rec_ptr_no_max mov bx,0
rec_ptr_no_max:
mov start_r_data[si],bx dec size_r_data[si]
exit_receive_com:
pop si pop bx popf
pop si mov sp,bp pop bp
ret
_receive_com ENDP
; ; функция возвращает в регистре ax число свободных байт в ; буфере передатчика, а в регистре dx общий размер буфера передатчика ; _s_count PROC FAR
push bp mov bp,sp push si
pushf push si
mov si,CURRENT_AREA mov ax,0 mov dx,S_SIZE
test installed[si],1 jz exit_s_count
mov ax,S_SIZE sub ax,size_s_data[si]
exit_s_count:
pop si popf
pop si mov sp,bp pop bp
ret
_s_count ENDP
; ; поместить символ в буфер передатчика ; [bp+6] - символ ; _send_com PROC FAR
push bp mov bp,sp push si
mov al,[bp+6]
pushf push ax push bx push dx push si
mov si,CURRENT_AREA
test installed[si],1 jz exit_send_com
cmp size_s_data[si],S_SIZE jl no_s_EOVFLOW
; произошло переполнение буфера передатчика
inc WORD PTR EOVFLOW[si] jmp short exit_send_com
no_s_EOVFLOW:
mov bx,end_s_data[si] mov send_buf[si][bx],al inc bx cmp bx,S_SIZE jl no_send_ptr_max mov bx,0
no_send_ptr_max:
mov end_s_data[si],bx inc size_s_data[si]
; считываем регистр управления прерываниями
mov dx,IER[si] in al,dx
; завершаем функцию, если разрешены прерывания после передачи байта
test al,2 jnz exit_send_com
; разрешаем прерывания после передачи байта, после приема байта, ; при обнаружении состояния BREAK и при возникновении ошибки
mov al,7 out dx,al
exit_send_com:
pop si pop dx pop bx pop ax popf
pop si mov sp,bp pop bp
ret
_send_com ENDP
; ; S_local ; _send_local PROC FAR push bp mov bp,sp push si
mov al,[bp+6] pushf push ax push bx push si mov si,CURRENT_AREA test installed[si],1 jz SLX
cli cmp size_r_data[si],R_SIZE jb L13A inc WORD PTR EOVFLOW[si] jmp short L14
L13A: mov bx,end_r_data[si] mov reciave_buf[si][bx],al inc bx cmp bx,R_SIZE jl L13 mov bx,0
L13: mov end_r_data[si],bx inc size_r_data[si]
L14: sti
SLX: pop si pop bx pop ax popf pop si mov sp,bp pop bp ret _send_local ENDP
; ; передаем удаленному модему сигнал BREAK ;
_break_com PROC FAR
push bp mov bp,sp push si
pushf push ax push cx push dx
mov si,CURRENT_AREA test installed[si],1 jz exit_break_com
; передаем сигнал BREAK
mov dx,LCR[si] in al,dx jmp $+2 or al,40h out dx,al
mov cx,0C000h
do_BREAK: loop do_BREAK
and al,0BFh out dx,al
exit_break_com:
pop dx pop cx pop ax popf
pop si mov sp,bp pop bp
ret
_break_com ENDP
; ; возвращаем в dx:ax указатель на счетчики ошибок ; _com_errors PROC FAR
push bp
mov bp,sp mov ax,OFFSET DGROUP:CURRENT_AREA add ax,error_block mov dx,ds mov sp,bp
pop bp
ret _com_errors ENDP
; ; заполняем счетчики ошибок ;
set_err PROC NEAR
test al,2 jz test1 inc WORD PTR EOVRUN[si]
test1: test al,4 jz test2 inc WORD PTR EPARITY[si]
test2: test al,8 jz test3 inc WORD PTR EFRAME[si]
test3: test al,16 jz exit_set_err inc WORD PTR EBREAK[si]
exit_set_err:
ret
set_err ENDP
; ; протокол модема для передачи данных ;
modem_protocol PROC NEAR
cmp device_conn[si],'M' jne no_modem
; устанавливаем сигналы DTR, RTS и OUT2
mov dx,MCR[si] mov al,00001011B out dx,al jmp $+2
; ожидаем, пока модем ответит о готовности сигналом DSR
mov cx,1000 mov dx,MSR[si]
wait_dsr: in al,dx test al,20H jnz test_cts loop wait_dsr
; модем не ответил сигналом DSR
inc WORD PTR EDSR[si] jmp short no_modem
test_cts:
; ожидаем, пока модем ответит о готовности сигналом CTS
mov cx,1000
wait_cts: in al,dx test al,10H jnz test_lcr loop wait_cts
; модем не ответил сигналом CTS
inc WORD PTR ECTS[si]
test_lcr: no_modem:
; проверяем, пуст ли регистр хранения передатчика
mov dx,LSR[si] in al,dx test al,20H jnz s_reg_empty
; ошибка при передаче
inc WORD PTR EXMIT[si]
s_reg_empty:
ret
modem_protocol ENDP
; ; обработчик прерываний от COM1 ;
int_hndlr1 PROC FAR
push si mov si,OFFSET DGROUP:AREA1 jmp short handle_int
; ; обработчик прерываний от COM2 ;
int_hndlr2 PROC FAR
push si mov si,OFFSET DGROUP:AREA2 jmp short handle_int
; ; обработчик прерываний от COM3 ;
int_hndlr3 PROC FAR
push si mov si,OFFSET DGROUP:AREA3 jmp short handle_int
; ; обработчик прерываний от COM4 ;
int_hndlr4 PROC FAR
push si mov si,OFFSET DGROUP:AREA4
; ; обработчик прерываний ;
handle_int:
push ax push bx push cx push dx push bp push di push ds push es
mov ax,DGROUP mov ds,ax
next_pr:
; передаем контроллеру прерываний команду конца обработки ; прерывания
mov dx,OCR mov al,eoi[si] out dx,al
next_inter:
; считываем значение регистра идентификации прерывания
mov dx,IIR[si] in al,dx
; определяем причину прерывания
; данные приняты и доступны для чтения
cmp al,4 je RX_int
; буфер передатчика пуст
cmp al,2 je TX_int
; изменилось состояние линий CTS, RI, DCD, DSR
cmp al,0 je MSTAT_int
; обнаружено сотояние BREAK или произошла ошибка
cmp al,6 je LSTAT_int
; завершаем обработку прерываний
jmp FAR PTR exit_handler
LSTAT_int:
; считываем регистр сотояния линии и вызываем функцию ; set_err, которая определит причину прерывания
mov dx,LSR[si] in al,dx call set_err jmp next_inter
MSTAT_int:
; считываем регистр состояния модема
mov dx,MSR[si] in al,dx jmp next_inter
TX_int:
; смотрим, есть ли данные для передачи модему
cmp size_s_data[si],0
jg have_data_for_send
; если буфер передатчика пуст, переустанавливаем регистр ; управления прерываниями
mov dx,IER[si] mov al,5 out dx,al jmp next_inter
have_data_for_send:
; передаем символ модему в соответствии с состоянием ; линий RS-232-С
call modem_protocol
; передаем очередной символ из буфера передатчика
mov bx,start_s_data[si] mov al,send_buf[si][bx] mov dx,DATREG[si] out dx,al
inc bx cmp bx,S_SIZE jb ptr_no_max mov bx,0
ptr_no_max:
mov start_s_data[si],bx dec size_s_data[si] jmp next_inter
; данные приняты и доступны для чтения RX_int:
; считываем принятый байт из регистра данных UART
mov dx,DATREG[si] in al,dx
cmp size_r_data[si],R_SIZE jl no_r_EOVFLOW
; буфер приемника переполнен, увеличиваем соответствующий ; счетчик ошибок
inc WORD PTR EOVFLOW[si] jmp next_inter
no_r_EOVFLOW:
mov bx,end_r_data[si] mov reciave_buf[si][bx],al inc size_r_data[si] inc bx
cmp bx,R_SIZE jb no_max_r_ptr mov bx,0
no_max_r_ptr:
mov end_r_data[si],bx
jmp next_inter
exit_handler:
mov al,20h out 20h,al
pop es pop ds pop di pop bp pop dx pop cx pop bx pop ax
pop si iret
int_hndlr4 ENDP int_hndlr3 ENDP int_hndlr2 ENDP int_hndlr1 ENDP
COM_TEXT ENDS
END
Теперь мы приведем исходные тексты включаемых файлов, используемых нашей программой.
// COMMON.H
#define equal(a,b) (!strcmp(a,b)) #define equali(a,b) (!stricmp(a,b)) #define equalni(a,b,n) (!strnicmp(a,b,n)) #define equaln(a,b,n) (!strncmp(a,b,n))
#define nil(type) ((type *)NULL)
#define boolean unsigned #define TRUE 1 #define FALSE 0
Файл CONF.H
#define OPERATOR(x) !strncmp(LineBuf,(x),strlen((x))) #define STRIP(x) (x)[strlen(x)-1] = 0;
Файл GET_WORD.H
unsigned expectstr(char *Search, unsigned int Timeout); void sendstr(char *str);
Файл MOD_LINK.H
#define CONN_INITIALIZE 1 #define CONN_CALLUP 2 #define CONN_HOTMODEM 3
#define CONN_ANSWER 4
#define CONN_LOGIN 5 #define CONN_HOTLOGIN 6
#define CONN_PROTOCOL 7 #define CONN_SERVER 8 #define CONN_CLIENT 9 #define CONN_TERMINATE 10 #define CONN_DROPLINE 11 #define CONN_EXIT 12
#define CONN_STATE char
Файл MODEM.H
int call( void ); int answer( void );
void slowwrite( char *s, int len); void shutdown( void );
extern char *device;
Файл TIMER.H
void sleep(time_t interval); void delay(int milliseconds);
Файл TOOLS.H
extern unsigned port_active;
int openline(char *name, unsigned baud);
unsigned int sread(char *buffer, unsigned int wanted, unsigned int timeout);
int swrite(char *data, unsigned int len);
void ssendbrk(unsigned int duration);
void closeline(void);
Файл TOTAL.H
#define WHITESPACE " \t\n\r"
#define equal(a,b) (!strcmp(a,b)) #define equali(a,b) (!stricmp(a,b)) #define equalni(a,b,n) (!strnicmp(a,b,n)) #define equaln(a,b,n) (!strncmp(a,b,n))
#define nil(type) ((type *)NULL)
Файл UART.H
/** *.Name select_port * *.Title Выбираем используемый COM-порт * *.Descr Эта функция определяет, с каким COM-портом * мы в дальнейшем будем работать. * *.Proto void far select_port(int port) * *.Params int port - номер порта (COM1..COM4) * *.Return не используется * *.Sample comm.asm **/
void far select_port(int);
/** *.Name save_com * *.Title Сохраняем старый вектор прерывания COM-порта * *.Descr Эта функция сохраняет адрес старого обработчика * прерываний от COM-порта. * *.Proto void far save_com(void) * *.Params не используется * *.Return не используется **/
void far save_com(void);
/** *.Name restore_com * *.Title Восстанавливаем старый вектор прерывания COM-порта * *.Descr Эта функция восстанавливает адрес старого обработчика * прерываний от COM-порта. * *.Proto void far restore_com(void) * *.Params не используется * *.Return не используется **/ void far restore_com(void);
/** *.Name install_com * *.Title Устанавливаем свой обработчик прерывания COM-порта * *.Descr Эта функция устанавливает новый обработчик * прерываний от COM-порта. * *.Proto int far install_com(void) * *.Params не используется * *.Return возвращает 1 при успешной установке * и 0 в случае ошибки **/ int far install_com(void);
/** *.Name install_com * *.Title Устанавливаем свой обработчик прерывания COM-порта * *.Descr Эта функция устанавливает новый обработчик * прерываний от COM-порта. * *.Proto void far open_com( int baud, int device, * int parity, int stop_bits ) * *.Params int baud - скорость обмена * int device - тип устройства связи: M - модем * D - нуль-модем * int parity - проверка на четность: N - нет проверки * на четность, O - проверка по нечетности, * E - проверка по четности, S - Space, * M - Mark * int stop_bits - количество стоповых бит * *.Return не используется **/ void far open_com( int, int, int, int );
/** *.Name close_com * *. Title Запрещаем прерывания от асинхронного порта COM-порта * *.Descr Эта функция запрещает прерывания от COM-порта * *.Proto void far close_com(void) * *.Params не используется * *.Return не используется **/ void far close_com(void); /* close com port */
/** *.Name dtr_off * *.Title Сбрасываем сигнал DTR * *.Proto void far dtr_off(void) * *.Params не используется * *.Return не используется **/ void far dtr_off(void);
/** *.Name dtr_on * *.Title Устанавливаем сигнал DTR * *.Proto void far dtr_on(void) * *.Params не используется * *.Return не используется **/ void far dtr_on(void);
/** *.Name r_count * *.Title Определяем состояние буфера приемника * *.Proto long far r_count(void) * *.Params не используется * *.Return в младшем байте - число байт в буфере приемника, * а в старшем - общий размер буфера приемника * **/ long far r_count(void);
// макроопределение r_count_size возвращает // общий размер буфера приемника
#define r_count_size() ((int)(r_count() >> 16))
// макроопределение r_count_pending возвращает // число байт в буфере приемника
#define r_count_pending() ((int)r_count())
/** *.Name receive_com * *.Title Получить один символ из буфера приемника * *.Proto int far receive_com(void) * *.Params не используется * *.Return очередной символ из буфера приемника **/ int far receive_com(void);
/** *.Name s_count * *.Title Определяем состояние буфера приемника * *.Proto long far s_count(void) * *.Params не используется * *.Return в младшем байте - число свободных байт в буфере передатчика, * а в старшем - общий размер буфера передатчика * **/ long far s_count(void);
// макроопределение s_count_size возвращает // общий размер буфера приемника
#define s_count_size() ((int)(s_count() >> 16))
// макроопределение s_count_pending возвращает // число байт в буфере приемника
#define s_count_free() ((int)s_count())
/** *.Name send_com * *.Title Передать один символ в буфер передатчика * *.Proto void far send_com(int ch) * *.Params int ch - передаваемый символ * *.Return не используется **/ void far send_com(int);
/** *.Name break_com * *.Title Передать сигнал BREAK удаленному модему * *.Proto void far break_com(void) * *.Params не используется * *.Return не используется **/ void far break_com(void);
/** *.Name com_errors * *.Title Возвращает указатель на массив счетчиков ошибок * *.Proto int far *far com_errors(void) * *.Params не используется * *.Return не используется **/ int far *far com_errors(void);
#define COM_EOVFLOW 0 // переполнен буфер #define COM_EOVRUN 1 // ошибка переполнения при приеме #define COM_EBREAK 2 // обнаружен запрос на прерывание #define COM_EFRAME 3 // ошибка синхронизации #define COM_EPARITY 4 // ошибка четности #define COM_EXMIT 5 // ошибка при передаче #define COM_EDSR 6 // не получен сигнал dsr #define COM_ECTS 7 // не получен сигнал cts