エミュレータはどのように機能し、どのように書かれていますか? [閉まっている]

StackOverflow https://stackoverflow.com/questions/448673

  •  19-08-2019
  •  | 
  •  

質問

エミュレータはどのように機能しますか? NES / SNESまたはC64エミュレーターを見ると、びっくりします。

http://www.tommowalker.co.uk/snemzelda.png

特定のアセンブリ命令を解釈して、これらのマシンのプロセッサをエミュレートする必要がありますか?他に何が入りますか?通常どのように設計されていますか?

エミュレータ(特にゲームシステム)の作成に興味がある人にアドバイスをお願いできますか?

役に立ちましたか?

解決

エミュレーションは多面的な領域です。基本的なアイデアと機能コンポーネントは次のとおりです。それを細かく分割し、編集して詳細を入力します。これから説明するものの多くは、プロセッサの内部動作に関する知識を必要とします。アセンブリの知識が必要です。特定のことにあまりにも曖昧な場合は、この回答を改善し続けることができるように質問してください。

基本的な考え方:

エミュレーションは、プロセッサと個々のコンポーネントの動作を処理することで機能します。システムの個々の部分を構築してから、ハードウェアでワイヤが行うように部分を接続します。

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

プロセッサエミュレーションを処理する方法は3つあります。

  • 解釈
  • 動的再コンパイル
  • 静的再コンパイル

これらすべてのパスでは、全体的な目標は同じです。つまり、コードを実行してプロセッサの状態を変更し、「ハードウェア」とやり取りします。プロセッサの状態は、特定のプロセッサターゲットのプロセッサレジスタ、割り込みハンドラなどの集合です。 6502の場合、レジスタを表す8ビット整数が多数あります。AXYP、およびS; 16ビットのPCレジスタもあります。

解釈では、IP(命令ポインタ-<=>、プログラムカウンタとも呼ばれる)から開始し、メモリから命令を読み取ります。コードはこの命令を解析し、この情報を使用して、プロセッサの指定に従ってプロセッサの状態を変更します。解釈の核となる問題は、非常に遅いことです。特定の命令を処理するたびに、その命令をデコードして必要な操作を実行する必要があります。

動的再コンパイルでは、解釈のようにコードを反復処理しますが、オペコードを実行するだけでなく、操作のリストを作成します。分岐命令に到達したら、この操作リストをホストプラットフォームのマシンコードにコンパイルし、このコンパイルされたコードをキャッシュして実行します。その後、特定の命令グループに再度ヒットすると、キャッシュからコードを実行するだけで済みます。 (ところで、ほとんどの人は実際に命令のリストを作成するのではなく、その場でマシンコードにコンパイルします-これにより最適化が難しくなりますが、十分な人々が興味を持たない限り、この答えの範囲外です)

静的再コンパイルでは、動的再コンパイルと同じことを行いますが、分岐に従います。最終的に、プログラム内のすべてのコードを表すコードのチャンクを作成し、それ以上の干渉なしに実行できます。次の問題がなければ、これは素晴らしいメカニズムです。

  • 最初にプログラム内にないコード(たとえば、圧縮、暗号化、実行時の生成/変更など)は再コンパイルされないため、実行されません
  • 特定のバイナリですべてのコードを見つけることは、 Haltingの問題と同等であることが証明されています。

これらの組み合わせにより、99%のケースで静的再コンパイルが完全に実行不可能になります。詳細については、Michael Steilが静的再コンパイルに関する優れた研究を行っています-私が見た中で最高です。

プロセッサエミュレーションの反対側は、ハードウェアとやり取りする方法です。これには本当に2つの側面があります。

  • プロセッサのタイミング
  • 割り込み処理

プロセッサのタイミング:

特定のプラットフォーム(特にNES、SNESなどの古いコンソール)では、エミュレータに完全な互換性を確保するために厳密なタイミングが必要です。 NESには、PPU(ピクセル処理ユニット)があり、CPUが正確なタイミングでピクセルをメモリに入れる必要があります。解釈を使用すると、サイクルを簡単にカウントし、適切なタイミングをエミュレートできます。動的/静的再コンパイルでは、/ lot /はより複雑になります。

割り込み処理:

割り込みは、CPUがハードウェアと通信する主要なメカニズムです。通常、ハードウェアコンポーネントは、CPUにどの割り込みを処理するかを指示します。これは非常に簡単です-コードが特定の割り込みをスローすると、割り込みハンドラテーブルを見て適切なコールバックを呼び出します。

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

特定のハードウェアデバイスをエミュレートするには、2つの側面があります:

  • デバイスの機能のエミュレート
  • 実際のデバイスインターフェースのエミュレート

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

デバイスの実際のインターフェースはもう少し複雑です。これは通常、メモリマップレジスタ(たとえば、デバイスがシグナリングを行うための変更を監視するメモリの一部)と割り込みの組み合わせです。ハードドライブの場合、読み取りコマンド、書き込みなどを配置したメモリマップ領域があり、このデータを読み戻します。

さらに詳しく説明しますが、それを使用する方法は数百通りあります。ここに特定の質問がある場合は、お気軽にお問い合わせください。情報を追加します。

リソース:

ここではかなり良い紹介を行ったと思いますが、追加の領域がトンあります。ご質問があれば喜んでお手伝いします。私はこれの大部分で非常にあいまいでした、単に巨大な複雑さのためです。

ウィキペディアの必須リンク:

一般的なエミュレーションリソース:

  • Zophar -ここでエミュレーションを開始し、最初にエミュレーターをダウンロードし、最終的には膨大な量の略奪ドキュメントのアーカイブ。これは、可能な限り絶対に最適なリソースです。
  • NGEmu -多くの直接的なリソースはありませんが、フォーラムは無敵です。
  • RomHacking.net -ドキュメントセクションには、一般的なコンソールのマシンアーキテクチャに関するリソースが含まれています

参照するエミュレータープロジェクト:

  • IronBabel -これはNemerleで記述され、C#にコードを再コンパイルする.NETのエミュレーションプラットフォームです。急いで。免責事項:これは私のプロジェクトですので、恥知らずなプラグをご容赦ください。
  • BSnes -サイクル完璧な精度を目標とする素晴らしいSNESエミュレータ。
  • MAME - アーケードエミュレータ。素晴らしいリファレンス。
  • 6502asm.com -これは、クールな小さなフォーラムを備えたJavaScript 6502エミュレーターです。
  • dynarec'd 6502asm -これは1〜2日で行った小さなハックです。 。 6502asm.comから既存のエミュレーターを取得し、大幅に高速化するためにコードをJavaScriptに動的に再コンパイルするように変更しました。

プロセッサの再コンパイルの参照:

補遺:

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

おそらく、現在のエミュレーションで最もエキサイティングなことは、前述のMichael Steilによって開始された libcpu です。これは、LLVMを再コンパイル(静的および動的!)に使用する多数のCPUコアをサポートすることを目的としたライブラリです。巨大な可能性を秘めているので、エミュレーションのために素晴らしいことができると思います。

emu-docs も注目されました。これには、システムドキュメントの優れたリポジトリがあります。エミュレーションの目的に役立ちます。私はそこにあまり時間を費やしていませんが、彼らは多くの素晴らしいリソースを持っているようです。

この投稿がお役に立てたことをうれしく思います。また、年末/来年の初めまでに、お尻を下ろして主題に関する本を仕上げることができることを望んでいます。

他のヒント

Victor Moya del Barrioという名前の男がこのトピックについて論文を書いています。 152ページに関する多くの有益な情報。 PDF こちら

scribd に登録したくない場合は、GoogleでPDFタイトルを検索できます。 <!> quot;エミュレーションのテクニックの研究プログラミング<!> quot; 。 PDFにはいくつかの異なるソースがあります。

エミュレーションは困難に思えるかもしれませんが、実際にはシミュレーションするよりもかなり簡単です。

通常、すべてのプロセッサには、状態、相互作用などを記述する明確に記述された仕様があります。

パフォーマンスにまったく関心がなければ、非常にエレガントなオブジェクト指向プログラムを使用して、ほとんどの古いプロセッサーを簡単にエミュレートできます。たとえば、X86プロセッサには、レジスタの状態を維持するもの(簡単)、メモリの状態を維持するもの(簡単)、および各着信コマンドを取得してマシンの現在の状態に適用するものが必要です。正確性が本当に必要な場合は、メモリ変換やキャッシュなどもエミュレートしますが、それは実行可能です。

実際、多くのマイクロチップおよびCPUメーカーは、プログラムをチップのエミュレーターに対してテストし、次にチップ自体に対してテストします。これは、チップの仕様に問題があるか、チップの実際の実装に問題があるかどうかを調べるのに役立ちますハードウェアで。たとえば、デッドロックを引き起こすチップ仕様を作成することができます。ハードウェアでデッドラインが発生した場合、仕様で再現できるかどうかを確認することが重要です。

もちろん、ビデオゲームのエミュレーターは通常、パフォーマンスを重視するため、素朴な実装を使用しません。また、描画や音声を使用するなど、ホストシステムのOSとインターフェイスするコードも含まれます。

古いビデオゲーム(NES / SNESなど)の非常に遅いパフォーマンスを考慮すると、エミュレーションは最新のシステムでは非常に簡単です。実際、これらのシステムが普及したときにすべてのカートリッジに無料でアクセスできることが夢だったと考えて、これまでのすべてのSNESゲームまたはAtari 2600ゲームのセットをダウンロードすることができたことはさらに驚くべきことです。

この質問は少し古いことは知っていますが、議論に何かを追加したいと思います。ここでの回答のほとんどは、エミュレートするシステムのマシン命令を解釈するエミュレーターを中心にしています。

ただし、これには<!> quot; UltraHLE <!> quotと呼ばれる非常によく知られた例外があります。 ( WIKIpediaの記事)。これまでに作成された最も有名なエミュレーターの1つであるUltraHLEは、市販の任天堂64ゲームをエミュレートしました(家庭用コンピューターで適切なパフォーマンスを発揮)。実際のところ、任天堂はUltraHLEが作成されたとき、まだ任天堂64の新しいタイトルを作成していました!

初めて、印刷雑誌でエミュレーターに関する記事を見ましたが、以前はウェブ上でしか議論されていませんでした。

UltraHLEのコンセプトは、マシンレベルの呼び出しではなくCライブラリの呼び出しをエミュレートすることで不可能を可能にすることでした。

見てみる価値のあるものは、ゲームボーイ JavaScriptのエミュレータ。

80年代のBBCマイクロコンピューター(GoogleにVBeebと入力)のエミュレーターを自分で作成したので、知っておくべきことがたくさんあります。

  • あなたは本物をエミュレートしていないので、それはレプリカです。代わりに、 State をエミュレートしています。良い例は電卓です。本物にはボタン、画面、ケースなどがあります。しかし、電卓をエミュレートするには、ボタンがアップかダウンか、LCDのどのセグメントがオンかなどをエミュレートするだけです。基本的に、数字のセット電卓で変化する可能性のあるすべての組み合わせを表します。
  • エミュレータのインターフェースが必要なのは、本物のように表示および動作するためだけです。これが納得できるほど、エミュレーションは近くなります。舞台裏で起こっていることは、あなたが好きなものでありえます。ただし、エミュレータの作成を簡単にするために、実際のシステム、つまりチップ、ディスプレイ、キーボード、回路基板、抽象的なコンピューターコードの間で行われるメンタルマッピングがあります。
  • コンピューターシステムをエミュレートするには、それを小さなチャンクに分割し、それらのチャンクを個別にエミュレートするのが最も簡単です。次に、完成品のためにロット全体をつなぎ合わせます。入力と出力を備えたブラックボックスのセットのように、オブジェクト指向プログラミングに最適です。これらのチャンクをさらに細分化して、生活を楽にすることができます。

実際には、一般的に、エミュレーションの速度と忠実度を記述しようとしています。これは、ターゲットシステム上のソフトウェアの実行が、ソースシステム上の元のハードウェアよりも遅くなる可能性があるためです。そのため、プログラミング言語、コンパイラ、ターゲットシステムなどの選択が制約される可能性があります。
さらに、エミュレートする準備を外さなければなりません。たとえば、マイクロプロセッサのトランジスタの電圧状態をエミュレートする必要はありませんが、マイクロプロセッサのレジスタセットの状態をエミュレートすることはおそらく必要です。
一般的に、エミュレーションの詳細レベルが小さいほど、元のシステムに忠実になります。
最後に、古いシステムの情報は不完全または存在しない場合があります。したがって、元の機器を手に入れることは不可欠です。または、少なくとも他の誰かが書いた別の優れたエミュレータをばらばらにすることです!

はい、バイナリマシンコードの混乱<!> quot;を手動で解釈する必要があります<!> quot;。それだけでなく、ほとんどの場合、ターゲットマシンに同等のハードウェアがないエキゾチックなハードウェアもシミュレートする必要があります。

簡単なアプローチは、指示を1つずつ解釈することです。それはうまくいきますが、遅いです。より高速なアプローチは再コンパイルです-ソースマシンコードをターゲットマシンコードに変換します。ほとんどの指示は1対1にマッピングされないため、これはより複雑です。代わりに、追加のコードを含む手の込んだ回避策を作成する必要があります。しかし、最終的にははるかに高速です。ほとんどの最新のエミュレーターはこれを行います。

エミュレータを開発するとき、システムが動作しているプロセッサアセンブリ(Z80、8080、PS CPUなど)を解釈しています。

また、システムに搭載されているすべての周辺機器(ビデオ出力、コントローラー)をエミュレートする必要があります。

古き良きゲームボーイのようなシンプルシステム用のエミュレータの作成を開始する必要がありますZ80プロセッサ、私は間違えていませんか)またはC64。

  

多くのハックがあるため、エミュレーターの作成は非常に困難です(通常とは異なります)   効果)、タイミングの問題など、シミュレートする必要があります。

この例については、 http://queue.acm.org/detailをご覧ください。 .cfm?id = 1755886

これはまた、なぜあなたが<!>#8216; need <!>#8217; 1MHzをエミュレートするためのマルチGHz CPU。

また、Darek Mihockaの Emulators.com をチェックして、JITの命令レベルの最適化、およびその他の多くの利点を調べてください。効率的なエミュレータの構築について。

ゲームコンソールをエミュレートするほど空想的なことは一度もしませんでしたが、Andrew Tanenbaums 構造化されたコンピューター組織。それは楽しかったし、たくさんの瞬間をくれました。実際のエミュレーターを作成する前に、その本を取り上げることをお勧めします。

実際のシステムまたは自分のものをエミュレートすることについてのアドバイスはありますか? エミュレータは、ハードウェア全体をエミュレートすることで機能すると言えます。 (ハードウェアのようにビットを移動するように。バイトを移動することは最終結果なので、バイトをコピーしても問題ありません)エミュレーターを作成するのは非常に困難です。これは、多くのハック(異常な効果など)、タイミングの問題など、シミュレートする必要があるためです。 1つの(入力)ピースが間違っていると、システム全体がダウンするか、せいぜいバグ/グリッチが発生する可能性があります。

共有ソースデバイスエミュレータにはビルド可能なものが含まれていますPocketPC / Smartphoneエミュレーターへのソースコード(Visual Studioが必要、Windows上で実行)。バイナリリリースのV1とV2に取り組みました。

多くのエミュレーションの問題に取り組んでいます: -ゲスト仮想からゲスト物理、ホスト仮想への効率的なアドレス変換 -ゲストコードのJITコンパイル -ネットワークアダプター、タッチスクリーン、オーディオなどの周辺機器のシミュレーション -ホストのキーボードとマウスのUI統合 -低電力モードからの再開のシミュレーションのための状態の保存/復元

@Cody Brociousが提供する回答を追加するには
新しいシステム(CPU、I / Oなど)を仮想マシンにエミュレートする仮想化のコンテキストでは、次のカテゴリのエミュレーターを見ることができます。

解釈:bochsはインタープリターの例であり、x86 PCエミュレーターであり、ゲストシステムからの各命令を別の命令セット(ホストISAの)に変換し、意図した効果を生成します。はい、非常に遅いです。 、何もキャッシュしないため、すべての命令が同じサイクルを通過します。

動的エマレーター:Qemuは動的エミュレーターです。ゲスト命令のオンザフライ変換も結果をキャッシュします。最良の部分は、可能な限り多くの命令をホストシステム上で直接実行し、エミュレーションを高速化することです。また、Codyが述べたように、コードをブロックに分割します(1つの実行フロー)。

静的エミュレーター:仮想化に役立つ静的エミュレーターはありません。

エミュレーションの開始方法。

1。低レベルのプログラミングに基づいて本を入手してください。<!> quot; pretend <!> quot;任天堂のオペレーティングシステム...ゲームボーイ...

2。特にエミュレーションに関する書籍を入手し、おそらく開発を行ってください。 (OSは作成しませんが、それに最も近いものにします。

3。いくつかのオープンソースエミュレータ、特にエミュレータを作成したいシステムのエミュレータを見てください。

4。より複雑なコードのスニペットをIDE /コンパイラにコピーします。これにより、長いコードを書く必要がなくなります。これは私がOS開発のために行うことで、Linuxの地区を使用します

Chip-8システムのJavaScript のエミュレートに関する記事を書きました。

システムはそれほど複雑ではないので、始めるのに最適な場所ですが、それでもオペコード、スタック、レジスタなどの仕組みを学ぶことができます。

NES向けの長いガイドをまもなく作成します。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top