Bozo's Night Out/Schnelllader

Aus C64-Wiki
Zur Navigation springenZur Suche springen

<< zurück zu Bozo's Night Out


Bozo's Night Out/Schnelllader: Die folgenden Abschnitte stellen den disassemblierten Kassetten-Schnelllader des Spiels Bozo's Night Out dar. Sie sind gegliedert in einzelne Codeblöcke, die gemäß ihrer Funktion während und nach dem Laden gruppiert sind.

Startblock der Datassetten-Datei[Bearbeiten | Quelltext bearbeiten]

Der folgende Abschnitt ist der erste Block auf der Kassette, sollte also nur den Dateityp, die Anfangs- und Endadresse sowie den Namen des Spiels enthalten. In den darauffolgenden, üblicherweise nicht genutzten 171 Bytes findet sich aber bereits ein großer Teil des Code für den Schnelllader.

ORG $033C

      DB $03       ; Code für "Absolut zu ladende Datassetten-Datei"
      DW P_BA      ; Anfangsadresse
      DW $032A     ; Endadresse+1
      DB 'B'       ; Dateiname
      DB $94       ; Code für "Insert"
      DB "              "

; Ein Bit von Band lesen
P_AA: LDA #$10
AA00: BIT $DC0D    ; Auf Bit von Band warten
      BEQ AA00
      INC $D020    ; Bildschirmfarbe hochzählen
      LDA #$99     ; CIA2 Timer A neu laden und starten (one-shot)
      STA $DD0E
      LSR $DD0D    ; Flag für "Timer A abgelaufen" löschen
AA01: LSR $DD0D    ; Auf Ablauf von Timer A warten
      BCC AA01
      LDA $DC0D    ; Prüfen, ob in der Zwischenzeit ein weiteres Bit von Band gelesen wurde
      AND #$10
      BNE AA02     ; Sprung falls ja (dann CF=1 lassen), 1-Bit empfangen
      CLC          ; sonst CF=0 setzen, 0-Bit empfangen
AA02: RTS          ; Rückkehr

; Initialisierung und Hauptschleife des Schnellladers
P_AB: SEI
      LDA #$C1     ; NMI-Vektor auf "RTI" umbiegen
      STA $0318    ; Low-Byte
      LDA #$FE
      STA $0319    ; High-Byte
      LDA #$8B     ; Bildschirm abschalten
      STA $D011
      LDA #$00
      STA *$9E     ; Prüfsumme initialisieren
      STA $DD05    ; Startwert CIA2 Timer 1 auf 120 setzen: High-Byte=0
      LDA #$78
      STA $DD04    ; Startwert CIA2 Timer 1 auf 120 setzen: Low-Byte=120
      LDA #$81
      STA $DD0E    ; CIA2 Timer A starten (fortlaufend)
      LDA #$01     ; Startwert CIA2 Timer 1 auf 330 setzen
      STA $DD05    ; High-Byte
      LDA #$4A
      STA $DD04    ; Low-Byte
AB00: LDA #$64     ; Zähler für Synchronisation (100 0-Bits) initialisieren
      STA *$92
AB01: JSR P_AA     ; Ein Bit von Band lesen
      BCS AB00     ; Warten auf 0-Bit
      DEC *$92     ; Anzahl 0-Bits herunterzählen
      BNE AB01     ; Rücksprung falls noch nicht 100 0-Bits
AB02: JSR P_AA     ; Ein Bit von Band lesen
      BCC AB02     ; Weitere 0-Bits überlesen
      JSR P_BA     ; Ein Byte von Band lesen
      STA *$C3     ; und als Startadresse im Schreibzeiger speichern (Low-Byte)
      JSR P_BA     ; Ein Byte von Band lesen
      STA *$C4     ; und als Startadresse im Schreibzeiger speichern (High-Byte)
      JSR P_BA     ; Ein Byte von Band lesen
      STA *$AE     ; und als Endadresse+1 speichern (Low-Byte)
      JSR P_BA     ; Ein Byte von Band lesen
      STA *$AF     ; und als Endadresse+1 speichern (High-Byte)
AB03: LDX *$C4     ; Aktuellen Schreibzeiger nach X holen (High-Byte)
      CPX *$AF     ; und mit Endadresse+1 vergleichen (High-Byte)
      BNE AB04     ; Weiter laden falls ungleich
      LDX *$C3     ; Aktuellen Schreibzeiger nach X holen (Low-Byte)
      CPX *$AE     ; und mit Endadresse+1 vergleichen (Low-Byte)
      BEQ AB05     ; Zum Abschluss der Laderoutine springen falls gleich
AB04: JSR P_BA     ; Ein Byte von Band lesen
      TAX          ; und nach X retten
      SEC          ; Gelesenes Byte zur Prüfsumme addieren
      ADC *$9E
      STA *$9E
      TXA          ; Gelesenes Byte zurückholen
      LDY #$00     ; Schreibindex initialisieren
      STA ($C3),Y  ; und gelesenes Byte in Speicher schreiben
      INC *$C3     ; Schreibzeiger erhöhen (Low-Byte)
      BNE AB03     ; Rücksprung falls kein Übertrag
      INC *$C4     ; Schreibzeiger erhöhen (High-Byte)
      JMP AB03     ; Weiter laden
AB05: JSR P_BA     ; Ein Byte von Band lesen (Prüfsumme)
      CMP *$9E     ; und mit errechneter Prüfsumme vergleichen
      BNE AB07     ; Sprung falls Prüfsummenfehler
AB06: JMP $0400    ; Nachgeladene Erweiterung des Schnellladers starten
AB07: JMP $FCE2    ; Bei Prüfsummenfehler Reset auslösen

; Alle ROMs einblenden und Schnelllader starten
P_AC: LDA #$07     ; Alle ROMs einblenden
      STA *$01
      JMP P_AB     ; Sprung zum Start des Schnellladers

; Füllbytes ohne Funktion
      DB $00,$00,$20

Ausgelagerter Code und Autostart[Bearbeiten | Quelltext bearbeiten]

Der Programmblock, der beim Laden von Bozo's Night Out nachgeladen wird, überschreibt zum einen den OUTPUT-Vektor des Kernals mit der Startadresse des Schnellladers und bewirkt damit, dass dieser unmittelbar nach dem Laden gestartet wird. Zum anderen enthält dieser Programmblock eine ausgelagerte Routine für das Lesen eines Bytes von Band, die offensichtlich im ersten Datenblock auf der Kassette keinen Platz mehr gefunden hat.

PRG $031B

; Ein Byte von Band lesen und nach A und Y
P_BA: LDY #$01     ; Bitzähler initialisieren
BA00: JSR P_AA     ; Ein Bit von Band lesen
      TYA          ; Bitzähler nach A holen
      ROL A        ; und gelesenes Bit von rechts hineinschieben
      TAY          ; Bitzähler zurück nach Y
      BCC BA00     ; Rücksprung, falls noch nicht 8 Bit von Band gelesen
      RTS          ; Rückkehr mit gelesenem Byte in A und Y

      DW P_AC      ; OUTPUT-Vektor, umgeboten auf Start des Schnellladers
      DW $F6ED     ; STOP-Vektor

Einbindung des Schnellloaders in LOAD-Routine des Kernals[Bearbeiten | Quelltext bearbeiten]

Der Schnelllader liest zunächst nur das folgenden Codesegment von Band und führt dieses dann aus. In diesem Codeabschnitt werden dann alle zuvor überschriebenen Sprungvektoren wiederhergestellt, bevor sich der Schnelllader in die Laderoutine des Kernals einklinkt. Auf diese Weise kann er die BASIC-Routinen nutzen, um Bozo's Night Out zu starten: Löschen des Hauptspeichers mittels NEW, und anschließendes Laden und Starten des Spiels mit Hilfe eines im Tastaturpuffer abgelegten Tastendrucks auf "LOAD+RUN" (PETSCII $83).

PRG $0400

P_CA: LDY #$0D     ; 13+1=14 Bytes umkopieren
CA00: LDA $FD37,Y  ; Hardware- und I/O-Vektoren
      STA $031B,Y  ; wiederherstellen
      DEY          ; Schreibindex vermindern
      BPL CA00     ; Rücksprung falls noch nicht alle Bytes umkopiert
      LDY #$2D     ; 45+1=46 Bytes umkopieren
CA01: LDA T001,Y   ; Neue Schnellade-Routinen byteweise lesen
      STA $02A7,Y  ; und umkopieren
      DEY          ; Schreibindex vermindern
      BPL CA01     ; Rücksprung falls noch nicht alle Bytes umkopiert
      LDA #>P_AC   ; LOAD-Vektor auf Schnelllader umbiegen
      STA $0331    ; High-Byte
      LDA #<P_AC
      STA $0330    ; Low-Byte
      LDX #$05     ; 5+1=6 Unterprogrammaufrufe patchen
CA02: LDY T000,X   ; Aufruf "Ein Byte von Band lesen" auf P_CC patchen
      LDA #<P_CC   ; Low-Byte
      STA $0300,Y
      LDA #>P_CC   ; High-Byte
      STA $0301,Y
      DEX          ; Schreibindex vermindern
      BPL CA02     ; Rücksprung falls noch nicht alle Bytes umkopiert
      LDX #$F1     ; STOP-Vektor verbiegen und unwirksam machen
      LDA #$B1
      STX $0329    ; High-Byte
      STA $0328    ; Low-Byte
      LDA #<P_CB   ; Sprung zum simulierten Abschluss des ladevorgangs vorbereiten
      STA AB06+$01 ; Low-Byte
      LDA #>P_CB
      STA AB06+$02 ; High-Byte
      LDA #$02     ; 2 Tasten im Tastaturpuffer
      STA *$C6
      LDA #$93     ; PETSCII-Code für "Clear"
      STA $0277    ; als erstes Zeichen im Tastaturpuffer
      LDA #$83     ; PETSCII-Code für "LOAD+RUN"
      STA $0278    ; als zweites Zeichen im Tastaturpuffer
      JMP $A644    ; BASIC-Befehl NEW

; Patch-Tabelle (Adressen der Aufrufe von "Ein Byte von Band lesen" relativ zu $0300)
T000: DB $AF,$B4,$B9,$BE,$CF,$E6

T001:

PRG $02A7

; Abschluss des Ladevorgangs für Kernal simulieren
P_CB: LDA *$01    ; Datassettenmotor ausschalten
      ORA #$20
      STA *$01
      LDA #$07    ; Flag für Bandmotor setzen
      STA *$C0
      LDA $D011   ; Bildschirm einschalten
      ORA #$10
      STA $D011
      LDY #$00    ; CIA2 Timer A stoppen
      STY $DD0E
      CLI         ; Interrupts wieder zulassen
      CLC         ; Zeichen für "Kein Fehler"
      LDX *$AE    ; Endadresse+1 des Programms, Low-Byte
      LDY *$AF    ; Endadresse+1 des Programms, High-Byte
      RTS         ; Rückkehr aus Laderoutine

; Ein Byte von Band lesen und nach A und Y
P_CC: LDY #$01    ; Bitzähler initialisieren
CC00: JSR P_AA    ; Ein Bit von Band lesen
      TYA         ; Bitzähler nach A holen
      ROL A       ; und gelesenes Bit von rechts hineinschieben
      TAY         ; Bitzähler zurück nach Y
      BCC CC00    ; Rücksprung, falls noch nicht 8 Bit von Band gelesen
      STA $D020   ; Gelesenes Byte als Bildschirmfarbe setzen
      RTS         ; Rückkehr mit gelesenem Byte in A und Y