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.