gameboy-sharp
Emulator Game Boya (DMG) w C#. Rdzeń bez zależności od frontendu, więc cała maszyna działa i jest weryfikowana headless na standardowych testowych ROM-ach.
Emulator Game Boya (model DMG) napisany w C#. Rdzeń jest oddzielony od frontendu i sprawdzany headless na standardowych testowych ROM-ach, żeby cudze gry chodziły bit w bit.
Wprowadzenie
gameboy-sharp to emulator oryginalnego Game Boya, modelu DMG, napisany w C#. Zadanie brzmi prosto: sprawić, żeby gry wydane trzydzieści lat temu działały bez zmiany ani jednego bajtu. Trudność kryje się w słowie "wiernie" - emulator musi udawać sprzęt na tyle dokładnie, że oryginalny kod niczego nie zauważy.
Rdzeń zbudowaliśmy tak, żeby nie wiedział nic o oknie ani o dźwięku. Cała maszyna to czysta biblioteka, która oddaje bufor obrazu i próbki audio, a dopiero frontend zamienia je na to, co widzisz i słyszysz. Dzięki temu całą konsolę da się uruchomić bez ekranu i porównać jej wynik z tym, co powinno wyjść.
Błędy w emulatorze prawie nigdy nie wywalają się z hukiem. Gra się uruchamia, gra chodzi - tylko dźwięk jest odrobinę za wysoki albo postać przesuwa się o piksel za daleko. Nie ma wyjątku, nie ma crasha, jest tylko subtelnie złe zachowanie, którego "na oko" nie odróżnisz od poprawnego.
To zmienia cały sposób pracy. Nie możesz ufać wrażeniu, że "wygląda dobrze", bo właśnie to wrażenie kłamie najczęściej. Jedyne, czemu można ufać, to porównanie stanu maszyny z wartością, którą znasz z góry - i właśnie wokół tego zbudowany jest cały projekt.
Serce maszyny: procesor
Sercem konsoli jest procesor Game Boya, odmiana Z80. Każda instrukcja to odczyt bajtu opcode i wykonanie dokładnie tego, co zrobiłby oryginał, razem z liczbą cykli, którą ta instrukcja kosztuje. Timing nie jest szczegółem - to on decyduje, czy dźwięk i obraz są zsynchronizowane tak jak na prawdziwym sprzęcie.
Instrukcja i jej koszt w cyklach
| Opcode | Instrukcja | Cykle |
|---|---|---|
| 0x00 | NOP | 4 |
| 0x3E | LD A, d8 | 8 |
| 0xC3 | JP a16 | 16 |
Rdzeń nie wie nic o oknie ani o dźwięku - to czysta maszyna. Frontend dostaje tylko bufor obrazu i próbki audio. Dzięki temu całą konsolę da się odpalić bez ekranu i porównać wynik z oczekiwanym, cykl po cyklu.
Testy co do cyklu
Społeczność emulatorowa utrzymuje zestawy testowych ROM-ów, które sprawdzają poszczególne rejestry, flagi i timing co do cyklu. Uruchamiamy je headless i porównujemy stan maszyny z tym, co powinno wyjść. To jedyny uczciwy sposób, żeby powiedzieć, że emulator jest poprawny, a nie tylko "działa u mnie".
Właśnie dlatego rdzeń został odcięty od frontendu. Gdyby maszyna wymagała okna, każdy test trzeba by odklikać ręcznie. A że jest czystą biblioteką, testowe ROM-y przelatują automatycznie, a wynik jest binarny: zgadza się albo nie.
Efekt: cudze gry bit w bit
Na wyjściu jest emulator, który uruchamia cudze gry i przechodzi standardowe testowe ROM-y, a nie tylko wyświetla logo Nintendo i staje. Dźwięk jest tam, gdzie ma być, postać przesuwa się o tyle pikseli, o ile powinna, a poprawność jest dowiedziona testami, nie wrażeniem. To projekt zrobiony z miłości do starego sprzętu i doprowadzony do stanu, w którym to widać.
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ę.



