it-swarm.com.de

Wie funktionieren Emulatoren und wie werden sie geschrieben?

Wie funktionieren Emulatoren? Wenn ich NES/SNES- oder C64-Emulatoren sehe, wundert es mich.

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

Müssen Sie den Prozessor dieser Maschinen durch Interpretation der jeweiligen Montageanweisungen emulieren? Was steckt noch dahinter? Wie sind sie typischerweise aufgebaut?

Können Sie jemandem einen Rat geben, der daran interessiert ist, einen Emulator (insbesondere ein Spielesystem) zu schreiben?

968
mmcdole

Die Emulation ist ein facettenreicher Bereich. Hier sind die Grundideen und Funktionskomponenten. Ich werde es in Stücke brechen und dann die Details über Bearbeitungen ausfüllen. Viele der Dinge, die ich beschreiben werde, erfordern Kenntnisse über das Innenleben von Prozessoren - Montagekenntnisse sind erforderlich. Wenn ich in bestimmten Dingen ein bisschen zu vage bin, stelle bitte Fragen, damit ich diese Antwort weiter verbessern kann.

Grundidee:

Bei der Emulation wird das Verhalten des Prozessors und der einzelnen Komponenten berücksichtigt. Sie bauen jedes einzelne Teil des Systems und verbinden die Teile dann ähnlich wie Drähte in der Hardware.

Prozessoremulation:

Es gibt drei Möglichkeiten, mit der Prozessoremulation umzugehen:

  • Interpretation
  • Dynamische Neukompilierung
  • Statische Neukompilierung

Mit all diesen Pfaden haben Sie dasselbe übergeordnete Ziel: Führen Sie einen Code aus, um den Prozessorstatus zu ändern und mit „Hardware“ zu interagieren. Der Prozessorstatus ist eine Zusammenstellung 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 darstellen: A, X, Y, P und S; Sie hätten auch ein 16-Bit-PC -Register.

Bei der Interpretation starten Sie am IP (Anweisungszeiger - auch PC, Programmzähler genannt) und lesen die Anweisung aus dem Speicher. Ihr Code analysiert diese Anweisung und verwendet diese Informationen, um den von Ihrem Prozessor angegebenen Prozessorstatus zu ändern. Das Hauptproblem bei der Interpretation ist, dass es sehr langsam ist; 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 Verzweigungsanweisung erreicht haben, kompilieren Sie diese Liste von Vorgängen, um Code für Ihre Host-Plattform zu maschinieren, dann zwischenspeichern Sie diesen kompilierten Code und führen ihn aus. Wenn Sie dann eine bestimmte Anweisungsgruppe erneut treffen, müssen Sie nur den Code aus dem Cache ausführen. (Übrigens: Die meisten Leute erstellen keine Liste mit Anweisungen, sondern kompilieren sie im laufenden Betrieb zu Maschinencode. Dies erschwert die Optimierung, ist jedoch nicht Gegenstand dieser Antwort, es sei denn, es sind genügend Leute interessiert.)

Bei der statischen Neukompilierung verfahren Sie genauso wie bei der dynamischen Neukompilierung, folgen jedoch den Zweigen. Am Ende erstellen Sie einen Codeabschnitt, der den gesamten Code im Programm darstellt, der dann ohne weitere Beeinträchtigung ausgeführt werden kann. Ohne die folgenden Probleme wäre dies ein großartiger Mechanismus:

  • Code, der zu Beginn nicht im Programm enthalten ist (z. B. komprimiert, verschlüsselt, zur Laufzeit generiert/geändert usw.), wird nicht neu kompiliert und daher nicht ausgeführt
  • Es wurde bewiesen, dass das Finden des gesamten Codes in einer bestimmten Binärdatei dem Halteproblem entspricht

Diese kombinieren, um statische Rekompilierung in 99% der Fälle vollständig unmöglich zu machen. Für weitere Informationen hat Michael Steil einige großartige Untersuchungen zur statischen Rekompilierung durchgeführt - die besten, die ich je gesehen habe.

Die andere Seite der Prozessoremulation ist die Art und Weise, wie Sie mit der Hardware interagieren. Das hat wirklich zwei Seiten:

  • Prozessor-Timing
  • Behandlung unterbrechen

Prozessor-Timing:

Bestimmte Plattformen - insbesondere ältere Konsolen wie NES, SNES usw. - erfordern ein genaues Timing Ihres Emulators, um vollständig kompatibel zu sein. Mit dem NES haben Sie die PPU (Pixel Processing Unit), die es erfordert, dass die CPU zu bestimmten Zeitpunkten Pixel in ihren Speicher legt. Wenn Sie die Interpretation verwenden, können Sie die Zyklen leicht zählen und das richtige Timing emulieren. Bei der dynamischen/statischen Neukompilierung sind die Dinge viel komplexer.

Interrupt-Behandlung:

Interrupts sind der primäre Mechanismus, mit dem die CPU mit der Hardware kommuniziert. Im Allgemeinen teilen Ihre Hardwarekomponenten der CPU mit, um welche Interrupts es sich handelt. Dies ist ziemlich einfach: Wenn Ihr Code einen bestimmten Interrupt auslöst, sehen 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 Sie den Fall einer Festplatte. Die Funktionalität wird durch das Erstellen von Sicherungsspeichern, Lese-/Schreib-/Formatierungsroutinen usw. emuliert. Dieser Teil ist im Allgemeinen sehr einfach.

Die eigentliche Schnittstelle des Gerätes ist etwas komplexer. Dies ist im Allgemeinen eine Kombination von Registern mit Speicherzuordnung (z. B. Teile des Speichers, die das Gerät auf Änderungen überwacht, um eine Signalisierung durchzuführen) und Unterbrechungen. Auf einer Festplatte befindet sich möglicherweise ein Speicherbereich, in dem Sie Lesebefehle, Schreibvorgänge usw. platzieren und diese Daten dann zurücklesen können.

Ich würde genauer darauf eingehen, aber es gibt eine Million Möglichkeiten, wie Sie damit umgehen können. Wenn Sie hier spezielle Fragen haben, können Sie diese gerne stellen und ich füge die Informationen hinzu.

Ressourcen:

Ich denke, ich habe hier ein ziemlich gutes Intro gegeben, aber es gibt eine Tonne zusätzlicher Bereiche. Bei Fragen stehe ich Ihnen gerne zur Verfügung. Ich war in den meisten Fällen nur aufgrund der immensen Komplexität sehr vage.

Obligatorische Wikipedia-Links:

Allgemeine Emulationsressourcen:

  • Zophar - Hier begann ich mit der Emulation, lud zuerst Emulatoren herunter und plünderte schließlich ihre immensen Dokumentationsarchive. Dies ist die absolut beste Ressource, die Sie haben können.
  • NGEm - Nicht viele direkte Ressourcen, aber ihre Foren sind unschlagbar.
  • RomHacking.net - Der Dokumentabschnitt enthält Ressourcen zur Rechnerarchitektur für gängige Konsolen

Emulatorprojekte, auf die verwiesen wird:

  • IronBabel - Dies ist eine Emulationsplattform für .NET, die in Nemerle geschrieben wurde und Code sofort in C # neu kompiliert. Haftungsausschluss: Dies ist mein Projekt, also entschuldigen Sie den unverschämten Stecker.
  • BSnes - Ein großartiger SNES-Emulator mit dem Ziel einer zyklengenauen Genauigkeit.
  • MAME - Der Arcade-Emulator. Großartige Referenz.
  • 6502asm.com - Dies ist ein JavaScript 6502-Emulator mit einem coolen kleinen Forum.
  • dynarec'd 6502asm - Dies ist ein kleiner Hack, den ich über ein oder zwei Tage gemacht habe. Ich habe den vorhandenen Emulator von 6502asm.com übernommen und ihn geändert, um den Code für massive Geschwindigkeitssteigerungen dynamisch in JavaScript zu kompilieren.

Prozessor-Neukompilierungsreferenzen:

  • Die von Michael Steil (siehe oben) durchgeführten Untersuchungen zur statischen Rekompilierung gipfelten in diesem Artikel und Sie können Quelle und solche hier finden.

Nachtrag:

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

Das vielleicht aufregendste Moment in der Emulation ist libcp , 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 Emulation bewirken.

emu-docs wurde auch auf mich aufmerksam gemacht, in dem sich eine große Sammlung von Systemdokumentationen befindet, die für Emulationszwecke sehr nützlich sind. Ich habe dort nicht viel Zeit verbracht, aber es sieht so aus, als hätten sie eine Menge großartiger Ressourcen.

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

1126
Cody Brocious

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

Wenn Sie sich nicht bei scribd registrieren möchten, können Sie nach dem Titel PDF googeln. "Studium der Techniken für die Emulationsprogrammierung" . Es gibt verschiedene Quellen für das PDF.

126
mdm

Die Emulation mag entmutigend erscheinen, ist aber tatsächlich einfacher als das Simulieren.

Jeder Prozessor verfügt normalerweise über 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 mit sehr eleganten objektorientierten Programmen emulieren. Zum Beispiel würde ein X86-Prozessor etwas benötigen, um den Zustand der Register (einfach) aufrechtzuerhalten, etwas, um den Zustand des Speichers (einfach) aufrechtzuerhalten, und etwas, das jeden eingehenden Befehl entgegennimmt und ihn auf den aktuellen Zustand der Maschine anwendet. Wenn Sie Genauigkeit wirklich wollten, würden Sie auch Speicherübersetzungen, Caching usw. emulieren, aber das ist machbar.

Tatsächlich testen viele Mikrochip- und CPU-Hersteller Programme gegen einen Emulator des Chips und dann gegen den Chip selbst, um herauszufinden, ob es Probleme mit den Spezifikationen des Chips oder mit 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 Deadline in der Hardware auftritt, ist es wichtig zu prüfen, ob sie in der Spezifikation reproduziert werden kann, da dies auf ein größeres Problem als etwas in der Chipimplementierung hinweist.

Natürlich achten Emulatoren für Videospiele in der Regel auf die Leistung, sodass sie keine naiven Implementierungen verwenden. Außerdem enthalten sie Code, der mit dem Betriebssystem des Host-Systems kommuniziert, um beispielsweise Zeichnen und Sound zu verwenden.

Angesichts der sehr langsamen Leistung alter Videospiele (NES/SNES usw.) ist die Emulation auf modernen Systemen recht einfach. Umso erstaunlicher ist es, dass Sie einfach ein Set von jedem SNES-Spiel oder jedes Atari 2600-Spiel herunterladen können, wenn man bedenkt, dass es ein Traum gewesen wäre, wenn diese Systeme populär gewesen wären, freien Zugang zu jeder Kassette zu haben.

43
Uri

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

Es gibt jedoch eine sehr bekannte Ausnahme, die als "UltraHLE" bezeichnet wird ( WIKIpedia-Artikel ). UltraHLE, einer der bekanntesten Emulatoren aller Zeiten, emulierte kommerzielle Nintendo 64-Spiele (mit anständiger Leistung auf Heimcomputern) zu einer Zeit, als dies allgemein als unmöglich galt. Tatsächlich produzierte Nintendo noch neue Titel für den Nintendo 64, als UltraHLE erstellt wurde!

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

Das Konzept von UltraHLE bestand darin, das Unmögliche möglich zu machen, indem C-Bibliotheksaufrufe anstelle von Aufrufen auf Maschinenebene emuliert wurden.

29

Es lohnt sich, einen Blick auf Imran Nazars Versuch zu werfen, einen Gameboy -Emulator in JavaScript zu schreiben.

22
Julio

Nachdem ich meinen eigenen Emulator für den BBC-Mikrocomputer der 80er Jahre erstellt habe (geben Sie VBeeb in Google ein), gibt es eine Reihe von Dingen, die Sie wissen müssen.

  • Sie ahmen nicht die reale Sache als solche nach, das wäre eine Nachbildung. Stattdessen emulieren Sie State. Ein gutes Beispiel ist ein Taschenrechner, der über Schaltflächen, Bildschirm, Gehäuse usw. verfügt. Um einen Taschenrechner zu emulieren, müssen Sie nur emulieren, ob Schaltflächen nach oben oder unten zeigen, welche Segmente von LCD aktiviert sind usw. Grundsätzlich eine Reihe von Zahlen, die alle möglichen Kombinationen von Dingen darstellen, die sich in einem Taschenrechner ändern können.
  • Sie brauchen nur die Oberfläche des Emulators, um wie das Original zu erscheinen und sich zu verhalten. Je überzeugender dies ist, desto näher ist die Emulation. Was sich hinter den Kulissen abspielt, kann alles sein, was Sie wollen. Um das Schreiben eines Emulators zu vereinfachen, 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 Blöcke aufzuteilen und diese Blöcke einzeln zu emulieren. Dann reihen Sie die ganze Partie für das fertige Produkt zusammen. Ähnlich wie eine Reihe von Blackboxen mit Ein- und Ausgängen, die sich hervorragend für die objektorientierte Programmierung eignen. Sie können diese Stücke weiter unterteilen, um das Leben zu erleichtern.

In der Praxis streben Sie im Allgemeinen danach, schnell und genau zu emulieren. Dies liegt daran, dass die Software auf dem Zielsystem langsamer (möglicherweise) ausgeführt wird 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 umschreiben, was Sie zu emulieren bereit sind, zum Beispiel, dass es nicht erforderlich ist, den Spannungszustand von Transistoren in einem Mikroprozessor zu emulieren, aber es ist wahrscheinlich erforderlich, den Zustand des Registersatzes von zu emulieren Mikroprozessor.
Im Allgemeinen erhalten Sie umso mehr Wiedergabetreue zum ursprünglichen System, je kleiner der Detailgrad der Emulation ist.
Schließlich können Informationen für ältere Systeme unvollständig oder nicht vorhanden sein. Es ist also unerlässlich, sich die Originalausrüstung zu beschaffen, oder zumindest einen anderen guten Emulator, den jemand anderes geschrieben hat, auseinanderzunehmen!

18

Ja, Sie müssen den gesamten Code des Binärcomputers "von Hand" interpretieren. Darüber hinaus müssen Sie die meiste Zeit auch exotische Hardware simulieren, die auf dem Zielcomputer kein Äquivalent aufweist.

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

17
Vilx-

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

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

Sie sollten anfangen, Emulatoren für die simpe Systeme wie das gute alte zu schreiben Game Boy (die einen Prozessor Z80 verwenden, irre ich mich nicht) OR für C64.

15
Baget

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

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

Hier erfahren Sie auch, warum Sie eine Multi-GHz-CPU für die Emulation einer 1-MHz-CPU benötigen.

10
Someone

Lesen Sie auch Darek Mihockas Emulators.com , um gute Ratschläge zur Optimierung auf Anweisungsebene für JITs und viele andere Tipps zum Erstellen effizienter Emulatoren zu erhalten.

9
Barry Bond

Ich habe noch nie etwas Besonderes getan, um eine Spielekonsole zu emulieren, aber ich habe einmal einen Kurs besucht, in dem es darum ging, einen Emulator für die in Andrew Tanenbaums Structured Computer Organization beschriebene Maschine zu schreiben. Das hat Spaß gemacht und mir viele Aha-Momente beschert. Vielleicht möchten Sie dieses Buch in die Hand nehmen, bevor Sie einen echten Emulator schreiben.

7
oivvio

Ratschläge zur Emulation eines echten Systems oder Ihres eigenen? Ich kann sagen, dass Emulatoren funktionieren, indem sie die GESAMTE Hardware emulieren. Vielleicht nicht auf den Stromkreis zurückzuführen (wie das Bewegen von Bits wie beim HW. Das Verschieben des Bytes ist das Endergebnis, sodass das Kopieren des Bytes in Ordnung ist). Emulatoren sind sehr schwer zu erstellen, da es viele Hacks (wie bei ungewöhnlichen Effekten), Timing-Probleme usw. gibt, die Sie simulieren müssen. Wenn ein (Eingabe-) Teil falsch ist, kann das gesamte System ausfallen oder bestenfalls einen Fehler aufweisen.

4
user34537

Der Shared Source Device Emulator enthält baubaren Quellcode für einen PocketPC/Smartphone-Emulator (erfordert Visual Studio, läuft unter Windows). Ich habe an V1 und V2 der Binärversion gearbeitet.

Es behebt viele Emulationsprobleme: - Effiziente Adressumsetzung vom virtuellen Gast zum physischen Gast zum virtuellen Host - JIT-Kompilierung des Gastcodes - Simulation von Peripheriegeräten wie Netzwerkadaptern, Touchscreen und Audio - Integration der Benutzeroberfläche für Tastatur und Maus des Hosts - Speichern/Wiederherstellung des Zustands für die Simulation der Wiederaufnahme aus dem Energiesparmodus

4
Barry Bond

Hinzufügen der Antwort von @Cody Brocious
Im Kontext der Virtualisierung, in der Sie ein neues System (CPU, E/A usw.) für eine virtuelle Maschine emulieren, werden die folgenden Kategorien von Emulatoren angezeigt.

Interpretation: bochs ist ein Beispiel für einen Interpreter, es ist ein x86-PC-Emulator, bei dem jeder Befehl vom Gastsystem in einen anderen Befehlssatz (des Host-ISA) übersetzt wird, um den beabsichtigten Effekt zu erzielen Sie können nichts zwischenspeichern, sodass jeder Befehl denselben Zyklus durchläuft.

Dynamischer Emulator: Qemu ist ein dynamischer Emulator. Die direkte Übersetzung von Gastanweisungen speichert auch die Ergebnisse zwischen. Das Beste daran ist, dass so viele Anweisungen wie möglich direkt auf dem Host-System ausgeführt werden, damit die Emulation schneller erfolgt. Wie auch von Cody erwähnt, unterteilt es den Code in Blöcke (1 einzelner Ausführungsfluss).

Statischer Emulator: Soweit ich weiß, gibt es keinen statischen Emulator, der bei der Virtualisierung hilfreich sein kann.

1
Deepthought

Ich schrieb einen Artikel über das Emulieren des Chip-8-Systems in JavaScript .

Es ist ein großartiger Ausgangspunkt, da das System nicht sehr kompliziert ist, Sie aber dennoch lernen, wie Opcodes, der Stack, die Register usw. funktionieren.

Ich werde demnächst einen längeren Leitfaden für das NES schreiben.

1
alex

Wie ich die Emulation starten würde.

1.Holen Sie sich Bücher, die auf Low-Level-Programmierung basieren. Sie benötigen sie für das "so tun als ob" Betriebssystem des Nintendo ... game boy ...

2. Holen Sie sich Bücher über Emulation und vielleicht auch über die Entwicklung von Betriebssystemen. (du machst kein os, aber das am nächsten dran.

3. Sehen Sie sich einige Open Source-Emulatoren an, insbesondere diejenigen des Systems, für das Sie einen Emulator erstellen möchten.

4. Kopieren Sie Ausschnitte des komplexeren Codes in Ihre IDE/Ihren Compliler. Dadurch sparen Sie sich das Ausschreiben von langem Code. Dies ist, was ich für die OS-Entwicklung tue, benutze einen Bezirk von Linux

1
5Mixer