По адресу 0xC043 в регистр `A` загружается значение 0×3A. Казалось бы, это бесполезное действие, потому что следующая команда уменьшает это значение на единицу. Почему бы сразу не загрузить в регистр `A` значение 0×39?
Идея заключается в том, что команда по адресу 0xC048 записывает уменьшенное (и, кстати, защищенное от переполнения благодаря инструкции `AND 3Fh`) значение по адресу операнда первой команды (0xC043), прямо в код программы, так что в следующий раз команда `LD A, ...` считает значение на единицу меньше, через раз — еще меньше и так далее. Другими словами, по адресу 0xC044 хранится счетчик, который уменьшается на единицу, каждый раз при выполнении данного кода.
К счастью, сейчас память программ доступна только для чтения либо по аппаратным причинам, как например в микроконтроллерах, либо из-за запрета прямой записи в область кода операционными системами и процессорами.
Реализация на Windows Я начал с того, что написал бОльшую часть кода сначала для Windows, чтобы было легче производить отладку. «Борьбу» с Arduino оставил на момент адаптации кода к аппаратным возможностям платы. Мою реализацию для Windows можно собрать самостоятельно (каталог win-implementation у меня на GitHub ; понадобится компилятор от Visual Studio 2017) или запустить готовые сборки: мелодия 1 , мелодия 2 .
Пара комментариев о том, как все реализовано.
Чтобы не возникло потом проблем с переносом, я старался писать код, ориентируясь на восьмибитную природу микроконтроллеров ATMega. Поэтому он изобилует типами ’uint8_t’ и ’uint16_t’.
Для преобразования исходного музыкального файла в код, пригодный для компиляции, я написал небольшой скрипт на python (каталог extractor у меня на GitHub ), который копирует данные мелодии в массив байт и извлекает некоторые адреса:
адрес начала данных мелодии (без учета плеера), адрес таблицы позиций, адрес таблицы банков инструментов для позиций. В оригинальном спектруме звукогенератор AY-3-8912 производил сигналы прямоугольной формы, которые раньше мне казались грубоватыми. Изначально цели добиться аутентичного звучания передо мной не стояло, поэтому в первой версии я пробовал генерировать синусоидальные сигналы, вместо меандра. Оказалось, что вместе с прямоугольной формой сигнала ушли и все гармоники, из-за чего звук стал очень глухой. Так что от синусоидальной формы сигнала пришлось отказаться. Также в реализацию была добавлена таблица маппинга громкости. Напомню, что амплитуда выходного сигнала звукогенератора имеет 15 градаций. Энтузиасты, разрабатывающие эмуляторы AY-3-8912, установили, что зависимость между значением в регистре громкости и напряжением на выходе вовсе не линейная. Так появились таблицы для соответствующего преобразования. В «больших» эмуляторах такие таблицы 16-битные, в нашем же эмуляторе для быстроты расчетов используется 4 бита. Поэтому если заглянуть в исходники, можно увидеть, что уровни громкости от 0 до 3 вообще не воспроизводятся, а уровни от 4 до 9 имеют незначительную громкость на выходе. Действительно, субъективно я это почувствовал еще во время моих экспериментов по написанию музыки. Думаю, что инженеры, разрабатывающие звукогенератор, сделали это намеренно, в соответствии с законом Вебера-Фехнера (интенсивность ощущения пропорциональна логарифму интенсивности раздражителя).
В музыкальном модуле от Dizzy IV используются несложные шумовые эффекты. Их удалось реализовать при помощи простого генератора случайных чисел, работающего по алгоритму xorshift .
Реализация на Arduino Поскольку проект делался исключительно для веселья, я выбрал плату Arduino Nano. У нее широко распространенный микроконтроллер, но он менее всего подходит для решения этой задачи. Можно было бы выбрать, например STM32, но там есть даже ЦАП, поэтому было бы совсем неинтересно, тем более на STM32 удалось запустить эмуляцию ZX Spectrum, включая генерацию видеосигнала. Что уж говорить о Raspberry Pi и аналогах, ведь на них можно эмулировать спектрум, не написав ни строчки кода.
Генерация ШИМ сигнала обеспечивается TIMER2, вывод OC2B или PD3 (или вывод D3 в терминологии Arduino). Частота ШИМ сигнала выбрана достаточно высокой — 31373 Гц, поэтому выход Arduino удалось подключить напрямую к портативной колонке без каких-либо фильтрующих цепей, посторонние призвуки отсутствовали.
На плате Arduino Nano можно управлять только двумя светодиодами. Я сделал так, что интенсивность одного из них зависит от громкости мелодии. Для простоты реализации, ШИМ-сигнал для этого светодиода формируется из прерывания второго таймера. Это происходит достаточно часто, но до тех пор пока нет цели сэкономить процессорное время, можно оставить так. Другой светодиод управляется через преобразователь uart -> usb, тут удалось вывести на него индикацию канала шума.
Выбор мелодии пока жестко задан программно, но его можно легко поменять, вызывая функцию ’get_music_data_1′ или ’get_music_data_2′. Для первой композиции главный голос находится в канале A, для второй — в канале B, это надо учитывать при визуализации мелодии на светодиодах.
Проект можно собрать из каталога arduino-sketch и послушать мелодии на своем Arduino или посмотреть получившийся результат на видео.
Видео мелодии 1 VIDEO Видео мелодии 2 VIDEO Как я уже писал, все исходники проекта можно найти на Github .
Автор статьи: Антон Дмитриевский, Максилект.
P.S. Когда-то мы с другом делали клон известной игры «Color Lines» (тогда было модно делать клоны именно этой игры, их тысячи) и там в дополнение к красивой графике у нас тоже была достойная, на мой взгляд, музыка. Представляю вашему вниманию интро для этой игры. А если возникнет желание послушать внутриигровую музыку, то придется запустить игру в эмуляторе — образ диска TR-DOS можно скачать здесь , после запуска необходимо нажать на пункт меню «Выбор мелодии».
Видео с интро VIDEO P.P.S. Выражаю благодарность всем авторам эмулятора Unreal Speccy, участникам проекта speccy.info.
P.P.P.S. Мы публикуем наши статьи на нескольких площадках Рунета. Подписывайтесь на наши страницы в VK , FB , Instagram или Telegram-канал , чтобы узнавать обо всех наших публикациях и других новостях компании Maxilect.