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


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

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

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

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


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


    Найденная в интернете схема подключения

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

    Код для инициализации генератора тактового сигнала
    // Sets a 4MHz clock OC2A (PORTB3)
    void set_ym_clock(void) {
      // PB3 - output
      DDRB |= 0x01 << PORTB3;
      // Set Timer 2 CTC mode with no prescaling. OC2A toggles on compare match
      //
      // WGM22:0 = 010: CTC Mode, toggle OC
      // WGM2 bits 1 and 0 are in TCCR2A,
      // WGM2 bit 2 is in TCCR2B
      // COM2A0 sets OC2A (arduino pin 11 on Uno or Duemilanove) to toggle on compare match
      //
      TCCR2A = ((1 << WGM21) | (1 << COM2A0));
      
      // Set Timer 2 No prescaling (i.e. prescale division = 1)
      //
      // CS22:0 = 001: Use CPU clock with no prescaling
      // CS2 bits 2:0 are all in TCCR2B
      TCCR2B = (1 << CS20);
      
      // Make sure Compare-match register A interrupt for timer2 is disabled
      TIMSK2 = 0;
    
      // Divide the 16MHz clock by 8 -> 2MHz
      OCR2A = 3;
    }
    



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

    Я раньше не очень интересовался спектрумовской музыкой отдельно от самого Спектрума, но всё же слышал про формат 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 почему-то не очень. Поэтому видео работы плеера я пока не выкладываю.



    Ссылки


    1. Репозиторий к кодом проигрывателя
    2. Как мы подключали светодиодный дисплей 64x64 к Arduino
    3. Управление чипом YM2149F с помощью Arduino
    4. Эмулятор процессора Z80 в виде библиотеки на C
    5. Проигрыватель музыки для AY
    6. Архивы музыки
    Метки:
    Поделиться публикацией
    Похожие публикации
    Комментарии 34
    • +1
      Хотелось бы посмотреть, как это звучит.
      • 0
        Прилажу усилитель и выложу.
        Сейчас я (почти) в лесу, а транзисторов с собой не захватил.
        Вон даже килоомные резисторы паять пришлось из 220-омных.
      • +3
        Бацали мы в этот Тетяис 2. Было зверски круто! И музыка прям вот вообще в тему и не отвлекала от игры.

        По-моему, в наши дни надо на ARM всё делать.
        • +2
          Я всё жду, когда AVR умрёт. Но как-то стюардессу никак не закопают. Уже такие крутые мощные ДЕШЁВЫЕ (дешевле AVR) камни есть, а люди всё мучают старьё… Сколько бы крутых проектов было бы на армах, если бы народ отказался от ардуины.
          • +3
            Ну и 8-битный процессор же более труъ для 8-битной музыки.
            Это всё же ненормальное программирование :)
            • –1
              Дешевле AVR это какие?

              Имхо AVR популярны из за достовабельности, зайди в любой радиомагазин, там точно есть.
              • 0
                Пожалуйста, покажите мне магазин, где есть только АВР и нет Армов (тех же STM32). Ну и сами сравните их цены.
                • 0
                  www.chipdip.ru/catalog/ic-microcontrollers?gq=stm32
                  и
                  www.chipdip.ru/catalog/ic-microcontrollers?gq=atmega
                  Блин, в пять раз дороже, при мощности в сотни раз хуже!!! СЖЕЧЬ!
                  • 0
                    Вы отлично сделали, просто кинув ссылки на список процессоров обоих производителей.
                    Может сопоставимых примеров подкинете, по характеристикам и цене?
                    мощности в сотни раз хуже

                    Точно, у меги 1.00 мипс, у кортекса 1.27 дмимс, о каких сотнях разницы в мощности речь?
                    • 0
                      ну напомню, что разрядность 8 бит у одних, а у других 32 бита. Следовательно на арифметических операциях с большими чисами мега будет проигрывать сразу раз в 10 или более ОЗУ больше в десятки раз. В общем, домашнее задание разобраться с этим самому. Мне нет резона доказывать что не верблюд. Хочется юзать старое говно — пожалуйста. Сейчас есть нереально мощнее и дешевле процы. После АВР, АРМ показался космическим кораблём в сравнении с велосипедом.
                      • 0
                        Вы конкретно сказали:
                        Блин, в пять раз дороже, при мощности в сотни раз хуже!!! СЖЕЧЬ!

                        и опять:
                        Сейчас есть нереально мощнее и дешевле процы.

                        пример приведите.
                        Мне нет резона доказывать что не верблюд.

                        Мне нужно доказать что ваш тезис верный. Я правильно понял?

                        что разрядность 8 бит у одних, а у других 32 бита. Следовательно на арифметических операциях с большими чисами мега будет проигрывать сразу раз в 10 или более ОЗУ больше в десятки раз.

                        Ни разу не следовательно. До этих больших чисел нужна задача особая. ОЗУ требуется больше просто потому, что переменные 32 битные и требуют большего размера в ОЗУ, хотя бы для передачи параметров через стек, и это тоже касается флеша, одна инструкция съедает 4 байта. Если учесть необходимость инициализации кучи периферии, например, тактирования и гпио, используя HAL, то эта инициализация может съедать от 1кб до десятка кб
                        И далее, о каких арифметических операциях идет речь?
                        Кортекс-м0 также захлебнется при серьезной задаче, его мат. аппарат не как достоинство, а как возможность.
                        Хочется юзать старое говно — пожалуйста.

                        С этого и надо было начинать. Все зависит от поставленных целей.
                        Меги мне стоят 26 центов с НДС, кортексы-м0 — 56 с НДС.
                        • 0
                          Вы меня на понт берёте? Вы тут сказали, что авр доступнее армов. Я попросил дать пример магазина, где нельзя купить арм, но можно купить авр.

                          Касательно цен. Можно сравнить самую крутую мегу (8 бит), с самым слабым армом 32 бита (главное чтобы количество ног совпадало). Будем сравнивать STM32 и Atmega. Сотни раз здесь не в попугаях. А в удобстве разработки, что не нужно считать байты, надо алокировать память — выделил и т.п. Лично я не знаю ни одной задачи где сейчас целесообразнее использовать 8 бит, против 32-х. А даже если и есть, то есть современные недорогие (дешевле АВР) 8-ми битные процы.
                          • –1
                            Вы меня на понт берёте?

                            Конкретно вы утверждали, что авр в сто раз по мощности хуже, не я.
                            Когда с вас попросили конкретные примеры, вы начали срываться, мол, сами доказывайте.
                            Я попросил дать пример магазина, где нельзя купить арм, но можно купить авр.

                            Во первых, существуют иные города, где чипдипа может не быть в принципе. Я надеюсь это не нужно доказывать.
                            Во вторых, магазинчик в 2.5 км от меня в наличии пик и авр, из армов только отладки для стм32.
                            Будем сравнивать STM32 и Atmega. Сотни раз здесь не в попугаях.

                            А в чем? Как вы можете объективно оценить лишь своё личное удобство, кроме как не в попугаях?
                            что не нужно считать байты, надо алокировать память — выделил и т.п.

                            Это ваш путь не оптимизировать программу, например, по скорости, размеру или ещё каким параметрам.
                            Лично я не знаю ни одной задачи где сейчас целесообразнее использовать 8 бит, против 32-х.

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

                            Можно сравнить самую крутую мегу (8 бит), с самым слабым армом 32 бита (главное чтобы количество ног совпадало)

                            Надеюсь вы понимаете, что самая крутая мега будет дороже самого слабого арма32?
                            Почему бы не взять самую дешевую мегу и самый дорогой кортекс-м0, в пику вашей логике, подгона вопроса под ответ?
                            • 0
                              Ни в одном нормальном промышленном решении AVR не используется из-за их цены, к слову. Есть дешёвые 8-бит контроллеры.

                              На самом деле спор бессмысленен. Я тут конечно в сердцах написал много глупостей. Просто обратите внимание, что есть другие контроллеры, которые дают много больше, чем АВР. И с точки зрения удобства и возможностей. АВР очень уж устарели, право.
                              • 0
                                На самом деле спор бессмысленен.

                                Я абсолютно с вами согласен, от вас ни одного аргумента.
                                По факту: «лучше», «сжечь», без какой либо конкретики.
                                Ни в одном нормальном промышленном решении AVR не используется из-за их цены, к слову.

                                Раз уж вас болидовские или мифейровские считыватели не устраивают, а может, вам домофоны тоже под промышленные изделия не подходят?
                                Разумеется, сами клоны iButton тоже не в счет, там не стоит арм32.
                                Покажите ваш промышленно производимый продукт, который именно вы считаете «нормальным».
                                И второе: не всегда закладывается в пром изделие самое дешевое, все зависит от целей.
                                Есть дешёвые 8-бит контроллеры.

                                Конечно есть, но почему то сжечь только авр, а православно правильные арм32 — рулез. Цена определяется объемом закупа. Контора, с которой мы сотрудничаем, закупает процессоры по десяку центов, потому что миллионами берет.
                                АВР очень уж устарели, право.

                                Если вы не знаете, как использовать инструмент, то это не вина инструмента, а ваша.

                                PS. Моя микроволновка жалуется, что у нее не нанотехнологичный процессор стоит, хочет кроме времени отсчитывать траекторию полетов баллистических ракет. Преобразователь интерфейсов USB-CAN вообще отказывается работать, протестует, какую то пенсию требует )
                • 0
                  От Ардуины не откажутся, потому что под Ардуину всё есть, от документации до примеров на все случаи жизни, а под ARM — редко. Говорю как человек, пытавшийся что-то делать на STM32, плюнувший, и купивший Ардуино.
                  К счастью, сейчас есть Arduino на ARM, и много кода и библиотек для AVR там тоже успешно работает (в чём и есть заслуга Ардуино — создание библиотек и вменяемого слоя абстракции от железа). У меня Arduino Due, портировал на неё с Uno свой проект за час.
                  • 0
                    Никто не мешает ардуиновский код портировать на арм. Я так делал.
                    • 0
                      Смотря под какой АРМ. Если под ардуино — нет проблем) Мои претензии конкретно к STM32.
                      • –1
                        STM32 сыроват и мне он не очень понравился. Я работал много с lpc2103 самый афигенный. После AVR как космический корабль. Никогда после него в руки не возьму АВР
                        • 0
                          Интересно, впервые слышу. А инструкция по управлению этим космическим кораблём в комплект входит? У STM32 тоже отличная начинка, только работающих примеров — около нуля. А это семейство хотя бы на слуху, об lpc2103 вообще не слышал.

                          Навскидку, в LPC210Х очень мало SRAM. В копеечных STM32 её 32-64К, а тут 2-8К.
                          • 0
                            Они все lpc очень друг от друга отличаются (не совместимы, даже lpc2103 от lpc2104 отличаются абсолютно). Не рекомендую камень для начала. Примеров вообще почти нет, всё писал сам. Но он просто чумовой проц и всё работает. В 2103 по моему 32 кила (плюс там же регистры). Касательно STM32 я сталкивался с тем, что там ошибки в документации, аппаратные проблемы (в частности не работает i2c, реализовывали программно) и т.п. Я не против АВР, но просто они уже устарели, медленные, дорогие, тупые. Давайте двигаться в ногу со временем.
                            • 0
                              Я тоже против AVR. Я за ARM от Atmlel (SAM3X / SAM3A), потому что половина code base для Ардуино там заводится с полпинка. Это Cortex-M3, до 512 КБ флэша, до 100 КБ SRAM.
                • 0
                  Эту машину с дисплеем мы планируем использовать для обучения программированию.
                  ARM я тоже рассматривал, но пока использование Arduino IDE кажется более простым.
                  Может потом сделаю ещё одну версию.
                • +1
                  Правда пока почему-то не получается, чтобы громкость динамика была нормальной, сейчас всё очень тихо. Может кто-нибудь знает как заставить такой динамик из игрушечного телефона звучать?
                  На схеме же выше на выходе цепочка из резисторов, суммирующая каналы, поэтому и тихо. Нужно через усилитель подключать.
                  • 0
                    Ыххх. А хочется без микросхемы AY/YM звук получить. Думаю, скорости даже AVR вполне достаточно для эмуляции. Бродит такая мысль уже пару лет…
                    • 0
                      кстати, есть готовый эмулятор github.com/asashnov/libayemu — но навскидку он хочет 2кб+ памяти для инициализации (что, впрочем, можно прекалькулировать и включить в исходник готовым). На 16кГц моно 8 бит выходе буфер на 1/50 секунды будет занимать всего 320 байт. Из 8 ног контроллера вполне на резисторах делается covox. Никаких ужасно сложных расчетов в библиотеке не наблюдается, должно по быстродействию потянуть.
                    • 0
                      Все было украдено сделано до нас сумасшедшими гениальными спектрумистами.
                      Навскидку с профильного форума zx-pk.ru
                      zx-pk.ru/threads/12485-emulyator-ay-na-avr.html Эмулятор AY на AVR
                      zx-pk.ru/threads/17226-sdelal-vot-ay-player-na-arduino.html Сделал вот AY Player на Arduino…
                      zx-pk.ru/threads/23671-avr-zx-spectrum-v2_0.html AVR ZX Spectrum V2_0
                      Конечно собственные костыли всегда приятнее до последнего сучка, но если люди уже все шишки собрали, отладили код, может стоит воспользоваться им?
                      • 0
                        Спасибо за ссылки. Перед тем как сделать я поискал в интернете, но на zx-pk.ru зайти не догадался.
                        Правда первые две ваши ссылки — это софтверная эмуляция, а мне был нужен аппаратный AY, так как всё процессорное время отъедает дисплей.
                        • 0
                          На самом деле софтовая эмуляция это не так плохо, если она максимально близко соответствует оригиналу. А спектрумисты понимая что старые микросхемы становятся все большей редкостью, которую даже ушлые китайцы не могут сделать заново, сделали программно-аппаратный эмулятор в виде печатной платы с pin2pin совместимостью с оригинальной микросхемой, которую можно просто поставить в панельку вместо оригинальной.
                          Вот кстати ссылка на самое первое обсуждение эмуляции AY на AVR на форуме которое идет с 2009 года zx-pk.ru/threads/10510-emulyator-ay-8910-na-atmega/page33.html Эмулятор AY-8910 на ATMega
                          и вот отсюда www.avray.ru/ru/ay_ym_emulator
                          А если под аппаратным подразумевается FPGA то вроде бы там и такой проект был.
                    • 0
                      Поправьте, если я не прав. У вас получается, что контроллер может проигрывать музыку только в «высокоуровневом» формате .fxm (.ay), который являются блоком данных для спектрумовского проигрывателя. Масса музыки была сделана в других редакторах, или даже с использованием уникального ПО (это было очень распространено в демо). Как насчёт поддержки «дамповой» музыки — это форматы vtx, ym, (e)psg. В них передаются значения регистров чипа YM/AY в реалтайме так, как это происходит на реальной машине. Эмулировать такое намного проще, чем код целого плеера.

                      Насчёт китайского динамика можно предположить, что в вашей реализации он подключается напрямую. Неудивительно, что AY не справляется с такой нагрузкой. Нужен усилитель HЧ, хотя бы на одном транзисторе.

                      Ещё одно существенное замечание насчёт резисторов для каналов A,B,C — у вас они равноправны и микшируются в равных пропорциях. На самом деле в Спектруме это реализовано не так, причём существуют разные реализации микширования. Постараются изложить кратко.

                      пример микширования ABC
                      image

                      Суть такова, что «средний канал» смешивается в равных долях в левый и правый стерео каналы, а «крайние каналы» коммутируются только в соответствующие стерео каналы. Для моно-варианта нужно соблюсти только относительное соотношение среднего и крайних каналов AY/YM. В спектруме было два варианта микширования: ACB/ABC. Этот факт и неодинаковость смешивания существенно влияют на аутентичность воспроизведения мелодии.

                      Вообще, неодинаковость микширования среднего и крайних каналов дало целый пласт демок с использованием так называемого МСС-метода воспроизведения 8-битного потока. Здесь модификация этого же метода. Используя 4-битные ЦАП AY/YM, можно было вполне сносно воспроизводить 8-битные сэмплы. Сейчас не могу точно сказать, но в играх тоже применялись такие звуки в качестве эффектов. Точность воспроизведения поднималась до 7 бит (108 отсчётов) против 4 «законных» (16 отсчётов) именно благодаря перекосу в микшировании. Естественно, такое воспроизведение в эмуляторах AY можно достичь только снимая дамп с регистров в эмуляторе Спектрума, ну или программно, модифицируя код напрямую внутри Спектрума.
                      • 0
                        Спасибо за подробное описание микширования каналов.
                        Я планирую воспроизводить только форматы из музыкальных редакторов. Сейчас выбор пал на FXM из-за Тетриса. Просто я не хочу добавлять слот для SD, а файлы из редакторов достаточно малы, чтобы поместиться в ПЗУ.
                        Дамповые форматы — это хорошо для режима «ipod», чтобы слушать оригинальную музыку, но мне они не очень нужны — всё ведь не охватишь. Кстати, описание таких проигрывателей уже есть на хабре/geektimes.

                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.