調査方法は、Nestopiaというlibretroを使ったNESエミュレータ(OSS)のソースコードを解析する形で実施しました。(たぶん、それが一番楽だと思います)
なお、オリジナルのNestopiaのソースコードはコチラにありますが、本記事では見やすさを優先してGitHubにミラーされている以下のリポジトリ(のバージョン1.47)を参照します。
https://github.com/rdanbrook/nestopia
全体像
上記から、大まかに以下5項目のデータを保持していることが分かります。
- CPU: Central Processing Unit
- APU (CPU内部): Audio Processing Unit
- PPU: Picture Processing Unit
- IMG: イメージ?
- PRT: 拡張ポート
ファミコンの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)
https://github.com/rdanbrook/nestopia/blob/1.47/source/core/NstPpu.cpp#L339https://github.com/rdanbrook/nestopia/blob/1.47/source/core/NstPpu.hpp#L303
3-3 OAM: RAM (256byte)
https://github.com/rdanbrook/nestopia/blob/1.47/source/core/NstPpu.cpp#L340https://github.com/rdanbrook/nestopia/blob/1.47/source/core/NstPpu.hpp#L348
3-4 NMT: name table RAM (2KB)
https://github.com/rdanbrook/nestopia/blob/1.47/source/core/NstPpu.cpp#L341https://github.com/rdanbrook/nestopia/blob/1.47/source/core/NstPpu.hpp#L375
3-5 FRM<optional>: PPU_RP2C02 only (1byte)
3-6 POW<optional>: HCLOCK_BOOT only (1byte)
④ IMG
https://github.com/rdanbrook/nestopia/blob/1.47/source/core/NstMachine.cpp#L359https://github.com/rdanbrook/nestopia/blob/1.47/source/core/NstImage.hpp#L101
この領域はよく分かりませんが、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 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。