1. ホーム
  2. emulation

[解決済み] エミュレータの仕組みと書き方を教えてください。[クローズド]

2022-03-17 08:26:52

質問

エミュレータはどのように機能するのか? NES/SNESやC64のエミュレータを見ると、驚かされます。

それらのマシンのプロセッサは、その固有のアセンブリ命令を解釈してエミュレートする必要があるのですか?他に何が必要なのでしょうか?また、一般的にどのように設計されているのでしょうか?

これからエミュレータ(特にゲーム系)を作ろうと思っている人にアドバイスをお願いします。

どのように解決するのですか?

エミュレーションは多面的な分野です。 ここでは、基本的な考え方と機能的な構成要素を紹介します。 バラバラにして、編集で詳細を埋めていくつもりです。 これから説明することの多くは、プロセッサの内部構造に関する知識を必要とします -- アセンブリの知識が必要です。 もし私がある事柄について少し曖昧すぎるようであれば、この答えを改善し続けるために質問してください。

基本的な考え方

エミュレーションは、プロセッサと個々のコンポーネントの動作を処理することで機能します。 システムの個々のピースを構築し、ハードウェアの配線と同じようにピースを接続するのです。

プロセッサのエミュレーション

プロセッサエミュレーションの取り扱い方法は3つあります。

  • インタプリタ
  • ダイナミック・リコンパイル
  • 静的リコンパイル

これらのすべてのパスで、あなたは同じ全体的な目標を持っています:プロセッサの状態を変更し、「ハードウェア」と相互作用するコードの一部を実行します。 プロセッサの状態は、プロセッサのレジスタ、割り込みハンドラなど、与えられたプロセッサのターゲットの集合体です。 6502の場合、レジスタを表す8ビット整数があります。 A , X , Y , P および S また、16ビットの PC レジスタを使用します。

解釈では、まず IP (命令ポインタ -- とも呼ばれる) PC プログラムカウンタ)、メモリから命令を読み取ります。 コードはこの命令を解析し、この情報を使ってプロセッサの状態をプロセッサの指定通りに変化させます。 解釈の核心的な問題点は 非常に 与えられた命令を処理するたびに、その命令をデコードして必要な処理を行わなければならないからです。

ダイナミック・リコンパイルでは、解釈と同じようにコードを繰り返し実行しますが、単にオペコードを実行するのではなく、操作のリストを構築していくのです。 分岐命令に到達したら、この操作のリストをホスト・プラットフォーム用の機械語にコンパイルし、このコンパイルされたコードをキャッシュして実行します。 そして、このコンパイルしたコードをキャッシュし、実行する。そうすれば、再びある命令群にぶつかったときに、キャッシュの中のコードだけを実行すればよい。 (ところで、ほとんどの人は実際には命令のリストを作らず、その場で機械語にコンパイルしています。このため最適化が難しくなりますが、多くの人が興味を持っていなければ、この答えの範囲外です)

静的リコンパイルでは、動的リコンパイルと同じように、分岐をたどっていくことになります。 最終的には、プログラム内のすべてのコードを表すコードの塊を構築し、それ以上干渉されることなく実行できるようになります。 これは、次のような問題さえなければ、素晴らしい仕組みです。

  • そもそもプログラムにないコード(圧縮、暗号化、実行時に生成/変更など)は再コンパイルされないので、実行されない
  • 与えられたバイナリに含まれるすべてのコードを見つけることは、そのバイナリに含まれるすべてのコードを見つけることと同等であることが証明されています。 ハルティング問題

これらの組み合わせにより、静的リコンパイルは99%のケースで完全に実行不可能となります。 詳しくは、Michael Steil がスタティック・リコンパイルについて素晴らしい研究をしています。

プロセッサ・エミュレーションのもうひとつの側面は、ハードウェアとのインタラクションの方法です。 これには実に2つの側面があります。

  • プロセッサのタイミング
  • インタラプト処理

プロセッサのタイミング

特定のプラットフォーム、特にファミコンやスーファミなどの古いゲーム機では、完全に互換性を保つために、エミュレータに厳密なタイミングを要求します。 ファミコンの場合、PPU(ピクセルプロセッシングユニット)があり、CPUが正確なタイミングでピクセルをメモリに書き込む必要があります。 もし解釈を使えば、簡単にサイクルをカウントして適切なタイミングをエミュレートできますが、動的/静的リコンパイルでは、物事はもっと複雑です。

インタラプト処理。

割り込みは、CPUがハードウェアと通信するための主要なメカニズムです。 一般的に、ハードウェアコンポーネントは、CPUにどの割り込みが重要かを伝えます。 コードが割り込みを発生させたら、割り込みハンドラテーブルを見て、適切なコールバックを呼び出すのです。

ハードウェア・エミュレーション。

あるハードウェアデバイスのエミュレーションには、2つの側面があります。

  • デバイスの機能をエミュレートする
  • 実機インターフェースのエミュレーション

ハードディスクを例にとると この機能は、バッキングストレージ、読み取り/書き込み/フォーマットルーチンなどを作成することでエミュレートされます。 この部分は一般的に非常に簡単です。

実際のデバイスのインターフェースは、もう少し複雑です。 これは一般に、メモリマップド・レジスタ(例えば、デバイスが信号の変更を監視するメモリの部分)と割り込みの組み合わせです。 ハードドライブの場合、メモリマップドエリアに読み取りコマンドや書き込みなどを配置し、そのデータを読み取ることができます。

もっと詳しく説明したいのですが、100万通りもあるんです。 もし、ここで具体的な質問があれば、遠慮なく質問してください。

リソース

ここで、かなり良い紹介ができたと思いますが、この他にも トン があります。 質問があれば、喜んでお手伝いします。非常に複雑なため、これまでほとんどを曖昧にしてきました。

ウィキペディアへのリンクは必須です。

一般的なエミュレーションのリソースです。

  • ゾファー -- エミュレータをダウンロードし、最終的には膨大なドキュメントのアーカイブを略奪しました。 これは、あなたが持つことができる絶対的な最高のリソースです。
  • NGEmu -- 直接の情報源は多くありませんが、フォーラムは無敵です。
  • ロムハッキング・ネット -- ドキュメントセクションには、人気ゲーム機用のマシンアーキテクチャに関する資料が含まれています。

参考となるエミュレータのプロジェクト

  • アイアンバベル -- これは.NETのエミュレーション・プラットフォームで、Nemerleで書かれており、コードをオンザフライでC#に再コンパイルします。 免責事項:これは私のプロジェクトなので、恥知らずなプラグインをお許しください。
  • BSnes -- サイクルパーフェクトな精度を目指した素晴らしいスーファミエミュレータです。
  • MAME -- は、その アーケード用エミュレータ。 素晴らしいリファレンスです。
  • 6502asm.com -- これは JavaScript 6502 エミュレータで、クールな小さなフォーラムがあります。
  • dynabook 6502asm -- これは一日か二日かけてやった小さなハックです。 6502asm.com から既存のエミュレータを入手して、コードを JavaScript に動的に再コンパイルするように変更し、大幅なスピードアップを図りました。

プロセッサの再コンパイルの参考資料です。

  • Michael Steil氏による静的リコンパイルの研究(上記参照)は、次のような形で結実しました。 本紙 をクリックすると、出典などがわかります。 こちら .

追記

この回答が提出されてから1年以上経ちますが、注目されているため、いくつか更新する時期が来たと思いました。

今、エミュレーションで最もエキサイティングなことは、おそらく libcpu 前出のマイケル・スタイルが始めたものです。 これは、多数のCPUコアをサポートすることを目的としたライブラリで、リコンパイル(静的および動的)にLLVMを使用しています。 これは大きな可能性を秘めていて、エミュレーションに素晴らしい効果をもたらすと思います。

エミュ・ドックス このサイトには、エミュレーションに役立つシステム・ドキュメントの素晴らしいリポジトリがあります。 まだあまり時間をかけていませんが、素晴らしいリソースがたくさんあるようです。

この投稿がお役に立てたようで嬉しいです。年末から来年初めにかけて、腰を据えてこのテーマの本を完成させたいと思っています。