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

Коммуникационная программа 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


Содержание раздела