Wie funktionieren Emulatoren? Wenn ich NES / SNES- oder C64-Emulatoren sehe, wundert es mich.
Müssen Sie den Prozessor dieser Maschinen emulieren, indem Sie die jeweiligen Montageanweisungen interpretieren? Was steckt noch drin? Wie sind sie normalerweise gestaltet?
Können Sie jemandem einen Rat geben, der daran interessiert ist, einen Emulator zu schreiben (insbesondere ein Spielsystem)?
Antworten:
Emulation ist ein facettenreicher Bereich. Hier sind die Grundideen und Funktionskomponenten. Ich werde es in Stücke zerbrechen und dann die Details über Änderungen ausfüllen. Viele der Dinge, die ich beschreiben werde, erfordern Kenntnisse über das Innenleben von Prozessoren - Montagekenntnisse sind erforderlich. Wenn ich in bestimmten Dingen etwas zu vage bin, stellen Sie bitte Fragen, damit ich diese Antwort weiter verbessern kann.
Grundidee:
Bei der Emulation wird das Verhalten des Prozessors und der einzelnen Komponenten behandelt. 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:
Mit all diesen Pfaden haben Sie das gleiche allgemeine Ziel: Führen Sie einen Code aus, um den Prozessorstatus zu ändern und mit 'Hardware' zu interagieren. Der Prozessorstatus ist ein Konglomerat der Prozessorregister, Interrupt-Handler usw. für ein bestimmtes Prozessorziel. Für die 6502, dann würden Sie eine Reihe von 8-Bit - Integer haben repräsentiert Register:
A
,X
,Y
,P
, undS
; Sie hätten auch ein 16-Bit-PC
Register.Bei der Interpretation beginnen Sie am
IP
(Befehlszeiger - auchPC
Programmzähler genannt) und lesen den Befehl aus dem Speicher. Ihr Code analysiert diese Anweisung und verwendet diese Informationen, um den von Ihrem Prozessor angegebenen Prozessorstatus zu ändern. Das Kernproblem 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 iterieren Sie ähnlich wie bei der Interpretation über den Code, 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 Hostplattform zu verarbeiten. Anschließend speichern Sie diesen kompilierten Code im Cache 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 erstellen die meisten Leute keine Liste von Anweisungen, sondern kompilieren sie im laufenden Betrieb zu Maschinencode - dies erschwert die Optimierung, aber das liegt außerhalb des Rahmens dieser Antwort, es sei denn, es sind genügend Leute interessiert.)
Bei der statischen Neukompilierung machen Sie dasselbe wie bei der dynamischen Neukompilierung, folgen jedoch Zweigen. Am Ende erstellen Sie einen Codeabschnitt, der den gesamten Code im Programm darstellt und dann ohne weitere Eingriffe ausgeführt werden kann. Dies wäre ein großartiger Mechanismus, wenn nicht die folgenden Probleme auftreten würden:
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 großartige Untersuchungen zur statischen Neukompilierung durchgeführt - das Beste, was ich je gesehen habe.
Die andere Seite der Prozessoremulation ist die Art und Weise, wie Sie mit Hardware interagieren. Das hat wirklich zwei Seiten:
Prozessor-Timing:
Bestimmte Plattformen - insbesondere ältere Konsolen wie NES, SNES usw. - erfordern ein striktes Timing Ihres Emulators, um vollständig kompatibel zu sein. Mit dem NES haben Sie die PPU (Pixel Processing Unit), die erfordert, dass die CPU Pixel zu bestimmten Zeitpunkten in ihren Speicher legt. Wenn Sie die Interpretation verwenden, können Sie 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, den 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 Rückruf auf.
Hardware-Emulation:
Das Emulieren eines bestimmten Hardwaregeräts hat zwei Seiten:
Nehmen Sie den Fall einer Festplatte. Die Funktionalität wird durch Erstellen des Sicherungsspeichers, der 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 aus speicherabgebildeten Registern (z. B. Teilen des Speichers, die das Gerät auf Änderungen überwacht, um die Signalisierung durchzuführen) und Interrupts. Für eine Festplatte verfügen Sie möglicherweise über einen Speicherbereich, in dem Sie Lesebefehle, Schreibvorgänge usw. platzieren und diese Daten dann zurücklesen.
Ich würde näher 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 werde die Informationen hinzufügen.
Ressourcen:
Ich denke, ich habe hier ein ziemlich gutes Intro gegeben, aber es gibt eine Menge zusätzlicher Bereiche. Bei Fragen stehe ich Ihnen gerne zur Verfügung. Ich war in den meisten Fällen sehr vage, einfach wegen der immensen Komplexität.
Obligatorische Wikipedia-Links:
Allgemeine Emulationsressourcen:
Emulator-Projekte als Referenz:
Referenzen zur Prozessorkompilierung:
Nachtrag:
Es ist weit über ein Jahr her, seit diese Antwort eingereicht wurde, und mit all der Aufmerksamkeit, die sie bekommen hat, dachte ich, es ist Zeit, einige Dinge zu aktualisieren.
Das vielleicht aufregendste im Moment in der Emulation ist libcpu , das von dem oben genannten Michael Steil gestartet wurde. Es handelt sich um 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 tun.
Ich wurde auch auf emu-docs aufmerksam gemacht, das ein großes Repository an Systemdokumentation enthält, das für Emulationszwecke sehr nützlich 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 mich bis Ende des Jahres / Anfang nächsten Jahres von meinem Arsch lösen und mein Buch zu diesem Thema fertigstellen kann.
quelle
Ein Mann namens Victor Moya del Barrio hat seine Diplomarbeit zu diesem Thema geschrieben. 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 den PDF-Titel "Studium der Techniken für die Emulationsprogrammierung" bei Google suchen . Es gibt verschiedene Quellen für das PDF.
quelle
Die Emulation mag entmutigend erscheinen, ist aber tatsächlich einfacher als die Simulation.
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 problemlos emulieren. Zum Beispiel würde ein X86-Prozessor etwas benötigen, um den Status der Register aufrechtzuerhalten (einfach), etwas, um den Status des Speichers aufrechtzuerhalten (einfach), und etwas, das jeden eingehenden Befehl entgegennimmt und ihn auf den aktuellen Status des Computers anwendet. Wenn Sie wirklich Genauigkeit 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 Frist in der Hardware eintritt, 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 kümmern sich Emulatoren für Videospiele normalerweise um die Leistung, sodass sie keine naiven Implementierungen verwenden. Außerdem enthalten sie Code, der mit dem Betriebssystem des Hostsystems verbunden ist, z. B. um Zeichnung 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 einen Satz jedes SNES-Spiels oder eines Atari 2600-Spiels herunterladen können, wenn man bedenkt, dass der freie Zugriff auf jede Kassette ein Traum gewesen wäre, wenn diese Systeme beliebt gewesen wären.
quelle
Ich weiß, dass diese Frage etwas alt ist, aber ich möchte der Diskussion etwas hinzufügen. Die meisten Antworten beziehen sich auf Emulatoren, die die Maschinenanweisungen der von ihnen emulierten Systeme interpretieren.
Es gibt jedoch eine sehr bekannte Ausnahme namens "UltraHLE" ( WIKIpedia-Artikel ). UltraHLE, einer der bekanntesten Emulatoren, die jemals entwickelt wurden, emulierte kommerzielle Nintendo 64-Spiele (mit anständiger Leistung auf Heimcomputern) zu einer Zeit, als dies allgemein als unmöglich angesehen wurde. 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.
quelle
Ein Blick lohnt sich auf Imran Nazars Versuch, einen Gameboy- Emulator in JavaScript zu schreiben .
quelle
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 zu wissen.
In der Praxis möchten Sie im Allgemeinen nach Geschwindigkeit und Wiedergabetreue schreiben. Dies liegt daran, dass Software auf dem Zielsystem (möglicherweise) langsamer ausgeführt wird als die ursprüngliche Hardware auf dem Quellsystem. Dies kann die Auswahl der Programmiersprache, der Compiler, des Zielsystems usw. einschränken.
Außerdem müssen Sie umschreiben, was Sie emulieren möchten. Beispielsweise ist es nicht erforderlich, den Spannungszustand von Transistoren in einem Mikroprozessor zu emulieren, aber es ist wahrscheinlich erforderlich um den Zustand des Registersatzes des Mikroprozessors zu emulieren.
Im Allgemeinen gilt: 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 unerlässlich, sich die Originalausrüstung zu besorgen oder zumindest einen anderen guten Emulator auseinander zu nehmen, den jemand anderes geschrieben hat!
quelle
Ja, Sie müssen das gesamte binäre Maschinencode-Chaos "von Hand" interpretieren. Darüber hinaus müssen Sie meistens auch exotische Hardware simulieren, die auf dem Zielcomputer kein Äquivalent hat.
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 Quellmaschinencodes in den Zielmaschinencode. Dies ist komplizierter, da die meisten Anweisungen nicht eins zu eins zugeordnet werden. Stattdessen müssen Sie aufwändige Problemumgehungen vornehmen, die zusätzlichen Code enthalten. Aber am Ende ist es viel schneller. Die meisten modernen Emulatoren tun dies.
quelle
... --- ...
- Diese drei Morsecodes repräsentieren die drei Buchstaben S, O, S." Denn...
ist ein Code, der den Buchstaben "S" darstellt. Nein?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 des Systems emulieren (Videoausgang, Controller).
Sie sollten anfangen, Emulatoren für die Simpe-Systeme wie den guten alten Game Boy (die einen Z80-Prozessor verwenden, verwechsle ich nicht) ODER für C64 zu schreiben .
quelle
Ein Beispiel hierfür finden Sie unter http://queue.acm.org/detail.cfm?id=1755886 .
Das zeigt Ihnen auch, warum Sie eine Multi-GHz-CPU zum Emulieren einer 1-MHz-CPU benötigen.
quelle
Besuchen Sie auch Darek Mihockas Emulators.com, um gute Ratschläge zur Optimierung auf Befehlsebene für JITs und viele andere Extras zum Erstellen effizienter Emulatoren zu erhalten.
quelle
Ich habe noch nie etwas so Besonderes getan, um eine Spielekonsole zu emulieren, aber ich habe einmal einen Kurs besucht, in dem die Aufgabe darin bestand, 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 das Buch abholen, bevor Sie einen echten Emulator schreiben.
quelle
Ratschläge zur Emulation eines realen Systems oder Ihres eigenen Dings? Ich kann sagen, dass Emulatoren funktionieren, indem sie die GESAMTE Hardware emulieren. Möglicherweise nicht auf die Schaltung zurückzuführen (wie es das Verschieben von Bits wie bei der HW tun würde. Das Verschieben des Bytes ist das Endergebnis, daher ist das Kopieren des Bytes in Ordnung). 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 haben.
quelle
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 behandelt viele Emulationsprobleme: - Effiziente Adressübersetzung von virtuellem Gast zu physischem Gast zu virtuellem Host - JIT-Kompilierung von Gastcode - Simulation von Peripheriegeräten wie Netzwerkadaptern, Touchscreen und Audio - Integration der Benutzeroberfläche für Host-Tastatur und -Maus - Speichern / Wiederherstellung des Zustands zur Simulation der Wiederaufnahme aus dem Energiesparmodus
quelle
Hinzufügen der Antwort von @Cody Brocious
Im Zusammenhang mit der Virtualisierung, bei der Sie einer virtuellen Maschine ein neues System (CPU, E / A usw.) emulieren, werden die folgenden Kategorien von Emulatoren angezeigt.
Interpretation: bochs ist ein Beispiel für einen Interpreter. Es ist ein x86-PC-Emulator. Jeder Befehl vom Gastsystem übersetzt ihn in einen anderen Befehlssatz (des Host-ISA), um den beabsichtigten Effekt zu erzielen. Ja, er ist sehr langsam, nicht Es wird nichts zwischengespeichert, sodass jeder Befehl denselben Zyklus durchläuft.
Dynamischer Emalator: Qemu ist ein dynamischer Emulator. Die Übersetzung von Gastanweisungen wird auch im laufenden Betrieb zwischengespeichert. Das Beste daran ist, dass so viele Anweisungen wie möglich direkt auf dem Hostsystem ausgeführt werden, sodass die Emulation schneller ist. Wie von Cody erwähnt, wird der Code in Blöcke unterteilt (1 einzelner Ausführungsfluss).
Statischer Emulator: Soweit ich weiß, gibt es keinen statischen Emulator, der bei der Virtualisierung hilfreich sein kann.
quelle
Wie ich mit der Emulation anfangen würde.
1. Holen Sie sich Bücher, die auf Low-Level-Programmierung basieren. Sie benötigen sie für das "vorgetäuschte" Betriebssystem des Nintendo ... Game Boy ...
2. Holen Sie sich Bücher speziell über Emulation und möglicherweise über die Entwicklung. (Sie werden kein Betriebssystem erstellen, aber das nächstgelegene.
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. Dies erspart Ihnen das Schreiben von langem Code. Dies ist, was ich für die OS-Entwicklung mache, benutze einen Linux-Distrikt
quelle
Ich habe einen Artikel über die Emulation des Chip-8-Systems in JavaScript geschrieben .
Es ist ein großartiger Ausgangspunkt, da das System nicht sehr kompliziert ist, aber Sie lernen trotzdem, wie Opcodes, der Stapel, Register usw. funktionieren.
Ich werde bald einen längeren Leitfaden für das NES schreiben.
quelle