Equinox/Schnelllader
<< 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[Bearbeiten | Quelltext bearbeiten]
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