Programmierung von WHDLoad

### Schematischer Ablaufplan

Die folgende Tabelle zeigt den Ablauf wie ein unter WHDLoad installiertes Programm ausgeführt wird. Es soll helfen zu verstehen wie WHDLoad arbeitet und wie WHDLoad, der Slave und das installierte Programm interagieren.

der Nutzer

- startet das Demo oder Spiel, indem der das entsprechende Icon doppelklickt oder WHDLoad von der Kommandozeile aufruft

das Betriebssystem

- lädt WHDLoad und startet es

WHDLoad

- prüft die Hardware und Softwareumgebung
- lädt und prüft den Slave
- belegt benötigten Speicher für das installierte Programm
- wenn Preload/S aktiv ist werden Diskettenabbilder und Dateien in den Speicher geladen (soweit freier Speicher vorhanden ist)
- schaltet das Betriebssystem ab (Ausschalten des Multitaskings und der Interrupts, zurückschalten der Grafikhardware auf OCS, initialisieren aller Hardware mit definierten Werten)
- springt in den Slave

der Slave

- lädt das Hauptprogramm des installierten Programmes mittels einer WHDLoad Funktion (z.B. resload_DiskLoad oder resload_LoadFile)
- modifiziert das Hauptprogramm in der Art und Weise, dass dieses seine Daten über den Slave (WHDLoad) lädt, behebt Kompatibilitätsprobleme, ermöglicht das Beenden des Programmes
- startet das Hauptprogramm

das installierte Programmm

- folgt dem normalen Ablauf
- wenn Daten von Diskette geladen werden sollen, ruft es den Slave (weil der Slave es vorher in dieser Art und Weise modifiziert hat), der Slave ruft WHDLoad, WHDLoad aktiviert kurzzeitig das Betriebssystem um die Daten von der Festplatte zu laden (nur wenn Preload nicht aktiv war), dann Rückkehr zum installierten Programm, welches wie gewohnt fortgesetzt wird

der Nutzer

- Beendet das Programm indem er den QuitKey betätigt

der Slave

- kehrt zu WHDLoad mittels resload_Abort zurück

WHDLoad

- aktiviert wieder das Betriebssystem (restauriert Hardware Register, Grafik und Speicher)
- gibt belegte Ressourcen frei
- kehrt zum Betriebssystem zurück

### Wie man einen einfachen Ein-Disk Trackloader installiert

Dies ist eine sehr kleine und kurze Schritt für Schritt Beschreibung für eine Install mit WHDLoad. Die Beschreibung geht von einem ideal einfachen Fall aus. In der Praxis wird dieser womöglich nie oder nur sehr selten auftreten. Für spezielle Fälle und Probleme bitte die nachfolgenden Kapitel lesen.

1. Vorarbeiten
   - Lege ein Verzeichnis an, in welchem alle Dateien abgelegt werden sollen.
   - Erzeuge ein Diskettenabbild mittels DIC.
   - Lege eine #?.info Datei mit "WHDLoad" als <Default Tool> und einem Tooltype "SLAVE=#?" welches den Name des Slaves enthält an (oder kopiere einfach ein Icon von einer anderen Install und passe den "SLAVE=#?" Tooltype an).
2. Der Slave
   Um den Slave zu schreiben benötigen wir die folgenden Informationen:
   1. Wo auf der Diskette befindet sich das Hauptprogramm?
   2. Wo im Hauptprogramm befindet sich die Diskladeroutine?
   Um diese Informationen zu erhalten analysieren wir zuerst den Bootblock. Meistens wird von hier aus das Hauptprogramm mit exec.DoIO() geladen. Manchmal befindet sich auch ein eigener Trackloader im Bootblock. Wir schreiben nun einen Slave welcher den Bootblock nachstellt und das Hauptprogramm vom Diskettenabbild lädt. Nun extrahieren wir das Hauptprogramm aus dem Diskettenabbild oder einem Memory Dump. Jetzt müssen wir den Trackloader im Hauptprogramm finden. Ein schneller Weg ist es nach dem Wert `$AAAAAAAA` (benutzt bei der MFM Dekodierung) mit einem Hex-Editor zu suchen. Danach schneidet man den Bereich (+/- `$1000` Bytes) heraus (oder man macht das mit dem gesamten Hauptprogramm), disassembliert ihn und sucht den Anfang der Trackloader-Routine. Man muss noch herausfinden was für Parameter der Routine übergeben werden. Danach können wir den Slave so programmieren, dass er das Hauptprogramm so modifiziert, dass Aufrufe des Trackloaders auf den Slave umgeleitet werden. Der Slave passt dann die Parameter an und ruft die WHDLoad Funktion resload_DiskLoad.
3. Im idealen Fall ist die Install jetzt fertig.
   Die einzige Sache die noch zu erledigen ist, wäre ein schönes Icon herzustellen. Am besten geht das, wenn man mit der Snoop Option von WHDLoad und dem Programm SP oder mit einem Freezer oder UAE Bilder extrahiert und daraus ein Icon erstellt. Die 16 farbige RomIcon Palette wird dabei empfohlen.

### Mögliche Probleme und Sonderfälle

#### Vom Standard abweichende Trackloader

Einige Demos und Spiele nutzen ihr eigenes spezielles Diskformat. Das bedeutet, dass DIC nicht in der Lage ist von solchen Disketten Abbilder herzustellen. Zur Erzeugung von Abbildern solcher Fremdformate wird das Programm RawDIC empfohlen. Für weitere Informationen bitte die Dokumentation zu RawDIC konsultieren.

#### Verwendung mehrerer Disketten

Wenn das zu installierende Programm mehr als eine Diskette verwendet, muss der Slave dafür sorgen, dass die Zugriffe auf das jeweilige richtige Diskettenabbild erfolgt. Manchmal ist dies nicht einfach. Einige Spiele unterstützen mehr als ein Diskettenlaufwerk, in solchen Fällen kann eventuell die Laufwerksnummer benutzt werden um davon die Nummer des Diskettenabbildes abzuleiten. Die meisten Programme nutzen eine Identifikation auf jeder Diskette um diese voneinander zu unterscheiden. Hier kann zum Beispiel eine Variable, welche die aktuelle Disknummer enthält verwendet werden. Bei jedem Zugriff auf die Diskidentifikation (erkennbar an den Parametern des Trackloaders) wird die Variable erhöht und beim Erreichen der letzten gültigen Disknummer wieder auf den Anfangswert zurückgesetzt. Dadurch würde das installierte Programm bei jedem Zugriff auf die Diskidentifikation auf eine andere Diskette zugreifen bis die gewünschte Diskette virtuell eingelegt ist. Wenn das installierte Programm beim Diskettenwechsel eine Nutzerinteraktion erfordert, sollte diese durch eine Modifikation des Slaves entfernt werden.

#### Speichern einer Bestentabelle

Hier gibt es nicht viel zu sagen. Mit der Funktion resload_SaveFile ist der entsprechende Speicherbereich abzuspeichern. Wenn gewünscht, kann dieser noch etwas kodiert werden, damit nicht jedermann darin ohne weiteres editieren kann. Es wird ausdrücklich davon abgeraten direkt in Diskettenabbilder (unter Verwendung von resload_SaveFileOffset) zu Speichern, weil bei einem Fehler (z.B. Absturz) die Abbilder beschädigt werden können.

#### Spielstände

Das Handling von Spielständen ist analog dem einer Bestenliste.

#### Zugriffe auf das Betriebssystem

Während der Slave und das installierte Programm ausgeführt werden, existiert weder ein Betriebssystem noch kann darauf zugegriffen werden, noch macht es Sinn darauf zuzugreifen! Deshalb müssen alle potentiellen Zugriffe durch das installierte Programm deaktiviert werden. Wenn es nur wenige davon gibt und diese ohnehin keinen Sinn in der WHDLoad-Umgebung machen (wie z.B. exec.Disable() oder exec.SuperState()) sollten diese ge'NOP't (`$4e71`) werden. Wenn die Zugriffe eine wesentliche Funktion haben (z.B. exec.DoIO()) müssen sie vom Slave durch entsprechende Aufrufe ersetzt werden. Wenn es eine Menge gibt, ist es sinnvoll eine einfache exec.library in einem unbenutzten Speicherbereich zu erzeugen (`$4` damit initialisieren). Als Beispiel hierfür ist der Quelltext des Oscar.Slave im `DEV` Archiv enthalten, welcher exec.AllocMem() emuliert. Um Zugriffe des installierten Programmes auf das Betriebssystem zu erkennen wird die ExecBase (`$4`) von WHDLoad mit dem Wert `$f0000001` initialisiert. Dadurch erzeugt jedes Programm beim Versuch diese zu benutzen eine "Address Error" Exception.
Wenn das installierte Programm eine intensive Benutzung des Betriebssystems erfordert, ist es angebracht eines der sogenannten KickEmu-Pakete zu verwenden, welche im `DEV` Archiv zu finden sind. Es gibt ein Paket für Kick 1.3 ('src/sources/whdload/kick13.s') und eines für Kick 3.1 ('src/sources/whdload/kick31.s'). Diese Pakete erfordern ein originales Kickstart-Image und erzeugen damit eine komplette Betriebssystemumgebung innerhalb der WHDLoad-Umgebung. Für weitere Informationen bitte die entsprechende Dokumentation konsultieren.

### Häufige Kompatibilitätsprobleme

#### Eingeschränkter Adressbereich auf 68000/68010/68ec020

Diese Prozessoren haben einen eingeschränkten Adressbereich von 16 MB (`$000000...$ffffff`) da sie nur 24 Adressleitungen besitzen. Daher werden alle Zugriffe auf größere Adressen automatisch auf die unteren 16 MB ausgeführt, indem die höchsten 8 Bit ignoriert werden. Einige Programme nutzen diese Bits um Daten darin zu speichern oder vergessen diese zu löschen bzw. korrekt zu initialisieren. Auf einem Prozessor mit vollem 4 GB Adressraum wie 68020/680ec30/68030/68040/68060 funktioniert dies nicht, da dann die vollen 32 Bit für den Zugriff verwendet werden.
Um solche Probleme zu lösen, müssen die Zugriffe modifiziert werden und auf die richtige Speicheradresse umgeleitet werden.
Manchmal sind die Ursachen für solche Fehler auch nicht initialisierte Zeiger. Dann reicht es teilweise den Speicherbereich `$400` - ws_BaseMemSize bei Programmstart vom Slave zu löschen.

#### Unterschiedliche Stackframes auf verschiedenen Prozessoren

Die Stackframes die von den Prozessoren bei Interrupts und Exceptions erzeugt werden unterscheiden sich innerhalb der 68k-Familie. Auf dem 68000'er ist ein Stackframe 6 Byte groß, außer bei Bus- und Address-Error. Das Stackframe enthält das gesicherte SR bei (a7) und den gesicherten PC bei (2,a7). Auf allen anderen Prozessoren (68010+) ist das kleinste Stackframe 8 Byte groß und enthält zusätzlich die Vektornummer als Wort bei (6,a7). Dieses Four-Word Stackframeformat `$0` wird erzeugt für "Trap #xx" und Interrupts auf 68010 - 68060. Die Stackframes anderer Exceptions unterscheiden sich von Prozessor zu Prozessor. Die RTE Instruktion arbeitet unterschiedlich auf 68000 und 68010+. Auf einem 68000'er restauriert sie SR und PC indem sie es vom Stack holt und setzt die Programmausführung fort. Auf 68010+ wird zusätzlich das Stackframe in Abhängigkeit vom Stackframeformat freigegeben.
Einige Programme legen einfach eine Adresse (PC) und ein SR auf dem Stack ab und führen dann eine RTE Instruktion aus. Das funktioniert nur auf einem 68000'er. Auf 68010+ hat es unvorhersehbare Folgen.
Wenn das installierte Programm dies tut, muss es korrigiert werden. Manchmal reicht es aus, die RTE Instruktion durch eine RTR Instruktion zu ersetzen.

#### MOVEM.x RL,-(An) auf 68000/010 und 68020/030/040

Hier gibt es einen Unterschied, wenn das Register welches im PreDecrement Mode (RL) verwendet wird auch in der Registerliste enthalten ist. Beim 68020 - 68040 wird das Register bereits vollständig dekrementiert gespeichert. Auf 68000 - 68010 wird das Register nicht dekrementiert gespeichert.
Da dieses Konstrukt nicht sehr sinnvoll ist, sind gegenwärtig keine diesbezüglichen Problemfälle bekannt.

### Allgemeine Richtinien für das Schreiben von Installs

- Modifiziere niemals CPU-Register wie VBR oder CACR die nur in höheren Prozessoren vorhanden sind. Das VBR ist aus Sicht des Slave-Programmierers immer 0, auch wenn es von WHDLoad verschoben wurde, da die Autovektoren (und Traps wenn das Flag WHDL_EmulTrap gesetzt ist) emuliert werden. Die Bits im CACR sind bei jedem Prozessor unterschiedlich belegt. Daher gibt es nur einen richtigen Weg die Caches zu setzen, mittels resload_SetCACR/SetCPU und den Bitdefinitionen in 'exec/execbase.i' und 'whdload.i'. Auch alles im installierten Programm was solche Register verändert muss entfernt werden, ansonsten ist eine korrekte Arbeitsweise von WHDLoad nicht gewährleistet!
- Verändere nie Diskettenabbilder. Das hat den Vorteil, dass man die Abbilder einfach wieder auf Diskette zurückschreiben kann und sie dann wieder von Diskette ausführen kann (unter der Annahme, dass das Programm ohne die verschiedenen Kompatibilitätskorrekturen funktioniert, mutmaßlich kein sehr häufiger Fall).
- Benutze niemals Code/Daten aus dem Original-Programm im Slave (Urheberrecht).
- Aktiviere Caches nur wenn du dir sicher bist, dass es mit allen Prozessoren funktioniert.
- Benutze so wenig wie möglich Speicher für ws_BaseMemSize. Einige Leute haben Residente Strukturen am Ende des ChipMem-Speichers, da hilft es nur `$1f0000` anstelle von `$200000` zu benutzen um WHDLoad zu ermöglichen absolut belegten Speicher zu verwenden.

### Tipps & Tricks

#### Was ist besser, Diskettenabbilder oder Dateien?

Manchmal hat man die Wahl entweder Diskettenabbilder oder Dateien zu verwenden. Beides hat seine Vor- und Nachteile. Die Verwendung von Diskettenabbildern ist für gewöhnlich der einfachere und schnellere Weg den Slave zu schreiben. Aber Dateien können von WHDLoad besser zwischengespeichert werden (wenn wenig freier Speicher vorhanden ist und/oder dieser stark fragmentiert ist). Der belegte Plattenplatz ist bei Dateien auch geringer als bei Diskettenabbildern. Diese sollten vor allem verwendet werden, wenn bei der Verwendung von Dateien sehr viele Dateien entstehen würden (mehr als 30).