2017年8月13日日曜日

ファミコンのステートセーブ(QuickSave)に必要なストレージ容量を計算

だいたいのファミコンのエミュレータには、ステートセーブ機能(マシンの情報を丸ごと保存して復元できる機能)が付いていますが、その中には一体どのような情報が何バイトぐらい記録されているのか、興味本位で調べてみました。(当初、お手軽にググって調べれば分かるだろうと思っていたのですが、良い感じに纏まった情報がパッと見つからなかったので)

調査方法は、Nestopiaというlibretroを使ったNESエミュレータ(OSS)のソースコードを解析する形で実施しました。(たぶん、それが一番楽だと思います)

なお、オリジナルのNestopiaのソースコードはコチラにありますが、本記事では見やすさを優先してGitHubにミラーされている以下のリポジトリ(のバージョン1.47)を参照します。
https://github.com/rdanbrook/nestopia

全体像


上記から、大まかに以下5項目のデータを保持していることが分かります。
  1. CPU: Central Processing Unit
  2. APU (CPU内部): Audio Processing Unit
  3. PPU: Picture Processing Unit
  4. IMG: イメージ?
  5. PRT: 拡張ポート
① CPU


ファミコンのCPUはRP2A03というMOS 6502のカスタム品(リコー製)です。
(オリジナルのMOS 6502との違い)
  • 二進化十進表現(BCD)関連の機能が削除されている
  • APU(後述)が付いている
  • DMA関連の機能が追加されている

1-1 REG: レジスタコンテキスト (7byte)
  • pc (プログラムカウンタ) 2byte
  • s (スタックポインタ) 1byte
  • p (ステータスレジスタ) 1byte
  • a (アキュームレータ) 1byte
  • x (インデックス) 1byte
  • y (インデックス) 1byte

1-2 RAM: メインメモリ (2KB)
流石にメインメモリはデカイ。
デカイといっても、昨今のスマホの数百万分の一程度ですが。
当初(調査前)は、ココ(RAM)が圧倒的にデカくて、他は大したことが無いんじゃないかと想像していたのですが、調べてみるとname-tableというRAMに匹敵する(というか同じ)サイズの巨大なメモリ空間がPPUにありました。その辺は「流石ゲーム機」といった感じですね。(name-tableについての詳細は後述)

1-3 FRM: 割り込み制御情報 (5byte)

1-4 CLK: ticksカウンタ (8byte)
ticksカウンタというのは、CPUの動作周回(Hz)動いたかを示すカウンタです。ココに何バイト使うかはエミュレータの実装依存かと思います。ちなみにファミコンのCPUは 1.79MHz で, 8byte (64bit) は 0~18446744073709551615 までの数値を記憶できるので, だいたい1億日(30万年)ぐらい起動し続けるとticksカウンタがラップアラウンドすることで, 再現性が失われる可能性があるかもしれません。(つまり、8byteあれば事実上ticksカウンタはラップアラウンドしない)

② APU

APU; Audio Processing Unitは、音声の制御に特化した演算装置のことで、実際にはRP2A03の内部に実装されています。だから、Nestopiaの場合、CPUのsaveStateの内部でAPUのsaveStateを呼び出す構造になっているのかと思うと、何かロマンめいたものを感じてしまう。構造的にはAY-3-8910 (PSG音源) のカスタム品ですが, 矩形波だけでなく三角波(笛みたいな音. 1系統のみ)を扱うことができます。また、矩形波 (2系統) についてもデューティー比を0.25, 0.5, 0.75の3種類から選べるようになっていて更に系統別のエンベロープも実装されている豪華仕様です。デューティー比0.25 (, 0.75) の音はノコギリ波に似た感じのあの音です。またノイズ (1系統) と DPCM (1系統) も扱うことが出来ます。オリジナルのAY-3-8910と比べて格段に高い音楽表現能力を有しています。この時代のチップチューン音源は, 現代の表情のない音源システムには無いカオスさがあって面白い。(そういう所に面白さを感じる人間だから東方VGSとかをやっていた訳で)
2-1 FRM (4byte)

2-2 IRQ (3byte)

2-3 EXT (2byte)

2-4 矩形波1 (4+1+3byte)
波形の基本情報4byte + 長さカウンタ1byte + エンベロープ3byteです。

2-5 矩形波2 (4+1+3byte)
系統1と同じ内容です。

2-6 三角波 (4+1byte)
三角波にはエンベロープが無いので, 矩形波と比べて3byte少ない領域で保存できます。

2-7 ノイズ (1+1+3byte)
ノイズにはエンベロープがあります。(何故、三角波にはつけなかったんだろ)

2-8 DMC (12byte)
DMAを制御するためのコントローラのレジスタコンテキストを保持しています。

③ PPU

PPU; Picture Processing Unitは、グラフィックスの制御に特化した演算装置で, スプライトやBGなどの表示制御を行います。 要はGPUですね。(現代のGPUとは根本的に異なりますが)

3-1 REG: レジスタコンテキスト (11byte)

3-2 PAL: パレットRAM (32byte)

3-3 OAM: RAM (256byte)

3-4 NMT: name table RAM (2KB)

3-5 FRM<optional>: PPU_RP2C02 only (1byte)

3-6 POW<optional>: HCLOCK_BOOT only (1byte)

④ IMG

この領域はよく分かりませんが、SaveStateが空関数になっているので何も保存していませんが、friendメソッドなので派生クラスの方で色々と保存しているようです。(少し追いかけてみたところ、マッパー種別に実装が分かれているようでした)

⑤ PRT

5-1 4SC optional (1byte)
拡張ポートが4つかどうかだけをフラグとして管理しているらしい。 データは特に無いので1bitでも十分だが、1byteとしておきました。

5-2 拡張ポート device (0byte)
IMGと同様、saveSatateメソッドが実装されていましたが空でした。
何かしらのコンテキストやRAMを持つデバイスの場合は実装する必要がある(現状対応していない)のかもしれません。

5-3 拡張ポート (3byte)

結論
上記の容量を合計すると 4468 + α byte になります。
約4KBってところですね。なお、Nestopiaの場合、RAMやNMTなどのサイズが大きな領域はZLIBを用いて圧縮しているので、ファイル上のサイズはもっと小さくなる筈です。ただし、これは飽くまでも全ROM共通の最低サイズで、マッパー(ROMの種類)によってαのサイズが大分変わってくる筈です。(その内、代表的なマッパー分だけでも解析してみようと思います)
もちろん、これはNestopia (v1.47) の場合の話しです。
でも、ファミコンは既に解析され尽くされている感があるので、他のエミュレータでもだいたい同じようなものだろうと思っています。

0 件のコメント:

コメントを投稿

注: コメントを投稿できるのは、このブログのメンバーだけです。

合理的ではないものを作りたい

ここ最近、実機版の東方VGSの開発が忙しくて、東方VGSの曲追加が滞っています。 東方VGS(実機版)のデザインを作りながら検討中。基本レトロUIベースですがシークバーはモダンに倣おうかな…とか pic.twitter.com/YOYprlDsYD — SUZUKI PLAN (...