Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Раздел 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 или расширенному набору.

Имеется несколько комбинаций клавиш, которые выполняют спе­циальные функции и не генерируют скан-коды. Эти комбинации вклю­чают <Ctrl-Break>, <Ctrl-Alt-Del> и <PrtSc>, плюс <SysReq> для AT и <Ctrl-Alt-стрелка влево, -стрелка вправо, -CapsLock, -Ins> для PCjr. Эти исключения приводят к заранее предопределенным резуль­татам [3.3.2]. Все остальные нажатия клавиш должны интерпретиро­ваться Вашей программой и если они имеют специальное назначение, скажем сдвинуть курсор влево, то Ваша программа должна содержать код, обеспечивающий достижение этого эффекта.

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

 

3.1.1 Очистка буфера клавиатуры.

Программа должна очистить буфер клавиатуры, перед тем, как выдать запрос на ввод, исключая тем самым посторонние нажатия клавиш, которые могут к тому времени накопиться в буфере. Буфер может накапливать до 15 нажатий на клавишу, независимо от того, являются ли они однобайтными кодами ASCII или двухбайтными расши­ренными кодами. Таким образом, буфер должен отвести два байта памяти для каждого нажатия на клавишу. Для однобайтных кодов первый байт содержит код ASCII, а второй - скан-код клавиши. Для расширенных кодов первый байт содержит ASCII 0, а второй номер расширенного кода. Этот код обычно совпадает со скан-кодом клави­ши, но не всегда, поскольку некоторые клавиши могут комбиниро­ваться с клавишами сдвига для генерации различных кодов.

Буфер устроен как циклическая очередь, которую называют также буфером FIFO (первый вошел - первый ушел). Kак и любой буфер он занимает непрерывную область адресов памяти. Однако не имеется определенной ячейки памяти, которая хранит "начало строки" в буфере. Вместо этого два указателя хранят позиции головы и хвоста строки символов, находящейся в буфере в текущий момент. Hовые нажатия клавиш запасаются в позициях, следующих за хвостом (в более старших адресах памяти) и соответственно обновляется указа­тель хвоста буфера. После того, как израсходовано все буферное пространство, новые символы продолжают вставляться, начиная с самого начала буферной области; поэтому возможны ситуации, когда голова строки в буфере имеет больший адрес, чем хвост. После того как буфер заполнен, новые вводимые символы игнорируются, при этом прерывание клавиатуры выдает гудок через динамик. Hа рис. 3-2 показаны некоторые возможные конфигурации данных в буфере.

В то время как указатель на голову установлен на первый вве­денный символ, указатель на хвост установлен на позицию за пос­ледним введенным символом. Kогда оба указателя равны, то буфер пуст. Чтобы разрешить ввод 15 символов требуется 16-я пустая позиция, 2 байта которой всегда содержат код возврата каретки (ASCII 13) и скан-код клавиши <Enter>, равный 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;разрешаем прерывания

 

3.1.2 Проверка символов в буфере.

Вы можете проверить был ли ввод с клавиатуры, не удаляя символ из буфера клавиатуры. Буфер использует два указателя, которые отмечают голову и хвост очереди символов, находящихся в буфере в текущий момент. 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;если неравны, то к процедуре ввода

 

 

3.1.3 Ожидать ввод символа и не выводить его на экран.

Обычно вводимые символы выводятся на экран, чтобы было видно, что напечатано. 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

;и т.д.

 

3.1.4 Ожидание нажатия клавиши и эхо на экран.

При вводе данных и текста, эхо вводимых символов обычно вы­дается на экран. При этом такие символы как возврат каретки или забой переводятся в соответствующие перемещения курсора, а не изображаются как 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;... и т.д.

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

 

3.1.5 Прием символа без ожидания.

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

 

3.1.6 Получение строки символов.

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

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

Бейсик может принимать с и без автоматического эха на экране. Более просто делается ввод с эхом, так как он выполняется встроенной функцией ввода строки INPUT. INPUT автоматически соби­рает вводимые символы, выводя их на экран по мере получения. При нажатии клавиши <Enter> ввод завершается и значение строки прис­ваивается указанной переменной (посылаемый клавишей <Enter> код 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 и это значение включает два символа-ограничителя.

 

3.1.7 Проверка/установка статуса клавиш-переключателей.

Два байта, расположенные в ячейках памяти 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;меняем байт статуса

 

3.1.8 Hаписание процедуры ввода с клавиатуры общего назначе­ния.

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

Здесь приведены примеры на Бейсике и с использованием прерыва­ния 16H. В них показано как свести вместе большинство информации, приведенной в данной главе. Общий алгоритм показан на рис. 3-3.

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

Процедура обработки ввода с клавиатуры, написанная на Бейсике, может делать все что делает ассемблерная процедура, за одним исключением. Функция INKEY$ не предоставляет доступа к скан-ко­дам. Это означает, что Вы не можете сказать получены ли коды ASCII 8, 9, 13 и 27 от нажатия клавиш <BackSpace>, <Tab>, <Enter> и <Escape> или через 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 'обработка <BackSpace>

530 IF C=9 THEN GOSUB 6000 'обработка <Tab>

540 IF C=13 THEN GOSUB 7000 'обработка <CR>

550 IF C=27 THEN GOSUB 8000 'обработка <Esc>

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;иначе проверяем скан-код <CR>

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;переход к следующему символу

 

 

3.1.9 Перепрограммирование прерывания клавиатуры.

Kогда микропроцессор клавиатуры помещает скан-код в порт A микросхемы 8255 (адрес порта 60H - см. [1.1.1]), то при этом вызывается прерывание 9. Задача этого прерывания - преобразовать скан-код символа, основываясь на состоянии клавиш-переключателей, и поместить его в буфер клавиатуры. (Если скан-код соответствует клавише-переключателю, то в буфер клавиатуры не пишется ничего, за исключением случая клавиши <Ins>, а вместо этого прерывание изменяет байты статуса, расположенные в о<

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...