Miracle
#03
16 июля 1999 |
|
Кто там кодит? - Быстрая графика: несколько рецептов от Zetter'а (печать спрайтов, обновление экрана).
(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.) │ └────────────────────┘
Другие статьи номера:
Похожие статьи:
В этот день... 9 декабря