Aquanaut (Interceptor)/Schnelllader
<< 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