Emulator programmieren: Mit Software das Verhalten von Hardware nachbilden

In Hardware läuft alles parallel, in Software nicht. Wie kann man das emulieren? Und wie bremst man einen schnellen Prozessor zum langsamen Gameboy?

Artikel von Johannes Hiltscher veröffentlicht am
Ein Emulator muss nicht nur dasselbe tun wie der Original-Gameboy, er muss es auch in derselben Reihenfolge tun.
Ein Emulator muss nicht nur dasselbe tun wie der Original-Gameboy, er muss es auch in derselben Reihenfolge tun. (Bild: Johannes Hiltscher/Golem.de)

Die Programmteile zur Emulation von Hardwarekomponenten sind mehr oder weniger schnell programmiert. Wie kommt dann aber alles zusammen? Gar nicht so leicht, denn in einem Chip arbeiten alle Komponenten parallel. Gelegentlich beeinflussen sie einander, die meiste Zeit arbeiten sie aber unabhängig voneinander. Während sie unabhängig arbeiten, erledigen sie eine gewisse Menge ihrer jeweiligen Aufgaben.

Inhalt:
  1. Emulator programmieren: Mit Software das Verhalten von Hardware nachbilden
  2. Eigene Prozessverwaltung
  3. Zeitsynchronisation
  4. Bild und Ton müssen stimmen
  5. Letzte Schritte und Fazit

Ein Emulator muss die zeitlichen Abhängigkeiten zwischen den Komponenten nachbilden, um die Funktion des Originalsystems überzeugend zu imitieren. Ein weiteres Problem ist, dass das emulierende System meist wesentlich leistungsfähiger ist als das emulierte. Wir nehmen hier wieder den Gameboy als Beispiel, der führt maximal einen Befehl pro Prozessortakt aus. Superskalare Prozessoren, wie sie seit Jahrzehnten in Computern und seit Jahren auch Smartphones üblich sind, können mehrere Befehle pro Takt ausführen. Zudem takten sie um ein Mehrtausendfaches höher.

Ein Spiel, das mehrere hundert Mal schneller läuft, als es sollte, macht aber wenig Spaß. Wir benötigen eine Lösung für die beiden zuvor genannten Probleme - und werden uns damit andere, neue Probleme einhandeln. Aber auch die lassen sich lösen.

Parallelität der Hardware

Ein Emulator muss zuerst einmal dafür sorgen, dass die zeitlichen Zusammenhänge zwischen den einzelnen Komponenten erhalten bleiben. Am besten verstehen lässt sich das an einem konkreten Beispiel. Die Grafikeinheit erzeugt zeilenweise das dargestellte Bild. Während des Großteils dieser Zeit hat die CPU keinen Zugriff auf den Grafikspeicher und kann entsprechend auch keine Änderungen vornehmen. Das ist erst am Ende der Zeile während des horizontalen Strahlrücklaufs (horizontal blank) möglich.

Dann kann das ausgeführte Programm Änderungen vornehmen, die sich auf die Grafik auswirken. Viele Spiele nutzen das für Effekte. Der Emulator muss sicherstellen, dass in beiden Phasen der Zeilenausgabe - während der Pixelausgabe und des Strahlrücklaufs - die CPU genauso viele Befehle ausführen kann, wie sie es auf einem Gameboy könnte. Problematischer als zu viele ausgeführte Befehle ist es, wenn der emulierte Prozessor weniger Befehle ausführen kann als die echte Hardware. Denn dann können Berechnungen noch nicht abgeschlossen sein und sich anders auswirken als beim Original.

Genau dafür haben wir doch viele Prozessorkerne! Oder?

Es mag intuitiv erscheinen, die Parallelität der Hardware zu erreichen, indem jede Komponente mittels Thread auf einem eigenen Prozessorkern emuliert wird. Threads zerlegen ein Programm in mehrere, parallel abzuarbeitende Teile auf, die allerdings noch immer im selben Adressraum liegen und so mit wenig Aufwand kommunizieren und Daten austauschen können. Theoretisch wäre es also denkbar, einfach jede Hardwarekomponente durch einen Prozessorkern abarbeiten zu lassen. Der Ansatz hat allerdings ein Problem: Die einzelnen Threads müssen sehr oft aktiviert werden und führen teils nur wenige Befehle aus.

Das funktioniert zwar, ist aber ziemlich ineffizient. Denn die Prozessverwaltung (Scheduling) eines Betriebssystems verteilt die Prozessorzeit aus Effizienzgründen nicht in beliebig kleinen Teilen (Zeitscheiben). Hierfür gibt es eine Untergrenze, bei Linux ist sie variabel und liegt im Bereich einiger Millisekunden. Keiner der Abläufe in der Gameboy-Hardware dauert so lang. Da zudem die emulierende Hardware wesentlich leistungsfähiger ist als die originale, benötigen die entsprechenden Programmteile weniger Zeit, als sie es in der Hardware täten.

Die einzelnen Threads warten also viel, und wenn ein Thread für einen kürzeren Zeitraum warten muss, als das Betriebssystem ihn anbietet, geschieht das mit busy waiting. Dabei verbringt der Prozess die Zeit bis zur nächsten Aktivierung in einer Warteschleife und belegt weiterhin die von ihm genutzten Ressourcen. Schlimmer noch: Da ihm vom Betriebssystem irgendwann der Prozessor weggenommen wird, läuft er dann eine ganze Weile gar nicht.

Die Synchronisation eines solchen Systems wäre der reinste Horror. Abgesehen davon nutzt es mehr Rechenzeit als benötigt wird und verhindert, dass Prozessorkerne heruntertakten oder in einen Stromsparmodus versetzt werden. Die Lösung des Problems ist eine eigene Prozessverwaltung für die sehr kurzen Ausführungsintervalle von teils wenigen Dutzend Mikrosekunden.

Bitte aktivieren Sie Javascript.
Oder nutzen Sie das Golem-pur-Angebot
und lesen Golem.de
  • ohne Werbung
  • mit ausgeschaltetem Javascript
  • mit RSS-Volltext-Feed
Eigene Prozessverwaltung 
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6.  


Aktuell auf der Startseite von Golem.de
USA
DJI-Drohnen könnten Betriebsverbot erhalten

DJI sieht sich in den USA wegen Bedenken hinsichtlich der nationalen Sicherheit zunehmendem Druck ausgesetzt. Am Ende könnte ein Betriebsverbot kommen.

USA: DJI-Drohnen könnten Betriebsverbot erhalten
Artikel
  1. Nur in der EU: Safari auf iOS ermöglicht Device Tracking
    Nur in der EU
    Safari auf iOS ermöglicht Device Tracking

    Auch im Inkognito-Modus bleibt ein iPhone auf einer entsprechend präparierten Webseite identifizierbar. Verantwortlich ist ein neues URI-Schema für alternative App-Stores.

  2. Delta Airlines: Boeing 767 verliert während des Fluges Notrutsche
    Delta Airlines
    Boeing 767 verliert während des Fluges Notrutsche

    Bei einer Boeing 767 der Delta Airlines kam es am vergangenen Freitag während eines Fluges von New York nach Los Angeles zu einem ungewöhnlichen Zwischenfall.

  3. Elektromobilität: Tesla verschweigt 82 Prozent der FSD- und Autopilotunfälle
    Elektromobilität
    Tesla verschweigt 82 Prozent der FSD- und Autopilotunfälle

    Von 2018 bis August 2023 deckte eine US-Behörde beschönigte Unfallstatistiken mit 15 Todesfällen auf, darunter einen durch Teslas Full Self Driving.

Du willst dich mit Golem.de beruflich verändern oder weiterbilden?
Zum Stellenmarkt
Zur Akademie
Zum Coaching
  • Schnäppchen, Rabatte und Top-Angebote
    Die besten Deals des Tages
    Daily Deals • HyperX Pulsefire Core RGB 16,43€ • TCL 75T7B QLED Pro 899€ • Alternate: ADATA LEGEND 960 MAX 2 TB 146,89€, Fractal Design Define 7 PCGH 156,89€ • ASUS PG27AQDM OLED 799€ • Gigabyte RTX 4070 Ti 709,95€ • MediaMarkt: ASUS-Gaming-Laptop 999€ statt 1.599€ • Gamesplanet Spring Sale [Werbung]
    •  /