ファミコンのディスクシステムで1MBのミュージックビデオ──ディスク10枚分を自動で読み替える魔改造<環境整備編>

テクノロジー

スーパーファミコンの新作推理アドベンチャーを作って実機でクリアしたとき、私は「次は、ファミコンで試してみようと思います」と書きました。ファミコン用のフラッシュROMのカセットは手に入りにくく、値段も高いので、手持ちのディスクシステムのRAMアダプターを使うことを検討中だ、と。

検討の結果、ディスクシステムで行くことにしました。ただ、ディスクシステムには、ディスクカード1枚に入る量が少ない、読み込みが遅い、入れ替えは手で、という弱点があります。そこで、ゲームを作る前に、まず開発の環境を整えることにしました。

ちょっとした環境整備のつもりでした。終わってみれば、ディスクシステムのRAMアダプターの性能を限界まで引き出す魔改造になっていました(笑

先に結論を書きます。

  • 実機のディスクシステムで、1,089KBのミュージックビデオを最後まで流しました。 ディスクカード10枚分(20面)を、人の手を使わずに自動で読み替えながら流します。絵は256×136ドット、曲は2分29秒。曲も絵も再生プログラムも、すべてClaude Codeがコードで作ったオリジナルです
  • 仕掛けは、ディスクドライブの代わりになる機械「FDSKEY」の、ファームウェア(中で動くプログラム)の改造です。 ゲームはドライブのモーターをオン・オフする合図で「次はこの面」と伝え、FDSKEYがSDカードから面を差し替えます。合図を送ってから次の面のデータを読み終えるまで(場面転換)が、0.35秒になりました
  • 読み込み中も、絵と曲は止まりません。 Claude Codeが、任天堂のBIOS(RAMアダプターに入っている、ディスクを読むためのプログラム)を使わずに読む部分を一から書き、1バイト届くたびに受け取るようにしました
  • いちばんの山場は、実機でしか起きない不具合でした。 RAMアダプターのメモリを守る見張りの割り込みと、バイトの受け取りが遅れてたまにデータが化ける問題です。2日目は朝9時すぎから夜8時前まで、私がミュージックビデオを実機で12回流し、Claude Codeが結果を読んでは次の版を作りました
  • 作ったのは、すべてClaude Code(Claude Opus 5.5)です。 プログラム(ファミコンのCPU「6502」のアセンブリ、Pythonの道具、C言語で書いたFDSKEYのファームウェアの改造部分)、曲、絵、テストまで。私は、FDSKEYを買って方向を決め、あとはファームウェアを書き込み、実機で動かして結果を伝える、を繰り返しました
  • ゲームを作るための準備なので、<環境整備編>です。 次は、この容量を生かしたゲームを作ります

まず、完成品をどうぞ

明るさが大きく変わる場面があります。明るい部屋で、画面から離れてご覧ください(点滅は1秒に3回までに抑えてあります)。

ニューファミコンとディスクシステムのRAMアダプター、その先にFDSKEY。画面上部のSIDEが読んでいる面、KBが読み込んだ量です。途中から画面のキャプチャーに切り替わります。音の出るところでどうぞ

画面上部の「SIDE」の数字は、いま読んでいる面の番号です。曲が進むにつれて、SIDE 01からSIDE 20まで自動で進み、右の数字は1,089KBまで増えていきます。ディスクシステムのソフトでいえば、ディスクカード10枚を、裏返しと入れ替えを繰り返しながら読んでいることになります。

場面は8小節ずつ、全部で10区間です。題名、回るディスクカード、踊る影(2区間)、惑星、輪になって跳ねる立方体(2区間)、鏡合わせで踊る2人の影(2区間)、最後にクレジット。曲はテクノで、ファミコン本体の音源とディスクシステムの音源だけで鳴らしています。

道具:ディスクシステムとFDSKEY

ディスクシステムのRAMアダプター(HVC-023)。ファミコンのカセットの差し込み口に差します。中にプログラム用のメモリ32KBと絵用のメモリ8KB、ディスクを読むためのBIOSが入っています
ディスクシステムのRAMアダプター(HVC-023)。ファミコンのカセットの差し込み口に差します。中にプログラム用のメモリ32KBと絵用のメモリ8KB、ディスクを読むためのBIOSが入っています

ディスクシステムは、1986年に任天堂が出した、ファミコンにつなぐディスクドライブです。ゲームはカセットではなく、両面のディスクカードで売られていました。ファミコンとドライブの間をつなぐのが、このRAMアダプターです。プログラムはディスクからRAMアダプターのメモリ(32KB)に読み込んで動きます。

FDSKEY。ディスクドライブの代わりに、RAMアダプターのケーブルの先につなぎます。ディスクの中身はmicroSDカードの中。小さな画面で、読んでいるファイルが見えます(意外と多機能
FDSKEY。ディスクドライブの代わりに、RAMアダプターのケーブルの先につなぎます。ディスクの中身はmicroSDカードの中。小さな画面で、読んでいるファイルが見えます(意外と多機能

FDSKEYは、ClusterM氏が作った、ディスクドライブの代わりになる機械です。ディスクの中身(.fdsファイル)をmicroSDカードから読み、本物のドライブのふりをしてRAMアダプターに流します。設計もファームウェアも公開されていて(GPL-3.0)、ファームウェアはSDカードから書き換えられます。今回の改造は、ここが肝でした。

今回の環境。ニューファミコン(AV仕様)にRAMアダプター、その先にFDSKEY。モニターに出ているのが、ミュージックビデオの題名です
今回の環境。ニューファミコン(AV仕様)にRAMアダプター、その先にFDSKEY。モニターに出ているのが、ミュージックビデオの題名です

ディスクシステムで大きなゲームを作るときの壁は、3つあります。

壁数字
入る量が少ない1面は約64KB(.fdsで65,500バイト)。ディスクカード1枚は両面で2面
読み込みが遅い転送は1秒に約12.5KB。BIOSで読むと、1回に6秒かかった(実測)
入れ替えは手で面を変えるには、人がディスクを抜いて裏返すか、別のディスクを入れる

当時の大作でも、ディスク2枚(4面)くらいでした。私が筋金入りの信者である『ファミコン探偵倶楽部 消えた後継者』も、前編と後編がディスク1枚ずつで、別々に発売されました。

1日目:面の自動切り替えと、BIOSを使わない読み込み

FDSKEYが届いた日の昼から、実機での作業が始まりました。

まず測る

最初に、Claude Codeが作った測定用のディスクを実機で動かしました。分かったのは、BIOSの読み込みの遅さです。

  • BIOSで読むと、何を読んでも6.0秒。 約48KB入った測定用の面では、1KBのファイルでも16KBでも同じでした。BIOSは毎回、面の最後まで読み通すので、時間は面に入っているデータの量で決まります
  • 1回の読み込みは、約2.2秒+面のデータ量÷12.5KB/秒。 小さい面(約18KB)なら3.68秒でした
  • 書き込みは12.1秒。 BIOSは書いたあと、読み戻して確かめるからです

測定用のディスクは、スプライトの化け(RAMアダプターと本体の組み合わせによっては、絵が崩れるという話があります)も調べます。結果は誤り0。はんだ付けで手を入れる必要はありませんでした(ホッ

ゲームから「次はこの面」と伝える

FDSKEYは、RAMアダプターから見ればただのディスクドライブです。ゲームのプログラムが直接FDSKEYに話しかける線はありません。FDSKEYに見えるのは、ドライブへの書き込みと、モーターのオン・オフくらいです。

そこでClaude Codeは、FDSKEYのファームウェアを改造して、ゲームの合図で面を差し替えられるようにしました。最初はBIOSでファイルを書き込んで命令を伝える方式、最後はモーターを短くオン・オフするモールス信号のような合図(24ビット)です。私はFDSKEYのボタンを4つ押しながら電源を入れて、新しいファームウェアを書き込むだけです。

ファームウェアの最初の版は、新しい面をいきなり壊しました。BIOSの書き込みは、書いたあとにもう一度ディスクを回して読み戻します。改造したファームウェアが、その2回の間に面を差し替えてしまい、BIOSは「書いたものと違う」と判断して、新しい面に書き直してしまったのです。SDカードの中のディスクは、見事に壊れました(南無

Claude Codeはファームウェアの第2版で、読み戻しが終わるのを待ってから差し替えるようにし、自動の切り替えは成功しました。ただし、場面転換(面を切り替えて、次の面のデータを読み終えるまで)に、大きい面からだと17秒ほどかかります。

17秒から0.35秒へ

場面転換1回にかかった時間(実機で測定。大きい面0から小さい面1へ切り替えたとき)。BIOSを使わない読み込みに変えて、一気に縮みました
場面転換1回にかかった時間(実機で測定。大きい面0から小さい面1へ切り替えたとき)。BIOSを使わない読み込みに変えて、一気に縮みました
ファームウェアの版やり方場面転換(面0→面1)
第2版BIOSで書いて命令、BIOSで読む16.9秒
第4版ファームウェアの待ちを詰める13.5秒
第6版BIOSを使わない読み込み、モーターの合図0.98秒
第7版合図を短く、SDカードの読み込みを速く0.35秒

BIOSを使うやり方は、命令を書き込む面が大きいほど遅くなります。小さい面どうしなら、第2版で9.5秒、第4版で5.3秒でした。

BIOSには、モーターを回して読むたびに自分で合計0.93秒待つ癖があり、BIOS経由では小さい面どうしでも4.5秒くらいが限界だとClaude Codeは見積もりました。そこでClaude Codeは、ディスクを読む部分を一から書きました。この読み込みは、欲しいファイルがそろったら、すぐにモーターを止めます。

この日のうちに、もう1つ大事なものができました。読み込みながら、画面と音楽を動かし続ける仕組みです。ディスクから1バイト届くたびに割り込みで受け取り、その合間にゲームが動きます。割り込みは、CPUにいまの仕事をいったん止めさせて、急ぎの処理を先にさせる仕組みです。実機で測ると、読み込み中もCPUの64〜87%がゲームに残りました。

ミュージックビデオを流しているときのFDSKEYの画面。FILE 07/14から14/14まで読むと、次の面に切り替わってまた先頭から。人は何も触っていません

ファームウェアは、この日だけで第1版から第8版まで進みました。

ミュージックビデオを作る

ゲームを作る前の「動作確認」として、何を流すか。私はオリジナル曲のミュージックビデオを選びました。読み込みの速さも、読みながら絵と音を動かす力も、一目で分かるからです。

ここでも、作ったのはClaude Codeです。

  • 曲:テクノで2分29秒(80小節、テンポ128.6)。ファミコン本体の音源(矩形波2つ、三角波、ノイズ)と、ディスクシステムの波形メモリ音源で鳴らします。楽譜はPythonで書かれていて、6502のアセンブリで書いた再生プログラムが鳴らします
  • 絵:図形も、踊る影の人形も、文字のフォントも、すべてPythonのコードで描いています。画像生成AIは使っていません
  • 再生プログラム:6502のアセンブリ。ディスクから届いたデータを受け皿にためて、1コマずつ画面に書きます

私が頼んだのは、題名を英語にすること、画面に読んでいる面と量を出すこと、そして人の形が踊る場面です。踊る影は、ポニーテールの女の子のシルエットで、ポーズは11種類。Claude Codeがこの作品のために作ったオリジナルです。

ファミコンの画面に、どうやって書くか

ファミコンは、画面を描いている最中には、画面のメモリに書き込めません。書けるのは、描き終わってから次の画面を描き始めるまでの短い時間だけです。

そこでClaude Codeは、絵の高さを136ドットに抑え、絵の下の帯の間は画面を描くのを止めて(だから黒く映ります)、そこでも書き込めるようにしました。帯の始まりを知るために使ったのが、サンプル音を鳴らす音源(DMC)です。再生プログラムは毎フレーム、無音のサンプルを鳴らし始め、鳴り終わった瞬間を合図に書き込みます。音源を時計として使う、という発想です(ファミコンらしい

さらに、Claude CodeがPythonで書いた変換プログラム(エンコーダー)が、いつどのファイルを読むかを前もって計画し、映像のデータの中に「ここで次のファイルを読め」という命令を埋め込んでいます。再生プログラムは、その命令どおりにFDSKEYへ合図を送るだけです。

項目中身
絵256×136ドット、1場面4色、毎秒60コマ(変化の大きいところは前のコマを出し続けるので、平均は毎秒約36コマ)
長さ2分29秒(8,960フレーム)
データ1,089KB(4KBのファイル273個)を20面に。読み込みは78回
曲ファミコン本体の音源とディスクシステムの音源(サンプル音の音源は時計に使うので使わない)
点滅1秒に3回まで、赤い点滅なし(NHKと民放連のガイドラインに合わせ、Claude Codeが機械で検査)

最後の点滅の決まりは、私が光過敏性発作のことを気にしたのがきっかけで、Claude Codeが調べて決めたものです。

2日目:実機でしか起きない不具合

1日目の夜中、ミュージックビデオの第1版(436KB)は、実機で最後まで流れました。調子に乗った私は、もっと大きく、もっと滑らかな第2版(1MB超え)を選びました。

エミュレーターでは、第2版は完璧に動きました。ところが朝9時すぎ、実機に入れると、映像が始まってすぐに読み込みが全部失敗しました。ここから、夜8時前までの長い1日が始まります。

正体不明の割り込み

Claude Codeが記録を足した版を何枚も作り、私が実機で流して結果を伝える。その繰り返しで分かったのは、来るはずのない割り込み(IRQ)が入ってくることでした。しかも、エミュレーターでは一度も起きません。

Claude Codeは、待ちの処理の置き場所を変えるなどして、割り込みをかわす版を作りました。午後3時ごろ、ミュージックビデオの第2版は、実機で初めて最後まで流れます。ただ、ところどころ画面が崩れていました。

同じころ、Claude Codeは引き金を探す試験用のディスクも作っていました。結果は、CPUがRAMアダプターのメモリだけを読み続けるループで、1フレームに約5回、割り込みが入るというものでした。

正体は、メモリの見張りだった

手がかりは、RAMアダプターの状態を示すレジスタの、ある1ビットでした。NESdevというファミコンの技術資料のサイトでも、メインのページには今も「正体不明」と書かれているビットです。Claude Codeは、同じNESdevの中で、有志(TakuikaNinja氏)が2025年から実機で確かめながらまとめているページ(資料)を見つけました。

正体は、RAMアダプターのメモリ(DRAM)を守る見張りでした。DRAMは、ときどき中身を読み直して書き戻す「リフレッシュ」をしないと、中身が消えていくメモリです。RAMアダプターは、CPUがRAMアダプターのメモリ以外に触っているすきにリフレッシュをします。CPUがRAMアダプターのメモリばかり読み続けると、リフレッシュが追いつかず、見張りが割り込みで知らせてくる、という仕組みです。

ミュージックビデオの再生プログラムは、まさにRAMアダプターのメモリの中で、ぐるぐる待つ処理をしていました。主なエミュレーターは、この見張りをまだ再現していません。エミュレーターで起きなかったのは、そのためです。

画面の崩れは、メモリの中身が消えたせいなのか。Claude Codeは、それを確かめる試験用のディスクも作りました。

  • 見張りは、計算では鳴らないはずの条件でも鳴る。 この本体では、資料の計算より2割ほど多くRAMアダプターのメモリ以外に触らないと、見張りが鳴りました。本体が温まると、さらに鳴りやすくなりました
  • DRAMは意外と丈夫。 リフレッシュを止めても、2.9秒までは1ビットも変わりませんでした。8.8秒で、やっと1ビットだけ

メモリの中身が消えたせいではありませんでした。

壊れたのは、届いた時点だった

夕方5時すぎ、Claude Codeは、見張りが鳴ったらBIOSの中の「少し待つだけの処理」を呼ぶ版を作りました。CPUがBIOSのROMの中で時間をつぶしているあいだに、RAMアダプターがリフレッシュを済ませる。これが見張りへの決まった応え方です。この版では、映像のデータの1コマずつに確かめ用のバイトを足してあり、再生プログラムが、届いた直後と画面に出す直前の2回、コマを確かめます。

第2版は最後まで流れました。届いた直後の確かめで見つかった壊れたコマは14個。画面に出す直前の確かめでも14個で、壊れたのは届いた時点でした。

Claude Codeが疑ったのは、FDSKEYがSDカードを読むところです。元のファームウェアは、SDカードの読み込みの誤りを確かめていなかったからです。Claude Codeはファームウェアの第11版で、SDカードから読んだ512バイトごとに確かめて、合わなければ読み直すようにしました。

結果は、SDカードの読み込みの誤りは0回。SDカードは無実でした。それでも、壊れたコマは5つ残りました。

「次のバイト」という手がかり

決め手は、壊れ方でした。Claude Codeは、壊れたコマを元のデータと照らし合わせて、ある1バイトが「次のバイトの値」に置き換わっていることに気づきます。2回目の再生の1件は、はっきりこの形でした。1回目の1件も、同じ形で説明がつきます(こちらは1ビットだけの化けとも読めます)。

ディスクのデータは、1バイトずつ届きます。プログラムが1バイトを受け取るのが遅れると、受け口にはもう次のバイトが入っています。Claude Codeの見立ては「受け取りが間に合っていない」でした。

Claude Codeは、エミュレーターの上で、全編約110万バイトについて「届いてから受け取るまで」の時間を測りました。

届いたバイトを受け取るまでの時間(エミュレーターで測定)。直す前は、毎フレームの割り込みと重なると125サイクルまで遅れていました。実機では次のバイトが約143サイクルで届くので、ギリギリです(冷や汗
届いたバイトを受け取るまでの時間(エミュレーターで測定)。直す前は、毎フレームの割り込みと重なると125サイクルまで遅れていました。実機では次のバイトが約143サイクルで届くので、ギリギリです(冷や汗

ふつうは約40サイクル(約22マイクロ秒)で受け取れています。ところが、毎フレーム1回入る画面の割り込み(NMI)とちょうど重なると、その分だけ遅れ、最大125サイクルになっていました。

エミュレーターでは、次のバイトが届くのは約149サイクル後なので、これでも間に合っています。ところが実機のFDSKEYは、バイトの間隔が約143サイクルとエミュレーターより少し短く、125サイクルまで遅れると、次のバイトまで20サイクルもありません。Claude Codeの見立てでは、実機ではこのギリギリの受け取りが、たまに間に合わなかったわけです。エミュレーターで123〜125サイクルまで遅れた回数(約70回)は、実機で壊れたコマの数(1回の再生で5〜14個)と同じ桁でした。

Claude Codeは、NMIの処理を必要最小限に削りました。約80サイクルかかっていたのを約47サイクルに。エミュレーターで測り直すと、いちばん遅い受け取りは101サイクルまで下がりました。

崩れ0

夜8時前、直した版を実機で流しました。

再生のあとに出る一覧。2行目の「S 078 E 000」は、読み込み78回で失敗0回。下から4行目は、届いた直後に確かめたコマが8,897、画面に出す直前が8,913で、壊れたものはどちらも000。大きな数は2つに分けて出るので、8,897は「034 193」(034×256+193)と表示されています(数字の壁
再生のあとに出る一覧。2行目の「S 078 E 000」は、読み込み78回で失敗0回。下から4行目は、届いた直後に確かめたコマが8,897、画面に出す直前が8,913で、壊れたものはどちらも000。大きな数は2つに分けて出るので、8,897は「034 193」(034×256+193)と表示されています(数字の壁

読み込み78回、失敗0回、壊れたコマ0。 画面の崩れも、もうありません。私の感想は「今回のMV再生は何も問題がありませんでした」の一言です。

この日、実機で流したミュージックビデオの第2版は12回。朝の失敗から、約10時間半かかりました。

人間がやったこと、Claude Codeに任せたこと

人間(私)Claude Code
「次はファミコン」と決める。FDSKEYを買うディスクシステムとFDSKEYの調査、設計
自分のRAMアダプターからBIOSを吸い出す(エミュレーターの試験用。配布はしない)測定用のディスク、エミュレーターの自動テスト
FDSKEYにファームウェアを書き込み、実機で試して、写真と結果を伝える(2日間、何度も)FDSKEYのファームウェアの改造(Cで約760行、11版)、ディスクを読む部分(6502)
ミュージックビデオにすると決める。題名、画面の表示、踊る影の注文。点滅への配慮を頼む。1MB超えの第2版を選ぶ作曲、絵、再生プログラム、エンコーダー、点滅の検査
「ところどころ崩れる」「画面の上が切れている」などの報告記録の読み解き、原因の仮説と試験、修正(別のClaudeによる見直しも)
写真と動画の撮影、動画の編集この記事の下書きと図

Claude Codeが書いたコードは、6502のアセンブリが約7,700行、Pythonの道具が約2,600行、エミュレーターの試験(Lua)が約1,900行、FDSKEYのファームウェアの改造がCで約760行でした。構想から完成まで、約3日です。

正直、気になっていること

  • 立方体の場面は、少しカクつきます。 変化の大きいコマは書き込みが追いつかず、前のコマを出し続けるからです。後半の場面では、半分くらいのコマがこれでした。滑らかにするなら、エンコーダーの工夫が要ります
  • 実機で試したのは、本体1台、RAMアダプター1台だけです。 Claude Codeの見立てでは、メモリの見張りは本体や温度で起き方が変わります(この本体でも、温まると鳴りやすくなりました)。ほかの機体で同じように動くかは分かりません
  • エミュレーターでは見つからない不具合でした。 今回の2つの不具合は、どちらも実機でしか起きません。これからも、実機で試すのは私の役目です
  • 改造したファームウェアとディスクイメージは、配布していません。 FDSKEYはGPL-3.0なので、配るならソースも公開することになります

これから:容量を生かしたゲームを

容量の上限は、大幅に上がりました。今のファームウェアは、1つのディスクイメージで255面まで扱えます。データにすると約14〜15MB、ディスクカード約128枚分です。当時のディスク2枚の大作の、60倍以上になります。

せっかくなので、この容量を生かしたゲームを作ってみたいと思っています。

しかも、255面で終わりではありません。Claude Codeによると、面の番号を1バイトから2バイトに広げれば、上限はSDカードの1ファイルの上限(FAT32で4GB)まで上がります。GB級のファミコンのゲームです。ファミコンで、オープンワールドのRPGを作るのもいいかもしれません。

とはいえ、ディスクの自動読み込みやストリーミング読み込みには、癖があります。ディスクは頭から順に読むもので、好きな場所にすぐ飛べるわけではありません。プレイヤーがどこへ歩いていくか分からないRPGならではの難しさがありそうです。それでも、挑戦してみたいと思います。

まとめ

  • ファミコンのディスクシステムで、1,089KB(ディスク10枚分)のミュージックビデオを、実機で自動で読み替えながら流せた
  • 鍵は、FDSKEYのファームウェアの改造と、BIOSを使わない自前の読み込み。場面転換は17秒から0.35秒に
  • 本当の敵は、実機でしか起きない不具合だった。RAMアダプターのメモリの見張り、受け取りの遅れによるバイト化け
  • 手がかりは「壊れ方」。1バイトが次のバイトに置き換わっていたことから、原因が分かった
  • 容量の上限は255面(約14〜15MB)。広げればGB級も見えてくる

ちょっとした環境整備のはずが、気づけば魔改造。次はいよいよ、この容量を生かしたゲームです。お楽しみに。

環境は整った。次は、ゲームだ。

使わせていただいたもの:FDSKEY(ClusterM氏、GPL-3.0)、Mesen(エミュレーター、GPL-3.0)、cc65(アセンブラ)、NESdev Wiki(ファミコンの技術資料)、Brad Smith氏のディスクシステム用のサンプル、TakuikaNinja氏のFDS-BIOS-Dumper。

「ファミコン」「ファミリーコンピュータ」「スーパーファミコン」「ディスクシステム」は任天堂株式会社の商標または登録商標です。この記事は個人の試みで、任天堂とは関係ありません。

コメント

タイトルとURLをコピーしました