Когда ты много лет руководишь командами, легко забыть, каково это - просто сесть и закодить что-то самому.
Эта статья о том, как я сделал фэнтези-консоль под DOS, и снова влюбился в программирование.
Введение
Первый компьютер появился у меня в самом раннем детстве. Он включался долго, шумел, как старый пылесос, и запускал DOS-версию "Поля чудес", Scorched Earth, Doom и десятки странных игрушек с дискет (а потом и с дисков типа “200 лучших игр всех жанров”)

Потом пришёл интернет, рецензии и коллекционные карты ag.ru, журналы про компьютерные игры с постерами, игра “Full Throttle”, после которой я решил, что хочу делать игры, и рассказы Дани Шеповалова в журнале Хакер, после которых я в 10 классе устроился тестировать шутер “Невский Титбит” (кто знает - тот знает :). Как вы поняли, игры меня окончательно захватили.
В процессе я немного учился программировать: мы с отцом делали проекты на Delphi и Flash для слабослышащих детей (IBM SpeechViewer в то время стоил совершенно негуманных денег и был недоступен для наших школ), но в отличие от многих, программировать ради удовольствия я по-настоящему полюбил не с детства.
По-настоящему меня торкнуло в 2006-м, когда ВКонтакте открыл API. Это был портал в другой мир. Потому что теперь можно было за ночь сделать проект, которым будут пользоваться сотни тысяч людей и понеслось: сервисы, игры, клиент, бэкенд…

И вот на дворе 2022 год.
Роль программиста давно сменилась на менеджерскую.
Команды, метрики, воронки, фичи, баги, ответственность, итерации, стратегические цели.
Мы с командой только что запустили наш долгожданный проект. Пережили ковид. Перешли на удалёнку.
Но, по моему опыту, в жизни каждого менеджера рано или поздно наступает переломный момент.
В голову закрадываются мысли: "Я собираю команду, задаю цели, подбадриваю, подталкиваю, поддерживаю...", "Но в итоге - все делает моя команда.", "А что делаю я?", "В чём продукт моего труда?"
Если ты когда-то любил делать что-то руками - кодить, рисовать, строить - то в какой-то момент всё внутри тебя начинает хотеть этого снова.
Но…
После лет в менеджменте, другого ритма, другой ответственности - вернуться к творчеству своими руками бывает тяжело, почти невозможно.
Но тут вышел ChatGPT и изменил все. Немного поэкспериментировав с ним я понял, что снова могу кодить и создавать.
Спецификация
За прошедшие годы накопилось много мыслей:
Я прочитал Crafting Interpreters Боба Найстрома - и загорелся идеей плотнее поработать с интерпретаторами и виртуальными машинами.
Выход GB Studio заставил меня впервые серьёзно попробовать pixel-art. И оказалось, что в в таких рамках я реально могу рисовать!
PICO-8 и TIC-80 открыли глаза на то, как кайфово делать игры в жестко очерченных рамках: маленький экран, ограниченный код и просто офигенные игры.
И всё это начало складываться в одну идею: а что если вместо инструмента, “эмулирующего” несуществующее старое железо, сделать инструмент, фэнтези консоль, работающую на реальном старом железе под DOS. Своего рода возможность немного “сбежать” в прошлое, взять свой старенький DOS ноутбук и попрограммировать по старинке.

Ну и в целом это был некоторый вызов себе - смогу ли я воплотить эту идею сам, без команды.
Итак, внутри должны быть
псевдо-операционная система с терминалом
редактор кода
редактор графики
редактор карт
редактор звуков и музыки
Вооружившись ChatGPT я взялся за дело…
Архитектура
Чтобы обеспечить максимальную переносимость, я решил подойти к архитектуре таким образом:
backend - код на С, эмулирующий аппаратное обеспечение несуществующей 8-битной консоли и дающий API для интерпретируемого ЯП
adapter - по-сути связка с платформой, реализующей вывод звука, график работу с вводом и другие необходимые платформоспецифичные функции
frontend - интерпретируемый код, реализующий всю “операционную систему” и программы
Благодаря этому подходу после отладки всего функционала на SDL адаптере реализация DOS версии прошла практически безболезненно.
В роли стека для DOS выступили:
DJGPP (порт GCC для DOS), последняя версия вышла в 2015 году
Allegro(в качестве адаптера на замену SDL) - последняя версия 4.2 с поддержкой DOS вышла в 2007 году
DOS по-умолчанию - 16-битная ОС, но DJGPP запускает программы в 32-битном защищённом режиме через DPMI (DOS Protected Mode Interface).
Именно на DJGPP id Software собирала первый Quake под DOS.
Похожую роль играл компилятор Watcom в связке с загрузчиком DOS4GW.EXE, который пребывал в каждом втором дистрибутиве игр 90-х.
Графика
Я писал в своей статье про инструмент для работы с AI пиксель-артом, что ограниченная палитра является важной частью идентичности игровой консоли. Так что выбор палитры - важный этап.

Fun Fact: Celeste изначально вышла как мини-игра на PICO-8 и только потом из нее развернулся хитовый Steam проект
Как я уже говорил, мне очень понравилось рисовать GameBoy графику (можете сами попробовать, воспользовавшись, например, этим тутором). Спрайт 16x16 рисуется быстро, а 4 цвета не требуют понимания теории цвета.

Еще мне всегда нравились янтарные CRT мониторы (есть в них что-то магическое)

Так что я сделал 4х цветную палитру вдохновленную ими

В памяти консоли графика хранится в 2bit формате (00, 01, 10, 11) для 4 цветов палитры. Для спрайтов один из цветов помечается как прозрачный.
В качестве разрешения я выбрал 160x120 пикселей:
это разрешение дает хороший integer-scaling для 4:3 мониторов
оно близко к оригинальному GameBoy (160×144)
меньше пикселей для обработки уменьшают нагрузку на отрисовку

Шрифт
Поскольку размер экрана очень маленький, то чтобы вместить максимум информации, нужен подходящий шрифт. Я сделал много экспериментов со своими и чужими шрифтами, но по итогу лучше распространяющегося по CC-0 лицензии шрифта PICO-8 с размером глифа 3x5 ничего не получилось.

Работа со шрифтами сразу разблокировала воспоминание: когда я учился в универе, масса материалов по программированию была в формате Lexicon, так что практически каждое поколение студентов делало свой просмотрщик Lexicon, благо у него был моноширинный шрифт 8x8 и один байт обозначал одну строчку пикселей в глифе.

Язык программирования
Сперва я начал делать свой язык программирования с различными короткими ключевыми словами типа var
и fn
, но быстро понял, что тягаться по оптимизации с классическим Lua будет тяжеловато, и что если я хочу когда-нибудь закончить этот проект - лучше срезать здесь и не изобретать велосипед.
API функции я решил сделать похожими на PICO-8 и TIC-80, чтобы можно было использовать существующие туториалы (например Nerdy Teachers и LazyDevs)
Звук
Насчет звука я долго думал, хотел сделать свои FM синтезаторы с блэкджеком осцилляторами и ADSR

Но потом, посмотрев классное видео про трекерную музыку, я понял, что именно это мое ретро :) Демо-сцена, моя коллекция музыки из кейгенов, которую я много лет собирал. Да хотя бы трек Space Debris, который сразу телепортирует в детство.
Fun fact: автор классического трека “Space Debris” Markus Captain Kaarlonen является клавишником группы Poets of the Fall, написавшей песни к Max Payne 2, Alan Wake 1/2 и Control
Так что я взял MOD-формат и сделал интерфейс в духе ProTracker - 4 дорожки, сэмплы, эффекты.

Для работы с MOD я использовал легковесную библиотеку pocketmod.
Для каждой новой игры создается копия пустого MOD файла с предустановленным наборов сэмплов.
Для DOS-версии я споткнулся об формат потока аудио. Pocketmod рендерит звук как AUDIO_F32 (32-бит float), а Allegro под DOS ждёт 8-бит unsigned. Если подать что-то «не то» - получите кошмарный громкий шум (интересно, сколько программистов работающих со звуком повредили слух в процессе обучения тому, как это работает ?)
Так что пришлось немного доработать напильником библиотеку, чтобы она выдавал сэмплы в нужном формате.
Компьютерный звук - это просто поток чисел, а процесс превращение звука в этот поток называется дискретизацией. Каждое число - сэмпл, фиксированная запись амплитуды волны; чем выше частота дискретизации (например 22 050 или 44 100 Гц), тем больше таких снимков в секунду и качественнее звук. Программа заполняет аудиобуфер этими сэмплами в нужном формате (8-бит/16-бит, signed/unsigned, mono/stereo) и передаёт драйверу. Дальше буфер циклично «съедает» звуковая карта - и мы слышим музыку.
Картридж

Для распространения игр фэнтези консоли как правило используют некий формат, включающий Lua код, атлас, карту и музыку со звуками.
Чтобы не усложнять, я решил просто сделать формат n8 в виде zip архива, содержащий:
app.lua - весь код, скленный в один файл
map.bin - карту из редактора карт
spriteFlags.bin - спрайт флаги
music.mod - файл содержащий все звуки, музыку и дефолтный набор сэмплов
Название и маскот
Как известно, слово BYTE звучит похоже на BITE - в переводе “укус”, поэтому полубайт называется NIBBLE - “грызть”
Поскольку один пиксель в игре занимает полбайта я решил выбрать название NIBBLE8
А маскотом стал пиксельный пес Ниббл из незаконченной мной JRPG для GameBoy.

Итог
В итоге NIBBLE8 можно собрать под веб, на любой системе поддерживающей SDL 1 или 2 и под DOS (наилучший перформанс на процессорах Pentium MMX и выше)

У меня было много разных идей (к примеру превратить это в своего рода альтернативный интернет в духе FidoNet и BBS ), но со временем меня немного попустило.

Первая версия получилась неидеальной, сейчас бы я сделал все совсем по-другому, есть многое, что стоило бы доработать и оптимизировать (к примеру мне дали хороший совет реализовать компиляцию Lua в C, чтобы можно было получать очень быстрый код для DOS).
Но главный результат для меня - снова то самое ощущение, что я могу визуализировать идею в своей голове, а потом реализовать ее и выпустить наружу. Так что я закончу статью этой картинкой

? Код на GitHub: https://github.com/jenissimo/NIBBLE8
? API Reference: https://github.com/jenissimo/NIBBLE8/wiki/API-Reference
? Попробовать онлайн: https://nibble8.com/try.html
Буду рад увидеть, как вы запускаете NIBBLE8 на своих ретро-компах (или эмуляторах!)
✉️ Пишите фидбек, делитесь своими историями.
Может, вам тоже удастся вытащить старое вдохновение из ящика и вдохнуть в него новую жизнь.
Также если понравилась статья, возможно тебе будет интересно подписаться на мой Telegram