е кодов 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 Конечно надо сначала преобразовать 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. Когда 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 не забудьте поставить в конце двоеточие, иначе будет автоматически добавлен код возврата карет- ки. Ниже приведены примеры использования обоих методов. При этом не проводится никакого анализа на управляющие символы. Вводимые символы формируются в виде строки данных в переменной 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>. Клавиша табу- ляции интерпретируется нормально. Клавиша забой сдвигает курсор на одну позицию влево, но символ, находящийся в этой позиции не стирается. Клавиша <Enter> вызывает перемещение курсора в первую позицию текущей строки (нет автоматического перевода строки). 3.1.5 Прием символа без ожидания. Некоторые программы, работающие в реальном времени не могут останавливаться и ждать нажатия клавиши; они принимают символ из буфера клавиатуры только в те моменты, когда это удобно для прог- раммы. Например, бездействие процессора во время ожидания ввода с клавиатуры остановило бы все действия на экране в игровой прог- рамме. Напомним, что легко проверить пуст или нет буфер клавиату- ры, используя методы, описанные в [3.1.2]. Высокий уровень. Надо просто использовать INKEY$, не помещая его в цикл: 100 C$=INKEY$ 'получение символа 110 IF C$ <> "" THEN...'если символ введен, то ... 120 ... 'иначе нет символа в буфере Средний уровень. Функция 6 прерывания 21H - это единственный способ получить введенный символ без ожидания. Эта функция не дает эха на экран и не распознает Ctrl-Break. Перед вызовом прерывания в DL должно быть помещено 0FFH. В противном случае функция 6 служит совершен- но противоположной цели - печатает в текущей позиции курсора символ, находящийся в DL. Флаг нуля устанавливается в 1, если буфер клавиатуры пуст. Если символ принят, то он помещается в AL. Код 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 предоставляют процедуры для приема строки символов. Они автоматически повторяют процедуры ввода одного символа, описанные в предыдущих разделах, ожидая ввода возврата каретки, сигнализирующего окончание строки. Конечно должна быть отведена память, достаточная для приема всех символов строки, и должна записываться длина каждой строки для того, чтобы отделить одну строку от другой. Это делается с помощью дескрипторов стро- ки, которые состоят из одного или более байтов, содержащих адрес и/или длину строки. В Бейсике первые два байта дескриптора строки содержат адрес строки, а сами дескрипторы хранятся в массиве отдельно от строк. Длина строки хранится в третьем байте 3-байт- ного дескриптора. С другой стороны, DOS хранит длину строки прямо в начале самой строки и для программы достаточно знать положение строки в памяти. Высокий уровень. Бейсик может принимать с и без автоматического эха на экране. Более просто делается ввод с эхом, так как он выполняется встроенной функцией ввода строки INPUT. INPUT автоматически соби- рает вводимые символы, выводя их на экран по мере получения. При нажатии клавиши <Enter> ввод завершается и значение строки прис- ваивается указанной переменной (посылаемый клавишей <Enter> код ASCII 13 не добавляется к строке). INPUT допускает возможность редактирования строки, предоставляемую DOS, поэтому опечатки могут быть исправлены перед вводом строки. INPUT принимает числа в ввиде строки и автоматически преобразует их в числовую форму, если для ввода будет указано имя числовой переменной. Наконец, 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 указывает на адрес памяти, куда должна быть помещена строка. При входе первый байт в этой позиции должен содержать число байтов, отводимых для этой строки. После того как строка введена, второй байт даст число реально введенных симво- лов. Сама строка начинается с третьего байта. Надо отвести достаточно памяти для строки нужной длины плюс два байта для дескриптора строки и один добавочный байт для возв- рата каретки. Когда Вы устанавливаете максимальную длину строки в первом байте, то не забудьте добавить 1 для возврата каретки. Код возврата каретки - 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. Нажатие клавиши забой или "стрелка-влево" удаляет символ с экрана, а также не помещает его в память. Работает кла- виша табуляции, расширенные коды игнорируются, пустые строки допускаются (имеется ввиду возврат каретки, которому не предшест- вует другого символа). На терминале при достижении правого края строка переносится на следующую строку, а при достижении правого нижнего угла экран сдвигается на строку вверх. Когда вводится больше символов, чем отведено места для строки, то лишние символы игнорируются и включается гудок динамика. 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 содержат биты, отражающие статус клавиши сдвига и дру- гих клавиш-переключателей следующим образом: Бит Клавиша Значение, когда бит = 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, чтобы быть уве- ренным, что ввод будет требуемого вида. Наоборот, Ваша программа может нуждаться в чтении статуса этих клавиш, например для того, чтобы вывести текущий статус на экран. Отметим, что клавиатура 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 выключен Низкий уровень. В данном примере устанавливается режим вставки, за счет уста- новки бита 7 байта статуса по адресу 0040:0017 (который адресует- ся как 0000:0417). SUB AX,AX ;устанавливаем добавочный сегмент на MOV ES,AX ;начало памяти MOV AL,10000000B ;готовим бит 7 к установке OR ES:[417H],AL ;меняем байт статуса 3.1.8 Написание процедуры ввода с клавиатуры общего назначения. Система кодов, используемых клавиатурой, не поддается простой интрепретации. Коды могут иметь длину 1 или 2 байта и нет просто- го соответствия между длиной кода и тем, служит ли он для обозна- чения символа или для управления оборудованием. Не все комбинации клавиш даже выдают уникальный код, поэтому необходимы добавочные усилия, чтобы различить их. Ни коды ASCII, ни расширенные коды не упорядочены таким образом, который бы позволил их простую группи- ровку и проверку ошибок. Другими словами, процедура ввода с кла- виатуры общего назначения требует хлопотливого программирования. Здесь приведены примеры на Бейсике и с использованием прерыва- ния 16H. В них показано как свести вместе большинство информации, приведенной в данной главе. Общий алгоритм показан на рис. 3-3. Высокий уровень. Процедура обработки ввода с клавиатуры, написанная на Бейсике, может делать все что делает ассемблерная процедура, за одним исключением. Функция INKEY$ не предоставляет доступа к скан-ко- дам. Это означает, что Вы не можете сказать получены ли коды ASCII 8, 9, 13 и 27 от нажатия клавиш <BackSpace>, <Tab>, <Enter> и <Escape> или через Ctrl-H, -I, -M и -[. Различие может быть установлено проверкой бита статуса клавиши Ctrl, по адресу 0040:0017, в момент нажатия клавиши. Но этот метод не будет рабо- тать, если введенный символ был запасен в буфере клавиатуры в течение некоторого времени. 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 Перепрограммирование прерывания клавиатуры. Когда микропроцессор клавиатуры помещает скан-код в порт A микросхемы 8255 (адрес порта 60H - см. [1.1.1]), то при этом вызывается прерывание 9. Задача этого прерывания - преобразовать скан-код символа, основываясь на состоянии клавиш-переключателей, и поместить его в буфер клавиатуры. (Если скан-код соответствует клавише-переключателю, то в буфер клавиатуры не пишется ничего, за исключением случая клавиши <Ins>, а вместо этого прерывание изменяет байты статуса, расположенные в области данных BIOS [3.1.7]). Прерывания "ввода с клавиатуры" DOS и BIOS на самом деле всего лишь прерывания "ввода из буфера клавиатуры". На самом деле они не распознают нажатия клавиш. Точнее, они читают интерп- ретацию введенных клавиш, которую обеспечило прерывание 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), чтобы быстро послать сиг- нал подтверждения микропроцессору клавиатуры. Надо просто устано- вить бит 7 в 1, а затем сразу изменить его назад в 0. Заметим, что бит 6 порта B управляет сигналом часов клавиатуры. Он всегда должен быть установлен в 1, иначе клавиатура будет выключена. Эти адреса портов применимы и к AT, хотя он и не имеет микросхемы интерфейса с периферией 8255. Сначала скан-код анализируется на предмет того, была ли клави- ша нажата (код нажатия) или отпущена (код освобождения). На всех машинах, кроме 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". Конеч- но для получения заглавной A нужна другая таблица, к которой обращение будет происходить, если статус сдвига установлен. Или заглавные буквы могут храниться в другой части той же таблицы, но в этом случае к скан-коду надо будет добавлять смещение, опреде- ляемое статусом клавиш-переключателей. Наконец, номера кодов должны быть помещены в буфер клавиатуры. Процедура должна сначала проверить, имеется ли в буфере место для следующего символа. В [3.1.1] показано, что этот буфер устроен как циклическая очередь. Ячейка памяти 0040:001A содержит указа- тель на голову буфера, а 0040:001C - указатель на хвост. Эти словные указатели дают смещение в области данных BIOS (которая начинается в сегменте 40H) и находятся в диапазоне от 30 до 60. Новые символы вставляются в ячейки буфера с более старшими адре- сами, а когда достигнута верхняя граница, то следующий символ переносится в нижний конец буфера. Когда буфер полон, то указа- тель хвоста на 2 меньше указателя на голову - кроме случая, когда указатель на голову равен 30 (начало области буфера), а в этом случае буфер полон, когда указатель хвоста равен 60. Для вставки символа в буфер, надо поместить его в позицию, на которую указывает хвост буфера и затем увеличить указатель хвоста на 2; если указатель хвоста был равен 60, то надо изменить его значение на 30. Вот и все. Схема прерывания клавиатуры показана на рис. 3-4. Низкий уровень. Эффективная процедура требует глубокого продумывания. В этом примере даны только самые зачатки. Он принимает только буквы на нижнем и верхнем регистрах, причем все они загружены в одну таб- лицу, в которой буквы верхнего регистра находятся на 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. Доступ к отдельным клавишам. Процедура обработки нажатия клавиши должна проверять массу различных типов клавиш и условий, поскольку как одно-, так и двухбайтные коды могут появляться в комбинации с клавишами-перек- лючателями. Не все клавиши логически сгруппированы, по типу кода, который им соответствует. Например, клавиша <Backspace> генери- рует однобайтный код ASCII, а клавиша <Delete> - двухбайтный расширенный код. Клавиша Ctlr генерирует однобайтный код, когда она используется в сочетании с алфавитными клавишами и двухбайт- ный код в остальных случаях. Эти нерегулярности вознмкают из-за ограниченности набора ASCII: прерывание клавиатуры следует согла- шениям ASCII, когда возможно, но когда это невозможно выдает свои (расширенные) коды. В данном разделе перечислены различные группы клавиш, даны их коды и указаны встречающиеся аномалии. В большинстве случаев эта информация доступна в менее удобном виде из таблиц кодов ASCII и расширенных кодов, приведенных в разделе 3 этой главы. Здесь обсуждаются также специальные свойства, приписываемые отдельным клавишам Бейсиком, а также специальная обработка, для интерпрета- ции отдельных клавиш (таких как забой), применяемая в прерываниях DOS. 3.2.1 Использование клавиш <BackSpace>, <Enter>, <Escape> и <Tab>. Клавиши <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. Некоторые из прерываний обработки ввода с клавиатуры автомати- чески интерпретируют эти четыре специальных кода. В Бейсике функ- ция INPUT реагирует на <Backspace>, <Tab> и <Enter>. Функция INKEY$ не интерпретирует ни один из управляющих кодов, поскольку у нее нет автоматического эха на экран. Всю работу должна выпол- нять Ваша программа. Напомним, что для управления движением кур- сора Бейсик предоставляет функцию TAB. Из прерываний BIOS и DOS, те которые выдают эхо на терминал интерпретируют также клавиши <BackSpace> и <Tab>. После того как эти коды интерпретируются соответствующим образом, коды ASCII все равно появляются в AL, после чего они могут быть включены в строку символов или игнори- рованы, в зависимости от того, что требуется. 3.2.2 Использование клавиш-переключателей: <Shift>, <Ctrl> и <Alt>. Три типа клавиш-переключателей заставляют только другие клави- ши клавиатуры генерировать различные коды. Как правило, такие комбинации генерируют расширенные коды. Но в двух случаях они дают коды ASCII: (1) когда используется клавиша <Shift> с клавишами алфавитно-цифровых символов и (2) нажатие комбинации клавиш от Ctrl-A до Ctrl-Z дает ASCII коды от 1 до 26. Все остальные комби- нации дают расширенные коды, перечисленные в [3.3.5]. PCjr имеет несколько исключений, которые обсуждаются ниже. Недопустимые комбинации клавиш не производят кода, вообще. За исключением случая специальных комбинаций с Ctrl-Alt, одновремен- ное нажатие нескольких переключателей приводит к тому, что только один из них становится эффективным, причем приоритет у Alt, затем Ctrl, и затем Shift. В [3.1.7] показано как проверить нажата ли в данный момент клавиша-переключатель. В [3.2.3] показано, как использовать клавишу ScrollLock, в качестве переключателя с любой другой клавишей клавиатуры. Другие комбинации с клавишами-перек- лючателями можно сделать допустимыми только полностью переписав прерывание клавиатуры, которое заменило бы прерывание BIOS [3.1.9]. Имеется проблема, связанная с некоторыми комбинациями с клави- шей Ctrl, такими как Ctrl + H, I, M и [, поскольку они генерируют коды ASCII, идентичные тем, которые генерируют клавиши <BackSpa- ce>, <Tab>, <Enter> и <Escape>. В [3.1.8] показано как программа на ассемблере может может, проверив скан-коды, определить была ли нажата управляющая клавиша или комбинация буквы с Ctrl (скан-код находится в AH, когда мы получаем код нажатой клавиши через пре- рывание 16H). К сожалению, программы на Бейсике лишены такой возможности. В таком случае программа может попытаться различить эти две возможности, анализируя состояние регистра статуса. Если бит 2 байта статуса по адресу 0040:0017 установлен, то клавиша Ctrl - нажата. Этот метод работает только в тот момент, когда происходит нажатие клавиши, но не тогда, когда Вы берете символ из буфера клавиатуры через некоторое время. Клавиатура PCjr имеет только 63 клавиши, по сравнению с 83 для IBM PC или XT и 84 для AT. Некоторые комбинации клавиш-переключа- телей служат для имитации некоторых недостающих клавиш (комбина- ции с использованием функциональных клавиш приведены в [3.2.5]): Комбинация клавиш PCjr PC/XT/AT эквиваленты Alt + Fn + 0-9 0-9 (скан-коды дополнительной циф- ровой клавиатуры Alt + / \ Alt + ' ` Alt + [ | Alt + ] ~ Alt + . * (скан-код, как от клавиши PrtSc Shift + Del . (скан-код, как от доп. кл-ры) Клавиатура PCjr допускает также следующие уникальные комбина- ции с участием клавиш-переключателей: Fn + Shift + Esc переключает цифровые клавиши в функциональные Ctrl + Alt + CapsLock переключает звуковое подтверждение нажатия клавиши Ctrl + Alt + Ins запускает диагностику Ctrl + Alt + CursorLeft сдвигает экран влево Ctrl + Alt + CursorRight сдвигает экран вправо 3.2.3 Использование клавиш-переключателей: NumLock, CapsLock, Ins и ScrollLock. За исключением клавиши Ins, все остальные клавиши-переключате- ли не производят кода, который помещался бы в буфер клавиатуры. Вместо этого, они изменяют состояние двух байтов статуса, которые расположены в области данных BIOS по адресам 0040:0017 и 0040:0018. Прерывание клавиатуры проверяет установку этих байтов перед тем как присвоить код введенному символу. Ваши программы имеют доступ к регистрам статуса и могут изменить установку любой из клавиш-переключателей как объяснено в [3.1.7]. Другие биты регистра статуса показывают нажата ли данная кла- виша-переключатель в текущий момент. Это свойство позволяет прог- рамме использовать клавиши-переключатели в качестве клавиш сдви- га. Возможны потенциальные применения этого, пока не создано новых кодов клавиш. Например, <ScrollLock> может быть итспользо- ван для того, чтобы добавить добавочный набор комбинаций сдвиг + функциональная клавиатура. Программа, которая будет получать код обычной функциональной клавиши, проверять нажата ли клавиша <ScrollLock> и соответственно интерпретировать нажатие клавиши. Отметим, что любая из клавиш <Shift> обращает текущую установку клавиши <NumLock>. Клавиша <Ins> помещает в буфер клавиатуры код 0;82, который Ваша программа может прочитать в любой момент. Однако установка для <Ins> в байтах регистра статуса меняется немедленно. Даже если в буфере нет места для кода <Ins>, то в регистре статуса при нажатии клавиши вносятся изменения. Как <Ins>, так и <Scroll- Lock>, не влияют на другие клавиши клавиатуры (в отличие от <Num- Lock> и <CapsLock>). Вы можете приписать им любую роль, какую захотите. Техническое руководство IBM утверждает, что клавиша <ScrollLock> должна использоваться для переключения между состоя- ниями, когда нажатие клавиши перемещения курсора приводит к сдвижке экрана, а не к передвижению курсора. Конечно, Вы можете создать все требуемые Вашей программе кла- виши-переключатели просто назначив клавиши для этой цели. Хотя для этой цели Вы не имеете готовых регистров статуса, но Вы може- те создать переменную, значение которой -1 соответствует включен- ному состоянию Вашего переключателя, а значение 0 - выключенному. Например, используем клавишу F10 для включения и выключения пере- менной Clock: 100 '''переключение статуса переменной 110 CLOCK = -1 'начинаем с включенным состоянием 120 IF X<=100 THEN NOT CLOCK 'переключаем переменную 3.2.4 Использование цифровой дополнительной клавиатуры и клавиш перемещения курсора. Для IBM PC и XT дополнительная цифровая клавиатура включает цифровые клавиши, клавиши <Ins> и <Del>, а также клавиши + и -. На AT добавляется клавиша "System Request" (Sys Rec), в то время как PCjr имеет только 4 клавиши перемещения курсора (остальные могут быть эмулированы специальными комбинациями с клавишами <Shift> и <Fn>, описанными в [3.2.2] и [3.2.5]). Клавиша <Num- Lock> переключает между цифрами и клавишами управления курсором. Клавиши <Ins> и <Del> работают только если режим <NumLock> вклю- чен, т.е. дополнительная клавиатура выдает цифры. Клавиши + и - выдают одни и те же коды независимо от установки режима <Num- Lock>. Цифровые клавиши дополнительной клавиатуры выдают в точности те же однобайтные коды, которые выдают цифровые клавиши верхнего ряда основной клавиатуры - т.е. коды ASCII от 48 до 57 для цифр от 0 до 9. Это верно и для клавиш + и -. Программисты на ассемб- лере могут определить какая из двух клавиш нажата по скан-коду клавиши, который находится в AH при возврате как из прерывания 16H, так и из процедур ввода одной клавиши прерывания 21H. Отме- тим, что любая из клавиш <Shift> переводит клавиши дополнительной клавиатуры в режим противоположный тому, который установлен кла- вишей <NumLock>. Установка клавиши <CapsLock> не имеет значения. Клавиша "5" в центре активна только как цифровая клавиша и в режиме перемещения курсора ввобще не выдает кода. Кроме четырех общепринятых стрелок клавиши управления курсором включают также <Home>, <End>, <PgUp> и <PgDn>, которые часто используются для перемещения курсора сразу на целую строку или страницу. Все они генерируют двухбайтные расширенные коды. Эти клавиши не обеспечивают прямого контроля над курсором. Они просто выдают коды, как и все другие клавиши, и это уже задача програм- миста преобразовать эти коды в перемещения курсора на экране. Допустимы некоторые комбинации клавиш дополнительной клавиату- ры с клавишей Ctrl. <NumLock> должен соответствовать режиму уп- равления курсором, чтобы эти комбинации работали. В [3.1.7] пока- зано как Ваша программа может автоматически устанавливать режим NumLock. Вот перечень кодов клавиш дополнительной клавиатуры: Коды 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, которая уникальна по своей функции. Клавиша предназначена для многопользовательских систем, как способ входа в главное меню системы. Когда клавиша нажимает- ся, в AX появляется код 8500H и выполняется прерывание 15H. При отпускании клавиши в AX появляется код 8501H, и опять же выпол- няется прерывание 15H. BIOS AT не обрабатывает функции 84H и 85H прерывания 15H, а просто делает возврат. Но можно программно заменить вектор прерывания для 15H, чтобы он указывал на процеду- ру обработки клавиши Sys Req. Такая процедура должна сначала прочитать AL, чтобы узнать была ли клавиша нажата (AL = 0) или отпущена (AL = 1). Заметим, что прерывание 15H предоставляет ряд процедур, некоторые из которых могут потребоваться программе обработки Sys Req. В этом случае процедура обработки Sys Req должна восстанавливать замененный ей вектор прерывания, и если в AH указаны функции отличные от 84H и 85H, то надо передать управ- ление оригинальному прерыванию 15H [1.2.4]. 3.2.5 Использование функциональных клавиш. 10 функциональных клавиш генерируют различные коды в сочетании с Shift, Ctrl и Alt, что обеспечивает 40 разных вариантов. Во всех случаях генерируется двухбайтный расширенный код, в котором первый байт всегда ASCII 0, а второй байт приведен в таблице: Коды Клавиши 59-68 F1-F10 84-93 Shift + F1-F10 94-103 Ctrl + F1-F10 104-113 Alt + F1-F10 Слишком много комбинаций с использованием функциональных клавиш могут смущать пользователя, но если Вам потребовалось еще 10 комбинаций, то можно использовать сочетание <ScrollLock> + <Fn>, как объяснено в [3.2.3]. Клавиатура PCjr имеет только 62 клавиши, по сравнению с 83 для IBM PC и XT, и 84 для AT. Некоторые комбинации с участием функ- циональных клавиш эмулируют часть недостающих клавиш, согласно следующей таблице: 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 + = (скан-код серого плюса) Комбинации с участием клавиш-переключателей описаны в [3.2.2]. 3.2.6 Перепрограммирование отдельных клавиш. Под перепрограммированием клавиши понимается способ заставить ее выдавать другой код. Но к тому времени, когда программа полу- чает код нажатой клавиши, прерывание клавиатуры уже проинтерпре- тировало входящий скан-код и преобразовало его в некоторый зара- нее предопределенный код ASCII или расширенный код. К счастью, начиная с MS DOS версии 2.0, система содержит средства перепрог- раммирования клавиш. Это средство действует только если ввод воспринимается через функции DOS ввода с клавиатуры - функции прерывания BIOS 16H продолжают интерпретировать нажатия клавиш нормальным образом. Перепрограммирование доступно за счет Esc-последовательностей. Короткая строка, которая начинается с символа Esc (ASCII 27), предназначается для вывода на "стандартное устройство вывода", т.е. на терминал. Но благодаря наличию кода Esc символы даже не достигают монитора. Вместо этого такая строка заставляет MS DOS по другому интерпретировать клавишу, указанную в этой строке. Каждое изменение клавиши требует собственной строки, при этом один и тот же код может присваиваться какому угодно количеству клавиш. Общий вид такой строки такой: она начинается с кода 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. На самом деле эти Esc-последователь- ности - ничего более, чем строки, в которых первый код или символ указывает какую клавишу нужно переопределить, а оставшаяся часть строки указывает какое значение Вы хотите ей придать. Помните, что номера кодов должны быть всегда разделены точкой с запятой, а символы заключены в кавычки. Коды и символы могут быть перемешаны в любых сочетаниях. Для того чтобы такие переопределения клавиш были возможны, необходимо чтобы драйвер ANSI.SYS был загружен при загрузке операционной системы. В противном случае Esc-последова- тельности будут игнорироваться. В приложении Д показано как это делается. Некоторые аспекты функционирования клавиатуры программируются на PCjr и AT. Процедуры доступные для AT интересны в основном для системных программистов; поскольку они нужны весьма немногим и достаточно сложны, то мы не будем рассматривать их здесь. При необходимости Вам придется обратиться к Техническому руководству по AT. В случае PCjr прерывание BIOS 16H имеет две дополнительные функции (AH = 3 и AH = 4), первая из которых устанавливает часто- ту автоповтора. "Частота автоповтора" - это та частота, с которой клавиша посылает свой код, когда она постоянно держится нажатой. Вторая функция включает и выключает звуковое подтверждение нажа- тия клавиши. Для функции 3 надо поместить в AL 0, чтобы вернуться к частоте автоповтора, устанавливаемой по умолчанию, 1 - чтобы увеличить начальную задержку перед тем, как начинается режим автоповтора, 2 - чтобы уменьшить частоту автоповтора вдвое, 3 - чтобы установить свойства 1 и 2 вместе и 4 - выключить автоповтор вообще. Для функции 4, поместив в AL 1, Вы будете иметь звуковое подтверждение нажатия клавиши, а 0 - выключите его. Высокий уровень. К несчастью, операторы Бейсика 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 ;переопределение клавиши 3.2.7 Создание макроопределений для отдельных клавиш. Макроопределение - это строка символов, которая будет выво- диться при нажатии одной клавиши. Макроопределения могут быть запрограммированы в интерпретаторе Бейсика или на уровне опера- ционной системы для уменьшения печатания. Поскольку строка может содержать управляющие коды, такие как символ возврата каретки (ASCII 13), то одно макроопределение может выполнять целый набор команд. Для ускорения разработки программ, например, можно напи- сать макроопределение, содержащее все необходимые команды, чтобы оттранслировать и скомпоновать определенную программу. Макроопределения, обеспечиваемые Бейсиком, работают как в Бейсиковских программах, так и на командном уровне Бейсика. Нап- ример, если Вы запрограммировали клавишу, чтобы при ее нажатии выводилось слово "Орангутан", то при нажатии этой клавиши функция INPUT получит всю эту строку, а цикл, включающий INKEY$, последо- вательно получит девять символов. С другой стороны, макроопреде- ления, созданные на уровне операционной системы, всегда работают на командном уровне DOS, но внутри программ они будут работать только если программа для ввода с клавиатуры использует функции DOS. Поскольку большинство коммерческих программных продуктов используют прерывание BIOS 16H, то для этих программ макроопреде- ления не будут работать. Конечно, средства для создания макрооп- ределений могут быть вставлены в процедуры ввода с клавиатуры. Например, чтобы позволить пользователю программы создать макрооп- ределение для 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$) или как комбинация того и другого. Команды KEY 5,"A" и KEY 5,CHR$(65) эквивалентны. Для того, чтобы строка сразу исполнялась надо добавить в конце строки символ возврата каретки (ASCII 13). Команда 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<CR> клавише Esc 27,'[0;59;"copy *.* b:";13p' ;присваивает F1 команду 27,'[0;68;0;72;0;72;0;72p' ;заставляет F10 сдвинуть курсор на ;три строки вверх 3.2.8 Создание процедуры обработки Ctrl-Break. Когда вводится комбинация Ctrl-Break, то прерывание клавиатуры устанавливает флаг, указывающий что должна быть выполнена проце- дура обработки Ctrl-Break. Управление передается этой процедуре только в тот момент, когда программа использует функцию DOS, способную распознавать этот флаг. Обычно только стандартные функ- ции ввода/вывода MS DOS могут распознавать этот флаг (функции от 1 до C прерывания 21H, за исключением функций 6 и 7). Но поместив строку BREAK=ON либо в файл AUTOEXEC.BAT, либо в CONFIG.SYS, используемые MS DOS при старте системы, Вы получите ситуацию, когда обращение к любой функции DOS приведет к вызову процедуры обработки Ctrl-Break. При этом выполнение программы будет немного замедлено. Процедура обработки Ctrl-Break дает возможность завершить программу в любой момент времени. Когда функция DOS распознает статус Ctrl-Break, то управление передается процедуре, на которую указывает вектор прерывания 23H. DOS использует эту процедуру для завершения работающей программы. Но процедура может быть перепи- сана Вами, с тем чтобы она удовлетворяла любым Вашим требованиям. Эта процедура должна быть программируемой, с тем чтобы перед завершением программы могли быть выполнены все критические опера- ции. Может требоваться выравнивание стека, с тем чтобы 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. Надо поместить в AL 0 и вызвать функцию 33 прерывания 21H. При возврате DL будет содержать 1, если был установлен флаг прерывания по Ctrl-Break, и 0 - в противном случае. Если при вызове поместить в AL 1, то статус будет установлен. В этом случае, перед вызовом функции, поместите в DL 0 или 1, чтобы флаг был установлен или очищен. 3.2.9 Перепрограммирование клавиши PrtSc. Клавиша PrtSc выдает звездочку (ASCII 42), если нажать ее одну, она выдает расширенный код 114, если нажать ее вместе с клавишей Ctrl. Но комбинация <Shift> + <PrtSc> имеет совершенно отдельный статус. Нажатие на другие клавиши заставляют прерывание клавиатуры помещать их коды в буфер клавиатуры (или, для кла- виш-переключателей, записывать их состояние [3.1.7]). Нажатие клавиши не влияет на выполняемую программу, до тех пор пока прог- рамма не станет считывать символ клавиши из буфера клавиатуры. Но комбинация <Shift> + <PrtSc> заставляет прерывание клавиатуры немедленно передать управление процедуре, на которую указывает вектор прерывания 5. В некотьором смысле она работает как аппа- ратное прерывание. Прерывание 5 запрограммировано таким образом, чтобы выдать содержимое экрана на принтер. Но вектор прерывания может указы- вать на процедуру, предназначенную для совершенно другой цели. Например, изощренная программа имитации, которой требуются часы для завершения своей работы, может прервана в любое время комби- нацией Shift + PrtSc, чтобы она выдала рапорт о текущем состоянии расчетов. Вам может также захотеться, чтобы на принтер можно было посылать копию графического экрана. Другая возможность, использо- вать PrtSc как способ доступа к программе, которая находится резидентно в памяти во время загрузки MS DOS [1.3.4]. Такая стра- тегия позволит Вам написать утилиту, которая может работать из другого программного обеспечения. Низкий уровень. Здесь приведена основная форма перепрограммирования процедуры. Не забудьте восстановить оригинальный вектор прерывания (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 исключен, то рас- ширенные коды не могут быть введены таким способом. Отметим, что Вы практически ничего не можете сделать, чтобы прео- долеть ограничения, накладываемые на недопустимые комбинации клавиш. Например, Вы не можете определить комбинацию Ctrl + Cur- sorUp, принимая код CursorUp, а затем проверяя регистр статуса перключателей для определения того, была ли нажата клавиша Ctrl. Если Ctrl была нажата, то клавиша CursorUp вообще не выдает ника- кого кода. 3.3.1 Предопределенное использование клавиш. Имеется ряд соглашений относительно использования клавиш, которые должны выполняться всеми программами. Они описаны в Тех- ническом руководстве и если программисты будут придерживаться их, то пользователю будет легко переходить от одной программы к дру- гой. Заметим, однако, что программное обеспечение самой фирмы 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 Выход из программы или процедуры. 3.3.2 Сводная таблица скан-кодов. Каждая клавиша генерирует два типа скан-кодов, "код нажатия" - когда клавиша нажимается, и "код освобождения" - когда клавиша отпускается. Для всех машин, кроме AT, код освобождения на 128 больше кода нажатия (бит 7 = 1). Таким образом клавиша T создает код 20 при нажатии и код 148 при отпускании. AT использует одну и ту же цепочку битов для кодов нажатия и освобождения, но коды освобождения состоят из двух байтов, первый из которых всегда равен 0F0H. PCjr имеет специальный скан-код мнимой клавиши, номер 55. Этот код порождается, когда были одновременно нажаты три или более клавиш, что помогает избежать ошибок при вводе. Прерывание клавиатуры отбрасывает этот код и он не связывается ни с каким кодом ASCII или расширенным кодом. Клавиши пишущей машинки Клавиша Код нажатия Клавиша Код нажатия Клавиша Код нажатия "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 Клавиши дополнительной клавиатуры "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) 3.3.3 Сводная таблица кодов ASCII Номера кодов от 0 до 31, управляющих кодов, объяснены более детально в [7.1.9]. Напоминаем, что любой код ASCII от 1 до 255 может быть введен с клавиатуры, если держать нажатой клавишу Alt при наборе номера кода на дополнительной клавиатуре (с соответст- венно установленным режимом NumLock). Когда клавиша 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 К 138 8A 10001010 Г 195 C3 11000011 Л 139 8B 10001011 Д 196 C4 11000100 М 140 8C 10001100 Е 197 C5 11000101 Н 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 3.3.4 Сводка кодов псевдографики для построения рамок. Ниже приведены для удобства номеров кодов 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 3.3.5 Сводная таблица расширенных кодов. Значение 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 Глава 4. Вывод на терминал. Раздел 1. Управление выводом на терминал. В этой главе рассмотрены монохромный адаптор, цветной графи- ческий адаптор, видеосистема PCjr и улучшенный графический адап- тер (EGA). Все 4 системы базируются на микросхеме Motorola 6845 CRTC (cathode ray tube controller); хотя EGA на самом деле ис- пользует заказную микросхему, основанную на принципах 6845. Эта микросхема выполняет массу технических задач, которые обычно не интересуют программиста. Однако, она также устанавливает режим экрана, управляет курсором и (для цветного графического адаптора) управляет цветом. Микросхема легко программируется напрямую, хотя процедуры операционной системы позволяют управлять большинством ее действий. PCjr имеет вспомогательную микросхему для дисплея, "video gate array" (массив ворот дисплея), которая обсуждается в этом разделе вместе с 6845. EGA имеет архитектуру, отличающуюся от всех остальных, поэтому он обсуждается отдельно. Среди не-EGA систем имеется совместимость по использованию адресов портов, но есть и некоторые важные отличия. Некоторые адреса портов EGA такие же, как и у других систем. Все видеосистемы используют буфера, в которые отображаются данные для изображения на экране. Экран периодически обновляется сканированием этих данных. Размер и расположение этих буферов меняется с системой, режимом экрана, а также количеством заранее отведенной памяти. Когда в буфере хранится несколько образов экрана, то каждый отдельный образ называют дисплейной страницей. Ниже приведена короткая сводка: Монохромный адаптор Монохромный адаптор имеет 4K байт памяти на плате, начиная с адреса B0000H (т.е. B000:0000). Этой памяти хватает только для хранения одной 80-символьной страницы текста. Цветной графический адаптор. Цветной графический адаптор имеет 16K байт памяти на плате, начиная с адреса памяти B8000H. Этого достаточно для отображения одного графического экрана, без страниц, или от четырех до восьми экранов текста, в зависимости от числа символов в строке - 40 или 80. PCjr. PCjr имеет видеосистему, которая на самом деле является улуч- шенной версией цветного графического адаптора. Она уникальна тем, что использует для видеобуфера обычную оперативную память систе- мы. Когда BIOS инициализирует систему, то верхние 16K установлен- ной памяти отводятся под буфер терминала. Таким образом адрес буфера зависит от того сколько памяти имеется в системе. Для добавочных дисплейных страниц могут быть отведены блоки памяти в других местах, а также начальный объем может быть уменьшен до 4K и была поддержка только одного экрана текста. EGA. EGA может быть снабжен 64K, 128K или 256K памяти. Кроме ис- пользования в качестве видеобуфера эта память может также хранить битовые описания вплоть до 1024 символов (как объяснено в [4.3.4]). Стартовый адрес буфера дисплея программируем, поэтому буфер начинается с адреса A000H для улучшенных графических режи- мов, и с B000H и B800H для совместимости со стандартными монох- ромным и цветным графическим режимами. В большинстве случаев EGA занимает два сегмента с адресами от A000H до BFFFH, даже когда имеется 256K памяти. Это возможно, поскольку в некоторых режимах два или более байтов памяти дисплея считываются из одних и тех же адресов. Доступное число страниц зависит как от режима экрана, так и от количества имеющейся памяти. Вследствие своей сложности EGA имеет ПЗУ на 16K байт, которое заменяет и расширяет процедуры работы с терминалом BIOS. Начало области ПЗУ - адрес C000:0000. В текстовых режимах буфера начинаются с данных для верхней строки экрана, начиная с левого угла. Дальнейшие данные перено- сятся с правого конца одной строки на левый конец следующей, как будто экран представляется одной большой строкой - и с точки зрения видеобуфера так оно и есть. Однако в графических режимах буфер может быть разделен на 2 или 4 части. У цветного графичес- кого адаптора и PCjr различные части буфера содержат информацию, относящуюся к каждой второй или каждой четвертой линии точек на экране. У EGA каждая часть буфера содержит один бит из двух или четырех, которые определяют цвет данной точки экрана. При выводе текста различные видеосистемы работают одинаково. Для экрана отводится 4000 байтов, так что на каждую из 2000 пози- ций экрана приходится 2 байта (25 строк * 80 символов). Первый байт содержит код ASCII. Аппаратура дисплея преобразует номер кода ASCII в связанный с ним символ и посылает его на экран. Второй байт (байт атрибутов) содержит информацию о том, как дол- жен быть выведен данный символ. Для монохромного дисплея он ус- танавливает будет ли данный символ подчеркнут, выделен яркостью или негативом, или использует комбинацию этих атрибутов. В цвето- вых системах байт атрибутов устанавливает основной и фоновый цвета символа. В любом случае Ваша программа может писать данные прямо в буфер терминала, что значительно повышает скорость вывода на экран. Все системы, кроме монохромной, предоставляют набор цветных графических режимов, которые отличаются как разрешением, так и числом одновременно выводимых цветов. И PCjr и EGA могут одновре- менно выводить 16 цветов, причем EGA может выбирать эти 16 из набора 64 цветов. При использовании 16 цветов каждая точка экрана требует четырех бит памяти, поскольку 4 бита могут хранить числа от 0 до 15. По аналогии, четырехцветная графика требует только 2 бита на точку. Двухцветная графика может упаковать представление восьми точек в один байт видеобуфера. Количество памяти, требуе- мое для данного режима экрана может быть легко вычислено, если известно количество выводимых в этом режиме точек и количество бит, необходимое для описания одной точки. Текст легко комбини- руется с графикой (BIOS рисует символы на графическом экране) и Вы можете создавать свои специальные символы. 4.1.1 Программирование контроллера дисплея 6845. Все видеосистемы строятся вокруг микросхемы контроллера видео- терминала Motorola 6845 (EGA использует заказную микросхему, основанную на 6845). Микросхема используется во многом аналогично в монохромном адапторе, в цветном адапторе и в PCjr; но EGA не настолько совместим и по этой причине мы рекомендуем Вам избегать прямого программирования микросхемы, когда BIOS может выполнить работу за Вас. Говоря общими словами, микросхема 6845 устанавли- вает видеодисплей в один из нескольких алфавитноцифровых или графических режимов. Она выполняет основную работу по интерпрета- ции номеров кодов ASCII и поиску данных для вывода соответствую- щих символов в микросхеме ПЗУ (а иногда в оперативной памяти). Она декодирует значения атрибутов цвета и соответственно устанав- ливает экран. Она также создает курсор и управляет им. В архитек- туре EGA часть этих функций распределена между другими микросхе- мами. Микросхема 6845 имеет 18 управляющих регистров, пронумерован- ных от 0 до 17. Первые 10 регистров фиксируют горизонтальные и вертикальные параметры дисплея. Эти регистры, как правило, неин- тересны для программистов, поскольку они автоматически устанавли- ваются BIOS при изменении режима экрана. Не советуем эксперимен- тировать с этими регистрами, поскольку имеется возможность испор- тить терминал. Регистры имеют размер 8 бит, но некоторые связаны в пары, чтобы хранить 16-битные величины. Пары #10-11 и #14-15 устанавливают форму [4.2.4] и местоположение [4.2.1] курсора. Пара #12-13 управляет страницами дисплея [4.5.3]. Пара #16-17 сообщает позицию светового пера [7.3.2]. Большинство регистров доступно только для записи; только регистр адреса курсора можно и читать и писать, а регистр светового пера предназначен только для чтения. EGA имеет 6 добавочных регистров, которые связаны с тех- ническими деталями. Регистр 20 наиболее интересен; он определяет какая линия сканирования в строке символа используется для под- черкивания. Доступ ко всем 18 регистрам осуществляется через один и тот же порт, адрес которого для монохромного адаптора равен 3B5H. Этот адрес равен 3D5H для цветного адаптора и PCjr (заметим, что все адреса портов для монохромного адаптора такие же, как и для цвет- ного, за исключением того, что средней цифрой является B, а не D). EGA использует один из этих двух адресов, в зависимости от того, присоединен ли к нему цветной или монохромный монитор. Для записи в регистр монохромного адаптора надо сначала в регистр адреса, расположенный в порте 3B4H (3D4H для цветного), послать номер требуемого регистра. Тогда следующий байт, посланный в порт с адресом 3B5H будет записан в этот регистр. Поскольку регистры, интересные для программиста, используются попарно, то надо снача- ла записать в адресный регистр, потом в первый регистр пары, потом снова в адресный регистр и, наконец, во второй регистр пары. Поскольку адреса портов смежные, то легче всего адресовать их, используя инструкции INC и DEC, как в следующем примере: ;---запись в регистры 11 и 12 микросхемы 6845 (данные в BX) ;---выбираем регистр младшего байта MOV DX,3B4H ;порт адресного регистра MOV AL,11 ;номер регистра для младшего байта OUT DX,AL ;посылаем номер регистра ;---посылаем байт INC DX ;увеличиваем адрес порта MOV AL,BL ;берем младший байт OUT DX,AL ;посылаем его в регистр 11 ;---выбираем регистр старшего байта DEC DX ;восстанавливаем адрес порта MOV AL,12 ;номер регистра для старшего байта OUT DX,AL ;посылаем номер регистра ;---посылаем байт INC DX ;увеличиваем адрес порта MOV AL,BH ;берем старший байт OUT DX,AL ;посылаем его в регистр 12 У монохромного и цветного адапторов имеются еще три порта, которые важны для программистов. Они имеют адреса 3B8H, 3B9H и 3BAH для монохромного и 3D8H, 3D9H и 3DAH - для цветного адапто- ра. Первый устанавливает режим экрана, второй - связан в основном с установкой цветов экрана, а третий сообщает полезную информацию о статусе дисплея. PCjr использует не все эти адреса аналогичным образом. Вместо этого, он держит часть информации, относящейся к этим портам, в микросхеме массива ворот дисплея, основное назначение которой - обеспечить дополнительное управление цветами экрана. Доступ к массиву ворот дисплея осуществляется через порт с адресом 3DAH. У цветного адаптора этот порт возвращает байт статуса; у PCjr этот порт также возвращает байт статуса при использовании инструкции IN, но он предоставляет доступ к массиву ворот, когда использует- ся инструкция OUT. Массив ворот дисплея имеет следующие регистры: Номер Назначение 0 режим управления 1 1 маска набора цветов (палетты) 2 цвет границы 3 режим управления 2 4 сброс 10H-1FH назначение цветов палетты Доступ ко всем регистрам осуществляется через порт 3DAH. Сна- чала надо послать в этот порт номер требуемого регистра, а затем значение этого регистра. Порт автоматически переключается между этими функциями работы с адресами и с данными. Чтобы он начал ожидать ввод адреса, надо прочитать его. Отдельные регистры об- суждаются в различных местах этой главы. Особый интерес представляют 16 регистров палетты с номерами от 10H до 1FH. Каждый регистр имеет размер всего 4 бита, что как раз достаточно, чтобы хранить 16 кодовых номеров для 16 возможных цветов. Для каждой позиции символа или точки на экране видеобуфер содержит данные, указывающие каким цветом должен выводиться этот объект. Эту информацию называют данными атрибутов. В отличие от цветного графического адаптора PCjr не использует данные атрибу- тов для непосредственного определения цвета, который будет выво- диться. Вместо этого данные атрибутов являются указателями на один из 16 регистров палетты, а число, содержащееся в этом ре- гистре, определяет каким цветом будет выводиться данный символ. При таком методе, программе нужно изменить только установку ре- гистра палетты, и все символы или точки с соответствующим атрибу- том изменят свой цвет. Регистры палетты работают во всех режимах, как текстовых, так и графических. EGA распределяет эти функции между микросхемой контроллера атрибутов (адрес порта 3C0H) и двумя микросхемами контроллера графики (адреса портов 3CCH-3CFH). Контроллер атрибутов содержит 16 регистров палетты EGA, пронумерованных от 00 до 0FH. Эти ре- гистры могут содержать 6-битные коды цветов, когда EGA связан с улучшенным цветным дисплеем, поэтому могут быть использованы любые 16 цветов из набора 64-х. В [4.4.1] показано как программи- ровать регистры палетты для PCjr и EGA. 4.1.2 Установка/проверка режима дисплея. Монохромный адаптор поддерживает один режим терминала, цветной графический - семь, PCjr - десять, а EGA - двенадцать. Система PCjr более гибкая, чем монохромный или цветной адапторы, посколь- ку она предоставляет широкий выбор цветов в режимах с двумя и четырьмя цветами, а также серые тени в черно-белом режиме. EGA еще более сложен, поддерживая палетту из 64 цветов, графику на монохромном дисплее и вывод в 43 строки. Ниже приведен перечень различных режимов: Номер Режим Адапторы 0 40*25 (320*200) B&W алфавитноцифровой цветной, PCjr, EGA 1 40*25 (320*200) цветной алфавитноцифровой цветной, PCjr, EGA 2 80*25 (640*200) B&W алфавитноцифровой цветной, PCjr, EGA 3 80*25 (640*200) цветной алфавитноцифровой цветной, PCjr, EGA 4 320*200 4-цветная графика цветной, PCjr, EGA 5 320*200 B&W графика (4 тени на PCjr) цветной, PCjr, EGA 6 640*200 B&W графика цветной, PCjr, EGA 7 80*25 (720*350) B&W алфавитноцифровой монохромный, EGA 8 160*200 16-цветный графика PCjr 9 320*200 16-цветный графика PCjr A 640*200 4-цветный графика PCjr B зарезервирован для EGA C зарезервирован для EGA D 320*200 16-цветный графика EGA E 640*200 16-цветный графика EGA F 640*350 4-цветная графика на монохромном EGA 10 640*350 4- или 16-цветная графика EGA EGA разрешает иметь 8 страниц в режиме 7 - стандартном монох- ромном текстовом режиме. Режимы 0-6 полностью совместимы, исполь- зуя память одинаковым образом. При условии, что переключатели на EGA установлены для работы с улучшенным цветным дисплеем фирмы IBM, традиционные текстовые режимы выводятся с