Paradroid/Schnelllader

Aus C64-Wiki
Zur Navigation springenZur Suche springen

<< zurück zu Paradroid


Paradroid/Schnelllader: Die folgenden Abschnitte stellen den disassemblierten Kassetten-Schnelllader des Spiels Paradroid (Competition Edition) dar. Sie sind gegliedert in einzelne Codeblöcke, die gemäß ihrer Funktion während des Ladens gruppiert sind.


Dateiname und Interruptroutine des Schnellladers[Bearbeiten | Quelltext bearbeiten]

Direkt hinter dem Dateinamen findet sich im allerersten Block auf Kassette die Interruptroutine des Schnellladers. Diese liest ein einzelnes Bit von Kassette und schiebt dieses von rechts in die Speicherzelle $00A9. Falls es Zeit ist, den Inhalt dieser Speicherzelle zu verarbeiten (während der Synchronisation nach jedem Bit, später nach jedem 8. Bit), so zeigt der BCC-Befehl bei AA00 jeweils auf den hierfür aktuell notwendigen Codeabschnitt.

ORG $033C
      DB $03   ; Code für "Absolut zu ladendes Programm"
      DW $02A7 ; Startadresse
      DW $0334 ; Endadresse+1 (wird nicht erreicht)

      DB $05   ; Code für "weiß"
      DB $0E   ; Code für "Groß-/Kleinbuchstaben"
      DB "Paradroid.   "
      DB $9A   ; Code für "hellblau"

; IRQ-Routine des Schnellladers
P_AA: PHA          ; Akku retten
      TYA
      PHA          ; Y-Register retten
      LDA $DC05    ; Timerstand CIA1 Timer holen (High-Byte)
      LDY #$19     ; CIA1 Timer A neu laden und starten (one shot)
      STY $DC0E
      EOR #$02     ; Bit 9 des Timerstands von CIA1 Timer A invertieren
      LSR A        ; in CF übertragen (Timerstand>=512: 0-Bit, <512: 1-Bit)
      LSR A
      ROL *$A9     ; und von rechts in das empfangene Byte schieben
      LDA *$A9     ; Empfangenes Byte nach A holen
AA00: BCC AA01     ; Zentraler Sprungverteiler, falls 0-Bit herausgeschoben
      BCS AA03     ; sonst zum Ende der Interruptroutine
; 1. Einsprung (Suche nach dem ersten Synchronisationszeichen)
AA01: CMP #$40     ; Synchronisationszeichen $40 empfangen?
      BNE AA03     ; zum Ende der Interruptroutine falls nicht
      LDA #AA04-AA00-$02
      STA AA00+$01 ; sonst nächstes Byte bei 2. Einsprung verarbeiten lassen
AA02: LDA #$FE     ; Bitzähler %11111110, immer 8 Bit einlesen
      STA *$A9     ; als empfangenes Byte setzen
AA03: LDA $DC0D    ; Interruptanforderung löschen
      PLA          ; Y-Register zurückholen
      TAY
      PLA          ; Akku zurückholen
      RTI          ; Rückkehr aus Interrupt
; 2. Einsprung: Suche nach dem 2. Synchronisationszeichen
AA04: CMP #$40     ; Weiteres Synchronisationszeichen $40 empfangen?
      BEQ AA02     ; dann überlesen
      CMP #$5A     ; Nächstes Synchronisationszeichen $5A empfangen?
      BEQ AA05     ; dann Synchronisation erfolgreich
      BNE AA10     ; sonst Synchronisation neu starten
AA05: LDA #AA06-AA00-$02
      STA AA00+$01 ; Nächstes Byte bei 3. Einsprung verarbeiten lassen
      LDA #$00     ; Prüfsumme für empfangene Bytes initialisieren
      STA *$C1
      BEQ AA02     ; Bitzähler initialisieren und Rückkehr aus Interrupt
; 3. Einsprung: Header einlesen und in Speicher schreiben
AA06: STA *$FB     ; Empfangenes Headerbyte an $FB...$FE speichern
      INC AA06+$01 ; Schreibzeiger für Headerbytes weiterzählen
      LDA AA06+$01 ; und nach A holen
      CMP #$FF     ; Schon alle Headerbytes empfangen?
      BNE AA02     ; Bitzähler initialisieren und Rückkehr aus Interrupt falls nicht
      LDA #AA07-AA00-$02
      STA AA00+$01 ; sonst nächstes Byte bei 4. Einsprung verarbeiten lassen
      BNE AA02     ; Bitzähler initialisieren und Rückkehr aus Interrupt
; 4. Einsprung: Datenblock einlesen und in Speicher schreiben
AA07: LDY #$00     ; Schreibindex für empfangene Bytes initialisieren
      STA ($FB),Y  ; Empfangenes Byte in Speicher schreiben
      EOR *$C1     ; und in Prüfsumme einarbeiten
      STA *$C1
      INC *$FB     ; Schreibzeiger erhöhen, Low-Byte
      BNE AA08     ; Sprung falls kein Überlauf
      INC *$FC     ; Schreibzeiger erhöhen, High-Byte
AA08: LDA *$FB     ; Schreibzeiger mit Endadresse vergleichen, Low-Byte
      CMP *$FD
      LDA *$FC     ; Schreibzeiger mit Endadresse vergleichen, High-Byte
      SBC *$FE
      BCC AA02     ; Sprung falls Endadresse noch nicht erreicht
      LDA #AA09-AA00-$02
      STA AA00+$01 ; sonst nächstes byte bei 5. Einsprung verarbeiten lassen
      BNE AA02     ; Unbedingter Sprung
; 5. Einsprung: Prüfsumme merken und Flag für "Block gelesen" setzen
AA09: STA *$C2     ; Empfangenes Byte (Prüfsumme) merken
      LDA #$FF     ; Flag für "Block gelesen" setzen
      STA *$02
      LDA #$FB     ; Schreibzeiger für nächsten Header initialisieren
      STA AA06+$01
AA10: LDA #AA01-AA00-$02
      STA AA00+$01 ; Nächste Resynchronisation vorbereiten
      BNE AA02     ; Bitzähler initialisieren und Rückkehr aus Interrupt

; Auf Blockende warten, dann Checksumme prüfen und Ladebildschirm starten
P_AB: CLI
      LDA #$58     ; Opcode für "CLI"
      STA BA00     ; anstelle von "NOP"
      LDA #$1B     ; Bildschirm einschalten
      STA $D011
AB00: LDA #$F8     ; Farbcode für "orange"
      STA $D020    ; als Rahmenfarbe setzen
      LDA *$02     ; Flag für "Vollständigen Block empfangen" (zunächst $00, Farbcode für "schwarz")
      STA $D020    ; als Rahmenfarbe setzen
      BEQ AB00     ; Rücksprung falls Block noch nicht vollständig empfangen
      JSR P_BB     ; Checksumme prüfen
      LDA #$00     ; Sprung nach $E000 vorbereiten
      STA BA01+$01
      LDA #$E0
      STA BA01+$02
      JMP P_BA     ; Ladebildschirm starten


Initialisierung[Bearbeiten | Quelltext bearbeiten]

Der folgende Programmteil wird beim Laden von Paradroid von Kassette gelesen. Dieser Vorgang überschreibt den IRQ-Vektor und setzt ihn auf seinen Standardwert $EA31 zurück, was vom Kernal als Ende der I/O-Operation interpretiert wird (an Adresse $F8BE). Über den auf die Initialisierungsroutine P_BA umgebogenen Vektor für die Eingabe einer Zeile startet dann der Schnelllader.

PRG $02A7

; Initialisierung der I/O-Bausteine
P_BA: SEI        ; IRQs verbieten
      LDA #$05   ; Alle ROMs ausblenden
      STA *$01
      LDA #$1F
      STA $DC0D  ; Alle IRQs von CIA1 abschalten
      LDA $DC0D  ; Eventuell wartende IRQs von CIA1 löschen
      LDA #$68   ; Startwert von CIA1 Timer A auf $0368 (912) setzen
      STA $DC04  ; Low-Byte
      LDA #$03
      STA $DC05  ; High-Byte
      LDA #$90   ; Signal an CIA1 Pin FLAG löst IRQ aus
      STA $DC0D
      LDA #<P_AA ; IRQ-Vektor
      STA $FFFE  ; Low-Byte setzen
      LDA #>P_AA ; IRQ-Vektor
      STA $FFFF  ; High-Byte setzen
      LDA #$00
      STA *$02   ; Flag für "Block empfangen" löschen
      STA *$A9   ; Bitzähler %00000000, empfangenes Byte nach jedem Bit prüfen
BA00: NOP        ; Wird im zweiten Durchlauf durch "CLI" ersetzt
BA01: JMP P_BC   ; Wird im zweiten Durchlauf durch "JMP $E000" ersetzt

; Checksumme prüfen
P_BB: LDA #$07   ; Alle ROMs einblenden
      STA *$01
      LDA *$C1   ; Errechnete Checksumme nach A holen
      CMP *$C2   ; und mit von Band gelesener Prüfsumme vergleichen
      BNE BB00   ; Sprung falls Prüfsummenfehler
      RTS        ; sonst Rückkehr aus Prüfroutine
BB00: JMP $FCE2  ; Reset

; Sprung nach P_AB per NMI
P_BC: LDA #<P_AB ; NMI-Vektor
      STA $FFFA  ; Low-Byte setzen
      LDA #>P_AB ; NMI-Vektor
      STA $FFFB  ; High-Byte setzen
      LDA #$01   ; Startwert von CIA2 Timer A auf $01xx (256..511) setzen
      STA $DD05  ; High-Byte
      LDA #$81   ; Unterlauf von CIA2 Timer A löst NMI aus
      STA $DD0D
      LDA #$99   ; CIA2 Timer A laden und starten (one-shot)
      STA $DD0E
BC00: BNE BC00   ; Endlosschleife, die durch NMI von CIA2 verlassen wird

T000: DW  $E38B  ; Vektor für BASIC-Warmstart
      DW  P_BA   ; Vektor für Eingabe einer Zeile
      DW  $A57C  ; Vektor für Umwandlung in Interpretercode
      DW  $A71A  ; Vektor für Umwandlung in Klartext (LIST)
      DW  $A7E4  ; Vektor für BASIC-Befehlsadresse holen
      DW  $AE86  ; Vektor für Ausdruck auswerten
      DB  $00    ; Akku für SYS-Befehl
      DB  $51    ; X-Reg für SYS-Befehl
      DB  $03    ; Y-Reg für SYS-Befehl
      DB  $30    ; Status-Register für SYS-Befehl
      DB  $4C    ; JMP-Befehl für USR-Funktion
      DW  $B248  ; USR-Vektor
      DB  $00
      DW  $EA31  ; IRQ-Vektor