Эмулятор в эмуляторе для проигрывания чиптюн-мелодий на YM2149F

Эмулятор в эмуляторе для проигрывания чиптюн-мелодий на YM2149F

Кто помнит Tetris 2 на Спектруме? Там были куча уровней, возможность играть вдвоём и классная музыка.

Недавно мы сделали 8-битный компьютер для несложных игр, но никаких звуковых возможностей в нём не предусмотрели. И вот захотелось добавить туда какую-нибудь 8-битную музыку. Мне вспомнилась именно мелодия из Tetris 2 (много за ним часов проведено), поэтому я стал с ней ковыряться.

Процессор ATmega328P в нашем компьютере большую часть времени занят отрисовкой изображения, поэтому времени на синтезирование нормальной музыки совсем нет. Значит нам понадобится звуковой процессор YM2149F (он же AY-3-8910), такой же как в ZX Spectrum и других компьютерах.

Подключаем YM2149F к Arduino

Начал я с подключения звукового синтезатора к плате Arduino и с вывода на него простых нот. YM управляется с помощью записи значений в один из 15 (на самом деле мы используем 13) регистров. В регистры записываются частоты звука в каждом из трёх каналов, частота шума, уровень громкости, частота и форма огибающей. Для адресации и передачи данных используется 8 сигналов. Ещё парочка нужны для управления режимом шины — выбор регистра или загрузка значения.

Для тактирования YM используется встроенный таймер процессора с делителем частоты. В итоге на вход звукового чипа приходит 2МГц, что неправильно с точки зрения соответствия оригиналу, но для наших тестов пока сойдёт. Мы же программисты, а не железячники.

Тестовые ноты проигрались успешно и надо было двигаться дальше.

Я раньше не очень интересовался спектрумовской музыкой отдельно от самого Спектрума, но всё же слышал про формат AY. Ещё в одной из статей я видел упоминание формата PSG. Он похож на всякие WAV в MP3 в том смысле, что там содержится линейная последовательность действий с регистрами музыкального сопроцессора. Поэтому файлы получаются большие и в память ATmega не влезают.

Файлы в формате AY намного меньше. В чём же там секрет? А в том, что это куски кода из игр или демок для проигрывания мелодии, а также некоторые массивы данных для этого кода. Обычно проигрыватели просто эмулируют центральный процессор Спектрума, чтобы выполнить эту программу и так проиграть мелодию.

Почему бы не сэмулировать Z80 на AVR?

… подумал я и поискал какую-нибудь библиотеку-симулятор процессора Z80 на языке C. Такой симулятор нашёлся. Ему надо было подсунуть только функции чтения/записи памяти и портов ввода-вывода.

Небольшая сложность возникла с памятью — ведь у ZX Spectrum 48 килобайт ОЗУ, а у ATmega328P всего 2 — напрямую создать массив памяти для функций чтения/записи не получится. Пришлось сделать массив адресов и ячеек, а в нем искать значения при обращениях от процессора.

Оказалось (кто бы мог подумать!), что эмулировать один 8-битный компьютер на другом 8-битном — не очень хорошая идея. Какой-то звук выводится, но всё происходит настолько медленно, что мелодией это назвать сложно. Тогда я решил разобраться с кодом проигрывателя и переписать его на C.

Декомпиляция или закат Солнца вручную

Код оказался немного запутанным. Это был интерпретатор какого-то байт-кода, управляющего музыкальным сопроцессором. В байт-коде даже есть поддержка циклов и подпрограмм. Каждый канал YM управляется отдельной программой с отдельным стеком. Получилось, что изначально я эмулировал компьютер, который эмулирует другой трехпроцессорный компьютер. И хоть программа получалась небольшая (и запускалась лишь 50 раз в секунду), всё равно это было очень медленно.

После того, как я почти раскусил формат этого байт-кода, я случайно наткнулся на его описание. Это был Fuxoft AY Language. Его разработал Frantisek Fuka (Fuxoft), который написал и сам тетрис, и музыку к нему. Этот язык используется в нескольких десятках композиций. И их код даже извлечён из игр в виде файлов FXM. Тот код, что я уже проанализировать, пришлось выкинуть, чтобы начать всё заново (но его по-прежнему можно увидеть в истории изменений репозитория).

Проигрыватель FXM

Краткое описание формата и его декодер мне попались в исходниках бульбовского проигрывателя. Сравнивая его с дизассемблированным кодом, я нашёл всего пару небольших отличий, но семантика структур данных стала намного понятнее.

Теперь проигрыватель работает достаточно быстро. Музыка играется, можно объединять компьютер и прогрыватель. Каждая композиция занимает всего пару килобайт, поэтому если взять Arduino Mega (на процессоре ATmega2560), то можно уместить в его память все существующие мелодии в формате FXM.

Что дальше?

Осталось добавить правильный кварцевый генератор, чтобы частота работы микросхемы была как на Спектруме. Ещё можно написать декодеры других трекерных форматов, добавить SD-карту и получится аппаратный плеер спектрумовской музыки. А когда подсоединим это к нашей игровой коробке — получится настоящий игровой автомат.

Правда пока почему-то не получается, чтобы громкость динамика была нормальной, сейчас всё очень тихо. Может кто-нибудь знает как заставить такой динамик из игрушечного телефона звучать? Китайский телефон с этим справляется, а выходы YM2149F почему-то не очень. Поэтому видео работы плеера я пока не выкладываю.

📎📎📎📎📎📎📎📎📎📎