Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Методички по ассемблеру / Методичка_часть2.doc
Скачиваний:
602
Добавлен:
02.05.2014
Размер:
545.28 Кб
Скачать

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

4.4.1. Средства dos

Как и в случае вывода на экран, DOS предоставляет набор функций для чтения данных с клавиатуры, которые используют стандартное устройство ввода STDIN, так что можно использовать в качестве источника данных файл или стандартный вывод другой программы.

Функция DOS 0Ah— Считать строку символов из STDIN в буфер

Ввод:

АН = 0Ah DS:DX = адрес буфера

Вывод:

Буфер содержит введенную строку

Для вызова этой функции надо подготовить буфер, первый байт которого содержит максимальное число символов для ввода (1 – 254), а содержимое, если оно задано, может использоваться как подсказка для ввода. При наборе строки обрабатываются клавиши Esc, F3, F5, BS, Ctrl-C/Ctrl-Break и т.д., как при наборе команд DOS (то есть Esc начинает ввод сначала, F3 восстанавливает подсказку для ввода, F5 запоминает текущую строку как подсказку, Backspace стирает предыдущий символ). После нажатия клавиши Enter строка (включая последний символ CR (0Dh)) записывается в буфер, начиная с третьего байта. Во второй байт записывается длина реально введенной строки без учета последнего CR.

Рассмотрим пример программы, выполняющей преобразование десятичного числа в шестнадцатеричное.

; dosinl.asm

; Переводит десятичное число в шестнадцатеричное

;

.model tiny

.code

.286 ; для команды shr al,4

org 100h ; начало СОМ-файла

start:

mov dx,offset message1

mov ah,9

int 21h ; вывести приглашение ко вводу message1

mov dx,offset buffer

mov ah,0Ah

int 21h ; считать строку символов в буфер

mov dx,offset crlf

mov ah,9

int 21h ; перевод строки

; перевод числа в ASCII-формате из буфера в бинарное число в АХ

xor di,di ; DI = 0 - номер байта в буфере

xor ах,ах ; АХ = 0 - текущее значение результата

mov cl,blength

xor ch,ch

xor bx,bx

mov si,cx ; SI - длина буфера

mov cl,10 ; CL = 10, множитель для MUL

asc2hex:

mov bl,byte ptr bcontents[di]

sub bl,'0' ; цифра = код цифры - код символа "0",

jb asc_error ; если код символа был меньше, чем код "0",

cmp bl,9 ; или больше, чем "9",

ja asc_error ; выйти из программы с сообщением об ошибке,

mul cx ; иначе: умножить текущий результат на 10,

add ax,bx ; добавить к нему новую цифру,

inc di ; увеличить счетчик

cmp di,si ; если счетчик+1 меньше числа символов -

jb asc2hex ; продолжить (счетчик считается от 0)

; вывод на экран строки message2

push ax ; сохранить результат преобразования

mov ah,9

mov dx,offset message2

int 21h

pop ax

; вывод на экран числа из регистра АХ

push ax

xchg ah,al ; поместить в AL старший байт

call print_al ; вывести его на экран

pop ax ; восстановить в AL младший байт

call print_al ; вывести его на экран

ret ; завершение СОМ-файла

asc_error:

mov dx,offset err_msg

mov ah,9

int 21h ; вывести сообщение об ошибке

ret ; и завершить программу

; Процедура print_al

; выводит на экран число в регистре AL

; в шестнадцатеричном формате,

; модифицирует значения регистров АХ и DX

print_al:

mov dh,al

and dh,0Fh ; DH - младшие 4 бита

shr al,4 ; AL - старшие

call print_nibble ; вывести старшую цифру

mov al,dh ; теперь AL содержит младшие 4 бита

print_nibble: ; процедура вывода 4 бит (шестнадцатеричной цифры)

cmp al,10 ; три команды, переводящие цифру в AL

sbb al,69h ; в соответствующий ASCII-код

das ; (см. описание команды DAS)

mov dl,al ; код символа в DL

mov ah,2 ; номер функции DOS в АН

int 21h ; вывод символа

ret ; этот RET работает два раза - один раз

; для возврата из процедуры print_nibble,

; вызванной для старшей цифры,

; и второй раз - для возврата из print_al

messagel db "Десятичное число: $"

message2 db "Шестнадцатеричное число: $"

err_msg db "Ошибка ввода"

crlf db 0Dh,0Ah,'$'

buffer db 6 ; максимальный размер буфера ввода

blength db ? ; размер буфера после считывания

bcontents: ; содержимое буфера располагается за

; концом СОМ-файла

end start

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

Функция DOS 01h— Считать символ из STDIN с эхом, ожиданием и проверкой на Ctrl-Break

Ввод:

АН = 01h

Вывод:

AL = ASCII-код символа или 0. Если AL = 0, второй вызов этой функции возвратит в AL расширенный ASCII-код символа

При чтении с помощью этой функции введенный символ автоматически немедленно отображается на экране (посылается в устройство STDOUT — так что его можно перенаправить в файл). При нажатии Ctrl-C или Ctrl-Break выполняется команда INT 23h. Если нажата клавиша, не соответствующая какому-нибудь символу (стрелки, функциональные клавиши Ins, Del и т.д.), то в AL возвращается 0 и функцию надо вызвать еще один раз, чтобы получить расширенный ASCII-код (см. приложение 1).

В трех следующих вариантах этой функции код символа возвращается в AL по такому же принципу.

Функция DOS 08h— Считать символ из STDIN без эха, с ожиданием и проверкой на Ctrl-Break

Ввод:

АН = 08h

Вывод:

AL = код символа

Функция DOS 07h— Считать символ из STDIN без эха, с ожиданием и без проверки на Ctrl-Break

Ввод:

АН = 07h

Вывод:

AL = код символа

Функция DOS 06h— Считать символ из STDIN без эха, без ожидания и без проверки на Ctrl-Break

Ввод:

АН = 07h DL = 0FFh

Вывод:

ZF = 1, если не была нажата клавиша, и AL = 00 ZF = 0, если клавиша была нажата. В этом случае AL = код символа

Кроме перечисленных функций могут потребоваться и некоторые служебные функции DOS для работы с клавиатурой.

Функция DOS 0Bh— Проверить состояние клавиатуры

Ввод:

АН = 0Bh

Вывод:

AL = 0, если не была нажата клавиша AL = 0FFh, если была нажата клавиша

Эту функцию удобно использовать перед функциями 01, 07 и 08, чтобы не ждать нажатия клавиши. Кроме того, вызов этой функции позволяет проверить, не считывая символ с клавиатуры, была ли нажата комбинация клавиш Ctrl-Break; если это произошло, выполнится прерывание 23h.

Функция DOS 0Ch— Очистить буфер и считать символ

Ввод:

АН = 0Ch AL = Номер функции DOS (01, 06, 07, 08, 0Ah)

Вывод:

Зависит от вызванной функции

Функция 0Ch очищает буфер клавиатуры, так что следующая функция чтения символа будет ждать ввода с клавиатуры, а не использовать нажатый ранее и еще не обработанный символ. Например, именно эта функция используется для считывания ответа на вопрос «Уверен ли пользователь в том, что он хочет отформатировать диск?».

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

; dosin2.asm

; Изображает пентамино F, которое можно перемещать по экрану клавишами

; управления курсором и вращать клавишами X и Z. Выход из программы - Esc.

;

line_length = 3 ; число символов в строке изображения

number_of_lines = 3 ; число строк

.model tiny

.code

org 100h ; начало СОМ-файла

start:

cld ; будут использоваться команды

; строковой обработки

mov ax,0B800h ; адрес начала текстовой видеопамяти

mov es,ax ; в ES

mov ax,0003h

int 10h ; текстовый режим 03 (80x25)

mov ah,02h ; установить курсор

mov bh,0

mov dh,26 ; на строку 26, то есть за пределы экрана

mov dl,1

int 10h ; теперь курсора на экране нет

call update_screen ; вывести изображение

; основной цикл опроса клавиатуры

main_loop:

mov ah,08h ; считать символ с клавиатуры

int 21h ; без эха, с ожиданием, с проверкой на Ctrl-Break

test al,al ; если AL = 0

jz eASCII_entered ; введен символ расширенного ASCII

cmp al,1Bh ; иначе: если введен символ 1Bh (Esc),

je key_ESC ; выйти из программы,

cmp al,'Z' ; если введен символ Z,

je key_Z ; перейти на его обработчик

cmp al,'z' ; то же для z

je key_Z

cmp al,'X' ; если введен символ X,

je key_X ; перейти на его обработчик

cmp al,'х' ; то же для х

je key_X

jmp short main_loop ; считать следующую клавишу

eASCII_entered: ; был введен расширенный ASCII-символ

int 21h ; получить его код (повторный вызов функции)

cmp al,48h ; стрелка вверх

je key_UP

cmp al,50h ; стрелка вниз

je key_DOWN

cmp al,4Bh ; стрелка влево

je key_LEFT

cmp al,4Dh ; стрелка вправо

je key_RIGHT

jmp short main_loop ; считать следующую клавишу

;

; обработчики нажатий клавиш

;

key_ESC: ; Esc

ret ; завершить СОМ-программу

key_UP: ; стрелка вверх

cmp byte ptr start_row,0 ; если изображение на верхнем

; краю экрана,

jna main_loop ; считать следующую клавишу,

dec byte ptr start_row ; иначе - уменьшить номер строки,

call update_screen ; вывести новое изображение

jmp short main_loop ; и считать следующую клавишу

key_DOWN: ; стрелка вниз

cmp byte ptr start_row,25-number_of_lines ; если

; изображение на нижнем краю экрана,

jnb main_loop ; считать следующую клавишу,

inc byte ptr start_row ; иначе - увеличить номер строки,

call update_screen ; вывести новое изображение

jmp short main_loop ; и считать следующую клавишу

key_LEFT: ; стрелка влево

cmp byte ptr start_col,0 ; если изображение на левом краю

; экрана,

jna main_loop ; считать следующую клавишу,

dec byte ptr start_col ; иначе - уменьшить номер столбца,

call update_screen ; вывести новое изображение

jmp short main_loop ; и считать следующую клавишу

key_RIGHT: ; стрелка вправо

cmp byte ptr start_col,80-line_length ; если

; изображение на правом краю экрана,

jnb main_loop ; считать следующую клавишу,

inc byte ptr start_col ; иначе - увеличить номер столбца,

call update_screen ; вывести новое изображение

jmp short main_loop ; и считать следующую клавишу

key_Z: ; клавиша Z (вращение влево)

mov ax,current_screen ; считать номер текущего изображения

; (значения 0, 1, 2, 3),

dec ax ; уменьшить его на 1,

jns key_Z_ok ; если получился -1 (поменялся знак),

mov ах,3 ; АХ = 3

key_Z_ok:

mov current_screen,ax ; записать номер обратно,

call update_screen ; вывести новое изображение

jmp main_loop ; и считать следующую клавишу

key_X: ; клавиша X (вращение вправо)

mov ax,current_screen ; считать номер текущего изображения

; (значения 0, 1, 2, 3),

inc ax ; увеличить его на 1,

cmp ax,4 ; если номер стал равен 4,

jne key_X_ok

xor ах,ах ; АХ = 0

key_X_ok:

mov current_screen,ax ; записать номер обратно,

call update_screen ; вывести новое изображение

jmp main_loop ; и считать следующую клавишу

; процедура update_screen

; очищает экран и выводит текущее изображение

; модифицирует значения регистров АХ, ВХ, СХ, DX, SI, DI

update_screen:

mov cx,25*80 ; число символов на экране

mov ax,0F20h; ; символ 20h (пробел) с атрибутом 0Fh

; (белый на черном)

xor di,di ; ES:DI = начало видеопамяти

rep stosw ; очистить экран

mov bx,current_screen ; номер текущего изображения в ВХ

shl bx,1 ; умножить на 2, так как screens - массив слов

mov si,screens[bx] ; поместить в ВХ смещение начала

; текущего изображения из массива screens,

mov ax,start_row ; вычислить адрес начала

mul row_length ; изображения в видеопамяти

add ax,start_col ; (строка * 80 + столбец) * 2

shl ax,1

mov di,ax ; ES:DI - начало изображения в видеопамяти

mov ah,0Fh ; используемый атрибут - белый на черном

mov dx,number_of_lines ; число строк в изображении

сору_lines:

mov cx,line_length ; число символов в строке

copy_1: lodsb ; считать ASCII-код в AL,

stosw ; записать его в видеопамять

; (AL - ASCII, АН - атрибут),

loop copy_1 ; вывести так все символы в строке,

add di,(80-line_length)*2 ; перевести DI на начало

; следующей строки экрана,

dec dx ; если строки не закончились -

jnz copy_lines ; вывести следующую

ret ; конец процедуры update_screen

; изображение пентамино F

screen1 db " XX" ; выводимое изображение

db "XX "

db " X "

screen2 db " X " ; поворот на 90 градусов вправо

db "XXX"

db " X"

screen3 db " X " ; поворот на 180 градусов

db " XX"

db "XX "

screen4 db "X " ; поворот на 90 градусов влево

db "XXX"

db " X "

; массив, содержащий адреса всех вариантов изображения

screens dw screen1,screen2,screen3,screen4

current_screen dw 0 ; текущий вариант изображения

start_row dw 10 ; текущая верхняя строка изображения

start_col dw 37 ; текущий левый столбец

row_length db 80 ; длина строки экрана для команды MUL

end start

В этом примере для вывода на экран используется прямое копирование в видеопамять, так как вызов функции BIOS вывода строки (INT 10h, АН = 13h) прокручивает экран вверх на одну строку при выводе символа в нижнем правом углу экрана.