Консультация № 179122
16.06.2010, 08:31
0.00 руб.
0 8 1
В приложении программа движущегося вертикально вниз отрезка. Помогите сделать, чтобы так же выводилась и двигалась маска размером, например, 3х5 байт.
Msk db 01,04,01
01,04,01
01,04,01
01,04,01
01,04,01
В режиме 13h я смог сделать движение маски, а в 12h не могу даже вывести на экран маску – у меня просто регистров почему-то не хватает.


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

mov ax, 0012h ;графический режим 12h, vga 640х480х16
int 10h

mov dx, 03CEh ;индексный порт графического контроллера
mov ax, 0F01h ;регистр 01h: разрешение установки/сброса
out dx, ax

mov ax, 0a000h
mov es, ax ; es - сегмент видео
MainLoop:
call Draw,0Ch ;рисуем красным цветом
call DELAY ;задержка
call Draw,0h ;стираем
add row,5 ;шаг движения
call Proverka ;проверка на последнюю строку

mov ah,1
int 16h
jz MainLoop ;проверка на нажатие клавиши
mov ah, 0 ;проверяем на код клавиши
int 16h
cmp ah, 1 ;по Esc выходим
je Exit
jmp MainLoop
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Draw proc color:word
pusha
push row ;если не сохраним, движения не будет
mov cx,0020 ;длина пунктира в строках

L1:
push cx
mov cx, column
; вычислить номер байта в видеопамяти
mov ax, row ;AX = строка
mov bx, 80
mul bx ;AХ = АХ * 80 = строка * байт_в_строке
mov di, ax ;сохранить адрес начала строки в di
mov ax, cx
shr ax, 3 ;AX = номер байта в строке
add di, ax ;DI = номер байта в видеопамяти
and cx, 7 ;вычислить номер бита в байте, остаток от
; деления на 8 – номер бита в байте
mov ch, 80h
shr ch, cl ;нужный бит установлен в 1

mov dx, 03CEh ;индексный порт графического контроллера
mov ax, color
shl ax, 8 ;al = регистр установки/сброса
out dx, ax ;ah = цвет
mov al, 8 ; al = битовая маска
mov ah, ch ;записать в битовую маску нули всюду, кроме
out dx, ax ;бита, соответствующего выводимому пикселю
mov al, byte ptr es:[di] ;заполнить регистры-защелки
mov byte ptr es:[di],ah ; вывод на экран пикселя
pop cx
add row,1 ;для вывода следующей строки
Loop L1
pop row
popa
ret
Draw endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
time equ 30 ;число интервалов по 10мс
DELAY:
pusha ;сохраним все регистры
mov ah,2dh ;сбросим время
xor cx,cx
xor dx,dx
int 21h

dl2:
mov ah,2ch ; читаем время, GET SYSTEM TIME
int 21h ; Return: CH = hour, CL = minute, DH = second, DL = 1/100 seconds
;считаем сотни мс
mov al,100
mul dh ;секунды умножаем на 100 - ax = количество интервалов по 10мс
xor dh,dh ;dx - число сотых
xchg ax,dx ;поменяем местами
mov cl,10
div cl ;ax = количество интервалов по 10мс из сотых
add ax,dx ;складываем с количеством из секунд
cmp ax,time ;сравним с ожидаемым интервалом
jl dl2 ;ждем, если меньше
popa ;восстановим все регистры
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Proverka proc
pusha
cmp row,470 ;доходим почти до края экрана, обнуляем строку. Если сравнить с 479, то
;верхний следующий пунктир выведется не с 320 колонки, а больше.
jge L
jmp LL
L:
mov row,0
mov column,320
LL:
popa
ret
Proverka endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.data
column dw 320
row dw 0

Exit:
mov ax, 0003h ;восстановим текстовый режим
int 10h

mov ax, 4c00h
int 21h
END

Обсуждение

Неизвестный
16.06.2010, 15:54
общий
это ответ
Здравствуйте, Adsorores.

Вот, пожалуйста:
Код:
locals	@@
.model tiny, C

.code
.startup

mov ax, 0012h ;графический режим 12h, vga 640х480х16
int 10h

mov dx, 03CEh ;индексный порт графического контроллера
mov ax, 0F01h ;регистр 01h: разрешение установки/сброса
out dx, ax

mov ax, 0a000h
mov es, ax ; es - сегмент видео
MainLoop:
call Draw,offset msk ;адрес маски
call DELAY ;задержка

mov ah,1 ;проверка на нажатие клавиши
int 16h
jz @@1
cmp al,' ' ; пробел - пауза, можно рассмотреть рисунок
jne @@1
mov ah, 0 ;извлекаем код клавиши из буфера
int 16h
xor ax,ax ; ждем нажатия клавиши
int 16h
cmp ah, 1 ; по Esc выходим
je Exit
@@1:
call Draw,offset black ;стираем
add row,5 ;шаг движения
call Proverka ;проверка на последнюю строку

mov ah,1 ;проверка на нажатие клавиши
int 16h
jz MainLoop
mov ah, 0 ;извлекаем код клавиши из буфера
int 16h
cmp ax,4b00h ; влево
jne @@2
dec [column]
jmp MainLoop
@@2: cmp ax,4d00h ; вправо
jne @@3
inc [column]
jmp MainLoop
@@3: cmp ah, 1 ;по Esc выходим
jne MainLoop
Exit:
mov ax, 0003h ;восстановим текстовый режим
int 10h

mov ax, 4c00h
int 21h

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

.286
Draw proc maska: word
; вычислить номер байта в видеопамяти
mov ax, [row] ;AX = строка
mov bx, 80
mul bx ;AХ = АХ * 80 = строка * байт_в_строке

mov di, ax ;сохранить адрес начала строки в di
mov ax, [column]
mov cl, al
shr ax, 3 ;AX = номер байта в строке
add di, ax ;DI = номер байта в видеопамяти
and cl, 7 ;вычислить номер бита в байте, остаток от
; деления на 8 – номер бита в байте
mov ch, 80h
shr ch, cl ;нужный бит установлен в 1
mov dx, 03CEh ;индексный порт графического контроллера

mov si,[maska]
mov bl,[si] ; число строк
inc si
mov cl,[si] ; число столбцов
inc si
@@next_row:
push di ; сохраняем смещение начала маски в видеобуфере
push cx ; маска пикселя и число столбцов

@@next_pixel:
mov ah, [si] ;цвет
mov al, 0 ;al = регистр установки/сброса
out dx, ax ;ah = цвет

mov al, 8 ; al = битовая маска
mov ah, ch ;записать в битовую маску нули всюду, кроме
out dx, ax ;бита, соответствующего выводимому пикселю

xchg es:[di],ah ;заполнить регистры-защелки и вывести на экран пиксель

inc si ; к следующему байту маски
ror ch,1 ; маска для следующего пикселя
jnc @@2
inc di ; к следующему байту видеобуфера
@@2:
dec cl
jnz @@next_pixel
pop cx ; восстанавливаем маску
pop di
add di,80 ; к следующей строке
dec bl
jnz @@next_row
ret
Draw endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
time equ 30 ;число интервалов по 10мс
DELAY:
; pusha ;сохраним все регистры
mov ah,2dh ;сбросим время
xor cx,cx
xor dx,dx
int 21h

dl2:
mov ah,2ch ; читаем время, GET SYSTEM TIME
int 21h ; Return: CH = hour, CL = minute, DH = second, DL = 1/100 seconds
;считаем сотни мс
mov al,100
mul dh ;секунды умножаем на 100 - ax = количество интервалов по 10мс
xor dh,dh ;dx - число сотых
xchg ax,dx ;поменяем местами
mov cl,10
div cl ;ax = количество интервалов по 10мс из сотых
add ax,dx ;складываем с количеством из секунд
cmp ax,time ;сравним с ожидаемым интервалом
jl dl2 ;ждем, если меньше
; popa ;восстановим все регистры
ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Proverka proc
cmp row,470 ;доходим почти до края экрана, обнуляем строку. Если сравнить с 479, то
;верхний следующий пунктир выведется не с 320 колонки, а больше.
jb @@1
mov row,0
@@1: cmp [column],630
jb @@2
mov [column],1
@@2: cmp [column],0
ja @@3
mov [column],629
@@3: ret
Proverka endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

.data

column dw 320
row dw 0
Msk db 10, 7 ; размер маски в точках: строк, столбцов
db 8,8,8,8,8,8,8
db 8,1,1,14,1,1,8
db 8,1,1,14,1,1,8
db 8,1,1,14,1,1,8
db 8,1,1,14,1,1,8
db 8,1,1,14,1,1,8
db 8,14,14,14,14,14,8
db 8,1,14,14,14,1,8
db 8,1,1,14,1,1,8
db 8,8,8,8,8,8,8

black db 10, 7 ; размер маски в точках: строк, столбцов
db 0,0,0,0,0,0,0
db 0,0,0,0,0,0,0
db 0,0,0,0,0,0,0
db 0,0,0,0,0,0,0
db 0,0,0,0,0,0,0
db 0,0,0,0,0,0,0
db 0,0,0,0,0,0,0
db 0,0,0,0,0,0,0
db 0,0,0,0,0,0,0
db 0,0,0,0,0,0,0
END

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

Ваша программа построена таким образом, что сохранение/восстановление регистров в процедурах не требуется, поэтому я удалил или закомментировал pusha/popa.

Успехов!
5
Неизвестный
17.06.2010, 09:11
общий
Спасибо! Интересно заполнение регистров-защелок и вывода на экран пикселя одной командой xchg es:[di],ah, а не двумя.
Неизвестный
17.06.2010, 13:05
общий
Все-таки я довольно редко программирую на ассемблере и местами пишу корявый код.
В процедуре Draw вместо
Код:

ror ch,1 ; маска для следующего пикселя
jnc @@2
inc di ; к следующему байту видеобуфера
@@2:

лучше написать:
Код:

ror ch,1 ; маска для следующего пикселя
adc di,0 ; если был перенос, то к следующему байту видеобуфера

Красивее и быстрее выполняется.
давно
Посетитель
7438
7205
17.06.2010, 13:28
общий
amnick:
Однозначно
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
Неизвестный
17.06.2010, 19:55
общий
А можно ли переделать процедуру Draw для вывода не одной, а нескольких движущихся вертикально одинаковых масок? Я пытался с помощью
call Draw,0,320,offset msk
call Draw,100,320,offset msk
……
Draw proc row:word, column:word, maska:word,

т.е. подставить координаты в месте, нужном для вывода маски. Но не получается вывод даже с одной командой call Draw,0,320,offset msk, т.к. при ассемблирование возникает ошибка на Relative jump out of range by … bytes.
Неизвестный
17.06.2010, 22:47
общий
Вот, пожалуйста:
Код:
; вывод нескольких масок (прямоугольных шаблонов) в видеорежиме 12h

locals @@
.model tiny, pascal

N_MASK = 7 ; количество выводимых шаблонов

T_MASK STRUC ; положение маски и шаг смещения
x dw ?
y dw ?
dy dw ?
T_MASK ENDS

.code
.startup

mov ax, 0012h ;графический режим 12h, vga 640х480х16
int 10h

mov dx, 03CEh ;индексный порт графического контроллера
mov ax, 0F01h ;регистр 01h: разрешение установки/сброса
out dx, ax

mov ax, 0a000h
mov es, ax ; es - сегмент видео

MainLoop:
mov ax,offset msk
call drawAll ; рисуем все маски
call DELAY ; задержка

mov ah,1 ; проверка на нажатие клавиши
int 16h
jz @@1
cmp al,' ' ; пробел - пауза, можно рассмотреть рисунок
jne @@1
mov ah, 0 ; извлекаем код клавиши из буфера
int 16h
xor ax,ax ; ждем нажатия клавиши
int 16h
cmp ah, 1 ; по Esc выходим
je Exit
@@1:

mov ax,offset black
call drawAll ; стираем маски

call drop ; сдвигаем маски вниз
call Proverka ; проверка на последнюю строку/столбец

mov ah,1 ;проверка на нажатие клавиши
int 16h
jz MainLoop
mov ah, 0 ;извлекаем код клавиши из буфера
int 16h
cmp ax,4b00h ; влево
jne @@2
mov ax,-1
call shift
jmp MainLoop
@@2: cmp ax,4d00h ; вправо
jne @@3
mov ax,1
call shift
jmp MainLoop
@@3: cmp ah, 1 ;по Esc выходим
jne MainLoop
Exit:
mov ax, 0003h ;восстановим текстовый режим
int 10h

mov ax, 4c00h
int 21h

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; рисуем все маски
; AX = адрес маски
drawAll proc
mov cx,N_MASK
mov si,offset pos
@@draw:
push ax cx si
call Draw, [si].x, [si].y, ax ; x, y, адрес маски
pop si cx ax
add si,SIZE T_MASK
loop @@draw
ret
drawAll endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; сдвиг масок влево-вправо
shift proc
mov cx,N_MASK
mov si,offset pos
@@next:
add [si].x,ax
add si,SIZE T_MASK ; к следующей маске
loop @@next
ret
shift endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; сдвигаем маски вниз
drop proc
mov cx,N_MASK
mov si,offset pos
@@next:
mov ax,[si].dy ; шаг движения
add [si].y,ax ; новая позиция по вертикали
add si,SIZE T_MASK ; к следующей маске
loop @@next
ret
drop endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

.286
Draw proc _x: word, _y: word, maska: word
; вычислить номер байта в видеопамяти
mov ax, [_y] ;AX = строка
mov bx, 80
mul bx ;AХ = АХ * 80 = строка * байт_в_строке

mov di, ax ;сохранить адрес начала строки в di
mov ax, [_x]
mov cl, al
shr ax, 3 ;AX = номер байта в строке
add di, ax ;DI = номер байта в видеопамяти
and cl, 7 ;вычислить номер бита в байте, остаток от
; деления на 8 – номер бита в байте
mov ch, 80h
shr ch, cl ;нужный бит установлен в 1
mov dx, 03CEh ;индексный порт графического контроллера

mov si,[maska]
mov bl,[si] ; число строк
inc si
mov cl,[si] ; число столбцов
inc si
@@next_row:
push di ; сохраняем смещение начала маски в видеобуфере
push cx ; маска пикселя и число столбцов

@@next_pixel:
mov ah, [si] ;цвет
mov al, 0 ;al = регистр установки/сброса
out dx, ax ;ah = цвет

mov al, 8 ; al = битовая маска
mov ah, ch ;записать в битовую маску нули всюду, кроме
out dx, ax ;бита, соответствующего выводимому пикселю

xchg es:[di],ah ;заполнить регистры-защелки и вывести на экран пиксель

inc si ; к следующему байту маски
ror ch,1 ; маска для следующего пикселя
adc di,0 ; если был перенос, то к следующему байту видеобуфера
dec cl
jnz @@next_pixel
pop cx ; восстанавливаем маску
pop di
add di,80 ; к следующей строке
dec bl
jnz @@next_row
ret
Draw endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
time equ 30 ;число интервалов по 10мс
DELAY:
; pusha ;сохраним все регистры
mov ah,2dh ;сбросим время
xor cx,cx
xor dx,dx
int 21h

dl2:
mov ah,2ch ; читаем время, GET SYSTEM TIME
int 21h ; Return: CH = hour, CL = minute, DH = second, DL = 1/100 seconds
;считаем сотни мс
mov al,100
mul dh ;секунды умножаем на 100 - ax = количество интервалов по 10мс
xor dh,dh ;dx - число сотых
xchg ax,dx ;поменяем местами
mov cl,10
div cl ;ax = количество интервалов по 10мс из сотых
add ax,dx ;складываем с количеством из секунд
cmp ax,time ;сравним с ожидаемым интервалом
jl dl2 ;ждем, если меньше
; popa ;восстановим все регистры
ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Proverka proc
mov cx,N_MASK
mov si,offset pos
xor ax,ax
@@next:

cmp [si].y,470 ; доходим почти до края экрана, обнуляем строку. Если сравнить с 479, то
; верхний следующий пунктир выведется не с 320 колонки, а больше.
jb @@1
mov [si].y,ax
@@1: cmp [si].x,630
jb @@2
mov [si].x,1
@@2: cmp [si],ax
ja @@3
mov [si].x,629
@@3:
add si,SIZE T_MASK ; к следующей маске
loop @@next
ret
Proverka endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

.data

pos T_MASK < 20, 110, 5>
T_MASK <100, 5, 3>
T_MASK <155, 300, 8>
T_MASK <270, 127, 4>
T_MASK <333, 72, 2>
T_MASK <450, 215, 7>
T_MASK <517, 20, 6>

Msk db 10, 7 ; размер маски в точках: строк, столбцов
db 8,8,8,8,8,8,8
db 8,1,1,14,1,1,8
db 8,1,1,14,1,1,8
db 8,1,1,14,1,1,8
db 8,1,1,14,1,1,8
db 8,1,1,14,1,1,8
db 8,14,14,14,14,14,8
db 8,1,14,14,14,1,8
db 8,1,1,14,1,1,8
db 8,8,8,8,8,8,8

black db 10, 7 ; размер маски в точках: строк, столбцов
db 0,0,0,0,0,0,0
db 0,0,0,0,0,0,0
db 0,0,0,0,0,0,0
db 0,0,0,0,0,0,0
db 0,0,0,0,0,0,0
db 0,0,0,0,0,0,0
db 0,0,0,0,0,0,0
db 0,0,0,0,0,0,0
db 0,0,0,0,0,0,0
db 0,0,0,0,0,0,0
END


Цитата: 289969
возникает ошибка на Relative jump out of range by … bytes

Это совсем не страшно и обходится очень просто, например, если
Код:
	jc lbl

дает такую ошибку, то заменяем на
Код:

jnc @@1 ; обратное условие
jmp near lbl
@@1:

Неизвестный
18.06.2010, 14:54
общий
amnick:
Большое спасибо!!! Но не очень понятно о jc lbl, в программе этой команды и метки нет. Единственная команда на состояние флага переноса была jnc @@2, которую потом заменили.
Но у Вас в любом случае лучше получилось, даже чем я хотел.
Неизвестный
18.06.2010, 15:59
общий
Adsorores:
Цитата: 289969
не очень понятно о jc lbl, в программе этой команды и метки нет

Это всего лишь пример, как решается проблема с ошибкой "Relative jump out of range by … bytes". В 16-битной среде дальность условных переходов - от -128 байт (назад) до +127 байт (вперед). Это, так называемые, короткие (short) переходы. Если целевая метка находится вне этого диапазона (слишком далеко), то возникает ошибка "Relative jump out of range by … bytes". Исправляется так, как я написал выше.
Форма ответа