Hallo Leute! Da das Interesse groß ist, Diskettenspiele für GX4000-Steckmodule umzuwandeln, dachte ich, ich stelle mal ein kurzes (na ja, in meinem Kopf war es zumindest kurz geplant) How-To/FAQ zusammen, das die Methoden beschreibt, die ich verwende. Vorab ein Haftungsausschluss: Ich mache das erst seit Kurzem und lerne selbst noch dazu. Nimm also bitte nichts von dem, was ich sage, als bares Geld hin – ich bin keineswegs ein Experte für die Amstrad-Hardware. Es macht mir aber riesigen Spaß, mehr über diese alte Kiste zu lernen und mich selbst herauszufordern; es kann ein sehr erfüllendes und süchtig machendes Hobby sein.
Als ich mich das erste Mal daran versuchen wollte, habe ich diesen guten CPC-Fanzine-Artikel gelesen: https://www.cpcwiki.eu/imgs/6/6f/CPC_Fanzine_-_GX4000_conversion.pdf
Der Artikel enthält viele sehr nützliche Informationen, die mir den Einstieg erleichtert haben. Ich hatte großes Glück, dass das erste Spiel, das ich mir für die Konvertierung ausgesucht hatte – Dynamite Dan –, für die dort beschriebenen Methoden geeignet war. Leider gibt es einige Situationen, die im Artikel nicht erwähnt werden, und tatsächlich ließ sich die Mehrheit der Spiele, die ich seitdem konvertiert habe, nicht mit den Methoden aus diesem Artikel umwandeln.
Der Artikel beschreibt, wie man mit einem Hex-Editor eine Diskettendatei direkt bearbeitet, um Änderungen auf Byte-Ebene am Spielcode vorzunehmen. Das funktioniert nur, wenn das Programm weder komprimiert noch verschlüsselt ist. Bei den meisten Spielen, die du versuchen wirst zu konvertieren, ist die Ausgangs-Diskettendatei gekrackt – und Cracker lieben es einfach, ihre Werke zu verschlüsseln und (vor allem) zu komprimieren. Bei simplen Verschlüsselungsformen (oft nur einfache XOR-Algorithmen) kann man sich zwar zurückarbeiten, um herauszufinden, wie die Bytes auf der Diskette aussehen müssten (das habe ich bei Shogun gemacht), aber das ist extrem mühsam und erfordert hohe Konzentration und genaue Notizen. Keineswegs ein Weg, den ich empfehlen würde.
Warum muss man oft mit einer gekrackten Version statt der kommerziellen Originalversion beginnen? In vielen Fällen laden Diskettenspiele irgendwann Daten von der Diskette nach – und sei es nur ein Ladebildschirm. Viele größere Spiele laden jedes Level einzeln. Kommerzielle Spiele griffen oft direkt auf den Floppy-Disk-Controller zu, anstatt den Weg über das AMSDOS-ROM zu gehen. Sobald ein Spiel das tut, kann es nicht mit nocart konvertiert werden und läuft somit nicht auf dem GX4000. Cracker haben bei kommerziellen Spielen oft den direkten Floppy-Zugriff durch Aufrufe der AMSDOS-Firmware ersetzt, um den Kopierschutz zu umgehen. Gekrackte Versionen funktionieren daher mit nocart oft direkt so, wie sie sind.
Es gibt zwei zuverlässige Wege, um zu prüfen, ob die vorliegende .dsk-Datei die Firmware nutzt oder nicht. Erstens: Lade sie in WinAPE und gehe in das Menü „Edit Disk“. Klicke auf „Show system files“. Wenn du mehrere Dateien siehst, stehen die Chancen gut, dass es direkt mit nocart funktioniert, da Standard-AMSDOS-Dateien mit Standard-Headern verwendet werden. Zur Bestätigung kannst du das Spiel in WinAPE ausführen und einen Breakpoint auf den FDC (Floppy Disk Controller) setzen über Debug → Breakpoints → Input/Output → Add → FDC Data. Wenn der Debugger anhält, sobald das Spiel Daten nachladen will, schau unten im Debugger nach, ob das Kontrollkästchen für „Upper ROM“ aktiviert ist. Wenn ja, ist das super, denn es bedeutet, dass AMSDOS verwendet wird.
Ich sollte dazu sagen, dass ich nicht jeden einzelnen Emulator da draußen ausprobiert habe, aber von denen, die ich getestet habe, ist WinAPE mit Abstand der beste für jegliche Entwicklungsarbeit – auch wenn er ein paar kleine Zicken hat, die ich gerne ändern würde.
Ich habe also ein paar Spiele mit der manuellen Bearbeitungsmethode für .dsk-Dateien (unter Verwendung des Hex-Editors HxD) konvertiert. Aber ich fand das frustrierend, da es sehr schwer war, den Überblick über die vorgenommenen Änderungen zu behalten. Wenn man sich bei einem Byte vertan hat, war man schnell wieder am Anfang oder in einem undefinierten Zustand. Da ich beruflich Programmierer bin, bin ich an Versionsverwaltungen wie Git gewöhnt, um all meine Änderungen in kleine Commits zu unterteilen, damit ich jederzeit eine Historie mit Notizen zu meinen Schritten habe.
Beim nächsten Spiel, das ich konvertiert habe, beschloss ich daher, mit einer alternativen Methode zu experimentieren. Sie erwies sich als erfolgreich und ist seitdem die Methode, die ich für jedes weitere Spiel verwendet habe.
Ich starte mit einem leeren CPCTelera-Projekt. Du kennst dieses hervorragende Tool vielleicht schon: Es ist ein C-Compiler für den Amstrad, der eine .dsk-Datei generiert. Wenn du danach googelst, findest du jede Menge Dokumentation.
Nun gibt es bei jedem Spiel zwei Möglichkeiten: Sind die Dateien komprimiert oder nicht? Um das herauszufinden, starte das Spiel (das eigentliche Gameplay, wo du steuern kannst) und pausiere die Ausführung an einer beliebigen Stelle mit F7. Im Debugger siehst du die aktuelle Instruktionsadresse (PC) mit den Opcode-Bytes auf der rechten Seite. Öffne die .dsk-Datei in einem Hex-Editor und suche nach dieser Handvoll Bytes ab der Stelle, an der du pausiert hast. Wenn du sie in der .dsk-Datei findest und alles übereinstimmt: super, dann sind die Binärdateien höchstwahrscheinlich nicht komprimiert und du kannst den nächsten Teil überspringen.
Wenn sie komprimiert sind, wird es etwas kniffliger, da du genau den Punkt finden musst, an dem die Dekomprimierung abgeschlossen ist und das Spiel startet. Der einfachste Weg besteht darin, das Spiel zu starten (z. B. run "disc") und vorher in WinAPE einen Breakpoint auf die tatsächliche Startadresse zu setzen. Diese findest du im Disc-Editor, indem du mit der rechten Maustaste auf eine Datei klickst, „Properties“ auswählst und nach der „Exec Address“ schaust.
Jetzt komme ich zur Nutzung von KI zum Kommentieren und Annotieren von Disassemblierungen – das nützlichste Werkzeug im Werkzeugkasten überhaupt. Das von Hand zu machen ist zwar möglich, aber extrem zeitaufwendig und frustrierend – absolut nicht machbar, wenn man einen Job, Familie oder andere Hobbys hat. Wenn du also irgendwo am Anfang oder innerhalb der Dekomprimierungsroutine pausiert hast, scrolle im Debugger ein Stück herum, meistens im Speicher nach vorne, etwa 0x1000 Bytes weit. Generell so lange, bis du ein „RET“ siehst, wobei die Endadresse nicht extrem kritisch ist, da man später noch mehr erfassen kann. Wähle diesen Befehlsblock mit Shift-Klick aus, mache dann einen Rechtsklick und wähle „Disassemble“. Bestätige den Dialog einfach mit OK. Drücke F3, um den Assembler anzuzeigen, falls er nicht automatisch aufgeploppt ist. Jetzt hast du den Quellcode vor dir, allerdings in einem sehr rohen Format, das im Grunde unbrauchbar ist, solange es nicht in Routinen unterteilt und gründlich kommentiert wird.
Sobald du den rohen Assemblercode aus WinAPE hast, kopiere ihn in eine neue Datei in deinem Projekt (erstelle für jedes Spiel, das du konvertierst, einen neuen Git-Ordner/ein neues Projekt). Ich verwende Namenskonventionen wie GameZ0x1000-0x2000.raw.asm. Nutze dann ein KI-Tool mit einem spezifischen Prompt, um das Ganze in etwas Lesbares zu verwandeln. Du musst für KI-Tools kein Geld ausgeben! Die kostenlosen reichen völlig aus. Ich verwende OpenCode und Freebuff; beide bieten verschiedene Modelle zur völlig kostenlosen Nutzung an. Sie sind extrem einfach einzurichten und zu bedienen. Wähle das leistungsfähigste verfügbare Modell auf der höchsten Einstellung (frag die KI, falls du unsicher bist, welches das beste ist). Untern füge ich den Prompt ein, den ich benutze. Du kannst ihn in eine Datei packen und entweder kopieren/einfügen oder das KI-Tool direkt darauf und auf deine .asm-Datei verweisen:
„Für den Z80-Codeblock in (dein Dateipfad), der für die Ausführung auf dem Amstrad CPC gedacht ist: Füge Inline-Kommentare hinzu, die High-Level-Funktionen erklären, und unterteile den Code in Unterroutinen mit einem Header für jede größere/kleinere Unterroutine, der ihren Zweck beschreibt. Ändere Sprungmarken (Labels) so ab, dass sie aussagekräftiger sind, aber behalte die Adresse am Ende des Labels bei. Füge ganz oben in der Datei einen zusammenfassenden Kommentar mit einer Aufzählung der wichtigsten enthaltenen Funktionen ein. Gib die kommentierte Disassemblierung als einen einzigen Block aus, ohne Befehle oder Daten wegzulassen, und speichere sie in einer neuen Datei neben der bestehenden. Versuche nach Möglichkeit Bereiche zu identifizieren, die eigentlich Daten und kein Code sind. Ziehe bei Bedarf das Firmware-Handbuch für ROM-Aufrufe etc. zu Rate: https://www.cantrell.org.uk/david/tech/cpc/cpc-firmware/firmware.pdf“
Sobald die KI ihre Arbeit erledigt hat, hast du etwas hoffentlich gut Lesbares zur Analyse vorliegen. Wenn sie schlechte Arbeit geleistet hat oder die Routinen für die Dekomprimierung und den Spielstart nicht relevant aussehen, schnapp dir einfach einen anderen Speicherbereich und wiederhole den Vorgang oder versuche ein anderes KI-Modell.
Hoffentlich siehst du dann so etwas wie Daten dekomprimieren, gefolgt von JP 0xXXXX, um das Spiel (oder seinen Titelbildschirm) tatsächlich zu starten. Meiner Erfahrung nach gibt es fast immer eine JP 0xXXXX-Anweisung, die den Einsprungpunkt zu dem darstellt, was im kommerziellen Spiel gestanden hätte. Oft ist es eine glatte Zahl wie 0x100 oder 0x500, da Entwickler runde Zahlen lieben. Eine weitere Alternative zum Finden des Einsprungpunkts besteht darin, die eigentliche kommerzielle Version auszuführen und über die Eigenschaften der Diskettendatei zu prüfen, wo sie tatsächlich startet.
Sobald du den Einsprungpunkt des Spiels hast (was trivial ist, wenn du keine komprimierte/verschlüsselte Version hast), kannst du den Inhalt des RAMs sichern, indem du an dieser Adresse einen Breakpoint setzt.
OK, das wird jetzt doch viel länger als gedacht! Wenn jemand an mehr interessiert ist, lasst es mich wissen, dann schreibe ich unten weiter oder verlagere das Ganze in einen Wiki-Artikel!