Графические устройства

         

Ввод символов с клавиатуры

Для иллюстрации способов работы с описанными подпрограммами мы рассмотрим ввод текста в специально выделенный буфер строки. Такой буфер нужен для того, чтобы оператор мог исправить допущенные им ошибки до того, как задача начнет обрабатывать введенную строку. Обычно содержимое буфера становится доступным для дальнейшей обработки после того, как оператор нажмет клавишу <Enter> ("возврат каретки").

Работа клавиатуры никак не связана с текущим видеорежимом, но от него зависит способ отображения вводимых символов на экране (эхо-печать).

Чтение введенного символа

При нажатии и отпускании любой клавиши контроллер клавиатуры генерирует аппаратное прерывание, при этом прекращается выполнение текущего вычислительного процесса и вызывается специальный драйвер, расположенный в ROM BIOS. Он считывает код введенного символа (scan code), преобразует его в код ASCII и помещает оба кода в специальный буфер, расположенный в области данных BIOS, начиная с адреса оооо:041Е. Последующая обработка введенного символа, включая вывод его изображения на экран, находится вне компетенции драйвера.



scan code — это просто порядковый номер нажатой или отпущенной клавиши, для его преобразования в код ASCII надо учитывать состояние одной или нескольких функциональных клавишей. Например, scan code lEh соответствует сразу четырем буквам — латинским "А", "а" и русским "Ф", "ф", в то время как ASCII коды этих букв равны, соответственно, 4ih, 6ih, 94h и OE4h.

BIOS работает только с латинским алфавитом, для ввода с русских букв нужны специальные программы русификаторы, например Keyrus, или драйверы, входящие в состав русифицированных версий Windows. Они перехватывают аппаратное прерывание от клавиатуры и формируют коды русских букв. Причем коды русских букв зависят от используемой кодовой таблицы, а их не так уж мало.

Задача может считывать введенные с клавиатуры символы как в режиме прерываний, так и путем опроса состояния буфера, расположенного в области данных BIOS. В первом случае задача должна перехватывать вектор прерывания 09, подобно тому, как это делалось для вектора ich. Нас интересует режим опроса. В этом режиме задача может самостоятельно работать с буфером клавиатуры, или вызывать специальную функцию BIOS.

Функции, обслуживающие клавиатуру, сгруппированы в прерывание int I6h (Keyboard Services). Нам нужна функция с кодом 0 (ah=o), которая опрашивает буфер клавиатуры до тех пор, пока в него не будет записан код введенного символа. После этого происходит возврат в задачу, scan code находится в регистре ah, a ASCII код — в регистре ai. Заметим, что при вводе русских букв scan code может отсутствовать (ah=o), это зависит от установленного русификатора.

Управление курсором

Изменение состояния курсора можно разрешить перед началом ввода строки и запретить в конце ввода. Однако в таком случае при редактировании вводимого текста придется следить за текущим состоянием курсора, неоднократно удалять его с одного места и выводить в другом, т. е. выполнять много вспомогательных действий.
Мы выберем другой способ, при котором курсор виден (и мигает) только во время ожидания ввода символа с клавиатуры.

Для разрешения работы с курсором в байт Ntick записывается число 9, что соответствует паузе примерно в 0,5 сек, а в байт curstat — число 3. Напомним, что 1 в байте Curstat разрешает прерывающей подпрограмме выполнять отсчет времени и изменять состояние курсора, а 2 указывает на то, что курсор нарисован. После этого надо вызвать подпрограмму Tgicrsr, которая нарисует курсор. Теперь при каждом тике таймера состояние курсора будет изменяться на противоположное.

После ввода символа очищается байт curstat, а рисунок курсора удаляется с экрана, если он там находился.

Таким образом, мы "привязали" рисунок курсора к тому знакоместу, в которое помещается вводимый с клавиатуры символ и никаких других действий для управления курсором не требуется.

Обработка служебных символов

При вводе с клавиатуры BIOS не отображает символы на экране, это должна делать подпрограмма, используемая для записи кодов символов в буфер строки. Перед выводом изображения символа на экран надо убедиться в том, введен ли код ASCII. Он генерируется только при нажатии на определенные клавиши (их примерно половина), а в остальных случаях если код и существует, то не имеет практического смысла.

Вопрос о существовании кода ASCII решается на основании анализа scan code, если его значение меньше чем Збь, то код ASCII существует, в противном случае нажата одна из служебных клавиш. Если значение кода ASCII больше или равно 20п (код символа "пробел"), то изображение символа можно выводить на экран, в противном случае прочитан один из управляющих символов. По традиции управляющими называют те символы, у которых код ASCII имеет значения от о до iFh.

В тех случаях, когда код ASCII меньше, чем 20h или scan code больше чем 35h, нужен дополнительный анализ введенного кода для выбора вспомогательных действий. При редактировании строки текста достаточно использовать следующие коды: <левая стрелка> (4вь), <правая стрелка> (4Dh), <Delete> (53h), <возврат на шаг> (ОБЬ), <Enter> (ich); в скобках указано значение scan code. Дополнительно могут использоваться <стрелка вверх> (48h), <стрелка вниз> (зоь), <табуляция> (OFh) и другие коды.

Подпрограмма Inline

Текст подпрограммы, выполняющей ввод символов с клавиатуры и простые функции редактирования, приведен в примере 5.28. В разделе данных задачи надо выделить буфер, имеющий метку Linbuf, его размер должен быть достаточен для размещения вводимого текста (не более 80 байтов). В конце текста подпрограмма записывает пустой байт.

Пример 5.28. Ввод символов текста в буфер строки (Linbuf)

Inline: lea si, Linbuf si = адрес буфера строки
; Ввод очередного символа и управление курсором
Gs: cli запрещаем прерывания
mov CurStat, 03 CurStat = 3
mov Ntick, 09 Ntick =9 (0,5 сек)
sti разрешаем прерывание
call TglCrsr рисуем изображение курсора
mov ah, 00 код запроса ввода символа
int 16h ожидание ввода символа
cli запрещаем прерывания
test CurStat, 02 курсор нарисован ?
mov CurStat, 00 CurStat = О
sti разрешаем прерывания
je Prevanl -> нет, курсор погашен
call TglCrsr гашение курсора ; Предварительный анализ введенного кода
Prevanl: cmp ah, 35h код ASCII существует ?
ja Detail -> нет, это служебный символ
cmp al, 2Oh это управляющий символ ?
jb Detail -> да
mov ds:[si], al запись ASCII кода в буфер строки
inc si коррекция адреса
call outsgn вывод символа на экран
jmp SHORT Gs переход на продолжение ввода ; Детальный анализ служебных кодов
Detail: crap ah, ICh это символ "Enter" ?
jne Cont_l -> нет, продолжение анализа
mov byte ptr ds:[di], 00; запись признака конца текста
ret возврат из подпрограммы
Cont_l: cmp ah, OEh это символ "возврат на шаг" ?
jne Cont_2 -> нет, продолжение анализа
call prevpos адрес предыдущего знакоместа
mov al, ' ' al = код символа "пробел"
call outsgn стираем предыдущий символ
dec si и удаляем его из буфера строки
call prevpos адрес предыдущего знакоместа
jmp short Gs переход на продолжение ввода
Cont 2: jmp short Gs здесь можно продолжить анализ
; Вьиисление позиции предыдущего символа
Prevpos:sub di, 08 уменьшение адреса видеопамяти
jnc @F -> смена окна не нужна
mov ax, GrUnit ax = единица приращения памяти
sub Cur_win, ax Cur_win = Cur_win — GrUnit
@@: ret возврат из подпрограммы

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

Прерывания от таймера надо запрещать на время работы с переменными Ntick и CurStat. Напомним, что команда cli запрещает, a sti разрешает маскируемые прерывания. В примере 5.28 они используются дважды при разрешении и запрещении изменения состояния рисунка курсора. Во втором случае между test Curstat, 02 и je Prevani расположены две команды, выполнение которых не изменяет состояние регистра флагов (признаков). Это просто трюк, упрощающий проверку состояния и очистку байта
CurStat.

Коды введенного с клавиатуры символа находятся в регистрах ah (scan) и al (ASCII). Если код ASCII существует и его значение больше, чем iFh, то изображение символа выводится на экран и продолжается ввод.
Если обнаружен код служебного символа, то производится его детальный анализ. В примере 5.28 обрабатываются коды только двух символов — <Enter> и <возврат на шаг>. В других случаях подпрограмма просто игнорирует введенный код и ждет ввода с клавиатуры очередного символа. Если вы захотите дополнить подпрограмму, то вместо команды jmp short GS, имеющей метку Cont_2, вставьте команды, выполняющие анализ и обработку нужных вам кодов.

При обнаружении кода символа <Enter> в буфер строки записывается пустой байт и происходит возврат из подпрограммы.

Символ <позврат на шаг> обрабатывается так. Устанавливается адрес предыдущего знакоместа, в него выводится символ <пробел>, последний введенный символ удаляется из буфера строки и повторно устанавливается адрес предыдущего знакоместа. Установку адреса предыдущего знакоместа выполняет подпрограмма Prevpos, которая понадобится, если вы добавите обработку клавиши <левая стрелка> для перемещения влево по строке.

Пример вызова Inline. Для иллюстрации использования описанной подпрограммы мы перепишем текст примера 5.23, заменив в нем строку комментария двумя командами. Результат показан в примере 5.29.

Пример 5.29. Вывод текста информационной строки

Outlnf: push Cur win сохранение исходного значения Cur win
mov ax, Inflinw ax = номер окна информационной строки
mov Cur_win, ax Cur_win = ax
call Savinfo сохранение исходного фона
jmp short outstr переход на выборку первого символа
outl: call outsgn вывод на экран очередного символа
outstr: lodsb al = код очередного символа (al = ds:si)
or al, al конец выводимого текста ?
jne outl -> нет, переход на метку outl
call Inline ввод строки теста с клавиатуры
call Delinfo удаление информационной строки с экрана
pop Cur_win восстановление исходного значения Cur_win
call setwin восстановление исходного окна
ret возврат из подпрограммы

При выполнении примера 5.29 исходный фон будет сохранен на месте информационной строки, на экран будет выведен текст подсказки оператору, введен его ответ с эхо-печатью и записью введенных кодов в массив Linbuf и, после нажатия оператором на клавишу <Enter>, восстановлен исходный фон на месте информационной строки. Перечисленные действия выполняются с помощью подпрограмм, описанных в данном разделе. Использование данного примера для ввода спецификации файла описано в приложении А данной книги.

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


Содержание раздела