21.7. Эффект обратной записи в буфер ввода

Весь ввод данных с клавиатуры осуществляется на самом нижнем уровне через небольшой буфер ввода емкостью в шестнадцать симво-

- 511 -

лов. Если нажимается клавиша, то ее атрибуты уходят в этот буфер и могут быть оттуда считаны стандартными процедурами Read и ReadKey. Наличие хотя бы одного символа в буфере показывает логическая функция KeyPressed. При считывании из буфера символа его место освобождается. Если программа не опрашивает буфер клавиатуры, то, нажимая клавиши, можно его переполнить. Компьютер при этом издает характерное жалобное попискивание, и все «излишки» в буквальном смысле пропадают.

Ниже рассматривается пример, который вместо того, чтобы считывать информацию из буфера, записывает ее туда. При этом получается достаточно интересный эффект. Можно программно «изобразить» последовательность нажатий до 16 клавиш, в действительности к ним даже не прикасаясь. Такой прием, в частности, может хорошо работать при написании демонстрационных версий программ — достаточно лишь перед операторами ввода с клавиатуры заполнить буфер нужными ответами. Более того, можно организовывать запуск субпроцессов и отвечать на ряд вопросов запускаемых программ, в том числе и управлять меню или функциональными клавишами.

Сама структура буфера достаточно сложна. И способ хранения символов в ней тоже не очевиден (для обычных кодов хранится ASCII-код символа и скэн-код клавиши, для расширенных запоминается код 0 и собственно расширенный код). В силу этого вид процедуры UnReadKey на рис. 21.12 может показаться запутанным. Тем не менее процедура нормально работает.

| { $М 4096, 0, 0}

| { Программа не использует кучи и мало использует стек. }

| { Такая директива нужна для запуска субпроцесса OUTER. }

| USES CRT, DOS;

| { Процедура возвращает в буфер клавиатуры слово (Word). }

| { Значение KeyW должно содержать ASCII-код клавиши. }

| PROCEDURE UnReadKey( KeyW : Word );

| CONST

| KbdStart = $1E; { специальные значения }

| KbdEnd = $3C;

| VAR

| KbdHead Word absolute $40:$1A; { голова буфера }

| KbdTail Word absolute $40:$1C; { хвост буфера }

| OldTail Word;

Рис. 21.12

- 512 -

| BEGIN

| OldTail := KbdTail;

| if KbdTail = KbdEnd

| then KbdTail := KbdStart

| else Inc( KbdTail, 2 );

| if KbdTail = KbdHead

| then KbdTail := OldTail

| else MemW[$40:OldTail] := KeyW;

| END;

{Эта процедура посылает в буфер ввода значение расширенного кода, имитируя нажатия Alt-комбинаций, функциональных клавиш и клавиш управления курсором. В ExtCode должен содержаться второй (расширенный) код клавиши. }

| PROCEDURE UnReadExtCode( ExtCode : Word );

| BEGIN

| UnReadKey( Swap( ExtCode ) )

| END;

| { Процедура помещает в буфер строку в 16 символов }

| PROCEDURE UnReadString( S : String );

| VAR i : Byte;

| BEGIN

| Delete( S, 17, 255 ); { первые 16 символов }

| for i:=1 to Length(S) do

{ заполнение буфера }

| UnReadKey( Ord( S[i] ) );

| END;

| VAR St : String; { ПРИМЕР ПРИМЕНЕНИЯ ПРОЦЕДУР }

| BEGIN

| { — Пример 1 : автоматизация чтения с клавиатуры — }

| ClrScr;

| TextAttr := Black + 16*LightGray; { текст запроса: }

| Write( ' Допишите число или исправьте : ' );

| TextAttr := LightGray;

| UnReadString(22-2-19'); {подсовываем ответ в буфер }

| Readln(St); { чтение строки }

| Write(#10'Результирующая строка -> '); { что прочитали }

| HighVideo; WriteLn( St ); LowVideo; ReadLn;

| {--Пример 2: готовые ответы на вопросы внешнего файла-- }

| St := 'Hello, Outer !' + #13;

| {St - передаваемая строка. #13 на конце - символ ввода }

| WriteLn( 'В буфер передается строка : ',St );

| UnReadString(St);

| WriteLn( #10 Запускается внешний файл OUTER.' );

| {запуск внешнего файла}

| SwapVectors; Exec('OUTER.EXE','');

| SwapVectors;

| ReadLn { пауза до нажатия ввода }

| END.

Рис. 21.12 (окончание)

- 513 -

Здесь в качестве примера приводится запуск программы OUTER, ожидающей ввод строки с клавиатуры. При отладке этого примера демонстрационная программа OUTER.PAS имела содержание, приведенное на рис. 21.13.

| VAR { -- ПРОГРАММА OUTER.PAS -- }

| S : String;

| BEGIN

| Write( #10' ЗАПРОС <-- ' ); ReadLn( S );

| WriteLn( #10#10'Ha запрос была введена строка : ', S )

| END.

Рис. 21.13

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

UnReadExtCode( 80 ); { три нажатия стрелки }

UnReadExtCode( 80 ); { курсора вниз - код 80 }

UnReadExtCode( 80 );

UnReadKey( #13 ); { клавиша ввода -код 13 }

SwapVectors;

Ехес( '...', '...' ); { запуск программы с меню )

SwapVectors;

К сожалению, всегда существует ограничение в шестнадцать символов в буфере. Это не так неприятно в одной программе, где всегда можно при необходимости «подпитать» буфер новой порцией кодов, но плохо в случае передачи кодов субпроцессу, где уже никак их после запуска не дописать. В принципе, можно организовать «подпитку» буфера, но для этого надо перехватывать прерывание 16H и обрабатывать его весьма нетривиальным способом.

- 514 -