Aspect #04
18 января 1998

Программирование - Вывод спрайта на экран. Вывод на виртуальный экран.

╔──────────────═══════════───────────────╗
│       Основы программирования          │
╚──────────────═══════════───────────────╝

 Мы  начинаем  серию публикаций об основах
программирования. Сегодня первая статья.
         ───────────────────────

   БЫСТРАЯ,И НЕ ОЧЕНЬ БЫСТРАЯ ГРАФИКА.

В  жизни  каждого  программиста  наступает
момент, когда он, вроде бы зная ассемблер,
пытается     написать     что-нибудь     с
двигающимися спрайтами,при помощи процедур
типа такой:

        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)







Темы: Игры, Программное обеспечение, Пресса, Аппаратное обеспечение, Сеть, Демосцена, Люди, Программирование

Похожие статьи:
Введение - Прoшлo нe тaк уж мнoгo врeмeни co дня oбъявлeния oб oбъeдинeнии 2x элeктрoнныx издaний, и мы прeдcтaвляeм Вaшeму внимaнию пeрвый нoмeр нoвoй гaзeты пoд нaзвaниeм Sinc Re-Stared...
Birthday - поздравления спектрумистов с днями рождения.
Реклама - Реклама и объявления ...

В этот день...   16 ноября