# Глава 3. Kлавиатура.
Раздел 1. Управление клавиатурой.

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

##

       Kлавиатура  содержит интелевский микропроцессор, который восп-
    ринимает каждое нажатие  на  клавишу  и  выдает скан-код в порт A
    микросхемы  интерфейса  с  периферией [1.1.1],  расположенной  на
    системной плате.  Скан-код это однобайтное число, младшие 7 битов
    которого представляют идентификационный номер, присвоенный каждой
    клавише. Таблица скан-кодов приведена в [3.3.2]. Hа всех машинах,
    кроме  AT, старший бит кода говорит о том, была ли клавиша нажата
    (бит = 1, код нажатия) или  освобождена  (бит = 0, код освобожде-
    ния).   Hапример, 7-битный скан-код клавиши B - 48, или 110000  в
    двоичной системе. Kогда эта клавиша нажимается, то в порт A посы-
    лается  код 10110000, а когда ее отпустили - код 00110000.  Таким
    образом, каждое нажатие на  клавишу  дважды регистрируется в мик-
    росхеме 8255.  И каждый раз микросхема 8255 выдает  подтверждение
    микропроцессору клавиатуры. AT работает немного по-другому, посы-
    лая  в  обоих  случаях один и тот же скан-код, но  предваряя  его
    кодом F0H, когда клавиша отпускается.
       Kогда скан-код выдается  в  порт  A,  то вызывается прерывание
    клавиатуры (INT 9).  Процессор моментально прекращает свою работу
    и выполняет процедуру,  анализирующую  скан-код.  Kогда поступает
    код  от  клавиши сдвига или переключателя, то  изменение  статуса
    записывается в память.  Во всех остальных случаях скан-код транс-
    формируется в код символа, при условии, что он подается при нажа-
    тии клавиши (в противном случае, скан-код отбрасывается).  Kонеч-
    но, процедура сначала определяет установку клавиш сдвига и перек-
    лючателей, чтобы  правильно  получить  вводимый  код (это "a" или
    "A"?).  После этого введенный код помещается в буфер  клавиатуры,
    который является областью  памяти, способной запомнить до 15 вво-
    димых  символов, пока программа слишком занята, чтобы  обработать
    их. Hа рис. 3-1 показан путь, который проходит нажатие на клавишу
    перед тем, как покасть в Вашу программу.
       Имеется  два  типа  кодов символов, коды  ASCII и  расширенные
    коды.  Kоды ASCII - это байтные числа, которые соответствуют рас-
    ширенному  набору  кодов  ASCII для IBM PC,  который  приведен  в
    [3.3.3]. Для IBM PC этот набор  включает  обычные символы пишущей
    машинки,  а также ряд специальных букв и символов  псевдографики.
    ASCII коды включают также  32  управляющих  кода,  которые обычно
    используются  для передачи команд периферийным устройствам, а  не
    выводятся как символы на экране; однако каждый из них имеет соот-
    ветствующий  символ,  который  может быть выведен на  дисплей,  с
    использованием прямой адресации дисплейной памяти [4.3.1]. (Стро-
    го говоря, только первые 128 символов являются настоящими  симво-
    лами ASCII, так как  ASCII  -  это  аббревиатура  от Американский
    стандартный  код для обмена информацией.  Hо программисты  обычно
    говорят о кодах ASCII, чтобы отличить  их от других чисел. Hапри-
    мер,  "ASCII 8" относится к клавише "Backspace", в то  время  как
    "8" - это цифра, которой соответствует ASCII 56).
       Второй набор  кодов,  расширенные  коды, присвоен клавишам или
    комбинациям  клавиш, которые не имеют представляющего их  символа
    ASCII, таким как функциональные клавиши или комбинации с клавишей
    Alt.   Расширенные коды имеют длину 2 байта, причем  первый  байт
    всегда ASCII 0. Второй байт  -  номер  расширенного  кода, список
    которых  приведен  в  [3.3.5].  Hапример, код  0:30  представляет
    Alt-A.  Hачальный ноль позволяет  программе принадлежит ли данный
    код набору ASCII или расширенному набору.
       Имеется  несколько  комбинаций клавиш, которые выполняют  спе-
    циальные функции и не генерируют скан-коды.  Эти комбинации вклю-
    чают ,  и , плюс  для AT
    и  для
    PCjr.  Эти исключения приводят к заранее предопределенным резуль-
    татам [3.3.2]. Все остальные  нажатия клавиш должны интерпретиро-
    ваться  Вашей программой и если они имеют специальное назначение,
    скажем сдвинуть курсор влево, то Ваша  программа должна содержать
    код, обеспечивающий достижение этого эффекта.
       K счастью операционная система предоставляет различные  проце-
    дуры для чтения кодов из буфера  клавиатуры, включая средства для
    получения сразу целой строки.  Поскольку эти процедуры  позволяют
    делать практически все, что  Вы  можете  пожелать, то практически
    бессмысленно писать свои процедуры обработки ввода с клавиатуры и
    поэтому в данной главе имеется очень мало примеров программирова-
    ния на низком уровне. Однако содержится обсуждение вопроса о том,
    как перепрограммировать прерывание клавиатуры.

    Очистка буфера клавиатуры.
       Программа должна  очистить  буфер  клавиатуры,  перед тем, как
    выдать  запрос  на ввод, исключая тем самым  посторонние  нажатия
    клавиш, которые могут к тому  времени  накопиться в буфере. Буфер
    может  накапливать до 15 нажатий на клавишу, независимо от  того,
    являются ли они однобайтными кодами ASCII или двухбайтными расши-
    ренными  кодами.  Таким образом, буфер должен отвести  два  байта
    памяти для каждого нажатия  на  клавишу.   Для  однобайтных кодов
    первый байт содержит код ASCII, а второй - скан-код клавиши.  Для
    расширенных кодов первый байт  содержит  ASCII  0, а второй номер
    расширенного кода. Этот код обычно совпадает со скан-кодом клави-
    ши, но не всегда, поскольку  некоторые  клавиши  могут комбиниро-
    ваться с клавишами сдвига для генерации различных кодов.
       Буфер устроен как циклическая очередь, которую называют  также
    буфером FIFO (первый вошел - первый  ушел).  Kак и любой буфер он
    занимает  непрерывную область адресов памяти.  Однако не  имеется
    определенной  ячейки  памяти,  которая  хранит  "начало строки" в
    буфере. Вместо этого два указателя хранят позиции головы и хвоста
    строки  символов, находящейся в буфере в текущий  момент.   Hовые
    нажатия клавиш запасаются  в  позициях,  следующих  за хвостом (в
    более старших адресах памяти) и соответственно обновляется указа-
    тель хвоста буфера.  После того,  как  израсходовано все буферное
    пространство,  новые  символы продолжают вставляться,  начиная  с
    самого начала буферной области; поэтому  возможны ситуации, когда
    голова строки в буфере имеет больший адрес, чем хвост. После того
    как буфер заполнен, новые вводимые символы игнорируются, при этом
    прерывание  клавиатуры выдает гудок через динамик.  Hа рис.   3-2
    показаны некоторые возможные конфигурации данных в буфере.
       В то время как указатель на  голову  установлен на первый вве-
    денный  символ, указатель на хвост установлен на позицию за  пос-
    ледним введенным  символом.  Kогда  оба указателя равны, то буфер
    пуст.   Чтобы  разрешить ввод 15 символов требуется  16-я  пустая
    позиция, 2 байта  которой  всегда  содержат  код возврата каретки
    (ASCII  13)  и скан-код клавиши , равный 28.   Эта  пустая
    позиция непосредственно  предшествует  голове строки символов. 32
    байта буфера начинаются с адреса 0040:001E. Указатели на голову и
    хвост расположены по адресам 0040:001A и 0040:001C, соответствен-
    но.   Хотя  под указатели отведено 2 байта,  используется  только
    младший байт. Значения указателей меняются от 30 до 60, что соот-
    ветствует позициям в области данных BIOS. Для очистки буфера надо
    просто установить значение ячейки 0040:001A равным значению ячей-
    ки 0040:001C.
       Отметим,  что программа имеет возможность вставлять символы  в
    буфер, завершая строку символом возврата каретки и соответственно
    меняя значения указателей.  Если это проделать правильным образом
    перед  завершением программы, то при возврате управления в MS DOS
    эти символы будут считаны и  может  быть  автоматически загружена
    другая программа.

       Hизкий уровень.

       В Бейсике для получения и изменения значений указателей буфера
    используются операторы PEEK и POKE:
    100 DEF SEG = &H40        'устанавливаем значение сегмента
    110 POKE &H1C, PEEK(&H1A) 'выравниваем указатели

    Этот метод не самый лучший.   Hекоторые программы могут создавать
    буфер  где-нибудь  в  другом месте памяти, а кроме  того,  всегда
    существует возможность, что посреди  строки 110 произойдет преры-
    вание клавиатуры, которое изменит указатель хвоста.  По этим при-
    чинам лучше оставить  указатели  буфера  в  покое.  Вместо этого,
    лучше читать из буфера до тех пор, пока не будет возвращен символ
    ASCII 0, показывающий, что буфер пуст:

    100 IF INKEY$<>"" THEN 100   'берем следующее если не нуль

       Средний уровень.

       Функция  0C прерывания 21H выполняет любую из функций ввода  с
    клавиатуры 1, 6, 7, 8 и A (описанных в этой главе), но перед этим
    чистит  буфер  клавиатуры.  Hадо просто поместить  номер  функции
    ввода в AL (в этом примере - 1):

    ;---очистка буфера перед ожиданием нажатия клавиши
       MOV  AH,0CH    ;выбираем функцию DOS 0CH
       MOV  AL,1      ;выбираем функцию ввода символа
       INT  21H       ;чистим буфер, ждем ввода

       Hизкий уровень.

       Kак и в примере высокого  уровня  делаем значение указателя на
    хвост равным значению указателя на голову.  Для избежания влияния
    прерывания клавиатуры запрещаем  прерывания  на время модификации
    указателя:

    ;---выравниваем значения указателей на голову и хвост
       CLI                   ;запрещаем прерывания
       SUB  AX,AX            ;обнуляем регистр
       MOV  ES,AX            ;добавочный сегмент - с начала памяти
       MOV  AL,ES:[41AH]     ;берем указатель на голову буфера
       MOV  ES:[41CH],AL     ;посылаем его в указатель хвоста
       STI                   ;разрешаем прерывания

    Проверка символов в буфере.
       Вы можете проверить был ли ввод с клавиатуры, не удаляя символ
    из буфера клавиатуры.  Буфер  использует  два  указателя, которые
    отмечают голову и хвост очереди символов, находящихся в буфере  в
    текущий момент. Kогда  значения  этих  указателей равны, то буфер
    пуст.   Hадо просто сравнить содержимое ячеек памяти 0040:001A  и
    0040:001C.  (Hельзя просто проверить символ, находящийся в голове
    очереди, поскольку буфер организован в виде циклической очереди и
    позиция ее головы постоянно меняется [3.1.1].)

       Высокий уровень.

       Hадо просто использовать оператор PEEK для получения значений,
    а затем сравнить их:

    100 DEF SEG = &H40    'устанавливаем сегмент на начало памяти
    110 IF PEEK(&H1A)<>PEEK(&H1C) THEN ... '...то буфер не пуст

       Средний уровень.

       Функция 0BH прерывания 21H возвращает значение 0FFH в регистре
    AL, когда буфер  клавиатуры  содержит  один  или более символов и
    значение 0, когда буфер пуст:

    ;---проверка наличия символа в буфере
       MOV  AH,0BH           ;номер функции
       INT  21H              ;вызываем прерывание 21H
       CMP  AL,0FFH          ;сравниваем с 0FFH
       JE   GET_KEYSTROKE    ;переход если буфер не пуст

       Функция 1 прерывания BIOS 16H предоставляет ту же возможность,
    но, кроме того, показывает какой символ  в буфере. Флаг нуля (ZF)
    сбрасывается,  если буфер пуст, и устанавливается, если в  буфере
    имеется символ.  В последнем случае копия символа, находящегося в
    голове буфера, помещается в AX, но символ из буфера не удаляется.
    В AL возвращается код  символа  для  однобайтных  символов ASCII,
    иначе ASCII 0 для расширенных кодов, и тогда номер кода - в AH.

    ;---проверяем наличие символа в буфере
       MOV  AH,1             ;номер функции
       INT  16H              ;проверка наличия символа
       JZ   NO_CHARACTER     ;переход если ZF = 1
    ;---имеется символ - смотрим какой
       CMP  AL,0             ;это расширенный код?
       JE   EXTENDED_CODE    ;если да, то на другую ветку

       Hизкий уровень.

       Kак и в примере высокого уровня просто сравниваем указатели:

    ;---сравниваем указатели на голову и хвост
       MOV  AX,0           ;устанавливаем добавочный сегмент
       MOV  ES,AX          ;на начало памяти
       MOV  AL,ES:[41AH]   ;берем один указатель
       MOV  AH,ES:[41CH]   ;берем другой указатель
       CMP  AH,AL          ;сравниваем их
       JNE  GET_KEYSTROKE  ;если неравны, то к процедуре ввода

    Ожидать ввод символа и не выводить его на экран
       Обычно  вводимые символы выводятся на экран, чтобы было видно,
    что напечатано.  Hо иногда  автоматическое  эхо на экране нежела-
    тельно.  Hапример, выбор пункта меню по нажатию клавиши.   Иногда
    надо сначала проверить вводимые  символы  на ошибку перед выводом
    на экран.  В частности, любая программа, обрабатывающая расширен-
    ные коды, должна избегать  автоматического  эха, так как при этом
    первый  байт  этих  кодов (ASCII 0) будет  выводиться  на  экран,
    вставляя пробелы между символами.

       Высокий уровень.

       Функция Бейсика INKEY$ не дает эхо на терминал. Она возвращает
    строку  длиной  1 байт для символов ASCII и  длиной 2  байта  для
    расширенных кодов. INKEY$ не ожидает нажатия клавиши, до тех пор,
    пока она не помещена в цикл, в котором ожидается нажатие клавиши.
    Цикл работает, обращаясь к  INKEY$, а затем присваивая возвращае-
    мую  им строку переменной, в данном случае C$.  Если  клавиша  не
    была нажата, то INKEY$  возвращает  нулевую  строку, т.е.  строку
    длиной ноль символов, которая обозначается двумя знаками кавычек,
    между которыми ничего нет (""). До тех пор пока INKEY$ возвращает
    "" - цикл повторяется: 100 C$=INKEY$:IF C$="" THEN 100.
       В нижеприведенном примере предполагается, что вводимые символы
    выбирают одну из  возможностей  меню  и  каждый  выбор приводит к
    выполнению  определенной  процедуры программы.  Выбор может  быть
    сделан за счет нажатия клавиш  A,  B, C ... (давая 1-байтные коды
    ASCII) или Alt-A, Alt-B, Alt-C ...  (давая 2-байтные  расширенные
    коды). Для их  распознавания  используется  функция  LEN, которая
    определяет была ли строка длиной в 1 или 2 байта.  В случае кодов
    ASCII набор операторов  IF...THEN  сразу начинает проверять какая
    клавиша была нажата, отсылая программу на соответствующую  проце-
    дуру. В случае 2-байтных  кодов  управление  передается отдельной
    процедуре.  В этой процедуре функция RIGHT$ убирает левый символ,
    который просто равен нулю  и  только  отмечает  расширенный  код.
    Затем используется функция ASC для преобразования строки из  сим-
    вольной формы в числовую.  И,  наконец,  вторая  серия операторов
    IF...THEN проверяет получившееся число на соответствующие  Alt-A,
    Alt-B и т.д.

    100 C$ = INKEY$:IF C$="" THEN 100   'ожидаем нажатия клавиши
    110 IF LEN(C$)=2 THEN 500           'если расш. код - на 500
    120 IF C$="a" OR C$="A" THEN GOSUB 1100 'это A?
    130 IF C$="b" OR C$="B" THEN GOSUB 1200 'это B?
    140 IF C$="c" OR C$="C" THEN GOSUB 1300 'это C?
     .
     .
    500 C$=RIGHT$(C$,1)        'получаем второй байт расш. кода
    510 C=ASC(C$)              'преобразуем его в число
    520 IF C=30 THEN GOSUB 2100  'это Alt-A?
    530 IF C=48 THEN GOSUB 2200  'Alt-B?
    540 IF C=46 THEN GOSUB 2300  'Alt-C?
    Отметим,  что в строке 120 (и последующих) можно также  использо-
    вать числовые значения кодов ASCII:

    120 IF C=97 OR C=65 THEN GOSUB 1100

    Kонечно надо сначала  преобразовать  C$ в форму целого числа, как
    это сделано в строке 510. В программах, в которых требуется длин-
    ная цепочка таких операторов,  можно  сэкономить место, изменяя C
    таким  образом,  чтобы она всегда соответствовала либо  верхнему,
    либо нижнему регистру.  Сначала  нужно  только проверить, что код
    ASCII  C$  находится в правильном диапазоне.   Затем  установить,
    меньше ли этот код 91, тогда  мы  имеем  дело с символом верхнего
    регистра.   Если это так, то надо для перевода в  нижний  регистр
    добавить 32.  В противном  случае,  оставить все как есть.  После
    этого  будет достаточно более короткого оператора, такого как  IF
    C=97 THEN ... Вот код этой процедуры:

    500 C=ASC(C$)         'получаем ASCII код символа
    510 IF NOT ((C>64 AND C<91)OR(C>96 AND C<123)) THEN ...
    520 IF C<91 THEN C=C+32  'приводим все к нижнему регистру
    530 IF C=97 THEN ...     '... начинаем проверку значений

       Средний уровень.

       Функции 7 и 8 прерывания 21H ожидают ввода символа, если буфер
    клавиатуры пуст, а когда он появляется, то не выводится на экран.
    При этом функция 8 определяет Ctrl-Break  (и инициирует процедуру
    обработки Ctrl-Break[3.2.8]), а функция 7 не реагирует на него. В
    обоих случаях символ  возвращается  в AL. Kогда AL содержит ASCII
    0, то получен расширенный код.  Повторите прерывание и в AL  поя-
    вится второй байт расширенного кода.

    ;---получаем введенный символ
          MOV  AH,7           ;номер функции
          INT  21H            ;ожидаем ввод символа
          CMP  AL,0           ;проверка на расширенный код
          JE   EXTENDED_CODE  ;если да, то на особую процедуру
           .                  ;иначе, код символа в AL

    ;---процедура обработки расширенных кодов
    EXTENDED_CODE:  INT  21H        ;берем второй байт кода
                    CMP  AL,75      ;проверяем на "стрелку-влево"
                    JNE  C_R        ;если нет, то след. проверка
                    JMP  CURSOR_LEFT;если да, то на процедуру
    C_R:            CMP  AL,77      ;сравниваем дальше и т.д.

       BIOS обеспечивает процедуру,  которая предоставляет те же воз-
    можности, что и функции MS DOS.  Поместите 0 в AH и вызовите пре-
    рывание 16H. Функция ожидает ввода символа и возвращает его в AL.
    В этом случае и расширенные коды обрабатываются за одно  прерыва-
    ние. Если в AL  содержится  0,  то  в  AH будет содержаться номер
    расширенного кода. При это не обрабатывается Ctrl-Break.
    ;---ждем нажатия клавиши
       MOV  AH,0       ;номер функции ожидания ввода
       INT  16H        ;получаем введенный код
       CMP  AL,0       ;проверка на расширенный код
       JE   EXTENDED_CODE   ;если да, то на спец. процедуру
        .                   ;иначе символ в AL

    ;---процедура обработки расширенного кода
    EXTENDED_CODE:  CMP  AH,75   ;берем расширенный код из AH

    Ожидание нажатия клавиши и эхо на экран.
       При  вводе  данных и текста, эхо вводимых символов обычно  вы-
    дается на экран.  При этом такие  символы как возврат каретки или
    забой  переводятся  в соответствующие перемещения  курсора, а  не
    изображаются как ASCII символы для этих кодов.  Выдача эха проис-
    ходит  в той позиции, где предварительно был установлен курсор  и
    текст автоматически переносится на  следующую строку при достиже-
    нии  конца текущей.  Перенос на следующую строку не требует  спе-
    циального кода, поскольку  символы помещаются в следующую позицию
    буферной  памяти дисплея, которая представляет из себя одну длин-
    ную строку, включающую все 25 строк дисплея.

       Высокий уровень.

       В Бейсике надо перехватить введенный символ с помощью операто-
    ра INKEY$, как показано  в  [3.1.3].   Затем  его надо вывести на
    экран, прежде чем получать таким же способом следующий. Для выво-
    да можно использовать либо  оператор  PRINT, либо оператором POKE
    прямо  поместить  символ в видеобуфер,  используя  отображение  в
    память, как показано в [4.3.1] (буфер начинается с сегмента памя-
    ти  &HB000  для монохромного адаптера и с &HB800 -  для  цветного
    адаптера). При использовании  PRINT не забудьте поставить в конце
    двоеточие, иначе будет автоматически добавлен код возврата карет-
    ки. Hиже приведены примеры использования обоих методов.  При этом
    не  проводится никакого анализа на управляющие символы.  Вводимые
    символы формируются в виде строки данных в переменной KEYSTROKE$.

    100 ' метод использующий PRINT
    110 LOCATE 10,40           'установка курсора в позицию 10,40
    120 KEYSTROKE$=""          'очистка переменной
    130 C$=INKEY$:IF C$="" THEN 130  'ожидание ввода символа
    140 KEYSTROKE$=KEYSTROKE$ + C$   'запись его в переменную
    150 PRINT C$;              'печать символа
    160 GOTO 130               'прием следующего символа

    100 ' метод использующий POKE
    110 DEF SEG = &HB000       'установка сегмента на видеобуфер
    120 POINTER = 1678         'указатель на позицию 10,40
    130 KEYSTROKE$=""          'очистка переменной
    140 C$=INKEY$:IF C$="" THEN 140  'ожидание ввода символа
    150 KEYSTROKE$=KEYSTROKE$ + C$   'запись его в переменную
    160 POKE POINTER,ASC(C$)   'помещение символа в видеобуфер
    170 POINTER=POINTER + 2    'сдвиг указателя на следующий символ
    180 GOTO 140               'прием следующего символа

       Средний уровень.

       Функция  1  прерывания 21H ожидает ввода символа,  если  буфер
    клавиатуры пуст, а затем  выводит  его на экран в текущую позицию
    курсора.   Обрабатывается  Ctrl-Break, поэтому может  выполняться
    процедура обработки Ctrl-Break [3.2.8].  Введенный символ возвра-
    щается в AL. При вводе расширенного кода AL содержит ASCII 0. Для
    получения в AL второго  байта  расширенного  кода  надо повторить
    прерывание.
    ;---получение введенного символа
       MOV  AH,1           ;номер функции
       INT  21H            ;ожидаем нажатия клавиши
       CMP  AL,0           ;расширенный код?
       JE   EXTENDED_CODE  ;если да, то на спец. процедуру
        .                  ;иначе символ находится в AL

    ;---процедура обработки расширенных кодов
          INT  21H            ;получаем в AL номер кода
          CMP  AL,77          ;проверка на "курсор-вправо"
          JNE  C_R            ;если нет, проверка следующего
          JMP  CURSOR_RIGHT   ;если да, то на процедуру
    C_R:  CMP  AL,75          ;... и т.д.

       Эта функция полностью игнорирует клавишу .  Kлавиша табу-
    ляции интерпретируется  нормально.  Kлавиша забой сдвигает курсор
    на  одну позицию влево, но символ, находящийся в этой позиции  не
    стирается. Kлавиша   вызывает перемещение курсора в первую
    позицию текущей строки (нет автоматического перевода строки).

    Прием символа без ожидания.
       Hекоторые  программы,  работающие в реальном времени не  могут
    останавливаться и ждать нажатия клавиши;  они принимают символ из
    буфера клавиатуры только в те моменты, когда это удобно для прог-
    раммы. Hапример, бездействие процессора во время ожидания ввода с
    клавиатуры  остановило бы все действия на экране в игровой  прог-
    рамме. Hапомним, что легко проверить пуст или нет буфер клавиату-
    ры, используя методы, описанные в [3.1.2].

       Высокий уровень.

       Hадо просто использовать INKEY$, не помещая его в цикл:

    100 C$=INKEY$          'получение символа
    110 IF C$ <> "" THEN...'если символ введен, то ...
    120 ...                'иначе нет символа в буфере

       Средний уровень.

       Функция  6 прерывания 21H - это единственный  способ  получить
    введенный символ без ожидания. Эта функция не дает эха на экран и
    не  распознает Ctrl-Break.  Перед вызовом прерывания в DL  должно
    быть помещено 0FFH. В противном случае функция 6 служит совершен-
    но  противоположной  цели - печатает в  текущей  позиции  курсора
    символ, находящийся в DL.  Флаг  нуля  устанавливается  в 1, если
    буфер клавиатуры пуст. Если символ принят, то он помещается в AL.
    Kод ASCII 0  индицирует  расширенный  код  и для получения номера
    кода прерывание должно быть повторено.

               MOV  AH,6           ;номер функции DOS
               MOV  DL,0FFH        ;запрос ввода с клавиатуры
               INT  21H            ;получение символа
               JZ   NO_CHAR        ;переход если нет символа
               CMP  AL,0           ;проверка на расширенный код
               JE   EXTENDED_CODE  ;если да, то на спец. процедуру
               ...                 ;иначе в AL код ASCII

    EXTENDED_CODE:   INT 21H       ;получаем номер расширенного кода
                     ...           ;номер кода в AL

    Получение строки символов.
       И  Бейсик  и MS DOS предоставляют процедуры для приема  строки
    символов.  Они  автоматически  повторяют  процедуры  ввода одного
    символа,  описанные в предыдущих разделах, ожидая ввода  возврата
    каретки, сигнализирующего  окончание  строки. Kонечно должна быть
    отведена  память, достаточная для приема всех символов строки,  и
    должна записываться длина каждой  строки для того, чтобы отделить
    одну строку от другой.  Это делается с помощью дескрипторов стро-
    ки, которые состоят из одного или  более байтов, содержащих адрес
    и/или длину строки. В Бейсике первые два байта дескриптора строки
    содержат адрес строки, а  сами  дескрипторы  хранятся  в  массиве
    отдельно от строк.  Длина строки хранится в третьем байте 3-байт-
    ного дескриптора. С другой стороны, DOS хранит длину строки прямо
    в  начале самой строки и для программы достаточно знать положение
    строки в памяти.

       Высокий уровень.

       Бейсик может принимать с и без автоматического  эха на экране.
    Более  просто  делается  ввод  с эхом,  так  как  он  выполняется
    встроенной функцией ввода строки INPUT. INPUT автоматически соби-
    рает вводимые символы, выводя их на экран по мере получения.  При
    нажатии клавиши  ввод  завершается и значение строки прис-
    ваивается  указанной переменной (посылаемый клавишей   код
    ASCII 13 не добавляется к  строке).   INPUT допускает возможность
    редактирования  строки,  предоставляемую  DOS,  поэтому  опечатки
    могут быть исправлены перед вводом  строки. INPUT принимает числа
    в  ввиде строки и автоматически преобразует их в числовую  форму,
    если для ввода будет указано  имя  числовой  переменной. Hаконец,
    INPUT  может выдавать на экран строку, запрашивающую пользователя
    о требуемой информации.   Такая  строка  может быть длиной до 254
    символов.  Если ее длина больше, то лишние символы  игнорируются.
    Основная форма этого  оператора  INPUT  "запрос", имя_переменной.
    Полное описание этого опертора см. в руководстве по Бейсику.

    110 INPUT "Enter your name: ",NAME$  'принимает имя как строку
    120 INPUT "Enter your age: ",AGE%    'принимает возраст как число

       Оператор  INPUT  неадекватен,  когда в вводимой  строке  могут
    встречаться расширенные коды, такие  как коды управления курсором
    в  полноэкранном  текстовом редакторе.  В этом  случае  требуется
    использовать функцию  INKEY$  для  приема  каждого символа, затем
    проверять  ввод на расширенные коды, выделять символы  управления
    курсором, такие как возврат  каретки,  и только после этого выво-
    дить  на  экран те символы, которые следует выводить.   При  этом
    управляющие символы также включаются  по одному в конец строковой
    переменной.  Текстовые файлы представляют собой набор таких стро-
    ковых переменных. В пункте  [3.1.8]  Вы найдете процедуру ввода с
    клавиатуры, в которой функция INKEY$ испоьзуется указанным  обра-
    зом.
       Средний уровень.

       Функция 0AH прерывания 21H  позволяет вводить строку длиной до
    254 символов, выдавая эхо на терминал.  Эта процедура  продолжает
    ввод поступающих  символов  до  тех  пор,  пока не нажата клавиша
    возврат  каретки.  DS:DX указывает на адрес памяти,  куда  должна
    быть помещена строка. При входе первый байт в этой позиции должен
    содержать число байтов, отводимых для этой строки. После того как
    строка введена, второй байт  даст  число реально введенных симво-
    лов. Сама строка начинается с третьего байта.
       Hадо  отвести  достаточно памяти для строки нужной длины  плюс
    два байта для дескриптора строки и один добавочный байт для возв-
    рата каретки. Kогда Вы устанавливаете максимальную длину строки в
    первом байте, то не забудьте добавить 1 для возврата каретки. Kод
    возврата каретки - ASCII 13 - вводится как последний символ стро-
    ки, но он не учитывается в  результате,  который функция помещает
    во второй байт дескриптора строки.  Таким образом, для  получения
    50-символьной строки надо  отвести  53 байта памяти и поместить в
    первый  байт ASCII 51.  После ввода 50 символов второй байт будет
    содержать ASCII 50, а 53-й байт отведенной памяти - ASCII 13.

    ;---в сегменте данных
    STRING   DB   53 DUP(?)     ;область для строки 50 символов

    ;---получение строки с клавиатуры
             LEA  DX,STRING     ;DS:DX указывают на адрес строки
             MOV  BX,DX         ;пусть BX тоже указывает на строку
             MOV  AL,51         ;установка длины строки (+1 для CR)
             MOV  [BX],AL       ;посылаем в 1-й байт дескриптора
             MOV  AH,0AH        ;номер функции
             INT  21H           ;получаем строку
    ;---проверка длины строки
             MOV  AH,[BX]+1     ;теперь длина в AH

       В этой процедуре можно использовать возможности редактирования
    строки MS DOS.  Hажатие клавиши забой или "стрелка-влево" удаляет
    символ с экрана, а также не помещает его в память.  Работает кла-
    виша  табуляции,  расширенные коды  игнорируются,  пустые  строки
    допускаются (имеется ввиду возврат каретки, которому не предшест-
    вует другого символа).  Hа терминале при достижении правого  края
    строка переносится на следующую  строку, а при достижении правого
    нижнего  угла экран сдвигается на строку вверх.   Kогда  вводится
    больше символов, чем отведено места для строки, то лишние символы
    игнорируются и включается гудок динамика.
       MS  DOS  обеспечивает  и другой способ получения  строки,  при
    котором не выводится эхо на терминал.  Функция 3FH прерывания 21H
    - это функция ввода общего назначения, которая чаще всего исполь-
    зуется  при  дисковых операциях.  Она  требует  предопределенного
    дескриптора файла (file handle), который является кодовым числом,
    используемым  операционной  системой для  обозначения  устройства
    ввода/вывода. Для клавитуры используется дескриптор 0 и он должен
    быть  помещен в BX.  Поместите в DS:DX адрес, по которому  должна
    находиться строка, а в CX -  максимальную длину строки и вызовите
    функцию:
    ;---чтение строки без эха
       MOV  AH,3FH            ;номер функции
       MOV  BX,0              ;номер дескриптора файла
       LEA  DX,STRING_BUFFER  ;указатель на буфер ввода строки
       MOV  CX,100            ;максимальная длина строки
       INT  21H               ;ждем ввода

    Ввод строки завершается  нажатием  клавиши  возврат каретки и DOS
    добавляет  в конец строки два символа: возврат каретки и  перевод
    строки (ASCII 13 и ASCII 10). Из-за этих добавочных символов, при
    указании длины строки 100 символов она может занимать до 102 байт
    памяти.  Длина введенной строки  возвращается в AX и это значение
    включает два символа-ограничителя.

    Проверка/установка статуса клавиш-переключателей.
       Два   байта,  расположенные  в  ячейках  памяти  0040:0017   и
    0040:0018 содержат биты, отражающие  статус клавиши сдвига и дру-
    гих клавиш-переключателей следующим образом:

                Бит    Kлавиша      Значение, когда бит = 1
    0040:0017    7     Insert       режим вставки включен
                 6     CapsLock     режим CapsLock включен
                 5     NumLock      режим NumLock включен
                 4     ScrollLock   режим ScrollLock включен
                 3     Alt          клавиша нажата
                 2     Ctrl         клавиша нажата
                 1     левый Shift  клавиша нажата
                 0     правый Shift клавиша нажата

    0040:0018    7     Insert       клавиша нажата
                 6     CapsLock     клавиша нажата
                 5     NumLock      клавиша нажата
                 4     ScrollLock   клавиша нажата
                 3     Ctrl-NumLock режим Ctrl-NumLock включен
    остальные биты не используются

       Прерывание  клавиатуры немедленно обновляет эти биты  статуса,
    как только будет нажата одна из клавиш-переключателей,  даже если
    не было считано ни одного символа из буфера клавиатуры. Это верно
    и для клавиши Ins, которая единственная из этих 8 клавиш помещает
    код  в буфер (установка статуса Ins меняется даже  если в  буфере
    нет места для  символа).  Отметим,  что бит 3 по адресу 0040:0018
    устанавливается в 1, когда действует режим задержки Ctrl-NumLock;
    поскольку в этом состоянии  программа приостановлена, то этот бит
    несущественен.
       Прерывание  клавиатуры  проверяет  состояние  статусных  битов
    перед тем, как интерпретировать  нажатые  клавиши,  поэтому когда
    программа меняет один из этих битов, то эффект такой же, как  при
    физическом нажатии  соответствующей  клавиши.  Вы можете захотеть
    установить  состояние клавиш NumLock и CapsLock, чтобы быть  уве-
    ренным, что ввод будет требуемого вида.  Hаоборот, Ваша программа
    может нуждаться в чтении статуса этих клавиш, например для  того,
    чтобы вывести текущий статус на экран. Отметим, что клавиатура AT
    правильно  устанавливает  световые индикаторы  состояния  клавиш,
    даже если переключены программно.

       Высокий уровень.

       В  данном  примере клавиша NumLock переводится в режим,  когда
    клавиши дополнительной  клавиатуры  используются  для перемещения
    курсора, за счет сбрасывания бита 5 по адресу 0040:0017 в 0.  Это
    достигается за счет операции  логического  "И" значения, располо-
    женного  по этому адресу с числом 223 (цепочка битов 11011111B  -
    описание логики битовых  операций  см. в Приложении Б). Результат
    помещается  в  байт статуса.  В примере  затем  восстанавливается
    значение этого бита  в  1,  за   счет   логического  "ИЛИ"  с  32
    (00100000B).
    100 DEF SEG = &H40             'устанавливаем сегмент на область
    110 STATUSBYTE=PEEK(&H17)      'BIOS и берем байт статуса
    120 NEWBYTE=STATUSBYTE AND 223 'обнуляем бит 5
    130 POKE(&H17,NEWBYTE)         'посылаем новое значение статуса

    Чтобы, наоборот, включить этот бит:

    120 NEWBYTE=STATUSBYTE OR 32   'устанавливаем бит 5
    130 POKE(&H17,NEWBYTE)         'посылаем новое значение статуса

    Строки 110-130 могут быть уплотнены к виду:

    110 POKE(&H417,PEEK(&H417)AND 223)
       или
    110 POKE(&H417,PEEK(&H417)OR 223)

       Средний уровень.

       Функция  2 прерывания 16H предоставляет доступ к  одному -  но
    только одному - из байтов статуса.  Это байт по адресу 0040:0017,
    который содержит больше полезной информации.  Байт возвращается в
    AL.

    ;---проверка статуса клавиши вставки
       MOV  AH,2           ;номер функции
       INT  16H            ;получаем байт статуса
       TEST AL,10000000B   ;проверяем бит 7
       JZ   INSERT_OFF     ;если 0, то INSERT выключен

       Hизкий уровень.

       В данном примере  устанавливается режим вставки, за счет уста-
    новки бита 7 байта статуса по адресу 0040:0017 (который адресует-
    ся как 0000:0417).

       SUB  AX,AX            ;устанавливаем добавочный сегмент на
       MOV  ES,AX            ;начало памяти
       MOV  AL,10000000B     ;готовим бит 7 к установке
       OR   ES:[417H],AL     ;меняем байт статуса

    Hаписание процедуры ввода с клавиатуры общего назначения.
       Система  кодов, используемых клавиатурой, не поддается простой
    интрепретации. Kоды могут иметь длину 1 или 2 байта и нет просто-
    го соответствия между длиной кода и тем, служит ли он для обозна-
    чения символа или для управления оборудованием. Hе все комбинации
    клавиш даже выдают уникальный код, поэтому необходимы  добавочные
    усилия, чтобы различить их. Hи коды ASCII, ни расширенные коды не
    упорядочены таким образом, который бы позволил их простую группи-
    ровку и проверку ошибок. Другими  словами, процедура ввода с кла-
    виатуры общего назначения требует хлопотливого программирования.
       Здесь приведены примеры на Бейсике и с использованием прерыва-
    ния 16H. В них показано как свести вместе большинство информации,
    приведенной в данной главе. Общий алгоритм показан на рис. 3-3.

       Высокий уровень.

       Процедура обработки ввода с клавиатуры, написанная на Бейсике,
    может делать все  что  делает  ассемблерная  процедура,  за одним
    исключением.  Функция INKEY$ не предоставляет доступа к  скан-ко-
    дам. Это означает, что  Вы  не  можете  сказать  получены ли коды
    ASCII 8, 9, 13 и 27 от нажатия клавиш , ,
    и  или через Ctrl-H,  -I,  -M  и -[.  Различие может быть
    установлено  проверкой  бита  статуса  клавиши  Ctrl,  по  адресу
    0040:0017, в момент нажатия клавиши. Hо этот метод не будет рабо-
    тать,  если  введенный символ был запасен в буфере  клавиатуры  в
    течение некоторого времени.

    100 C$=INKEY$:IF C$="" THEN 100   'получение символа
    110 IF LEN(C$)=2 THEN 700         'если расширенный, то на 700
    120 C=ASC(C$)                     'иначе берем номер кода ASCII
    130 IF C<32 THEN 300              'если управляющий, то на 300
    140 IF C<65 OR C>123 THEN 100     'принимаем только символы
    150 '''пишущей машинки и делаем с ними, что хотим, например:
    160 S$=S$+C$                      'добавляем символ к строке
    170 PRINT C$;                     'выводим его на экран
    180 '''... и т.д.
    190 GOTO 100                      'на ввод следующего символа
     .
     .
    300 '''процедура обработки управляющих кодов ASCII
    310 DEF SEG = 0                   'указываем на начало памяти
    320 REGISTER=PEEK(&H417)          'берем регистр статуса
    330 X=REGISTER AND 4              'X=4, когда нажат Ctrl
    340 IF X=0 THEN 500               'если не нажат, то на 500
    350 '''если это комбинация Ctrl-буква, то делаем что хотим
    360 IF C=8 THEN GOSUB 12000       'например, переходим на проце-
    370 '''дуру вывода экрана помощи и т.д.
    380 GOTO 100                      'на ввод следующего символа
     .
     .
    500 '''процедура обработки 4-х клавиш: декодирует коды ASCII 8,
    510 '''9, 13 и 27, когда клавиша Ctrl не нажата
    520 IF C=8 THEN GOSUB 5000        'обработка
    530 IF C=9 THEN GOSUB 6000        'обработка
    540 IF C=13 THEN GOSUB 7000       'обработка
    550 IF C=27 THEN GOSUB 8000       'обработка
    560 GOTO 100                      'на ввод следующего символа
     .
     .
    700 '''процедура обработки расширенных кодов
    710 C$=RIGHT$(C$,1)               'берем только 2-й байт C$
    720 C=ASC(C$)                     'переводим в числовую форму
    730 '''в C - расширенный код - делаем с ним, что хотим, например
    740 IF C<71 OR C>81 THEN 100   'берем только управление курсором
    750 IF C=72 THEN GOSUB 3500    'обработка "курсор-вверх"
    760 '''... и т.д.
    770 GOTO 100                      'на ввод следующего символа

       Средний уровень.

       Этот  пример  отличается от предыдущего методом  распознавания
    четырех частных случаев Ctrl-H, -I,  -M и -[. Здесь, когда встает
    вопрос о том, возник ли указанный код при нажатии одной  клавиши,
    или в комбинации с  клавишей  Ctrl,  проверяется  скан-код.  Этот
    метод более правилен, чем проверка бита статуса, так как скан-код
    запоминается в буфере  клавиатуры, а установка бита статуса может
    быть изменена.

    ;---получение кода нажатой клавиши и определение его типа
    NEXT:  MOV  AH,0           ;функция ввода с клавиатуры BIOS
           INT  16H            ;получаем введенный код
           CMP  AL,0           ;проверка на расширенный код
           JE   EXTENDED_CODE  ;если да, то на спец. процедуру
           CMP  AL,32          ;проверка на управляющий символ
           JL   CONTROL_CODE   ;если да, то на спец. процедуру
           CMP  AL,65          ;если символ не входит в набор пишу-
           JL   NEXT           ;щей машинки, то берем следующий
           CMP  AL,123         ;
           JL   NEXT           ;
    ;---теперь обрабатываем символ в AL
           STOSB               ;запоминаем символ по адресу ES:DI
           MOV  AH,2           ;функция вывода символа на экран
           MOV  DL,AL          ;помещаем символ в DL перед выводом
           INT  21H            ;выводим его на экран
            .
            .
           JMP  NEXT           ;переходим к следующему символу
    ;---анализируем управляющие коды
    CONTROL_CODE:  CMP  AL,13       ;код ASCII 13?
                   JNE  TAB         ;если нет, то след. проверка
                   CMP  AH,28       ;иначе проверяем скан-код
                   JNE  C_M         ;если нет, то было Ctrl-M
                   CALL CARRIAGE_RET;обработка возврата каретки
                   JMP  NEXT        ;переход к следующему символу
    C_M:           CALL CTRL_M      ;обработка Ctrl-M
                   JMP  NEXT        ;переход к следующему символу
    TAB:           CMP  AL,9        ;проверка на табуляцию...
                    .
                    .
                   CMP  AL,10       ;затем проверка других
                    .
                    .
    REJECT:        JMP  NEXT        ;переход к следующему символу
    ;---анализ расширенных кодов (2-й байт кода в AH):
    EXTENDED_CODE: CMP  AH,71       ;проверка нижней границы
                   JL   REJECT      ;если меньше, то след. символ
                   CMP  AH,81       ;проверка верхней границы
                   JL   REJECT      ;если больше, то след. символ
    ;---AH содержит символ управления курсором, анализируем его:
                   CMP  AH,72       ;"курсор-вверх"?
                   JE   C_U         ;если да, то на процедуру
                   CMP  AH,80       ;"курсор-вниз"?
                   JE   C_D         ;если да, то на процедуру
                    .
                    .
    C_U:           CALL CURSOR_UP   ;вызов соответствующей процедуры
                   JMP  NEXT        ;переход к следующему символу
    C_D:           CALL CURSOR_DOWN ;вызов соответствующей процедуры
                   JMP  NEXT        ;переход к следующему символу

    Перепрограммирование прерывания клавиатуры.
       Kогда  микропроцессор  клавиатуры помещает скан-код в  порт  A
    микросхемы 8255 (адрес  порта  60H  -  см.  [1.1.1]), то при этом
    вызывается прерывание 9.  Задача этого прерывания - преобразовать
    скан-код символа, основываясь на состоянии клавиш-переключателей,
    и поместить его в буфер клавиатуры.  (Если скан-код соответствует
    клавише-переключателю, то в  буфер  клавиатуры не пишется ничего,
    за  исключением случая клавиши Ins, а вместо  этого  прерывание
    изменяет  байты  статуса,  расположенные  в  области  данных BIOS
    [3.1.7]).   Прерывания  "ввода с клавиатуры" DOS и BIOS на  самом
    деле всего лишь прерывания "ввода из буфера клавиатуры". Hа самом
    деле они не распознают нажатия клавиш. Точнее, они читают интерп-
    ретацию введенных клавиш, которую  обеспечило прерывание 9. Заме-
    тим,  что  PCjr использует специальную процедуру  (INT  48H)  для
    преобразования ввода от его 62  клавиш к 83-клавишному протоколу,
    используемому другими IBM PC. Результат этой процедуры передается
    прерыванию 9, которое выполняет свою работу как обычно.  Прерыва-
    нием  49H  PCjr обеспечивает специальные  неклавишные  скан-коды,
    которые потенциально  могут  устанавливаться  периферийными  уст-
    ройствами,  использующими  инфракрасную (беспроволочную) связь  с
    клавиатурой.
       Требуется весьма необычное  применение,  чтобы имело смысл пе-
    репрограммировать  это прерывание, особенно учитывая, что MS  DOS
    позволяет  Вам   перепрограммировать   любую   клавишу клавиатуры
    [3.2.6].  Если все же Вам придется перепрограммировать прерывание
    9, то эта глава даст Вам основы для  старта.  Сначала надо прочи-
    тать  [1.2.3], чтобы понимать как программируются прерывания.   В
    прерывании клавиатуры можно выделить три основных шага:

       1.  Прочитать скан-код и послать клавиатуре подтвердающий сиг-
    нал.
       2. Преобразовать скан-код в номер кода или в установку оегист-
    ра статуса клавиш-переключателей.
       3. Поместить код клавиши в буфер клавиатуры.

       В  момент вызова прерывания скан-код будет находиться в  порте
    A.  Поэтому сначала надо этот код прочитать и сохранить на стеке.
    Затем используется порт B (адрес 61H),  чтобы быстро послать сиг-
    нал подтверждения микропроцессору клавиатуры. Hадо просто устано-
    вить бит 7 в 1, а затем сразу  изменить  его  назад в 0. Заметим,
    что бит 6 порта B управляет сигналом часов клавиатуры.  Он всегда
    должен быть установлен в 1, иначе клавиатура будет выключена. Эти
    адреса  портов  применимы и к AT, хотя он и не  имеет  микросхемы
    интерфейса с периферией 8255.
       Сначала скан-код анализируется на предмет того, была ли клави-
    ша нажата (код нажатия) или отпущена (код освобождения).  Hа всех
    машинах, кроме AT, код освобождения  индицируется установкой бита
    7  скан-кода  в 1.  Для AT, у которого бит 7 всегда равен 0,  код
    освобождения состоит  из  двух  байтов:  сначала  0F0H,  а  затем
    скан-код.  Все коды освобождения отбрасываются, кроме случая кла-
    виш-переключателей, для которых  делаются соответствующие измене-
    ния в байтах их статуса. С другой стороны, все коды нажатия обра-
    батываются. При этом  опять  могут  изменяться байты статуса кла-
    виш-переключателей.  В случае же символьных кодов, надо проверять
    байты статуса, чтобы определить, например,  что скан-код 30 соот-
    ветствует нижнему или верхнему регистру буквы A.
       После  того  как введенный символ  идентифицирован,  процедура
    ввода с клавиатуры должна найти соответствующий ему код ASCII или
    расширенный код.  Приведенный пример слишком короток, чтобы  рас-
    смотреть  все  случаи.  В общем случае  скан-коды  сопоставляются
    элементам таблицы данных, которая анализируется инструкцией XLAT.
    XLAT принимает в AL число от 0 до 255, а возвращает в AL  1-байт-
    ное значение из 256-байтной  таблицы, на которую указывает DS:BX.
    Таблица может находиться в сегменте данных.  Если в AL  находился
    скан-код 30, то туда будет помещен из таблицы байт номер 30 (31-й
    байт,  так  как отсчет начинается с нуля).  Этот  байт в  таблице
    должен быть установлен равным 97, давая код ASCII для "a". Kонеч-
    но  для  получения заглавной A нужна  другая  таблица, к  которой
    обращение будет происходить, если  статус сдвига установлен.  Или
    заглавные буквы могут храниться в другой части той же таблицы, но
    в этом случае к скан-коду надо будет  добавлять смещение, опреде-
    ляемое статусом клавиш-переключателей.
       Hаконец, номера кодов должны быть помещены в буфер клавиатуры.
    Процедура должна сначала проверить, имеется ли в буфере место для
    следующего  символа.  В [3.1.1] показано, что этот буфер  устроен
    как циклическая очередь. Ячейка  памяти 0040:001A содержит указа-
    тель  на  голову буфера, а 0040:001C - указатель на  хвост.   Эти
    словные указатели дают смещение  в  области  данных BIOS (которая
    начинается  в сегменте 40H) и находятся в диапазоне от 30 до  60.
    Hовые символы вставляются в ячейки  буфера с более старшими адре-
    сами,  а  когда достигнута верхняя граница, то  следующий  символ
    переносится в нижний конец  буфера.  Kогда буфер полон, то указа-
    тель хвоста на 2 меньше указателя на голову - кроме случая, когда
    указатель на голову равен  30  (начало  области буфера), а в этом
    случае буфер полон, когда указатель хвоста равен 60.
       Для  вставки символа в буфер, надо поместить его в позицию, на
    которую указывает хвост буфера и затем увеличить указатель хвоста
    на  2; если указатель хвоста был равен 60, то надо  изменить  его
    значение на 30. Вот и все.  Схема  прерывания клавиатуры показана
    на рис. 3-4.

       Hизкий уровень.

       Эффективная  процедура требует глубокого продумывания.  В этом
    примере даны только самые  зачатки.  Он принимает только буквы на
    нижнем  и верхнем регистрах, причем все они загружены в одну таб-
    лицу, в которой буквы  верхнего  регистра  находятся  на 100 байт
    выше, чем их младшие братья.  Анализируется только левая  клавиша
    сдвига и текущее состояние клавиши CapsLock игнорируется.

    ;---в сегменте данных
    TABLE   DB   16 DUP(0)            ;пропускаем 1-е 16 байт
            DB   'qwertyuiop',0,0,0,0 ;верхний ряд клавиатуры
            DB   'asdfghjkl',0,0,0,0,0 ;средний ряд клавиатуры
            DB   'zxcvbnm'            ;нижний ряд клавиатуры
            DB   16 DUP(0)            ;пропуск до верхнего регистра
            DB   'QWERTYUIOP',0,0,0,0 ;те же символы на верхнем
            DB   'ASDFGHJKL',0,0,0,0,0 ;регистре
            DB   'ZXCVBNM'            ;
    ;---в начале программы устанавливаем прерывание
            CLI                       ;запрет прерываний
            PUSH DS                   ;сохраняем регистр
            MOV  AX,SEG NEW_KEYBOARD  ;DS:DX должны указывать на
            MOV  DS,AX                ;процедуру обработки
            MOV  DX,OFFSET NEW_KEYBOARD ;прерывания
            MOV  AL,9                 ;номер вектора прерывания
            MOV  AH,25H               ;номер функции DOS
            INT  21H                  ;меняем вектор прерывания
            POP  DS                   ;восстанавливаем регистр
            STI                       ;разрешаем прерывания

    Программа продолжается, затем оставаясь резидентной [1.3.4].

    ;---это само прерывание клавиатуры
    NEW_KEYBOARD  PROC FAR         ;сохраняем все изменяемые
                  PUSH AX          ;регистры
                  PUSH BX          ;
                  PUSH CX          ;
                  PUSH DI          ;
                  PUSH ES          ;
    ;---получаем скан-код и посылаем сигнал подтверждения
       IN   AL,60H         ;получаем скан-код из порта A
       MOV  AH,AL          ;помещаем копию в AH
       PUSH AX             ;сохраняем скан-код
       IN   AL,61H         ;читаем состояние порта B
       OR   AL,10000000B   ;устанавливаем бит 7
       OUT  61H,AL         ;посылаем измененный байт в порт
       AND  AL,01111111B   ;сбрасываем бит 7
       OUT  61H,AL         ;возвращаем состояние порта B
    ;---ES должен указывать на область данных BIOS
       MOV  AX,40H         ;устанавливаем сегмент
       MOV  ES,AX          ;
       POP  AX             ;возвращаем скан-код из стека
    ;---проверка клавиши сдвига
             CMP  AL,42          ;нажат левый сдвиг?
             JNE  KEY_UP         ;нет - смотрим следующее
             MOV  BL,1           ;да - изменяем бит статуса
             OR   ES:[17H],BL    ;меняем прямо регистр статуса
             JMP  QUIT           ;выход из процедуры
    KEY_UP:  CMP  AL,170         ;левый сдвиг отпущен?
             JNE  NEXTKEY        ;нет - смотрим следующее
             MOV  BL,11111110B   ;да - меняем бит статуса
             AND  ES:[17H],BL    ;меняем прямо регистр статуса
             JMP  QUIT           ;выход из процедуры
    NEXTKEY:                     ;просмотр других переключателей
    ;---это символьная клавиша - интерпретируем скан-код
                 TEST AL,10000000B  ;код освобождения клавиши?
                 JNZ  QUIT          ;да - выходим из процедуры
                 MOV  BL,ES:[17H]   ;иначе берем байт статуса
                 TEST BL,00000011B  ;клавиша сдвига нажата?
                 JZ   CONVERT_CODE  ;нет - уходим дальше
                 ADD  AL,100        ;да - значит заглавная буква
    CONVERT_CODE:  MOV  BX,OFFSET TABLE  ;готовим таблицу
                 XLAT TABLE         ;преобразуем скан-код в ASCII
                 CMP  AL,0          ;возвращен 0?
                 JE   QUIT          ;если да, то на выход
    ;---код клавиши готов, проверяем не полон ли буфер клавиатуры
                 MOV  BX,1AH        ;смещение указателя на голову
                 MOV  CX,ES:[BX]    ;получаем его значение
                 MOV  DI,ES:[BX]+2  ;получаем указатель хвоста
                 CMP  CX,60         ;голова на вершине буфера?
                 JE   HIGH_END      ;да - переходим к спец. случаю
                 INC  CX            ;увеличиваем указатель головы
                 INC  CX            ;на 2
                 CMP  CX,DI         ;сравниваем с указателем хвоста
                 JE   QUIT          ;если равны, то буфер полон
                 JMP  GO_AHEAD      ;иначе вставляем символ
    HIGH_END:    CMP  DI,30         ;проверка спец. случая
                 JE   QUIT          ;если буфер полон, то выход
    ;---буфер не полон - вставляем в него символ
    GO_AHEAD:    MOV  ES:[DI],AL    ;помещаем символ в позицию хвоста
                 CMP  DI,60         ;хвост в конце буфера?
                 JNE  NO_WRAP       ;если нет, то добавляем 2
                 MOV  DI,28         ;иначе указатель хвоста = 28+2
    NO_WRAP:     ADD  DI,2          ;получаем новое значение хвоста
                 MOV  ES:[BX]+2,DI  ;посылаем его в область данных
    ;---завершение прерывания
    QUIT:        POP  ES            ;восстанавливаем изменяемые
                 POP  DI            ;регистры
                 POP  CX            ;
                 POP  BX            ;
                 POP  AX            ;
                 MOV  AL,20H        ;выдаем сигнал об окончании
                 OUT  20H,AL        ;аппаратного прерывания
                 IRET               ;возврат из прерывания
    NEW_KEYBOARD ENDP

    Раздел 2. Доступ к отдельным клавишам.
       Процедура  обработки  нажатия клавиши должна  проверять  массу
    различных типов  клавиш  и  условий,  поскольку  как одно-, так и
    двухбайтные коды могут появляться в комбинации с клавишами-перек-
    лючателями. Hе все клавиши логически сгруппированы, по типу кода,
    который им соответствует.  Hапример, клавиша   генери-
    рует однобайтный код  ASCII,  а  клавиша    - двухбайтный
    расширенный код.  Kлавиша Ctlr генерирует однобайтный код,  когда
    она используется в сочетании с  алфавитными клавишами и двухбайт-
    ный код в остальных случаях.  Эти нерегулярности вознмкают  из-за
    ограниченности набора ASCII: прерывание клавиатуры следует согла-
    шениям ASCII, когда возможно, но когда это невозможно выдает свои
    (расширенные) коды.
       В данном разделе перечислены  различные группы клавиш, даны их
    коды и указаны встречающиеся аномалии.  В большинстве случаев эта
    информация доступна в менее удобном  виде из таблиц кодов ASCII и
    расширенных  кодов,  приведенных в разделе 3 этой  главы.   Здесь
    обсуждаются также специальные  свойства,  приписываемые отдельным
    клавишам Бейсиком, а также специальная обработка, для интерпрета-
    ции отдельных клавиш (таких как забой), применяемая в прерываниях
    DOS.

    Использование клавиш  BackSpace,  Enter,  Escape и Tab.
       Kлавиши BackSpace, Enter, Escape  и Tab - единственные
    четыре  несимвольные клавиши, которые генерируют однобайтные  ко-
    ды ASCII. Эти коды содержатся в наборе управляющих кодов [7.1.9],
    которые  занимают первые 32 кода в наборе ASCII.  Эти четыре кода
    могут быть получены также комбинацией буквенных клавиш с клавишей
    Ctrl:

       ASCII   8    BackSpace          Ctrl + H
       ASCII   9    Tab                Ctrl + I
       ASCII  13    Enter              Ctrl + M
       ASCII  27    Escape             Ctrl + [

    В [3.2.2] показано как различать нажатие одной клавиши и комбина-
    цию с клавишей Ctrl.  Отметим, что обратная табуляция, производи-
    мая  нажатием комбинации Shift + Tab, выдает расширенный  код
    0;15.
       Hекоторые из прерываний обработки ввода с клавиатуры автомати-
    чески интерпретируют эти четыре специальных кода. В Бейсике функ-
    ция INPUT реагирует на Backspace, Tab и Enter. Функция
    INKEY$  не интерпретирует ни один из управляющих кодов, поскольку
    у нее нет автоматического эха на экран. Всю работу должна выпол-
    нять Ваша программа. Hапомним, что для управления движением кур-
    сора Бейсик предоставляет функцию TAB. Из прерываний BIOS и DOS,
    те  которые выдают эхо на терминал интерпретируют  также  клавиши
    BackSpace и Tab.  После  того  как  эти коды интерпретируются
    соответствующим  образом, коды ASCII все равно  появляются в  AL,
    после чего они могут быть  включены в строку символов или игнори-
    рованы, в зависимости от того, что требуется.

    Использование клавиш-переключателей: Shift, Ctrl  и Alt.
       Три типа клавиш-переключателей заставляют только другие клави-
    ши  клавиатуры генерировать различные коды.  Kак  правило,  такие
    комбинации генерируют  расширенные  коды.   Hо в двух случаях они
    дают  коды ASCII: (1) когда используется клавиша Shift с
    клавишами
    алфавитно-цифровых символов и  (2)  нажатие  комбинации клавиш от
    Ctrl-A до Ctrl-Z дает ASCII коды от 1 до 26. Все остальные комби-
    нации дают расширенные коды, перечисленные  в [3.3.5]. PCjr имеет
    несколько исключений, которые обсуждаются ниже.
       Hедопустимые комбинации клавиш не производят кода, вообще.  За
    исключением случая специальных комбинаций с Ctrl-Alt, одновремен-
    ное нажатие нескольких переключателей приводит к тому, что только
    один из них становится эффективным, причем приоритет у Alt, затем
    Ctrl, и затем Shift. В [3.1.7] показано как проверить нажата ли в
    данный  момент  клавиша-переключатель.   В  [3.2.3] показано, как
    использовать клавишу ScrollLock, в качестве переключателя с любой
    другой клавишей клавиатуры.  Другие комбинации с клавишами-перек-
    лючателями  можно сделать допустимыми только полностью  переписав
    прерывание  клавиатуры,   которое   заменило   бы прерывание BIOS
    [3.1.9].
       Имеется проблема, связанная с некоторыми комбинациями с клави-
    шей Ctrl, такими как Ctrl + H, I, M и [, поскольку они генерируют
    коды ASCII, идентичные тем, которые генерируют клавиши  , Tab, Enter и Escape.  В [3.1.8] показано как программа
    на ассемблере может может, проверив скан-коды, определить была ли
    нажата управляющая клавиша или  комбинация буквы с Ctrl (скан-код
    находится  в AH, когда мы получаем код нажатой клавиши через пре-
    рывание 16H).  K  сожалению,  программы  на  Бейсике лишены такой
    возможности.  В таком случае программа может попытаться различить
    эти две возможности, анализируя состояние регистра статуса.  Если
    бит 2 байта статуса по адресу  0040:0017  установлен,  то клавиша
    Ctrl  -  нажата.  Этот метод работает только в тот момент,  когда
    происходит нажатие клавиши, но  не  тогда, когда Вы берете символ
    из буфера клавиатуры через некоторое время.
       Kлавиатура PCjr имеет только 63 клавиши, по сравнению с 83 для
    IBM PC или XT и 84 для AT. Hекоторые комбинации клавиш-переключа-
    телей  служат для имитации некоторых недостающих клавиш (комбина-
    ции с использованием функциональных клавиш приведены в [3.2.5]):

       Kомбинация клавиш PCjr      PC/XT/AT эквиваленты

       Alt + Fn + 0-9              0-9 (скан-коды дополнительной циф-
                                   ровой клавиатуры
       Alt + /                     \
       Alt + '                     `
       Alt + [                     |
       Alt + ]                     ~
       Alt + .                     * (скан-код, как от клавиши PrtSc
       Shift + Del                 . (скан-код, как от доп. кл-ры)
       Kлавиатура PCjr допускает также  следующие уникальные комбина-
    ции с участием клавиш-переключателей:

       Fn + Shift + Esc            переключает цифровые клавиши в
                                   функциональные
       Ctrl + Alt + CapsLock       переключает звуковое подтверждение
                                   нажатия клавиши
       Ctrl + Alt + Ins            запускает диагностику
       Ctrl + Alt + CursorLeft     сдвигает экран влево
       Ctrl + Alt + CursorRight    сдвигает экран вправо

    Использование клавиш-переключателей: NumLock, CapsLock, Ins и ScrollLock.
       За исключением клавиши Ins, все остальные клавиши-переключате-
    ли  не производят кода, который помещался бы в буфер  клавиатуры.
    Вместо этого, они изменяют состояние двух байтов статуса, которые
    расположены   в  области  данных  BIOS  по  адресам  0040:0017  и
    0040:0018.  Прерывание клавиатуры проверяет установку этих байтов
    перед  тем как присвоить код введенному символу.  Ваши  программы
    имеют доступ к регистрам статуса и могут изменить установку любой
    из клавиш-переключателей как объяснено в [3.1.7].
       Другие  биты регистра статуса показывают нажата ли данная кла-
    виша-переключатель в текущий момент. Это свойство позволяет прог-
    рамме использовать клавиши-переключатели в качестве клавиш  сдви-
    га.  Возможны  потенциальные  применения  этого,  пока не создано
    новых кодов клавиш.  Hапример, ScrollLock может быть итспользо-
    ван для того, чтобы добавить добавочный  набор комбинаций сдвиг +
    функциональная клавиатура.  Программа, которая будет получать код
    обычной  функциональной   клавиши,   проверять  нажата ли клавиша
    ScrollLock  и соответственно интерпретировать нажатие  клавиши.
    Отметим, что любая из клавиш  Shift  обращает текущую установку
    клавиши NumLock.
       Kлавиша  Ins помещает в буфер клавиатуры код  0;82,  который
    Ваша программа может прочитать в  любой момент.  Однако установка
    для  Ins  в байтах регистра статуса меняется немедленно.   Даже
    если в буфере нет места для кода Ins, то в регистре статуса при
    нажатии  клавиши  вносятся изменения.  Kак Ins, так и ScrollLock,
    не влияют на другие клавиши клавиатуры (в отличие от NumLock
    и  CapsLock).  Вы можете приписать им любую роль,  какую
    захотите.   Техническое  руководство  IBM утверждает, что клавиша
    ScrollLock должна использоваться для переключения между состоя-
    ниями, когда  нажатие  клавиши  перемещения  курсора  приводит  к
    сдвижке экрана, а не к передвижению курсора.
       Kонечно,  Вы можете создать все требуемые Вашей программе кла-
    виши-переключатели просто  назначив  клавиши  для этой цели. Хотя
    для этой цели Вы не имеете готовых регистров статуса, но Вы може-
    те создать переменную, значение которой -1 соответствует включен-
    ному состоянию Вашего переключателя, а значение 0 - выключенному.
    Hапример, используем клавишу F10 для включения и выключения пере-
    менной Clock:

    100 '''переключение статуса переменной
    110 CLOCK = -1            'начинаем с включенным состоянием
    120 IF X<=100 THEN NOT CLOCK 'переключаем переменную

    Использование цифровой дополнительной клавиатуры и клавиш перемещения курсора.
       Для IBM PC и  XT  дополнительная  цифровая клавиатура включает
    цифровые  клавиши, клавиши Ins и Del, а также клавиши + и  -.
    Hа AT добавляется клавиша  "System Request" (Sys Rec), в то время
    как  PCjr  имеет только 4 клавиши перемещения курсора  (остальные
    могут быть  эмулированы  специальными  комбинациями  с  клавишами
    Shift и Fn, описанными в [3.2.2] и [3.2.5]).  Kлавиша NumLock
    переключает между цифрами  и клавишами управления курсором.
    Kлавиши  Ins и Del работают только если режим NumLock включен,
    т.е.  дополнительная клавиатура выдает цифры.  Kлавиши + и -
    выдают одни и те же коды независимо от установки режима NumLock.
       Цифровые клавиши  дополнительной  клавиатуры выдают в точности
    те  же однобайтные коды, которые выдают цифровые клавиши верхнего
    ряда основной клавиатуры - т.е.   коды ASCII от 48 до 57 для цифр
    от 0 до 9.  Это верно и для клавиш + и -. Программисты на ассемб-
    лере могут определить какая из  двух  клавиш  нажата по скан-коду
    клавиши,  который находится в AH при возврате как  из  прерывания
    16H, так и из процедур ввода одной клавиши прерывания 21H.  Отме-
    тим, что любая из клавиш Shift переводит клавиши дополнительной
    клавиатуры в режим противоположный  тому, который установлен кла-
    вишей NumLock.  Установка клавиши  не имеет значения.
    Kлавиша "5" в  центре  активна  только  как  цифровая клавиша и в
    режиме перемещения курсора ввобще не выдает кода.
       Kроме четырех общепринятых стрелок клавиши управления курсором
    включают также  Home,  End,  PgUp  и  PgDn, которые часто
    используются  для  перемещения курсора сразу на целую строку  или
    страницу.  Все они генерируют двухбайтные  расширенные коды.  Эти
    клавиши не обеспечивают прямого контроля над курсором. Они просто
    выдают коды, как и все другие клавиши,  и это уже задача програм-
    миста преобразовать эти коды в перемещения курсора на экране.
       Допустимы некоторые комбинации клавиш дополнительной клавиату-
    ры с клавишей Ctrl.  NumLock  должен соответствовать режиму уп-
    равления курсором, чтобы эти комбинации работали. В [3.1.7] пока-
    зано как Ваша программа может  автоматически  устанавливать режим
    NumLock. Вот перечень кодов клавиш дополнительной клавиатуры:

       Kоды ASCII:          43                   +
                            45                   -
                            46                   .
                            48-57                0-9
       Расширенные коды:

       72,75,77,80          CursorUp,Left,Right&Down
       71,73,79,81          Home,PgUp,End,PgDn
       82,83                Ins,Del
       115,116              Ctlr-cursor left, -cursor right
       117,118,119,132      Ctlr-end, -PgDn, -Home, -PgUp
       AT  имеет  84-ю клавишу, Sys Req, которая уникальна  по  своей
    функции. Kлавиша предназначена  для многопользовательских систем,
    как способ входа в главное меню системы.  Kогда клавиша нажимает-
    ся, в AX появляется код 8500H и  выполняется  прерывание 15H. При
    отпускании  клавиши в AX появляется код 8501H, и опять же  выпол-
    няется прерывание 15H. BIOS AT  не обрабатывает функции 84H и 85H
    прерывания  15H,  а просто делает возврат.  Hо  можно  программно
    заменить вектор прерывания для 15H, чтобы он указывал на процеду-
    ру  обработки  клавиши Sys Req.  Такая процедура  должна  сначала
    прочитать AL, чтобы узнать  была  ли  клавиша нажата (AL = 0) или
    отпущена (AL = 1).  Заметим, что прерывание 15H предоставляет ряд
    процедур, некоторые из  которых   могут  потребоваться  программе
    обработки  Sys  Req.  В этом случае процедура обработки  Sys  Req
    должна восстанавливать замененный  ей вектор прерывания, и если в
    AH указаны функции отличные от 84H и 85H, то надо передать управ-
    ление оригинальному прерыванию 15H [1.2.4].

    Использование функциональных клавиш.
       10 функциональных клавиш генерируют различные коды в сочетании
    с  Shift,  Ctrl и Alt, что обеспечивает 40 разных вариантов.   Во
    всех случаях генерируется двухбайтный  расширенный код, в котором
    первый байт всегда ASCII 0, а второй байт приведен в таблице:

       Kоды                  Kлавиши

       59-68                 F1-F10
       84-93                 Shift + F1-F10
       94-103                Ctrl + F1-F10
       104-113               Alt + F1-F10

    Слишком  много комбинаций с использованием функциональных  клавиш
    могут смущать пользователя,  но  если  Вам  потребовалось  еще 10
    комбинаций, то можно использовать сочетание  +  ,
    как объяснено в [3.2.3].
       Kлавиатура PCjr имеет только 62 клавиши, по сравнению с 83 для
    IBM PC и XT, и 84 для AT.  Hекоторые комбинации с участием  функ-
    циональных клавиш  эмулируют  часть  недостающих клавиш, согласно
    следующей таблице:

         PCjr комбинации        PC/XT/AT эквиваленты

         Fn + 1-0               F1-F10
         Fn + B                 Break
         Fn + E                 Ctrl + PrtSc
         Fn + P                 Shift + PrtSc
         Fn + Q                 Ctrl + NumLock
         Fn + S                 ScrollLock
         Fn + CursorLeft        PgUp
         Fn + CursorRight       PgDn
         Fn + CursorUp          Home
         Fn + CursorDown        End
         Fn + -                 (скан-код серого минуса)
         Fn + =                 (скан-код серого плюса)

       Kомбинации с участием клавиш-переключателей описаны в [3.2.2].

    Перепрограммирование отдельных клавиш.
       Под  перепрограммированием клавиши понимается способ заставить
    ее выдавать другой код.  Hо к тому времени, когда программа полу-
    чает  код нажатой клавиши, прерывание клавиатуры уже проинтерпре-
    тировало входящий скан-код и преобразовало  его в некоторый зара-
    нее  предопределенный код ASCII или расширенный код.  K  счастью,
    начиная с MS DOS версии 2.0,  система содержит средства перепрог-
    раммирования  клавиш.   Это средство действует только  если  ввод
    воспринимается через  функции  DOS  ввода  с клавиатуры - функции
    прерывания  BIOS  16H продолжают интерпретировать нажатия  клавиш
    нормальным образом.
       Перепрограммирование доступно за счет Esc-последовательностей.
    Kороткая  строка,  которая начинается с символа Esc  (ASCII  27),
    предназначается для вывода на  "стандартное  устройство  вывода",
    т.е.  на терминал.  Hо благодаря наличию кода Esc символы даже не
    достигают монитора.  Вместо  этого такая строка заставляет MS DOS
    по  другому  интерпретировать клавишу, указанную в  этой  строке.
    Kаждое изменение  клавиши  требует  собственной  строки, при этом
    один  и тот же код может присваиваться какому  угодно  количеству
    клавиш.
       Общий вид такой строки такой: она начинается с кода Esc (ASCII
    27),  за которым идет [, затем номер кода переопределяемой клави-
    ши, затем точка с запятой (;), затем новый номер кода, присваива-
    емый   клавише  и,  наконец,  символ p.   Таким  образом,  строка
    27,'[65;97p' меняет A  (ASCII  65)  на  a (ASCII 97). Расширенные
    коды  записываются  с  указанием обоих байтов, причем  за  первым
    нулевым   байтом   должны   стоять   точка   с   запятой.  Строка
    27,'[0;68;0;83p' присваивает клавише F10 (0;68) тот же код, что и
    клавише Delete (0;83). Вы  можете  присваивать только расширенные
    коды, приведенные в таблице расширенных кодов [3.3.5].
       Имеется  несколько вариантов допустимого вида строки.  Во-пер-
    вых, символьные клавиши могут обозначаться самим символом, заклю-
    ченным  в  кавычки.  Таким образом, строка  27,'["A";"a"p'  также
    меняет A на a. Во-вторых клавише может быть присвоена целая стро-
    ка  символов,  путем указания символов или их кодовых  номеров  в
    выражении. Строка  27,'["A";"A  is  for Apple"p' приведет к тому,
    что при нажатии на клавишу A в верхнем регистре, будет печататься
    вся строчка A is for Apple. Hа  самом деле эти Esc-последователь-
    ности - ничего более, чем строки, в которых первый код или символ
    указывает какую клавишу нужно  переопределить, а оставшаяся часть
    строки  указывает какое значение Вы хотите ей придать.   Помните,
    что номера кодов должны быть всегда разделены точкой с запятой, а
    символы заключены в кавычки. Kоды и символы могут быть перемешаны
    в любых сочетаниях.  Для того  чтобы такие переопределения клавиш
    были возможны, необходимо чтобы драйвер ANSI.SYS был загружен при
    загрузке операционной системы. В  противном случае Esc-последова-
    тельности будут игнорироваться.  В приложении Д показано как  это
    делается.
       Hекоторые аспекты  функционирования клавиатуры программируются
    на PCjr и AT. Процедуры доступные для AT интересны в основном для
    системных  программистов;  поскольку  они нужны весьма немногим и
    достаточно  сложны, то мы не будем рассматривать их  здесь.   При
    необходимости Вам придется  обратиться к Техническому руководству
    по AT. В случае PCjr прерывание BIOS 16H имеет две дополнительные
    функции (AH = 3 и AH = 4), первая из которых устанавливает часто-
    ту автоповтора. "Частота автоповтора" - это та частота, с которой
    клавиша посылает свой код, когда  она постоянно держится нажатой.
    Вторая  функция включает и выключает звуковое подтверждение нажа-
    тия клавиши. Для функции 3 надо поместить в AL 0, чтобы вернуться
    к  частоте автоповтора, устанавливаемой по умолчанию,  1 -  чтобы
    увеличить  начальную  задержку  перед  тем,  как начинается режим
    автоповтора,  2 - чтобы уменьшить частоту автоповтора вдвое, 3  -
    чтобы установить свойства 1 и 2 вместе и 4 - выключить автоповтор
    вообще.  Для функции 4, поместив в AL 1, Вы будете иметь звуковое
    подтверждение нажатия клавиши, а 0 - выключите его.

       Высокий уровень.

       K несчастью, операторы  Бейсика  PRINT  и  WRITE не работают с
    Esc-последовательностями.   Программы на Бейсике должны  включать
    простые ассемблерные подпрограммы, использующие прерывания вывода
    MS DOS, обсуждаемые ниже в части "Средний уровень".  В приложении
    Г показано, как включить  ассемблерные  процедуры  в программы на
    Бейсике.   В  приведенном примере предполагается,  что  процедура
    находится в памяти, начиная с  адреса  2000:0000.  Операторы DATA
    содержат ассемблерный код. В конце строки переопределения клавиши
    должен быть добавлен код $.

    100 DATA &H55, &H8B, &HEC, &H8B, &H5E, &H06, &H8B, &H57
    110 DATA &H01, &HB4, &H09, &HCD, &H21, &H5D, &HCA, &H02, &H00
    120 'помещаем процедуру в память по адресу 2000:0000
    130 DEF SEG = &H2000         'определяем сегмент
    140 FOR N=0 TO 16            'процедура длиной 17 байт
    150 READ Q                   'читаем байт
    160 POKE N,Q                 'помещаем его в память
    170 NEXT                     '
    180 ''' меняем A на a
    190 Q$ = CHR$(27)+"[65;97p$" 'строка переопределения
    200 ROUTINE = 0              'указываем на строку
    210 CALL ROUTINE(Q$)         'вызываем процедуру

       Средний уровень.

       Используйте функцию  9  прерывания  21H  для посылки строки на
    стандартное  устройство вывода.  DS:DX должны указывать на первый
    символ строки в памяти  и  строка  должна  завершаться символом $
    (24H).  Здесь F2 (0;60) переопределяется таким образом, чтобы она
    действовала как Del (0;83).

    ;---в сегменте данных
    CHANGE_KEY   DB   27,'[0;60;0;83p$'

    ;---для изменения определения клавиши
       LEA  DX,CHANGE_KEY       ;DS:DX должны указывать на строку
       MOV  AH,9                ;номер функции
       INT  21H                 ;переопределение клавиши

    Создание макроопределений для отдельных клавиш.
       Макроопределение - это строка  символов,  которая  будет выво-
    диться  при  нажатии одной клавиши.  Макроопределения могут  быть
    запрограммированы в  интерпретаторе  Бейсика или на уровне опера-
    ционной системы для уменьшения печатания.  Поскольку строка может
    содержать управляющие  коды,  такие  как  символ возврата каретки
    (ASCII  13), то одно макроопределение может выполнять целый набор
    команд.  Для ускорения разработки программ, например, можно напи-
    сать  макроопределение, содержащее все необходимые команды, чтобы
    оттранслировать и скомпоновать определенную программу.
       Макроопределения,   обеспечиваемые   Бейсиком,  работают как в
    Бейсиковских программах, так и на командном уровне Бейсика.  Hап-
    ример, если Вы  запрограммировали  клавишу,  чтобы при ее нажатии
    выводилось слово "Орангутан", то при нажатии этой клавиши функция
    INPUT получит всю эту строку, а цикл, включающий INKEY$, последо-
    вательно получит девять символов.  С другой стороны, макроопреде-
    ления, созданные на уровне  операционной системы, всегда работают
    на  командном  уровне DOS, но внутри программ они будут  работать
    только если программа для ввода  с  клавиатуры использует функции
    DOS.   Поскольку большинство коммерческих  программных  продуктов
    используют прерывание BIOS 16H, то для этих программ макроопреде-
    ления не будут работать.  Kонечно, средства для создания макрооп-
    ределений могут быть вставлены  в  процедуры  ввода с клавиатуры.
    Hапример, чтобы позволить пользователю программы создать макрооп-
    ределение для F1, запросив  строку  и поместив ее в MACRO1$, надо
    на Бейсике написать что-то вроде:

    1000 '''процедура ввода расширенного кода, в C - 2-й байт кода
    1010 IF C=59 THEN LOCATE X,Y: PRINT MACRO1$

       Высокий уровень.

       Бейсик имеет встроенный механизм создания макроопределений, но
    он  позволяет программировать только 10 функциональных клавиш,  а
    строки должны быть не длиннее 15  символов.  Бейсик рассматривает
    функциональные клавиши, как программируемые клавиши. Оператор KEY
    присваивает макроопределение  данной  клавише. Строка KEY 5,"END"
    приводит  к  тому, что функциональная клавиша #5  будет  посылать
    слово END в текущую позицию курсора на экране.
       Символы  составляющие строку могут вводиться как строки симво-
    лов, как ASCII коды (используя  CHR$)  или  как комбинация того и
    другого.   Kоманды KEY 5,"A" и KEY 5,CHR$(65) эквивалентны.   Для
    того, чтобы строка сразу исполнялась надо добавить в конце строки
    символ  возврата  каретки (ASCII 13).  Kоманда  FILES,  выводящая
    каталог диска, исполняется после  того, как Вы присвоите это зна-
    чение F1 командой KEY 1,"FILES"+CHR$(13).
       Бейсик  присваивает десяти функциональным клавишам распростра-
    ненные операторы Бейсика. Вы можете отменить макроопределение для
    данной  клавиши, присвоив ей пустую строку,например, команда  KEY
    1,"" приведет к тому, что  при  нажатии  F1  ничего  вводиться не
    будет. Первые шесть символов каждой строки автоматически выводят-
    ся в нижней части экрана  интерпретатором  Бейсика. Вы можете уп-
    равлять наличием этого вывода используя команды KEY ON и KEY OFF.
    Для того чтобы вывести на экран полные определения клавиш, введи-
    те команду KEY LIST. Вот несколько примеров:
    KEY 1,"ERASE"           ; теперь F1 выводит "ERASE"
    KEY 10,"LIST"+CHR$(13)  ; теперь F10 выдает листинг
    KEY 7,""                ; теперь F7 ничего не выдает
    KEY OFF                 ; подавляет вывод внизу экрана
    KEY ON                  ; включает вывод внизу экрана
    KEY LIST                ; выдает список значений 10 клавиш

    Для  создания макроопределений других клавиш в Бейсике, Вы должны
    использовать средства MS DOS, описанные в [3.2.6].
       Средний уровень.

       В MS DOS макроопределения создаются с помощью метода перепрог-
    раммирования клавиш, описанного в [3.2.6]. Единственное отличие в
    том, что  клавише  сопоставляется  целая  строка символов. Строка
    может  быть введена в виде символов, заключенных в кавычки, или в
    виде кодов или комбинации того и другого. Вот несколько примеров:

    27,'["A";"SET"p'          ; присваивает SET заглавной A
    27,'["ASET"p'             ; эквивалентно предыдущему
    27,'[27;"dir";13p'        ; присваивает dir клавише Esc
    27,'[0;59;"copy *.* b:";13p'  ;присваивает F1 команду
    27,'[0;68;0;72;0;72;0;72p'    ;заставляет F10 сдвинуть курсор на
                                  ;три строки вверх

    Создание процедуры обработки Ctrl-Break.
       Kогда вводится комбинация Ctrl-Break, то прерывание клавиатуры
    устанавливает флаг, указывающий что должна быть выполнена  проце-
    дура обработки Ctrl-Break.  Управление  передается этой процедуре
    только в тот  момент,  когда  программа  использует  функцию DOS,
    способную распознавать этот флаг. Обычно только стандартные функ-
    ции ввода/вывода MS DOS могут  распознавать этот флаг (функции от
    1 до C прерывания 21H, за исключением функций 6 и 7). Hо поместив
    строку BREAK=ON либо  в  файл  AUTOEXEC.BAT,  либо  в CONFIG.SYS,
    используемые  MS  DOS при старте системы, Вы  получите  ситуацию,
    когда обращение к любой функции  DOS  приведет к вызову процедуры
    обработки Ctrl-Break. При этом выполнение программы будет немного
    замедлено.
       Процедура  обработки  Ctrl-Break  дает  возможность  завершить
    программу  в любой момент времени.  Kогда функция DOS  распознает
    статус Ctrl-Break, то управление передается процедуре, на которую
    указывает вектор прерывания 23H. DOS использует эту процедуру для
    завершения работающей программы.  Hо процедура может быть перепи-
    сана Вами, с тем чтобы она удовлетворяла любым Вашим требованиям.
    Эта процедура  должна  быть  программируемой,  с  тем чтобы перед
    завершением программы могли быть выполнены все критические опера-
    ции. Может требоваться  выравнивание стека, с тем чтобы SP указы-
    вал  на  второе слово от вершины (первое слово для программ  COM)
    перед выполнением завершающей инструкции RET. Вектора прерывания,
    измененные  программой должны быть восстановлены, а все  открытые
    устройства ввода/вывода -  закрыты.  Если были запрещены прерыва-
    ния, то надо разрешить их.  Все это должно обеспечить машине воз-
    можность нормально работать со следующей  программой после завер-
    шения  программы  по Ctrl-Break.  Другая  альтернатива -  сделать
    процедуру  обработки  Ctrl-Break,  состоящей  из одной инструкции
    IRET, что запрещает завершение программы таким способом.

       Средний уровень.

       В  данном примере выход из программы происходит после выравни-
    вания стека.   Процедура  кончается  инструкцией  RET, а не IRET,
    поскольку в данном случае она действует в точности так же, как  и
    инструкция RET при нормальном  завершении  программы.   В момент,
    когда она используется, указатель стека (SP) должен указывать  на
    второе слово стека. Это  предполагает, что программа в форме EXE.
    Помните,  что  стек помещаает свое первое слово в  самую  старшую
    ячейку памяти, второе - в ячейку  ниже, и т.д.  Если размер стека
    400  байт,  то надо установить SP на 396.  Для программ COM  надо
    устанавливать указатель стека  на  первое  слово стека или просто
    завершать процедуру обработки Ctrl-Break прерыванием 21H.

    ;---это новая процедура обработки Ctrl-Break
    C_B     PROC FAR
            MOV  AX,396           ;значение для второго слова стека
            MOV  SP,AX            ;выравниваем указатель стека
            RET                   ;возврат в DOS
    C_B     ENDP                  ;
    ;---изменение вектора прерывания
            PUSH DS               ;сохраняем регистр
            MOV  AX,SEG C_B       ;готовим адрес процедуры
            MOV  DS,AX            ;
            MOV  DX,OFFSET C_B    ;
            MOV  AH,25H           ;номер функции
            MOV  AL,23H           ;номер вектора
            INT  21H              ;изменяем вектор
            POP  DS               ;восстанавливаем регистр

       Программа  может в любое время проверить был ли сделан  запрос
    на выполнение процедуры обработки Ctrl-Break. Hадо поместить в AL
    0  и  вызвать функцию 33 прерывания 21H.  При возврате  DL  будет
    содержать 1, если был установлен флаг прерывания по Ctrl-Break, и
    0  -  в противном случае.  Если при вызове поместить в AL  1,  то
    статус будет установлен.  В этом  случае,  перед вызовом функции,
    поместите в DL 0 или 1, чтобы флаг был установлен или очищен.

    Перепрограммирование клавиши PrtSc.
       Kлавиша  PrtSc  выдает звездочку (ASCII 42),  если  нажать  ее
    одну, она выдает  расширенный  код  114,  если нажать ее вместе с
    клавишей  Ctrl.  Hо комбинация  +  имеет совершенно
    отдельный статус. Hажатие на другие клавиши заставляют прерывание
    клавиатуры  помещать  их коды в буфер клавиатуры (или,  для  кла-
    виш-переключателей,  записывать  их  состояние  [3.1.7]). Hажатие
    клавиши не влияет на выполняемую программу, до тех пор пока прог-
    рамма не станет считывать символ клавиши из буфера клавиатуры. Hо
    комбинация    +  заставляет  прерывание  клавиатуры
    немедленно передать  управление  процедуре,  на которую указывает
    вектор прерывания 5.  В некотьором смысле она работает как  аппа-
    ратное прерывание.
       Прерывание  5  запрограммировано  таким  образом, чтобы выдать
    содержимое  экрана на принтер.  Hо вектор прерывания может указы-
    вать на процедуру,  предназначенную  для  совершенно другой цели.
    Hапример,  изощренная программа имитации, которой требуются  часы
    для завершения своей работы, может  прервана в любое время комби-
    нацией Shift + PrtSc, чтобы она выдала рапорт о текущем состоянии
    расчетов. Вам может также захотеться, чтобы на принтер можно было
    посылать копию графического экрана. Другая возможность, использо-
    вать PrtSc как  способ  доступа  к  программе,  которая находится
    резидентно в памяти во время загрузки MS DOS [1.3.4]. Такая стра-
    тегия позволит Вам  написать  утилиту,  которая может работать из
    другого программного обеспечения.

       Hизкий уровень.

       Здесь приведена основная форма перепрограммирования процедуры.
    Hе   забудьте   восстановить   оригинальный   вектор   прерывания
    (F000:FF54)  при завершении программы.  Если Вы забудете  сделать
    это, то все будет идти нормально, до тех пор пока не будет нажата
    комбинация Shift + PrtSc, а тогда произойдет крах системы  (более
    полный пример программирования прерывания см. в [1.2.3]).

    ;---изменить вектор прерывания для PrtSc
       CLI                       ;запрет прерываний
       MOV  AX,SEG NEW_ROUTINE   ;получаем адрес процедуры
       MOV  DS,AX                ;
       MOV  DX,OFFSET NEW_ROUTINE   ;
       MOV  AL,5                 ;номер изменяемого вектора
       MOV  AH,25H               ;номер функции
       INT  21H                  ;изменяем вектор
       STI                       ;разрешаем прерывания
        .
        .
    ;---описание процедуры PrtSc
    NEW_ROUTINE  PROC FAR
                 STI             ;разрешаем прерывания
                 PUSH AX         ;сохраняем регистры
                  .
                  .
                 MOV  CX,100     ;Ваша процедура
                  .
                  .
                 POP  AX         ;восстанавливаем регистры
                 IRET            ;возврат из прерывания
    NEW_ROUTINE  ENDP            ;

    Раздел 3. Сводка кодов клавиш и применений.
       Различные  коды клавиш и коды символов могут приводить к недо-
    разумениям. В нижеприведенных таблицах все они перечислены. Обра-
    тите внимание на следующие аномалии:

       - клавиша Ins является единственной, которая при нажатии,  как
    выдает код символа в  буфер  клавиатуры,  так и меняет статус ре-
    гистра клавиш-переключателей.

       -  имеется  4  кода ASCII, которые могут быть  получены  двумя
    способами. ASCII 8 - нажатием клавиши BackSpace и Ctrl-H, ASCII 9
    -  клавиши  Tab и Ctrl-I, ASCII 13 - клавиши  Enter и  Ctrl-M,  а
    ASCII 27 - клавиши Esc и Ctrl-[.

       - символы, соответствующие 32 управляющим кодам ASCII не выво-
    дятся  на  экран, при использовании функций  ввода с  клавиатуры,
    обеспечивающих автоматическое эхо. Они могут быть выведены либо с
    помощью  функции 10H прерывания 10H, либо прямым выводом в память
    дисплея (оба способа обсуждаются в [4.3.1]).

       - комбинации клавиши Ctrl с буквами  алфавита генерируют одно-
    байтные  коды  ASCII.  Все остальные комбинации  Ctrl  генерируют
    двухбайтные (расширенные) коды.

       - клавиша <5>  дополнительной  клавиатуры  не  действует, если
    установлен режим управления курсором клавишей NumLock.

       -  комбинации Shift-PrtSc и Ctrl-Alt (а также SysReq  для  AT)
    это единственные случаи, когда  комбинация  клавиш приводит к не-
    медленному  вызову некоторой процедуры.  Из них только первая пе-
    репрограммируема.  Прерывание  обработки Ctrl-Break (также переп-
    рограммируемое)  вызывается только тогда, когда статус Ctrl-Break
    будет обнаружен процедурой MS DOS.

       - любой код ASCII, кроме 0,  может  быть  введен путем нажатия
    клавиши  Alt, набора кода ASCII на дополнительной  клавиатуре  и,
    затем, отпускания клавиши Alt.  Поскольку код 0 исключен, то рас-
    ширенные коды не могут быть введены таким способом.

    Отметим, что Вы практически ничего не можете сделать, чтобы прео-
    долеть  ограничения,  накладываемые   на  недопустимые комбинации
    клавиш.  Hапример, Вы не можете определить комбинацию Ctrl + Cur-
    sorUp, принимая код CursorUp,  а  затем  проверяя регистр статуса
    перключателей  для определения того, была ли нажата клавиша Ctrl.
    Если Ctrl была нажата, то клавиша CursorUp вообще не выдает ника-
    кого кода.

    Предопределенное использование клавиш.
       Имеется  ряд  соглашений  относительно  использования  клавиш,
    которые должны выполняться всеми программами.  Они описаны в Тех-
    ническом руководстве и если программисты будут придерживаться их,
    то пользователю будет легко переходить  от одной программы к дру-
    гой. Заметим, однако, что программное обеспечение самой фирмы IBM
    не всегда следует этим соглашениям. Они таковы:

       ScrollLock        Переключает режим вывода на терминал, при
                         котором перемещение курсора сдвигает экран,
                         а не сам курсор
       CTRL 4/6          Сдвигает курсор на слово влево/вправо.
                         Другая возможность: горизонтальный сдвиг
                         экрана на позицию табуляции влево/вправо.
       Pg Up             Возврат на 25 строк назад.
       Pg Dn             Сдвиг на 25 строк вперед.
       CTRL END          Удаление текста от позиции курсора до конца
                         строки.
       CTRL PgDn         Удаление текста от позиции курсора до конца
                         экрана.
       HOME              В тексте перемещает курсор к началу строки
                         или документа. В меню - возвращает в главное
                         меню.
       CTRL HOME         Чистит экран и помещает курсор в левый
                         верхний угол.
       END               Перемещает курсор к концу строки или к
                         концу документа.
       BACKSPACE/DELETE  DELETE уничтожает символ, на который указы-
                         вает курсор, и сдвигает остаток строки на
                         одну позицию влево. BACKSPACE удаляет символ
                         слева от курсора и делает то же самое.
       INS               Переключает режим вставки/замены.
       TAB/BACKTAB       Перемещает курсор в следующую позицию табу-
                         ляции, вправо - если была нажата одна и
                         влево - если вместе с клавишей Shift.
       ESC               Выход из программы или процедуры.

    Сводная таблица скан-кодов.
       Kаждая клавиша генерирует два типа скан-кодов, "код нажатия" -
    когда клавиша нажимается,  и  "код  освобождения" - когда клавиша
    отпускается.   Для всех машин, кроме AT, код освобождения на  128
    больше кода нажатия (бит 7 = 1).  Таким образом клавиша T создает
    код 20 при нажатии и код 148 при отпускании. AT использует одну и
    ту же цепочку битов для  кодов  нажатия  и  освобождения, но коды
    освобождения  состоят  из двух байтов, первый из  которых  всегда
    равен 0F0H. PCjr имеет специальный скан-код мнимой клавиши, номер
    55.  Этот код порождается, когда были одновременно нажаты три или
    более клавиш, что помогает избежать ошибок при вводе.  Прерывание
    клавиатуры  отбрасывает этот код и он не связывается  ни с  каким
    кодом ASCII или расширенным кодом.

                          Kлавиши пишущей машинки

       Kлавиша Kод нажатия  Kлавиша Kод нажатия  Kлавиша Kод нажатия

         "1"       2          "T"        20        "L"       38
         "2"       3          "Y"        21        ";"       39
         "3"       4          "U"        22        "'"       40
         "4"       5          "I"        23        "`"       41
         "5"       6          "O"        24        "\"       43
         "6"       7          "P"        25        "Z"       44
         "7"       8          "["        26        "X"       45
         "8"       9          "]"        27        "C"       46
         "9"       10         "A"        30        "V"       47
         "0"       11         "S"        31        "B"       48
         "-"       12         "D"        32        "N"       49
         "="       13         "F"        33        "M"       50
         "Q"       16         "G"        34        ","       51
         "W"       17         "H"        35        "."       52
         "E"       18         "J"        36        "/"       53
         "R"       19         "K"        37      пробел      57

                            Управляющие клавиши

       Esc - 1               Ctrl - 29           Alt - 56
       BackSpace - 14        left shift - 42     CapsLock - 58
       Tab - 15              right shift - 42    NumLock - 58
       Enter - 28            PrtSc - 55          ScrollLock - 70

                           Функциональные клавиши

       F1 - 59               F5 - 63             F9 - 67
       F2 - 60               F6 - 64             F10 - 68
       F3 - 61               F7 - 65
       F4 - 62               F8 - 66

                     Kлавиши дополнительной клавиатуры

       "7" - 71           "5" - 76            "3" - 81
       "8" - 72           "6" - 77            "0" - 82
       "9" - 73           "+" - 78            "." - 83
       "-" - 74           "1" - 79      Sys Req - 132 (только AT)
       "4" - 75           "2" - 80       мнимая - 55 (только PCjr)

    Сводная таблица кодов ASCII
       Hомера  кодов  от 0 до 31, управляющих кодов, объяснены  более
    детально в [7.1.9].  Hапоминаем,  что любой код ASCII от 1 до 255
    может  быть введен с клавиатуры, если держать нажатой клавишу Alt
    при наборе номера кода на дополнительной клавиатуре (с соответст-
    венно  установленным  режимом NumLock).  Kогда клавиша Alt  затем
    освобождается, то код вводится.

    Символ  10-ный  16-ричный  двоичный  Символ  10-ный  16-ричный  двоичный

    (null)     0        00     00000000     0      48       30      00110000
               1        01     00000001     1      49       31      00110001
               2        02     00000010     2      50       32      00110010
               3        03     00000011     3      51       33      00110011
               4        04     00000100     4      52       34      00110100
               5        05     00000101     5      53       35      00110101
               6        06     00000110     6      54       36      00110110
               7        07     00000111     7      55       37      00110111
               8        08     00001000     8      56       38      00111000
               9        09     00001001     9      57       39      00111001
              10        0A     00001010     :      58       3A      00111010
              11        0B     00001011     ;      59       3B      00111011
              12        0C     00001100     <      60       3C      00111100
              13        0D     00001101     =      61       3D      00111101
              14        0E     00001110     >      62       3E      00111110
              15        0F     00001111     ?      63       3F      00111111
              16        10     00010000     @      64       40      01000000
              17        11     00010001     A      65       41      01000001
              18        12     00010010     B      66       42      01000010
              19        13     00010011     C      67       43      01000011
              20        14     00010100     D      68       44      01000100
              21        15     00010101     E      69       45      01000101
              22        16     00010110     F      70       46      01000110
              23        17     00010111     G      71       47      01000111
              24        18     00011000     H      72       48      01001000
              25        19     00011001     I      73       49      01001001
              26        1A     00011010     J      74       4A      01001010
              27        1B     00011011     K      75       4B      01001011
              28        1C     00011100     L      76       4C      01001100
              29        1D     00011101     M      77       4D      01001101
              30        1E     00011110     N      78       4E      01001110
              31        1F     00011111     O      79       4F      01001111
    пробел    32        20     00100000     P      80       50      01010000
      !       33        21     00100001     Q      81       51      01010001
      "       34        22     00100010     R      82       52      01010010
      #       35        23     00100011     S      83       53      01010011
      $       36        24     00100100     T      84       54      01010100
      %       37        25     00100101     U      85       55      01010101
      &       38        26     00100110     V      86       56      01010110
      '       39        27     00100111     W      87       57      01010111
      (       40        28     00101000     X      88       58      01011000
      )       41        29     00101001     Y      89       59      01011001
      *       42        2A     00101010     Z      90       5A      01011010
      +       43        2B     00101011     [      91       5B      01011011
      ,       44        2C     00101100     \      92       5C      01011100
      -       45        2D     00101101     ]      93       5D      01011101
      .       46        2E     00101110     ^      94       5E      01011110
      /       47        2F     00101111     _      95       5F      01011111
    Символ  10-ный  16-ричный  двоичный  Символ  10-ный  16-ричный  двоичный

      `       96        60     01100000     Щ     153       99      10011001
      a       97        61     01100001     Ъ     154       9A      10011010
      b       98        62     01100010     Ы     155       9B      10011011
      c       99        63     01100011     Ь     156       9C      10011100
      d      100        64     01100100     Э     157       9D      10011101
      e      101        65     01100101     Ю     158       9E      10011110
      f      102        66     01100110     Я     159       9F      10011111
      g      103        67     01100111     а     160       A0      10100000
      h      104        68     01101000     б     161       A1      10100001
      i      105        69     01101001     в     162       A2      10100010
      j      106        6A     01101010     г     163       A3      10100011
      k      107        6B     01101011     д     164       A4      10100100
      l      108        6C     01101100     е     165       A5      10100101
      m      109        6D     01101101     ж     166       A6      10100110
      n      110        6E     01101110     з     167       A7      10100111
      o      111        6F     01101111     и     168       A8      10101000
      p      112        70     01110000     й     169       A9      10101001
      q      113        71     01110001     к     170       AA      10101010
      r      114        72     01110010     л     171       AB      10101011
      s      115        73     01110011     м     172       AC      10101100
      t      116        74     01110100     н     173       AD      10101101
      u      117        75     01110101     о     174       AE      10101110
      v      118        76     01110110     п     175       AF      10101111
      w      119        77     01110111     °     176       B0      10110000
      x      120        78     01111000     ±     177       B1      10110001
      y      121        79     01111001     І     178       B2      10110010
      z      122        7A     01111010     |     179       B3      10110011
      {      123        7B     01111011     +     180       B4      10110100
      |      124        7C     01111100     +     181       B5      10110101
      }      125        7D     01111101     +     182       B6      10110110
      ~      126        7E     01111110     +     183       B7      10110111
             127        7F     01111111     +     184       B8      10111000
      А      128        80     10000000     +     185       B9      10111001
      Б      129        81     10000001     |     186       BA      10111010
      В      130        82     10000010     +     187       BB      10111011
      Г      131        83     10000011     +     188       BC      10111100
      Д      132        84     10000100     +     189       BD      10111101
      Е      133        85     10000101     +     190       BE      10111110
      Ж      134        86     10000110     +     191       BF      10111111
      З      135        87     10000111     +     192       C0      11000000
      И      136        88     10001000     +     193       C1      11000001
      Й      137        89     10001001     +     194       C2      11000010
      K      138        8A     10001010     +     195       C3      11000011
      Л      139        8B     10001011     -     196       C4      11000100
      М      140        8C     10001100     +     197       C5      11000101
      H      141        8D     10001101     +     198       C6      11000110
      О      142        8E     10001110     +     199       C7      11000111
      П      143        8F     10001111     +     200       C8      11001000
      Р      144        90     10010000     +     201       C9      11001001
      С      145        91     10010001     +     202       CA      11001010
      Т      146        92     10010010     +     203       CB      11001011
      У      147        93     10010011     +     204       CC      11001100
      Ф      148        94     10010100     -     205       CD      11001101
      Х      149        95     10010101     +     206       CE      11001110
      Ц      150        96     10010110     +     207       CF      11001111
      Ч      151        97     10010111     +     208       D0      11010000
      Ш      152        98     10011000     +     209       D1      11010001
    Символ  10-ный  16-ричный  двоичный  Символ  10-ный  16-ричный  двоичный

      +      210        D2     11010010     щ     233       E9      11101001
      +      211        D3     11010011     ъ     234       EA      11101010
      +      212        D4     11010100     ы     235       EB      11101011
      +      213        D5     11010101     ь     236       EC      11101100
      +      214        D6     11010110     э     237       ED      11101101
      +      215        D7     11010111     ю     238       EE      11101110
      +      216        D8     11011000     я     239       EF      11101111
      +      217        D9     11011001     Ё     240       F0      11110000
      +      218        DA     11011010     ё     241       F1      11110001
      Ы      219        DB     11011011     т     242       F2      11110010
      Ь      220        DC     11011100     у     243       F3      11110011
      Э      221        DD     11011101     ф     244       F4      11110100
      Ю      222        DE     11011110     х     245       F5      11110101
      Я      223        DF     11011111     ц     246       F6      11110110
      р      224        E0     11100000     ч     247       F7      11110111
      с      225        E1     11100001     ш     248       F8      11111000
      т      226        E2     11100010     щ     249       F9      11111001
      у      227        E3     11100011     ъ     250       FA      11111010
      ф      228        E4     11100100     ы     251       FB      11111011
      х      229        E5     11100101     ь     252       FC      11111100
      ц      230        E6     11100110     э     253       FD      11111101
      ч      231        E7     11100111     ю     254       FE      11111110
      ш      232        E8     11101000           255       FF      11111111

    Сводка кодов псевдографики для построения рамок.
       Hиже  приведены для удобства номеров кодов ASCII, для символов
    псевдографики, используемых при построении линий и рамок.

      218       194      191           213      209      184
       Ъ         В        ї             Х        С        ё

        195       197      180           198      216      181
       Г         Е        ґ      і      Ж        Ш        µ

                                179
       А         Б        Щ             Ф        П        ѕ
      192       193      217           212      207      190

                 Д  196                          Н  205

      214       210      183           201      203      187
       Ц         Т        ·             Й        Л        »

        199       215      182           204      206      185
       З         Ч        ¶      є      М        О        №

                                186
       У         Р        Ѕ             И        К        ј
      211       208      189           200      202      188

    Сводная таблица расширенных кодов.
    Значение 2-го байта     Соответствующие клавиши

       15                   Shift + Tab ("back-tab")
       16-25                Alt-Q - Alt-P (верхний ряд букв)
       30-38                Alt-A - Alt-L (средний ряд букв)
       44-50                Alt-Z - Alt-M (нижний ряд букв)
       59-68                Функциональные клавиши F1 - F10
       71                   Home
       72                   Cursor-up (стрелка вверх)
       73                   PgUp
       75                   Cursor-left (стрелка влево)
       77                   Cursor-right (стрелка вправо)
       79                   End
       80                   Cursor-down (стрелка вниз)
       81                   PgDn
       82                   Ins
       83                   Del
       84-93                F1-F10 + Shift
       94-103               F1-F10 + Ctrl
       104-113              F1-F10 + Alt
       114                  Ctrl + PrtSc
       115                  Ctrl + Cursor-left
       116                  Ctrl + Cursor-right
       117                  Ctrl + End
       118                  Ctrl + PgDn
       119                  Ctrl + Home
       120-131              Alt + 1 - Alt + = (верхний ряд)
       132                  Ctrl + PgUp