エミュレーターはどのように機能しますか? NES/SNES や C64 エミュレーターを見ると、私は驚きます。
特定のアセンブリ命令を解釈して、それらのマシンのプロセッサをエミュレートする必要がありますか? 他に何が入りますか?それらは通常どのように設計されていますか?
エミュレーター (特にゲームシステム) を書くことに興味がある人に何かアドバイスをいただけますか?
エミュレーションは多面的な領域です。基本的なアイデアと機能コンポーネントは次のとおりです。バラバラにしてから編集で詳細を埋めていきます。これから説明することの多くには、プロセッサの内部動作に関する知識が必要です。アセンブリの知識が必要です。特定の事柄について少し漠然としすぎている場合は、質問してください。この回答を改善し続けることができます。
エミュレーションは、プロセッサと個々のコンポーネントの動作を処理することによって機能します。システムの個々の部品を構築し、ハードウェアでワイヤーが行うのと同じように部品を接続します。
プロセッサ エミュレーションを処理するには、次の 3 つの方法があります。
これらすべてのパスで、全体的な目標は同じです。コードを実行してプロセッサの状態を変更し、「ハードウェア」と対話します。プロセッサの状態は、特定のプロセッサ ターゲットのプロセッサ レジスタ、割り込みハンドラなどの集まりです。6502 の場合、レジスタを表す多数の 8 ビット整数があります: A
、X
、Y
、P
、およびS
; 16ビットのPC
レジスタもあります。
解釈では、IP
(命令ポインタ -PC
プログラム カウンタとも呼ばれます) から開始し、メモリから命令を読み取ります。コードはこの命令を解析し、この情報を使用して、プロセッサによって指定されたとおりにプロセッサの状態を変更します。解釈の中心的な問題は、解釈が非常に遅いことです。特定の命令を処理するたびに、それをデコードして必要な操作を実行する必要があります。
動的再コンパイルでは、解釈と同じようにコードを繰り返し処理しますが、オペコードを実行するだけでなく、操作のリストを作成します。分岐命令に到達したら、この操作のリストをホスト プラットフォーム用のマシン コードにコンパイルし、このコンパイルされたコードをキャッシュして実行します。その後、特定の命令グループを再度ヒットすると、キャッシュからコードを実行するだけで済みます。(ところで、ほとんどの人は実際に命令のリストを作成するのではなく、その場でマシンコードにコンパイルします。これにより、最適化がより困難になりますが、十分な人が興味を持っていない限り、それはこの回答の範囲外です)
静的再コンパイルでは、動的再コンパイルと同じことを行いますが、分岐に従います。プログラム内のすべてのコードを表すコードのチャンクを構築することになり、それ以上の干渉なしに実行できます。次の問題がなければ、これは素晴らしいメカニズムです。
これらが組み合わさって、静的再コンパイルは 99% のケースで完全に実行不可能になります。詳細については、Michael Steil が静的再コンパイルに関する優れた調査を行っています。これは私が見た中で最高のものです。
プロセッサ エミュレーションのもう 1 つの側面は、ハードウェアと対話する方法です。これには 2 つの側面があります。
特定のプラットフォーム (特に NES や SNES などの古いコンソール) では、エミュレーターが完全に互換性を持つように厳密なタイミングを設定する必要があります。NES では、正確な瞬間に CPU がメモリにピクセルを配置する必要がある PPU (ピクセル処理ユニット) があります。解釈を使用すると、簡単にサイクルを数えて適切なタイミングをエミュレートできます。動的/静的再コンパイルでは、物事は /lot/ より複雑になります。
割り込みは、CPU がハードウェアと通信する主要なメカニズムです。通常、ハードウェア コンポーネントは、CPU が関心を持っている割り込みを CPU に伝えます。これは非常に簡単です。コードが特定の割り込みをスローすると、割り込みハンドラー テーブルを見て、適切なコールバックを呼び出します。
特定のハードウェア デバイスのエミュレートには 2 つの側面があります。
ハードドライブの場合を考えてみましょう。この機能は、バッキング ストレージ、読み取り/書き込み/フォーマット ルーチンなどを作成することによってエミュレートされます。この部分は通常、非常に簡単です。
デバイスの実際のインターフェースはもう少し複雑です。これは通常、メモリ マップド レジスタ (たとえば、デバイスが通知を行うための変更を監視するメモリの一部) と割り込みの組み合わせです。ハードドライブの場合、読み取りコマンドや書き込みなどを配置し、このデータを読み戻すメモリマップ領域がある場合があります。
もっと詳しく説明しますが、何百万通りもの方法があります。ここで具体的な質問がある場合は、お気軽にお問い合わせください。情報を追加します。
ここでかなり良い紹介をしたと思いますが、追加の領域がたくさんあります。ご不明な点がございましたら、お気軽にお問い合わせください。単純に非常に複雑なため、私はこれのほとんどについて非常に曖昧でした。
この回答が提出されてから 1 年以上が経過しましたが、注目を集めているため、いくつかのことを更新する時が来たと考えました。
おそらく、現在エミュレーションで最もエキサイティングなのは、前述の Michael Steilによって開始された libcpu です。これは、再コンパイルに LLVM を使用する (静的および動的!) 多数の CPU コアをサポートするためのライブラリです。それは大きな可能性を秘めており、エミュレーションのために素晴らしいことをすると思います.
emu-docsも私の注意を引きました。これには、エミュレーションの目的に非常に役立つシステム ドキュメントの優れたリポジトリが含まれています。私はそこに多くの時間を費やしていませんが、素晴らしいリソースがたくさんあるようです。
この投稿がお役に立ててうれしいです。今年の終わりか来年の初めまでに、お尻から降りて、このテーマに関する本を完成させたいと思っています。
ビクター・モヤ・デル・バリオという男が、このトピックに関する論文を書きました。152ページにお得な情報が盛りだくさん。PDFはこちらからダウンロードできます。
scribdに登録したくない場合は、PDF タイトル「エミュレーション プログラミングのテクニックの研究」でググってください。PDF にはいくつかの異なるソースがあります。
エミュレーションは気が遠くなるように思えるかもしれませんが、実際にはシミュレーションよりもはるかに簡単です。
通常、どのプロセッサにも、状態や相互作用などを記述した適切に記述された仕様があります。
パフォーマンスをまったく気にしない場合は、非常に洗練されたオブジェクト指向プログラムを使用して、ほとんどの古いプロセッサを簡単にエミュレートできます。たとえば、X86プロセッサには、レジスタの状態を維持するもの(easy)、メモリの状態を維持するもの(easy)、および各着信コマンドを取得してマシンの現在の状態に適用するものが必要です。本当に正確さが必要な場合は、メモリ変換やキャッシュなどもエミュレートしますが、それは実行可能です。
実際、多くのマイクロチップおよびCPUメーカーは、チップのエミュレーターに対してプログラムをテストし、次にチップ自体に対してプログラムをテストします。これは、チップの仕様またはハードウェアでのチップの実際の実装に問題があるかどうかを確認するのに役立ちます。たとえば、デッドロックが発生するチップ仕様を作成することは可能です。ハードウェアで期限が発生した場合、それがチップ実装の問題よりも大きな問題であることを示しているため、仕様で再現できるかどうかを確認することが重要です。
もちろん、ビデオゲームのエミュレーターは通常、パフォーマンスを重視するため、単純な実装を使用しません。また、描画やサウンドを使用するなど、ホストシステムのOSとインターフェイスするコードも含まれています。
古いビデオゲーム(NES / SNESなど)のパフォーマンスが非常に遅いことを考えると、最新のシステムではエミュレーションは非常に簡単です。実際、これらのシステムが普及したときにすべてのカートリッジに無料でアクセスできることは夢の実現だったことを考えると、これまでのすべてのSNESゲームまたはこれまでのAtari2600ゲームのセットをダウンロードできることはさらに驚くべきことです。
この質問は少し古いことは承知していますが、議論に何かを追加したいと思います。ここでの回答のほとんどは、エミュレーターがエミュレートするシステムのマシン命令を解釈することに集中しています。
ただし、これには「UltraHLE」と呼ばれる非常によく知られた例外があります ( WIKIpedia の記事)。これまでに作成された中で最も有名なエミュレーターの 1 つである UltraHLE は、市販の Nintendo 64 ゲームを (家庭用コンピューターでまともなパフォーマンスで) エミュレートすることが不可能であると広く考えられていた時代にエミュレートしました。実は、UltraHLE が作成されたとき、任天堂はまだニンテンドー 64 用の新しいタイトルを作成していました。
以前は Web 上でしか議論されていなかったエミュレーターに関する記事を、印刷された雑誌で初めて見ました。
UltraHLE のコンセプトは、マシン レベルの呼び出しではなく C ライブラリ呼び出しをエミュレートすることで、不可能を可能にすることでした。
一見の価値があるのは、JavaScriptでゲームボーイエミュレーターを作成しようとしたイムランナザールの試みです。
80 年代の BBC マイクロコンピューター (Google に VBeeb と入力) の独自のエミュレーターを作成したので、知っておくべきことがたくさんあります。
実際に言えば、エミュレーションの速度と忠実度のために書くことが一般的です。これは、ターゲット システム上のソフトウェアが、ソース システム上の元のハードウェアよりも遅く実行される (可能性がある) ためです。これにより、プログラミング言語、コンパイラ、ターゲットシステムなどの選択が制限される可能性があります。
さらに、エミュレートする準備ができているものを制限する必要があります。たとえば、マイクロプロセッサのトランジスタの電圧状態をエミュレートする必要はありませんが、おそらく必要ですマイクロプロセッサのレジスタセットの状態をエミュレートします。
一般的に言えば、エミュレーションの詳細レベルが小さいほど、元のシステムの忠実度が高くなります。
最後に、古いシステムの情報は不完全または存在しない可能性があります。したがって、元の機器を手に入れることが不可欠です。または、少なくとも他の誰かが作成した別の優れたエミュレーターを引き離してください!
はい、バイナリマシンコードの混乱全体を「手で」解釈する必要があります。それだけでなく、ほとんどの場合、ターゲット マシンに同等のハードウェアがない特殊なハードウェアをシミュレートする必要もあります。
簡単なアプローチは、命令を 1 つずつ解釈することです。それはうまくいきますが、遅いです。より迅速なアプローチは再コンパイルです。ソース マシン コードをターゲット マシン コードに変換します。ほとんどの命令は 1 対 1 でマップされないため、これはより複雑です。代わりに、追加のコードを含む精巧な回避策を作成する必要があります。しかし、最終的にははるかに高速です。最近のほとんどのエミュレーターはこれを行います。
エミュレータを開発する場合、システムが動作しているプロセッサ アセンブリ (Z80、8080、PS CPU など) を解釈します。
また、システムにあるすべての周辺機器 (ビデオ出力、コントローラー) をエミュレートする必要があります。
古き良きゲームボーイのようなシンプルなシステム(Z80 プロセッサを使用しますが、間違いではありません) または C64用のエミュレータの作成を開始する必要があります。
シミュレートする必要がある多くのハック (異常な効果など)、タイミングの問題などがあるため、エミュレーターを作成するのは非常に困難です。
この例については、 http: //queue.acm.org/detail.cfm?id=1755886を参照してください。
また、1MHz の CPU をエミュレートするためにマルチ GHz の CPU が「必要」な理由もわかります。
また、Darek Mihocka のEmulators.comをチェックして、JIT の命令レベルの最適化に関する優れたアドバイスや、効率的なエミュレーターを構築するためのその他の多くの情報を確認してください。
ゲーム コンソールをエミュレートするほど凝ったことはしたことがありませんが、Andrew Tanenbaum の Structured Computer Organizationで説明されているマシン用のエミュレーターを作成するという課題のコースを受講したことがあります。それは楽しかったし、たくさんのアハの瞬間を与えてくれました。実際のエミュレーターの作成に飛び込む前に、その本を手に取りたいと思うかもしれません。
実際のシステムまたは独自のものをエミュレートするためのアドバイスはありますか? エミュレータは、ハードウェア全体をエミュレートすることで機能すると言えます。回路にまでは行かないかもしれません(HWのようにビットを移動するように。バイトを移動することは最終結果であるため、バイトをコピーすることは問題ありません)。シミュレートする必要がある多くのハック (異常な効果など)、タイミングの問題などがあるため、エミュレーターを作成するのは非常に困難です。1 つの (入力) 部分が間違っていると、システム全体がダウンするか、せいぜいバグ/グリッチが発生する可能性があります。
共有ソース デバイス エミュレーターには、PocketPC/Smartphone エミュレーター (Windows 上で実行される Visual Studio が必要) へのビルド可能なソース コードが含まれています。バイナリリリースの V1 と V2 に取り組みました。
多くのエミュレーションの問題に取り組んでいます: - ゲスト仮想からゲスト物理からホスト仮想への効率的なアドレス変換 - ゲスト コードの JIT コンパイル - ネットワーク アダプター、タッチスクリーン、オーディオなどの周辺機器のシミュレーション - ホスト キーボードとマウスの UI 統合 - 保存/低電力モードからの再開のシミュレーション用の状態の復元
JavaScript での Chip-8 システムのエミュレートに関する記事を書きました。
システムはそれほど複雑ではないため、開始するのに最適な場所ですが、オペコード、スタック、レジスタなどがどのように機能するかを学習できます。
私はすぐに NES のより長いガイドを書く予定です。
エミュレーションを開始する方法。
1.低レベルのプログラミングに基づいた本を入手してください。任天堂の「ふりをする」オペレーティングシステムに必要です...ゲームボーイ...
2.特にエミュレーションに関する書籍を入手し、OS 開発に関する書籍を入手してください。(os は作成しませんが、それに最も近いものを作成します。
3.いくつかのオープンソースエミュレーター、特にエミュレーターを作成したいシステムのエミュレーターを見てください。
4.より複雑なコードのスニペットを IDE/コンパイラにコピーします。これにより、長いコードを書く必要がなくなります。これは私がOS開発のために行っていることであり、Linuxの地区を使用しています
@Cody Brocious が提供する回答を追加するには
、新しいシステム (CPU、I/O など) を仮想マシンにエミュレートする仮想化のコンテキストでは、次のカテゴリのエミュレーターを確認できます。
解釈: bochs はインタープリターの例です。これは x86 PC エミュレーターです。ゲスト システムから各命令を受け取り、それを別の命令セット (ホスト ISA の) に変換して、意図した効果を生成します。はい、非常に遅いです。そうではありません。何もキャッシュしないので、すべての命令が同じサイクルを通過します。
動的エミュレーター: Qemu は動的エミュレーターです。ゲスト命令のオンザフライ変換も結果をキャッシュします。最良の部分は、ホストシステムで直接できるだけ多くの命令を実行して、エミュレーションを高速化することです。また、コーディが述べたように、コードをブロックに分割します(1つの実行フロー)。
静的エミュレーター: 私の知る限り、仮想化に役立つ静的エミュレーターはありません。