Gridtrap/Schnelllader
<< zurück zu Gridtrap
Gridtrap/Schnelllader: Die folgenden Abschnitte stellen den disassemblierten Kassetten-Schnelllader des Spiels Gridtrap dar. Sie unterteilen sich in den Header des Programms, der bereits bei der Suche nach einem Programm in den Kassettenpuffer übertragen wird, und dem ersten Programmteil, der anschließend geladen und durch Überschreiben des Sprungvektors für die Eingabe einer Zeile automatisch gestartet wird.
Startblock der Datassetten-Datei[Bearbeiten | Quelltext bearbeiten]
Das folgende Datensegment ist der erste Block auf der Kassette, sollte also nur den Dateityp, die Anfangs- und Endadresse sowie den Namen des Spiels enthalten. Im 2. bis 16. Zeichen des Dateinamens wie auch in den darauffolgenden, üblicherweise nicht genutzten 171 Bytes findet sich aber ein großer Teil der Routinen für den Schnelllader.
ORG $033C
DB $03 ; Code für "Absolut zu ladendes Programm"
DW $02A5 ; Ladeadresse
DW $0304 ; Endadresse+1
DB $1F ; Erstes Zeichen des Dateinamens: Zeichenfarbe auf "Dunkelblau" setzen
; Programmblock von Band lesen
P_BA: JSR $E1D4 ; Default-Parameter für LOAD und SAVE setzen
JSR BA01 ; Entspricht "JSR BA00", Synchronisation und Programmblock von Band lesen
JSR $E17A ; Abschluss der LOAD-Routine
BA00: JSR P_BB ; Synchronisation, bis Programm mit Blocknummer ungleich 0 gefunden
BA01: LDA *$AB ; Nummer des Programmblocks nach A holen
CMP #$02 ; A mit konstanter Blocknummer 2 vergleichen
BEQ BA02 ; Sprung, falls Programmblock 2 gefunden (nicht verwendet)
CMP #$01 ; A mit konstanter Blocknummer 1 vergleichen
BNE BA00 ; Erneute Synchronisation, falls weder Block 1 noch Block 2 gefunden
BA02: JSR $F5D2 ; "LOADING"/"VERIFYING" ausgeben
JSR P_BC ; Laderoutine aufrufen
; Unerreichbarer Code
LDA *$BD ; Von Band gelesene Prüfsumme nach A holen
EOR *$D7 ; mit errechneter Prüfsumme vergleichen
ORA *$90 ; und mit Statusvariable ST ODER-verknüpfen
BEQ BA03 ; Sprung, falls Prüfsumme korrekt und Statusvariable gleich $00
LDA #$FF ; Sonst Statusvariable ST auf "Fehler setzen"
STA *$90
BA03: JMP $F5A9 ; Ladevorgang mit Status "OK" beenden
; Synchronisation, bis Programmblock mit Blocknummer ungleich 0 gefunden
P_BB: JSR P_BF ; Synchronisation
CMP #$00 ; Erneute Synchronisation, falls Blocknummer $00 folgt
BEQ P_BB
STA *$AB ; Blocknummer merken
JMP BD00 ; Bandbetrieb beenden
; Laderoutine
P_BC: JSR P_BF ; Synchronisation
BC00: JSR P_AB ; Ein Byte von Band lesen und in A zurückliefern
CPY *$93 ; Flag für LOAD/VERIFY mit "LOAD" vergleichen
BNE BC01 ; Sprung falls "VERIFY"
STA ($C3),Y ; Gelesenes Byte in Speicher schreiben
BC01: INC $D020 ; Bildschirm-Rahmenfarbe hochzählen
NOP
STX *$90 ; Statusvariable ST auf $01 setzen
EOR *$D7 ; Gelesenes Byte in Prüfsumme einarbeiten
STA *$D7 ; und Prüfsumme zurückschreiben
INC *$C3 ; Schreibzeiger erhöhen (Low-Byte)
BNE BC02 ; Sprung falls kein Überlauf
INC *$C4 ; Schreibzeiger erhöhen (High-Byte)
BC02: LDA *$C3 ; Schreibzeiger nach A holen (Low-Byte)
CMP #$00 ; und mit Endadresse des Programms vergleichen (Low-Byte)
LDA *$C4 ; Schreibzeiger nach A holen (High-Byte)
SBC #$6F ; und mit Endadresse des Programms vergleichen (High-Byte)
BCC BC00 ; Weiter laden, falls Endadresse noch nicht erreicht
JSR P_AB ; Ein Byte von Band holen und in A zurückliefern (Prüfsumme, anschließend ignoriert)
JMP P_BG ; Fälschlicherweise Bandbetrieb nochmals starten (statt beenden) und Spiel starten
; Bandbetrieb beenden
P_BD: INY ; Befehl nie ausgeführt; Einsprung über BD00
BD00: STY *$C0 ; Recordermotor ausschalten
CLI ; Interrupts wieder zulassen
CLC
LDA #$00 ; Interruptvektor während Bandbetrieb ungültig machen
STA $02A0
JMP $FC93 ; Recorderbetrieb beenden
; Bandbetrieb starten
P_BE: LDY #$00 ; Recordermotor einschalten
STY *$C0
LDA $D011 ; Bildschirm ausschalten
AND #$EF
STA $D011
BE00: DEX ; Etwa 330 ms Verzögerung
BNE BE00
DEY
BNE BE00
SEI ; Interrupts während Ladevorgang abschalten
RTS
; Synchronisation, liefert Nummer des Programmblocks zurück
P_BF: JSR $F817 ; Warten auf Bandtaste
JSR P_BE
STY *$D7
LDA #$07 ; Timer B Low-Byte
STA $DD06 ; setzen
LDX #$01 ; Timer B High-Byte
BF00: JSR P_AC ; setzen und ein Bit von Band holen
ROL *$BD ; Gelesenes Bit von rechts in Speicherzelle $BD schieben
LDA *$BD
CMP #$02 ; Synchronisationszeichen $02 gelesen?
BNE BF00 ; Rücksprung falls nicht
LDY #$09
BF01: JSR P_AB ; Byte von Band lesen
CMP #$02 ; Weitere Synchronisationszeichen $02 überlesen
BEQ BF01
BF02: CPY *$BD ; Synchronisationszeichen $09,...,$01 gelesen?
BNE BF00 ; Erneute Synchronisation falls nicht
JSR P_AB ; Byte von Band lesen
DEY ; Synchronisationszeichen herunterzählen
BNE BF02 ; Sprung falls noch nicht alle Synchronisationszeichen $09..$01 gelesen
RTS ; Rückkehr zur aufrufenden Routine
; Programm laden und dann Spiel durch Sprung an Startadresse $6E00 starten
P_BG: JSR P_BE ; Bandbetrieb starten
JMP $6E00 ; Spiel starten
Programmblock[Bearbeiten | Quelltext bearbeiten]
Die Programmdaten, die beim Laden der ersten Datei von den Datassettenroutinen des Kernals in den Speicher übertragen werden, enthalten in erster Linie drei weitere Routinen des Schnellladers, die im Programmheader keinen Platz mehr gefunden haben. Die Ladeadresse dieses Programms ist so gewählt, dass es zudem mit seinen letzten beiden Bytes den BASIC-Vektor für die Eingabe einer Zeile mit der Anfangsadresse des Schnellladers überschreibt, so dass dieser anschließend automatisch startet.
PRG $02A5
DB $00 ; Bildschirmzeile
DB $01 ; Flag für PAL- oder NTSC-Version (1="PAL")
; Mittels "CBM80"-Kennung Steckmodul ab Adresse $8000 vortäuschen, dann Ladevorgang starten
P_AA: LDX #$0A ; Anzahl zu kopierender Bytes
AA00: LDA AA01-$01,X ; Vektoren und "CBM80"-Kennung lesen
STA $7FFF,X ; und an Adresse $8000 kopieren
DEX ; Schreibindex vermindern
BNE AA00 ; Rücksprung, falls noch nicht alle Bytes kopiert
JMP P_AD ; Sprung zur Laderoutine
AA01: DW P_AD ; Reset-Vektor
DW P_AD ; NMI-Vektor
DB $C3,$C2,$CD,$38,$30 ; "CBM80"-Kennung
DB $00,$40 ; Füllbytes
; Ein Byte von Band lesen und in A zurückliefern
P_AB: LDA #$08 ; Bitzähler initialisieren
STA *$A3
AB00: JSR P_AC ; Ein Bit von Band lesen und nach CF
ROL *$BD ; Gelesenes Bit von rechts in Speicherzelle $BD schieben
NOP ; 6 us warten
NOP
NOP
DEC *$A3 ; Bitzähler vermindern
BNE AB00 ; Rücksprung falls noch nicht 8 Bit gelesen
LDA *$BD ; Gelesenes Byte nach A holen
RTS ; Rückkehr zur aufrufenden Routine
; Ein Bit von Band lesen
P_AC: LDA #$10 ; Bitmaske für Interrupt durch Pin FLAG
AC00: BIT $DC0D ; Auf Interrupt prüfen
BEQ AC00 ; Rücksprung falls kein Impuls vom Band
LDA $DD0D ; Interruptanforderungen CIA2 holen und löschen
STX $DD07 ; CIA2 Timer B High-Byte auf 1 setzen
PHA ; A retten
LDA #$19 ; CIA2 Timer B neu laden und starten (one shot)
STA $DD0F
PLA ; A zurückholen
LSR A ; Bit für "Unterlauf Timer B" nach CF
LSR A
RTS ; Rückkehr in aufrufende Routine
; Initialisierung
P_AD: LDX #$00 ; LOAD/VERIFY Flag
LDY #<$17FA ; Startadresse für Laden, Low-Byte
LDA #>$17FA ; Startadresse für Laden, High-Byte
STX *$0A ; LOAD/VERIFY-Flag auf "LOAD" setzen
STX *$93 ; LOAD/VERIFY-Flag auf "LOAD" setzen
STX *$90 ; Statuswort ST löschen
STY *$C3 ; Startadresse für Laden auf $17FA setzen
STA *$C4
INC $D020 ; Bildschirm-Rahmenfarbe weiterzählen
JMP P_BA ; Zur Laderoutine im Header des Programms springen
DW $8000 ; Vektor für BASIC-Warmstart, ungültig
DW P_AA ; Vektor für Eingabe einer Zeile, umgebogen auf Schnelllader