1

私は現在、次の学年の秋学期に備えるための夏のプログラミング プロジェクトとして、C++ で NES エミュレーターを作成しようとしています (しばらくコーディングしていません)。私はすでに Chip8 エミュレーターを書いているので、次のステップは NES エミュレーターを書いてみることだと思いました。

とにかく、私は立ち往生しています。オペコード テーブルにこのWeb サイトを使用していますが、障害が発生しています。Chip8 では、すべてのオペコードが 2 バイトの長さだったので、簡単に取得できました。ただし、NES には、CPU のアドレッシング モードに応じて、2 バイトまたは 3 バイトのオペコードがあるようです。オペコードごとに読み取る必要があるバイト数を簡単に計算する方法は思いつきません (私の唯一のアイデアは、オペコードの最初のバイトをチェックして、あと何バイト読み取るかを確認する、非常に長い if ステートメントを作成します)。

また、サイクルをカウントする方法を理解するのにも問題があります。すべてが同期するようにプログラミング言語内で時計を作成するにはどうすればよいですか?

無関係な補足として、NES はリトルエンディアンであるため、programCounter + 1 を読み取り、次に programCounter を読み取って正しいオペコードを取得する必要がありますか?

4

2 に答える 2

2

ただし、NES には、CPU のアドレッシング モードに応じて、2 バイトまたは 3 バイトのオペコードがあるようです。オペコードごとに読み取る必要があるバイト数を簡単に計算する方法は思いつきません。

オペコードはまだ 1 バイトです。余分なバイトは、明示的なオペランドを持つ命令のオペランドを指定します。デコードを行うには、256 ケースの -block を作成できますswitch(一部のオペコードが不正であるため、実際には 256 ケースにはなりません)。次のようになります。

opcode = ReadByte(PC++);
switch (opcode) {
...
case 0x4C:              // JMP abs
    address = ReadByte(PC++);
    address |= (uint16_t)ReadByte(PC) << 8;
    PC = address;
    cycles += 3;
    break;
...
}

コンパイラは通常、ケースのジャンプ テーブルを作成するため、かなり効率的な (少し肥大化した) コードが得られます。

もう 1 つの方法は、オペコードごとに 1 つのエントリを持つ配列を作成することです。これは単純に、オペコードごとに 1 つの関数を持つ関数ポインターの配列である可能性があります。または、テーブルには、オペランドをフェッチするための 1 つの関数へのポインター、実際の操作を実行するための関数へのポインター、および命令が必要とするサイクル数に関する情報を含めることができます。このようにして、多くのコードを共有できます。例:

const Instruction INSTRUCTIONS[] =
{
    ...
    // 0x4C: JMP abs
    {&jmp, &abs_operand, 3},
    ...
};

また、サイクルをカウントする方法を理解するのにも問題があります。すべてが同期するようにプログラミング言語内で時計を作成するにはどうすればよいですか?

CPU サイクルのカウントは、上記のコード例で示したように、カウンターをインクリメントするだけです。

ビデオを CPU と同期させる最も簡単な方法は、1 つのスキャンラインのアクティブな表示期間に対応するサイクル数だけ CPU を実行し、次に 1 つのスキャンラインを描画してから、水平方向に対応するサイクル数だけ CPU を実行することです。ブランキング期間、最初からやり直します。

オーディオを使い始めると、どのように同期するかは、使用しているオーディオ API によって少し異なります。たとえば、一部の API はコールバックを送信し、これに応答してバッファをサンプルで満たし、生成されたサンプルの数を返します。この場合、前のコールバック以降にエミュレートされた CPU サイクル数を計算し、それに基づいて生成するサンプル数を決定できます。


無関係な補足として、NES はリトルエンディアンであるため、正しいオペコードを取得するには、programCounter + 1 を読み取ってから programCounter を読み取る必要がありますか?

オペコードは 1 バイトであり、6502 の命令は他の CPU アーキテクチャのように 1 つのワードにパックされていないため、エンディアンはあまり重要ではありません。これ16 ビット オペランドに関連しますが、一方で PC やほとんどの携帯電話もリトルエンディアンの CPU に基づいています。

于 2013-10-22T11:55:08.647 に答える