Хабрахабр

Создание 1k intro Chaos для ZX-Spectrum

Изначально я не планировал делать демо на Chaos Constrictions 2018, однако за 2-3 недели до cc понял, что с пустыми руками идти на демопати никак нельзя, и решил написать небольшую демонстрацию для 386/EGA/DOS.

Демо в том виде, в котором я хотел бы его видеть, за этот весьма ограниченный срок, сделать было невозможно. Скомпилировав в Turbo-C под DOS свою либу AnotherGraphicsLibrary, которая идеально ложиться в битплановую структуру EGA режима, я разочаровался, от тормозов, прежде всего тормозов EGA.

И тут я вспомнил, что давно хотел принять участие в ZX-Spectrum конкурсах демо. Однако сдаваться и не делать что-либо, я уже не мог. К слову — для меня самое главное в написании демо это именно тестирование на реале, эмульгаторы не дают такого наслаждения от процесса, уж очень это замечательное чувство, когда после очередного изменения в коде ты закачиваешь демо на реал, и видишь как настоящая железка тасует байтики в памяти, отрисовывая эффект. А так, как за последний год у меня появилось целых два 48k реала, я мог получить определенное удовольствие от создания демо.

А из-за ограниченности сроков и отсутствия каких-либо наработок, выбор пал на создание 1k intro(демо объёмом всего 1 килобайт, или 1024 байта).
Последний раз z80 asm я тыкал в EmuZWin — замечательном эмуляторе со встроенным ассемблером. Поскольку из реалов у меня только 48k, то и демо я решил сделать для 48k. Но к сожалению EmuZWin на чем-либо выше Windows XP не работает, либо глючит.
Рассмотрев различные варианты остановился на связке программ Unreal+sjAsm+Notepad++, которые, на мой взгляд, по удобству сильно проигрывают EmuZWin, но в отличие от него живы.

Во время написания этого интро я вел, прямо в исходниках, лог разработки, по мотивам которого написан дальнейший текст:

Hello World!

Что надо писать первое, имея практически нулевой опыт в z80 asm? Правильно, вывод спрайта 5x5 знакомест или 40x40 пикселей, для одного из эффектов (по иронии, в дальнейшем, для того чтобы влезть в 1k эта недоделанная часть была выкинута из интро).

Поразительно, но это было довольно просто сделать с нуля, используя наперед сгенерированную табличку адресов строк с помощью Down HL.

Пришлось выкинуть их использование из кучи мест. Ох, индексные регистры, какие же они удобные, но какие меееедленные, буквально выжирают такты.

Дизасм в Unreal показывал абсолютно бредовую последовательность команд. Еще здесь, в самом начале, я наткнулся на невероятные глюки sjAsm, точнее его последней версии. Скачал предпоследнюю версию — с ней уже можно было хоть как-то жить.

Причем не абы как, а нарисовать с помощью полигонов. Понятное дело, что сколько-нибудь адекватное кол-во заранее нарисованных спрайтов в 1k не засунуть, поэтому я решил сгенерировать их динамически.

По большей части это было портирование своего-же кода, написанного на Си. Поэтому второй процедурой, которую я написал, стала процедура рисования треугольника. С единственным глобальным отличием от Си версии — сначала генерируются сканалйны полигона, а только потом он рисуются по этим сканлайнам.

После высокоуровневых языков, получаешь определённое наслаждение от jr aka goto:

.sort_me_please: ld de,(tr_x2) ld bc,(tr_x0) ld a,d cp b jr nc,.skip1 ld (tr_x2),bc ld (tr_x0),de
.skip1: ld de,(tr_x1) ld bc,(tr_x0) ld a,d cp b jr nc,.skip2 ld (tr_x0),de ld (tr_x1),bc jr .sort_me_please
.skip2: ld de,(tr_x2) ld bc,(tr_x1) ld a,d cp b jr nc,.skip3 ld (tr_x2),bc ld (tr_x1),de jr .sort_me_please
.skip3:

Я был немного шокирован, тем, что у меня вышло в разумное время написать работающую draw_triangle на z80 asm, рисующую полигон пиксель в пиксель и без дыр при стыковке полигонов.


Hello triangles!

Частицы

Процедура имеет две точки входа — просто инверсия пикселя, и инверсия пикселя с закрашиванием его INK-а+BRIGHT+а цветом указанном в одном из регистров. Из-за наличия генератора табличкек строк экрана, я написал довольно кривую и медленную процедуру вывода точки, использующую эту табличку.

Гугление вывело на тему с сайта zx-pk.ru, где описана эта проблема, и нет ее решения — ха, отлично — еще один глюк. На этапе создания эффекта с частицами обнаружил что пример со структурами из примеров в wiki sjAsm просто не работает.

Ага… плюс дохрена байтов для генерации таблицы прерываний. Решил сделать все четко — обновление координат независимо от отрисовки, по прерыванию.

это сильно экономило место на необходимости только одного генератора таблиц. Частиц на этом этапе было немного, и они едва влезали в фрейм — это к слову о медлительности моей процедуры вывода точки %) Но использование общей таблицы со спрайтами, не давало мне ее выкинуть, и взять готовую, т.к. Да и моя любовь к велосипедам тоже 🙂

Слишком мало частиц… увеличил их количество, но теперь отрисовка разжирела до двух фреймов.


Тестирование на Peters WS64, который я добыл на прошлом cc и починил этой зимой 🙂

Вышло это из-за изначально малого кол-ва частиц, и моей неудовлетворённостью тем, что они были довольно незаметны при прогоне на реале. Кстати, уже на этом этапе точки превратились в жирные горизонтальные точки 2:1, как на Commodore 64. Решил проблему заменой таблички

db 128,64,32,16,8,4,2,1;

на

db 192,192,96,24,12,6,3,3;

что ухудшило точность позиционирования и сделало полет чуточку дерганым, но увеличило заметность. Тут на руку также сыграло еще и то, что частицы падали сверху вниз — по вертикали их размазывало зрение.

Частицы кстати падают каждая со своей случайной скоростью, а для хранения координат по Y используется два байта.

Спрайты

Выкинул недоделанный кусок части со спрайтами, поняв что не уложусь в 1k с ним.

Кстати возможно кто-то знает более подходящий пакер для 1k интро? Кроме того, уже ощущалась нехватка места, поэтому вспомнил про замечательную статью Интроспека про пакеры, выбрал zx7 в качестве пакера, что дало экономию примерно в 110 байт.

Chaos Constructions

Поскольку у меня уже бала процедура вывода полигона, мне показалось крутой идеей разбить лого cc на полигоны и вывести на экран их друг за другом.

Написал некоторый тестовый код, выводящий несколько полигонов — все работало как я и задумал — отлично.

Скомпилировал, и убедился что — отлично — интро, в этом виде, влезает в лимит 1024 байта. Для проверки того, влезет или нет моя задумка в 1k, нагенерировал некоторое кол-во рандомных полигонов, по прикидкам, достаточное кол-во для логотипа, и загнал в исходники.

:)))
Лайф фото, узнаете девайс на столе?

Впервую очередь я стал грешить на то, что где-то забыл проинициализировать память, от чего, там, где на эмуляторе 0x00 и все отлично работает, на реале мусор, вызывающий сброс. Решил еще раз протестировать полуфабрикат интро, уже с полигонами, и пакером, загрузил на реал и… получил сброс.

Ничего лучше, для нахождения проблемного места, чем метод половинного деления и di halt я не смог придумать.

Улучшайзер из потока битов в WAV файле генерировал поток бреда. Провозился со сбросом на реале в течении двух часов, локализовать глюк никак не выходило…
Как оказалось, дело было не в моем коде, дело было в включённом улучшайзере звука на телефоне с которого я грузил WAV-ки.

Как только я отключил его все волшебным образом заработало.

Запихнув лого Chaos Constructions полностью и запустив на реале — порадовался — выглядело довольно не плохо. Обрисовал логотип в графическом редакторе errorsoft greenpixel, разбив его на кучу треугольников, и загнал вручную координаты в исходники.


Первое отображение лого на реале

И это при том, что эффект частиц был все еще не доделан, а переход между частями был резкий. Однако рандомных полигонов я напихал слишком мало, и на реальном лого, произошел выход за лимит 1k на 150 байт.

Лечь спать в этот день, из-за возни с глюками, вышло аж в 8 часов утра 8)

И да, я пытался оптимизировать размер, храня координаты и индексы вершин отдельно, но это сильно не нравилось пакеру, от чего размер только увеличивался.

Финал

Придумал как разнообразить вывод логотипа, для этого не потребовалось почти ничего, кроме еще двух табличек пикселей:

fake_points1: db 1,2,4,8,16,32,64,128; 1 ch
fake_points2: db 32,8,128,2,16,1,64,4; 2 ch
normal_points: db 128,64,32,16,8,4,2,1; 3 ch

Что дало прикольный эффект увеличения детализации отрисовки логотипа, или изначальной заблюренности и постепенного увеличения резкости.

В процессе нашел глюк в процедуре рисования треугольника — если у двух вершин координаты по Y одинаковые, то треугольник рисуется криво (похоже деление на 0 при вычислении dx), обошел временным хаком. И наконец, я сделал окончательную версию со всеми переходами, выкинув при этом кучу всего.


Тестирование окончательной версии на Leningrad 48

Оптимизация размера


Двузначные цифры — это «лишние байты»

94 лишних байта…

Пришла пора вырезать «культурное» сохранение/восстановление регистров у процедур на входе/выходе, далеко не везде это надо, а память жрет.

86 байтов…

Протестировал на реале — работает! Отбил еще немного памяти, попутно пофиксив баг с делением на 0 — 63 байта!

57 байт…

Добавил зацикливание.

random_store:
start:

Зацикливание кстати сделано на уровне распаковки, т.к. в качестве источника энтропии для ГСЧ использовались несколько байтов из кода инициализации (для экономии места), которые в процессе работы первой части портил ГСЧ. Поэтому для зацикливания, после окончания интро стоит ret, ну, а дальше — еще одна распаковка и переход к распакованному коду…

Запихал! Избавиться от последних 48 байт не удалось никак, пришлось выпилить обработчик прерывания, но УРА! Даже 1 лишний байт остался.

А раз нет прерываний, то можно забить на фреймовость, да и сложно увидеть сечение луча на одном пикселе в первом эффекте, поэтому увеличил кол-во частиц на глаз, с компромиссом между скоростью и зрелищностью.

Что заняло определенное время 🙂 Еще сильнее ужал, тупо переносом кода и данных из одного места в другое, помогая пакеру.

Кхк, кхе, звук — это в некоторых местах, на слух вставленное: Это освободило около 10 байт, в которые я засунул пародию на звук.

ifdef UseSound ld a,d and #10 out (#FE),a endif

Я не стал «прибивать гвоздями» звук, а сделал дефайн, получив таким образом две версии интро, со звуком и без. В ту, что без звука, в «лишние» байты запихал

db 'e','r','r','o','r'

Собрал trd и tap, и залил все это на сайт cc.

Ура — я участвую в демопати!

Послесловие

Со звуком вообще забавно получилось, кто-то на патиплейсе говорил про «четкий звук», кто-то странно на меня смотрел, а на pouet я обнаружил следующее:

И это:

В общем, так я и не понял, понравился кому-либо 10-ти байтный звук или нет 🙂

И последнее — обидно что конкурс 1k в этом году так и не состоялся, работа на мой взгляд получилась достойная, но с 640k соревноваться сложно, а очень хотелось побороться.

Пишите демки, пишите 1k!

А вот и то, что в итоге получилось(пс, берегите уши):

Версия без звука:

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть