![](https://habrastorage.org/webt/7u/dh/ww/7udhwwjcbgzix5jaxzonykfmcf8.png)
Изначально я не планировал делать демо на 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? Правильно, вывод спрайта 5x5 знакомест или 40x40 пикселей, для одного из эффектов (по иронии, в дальнейшем, для того чтобы влезть в 1k эта недоделанная часть была выкинута из интро).
Поразительно, но это было довольно просто сделать с нуля, используя наперед сгенерированную табличку адресов строк с помощью Down HL.
Ох, индексные регистры, какие же они удобные, но какие меееедленные, буквально выжирают такты. Пришлось выкинуть их использование из кучи мест.
Еще здесь, в самом начале, я наткнулся на невероятные глюки sjAsm, точнее его последней версии. Дизасм в Unreal показывал абсолютно бредовую последовательность команд. Скачал предпоследнюю версию — с ней уже можно было хоть как-то жить.
![](https://habrastorage.org/webt/xh/yh/ot/xhyhotktsbfmg1iqk_gtrbmsvjy.png)
Понятное дело, что сколько-нибудь адекватное кол-во заранее нарисованных спрайтов в 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, рисующую полигон пиксель в пиксель и без дыр при стыковке полигонов.
![](https://habrastorage.org/webt/tl/sv/or/tlsvorwso0wpqcrulnaewu23_84.png)
Hello triangles!
Частицы
![](https://habrastorage.org/webt/ng/kx/mq/ngkxmqez4envpqx6-tzzc0wd2ge.png)
Из-за наличия генератора табличкек строк экрана, я написал довольно кривую и медленную процедуру вывода точки, использующую эту табличку. Процедура имеет две точки входа — просто инверсия пикселя, и инверсия пикселя с закрашиванием его INK-а+BRIGHT+а цветом указанном в одном из регистров.
На этапе создания эффекта с частицами обнаружил что пример со структурами из примеров в wiki sjAsm просто не работает. Гугление вывело на тему с сайта zx-pk.ru, где описана эта проблема, и нет ее решения — ха, отлично — еще один глюк.
Решил сделать все четко — обновление координат независимо от отрисовки, по прерыванию. Ага… плюс дохрена байтов для генерации таблицы прерываний.
Частиц на этом этапе было немного, и они едва влезали в фрейм — это к слову о медлительности моей процедуры вывода точки %) Но использование общей таблицы со спрайтами, не давало мне ее выкинуть, и взять готовую, т.к. это сильно экономило место на необходимости только одного генератора таблиц. Да и моя любовь к велосипедам тоже :)
Слишком мало частиц… увеличил их количество, но теперь отрисовка разжирела до двух фреймов.
![](https://habrastorage.org/webt/tb/eu/qq/tbeuqqaoeef05uf04tfe33-t7f4.jpeg)
Тестирование на 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 с ним.
Кроме того, уже ощущалась нехватка места, поэтому вспомнил про замечательную статью Интроспека про пакеры, выбрал zx7 в качестве пакера, что дало экономию примерно в 110 байт. Кстати возможно кто-то знает более подходящий пакер для 1k интро?
Chaos Constructions
![](https://habrastorage.org/webt/83/xj/h4/83xjh4wggmay449rwrmrfphx62y.png)
Поскольку у меня уже бала процедура вывода полигона, мне показалось крутой идеей разбить лого cc на полигоны и вывести на экран их друг за другом.
Написал некоторый тестовый код, выводящий несколько полигонов — все работало как я и задумал — отлично.
Для проверки того, влезет или нет моя задумка в 1k, нагенерировал некоторое кол-во рандомных полигонов, по прикидкам, достаточное кол-во для логотипа, и загнал в исходники. Скомпилировал, и убедился что — отлично — интро, в этом виде, влезает в лимит 1024 байта.
![](https://habrastorage.org/webt/fq/8k/bc/fq8kbcfvtdbdirtysojcd0hix8m.jpeg)
Лайф фото, узнаете девайс на столе? :)))
Решил еще раз протестировать полуфабрикат интро, уже с полигонами, и пакером, загрузил на реал и… получил сброс. Впервую очередь я стал грешить на то, что где-то забыл проинициализировать память, от чего, там, где на эмуляторе 0x00 и все отлично работает, на реале мусор, вызывающий сброс.
Ничего лучше, для нахождения проблемного места, чем метод половинного деления и di halt я не смог придумать.
Провозился со сбросом на реале в течении двух часов, локализовать глюк никак не выходило…
Как оказалось, дело было не в моем коде, дело было в включённом улучшайзере звука на телефоне с которого я грузил WAV-ки. Улучшайзер из потока битов в WAV файле генерировал поток бреда.
Как только я отключил его все волшебным образом заработало.
Обрисовал логотип в графическом редакторе errorsoft greenpixel, разбив его на кучу треугольников, и загнал вручную координаты в исходники. Запихнув лого Chaos Constructions полностью и запустив на реале — порадовался — выглядело довольно не плохо.
![](https://habrastorage.org/webt/vy/4m/ul/vy4mul7kzdo_o7qav3wtnvc3oki.jpeg)
Первое отображение лого на реале
Однако рандомных полигонов я напихал слишком мало, и на реальном лого, произошел выход за лимит 1k на 150 байт. И это при том, что эффект частиц был все еще не доделан, а переход между частями был резкий.
Лечь спать в этот день, из-за возни с глюками, вышло аж в 8 часов утра 8)
И да, я пытался оптимизировать размер, храня координаты и индексы вершин отдельно, но это сильно не нравилось пакеру, от чего размер только увеличивался.
Финал
![](https://habrastorage.org/webt/qv/h9/is/qvh9ist1fdq9xunbrd4uyejd3gi.png)
Придумал как разнообразить вывод логотипа, для этого не потребовалось почти ничего, кроме еще двух табличек пикселей:
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), обошел временным хаком.
![](https://habrastorage.org/webt/xo/a3/--/xoa3--9_kt2k-uodr2gabj8ithm.jpeg)
Тестирование окончательной версии на Leningrad 48
Оптимизация размера
![](https://habrastorage.org/webt/-e/ro/l8/-erol8uszgdcler4i9jhp4n-9cu.jpeg)
Двузначные цифры — это «лишние байты»
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 я обнаружил следующее:
![](https://habrastorage.org/webt/vt/p_/sw/vtp_swwrkskgpqbexsuj1nubn40.png)
И это:
![](https://habrastorage.org/webt/og/vh/wc/ogvhwc8p907emey1wk2erb-ejtm.png)
В общем, так я и не понял, понравился кому-либо 10-ти байтный звук или нет :)
И последнее — обидно что конкурс 1k в этом году так и не состоялся, работа на мой взгляд получилась достойная, но с 640k соревноваться сложно, а очень хотелось побороться.
Пишите демки, пишите 1k!
А вот и то, что в итоге получилось(пс, берегите уши):
Версия без звука:
Комментарии (50)
Smayliks
30.08.2018 23:23Очень понравилась фотка, где процессор «магнитофона» с программой раз в 100-200 быстрее самого компьютера. ;-)
P.S. Ну и по оперативке «слегка» выигрывает, да.tormozedison
31.08.2018 09:52Только не смейтесь: одно время были популярны плееры, где процессом «рулило» ядро, совместимое с Z80 и работающее на 20 мегагерцах.
engine9
31.08.2018 09:59Кассетные плееры?
tormozedison
31.08.2018 11:35Нет, MP3, начало двухтысячных.
Кассетники с микропроцессорным управлением, впрочем, тоже были, но там четырёхбитных ядер хватало.
Вильма-100-стерео особняком, там 16-битный.
Smayliks
31.08.2018 12:37Ну а почему нет? Если бы мне сейчас понадобилось сделать электронику/управление на бобинник, я бы тоже поставил stm32, который быстрее Z-80 в 10 и более раз ;-)
namikiri
31.08.2018 13:42+2Не могу не отметить также оригинальный способ передачи программ — посредством голосовых сообщений в Telegram.
Smayliks
31.08.2018 14:09Некасательно этой фотки, на iPad/iPhone действительно легче передать через телеграмм/ё-мыло/етц, чем городить огород с айтюнсами. Хотя и ПК и планшет/телефон в одной комнате.
tormozedison
31.08.2018 16:04Наиболее рациональный способ с точки зрения занимаемого файлом места — приложение tapDancer. Вместо перекодировки tap-файла в wav — проигрывание tap на лету.
Jenix
31.08.2018 16:07кхм.
ZX — 3,5 МГц 8 бит. 2 такта сложение.
средний смарт 1 ГГц — 4 ядра, 32 бита, 2команды/1 такт.
Я бы сказал, что смарт быстрее чем ZX не менее чем в
1000М х 4я х 2к/т х 32бит / 3,5 * 8 * 0,5к/т = 18 285 раз. )
Это минимум.
Pyhesty
31.08.2018 00:05=) мне очень понравилась статья) подробненько)
отдельное спасибо за ссылку на пакеры!
ps: демо очень достойное своего объёма )
«музон» тем более =)
pss: ностальджи )
gban
31.08.2018 03:44load ""
пшшшш...tormozedison
31.08.2018 11:34+1Сначала «ииииииии», потом «пшик», потом опять «иииитиии», и только потом «пшшшш».
Smayliks
31.08.2018 12:42А это точно не 33600, а программа? ;-)
tormozedison
31.08.2018 16:06Точно. Тон для настройки на скорость магнитофона, имя файла, снова тон, сам файл.
iga2iga
31.08.2018 05:30Вообще экран спекки можно представить и как линейное пространство 2048х24 пикселей. Ну или 256х3 знакоместа (которые в свою очередь 8х8 пикселей). Интересно, но этот DOWN_HL я почти до сих пор помню наизусть, т.к. это важнейшая часть рисования спрайтов, а особенно «указателя мышки» в любой программке для спекки. Ну и вторая важнейшая часть это от 65000 до 72000 тактов за которые надо успеть сделать всё… Как же я тогда мечтал о 7 или еще лучше о 14 МГц…
Dioxin
31.08.2018 07:48«Ох, индексные регистры, какие же они удобные, но какие меееедленные, буквально выжирают такты. Пришлось выкинуть их использование из кучи мест.»
PUSH POP only!
CoolCmd
31.08.2018 10:31а частицы в начале демки крутятся по часовой стрелке или наоборот? :)
Error1024 Автор
31.08.2018 14:55Слева направо :)
Oxyd
01.09.2018 02:23Как-то так?
Javian
31.08.2018 12:00если вывести звук не на пьезо, а на динамическую головку, то звук будет мягче.
dmnBrest
31.08.2018 13:19Да, было время! В каждом дворе были специалисты способные писать такие демо. Сам помню принимал участие в соревновании местных умов по созданию демо в 100 байт. Жаль что у большей части нынешней аудитории ничего кроме недоумения данная статья не вызовет. Не знал что СС еще проводится. Спасибо за позитив!
vheinitz
31.08.2018 13:20-1когда я читал о заговоре Римского Клуба против научно-технического прогресса я посмеивался. А вот теперь и подтверждение. Последнее время завалили статьями, как сделать нинтендо, синклер, тетрис, спейс-инвайдерс, итп. И всё на полном серьёзе. Как на русском так и на инстрактаблс итп.
Теперь понимаю, почему один клиент из мед-обласдти уже 3 года гика ищет со знаниями железа, протоколов, сетей, серверов и ИИ ищет и найти не может. Оказывается все демо для спектрума пишут и звуковые карты из резисторов паяют.Pyhesty
31.08.2018 13:39+2уровень мудрости (даже не знаю, как назвать совокупность знаний, опыта, желания учиться и тп) специалиста, который может написать демо для такого железа значительно (намного-намного) выше, чем типичное представление о программисте — как администраторе локальной сети…
ps: вообще программистов, которые что-то понимаю в железе и могут написать что-то под устаревшую платформу или микропроцессор — немного…vheinitz
31.08.2018 14:09-1При чем тут ассемблер и системное администрирование? В такой манере можно аргументировать «Лучше уж ассемблер чем бухать»
Я еще застал последние Ассембли-партис. Такого «добра» столько написано, что уже не переплюнуть. Там ни чего нового нет. Мигать спрайтами и проигрывать мод-файлы — это было актуально в 80х-90х. До мп3 и Куда.
Ассемблер был актуален, когда на рынке было процессоров на пальцах перечесть а не тысячи как сейчас. Не успееш на ассемблере что-то написать, как он уже устарел.
Полезных заданий на С и сейчас хватает. Возьми какой-либо протокол или алгоритм разгони его до скорости света. Или сделай прибор на контроллере, который гарантированно 10 лет на одной батарейке работает (чем сейчас и занимаюсь:) ).IvUyr
31.08.2018 20:06Вы таки немного ошибаетесь, это сейчас процессоров мало — iNTEL, AMD (хотя их двоих можно в один свести, как бы оба x86_64), ARM, MIPS, AVR, pic да stm8.
А раньше были iNTEL, AMD, Zilog, Motorola (68k, 68010, 68020,… 68050), mos, st, ti, DEC PDP, cyrix, arm… Это только те, кого я за пару минут вспомнил.
u-235
31.08.2018 13:58уже 3 года гика ищет со знаниями железа, протоколов, сетей, серверов и ИИ ищет и найти не может
edbuwg
31.08.2018 14:05ЗП достойную просто не предлагают — вот и весь секрет многолетних «поисков»
vheinitz
31.08.2018 14:31Да нет, всё нормально с ЗП. Фирма просото на отшибе. Стоит в поле за три девять земель. Область деятельности мед. диагностика. Там по определению много платят.
Но фирма маленькая, а в округе фарма-гиганты. Постоянно к себе персонал сманивают. С ними не поконкурируеш.
Все приходившие информатики совершенно не в курсе что такое предметная область. Боятся почитать что-то про клетки, вирусы; боятся железа (не платин с ЦПУ а реального, с моторами, сенсорами, итп) или просто неопытные.
Все инженеры в софте мало разбираются. А нужна утка — которая всего понимножку может плавать, летать, ходить.
Один приходил из Гейделбергского университета, работал там в проекте по распознаванию рака по снимкам легких. Пытал я его, пытал. Спросил, назови одну из важных фич в векторе обучения вашей системы — говорит «возраст». Короче, скажи мне твой возраст и я с точностью 70% определю у тебя рак и без рентгена.
Error1024 Автор
31.08.2018 15:17Оказывается все демо для спектрума пишут и звуковые карты из резисторов паяют.
Кто «все»? — большенство разработчиков не слышали или слышать не хотят о демосцене.
dimitry78
31.08.2018 23:37Насчет демки круто и посильно! в разумное время достойное творение. сам не железячник, а познакомился с компами на msx yamaha
DolphinSoft
01.09.2018 18:00+2Тоже отнастольгировал тут недавно, достав запылившегося ученика. Решил отдать ему дань уважения, апнуть до 4Мб оперативы и поменять VDP с прошивкой, превратив в полноценный MSX2+.
А тем временем, тоже наваял пару зрелищь как раз под него:
1.AntonSazonov
01.09.2018 10:06А зачем нужно хранить массивы fake_points1 и normal_points? Это же просто степень двойки. Не прощще их генерировать?
Error1024 Автор
01.09.2018 12:42Из-за применения пакера дешевле оказалось хранить, чем генерировать.
AntonSazonov
01.09.2018 13:28А хранить один массив, но обращаться к нему по-разному (arr[i] и arr[7-i] соответственно) не пробовали? Возможно так можно было бы сэкономить ещё пару байт?
Error1024 Автор
01.09.2018 14:43+1Это только увеличит размер, тут вся фишка в том что процедура рисования пикселя одна на всю интро, без частных случаев.
tormozedison
Да очень даже ничего там звук.