web-dev-qa-db-ja.com

エミュレータはどのように動作し、どのように書かれていますか?

エミュレータはどのように動作しますか? NES/SNESまたはC64エミュレータを見たとき、それは私を驚かせます。

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

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

エミュレータ(特にゲームシステム)を書くことに興味がある人に何かアドバイスはありますか?

968
mmcdole

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

基本的な考え方:

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

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

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

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

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

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

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

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

これらが組み合わさって静的再コンパイルが99%のケースで完全に実行不可能になります。詳細については、Michael Steilが静的再コンパイルについていくつかの素晴らしい研究を行っています。

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

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

プロセッサのタイミング

特定のプラットフォーム、特にNES、SNESなどの古いコンソールでは、エミュレータが完全に互換性を持つために厳密なタイミングを必要とします。 NESを使うと、正確な瞬間にCPUがメモリにピクセルを入れることを要求するPPU(pixel processing unit)があります。解釈を使用すれば、簡単にサイクル数を数えて適切なタイミングをエミュレートすることができます。動的/静的再コンパイルでは、物事は/ lot /より複雑になります。

割り込み処理

割り込みは、CPUがハードウェアと通信するための主要なメカニズムです。一般的に、あなたのハードウェアコンポーネントは、どんな割り込みを気にかけているのかをCPUに伝えます。これはかなり簡単です - あなたのコードが与えられた割り込みを投げるとき、あなたは割り込みハンドラテーブルを見て、そして適切なコールバックを呼び出します。

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

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

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

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

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

私はもっ​​と詳細に行きますが、あなたがそれと一緒に行くことができるたくさんの方法があります。ここに具体的な質問がある場合は、お気軽にお尋ねください。情報を追加します。

リソース:

私はここでかなり良いイントロを与えたと思いますが、 ton の追加領域があります。私はどんな質問でも手伝ってうれしいです。私は、非常に複雑なため、これらのほとんどについて非常に曖昧です。

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

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

  • Zophar - ここからエミュレーションを始めました。最初にエミュレータをダウンロードし、最終的にそれらの膨大なドキュメントのアーカイブを略奪していました。これはあなたが持つことができる絶対的な最高のリソースです。
  • NGEmu - 多くの直接的な資料はありませんが、彼らのフォーラムは無敵です。
  • RomHacking.net - ドキュメントセクションには、人気のあるコンソールのマシンアーキテクチャに関するリソースが含まれています。

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

  • IronBabel - これは、Nemerleで書かれた.NET用のエミュレーションプラットフォームで、コードをその場でC#に再コンパイルします。免責事項:これは私のプロジェクトです。
  • BSnes - サイクル完全な精度を目標とした素晴らしいSNESエミュレータ。
  • MAME - アーケードエミュレータ。すばらしい参考資料です。
  • 6502asm.com - これはクールな小さなフォーラムを持つJavaScript 6502エミュレータです。
  • dynarec'd 6502asm - これは1日か2日で行ったちょっとしたハックです。既存のエミュレータを6502asm.comから入手し、大幅な速度向上のために動的にコードをJavaScriptに再コンパイルするように変更しました。

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

  • Michael Steil(上記参照)によって行われた静的再コンパイルの研究は、 本稿 そしてあなたはソースとそのような ここ )を見つけることができます。

補遺:

この回答が提出されてから1年以上が経ちました。そして、それが得られているすべての注意を払って、私はそれがいくつかのものを更新する時が来たと考えました。

おそらく今エミュレーションで最もエキサイティングなことは libcpu 、前述のMichael Steilによって始められました。それは再コンパイルのためにLLVMを使う多数のCPUコアをサポートするように意図されたライブラリです。非常に大きな可能性があり、それはエミュレーションにとって素晴らしいことになると思います。

emu-docs も私の注意を引いています。そこにはエミュレーションの目的には非常に有用な、システムドキュメントの素晴らしいリポジトリがあります。私はそこではあまり時間を費やしていませんが、素晴らしいリソース。

この記事が参考になったことを嬉しく思います。そして、年末までに、または来年の初めまでに、私のお尻を降りて、この本についての本を完成させることができれば幸いです。

1126
Cody Brocious

このトピックに関するVictor Moya del Barrioという男が論文を書きました。 152ページにたくさんの良い情報があります。 PDF こちら をダウンロードできます。

scribd に登録したくない場合は、PDFというタイトルでGoogleにアクセスできます。 "エミュレーションプログラミングのためのテクニックの研究" 。 PDFにはいくつかの異なるソースがあります。

126
mdm

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

どのプロセッサにも、通常、状態、対話などを説明する適切に記述された仕様があります。

パフォーマンスをまったく気にしないのであれば、非常に洗練されたオブジェクト指向プログラムを使用して、ほとんどの古いプロセッサを簡単にエミュレートできます。たとえば、X86プロセッサでは、レジスタの状態を維持するためのもの(easy)、メモリの状態を維持するためのもの(easy)、および各入力コマンドを受け取ってそれをマシンの現在の状態に適用するものが必要になります。もしあなたが本当に正確さを望むのであれば、メモリの翻訳、キャッシュなどをエミュレートするでしょうが、それは可能です。

実際、多くのマイクロチップおよびCPU製造元は、チップのエミュレータに対して、次にチップ自体に対してプログラムをテストしています。これは、チップの仕様に問題があるのか​​、またはハードウェアで実際にチップを実装するのに問題があるかどうかを調べるのに役立ちます。例えば、デッドロックを引き起こすようなチップ仕様を書くことが可能であり、ハードウェアで期限が発生したとき、それが仕様の中で再現されるかどうかを見ることは重要です。

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

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

43
Uri

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

しかし、これには「UltraHLE」と呼ばれる非常によく知られた例外があります( WIKIpediaの記事 )。 UltraHLEは、史上最も有名なエミュレータの1つであり、市販のニンテンドー64ゲーム(家庭用コンピュータではかなりの性能を備えています)をエミュレートすることが不可能であると広く考えられていました。実際のところ、UltraHLEが作成されたとき、任天堂はまだ任天堂64のための新しいタイトルを制作していました!

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

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

29

見てみる価値があるのは、JavaScriptで Gameboy エミュレータを作成しようとしているImran Nazarの試みです。

22
Julio

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

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

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

18

はい、バイナリマシンコード全体を「手作業で」混乱させて解釈する必要があります。それだけでなく、ほとんどの場合、ターゲットマシン上に同等のものを持っていないいくつかの変わったハードウェアをシミュレートする必要があります。

簡単な方法は、命令を1つずつ解釈することです。それはうまくいきますが、遅いです。より速いアプローチは再コンパイルです - ソースマシンコードをターゲットマシンコードに翻訳します。ほとんどの命令は一対一に対応しないので、これはもっと複雑です。代わりに、追加のコードを含む複雑な回避策を講じる必要があります。しかし結局それははるかに速いです。最近のほとんどのエミュレータはこれを行っています。

17
Vilx-

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

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

古き良き Game Boy (Z80プロセッサを使用していますが、私は間違えていません)ORのような単純なシステム用のエミュレータをC64用に書き始めるべきです。

15
Baget

あなたがシミュレートする必要がある多くのハック(異常な効果のように)、タイミングの問題などがあるので、エミュレータは作成するのがとても難しいです。

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

それはまた、なぜ1MHzのCPUをエミュレートするためにマルチGHzのCPUが必要なのかを示しています。

10
Someone

JITのための命令レベルの最適化に関する素晴らしいアドバイスや、効率的なエミュレータを構築するための他の多くの良い点については、Darek Mihockaの Emulators.com を調べてください。

9
Barry Bond

私はゲーム機をエミュレートするほど空想的なことをしたことは一度もありませんでしたが、Andrew Tanenbaums Structured Computer Organisation で説明されているマシン用のエミュレータを書くことを課題としました。それは楽しかったと私はああ瞬間の多くを与えた。本当のエミュレータを書くことに飛び込む前に、あなたはその本を拾いたいと思うかもしれません。

7
oivvio

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

4
user34537

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

それは多くのエミュレーション問題に取り組みます: - ゲスト仮想からゲスト物理へのホスト仮想への効率的なアドレス変換 - ゲストコードのJITコンパイル - ネットワークアダプタ、タッチスクリーンとオーディオのような周辺機器のシミュレーション低電力モードからの再開のシミュレーションのための状態の復元

4
Barry Bond

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

解釈:bochsはインタプリタの一例です、それはx86 PCエミュレータです、それは意図された効果を生み出すためにそれを(ホストISAの)別の命令セットに変換します。すべての命令が同じサイクルを通るように何かをキャッシュしないでください。

動的エミュレータ:Qemuは動的エミュレータです。ゲスト命令のオンザフライ変換も結果をキャッシュします。最良の部分は、エミュレーションが高速になるように、できるだけ多くの命令をホストシステム上で直接実行することです。同じくCodyによって言及されるように、それはコードをブロックに分割します(1つの実行フロー)。

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

1
Deepthought

Chip-8システムをJavaScriptでエミュレートすることについての記事を書きました

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

私はNESのためにもう少し長いガイドを書く予定です。

1
alex

エミュレーションを始める方法.

1.低レベルのプログラミングをベースにした本を手に入れよう、あなたはニンテンドーの "ふりをする"オペレーティングシステムのためにそれを必要としている...ゲームの男の子...

2.特にエミュレーションに関する書籍を入手してください。 (あなたはOSを作ることはないでしょう、しかしそれに最も近いものです。

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

4.より複雑なコードの断片をIDE /コンパイラにコピーします。これで長いコードを書く手間が省けます。これは私がOS開発のためにしていることです、Linuxの地区を使用してください

1
5Mixer