Elidon/Schnelllader

Aus C64-Wiki
Zur Navigation springenZur Suche springen

<< 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