Ghettoblaster/Schnelllader
<< zurück zu Ghettoblaster
Ghettoblaster/Schnelllader: Die folgenden Abschnitte stellen den disassemblierten Kassetten-Schnelllader des Spiels Ghettoblaster dar. Dazwischenliegende Bereiche mit Daten ohne Funktion sind weggelassen, um die Darstellung kompakt zu halten.
Autostart des Schnellladers[Bearbeiten | Quelltext bearbeiten]
Wenn das Spiel Ghettoblaster von Kassette geladen wird, werden zunächst die Kernal-Vektoren mit den folgenden Adressen überschrieben. Insbesondere wird hierbei der OUTPUT-Vektor auf die Initialisierungsroutine P_AH des Schnellladers umgebogen, so dass diese nach dem Abschluss des Ladevorgangs angesprungen und gestartet wird. Dass hierbei auch — vermutlich versehentlich — der CHKOUT-Vektor auf einen ungültigen Wert gesetzt wird, fällt beim Laden nicht auf, weil die Initialisierungsroutine die Originalwerte der Vektoren wieder herstellt.
ORG $0316
DW $FE66 ; BRK-Vektor (unverändert)
DW $FE47 ; NMI-Vektor (unverändert)
DW $F34A ; OPEN-Vektor (unverändert)
DW $F291 ; CLOSE-Vektor (unverändert)
DW $F20E ; CHKIN-Vektor (unverändert)
DW $F25A ; CHKOUT-Vektor (sollte $F250 sein)
DW $F333 ; CLRCH-Vektor (unverändert)
DW $F157 ; INPUT-Vektor (unverändert)
DW P_AH ; OUTPUT-Vektor (ursprünglich $F1CA)
DW $F6ED ; STOP-Vektor (unverändert)
DW $F13E ; GET-Vektor (unverändert)
DW $F32F ; CLALL-Vektor (unverändert)
DW $FE66 ; Warmstart-Vektor (unverändert)
DW $F4A5 ; LOAD-Vektor (unverändert)
DW $F5ED ; SAVE-Vektor (unverändert)
Unmittelbar auf diese Routine folgen 204 Datenbytes, die hauptsächlich den Kassettenpuffer überschreiben, jedoch keine Funktion erfüllen.
Schnelllade-Routine[Bearbeiten | Quelltext bearbeiten]
Die nachfolgende Routine P_AA ist der zentrale Programmteil des Schnellladers. Hier synchronisiert sich der Schnelllader zunächst mit den Banddaten, bevor er den Header des nächsten Programmteils einliest und — falls die dort abgelegte Sequenznummer mit der Nummer des gewünschten Programms an T006 übereinstimmt — diesen in den Speicher überträgt.
Ein 0-Bit ist auf Band als Impuls von 312 Systemtakten Dauer codiert, ein 1-Bit als Impuls mit einer Länge von 504 Takten. Die für die Zeitmessung verwendeten Zähler von CIA 2 werden so initialisiert, dass ein Impuls mit einer Länge von maximal 339 Takten als 0-Bit, ein längerer, maximal 539 Takte langer Impuls als 1-Bit interpretiert wird. Noch längere Impulse werden als Lesefehler gewertet und lösen einen Neustart der Synchronisation aus.
Ein Auftreten eines solchen Fehlers während eines Datentransfers führt, wie auch alle anderen Arten von Fehlern, zu einem Halt des Schnellladers. Hierbei lässt das Programm nur noch in einer Endlosschleife alle Bildschirmelemente blinken. Aus dieser Situation führt auch kein Reset und kein Druck auf RUN/STOP +RESTORE mehr heraus, da der Schnelllader mit Hilfe einer "CBM80"-Signatur an Adresse $8004 ein Steckmodul vortäuscht und dessen Reset- und NMI-Vektor ebenfalls auf die Endlosschleife gerichtet sind.
ORG $0400
P_AA: SEI ; Interrupts abschalten
LDA #$00 ; Zähler 1 von CIA 1 stoppen
STA $DC0E
LDA *$01 ; Datassettenmotor einschalten
AND #$C7
STA *$01
LDA #$0B ; Bildschirm abschalten
STA $D011
LDA #<P_AG ; IRQ würde Bildschirm endlos flimmern lassen
STA $0318
LDA #>P_AG
STA $0319
LDA $DC0D ; Alle aufgelaufenen Interrupts von CIA 1 löschen
LDA #$90 ; Impuls an FLAG (Datassette) als einzige Interruptquelle
STA $DC0D
LDA #$54 ; Zähler 1 von CIA 2 startet bei $0154 = 340
STA $DD04
LDA #$01
STA $DD05
LDA #$1C ; Zähler 2 von CIA 2 startet bei $021C = 540
STA $DD06
LDA #$02
STA $DD07
LDA $DD0D ; Alle aufgelaufenen Interrrupts von CIA 2 löschen
LDA #$19 ; Timer neu laden und im "One Shot"-Betrieb starten
STA $DD0E ; ... CIA 2 Timer A
STA $DD0F ; ... CIA 2 Timer B
; Synchronisation (mindestens 256 1-Bits, dann 1 0-Bit)
AA00: LDX #$00 ; Synchronisation mit 256 1-Bits
STX T003 ; Prüfsumme initialisieren
AA01: JSR P_AF ; Bit von Band lesen
BNE AA00 ; Neustart falls Impuls zu lang
BCC AA00 ; Neustart falls 0-Bit
DEX ; Zähler für 1-Bits erniedrigen
BNE AA01 ; Rücksprung falls noch nicht 256 1-Bits
AA02: JSR P_AF ; Bit von Band lesen
BNE AA00 ; Neustart falls Impuls zu lang
BCS AA02 ; Auf erstes 0-Bit warten
; Programmheader laden
JSR P_AE ; Sequenznummer von Band einlesen
CMP T006 ; und mit gewünschter Sequenznummer vergleichen
BNE P_AB ; bei falscher Sequenznummer Laderoutine verlassen
JSR P_AE ; Anfangsadresse (low) von Band einlesen
STA AA04+$01 ; und in Programmcode einarbeiten
JSR P_AE ; Anfangsadresse (high) von Band einlesen
STA AA04+$02 ; und in Programmcode einarbeiten
JSR P_AE ; Endadresse (low) von Band einlesen
STA T004+$00 ; und merken
JSR P_AE ; Endadresse (high) von Band einlesen
STA T004+$01 ; und merken
JSR P_AE ; Einsprungpunkt (low) von Band einlesen
STA T005+$00 ; und setzen
JSR P_AE ; Einsprungpunkt (high) von Band einlesen
STA T005+$01 ; und merken
; Programm laden
AA03: JSR P_AE ; Datenbyte von Band einlesen
AA04: STA $FFFF ; in Speicher schreiben
EOR T003 ; und in Prüfsumme einberechnen
STA T003
LDA AA04+$01 ; Aktuellen Schreibzeiger (low) holen
CMP T004+$00 ; und mit Endadresse (low) vergleichen
BNE P_AD ; Sprung falls Endadresse noch nicht erreicht
LDA AA04+$02 ; Aktuellen Schreibzeiger (high) holen
CMP T004+$01 ; und mit Endadresse (high) vergleichen
BNE P_AD ; Sprung falls Endadresse noch nicht erreicht
; Endadresse erreicht
JSR P_AE ; Prüfsumme von Band einlesen
CMP T003 ; und mit berechneter Prüfsumme vergleichen
BNE P_AC ; Bei Prüfsummenfehler flimmert Bildschirm endlos
INC T006
JSR $FDA3 ; Interrupt-Initialisierung
JSR $FC93 ; Rekorderbetrieb beenden
LDA T005+$00 ; Einsprungpunkt von 0 verschieden?
ORA T005+$01
BEQ AB00 ; Programmteil nicht anspringen falls 0
CLI ; Interrupts wieder zulassen
JMP (T005) ; Programm starten
P_AB: JSR $FDA3 ; Interrupt-Initialisierung
JSR $FC93 ; Rekorderbetrieb beenden
AB00: CLI ; Interrupts wieder zulassen
RTS
P_AC: JMP P_AG ; Bildschirm flimmert endlos
; Schreibzeiger erhöhen
P_AD: INC AA04+$01 ; Schreibzeiger (low) erhöhen
BNE AA03 ; Falls kein Überlauf nächstes Datenbyte holen
INC $D020 ; Rahmenfarbe hochzählen
INC AA04+$02 ; Schreibzeiger (high) erhöhen
JMP AA03 ; Nächstes Datenbyte holen
; Ein Byte von Band einlesen
P_AE: LDY #$08 ; Bitzähler initialisieren
AE00: JSR P_AF ; Ein Bit vom Band holen
BNE P_AC ; Bei Lesefehler zu flimmerndem Bildschirm wechseln
ROL T002 ; Gelesenes Bit von rechts in Adresse T002 schieben
DEY ; Bitzähler erniedrigen
BNE AE00 ; Rücksprung falls noch nicht 8 Bit
LDA T002 ; Gelesenes Byte nach A holen
RTS
; Ein Bit von Band einlesen
; 1..339 Takte: 0-Bit, 340..539 Takte: 1-Bit, 540.. Takte: Lesefehler
P_AF: LDA $DC0D ; Interruptregister CIA 1 holen
AND #$10 ; Warten auf Impuls vom Band
BEQ P_AF
LDA $DD0D ; Interruptregister CIA 2 löschen
PHA ; und merken
LDA #$19 ; Timer neu laden und im "One Shot"-Betrieb starten
STA $DD0E ; ... CIA 2 Timer A
STA $DD0F ; ... CIA 2 Timer B
LDA $D020 ; Rahmenfarbe holen
EOR #$05 ; blinken lassen
STA $D020 ; und zurückschreiben
PLA ; Wert Interruptregister zurückholen
AND #$03 ; Bits für Unterlauf Timer A/Timer B isolieren
LSR A ; Unterlauf Timer A nach CF, Unterlauf Timer B nach ZF
RTS
; Fehler: Bildschirm flimmert endlos
P_AG: LDX #$08 ; Bytezähler initialisieren
AG00: LDA T000,X ; Cartridge-Signatur byteweise lesen
STA $8000,X ; und an Adresse $8000 kopieren
DEX ; Bytezähler vermindern
BPL AG00 ; Rücksprung falls noch nicht alle Bytes kopiert
LDA #$1B ; Bildschirm einschalten
STA $D011
AG01: INC $D020 ; Rahmenfarbe erhöhen
DEC $D021 ; Hintergrundfarbe erhöhen
INC $D800,X ; Gesamten Inhalt des Farb-RAM erhöhen
INC $D900,X
INC $DA00,X
INC $DB00,X
INX ; Schreibzeiger für Farb-RAM erhöhen
BNE AG01 ; endlos Rahmenfarbe, Hintergrundfarbe und Farb-RAM erhöhen
BEQ AG01
T000: DW P_AG ; Reset-Vektor des Steckmoduls
DW P_AG ; NMI-Vektor des Steckmoduls
T001: DB $C3,$C2,$CD,$38,$30 ; Steckmodul-Signatur "CBM80"
T002: DB $00 ; Empfangenes Datenbyte
T003: DB $00 ; Prüfsumme
T004: DB $00,$00 ; Endadresse des zu ladenden Programms (low/high)
T005: DB $00,$00 ; Einsprung des zu ladenden Programms (low/high)
T006: DB $00 ; Sequenznummer des zu ladenden Programms
P_AH: LDA #$01 ; Sequenznummer des nachzuladenden Programms
STA T006 ; initialisieren
JSR $FD15 ; Kernal-Sprungvektoren wiederherstellen
LDY #$04 ; Bytezähler für Cartridge-Signatur
AH00: LDA T001,Y ; Zeichenweise Signatur "CBM80" lesen
CMP $8004,Y ; Mit Modul-Bereich ab $8000 vergleichen
BNE AH01 ; Sprung falls kein Steckmodul erkannt
DEY ; Lesezeiger erniedrigen
BPL AH00 ; Rücksprung falls noch nicht alle Zeichen verglichen
BMI P_AG ; Sprung falls Steckmodul erkannt
AH01: LDY #$08
AH02: LDA T000,Y ; Reset-/NMI-Vektor und Steckmodul-Signatur
STA $8000,Y ; nach $8000 umkopieren
DEY ; Schreibzeiger erniedrigen
BPL AH02 ; Rücksprung falls noch nicht alle Bytes kopiert
AH03: JSR P_AA ; Programmteil mit Sequenznummer T006 laden
JMP AH03 ; Nächsten Programmteil laden
Auf diesen Code folgen auf der Kassette noch weitere 666 Bytes, die keine Funktion besitzen, jedoch den Ladevorgang unnötigerweise um rund 13 Sekunden verlängern.