Наблюдения за книжными прилавками показывают, что в последнее время снова накатила волна повышенного интереса к старым инструментальным средствам, таким как, например, QuickBASIC. Особым вниманием в конференциях сетей The Basic Network (см. врезку "Как найти The Basic Network") и FIDO пользуются работы отечественных авторов Григория Зельднера и Андрея Колесова. В материалах, передаваемых по сетям, много места уделяется программированию на QuickBASIC разнообразных устройств: модемов, звуковых плат и т. д. В данной публикации по программированию на этом замечательном простом и мощном языке высокого уровня мы затронем методы работы с так называемыми ESC-последовательностями, которые можно реализовать в коммуникационных программах при поддержке драйвера ANSI.SYS. Надеемся, что материал будет полезен и интересен как для опытных, так и для начинающих программистов.
ANSI.SYS: что это?
Драйвер ANSI.SYS - программа, значительно расширяющая возможности ввода с клавиатуры и вывода на экран. ANSI.SYS постоянно просматривает информацию, выводимую на экран, и выискивает в ней специальные коды, являющиеся командами для драйвера. Он распознает команды и затем удаляет их, так что на экране дисплея эти коды команд не появляются. ANSI.SYS выполняет разные функции. Но используя его в коммуникационных программах, вы можете представить себе ANSI.SYS как драйвер, который, интерпретируя специальные последовательности символов, называемые ANSI- или ESC-последовательностями, может очищать весь экран или часть его, помещать курсор в заданную позицию, изменять цвет шрифта и фона, выводить на экран данные.
Команды драйвера ANSI идентифицируются специальным кодом: в первом байте находится число 1BH, на Бейсике записывается как CHR$(27), называемое символом ESC (далее
Команды ANSI.SYS
Драйвер ANSI распознает множество команд, но все они распадаются на две категории: команды управления экраном и команды преобразования ввода с клавиатуры. Команды управления экраном, обрабатываемые драйвером ANSI, образуют целую систему для работы с полным экраном и могут выполнить почти все, что вообще позволяет экран дисплея. В нее входят команды перемещения курсора, очистки экрана, установки атрибутов изображения (цвет, подчеркивание, мерцание и т. д.), изменения режима изображения с текстового на графический и наоборот.
Когда к драйверу поступает какая-либо из команд преобразования ввода с клавиатуры, то он просматривает ее и заменяет символ, полученный в результате нажатия на одну из клавиш, на другой символ или даже целую цепочку символов.
Оба типа этих команд передаются драйверу ANSI одним и тем же путем - в потоке символов, выводимых на экран дисплея.
В коммуникационных программах наиболее часто применяются 13 ANSI-команд, обозначаемых символами: H A B C D R n f s u J K m. Обращайте свое внимание на регистр букв! "A" не эквивалентно "a" (см. врезку "Синтаксис и краткое описание некоторых ANSI-команд").
Доступ к ANSI.SYS из коммуникационной программы
Для доступа к ANSI.SYS вы должны открыть консоль как файл. (Исторически сложившийся термин "консоль" в этом случае обозначает то место, куда в данный момент происходит вывод информации. Открыть консоль как файл - назначить вывод данных в файл. - Прим. ред.) Это обеспечит доступ к драйверу ANSI.SYS, вызов которого должен быть помещен в вашем файле CONFIG.SYS. Открытие консоли легко осуществляется в QuickBASIC. Вам лишь необходимо ввести следующую команду:
OPEN "CON" FOR OUTPUT AS filenum
где filenum - номер файла. Например, если вы хотите открыть консоль как файл под номером 2, введите команду:
OPEN "CON" FOR OUTPUT AS 2
Следовательно, все, что вы будете писать в файл номер 2, будет обрабатываться не QuickBASIC, а ANSI.SYS.
Использование драйвера ANSI.SYS
Итак, мы собираемся управлять экраном только посредством ANSI.SYS. Трудность в том, что драйвер ANSI.SYS и интерпретатор языка QuickBASIC ничего "не знают" друг о друге. Если вы хотите использовать ANSI.SYS для работы с экранными функциями, не употребляйте конструкции типа LOCATE, PRINT, CLS, COLOR. Вместо них применяйте эквивалентные команды и посылайте их через ANSI.SYS, вводя:
PRINT #filenum, ANSIcommand;
где filenum - номер файла, который вы ранее открыли, а ANSIcommand - специальная команда ANSI, включающая любые необходимые параметры.
Другой важный момент - сохранение нескольких общих ANSI-символов и команд в строковых переменных типа SHARED. (SHARED обозначает, что объявленные таким образом переменные можно будет использовать во всех подпрограммах.)
Это облегчит программирование и увеличит скорость выполнения. Делается это следующим образом:
DIM SHARED ao$, cs$, es$, hc$
es$ = CHR$(27) 'это ESC-символ
ao$ = es$ + "[0m" 'отключаем все атрибуты экрана
cs$ = es$ + "[2J" 'стираем содержимое экрана и возвращаем курсор
hc$ = es$ + "[f" 'это возвращает курсор
Приведем некоторые примеры. Примем открытую консоль за файл номер 2. Предположим, что вы хотите изменить цвет шрифта на светло-синий на черном фоне перед печатью какого-либо текста. Вы могли бы воспользоваться оператором COLOR 11, 0. Но то же самое может сделать сам ANSI.SYS. Для этого вам нужно ввести следующую команду:
PRINT #2, es$ + "[0;1;36;40m";
где 0 - отключает все атрибуты; 1 - устанавливает жирный шрифт; 36 - устанавливает синий цвет шрифта; 40 - устанавливает черный фон; m - устанавливает графическое представление.
Допустим, вы хотите очистить экран и возвратить курсор. Для этого можно использовать оператор CLS, но воспользуемся конструкцией:
PRINT #2, cs$;
Предположим, вы редактируете поле и хотите имитировать принудительный возврат на один символ. Можно было бы воспользоваться последовательностью команд на QuickBASIC:
LOCATE , POS(0) - 1
PRINT " ";
LOCATE , POS(0) - 1
Заменим их аналогичными, но для драйвера ANSI:
PRINT #2, es$ + "[1D";
PRINT #2, " ";
PRINT #2, es$ + "[1D";
Необходимо заметить, что приведенная выше программа работает только с цветными мониторами.
Работа с портами COM3 и COM4 в QuickBASIC
В приведенном исходном тексте используются обращения к коммуникационным портам COM1 и COM2. Как известно, QuickBASIC может работать только с ними. А что же делать, если модем подключен к порту COM3 или COM4?
Такое устройство, как мышь, использующее COM1, конфликтует со всеми устройствами на COM3 (аналогичная проблема для COM2/ COM4). Что же делать, если модем установлен на COM4 и вы хотите использовать операторы OPEN COMx? Используйте адрес для COM4, по которому должен "сидеть" COM2.
Распечатки карт памяти для IBM-совместимых ПК показывают, что в области памяти, принадлежащей базовой системе ввода-вывода (BIOS), по адресам 0000:400 - 407 можно найти значения адресов для адаптеров последовательных портов COM1 - COM4. Таким образом, если у вас есть четыре коммуникационных порта, то при вводе команды D 0000:400 в командной строке отладчика Debug вы увидите примерно следующее:
0000:0400 F8 03 F8 02 E8 03 00 00-78 03 00 00 00 00 00 00 ........x.......
Числа F803 и F802 представляют собой адреса COM1 (3F8) и COM2 (2F8). Позиции для COM3 и COM4 будут содержать только нули.
В листинге 2 дан исходный текст программы, выполняющей последовательность действий:
1. Сохраняем текущие адреса в областях BIOS ($h400 - $h403) для дальнейшего сброса.
2. Определяем, какой порт необходим, и заполняем оператором POKE соответствующий адрес в COM1 ($h400 - $h401) или COM2 ($h402 - $h403).
3. Используем операторы OPEN COMx.
4. В конце программы восстанавливаем старые адреса, иначе могут возникнуть проблемы с ссылающимися на них устройствами.
Аналогичные функции выполняет простая программа, приведенная в листинге 3.
Таким образом, в зависимости от того, хотите ли вы больше возможностей и готовы пожертвовать для этого дополнительным местом, или привыкли довольствоваться малым, можете выбрать между программами, приведенными на листингах 2 или 3.
Михаил Евдокимов - программист, координатор сети The Basic Network. E-mail: michaely@mati.edu.ru
Как найти The Basic Network
Сеть The Basic Network (посвящена различным вопросам программирования не только на Бейсик) вышла за пределы Москвы: существуют узлы в таких городах, как Можайск, С.-Петербург, Иркутск, Междуреченск, Одесса, Киев. В ближайшее время откроются представительства в дальнем зарубежье (США, Англии, Канаде). За дополнительной информацией о сети The Basic Network вы можете обратиться по следующим телефонам в Москве: (095) 351-04-72 (Сергей Чабунин), 356-50-07 (Михаил Евдокимов), 356-52-82 (Андрей Юров), 412-38-76 (Михаил Ивановский), 232-11-66 (Игорь Кирисюк); в Иркутске: (395) 234-40-28 (Максим Пензин); в Одессе: 380 (482) 26-07-04 (Игорь Володин). По этим же телефонам вы можете договориться о подключении к сети. Можно послать запрос по затронутой в статье теме по электронной почте: michaely@mati.edu.ru, bcs@mati.edu.ru, maxp@max.irk.ru, vis@te.net.ua, а также через Internet: misha@ivanovsky.msk.ru, написав в поле Subject заголовка сетевые адреса координаторов The Basic Network: 777:5020/0 или 777:5020/1.
Синтаксис и краткое описание некоторых ANSI-команд
Адреса коммуникационных портов:
|
|
|
|
|
|
|
|
|
|
Листинг 1
' C.BAS - это элементарная коммуникационная программа, ' написанная на языке Microsoft QuickBASIC 4.5. Она ' открывает компьютерную консоль как файл, и все выводы ' на экран осуществляет через CON. ' Если у пользователя установлен правильно драйвер ' ANSI.SYS, то тогда C.BAS может использовать ' ANSI-ESC-последовательности для вывода цветов и ' ANSI-анимации на пользовательские экраны. ' Требования: ' 1) IBM-совместимый компьютер со свободными 128 Kбайт оперативной памяти; ' 2) модем; ' 3) цветные монитор и видеоплата; ' 4) загруженный в память драйвер ANSI.SYS. DECLARE SUB bottomline () ' Печатает 25 строку DECLARE SUB delay () ' Задержка времени DECLARE SUB getparms () ' You need these DECLARE SUB hangup () ' Повесить трубку DECLARE SUB initialize () ' Инициализация DECLARE SUB makeacall () ' Позвонить DECLARE SUB opencomport () ' Открыть порт DECLARE SUB outtahere () ' Выход! DIM SHARED ao$ ' Большинство подпрограмм DIM SHARED cs$ ' требует переменные, объявленные DIM SHARED bp$ ' с помощью SHARE DIM SHARED comport$ DIM SHARED comspec$ DIM SHARED es$ DIM SHARED dialmode$ DIM SHARED firsttime$ DIM SHARED init$ DIM SHARED hc$ ON ERROR GOTO errorroutine DEFINT A-Z es$ = CHR$(27) ' Код клавишив es$ ao$ = es$ + "[0m" ' Выключить атрибуты cs$ = es$ + "[2J" ' Очистить экран f1$ = CHR$(0) + CHR$(59) ' Клавиша f10$ = CHR$(0) + CHR$(68) ' Клавиша ff$ = CHR$(12) hc$ = es$ + "[f" ' Возвратить курсор OPEN "CON" FOR OUTPUT AS 2 ' Открыть консоль как файл CALL initialize ' Подпрограмма инициализации PRINT #2, ao$; ' Выключить атрибуты PRINT #2, cs$ + " " + hc$; ' Возвратить курсор CALL getparms CALL opencomport ' Открыть порт CALL bottomline ' Печатать самую ' нижнюю строку (25) DO ' Бесконечный цикл a$ = INKEY$ ' Проверка клавиатуры IF a$ <> "" THEN ' Нажата ли клавиша? SELECT CASE a$ ' Что делать? CASE es$ ' Клавиша ? CALL hangup ' Повесить трубку CASE f1$ ' Клавиша ? CALL makeacall CASE f10$ ' Клавиша ? CALL outtahere ' Выходим! CASE ELSE ' Какие-нибудь другие клавиши? PRINT #1, a$; ' Послать ее в модем END SELECT END IF WHILE NOT EOF(1) ' Прибыли ли символы? b$ = INPUT$(LOC(1), #1) ' Принять символы WHILE INSTR(b$, ff$) <> 0 ' Если ASCII-формат, q = INSTR(b$, ff$) ' тогда символы получены b$ = LEFT$(b$, q - 1) + cs$ + MID$(b$, q + 1) ' поменять ее на ANSI WEND PRINT #2, b$; ' Печатать символы a$ = INKEY$ ' Проверка клавиатуры IF a$ <> "" THEN ' Нажата ли клавиша? SELECT CASE a$ ' Что делать? CASE es$ ' Клавиша ? CALL hangup ' Повесить трубку CASE f1$ ' Клавиша ? CALL makeacall CASE f10$ ' Клавиша ? CALL outtahere ' Выходим! CASE ELSE ' Какие нибудь другие клавиши? PRINT #1, a$; ' Послать a$ в модем END SELECT END IF WEND LOOP ' Конец цикла errorroutine: PRINT "Error type "; ERR; " occurred!" RESUME NEXT SUB bottomline ' Печатаем строку 25 PRINT #2, cs$; ' Очищаем экран PRINT #2, es$ + "[25;1f"; ' LOCATE 25, 1 PRINT #2, es$ + "[1;37;44m"; 'Яркий белый на голубом фоне PRINT #2, "Communication (Little BBS) programe writtened using QB 4.5"; PRINT #2, es$ + "[1;33m"; ' Желтый на голубом фоне PRINT #2, " -Hang Up -Dial -End"; PRINT #2, ao$; ' Выключить атрибуты PRINT #2, hc$ + " " + hc$; ' Курсор в начало END SUB SUB delay ' Подпрограмма задержки now! = TIMER WHILE TIMER - now! < 1.5 WEND END SUB ' Подпрограмма получения параметров PRINT #2, cs$; SUB getparms 'Очистим экран IF firsttime$ = "on" THEN ' Пропустить это, если firsttime$ = "off" ' это ваш первый звонок; ELSE PRINT #2, es$ + "[1;36m"; ' Светло-синий PRINT #2, hc$; ' Курсор в начало PRINT #2, "Modem speed "; ' Скорость модема PRINT #2, es$ + "[1;33m"; ' Желтый PRINT #2, "3"; PRINT #2, es$ + "[1;36m"; ' Светло-синий PRINT #2, "00 "; PRINT #2, es$ + "[1;33m"; ' Желтый PRINT #2, "1"; PRINT #2, es$ + "[1;36m"; ' Светло-синий PRINT #2, "200 "; PRINT #2, es$ + "[1;33m"; ' Желтый PRINT #2, "2"; PRINT #2, es$ + "[1;36m"; ' Светло-синий PRINT #2, "400 "; PRINT #2, es$ + "[1;33m"; ' Желтый PRINT #2, "9"; PRINT #2, es$ + "[1;36m"; ' Светло-синий PRINT #2, "600 ["; PRINT #2, es$ + "[1;33m"; ' Желтый PRINT #2, "1"; PRINT #2, es$ + "[1;36m"; ' Светло-синий PRINT #2, "/"; PRINT #2, es$ + "[1;33m"; ' Желтый PRINT #2, "2"; PRINT #2, es$ + "[1;36m"; ' Светло-синий PRINT #2, "/"; PRINT #2, es$ + "[1;33m"; ' Желтый PRINT #2, "3"; PRINT #2, es$ + "[1;36m"; ' Светло-синий PRINT #2, "/"; PRINT #2, es$ + "[1;33m"; ' Желтый PRINT #2, "9"; PRINT #2, es$ + "[1;36m"; ' Светло-синий PRINT #2, "]? "; PRINT #2, es$ + "[1;33m"; ' Желтый bp$ = "" 'Инициализация в нуль WHILE bp$ <> "1" AND bp$ <> "2" AND bp$ <> "3" AND bp$ <> "9" bp$ = INKEY$ ' Ждем, пока не нажаты <1>, <2>, <3> WEND ' или <9> comspec$ = "COM" + comport$ + ":" ' Инициализировать ' comspec$ SELECT CASE bp$ ' Выбрать CASE "1" ' Нажата 1? PRINT #2, "1200 bps"; ' Скорость 1200 bps comspec$ = comspec$ + "1200" 'Добавить 1200 'к comspec$ CASE "2" ' Нажата 2? PRINT #2, "2400 bps"; ' Скорость 2400 bps comspec$ = comspec$ + "2400" 'Добавить 2400 'к comspec$ CASE "3" ' Нажата 3? PRINT #2, "300 bps"; ' Скорость 300 bps comspec$ = comspec$ + "300" 'Добавить 300 'к comspec$ CASE "9" ' Нажата 9? PRINT #2, "9600 bps"; ' Скорость 9600 bps comspec$ = comspec$ + "9600" 'Добавить 9600 'к comspec$ END SELECT ' Конец выбора comspec$ = comspec$ + ",N,8,1,DS" 'К полученной 'comspec$ 'добавить N81DS END IF END SUB SUB hangup ' Byebye, BBS PRINT #2, cs$; ' Очистим экран PRINT #2, es$ + "[1;5;37;41m"; ' Темно-белый на красном PRINT #2, es$ + "[25;39H"; ' LOCATE 25, 39 PRINT #2, "Wait"; ' Ждите CALL delay ' Вызвать подпрограмму задержки PRINT #1, "+++"; ' "Разбудить" модем CALL delay ' Вызвать подпрограмму задержки PRINT #1, "ATH0" ' Повесить трубку PRINT #2, ao$; ' Убрать атрибуты IF NOT EOF(1) THEN b$ = INPUT$(LOC(1), #1) END IF CALL bottomline END SUB SUB initialize ' Инициализация модема PRINT #2, ao$; ' Убрать атрибуты PRINT #2, cs$; ' Очистить экран PRINT #2, es$ + "[1;36m"; ' Светло-синий PRINT #2, "Modem initialization string? "; PRINT #2, es$ + "[1;33m"; ' Желтый a$ = "" WHILE a$ <> CHR$(13) ' Пока не нажата a$ = INKEY$ ' Получить код клавиши IF a$ = CHR$(8) AND LEN(init$) THEN ' ? PRINT #2, es$ + "[1D"; PRINT #2, " "; PRINT #2, es$ + "[1D"; IF LEN(init$) > 1 THEN init$ = LEFT$(init$, LEN(init$) - 1) ELSE init$ = "" END IF ELSE a$ = UCASE$(a$) IF a$ > " " THEN init$ = init$ + a$ ' Добавить символ к init$ PRINT #2, a$; ' Печатать символ END IF END IF WEND PRINT #2, cs$; ' Очистить экран PRINT #2, es$ + "[1;36m"; ' Светло-синий PRINT #2, "Comm Port ["; PRINT #2, es$ + "[1;33m"; ' Желтый PRINT #2, "1"; PRINT #2, es$ + "[1;36m"; ' Светло-синий PRINT #2, "/"; PRINT #2, es$ + "[1;33m"; ' Желтый PRINT #2, "2"; PRINT #2, es$ + "[1;36m"; ' Светло-синий PRINT #2, "]? "; PRINT #2, es$ + "[1;33m"; ' Желтый WHILE comport$ <> "1" AND comport$ <> "2" ' Ждать нажатия <1> или <2> comport$ = INKEY$ WEND PRINT #2, comport$; PRINT #2, cs$; ' Очистить экран PRINT #2, es$ + "[1;36m"; ' Светло-синий PRINT #2, "DIAL "; PRINT #2, es$ + "[1;33m"; ' Желтый PRINT #2, "P"; PRINT #2, es$ + "[1;36m"; ' Светло-синий PRINT #2, "ulse or "; PRINT #2, es$ + "[1;33m"; ' Желтый PRINT #2, "T"; PRINT #2, es$ + "[1;36m"; ' Светло-синий PRINT #2, "one ["; PRINT #2, es$ + "[1;33m"; ' Желтый PRINT #2, "P"; PRINT #2, es$ + "[1;36m"; ' Светло-синий PRINT #2, "/"; PRINT #2, es$ + "[1;33m"; ' Желтый PRINT #2, "T"; PRINT #2, es$ + "[1;36m"; ' Светло-синий PRINT #2, "]? "; WHILE dialmode$ <> "P" AND dialmode$ <> "T" ' Ждать нажатия или
dialmode$ = INKEY$ dialmode$ = UCASE$(dialmode$) 'Объявить заглавные буквы WEND PRINT #2, es$ + "[1;33m"; ' Желтый IF dialmode$ = "P" THEN ' Нажата клавиша ? PRINT #2, "Pulse"; ' Тип линии - импульсная, ELSE ' в противном случае - PRINT #2, "Tone"; ' тоновая END IF END SUB SUB makeacall ' Позвонить PRINT #2, cs$; ' Очистить экран PRINT #2, es$ + "[1;5;37;41m"; ' Мигание PRINT #2, es$ + "[25;39H"; ' LOCATE 25, 39 PRINT #2, "Wait"; CALL delay PRINT #1, "+++"; ' "Разбудить" модем CALL delay ' Задержка PRINT #1, "ATH0" ' Повесить трубку PRINT #2, ao$; ' Отключить все атрибуты IF NOT EOF(1) THEN b$ = INPUT$(LOC(1), #1) END IF CALL getparms ' Получить параметры CALL opencomport ' Открыть порт PRINT #2, cs$; ' Очистить экран PRINT #2, es$ + "[1;36m"; ' Светло-синий PRINT #2, "Phone number? "; PRINT #2, es$ + "[1;33m"; ' Желтый dial$ = "" ' Обнуляем WHILE a$ <> CHR$(13) ' Пока не нажата кл.
a$ = INKEY$ ' Записываем код нажатой клавиши IF a$ = CHR$(8) AND LEN(dial$) THEN ' PRINT #2, es$ + "[1D"; PRINT #2, " "; PRINT #2, es$ + "[1D"; ' Переместить ' курсор влево IF LEN(dial$) > 1 THEN dial$ = LEFT$(dial$, LEN(dial$) - 1) ' один символ ELSE ' или dial$ = "" ' обнуляем END IF ELSE a$ = UCASE$(a$) ' Объявить верхний регистр IF a$ > " " THEN ' Убрать странные символы dial$ = dial$ + a$ ' Добавить символ к dial$ PRINT #2, a$; ' Печатать символ END IF END IF WEND PRINT #1, "ATD" + dialmode$ + dial$ ' Звонить PRINT #2, ao$; ' Отключить атрибуты PRINT #2, cs$ + " " + hc$; ' Очистить экран END SUB SUB opencomport ' Открыть порт CLOSE #1 ' Закрыть Сom-порт OPEN comspec$ FOR RANDOM AS 1 'Открыть Сom-порт IF firsttime$ = "" THEN PRINT #1, init$ ' Послать строчку init$ в модем firsttime$ = "on" ' Запомнить END IF END SUB SUB outtahere ' Выход PRINT #2, cs$; ' Очистить экран PRINT #2, es$ + "[1;5;37;41m"; ' Мерцание PRINT #2, es$ + "[25;39H"; ' LOCATE 25, 39 PRINT #2, "Wait"; CALL delay ' Задержка PRINT #1, "+++"; ' "Разбудить" модем CALL delay ' Задержка PRINT #1, "ATH0" ' Повесить трубку IF NOT EOF(1) THEN ' Очистить Com-буфер b$ = INPUT$(LOC(1), #1) ' от неиспользованных END IF ' байтов CLOSE #1 ' Закрыть Com-порт PRINT #2, ao$; ' Отключить атрибуты PRINT #2, cs$ + " " + hc$; ' Очистить экран CLOSE #2 ' Закрыть консоль b$ = "" END END SUB
Листинг 2
' DIAL.BAS ' Поддерживаемые порты: COM1 - COM4 ' Синтаксис: DIAL portnum% (portnum% - номер порта) ' Замечания: переместите курсор на телефонный номер; ' : нажмите клавишу ']', переместитесь в ' : конец номера и нажмите; ' : используйте команды OPEN COMx DECLARE SUB Hangup (Port%) DECLARE SUB Getnum (row%, Col%, markit%, Port%) DECLARE SUB Setup (Port%) COLOR 0, 7 LOCATE 25, 1 PRINT " Переместите курсор в начало телефонного номера и нажмите <Пробел> (Spacebar) "; LOCATE 10, 1 IF VAL(COMMAND$) < 1 OR VAL(COMMAND$) > 4 THEN ' Получить номер порта (portnum%) PRINT "Номер порта должен быть указан в командной строке " END ELSE Port% = VAL(COMMAND$) END IF ' Устанавливаем некоторые специальные функции клавиатуры CR$ = CHR$(13) Nul$ = CHR$(0) ArrowLt$ = Nul$ + CHR$(75) ArrowRt$ = Nul$ + CHR$(77) ArrowUp$ = Nul$ + CHR$(72) ArrowDn$ = Nul$ + CHR$(80) EndKey$ = Nul$ + CHR$(79) Esc$ = CHR$(27) Home$ = Nul$ + CHR$(71) SpaceBar$ = CHR$(32) 'Сохраняем вектора в BIOS адреса для Com1-Com2 OldPort1H = PEEK(&H400) OldPort1L = PEEK(&H401) OldPort2H = PEEK(&H402) OldPort2L = PEEK(&H403) 'Перемещаем курсор по экрану DO ' Этот отрывок позволяет пользователю In$ = INKEY$ ' перемещать курсор по экрану SELECT CASE In$ ' и перейти в начало телефонного номера CASE CR$ IF markit% THEN ' Нажатие (CR$) ' означает конец выделения row% = CSRLIN Col% = POS(0) - count% EXIT DO END IF CASE Esc$ ' Нажатие (Esc$) - отмена операции END CASE Home$ ' Перейти в начало строки LOCATE , 1 CASE EndKey$ ' Перейти в конец строки LOCATE , 80 CASE ArrowUp$ ' Клавиша <Вверх> x% = CSRLIN IF x% > 1 THEN LOCATE x% - 1 CASE ArrowDn$ ' Клавиша <Вниз> x% = CSRLIN IF x% < 25 THEN LOCATE x% + 1 CASE ArrowLt$ ' Клавиша <Влево> IF POS(0) > 1 THEN LOCATE , POS(0) - 1 IF markit% THEN count% = count% - 1 'Если markit%, тогда был нажата клавиша <Пробел> CASE ArrowRt$ ' Клавиша <Вправо> IF markit% THEN 'Если markit%, тогда была нажата клавиша <Пробел> count% = count% + 1 row% = CSRLIN: Col% = POS(0) a% = SCREEN(row%, Col%) PRINT CHR$(a%); ELSE IF POS(0) < 80 THEN LOCATE , POS(0) + 1 END IF CASE SpaceBar$ IF markit% THEN 'Если markit%, тогда была нажата клавиша <Пробел> count% = count% + 1 row% = CSRLIN: Col% = POS(0) a% = SCREEN(row%, Col%) PRINT CHR$(a%); ELSE BEEP markit% = -1 ' Установим флаг для пометки номера END IF END SELECT LOCATE , , 1 ' Сохраним позицию курсора LOOP ' Получим номер телефона с экрана Getnum row%, Col%, count%, Port% ' Восстановим старые вектора CLOSE 1 DEF SEG = 0 POKE &H400, OldPort1H POKE &H401, OldPort1L POKE &H402, OldPort2H POKE &H403, OldPort2L DEF SEG END SUB Getnum (row%, Col%, markit%, Port%) IF row% < 1 THEN row% = 1: IF Col% < 1 THEN Col% = 1 LOCATE row%, Col% FOR x% = 0 TO markit% ' Прочитаем номер телефона с экрана a% = SCREEN(row%, Col% + x%) Dialstr$ = Dialstr$ + CHR$(a%) NEXT x% LOCATE 23, 25 PRINT "Dialing : "; Dialstr$; LOCATE 25, 1 PRINT "Поднимите трубку и нажмите клавишу <Пробел> или нажмите для отмены операции"; COLOR 7, 0 Setup Port% PRINT #1, "ATDP" + Dialstr$ ' Набрать номер DO b$ = INKEY$ IF b$ = " " THEN Hangup Port% EXIT DO END IF IF b$ = CHR$(27) THEN Hangup Port% EXIT DO END IF LOOP END SUB SUB Hangup (Port%) PRINT "...Disconnecting 1"; SELECT CASE Port% ' Снизим DTR CASE 1 OUT &H3FC, (INP(&H3FC) AND 252) ' Com1 CASE 2 OUT &H2FC, (INP(&H2FC) AND 252) ' Com2 CASE 3 OUT &H3FC, (INP(&H3FC) AND 252) ' Com3 CASE 4 OUT &H2FC, (INP(&H2FC) AND 252) ' Com4 END SELECT PRINT "...2..."; PRINT #1, "+++"; ' Переключить в командный режим SLEEP 1 PRINT #1, "ATH" ' Пошлем команду, ' чтобы повесить трубку PRINT "...CLICK"; END SUB SUB Setup (Port%) ' Если нужно, установим порты с помощью обмена адресов ' между Com4 и Com2 или Com3 и Com1 DEF SEG = 0 POKE &H400, &HF8 POKE &H401, 3 POKE &H402, &HF8 POKE &H403, 2 SELECT CASE Port% CASE 1 Start$ = "COM1:2400,N,8,1,DS0" CASE 2 Start$ = "COM2:2400,N,8,1,DS0" CASE 3 POKE &H400, &HE8 ' Для Com1 в Com3 POKE &H401, &H3 Start$ = "COM1:2400,N,8,1,DS0" CASE 4 POKE &H402, &HE8 ' Для Com2 в Com4 POKE &H403, &H2 Start$ = "COM2:2400,N,8,1,DS0" END SELECT DEF SEG OPEN Start$ FOR RANDOM AS 1 END SUB
Листинг 3
DEFINT C ' Считываем адреса портов Com2 и Com3 DEF SEG = 0 Com2 = PEEK(&H402) + 256 * PEEK(&H403) Com3 = PEEK(&H404) + 256 * PEEK(&H405) ' Меняем местами адреса портов Com2 и Com3 POKE &H402, Com3 AND 255 POKE &H403, Com3 256 POKE &H404, Com2 AND 255 POKE &H405, Com2 256 ' Следующая часть программы является простым ' примером работы предыдущих функций. ' Она протестирована на Hayes-совместимом ' модеме (ON COM3). Так как адреса портов ' поменялись местами, то вы можете открыть ' порт COM2 вместо COM3. CLS C = FREEFILE OPEN "Com2:2400,N,8,1,RS,DS" FOR RANDOM AS #C LEN = 40 ' Dial 999 9999 : введите нужный вам номер телефона PRINT #C, "ATDP 123 4567,,,,,,,,,,,,," PRINT "Нажмите любую клавишу, чтобы повесить трубку" k$ = INPUT$(1) ' Разъединить линию PRINT #C, "ATH+++" CLOSE #C ' Восстановить адреса портов Com2 и Com3 ' Это очень важно, так как если это не будет ' сделано, появятся проблемы с присоединенными ' к этим портам устройствами. Так, ' например, у вас исчезнет курсор мыши, и вы не ' сможете использовать ее до полной перезагрузки компьютера. POKE &H402, Com2 AND 255 POKE &H403, Com2 256 POKE &H404, Com3 AND 255 POKE &H405, Com3 256 END