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