Aquanaut (Interceptor)/Schnelllader

Aus C64-Wiki
Zur Navigation springenZur Suche springen

<< zurück zu Aquanaut (Interceptor)


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

Erster Programmblock (Autostart)[Bearbeiten | Quelltext bearbeiten]

Das folgende, kurze Programm ist auf der Kassette unter dem Namen "AQUANAUT" gespeichert, wobei dem Dateinamen noch das Steuerzeichen CHR$(147) (Bildschirm löschen) und viermal der Code CHR$(17) (Cursor abwärts) vorangestellt sind. Das Programm bewirkt einen Autostart sowie das Nachladen und Aktivieren des eigentlichen Schnellladers.

ORG $02A7

; Nachladen des eigentlichen Schnellladers
P_AA: LDA #$00
      LDX #$01
      STX *$B9  ; Sekundäradresse=1
      STX *$BA  ; Geräteadresse=1
      STX *$B8  ; Logische Dateinummer=1
      DEX
      STX *$B7  ; Länge des Dateinamens=0
      LDA #$00  ; (überflüssig, bereits A=$00)
      STA *$9D  ; Modus=Programm
      JSR $F4A5 ; Einsprung in LOAD-Befehl
      JMP AB01

; Hauptschleife der Laderoutine
P_AB: LDX #$00  ; Kennzeichen für "LOAD"
      DB  $2C   ; BIT abs (nächsten 2-Byte-Befehl überspringen)
AB00: LDX #$01  ; Kennzeichen für "VERIFY" (nie verwendet)
      LDY *$2B  ; Anfang BASIC-Programm (Low-Byte)
      LDA *$2C  ; Anfang BASIC-Programm (High-Byte)
      STX *$0A  ; LOAD/VERIFY-Flag
      STX *$93  ; LOAD/VERIFY-Flag
      STY *$C3  ; Endadresse Ladevorgang (Low-Byte)
      STA *$C4  ; Endadresse ladevorgang (High-Byte)
      JSR $E1D4 ; Parameter für LOAD und SAVE holen
      JSR P_BC  ; Programmteil per Schnelllader laden
      JSR $E17A ; Ergebnis von LOAD ausgeben
AB01: LDA #$E1
      STA $0328 ; STOP-Vektor ungültig machen
      NOP
      NOP
      NOP
      LDX T000  ; Zähler für zu ladende Programmteile
      DEX       ; vermindern
      NOP
      NOP
      STX T000  ; und zurückschreiben
      CPX #$00  ; Alle Programmteile nachgeladen? (überflüssiger Vergleich)
      BEQ AB02  ; Sprung falls ja
      JMP P_AB  ; sonst nächsten Programmteil nachladen
AB02: LDA #$83  ; Vektor für Eingabe einer Zeile wiederherstellen
      STA $0302 ; Low-Byte
      LDA #$A4
      STA $0303 ; High-Byte
      JMP P_BB  ; Zum Abschluss des Ladevorgangs und Start des Spiels

      DB $00,$00 ; Füllbytes

T000: DB $03     ; Zähler für nachzuladende Programmteile

T001: DW $E302 ; Vektor für BASIC-Warmstart (ungültig)
      DW P_AA  ; Vektor für Eingabe einer Zeile (umgebogen auf Laderoutine)

Zweiter Programmblock (Schnelllader)[Bearbeiten | Quelltext bearbeiten]

Die folgenden Routinen werden als Programm unter dem Namen "P1" mit Hilfe der Datassetten-Routinen des Kernal nachgeladen. Durch Aufruf der Routine P_BC wird jeweils ein Programmblock (Header und Daten) von Band gelesen und in den Speicher geschrieben. Aus der Hauptschleife der Laderoutine heraus wird P_BC nacheinander zwei Mal aufgerufen.

PRG $0400

; Bandbetrieb vorbereiten
P_BA: LDY #$00    ; Flag für Bandmotor löschen
      STY *$C0
      LDA $D011   ; Bildschirm abschalten
      AND #$EF
      STA $D011
BA00: DEX         ; Etwa 330 ms warten
      BNE BA00
      DEY
      BNE BA00
      SEI         ; Interrupts verbieten
      RTS

      DB $00,$00,$00

; Abschluss der Laderoutine und Start des Spiels
P_BB: LDA #$2D    ; Korrekturen am Programmcode
      STA $86FE   ; vornehmen
      LDA #$20
      STA $A2DA
      LDA #$95    ; Zeichenfarbe "Braun"
      JSR $FFD2   ; BSOUT Ausgabe eines Zeichens
      JMP $6182   ; Spiel starten
      NOP
      NOP
      NOP
      NOP
      NOP
      NOP
      NOP
      NOP
      NOP
      NOP
      NOP

; Header eines Programmblocks einlesen und verarbeiten, dann Programmblock laden
P_BC: JSR P_BD    ; Header in Kassettenpuffer einlesen
      LDA *$AB    ; Letztes Byte der Synchronisationssequenz
      CMP #$02    ; mit gültigem Wert vergleichen
      BEQ BC00    ; Sprung falls gleich
      CMP #$01    ; mit weiterem gültigem Wert vergleichen
      BNE P_BC    ; Rücksprung falls nicht
      NOP
      NOP
      NOP
      NOP
BC00: LDA $033C   ; Startadresse (Low-Byte)
      STA *$C3    ; als Schreibzeiger umkopieren (Low-Byte)
      LDA $033D   ; Startadresse (High-Byte)
      STA *$C4    ; als Schreibzeiger umkopieren (High-Byte)
      JSR $F750   ; "FOUND " und Dateiname ausgeben
      NOP
      NOP
      NOP
      NOP
      NOP
      JSR $A82C   ; STOP-Taste abfragen
      LDY *$B7    ; Länge des Dateinamens
      BEQ BC02    ; Sprung falls kein Dateiname
BC01: DEY         ; Lesezeiger vermindern
      LDA ($BB),Y ; Angegebenen Dateinamen zeichenweise lesen
      CMP $0341,Y ; und mit gefundenem Dateinamen vergleichen
      BNE P_BC    ; Nächsten Header suchen und laden falls ungleich
      TYA         ; Alle Zeichen verglichen?
      BNE BC01    ; Rücksprung falls nicht
BC02: STY *$90    ; Status ST auf "OK" initialisieren
      JSR $F5D2   ; "LOADING" ausgeben
      LDA $033E   ; Bisherige Endadresse des Programms lesen (Low-Byte)
      SEC
      SBC $033C   ; und bisherige Anfangsadresse subtrahieren (Low-Byte)
      PHP
      CLC
      ADC *$C3    ; und Differenz zur Startadresse addieren (Low-Byte)
      STA *$AE    ; und als Endadresse speichern (Low-Byte)
      LDA $033F   ; Bisherige Endadresse des Programms lesen (High-Byte)
      ADC *$C4    ; zur Startadresse addieren (High-Byte)
      PLP
      SBC $033D   ; und bisherige Anfangsadresse subtrahieren (High-Byte)
      STA *$AF    ; und als Endadresse speichern (High-Byte)
      JSR P_BE    ; Programmblock laden
      LDA *$BD    ; Letztes gelesenes Byte (Prüfsumme) holen
      EOR *$D7    ; durch XOR mit errechneter Prüfsumme vergleichen
      ORA *$90    ; und mit Status ST verknüpfen
      BEQ BC03    ; Sprung falls alles fehlerfrei
      LDA #$FF    ; sonst Status ST
      STA *$90    ; auf "ungültig" setzen
BC03: JMP $F5A9   ; Endadresse nach X/Y holen

; Header in Kassettenpuffer einlesen
P_BD: JSR P_BF    ; Ladevorgangs starten, Synchronisation
      CMP #$00    ; Synchronisation gescheitert?
      BEQ P_BD    ; Rücksprung zur erneuten Synchronisation falls ja
      STA *$AB    ; sonst letztes Byte der Synchronisationssequenz merken
BD00: JSR P_BG    ; Ein Byte von Band lesen
      STA ($B2),Y ; und in Kassettenpuffer schreiben
      INY         ; Schreibindex erhöhen
      CPY #$C0    ; Schon ganzen Header in Kassettenpuffer eingelesen?
      BNE BD00    ; Rücksprung falls noch nicht
      BEQ BE04    ; Unbedingter Sprung zum Ende des Bandbetriebs

; Programmblock laden oder vergleichen
P_BE: JSR P_BF    ; Ladevorgang starten, Synchronisation
BE00: JSR P_BG    ; Ein Byte von Band lesen
      CPY *$93    ; LOAD- oder VERIFY-Phase?
      BNE BE01    ; Sprung falls VERIFY
      STA ($C3),Y ; Gelesenes Datenbyte in Speicher schreiben
BE01: CMP ($C3),Y ; Gelesenes Datenbyte mit Speicher vergleichen
      BEQ BE02    ; Sprung falls gleich
      STX *$90    ; sonst Status ST auf "..." setzen
BE02: EOR *$D7    ; Datenbyte in Prüfsumme einarbeiten
      STA *$D7    ; und Prüfsumme zurückschreiben
      INC *$C3    ; Schreibzeiger erhöhen (Low-Byte)
      BNE BE03    ; Sprung falls kein Übertrag
      INC *$C4    ; Schreibzeiger erhöhen (High-Byte)
BE03: LDA *$C3    ; Schreibzeiger holen (Low-Byte)
      CMP *$AE    ; und mit Endadresse vergleichen (Low-Byte)
      LDA *$C4    ; Schreibzeiger holen (High-Byte)
      SBC *$AF    ; und mit Endadresse vergleichen (High-Byte)
      BCC BE00    ; Rücksprung falls Endadresse noch nicht erreicht
      JSR P_BG    ; Weiteres Byte von Band lesen und ignorieren
      JSR P_BA    ; Bandmotor starten, Bildschirm abschalten
      INY
BE04: STY *$C0
      CLI         ; Interrupts wieder zulassen
      CLC         ; Zeichen für "kein Fehler"
      LDA #$00
      STA $02A0
      JMP $FC93   ; Bandbetrieb beenden

; Start des Ladevorgangs und Synchronisation
P_BF: JSR $F817   ; Bandbetrieb beginnen
      JSR P_BA    ; Bandmotor starten, Bildschirm abschalten
      STY *$D7    ; Prüfsumme mit Y=0 initialisieren
      LDA #$07    ; Low-Byte CIA2 Timer B auf 7 setzen
      STA $DD06
      LDX #$01    ; High-Byte CIA2 Timer B
BF00: JSR P_BH    ; Ein Bit von Band lesen
      ROL *$BD    ; und von rechts in Speicher an Adresse $BD schieben
      LDA *$BD    ; Bisher gelesenes Byte holen
      CMP #$02    ; und mit Synchronisationsbyte $02 vergleichen
      BNE BF00    ; Rücksprung falls nicht
      LDY #$09    ; Länge der Synchronisations-Sequenz
BF01: JSR P_BG    ; Ein Byte von Band lesen
      CMP #$02    ; Weiteres Synchronisationsbyte $02?
      BEQ BF01    ; Überlesen falls ja
BF02: CPY *$BD    ; Ansonsten gelesene Bytes mit Sequenz $09...$01 vergleichen
      BNE BF00    ; Synchronisation neu starten falls ungleich
      JSR P_BG    ; Nächstes Byte von Band lesen
      DEY         ; Zähler für Synchronisationssequenz vermindern
      BNE BF02    ; Rücksprung, falls Sequenz noch nicht zu Ende
      RTS

; Ein Byte von Band lesen
P_BG: LDA #$08    ; Bitzähler initialisieren (8 Bits)
      STA *$A3
BG00: JSR P_BH    ; Ein Bit von Band lesen
      ROL *$BD    ; und von rechts in Speicher an Adresse $00BD schieben
      NOP
      NOP
      NOP
      DEC *$A3    ; Bitzähler vermindern
      BNE BG00    ; Rücksprung falls noch nicht 8 Bits von Band gelesen
      LDA *$BD    ; Gelesenes Byte in A zurückliefern
      RTS

; Ein Bit von Band lesen
P_BH: LDA #$10    ; Auf Impuls an Pin FLAG warten
BH00: BIT $DC0D
      BEQ BH00
      LDA $DD0D   ; Interruptregister CIA2 holen
      STX $DD07   ; High-Byte von CIA2 Timer B auf X=1 setzen
      PHA         ; Interruptregister CIA2 retten
      LDA #$19    ; CIA2 Timer B neu laden und starten (one shot)
      STA $DD0F
      PLA         ; Interruptregister CIA2 zurückholen
      LSR A       ; CF setzen, falls CIA2 Timer B abgelaufen
      LSR A
      RTS