Программирование - Вывод спрайта на экран. Вывод на виртуальный экран.
╔──────────────═══════════───────────────╗
│ Основы программирования │
╚──────────────═══════════───────────────╝
Мы начинаем серию публикаций об основах
программирования. Сегодня первая статья.
───────────────────────
БЫСТРАЯ,И НЕ ОЧЕНЬ БЫСТРАЯ ГРАФИКА.
В жизни каждого программиста наступает
момент, когда он, вроде бы зная ассемблер,
пытается написать что-нибудь с
двигающимися спрайтами,при помощи процедур
типа такой:
LD HL,SPRDTA ;Адрес спрайта в HL
LD DE,#808 ;Координаты вывода в D - по Y в E - по X
LD BC,#809 ;Высота спрайта в B длина спрайта в C.
;высота,длина и координаты - текстовые
JP SPROUT ; Выводим спрайт на экран
SPROUT LD (SPROUT1+1),HL;сохраняем нужные данные
LD A,B
LD (SPROUT2+1),A
LD A,D ;заполняем таблицу адресов начиная
ADD A,B ;с низа экрана
DEC A
LD D,A
SPROUT3 LD A,D ;здесь вычисляется адрес
LD H,A ;строки ,номер которой задан
RRCA ;в регистре A
RRCA ;3 команды RRCA
RRCA
AND #E0
ADD A,E ;добавляем смещение по X
LD L,A
LD A,H
AND #18
OR #40
ADD A,7 ;добавляем смещение по Y
LD H,A ;на 7 (т.к заполняем снизу)
PUSH HL ;забиваем стек адресами
DEC H
PUSH HL
DEC H
PUSH HL
DEC H
PUSH HL
DEC H
PUSH HL
DEC H
PUSH HL
DEC H
PUSH HL
DEC H
PUSH HL
DEC H
DEC D ;уменьшаем координату
DJNZ SPROUT3
SPROUT1 LD HL,0000 ;здесь будет адрес спрайта
LD A,C
EX AF,AF'
SPROUT2 LD A,0 ;здесь будет высота спрайта
SLA A ;умножаем на число байт в символе
SLA A
SLA A
LD B,0
SPROUT4 POP DE ;снимаем адрес вывода со стека
EX AF,AF'
LD C,A
LDIR ;выводим спрайт
EX AF,AF'
DEC A
JP NZ,SPROUT4
RET
Нет, я не хочу сказать, что процедуры типа
этой не нужны: они очень даже нужны при
выводе статических изображений. Но чтобы
на экране было много движущихся спрайтов ,
а изображение не дергалось ,используются
такие хитрые приемы, как вывод на
виртуальный (невидимый глазу) экран, или
процедуры быстрой графики (для вывода
движущихся изображений, под которые
виртуального экрана будет много)...
Ну что же,рассмотрим для начала, первый
метод , так как он включает в себя второй.
Суть вывода изображения при помощи
виртуального экрана заключается в том, что
во-первых, выделяем область памяти под
экран , размер которого равен изменяючейся
части спековского экрана( только все
строчки там идут последовательно - т. е.
нулевая строчка с адреса SCRADR, первая с
адреса SCRADR+длина экрана по горизонтали
и т. д. и т. п.),в играх - это игровое
поле, туда,при помощи процедур наподобие
предыдущей,кладем фон, затем предметы, и
,наконец,персонажей, теневой экран очень
быстро выводится на обычный (при помощи
специальных процедур) , обычно вывод
осуществляется в процедуре обработки
прерывания. Теперь подробнее о процедурах
вывода виртуального экрана: сию пакость
можно делать аж тремя способами -
во-первых, при помощи команд LDI, во-
вторых, при помощи команд POP и PUSH
и , в третьих, при помощи команд POP HL и
LD (ADR), HL. А теперича все по-порядку...
Вывод первым способом осуществляется при
помощи процедур типа:
;ВХОД:
;HL - АДРЕС ТАБЛИЦЫ ЗАПОЛНЯЕМОЙ ZAPTAB
;DE - АДРЕС ВИРТУАЛЬНОГО ЭКРАНА
;B - РАЗМЕР ЭКРАНА ПО ВЕРТИКАЛИ
;В ТЕКСТОВЫХ КООРДИНАТАХ
;ВЫХОД : НЕТ
OUTSCR LD (OUTSP+1),SP;сохраняем SP
LD A,B ;умножаем B на 8
SLA A
SLA A
SLA A
DI ;запретим прерывания т. к. работаем со
LD SP,HL ;стеком таблицу в SP
EX DE,HL
ОUTSCR1 POP DE ;берем адрес вывода и выводим
DUP 20 ;РАЗМЕР ЭКРАНА ПО ГОРИЗОНТАЛИ
LDI ;В ТЕКСТОВЫХ КООРДИНАТАХ
EDUP ;DUP 20 - LDI повторяем 20 раз
;Команды DUP n и EDUP, в XAS'е соответст-
;вуют командам ASSM! n и CONT!
DEC A ;а EDUP - конец фрагмента который
JP NZ,OUTSCR1;повторяем
OUTSP LD SP,0000
EI ;разрешаем прерывания
RET
Для работы этой процедуры необходимо
заполнить таблицу, содержащую адреса
вывода на реальный экран (чтобы не
вычислять их). Это можно сделать следующей
процедурой:
;НА ВХОД :
;HL - АДРЕС ТАБЛИЦЫ
;D - ВЕРХНЯЯ СТРОКА
;E - ЛЕВЫЙ СТОЛБЕЦ
;B - ВЫСОТА ТЕНЕВОГО ЭКРАНА
;ВСЕ КООРДИНАТЫ - ТЕКСТОВЫЕ
;НА ВЫХОДЕ :НЕТ
ZAPTAB LD A,D
LD C,A
AND A
DUP 3
RRCA
EDUP
AND #E0
ADD A,E
EX AF,AF'
LD A,C
AND #18
OR #40
EX AF,AF'
LD C,A
EX AF,AF'
DUP 8
LD (HL),C
INC HL
LD (HL),A
INC A
INC HL
EDUP
INC D
DJNZ ZAPTAB
EI
RET
Да, еще: эти процедуры расчитаны на вывод
виртуального экрана, размером 20x20. Для
того, чтобы изменить размеры выводимого
изображения, нужно изменить процедуру
вывода: заместо DUP 20 (повторять 20 раз )
поставить DUP NN, где NN размер экрана по
горизонтали, и входные значения регистров.
Пример заполнения таблицы:
LD HL,TABLE ;HL - адрес таблицы
LD DE, #101 ;D - координата по Y, E - координата по X
LD B,20 ;размер экрана по вертикали
CALL ZAPTAB ;заполняем таблицу
Пример вызова процедуры вывода:
LD DE,SCRADR ;DE - адрес виртуального экрана
LD HL,TABLE ;HL - адрес таблицы
LD B,20 ;B - размер экрана по вертикали
CALL OUTSCR ;вызываем процедуру вывода
Будем выводить тот же экран при помощи
команд PUSH и POP. Для этого потребуется
процедура типа такой :
;ВХОД HL - АДРЕС НА ВИРТУАЛЬНОМ ЭКРАНЕ
; DE - АДРЕС НА РЕАЛЬНОМ ЭКРАНЕ
; A - СТАРШИЙ БАЙТ СЛЕДУЩЕЙ ТРЕТИ
; РЕАЛЬНОГО ЭКРАНА
; B - ВЫСОТА ВЫВОДИМОГО ИЗОБРАЖЕНИЯ
; (В ПИКСЕЛЯХ)
OUTSCR PUSH IY
LD (OUTSP+1),SP;сохраняем SP
DI ;обязательно запрещаем прерывания не
;есть вероятность летального исхода
LD (SEGSTOP+1),A
LD A,B
LD (NUMOF+1),A
LD (OUTSCR1+1),HL
LD HL,#114 ;смещение вниз на 1 и вправо
;на 20 т. к. запись ведется командой
;PUSH ,а смещение вниз на 1 есть переход
;к следующей строчке
LD (OUTSCR3+1),HL
LD H,0 ;начальное положение - вниз 0,
;вправо - 20 -длина виртуального экрана
ADD HL,DE
LD (OUTSCR2+1),HL
OUTSCR1 LD SP,0000 ;здесь будет адрес в виртуальном экране
POP HL ;берем данные
POP BC
POP DE
POP AF
EX AF,AF'
EXX
POP HL
POP BC
POP DE
POP AF
POP IX
POP IY
LD (OUTSCR1+1),SP;запоминаем где остановилися
OUTSCR2 LD SP,0000 ;здесь будет адрес на реальном экране
PUSH IY ;кладем данные
PUSH IX
PUSH AF
PUSH DE
PUSH BC
PUSH HL
EXX
EX AF,AF'
PUSH AF
PUSH DE
PUSH BC
PUSH HL
OUTSCR3 LD HL,0000 ;смещаем вниз и вправо
ADD HL,SP
LD A,H ;8 строчек уже положили
SEGSTOP CP 00 ;здесь старший байт следующей трети
;реального экрана
JR C,OUTSCR4 ;если нет тогда отдыхаем
LD DE,#F820 ;фактически #F820+HL=HL-#800+#20 перейдем
;к следующей символьной строке
ADD HL,DE
OUTSCR4 LD (OUTSCR2+1),HL;положим адрес туда откуда взяли
NUMOF LD A,0 ;здесь высота спрайта в пикселях
DEC A
LD (NUMOF+1),A
JP NZ,OUTSCR1;если не вывели то выводим
OUTSP LD SP,0000 ;восстанавливаем указатель на стек и
POP IY ;прощаемся
EI
RET
Процедура не является совершенством, и
писалась только для того, чтобы примерно
показать сей прикол. А именно быстрота
вывода дистигается здесь за счет вывода
сразу двух байт (команды POP и PUSH).
А вот и пример ее вызова (выводится
многострадальный виртуальный экран 20x20)
в верхний левый угол:
LD HL,SCRADR
LD DE,#4000
LD A,#48
LD B,64 ;8*8=64
CALL OUTSCR
LD HL,SCRADR+#500 ;#500=64*20=1280
LD DE,#4800
LD A,#50
LD B,64
CALL OUTSCR
LD HL,SCRADR+#A00 ;#A00=#500+#500
LD DE,#5000
LD A,#58
LD B,32 ;8*4=32
CALL OUTSCR
;или в позицию с координатой X=1,Y=2 (в текстовых координатах)
LD HL,SCRADR
LD DE,#4041
LD A,#48
LD B,48 ;6*8=48
CALL OUTSCR
LD HL,SCRADR+#500 ;#500=64*20=1280
LD DE,#4801
LD A,#50
LD B,64
CALL OUTSCR
LD HL,SCRADR+#A00 ;#A00=#500+#500
LD DE,#5001
LD A,#58
LD B,48 ;8*6=48
CALL OUTSCR
Однако при размере виртуалного экрана
32xNN в текстовых координатах, быстрее
будет работать процедура, основанная на
командах POP HL, LD (ADDR), HL. Однако она
имеет немеряные (большие) размеры, и
набивать ее в ассемблере было бы
утомительно, поэтому для ее изготовления
используется следующая процедура :
;ВХОД :
; HL - АДРЕС ПРОЦЕДУРЫ ВЫВОДА
; D - ВЕРХНЯЯ ПОЗИЦИЯ ПО Y
; E - ЛЕВАЯ ПОЗИЦИЯ ПО X
; B - ВЫСОТА ВИРТУАЛЬНОГО ЭКРАНА
; (В ТЕКСТОВЫХ КООРДИНАТАХ)
; C - ДЛИНА ВИРТУАЛЬНОГО ЭКРАНА/2
; (ТОЖЕ В ТЕКСТВЫХ КООРДИНАТАХ)
;ВЫХОД : НЕТ
MAKOUT LD (HL),#21 ;LD HL,0000 Сохраняет указатель стека
XOR A
DUP 2
INC HL
LD (HL),A
EDUP
INC HL
LD (HL),#39 ;ADD HL,SP
INC HL
LD (HL),#EB ;EX DE,HL
INC HL
LD (HL),#F9 ;LD SP,HL
INC HL
MAKOUT1 PUSH DE
LD A,D ;вычисляем адрес на реальном экране
DUP 3
RRCA
EDUP
AND #E0
ADD A,E
LD E,A
LD A,D
AND #18
OR #40
LD D,A ;в DE - адрес
LD A,8 ;8 байт в символе
MAKOUT2 PUSH BC ;сохраняемся для последующих циклов
PUSH DE
MAKOUT3 LD (HL),#E1 ;POP HL
INC HL
LD (HL),#22 ;LD (ADDR),HL
INC HL
LD (HL),E ;АДРЕС
INC HL
LD (HL),D
INC HL
INC E
DEC C
JR NZ,MAKOUT3;заполняем строчку
POP DE
POP BC
INC D
DEC A
JR NZ,MAKOUT2;заполняем восем строк,
;которые в одном символе
POP DE
INC D
DJNZ MAKOUT1 ;заполняем D символов
LD (HL),#EB ;EX DE,HL
INC HL
LD (HL),#F9 ;LD SP,HL
INC HL
LD (HL),#C9 ;RET
RET
Процедура сия делает по адресу HL
процедуру быстрого вывода. Вот пример
построения таблицы,для вывода виртуального
экрана размером 20x20 на реальный экран с
позиции X=2, Y=1:
LD HL,SPEED ;Адрес процедуры
LD DE,#102 ;D=Y,E=X
LD BC,#1414 ;B=20,C=20
CALL MAKOUT
Теперича, приведу пример обращения к этой
процедуре :
DI ;Обязательно прерывания запрещаем
LD DE,SPRADR ;в DE адрес виртуального экрана
CALL SPEED ;выводим сие дело
EI ;Все свободны, и прерывания тоже
Собственно,из приведенных здесь процедур,
эта наилучшим образом подходит для вывода
больших изображений (имеет наибольший
кпд), однако она имеет существенный
недостаток - на каждый выводимый байт
в процедуре вывода приходится два !
Вот,собственно,и все ,что я хотел сказать.
В следующий раз я расскажу как извращаясь
с последней процедурой можно получать
прикольные эффекты.
P.S. Все ассемблерные листинги даны в
формате ассемблера ALASM, и единственной
возможной несовместимостью с другими
ассемблерами является команда DUP nn,
означающая участок , ограниченный DUP и
EDUP надо повторить nn раз...
Все эти процедуры вы найдете в приложении
к газете, они находятся в текстовом форма-
те и их легко можно переконвертировать в
любой из ассмов.
До встречи !
(с)Ilya Trusov(500:812/08.19)
Другие статьи номера:
|
|
|
|
|
|
|
|
Программирование - Вывод спрайта на экран. Вывод на виртуальный экран.
|
|
|
|
|
|
|