CyberLoad/Quellcode
<< 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