Программирование модемов© Александр Фролов, Григорий ФроловТом 4, М.: Диалог-МИФИ, 1993, 236 стр. 5.4. Коммуникационная программа, использующая прерыванияПрограмма, представленная в предыдущей главе, имеет один большой недостаток: она должна постоянно производить опрос регистра состояния линии, с тем чтобы определить момент, когда от модема поступит очередной символ. В результате становится трудной, а иногда невозможной обработка поступающих символов. Например, если вы сразу отображаете символы, получаемые от COM-порта, на экране, то при использовании для этого функции putch() отдельные символы могут быть потеряны. Дело в том, что функция putch() работает слишком медленно и на скоростях 2400 бод и выше модем может успеть передать в COM-порт несколько новых символов, в то время как функция putch() еще не вывела на экран ни одного символа. В этом случае происходит ошибка переполнения входного буфера микросхемы UART (см. бит D2 регистра состояния линии). Таким образом, имеет смысл организовать прием и передачу символов модему в фоновом режиме, используя прерывания по окончании приема и передачи символа. Если ваша коммуникационная программа будет использовать прерывания, можно организовать буфер принимаемых и передаваемых данных. Обработчик прерываний должен проанализировать причину прерывания и либо передать в COM-порт очередной символ из буфера передатчика (если прерывание произошло в результате передачи очередного символа), либо считать поступивший символ из регистра данных и записать его в буфер приемника (если прерывание произошло в результате приема от модема очередного символа). В этом случае процесс обмена идет в фоновом режиме и процессор может спокойно заниматься обработкой принимаемых и передаваемых символов. Если программе понадобится передать данные модему, она может просто записать их в буфер передатчика. Для приема данных она должна считать их из буфера приемника. Принципы использования прерыванийПоследовательный асинхронный адаптер можно запрограммировать таким образом, что всякий раз, когда он примет или передаст очередной байт, будет выработано соответствующее прерывание. Прерывания могут вырабатываться асинхронным адаптером в следующих случаях:
Вы можете отдельно запрещать или разрешать эти прерывания. Для этого необходимо установить соответствующие биты в регистре управления прерываниями. Как приходит прерывание от COM-порта? Как мы
указывали ранее, каждому COM-порту соответствует,
кроме базового адреса его регистров, линия IRQ (см.
главы "Последовательный асинхронный
адаптер" и "COM-порт и номера IRQ"):
Заметим, что в данной таблице представлен только один возможный вариант соответствия номеру COM-порта линии IRQ. Некоторые платы асинхронных адаптеров и некоторые внутренние модемы имеют отдельно перемычки для выбора номера COM-порта (адреса базового регистра) и номера линии IRQ. Что представляет из себя обработчик прерываний асинхронного адаптера? После вызова обработчика прерываний он должен: Разрешить обработку прерыванийНеобходимо выполнить команду sti, для того чтобы разрешить обработку прерываний с более высоким приоритетом, чем прерывание от асинхронного адаптера. Определить причину прерывания Для этого следует считать содержимое регистра
идентификации прерываня. Состояние битов D1 D2
определяют причину прерывания:
В зависимости от того, какое произошло прерывание, его надо соответствующим образом обработать. Произошло прерывание по линии состоянияСчитать регистр состояния линии и конкретизировать причину прерывания (данное прерывание сбрасывается после чтения регистра состояния линии). Если это необходимо, подать основной программе сигнал о произошедшей ошибке с целью ее устранения. Например, в случае определения на линии сигнала BREAK (удаленный модем повесил трубку), надо попытаться возобновить связь. Прерывание по принятию данныхОчередной символ принят, и его можно считать через регистр данных. Прерывание сбрасывается после чтения регистра данных. Принятый байт необходимо записать в приемный буфер программы, из которого впоследствии его прочитает основная программа. Буфер приемника удобно организовать в виде очереди. Буфер передатчика пустПрерывание происходит в случае, если буфер передатчика пуст и можно передать COM-порту очередной символ. Можно организовать буфер передатчика программы, в который программа будет записывать данные, предназначенные для передачи через COM-порт. В этом случае, когда придет прерывание, надо считать очередной символ из буфера передатчика программы и записать его в регистр данных. Прерывание сбрасывается после записи очередного символа в регистр данных UART. Если нет данных для передачи (программный буфер передатчика пуст), можно запретить это прерывание через регистр управления прерываниями. Изменилось состояние модемаПрерывание происходит при изменении состояния входных линий CTS, RI, DCD, DSR. Состояние этих линий можно определить, считав регистр состояния модема. Это прерывание используется для обнаружения звонка на телефонной линии. Прерывание автоматически сбрасывается после чтения регистра состояния модема. Считать регистр идентификации прерыванияМожет случиться, что одновременно произойдет несколько прерываний. В этом случае бит D0 регистра идентификации прерываний равен единице. Тогда перед завершением обработки прерывания необходимо обработать следующее прерывание в соответствии с состоянием битов D1, D2. Так следует поступать до тех пор, пока не будут обработаны все прерывания (бит D0 не станет равен нулю). Обработать конец прерыванияПередать контроллеру прерываний команду обработки конца прерывания. Для этого посылается в порт с адресом 20h команда конца прерывания: mov al,20h out 20h,al Закончить обработку прерыванияТеперь можно закончить обработку прерывания, выполнив команду iret. Итак, мы завершили рассмотрение обработчика прерываний. Теперь нам осталось изучить примерный порядок работы коммуникационной программы с использованием прерываний. Установить обработчик прерыванийНеобходимо установить обработчик прерываний, изменив соответствующий элемент таблицы векторов прерываний. Адрес старого обработчика сохраняется в глобальных переменных. Инициализация COM-портаСначала надо перевести в неактивное состояние линии DTR и RTS. Затем сбросить регистр состояния линии, регистр состояния модема и регистр данных. После того как мы сбросили регистры UART, можно приступить к инициализации COM-порта. Во время инициализации задается формат данных - длина слова, количество стоповых битов, наличие контроля по четности и скорость обмена. Последним шагом в инициализации регистров UART является установка регистра управления прерываниями, в который записывается соответствующее значение. Например, чтобы разрешить генерацию прерываний при приеме очередного символа, надо записать значение 01h в регистр управления прерываниями: // устанавливаем регистр управления прерываниями outp(port_adr+ICR, 1); // ICR - адрес регистра // управления прерываниями На этом этап инициализации регистров UART можно считать законченным. Теперь COM-порт подготовлен для обмена через него данными с модемом, но модем пока еще не будет воспринимать данные от компьютера. Чтобы перевести его в рабочее состояние, надо передать ему сигналы DTR и RTS. В ответ на эти сигналы модем должен вернуть компьютеру сигналы DSR и CTS. Инициализация контроллера прерывнийДля того чтобы прерывания от асинхронного адаптера выполнялись, необходимо разрешить прерывание по соответствующей линии IRQ через регистр маски прерываний контроллера прерываний: // считываем состояние регистра маски прерываний mov dx,21h in dx,al // разрешаем прерывания от порта COM1 and al,11101111b // записываем новое значение в регистр маски прерываний out dx,al Инициализация модема и установление связиПосле установки обработчика прерываний и инициализации регистров COM-порта и контроллера прерываний можно передавать модему AT-команды и принимать от него ответ на них. При этом данные можно считывать (записывать) из COM-порта через буфер обработчика прерываний. Обмен данными с удаленным модемомУстановив связь с удаленным модемом, оба модема переходят в режим обмена данными. Теперь можно начинать передавать и принимать данные. Передача и прием данных от удаленного модема осуществляются так же, как передача модему команд и прием от него сообщениий. Завершение программыДля завершения коммуникационной программы, использующей прерывания, необходимо сбросить сигналы DTR и RTS и запретить через контроллер прерываний прерывания от COM-порта: // считываем состояние регистра маски прерываний mov dx,21h in dx,al // запрещаем прерывания от порта COM1 or al,00010000b // записываем новое значение в регистр маски прерываний out dx,al Затем нужно восстановить старый вектор обработчика прерываний. Коммуникационная программа CHATВ этой главе мы приведем исходный текст коммуникационной программы CHAT. В отличие от программы CHAT_S данная программа использует для работы с асинхронным адаптером прерывания. При помощи этой программы можно связаться с удаленным модемом, передавать и принимать от него данные в формате ASCII. Например, вы можете позвонить на станцию BBS и прочитать почтовые сообщения. Передачу и прием файлов программа не поддерживает, иначе пример занимал бы слишком много места. Данная телекоммуникационная программа может работать в двух режимах - активном, когда она сама производит вызов удаленного модема, и пассивном, когда программа находится в режиме ожидания звонка от удаленного модема. Для работы программы в активном режиме необходимо запустить ее с параметром "1", для пассивного режима - "0". Большинство параметров программы, таких, как AT-команды инициализации, телефонный номер, скорость обмена и номер COM-порта, можно настроить через файл конфигурации setup.cfg. Образец этого файла представлен ниже: // строка инициализации для режима активного вызова Initialize ATS0=0Q0E0M1V1X4&C1&D2 // команда, которая переводит модем в командный режим и // кладет трубку Dropline \d\d+++\d\dATH0\n\r\d // строка инициализации для режима ожидания звонка AutoAnswer ATS0=1Q0E0M1V1X4&C1&D2 // префикс телефонного номера DialPrefix \r\pATDP // суффикс телефонного номера DialSuffix // телефонный номер DialNumber 1135810 // номер COM-порта в формате COMn, где n - номер порта Device COM3 // время, отведенное на установку связи с удаленным модемом DialTimeout 30 TimeoutAnswer 30 // временная задержка между сиимволами при передаче CharDelay 0 // время реакции модема на команды ModemTimeout 3 // скорость обмена данными Speed 2400 Программа состоит из следующих модулей: CHAT.C // главная процедура программы MODEM.C // передача данных модему через COM-порт TIMER.C // реализация временных задержек CONF.C // чтение файла конфигурации SEND_COMM.C // передача команд модему TOOLS.C // набор функций для работы с модулем UART.ASM UART.ASM // обработчик прерываний и процедуры низкого уровня Теперь приведем сами исходные тексты программы. Основной модуль программы называется CHAT.C. В зависимости от параметра программы этот модуль вызывает функцию call() - режим вызова удаленного модема - или функцию answer() - режим ответа на приходящие звонки. Обе функции, call() и answer(), выполняют все действия по программированию COM-порта, контроллера прерываний и модема. Данные функции определены в модуле MODEM.C. После окончания связи вызывается функция shutdown(), которая опускает телефонную трубку и отключает обработчик прерываний. // CHAT.C // основной модуль коммуникационной программы #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <sys/types.h> #include "mod_link.h" #include "common.h" #include "modem.h" #include "timer.h" #include "tools.h" void hello(void) { printf("неправильно задан параметр программы \n" "mod_link n, где n = 1 вызов, n = 0 ответ\n"); exit(0); } // основная процедура void main( int argc, char *argv[] ) { // программа должна вызываться параметром 1 или 0 // 1 - это активный режим, когда программа сама вызывает // удаленный модем // 0 - программа находится в режиме автоответа на // приходящий вызов if( argc < 2 ) hello(); 0 if( strcmp( argv[1], "0") && strcmp( argv[1], "1" )) hello(); if( !strcmp( argv[1], "1" ) ) // если программа запущена с параметром "1", вызывается // функция call() из модуля MODEM.C, выполняющая вызов // удаленного модема call(); else // если программа запущена с параметром "0", вызывается // функция answer(), переключающая модем в режим автоответа answer(); // освобождаем телефон shutdown(); } Данный модуль определяет функции высокого уровня для работы с модемом:
Эти функции вызывают модули более низкого уровня: 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 |