3

ゲーム ボーイエミュレーター ( Z80は CPU ですが、詳しくない人のために説明します)をプログラムしようとしています。

1 つ目は、ここで選択するプログラミング言語は C であるということでした。それはたいした問題ではありませんが、今日の立場からのご意見をお聞かせください。C++ でさえ推奨されませんでした。

次に分かったのは、誰もがオペコードごとに 1 つの関数を使用していたことです。これは 1 つの関数呼び出しであり、"ADD" 命令に対して 1 つの関数を使用するよりもおそらく最適化されているため、論理的に思えます。次に、ここで使用されているレジスタを見つける必要があります。しかし、それは今日、どれほど必要なのでしょうか? それは私が固執するべきものですか、それともエミュレーターを書き直すべきですか?

また、「そのレジスタをこのレジスタに追加する」ための関数を何度も何度も書くのは、一種の意欲をそそります。オペコードマップなどからそれを自動化する方法はありますか?

4

3 に答える 3

9

私は主に WingsOfIcarus に同意します。私はすでにいくつかのエミュレーターを書いているので、ここに私の洞察があります:

  1. 関数ポインターを使用することをお勧めします (コードの速度と明瞭さのため)。
  2. OOPは問題ありません

    はい、メンバーの呼び出しは少し遅くなりますが、注意すればパフォーマンスに大きな影響はありません。一方、OOP エミュレーション コードは、管理/読み取り/理解がはるかに優れています。

  3. 固定命令デコードの代わりに命令データベースを使用します。

    すべての指示に必要なすべての情報で構成される単一のテキスト ファイルを使用しています。エミュレーターは初期化中にそれを解析します (関数ポインターとオペランドの配列をフィードします...)。このアーキテクチャでは、コードを変更することなく、命令セットのエラーを簡単に修正できます。

    複雑な命令セットのドキュメントは、ほとんどの場合、ある時点で欠陥があります。最悪のケースはZ80です(100% エラーのない命令セットは見たことがありません)。したがって、より多くの命令セットを使用し、それらを比較して、エラーのないセットを作成してください (可能であれば)。

  4. エミュレーションにサウンド、ビデオ、キーボード、マウスを追加する

    通常、これは問題ではありません。Windowsでは、 DirectSoundの代わりにWaveOutを使用ます。より安定しており、はるかに高速です (DSound の使用可能なレイテンシーは 400 ミリ秒を超える場合もあります)。WaveOut を使用すると、遅延を 20 ~ 80 ミリ秒に抑えることができましたが、これは問題ありません。

  5. 毎秒エミュレートされた CPU の T サイクルで制限速度を適用します

    私はマシンサイクルの正しいタイミングを使用していますが、これははるかに遅いですが、ハードウェア周辺エミュレーションを (FDC、DMAC、サウンドチップなど、ハックなしで) 正しく実装することができます。

  6. エミュレートされたプラットフォームにファイルのロード/セーブを適用

たとえば、これは私の命令セットの一部です (CPU エミュレーションに直接供給されます:

opc      T0 T1 MC1   MC2   MC3   MC4   MC5   MC6   MC7   mnemonic

B8       04 00 M1R 4 ... 0 ... 0 ... 0 ... 0 ... 0 ... 0 CP A,B
B9       04 00 M1R 4 ... 0 ... 0 ... 0 ... 0 ... 0 ... 0 CP A,C
BA       04 00 M1R 4 ... 0 ... 0 ... 0 ... 0 ... 0 ... 0 CP A,D
BB       04 00 M1R 4 ... 0 ... 0 ... 0 ... 0 ... 0 ... 0 CP A,E
BC       04 00 M1R 4 ... 0 ... 0 ... 0 ... 0 ... 0 ... 0 CP A,H
BD       04 00 M1R 4 ... 0 ... 0 ... 0 ... 0 ... 0 ... 0 CP A,L
BE       07 00 M1R 4 MRD 3 ... 0 ... 0 ... 0 ... 0 ... 0 CP A,(HL)
BF       04 00 M1R 4 ... 0 ... 0 ... 0 ... 0 ... 0 ... 0 CP A,A
C0       11 05 M1R 5 MRD 3 MRD 3 ... 0 ... 0 ... 0 ... 0 RET NZ
C1       10 00 M1R 4 MRD 3 MRD 3 ... 0 ... 0 ... 0 ... 0 POP BC
C2L2H2   10 10 M1R 4 MRD 3 MRD 3 ... 0 ... 0 ... 0 ... 0 JP NZ,U16
C3L1H1   10 00 M1R 4 MRD 3 MRD 3 ... 0 ... 0 ... 0 ... 0 JP U16
C4L2H2   17 10 M1R 4 MRD 3 MRD 4 MWR 3 MWR 3 ... 0 ... 0 CALL NZ,U16
C5       11 00 M1R 5 MWR 3 MWR 3 ... 0 ... 0 ... 0 ... 0 PUSH BC
C6U2     07 00 M1R 4 MRD 3 ... 0 ... 0 ... 0 ... 0 ... 0 ADD A,U8
C7       11 00 M1R 5 MWR 3 MWR 3 ... 0 ... 0 ... 0 ... 0 RST 00H
C8       11 05 M1R 5 MRD 3 MRD 3 ... 0 ... 0 ... 0 ... 0 RET Z
C9       10 00 M1R 4 MRD 3 MRD 3 ... 0 ... 0 ... 0 ... 0 RET
CAL2H2   10 10 M1R 4 MRD 3 MRD 3 ... 0 ... 0 ... 0 ... 0 JP Z,U16

opc:    operation code [hex]
        L1,H1,U1,S1 means first operand direct number or address
        L2,H2,U2,S2 means second operand direct number or address
        L3,H3,U3,S3 means third operand direct number or address
        H,L ... U16 high and low byte
        U   ... U8 unsigned byte
        S   ... S8 signed byte

T0      normal instruction duration [T] always 2 decimal digits
T1      instruction duration if condition not met [T] always 2 decimal digits

MC1++   Machine cycle first is type,second is duration [T] always 1 decimal digit
        ...     unused
        M1R     M1 cycle
        MRD     memory read
        MWR     memory write
        IOR     IO read
        IOW     IO write
        NON     no external operation (internal computation)
        INT     interrupt cycle

mnem    instruction text (mnemonic)
  • opcポインターの配列内のアドレスに使用されます
  • mnemonic適切な関数ポインタとオペランドの型を選択するために使用されます
  • T0命令のタイミングにT1使用されます(大まかなエミュレーションにはこれで十分です)
  • MC1++正しい MC タイミングに使用されます (正しいハードウェア エミュレーションと競合タイミングを実装するため)。

これは、ダウンロード用のマシン サイクル タイミング リンクを含む、私の Zilog Z80A 完全な命令セットです。自由に使用してください (私のニックネームをどこかに言及してください)。これに移植した後、最終的にZEXALLテストに100%合格することができました。詳細については、 C または C++ でグラフィカルな Z80 エミュレーターを作成するを参照してください。

于 2013-09-20T07:54:02.360 に答える
2

最初の提案は、ネストされた switch ステートメントを使用するべきではありません。むしろ、関数ポインターの配列を使用する必要があります。より高速 -> エミュレーションが向上し、コードが改善されます。ネストされた switch-es も少し面倒になる可能性があります。ここにいくつかのリンクがあります。これらの配列について詳しく読む
http://www.newty.de/fpt/fpt.html
http://www.multigesture.net/wp-content/uploads/mirror/zenogais/FunctionPointers.htm

2番目の提案、はい、C#、Java、C++で実行できますが、CPUサイクルのすべてのビットが必要なため、可能な限り近いエミュレーションを取得できます-最小数のCPUサイクルでターゲットアーキテクチャの1つのCPUサイクルをエミュレートします私が人々から聞いたり読んだりしたことによると、この場合、OOPはあまり良くありません。その 1 つはパフォーマンスであり、2 つ目はほぼ明白です。エミュレーションは、お気づきかもしれませんが、非常に複雑なタスクであり、それを OOP でラップすることは首の不必要な苦痛になる可能性があります。

于 2013-04-03T01:35:43.183 に答える
1

以下は、NES エミュレータ用のいくつかのオペコードを使用した非常にクールな実装です。

http://bisqwit.iki.fi/jutut/kuvat/programming_examples/nesemu1/

何が起こっているのかについてもう少し説明がある付属の youtube ビデオを次に示します。

http://www.youtube.com/watch?v=y71lli8MS8s

C++ テンプレートといくつかの追加の C++11 機能を使用します。C++ を選択するか C を選択するかはあなた次第ですが、それほど重要ではありません。ゲームボーイをエミュレートするだけなら、最新のプロセッサで速度が問題になるとは思えないので、使い慣れたものを使用してみてください.

于 2013-03-28T23:48:32.040 に答える