GTK +用のChip-8エミュレーターの実際

私が学校にいて、Sinclair 48Kのソビエトのクローンで働いた/遊んだとき、隣人8086を夢見ていました。

486DX66が登場したとき、私は再びZ80について夢を見ました。 そのため、彼はレトロコンピューターへの愛を現在に伝えました。 そして今、私はハードウェアで「PCデザイナー」として自分自身を具体化しようとしていますが、CPUでなく、まれなコレクションも持っていますが、私は常に自分で仮想バージョンを作りたいと思っていました。 しかし、その後、十分な知識がなく、それから何か他のものがありました。 より頻繁に-時間。 最終的に、私は試してみることにしました。 夢は、EUコンピューター用のSVMを起動することでした。 しかし、家は基礎から建てられているので、最初から始めることにしました。





また、学校、アガサ、自宅のミクロシュ、そしてジャワでプログラムしました。 しかし、彼はそれを放棄しました。 1年半前、1つのプロセスを自動化するのに手間がかかりました。 私はCで書き、Linuxで作業し、GTK +(3.0)を使用します(勝ちながら書きますが、慣れています。そして、これは倒錯していることを知っています)。 GTK +で必要なものを正確に実装する例は見つかりませんでした。そのため、この投稿はGTKとエミュレーションを開始するのと同じように役立つでしょう。



エミュレーションの原理に関する記事、特にChip-8-キャリッジと小さなカートはありません。そのため、たとえばここここここ などでよく説明されているものは再投稿しません。



独自のコードを作成する前に、エミュレータのソースコードを確認しませんでした。 結果の喜びに加えて、目的は自習でした。 答えをのぞき込むと、常に暗記ができなくなりました。 したがって、私は最初に「自分を苦しめる」ことを望みました。 Gladeを使用しています。 したがって、インターフェイス全体がその中に描かれました。 これはテストの試みであり、実際の使用は計画されていなかったため、いくつかのことが簡素化されました。 私は次のシステムのエミュレーターで既に何かをすることにしました。 コードスタイルについては事前におaび申し上げます。



したがって、エミュレータ用のウィンドウを描画します。 ベースバージョンのChip-8の解像度は64 * 32で、ピクセルサイズは8 * 8でした。 したがって、描画するGtkDrawingAreaの適切なプロパティを設定します。









仮想CPU内のすべてが構造にあります



typedef struct { uint64_t last_cycle; uint64_t vsync; gboolean pressed; uint8_t last_key; gboolean run; uint8_t delay_timer; uint8_t sound_timer; uint8_t cycle; uint8_t keypad[16]; uint8_t V[16]; uint16_t opcode; uint16_t stack[16]; uint16_t sp; uint16_t I; uint16_t pc; uint8_t video[SCREEN_X][SCREEN_Y]; uint8_t video_mirrored[SCREEN_X][SCREEN_Y]; uint8_t memory[RAM_SIZE]; }_CHIP8; extern _CHIP8 SYS;
      
      







ビデオメモリは「非常に自然」に見えないかもしれませんが、それから128 * 64ディスプレイを備えたマイクロコントローラーに転送し、可能であればすべての不要な乗算/除算を取り除きたいと思いました。 そしてそれは残った。



ROMの分解は簡単で原始的です。

SYS.opcode = SYS.memory[SYS.pc] << 8 | SYS.memory[SYS.pc + 1];





その後、スイッチ/ケースを備えた比較的大きな機能で「バイナリマジック」が行われます。

私はもう少し長い間マイクロコントローラーを使っていましたが、それでもバイナリー算術は理解しやすい主題というよりもブラックボックスのようでした。 エミュレータを1〜2時間使用して、必要なすべての情報を "皮質下"に焼き付けました。

オペコードはほとんどないため、このソリューションは完全に正当化されます。 マシンコード自体は非常に便利にコンパイルされるため、このような関数は非常に迅速に記述されます。 主なことは、AND-8とORを理解することです。また、Chip-8はビッグエンディアンマシンであることを覚えておいてください。



メインループは独立したストリームで回転し、周波数は24 Hzであるため、画面を更新する予定でした。

問題は、GTKではすべての操作がメインループから行われる必要があることです。 したがって、1/24秒ごとにビデオメモリがミラーリングされ、g_idle_addを使用して、refresh_screenを呼び出したいことをメインループに通知します。 この関数は、リソースが利用可能になるとすぐに呼び出されます。 これを行わずに描画関数を別のスレッドから呼び出すと、ほぼ確実に機能します。 ペイントまたは面白いものになり、アーティファクトや特殊効果がなくなるまで、長時間動作することもあります。



 void *chip8_vcpu_pipeline(void *data) { […...........] g_idle_add((GSourceFunc) refresh_screen, NULL); […............] return (0); }
      
      







まず、GtkDrawingAreaに適切なコールバックを作成する必要があります。 すべての描画はこの関数で行われます。



 gboolean draw_cb(GtkWidget *widget, cairo_t *cr, gpointer data) { cr = gdk_cairo_create( gtk_widget_get_window (widget)); cairo_set_source_rgb(cr, 0, 0, 0); cairo_paint(cr); for ( int x = 0; x < SCREEN_X; x++ ) { for ( int y = 0; y < SCREEN_Y; y++ ) { SYS.video_mirrored[x][y] ? set_dot(cr, x, y) : clear_dot(cr, x, y); } } cairo_destroy(cr); return FALSE; }
      
      







ウェル機能とピクセル機能:ポイントの追加/削除



 void set_dot(cairo_t *cr, int32_t cx, int32_t cy) { cairo_set_source_rgb(cr, 255, 255, 255); cairo_set_line_width(cr, 2); cairo_rectangle(cr, cx * 8, cy * 8, 8, 8); cairo_fill(cr); cairo_stroke(cr); } void clear_dot(cairo_t *cr, int32_t cx, int32_t cy) { cairo_set_source_rgb(cr, 0, 0, 0); cairo_set_line_width(cr, 2); cairo_rectangle(cr, cx * 8, cy * 8, 8, 8); cairo_fill(cr); cairo_stroke(cr); }
      
      







draw_cb関数をGtkDrawingAreaのdrawイベントに接続します。 ここで1つのフレームを描画しますが、画面を更新する方法は? これは、GUI.screenがGtkDrawingAreaであるrefresh_screenで行われます。



 gboolean refresh_screen(void) { gtk_widget_queue_draw_area(GTK_WIDGET(GUI.screen), 0, 0, 512, 256); return FALSE; }
      
      







g_idle_addを介してレンダリングを呼び出したので、レンダリングが単一になるようにFALSEを返します。



キーボード。 2つの関数を書く



 gboolean on_key_press (GtkWidget *widget, GdkEventKey *event, gpointer user_data) { switch(event->keyval) { case GDK_KEY_1: SYS.keypad[1]=1; SYS.last_key = 1; break; case GDK_KEY_2: SYS.keypad[2]=1; SYS.last_key = 2; break; …........
      
      







on_key_releaseでも同じで、それぞれkey-press-eventとkey-release-eventに接続します。



明確な仕様が見つかりませんでした。Chip-8仮想マシンのプロセッサ速度はどのくらいですか。その結果、サイクルの長さを目で選択しました。 いずれにせよ、画面上の動画、さらにはピンポンをプレイする機会まで、非常によく動機付けされています。










All Articles