4.6. Работа с мышью

Все общение с мышью в DOS выполняется через прерывание 33h, обработчик которого устанавливает драйвер мыши, загружаемый обычно при запуске сис­темы. Современные драйверы поддерживают около 60 функций, позволяющих настраивать разрешение мыши, профили ускорений, виртуальные координаты, дополнительные обработчики событий и т. п. Большинство этих функций требу­ются редко, сейчас рассмотрим основные.

INT33h, АХ- 0: Инициализация мыши Вход:   АХ - OOOOh

Выход: АХ = OOOOh, если мышь или драйвер мыши не установлены АХ = OFFFFh, если драйвер и мышь установлены ВХ = число кнопок:

0002 или OFFFFh - две '

0003 - три

0000 — другое количество

Выполняется аппаратный и программный сброс мыши и драйвера.

INT33K AX = 1: Показать курсор Вход:    AX = 0001h

INT33h, AX = 2: Спрятать курсор Вход:    АХ - 0002h

Драйвер мыши поддерживает внутренний счетчик, управляющий видимостью курсора мыши. Функция 2 уменьшает значение счетчика на единицу, а функция 1 увеличивает его, но только до значения 0. Если значение счетчика - отрицатель­ное число, он спрятан, если ноль - показан. Это позволяет процедурам, исйользу-прямой вывод в видеопамять, вызывать функцию 2 в самом начале и 1 в са­мом конце, не заботясь о том, в каком состоянии был курсор мыши у вызвавшей эту процедуру программы.

INT33h, АХ = 3: Определить состояние мыши Вход:    АХ = ОООЗЬ Выход: ВХ = состояние кнопок:

бит 0: нажата левая кнопка

бит 1: нажата правая кнопка

бит 2: нажата средняя кнопка

СХ = Х-координата ЭХ = У-координата

Возвращаемые координаты совпадают с координатами пикселов соответству­ющей точки на экране в большинстве графических режимов, кроме 04, 05, СЮп, 13Ь, где Х-координату мыши нужно разделить на 2, чтобы получить номер стол­бца соответствующей точки на экране. В текстовых режимах обе координаты надо разделить на 8 для получения номера строки и столбца соответственно.

В большинстве случаев эта функция не используется в программах, так как для того, чтобы реагировать на нажатие кнопки или перемещение мыши в заданную область, требуется вызывать это прерывание постоянно, что приводит к трате процессорного времени. Функции 5 (определить положение курсора при после­днем нажатии кнопки), 6 (определить положение курсора при последнем отпус­кании кнопки) и ОВЬ (определить расстояние, пройденное мышью) могут помочь оптимизировать работу программы, самостоятельно «следящей» за всеми пере­движениями мыши, но гораздо эффективнее указать драйверу контролировать ее передвижения (чем он, собственно, и занимается постоянно) и передавать управ­ление в программу, как только выполнится заранее определенное условие, напри­мер пользователь нажмет на левую кнопку мыши. Такой сервис обеспечивает функция ОСЬ - установить обработчик событий.

тГЗЗКАХ = ОСк Установить обработчик событий Вход:     АХ = ОООСЬ

ЕБ:ЭХ = адрес обработчика СХ = условие вызова

бит 0: любое перемещение мыши бит    нажатие левой кнопки бит 2: отпускание левой кнопки бит 3: нажатие правой кнопки бит 4: отпускание правой кнопки

бит 5: нажатие средней кнопки

бит 6: отпускание средней кнопки СХ = ООООЬ - отменить обработчик

Обработчик событий должен быть оформлен, как дальняя процедура (то есть завершаться командой ЯЕТЕ). На входе в процедуру обработчика АХ содержит условие вызова, ВХ - состояние кнопок, СХ, ЦХ - Х- и У-координаты курсора, Б1, - счетчики последнего перемещения по горизонтали и вертикали (едини­цы измерения для этих счетчиков - мики, 1/200 дюйма), - сегмент данных драйвера мыши. Перед завершением программы установленный обработчик со­бытий должен быть обязательно удален (вызов функции ОСЬ с СХ = 0), так как иначе при первом же выполнении условия управление будет передано по адресу в памяти, с которого начинался обработчик.

Функция ОСЬ используется так часто, что у нее появилось несколько модифи­каций - функция 14Ь, дающая возможность установить одновременно три обра­ботчика с разными условиями, и функция       также позволяющая установить три обработчика и включающая в условие вызова состояние клавиш Shift, Ctrl и Alt. Воспользуемся обычной функцией OCh, чтобы написать простую програм­му для рисования.

; moused г.asm

; Рисует на экране прямые линии, оканчивающиеся в позициях, которые указываются мышью.

.model

tiny

 

. code

 

 

org

100h ■

; СОМ-файл. ]

.186

 

; Для команды эЬг сх,3.

mov

ax,12h

 

int

lOh

; Видеорежим 640x480. ■

mov

ax,0

; Инициализировать мышь.

int

33h

 

mov

ax, 1

; Показать курсор мыши.

int

33h

 

mov

ax.OOOCh

; Установить обработчик событий мыши.

mov

cx,0002h

; Событие - нажатие левой кнопки.

mov

dx,offset handler

;  Е8:РХ - адрес обработчика.

int

33h

 

mov

ah,0

; Ожидание нажатия любой клавиши. :

int

16h

 

mov

ax.OOOCh

 

mov

cx.OOOOh

; Удалить обработчик событий мыши. ;

int

33h

 

mov

ax,3

; Текстовый режим.

int

10h

 

ret

 

; Конец программы.

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

точки к текущей.

handler:

push

OAOOOh

 

pop

es .

;  ES - начало видеопамяти.

push

cs

 

pop

ds

; DS - сегмент кода и данных этой программы.

push

cx

; СХ (Х-координата) и DX (Y-координата)

push

dx

;  потребуются в конце.

mov

ax, 2

; Спрятать курсор мыши перед выводом на: экран.

int

33h

 

cmp

word ptr previous_X,-1

;  Если это первый вызов,

je

first_point

; только вывести точку.

call

line_bresenham

;  Иначе - провести прямую.

exit_handler: pop pop mov mov

mov

int retf

.first_point: call jmp

dx

cx

previous_X,cx previous_Y,dx

ax, l

33h

putpixellb

short exit handler

; Восстановить СХ и ГХ

; и запомнить их как предыдущие

; координаты.

; Показать курсор мыши.

; Выход из обработчика - команда КЕТЕ.

; Вывод одной точки (при первом вызове).

Процедура рисования прямой линии с использованием алгоритма Брезенхама. Вход: СХ, ОХ - X, У конечной точки, ргеу1оиБ_Х,ргеу1ои5_У - X, У начальной точки.

line_bresenham:

mov

sub

jns neg mov jmp mov

dx_pos:

dx_neg:.

dy_pos: dy_neg:

cycle:

mov

sub

jns neg mov jmp mov

shl shl

call

cmp

jna

mov shr neg

add

cmp je cmp Л

ax.cx

ax,previous_X

dx_pos

ax

word ptr X_increment, 1

short dx_neg

word ptr X_increment, -1 bx, dx

bx,previous_Y

dy_pos bx

word ptr Y_increment, 1

short dy_neg

word ptr Y_increment,-1

ax, 1 bx,1

putpixellb

ax, bx

dx_le_dy di, ax

di,1 di

di.bx

ptr previous_X exit_bres

di,0

fractltO

АХ = длина проекции прямой на ось X. Если АХ отрицательный -сменить его знак, причем координата X при выводе прямой будет расти. Иначе - уменьшаться.

ВХ = длина проекции прямой на ось У. Если ВХ отрицательный -сменить его знак, причем координата У при выводе

прямой будет расти.

Иначе - уменьшаться.

Удвоить значения проекций,

чтобы избежать работы с полуцелыми числами.

Вывести первую точку (прямая рисуется от к

Если проекция на ось X больше, чем на У,

01 будет указывать,  в какую сторону

отклонились от идеальной прямой.

Оптимальное начальное значение ГЯ:

Г1 = 2 * бу - бх

Основной цикл выполняется,

пока X не станет равно ргеуЮиэ.Х.

Если Г1 > О,

мы

fractltO:

add sub

:

add

add

call

jmp

dx_le_dy:

mov shr neg

add

cycle2:.

cmp je

cmp

jl

add sub fractlt02: add add

call

jmp exit„bres:

ret

ptr

di, ax

ptr

di, bx putpixellb

short cycle

di, bx

di, 1

di

di, ax

dx.word ptr previous_Y exit_bres

di,0

fractlt02

cx.word ptr X_increment di, bx

ptr

di, ax

putpixellb short cycle2 перейти к следующему У и уменьшить Б1 на 2 * Сх.

Следующий X (на каждом шаге). Увеличить Б1 на 2 * Су. Вывести точку. Продолжить цикл.

Если проекция на ось У больше, чем на X.

Оптимальное начальное значение DI = 2 . dx - dy.

DI;

Основной цикл выполняется,

пока Y не станет равным previously..

Если 01 > О,

перейти к следующему X и уменьшить DI на 2 * dy.

Следующий Y (на каждом шаге). Увеличить DI на 2 * dy, вывести точку, продолжить цикл.

Конец процедуры.

; Процедура вывода точки на экран в режиме, исполвзующем один бит для

; хранения одного пиксела.

; БХ = строка,  СХ = столбец.

; Все регистры сохраняются.

ривріхеІІЬ:

pusha

 

; Сохранить регистры.

хог

bx, bx

 

mov

ax,dx

; АХ = номер строки.

imul

ax,ax,80

; АХ = номер строки х число байтов в

push

cx

 

shr

cx,3

; СХ = номер байта в строке.

add

ax, cx

; АХ = номер байта в видеопамяти.

mov

di, ax

; Поместитв его в 31 и Ш для команд

mov

si, di

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

pop

cx

; СХ снова содержит номер столбца.

mov

bx,0080h

 

and

cx,07h

: Последние три бита СХ =

строке.

shr

bx,

остаток от деления на 8 = номер бита в байте

считая справа налево.

Теперь в ВЬ установлен в 1 нужный бит.

lods      es:byte ptr some_label     ;  AL = байт из видеопамяти.

or ax.bx ;  Установитв выводимый бит в 1,

Чтобы стереть пиксел о экрана,   эту команду OR можно заменить на not bx and ax.bx

или лучше инициализировать ВХ не числом 0080h,  а числом FF7Fh и использовать

только and

stosb

 

 

И вернутв байт на место. ■

рора

 

 

Восстановитв регистры.

ret

 

 

Конец.

previous_X

dw

-1

Предыдущая Х-координата.

previous_Y

dw

-1

Предыдущая У-Координата.

Y_increment

dw

-1

Направление изменения У.

X_increment

dw

-1

Направление изменения X.

some_label:

 

 

Метка,   исполвзуемая для переопределения

сегмента-источника для lods с DS на ES.

end start

Алгоритм Брезенхама, использованный в нашей программе, является самым

распространенным алгоритмом рисования прямой. Существуют, конечно, и более

эффективные, например алгоритм Цаолинь By, работающий по принципу конеч­ного автомата, но алгоритм Брезенхама стал стандартом де-факто.

Реализовать этот алгоритм можно гораздо быстрее при помощи самомо­дифицирующегося кода, то есть после проверки на направление прямой вна­чале алгоритма прямо в дальнейший текст программы команды INC СХ, DEC СХ, INCDXu DEC DX вместо команд сложения этих регис­тров с переменными XJncrement и YJncrement. Самомодифицирующийся код часто применяется при программировании для DOS, но в большинстве многозадачных систем текст программы загружается в область памяти, защищенную от записи, вот почему в последнее время зона применения это­го подхода становится ограниченной.