CyberLoad/Quellcode

Aus C64-Wiki
Zur Navigation springenZur Suche springen

<< zurück zu CyberLoad


CyberLoad/Quellcode: Die folgenden Abschnitte stellen den disassemblierten Kassetten-Schnelllader CyberLoad dar. Sie sind gegliedert in einzelne Codeblöcke, die gemäß ihrer Funktion vor dem und während des Ladens gruppiert sind.

Der zugrundeliegende Maschinencode stammt aus der Kassettenversion des Spiels Cauldron. Bei anderen Programmen, die ebenfalls den Schnelllader Cyberload nutzen, können Routinen und Adressen geringfügig abweichen, da zum einen mehrere Versionen von CyberLoad existieren und zum anderen der Schnelllader sehr flexible Konfigurationsmöglichkeiten bietet.

Initialisierung[Bearbeiten | Quelltext bearbeiten]

Die nachfolgenden Codeabschnitte initialisieren den Bildschirm für die zweite Phase des Ladevorgangs und decodieren den ersten Schnelllader. Dieser war in verschlüsselter Form im Bandheader direkt hinter dem Dateinamen gespeichert und findet sich dementsprechend nun im Kassettenpuffer ab Adresse $0351.

Die Routine P_AA startet nach dem Laden automatisch. Erreicht wird dies dadurch, dass der Codeabschnitt als absolutes Maschinensprache-Programm ab Adresse $029F geladen wird und dort als erstes die Kopie des Interruptvektors mit der Startadresse von P_AA überschreibt. Üblicherweise ist an Adresse $029F/$02A0 der Vektor des Systeminterrupts zwischengespeichert, so dass er nach dem Ende des Ladevorgangs wiederhergestellt werden kann, sobald die Arbeit der Interruptroutine für den Bandbetrieb erledigt ist. Nun wird stattdessen nach dem Laden der Interruptvektor auf die Routine P_AA gerichtet — und diese startet, sobald der nächste Timerinterrupt ausgelöst wird.

ORG $029F
      DW P_AA          ; Überschreibt Puffer für Interruptvektor während Bandbetrieb
                       ; Wird nach dem Laden an $FCA8 als neuer Interruptvektor gesetzt

      DB $00,$91,$90,$91,$00 ; Flags für CIA1/CIA2

; Initialisierung, wird per Timer-Interrupt nach dem Laden mittels Kernal-Datassettenroutinen aufgerufen
P_AA: ORA ($4C,X)      ; DB $01,$4C (Flag für "PAL-Version")
      SEI
      JSR $E544        ; Bildschirm löschen
      LDX #T001-T000-1 ; Länge des Meldungstextes minus 1
AA00: LDA #$01         ; Farbcode für "weiß"
      STA $D945,X      ; in 9. Zeile des Farb-RAM schreiben
      STA $D9E5,X      ; in 13. Zeile des Farb-RAM schreiben
      LDA #$07         ; Farbcode für "gelb"
      STA $D96D,X      ; in 10. Zeile des Farb-RAM schreiben
      STA $D995,X      ; in 11. Zeile des Farb-RAM schreiben
      STA $D9BD,X      ; in 12. Zeile des Farb-RAM schreiben
      LDA #$A0         ; Inverses Leerzeichen
      STA $0545,X      ; in 9. Zeile des Bildschirmspeicher schreiben
      STA $05E5,X      ; in 13. Zeile des Bildschirmspeichers schreiben
      LDA #$52         ; Waagrechter Strich unten
      STA $056D,X      ; in 10. Zeile des Bildschirmspeichers schreiben
      LDA #$77         ; Waagrechter Strich oben
      STA $05BD,X      ; in 12. Zeile des Bildschirmspeichers schreiben
      LDA T000,X       ; Meldungstext
      STA $0595,X      ; in 11. Zeile des Bildschirmspeichers schreiben
      DEX              ; Schreibindex vermindern
      BPL AA00         ; Rücksprung falls noch nicht Unterlauf
      LDY #$AB         ; Länge des ersten Schnellladers (Routinen P_Bx)
AA01: EOR P_BA-$01,Y   ; Ersten Schnelllader entschlüsseln
      STA P_BA-$01,Y
      DEY              ; Zähler vermindern
      BNE AA01         ; Rücksprung falls noch nicht ganz entschlüsselt
      EOR #$AF         ; A=$7F
      STA $DC0D        ; Alle Interrupts von CIA 1 abschalten
      BNE P_BA         ; Unbedingter Sprung

T000: DB $03,$19,$02,$05,$12,$0C,$0F,$01,$04,$20 ; "CYBERLOAD "
      DB $0E,$0F,$17,$20                         ; "NOW "
      DB $0C,$0F,$01,$04,$09,$0E,$07,$20         ; "LOADING "
      DB $03,$01,$15,$0C,$04,$12,$0F,$0E         ; "CAULDRON"

T001: DB $39,$30,$31,$32,$33,$34,$35,$36         ; "90123456"

T002: DW P_AA          ; IRQ-Vektor

T003: DB $6B

P_AB: STX $FEC1        ; Wirkungsloser Code
      LDY $8B8D,X
      EOR #$7F         ; A=$00, Farbcode für "schwarz"
      STA $D160        ; Bildschirm-Rahmenfarbe setzen
      STA $D3E1        ; Bildschirm-Hintergrundfarbe setzen
; Wirkungsloser Code, setzt nur X auf $90 und erschwert die Analyse
AB00: NOP              ; Illegaler Opcode $7A
      INY
      NOP $EAF6,Y      ; Illegaler Opcode $DC
      INC $DE4C
      DEX
      CPX #$90
      BNE AB00
      STY $8C8C        ; Y ist irrelevant
      CPY $8C8C
      BEQ P_BA         ; Unbedingter Sprung

Erster Schnelllader[Bearbeiten | Quelltext bearbeiten]

Die nachfolgenden Abschnitte zeigen den Header der ersten Programmdatei auf der Kassette. Er beinhaltet zum einen die bei Datassetten-Dateien üblichen Informationen (Dateityp, Start- und Endadresse sowie Dateinamen), zum anderen findet sich in den üblicherweise nicht belegten Bytes 21...191 der erste Schnelllader in verschlüsselter Form. Normalerweise wird dieser erst nach dem Laden der Initialisierungsroutine decodiert; nachfolgend ist er in bereits entschlüsselter Form wiedergegeben.

Die Aufgabe dieser Laderoutine ist es, den zweiten Schnelllader in einzelnen Abschnitten von Band zu lesen und in einzelnen Blöcken von Band zu lesen. Jedem Abschnitt ist dabei ein in einem Byte gespeicherter Offset vorangestellt, der vor dem Laden zum Schreibzeiger addiert wird und eine entsprechend große Lücke in den geschriebenen Daten erzeugt. Auf diese Weise ist es möglich, den zweiten Schnelllader in den Adressbereich $0002...$03FF zu laden, ohne dabei kritische Inhalte von Zeropage und Stack zu überschreiben.

Die Adresse, ab der die vom Band gelesenen Daten in den Hauptspeicher übertragen werden sollen, findet sich hartcodiert hinter Label BA04. Um die Zieladresse des Ladevorgangs zu verschleiern, startet der Ladevorgang dabei mit einer Startadresse von $FFD5, auf die aber sofort ein Offset von $2D Bytes aufaddiert wird, um (unter Vernachlässigung des Additionsübertrags) die Zieladresse $0002 zu erhalten. Jedem Datenbyte folgt normalerweise ein 1-Bit; das letzte Byte eines Blocks ist durch die Bitkombination "00" gekennzeichnet, das Ende der nachzuladenden Datei durch die Bitkombination "01".

Der Schnelllader sieht die Möglichkeit vor, das nachgeladene Programm direkt durch einen Sprung an Adresse $0400 (Label P_BD) zu starten. Hiervon wird aber kein Gebrauch gemacht — stattdessen wird der JSR-Befehl bei BA07 beim Nachladen des zweiten Schnellladers durch den Opcode für JMP ersetzt, und auf dem beim Nachladen überschriebenen Stack findet sich an der passenden Stelle die Startadresse des dritten Schnellladers minus eins ($0001). Sobald der Prozessor auf den nächsten RTS-Befehl stößt, geht er daher zum zweiten Schnelllader an Adresse $0002 über.

Während des Ladevorgangs erscheint auf dem Bildschirm die Meldung "CYBERLOAD NOW LOADING " gefolgt vom Dateinamen des Hauptprogramms (im Beispiel hier "CAULDRON"). Tatsächlich werden in dieser Phase aber noch der zweite und dritte Schnellllader von Band gelesen, die mit dem Spiel selbst nichts zu tun haben.

PRG $033C

; Programmheader auf Kassette
      DB $03                         ; Code für "Absolute Programmdatei"
      DW $029F                       ; Startadresse
      DW $033B                       ; Endadresse+1
      DB $08                         ; Erstes Zeichen des Dateinamens: <Commodore>-<Shift> blockieren
      DB 'CAULDRON'                  ; Dateiname
      DB $20,$20,$20,$20,$20,$20,$20 ; Füller für Dateinamen

; Erster Schnellader, schon entschlüsselt
P_BA: BNE P_AB         ; Sprung falls von P_AA kommend, kein Sprung falls von P_AB kommend
      EOR #$16         ; LDA #$16
      STA *$71,X       ; STA *$01 (Bandmotor an, BASIC-ROM ausblenden)
      LDY #$85         ; CIA 1 Timer A mit $0285 (645) initialisieren
      STY $DC04        ; Low-Byte
      LDY #$02
      STY $DC05        ; High-Byte
      STX $DC0D        ; Impuls vom Band löst Interrupt aus (X=$90)
BA00: PHA              ; Gesammelte Bits retten
      JSR P_BC         ; Ein Bit von Band einlesen
      PLA              ; Gesammelte Bits zurückholen
      ROL A            ; und gerade gelesenes Bit von rechts dazuschieben
      CMP #$0F         ; Synchronisationskennzeichen $0F gelesen?
      BNE BA00         ; Rücksprung falls nicht
BA01: JSR P_BB         ; Nächstes Byte von Band einlesen
      CMP #$0F         ; Weiteres Synchronisationszeichen?
      BEQ BA01         ; Rücksprung falls ja
      CMP #$F0         ; Invertiertes Synchronisationszeichen?
      BNE BA00         ; Synchronisation neu starten falls nicht
BA02: JSR P_BB         ; Ein Byte von Band einlesen
      CLC              ; zum bisherigen Schreibzeiger addieren (Lücke lassen)
      ADC BA04+$01
      STA BA04+$01     ; und zurückschreiben
      BCC BA03         ; Sprung falls kein Additionsüberlauf
      INC BA04+$02     ; Schreibzeiger um Additionsüberlauf korrigieren
BA03: JSR P_BB         ; Ein Byte vom Band holen
      PHA              ; und retten
      JSR P_BC         ; Ein Bit von Band einlesen
      BCC BA06         ; Sprung falls 0-Bit (=Blockende oder Dateiende)
      PLA              ; Sonst gelesenes Byte zurückholen
BA04: STA $FFD5        ; und an Zieladresse schreiben
      INC BA04+$01     ; Zieladresse erhöhen
      BNE BA05         ; Sprung falls kein Übertrag
      INC BA04+$02     ; Übertrag berücksichtigen
BA05: BCS BA03         ; Unbedingter Sprung, da immer CS
BA06: JSR P_BC         ; Ein Bit von Band einlesen
      BCS BA02         ; Sprung falls 1-Bit (=Lücke lassen und dann weiterladen)
BA07: JSR P_BB         ; sonst Prüfsumme von Band einlesen (JSR wird beim Nachladen des zweiten Schnellladers durch JMP ersetzt)
      CMP *$EF         ; und mit berechneter Prüfsumme vergleichen
      BEQ P_BD         ; Geladenen Programmteil starten falls gleich
      JMP $FCE2        ; sonst Reset auslösen

; Ein Byte von Band in A einlesen
P_BB: LDA #$01         ; A sammelt 8 empfangene Bits
BB00: PHA              ; A retten
      JSR P_BC         ; Bit von Band einlesen und nach CF
      PLA              ; A zurückholen
      ROL A            ; Empfangenes Bit von rechts in A schieben
      BCC BB00         ; Rücksprung falls noch nicht 8 Bit empfangen
      PHA              ; Empfangenes Byte in A retten
      ADC *$EF         ; und in Prüfsumme einarbeiten
      STA *$EF
      PLA              ; Empfangenes Byte in A zurückholen
      RTS

; Ein Bit von Band in CF einlesen
P_BC: LDA #$10         ; Auf Impuls vom Band warten
BC00: BIT $DC0D
      BEQ BC00
      LDA $DC04        ; Low-Byte von CIA 1 Timer A holen
      CMP $DC04        ; Steht der Timer, weil abgelaufen?
      BEQ BC01         ; Sprung mit gesetztem CF wenn abgelaufen
      CLC              ; CF löschen wenn noch nicht abgelaufen
BC01: LDA #$19         ; CIA 1 Timer A wieder im "One Shot"-Modus starten
      STA $DC0E
BC02: PHP              ; Empfangenes Bit im CF retten
      DEC *$EE         ; Zähler für Scrolling vermindern (einmal scrollen pro 32 Bit)
      BPL BC04         ; Sprung falls kein Unterlauf
      LDA #$20         ; Zähler wieder auf 32 setzen
      STA *$EE         ; und merken
      LDY *$FE         ; Schreibindex für Animation holen
      LDA #$A0         ; Reverses Leerzeichen
      STA $0545,Y      ; an zwei Stellen auf Bildschirm darstellen
      STA $05E5,Y      ; und damit vorige Animation löschen
      INY              ; Schreibindex erhöhen
      CPY #$1E         ; Maximalwert erreicht?
      BCC BC03         ; Sprung wenn nicht
      LDY #$00         ; Schreibindex zurücksetzen
BC03: LDA #$20         ; Leerzeichen
      STA $0545,Y      ; an zwei Stellen auf Bildschirm darstellen
      STA $05E5,Y      ; und damit neue Animation anzeigen
      STY *$FE         ; Schreibindex merken
BC04: PLP              ; Empfangenes Bit ins CF zurückholen
      RTS

T100: DB $A6,$02,$00,$00,$00,$00

P_BD:                  ; $0400

Zweiter Schnelllader[Bearbeiten | Quelltext bearbeiten]

Der zweite Schnelllader ist ab Adresse $0002 gespeichert, belegt also sicherlich keinen Speicherplatz, der während des Ladens vom Hauptprogramm überschrieben werden könnte. Nur der erste Teil ist direkt lauffähig; ab Label CC02 an Adresse $0073 ist die Routine noch verschlüsselt und wird unmittelbar vor der Ausführung decodiert. In diesen Vorgang greift auch einmalig ein von den Timern des CIA 2 ausgelöster NMI mit Behandlungsroutine ab P_CB ein. Das nachfolgende Listing stellt den kompletten Code in decodierter Form dar.

Zu den Merkwürdigkeiten dieser Routine gehört es, dass sie überprüft, ob im RAM unterhalb des Kernals an Adresse $F37A...$F478 nicht lauter gleiche Bytes stehen. Falls dies doch der Fall ist, so überschreibt sich der Schnelllader selbst und bringt damit den C64 zum Absturz. Reproduzieren lässt sich dieser Effekt, indem man vor dem Laden von "CAULDRON" zuerst die folgende BASIC-Zeile und erst dann den "LOAD"-Befehl eingibt:

FOR N=62330 TO 62584:POKE N,42:NEXT

Der Sinn dieser Schutzmaßnahme ist nicht unmittelbar ersichtlich.

PRG $0002

; Zweiter Schnelllader
P_CA: SEI              ; Interrupts deaktivieren
      DEC *$01         ; $06->$05, alle ROMs ausblenden
      LDA $D011        ; Bildschirm ausschalten
      AND #$EF
      STA $D011
      LDX #$10         ; Etwa 20 ms Pause
CA00: INY
      BNE CA00
      DEX
      BNE CA00
      LDA #$1F         ; Startwert für CIA 2 Timer A und B auf $231F (8991)
      STA $DD04        ; Low-Byte
      STA $DD06
      EOR #$3C         ; A=$23
      STA $DD05        ; High-Byte
      STA $DD07
      EOR #$32         ; A=$11
      STA $DD0E        ; CIA 2 Timer A mit Startwert laden und starten (zeitgesteuerte Änderung der Entschlüsselung)
      EOR #$92         ; A=$83
      STA $DD0D        ; NMI zulassen bei Unterlauf CIA 2 Timer A und B
      EOR #$83         ; A=$00 = >P_CB
      STA $FFFB        ; NMI-Vektor auf P_CB richten, Low-Byte
      EOR #$3B         ; A=$3B = <P_CB
      STA $FFFA        ; NMI-Vektor auf P_CB richten, High-Byte
      BNE P_CC         ; Unbedingter Sprung

P_CB:  BIT $DD0B
      INC CC02+$02     ; Während des Entschlüsselns per NMI Daten modifizieren
      PHA
      LDA CC02+$02
      CMP #$E0
      BCC CB00
      LSR CC02+$02
CB00: LDA CC02+$00
      EOR #$B7
      STA CC02+$00
      PLA
      RTI

P_CC: LDA #$91         ; Zeitgesteuerte Änderung des Entschlüsselungsverfahrens aktivieren
      STA $DD0E        ; CIA 2 Timer A neu laden und starten
      STA $DD0F        ; CIA 2 Timer B neu laden und starten
      LDX #$8B
CC00: TXA              ; X=$8B
      AND #$0F
      TAY              ; Y=$0B
      EOR CC02-$01,X   ; Byteweise verschlüsselten Schnelllader lesen
CC01: EOR $0314,Y      ; und durch XOR-Verknüpfungen entschlüsseln
      DEY              ; Schon fertig entschlüsselt?
      BPL CC01         ; Rücksprung falls nicht
      STA CC02-$01,X   ; sonst entschlüsseltes Byte zurückschreiben
      DEX              ; Schreibindex vermindern
      BNE CC00         ; Rücksprung falls noch nicht fertig entschlüsselt
; Schnelllader, schon entschlüsselt
CC02: ROL $0934        ; Eigentlich Daten ($2E,$34,$09)
      LDA #$3E
      STA $FFFA
      LDA $F379,Y      ; Prüfen, ob alle Bytes im Bereich $F37A...$F478 gleich
CC03: CMP $F379,Y
      BNE CC05         ; Sprung falls nicht
      DEY              ; sonst weiter vergleichen
      BNE CC03         ; Rücksprung falls noch nicht 255 Bytes verglichen
CC04: STA $0002,Y      ; Schnellladeroutinen überschreiben
      STA $0092,Y      ; und Rechner damit zum Absturz bringen
      STA $0300,Y
      INY              ; Schreibindex erhöhen
      BNE CC04         ; Rücksprung falls noch nicht 3*256 Bytes geschrieben
CC05: LDA #$1B         ; Bildschirm einschalten
      STA $D011
CC06: LDA #$85         ; Startwert von CIA 1 Timer A auf $0285 (645)
      STA $DC04        ; Low-Byte
      LDA #$02
      STA $DC05        ; High-Byte
CC07: LDA #$FF         ; Von Band gelesene Daten nach jedem Bit prüfen
CC08: PHA              ; Aktuell eingelesene Daten retten
      JSR P_BC         ; Ein Bit von Band einlesen und nach CF
      PLA              ; Aktuell eingelesene Daten zurückholen
      ROL A            ; und eingelesenes Bit von rechts hineinschieben
      CMP #$0F         ; Synchronisationszeichen $0F?
      BNE CC08         ; Rücksprung falls nicht
CC09: JSR P_BB         ; Ein Byte von Band einlesen
      CMP #$0F         ; Immer noch Synchronisationszeichen $0F?
      BEQ CC09         ; Rücksprung falls ja
      CMP #$F0         ; Invertiertes Synchronisationszeichen $F0?
      BNE CC07         ; Neu synchronisieren falls nicht
      LDX #$00         ; Schreibindex für Dateiheader initialisieren
CC10: JSR P_BB         ; Byte von Band holen
      EOR #$AE         ; und entschlüsseln
      STA *$02,X       ; und in Headerpuffer an $02...$05 schreiben
      INX              ; Schreibindex erhöhen
      CPX #$04         ; Schon vier Byte empfangen?
      BCC CC10         ; Rücksprung falls noch nicht
CC11: JSR P_BB         ; Byte von Band holen
      EOR #$D2         ; und entschlüsseln
      LDY #$00         ; Schreibindex initialisieren
      DEC *$01         ; $05->$04, alle ROMs und I/O ausblenden
      STA ($02),Y      ; Gelesenes Byte in Hauptspeicher schreiben
      INC *$01         ; $04->$05, I/O wieder einblenden
      INC *$02         ; Low-Byte des Schreibzeigers erhöhen
      BNE CC12         ; Sprung falls kein Überlauf
      INC *$03         ; High-Byte des Schreibzeigers erhöhen
CC12: LDA *$04         ; Low-Byte der Dateilänge holen
      BNE CC13         ; Sprung falls nicht 0
      DEC *$05         ; High-Byte der Länge vermindern, da Unterlauf bei Low-Byte
CC13: DEC *$04         ; Low-Byte der Länge vermindern
      LDA *$04         ; Verbleibende Anzahl Bytes prüfen
      ORA *$05
      BNE CC11         ; Rücksprung falls nicht 0
CC14: RTS              ; Sprung nach P_DA (1.) / nach P_EA (2.)

Dritter Schnelllader[Bearbeiten | Quelltext bearbeiten]

Der dritte Schnelllader übernimmt das Lesen des eigentlichen Hauptprogramms von Band. Neben der hier gezeigten Variante an Adresse $E000 existieren auch zwei Versionen, die an Adresse $0800 bzw. $C800 geladen werden. Auf diese Weise kann auf die Speicheranforderungen des nachzuladenden Programms Rücksicht genommen werden.

Das Laden der Programmdaten geschieht in Blöcken zu jeweils maximal 256 Bytes, denen je ein Header von 8 Bytes Länge vorangestellt ist:

Länge Inhalt
2 Startadresse des Blocks im Hauptspeicher (Little Endian-Format)
1 Blocklänge in Bytes ($00 = 256 Bytes)
1 Prüfsumme (XOR-Verknüpfung von Rest des Headers und Daten)
1 Flags zur Ausführung von Musikroutinen und Programmteilen
Bit 7: Routine ausführen, Einsprung an Adresse $16/$17 (0=nein, 1=ja)
Bit 6: Nächster Block eingelesen (0=nein, 1=ja)
Bit 5: Datassettenmotor während Routine ausschalten (0=nein, 1=ja)
Bit 4: Musikroutine installiert (0=nein, 1=ja)
Bit 3: Rasterzeileninterrupt abschalten (0=nein, 1=ja)
Bit 2: Bildschirm einschalten (0=nein, 1=ja)
Bit 1: Musikwiedergabe aktiv (0=nein, 1=ja)
Bit 0: Fade-out erforderlich (0=nein, 1=ja)
1 Flag zur Steuerung des Ladebildschirms
Bit 7: Ladebildschirm initialisieren (0=nein, 1=ja)
2 Einsprungadresse für Ausführung geladener Programmteile (Little Endian-Format)


Das Laden findet in einer Endlosschleife statt, die schließlich dadurch verlassen wird, dass der letzte geladene Block einen Teil des Schnellladers überschreibt (ab Label DF17, Adressbereich $E297...$E2B2). Der Code, der dabei ersatzweise geladen wird, findet sich im nächsten Abschnitt.

Der dritte Schnelllader ist in vielfacher Weise konfigurierbar. Er kann eine mehrzeilige Meldung auf dem Textbildschirm anzeigen (in komprimierter Form abgelegt ab Label T400), per Rasterzeileninterrupt in einer wählbaren Bildschirmzeile einen Scrolltext darstellen (gespeichert ab Label T401), Musik abspielen, den Bildschirmrahmen flackern lassen, an beliebiger Stelle ein blinkendes Sprite zeigen (Definition ab Label T200, Farbverlauf ab Label T302, hier nicht genutzt), einen geladenen Programmteil ausführen und währenddessen auf Wunsch auch den Datassettenmotor abschalten, oder (durch Aufruf einer Musikroutine alle 20 ms, hier nicht genutzt) eine zwischenzeitlich geladene Begleitmelodie abspielen.

Und wer sich die Mühe gemacht hat, die Routine bis hierher zu analysieren, der erhält an Label T301 die wohlverdiente Anerkennung: "HACKERS, FUCK OFF AND DIE...".

PRG $E000

P_DA: JSR P_DG         ; VIC initialisieren, Bildschirm löschen
      LDA #$00
      STA *$14         ; Flags initialisieren
      STA DF01+$01     ; Sprungverteiler für Schnelllader initialisieren
      STA *$EE         ; Zähler für Softscrolling initialisieren
      LDA #<T401       ; Zeiger auf zu scrollende Meldung initialisieren
      STA *$0E         ; Low-Byte
      LDA #>T401
      STA *$0F         ; High-Byte
      LDA #$1B         ; Bildschirm einschalten
      STA $D011
      LDA #$00         ; Rasterinterrupt in Rasterzeile 0
      STA $D012
      LDA #$01         ; Rasterinterrupt
      STA $D019        ; Anforderungen löschen
      STA $D01A        ; und aktivieren durch Setzen des Masken-Bit
      LDA #<P_DF       ; IRQ-Vektor zeigt auf Routine P_DF
      STA $FFFE        ; Low-Byte
      LDA #>P_DF
      STA $FFFF        ; High-Byte
      LDA #<DA07       ; NMI-Vektor zeigt auf Adresse DA07
      STA $FFFA        ; Low-Byte
      LDA #>DA07
      STA $FFFB        ; High-Byte
      LDA #$85         ; Startwert für CIA 1 Timer A auf $0285 (645)
      STA $DC04        ; Low-Byte
      LDA #$02
      STA $DC05        ; High-Byte
      LDX #$12         ; Stackzeiger
      TXS              ; auf $12 setzen
      BNE DA01         ; Unbedingter Sprung
; Code, der bei "CAULDRON" nicht ausgeführt wird
      LDA #$18         ; A=24
      STA $D00E        ; als X-Position von Sprite 7 setzen
      LDA #$E5         ; A=229
      STA $D00F        ; als Y-Position von Sprite 7 setzen
      LDA #$00         ; MSB der X-Position aller Sprites löschen
      STA $D010
      LDA #$80         ; Sprite 7 aktivieren
      STA $D015
      LDA #$00         ; Lesezeiger für Farbverlauf an T302 initialisieren
      STA *$08
      STA $D02E        ; Farbe von Sprite 7 auf "schwarz"
      LDX #$3E         ; X auf Länge der Spritedefinition=63 Bytes setzen
DA00: LDA T200,X       ; Spritedefinition byteweise lesen
      STA $0200,X      ; und umkopieren nach $0200...$023E
      DEX              ; Lese-/Schreibindex vermindern
      BPL DA00         ; Rücksprung falls noch nicht ganzes Sprite umkopiert
      LDA #$08         ; Binärdaten in Block 8 (Adressbereich $0200...$023E)
      STA $07FF        ; als Spritedefinition für Sprite 7 verwenden
DA01: LDA #$00         ; Flag für "Sprite blinken lassen"
      STA *$0A         ; aktivieren
      LDA DE02+$01     ; Rasterinterrupt aktiv?
      BEQ DA03         ; Sprung falls nicht
      LDX #$27         ; Schreibindex auf Zeilenlänge-1 initialisieren
      LDA #$01         ; Farbcode für "weiß"
DA02: STA $DB98,X      ; Farb-RAM der zweitletzten Bildschirmzeile füllen
      DEX              ; Schreibindex vermindern
      BPL DA02         ; Rücksprung falls noch nicht ganze Zeile gefüllt
DA03: CLI              ; Interrupts aktivieren
DA04: LDA #$20
DA05: BIT *$14         ; Warten, bis Bit 6 an Adresse $14 gesetzt ("Block vollständig empfangen")
      BVC DA05         ; (geschieht an Adresse DF17)
      PHP              ; Statusregister retten
DA06: LDA $D011        ; Warten, bis Rasterzeile 256 erreicht
      BPL DA06
      SEI              ; Interrupts deaktivieren
      LDA $DD04        ; Laufen beide Timer von CIA 2 synchron?
      CMP $DD06
      BNE DA07         ; Sprung falls nicht
      CMP $DD04        ; Timer 1 von CIA 2 abgelaufen?
      BNE DA08         ; Sprung falls nicht
DA07: ASL DF13+$01     ; Anzahl Headerbytes von 8 auf 16 verdoppeln
      ROL *$17         ; Möglicherweise weiteres Laden unmöglich machen?
      ASL *$16
      ROL *$14
DA08: CLI              ; Interrupts wieder aktivieren
      BIT *$15         ; Ist Bildschirm schon initialisiert?
      BPL DA09         ; Sprung falls ja
      JSR P_DG         ; Bildschirm initialisieren
      LDA *$15         ; und Flag für "Bildschirm initialisieren" löschen
      AND #$7F
      STA *$15
DA09: PLP              ; Statusregister zurückholen
      BPL DA04         ; Rücksprung falls Bit 7 an Adresse *$14 nicht gesetzt ("Routine ausführen")
      BEQ DA10         ; Sprung falls Bit 5 an Adresse *$14 nicht gesetzt ("Motor währenddessen ausschalten")
      LDA *$01         ; Motor ausschalten
      ORA #$20
      STA *$01
DA10: LDA *$14         ; Bit für "Routine ausführen" zurücksetzen
      AND #$7F
      STA *$14
      JSR P_DB         ; Routine ausführen
      LDA *$01         ; Motor ggf. wieder einschalten
      AND #$DF
      STA *$01
      JMP DA04         ; Auf nächsten Block warten

P_DB: JMP ($0016)

; Spritedefinition (drei ineinandergeschachtelte Rechtecke)
T200: DB $FF,$FF,$FF,$FF,$FF,$FF,$C0,$00,$03,$CF,$FF,$F3,$CF,$FF,$F3,$CC
      DB $00,$33,$CC,$FF,$33,$CC,$FF,$33,$CC,$C3,$33,$CC,$C3,$33,$CC,$C3
      DB $33,$CC,$C3,$33,$CC,$C3,$33,$CC,$FF,$33,$CC,$FF,$33,$CC,$00,$33
      DB $CF,$FF,$F3,$CF,$FF,$F3,$C0,$00,$03,$FF,$FF,$FF,$FF,$FF,$FF

; VIC-Rasterinterrupt-Routine unterhalb des gescrollten Bilschirmteils
P_DC: LDA #$00         ; 38 Spalten, kein Scrolling in X-Richung
      STA $D016        ; aktivieren
      LDA #$00         ; Rasterzeile 0
      STA $D012        ; als Rasterzeile für nächsten Interrupt setzen
      CLI              ; Interrupts wieder zulassen (wegen Datassette)
      LDA *$EE         ; Scrolling in X-Richtung
      SEC              ; um 1 nach links bewegen
      SBC #$01
      AND #$07         ; dann auf Wertebereich 0...7 begrenzen
      STA *$EE         ; und zurückschreiben
      BCC DC00         ; Sprung falls Unterlauf
      PLA              ; sonst A zurückholen
      RTI              ; und aus Interrupt zurückkehren
DC00: TYA              ; Y retten
      PHA
      LDY #$00         ; Index für Umkopieren der Meldung initialisieren
DC01: LDA $0799,Y      ; Meldungstext byteweise lesen
      STA $0798,Y      ; und um 1 Zeichen (=8 Pixel) nach links scrollen
      INY              ; Index erhöhen
      CPY #$27         ; Schon 39 Zeichen umkopiert?
      BCC DC01         ; Sprung falls noch nicht
      LDY #$00         ; Leseindex initialisieren
      LDA ($0E),Y      ; Weiteres Zeichen für Meldungstext holen
      BPL DC02         ; Sprung falls nicht Ende-Kennzeichen $FF
      LDA #<T401       ; sonst Lesezeiger auf Anfang des Meldungstexts richten
      STA *$0E         ; Low-Byte
      LDA #>T401
      STA *$0F         ; High-Byte
      LDA ($0E),Y      ; Zeichen vom Anfang der Meldung holen
DC02: STA $07BF        ; und an gescrollten Text anhängen
      INC *$0E         ; Lesezeiger weiterbewegen
      BNE DC03         ; Sprung falls kein Überlauf
      INC *$0F         ; sonst Überlauf berücksichtigen
DC03: PLA              ; Y zurückholen
      TAY
      PLA              ; A zurückholen
      RTI              ; Rückkehr aus Interrupt

P_DD: JSR $FFFF        ; Einsprung Musikwiedergabe (alle 20 ms), hier nicht verwendet
DD00: LDA T300         ; A=$83
      STA $DD0D        ; CIA 2 löst bei Unterlauf von Timer 1 oder Timer 2 einen NMI aus
DD01: PLA              ; Y zurückholen
      TAY
      PLA              ; X zurückholen
      TAX
      PLA              ; A zurückholen
      RTI              ; Rückkehr aus Interrupt

; VIC-Rasterinterrupt-Routine
P_DE: STA $D019        ; Rasterinterrupt-Request löschen
      LDA $D012        ; Aktuelle Bildschirmzeile holen
      CMP DE03+$01     ; Anfang des gescrollten Bereichs erreicht (Rasterzeile $E2=226)?
      BCC DE01         ; Sprung falls noch nicht erreicht
      CMP DE00+$01     ; Ende des gescrollten Bereichs erreicht (Rasterzeile $F3=243)?
      BCS P_DC         ; Sprung falls erreicht oder überschritten
; VIC-Rasterinterrupt-Routine für gescrollten Bereich
      LDA *$EE         ; Aktuelles Scrolling der Laufschrift in X-Richtung
      STA $D016        ; aktivieren
DE00: LDA #$F3         ; Rasterzeile 243
      STA $D012        ; als Rasterzeile für nächsten Interrupt setzen
      CLI              ; Interrupts wieder zulassen (unnötig, denn RTI beinhaltet PLP)
      PLA              ; A zurückholen
      RTI              ; Rückkehr aus Interrupt
; VIC-Rasterinterrupt-Routine für nicht gescrollten Bereich
DE01: CLI
DE02: LDA #$FF         ; Flag für "Rasterinterrupt aktiv" ($00="Nein")
      BEQ DE04
DE03: LDA #$E2         ; Rasterzeile 226
      STA $D012        ; als Rasterzeile für nächsten Interrupt setzen
DE04: INC *$08         ; Lesezeiger für Farbe von Sprite 7 erhöhen
      TXA              ; X retten
      PHA
      TYA              ; Y retten
      PHA
      LDA *$0A
      ORA *$09
      BNE DE05
      LDA *$08         ; Lesezeiger für Farbe von Sprite 7 holen
      AND #$1F         ; auf Wertebereich 0...31 beschränken
      TAX              ; und als Index für Zugriff auf Farbtabelle nutzen
      LDA T302,X       ; Farbcode holen
      STA $D02E        ; und als Farbe für Sprite 7 setzen
      INC *$09
DE05: LDA $0075        ; Bei CAULDRON A=$09
      EOR #$0A         ; Bei CAULDRON A=$03
      AND *$14         ; Musikwidergabe erforderlich?
      BEQ DD00         ; Rückkehr aus Interrupt, falls keine Musikwiedergabe
; Code, der bei "CAULDRON" nicht ausgeführt wird
      CMP #$02         ; Flag "Musikwiedergabe" prüfen
      BEQ P_DD         ; Sprung falls gesetzt
      BCC DE06         ; Sprung falls nicht gesetzt
      JSR $FFFF        ; Initialisierung der Musikwiedergabe, hier nicht verwendet
      LDA #$0F         ; Start-Lautstärke für Fade-out
      STA *$0C         ; setzen
      LDA #$FE         ; Flag für "Fade-out erforderlich" 
      JMP DE07         ; löschen und Rückkehr aus Interrupt
DE06: LDA *$0C         ; Lautstärke schon auf 0
      BEQ DD00         ; Sprung falls ja
      LDA *$08
      AND #$0F         ; sonst bei jedem 16. Rasterinterrupt
      BNE P_DD
      DEC *$0C         ; die Lautstärke um eine Stufe vermindern
      LDA *$0C         ; Aktuelle Lautstärke holen
      STA $D418        ; und an SID übertragen
      BNE P_DD         ; Sprung falls Lautstärke noch nicht auf 0
      LDA #$FC         ; sonst Flags für Musikwiedergabe löschen
DE07: AND *$14
      STA *$14
      JMP DD00         ; Rückkehr aus Interrupt

; Interruptroutine
P_DF: PHA              ; A retten
      LDA $D019        ; Rasterzeileninterrupt?
      BMI P_DE         ; Sprung falls ja
      LDA $DC0D        ; Interrupt-Register von CIA 1
      LSR A            ; Empfangenes Bit nach CF (CS falls Timer A abgelaufen)
      LDA #$11         ; CIA 1 Timer A neu starten
      STA $DC0E
      BEQ DF00         ; Unnötig, springt nie
      LDA *$02         ; Rahmenfarbe blinken lassen
      EOR #$05
      STA *$02
      STA $D020
DF00: LDA $DC0D        ; Alle Interruptanforderungen von CIA 1 löschen
      LDA #$00
      STA *$09
DF01: BEQ DF02         ; Sprungverteiler, unbedingter Sprung
; Bitweise Synchronisation mit Banddaten
DF02: ROL *$0B         ; Empfangenes Bit von rechts in Adresse $0B schieben
      LDA *$0B         ; Empfangenes Byte holen
      CMP #$0F         ; und mit Synchronisationszeichen $0F vergleichen
      BNE DF04         ; Rückkehr aus Interrupt falls ungleich
      LDA #DF05-DF02   ; sonst Sprung von DF02 nach DF05 vorbereiten
      STA DF01+$01
      LDA #$00         ; Sprung von DF06 nach DF07 vorbereiten
      STA DF06+$01
DF03: LDA #$01         ; Empfang des nächsten Byte vorbereiten
      STA *$0B         ; und dafür Bitzähler an Adresse $0B initialisieren
DF04: PLA              ; Rückkehr aus Interrupt
      RTI
; Byteweise Synchronisation mit Banddaten
DF05: ROL *$0B         ; Empfangenes Bit von rechts in Adresse $0B schieben
      BCC DF04         ; Rückkehr aus Interrupt falls noch nicht 8 Bit
      LDA *$0B         ; Empfangenes Byte holen
      EOR *$EF         ; und in Prüfsumme einarbeiten
      STA *$EF
      TXA              ; X retten
      PHA
      TYA              ; Y retten
      PHA
      LDA *$0B         ; Empfangenes Byte nach A holen
      LDX #$01         ; Empfang des nächsten Byte vorbereiten
      STX *$0B         ; und dafür Adresse $0B als Bitzähler initialisieren
DF06: BCS DF07         ; Sprungverteiler, unbedingter Sprung
DF07: CMP #$0F         ; Immer noch Synchronisationszeichen $0F?
      BEQ DF09         ; dann Sprung von DF06 nach DF11 vorbereiten
DF08: LDA #$00         ; sonst Neustart der Synchronisation
      STA DF01+$01
      LDA #$FF         ; Nach jedem Bit auf Synchronisationszeichen prüfen
      STA *$0B
      JMP DD01         ; Rückkehr aus Interrupt
DF09: LDA #DF11-DF07
      STA DF06+$01
DF10: JMP DD01
DF11: CMP #$0F         ; Immer noch Synchronisationszeichen $0F?
      BEQ DF10         ; dann Rückkehr aus Interrupt falls ja
      CMP #$CC         ; Synchronisationszeichen $CC?
      BNE DF08         ; Erneute Synchronisation falls nicht
      LDA #DF12-DF07   ; sonst Sprung von DF07 nach DF12 vorbereiten
      STA DF06+$01
      LDA #$00
      STA *$0D         ; Schreibindex für Dateiheader initialisieren
      STA *$EF         ; Prüfsumme initialisieren
      JMP DD01         ; Rückkehr aus Interrupt
; 8 Byte langen Blockheader von Band einlesen
DF12: LDY *$0D         ; Schreibindex für Header holen
      STA $0010,Y      ; Dateiheader nach $0010...$0017 einlesen
      INY              ; Schreibindex erhöhen
      STY *$0D         ; und merken
DF13: CPY #$08         ; Schon 8 Headerbytes empfangen?
      BCS DF14         ; Sprung falls ja
      JMP DD01         ; sonst Rückkehr aus Interrupt
DF14: LDA #$00         ; Schreibindex initialisieren
      STA *$0D
      LDA #DF16-DF07   ; Sprung von DF06 nach DF16 vorbereiten
      STA DF06+$01
DF15: JMP DD01
; Block (maximal 256 Bytes) von Band einlesen
DF16: LDY *$0D         ; Schreibindex holen
      LDX #$04         ; I/O-Bereich und alle ROMs ausblenden
      STX *$01
      STA ($10),Y      ; Gelesenes Byte in Speicher schreiben
      LDX #$05         ; I/O-Bereich wieder einblenden
      STX *$01
      INY              ; Schreibindex erhöhen
      STY *$0D         ; und merken
      DEC *$12         ; Bytezähler vermindern
      BNE DF15         ; Rücksprung falls noch nicht 0
      INC *$02         ; Rahmenfarben wechseln
      LDA *$EF         ; Prüfsumme holen
      BEQ DF17         ; Sprung falls 0
      STA *$0A         ; sonst Sprite nicht mehr blinken lassen
DF17: LDA CC02+$00     ; A=$2E
      EOR #$6E         ; A=$40
      ORA *$14         ; Bit 6 an Adresse $14 setzen("Block vollständig empfangen")
      STA *$14
      LSR A            ; Bit 2 von Adresse $14 ins CF holen
      LSR A
      LSR A
      LDA $D011        ; Bildschirm abschalten
      AND #$6F
      BCC DF18         ; falls Bit 2 an Adresse $14 gelöscht
      ORA #$10         ; sonst Bildschirm einschalten
DF18: STA $D011
      LDA *$14         ; Bit 3 von Adresse $14 prüfen
      AND #$08
      BEQ DF19         ; Sprung falls nicht gesetzt
      LDA #$00         ; sonst kein Rasterinterrupt in Zeile 226
      STA DE02+$01
DF19: LDA *$14         ; Adresse $14 nochmals prüfen
      AND #$13
      CMP #$11         ; Code für "Fade-out"?
      BNE DF20         ; Sprung falls nicht
      LDA #$00         ; Flag für "Keine Musik"
      STA *$0C         ; merken
      STA $D418        ; Lautstärke auf 0
DF20: JMP DF08         ; Neustart der Synchronisation

T300: DB $83

T301: DB 'HACKERS, FUCK OFF AND DIE...'

; Farbverlauf für blinkendes Sprite (von der dunkelsten zur hellsten Farbe und wieder zurück)
T302: DB $00,$06,$09,$02,$0B,$04,$08,$0C,$0E,$05,$0A,$03,$0F,$07,$0D,$01
      DB $0D,$07,$0F,$03,$0A,$05,$0E,$0C,$08,$04,$0B,$02,$09,$06,$00,$00

T303: DB $B7,$93

P_DG: LDA $DD00       ; VIC-Speicherbank auf $0000-$3FFF setzen
      ORA #$03
      STA $DD00
      LDA #$14        ; Adressen von VideoRAM und CharGen auf Defaultwerte
      STA $D018
      LDA $D011       ; Bildschirm einschalten
      AND #$1F
      ORA #$10
      STA $D011
      LDA #$00        ; 38 Spalten, kein Scrolling in X-Richtung
      STA $D016
; Bildschirm löschen
      LDX #$00        ; Schreibindex initialisieren
      LDA #$20        ; Leerzeichen
DG00: STA $0400,X     ; Bereich $0400...$04FF löschen
      STA $0500,X     ; Bereich $0500...$05FF löschen
      STA $0600,X     ; Bereich $0600...$06FF löschen
      STA $06E8,X     ; Bereich $06E8...$07E7 löschen
      INX             ; Schreibindex erhöhen
      BNE DG00        ; Rücksprung falls noch nicht 256 Bytes geschrieben
      LDA #<T400      ; Low-Byte Startadresse der Meldungstexte
      STA *$03        ; als Low-Byte des Lesezeigers
      LDA #>T400      ; High-Byte Startadresse der Meldungstexte
      STA *$04        ; als High-Byte des Lesezeigers
      LDA #$00        ; Low-Byte der Startadresse des Video-RAM
      STA *$05        ; als Low-Byte des Schreibzeigers
      LDA #$04        ; High-Byte der Startadresse des Video-RAM
      STA *$06        ; als High-Byte des Schreibzeigers
      LDY #$00        ; Leseindex initialisieren
      LDA ($03),Y     ; Farboode für "Hellrot"
      STA $D020       ; als Rahmenfarbe setzen
      INY             ; Leseindex erhöhen
      LDA ($03),Y     ; Farbcode für "Schwarz"
      STA $D021       ; als Hintergrundfarbe setzen
      INY             ; Leseindex erhöhen
      LDX #$00        ; Schreibindex initialisieren, bleibt immer 0
DG01: LDA ($03),Y     ; Byte aus Meldung holen
      CMP #$FF        ; Steuercode?
      BEQ DG04        ; Sprung falls ja
DG02: STA ($05,X)     ; sonst Zeichen auf Bildschirm ausgeben
      LDA *$06        ; High-Byte des Schreibzeigers auf Video-RAM holen
      PHA             ; und retten
      AND #$03        ; Entsprechendes High-Byte der Adresse im Farb-RAM berechnen
      ORA #$D8
      STA *$06        ; und als Adresszeiger setzen
      LDA *$07        ; Zeichenfarbe holen
      STA ($05,X)     ; und in Farb-RAM schreiben
      PLA             ; High-Byte des Schreibzeigers auf Video-RAM zurückholen
      STA *$06        ; und wiederherstellen
      INC *$05        ; Schreibzeiger weiterbewegen
      BNE DG03        ; Sprung falls kein Übertrag
      INC *$06        ; sonst Übertrag berücksichtigen
DG03: INY             ; Leseindex erhöhen
      BNE DG01        ; Rücksprung falls kein Übertrag
      INC *$04        ; Übertrag berücksichtigen
      BNE DG01        ; Unbedingter Rücksprung
; Steuercode verarbeiten
DG04: INY             ; Lesezeiger erhöhen
      BNE DG05        ; Sprung falls kein Übertrag
      INC *$04        ; sonst Übertrag berücksichtigen
DG05: LDA ($03),Y     ; Nächstes Byte aus Meldung lesen
      CMP #$10        ; Nächstes Byte kleiner als 16?
      BCS DG06        ; Sprung falls nicht
      STA *$07        ; Byte kleiner 16 als neue Zeichenfarbe merken
      BCC DG03        ; unbedingter Rücksprung
DG06: CMP #$FF        ; Nächstes Byte gleich $FF?
      BEQ DG08        ; dann Ende der Meldung
      CMP #$FE        ; Nächstes Byte gleich $FE?
      BNE DG07        ; Sprung falls nicht
      LDA #$FF        ; Karomuster
      BNE DG02        ; ausgeben und zum nächsten Byte
DG07: AND #$7F        ; Unterste 7 Bit isolieren
      CLC             ; und als Offset zum derzeitigen Schreibzeiger
      ADC *$05        ; addieren
      STA *$05        ; und zurückschreiben
      LDA *$06        ; Eventuellen Additionsübertrag
      ADC #$00        ; berücksichtigen
      STA *$06
      BNE DG03        ; Unbedingter Sprung, Lesezeiger erhöhen und nächstes Byte
DG08: RTS

T400: DB $0A                                         ; Rahmenfarbe
      DB $00                                         ; Hintergrundfarbe
      DB $FF,$FD                                     ; Schreibzeiger +115
      DB $FF,$FD                                     ; Schreibzeiger +115
      DB $FF,$D0                                     ; Schreibzeiger +80
      DB $FF,$01                                     ; Zeichenfarbe weiß
      DB $0E,$0F,$17,$20                             ; "NOW "
      DB $0C,$0F,$01,$04,$09,$0E,$07,$20             ; "LOADING "
      DB $03,$01,$15,$0C,$04,$12,$0F,$0E             ; "CAULDRON"
      DB $FF,$BB                                     ; Schreibzeiger +59
      DB $10,$12,$0F,$04,$15,$03,$05,$04,$20         ; "PRODUCED "
      DB $15,$0E,$04,$05,$12,$20                     ; "UNDER "
      DB $0C,$09,$03,$05,$0E,$13,$05                 ; "LICENSE"
      DB $FF,$90                                     ; Schreibzeiger +16
      DB $06,$12,$0F,$0D                             ; "FROM"
      DB $FF,$82                                     ; Schreibzeiger +2
      DB $10,$01,$0C,$01,$03,$05,$20                 ; "PALACE "
      DB $13,$0F,$06,$14,$17,$01,$12,$05,$20         ; "SOFTWARE "
      DB $0C,$14,$04,$2E                             ; "LTD."
      DB $FF,$B6                                     ; Schreibzeiger +54
      DB $02,$19,$20                                 ; "BY "
      DB $14,$05,$0C,$05,$03,$0F,$0D,$13,$0F,$06,$14 ; "TELECOMSOFT"
      DB $FF,$82                                     ; Schreibzeiger +2
      DB $13,$09,$0C,$16,$05,$12,$02,$09,$12,$04     ; "SILVERBIRD"
      DB $FF,$FD                                     ; Schreibzeiger +115
      DB $FF,$FD                                     ; Schreibzeiger +115
      DB $FF,$FD                                     ; Schreibzeiger +115
      DB $FF,$C8                                     ; Schreibzeiger +72
      DB $FF,$FF                                     ; Ende der Meldung

T401: DB $0E,$0F,$17,$20                             ; "NOW "
      DB $0C,$0F,$01,$04,$09,$0E,$07,$2C,$20         ; "LOADING, "
      DB $10,$0C,$05,$01,$13,$05,$20                 ; "PLEASE "
      DB $17,$01,$09,$14,$2E,$20                     ; "WAIT. "
      DB $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20 ; "           "
      DB $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20 ; "           "
      DB $20,$20,$20,$20,$20,$20,$20,$20,$20,$20,$20 ; "           "
      DB $FF,$FF                                     ; Ende der Meldung

Das folgende Programmsegment ist der letzte Block, der mit dem dritten Schnelllader von Band gelesen wird und einen Teil der Laderoutine überschreibt. Es modifiziert zunächst noch geringfügig den zweiten Schnelllader, indem es die bisherigen Ladeanzeige ("CYBERLOAD NOW LOADING ...") durch einen in allen Farben flackernden Bildschirm ersetzt. Anschließend übergibt die Routine die Kontrolle wieder an den zweiten Schnelllader, damit dieser noch den Startcode des Programms von Band liest.

PRG $E297 ; DF17

      LDA #>[P_EA-$01] ; Sprung nach P_EA durch RTS an CC14 vorbereiten
      PHA
      LDA #<[P_EA-$01]
      PHA
; Statt Animation an BC02 Bildschirmrahmenfarbe hochzählen
      LDA #$EE         ; Opcode für "INC abs"
      STA BC02+$00
      LDA #<$D020      ; Adresse des VIC-Register für Rahmenfarbe, Low-Byte
      STA BC02+$01
      LDA #>$D020      ; Adresse des VIC-Register für Rahmenfarbe, High-Byte
      STA BC02+$02
      LDA #$60         ; Opcode für "RTS"
      STA BC02+$03
      LDA $D015        ; Sprite 7 abschalten
      AND #$7F
      STA $D015
      JMP CC06         ; Letzten Programmteil laden

Start des Hauptprogramms[Bearbeiten | Quelltext bearbeiten]

Die Startroutine löscht noch die Interruptmasken des VIC und der CIAs und sowie eventuell ausstehende Interruptanforderungen. Anschließend blendet sie alle ROMs wieder in den Adressraum ein und stellt damit die Standardkonfiguration wieder her, schaltet noch den Bildschirm wieder ein und springt schließlich zur Startadresse des geladenen Programms.

PRG $0400

P_EA: SEI              ; Interrupts deaktivieren
      LDA #$FF         ; Alle Interruptanforderungen löschen
      STA $D019
      LDA #$00         ; Interruptmaske löschen
      STA $D01A
      LDA #$00
      STA $DC0E        ; CIA 1 Timer A stoppen
      STA $DD0E        ; CIA 2 Timer A stoppen
      LDA #$7F
      STA $DC0D        ; Keine Interrupts von CIA 1
      STA $DD0D        ; Keine Interrupts von CIA 2
      LDA $DC0D        ; Interruptanforderungen von CIA 1 löschen
      LDA $DD0D        ; Interruptanforderungen von CIA 2 löschen
      LDA #$37         ; I/O und alle ROMs einblenden
      STA *$01
      LDA #$1B         ; Bildschirm einschalten
      STA $D011
      JMP $8009        ; Spiel starten