私は主に WingsOfIcarus に同意します。私はすでにいくつかのエミュレーターを書いているので、ここに私の洞察があります:
- 関数ポインターを使用することをお勧めします (コードの速度と明瞭さのため)。
OOPは問題ありません
はい、メンバーの呼び出しは少し遅くなりますが、注意すればパフォーマンスに大きな影響はありません。一方、OOP エミュレーション コードは、管理/読み取り/理解がはるかに優れています。
固定命令デコードの代わりに命令データベースを使用します。
すべての指示に必要なすべての情報で構成される単一のテキスト ファイルを使用しています。エミュレーターは初期化中にそれを解析します (関数ポインターとオペランドの配列をフィードします...)。このアーキテクチャでは、コードを変更することなく、命令セットのエラーを簡単に修正できます。
複雑な命令セットのドキュメントは、ほとんどの場合、ある時点で欠陥があります。最悪のケースはZ80です(100% エラーのない命令セットは見たことがありません)。したがって、より多くの命令セットを使用し、それらを比較して、エラーのないセットを作成してください (可能であれば)。
エミュレーションにサウンド、ビデオ、キーボード、マウスを追加する
通常、これは問題ではありません。Windowsでは、 DirectSoundの代わりにWaveOutを使用します。より安定しており、はるかに高速です (DSound の使用可能なレイテンシーは 400 ミリ秒を超える場合もあります)。WaveOut を使用すると、遅延を 20 ~ 80 ミリ秒に抑えることができましたが、これは問題ありません。
毎秒エミュレートされた CPU の T サイクルで制限速度を適用します
私はマシンサイクルの正しいタイミングを使用していますが、これははるかに遅いですが、ハードウェア周辺エミュレーションを (FDC、DMAC、サウンドチップなど、ハックなしで) 正しく実装することができます。
エミュレートされたプラットフォームにファイルのロード/セーブを適用
たとえば、これは私の命令セットの一部です (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 エミュレーターを作成するを参照してください。