基本的に、命令を取得したら、それをデコードする必要があります。たとえば、オペコードテーブルから:
if ((inst&0xF000)==0x1000)
{
write_register(pc,(inst&0x0FFF)<<1);
}
そして、命令ごとに2バイトのROMにアクセスしているので、アドレスはおそらくバイトアドレスではなく(16ビット)ワードアドレスであると推測して、1つ左にシフトしました(これらの命令がどのようにエンコードされているかを調べる必要があります。提供したオペコードテーブルそれには不十分であり、仮定を立てる必要はありません)。
やらなければならないことがもっとたくさんあり、githubサンプルにそれについて何か書いたかどうかはわかりません。アドレスで命令をフェッチするためのフェッチ関数、読み取りメモリ関数、書き込みメモリ関数、読み取りレジスタ関数、書き込みレジスタ関数を作成することをお勧めします。デコードおよび実行関数は、一度に1つの命令のみをデコードして実行することをお勧めします。通常の実行では、ループで呼び出すだけで、多くの余分な作業を行うことなく、割り込みなどを実行できます。また、ソリューションをモジュール化します。fetch()read_mem_byte()read_mem_word()などの関数を作成する。コードをモジュール化すると(パフォーマンスがわずかに低下します)、レジスタまたはメモリアクセスを監視し、何が起こっているか、何が起こっていないかを把握できる単一の場所があるため、デバッグがはるかに簡単になります。
あなたの質問に基づいて、そしてあなたがこのプロセスのどこにいるのか、私はあなたがエミュレーターを書く前にあなたが最初にする必要があることは逆アセンブラーを書くことだと思います。固定命令長の命令セット(16ビット)であるため、はるかに簡単です。ROM内の興味深いポイントから開始することも、必要に応じて最初から開始して、表示されるすべてをデコードすることもできます。例えば:
if ((inst&0xF000)==0x1000)
{
printf("jmp 0x%04X\n",(inst&0x0FFF)<<1);
}
たった35の命令で、午後、おそらく土曜日全体が、命令をデコードするのは初めてです(私はあなたの質問に基づいていると思います)。逆アセンブラは、エミュレータのコアデコーダになります。printf()をエミュレーションに置き換えます。さらに、printfsをそのままにして、命令の実行をエミュレートするコードを追加するだけで、実行を追跡できます。(同じ取引では、単一の命令関数を逆アセンブルし、命令ごとに呼び出します。これがエミュレーターの基盤になります)。
このタスクを実行するには、ビット操作を十分に理解している必要があります。コードのフェッチ行が何をしているのかについて、理解が曖昧である必要があります。
また、私はあなたが提供したそのコード行をバグがあるか、少なくとも危険だと呼びます。memory []がバイトの配列である場合、コンパイラはバイトサイズの計算を使用して左シフトを実行するとゼロになり、2番目のバイトでゼロまたはredを実行すると、2番目のバイトのみになります。
基本的に、コンパイラはこれを有効にする権利の範囲内にあります。
opcode = memory[pc] << 8) | memory[pc + 1];
これに:
opcode = memory[pc + 1];
これはまったく機能しません。非常に簡単な修正です。
opcode = memory[pc + 0];
opcode <<= 8;
opcode |= memory[pc + 1];
あなたにいくつかの頭痛を救うでしょう。最小限の最適化により、コンパイラーは、操作ごとに中間結果をRAMに保存する必要がなくなり、同じ(望ましい)出力/パフォーマンスが得られます。
私が上で書いた命令セットシミュレーターは、パフォーマンスを目的としたものではなく、読みやすさ、可視性、そしてできれば教育的なものを目的としています。私はそのようなものから始めます、そして例えばパフォーマンスが興味があるならあなたはそれを書き直さなければならないでしょう。このchip8エミュレーターは、一度経験すると、最初から午後のタスクになるため、これを初めて実行すると、週末に3〜4回書き直すことができますが、記念碑的なタスクではありません(書き直さなければなりません)。 )。(サムレーターは週末に大部分を費やしました。msp430はおそらく夕方か2分の仕事のようなものでした。オーバーフローフラグを正しく取得することが最大のタスクでしたが、それは後で起こりました。 )。とにかく、ポイントは、mameソースのようなものを見て、これらの命令セットシミュレータのすべてではないにしても、ほとんどが実行速度のために設計されており、多くはかなりの量の研究なしではほとんど読めません。多くの場合、テーブル駆動型であり、Cプログラミングのトリックが多い場合もあります。管理しやすいものから始めて、適切に機能させてから、速度、サイズ、移植性などを改善することを心配してください。このchip8はグラフィックベースのように見えるので、ビットマップ/画面/どこでも多くの線画やその他のビット操作を処理する必要があります。または、APIまたはオペレーティングシステムの関数を呼び出すこともできます。基本的に、このchip8は、レジスタとアドレッシングモードとalu演算のランドリーリストを備えた従来の命令セットではありません。時には多くのCプログラミングのトリックなどがあります。管理しやすいものから始めて、適切に機能させてから、速度、サイズ、移植性などを改善することを心配してください。このchip8はグラフィックベースのように見えるので、ビットマップ/画面/どこでも多くの線画やその他のビット操作を処理する必要があります。または、APIまたはオペレーティングシステムの関数を呼び出すこともできます。基本的に、このchip8は、レジスタとアドレッシングモードとalu演算のランドリーリストを備えた従来の命令セットではありません。時には多くのCプログラミングのトリックなどがあります。管理しやすいものから始めて、適切に機能させてから、速度、サイズ、移植性などを改善することを心配してください。このchip8はグラフィックベースのように見えるので、ビットマップ/画面/どこでも多くの線画やその他のビット操作を処理する必要があります。または、APIまたはオペレーティングシステムの関数を呼び出すこともできます。基本的に、このchip8は、レジスタとアドレッシングモードとalu演算のランドリーリストを備えた従来の命令セットではありません。このchip8はグラフィックベースのように見えるので、ビットマップ/画面/どこでも多くの線画やその他のビット操作を処理する必要があります。または、APIまたはオペレーティングシステムの関数を呼び出すこともできます。基本的に、このchip8は、レジスタとアドレッシングモードとalu演算のランドリーリストを備えた従来の命令セットではありません。このchip8はグラフィックベースのように見えるので、ビットマップ/画面/どこでも多くの線画やその他のビット操作を処理する必要があります。または、APIまたはオペレーティングシステムの関数を呼び出すこともできます。基本的に、このchip8は、レジスタとアドレッシングモードとalu演算のランドリーリストを備えた従来の命令セットではありません。