Консультация № 179212
23.06.2010, 12:01
0.00 руб.
0 12 1
Уважаемые эксперты, в приложении программа перемещения шаблона в виде прямоугольника по экрану с помощью управляющих клавиш. Прямоугольник перемещается только или вверх, или вниз, или вправо, или влево, что не очень удобно. А можно ли осуществить движение этого шаблона по экрану с помощью двух клавиш одновременно, сделать так, чтобы шаблон двигался не только или влево, или вправо, или вверх, или вниз, но и вверх-влево или вверх-вправо, т.е. не только под прямым углом, но и по диагонали под любым углом?


Приложение:
.model tiny
.386
.code
.startup

mov ax,0013h ;320x200x256
int 10h
mov ax, 0a000h
mov es, ax
cld
MainLoop:
call DrawMovingImage
WaitKey:
mov ah,1
int 16h
jz MainLoop
mov ah, 0
int 16h
cmp ah, 1 ;по Esc выходим
je Exit
mov bx,3 ;шаг движения
cmp ah, 4bh ;стрелка влево
je left
cmp ah, 4dh ;стрелка вправо
je right
cmp ah, 48h ;стрелка вверх
je up
cmp ah, 50h ;стрелка вниз
je down
jne WaitKey
right:
mov ax, StringLength;проверим правый край
sub ax, ImageL
sub ax, bx ;ax - самая правая позиция, при которой
; при добавлении шага не выйдем за правый край
cmp ax, ImageC ;сравним с текущей
jbe WaitKey
call ClearImage ;очищаем старое
add ImageC, bx ;добавляем шаг
jmp MainLoop ;и на прорисовку

left: mov ax, ImageC ;проверим, можем ли сдвинуться влево
sub ax, bx
jl WaitKey
call ClearImage
sub ImageC, bx ;уменьшаем шаг
jmp MainLoop ;перерисовываем

up:
mov ax,ImageS
sub ax, bx
jl WaitKey
call ClearImage
sub ImageS, bx ;добавляем шаг
jmp MainLoop ;перерисовываем
down:
mov ax,StringHeigth
sub ax, ImageH
sub ax, bx
cmp ax, ImageS
jbe WaitKey
call ClearImage
add ImageS, bx ;уменьшаем шаг
jmp MainLoop ;перерисовываем

Exit:
mov ax, 0003h
int 10h
mov ax, 4c00h
int 21h

DrawMovingImage proc
pusha
; Вычислить адрес начальной точки для вывода маски
mov AX,StringLength
mov DX,[ImageS]
mul DX
; Прибавить длину колонки
add AX,[ImageC]
mov DI,AX
; Записать адрес маски в индексный регистр
lea SI,[Flm] ;указатель на маску объекта

; Вывести изображение
mov DX,ImageH ;высота маски
M0: ;Вывести очередную строку маски
mov CX,ImageL ;ширина маски
M1: ; Проверить точку маски
lodsb
and AL,AL ;код цвета равен нулю?
jz M2 ;пропустить точку
mov ES:[DI],AL ;вывести точку
M2: ; Перейти к следующей точке
inc DI
loop M1
; Перейти на следующую строку
add DI,StringLength
sub DI,ImageL
dec DX
jnz M0
popa
ret
DrawMovingImage endp

ClearImage proc
pusha
; Вычислить адрес начальной точки для вывода маски
; Умножить длину строки на номер строки
mov AX,StringLength
mov DX,[ImageS]
mul DX
; Прибавить длину колонки
add AX,[ImageC]
mov DI,AX
; Записать адрес маски в индексный регистр
lea SI,[Flm] ;указатель на маску объекта

; Вывести изображение
mov DX,ImageH ;высота маски
M3: ;Вывести очередную строку маски
mov CX,ImageL ;ширина маски
mov al, 0
rep stosb
; Перейти на следующую строку
add DI,StringLength
sub DI,ImageL
dec DX
jnz M3
popa
ret
ClearImage endp
.data
; Маска
Flm DB 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15
DB 15,12,12,12,12,12,12,12,12,12,12,12,12,12,12,15
DB 15,12,12,12,12,12,12,12,12,12,12,12,12,12,12,15
DB 15,12,12,12,12,12,12,12,12,12,12,12,12,12,12,15
DB 15,12,12,12,12,12,12,12,12,12,12,12,12,12,12,15
DB 15,12,12,12,12,12,12,12,12,12,12,12,12,12,12,15
DB 15,12,12,12,12,12,12,12,12,12,12,12,12,12,12,15
DB 15,12,12,12,12,12,12,12,12,12,12,12,12,12,12,15
DB 15,12,12,12,12,12,12,12,12,12,12,12,12,12,12,15
DB 15,12,12,12,12,12,12,12,12,12,12,12,12,12,12,15
DB 15,12,12,12,12,12,12,12,12,12,12,12,12,12,12,15
DB 15,12,12,12,12,12,12,12,12,12,12,12,12,12,12,15
DB 15,12,12,12,12,12,12,12,12,12,12,12,12,12,12,15
DB 15,12,12,12,12,12,12,12,12,12,12,12,12,12,12,15
DB 15,12,12,12,12,12,12,12,12,12,12,12,12,12,12,15
DB 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15
StringLength dw 320 ;длина строки
StringHeigth dw 200 ;количество строк
; Позиция маски изображения на экране
ImageS dw 90 ;строка
ImageC dw 150 ;колонка
; Размеры маски изображения
ImageL equ 16 ;ширина маски
ImageH equ 16 ;высота маски
END

Обсуждение

Неизвестный
23.06.2010, 12:51
общий
под любым это сколько должно быть комбинаций клавиш, проще назначить др клавиши на движения под углом
Неизвестный
23.06.2010, 16:56
общий
Adsorores:
можно все, вопрос только в том, насколько это накладно.

вариант 1. Движение по 8-ми направлениям (вправо-влево, вверх-вниз, по диагоналям под углом 45 градусов)
способ 1. Вводим дополнительно еще 4 клавиши, которые будут отвечать за диагонали (напр., клавиши на доп.клавиатуре 1, 3, 7 и 9). Реализуемо в вашей программе путем добавления соответствующей обработки клавиш.
способ 2. Если хотим для диагоналей одновременное нажатие "стрелочек". Тогда нужно обрабатывать прерывание от клавиатуры (Int 09), чтобы "ловить" отпускание клавиш (с помощью 16-го прерывания их не получить).

вариант 2. Движение под любыми углами
Изменяем алгоритм: клавиши отвечают не за направление движения, а за скорость и угол поворота.
Вводим для описания состояния шаблона кроме координат еще и 2 величины: скорость и угол движения. Можно ограничить максимальную скорость.
Назначаем следующие функции для клавиш:
- стрелка вверх: увеличить скорость перемещения
- стрелка вниз: уменьшить скорость перемещения
- стрелка вправо: сделать правый поворот (поворот по часовой стрелке)
- стрелка влево: сделать левый поворот (поворот против часовой стрелки)

При поворотах увеличиваем или уменьшаем значение угла движения.

Тогда приращение координаты X будет равно "скорость" * Cos ("угол движения"), а приращение координаты Y - "скорость" * Sin ("угол движения")
давно
Старший Модератор
31795
6196
23.06.2010, 20:07
общий
это ответ
Здравствуйте, Adsorores.

Смотрите приложение.
В программе устанавливается обработчик контролера клавиатуры, который заполняет массив DataKey. Первые 128 байт служат признаком нажатия клавиши, вторые признаком её отпускания. Сама программа только контролирует нажата нужная клавиша и реагирует на событие. Такой подход позволяет Вам обрабатывать комбинацию из несколько нажатых клавиш одновременно.
К примеру: cmp byte ptr DataKey[ keyESC ],1, если к значению keyESC добавить ещё 128, то перемещать фигуру можно даже, когда нажата клавиша ЕSC, а программа завершится, только когда Вы её отпустите.
ps:вопросы задавайте в мини-форум.
Удачи!


Приложение:
.model tiny
.386
.code
.startup

mov ax,0013h ;320x200x256
int 10h
;читаем старый обработчик
mov ax,3509h
int 21h
mov old09o,bx
mov old09s,es
;ставим новый обработчик
mov ax,2509h
mov dx,offset new09
int 21h
;настраиваемся на видео буфер
mov ax,0A000h
mov es, ax
cld
keyESC equ 01h
keyUp equ 48h
keyDown equ 50h
keyLeft equ 4Bh
keyRight equ 4Dh
MainLoop:
;считаем адрес
call CalcAdr
;рисуем рисунок
call DrawMovingImage
;задержка
mov cx,5000
MM1: push cx
mov cx,5000
MM2: loop MM2
pop cx
loop MM1
;изменяем координаты
xor dx,dx
xor bx,bx
;изменяем приращение
add bl,DataKey[keyDown]
add dl,DataKey[keyRight]
sub bl,DataKey[keyUp]
sub dl,DataKey[keyLeft]
;контроль
;вертикальная координата
mov ax,bx
cbw
add ax,ImageS
jz IgnoreV
js IgnoreV
push ax
add ax,ImageH
cmp ax,StringHeigth
pop ax
jae IgnoreV
mov imageS,ax
IgnoreV:
;контроль
;горизонтальная координата
mov ax,dx
cbw
add ax,ImageC
jz IgnoreH
js IgnoreH
push ax
add ax,ImageL
cmp ax,StringLength
pop ax
jae IgnoreH
mov imageC,ax
;прячем рисунок
IgnoreH: call ClearImage
;проверяем ESC
cmp byte ptr DataKey[keyESC],1
jnz MainLoop
;востанавливаем старый обработчик
mov ax,2509h
mov dx,old09o
mov ds,old09s
int 21h
;завершение программы
mov ax, 4c00h
int 21h
;старый обработчик
old09o dw ?
old09s dw ?
;наш обработчик
new09: pusha
;читаем порт
in al,60h
;заполняем массив
xor bx,bx
mov bl,al
mov al,1
mov cs:DataKey[bx],al
xor bl,80h
xor al,al
mov cs:DataKey[bx],al
;стандартное завершение прерывания
in al,61h
push ax
or al,80h
out 61h,al
pop ax
out 61h,al
mov al,20h
out 20h,al
popa
iret
;
DrawMovingImage proc
pusha
; Записать адрес маски в индексный регистр
mov SI,offset Flm;указатель на маску объекта
; Вывести изображение
mov cx,ImageH ;высота маски
M0: ;Вывести очередную строку маски
push cx
mov CX,ImageL;ширина маски
push di
rep movsb
pop di
;Перейти на следующую строку
add DI,StringLength
pop cx
loop M0
popa
ret
DrawMovingImage endp
ClearImage proc
pusha
; Вывести изображение
mov cx,ImageH ;высота маски
M3: ;Вывести очередную строку маски
push cx
mov CX,ImageL ;ширина маски
mov al, 0
push di
rep stosb
pop di
; Перейти на следующую строку
add DI,StringLength
pop cx
loop M3
popa
ret
ClearImage endp
CalcAdr proc
; Вычислить адрес начальной точки для вывода маски
; Умножить длину строки на номер строки
mov AX,StringLength
mul ImageS
; Прибавить длину колонки
add AX,ImageC
mov DI,AX
ret
CalcAdr endp
.data
DataKey db 256 dup(0)
; Маска
Flm DB 15,15,15,15 ,15,15,15,15 ,15,15,15,15 ,15,15,15,15
ImageL equ $ - Flm; 16 ;ширина маски
DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15
DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15
DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15

DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15
DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15
DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15
DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15

DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15
DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15
DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15
DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15

DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15
DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15
DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15
DB 15,15,15,15 ,15,15,15,15 ,15,15,15,15 ,15,15,15,15
ImageH equ ($ - Flm) / ImageL;16 ;высота маски

StringLength dw 320 ;длина строки
StringHeigth dw 200 ;количество строк
; Позиция маски изображения на экране
ImageS dw 90 ;строка
ImageC dw 150 ;колонка
; Размеры маски изображения
END
5
Спасибо за программу!
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

Неизвестный
24.06.2010, 10:26
общий
Моя вина, упустил из вида варианты движения.
Если к нижней границе экрана провести перпендикуляр, то угол от перпендикуляра не более 45 градусов влево и не более 45 градусов вправо. Думаю, до 45 градусов алгоритм один будет. Это вариант 1 способ 2 - обработка прерывание от клавиатуры.
Неизвестный
24.06.2010, 10:31
общий
Зенченко Константин Николаевич:
Обработчики прерываний ставятся только в 21h? Есть ли обработчики до int 21h? С помощью BIOS например.
Неизвестный
24.06.2010, 10:49
общий
_Ayl_:
Спасибо за уточнение моего вопроса! Я уже получил нужный вариант ответа на свой вопрос, но интересно было бы посмотреть на программу Вашего варианта 2 "Движение под любыми углами".
Неизвестный
24.06.2010, 10:50
общий
Adsorores:
Установить обработчик прерывания можно либо напрямую (с помощью непосредственной записи адреса своего обработчика в таблицу прерываний), либо с помощью DOS (функция 25h прерывания 21h). В BIOS подобных функций нет.
В приведенном коде устанавливается обработчик аппаратного прерывания 09h - прерывание от клавиатуры. Это самый низкий уровень.
Вот код для установки прерывания непосредственно в таблицу прерываний:

Код:

; установим сегментный регистр на сегмент таблицы прерываний
xor ax, ax
mov es, ax
; читаем старый обработчик
mov ax, es:[4*9]
mov old09o, ax
mov ax, es:[4*9+2]
mov old09s, ax
; ставим новый обработчик (запрет прерываний - обязательно!)
cli
mov word ptr es:[4*9], offset new09
mov es:[4*9+2], cs
sti
давно
Старший Модератор
31795
6196
24.06.2010, 11:16
общий
Adsorores:
Цитата: 289969
Обработчики прерываний ставятся только в 21h? Есть ли обработчики до int 21h? С помощью BIOS например.

25,35h это функции DOS'а, которые позволяют получить-установить вектор прерывания. В действительности всё намного проще, они выполняют такие команды, к примеру 25h:
Код:
mov	bx,ax
and bx,0FFh
shl bx,2
xor ax,ax
mov es,ax
cli
mov es:[bx],dx
mov es:[bx+2],ds
sti

Записывает в таблицу векторов прерываний новые значения обработчика прерывания NumInterrupt.
Для того, чтобы установить новый вектор прерывания функции DOS'а, как таковые не нужны, но с ними программа выглядит более читабельной, т.к. увидев какую-то либо функцию уже сразу понятно, что там делается, иначе приходится в множестве команд выискивать алгоритм.
Цитата: 289969
упустил из вида варианты движения

В настоящий момент программа реализовывает движения по направлениям выделеным черным,

Вам нужны ещё другие направления? к примеру те, которые синим?

Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

Неизвестный
24.06.2010, 13:37
общий
_Ayl_:
Разобрался! Установил! Всё довольно просто оказалось. Спасибо!
Неизвестный
24.06.2010, 13:46
общий
Зенченко Константин Николаевич:
"более читабельной"
Да, пусть будет неоптимизированной, но более читабельной и понятней.

"Вам нужны ещё другие направления? к примеру те, которые синим?"
Было бы интересно посмотреть и проверить другие "синие" направления. Возможно, они окажутся предпочтительнее.
давно
Старший Модератор
31795
6196
24.06.2010, 14:19
общий
Adsorores:
Посмотрите установку нового вектора прерывания, без функции DOS
Код:
.model	tiny 
.386
.code
.startup

mov ax,0013h ;320x200x256
int 10h
;читаем старый обработчик
mov ax,3509h
int 21h
mov old09o,bx
mov old09s,es
;ставим новый обработчик
; mov ax,2509h
xor ax,ax
mov es,ax
mov dx,offset new09
push cs
pop ax
cli
mov es:[36],dx
mov es:[38],ax
sti
; int 21h
;настраиваемся на видео буфер
mov ax,0A000h
mov es, ax
cld
keyESC equ 01h
keyUp equ 48h
keyDown equ 50h
keyLeft equ 4Bh
keyRight equ 4Dh
MainLoop:
;считаем адрес
call CalcAdr
;рисуем рисунок
call DrawMovingImage
;задержка
mov cx,5000
MM1: push cx
mov cx,5000
MM2: loop MM2
pop cx
loop MM1
;изменяем координаты
xor dx,dx
xor bx,bx
;изменяем приращение
add bl,DataKey[keyDown]
add dl,DataKey[keyRight]
sub bl,DataKey[keyUp]
sub dl,DataKey[keyLeft]
;контроль
;вертикальная координата
mov ax,bx
cbw
add ax,ImageS
jz IgnoreV
js IgnoreV
push ax
add ax,ImageH
cmp ax,StringHeigth
pop ax
jae IgnoreV
mov imageS,ax
IgnoreV:
;контроль
;горизонтальная координата
mov ax,dx
cbw
add ax,ImageC
jz IgnoreH
js IgnoreH
push ax
add ax,ImageL
cmp ax,StringLength
pop ax
jae IgnoreH
mov imageC,ax
;прячем рисунок
IgnoreH: call ClearImage
;проверяем ESC
cmp byte ptr DataKey[keyESC],1
jnz MainLoop
;востанавливаем старый обработчик
mov ax,2509h
mov dx,old09o
mov ds,old09s
int 21h
;завершение программы
mov ax, 4c00h
int 21h
;старый обработчик
old09o dw ?
old09s dw ?
;наш обработчик
new09: pusha
;читаем порт
in al,60h
;заполняем массив
xor bx,bx
mov bl,al
mov al,1
mov cs:DataKey[bx],al
xor bl,80h
xor al,al
mov cs:DataKey[bx],al
;стандартное завершение прерывания
in al,61h
push ax
or al,80h
out 61h,al
pop ax
out 61h,al
mov al,20h
out 20h,al
popa
iret
;
DrawMovingImage proc
pusha
; Записать адрес маски в индексный регистр
mov SI,offset Flm;указатель на маску объекта
; Вывести изображение
mov cx,ImageH ;высота маски
M0: ;Вывести очередную строку маски
push cx
mov CX,ImageL;ширина маски
push di
rep movsb
pop di
;Перейти на следующую строку
add DI,StringLength
pop cx
loop M0
popa
ret
DrawMovingImage endp
ClearImage proc
pusha
; Вывести изображение
mov cx,ImageH ;высота маски
M3: ;Вывести очередную строку маски
push cx
mov CX,ImageL ;ширина маски
mov al, 0
push di
rep stosb
pop di
; Перейти на следующую строку
add DI,StringLength
pop cx
loop M3
popa
ret
ClearImage endp
CalcAdr proc
; Вычислить адрес начальной точки для вывода маски
; Умножить длину строки на номер строки
mov AX,StringLength
mul ImageS
; Прибавить длину колонки
add AX,ImageC
mov DI,AX
ret
CalcAdr endp
.data
DataKey db 256 dup(0)
; Маска
Flm DB 15,15,15,15 ,15,15,15,15 ,15,15,15,15 ,15,15,15,15
ImageL equ $ - Flm; 16 ;ширина маски
DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15
DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15
DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15

DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15
DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15
DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15
DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15

DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15
DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15
DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15
DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15

DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15
DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15
DB 15,12,12,12 ,12,12,12,12 ,12,12,12,12 ,12,12,12,15
DB 15,15,15,15 ,15,15,15,15 ,15,15,15,15 ,15,15,15,15
ImageH equ ($ - Flm) / ImageL;16 ;высота маски

StringLength dw 320 ;длина строки
StringHeigth dw 200 ;количество строк
; Позиция маски изображения на экране
ImageS dw 90 ;строка
ImageC dw 150 ;колонка
; Размеры маски изображения
END
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

Неизвестный
24.06.2010, 16:20
общий
Спасибо! Буду разбираться!
Форма ответа