Frage Wie funktionieren Emulatoren und wie werden sie geschrieben? [geschlossen]


Wie funktionieren Emulatoren? Wenn ich NES / SNES oder C64 Emulatoren sehe, verblüfft es mich.

http://www.tommowalker.co.uk/snemzelda.png

Müssen Sie den Prozessor dieser Maschinen emulieren, indem Sie seine speziellen Montageanweisungen interpretieren? Was geht sonst noch hinein? Wie sind sie typischerweise gestaltet?

Kannst du jemanden beraten, der einen Emulator (insbesondere ein Spielsystem) schreiben möchte?


969
2018-01-15 22:10


Ursprung


Antworten:


Emulation ist ein facettenreicher Bereich. Hier sind die grundlegenden Ideen und funktionalen Komponenten. Ich werde es in Stücke brechen und dann die Details über Änderungen eingeben. Viele der Dinge, die ich beschreiben werde, erfordern Kenntnisse über das Innenleben von Prozessoren - Montagewissen ist notwendig. Wenn ich bei bestimmten Dingen etwas zu vage bin, stelle bitte Fragen, damit ich diese Antwort weiter verbessern kann.

Grundidee:

Die Emulation arbeitet mit dem Verhalten des Prozessors und der einzelnen Komponenten. Sie bauen jedes einzelne Teil des Systems und verbinden dann die Teile ähnlich wie Drähte in Hardware.

Prozessor-Emulation:

Es gibt drei Möglichkeiten, die Prozessoremulation zu behandeln:

  • Deutung
  • Dynamische Neukompilierung
  • Statische Neukompilierung

Mit all diesen Pfaden haben Sie das gleiche Gesamtziel: Führen Sie einen Code aus, um den Prozessorzustand zu ändern und mit der Hardware zu interagieren. Der Prozessorzustand ist ein Konglomerat der Prozessorregister, Interrupt-Handler usw. für ein bestimmtes Prozessorziel. Für den 6502 hätten Sie eine Anzahl von 8-Bit-Ganzzahlen, die Register repräsentieren: A, X, Y, P, und S; Du hättest auch ein 16-Bit PC registrieren.

Mit Interpretation beginnen Sie bei der IP (Befehlszeiger - auch genannt PC, Programmzähler) und liest den Befehl aus dem Speicher. Ihr Code analysiert diesen Befehl und verwendet diese Informationen, um den vom Prozessor angegebenen Prozessorstatus zu ändern. Das Kernproblem bei der Interpretation ist, dass es so ist sehr langsam; Jedes Mal, wenn Sie eine bestimmte Anweisung bearbeiten, müssen Sie sie dekodieren und die erforderliche Operation ausführen.

Bei der dynamischen Neukompilierung durchlaufen Sie den Code ähnlich wie bei der Interpretation, aber anstatt nur Opcodes auszuführen, erstellen Sie eine Liste von Operationen. Sobald Sie eine Verzweigungsinstruktion erreicht haben, kompilieren Sie diese Liste von Operationen, um Maschinencode für Ihre Host-Plattform zu erstellen, dann cachen Sie diesen kompilierten Code und führen ihn aus. Wenn Sie dann erneut auf eine bestimmte Befehlsgruppe treffen, müssen Sie nur den Code aus dem Cache ausführen. (Übrigens, die meisten Leute erstellen nicht wirklich eine Liste von Anweisungen, sondern kompilieren sie, um Code on-the-fly zu bearbeiten - das macht es schwieriger zu optimieren, aber das ist außerhalb des Rahmens dieser Antwort, wenn nicht genug Leute interessiert sind)

Mit der statischen Neukompilierung machen Sie dasselbe wie bei der dynamischen Neukompilierung, aber Sie folgen den Verzweigungen. Am Ende erstellen Sie einen Codeabschnitt, der den gesamten Code im Programm darstellt, der dann ohne weitere Interferenz ausgeführt werden kann. Dies wäre ein großartiger Mechanismus, wenn es nicht die folgenden Probleme gäbe:

  • Code, der nicht im Programm enthalten ist (z. B. komprimiert, verschlüsselt, zur Laufzeit generiert / geändert usw.), wird nicht neu kompiliert, sodass er nicht ausgeführt wird
  • Es ist bewiesen, dass das Finden des gesamten Codes in einer gegebenen Binärdatei äquivalent ist zu Problem anhalten

Diese kombinieren, um eine statische Neukompilierung in 99% der Fälle vollständig unmöglich zu machen. Für weitere Informationen hat Michael Steil einige gründliche Nachforschungen über statische Neukompilierung angestellt - das Beste, was ich gesehen habe.

Die andere Seite der Prozessor-Emulation ist die Art, wie Sie mit Hardware interagieren. Das hat wirklich zwei Seiten:

  • Prozessorzeitsteuerung
  • Interruptbehandlung

Prozessor-Timing:

Bestimmte Plattformen - insbesondere ältere Konsolen wie NES, SNES usw. - erfordern, dass Ihr Emulator ein genaues Timing hat, um vollständig kompatibel zu sein. Mit dem NES haben Sie die PPU (Pixel Processing Unit), die es erfordert, dass die CPU in bestimmten Momenten Pixel in ihren Speicher legt. Wenn Sie die Interpretation verwenden, können Sie einfach Zyklen zählen und das richtige Timing emulieren. Mit dynamischer / statischer Rekompilierung sind die Dinge viel komplexer.

Unterbrechungsbehandlung:

Interrupts sind der primäre Mechanismus, über den die CPU mit der Hardware kommuniziert. Im Allgemeinen teilen Ihre Hardwarekomponenten der CPU mit, welche Unterbrechungen für sie wichtig sind. Das ist ziemlich einfach - wenn Ihr Code einen bestimmten Interrupt auslöst, schauen Sie sich die Interrupt-Handler-Tabelle an und rufen den richtigen Callback auf.

Hardware-Emulation:

Es gibt zwei Seiten, um ein bestimmtes Hardwaregerät zu emulieren:

  • Emulieren der Funktionalität des Geräts
  • Emulieren der tatsächlichen Geräteschnittstellen

Nehmen wir den Fall einer Festplatte. Die Funktionalität wird emuliert, indem der Sicherungsspeicher, Lese- / Schreib- / Formatierungsroutinen usw. erstellt werden. Dieser Teil ist im Allgemeinen sehr einfach.

Die eigentliche Schnittstelle des Geräts ist etwas komplexer. Dies ist im Allgemeinen eine gewisse Kombination von speicherabgebildeten Registern (z. B. Teile des Speichers, die das Gerät auf Änderungen überwacht, um eine Signalisierung durchzuführen) und Interrupts. Für eine Festplatte haben Sie möglicherweise einen Speicherbereich, in dem Sie Lesebefehle, Schreibvorgänge usw. platzieren und dann diese Daten zurücklesen.

Ich würde mehr ins Detail gehen, aber es gibt eine Million Möglichkeiten, wie Sie damit umgehen können. Wenn Sie spezielle Fragen haben, zögern Sie nicht zu fragen und ich werde die Informationen hinzufügen.

Ressourcen:

Ich denke, ich habe hier ein ziemlich gutes Intro gegeben, aber es gibt ein Tonne von zusätzlichen Bereichen. Ich bin mehr als glücklich, bei irgendwelchen Fragen zu helfen; Ich war in den meisten Fällen aufgrund der immensen Komplexität sehr vage.

Obligatorische Wikipedia-Links:

Allgemeine Emulationsressourcen:

  • Zophar  - Hier begann ich mit der Emulation, indem ich zuerst Emulatoren herunterlud und schließlich ihre riesigen Dokumentationsarchive plünderte. Dies ist die absolut beste Ressource, die Sie möglicherweise haben können.
  • NGEmu - Nicht viele direkte Ressourcen, aber ihre Foren sind unschlagbar.
  • RomHacking.net- Der Abschnitt Dokumente enthält Ressourcen zur Maschinenarchitektur für gängige Konsolen

Emulator-Projekte zum Referenzieren:

  • IronBabel - Dies ist eine Emulationsplattform für .NET, geschrieben in Nemerle und kompiliert Code in C # on-the-fly. Disclaimer: Dies ist mein Projekt, also entschuldige den schamlosen Stecker.
  • BSN - Ein genialer SNES-Emulator mit dem Ziel der Zyklus-perfekten Genauigkeit.
  • MAME - Das Arcade-Emulator. Große Referenz.
  • 6502asm.com  - Dies ist ein JavaScript 6502 Emulator mit einem coolen kleinen Forum.
  • dynarec'd 6502asm - Das ist ein kleiner Hack, den ich ein oder zwei Tage lang gemacht habe. Ich nahm den vorhandenen Emulator von 6502asm.com und änderte ihn, um den Code für massive Geschwindigkeitssteigerungen dynamisch in JavaScript neu zu kompilieren.

Referenzen zur Prozessorrekompilierung:

  • Die von Michael Steil (oben referenziert) durchgeführte Forschung zur statischen Rekompilierung gipfelte in dieses Papier und Sie können Quelle und so finden Hier.

Nachtrag:

Es ist gut ein Jahr her, seit diese Antwort eingereicht wurde und mit der ganzen Aufmerksamkeit, die es bekommen hat, dachte ich, es ist Zeit, einige Dinge zu aktualisieren.

Vielleicht ist das aufregendste in der Emulation gerade jetzt libcpu, gestartet von Michael Steil. Es ist eine Bibliothek, die eine große Anzahl von CPU-Kernen unterstützen soll, die LLVM für die Neukompilierung verwenden (statisch und dynamisch!). Es hat ein riesiges Potenzial und ich denke, es wird großartige Dinge für die Nachahmung tun.

Emu-Dokumente wurde auch auf mich aufmerksam gemacht, die eine große Sammlung von System-Dokumentation enthält, die sehr nützlich für Emulationszwecke ist. Ich habe dort nicht viel Zeit verbracht, aber es sieht so aus, als hätten sie viele großartige Ressourcen.

Ich bin froh, dass dieser Beitrag hilfreich war, und ich hoffe, dass ich bis Ende des Jahres / Anfang nächsten Jahres meinen Arsch verlassen und mein Buch zu diesem Thema fertigstellen kann.


1127
2018-01-15 22:13



Ein Mann namens Victor Moya del Barrio schrieb seine Diplomarbeit zu diesem Thema. Viele gute Informationen auf 152 Seiten. Sie können das PDF herunterladen Hier.

Wenn Sie sich nicht registrieren möchten beschriftet, können Sie den PDF-Titel googlen, "Untersuchung der Techniken für die Emulationsprogrammierung". Es gibt ein paar verschiedene Quellen für das PDF.


126
2018-02-02 17:27



Die Emulation mag entmutigend wirken, ist aber tatsächlich einfacher als simulieren.

Jeder Prozessor hat typischerweise eine gut geschriebene Spezifikation, die Zustände, Interaktionen usw. beschreibt.

Wenn Sie sich überhaupt nicht für die Leistung interessieren, können Sie die meisten älteren Prozessoren problemlos mit sehr eleganten objektorientierten Programmen emulieren. Zum Beispiel würde ein X86-Prozessor etwas benötigen, um den Zustand von Registern beizubehalten (einfach), etwas, um den Speicherzustand beizubehalten (einfach), und etwas, das jeden eingehenden Befehl übernehmen und auf den aktuellen Zustand der Maschine anwenden würde. Wenn Sie wirklich Genauigkeit wünschen, emulieren Sie auch Speicherübersetzungen, Caching usw., aber das ist machbar.

Tatsächlich testen viele Mikrochip- und CPU-Hersteller Programme gegen einen Emulator des Chips und dann gegen den Chip selbst, was ihnen hilft, herauszufinden, ob es Probleme in den Spezifikationen des Chips oder bei der tatsächlichen Implementierung des Chips in Hardware gibt. Zum Beispiel ist es möglich, eine Chipspezifikation zu schreiben, die zu Deadlocks führen würde, und wenn eine Frist in der Hardware auftritt, ist es wichtig zu sehen, ob sie in der Spezifikation reproduziert werden könnte, da dies ein größeres Problem als etwas in der Chipimplementierung anzeigt.

Natürlich interessieren sich Emulatoren für Videospiele in der Regel für die Leistung, so dass sie keine naiven Implementierungen verwenden, und sie enthalten auch Code, der mit dem Betriebssystem des Host-Systems verbunden ist, um beispielsweise Zeichen und Ton zu verwenden.

Angesichts der sehr langsamen Leistung alter Videospiele (NES / SNES, etc.) ist die Emulation auf modernen Systemen recht einfach. In der Tat ist es noch erstaunlicher, dass Sie einfach ein Set von jedem SNES-Spiel aller Zeiten oder jedem Atari 2600-Spiel herunterladen könnten, wenn man bedenkt, dass ein freier Zugang zu jeder Patrone zu einem Traum geworden wäre.


43
2018-01-15 22:43



Ich weiß, dass diese Frage ein bisschen alt ist, aber ich möchte etwas zur Diskussion hinzufügen. Die meisten Antworten hier konzentrieren sich auf Emulatoren, die die Maschinenanweisungen der Systeme, die sie emulieren, interpretieren.

Es gibt jedoch eine sehr bekannte Ausnahme namens "UltraHLE" (WIKIpedia Artikel). UltraHLE, einer der berühmtesten Emulatoren aller Zeiten, emulierte kommerzielle Nintendo 64-Spiele (mit anständiger Leistung auf Heimcomputern) zu einer Zeit, als es allgemein als unmöglich angesehen wurde. Tatsächlich hat Nintendo mit der Entwicklung von UltraHLE immer noch neue Titel für den Nintendo 64 produziert!

Zum ersten Mal sah ich Artikel über Emulatoren in Printmagazinen, wo ich sie vorher nur im Internet diskutiert hatte.

Das Konzept von UltraHLE sollte das Unmögliche möglich machen, indem C-Bibliotheksaufrufe statt Aufrufe auf Maschinenebene emuliert wurden.


29
2017-07-07 18:17



Etwas, das einen Blick wert ist, ist Imran Nazars Versuch, ein Buch zu schreiben Game Boy Emulator in JavaScript.


22
2017-11-11 10:19



Nachdem ich meinen eigenen Emulator des BBC Microcomputers der 80er Jahre (Typ VBeeb in Google) erstellt habe, gibt es eine Reihe von Dingen zu wissen.

  • Sie emulieren nicht das echte Ding als solches, das wäre eine Replik. Stattdessen emulieren Sie Zustand. Ein gutes Beispiel ist ein Taschenrechner, das echte Ding hat Knöpfe, Bildschirm, Fall usw. Aber um einen Taschenrechner zu emulieren, müssen Sie emulieren nur, ob Knöpfe oben oder unten sind, welche Segmente von LCD eingeschaltet sind, usw. Grundsätzlich eine Reihe von Zahlen Darstellen aller möglichen Kombinationen von Dingen, die sich in einem Taschenrechner ändern können.
  • Sie brauchen nur die Schnittstelle des Emulators, um so zu erscheinen und sich wie das Original zu verhalten. Je überzeugender das ist, desto näher ist die Emulation. Was hinter den Kulissen passiert, kann alles sein, was Sie wollen. Zum leichteren Schreiben eines Emulators gibt es jedoch eine mentale Zuordnung zwischen dem realen System, d. H. Chips, Anzeigen, Tastaturen, Leiterplatten und dem abstrakten Computercode.
  • Um ein Computersystem zu emulieren, ist es am einfachsten, es in kleinere Stücke aufzuteilen und diese Stücke einzeln zu emulieren. Dann string die ganze Menge für das fertige Produkt zusammen. Ähnlich wie eine Reihe von Black-Boxen mit Ein- und Ausgängen, die sich hervorragend für objektorientierte Programmierung eignen. Sie können diese Brocken weiter unterteilen, um das Leben einfacher zu machen.

In der Praxis suchen Sie im Allgemeinen nach Geschwindigkeit und Genauigkeit der Emulation zu schreiben. Dies liegt daran, dass Software auf dem Zielsystem langsamer läuft als die ursprüngliche Hardware auf dem Quellsystem. Dies kann die Wahl der Programmiersprache, der Compiler, des Zielsystems usw. einschränken.
Darüber hinaus müssen Sie das umschreiben, was Sie emulieren möchten, zum Beispiel ist es nicht notwendig, den Spannungszustand von Transistoren in einem Mikroprozessor zu emulieren, aber es ist wahrscheinlich notwendig, den Zustand des Registersatzes des Mikroprozessors zu emulieren.
Je kleiner der Detaillierungsgrad der Emulation ist, desto genauer wird das ursprüngliche System.
Schließlich können Informationen für ältere Systeme unvollständig oder nicht vorhanden sein. Es ist also wichtig, die Originalausrüstung zu beschaffen, oder zumindest einen anderen guten Emulator zu preisen, den jemand anders geschrieben hat!


18
2017-08-05 15:36



Ja, Sie müssen den gesamten binären Maschinencode "von Hand" interpretieren. Darüber hinaus müssen Sie meist auch exotische Hardware simulieren, die auf dem Zielrechner nicht gleichwertig ist.

Der einfache Ansatz besteht darin, die Anweisungen einzeln zu interpretieren. Das funktioniert gut, aber es ist langsam. Ein schnellerer Ansatz ist die Neukompilierung - Übersetzen des Quellmaschinencodes in den Zielmaschinencode. Dies ist komplizierter, da die meisten Anweisungen nicht eins zu eins zugeordnet werden. Stattdessen müssen Sie aufwendige Workarounds mit zusätzlichem Code durchführen. Aber am Ende ist es viel schneller. Die meisten modernen Emulatoren tun dies.


17
2018-01-15 22:17



Wenn Sie einen Emulator entwickeln, interpretieren Sie die Prozessorbaugruppe, an der das System arbeitet (Z80, 8080, PS-CPU usw.).

Sie müssen auch alle Peripheriegeräte emulieren, die das System besitzt (Videoausgang, Controller).

Sie sollten anfangen, Emulatoren für die einfachen Systeme wie das gute alte zu schreiben Game Boy (die einen Z80-Prozessor verwenden, verwechsle ich nicht) ODER für C64.


15
2018-01-15 22:21



Emulatoren sind sehr schwer zu erstellen, da es viele Hacks gibt (wie in ungewöhnlichen   Effekte), Timing-Probleme usw., die Sie simulieren müssen.

Ein Beispiel dafür finden Sie unter http://queue.acm.org/detail.cfm?id=1755886.

Das zeigt Ihnen auch, warum Sie eine Multi-GHz-CPU zum Emulieren eines 1-MHz-Rechners "brauchen".


10
2018-05-16 16:51



Schauen Sie sich auch Darek Mihockas an Emulatoren.com Für großartige Ratschläge zur Optimierung auf Befehlsebene für JITs und viele andere Leckerbissen zum Aufbau effizienter Emulatoren.


9
2018-05-16 13:27



Ich habe noch nie etwas so Kompliziertes getan, um eine Spielekonsole zu emulieren, aber ich habe einmal einen Kurs belegt, bei dem es darum ging, einen Emulator für die in Andrew Tanenbaums beschriebene Maschine zu schreiben Strukturierte Computerorganisation. Das hat Spaß gemacht und mir eine Menge Aha-Momente gegeben. Vielleicht möchten Sie dieses Buch aufheben, bevor Sie einen echten Emulator schreiben.


7
2017-09-30 09:23