Здарова, сынки!
   Сегодня я расскажу вам о более правиль-
ных методах программирования с оптимизаци-
ей по скорости.То есть,чтобы,к примеру,де-
мки писать (делать вам, сказал бы я,нечего
;).В качестве положительного примера сове-
тую дему 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