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