Из журнала ZX-Guide#4, Рязань, 25.11.2001 Автор: Alone Coder (Dyma Bystrov) Здарова, сынки! Сегодня я расскажу вам о более правиль- ных методах программирования с оптимизаци- ей по скорости.То есть,чтобы,к примеру,де- мки писать (делать вам, сказал бы я,нечего ;).В качестве положительного примера сове- тую дему Power Up (ту самую,которая пофик- сенная). В качестве отрицательного можно взять Refresh того же автора. Вообще-то странно: почему Exploder после красочной, быстрой и оптимизированной демы написал тормозную, чёрно-белую и глючную? Итак,возьмём два простейших эффекта:по- ворот (Rotation, включая его разновидность ZoomRotation) и освещение (BumpMapping) Rotation Нам требуется построить проекцию карты (Map) на экран или его заменитель - массив чанков (ChunkMap) под углом.Причём каждому пикселу экрана должен соответствовать пик- сел на карте,т.е. главный цикл заключается в переборе пикселов экранной строки. ! При проектировании эффекта следует в пер- вую очередь думать о скорости внутреннего цикла (inner loop) - цикла, тело которого выполняется максимальное количество раз. В данном случае inner loop перебирает все чанки экрана.Для оценки скорости программы можно использовать нижнюю границу времени выполнения - в тактах на чанк (пиксел, ат- рибут, байт - смотря чем мы оперируем) ! Данные об оттенке пиксела копируются из карты (координаты {x,y} заданы с точностью 1/256 пиксела) в ChunkMap,после чего коор- динаты на карте модифицируются прибавлени- ем некоего вектора - направления строки. Вектор этот равен {m*cos a;m*sin a}, где a - угол поворота (0 - нет поворота,т.е. го- ризонталь на карте соответствует горизон- тали на экране), а m - масштаб уменьшения. (При переходе на следующую строку коорди- наты меняются на вектор, перпендукулярный этому: {-m*sin a;m*cos a}.Если он будет не перпендикулярен, то проекция произойдёт с искажением. А что? Вращение с искажением - тоже эффект!) Первым делом в голову приходит примерно такой участок программы: EXX ADD IX,BC ;Y+=dY ADD HL,DE ;X+=dX LD A,H EXX LD L,A LD A,HX LD H,A LDI ;(HL)->(DE++) Итого:70 t/c (тактов на чанк) Скорость программы во многом определяе- тся количеством команд LD. Действительно,к чему переливать из пустого в порожнее? ADD A,B ;A=младшая часть y JR NC,$+3 ;B=dY INC H EX AF,AF' ADD A,C ;A'=младшая часть x JR NC,$+3 ;C=dX INC L LD E,(HL) -------------------- ADD A,C JR NC,$+3 INC L EX AF,AF' ADD A,B JR NC,$+3 INC H LD D,(HL) PUSH DE Итого в среднем 95/2=47.5 t/c Вывод идёт уже через стек,то есть задом наперёд. В данном случае даже не страшно внезапное прерывание. Тут,правда,можно делать только увеличе- ние, без уменьшения (масштаб должен быть меньше единицы).А для полного комфорта ну- жно четыре таких процедурки для всех воз- можных (inc/dec) знаков смещения по x и y. Понятно,что между пикселями HL изменяе- тся на какую-то величину, каждый раз раз- ную. Но от строки к строке последователь- ность сумматоров меняется незначительно, что позволяет сгенерировать табличку в на- чале построения кадра,а потом использовать её в каждой строке: POP BC ;SP=табличка ADD HL,BC LDI (37 t/c) Или так: LD BC,... ADD HL,BC LD E,(HL) -------------------- LD BC,... ADD HL,BC LD D,(HL) PUSH DE (67/2=33.5 t/c) Или даже так (опять ограничение на мас- штаб): [inc h] [inc l] LD E,(HL) -------------------- [inc h] [inc l] LD D,(HL) PUSH DE (в среднем 33/2=16.5 t/c!!!) Правда, подпрограмма,генерирующая такую процедурку,сама кушает как минимум 5000 t, но результат того стоит! Если это всё же чанки,то их надо как-то выводить. В идеальном случае для каждого эффекта нужно выбирать свою процедуру вывода чан- ков - самую быструю для данного случая.Со- ответственно выбирается и формат Chunk- Map'a: по одному чанку на байт или по два, и какие биты этих байтов лучше юзать... Вот хороший и быстрый метод вывода чан- ков by Monster/Sage,в моей,так сказать,ин- терпретации (кстати,этот метод использован в первой части интро). Где-то в памяти строится массив вот та- ких процедурок и JP'ов на них, причём JP'ы лежат по адресам типа %11aaaa00 11bbbb00, где aaaa,bbbb - цвета чанков: ;SP=ChunkMap LD H,A LD (HL),x ;это байт,или рег. B,C,D,E INC H ;причём BC=#00FF,DE=#55AA LD (HL),x INC H LD (HL),x INC H LD (HL),x INC L RET ;переход на следующую проце- ;дурку из этой серии... (В конце чанкмэпа лежит адрес выхода в вызывающую программу) В нашем случае вывод чанков можно поме- стить прямо в главный цикл: [inc h] [inc l] LD E,(HL) ;%0110aaa0 -------------------- [inc h] [inc l] LD D,(HL) ;%0110bbb0 LD A,(DE) LD (BC),A INC B INC E LD A,(DE) LD (BC),A DEC B ;<- killable INC C (28.5 t/c) Здесь выводится только 2 строчки чан- ков,так что есть выбор: либо любоваться на полосатую картинку,либо выводить в следую- щем кадре по недостающим строкам (со сдви- гом в 2 пиксела по вертикали)- в этом слу- чае за картинкой будет ползти прикольный "хвост". hint: нажмите Enter в конце интро к жу- рналу :) Можно избавиться от DEC B, помеченного стрелочкой, если INC B (четырьмя строчками выше) постоянно чередовать с DEC B. В этом случае чанки будут кувыркаться от знакоме- ста к знакоместу, но этого не будет замет- но,если в рисунке чанка убрать одну верти- кальную линию (цветов останется всего 7): . . . . . . . . 1 6 2 . 4 3 5 . 4 3 5 . 1 6 2 . . . . . . . . . Выигрыш - 2 t/c (26.5 t/c) Думаете,это уже всё? Как бы не так! По- чему бы нам брать с карты пикселы не по 1, а сразу по 2? Или по 4? (Illusion вспомни- те,там,где Sonic крутится!) Советую взглянуть на процедурку в при- ложении (SCALE.H), которая печатает аж по 32 пиксела (4x8) после каждой коррекции координат! Смотрится вполне неплохо, если учесть скорость масштабирования! А теперь угадайте,что это за процедура: [inc h] [inc l] LD E,(HL) ;%0aa00bb0 -------------------- [inc h] [inc l] LD D,(HL) ;%0cc00dd0 PUSH DE ;SP=screen! (33 t/4c) А эти две? [inc/dec h] ;по синусоиде LDI ;DE=screen! (18.5 t/b) [inc l] LD E,(HL) ;%0aa00bb0 или просто байт -------------------- [inc l] LD D,(HL) ;%0cc00dd0 или байт PUSH DE ;SP=screen! (<33 t/4c=17.5 t/b) Советую умножить 33(t)*16(x)*96(y)= =50688 t! И это весь экран! Причём при желании чанковый,в 3 цвета! Вспомните Power Up, а особенно Rotator+ +Mirror, 50 fps Flag, 50 fps Zoom. Выходит,что их можно ещё ускорить :))) BumpMapping Лучше не стройте чертёж с расчётом ин- тенсивности отражённого света на рельефе. Результат будет содержать синус и квадрат- ный корень, что для наших экспериментов по оптимизации неприемлемо. Практический способ освещения рельефа состоит в проецировании изображения источ- ника света (отсюда следует, что он может иметь любую форму) на картинку,причём фор- ма рельефа определяет смещение проекции в данной точке. (Аналогично реализуются и другие эффек- ты,связанные с проекцией:Inside Torus,Full Screen Lens,некоторые разновидности тунне- лей и т.п.) Если в данной точке поверхность понижа- ется по направлению вправо,то смещение бу- дет с положительным X, и, чем круче склон, тем больше смещение. То есть,если источник правее этой горки,то проекция в данной то- чке сместится вправо,где "ярче".А если ис- точник левее, то проекция сместится опять- таки вправо,где "темнее".Вполне реально ;) ;SP=relief POP HL ;HL=смещение в данной точке ADD HL,BC ;BC=адрес изображ-я фонаря LDI ;(HL)->(DE++),BC-- (37 t/c) Казалось бы, что тут сокращать? Исполь- зованы все 3;) мультимедийные команды Z80, они очень 16-битные и очень быстрые... Однако же,стек можно использовать и лу- чшим образом. Пусть из статичной картинки рельефа со- здаётся такая процедурка: LD HL,... ADD HL,BC LD E,(HL) -------------------- LD HL,... ADD HL,BC LD D,(HL) PUSH DE (67/2=33.5 t/c) Теперь поглядите: что же за константы у нас в HL? Сильно ли они меняются от точки к точке? А не входят ли они все в диапазон -128~ +127? Если ширина экрана около 56 чанков, а максимальное Y-смещение по модулю не пре- вышает 2, то очень даже входят! Значит,имеет право на существование та- кая процедурка: ADD A,... LD L,A LD E,(HL) -------------------- ADD A,... LD L,A LD D,(HL) PUSH DE (47/2=23.5 t/c) Каждая линия фонаря вместе с четырьмя соседними (выше и ниже) занимает сектор памяти.Этот сектор мы и будем использовать при построении экранной строки. Разумеется,перед построением каждой но- вой строки нужно занести в HL адрес соот- ветствующей линии (плюс 4 соседних) изоб- ражения источника света. Ну,и вариации: LD A,L ADD A,... LD L,A LD E,(HL) ;%0110aaa0 -------------------- ADD A,... LD L,A LD D,(HL) ;%0110bbb0 LD A,(DE) LD (BC),A INC B INC E LD A,(DE) LD (BC),A DEC B ;<- killable INC C (42 t/c) Или так (если юзать мультиколор): LD A,L ADD A,... LD L,A LD E,(HL) ;%0110aaa0 -------------------- ADD A,... LD L,A LD D,(HL) ;%0110bbb0 LD A,(DE) LD (BC),A INC C (29 t/c) Отсюда напрямую следует, что Bump можно сделать ничуть не менее фреймовым, чем его коллегу Zoom Rotator... А если во фрейм не влезет, можно чуть- чуть убавить высоту активной части экрана. Конечно,это всё простые эффекты,которы- ми теперь никого не удивишь... Но я хотел всего лишь научить вас мыслить нестандарт- но. Все использованные фрагменты программ сочинены мной, в чужой код глядеть не имею привычки. Любые совпадения случайны.  Alone Coder