Making of "Back to Basics"
Вступление
Я давно мечтал написать полноценное демо на бейсике.Еще в середине 90-х, экспериментируя на своем «Ленинграде», я делал пиксельный вертикальный скроллинг спрайта 8х3 знакомест с использованием кастомного шрифта и системной переменной CHARS.
В конце 90-х это вылилось в попытку организовать некое соревнование по бейсик-демо между пермскими спектрумистами из 4D и LFG. Кажется, даже пара демок была выпущена тогда, но свою работу я так и не закончил. После чего все исходники были утеряны.
К бейсику я вернулся только в 2011 году, когда Пермь посетил unbeliever и с его легкой руки родилась 3BM Openair demoparty. «Ass Invaders» (я извиняюсь за название, но тогда это казалось дико смешным) были написаны за день, прямо на iPad, в Спектакуляторе. Но это была не то демо, которое я хотел сделать на бейсике. Окончательно я утвердился в своих намерениях с выходом Cowina megademo — сборника из бейсик-дем с JHCon'2014. Казалось бы, уже выжато все, что только можно. Но все это, за исключением «взрослых» частей от Busy было TOO BASIC. Да, в части Factor6 был отличный дизайн и местами даже звук на бипере, а часть Equinox помимо отличной задумки была еще и здорово анимирована. Но, чего-то не хватало. Не хватало нормальной пиксельной графики, полноценного саундтрека. Не хватало полноэкранных динамичных эффектов. Авторы изначально отказывались от этого, а я не понимал, почему?
Я всегда считал, что бейсик способен на что-то большее (как и ZX Spectrum в целом, его графика и музыка). Именно поэтому я еще сильнее утвердился в мысли о необходимости возродить BASIC demo compo на 3BM. Ведь два подобных конкурса в год (3BM и, традиционно, — JHCon) – не так уж и много, учитывая, что демы пишутся гораздо быстрее, чем пол года. К тому же пати ориентированы на разные аудитории – Европа и Россия. Это я не к тому, что надо и дальше раздельно тусить и выставляться, а надо развиваться во всех местах и экспортировать свою культуру. Выставляется же Gemba на DiHalt? Почему бы и нам, не разогреться на 3BM и не выставиться на JHCon?
Ну и вообще, возврат к тому, с чего начался 3BM в 2011 году, — нормальный и логичный шаг.
Соответственно, написание собственной демы на бейсике на ближайшее пати – Multimatograf 11 позволило сбросить целую атомную бомбу на стадо зайцев:
Во-первых, написать свое демо и как-то продвинуть этот бейсик-гештальт к завершению.
Во-вторых, пригласить всех на 3BM.
В-третьих, поддержать демо-компо в Вологде.
В-четвертых, продвинуть отечественную бейсик-демосцену.
В-пятых, размяться перед летним сезоном.
И т.д.
Поэтому, не откладывая в долгий ящик, я начал писать демо в начале апреля. Делать его было довольно легко. Сперва :)
Выбор инструментария
Первым делом, конечно, я выбрал среду разработки. Выбор был невелик. Я посмотрел раздел по бейсику на worldofspectrum, и самое свежее, что там было — это BASin by Paul Dunn.
Множество встроенных инструментов: встроенный редактор графики и шрифтов, модуль по созданию и редактированию TAP-файлов, отладчик и даже ассемблер. Заявлен, но к сожалению не доделан BEEPER composer. Самое главное, — редактор сохраняет и читает BASIC-программы в текстовом формате, что позволяет использовать любые внешние привычные вам редакторы как для набора, так и для модификации текста (Например Sublime Text 2).
Позже я вспомнил про совсем свежую среду ZX Modules, в которой также есть средства для работы с программами на BASIC, но времени опробовать ее возможности у меня пока не было.
Первые шаги
Дальше я взялся за эффекты. Первым я написал совсем простой эффект — Starfield, с которого я планировал начать демо. Я сделал два варианта с пиксельным горизонтальным скроллингом звезд, как ни странно, вариант с PLOT OVER оказался быстрее своего аналога на CHARS/PRINT.
Вторым эффектом — были Advanced Color Lines. Первый вариант эффекта был реализован через UDG-символы («\», «/»), с познакоместной печатью, что позволяло на ходу менять цвет линии. Я сделал три вариации этого эффекта — с зацикливанием цвета 1-7, с зацикливанием 0-7 (что давало дырки в узоре) и последний вариант — с цветом по RND.
Тут же я заметил, что RND весьма затормаживает процесс рисования и сразу же отказался от использования этого оператора. Брать цвет из ПЗУ через PEEK и деление на коэффициент оказалось быстрее, чем RND примерно в 2 раза.
Подходы к конверсии звука
Далее, когда работа над эффектами сдвинулась, я решил взяться за графику и звук. Предложил Олегу Никитину (n1k-o ) написать одноканальный трек, чем его весьма обрадовал. Выяснилось, что в юности он так же баловался бейсиком в тайне от родителей («это нормально, сынок!» (Ц).
Следующим эффектом была пародия на демку с C-64, где шарики прыгали по изометрической стенке из кубиков с одновременным сдвигом стенки вверх. Сдвиг экрана делать было не впервой, вся загвоздка была в шариках. Написав печать стенки и измерив задержку при сдвиге экрана, мы с Олегом поняли, что для совмещения музыки и эффектов (а мы собирались сделать трек на командах BEEP от начала и до конца демо) нам понадобятся паузы — пустые места в паттернах. Поэтому выбрали drum'n'bass с достаточно разряженным ритмом и Олег тут же выдал 4 тестовых паттерна.
Тут и настал первый ключевой момент. Не пройди я его, звука в демо бы не было. Это — конверсия музыки из Vortex Tracker II в программу на BASIC. Проблемы было две: я никогда не делал музыку через команду BEEP, я не знал структуру модулей VT. Все, что я знал, — это то, что VT умеет сохранять музыку в текстовом формате, и что я могу попробовать что-то с этим сделать в Excel.
Вооружившись докой по бейсику я набросал таблицу соответствия нот VT числовым значениям для BEEP. Если с длительностью было примерно все понятно (она задается в секундах), то с нотами пришлось немного повозиться. Воспользовавшись представлением модуля в текстовом формате с помощью подстановок через серию формул Excel я получил некую последовательность команд BEEP и PAUSE, сцепленную в пронумерованные строки, которую можно сохранять в текстовой файл с расширением BAS и смело загружать в BASin.
И, случилось чудо. Музыка заиграла. Хотя первые два варианта были неудачными – пришлось разобраться с номерами октав и понять, что нота 1 (а в BEEP они нумеруются от -60 до +69) соответствует ноте C-4, то есть «до» четвертой октавы, а не первой, например. Дальше, пришлось откорректировать длительность. Трек решили писать на первой скорости, что означало длительность ноты — 0.02 секунды. Обработка интерпретатором команд BASIC так же занимает время и поэтому музыка на слух воспроизводилась немного медленнее. Корректировка же базовой длительности нот до 70%, то есть до 0.014 дала на слух звучание, довольно близкое к оригиналу.
Следующим шагом было тестовое совмещение музыки с эффектом. Эффекты без музыки выглядели сносно, музыка без эффектов звучала довольно схоже с VT. Совместив эффект сдвига изометрического поля с тестовыми паттернами, мы убедились, что оно хоть и двигается медленно, но все же двигается и паузы примерно совпадают с исходными, при этом музыка практически не рвется и звучит довольно узнаваемо по сравнению с оригиналом.
Дальше все стало примерно понятно — надо писать эффекты, рисовать графику, делать трек и оставить пару дней на сборку перед дэдлайном. Чем мы и занялись. Для написания трека был выбран референс, казалось, что можно взять любую музыку и потом она волшебным образом совместится с демо:). Но написать BEEP-трек без плана демо и без заранее известных задержек для эффектов — невозможно. Как невозможно и написать его без подсчета необходимой длительности каждой части. Если часть первую часть демо можно было прервать в любой момент, то advanced color lines должны были отрисовыть весь экран, messager (часть с текстом) должна успеть написать весь текст, а greetings — показать все имена. Но с этими сложностями я столкнулся немного позднее.
Эффекты
Вернемся снова к эффектам. Уже давно у меня чесались руки — заняться исследованием трюка с командой LPRINT, с помощью которой можно было рисовать в экранную память текст, в 8 раз увеличенный по вертикали, просто перенаправляя вектор, указывающий на буфера принтера в экранную память. Разумеется, просто так рисовать надписи было не интересно, увеличенные надписи шрифтом из ПЗУ в любой из третей экрана – было стандартным и единственным использованием этого трюка, мне же хотелось чего-то нового. Попытаться выжать из этого другие, принципиально не похожие на все, что было сделано раньше на бейсике, эффекты. Тогда я решил попробовать совместить два трюка вместе — сдвиг экрана и печать строки через LPRINT.
Прямое совмещение сдвига с печатью UDG-символа «::» сразу же дало эффект «1D Flame». Следующей логичной вариацией были Rasterbars — печать строчки 1 раз в случайное место экрана, что давало чистую горизонтальную линию + сдвиг экрана с одновременным подкрашиванием новой строки случайным цветом. Ну и то же самое с заменой UDG-символа на пиксельную сетку и отказом от PAPER и покраской только INK – Textured Rasterbars.
Дальше, уже предчувствуя проблему с длительностью эффектов, я решил ускорить эффект Advanced Color Lines, переписав его через DRAW. И если первая вариация эффекта не вызвала затруднений – каждая новая линия рисовалась цветом, после чего счетчик цвета просто увеличивался на 1, то для следующей вариации пришлось атрибуты выводить заранее. Что я седлал через команду PRINT, предварительно сконвертировав атрибуты в строки для печати с использованием управляющих цветовых кодов.
Далее — графика. Я намеренно пишу о ней в разделе «Эффекты», т.к. рассказывать о рисовании самой графики особо нечего, кажется, что давно уже все обсосано и разобрано, а вот ВЫВОД графики на экран, и, тем более быстрый вывод полноэкранной картинки, как оказалось, задача нетривиальная и тянет на целый эффект.
Короче, время уходило быстро, а эффекты писались медленнее, чем я планировал. Соль картинки с девушкой в середине демо конечно была задумана сперва одна, но ничего с задумкой не вышло (попробую реализовать в следующих демо, если они будут), поэтому в виду нехватки времени необходимо просто было сделать печать полноэкранной картинки. Печать атрибутов у меня уже была, поэтому дописав печать пикселей через CHARS/PRINT я получил процедуру печати картинки. Казалось бы — все готово. Но я не смог придумать, как напечатать все 24 строки с помощью PRINT. Использование каналов для печати позволяло печатать и в нижнюю область, но сплошная печать — никак мне не давалась, между нижней областью и верхней всегда оставалась 1 пустая строка. Вывод картинки в нижнюю область так же позволял напечатать только 22 строки. Я оказался в тупике. Если кто-то из читателей знает как напечатать 24 строки через PRINT — буду рад узнать или увидеть этот трюк в демо на 3BM! Я так и не осилил.
Так и не найдя решения с PRINT, я обратил взор к его собрату – LPRINT, ведь он тоже выводит графику в экранную память, причем без дурацкого разделения на окна, и с прямой адресацией в 256-байтные «сектора».
Вспомним стандартное использование трюка LPRINT – 8 раз в «сектора» с номерами 64-71, 72-79, 80-87 печатается одна и та же строка символов. В результате получаем в 8 раз растянутое по вертикали изображение. Если же в каждый «сектор» печатать 8 разных строк – получим полноценные 6 килобайт ч/б графики.
Реализовав такой вывод, мы получили весь экран, но только 22 строки атрибутов, которые по-прежнему печатались через PRINT. Выводить нижние 2 строки атрибутов кроме как через POKE, я до самого последнего момента не догадывался как.
Сборка party-версии демо
Наконец настал момент, когда все эффекты были написаны по-отдельности. Разумеется, опытные кодеры, читая эту фразу, усмехнутся. И будут сто раз правы. Ибо механизм сборки был не продуман и совмещение произвольного эффекта с музыкой я представлял только в общих чертах. Измерив все длительности основных циклов эффектов, мы наконец-то смогли дописать трек. Но на референс он был уже совсем н похож, зато учитывал (как мне тогда казалось) все задержки, необходимые для работы эффектов. Проблема была другая — это уже было утро среды, день дэдлайна. И полный трек занимал почти 30 килобайт памяти. А графика по плану занимала 16 килобайт. Что было весьма прикольно, учитывая, что упаковщики данных на бейсике я не планировал делать, да и не успевал уже.
Таким образом, встал вопрос по сжатию трека. Сразу же переходить к VAL не хотелось, это мгновенно тормозило музыку и превращало эффект, совмещенный с музыкой в жуткое адилище. Первое, что я сделал — заменил дробные длительности переменными и приступил к сведению циклов эффектов и проигрывания музыки.
Проблемы не заставили себя долго ждать. Эффекты пати-версии делились на 3 следующих типа:
1) В messager я сделал некое подобие проигрывания «на прерываниях», когда из основного цикла эффекта по истечении, отведенных для эффекта пауз, вызывался плеер музыки через go sub, игравший в следующую ноту. Это как-то прокатило, но было понятно, что дальше такое не прокатит в виду сложности паттерновой структуры и различной длительности пауз.
2) В других эффектах наоборот 1 простой цикл эффекта вызывался через go sub из основной процедуры проигрывания музыки. И это тоже как-то работало.
3) Самый ад — greetings: 16 паттернов, перемешанные в разном порядке, цикл эффекта, состоящий из двух частей – печать либо стирание надписей (одна задержка) и сдвиг экрана (совсем другая задержка). Пришлось полностью переписывать эффект, делая одну входную точку через go sub, попутно плодя кучу флагов, по которым определялось, что необходимо делать каждый новый вызов процедуры — печать, стирание или сдвиг экрана.
С учетом того, что верхняя страница памяти была занята графикой, память кончалась буквально каждый раз при добавлении в сборку нового эффекта.
Пришлось проанализировать все паттерны и разбить их на набор вызовов уникальных цепочек BEEP. Это дало некий выигрыш памяти – количество памяти, затраченной на хранение последовательностей BEEP, сократилось, но немного выросли по длине последовательности вызов этих цепочек через go sub. При этом главной ошибкой party-версии демо стало то, что пришлось полностью уйти от паттерновой структуры трека к структуре цепочек. В результате, если какой-то эффект я делал без музыки, я не мог просто выбросить какую-то цепочку BEEP для экономии памяти, т.к. она почти всегда встречалась в других местах трека.
Поэтому я продолжал хранить в памяти почти весь трек. И памяти все равно не хватало. Последним спасением, чтобы выпустить демо хоть в каком-то виде на пати, была экономия памяти через VAL. К этому моменту сборки уже пролетели круглые сутки без сна в BASin и было утро субботы. Двойная вложенность go sub, нахождение музыки в строках 9ххх, VAL дали дикие тормоза во всем. Недостаток памяти сказался на скорости работы, как эффектов, так и плеера музыки. В результате затормозилось все — месседжер, гритингсы, вывод картинок, т.е. флоу порвался на мелкие кусочки, но делать было нечего. В greetings музыку я, все таки, решил убрать, что дало финальную экономию памяти на диком количестве go sub (16 паттерновых позиций, самая длинная часть демо). И вот, днем субботы на свет появилось довольно таки странное демо, вроде бы и с музыкой и графикой, но при этом дико тормозное. Бейсиковость, вроде бы, и оправдывала всю эту, мягко говоря, неспешность, но и все это (судя по голосованию на пати) вызывало некоторое недоумение – как можно было вообще задумать настолько медленное демо?
Финальная версия демо
После показа на пати стало понятно, что оставлять демо в таком виде – невозможно. И совершенно ясно было, что трек может играться с нормальной скоростью (ведь сперва же он игрался). Только для этого придется переписать все демо почти с нуля таким образом, чтобы скорость проигрывания трека не менялась и эффекты не выходили за пределы пауз между нотами.
Первым делом я перенес музыку в самое начало в строки с номерами 10-50. Отказался от VAL и двойной вложенности go sub = структуры с уникальными цепочками BEEP, вернувшись к паттерновой структуре, когда одна строка = один паттерн с RETURN в конце строки. При этом довольно легко было чистить память, т.к. в каждой части демо — свои уникальные паттерны. То есть, отказываясь, от звука в любой из частей я просто удаляю 3-4 строки паттернов и сразу же экономлю 1-2 килобайта.
Кроме того, легко удалось сэкономить несколько килобайт, просто заменив переменными несколько самых наиболее употребляемых номеров нот (от 100 до 200 повторений за трек).
Ну и совсем уже финальная экономия – преобразование отображений 4- и 5-значных чисел в один символ, например «0».
В финальной версии все эффекты поделились на 3 типа:
1) Эффекты в которых сперва выводится графика на экран, а затем только играется музыка.
2) Эффекты в которых процедура вывода изображения вызываются вместо пауз прямо из строк паттернов через GO SUB, когда длительность паузы больше или равна длительности цикла эффекта.
3) Эффекты без звука, либо с простыми последовательностями BEEP, не входящими в паттерны.
Отказ от вывода тяжелых по длительности эффектов под музыку позволил немного подлечить флоу.
Структура финальной версии выглядит следующим образом:
Паттерны делятся на 2 вида:
A. Паттерны, содержащие только последовательности BEEP и PAUSE
B. Паттерны, в которых PAUSE M заменены на GO SUB Q: PAUSE N. Например, в паттерне есть паузы длительностью 14,7,8,1 фреймов. При этом основной цикл соответствующего эффекта, вызываемый по команде GO SUB Q, занимает 7 фреймов. Соответственно паузы больше или равные 7 заменяются на GO SUB Q: PAUSE N-7.
Часть с текстом так же вынесена в отдельную процедуру и относится к эффектам первого типа, вызывает паттерны типа A (только музыка).
Затем идут части со звуком – эффекты второго типа, они вызывают паттерны типа B (музыка, вызов эффекта).
В конце демо идут эффекты третьего вида, паттерны не вызываются.
Исходники
Исходный BAS-файл и ресурсы.
BeepornV1 конвертор не публикую, поскольку он достаточно сырой и сам результат конверсии далек от идеала. Если у кого-то есть желание поучаствовать в исследовании конверсии VT>BEEP или написать standalone-конвертор — готов предоставить всю имеющуюся информацию.
Заключение
В конце хотелось бы, прежде всего, поблагодарить Олега Никитина (n1k-o ) за терпение. Олег, спасибо, что ты так много раз пришлось переделывать трек, что он вошел в демо не полностью, что звучит не так, как в оригинале. Спасибо тебе большое за твой профессионализм и поддержку!
Большое спасибо Алексею Пичугину (introspec ) за модификацию mktap+ сперва для сжатия отображений чисел, а затем и под особенности синтаксиса файлов BASin.
Paul Dunn – огроменное спасибо за чудесный инструмент разработки. Очень жаль, что часть задумок так и осталась нереализованной, но это просто мечта BASIC-программиста на ZX Spectrum.
Спасибо Андрею Маринову (nyuk ) за предоставленную возможность для релиза демо, за multimatograf, events, retroscene.org и hype!
Простите, если кто-то не нашел себя в приветах! Сперва я хотел передать приветы в демо всем, кто когда-либо делал демо на бейсике, но довольно таки медленно там все выводится, поэтому времени хватило только на самых матерых бейсик-демомейкеров.
Спасибо всем, кто поддерживал меня во время написания демо и спасибо всем за отзывы!
С удовольствием отвечу на любые вопросы по поводу демо в комментариях или формате отдельных статей, если потребуется.
42 комментария
Один только вопрос. Допустимо ли использование AY-трека как это сделано в «Silabba»? Ибо так сражаться с Beeper'ом мало кому под силу!
Хотя я в курсе про Didaktik+Melodik и поддержку этого дела в эмуляторах.
Но я да, сидел на стуле и смотрел в монитор на работе :-D
Насиловать ПЗУ — пожалуйста. Не знаю можно ли чего-то вытащить из него через USR 1-16383, чего нет в обычном бейсике — можете пробовать.
Всякие данные можно в память грузить блоками хоть куда — графику, прекалк — сколько угодно, но пожалуйста не надо хитрить с ассемблером и запуском через какое нибудь там POKE без USR или как-то еще в верхних 48 килобайтах. Да, и не надо искать дырки в правилах с подменой ПЗУ или еще как-то. Все должно работать на класике (у буржуев) и стандартных клонах у наших.
впечатляюще. Конверсия музыки екселем — это что-то :)
Вообще — реально адилище :) сложнота
Лёша, извини, я не мог не пошутить про эксель. :)
Анимации не боимся, тем более, что ее и в 48к хватает. Насчет замечательности команды PLAY соглашусь, более того, на 3BM OpenAir 2011 ограничения на 48к не было, участвовали работы и для 128к. Поэтому, пожалуй, я не против.
Хотя 48к, конечно, как-то роднее.
С блекджекомсо строчными прерываниями ишлюхамиспрайтами? :-DОтдельное компо по Laser-basic, Mega-basic, Beta-basic. Там меньше памяти остаётся, но больше возмодностей в плане алгоритмических эффектов. Но это так, ремарка в сторону.
То есть можно даже для эффекта типа Advanced Color Lines посчитать задержку на каждом шаге и СКОМПЕНСИРОВАТЬ ее в зависимости от длины рисуемой линии. И это реально улучшит качество проигрывания музыки в плане соответствия темпа оригиналу. Сейчас-то там в этом эффекте кашица в таймингах.
Но вот чтобы реально на 100% попасть в скорость и не порвать темп, нужны нешуточные исследования быстродействия команд и зависимости от положения в команд в памяти с инструментарием на ASM. Т.к. влияет все — в начале листинга строка с номером 10 или в конце, с номером 9ххх, несколько команд в строке или строки в разных командах. Влияет даже количество инициализированных переменных. Пример: CLEAR перед Advanced Color Lines ускоряет эффект примерно 1,5-2 раза.
Но народ просит и 128К. Просят — пожалуста. Тем более 128 было в 2011 году.
А чем 128К бэйсик отличается от 48К кроме как командой «PLAY»?
zxpress.ru/book_articles.php?id=70
В bbb дядя вася юзал вторую видеостраницу.
23659 DF SZ The number of lines (including one blank line) in the lower part of the screen. )
То есть, забиваем в нее 0 и печатаем на весь экран. Правда если это дело прервать по BREAK, то бейсик слегка подвиснет :)