Equinox/Schnelllader

Aus C64-Wiki
Zur Navigation springenZur Suche springen

<< zurück zu Equinox


Equinox/Schnelllader: Die folgenden Abschnitte stellen den disassemblierten Kassetten-Schnelllader des Spiels Equinox dar. Sie sind gegliedert in einzelne Codeblöcke, die gemäß ihrer Funktion während und nach dem Laden gruppiert sind.


BASIC-Startzeile


Der folgende, kurze Programmabschnitt sorgt dafür, dass der Schnelllader als eigenständiges Programm geladen und mit dem BASIC-Befehl RUN gestartet werden kann.

ORG $0801

; BASIC-Programm für den Start mit "RUN"
      DW $080B       ; Zeiger auf nächste Zeile (=Programmende)
      DW 10          ; Zeilennummer
      DB $9E,'2061'  ; SYS2061 (=P_AA)
      DB $00,$00,$00 ; Programmende-Markierung

Hauptroutine[Bearbeiten | Quelltext bearbeiten]

Der folgende Codeabschnitt stellt das Hauptprogramm des Schnellladers dar. Es initialisiert den Bildschirm und die CIAs, ruft nacheinander für die einzelnen Programmteile die Lade- und die Prüfroutine auf, kopiert die Programmteile ggf. in die Ziel-Adressbereiche um und startet schließlich das Spiel. Alle Adressangaben sind als Konstanten im Programmcode zu finden und gelten nur für das Spiel "Equinox", so dass der Schnelllader nicht für andere, auf gleiche Weise codierte Datasetten-Dateien verwendet werden kann.

; Hauptprogramm, abwechselnd Datenblöcke lesen, Prüfsumme bilden und umkopieren
P_AA: SEI         ; Interrupts verbieten
      LDA $D011   ; Bildschirm abschalten
      AND #$EF
      STA $D011
      LDA #$01    ; Keine Interrupts bei Ablauf CIA 2 Timer A
      STA $DD0D
      LDA $DC0E   ; CIA 1 Timer A starten (continuous mode)
      ORA #$01
      STA $DC0E
      LDA *$01    ; Datassettenmotor einschalten
      AND #$DF
      STA *$01
      LDA #$00    ; Startadresse $1000 an $FD/$FE speichern
      STA *$FD    ; Low-Byte
      LDA #$10
      STA *$FE    ; High-Byte
      LDA #$29    ; Endadresse $3329 an $F8/$F9 speichern
      STA *$F8    ; Low-Byte
      LDA #$33
      STA *$F9    ; High-Byte
      LDA $DD0E   ; CIA 2 Timer A anhalten
      AND #$FE
      STA $DD0E
      JSR P_AB    ; Datenblock von Band lesen
      LDA *$01    ; BASIC-ROM ausblenden
      AND #$FE
      STA *$01
      LDA #$00    ; Startadresse $1000 an $FD/$FE speichern
      STA *$FD    ; Low-Byte
      LDA #$10
      STA *$FE    ; High-Byte
      JSR P_AC    ; Prüfsumme des gelesenen Datenblocks errechnen
      CMP #$00    ; Prüfsumme=$00?
      BEQ AA00    ; Sprung falls ja
      JMP ($FFFC) ; sonst Reset auslösen
AA00: SEI         ; Interrupts verbieten (unnötig)
      LDA #$00    ; Rahmenfarbe auf "schwarz" setzen
      STA $D020
      LDA #<AA00
      STA $0AB1   ; Unnötig
      STA $0A65   ; Unnötig
      STA AB09+$01 ; Rahmen nicht mehr blinken lassen (neues Low-Byte)
      STA AB02+$01
      LDA #>AA00
      STA $0AB2   ; Unnötig
      STA $0A66   ; Unnötig
      STA AB09+$02 ; Rahmen nicht mehr blinken lassen (neues High-Byte)
      STA AB02+$02
      LDA $DD02   ; CIA 2 Port A Bit 0 und 1 auf "Ausgang"
      ORA #$03
      STA $DD02
      LDA $DD00   ; CIA 2 Port A Bit 0 und 1 auf low (VIC-RAM an $C000..$FFFF)
      AND #$FC
      STA $DD00
      LDA $D011   ; Bitmap-Grafikmodus einschalten
      ORA #$20
      STA $D011
      LDA #$38    ; Video-RAM an $CC00, Zeichensatz an $E000
      STA $D018
; Bitmap des Titelbilds nach $E000 umkopieren
      LDA #$E8    ; Quelladresse $13E8 an $02/$03 speichern
      STA *$02    ; Low-Byte
      LDA #$13
      STA *$03    ; High-Byte
      LDA #$00    ; Zieladresse $E000 an $04/$05 speichern
      STA *$04    ; Low-Byte
      LDA #$E0
      STA *$05    ; High-Byte
      LDY #$00    ; Schreibindex initialisieren
AA01: LDA ($02),Y ; Speicherbereich lesen
      STA ($04),Y ; und umkopieren
      INC *$02    ; Lesezeiger weiterzählen
      BNE AA02    ; Sprung falls kein Überlauf
      INC *$03    ; sonst Überlauf berücksichtigen und High-Byte weiterzählen
AA02: INC *$04    ; Schreibzeiger weiterzählen
      BNE AA03    ; Sprung falls kein Überlauf
      INC *$05    ; sonst Überlauf berücksichtigen und High-Byte weiterzählen
AA03: LDA *$02    ; Lesezeiger mit Endadresse $3328 vergleichen
      CMP #$28    ; Low-Byte
      BNE AA01    ; Rücksprung falls Endadresse noch nicht erreicht
      LDA *$03    ; High-Byte
      CMP #$33
      BNE AA01    ; Rücksprung falls Endadresse noch nicht erreicht
; Video-RAM des Titelbilds nach $CC00 umkopieren
      LDA #$00    ; Quelladresse $1000 an $02/$03 speichern
      STA *$02    ; Low-Byte
      LDA #$10
      STA *$03    ; High-Byte
      LDA #$00    ; Zieladresse $CC00 an $04/$05 speichern
      STA *$04    ; Low-Byte
      LDA #$CC
      STA *$05    ; High-Byte
      LDY #$00    ; Lese-/Schreibindex initialisieren
AA04: LDA ($02),Y ; Datenbyte lesen
      STA ($04),Y ; und umkopieren
      INC *$02    ; Lesezeiger erhöhen
      BNE AA05    ; Sprung falls kein Überlauf
      INC *$03    ; sonst Überlauf berücksichtigen und High-Byte weiterzählen
AA05: INC *$04    ; Schreibzeiger erhöhen
      BNE AA06    ; Sprung falls kein Überlauf
      INC *$05    ; sonst Überlauf berücksichtigen und High-Byte weiterzählen
AA06: LDA *$02    ; Lesezeiger mit Endadresse $13E8 vergleichen
      CMP #$E8    ; Low-Byte
      BNE AA04    ; Rücksprung falls Endadresse noch nicht erreicht
      LDA *$03    ; High-Byte
      CMP #$13
      BNE AA04    ; Rücksprung falls Endadresse noch nicht erreicht
      LDA $D016   ; Auf 38 Bildschirmspalten umschalten
      AND #$F7
      STA $D016
      LDA $D011   ; Bildschirm einschalten
      ORA #$10
      STA $D011
; Spiel laden
      LDA #$06    ; Startadresse $0A06 nach $FD/$FE kopieren
      STA *$FD    ; Low-Byte
      LDA #$0A
      STA *$FE    ; High-Byte
      LDA #$C5    ; Endadresse $BBC5 nach $F8/$F9 kopieren
      STA *$F8    ; Low-Byte
      LDA #$BB
      STA *$F9    ; High-Byte
      LDA $DD0E   ; CIA 2 Timer A anhalten
      AND #$FE
      STA $DD0E
      JSR P_AB    ; Datenblock von Band lesen
      LDA #$06    ; Startadresse $0A06 nach $FD/$FE kopieren
      STA *$FD    ; Low-Byte
      LDA #$0A
      STA *$FE    ; High-Byte
      JSR P_AC    ; Prüfsumme des gelesenen Datenblocks errechnen
      CMP #$00    ; Prüfsumme=$00?
      BEQ AA09    ; Sprung falls ja
      LDA #$06    ; sonst Adresse $0A06 nach $FD/$FE kopieren
      STA *$FD    ; Low-Byte
      LDA #$0A
      STA *$FE    ; High-Byte
      LDY #$00    ; Schreibindex
AA07: TYA         ; Zu schreibendes Byte
      STA ($FD),Y ; Speicherbereich $0A06..$BBC5 löschen
      INC *$FD    ; Schreibzeiger erhöhen
      BNE AA08    ; Sprung falls kein Überlauf
      INC *$FE    ; sonst Überlauf berücksichtigen und High-Byte weiterzählen
AA08: LDA *$FD    ; Endadresse schon erreicht?
      CMP *$F8    ; Low-Byte
      BNE AA07    ; Rücksprung falls noch nicht erreicht
      LDA *$FE    ; High-Byte
      CMP *$F9
      BNE AA07    ; Rücksprung falls noch nicht erreicht
      LDA *$01    ; BASIC-ROM wieder einblenden (unnötig)
      ORA #$01
      STA *$01
      LDA $D011   ; Bildschirm wieder einschalten
      ORA #$10
      STA $D011
      JMP ($FFFC) ; Reset auslösen
AA09: JMP $0B09   ; Spiel starten

Laderoutine[Bearbeiten | Quelltext bearbeiten]

Die folgende Routine wartet auf die Synchronisations-Markierung (mindestens 256 aufeinanderfolgende 1-Bits) und lädt anschließend einen Programmteil in den Hauptspeicher des C64. Die Startadresse dieses Programmteils muss an Adresse $FD/$FE, die Endadresse+1 an $F8/$F9 hinterlegt sein.

P_AB: LDX #$00    ; Zähler für 1-Bits auf $0000 initialisieren
      STX *$FB    ; Low-Byte
      STX *$FC    ; High-Byte
AB00: LDA #$FF    ; Startwert CIA 2 Timer A auf $FFFF
      STA $DD04   ; Low-Byte
      STA $DD05   ; High-Byte
      LDA #$10    ; CIA 2 Timer A anhalten und neu laden
      STA $DD0E
      LDA #$01    ; CIA 2 Timer A starten (continuous mode)
      STA $DD0E
AB01: LDA $DC0D   ; Warten auf Impuls vom Band
      AND #$10
      BEQ AB01
AB02: INC $D020   ; Rahmenfarbe weiterzählen
      LDA #$FD    ; High-Byte von CIA 2 Timer A mit $FD vergleichen
      CMP $DD05   ; entspricht "Sind mindestens 512 Takte vergangen?"
      BCC AB04    ; Sprung falls nein (kein 0-Bit)
      LDA #$FB    ; High-Byte von CIA 2 Timer A mit $FB vergleichen
      CMP $DD05   ; entspricht "Sind mindestens 1024 Takte vergangen?"
      BCS P_AB    ; Rücksprung falls ja (Fehler)
      INC *$FB    ; Zähler für 1-Bits erhöhen, Low-Byte
      BNE AB00    ; Sprung falls kein Überlauf
      INC *$FC    ; High-Byte
AB03: JMP AB00
AB04: LDA #$00    ; High-Byte des Zählers für 1-Bits prüfen
      CMP *$FC
      BEQ P_AB    ; Rücksprung falls weniger als 256 aufeinanderfolgende 1-Bits
AB05: LDA #$08    ; Bitzähler auf "8 Bit" setzen
      STA *$FB
AB06: LDA #$FF    ; Startwert CIA 2 Timer A auf $FFFF (unnötig)
      STA $DD04
      STA $DD05
      LDA #$10    ; CIA 2 Timer A anhalten und neu laden
      STA $DD0E
      LDA #$01    ; CIA 2 Timer A starten (continuous mode)
      STA $DD0E
AB07: LDA $DD05   ; CIA 2 Timer A lesen (High-Byte)
      CMP #$FB    ; und mit $FB vergleichen (entspricht "1280 Takte vergangen?")
      BCS AB08    ; Sprung falls noch nicht
      RTS         ; sonst Rückkehr aus Laderoutine (Fehler)
AB08: LDA $DC0D   ; Warten auf Impuls vom Band
      AND #$10
      BEQ AB07
      LDA #$FD    ; High-Byte von CIA 2 Timer A mit $FD vergleichen
      CMP $DD05   ; entspricht "Sind mindestens 512 Takte vergangen?"
      ROR *$FA    ; CC="Nein"=0-Bit, CS="Ja"=1-Bit von links in $FA schieben
AB09: INC $D020   ; Rahmenfarbe weiterzählen
      DEC *$FB    ; Bitzähler vermindern
      BNE AB06    ; Rücksprung falls noch nicht 8 Bit gelesen
      LDA *$FA    ; Gelesenes Datenbyte holen
      STA ($FD,X) ; und über Schreibzeiger an $FD/$FE speichern (X=0)
      INC *$FD    ; Schreibzeiger weiterbewegen
      BNE AB10    ; Sprung falls kein Überlauf
      INC *$FE    ; sonst High-Byte erhöhen wegen Überlauf
AB10: LDA *$FE    ; Schreibzeiger an $FD/$FE mit Endadresse an $F8/$F9 vergleichen
      CMP *$F9    ; High-Byte
      BNE AB05    ; Rücksprung falls Endadresse noch nicht erreicht (High-Byte)
      LDA *$FD    ; Low-Byte
      CMP *$F8
      BNE AB05    ; Rücksprung falls Endadresse noch nicht erreicht (Low-Byte)
      RTS

Prüfroutine[Bearbeiten | Quelltext bearbeiten]

Die folgende Routine bildet eine Prüfsumme eines Programmteils (durch Addition aller Datenbytes; Additionsüberträge werden ignoriert) und liefert diese im Akkumulator zurück. Eine Prüfsumme von $00 lässt auf einen fehlerfrei geladenen Programmteil schließen, ansonsten wird ein von 0 verschiedener Wert an die aufrufende Hauptroutine zurückgeliefert (und löst dort einen Reset des C64 aus). Die Startadresse des zu prüfenden Programmteils muss wieder an Adresse $FD/$FE, die Endadresse+1 an $F8/$F9 hinterlegt sein.

; Prüfsumme berechnen und in A zurückliefern, Startadresse an $FD/$FE, Endaddresse an $F8/$F9
P_AC: LDX #$00    ; Leseindex initialisieren
      TXA         ; Prüfsumme initialisieren
AC00: CLC         ; Vorbereitung für Addition
      ADC ($FD,X) ; Nacheinander alle Bytes des Datenblocks addieren
      INC *$FD    ; Lesezeiger erhöhen
      BNE AC01    ; Sprung falls kein Überlauf
      INC *$FE    ; sonst Überlauf berücksichtigen und High-Byte erhöhen
AC01: LDY *$FD    ; Endadresse schon erreicht?
      CPY *$F8    ; Low-Byte
      BNE AC00    ; Rücksprung falls noch nicht
      LDY *$FE    ; High-Byte
      CPY *$F9
      BNE AC00    ; Rücksprung falls noch nicht
      RTS