2A03使用FC音源『NESizer2』の偽物を作った
作るきっかけ
2019年のある日,以下の動画を見つけました.
ファミコンのメインCPUであるRP2A03に直接マイコンから命令を送って自由自在に音を出すようにするという工作で,当時chiptuneにはまっていた私は「ぜひ再現したい!」と思いました.このプロジェクト自体は回路図やソースコードがGitHubにアップロードされており,こちらに従えば簡単(?)に作ることができます.せっかくなので何かアレンジをしたいと思っていたので,元ネタがAVRを使っているならとPICで同じものを作ってみようと思いました.
しかし,当時はPICを触り始めたばかりで知識も経験も無く,アセンブラ?割り込み?という状態でした.結局その時はCPUに命令を送ることさえできずお蔵入りとなりました.
それから4年,ふとこの工作を思い出し,酸いも甘いも経験してきた今ならできるのではないかと再チャレンジを思い立ちました.
余談:当時は知らなかったのですが,2A03をMIDI音源にするというのは星の数ほど擦られてきた題材だそうで,CPUに命令を送り込む方法が製作者によって設計思想の違いがあり,先行例を調べていて面白かったです.国内だと「ふぁみみみっでぃ」がキットを販売していることで有名だそうです.そういえばコミケで見たことがあるような.
製作過程
ハードウェア編
まずはCPU本体を入手する必要があります.動作不明のジャンクファミコン本体をヤフオクで落札し,はんだを吸い取ってCPUを抜き取りました.
MCUにはPIC16F887を使用しました.何でも屋という安心感があると個人的に思っているマイコンです.
回路自体は上述のGitHub記載の回路図を参考にし,今回は使わないRAM部分を除いた構成としました.また,音が出ているチャンネルがわかるほうが演奏している感が出て楽しいと思ったので,チャンネルの数だけLEDを用意してそのチャンネルが音を出している間だけ点灯するようにGPIOで制御しました.
ソフトウェア編
システム全体の動き
この装置は一種のMIDI音源として動作します.パソコンやMIDI機器とMIDIケーブルで接続し,MIDI信号をUART機能で監視しつつ,信号を受信したらそれを解析,チャンネルと音階を取得して対応する音を鳴らすように2A03に命令を送ります.
2A03へ命令を送る基本的な動作としては,2A03が何らかの命令を実行し終わって同期信号を送る→これをPICで検知して(音を出すといった)次の命令をデータバスに送り込む→2A03が次の命令をフェッチして音を出す というのを繰り返しています.同期を検知して命令を書くところは余計な処理を挟みたくないため,アセンブラで書かれています(ほかはC言語です).
PICへのリファクタリング
AVRからPICにリファクタリングするにあたり,マイコンと2A03の同期を取って命令を送り込むアセンブラコード(2a03.s)を一から書き直すことになりました.
書き直すことになって初めて意識したのですが,AVRは一つの命令を実行するのに最小で1クロック使うのに対して,PICは一命令に最小でも4クロック使うことがわかりました.この仕様は2A03との同期を取るにあたって障壁となり,その結果「運が良ければ同期がとれるし,PICが4クロック使って次の命令を読むまでに2A03の同期信号が途切れることもある」という仕様となってしまいました(結局治せず).同期を取るような工作には必要クロック数が最小限のAVRが向いているのだなあと学習することができました.
音色とタイマー
2A03が出力できる音は 矩形波×2,三角波,ノイズ,DPCMの5つがあります.DPCM以外の4つは音量や周波数をマイコンからの命令で指示すれば出すことができますが,DPCMはこれらと音の出し方が異なり,音声のデジタルデータをサンプリング周波数で送る必要があります.つまり,マイコン側でサンプリング周波数ごとに割り込みをかけて,大容量のROMから音声データを取得するとともにそのデータを2A03に送り込む必要があります.今回は手持ちのケースに収まるようにしたかったので,大きなパラレルEEPROMは置かないことにしました.DPCMは使わず,1台あたり4チャンネルの音源となります.
音色のエンベロープ(特にAttackとDecay)を再現するのにタイマー機能を使いました.音色をオンする命令が出た時のタイマーの値を記録して,一定時間経過ごとに音量を変化させるように命令を送ります.とはいっても下の実演ではあまり効果が表れていませんが.エンベロープを実装するとシンセサイザーって感じがして楽しいですね.
以上をブレッドボードに実装し,プログラムを書きました.その結果,DPCMの操作はできないものの一通りの音を出すことができました.よって基板に起こしてちゃんとした形にする段階に移りました.
組み立て
回路図をKiCadで基板に起こし,JLCPCBに注文して基板を作成しました.一週間で基板が届き,部品を実装して信号を送ってみます.無事に音が鳴って一安心です.
タカチのYM-150に収まるように,主電源スイッチや出力用のイヤホンジャック,MIDI受信/中継用のDINコネクタ,DCジャックと音量調整用のVRを取り付けて1台目が完成しました.
部品に余裕があったので,同じようにもう一台分実装して合計8チャンネル分の演奏装置にしようと思い立ちました.2号機のほうにはオペアンプを使った簡単な加算回路をユニバーサル基板に追加して,1台目の出力と合わせることにしました.1号機にはMIDI Throughのコネクタをつけました.これを経由して2号機にMIDI信号が渡されます.
音量調整に可変抵抗を入れてみましたが,LEDまわりの配線が大変だったのと思ったより見栄えが悪かったので2号機では無くしました.
実演
8チャンネル使用する音源を探していたところ,Ievan Polkkaに辿り着きました.電子工作のルーツであるニコニコ技術部の血がまだ流れている.
Dominoで曲を打ち込み,midファイルを出力してMidiTrailで読み込みます.USB⇔MIDIの変換が必要となりますが,中華製のケーブルは信用ならないので以下の記事を参考にProMicroで変換プログラムを実装しました. wave.hatenablog.com
おわりに
元ネタと比べて明らかに劣っているものの,当初の目標であったMIDI音源のようなものを勉強しながら4年越しで作成でき達成感はひとしおです.せっかくケースに入れたので時々引っ張り出して演奏させたいものです.
最後に,2A03の挙動に関しては以下のWikiに一挙手一投足記されています(特に,NES reference guide→APU を一番参照しました).音を出す以外にもあらゆる命令が記されているのでご興味がありましたらぜひご覧ください.
再掲ですが,回路図とプログラムの一部は以下で公開されているものを参考にしました.