Создание 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? Правильно, вывод спрайта 5x5 знакомест или 40x40 пикселей, для одного из эффектов (по иронии, в дальнейшем, для того чтобы влезть в 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 используется два байта.

    Спрайты

    Выкинул недоделанный кусок части со спрайтами, поняв что не уложусь в 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

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

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

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



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

    Share post

    Comments 56

      +2
      Да очень даже ничего там звук.
        +4
        Очень понравилась фотка, где процессор «магнитофона» с программой раз в 100-200 быстрее самого компьютера. ;-)

        P.S. Ну и по оперативке «слегка» выигрывает, да.
          0
          Только не смейтесь: одно время были популярны плееры, где процессом «рулило» ядро, совместимое с Z80 и работающее на 20 мегагерцах.
            0
            Кассетные плееры?
              0
              Нет, MP3, начало двухтысячных.
              Кассетники с микропроцессорным управлением, впрочем, тоже были, но там четырёхбитных ядер хватало.
              Вильма-100-стерео особняком, там 16-битный.
              0
              Ну а почему нет? Если бы мне сейчас понадобилось сделать электронику/управление на бобинник, я бы тоже поставил stm32, который быстрее Z-80 в 10 и более раз ;-)
              +2
              Не могу не отметить также оригинальный способ передачи программ — посредством голосовых сообщений в Telegram.
                0
                Некасательно этой фотки, на iPad/iPhone действительно легче передать через телеграмм/ё-мыло/етц, чем городить огород с айтюнсами. Хотя и ПК и планшет/телефон в одной комнате.
                  0
                  Наиболее рациональный способ с точки зрения занимаемого файлом места — приложение tapDancer. Вместо перекодировки tap-файла в wav — проигрывание tap на лету.
                    0
                    Для iPad/iPhone? В AppStore не нашёл, если это касательно моего комментария. А если вообще, то не знаю, андроид-девайса нет.
                      0
                      Его не так давно выпилили из appstore, к сожалению.
              • UFO just landed and posted this here
                  0
                  ZX — 3,5 МГц 8 бит. 2 такта сложение.

                  4 такта
                  • UFO just landed and posted this here
                +1
                =) мне очень понравилась статья) подробненько)
                отдельное спасибо за ссылку на пакеры!
                ps: демо очень достойное своего объёма )
                «музон» тем более =)
                pss: ностальджи )
                  +3

                  load ""
                  пшшшш...

                    +6
                    Сначала «ииииииии», потом «пшик», потом опять «иииитиии», и только потом «пшшшш».
                      0
                      А это точно не 33600, а программа? ;-)
                        0
                        Точно. Тон для настройки на скорость магнитофона, имя файла, снова тон, сам файл.
                        • UFO just landed and posted this here
                            0
                            О, спасибо, как-то пропустил.
                      +2
                      Вообще экран спекки можно представить и как линейное пространство 2048х24 пикселей. Ну или 256х3 знакоместа (которые в свою очередь 8х8 пикселей). Интересно, но этот DOWN_HL я почти до сих пор помню наизусть, т.к. это важнейшая часть рисования спрайтов, а особенно «указателя мышки» в любой программке для спекки. Ну и вторая важнейшая часть это от 65000 до 72000 тактов за которые надо успеть сделать всё… Как же я тогда мечтал о 7 или еще лучше о 14 МГц…
                        +2
                        «Ох, индексные регистры, какие же они удобные, но какие меееедленные, буквально выжирают такты. Пришлось выкинуть их использование из кучи мест.»
                        PUSH POP only!
                          +3
                          а частицы в начале демки крутятся по часовой стрелке или наоборот? :)
                            0
                            Слева направо :)
                              +3
                              Как-то так?
                            +4
                            Куда больше меня поразила дема на АОНе.
                              +1
                              Ну формально, почти все АОНы были на Z80. А Z80 — это весьма крутой процессор, для своих 8 бит, а если еще и на 3.5 МГц…
                                0
                                Потом перевели на 8051, блоки питания резко такими холодными стали.
                              0
                              если вывести звук не на пьезо, а на динамическую головку, то звук будет мягче.
                                +2
                                Да, было время! В каждом дворе были специалисты способные писать такие демо. Сам помню принимал участие в соревновании местных умов по созданию демо в 100 байт. Жаль что у большей части нынешней аудитории ничего кроме недоумения данная статья не вызовет. Не знал что СС еще проводится. Спасибо за позитив!
                                  0
                                  Ну, в принципе, одних только 256 байтных интр в этом году на CC было аж 27 штук для разных платформ. Так что про «были» — это как-то неактуально звучит :)
                                • UFO just landed and posted this here
                                    +5
                                    уровень мудрости (даже не знаю, как назвать совокупность знаний, опыта, желания учиться и тп) специалиста, который может написать демо для такого железа значительно (намного-намного) выше, чем типичное представление о программисте — как администраторе локальной сети…
                                    ps: вообще программистов, которые что-то понимаю в железе и могут написать что-то под устаревшую платформу или микропроцессор — немного…
                                    • UFO just landed and posted this here
                                        +1

                                        Вы таки немного ошибаетесь, это сейчас процессоров мало — iNTEL, AMD (хотя их двоих можно в один свести, как бы оба x86_64), ARM, MIPS, AVR, pic да stm8.
                                        А раньше были iNTEL, AMD, Zilog, Motorola (68k, 68010, 68020,… 68050), mos, st, ti, DEC PDP, cyrix, arm… Это только те, кого я за пару минут вспомнил.

                                          0
                                          вы забыли миландр, элвис, эльбрус (байкал?)… ;)
                                            0
                                            PowerPC и собсно сам Power наше всё!
                                              0

                                              "Наши" разработки я намеренно не указал ввиду трудности получения их обычными покупателями (Эльбрус, Байкал) и тем, что внутрях, в общем-то, те же arm да mips..

                                            • UFO just landed and posted this here
                                        • UFO just landed and posted this here
                                            +4
                                            ЗП достойную просто не предлагают — вот и весь секрет многолетних «поисков»
                                            • UFO just landed and posted this here
                                              • UFO just landed and posted this here
                                              +3
                                              Оказывается все демо для спектрума пишут и звуковые карты из резисторов паяют.

                                              Кто «все»? — большенство разработчиков не слышали или слышать не хотят о демосцене.
                                              • UFO just landed and posted this here
                                                  +2
                                                  Сборку ещё достать надо, а резисторы — пожалуйста — в любом радиомагазине.
                                                    0
                                                    «как доставали резисторы тоже»
                                                    kinocompass.ru/pic/1/1973/46/78/201205090730000_pic.jpg — некоторые, для «усилка» (вроде) «вега», мотал нихромом, и тестером по проволочке выставлял нужное значение, потом веселуха припаять к мегаомнику, и так для 2х каналов, в выходном каскаде, там что-то мощное требовалось
                                                +1
                                                Насчет демки круто и посильно! в разумное время достойное творение. сам не железячник, а познакомился с компами на msx yamaha
                                                  +3
                                                  Тоже отнастольгировал тут недавно, достав запылившегося ученика. Решил отдать ему дань уважения, апнуть до 4Мб оперативы и поменять VDP с прошивкой, превратив в полноценный MSX2+.
                                                  А тем временем, тоже наваял пару зрелищь как раз под него:
                                                  1.

                                                  2.

                                                    0
                                                    К слову, размер первого кода — чистые 256 байт (в ассемблере), второго — чуть больше.
                                                  0

                                                  А зачем нужно хранить массивы fake_points1 и normal_points? Это же просто степень двойки. Не прощще их генерировать?

                                                    0
                                                    Из-за применения пакера дешевле оказалось хранить, чем генерировать.
                                                      0
                                                      А хранить один массив, но обращаться к нему по-разному (arr[i] и arr[7-i] соответственно) не пробовали? Возможно так можно было бы сэкономить ещё пару байт?
                                                        +2
                                                        Это только увеличит размер, тут вся фишка в том что процедура рисования пикселя одна на всю интро, без частных случаев.
                                                    0
                                                    EmuZWin у меня под Windows 10 работал вполне себе норм. Я под ним в прошлом году расковыривал Highway Encounter, чтобы сделать порт под УКНЦ: github.com/nzeemin/uknc-highwayencounter

                                                    Only users with full accounts can post comments. Log in, please.