1 Punkte von GN⁺ 2025-07-02 | 1 Kommentare | Auf WhatsApp teilen
  • In einigen Abschnitten mit rotierenden Fässern in Donkey Kong Country 2 gibt es im alten SNES-Emulator ZSNES einen Bug, bei dem sich das Fass weiterdreht, selbst wenn man das Steuerkreuz loslässt; die Ursache ist die fehlende Implementierung des Open-Bus-Verhaltens
  • Ein echtes SNES liest beim Zugriff auf nicht gemappte Adressen den zuletzt auf dem Datenbus liegenden Wert erneut, und DKC2 verlässt sich bei Lesezugriffen auf $2000/$2001 in Bank $B3 auf diese Eigenschaft
  • Die problematische Routine XORt die vorherige und die neue Richtung des Fasses und führt dann and $2000 aus; auf echter Hardware liefert dieser 16-Bit-Open-Bus-Lesezugriff 0x2020 zurück und erkennt so das Überschreiten einer Richtungsgrenze
  • Wenn ein Open-Bus-Lesezugriff wie in ZSNES 0 zurückgibt, ist das AND-Ergebnis immer 0, sodass der Sprung zum Beenden der Rotation nicht ausgeführt wird und sich das Fass weiterdreht, bis man die Gegenrichtung drückt
  • Der betreffende Befehl hätte sehr wahrscheinlich and #$2000 sein sollen; in einer ROM-Revision funktioniert es auch ohne Open Bus korrekt, wenn man an $33EDAC den Opcode von 0x2D auf 0x29 ändert

Rotierende Fässer stoppen in ZSNES nicht

  • In Donkey Kong Country 2 gibt es einen alten Bug, bei dem einige rotierende Fässer in ZSNES nicht korrekt funktionieren
  • Im Normalfall kann der Spieler die Rotation nur steuern, solange er im Fass die linke oder rechte Richtungstaste gedrückt hält
  • In ZSNES dreht sich das Fass nach einem einzigen Druck auf links oder rechts dauerhaft in diese Richtung weiter, und bei Druck auf die Gegenrichtung rotiert es dann dauerhaft in die andere Richtung
  • In späteren Abschnitten sind die Fässer über Stacheln oder anderen Gefahren platziert, wodurch dieser Bug den Schwierigkeitsgrad der Stage deutlich über die ursprüngliche Absicht der Entwickler hinaus erhöht
  • Dasselbe Problem wurde offenbar schon vor rund 20 Jahren von Anomie gefunden und in Snes9x behoben; damals geschah der Fix nicht durch vollständige Open-Bus-Emulation, sondern durch Hardcoding der konkreten Adresswerte, von denen das Spiel abhängt
  • In ZSNES wurde dieser Bug letztlich nie behoben, und der letzte Release des Projekts stammt aus dem Jahr 2007

Open Bus auf dem SNES und 65816-Adressierung

  • Auf dem SNES führt selbst das Lesen ungültiger Speicheradressen normalerweise nicht zum Absturz des Programms
  • Beim Lesen nicht gemappter Adressen tritt das Open-Bus-Verhalten auf, bei dem die CPU den zuletzt auf dem Datenbus befindlichen Wert erneut liest
  • Die Haupt-CPU des SNES ist der 65C816 bzw. 65816 und steckt im Ricoh-5A22-S-CPU-Package
  • Der 65816 ist eine 16-Bit-Erweiterung der 6502-CPU, und das SNES verwendet zwar einen 24-Bit-Adressbus, aber die meisten Adressen entstehen aus der Kombination einer 8-Bit-Bank und eines 16-Bit-Offsets
  • Viele Befehle verwenden intern 16-Bit-Adressen; beim Instruction Fetch ist das Program Bank Register beteiligt, bei Datenzugriffen das Data Bank Register
  • Im Zustand der rotierenden Fässer liest DKC2 $2000 und $2001 in Bank $B3; diese Adressen sind in Bank $B3 auf nichts gemappt und führen daher zu Open-Bus-Lesezugriffen

Speicherzugriffe der problematischen Routine

  • Wenn man im rotierenden Fass die linke oder rechte Richtungstaste loslässt, führt die pro Frame laufende Routine einen Open-Bus-Lesezugriff aus
  • Der Anfangszustand hat das Data Bank Register auf $B3, die Direct Page auf $0000, und die M/X-Flags sind beide gelöscht, sodass Register und Speicherzugriffe 16 Bit breit sind
  • Diese Routine verwendet mehrere Adressen im Bereich $0000-$2001
    • $0EE6: aktuelle Fassrichtung
    • $0E0A: Rotationsmenge pro Frame
    • $0032: eine Position, die wie eine temporäre Variable wirkt
  • In den Banks $00-$3F und $80-$BF ist $0000-$1FFF auf die ersten 8 KB des 128-KB-WRAMs der Konsole gemappt
  • $2000-$20FF ist ein nicht gemappter Bereich, daher wird der Befehl and $2000 zu einem Open-Bus-Lesezugriff

Wie 0x2020 zur Erkennung des Rotationsendes verwendet wird

  • Die Routine addiert die Rotationsmenge zur aktuellen Richtung, erzeugt so die neue Richtung und speichert sie in einer temporären Variable
  • Danach XORt sie die vorherige und die neue Richtung, wendet auf das Ergebnis and $2000 an und prüft dann, ob das Ergebnis 0 ist
  • Auf echter SNES-Hardware liefert der 16-Bit-Open-Bus-Lesezugriff and $2000 immer 0x2020 zurück
    • Der Maschinenkode von and $2000 ist 2D 00 20
    • Der 65816 verwendet einen 8-Bit-Datenbus und führt einen 16-Bit-Lesezugriff daher als zwei 8-Bit-Lesezugriffe aus
    • In diesem Fall ist das zuletzt gelesene Byte das High-Byte der Adresse, also 0x20, weshalb beide Male 0x20 zurückgegeben wird
  • Dadurch entspricht das tatsächliche Verhalten funktional and #$2020
  • Wenn das AND-Ergebnis 0 ist, speichert die Routine die neue Richtung unverändert und setzt die Rotation im nächsten Frame fort
  • Ist es nicht 0, setzt sie die Rotationsmenge auf 0, addiert 0x1000 zur neuen Richtung und maskiert anschließend mit 0xE000, um auf die nächstgelegene Richtung auszurichten, die ein Vielfaches von 0x2000 ist

Warum es sich in ZSNES endlos weiterdreht

  • Der Richtungswert des Fasses scheint so skaliert zu sein, dass 0x0000 nach unten und 0x4000 nach links zeigt
  • Beim analysierten Fass beträgt die Rotationsmenge im Uhrzeigersinn 0x0300, gegen den Uhrzeigersinn 0xFD00, und eine volle 360-Grad-Drehung dauert etwas mehr als 85 Frames
  • Das ist bei 60 fps etwas kürzer als 1,5 Sekunden
  • Beim AND mit 0x2020 ist die tatsächlich relevante Änderung Bit 13, also 0x2000
  • Eine Änderung von 0x2000 im Richtungswert entspricht einem Schritt zur nächsten Grund- oder Diagonalrichtung
  • Auf korrekter Hardware dreht sich das Fass nach dem Loslassen der Richtungstaste bis zur nächsten Grund- oder Diagonalrichtung weiter und stoppt dann exakt in dieser Ausrichtung
  • Wenn ein Open-Bus-Lesezugriff dagegen immer 0 zurückgibt, ist auch das AND-Ergebnis immer 0 und der Code zum Beenden der Rotation wird nie ausgeführt

Möglicher Tippfehler und Ergebnis eines ROM-Patches

  • and $2000 ist ein Befehl mit absoluter Adressierung, und logisch hätte es wahrscheinlich and #$2000 mit Immediate-Adressierung sein sollen
  • Auf echter Hardware liefert Open Bus jedoch 0x2020, sodass and $2000 zufällig wie beabsichtigt funktioniert
  • Dieses zufällige Verhalten hängt von der Bedingung ab, dass die unteren 6 Bits der Rotationsmenge pro Frame immer 0 sind, wodurch 0x2020 funktional mit 0x2000 gleichgesetzt wird
  • Der fehlerhafte Opcode wird bei Bank $B3, Offset $EDAC ausgeführt und ist in der analysierten Revision innerhalb der 4-MB-ROM des Spiels auf $33EDAC gemappt
  • Wenn man dieses Byte von 0x2D auf 0x29 ändert, funktionieren die rotierenden Fässer auch dann korrekt, wenn Open-Bus-Lesezugriffe immer 0 zurückgeben
  • Die genaue ROM-Position kann sich in anderen Revisionen des Spiels unterscheiden
  • In fast allen SNES-Emulatoren außer altem ZSNES funktioniert das Spiel korrekt, daher ist dieser Fall weniger ein praktischer Patch als vielmehr eine Analyse hardwareabhängigen Verhaltens

1 Kommentare

 
GN⁺ 2025-07-02
Meinungen auf Hacker News
  • Beim Schreiben von 6502-Assembly habe ich wegen genau solcher Fehler – etwa das # vor einem Immediate-Wert zu vergessen und dadurch auf Speicher zuzugreifen – eine Menge Lebenszeit verloren.
    Wie in diesem Fall funktioniert es zufällig in manchen Situationen trotzdem. Noch schlimmer ist es, wenn man sich statt auf einen Floating Bus auf nicht initialisierten RAM verlässt: Wegen der Eigenschaften von DRAM kann es auf der eigenen Maschine oder im Emulator immer funktionieren, auf der Maschine eines anderen mit anderen DRAM-Chips aber kaputtgehen. Meist merkt man das dann auf einer Demoparty 15 Minuten vor der Präsentation, wenn es auf der Party-Maschine nicht läuft.

    • Ich frage mich, ob es Architekturen gab, die eine 6502-CPU zusammen mit dynamischem Speicher verwendeten. Nach meiner begrenzten Erfahrung haben diese Plattformen offenbar immer statisches RAM genutzt.
    • 6502 war meine erste Assemblersprache, und ich habe LDA #2 immer als „lade die Zahl 2 in A“ und LDA 2 als „lade den Wert an Speicheradresse 2 in A“ unterschieden.
    • In solchen Situationen kann es tatsächlich helfen, den Code einem LLM zu geben. Solche Tippfehler oder Versehen haben große Auswirkungen, werden vom menschlichen Auge aber viel zu leicht übersehen; LLMs finden so etwas ziemlich gut.
  • Weil Open Bus im Titel großgeschrieben war, begann ich zunächst zu lesen, weil ich dachte, es sei der Eigenname eines alten Busprotokolls oder Standards, von dem ich noch nie gehört hatte.
    Beim Lesen stellte sich heraus, dass der Adressleitungs-Decoder bei der angegebenen Adresse $2000 kein Speichergerät aktiviert und der Bus daher „offen“ ist, also mit nichts verbunden. Ziemlich witzig ist, dass das vergessene # bei der Immediate-Adressierung erst dadurch auffiel, dass ein alter Emulator Speicherlesezugriffe nicht wie echte Hardware behandelte. Wenn man als Fix statt absoluter Adressierung Immediate-Adressierung verwendet, erfolgt kein Speicherlesezugriff, also sollte die Ausführung auch schneller werden. In diesem Codeblock könnten es ungefähr 2 µs sein, aber das ist vermutlich nur auf Bare Metal relevant; Emulatoren werden ohnehin wahrscheinlich keine vollständig exakte Timing-Genauigkeit haben.

    • Rare hat mehrere Spielefälle, die in Tests gut liefen, deren versteckte Bugs aber erst Jahre später auf einer neuen Architektur zutage traten. Das soll nicht heißen, dass das bei anderen Firmen nicht vorkam; Rare ist in diesem Kontext nur ein leicht greifbarer Name.
      In Donkey Kong 64 gab es meines Wissens ein Speicherleck, durch das das Spiel nach 8 bis 9 Stunden starb – einer für damalige Verhältnisse unrealistisch langen ununterbrochenen Spielzeit. Während der Entwicklung wurde es nicht gefunden, aber wenn man Emulator-Savestates nutzt und statt der In-Game-Speicherfunktion einfach weiterspielt, summiert sich die Zeit schnell. Die Geschichte ist allerdings unklar. Manche Quellen behaupten, die Beilage des Memory Pak sei eine Last-Minute-Maßnahme gewesen, um den Bug zu kaschieren, indem die Crash-Zeit von 8–9 Stunden auf 13–20 Stunden hinausgeschoben wurde. Neuere Untersuchungen deuten jedoch eher darauf hin, dass das Zufall war und Rare oder Nintendo den Bug nicht wissentlich veröffentlicht haben.
    • Einige SNES-Emulatoren sind inzwischen praktisch sogar beim Timing nahezu perfekt [0]. Trotzdem werden 2 µs außer in Ausnahmefällen keinen spürbaren Unterschied machen.
      [0] https://arstechnica.com/gaming/2021/06/how-snes-emulators-go...
  • Als ich an RetroArchs RunAhead-Funktion arbeitete und prüfte, ab wann Savestates nicht mehr übereinstimmten, habe ich einmal gesehen, dass SNES Puyo Puyo den PPU Open Bus benutzt.
    Nach dem Laden eines States war der aus dem PPU Open Bus gelesene Wert anders, sodass die Trace-Logs der CPU-Ausführung nicht mehr übereinstimmten.

  • Ich mache nicht ständig Fehler der 6502-Art, aber wenn, dann ist es meist der Fehler, statt eines Immediate-Werts eine Speicheradresse zu verwenden.
    Das ist ein sehr häufiger und leicht zu begehender Fehler, und ich glaube, sogar Chuck Peddle selbst hat die Syntax, Immediate-Werte mit # wie in #$1234 zu kennzeichnen, zutiefst bereut. In der IDE habe ich # hellrot eingefärbt; das hilft ein wenig. Selbst die Assembly-Götter bei Rare sind auf dasselbe Problem hereingefallen.

    • Vor langer Zeit hatte ich im Modus intel_syntax noprefix des GNU Assemblers ein ähnliches Problem.
      Bei Instruktionen, die sowohl Immediate-Werte als auch Speicheradressen akzeptieren, gab es eine syntaktische Mehrdeutigkeit, bei der ein vorwärts referenzierter benannter Immediate-Konstantenwert als Referenz auf ein unbekanntes Symbol interpretiert werden konnte. Das Ergebnis war, dass nicht der erwartete Immediate-Wert assembliert wurde, sondern eine Instruktion mit einer Platzhalter-Speicheradresse, die beim Linken mit der Relocation-Adresse des Symbols gefüllt werden sollte. Das Debugging war qualvoll.
    • Befehlssätze wie ARM machen solche Fehler praktisch unmöglich. Für Speicherzugriffe muss man andere Instruktionen verwenden.
  • Ich mag solche Artikel. Assembly-Code kann ich gefühlt immer nur zu etwa 60 % nachvollziehen, daher hilft die danebenstehende Prosabeschreibung wirklich sehr.
    Es macht auch Spaß, von Bugs in klassischer Software zu hören, die niemand verstanden hat oder die vielleicht bis heute niemand bemerkt hatte.

    • Einer der Gründe, warum Systeme aus dieser Zeit faszinierend sind, ist, dass ihnen moderne Prüfmechanismen fehlten, die wir heute fast überall als selbstverständlich ansehen.
      Gemeint sind Mechanismen, die für Systeme mit Netzwerkanschluss unverzichtbar sind und inzwischen billig genug, um sie sogar in vollständig isolierte Embedded-Architekturen einzubauen. Beim ursprünglichen NES bestanden viele Lese- und Schreibzugriffe lediglich darin, auf irgendeiner Leitung eine Spannung umzuschalten, und dann geschah eben, was geschah. Den gewünschten Effekt erzielte man, indem man die Spannung auf sehr kontrollierte Weise exakt passend zu einem Signal umschaltete, das den CRT-Blanking-Abschnitt anzeigte. Einige Animationen in Super Mario Bros 3 schalteten einen RAM-Multiplexer um, sodass eine von mehreren Sprite-Datenbanken ausgewählt wurde und die Grafikhardware beim Holen der Sprites aus einem völlig anderen Chip las, in dem sie etwas anders aussahen. Weil das TV-Timing wichtig war, musste Software auch separat für NTSC- und PAL-TV-Regionen erscheinen; beide Verfahren haben unterschiedliche Bildwiederholraten, und diese Bildwiederholrate war der Takt, der die Rendering-Logik antrieb. Es war wirklich eine wilde Zeit.
  • Soweit ich weiß, sieht man Open Bus nur bei frühen Systemen mit einfachen synchronen Bussen.
    Bei den meisten anderen Systemen bekommt man beim Zugriff auf eine nicht vorhandene Adresse meines Wissens einen konstanten Wert, der nur aus Nullen oder nur aus Einsen besteht. Der Grund ist, dass das Busprotokoll ein Handshake hat, über das der Master erkennen kann, dass es keine Antwort gibt; in PCI-Terminologie entspricht das einem „master abort“.

  • Open Bus bedeutet wörtlich, dass die Datenbus-Leitungen ein offener Stromkreis sind.
    Die CPU hat eine Adresse auf den Adressbus gelegt, die entweder nicht gemappt oder nur schreibbar ist, und da keine Hardware am Bus reagiert, werden die Busleitungen nicht getrieben und bleiben schwebend. Formal ist das undefiniertes Verhalten auf Hardwareebene.
    Um zu verstehen, was tatsächlich passiert, muss man sich die physische Struktur des Datenbusses etwas genauer ansehen. Es gibt lange Leiterbahnen, die Signale über das Mainboard und zum Cartridge übertragen, getrennt von der Massefläche durch eine dünne isolierende Leiterplattenschicht. Das sieht wie ein Kondensator aus, und tatsächlich beschreiben und modellieren Ingenieure das als parasitäre Kapazität. Dieser Effekt begrenzt die maximale Datenübertragungsrate des Busses, weshalb man versucht, ihn zu minimieren. Gerade wegen dieses Effekts neigt der Bus aber dazu, auf der zuletzt getriebenen Spannung zu verharren, wenn er nicht mehr aktiv getrieben wird. Er verhält sich wie eine kleine DRAM-Zelle, wodurch der im Artikel beschriebene Effekt entsteht: „Ein Open-Bus-Read gibt den Wert zurück, der zuletzt über den Bus gegangen ist.“
    Dass ein Spiel wie DKC2 versehentlich von Open-Bus-Effekten abhängt, ist nicht ungewöhnlich. Beim NES treibt das serielle Port-Register für den Controller-Anschluss nur die unteren Bits; die oberen Bits sind Open Bus. Es gibt einige Spiele, die mit LDA $4016 die Controller-Eingabe lesen und dabei den Wert $40 oder $41 erwarten. Die 4 stammt dabei von dem Wert, der wegen des Open Bus noch übrig ist.
    Es gibt auch Speedrun-Strategien, die im Rahmen von Speicher-Korruption oder Arbitrary-Code-Execution-Exploits vom Open-Bus-Verhalten abhängen. Beim Credits Warp in Super Mario World etwa wird der Program Counter in nicht gemappten Speicher geschickt, läuft dort eine ganze Weile weiter und landet schließlich im RAM, wo ein durch präzise manipulierte Gegnerpositionen erzeugter Payload ausgeführt wird [1].
    Allerdings gibt es auch Ausnahmen vom normalerweise vorhersagbaren Open-Bus-Verhalten. Nicht standardkonforme Cartridges können für nicht gemappten Speicher Standardwerte zurückgeben oder Pull-up-/Pull-down-Widerstände enthalten, die das Open-Bus-Verhalten beeinflussen. Außerdem gibt es interessante Wechselwirkungen mit DMA. Das SNES unterstützt eine Funktion namens HDMA, mit der Anwendungen genau getaktete Datenübertragungen von der CPU zur Grafikhardware einplanen können, um mitten im Frame Daten hochzuladen oder Einstellungen zu ändern [2]. Diese DMA-Transfers halten die CPU kurz an, um den Bus für die Übertragung zu nutzen. Wenn ein DMA-Transfer mitten in die Ausführung einer Instruktion fällt – also nachdem die Zieladresse gelesen wurde, aber vor dem eigentlichen Open-Bus-Read –, kann er das Verhalten dieses Open-Bus-Reads verändern.
    Dieser sehr spezielle Grenzfall hat großen Einfluss auf einen Super-Metroid-Speedrun-Exploit [3]. Der Exploit löst ein memcpy außerhalb des gültigen Bereichs aus, um einen großen Datenblock vom Open Bus in den RAM zu kopieren. Open-Bus-Reads geben fast immer 0 zurück, weil das letzte Byte der relevanten Load-Instruktion 0 ist. In bestimmten Räumen mit vielen HDMA-Grafikeffekten ist es jedoch durchaus möglich, dass ein DMA-Transfer einen dieser Reads beeinflusst. Dann landet ein Nicht-Null-Byte an einer kritischen Stelle, der Exploit funktioniert nicht korrekt und das Spiel stürzt ab. Das hat in der Community eine kleine Debatte ausgelöst. Manche Routen und Strategien sind nur auf Emulatoren und nicht standardkonformer Firmware stabil. Spieler auf Originalhardware oder sehr genauen Emulatoren haben ein höheres Crash-Risiko; die meisten Emulatoren, einschließlich aller offiziellen Neuveröffentlichungen von Nintendo, emulieren diesen speziellen Grenzfall aber nicht, bei dem ein HDMA-Transfer mitten in einer Instruktion den Wert eines Open-Bus-Reads verändert.
    Auch der derzeit schnellste Super-Metroid-TAS-Clear [4] hängt von dieser HDMA-Wechselwirkung ab. Man hatte Situationen gefunden, in denen der Versuch, Open Bus auszuführen, zu einem Crash führte, aber normalerweise ließ sich das nicht sinnvoll kontrollieren. Durch Manipulation der Gegner im Raum konnte das CPU-Timing beeinflusst werden, und über HDMA ließ sich zum richtigen Zeitpunkt eine nützliche Instruktion auf den Bus legen. Am Ende wurde die Konsole dazu gebracht, Controller-Eingaben als Code auszuführen, wodurch vollständige Arbitrary Code Execution erreicht wurde.
    [1]: https://youtu.be/vAHXK2wut_I
    [2]: https://youtu.be/K7gWmdgXPgk
    [3]: https://youtu.be/CnThmKhtfOs
    [4]: https://tasvideos.org/8214S

    • An der Stelle, an der es heißt, man müsse sich die physische Struktur des Datenbusses ansehen, möchte ich noch einmal Ben Eater loben.
      Dank seiner Videoserie über den Bau eines Breadboard-Computers mit dem 6502 verstehe ich den Inhalt des Artikels und die Beschreibung der Hardwareprobleme tatsächlich. Natürlich ist das gedanklich eine Übertragung seiner einfachen Bus-Beispiele auf eine kommerzielle Maschine. Ohne das wüsste ich fast nichts darüber.
      https://eater.net
  • Beim Programmieren des Parallax-Propeller-Chips gibt es bis zu einem gewissen Grad ein ähnliches Problem.
    Man muss JMP #address verwenden, um zu der angegebenen Speicherposition zu springen, schreibt aber ständig JMP address, was zu der Adresse springt, die aus der angegebenen Speicherposition gelesen wurde. Vermutlich steckt der 6502-Assembler noch im Muskelgedächtnis.
    Propeller: JMP #address
    6502: JMP address
    Propeller: JMP address
    6502: JMP (address)
    Das Schlimmste ist, dass fehlerhafter Propeller-Code wie in diesem Artikel manchmal trotzdem funktioniert. Bis er irgendwann stehen bleibt und man Stunden damit verbringt herauszufinden, warum.

  • Die mit SGI vorgerenderten 3D-Grafiken von DKC 1 waren damals hochmodern. Vector Man auf dem Genesis machte etwas Ähnliches, bekam aber weniger Aufmerksamkeit.

    • Um 1995 war ich ein 11-jähriges Kind und damit ziemlich genau die Zielgruppe von DKC; dieses Spiel war wirklich ein Schock.
      Ich konnte kaum glauben, was ich da sah. Zur Veröffentlichung gab es eine Videokassette, die das Spiel anteaserte und auch Blicke hinter die Entwicklung zeigte; ich erinnere mich, dass es wohl Werbematerial war, das man über so etwas wie eine Müslipackung anfordern konnte. Ich habe mir dieses Tape wirklich oft angesehen. DKC habe ich selbst nie besessen, konnte es aber bei einem Freund spielen.
    • Als Kind wirkten diese Grafiken irgendwie unecht. Auf irgendeiner Ebene konnte man spüren, dass sie nicht „echt“ waren.
      Die Magazine jener Zeit formulierten das Thema ziemlich vage und deuteten oft an, die Leistung des SNES würde Figuren und Ähnliches in Echtzeit rendern. In Wirklichkeit war es im Kern eher eine Art Daumenkino-Animation.
  • Wenn man beim Spielen per Emulation nicht weiterkommt, fragt man sich am Ende oft, ob es nicht doch ein Emulator-Bug ist.
    Bei diesem konkreten Problem hätte ich gedacht, dass das Spiel einfach so designt wurde und eben schwierig ist. Es ist nicht direkt dasselbe, aber wenn sich ein Spiel wirklich schwer anfühlt, denke ich ähnlich: „Liegt das an der Emulationslatenz?“ Nachdem ich mich wegen dieses Problems tiefer damit beschäftigt hatte, habe ich mir schließlich selbst ein MiSTer FPGA gebaut.

    • In Chrono Trigger gab es auch so etwas.
      Ich erinnere mich an eine Stelle, an der man nach dem Fangen einer Ratte vier Tasten gleichzeitig drücken musste. USB-Eingaben übertrugen aber nur drei auf einmal, sodass man, um vorbeizukommen, die vier Tasten wild drücken musste, damit sie innerhalb eines sehr kurzen Zeitfensters schließlich alle registriert wurden. Man brauchte mehrere Versuche, und es war sehr frustrierend.
    • Ich habe DKC nur mit ZSNES gespielt und wusste bis zum Lesen des Artikels überhaupt nicht, dass das ein Emulator-Bug war.
      Wie gesagt dachte ich einfach, es sei beabsichtigtes Game Design, beim Abschuss aus dem Fass das Timing so zu treffen, dass der richtige Winkel entsteht. Ich war wirklich überrascht zu erfahren, dass es ein Bug war.
    • Als Kind habe ich viel Bionic Commando gespielt.
      Als ich es Anfang der 2000er in einem Emulator startete, war es viel schwieriger, als ich es in Erinnerung hatte. Später stellte sich heraus, dass es einen Emulations-Bug gab, bei dem die Gegner auch nach der Sprengung der Basis nicht verschwanden, und Ladd blieb weiterhin eingefroren. Dadurch brauchte man zum Abschließen des Levels ungefähr zwei Lebensbalken mehr. Einmal habe ich es so geschafft, nur um zu sehen, ob es möglich ist, aber danach nie wieder.