ANSI.SYS: что это?
Команды ANSI.SYS
Доступ к ANSI.SYS из коммуникационной программы
Использование драйвера ANSI.SYS
Работа с портами COM3 и COM4 в QuickBASIC
Как найти The Basic Network
Синтаксис и краткое описание некоторых ANSI-команд
Адреса коммуникационных портов
Листинг 1
Листинг 2
Листинг 3

Наблюдения за книжными прилавками показывают, что в последнее время снова накатила волна повышенного интереса к старым инструментальным средствам, таким как, например, 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 (далее ), а второй байт - 5BH (левая квадратная скобка), на Бейсике - CHR$(91). За этими байтами следуют параметры команды, а в заключение собственно сами команды. Параметры команды - это либо числа (в форме ASCII-символов, интерпретируемых как десятичные цифры), либо строки ASCII-символов, заключенные в кавычки, например: "строка параметров". Если в команде несколько параметров, то они разделяются точкой с запятой. Собственно код команды, завершающий команду драйвера ANSI, это всегда один символ алфавита.

Команды 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-команд

[row;colH - команда перемещает курсор в позицию, определенную параметрами row (ряд), col (колонка). Изначально каждый параметр равен единице. Ни один из них не возвращает текущее положение курсора.

[#A - команда передвигает курсор вверх на число строк, определенное параметром #. Изначально его значение равно единице. Команда игнорируется, если курсор находится в самом верхнем ряду.

[#B - команда передвигает курсор вниз на число строк, определенное параметром #. Изначально его значение равно единице. Команда игнорируется, если курсор находится в самом нижнем ряду.

[#C - команда перемещает курсор на число строк, определенное параметром #. Изначально его значение равно единице. Команда игнорируется, если курсор находится в крайней правой колонке.

[#D - команда перемещает курсор на число строк, определенное параметром #. Изначально его значение равно единице. Команда игнорируется, если курсор находится в самой левой колонке.

[row;colR - команда позиции курсора; генерируется командой состояния устройства. Параметр ряда содержит номер текущей строки и колонки, на которой находится курсор.

[6n - команда, вызывающая команду состояния позиции курсора.

[row;colf - команда горизонтальной и вертикальной позиций; работает так же, как и команда позиционирования курсора.

[s - команда сохраняет текущие параметры позиции курсора, т.е. номера колонки и ряда; используется с командой восстановления позиции курсора.

[u - команда восстанавливает заранее сохраненные параметры позиции.

[2J - команда очищает экран и возвращает курсор в исходную позицию.

[K - команда очищает содержимое строки от текущей позиции курсора до конца строки.

[#;...;#m - команда устанавливает графическое представление и меняет цвет экрана согласно указанным параметрам #. Ниже приведены возможные значения параметров # и описание каждого из них:

0 - все атрибуты выключены;
1 - установить жирный шрифт;
4 - установить подчеркивание (только монохромный режим экрана);
5 - установить мерцание;
7 - установить видеореверс;
8 - установить скрытый режим;
30 - черный шрифт;
31 - красный шрифт;
32 - зеленый шрифт;
33 - желтый шрифт;
34 - голубой шрифт;
35 - сиреневый шрифт;
36 - синий шрифт;
37 - белый шрифт;
40 - черный фон;
41 - красный фон;
42 - зеленый фон;
43 - желтый фон;
44 - голубой фон;
45 - сиреневый фон;
46 - синий фон;
47 - белый фон.

Адреса коммуникационных портов:

COM-порт
Базовый адрес порта
COM1
$H3F8
COM2
$H2F8
COM3
$H3F8
COM4
$H2F8

Листинг 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