命令セット エミュレーターは、ソフトウェア デバイスからバイナリ データを読み取り、データに含まれる命令を、あたかも物理データにアクセスする物理マイクロプロセッサであるかのように実行するソフトウェア プログラムです。
コモドール 64 は 6502 マイクロプロセッサを使用していました。このプロセッサ用のエミュレータを一度書きました。最初に行う必要があるのは、プロセッサのデータシートを読み、その動作について学習することです。どのような種類のオペコードがあり、メモリのアドレス指定、IO の方法はどうですか。そのレジスタは何ですか?どのように実行を開始しますか? これらはすべて、エミュレーターを作成する前に答える必要がある質問です。
これは、C でどのように表示されるかの一般的な概要です (100% 正確ではありません)。
uint8_t RAM[65536]; //Declare a memory buffer for emulated RAM (64k)
uint16_t A; //Declare Accumulator
uint16_t X; //Declare X register
uint16_t Y; //Declare Y register
uint16_t PC = 0; //Declare Program counter, start executing at address 0
uint16_t FLAGS = 0 //Start with all flags cleared;
//Return 1 if the carry flag is set 0 otherwise, in this example, the 3rd bit is
//the carry flag (not true for actual 6502)
#define CARRY_FLAG(flags) ((0x4 & flags) >> 2)
#define ADC 0x69
#define LDA 0xA9
while (executing) {
switch(RAM[PC]) { //Grab the opcode at the program counter
case ADC: //Add with carry
A = X + RAM[PC+1] + CARRY_FLAG(FLAGS);
UpdateFlags(A);
PC += ADC_SIZE;
break;
case LDA: //Load accumulator
A = RAM[PC+1];
UpdateFlags(X);
PC += MOV_SIZE;
break;
default:
//Invalid opcode!
}
}
この参照によると、 ADC には実際には 6502 プロセッサに 8 つのオペコードがあります。つまり、switch ステートメントに 8 つの異なる ADC があり、それぞれが異なるオペコードとメモリ アドレッシング スキームに対応しています。エンディアンとバイトオーダー、そしてもちろんポインターを処理する必要があります。まだ持っていない場合は、Cでのポインターと型キャストについてしっかりと理解できます。フラグ レジスタを操作するには、C のビット単位の操作を十分に理解している必要があります。賢い場合は、上記の CARRY_FLAG の例のように、C マクロや関数ポインターを使用して作業を節約できます。
命令を実行するたびに、その命令のサイズだけプログラム カウンターを進める必要があります。これはオペコードごとに異なります。一部のオペコードは引数を取らないため、サイズはわずか 1 バイトですが、上記の MOV の例のように 16 ビット整数を取るオペコードもあります。これはすべて、十分に文書化されている必要があります。
分岐命令 (JMP、JE、JNE など) は単純です。フラグ レジスタにフラグが設定されている場合は、指定されたアドレスに PC をロードします。これは、マイクロプロセッサで「決定」が行われる方法であり、それらをエミュレートすることは、実際のマイクロプロセッサが行うのと同じように、PC を変更するだけの問題です。
命令セット エミュレータの作成で最も難しいのはデバッグです。すべてが正常に機能しているかどうかは、どうすればわかりますか? あなたを助けるためのリソースはたくさんあります。すべての命令をデバッグするのに役立つテスト コードが作成されています。一度に 1 つの命令を実行して、リファレンス出力を比較できます。何かが違う場合は、どこかにバグがあり、それを修正できることがわかります。
始めるにはこれで十分です。重要なことは、A) エミュレートする命令セットを十分に理解していること、および B) C での低レベルのデータ操作 (型キャスト、ポインター、ビット演算、バイト オーダーなどを含む) をしっかりと理解していることです。