Ввод с клавиатуры
Прямой ввод-вывод "на среднем уровне" позволяет вводить и выводить текстовые символы в файл, на консоль, модем, удалённый терминал. При этом в MS-DOS он организован при помощи механизма прерываний. Для иллюстрации "прямого ввода" будут приведены функции прямого ввода с консоли на языках Ассемблер и Си.
5.1. Введение. Общие сведения о клавиатуре
Клавиатура - это не просто "доска с клавишами" (дословный перевод английского названия "keyboard"). Помимо механической части (собственно клавиш и контактов) она включает в себя сложную топологию проводников, и даже собственный микропроцессор - контроллер.
Задачи, решаемые этим микропроцессором, следующие:
- Реагировать на нажатие клавиш или комбинации клавиш;
- Реагировать на "длительное" нажатие на клавишу, и осуществлять соответствующие действия;
- По положению клавиш генерировать специальный скен-код клавиши (см. раздел 5.1.1.);
- В соответствии с "таблицей символов" (codepage) преобразовывать скен-код клавиши в ASCII символ, ей соответствующей;
Примечание: некоторые "серые" (управляющие) клавиши не генерируют символы, а используются только для управления компьютером. В этом случае для них ASCII символ не генерируется (записывается в 0).
- Заносит пару "ASCII код" - "Скен код" в клавиатурный буфер.
Видите, как всё сложно.… Теперь разберёмся с остальным.
5.1.1. Понятие о скэн-кодах клавиш
При нажатии клавиши на клавиатуре генерируется её скен-код. Скен-код можно считать "номером клавиши" на клавиатуре. Однако это не совсем так. Например, если скен код клавиши "F10" равен "68", то тот же код клавиши с нажатой клавишей "Shift" будет равен "93", с клавишей "Ctrl" - "103", а с клавишей "Alt" - "113".
Существуют так называемые "алфавитно-цифровые" ("белые") и "управляющие" ("серые") клавиши. Первые генерируют скен-код и ASCII символы. Вторые генерируют только расширенный скен-код, а в поле "ASCII символа" стоит ноль. Благодаря этому программист легко может понять, как обрабатывать код клавиши: выводить ли символ на экран или запускать управляющую последовательность действий.
Более подробно о скэн-кодах клавиш смотри [1, 8, 33].
5.1.2. "Эхопечать" символов
При вводе с клавиатуры пара значений "Скен-код" - "ASCII символ" заносится в клавиатурный буфер для её обработки на компьютере. При этом на старых терминалах все введённые символы отображались на экране дисплея. Но, с появлением персональных ЭВМ, выяснилось, что такой вывод ("Эхопечать") не всегда удобный. Что делать, если Вы вводите пароль, и Вы не хотите, чтобы кто-либо его прочитал с экрана? А что делать, если Вы используете алфавитно-цифровую клавишу как управляющую (например, при обработке меню)? В этом случае Вам помогут функции ввода с клавиатуры без эхопечати.
5.1.3. Клавиатурный буфер
Полученный в результате преобразований в контроллере двухбайтовый код посылается в кольцевой буфер ввода, который служит для синхронизации ввода данных с клавиатуры и приёма их выполняемой программой. Объём кольцевого буфера составляет 15 слов (30 байт). При этом буфер организован по принципу: "первым записан - первым считан" (английская аббревиатура "FIFO"). При переполнении буфера новые коды в него не поступают, а нажатие на клавиши вызывает предупреждающие сигналы.
5.2. Основные функции для ввода символов с клавиатуры
5.2.1. Консольный ввод в Ассемблере
5.2.1.1. Ожидание ввода символа без эхопечати
Функции 7 и 8 прерывания 21H ожидают ввода символа, если буфер клавиатуры пуст; появление символа на экране не отображается.
Разница между этими функциями в том, что функция 8 распознаёт специальное сочетание клавиш "Ctrl+Break", а функция 7 игнорирует это сочетание клавиш.
В обоих случаях функция возвращает символ в регистр AL. Если AL содержит ASCII 0, то получен расширенный код. Повторите вызов прерывания с теми же параметрами, и в AL появится второй байт расширенного кода.
Пример 5.1
;--- получаем введённый символ MOV AH,8 ;номер функции INT 21H CMP AL,0 ;проверяем на расширенный код JE EXT_COD ;если да, то переход на анализ кода … ; иначе - обработка символа ;--- анализ расширенного кода EXT_COD: INT 21H ; берём второй байт кода CMP AL, 75 ; проверяем на "стрелка влево" JE CUR_LEFT ; переход к обработчику CMP AL, 77 ; проверяем на "стрелка вправо" JE CUR_RIGHT ; переход к обработчику
5.2.1.2. Ожидание ввода символа с эхопечатью
При вводе символов с эхопечатью эхо вводимых символов выдаётся на экран. При этом символы "возврат каретки" и "забой" переводятся в соответствующие перемещения курсора, а не отображаются как ASCII символы. Вывод эха осуществляется с текущей позиции на экране. При этом курсор после вывода смещается на позицию вправо, а по достижении правой границы экрана - переносит вывод на новую строку.
Функция 1 прерывания 21H ожидает ввода символа с клавиатуры, если её буфер пуст, а затем выводит его на экран в текущую позицию курсора. Функция обрабатывает нажатие "Ctrl+Break". Введённый символ сохраняется в регистре AL. Если содержимое AL равно ASCII 0, то для получения расширенного кода повторите прерывание.
Пример использования этой функции аналогичен примеру 5.1.
Функция 1 прерывания 21H полностью игнорирует клавишу "Esc". Клавиши табуляции интерпретируются нормально. Клавиша "Backspace" сдвигает курсор на одну позицию влево, но не стирает символ в этой позиции. Клавиша "Enter" вызывает перемещение курсора на первую позицию строки (нет автоматического переноса на новую строку).
5.2.1.3. Получение строки символов
Большинство языков программирования предоставляют возможности для ввода строки символов. Они используют возможности ввода символов с эхопечатью, помещая автоматически введённые символы в буфер оперативной памяти. Конечно же, должна быть выделена память, достаточная для приёма символов строки, и должна записываться длина строки при вводе. Если этого не сделать, то произойдёт отказ системы типа "переполнения буфера", которым может воспользоваться злоумышленник.
Функция 0AH прерывания 21H позволяет вводить строку длиной до 254 символов, выдавая эхо на терминал. Эта функция продолжает ввод символов до тех пор, пока не нажата клавиша "возврат каретки" ( "Enter" ). DS:DX указывает на строку, куда помещаются вводимые символы. При вводе первый байт этой позиции должен содержать число байтов, отводимой для этой строки. После того, как строка введена, второй байт даёт число реально введённых символов. Сама строка начинается с третьего байта.
Надо отвести достаточно памяти для строки нужной длины, плюс 2 байта для дескриптора строки плюс 1 добавочный байт для символа "Возврат каретки". Код возврата каретки: ASCII 13, - вводится как последний символ строки, но не учитывается в результате, который функция помещает во второй байт дескриптора.
Таким образом, для получения 50 - символьной строки надо отвести минимум 53 байта памяти, и поместить в первый байт памяти строки число ASCII 51. После ввода 50 символов второй байт дескриптора будет содержать ASCII 50, а 53-й байт отведённой памяти будет содержать ASCII 13.
Пример 5.2.
; - - - в сегменте данных STRING DB 53DUP(?) ; область для 50 символов ; - - - получение строки с клавиатуры LEA DX, STRING ; DS:DX - адрес строки MOV BX,DX MOV AL,51 ; установка длины строки (+1 для CR) MOV [BX], AL ; посылаем в первый байт дескриптора MOV AH, 0AH ; номер функции INT 21H ; - - - получаем в AH длину строки MOV AH, [BX]+1 ; теперь длина в AH
В этой процедуре можно использовать возможности редактирования строки MS-DOS. Нажатие "Backspace" или "стрелка влево" удаляет символ с экрана без помещения его в память. Работает клавиша табуляции, а расширенные коды игнорируются. Допускаются пустые строки (содержащие только символ ASCII 13). На дисплее при достижении правого края экрана вводимая строка переносится на следующую строку дисплея, а при достижении правого нижнего угла экран сдвигается на строку вверх. Если вводится больше символов, чем отведено для строки, лишние символы игнорируются, и включается гудок динамика.