(C) ROM corp./VVG,1997
-----------------------
Быстрая графика.
Давно хотел рассказать о
графике SPECCY, причем не про-
сто о графике, а о быстрой
графике, которая применяется в
большинстве музыкальных DEMO,
а также во многих фирменных
игрушках. В этой статье я хочу
предложить несколько иной под-
ход к решению этих вопросов.
1:Применение метода табличного
программирования для печати
спрайтов.
2:Применение печати стеком для
очистки экрана. Под очисткой
здесь понимаются любые дейст-
вия, ведущие к освежению
экранной памяти, в том числе и
заполнение экрана спрайтами
любого формата, создание все-
возможных сеточек и т.д.
Итак, теперь подробнее обо
всем этом. Я не буду приводить
здесь все листинги о которых
будет идти речь, поскольку все
это довольно прозрачно, но
ключевые моменты будут подроб-
но освещены.
1.ПЕЧАТЬ СПРАЙТОВ
Все попытки создать быст-
рую процедуру печати спрайта
классическим методом, обречены
на неудачу из за чудовищно
медленной работы вычислителя
адреса в экране. Что же де-
лать? На помощь приходит таб-
личный метод программирования.
Пусть где-то в памяти находит-
ся таблица адресов всех линий
экрана, тогда процедура печати
спрайта выглядит так:
PRINT_SPRITE
LD H,0 ;В HL ПОМЕЩАЕМ Y КООРДИНАТУ
LD L,YCOORD ;НАШЕГО СПРАЙТА
;(В ПИКСЕЛЯХ)
ADD HL,HL ;УМНОЖАЕМ ЕЕ НА 2
;ТАК КАК АДРЕС ЗАНИМАЕТ
;2 БАЙТА, ТО НАДО УМНОЖИТЬ
;НА 2 ДЛЯ ПОЛУЧЕНИЯ
;СМЕЩЕНИЯ ОТ ВЕРХА
;ТАБЛИЦЫ
LD DE,DATA ;В DE-НАЧАЛО ТАБЛИЦЫ
;АДРЕСОВ
ADD HL,DE ;СЛОЖЕНИЕ
LD (STACK),SP ;СОХРАНИМ СТЕК
;В ПЕРЕМЕННОЙ
LD SP,HL ;ПЕРЕСТАВИМ СТЕК НА
;ТАБЛИЦУ АДРЕСОВ
LD A,LINES ;В A-ВЫСОТА СПРАЙТА
;В ПИКСЕЛЯХ
;ПОДГОТОВИТЕЛЬНЫЕ ОПЕРАЦИИ ЗАКОНЧЕНЫ,
;ТЕПЕРЬ НАЧИНАЕТСЯ СОБСТВЕННО ПЕЧАТЬ
LOOP LD BC,XCOORD ;В BC-КООРДИНАТА X
;СПРАЙТА ОТ ЛЕВОГО КРАЯ
;ЭКРАНА В ЗНАКОМЕСТАХ
POP DE ;ВОТ ЭТОЙ КОМАНДОЙ И
;ВЫБИРАЮТСЯ АДРЕСА ИЗ
;ТАБЛИЦЫ
EX DE,HL ;СЛОЖЕНИЕ С X
ADD HL,BC ;КООРДИНАТОЙ
EX DE,HL ;
LDI ;ПЕРЕБРОС ЛИНИИ СПРАЙТА
LDI ;В ЭКРАН КОМАНДАМИ
;LDI БЫСТРЕЕ ЧЕМ LDIR
... ;НА 5 ТАКТОВ НА БАЙТ
;КОЛИЧЕСТВО КОМАНД
LDI ;LDI РАВНО ШИРИНЕ
;СПРАЙТА
DEC A ;УМЕНЬШЕНИЕ СЧЕТЧИКА
JR NZ,LOOP ;ЛИНИЙ И ПЕРЕХОД
LD SP,(STACK) ;ВОССТАНОВЛЕНИЕ СТЕКА
RET ;ВОЗВРАТ
Теперь посмотрим, во что
нам это обошлось: 384 байта
таблички и немного на LDI,но
зато: печать независима от
знакомест и сегментов экрана,
обладает высочайшей скоростью:
расчет адреса (или точнее вы-
борка) всего 39 тактов.
ПРИМЕЧАНИЕ: если X равен 0,
можно удалить строки с измене-
нием X (это можно использовать
при scroll-е всего экрана).
Печать примет следующий вид:
POP DE
LDI
LDI
...
LDI
LDI
Можно пойти еще дальше и
раскрыть цикл по A, с'экономив
еще 17 тактов на линию. В этом
варианте на байт тратится 16
тактов и плюс 10 (POP DE) де-
ленное на ширину спрайта. Все-
го в среднем около 20 тактов
на байт.
Вы можете спросить: а как
же печать с точностью до пик-
селов по горизонтали? Отвечаю:
можно и до пиксела, но опять с
маленькой хитростью. Мы будем
печатать спрайт с точностью до
пиксела по знакоместам. Вы
скажете: глупость какая! Так
ведь нет! Идея заключается в
создании восьми фаз нашего
спрайта со сдвигом на один
пиксель и печать каждого из
них с точностью до знакоместа.
Алгоритм выглядит так: берется
X в пикселях, делится на 8.
Частное это X координата в
знакоместах, а остаток это но-
мер фазы нашего спрайта с со-
ответствующим сдвигом, а далее
просто печать уже знакомой нам
процедуркой
Теперь о том, как создать
табличку адресов. я не буду
приводить листинг, он не сло-
жен, а опишу алгоритм: берет-
ся любая процедура расчета ад-
реса в пикселях (я использовал
процедуру из Элементарной Гра-
фики). Далее цикл от 0 до 191.
Вычисляем адрес и заносим в
таблицу, увеличиваем указатель
текущего элемента на 2 и пов-
торяем все сначала. Кстати,
эта же табличка может приме-
няться для любых операций с
экраном
2.ОСВЕЖЕНИЕ ЭКРАНА И СЕТОЧКИ
Для обеспечения плавной графи-
ки необходимо освежать экран
50 раз в секунду. Для этой це-
ли замечательно подходит сте-
ковый метод печати. У него
есть немало разновидностей.
Рассмотрим некоторые из них.
1:ОЧИСТКА ЭКРАНА
LD A,0 (XOR A) ;ОБНУЛЯЕМ АККУМУЛЯТОР
LD H,A ;ЗАПОЛНЯЕМ HL АККУМУЛЯТОРОМ
LD L,A ;(ХОТЯ МОЖНО ИСПОЛЬЗОВАТЬ
;ЛЮБУЮ РЕГИСТРОВУЮ ПАРУ)
PUSH HL ;ПОВТОРЯЕМ PUSH СТОЛЬКО
... ;РАЗ, СКОЛЬКО НУЖНО
PUSH HL ;ДЛЯ ПОЛНОЙ ОЧИСТКИ
Вместо 0 можно засылать любой
другой байт и получать всевоз-
можные полоски на экране.
3.СЕТОЧКА 8x8
Из предыдущего пункта лег-
ко получаются разнообразные
сеточки на весь экран, которые
так любят использовать в де-
мах. Идея такова: берется
спрайтик 8x8 пикселей и с кон-
ца начинается выводится на эк-
ран, линия за линией. Сущест-
вует несколько способов реали-
зации этого эффекта, мы рас-
смотрим один из них. Пусть
имеется некий спрайтик 8x8pix,
наша задача заполнить им эк-
ран.
LD IX,SPRITE ;УКАЗАТЕЛЬ НА НАЧАЛО СПРАЙТА
LD (STACK),SP ;СОХРАНИМ SP
LD SP,22528 ;ПЕРЕСТАВИМ SP В КОНЕЦ ЭКРАНА
LD H,(IX+7) ;ЗАПОЛНИЛИ HL ПОСЛЕДНИМ
LD L,H ;БАЙТОМ СПРАЙТА
PUSH HL ; PUSH HL ПОВТОРЯЕТСЯ 128
... ; >РАЗ ДЛЯ ЗАПОЛНЕНИЯ ВСЕХ
PUSH HL ;/ НИЖНИХ ЛИНИЙ ЭКРАНА
LD H,(IX+6) ;ЗАПОЛНИМ HL ПРЕДПОСЛЕДНИМ
LD L,H ;БАЙТОМ
PUSH HL ;
... ; >PUSH HL*128
PUSH HL ;/
;ПОВТОРЯЕМ ВСЕ ДЛЯ IX+5,IX+4,
... ;IX+3,IX+2,IX+1
LD H,(IX+0) ;И ДЛЯ IX+0
LD L,H ;
PUSH HL ;
... ;
PUSH HL ;
;НИЖНИЙ СЕГМЕНТ ЗАПОЛНЕН.ТЕПЕРЬ НАДО
;ПОВТОРИТЬ ВЕСЬ ТЕКСТ ОТ LD H,(IX+7) ДО
;КОНЦА ЕЩЕ 2 РАЗА ДЛЯ ЗАПОЛНЕНИЯ ОСТАЛЬНЫХ
;СЕГМЕНТОВ ЭКРАНА
LD SP,(STACK) ;В КОНЦЕ ВОССТАНОВИМ СТЕК
RET ;И ВЕРНЕМСЯ ПО RET
Вот, неподвижная сеточка
на экране. А как заставить ее
двигаться? Очень просто: надо
создать блок спрайтов, содер-
жащих фазы движения сеточки по
экрану. В данном случае (8x8)
это будет обыкновенный фонт,
созданный в ARTSTUDIO или в
другом редакторе. Алгоритм
движения по файлу спрайтов
(фонту) очень напоминает нача-
ло процедуры печати символами
8x8:
LD A,NUM ;NUM-ЭТО НОМЕР ТЕКУЩЕГО
;СПРАЙТА
LD XH,0 ;ЗАГРУЗКА IX ЧЕРЕЗ НЕДОКУ-
LD XL,A ;МЕНТИРОВАННЫЕ КОМАНДЫ
;ДЛЯ УСКОРЕНИЯ ПРОЦЕССА
ADD IX,IX ;УМНОЖЕНИЕ НА 8
ADD IX,IX ;
ADD IX,IX ;
LD DE,SPRITES ;СЛОЖЕНИЕ С АДРЕСОМ НАЧАЛА
ADD IX,DE ;ФАЙЛА СПРАЙТОВ
Полученный в IX адрес исполь-
зуется в предыдущей процедуре.
Теперь хотелось бы сказать
о способах получения файла с
фазами движения. Первый и са-
мый наглядный, но и самый
трудоемкий это нарисовать их
(фазы) в графическом редакто-
ре. А второй - это сгенериро-
вать его в процессе инсталля-
ции программы. Рассмотрим идею
инсталляции поподробнее. В
самом крутом варианте это
будет выглядеть так: имеется
некая двумерная функция (хоть
та же синусоида), программа
вычисляет ее график и заносит
его в табличку. Далее програм-
ма производит преобразование
таблички по модулю 8, а по-
просту оставляет вместо полной
координаты остаток от деления
ее на 8. Следующий этап: фор-
мирование спрайтового набора
(фонта): берется начальный
спрайт, ротируется(!) X раз
вправо, Y раз вниз и заносится
на очередное место в таблице.
Можно создать несколько функ-
ций, и несколько наборов
спрайтов, а можно объединить
их по OR или по другой логи-
ческой операции для получения
сеточек с несколькими планами
движения (например: две сеточ-
ки: одна движется вверх, а
другая по диагонали вниз). Вот
и готова сеточка, но если вы
попробуете ее запустить, даже
через HALT то у вас ничего
путного не выйдет (хотя вся
печать занимает около половины
INTа (30000 тактов) из за пе-
ресечения хода ULA и заполне-
ния стека. Выручает второй эк-
ран (в 128-х моделях). Его
включение происходит практи-
чески мгновенно, чем и обеспе-
чивается необходимый эффект.
LOOP LD BC,32765 ;ПОДКЛЮЧАЕМ СТРАНИЦУ ВТОРОГО
LD A,23 ;ЭКРАНА ПРИ ВКЛЮЧЕННОМ
OUT (C),A ;ПЕРВОМ ЭКРАНЕ
CALL PRINT ;ПЕЧАТЬ
HALT ;СИНХРОНИЗАЦИЯ
LD BC,32765 ;ПОДКЛЮЧАЕМ СТРАНИЦУ ПЕРВОГО
LD A,21+8 ;ВОГО ЭКРАНА ПРИ ВКЛЮЧЕННОМ
OUT (C),A ;ВТОРОМ ЭКРАНЕ
CALL PRINT ;ПЕЧАТЬ
HALT ;СИНХРОНИЗАЦИЯ
JR LOOP ;
Вот и двухэкранная технология.
На это дело можно повесить
музыку, спрайты и еще, что
нибудь, но надо следить за
временем выполнения ,чтобы оно
не превысило 1 INT. Еще пара
замечаний: если используются
спрайты, то табличку адресов
необходимо генерировать под
адрес 49152 а не 16384, при
использовании IM2 вектор
прерываниий должен быть ниже
49152, а если выше, то он дол-
жен быть продублирован на всех
использующихся страницах. Му-
зыка тоже желательно должна
находится ниже 49152, а если
Вы хотите посадить ее на
страничку, то придется запоми-
нать число из порта 32765 и не
переключать экраны на время
проигрывания музыки.
2:СЕТОЧКИ 16x16
LD IX,SPRITE ;УКАЗАТЕЛЬ НА НАЧАЛО СПРАЙТА
LD (STACK),SP ;СОХРАНИМ SP
LD SP,22528 ;ПЕРЕСТАВИМ SP В КОНЕЦ ЭКРАНА
LD B,(IX+31) ;В BC ЗАГРУЗИМ ПОСЛЕДНИЙ И
LD C,(IX+30) ;ПРЕДПОСЛЕДНИЙ БАЙТЫ СПРАЙТА
LD D,(IX+15) ;А В DE ДВА БАЙТА ИЗ СЕРЕДИНЫ
LD E,(IX+14) ;СПРАЙТА :15 И 16
PUSH BC * 16*4 ;НАРИСУЕМ НИЖНЮЮ И СРЕДНЮЮ
PUSH DE * 16/ ;ЛИНИИ СПРАЙТА В ОДНОЙ
;ТРЕТИ ЭКРАНА
LD B,(IX+29) ;ПЕРЕЗАГРУЗИМ РЕГИСТРОВЫЕ
LD C,(IX+28) ;ПАРЫ,СМЕСТИВШИСЬ НА ОДНУ
LD D,(IX+13) ;ЛИНИЮ ВВЕРХ
LD E,(IX+12) ;
PUSH BC * 16*4 ;НАРИСУЕМ ЕЩЕ 2 ЛИНИИ
PUSH DE * 16/ ;
... ;БУДЕМ ПРОДОЛЖАТЬ СМЕЩАТЬСЯ
;ВВЕРХ И РИСОВАТЬ
;ПО ДВЕ ЛИНИИ
LD B,(IX+17) ;И,НАКОНЕЦ,ЗАГРУЖАЕМ В
LD C,(IX+16) ;РЕГИСТРОВЫЕ ПАРЫ БАЙТЫ
LD D,(IX+1) ;ИЗ СЕРЕДИНЫ И ИЗ НАЧАЛА
LD E,(IX+0) ;СПРАЙТА
PUSH BC * 16*4 ;
PUSH DE * 16/ ;
;ВСЕ,ОДНА ТРЕТЬ НАРИСОВАНА,ТЕПЕРЬ ОСТАЛОСЬ
;ПОВТОРИТЬ ВСЕ ЭТО ЕЩЕ 2 РАЗА ОТ КОМАНДЫ
;LD B,(IX+31) И ДО КОНЦА.
LD SP,(STACK)
RET
По времени эта сеточка
занимает почти столько же,
сколько и предыдущая т.е. око-
ло 30000 тактов. Все вышеска-
занное насчет движения и соз-
дания фаз относится и к этой
разновидности эффекта, но при
создании функции движения, не-
обходимо брать остаток от де-
ления на 16, а не на 8, как в
предыдущем случае. Надо ска-
зать, что это довольно универ-
сальный способ печати на экра-
не. Если Вы поняли принцип ра-
боты описанных выше эффектов,
то Вам будет нетрудно дорабо-
тать их до других размеров:
3x3, 4x4, 2x4 и т.д.
В заключение хотел бы получить
отзывы на этот материал,а
также предлагаю свою помощь и
сотрудничество по всем вопро-
сам, связанным с программиро-
ванием на ASSEMBLERе.
Мой адрес:
┌────────────────────┐
│ 394016,Г.ВОРОНЕЖ │
│ УЛ.ТУЛЬСКАЯ,Д.16 │
│ НАСОНОВУ Р.А. │
│ (ROM CORP.) │
└────────────────────┘