Среди энтузиастов компьютерного творчества, на так называемой «демосцене», есть старая добрая традиция посвящать небольшие программки-демонстрации различным событиям. По сути, это музыкально-визуальные открытки, показывающие какую-нибудь картинку и визуальный эффект, а также играющие мелодию.
Например, «гифт» — поздравление с днём рождения. Или же более актуальное прямо сейчас: новогодние поздравления. Признаться, я и сам не раз делал подобные вещи. Например, в предыдущий раз я пожелал всем незабываемого нового 2020 года. И так как дата публикации очередной статьи пришлась на конец декабря, это прекрасный повод снова поздравить читателей таким образом, а заодно и рассказать о том, как это сделано.
▍ Платформа и инструменты
Так как времени на проект было мало, я решил выбрать довольно самую что ни на есть классическую платформу: игровую консоль Atari 2600, вышедшую на рынок в конце 1977 года. Это весьма крепкий орешек, с очень скромными визуальными и звуковыми способностями и очень хитроумным программированием.
Безусловно, это усложняет техническую часть, но с другой стороны, ограничения стимулируют творчество, а небольшое количество контента, пусть и непростого в создании, всё же ограничивает требуемое количество времени и сил: чем меньше может быть сделано, тем меньше нужно будет сделать. И, конечно, я выбирал из уже более-менее знакомых мне платформ, чтобы не тратить время на освоение и подготовку инструментов. К тому же это довольно популярная платформа, и за годы существования коммьюнити накопило немало готовых решений для типовых проблем, что даёт неплохую базу для копи-паста.
Также я поставил дополнительную цель: проект мог бы заодно послужить примером программирования для желающих освоить платформу. По этой причине я решил и не переусложнять эффекты в сцене. а также разрабатывать проект в браузерной IDE 8bitworkshop, состоящей из ассемблера и эмулятора.
8bitworkshop
Я довольно скептически отношусь к браузерным инструментам в целом и для более серьёзных проектов я, вероятно, выбрал бы оффлайн-решение — для комфортного редактирования, автоматизации сборки, и прочих удобств. Поэтому впервые услышав про эту IDE много лет назад, не проявил к ней интереса. Но со временем случилось попробовать: я делал в ней 256-байтное интро для той же платформы, находясь вдали от рабочего компьютера. И оказалось, что она довольно неплоха.
В 8bitworkshop поддерживается множество 8-битных платформ, включая Atari 2600, ColecoVision, NES, PC Engine, и другие. Есть даже некоторые игровые автоматы. Для большинства платформ имеются необходимые базовые примеры, разнообразные подпрограммы и макросы, позволяющие быстро решить типовые начальные задачи. Решающее качество этой среды разработки — доступность, а именно бесплатность и отсутствие какой-либо регистрации. Просто открыл страницу и пишешь свой код. Монетизирует автор своё творение изданием сопутствующих книг, которые я не видел, и сказать про них ничего не могу.
▍ Музыка
Первая крупная, просто огромная проблема, которой нужно было заняться — музыка. Это очень важная составляющая, задающая настроение и динамику, общее впечатление от сцены. Если бы не удалось найти решения для этого вопроса, не было бы смысла и начинать сам проект для этой платформы.
Проблема в том, что музыкальные возможности Atari 2600 крайне ограничены, а точнее, они просто не предусмотрены. По этой причине платформа не богата на музыкальные кросс-редакторы. Есть довольно свежий Furnace, очень интересный проект с поддержкой огромного количества звуковых чипов. Он вполне удобный и понятный, но в нём пока не поддерживается экспорт данных для Atari 2600.
Furnace Tracker
Также есть TIA Tracker, уже относительно старый (2016-17). Он довольно запутанный и неудобный, но у него есть функция экспорта и готовый проигрыватель для целевой платформы. Я неоднократно пытался его освоить, но каждый раз находил занятие повеселее — например, посмотреть, как сохнет краска. На этот же раз других вариантов особо не было — случился хороший повод наконец-таки сделать это.
Моя проблема с TIA Tracker заключалась в том, что его интерфейс довольно сильно отличается от большинства трекеров, и в нём нет некоторых элементарных функций, таких, как копирование и вставка нотного текста. Но главное — он ведёт себя нестабильно. Обычно в трекерах каждый параметр имеет совершенно определённое влияние на звук. Здесь же то огибающие громкости или питча как будто перестают работать, то один и тот же инструмент звучит то коротко, то длинно. Поначалу было непонятно, делаю ли я что-то не так, или это особенности самого трекера. Как выяснилось позже, экспортированный трек звучит ровно и чётко, точно как и ожидается, а значит, есть какие-то проблемы именно в проигрывателе самого редактора.
В любом случае при всех (весьма значительных) неудобствах, ругать этот инструмент было бы несправедливо — он оказался огромным подспорьем, сильно сэкономившим время и силы. Вместе с трекером поставляется несколько готовых примеров музыки, а значит, я мог начать сразу с проверки возможности экспорта и встраивания музыки в мой будущий код.
TIA Tracker
Техническая часть в виде адаптации кода для сборки в 8bitworkshop прошла просто и быстро. Трекер умеет выдавать сразу и трек, и код плеера в виде набора asm-файлов с синтаксисом ассемблера DASM, специфика которого заключается в командах препроцессора и особенностях написания некоторых режимов адресации в мнемониках. Как оказалось, IDE знает этот синтаксис, поэтому не потребовалось ничего адаптировать и переименовывать руками.
Я просто экспортировал трек вместе с плеером, создал локальные файлы с такими же именами в 8bitworkshop, и скопировал в них содержимое моих файлов. При выборе основного файла всё сразу же само заиграло. Скачав получившийся ROM-файл, и увидел, что весьма крутой трек «glafouk — Miniblast» после экспорта занимает практически ровно один килобайт. Это дало понимание, что памяти для задуманной сцены мне должно хватить.
Следующая проблема с музыкой — собственно освоение интерфейса и сочинение хоть сколько-то удобоваримо звучащей композиции. Почему же это так сложно?
Если вы знакомы с играми для Atari 2600, то наверняка заметили, что музыка в них почти не встречается. На это, конечно, прежде всего влиял очень небольшой объём памяти картриджей. Если же вы интересовались и демосценой, где проблема памяти стоит не так остро, то, скорее всего, заметили, что за редкими исключениями музыка хоть и есть, но звучит очень нестройно, просто-таки за гранью фола.
Звуковые возможности Atari 2600 одновременно довольно богаты и бедны. Полифония всего два канала — только два звука могут играть одновременно. У этих каналов даже есть управление громкостью, что позволяет без проблем делать мои любимые эхо-эффекты. И даже есть из 11 форм сигнала — разные вариации тона и шума. Очень неплохой набор возможностей для классных звуковых эффектов в играх.
Подвох же в том, что звуковой синтезатор может формировать всего лишь 32 высоты тона, и к тому же каждая форма сигнала имеет свой набор высот тона. Только некоторые из них хоть сколько-то близки к музыкальным нотам, а в целом же они звучат как получится, полностью вразнобой. Говоря иначе, попасть в ноты крайне сложно, а в некоторые и просто невозможно.
В таких условиях можно писать либо очень специфические, ограниченные по гармонии мелодии, либо пожертвовать чистотой строя (не чуть-чуть, а очень конкретно пожертвовать), и сочинять атональную музыку. Также возможно компромиссное, и видимо самое оптимальное же решение: сохранить по возможности соотношение высот, но сильно изменить общий строй, например, вместо традиционных 440 герц для ноты Ля взять 585 Гц. Более подробно обо всём этом можно узнать из лекции собственно автора TIA Tracker.
Учитывая вышесказанное, с сочинением музыки всё оказалось очень непросто: дикий нестрой и ограниченное количество нот, резкие звуки. Попробуй тут создать праздничное настроение, а у нас тут всё-таки Новый год, а не кислотная вечеринка.
Провозившись целый день, поэкспериментировав с разными настройками, я смог сконструировать что-то, звучащее достаточно близко к мажорному стандарту, не ужасающе расстроенное, длительностью в 30 секунд и размером меньше килобайта. Этот вариант-минимум показал, что задача в принципе решаема, дальше нужно только немного удлинить и детализировать композицию.
Пора было переходить к главному блюду, к ещё более сложной задаче дизайна и реализации визуальной части.
▍ Дизайн сцены
Будучи более-менее близко знакомым с архитектурой Atari 2600 — очень много лет назад с ограниченным успехом писал её эмулятор — я изначально представлял себе, что я хочу получить на экране, и каким примерно образом буду этого добиваться.
Настало время как-то визуализировать это.
Первый скетч. Бумага, чернила
Как известно, у Atari 2600 нет привычного пиксельного разрешения, так как нет растрового буфера. Тем не менее размеры формируемого ей растра достаточно традиционные: 160 пикселей в ширину и около 192 в высоту (можно чуть больше). Это даёт 40-пиксельный регистр фона (20-пиксельный, с перезагрузкой в середине кадра). В этом прослеживаются параметры типичного текстового разрешения 40 на 25. По удивительному совпадению, как раз примерно столько клеток помещается на листе обычной тетрадки в клеточку, на котором и можно спланировать нужную сцену. Что я и сделал.
Далее я также условно набросал сцену в графическом редакторе:
Вторая версия первого скетча. Экран, пиксели
По моей задумке сцена должна состоять из пяти элементов:
- Логотип компании Atari на фоне, по которому проходит цветовой градиент зелёных оттенков. Он символизирует ёлку. Нормальную красивую ёлку нарисовать в таком разрешении затруднительно, поэтому я решил визуализировать её таким образом.
- Статичные спрайтовые элементы: звезда и шарики, также переливающиеся цветами. Призваны придать логотипу больше ёлочности.
- Эффект снега — медленно падающие точки, на каждой строке случайным образом перемещающиеся влево-вправо. Их цвет не меняется.
- Анимация вращающейся вокруг своей оси гирлянды, состоящей из крупных квадратных пикселей. Гирлянда переливается всеми цветами.
- Цифры 2024 в нижней части экрана, на слое фона, каждая из которых отображается своим цветом и также переливается оттенками.
Пробная анимация цифр, нарисованная вручную
Для проверки того, как может выглядеть раскрашивание части с цифрами в разные цвета тем способом, который я задумал, я сначала вручную нарисовал анимацию этого эффекта в редакторе пиксельной графики GraphicsGale. Было беспокойство, что это будет смотреться плохо, с сильным мерцанием, но на деле оказалось значительно лучше ожиданий. Я проверил базовый вариант с простым чередованием строк, а также с перехлёстом, дающим более плотное заполнение изображения, и убедился, что эта идея рабочая.
▍ Графика древности
Аппаратные средства Atari 2600 для отображения графики крайне примитивны, ведь они создавались в 1977 году и были рассчитаны на сюжеты в самых продвинутых на то время играх типа Pong и Tank. И даже для отображения столь простых сцен центральному процессору приходится постоянно менять параметры отображения, что занимает почти всё его время.
Вся графика формируется на лету, прямо во время движения луча развёртки по экрану. Она состоит всего из шести монохромных элементов: задника шириной в 40 пикселей, двух спрайтов шириной 8 пикселей, и трёх дополнительных пикселей, выполняющих роль выстрелов в игре Tank и мяча для игры в Pong. Этим элементам можно назначить цвет из большой палитры в 128 цветов, причём у мяча и у фона, а также у каждого игрока и его выстрела цвет общий, то есть раскрашивается изображение на экране всего четырьмя слотами цвета.
Тайминги растра на Atari 2600
Чтобы отобразить изображение заднего фона, нужно каждую строку или несколько строк обновлять регистры его графики. Хотя на экране он имеет ширину в 40 пикселей, в аппаратуре его графика представлена всего 20-ю пикселями, и по умолчанию они повторяются в строке дважды, с опцией зеркального отражения (симметрии). Чтобы получить изображение в полную ширину экрана, нужно перезагружать регистры графики задника в середине каждой строки.
Я не упомянул высоту отображаемых объектов, потому что такого параметра в аппаратуре просто не предусмотрено. Чтобы сформировать вертикальное содержание задника или спрайтов, или даже придать объекту мяча высоту или вертикальную координату, процессор должен обновлять регистры соответствующих объектов по мере прохода луча развёртки по строкам экрана.
Для отображения в середине экрана спрайта 8 на 8 пикселей нужно в начале экрана обнулить регистр графики спрайта, дождаться нужной строки, загрузить графику первой строчки пикселей спрайта в регистр, подождать следующей строки, загрузить графику второй строчки и так далее, а после восьмой строки снова обнулить графику.
Для позиционирования подвижных объектов по горизонтали нельзя просто взять и установить их координату, как в более поздних системах. Объекты начинают отображаться ровно в том месте строки, в каком находился луч во время записи регистра позиции. Так как на один такт процессора приходится три пикселя строки растра, позиционировать положение объектов с точностью до пикселя одним только точным таймингом записи невозможно. Для решения этой проблемы есть возможность смещения объектов относительно места записи на несколько пикселей влево или вправо, но применение этой возможности имеет особые, весьма специфические ограничения и артефакты.
Всё это делает создание на экране даже самых простейших сцен крайне нетривиальной задачей. Если вас печалит, что программирование умирает, что всем теперь правят фреймворки, и никто не оптимизирует код — рекомендую попробовать. Бодрит как мороз.
▍ Код
Без дополнительных аппаратных ухищрений Atari 2600 поддерживает ПЗУ объёмом до 4096 байт, а также имеет встроенное ОЗУ размером 128 байт. В такое количество ОЗУ могли бы поместиться слова этого абзаца от его начала до слова «размером» включительно. Во времена коммерческой жизни платформы эта память отводилась на целую игру, а на некоторые даже и вдвое или вчетверо меньше. В эти байты должен был помещаться код, графика, музыка и прочие данные. Я же для упрощения задачи решил использовать все эти байты для своей одной-единственной сцены — огромное расточительство по меркам прошлого, но вполне приемлемое для демосцены и не принципиальное для моих целей.
Код любой визуальной сцены для Atari 2600 обычно реализуется в формате так называемого «кернела», сиречь ядра. Кернел делится на строки растра, для каждой из которых спланированы определённые задачи — на одну строку экрана приходится всего 73 такта процессора, и за это время никак не получится выполнить много действий. Приходится решать, в какой строке нужно обновлять регистры фона, а в какой позиционировать спрайты. Обычно эти задачи повторяются, и таким образом, кернел часто делит изображение на группы по четыре или две строки, повторяя в них последовательность задач.
Игровая логика обычно выполняется в оставшееся время, в области гашения экрана сверху и снизу относительно растра. Если логика не успевает выполниться за кадр, её разбивают на несколько кадров. Иногда часть логики размещают и в видимых строках растра, например, через одну строку. Часто выполнение кода логики занимает переменное время, тогда на помощь приходит аппаратный таймер, чтобы по выполнению этого кода дождаться нужной строки.
Классическая процедура позиционирования объекта с точностью до пикселя
В моей сцене предполагается три объекта, требующих горизонтального позиционирования, а как было сказано выше, для этого нужно выдерживать задержку в нужное количество тактов. То есть за одну строку в общем случае можно задать позицию одного объекта. Также мне нужно перезагружать регистр фона, что занимает некоторое время. Итого по предварительной оценке моя сцена может быть реализована в группах по четыре строки.
Так как каждый эффект в сцене имеет свои особенности, я решил реализовать и отлаживать их по одному, по отдельности, и потом как-то объединять в одну общую сцену.
▍Цифры
Начать я решил с конца, а именно с вывода цифр в нижней части экрана.
Главный трюк этого эффекта в изменении данных регистров фона в каждой строке, причём дважды, так как надпись асимметричная. При этом нельзя расходовать слишком много памяти. четыре кадра всей графики для чередования цифр в разных строках заняли бы 720 байт, это довольно много. Но и тактов в строке не слишком много, чтобы честно складывать прочитанные из памяти байты изображения и масок.
Цифры. Начало
Пришлось пойти на типичные для таких платформ и случаев крайние меры: байты графики грузятся непосредственно в опкоде, маска читается из памяти по указателю, данные маски продублированы таким образом, чтобы при переборе строк не приходилось следить за переполнением счётчика кадров, тратя на это дополнительные такты.
В результате оптимизаций удалось получить задуманный эффект без компромиссов, но ценой этого стали совершенно не человеко-читаемые данные графики и масок, так как их байты пришлось перетасовать в наиболее эффективном для выборки порядке.
▍ Осадки
Следующим на очереди был снег. Эта задача оказалась весьма сложной, так как требовалось горизонтально позиционировать спрайты, а точнее, объект выстрела, с точностью до пикселя, и делать это много раз за проход луча по кадру, чтобы из одного объекта получить множество снежинок. Я также попытался сделать их высотой в один пиксель.
Идея заключалась в том, чтобы в начале каждой группы из четырёх строк устанавливать горизонтальную позицию объекта, а для перемещения однопиксельной точки внутри группы последовательно включать и выключать их на нужных строках. Таким образом, горизонтальная позиция будет меняться раз в четыре строки, но вертикальное падение будет плавным, попиксельным.
Первый снег
С этой задумкой возникла непредвиденная сложность, и на попытки понять, в чём кроется суть проблемы, ушло немало времени. Я не знал, что эффект записи в регистр установки горизонтальной позиции применяется только через 160 пикселей (должен переполниться счётчик), из-за чего в некоторых позициях на экране, в которых набегают лишние такты, объект не отображается до следующей строки. Этот эффект редко проявляется в обычных ситуациях, когда отображение объекта начинается на одну-две строки ниже от установки позиции, и между объектами по вертикали есть пустые строки.
На поиск корректного решения его уже не оставалось, и я подумал: случайное пропадание спрайтов при движении выглядит как мерцание, а это же снежинки, они должны блестеть. Иначе говоря, и так сойдёт! Я в любом случае планировал добавить какое-либо мерцание, например, цветом. Вот оно и самодобавилось.
▍ Освещение
Очередной достаточно сложный элемент сцены, гирлянда. Но сложности в нём иного рода — как сгенерировать нужную анимацию и как уложиться в необходимый объём памяти.
Я быстро переделал кернел снега в вывод спрайтов с попиксельным горизонтальным позиционированием, с высотой в три пикселя и пустой строкой между ними — это исключило проблему пропадания спрайтов.
Прототип анимации вращения гирлянды
Теперь нужно было сгенерировать хоть что-то и оценить, как оно смотрится и сколько кадров анимации нужно. Для этого я использовал свой традиционный подход с написанием простейшего прототипа на C/SDL. Поигравшись с параметрами и получив интересный визуальный результат, я понял, как элементы в эффект мне нужны: приглушение яркости спрайтов на «заднем» плане, когда они условно заходят за ёлку, а также постепенное изменение ширины огоньков гирлянды от верха к низу, это делает эффект визуально более интересным.
Гирлянда в процессе реализации, почти готово
Осталось только воплотить задуманное в коде для Atari 2600. Имея конкретную задачу, прототип, а также отлаженную заготовку кода в виде предыдущей сцены, это не заняло много сил. На данном этапе я сделал гирлянду высотой 32 огонька, с 64 кадрами анимации. Это 2048 байт данных, что, вероятно, станет проблемой при сборке сцены в единое целое. Сжать эти данные затруднительно, так как требуется доступ к ним во время прохода луча по растру, но всегда можно уменьшить количество огоньков и/или кадров анимации — прототип показал, что это вполне приемлемые варианты.
▍ Дерево
Перед тем, как приступать к реализации элемента, я пересмотрел изначальный дизайн. Теперь его изображение стало больше похоже на дерево, и только общими линиями напоминало логотип Atari. Также я задумал сделать ей коричневый корень, чтобы было очевиднее, что это за такая кракозябра.
Уточнённый дизайн я нарисовал в playfieldpal, крайне примитивном браузерном редакторе графики фона. Как и задумывалось, это полностью симметричный объект, так его проще отображать в моём относительно сложном кернеле. В процессе стало понятно, что крайние пиксели ёлки мне не нужны, значит нужно хранить на один столбец байт меньше, и обновлять только два регистра фона каждые четыре строки растра.
Ёлки-палки
Эффект цветового градиента на ёлке, однако, по задумке должен работать каждую строку. Для оптимальной реализации, чтобы уместиться на строках с позиционированием спрайтов, его нужно бы сделать табличкой в 256 байт, но уже было ясно, что столько места в ПЗУ вряд ли найдётся. Поэтому я сделал более медленный вариант с 64-байтовой табличкой, и оставил решение проблемы памяти и скорости на попозже, на момент будущего объединения кода элементов сцены.
▍ Украшения
С шарами я решил вопрос по-современному. Дело в том, что для раскрашивания их в отдельные цвета мне нужно было бы как-то разделить во времени использование регистров цвета: цвет снега плюс цвет шаров, или же цвет гирлянды плюс цвет шаров. Это можно было бы сделать чередованием строк графики этих элементов, но тогда шары получились бы полосатыми, и я посчитал, что это испортит впечатление целостности сцены. Поэтому шары я решил отменить.
Без звезды же обойтись было никак нельзя, благо, её реализация относительно простая. На строки, в которых она расположена, не заходят ни фон, ни гирлянда, только эффект снега, выводимый объектом выстрела. Таким образом, в этих строках доступно одновременно два спрайта, и из них за счёт использования аппаратного отражения по горизонтали можно составить симметричный спрайт в 16 пикселей шириной, чего вполне достаточно для этого элемента. Высоту я также выбрал в 16 строк. Таким образом, для хранения этого изображения достаточно 16 байт ПЗУ.
Звезда и упрощённый макет ёлки для совмещения позиций
Из-за того, что один из спрайтов имеет общий с объектом выстрела цвет, звезда не имеет цветовых эффектов и всегда отображается цветом снега — белым. Если бы я покрасил звезду в жёлтый, в сцене начал бы идти жёлтый снег.
В плане кода, это отдельная вариация кернела, где каждую строку только загружается графика. Спрайты позиционируются заранее, однократно, в первых двух строках растра, просто подобранным по времени выполнения кодом и смещением для HMOVE.
▍ Всё в одном
И конечно, самая сложная часть — соединение всех элементов с их хитросплетениями кода и тонко настроенными таймингами в один большой клубок.
Я дописал музыкальный трек до минутной длины, стараясь по максимуму использовать одинаковые паттерны, чтобы сэкономить память. Пришлось побороться и с отсутствием в TIA Tracker автоматической оптимизации повторяющихся паттернов, планируя их заранее, и с интерфейсом, не позволяющим редактировать список позиций сколько-нибудь удобным образом. Так или иначе, что-то получилось, и вместе с кодом плеера заняло 1460 байт. Это дало понимание, что саму сцену надо как-то утрамбовать в 2.5 килобайта.
Переработал код вывода цифр, так как в изначальном варианте этот элемент занимал около двух килобайт ПЗУ. Помимо самого кода переработал таблицы с масками для цветовых полосок, реорганизовал эти данные таким образом, чтобы каждая из таблиц включала часть другой.
План совмещения такого количества элементов в 4-строчном кернеле оказался слишком амбициозным, и после ряда экспериментов я решил чуть понизить планку, перейдя к 6-строчному кернелу. Даже такое упрощение оказалась не слишком простым в реализации, но всё же оно удалось.
Процесс соединения элементов
Проблемы возникли и с объёмом памяти. В результате пришлось пойти на некоторые компромиссы: в области звезды и корня дерева не работает эффект снега, чтобы избежать дублирования кода, а количество кадров анимации вращения гирлянды было уменьшено с 64 до 60. Зато добавил для звезды простое переливание градиентами всех цветов. На этом этапе свободного места в ПЗУ осталось пара десятков байт.
Мне хотелось немного дополнить интро динамикой, хотя бы минимальной синхронизацией действия с музыкой, а по возможности и текстовыми сообщениями. Поэтому далее я попытался оптимизировать код. Его чистка и небольшие улучшения кода выиграли ещё несколько десятков байт, что позволило добавить мигание цифр и звёзды на макушке в такт ударным в музыке.
Я также попытался выиграть немного памяти за счёт оптимизации данных музыки. Для этого я написал парсер модуля TIA Tracker (внутри он имеет формат JSON), разбивающий паттерны на более короткие. К сожалению, оказалось, что я изначально угадал с их оптимальной длиной — хотя более короткие паттерны дают больше совпадений, увеличивающаяся длина списка позиции превышает этот выигрыш. Немного изменив некоторые паттерны для увеличения количества совпадений удалось уменьшить трек на целых 37 байт — ценой нескольких часов работы. Но даже и этот выигрыш не удалось применить, так как длина списка позиций после такой оптимизации оказалась слишком большой для экспорта в трекере.
Исходная графика надписей
В таком виде интро всё ещё ощущалось незавершённым. Я набросал простенькую сценку с четырьмя текстовыми сообщениями и классическим градиентом (такой делают вместо Hello World на Atari 2600). Для неё было нужно около двухсот байт. Но с ней интро определённо смотрелось бы интереснее, и я продолжил оптимизации. Удалось выиграть ещё немного, плюс я отказался от ещё нескольких кадров в анимации гирлянды.
Финальный конфуз и новогодний сюрприз с проектом приключился уже во время записи итогового видео в эмуляторе MAME. Выяснилось, что снежинки в нём, а также в Stella, выглядят совершенно иначе, чем в браузерном эмуляторе. Нечеловеческим усилием воли я применил заклинание «да ну и ладно», и таким образом интро наконец-то обрело свою финальную форму.
Распределение памяти получилось следующим:
- Музыка с проигрывателем: 1460 байт
- Анимация гирлянды: 1210 байт
- Графика ёлки: 25 байт
- Графика звёзды: 16 байт
- Маски для цифр: 32 байта
- Текст поздравления: 120 байт
- Код и прочие данные: 1231 байт
- Переменные в ОЗУ: 71 байт
- Свободно в ПЗУ: 2 байта
Скачать исходный код программы и её отдельных элементов можно здесь. Можно загрузить и поэкспериментировать с ними непосредственно в 8bitworkshop.
▍ Заключение
Вот так, без помощи нехитрых приспособлений, можно прикоснуться к технологиям далёкого прошлого, показав на игровой консоли около-полувековой давности простенькую новогоднюю сценку, а заодно и оценить титанический труд игровых разработчиков той эпохи, в которую всё только начиналось.
С наступающим Новым годом!
Помоги спутнику бороться с космическим мусором в нашей новой игре! ?