Олдскул, хардкор, AY-3-8912. «Железный» чиптюн с последовательным входом
Клона Spectrum 128K, оснащенного музыкальным сопроцессором AY-3–8910 (YM2149F) у меня не было. Был 48K с расширенной клавиатурой и убогим блоком питания, перегревающим внутренности через час-два работы. От этого, помнится. домики посреди моря в Sim City образовывались и другие веселые артефакты. Но к делу данные воспоминания не относятся. Вдохновившись материалом tronix286, я решил восполнить пробел в ретро-образовании и склепать что-нибудь на легендарном (и при этом, легко добываемом и недорогом) чипе.
В ходе изучения различных поделок, идея сформировалась следующая: надо делать модуль с последовательным (UART) входом. Чтобы его уже можно было подключить с минимальными затратами к любому девайсу, добавляя тем самым +146 к чиптюновости. В процессе также было решено освоить пару дополнительных навыков, вроде программирования AVR и изготовления печатных плат с применением фоторезиста.
Сразу опишу результаты: модуль разработан и пиликает, побочные навыки прокачаны, грабли собраны, можно радоваться, расширять и углублять.
Синтезатор
AY-3–8910 — это большой красивый DIP-чип на 40 ножек. Помимо нужных вещей, там еще два 8-разрядных порта, которые для вывода звука нормальными людьми (не пытающимися на них сколхозить адский аналог Covox) не используются. AY-3–8912 не менее красив, но одним «лишним» портом обделен и упакован уже в DIP28. И еще бывает AY-3–8913, вообще без параллельного порта (DIP24). И это только General Instrument / Microchip. Yamaha клепала еще больше вариаций: от YM2149F (аналог AY-3–8910 с делителем тактовой частоты) до YMZ284 (DIP16, один смикшированный выход каналов). Подробнее о чипах на Wiki (англ.).
Управление
Для загрузки данных используется 8-разрядный параллельный порт. Плюс, три управляющих линии (одну, которая BC2, подтягиваем к плюсу питания). Логика следующая:
1. Исходное состояние — BC1=0, BDIR=0.2. Устанавливаем на ножках порта AY адрес регистра.3. BC1=1, BDIR=1. Загрузка адреса. Задержка между включением линий должна составлять не более 50 нс, поэтому всякие медленные подергивания ножек (типа ардуинского DigitalWrite) не годятся, надо, например, PORTC |= 0b00110000;4. BC1=0, BDIR=0.5. Устанавливаем на ножках порта AY значение регистра.6. BC1=0, BDIR=1. Загрузка значения.
Повторяем последовательность 14 раз (для каждого нужного регистра). И все это — 50 раз в секунду. Получаем музыку.
Выбор конкретного железа
В моем случае следующий:1. AY-3–8912 — был дешевле.2. Atmega8A (DIP28) — доступно, достаточно выводов. 3. Кварцевый генератор на 4 МГц — для тактирования AY.4. Счетчик К555ИЕ5 — как делитель частоты для тактирования AY.5. Кварц на 16 МГц — для Atmega8A.6. Для подключения к ПК — USB UART на FT232R.
Плата разведена именно под это дело. В процессе отладки и более вдумчивого изучения возникли следующие мысли:
1. Если использовать YM2149F, то не нужен счетчик, т.к. в этом чипе есть встроенный делитель частоты на 2.2. Похоже, что кварц для Atmega тоже не нужен — все прилично работает и от внутреннего генератора на 8 МГц.3. В теории, можно попробовать вообще избавиться от кварцевого генератора для AY, если поковыряться с аппаратными таймерами и счетчиками Atmega. Но! В этом случае мы сможем тактировать AY только на 2 МГц. А по-хорошему, надо иметь возможность тактирования на 1.7(много цифр) МГц — как это делается в Speccy. У меня кварцевый генератор на 4 МГц стоит в DIP-колодке, чтобы потом его заменить на 3.5(много цифр) МГц.
Выход звука срисован у tronix286, там горстка резисторов и два конденсатора.
Софт
Для вдохновления изучалась вот эта (недо)реализация. Там описан общий принцип работы связки «источник — UART — Atmega — AY», но использование на «меге» загрузчика Arduino в данном случае показалось мне совершенно лишним. Ну и, программа на ПК, написання на C#, мне не понравилась. Шарп здесь примерно так же «нужен», как и Arduino. Формат YM разложен по полочкам здесь.
Прошивка Atmega
Исходный код и hex доступны на гитхабе (ссылка в конце материала), пробегусь просто по основным функциям.
valToPort — запись 8-разрядного значения в «порт», состоящий из половинки порта B и половинки C. Так было удобнее разводить.sendToAY — запись 8-разрядного значения в регистр AY. Здесь как раз реализована логика, описанная в пункте «Управление».setup — инициализация портов и UART.main — зацикленное «получить 16 байт — записать в AY».
Демонстрационный пример на PC
Написан на Python 3 с использованием PySerial. Как и прошивка, лежит на гитхабе. Берет файл 1.ym (несжатый!) из текущей директории, разбирает его и заталкивает в COM6. Ради интереса пример проверен на OS X, работает «из коробки», достаточно только поменять название порта. Подозреваю, что столь же успешно будет работать на Linux, в т.ч. на «малинке».
В выдаче дампа регистров на AY есть один нюанс. Формат YM хранит данные в виде «все значения регистра 0, все значения регистра 1…». Это очень правильно с точки зрения дальнейшего сжатия. Я же работаю с несжатым YM, и мне нужно выдавать пачки байт «регистр 0, регистр 1…». Для PC задача решена в лоб — читаем данные из файла в нужном порядке в большой массив, затем из него последовательно отдаем контроллеру. Когда нужно будет делать «головное устройство» на базе чипа с малым объемом памяти, придется изобретать какие-то буферы.
Итого
Стоит помнить о том, что перед запуском воспроизведения неплохо бы сбросить «мегу» и AY. Потому что если в буфере контроллера что-то есть, при передаче новых данных весь поток сместится, и звуки будут воистину душераздирающие. Пин сброса на модуле предусмотрен, но в демонстрационном примере не используется. Сброс осуществляется тычком провода в сидящий на «земле» корпус кварцевого генератора.
Имеется и недопойманный баг. Который, наверное, прячется где-то в районе «Windows 10 — Python 3 — UART». Периодически скорость обновления падает с 50 Гц до 20 (осциллоскопировано). Системы в явлении не обнаружено, на другом компьютере глюк не воспроизвелся. Если поймаю когда-нибудь, сделаю UPD.
В дальнейшем модуль будет прикручен к находящейся в процессе разработке поделке, об этом в относительно обозримом будущем будет статья. Ну, и к «малинке» надо попробовать подцепить. Простор для экспериментов имеется.
Исходники, разводка платы (SL6), схема модуля (Eagle) на гитхабе.