(C) Bob Skath
Б Ы C Т Р А Я Г Р А Ф И К А
══════════════════════════════════════
У любого программиста бывают такие си-
туации, когда он уже вроде бы и знает ас-
семблер, и все равно не может написать ни-
чего путного и только с завистью смотрит
на шесть ПЛАВНО бегущих по бордюру и тро-
туару скроллеров у какого нибудь новояв-
ленного MAX IWAMOTO и сравнивает со своей
убогой бегушкой размером 3*3 пиксела, ко-
торая судорожно передергивается при каж-
дом в муках появляющимся новым символом. А
все дело в том, что программирование тако-
го рода эффектов (как и почти всех динами-
ческих игрушек) построено на принципе так
называемой "быстрой" графики.
Вот как, например, выглядит программа
программиста (боже ты мой, что я пишу), ко-
торый только-только освоил работу со
спрайтами, и голова которого занята струк-
турным программированием :
DI ; Пока все понятно
LD HL,#4000 ; Это тоже ясно
LD DE,SPRITE ; DE - адрес спрайта в памяти
LD BC,#0504 ; В - размер Х, С - размер Y
LOOP1 PUSH BC ; сохраняем BC
PUSH HL ; сохраняем текущий адрес в экране
LOOP2 LD A,(DE) ; берем байт спрайта
LD (HL),A ; помещаем его на экран
INC HL ; увеличиваем адрес на экране
INC DE ; увеличиваем адрес на спрайт
DJNZ LOOP2 ; пока Х<>0
POP HL ; восстанавливаем адрес в экране
POP BC ; восстанавливаем счетчик размера
CALL NXTLIN ; определяем следующую линию в экране
DEC C ; размер Y закончен ?
JR NZ,LOOP1 ; если нет то повторить цикл.
....
NXTLIN INC H ; подпрограмма определения последу-
LD A,H ; ющей линии в видеопамяти. На входе
AND 7 ; в HL - адрес в экране. На выходе
RET NZ ; нужный адрес опять в HL.
LD A,L
ADD A,#20
LD L,A
RET C
LD A,H
SUB 8
LD H,A
RET
Ну что ж... Вполне приличная программа
для вывода на экран статических картинок,
но вся беда начинается, когда этот самый
программист, окрыленный предыдущим успе-
хом, пытается переделать сию программку
для вывода ДВИЖУЩИХСЯ спрайтов да еще и
большего размера, оперируя регистровой па-
рой HL на входе, что бы выводить изображе-
ния в различные части экрана. Тут то и на-
чинается самое интересное...
Изображение начинает передергиваться,
мерцать и делать все, что бы заставить го-
ре-программиста кусать локти от ярости.
Так вот я и решил немного помочь сим
людям, дабы облегчить их страдания своим
циклом статей на эту тему. Весь принцип
быстрой графики состоит в том, что изобра-
жение строится не непосредственно на экра-
не компьютера, а в так называемом вир-
туальном экране, то есть где-то в глубинах
памяти машины. Картинка сначала строится
вся - спрайт за спрайтом накладываются в
нужной последовательности подпрограммой,
похожей на вышеприведенную, с тем отли-
чием, что в виртуальном буфере строки
растра идут последовательно одна за дру-
гой, и лишь затем как один гигантских раз-
меров спрайт выводится на экран.
Тут, как я вижу, половина читателей
этой статьи с криком : "Wooooow, я поооо-
няяял !!!!" лихо сбрасывают машину и пос-
пешно загружают TASM и SPRITE EDITOR.
Ну ничего, через пару минут они убедят-
ся, что все не так просто и понятно, как
кажется на первый раз. Все дело в том что
картинку нужно успеть перестроить не бо-
лее чем за один промежуток между прерыва-
ниями, иначе будет борода, и вся работа
пойдет насмарку. А это не так то просто,
что доказывает простой расчет времени вы-
полнения вышеприведенной программы. Навер-
ное, кое кто из читателей уже повесили но-
сы и подумали о бренности всего сущего, но
ничего - вспомните просто ROBOCOP'a или
там WEC LE MAN. Там ведь ничего не дер-
гается ??? Значит можно ??? Конечно можно
!!! Только нужно учесть несколько момен-
тов :
1. Обычно не требуется перестраивать весь
экран, хватает перерисовки какой то его
части а остальное - статичная информация (
как количество жизней, название MEGADEMO и
т.д. )
2. Нужно для перестроения отбросить вся-
кие глупые мысли о структурном подходе и
вложенных циклах ( это вам не сортировка
базы данных по 172627373 полям )
3. Необходимо немного подумать. И все !!!
В качестве примера я приведу процедуру,
которая строит картинку размером 26 знако-
мест *128 пикселов в верхней части экрана.
DI ; Прерывания обязательно DI !!!
LD C,#48 ; Маркер перехода в другую треть
экрана
LD HL,SCR+28+1 ; Адрес вывода в экран с учетом
вывода справа-налево
LD (ADDR1+1),HL ; Установка экранного адреса
LD IX,PICT+#11 ; Адрес виртуального экрана в
памяти с учетом чтения из се-
редины
CALL ROUT1 ; Рисование первой трети экрана
LD C,#50 ; Маркер перехода в третью треть
экрана
LD HL,SCR1+28+1 ; Адрес вывода в экран во второй
трети
LD (ADDR1+1),HL ; Установка экранного адреса
ROUT1 LD B,64 ; Количество линий рисуемых за
один проход процедуры
LOOP2 LD (SPSAVE),SP ; Сохраняем SP для корректного
выхода
LD SP,IX ; Текущий адрес для чтения
POP AF ; Чи
POP DE ; та
POP HL ; ем
EXX ;
POP BC ; кар
POP DE ; тин
POP HL ; ку
ADDR1 LD SP,0000 ; Здесь был установлен адрес для
записи картинки в экран
PUSH HL ; Запи
PUSH DE ; сы
PUSH BC ; ваем
EXX ;
PUSH HL ; кар
PUSH DE ; тин
PUSH AF ; ку
LD (ADDR2+1),SP ; Сохраняем текущий адрес записи
LD SP,#FFF2 ; Устанавливаем регистровую
ADD IX,SP ; пару IX на следующие байты
LD SP,IX ; виртуального экрана
POP AF ; Чи
POP DE ; та
POP HL ; ем
EXX ;
EX AF,AF' ;
POP AF ; кар
POP BC ; тин
POP DE ; ку
POP HL ;
ADDR2 LD SP,0000 ; Здесь был установлен адрес для
записи в экран
PUSH HL ; За
PUSH DE ; пи
PUSH BC ; сы
PUSH AF ; ва
EXX ; ем
EX AF,AF' ;
PUSH HL ; кар
PUSH DE ; тин
PUSH AF ; ку
LD HL,#011A ; Вычисляем адрес последующей
ADD HL,SP ; линии в этой же трети экрана
LD A,H ; Проверяем не вышли ли
CP C ; мы в другую треть экрана
JR C,LOOP1 ; или в другое знакоместо
LD DE,#F820 ; Если да, то производим
ADD HL,DE ; коррекцию
LOOP1 LD (ADDR1+1),HL ; Сохраняем текущий адрес для
записи в экран
LD SP,46 ; Следующая линия виртуального
ADD IX,SP ; буфера
LD SP,IX ;
DJNZ LOOP2 ; Если цикл не исчерпан-повто-
рить
SAVESP LD SP,000 ; Восстановить стек
RET ; Возврат
Ну как ??? На первый взгляд громоздко,
но работает прекрасно. Да, кстати, ясно
почему такая скорость работы ?? Правильно,
потому что за один раз читается и пишется
по два байта.
Следует обpатить внимание на то, что
для ускоpения pаботы здесь отсутствует
подпpогpамма опpеделения последующей стpо-
ки, а вывод в каждую тpеть пpоисходит от-
дельно.
Таким образом можно выводить на экран
буфера любого размера (но только кратного
по Х двум, интересно почему ??) с до-
вольно приличной скоростью для анимации
или оживления ваших игрушек.
Ну ладно... На сегодня хватит... В сле-
дующий раз я расскажу о том как быстро
впечатывать спрайты в виртуальный буфер с
маской и без. И еще... Но об этом в сле-
дующий раз.
Bob Skath. 1996. Latvia. Daugavpils.
─════─