Elidon/Schnelllader
<< zurück zu Elidon
Elidon/Schnelllader: Die folgenden Abschnitte stellen den disassemblierten Kassetten-Schnelllader des Spiels Elidon dar. Sie sind gegliedert in einzelne Codeblöcke, die gemäß ihrer Funktion während des Ladens gruppiert sind.
Nachladen des eigentlichen Schnellladers[Bearbeiten | Quelltext bearbeiten]
Lädt man "Elidon" von Band, so wird als erstes ein Datenblock in den Adressbereich $02E8-$0303 geladen. Dieser überschreibt den Vektor für Eingabe einer Zeile mit der Startadresse der Routine, die den eigentlichen Schnelllader nachlädt, wodurch diese unmittelbar nach dem Abschluss des Ladevorgangs automatisch gestartet wird.
ORG $02E8
; Laderoutine für Nachladen des eigentlichen Schnellladers
P_AA: LDA #$00 ; Programm-Modus setzen
STA *$9D
LDA #$00 ; Länge des Dateinamens=0
JSR $FFBD ; setzen
LDA #$01 ; Logische Dateinummer=1
TAX ; Geräteadresse=1
TAY ; Sekundäradresse=1
JSR $FFBA ; setzen
LDA #$00 ; Load/Verify-Flag auf "Load" setzen
JSR $FFD5 ; Nächstes Programm von Band laden
JMP P_AG ; und starten
DW $E38B ; Vektor für BASIC-Warmstart
DW P_AA ; Vektor für Eingabe einer Zeile, umgebogen auf Laderoutine
Unterprogramme des Schnellladers[Bearbeiten | Quelltext bearbeiten]
Der Schnelllader ist sehr klar strukturiert und enthält keinerlei Versuche, die Arbeitsweise des Programms zu verschleiern. Er ist damit ein Musterbeispiel für den Aufbau eines Datassetten-Schnellladers: Zunächst Initialisierung der I/O-Bausteine, danach erst bitweise, dann byteweise Synchronisation mit den Banddaten, gefolgt vom Einlesen der Datenblöcke in die zugehörigen Speicherbereiche. Übertragungsfehler werden schließlich anhand einer Prüfsumme erkannt, die als EXOR-Verknüpfung aller Datenbytes eines Programmblocks errechnet wird.
Ungewöhnlich ist allerdings die Reaktion auf eine fehlerhafte Prüfsumme: Anstatt — wie zahlreiche andere Datassetten-Schnellader — sofort einen Reset auszulösen oder den Rechner zu blockieren, färbt der Schnelllader von Elidon nur kurz den Bildschirm rot und setzt dann seine Arbeit fort. Für den Anwender hat dies denn Vorteil, dass das Spiel eventuell auch von einer nur geringfügig beschädigten Kassette geladen werden kann, falls der Lesefehler einen nicht genutzten Speicherabschnitt oder unkomprimierte Grafikdaten betrifft.
PRG $C003
; Einen Programmteil von Band lesen, Startadresse an $C3/$C4, Endadresse an $C1/$C2
P_AB: SEI ; Interrupts während Laderoutine verbieten
LDA #$05 ; Alle ROMs ausblenden
STA *$01
JSR P_AD ; Synchronisation
AB00: JSR P_AF ; Ein Datenbyte von Band lesen und nach A
DEC *$01 ; I/O-Bereich auch ausblenden, ganzer Adressraum mit RAM belegt
STA ($C3),Y ; Gelesenes Datenbyte in Speicher schreiben
INC *$01 ; I/O-Adressraum wieder einblenden
EOR *$FC ; Gelesenes Byte in Prüfsumme einarbeiten
STA *$FC ; und speichern
INC *$C3 ; Schreibzeiger erhöhen (Low-Byte)
BNE AB01 ; Sprung falls kein Überlauf
INC *$C4 ; Schreibzeiger erhöhen (High-Byte)
AB01: LDA *$C3 ; Schreibzeiger (Low-Byte)
CMP *$C1 ; mit Endadresse (Low-Byte) vergleichen
LDA *$C4 ; Schreibzeiger (High-Byte)
SBC *$C2 ; durch Subtraktion mit Endadresse (High-Byte) vergleichen
BCC AB00 ; Rücksprung, falls noch nicht ganzer Programmteil gelesen
JSR P_AF ; Prüfsumme von Band lesen und nach A
CMP *$FC ; und mit errechneter Prüfsumme vergleichen
BEQ AB03 ; Sprung, falls Prüfsummen gleich
LDA #$02 ; sonst Bildschirm rot färben
STA $D020
LDY #$00 ; und ca. 330 ms warten
AB02: DEX
BNE AB02
DEY
BNE AB02
AB03: RTS
; Bildschirm abschalten
P_AC: LDY #$00 ; Zähler für Warteschleife
LDA $D011 ; Bildschirm abschalten
AND #$EF
STA $D011
AC00: DEX ; und ca. 330 ms warten
BNE AC00
DEY
BNE AC00
RTS
; Synchronisation
P_AD: LDA #$00 ; Prüfsumme initialisieren
STA *$FC
JSR P_AC ; Bildschirm abschalten
LDA #$07
STA $DD06 ; Low-Byte von CIA 2 Timer B auf $07 setzen
LDX #$01
AD00: JSR P_AE ; Ein Datenbit von Band lesen
ROL *$FB ; und von rechts in bisher gelesenes Byte schieben
LDA *$FB ; Bisher gelesenes Byte nach A holen
CMP #$02 ; und mit Synchronisationszeichen $02 vergleichen
BNE AD00 ; Rücksprung und weitersuchen, falls ungleich
LDY #$09 ; Start der Synchronisations-Sequenz
AD01: JSR P_AF ; Ein Byte von Band lesen
CMP #$02 ; und mit weiterem Synchroinisationszeichen $02 vergleichen
BEQ AD01 ; Überlesen und Rücksprung, falls ja
AD02: CPY *$FB ; Gelesenes Byte mit Synchronisations-Sequenz vergleichen
BNE AD00 ; Rücksprung und erneute Synchronisation, falls ungleich
JSR P_AF ; sonst nächstes Byte von Band lesen
DEY ; und Synchronisations-Sequenz weiterzählen
BNE AD02 ; Rücksprung, falls noch nicht Ende der Sequenz erreicht
RTS
; Ein Datenbit von Band lesen und im Carry-Flag zurückliefern
P_AE: LDA #$10 ; Warten auf Signal von Datassette
AE00: BIT $DC0D
BEQ AE00
LDA $DD0D ; Flag für "CIA 2 Timer B abgelaufen" merken und löschen
STX $DD07 ; High-Byte von CIA 2 Timer B auf $01 setzen
PHA ; A auf Stack retten
LDA #$19 ; CIA 2 Timer B neu laden und im "One shot"-Modus starten
STA $DD0F
PLA ; A vom Stack zurückholen
LSR A ; Gelesenes Datenbit ins Carry-Flag übertragen
LSR A ; CIA 2 Timer B abgelaufen=1-Bit, nicht abgelaufen=0-Bit
RTS
; Ein Datenbyte von Band lesen und in A zurückliefern
P_AF: LDA #$08 ; 8 Bits
STA *$FD ; als Bitzähler merken
AF00: JSR P_AE ; Ein Bit von Band ins Carry-Flag holen
ROL *$FB ; und von rechts in das gelesene Datenbyte schieben
INC $D020 ; Bildschirm-Rahmenfarbe erhöhen
DEC *$FD ; Bitzähler erniedrigen
BNE AF00 ; Rücksprung falls noch nicht 8 Bits gelesen
LDA *$FB ; Gelesenes Byte nach A holen
RTS
Hauptprogramm des Schnelladers[Bearbeiten | Quelltext bearbeiten]
Das Hauptprogramm setzt nacheinander für die beiden Programmblöcke, aus denen sich Elidon zusammensetzt, jeweils die Anfangs- und Endadresse und ruft danach die Laderoutine für jeden der beiden Blöcke auf. Anschließend startet sie das Spiel durch einen absoluten Sprung in den zuletzt gelesenen Programmblock. Im Gegensatz zu vielen anderen Schnellladern geht den Banddaten kein Header mit Anfangs- und Endadressen voraus, sondern auf dem Band finden sich nur die eigentlichen Datenbytes.
; Hauptprogramm des Schnellladers
P_AG: LDA #$C6 ; Startadresse für ersten Programmteil auf $C600 setzen
STA *$C4
LDA #$00
STA *$C3
LDA #$00 ; Endadresse für ersten Programmteil auf $D000 setzen
STA *$C1
LDA #$D0
STA *$C2
JSR P_AB ; Ersten Programmteil von Band lesen
LDA #$08 ; Startadresse für zweiten Programmteil auf $0800 setzen
STA *$C4
LDA #$00
STA *$C3
LDA #$00 ; Endadresse für zweiten Programmteil auf $A000 setzen
STA *$C1
LDA #$A0
STA *$C2
JSR P_AB ; Zweiten Programmteil von Band lesen
LDA #$37 ; Alle ROMs wieder einblenden
STA *$01
CLI ; Interrupts wieder zulassen
LDA $D011 ; Bildschirm wieder einschalten
ORA #$10
STA $D011
LDX #$04 ; und ca. 5 ms warten
AG00: DEY
BNE AG00
DEX
BNE AG00
JMP $08D0 ; Spiel starten