Створення 1k intro Chaos для ZX-Spectrum


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

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

Однак здаватися і не робити що-небудь, я вже не міг. І тут я згадав, що давно хотів взяти участь у ZX-Spectrum конкурсах демо. А так, як за останній рік у мене з’явилося цілих два 48k реала, я міг отримати певне задоволення від створення демо. До речі — для мене найголовніше в написанні демо це саме тестування на реалі, емулятори не дають такої насолоди від процесу, вже дуже це чудове відчуття, коли після чергового зміни в коді ти закачувати демо на реал, і бачиш як справжня залізка тасує байтики в пам’яті, показуючи ефект.

Оскільки реалів у мене тільки 48k, то і демо я вирішив зробити для 48k. А з-за обмеженості термінів і відсутність будь-яких напрацювань, вибір припав на створення 1k intro(демо об’ємом всього 1 кілобайт, або 1024 байта).

Останній раз z80 asm я тикав в EmuZWin — чудовому емуляторі з вбудованим ассемблером. Але на жаль EmuZWin на чому-небудь вище Windows XP не працює, або глючить.
Розглянувши різні варіанти зупинився на зв’язці програм Unreal+sjAsm+Notepad++, які, на мій погляд, по зручності сильно програють EmuZWin, але на відміну від нього живі.

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

Hello World!

Що треба писати перше, маючи практично нульовий досвід в z80 asm? Правильно, висновок спрайту 5×5 знакомісць або 40×40 пікселів, для одного з ефектів (за іронією, в подальшому, для того щоб влізти в 1k ця недороблена частина була викинути з інтро).

Вражаюче, але це було досить просто зробити з нуля, використовуючи наперед згенеровану табличку адрес рядків з допомогою Down HL.

Ох, індексні регістри, які ж вони зручні, але які повільні, буквально вижирають такти. Довелося викинути їх використання з купи місць.

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

Ясна річ, що скільки-небудь адекватне кількість заздалегідь намальованих спрайтів в 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+а кольором зазначеному в одному з регістрів.

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

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

Частинок на цьому етапі небагато, і вони ледве влазили у фрейм — це до слова про повільності моєї процедури виведення точки %) Але використання загальної таблиці зі спрайтами, не давало мені її викинути, і взяти готову, т. к. це сильно економило місце на необхідності лише одного генератора таблиць. Та й моя любов до велосипедів теж 🙂

Занадто мало частинок… збільшив їх кількість, але тепер відтворення розжиріло до двох фреймів.


Тестування на 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 використовується два байти.

Читайте також  Фахівці з кібербезпеки створили детектор скімерів — SkimReaper
Спрайт

Викинув недороблений шматок частини зі спрайтами, зрозумівши що не вкладуся в 1k з ним.

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

Chaos Constructions

Оскільки у мене вже бала процедура виведення полігону, мені здалося крутий ідеєю розбити лого cc на полігони і вивести на екран їх один за одним.

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

Для перевірки того, влізе чи ні моя задумка в 1k, нагенерировал деякий кількість рандомних полігонів, за прикидками, достатню кількість для логотипу, і загнав у вихідні. Скомпілював, і переконався що — відмінно — інтро, в цьому вигляді, влазить в ліміт 1024 байта.


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

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

Нічого краще, для знаходження проблемного місця, ніж метод половинного ділення та di halt я не зміг придумати.

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

Як тільки я відключив його все чарівним чином запрацювало.

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


Перше відображення лого на реалі

Однак рандомних полігонів я напхав занадто мало, і на реальному лого, відбувся вихід за ліміт 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

Що дало прикольний ефект збільшення деталізації відтворення логотипу, або початкової заблюренности та поступового збільшення різкості.

Читайте також  Уразливість в Telegram дозволяє скомпрометувати секретні чати

І нарешті, я зробив остаточну версію з усіма переходами, викинувши при цьому купу всього. В процесі знайшов глюк у процедурі малювання трикутника — якщо у двох вершин координати по 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!

А ось і те, що в підсумку вийшло(пс, бережіть вуха):

Версія без звуку:

Степан Лютий

Обожнюю технології в сучасному світі. Хоча частенько і замислююся над тим, як далеко вони нас заведуть. Не те, щоб я прям і знаюся на ядрах, пікселях, коллайдерах і інших парсеках. Просто приходжу в захват від того, що може в творчому пориві вигадати людський розум.

You may also like...

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *