NSF Player for PC-8801


img
とりあえずリハビリということで、手持ちのネタから使えそうなものを88で動かしてみることにしました。

べーマガ誌上など、当時はファミコンの音楽をMMLでコピーしたものは多かったですが、
コードレベルで再現したものはさすがに無かったと思います。

果たして88上でファミコンは動くのか、FM音源でFC音源を再現するとどうなるのか、聞き比べてみると面白いかもしれません。




■ ディスクイメージと使い方

NSF Player for PC-8801 Series 20130312版 (D88イメージとソースコード)


同梱のディスクイメージ nsf88.d88 と makensf88.exe が同じフォルダにあることを確認して
演奏したい NSF ファイルを makensf88.exe にドラッグ&ドロップしてください。
または、同梱のディスクイメージとmakensf88、演奏したい NSF ファイルを同じフォルダに置いて、
コマンドプロンプトから、以下のように入力します。
演奏できる NSF ファイルは、サイズが 128KB 以下で、VRC6以外の拡張音源を使っていないものに限ります。


※ コマンドプロンプトから変換する場合、NSFファイル名にスペースが含まれていると正常に変換できません。
※ > makensf88 "nsf file name.nsf" のようにダブルクォーテーションで囲って指定してください。

D88イメージは NSFと1対1の対応です。複数タイトルを収納することはできません。
ディスクをセットしてロードが完了すると、曲を再生します。カーソルキーの左右で曲が変わります。
ちなみにロードにはちょっぴり時間がかかります。気長にお待ちください。

エミュレータで動かす場合、M88を推奨します。V2 & 8MHz以上 の設定で。
QUASI88は、標準では拡張RAMが設定されていないと思うので、オプションに -extram 4 を付け加えてください。
ただし、ノイズが乗りっぱなしになったり・速度が安定しないなど、再現性はいまひとつです。
エミュレータのオプション「全力駆動」などで高速化すると快適になります。

※「全力駆動」で動かすとvblankの取りこぼしが起きてM88では演奏できなくなる場合があります。

カーソルキーの上で演奏開始、下で演奏停止です。左・右で曲番号を選択できます。


他人様の nsf で恐縮なのですが、mp3に変換した動作サンプルです。こんな感じになるということで。
88で鳴っていると言われても、俄には信じがたいかもしれません。
M88でwaveに書き出しました。ノイズが相対的に大きくなってしまうので、FM音源の音量を上げてあります。
>>> nmm.mp3(ネコミミモード by Chibi-Tech) <<<


標準必要スペック

・128KB拡張RAMボード (Mxシリーズは標準装備)
・FM音源(OPN) I/Oポートは 0x44,0x45 を使用。VRC6使用時はOPNA(0x46,0x47)が必要。
・8MHzモード

4MHzでも動かすことはできますが、速度的にかなり厳しいでしょう。
128KB拡張RAMは NSF の格納に用います。メモリインターフェースを弄れば、VRAMなどの空いているメモリでの代用も可能です。
FM音源も基本的な機能(音程・音量)しか使っていないので、別の音源ボード(PC-8801-10など)で構成することも可能でしょう。

以上を踏まえると、V1モード(初代88に音源を付けた状態)で動かすことも出来なくはない、といったところ。
いろいろ弄る必要はありますが。
再生した音源ログを S98 フォーマットで録音して、別のプレイヤーで再生なんてのもアリかと。


※ 更 新 履 歴 ※
 2009.07.13版
 ・ディスク読み込み部にオーバーランのバグがあったのを修正。
 ・曲選択時に一旦音を消すようにしました。
 ・三角波チャンネルのリニアカウンタの挙動を修正しました。
 ・DPCMのコードを書きました。試行錯誤してみたのですが、まともに音が出ないのと現実的な速度では動かないので未実装のままです。
  88の割り込みの挙動がよくわかりません。
 ・VRC6仮実装。デューティー比固定です。ノコギリ波形chの仕様がよくわかりません。また、M88だと何故か動きません。
  デバッグの困難さに負けてリリース。

 2009.10.24版
 ・掲示板でkashiwaさんに音色をいただいたので、音色埋め込み & duty比実装。thanks!

 2009.11.01版
 ・さらに修正。高速化とTLの扱いを変えてみた。

 2010.05.12版
 ・カーソル点滅停止、ファンクションキーを非表示にした
 ・4byte転送を使ってディスク読み込みを高速化
 ・カーソルキーの上下で演奏開始・停止できるようにした
  アドバイス頂いた UME-3さん、コードを提供してくださった Fu-.さんに感謝します。

 2010.05.31版
 ・バンク未使用のnsfの読み込み処理がバンク使用のものと(事実上)同じになっていたバグを修正。
  今までハングアップして聴けなかったnsfも聴けるようになった(はず)。
 ・nsfサイズ情報をヘッダの使っていない部分に埋め込み。
  nsf本体もロードアドレスに揃えて空値を挿入するようにした。
  読み込み処理自体を書き直してロード高速化。
 ・CPU初期化時にNESのRAMをクリアするようにした。nsf演奏の正式な手順。
 ・曲を切り替えて演奏を開始した時にデューティ比が初期化されないバグを修正。
 ・曲切り替え時に$5FF8-$5FFFのバンク情報を初期値に書き戻すようにした。
 ・APU処理アドレス引きを気持ち程度高速化。
 ・SSGで出力しているノイズの音量を適正レベルにした。2ch合計の結果を半分にするのを忘れていた。
 ・エンベロープ設定($4000,$4004,$400C)を初期化せずに発音を開始するnsfでハングする不具合を修正。
 ・VRC6を改良。再現性が大幅に改善。専用周波数テーブルをVRAMに置いた。とてつもなく重い。
 ・バンクを使わないnsf向けにメモリインターフェースを簡略化した NSF88_H.D88 を付属。
  実機動作に耐えられる速度が期待できる。DPCM使用のものはやや重い。
  イメージ作成はmakensf88.exeによる自動判別。

 2010.06.01版
 ・$4080などサポート外のレジスタに書き込もうとして止まるnsfがあったので対策した。
 ・6502の未公開命令を使っているnsfがある模様。現状では問答無用で停止。

 2013.03.12版
 ・ソースコードを整理。前回入れ忘れていたvrc6.binも同梱。
 ・nes6502emuの高速化。フラグ回りなど全命令で数%程度速くなった。
 ・nes6502emuのバグ修正。ADC/SBC命令でVフラグが変化していなかった他、ROL A/ROR A命令でZ,Nフラグが異常だった。
 ・6502未公開命令を実装。資料によってまちまちな部分もあるので、十分なテストはできていない。



■ NSF Player for PC-8801 Series

再現性は GB版と同等程度、速度については GB版の方が速いのではないかと思います。
以下、実装状況とメモ。

・6502 CPU Emulator

GB版ではNESレジスタ(A,X,Y,SP,PC)は高速メモリに格納していましたが、PC版ではインデックスレジスタと
裏レジスタに格納しています。
アクセス速度はレジスタの方がメモリより高速なので、PC版の方が有利と思われるかもしれませんが、
そこまでの差は出ていないように思われます。
エミュレータを RAM に配置できるので自己書き換えを行ったり、6502の間接アクセスに16bitロードを
使うことで見た目的にはすっきりしたのですが、速度のインパクトには結びつきませんでした。

メモリもPC版の方がリニアに使えて便利です。(トータルのメモリ容量はGB版の方がずっと多いですが)
NESのRAMとROM領域が同じメモリアドレス域($0000-$7FFF)にマッピングされているせいで、切り替えに
若干のオーバーヘッドがあるのが難点です。

意外に遅い原因は、88の8MHzがメモリアクセスウェイトのせいで、実質6MHzと言われているせい??とも思ったのですが
いまひとつはっきりしません。

高速化 
バンク切り替え有り版はとバンク切り替え無し版で専用版を作ればもっと速くなるはず。
NES/NSFの構造に絞った高速化はまだまだ可能だと思います。
JR命令をJP命令に書き換えた方が良いかもしれません(GBはJRの方が速い)。
バグ 何回も見直しはしましたが、GB版から書き直した部分もあって、いまだに不安が残ります。
移植性 メモリーインターフェースがキモなので、多少弄ればGB版と同じくエミュレータ本体はROMに突っ込んでも動くはず。
拡張性 NESのROM領域への書き込みを STA STX STY 命令についてトラップするようにしました。
とりあえず VRC6 用です。他の拡張音源にもチャンネル数が足りていれば、対応できると思います。


・APU Simulator

GB版を踏襲していますが、ハードウェアエンベロープが無いので、ソフトで書き直しています。
音源の割り当ても含めて、一番手の入れ甲斐のあるところでしょう。
精度
音源レジスタアクセスはCPUのアクセスに同期。フレームシーケンサは60Hzです。
割り込みを使って240Hzで動かせば再現性も増すはずですが、それに見合う効果があるかどうかは疑問。
割り当て 矩形波1,2=FM音源のch1,ch2
三角波=FM音源のch3
ノイズ=SSGのch4,5
DPCM=未実装

FM音源・SSG共に音量がリニアではないので、テーブルを挟んで16段階に近似。
SSGは2音より3音の方が精度も高まるのですが、コストもかかるので簡略化。
DPCMは実装する気満々だったのですが、未実装状態でこの重さでは保留せざるを得ない…。
デューティ比 FM音源部での音色設定は初期化時のみ(つまりデューティー比は未実装)。
矩形波はデューティー比50%のPSGっぽい音で、三角波はサイン波っぽい音で固定です。
うまい音色が固まっていないから、というのが理由。

(できれば)直列2オペで良い音色があったら教えてください。
(2オペなのはレジスタ設定の簡略化のため)

FM音源で変位の極端な矩形波を作って、最大変位(TL)を時間軸に沿って弄ることで
波形メモリ音源っぽいものが実現できるのでは無いか、と思ったのですが、
さすがに6502をエミュレートしながらでは無理でしょう。
…S44PLAY を見て妄想しただけですが。
エンベロープ ハードウェアエンベロープが無いので、ソフトで実現。
240Hzの所を60Hzで動かしているため、精度は1/4程度。エンベロープループ含め、それなり?には聞こえるかと。
FM音源の矩形波1,2は SSG-TypeEG が使えそうな気もしますが、音程と共にスピードが変わってしまいそうなので却下。

SSGのノイズ音源はSSG内蔵のハードウェアエンベロープを流用することも考えたのですが、
ノイズを別の音源に置き換えることも考えて、当面このままにしておきます。
エンベロープパターン0と8がそのまま使えて、非常に好都合なのですが…。
長さレジスタ これはGB版とほぼ同じです。60Hz精度で問題無いと思います。
大丈夫、だと思います。
ノイズ NESが5bitで、都合の良いことにSSGも5bitなので、そのまま突っ込んでいます。
再現性という点では、もちろんそのままで良いわけが無いのですが、ノイズの近似をどうやれば良いのか
わからないので、当面このまま。
スイープ GBとほぼ同じです。矩形波1,2での違いは未実装。
周波数 NESが11bitで、FM音源が14bit,SSGが12bitなので、さすがにPC側が有利です。
全11bitに対して近似した値をテーブルに持っています。
発音域(出力する値が 8 以下になると無音になる)は未実装です。
DPCM 実装済みですが、使わないようにしてあります。
1サンプル取得毎にROMアドレス変換が必要になるなど、現実的な速度では動きません。
IRQ 未実装です。処理自体はCPUエミュレータに書いてあります。
NESはCPU1命令毎にIRQをチェックするらしいので、
実装するとなると、速度にかなりの影響が出るでしょう。
速度 GB版から書き直した部分を含めてかなりカオスなことになっているので、整理する必要があります。
FM音源のアクセスウェイトの待ちループの代わりに別の処理を挟む等、工夫をする余地は大いにあります。
テーブルも使用可能なメモリ範囲を確定させれば、もう少しアクセスが楽になるかも。

FM音源の音量は、(2 ^ (-0.125 * TL)) * 127 で計算、近似しています。

音程周波数に関しては、以下のように計算しました。
PC-8801のマスタークロック 3.9936MHz

PSG : n はレジスタに設定する 12bit の周波数値
freq = 3993600(Hz) / 2 / 16 * n (n=0~4095) n=0 の時は n=1

FM音源 : block=(0~7) F-Number=(0~2047)
freq = F-Number * (2 ^ (block - 1)) * (3993600 / 72) / 2^10

一方、NESは以下の式を使いました。n=0~2047
freq = 1789772.5(Hz) / 16 / (n+1)

・全般

GB版と違い音源には余裕があるので、OPNA等を使って対応範囲を広げるというのは当然アリだと思います。
サウンドボード2枚差しでI/Oをずらしてあれば、アクセスウェイトの隠蔽もある程度可能かも。
SUNSOFT-5B音源のように、AY-3-8910互換のものであれば、そのままSSGに突っ込んでみるというのも面白そうです。

プチノイズの発生に関して今ひとつよく分からないので、改良をためらっている部分があります。
キーオン(0x28)レジスタの書き込み、音量(TL)の書き換えでどの程度影響があるのか調査の必要があります。




■ PC-8800 Side

メモリマップは以下の通り。


PC の 0000-7FFF が NES から見た、全メモリ領域となります。
NSFプレイヤーは8000〜に配置されます。

NSF本体を格納する拡張メモリですが、I/Oポート(E3)への書き込みでバンク選択(0-3)が出来ます。
そしてI/Oポート(E2)のbit4(書き)とbit0(読み)で読み書き許可を指示できます(0:不可,1:許可)。
読み書きを不可にすると、同じメモリ領域にある「読める方/書ける方」にアクセスします。
拡張メモリが読めないときはメインメモリを読み、メインメモリに書けないときは拡張メモリに書く、という具合です。

CPUエミュレータは拡張メモリ側(NES的にはROM領域)から命令を一つ拾ってきて実行し、
メモリからの読み込みが必要な場合は、それがROMかRAMかを調べて PCの拡張メモリバンクを切り替え、
読み出し、書き戻しなどを行います。

NSF プレイヤーは NES の音源ドライバの処理を vblank毎に呼び出し、その処理が終わったら
次の呼び出しまで待機しますが、その間 NES の CPU とは独立して動くAPUのシミュレーションを
行います。これが APU のフレームシーケンサです。
フレームシーケンサは、実際には 240hzで各種カウンタ(音長/音量/音程)を増減する役割が
ありますが、このプレイヤーでは 60Hzで動くため、精度は1/4に落ちます。

図でDISK Loaderの部分は、プレイヤーとNSF本体をディスクからロードするプログラムです。
ディスクは N88-BASIC フォーマットで、1トラック16セクタ、1セクタにつき256バイトのフォーマットです。
PC-88はトラック0,セクター1の256バイトをIPLとしてC000番地から読み込み、実行します。
たった256バイトなので、複雑なロードを実現するためには、別のローダーを読み込み、実行しなければなりません。
IPLはLoaderを読み込み、LoaderはNSF本体を拡張メモリにロード、プレイヤーをメインメモリにロードします。
Loaderはトラック0セクタ2,3に配置、プレイヤーはトラック0,セクタ4〜トラック6,セクタ3までを占有します。

肝心のNSFファイルはトラック6,セクタ4から連続して makensf88 を使って書き込まれます。
ぶるー牧場 D88ファイルフォーマットの技術情報があります。

* 音源 : O P N (A) *

音源はI/Oポート(44)(45)を介してアクセスします(標準搭載機種の場合)。
FM音源のデータシートは検索すれば出てくるので省略するとして、
アクセス方法はポート(44)で音源レジスタ選択、ポート(45)でデータ出力となっています。

このレジスタ選択とデータ出力の間にはウェイトを挟まなければならないことになっており、
88のマスタークロックによって以下の通り違いがあります。

OPNOPNA
4MHz17クロック9クロック
8MHz34クロック17クロック

データ出力後にもウェイトがかかるのですが、こちらはポート(44)のbit7にbusyフラグが
出ているので、それをチェックすればOKです。かなり待たされます。
SSG部に関しては、レジスタ指定データ出力共にノーウェイトでアクセスできます。

後は…音程データを書き込む際、Block/F-Numberの上位、の方を先に書き込まないと音が化けます。


■ 後 記

難産。GB雫を終えて「しばらくインタプリタは見たくもない」と思っていたのですが、
なりゆきで88を弄ることに。環境も資料も貧相で目一杯苦労しました。
それでもNSFが初めてうまく鳴らせたときは、やはり感動ひとしお。
FM音源で鳴っているのに妙に優しい音がします。不思議。

とはいっても、今回の NSF Player は本命ではなかったりします。
もう少し高速に動くことを期待していたのですが、本当は SPC-700 を動かしたかったのでした…

8bit chiptune界隈も、昨今のマシンの高速化に従ってどんどん様変わりしていますね。
Flash上で動く NES/GB音源などの環境に加えて、今やFM音源なども扱えるようです。
全ての音源が1台のマシンに集約されていくのを尻目に、こちらはひとりクロスオーバー企画ですが、
仮想環境だけでなく、実機の可能性を拡張していくというのも一つの方向かなと思う次第です。


2010.06.01
いつのまにやらニコニコ動画にアップロードされていたことに気がつきました。動いているMA2に萌えた。
88でファミコンの曲を鳴らしている、と言われても当時の人でさえ「はぁそれで?」という感じなんだろうなぁ。
8bitマシンで同じ8bitマシンをエミュレートしている点や、同時代にありながら絡みそうで絡まなかったOPN(A)と2A03APUなど
文脈を押さえた上でないとワビサビが感じられないかもしれませんね。←なんかオタっぽいな…。
88が元祖の音源(イー○とか)のファミコン移植版を当 NSF Playerで聴くと妙な掻痒感がしたり、そういうのを目指しています。
あと、ロード遅くて本当にごめんなさい。暴走は直っていると思います。


▲ TOP