chip8-sharp

Kompletny, przetestowany emulator CHIP-8 w C#. Pełny zestaw instrukcji sprawdzany headless na społecznościowych ROM-ach testowych - czysta biblioteka bez zależności od renderowania.

chip8-sharp
TL;DR

Kompletny emulator CHIP-8 w C#. Pełny zestaw instrukcji, timery i wejście, wszystko sprawdzane headless na społecznościowych ROM-ach testowych. Czysta biblioteka, bez zależności od rysowania.

Wprowadzenie

chip8-sharp to kompletny emulator CHIP-8 napisany w C#. CHIP-8 jest mały - kilkadziesiąt instrukcji, prosta pamięć, ekran 64 na 32 piksele - i właśnie dlatego jest klasycznym "pierwszym emulatorem". Ale różnica między zabawką a projektem leży w tym słowie "kompletny": pełny zestaw instrukcji, timery i wejście, a nie połowa "wystarczająca do jednej gry".

Rdzeń jest czystą biblioteką bez zależności od rysowania, dokładnie jak przy Game Boyu. Dzięki temu całą maszynę da się odpalić headless i przepuścić przez testowe ROM-y bez żadnego okna, co zamienia "chyba działa" w "sprawdzone".

CHIP-8 to rytuał przejścia dla każdego, kto chce zrozumieć, jak działa emulacja. Jest na tyle mały, że da się go ogarnąć w całości w głowie, i na tyle prawdziwy, że uczy wszystkiego, co większe emulatory robią na większą skalę: cyklu pobierz-zdekoduj-wykonaj, pamięci, timerów, obsługi wejścia.

Jego rozmiar to zaleta, nie ograniczenie. Właśnie dlatego, że jest mały, da się go zrobić naprawdę poprawnie, a nie tylko "prawie". Każdy opcode można sprawdzić osobno, a społeczność ma gotowe ROM-y, które wypisują "OK" albo "FAIL" dla każdej instrukcji.

Jedna pętla: pobierz, zdekoduj, wykonaj

Cała maszyna kręci się wokół jednej pętli. Opcode to dwa bajty, a poszczególne bity mówią, która to instrukcja i na jakich rejestrach ma działać. Dekodowanie polega na wycięciu odpowiednich fragmentów z tych dwóch bajtów i skierowaniu ich do właściwej gałęzi.

Interpreter.cs · csharp
ushort opcode = (ushort)(memory[PC] << 8 | memory[PC + 1]);
PC += 2;

ushort nnn = (ushort)(opcode & 0x0FFF);
byte x = (byte)((opcode & 0x0F00) >> 8);
byte kk = (byte)(opcode & 0x00FF);

switch (opcode & 0xF000) {
    case 0x1000: PC = nnn; break;
    case 0x6000: V[x] = kk; break;
    case 0x7000: V[x] += kk; break;
}

Anatomia opkodu

Cały urok CHIP-8 polega na tym, że dekodowanie da się rozłożyć na czynniki pierwsze. Szesnastobitowy opcode dzieli się na fragmenty o ustalonym znaczeniu, a raz zrozumiana ta anatomia zamienia całą listę instrukcji w kilka prostych reguł.

Fragmenty szesnastobitowego opkodu

FragmentBityZnaczenie
nnndolne 12adres w pamięci
xbity 8-11numer rejestru
kkdolne 8stała ośmiobitowa
➜
Wskazówka

Jeśli dopiero zaczynasz z emulacją, zacznij właśnie od CHIP-8. Każdy opcode da się sprawdzić osobno, a społeczność ma gotowe ROM-y, które wypisują "OK" albo "FAIL" dla każdej instrukcji, więc od pierwszego dnia pracujesz z realnym feedbackiem.

Efekt: pełny zestaw, nie połowa

Na wyjściu jest mały, ale dopracowany emulator - pełny zestaw instrukcji, timery i wejście, sprawdzone headless na społecznościowych ROM-ach. Ten sam trik co przy Game Boyu, oddzielenie logiki od wyświetlania, sprawia, że poprawność jest dowiedziona, a nie deklarowana. To projekt mały z założenia, ale skończony naprawdę, a nie porzucony na "wystarczy do jednej gry".

Więcej projektów

Inne realizacje z tej samej kategorii - zobacz, jak podchodzimy do podobnych wyzwań.

Masz podobny projekt?

Napisz do nas - wycena jest bezpłatna i wraca w godzinę.