Alien Syndrome/Schnelllader

Aus C64-Wiki
Zur Navigation springenZur Suche springen

<< zurück zu Alien Syndrome


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

Floppy-seitige Schnelllade-Routinen[Bearbeiten | Quelltext bearbeiten]

Die folgenden Routine P_AA liest den nachzuladenden Programmteil Sektor für Sektor in Puffer 0 (ab Adresse $0300) ein und folgt dabei den Verkettungszeigern in den ersten beiden Bytes jedes Sektors. Aus dem Sektor werden, jeweils beginnend ab Offset 2, alle gültigen Datenbytes an den C64 gesandt. Die Übertragung eines Bytes übernimmt hierbei die Routine P_AB.

DEVICE EQU $08 ; Geräteadresse 8

SOURCE1:

P_AA: LDA #$08    ; CLOCK low
      STA $1800
      LDA #$C8    ; Standardwert setzen
      STA *$64    ; für Zahl der Halbspurschritte bis Spur 0
      LDA #$20    ; Rate des Jobschleifen-Interrupt erhöhen (alle 8 ms statt alle 14 ms)
      STA $1C07   ; VIA 2 Timer 1 High-Byte
      LDA *$18    ; Spur des zuletzt gelesenen Blocks
      LDX *$19    ; Sektor des zuletzt gelesenen Blocks
      STA *$06    ; als Spur von Puffer 0 merken
      STX *$07    ; als Sektor von Puffer 0 merken
AA00: CLI         ; Interrupts erlauben (Jobschleife aktivieren)
      LDY #$0F    ; Anzahl Leseversuche
AA01: LDA #$80    ; Jobcode "Lesen eines Sektors"
      STA *$00    ; als Job für Puffer 0 merken
AA02: LDA *$00    ; Warten bis Job ausgeführt
      BMI AA02
      CMP #$01    ; Kein Fehler?
      BEQ AA03    ; Sprung wenn kein Fehler
      DEY         ; Anzahl Leseversuche erniedrigen
      BNE AA01    ; Sprung falls noch nicht alle Leseversuche
      JSR $D042   ; BAM in Puffer lesen
      JMP $EB22   ; Zeropage initialisieren
AA03: SEI         ; Interrupts verbieten (Jobschleife deaktivieren)
      LDA #$00    ; Alle Blöcke außer dem letzten enthalten 256 Bytes
      LDX $0301   ; Sektornummer des nächsten Blocks
      STX *$07    ; als Sektor von Puffer 0 merken
      LDY $0300   ; Spurnummer des nächsten Blocks
      STY *$06    ; als Spur von Puffer 0 merken
      BNE AA04    ; Sprung wenn nicht letzter Block
      INX         ; Anzahl gültiger Bytes im letzten Block
      TXA         ; nach A
AA04: STA *$37    ; Anzahl gültiger Bytes im aktuellen Block merken
      LDA #$00    ; CLOCK high
      STA $1800
      LDY #$02    ; Datenübertragung ab Position 2 beginnen
AA05: LDA $0300,Y ; Nächstes Datenbyte aus Puffer holen
      CMP #$DF    ; Wert $DF (dient als End of Block/End of File)?
      BNE AA06    ; Sprung wenn nicht $DF
      JSR P_AB    ; $DF übertragen
      LDA #$DF    ; Bytestuffing, $DF nochmals
AA06: JSR P_AB    ; Datenbyte übertragen
      INY         ; Schreibzeiger erhöhen
      CPY *$37    ; Schon alle gültigen Bytes im Puffer übertragen?
      BNE AA05    ; Sprung falls nicht alle Bytes übertragen
      LDA $0300   ; Spur des nächsten Blocks gleich 0, also letzter Block?
      BEQ AA07    ; Sprung falls letzter Block
      LDA #$DF    ; $DF leitet Endekennzeichen ein
      JSR P_AB    ; übertragen
      LDA #$00    ; $00 als Blockendekennzeichen ($DF $00)
      JSR P_AB    ; übertragen
      LDA #$08    ; CLOCK low
      STA $1800
      JMP AA00    ; Nächsten Block übertragen
AA07: LDA #$DF    ; $DF leitet Endekennzeichen ein
      JSR P_AB    ; übertragen
      LDA #$FF    ; $FF als Dateiendekennzeichen ($DF $FF)
      JSR P_AB    ; übertragen
      JMP $EB22   ; Zeropage initialisieren

; Byte in A auf DATA senden, Synchronisation durch Pegelwechsel auf CLOCK
P_AB: STA *$14    ; Datenbyte merken
      LDA #$04    ; Bitmaske für CLOCK in
      JSR AB00    ; Bit 0 und 1 senden
      JSR AB00    ; Bit 2 und 3 senden
      JSR AB00    ; Bit 4 und 5 senden
AB00: LSR *$14    ; Niederwertigstes Bit nach CF
      LDX #$02    ; DATA high
      BCC AB01    ; Sprung falls Bit in CF gelöscht
      LDX #$00    ; DATA low
AB01: BIT $1800   ; Warten auf CLOCK high
      BNE AB01
      STX $1800   ; Bit auf DATA senden
      LSR *$14    ; Nächstes Bit nach CF
      LDX #$02    ; DATA high
      BCC AB02    ; Sprung falls Bit in CF gelöscht
      LDX #$00    ; DATA low
AB02: BIT $1800   ; Warten auf CLOCK low
      BEQ AB02
      STX $1800   ; Bit auf DATA senden
      RTS

Startroutinen[Bearbeiten | Quelltext bearbeiten]

Die nachfolgende Routine P_AC überträgt die Floppy-seitigen Schnelllade-Routinen mittels des "M-W"-Befehls ("Memory-Write") in Abschnitten zu jeweils 29 Bytes in das RAM der Floppy ab Adresse TARGET1=$0400. Anschließend wird der Schnelllader dort mittels "M-E" ("Memory-Execute") gestartet. Da beide Floppy-Befehle mit "M-" beginnen, übernimmt die Unterroutine P_AD das Versenden dieses Präfixes.

; Floppy-seitige Schnelllade-Routine in Floppy-Speicher schreiben und dort starten
P_AC: LDY #<TARGET1 ; Low-Byte Schreibzeiger im Floppy-RAM
AC00: JSR P_AD      ; Befehlskanal öffnen und Präfix "M-" senden
      LDA #$57      ; 'W' für "M-W", "Memory-Write"
      JSR $EDDD     ; ... auf IEC-Bus ausgeben (IECOUT)
      TYA           ; Low-Byte Schreibzeiger im Floppy-RAM
      JSR $EDDD     ; ... auf IEC-Bus ausgeben (IECOUT)
      LDA #>TARGET1 ; High-Byte Schreibzeiger im Floppy-RAM
      JSR $EDDD     ; ... auf IEC-Bus ausgeben (IECOUT)
      LDA #29       ; Blockgröße
      TAX           ; als Bytezähler nach X
      JSR $EDDD     ; ... auf IEC-Bus ausgeben (IECOUT)
AC01: LDA SOURCE1,Y ; Floppy-seitige Schnelllade-Routine byteweise lesen
      JSR $EDDD     ; und an Floppy senden
      INY           ; Schreibzeiger erhöhen
      DEX           ; Bytezähler erniedrigen
      BNE AC01      ; Rücksprung falls noch nicht ganzer Block übertragen
      JSR $EDFE     ; UNLISTEN senden
      CPY #$AE      ; Schon 6*29=174 Bytes gesendet?
      BNE AC00      ; Rücksprung falls noch nicht alle Bytes gesendet
      JSR P_AD      ; Befehlskanal öffnen und "M-" senden
      LDA #$45      ; 'E' für "M-E", "Memory-Execute"
      JSR $EDDD     ; ... auf IEC-Bus ausgeben (IECOUT)
      LDA #<TARGET1 ; Low-Byte der Startadresse im Floppyspeicher
      JSR $EDDD     ; ... auf IEC-Bus ausgeben (IECOUT)
      LDA #>TARGET1 ; High-Byte der Startadresse im Floppyspeicher
      JSR $EDDD     ; ... auf IEC-Bus ausgeben (IECOUT)
      JSR $EDFE     ; UNLISTEN senden
      LDA #$03      ; CLOCK und DATA
      AND $DD00
      ORA #$04
      STA $DD00     ; auf High setzen
      JSR $4711     ; Programmteil laden
      LDA #DEVICE   ; Geräteadresse
      JMP $F291     ; CLOSE

; Befehlskanal öffnen und Präfix "M-" senden
P_AD: LDA #DEVICE   ; Geräteadresse #8
      JSR $ED0C     ; LISTEN senden
      LDA #$6F      ; Sekundäradresse #15 (Befehlskanal)
      JSR $EDB9     ; Sekundäradresse nach LISTEN senden
      LDA #$4D      ; 'M'
      JSR $EDDD     ; ... auf IEC-Bus ausgeben (IECOUT)
      LDA #$2D      ; '-'
      JMP $EDDD     ; ... auf IEC-Bus ausgeben (IECOUT)

C64-seitige Schnelllade-Routinen[Bearbeiten | Quelltext bearbeiten]

Falls die Floppy bereit zum Senden ist (erkennbar an CLOCK=high), fordert die C64-seitigen Schnelllade-Routine P_AG mit 8 aufeinanderfolgenden Pegelwechseln auf der CLOCK-Leitung jeweils die Übertragung eines Bits an. Damit die Floppy das Dateiende signalisieren oder die Übertragung anhalten kann (um den nächsten Sektor von Diskette zu lesen), hat das Datenbyte $DF eine Sonderfunktion: Der Zwei-Byte-Code $DF $FF signalisiert das Dateiende, der Code $DF $00 hält die Übertragung an. Da beide Codes auch zufällig im Datenstrom des geladenen Programmteils vorkommen können, wird jedes Datenbyte $DF als Zwei-Byte-Code $DF $DF übertragen (Bytestuffing).

; Ein Datenbyte von der Floppy in <A> einlesen
P_AE: PHP           ; Flags und Indexregister retten
      TXA
      PHA
      TYA
      PHA
      LDA *$90      ; Dateiende erreicht?
      BNE AE02      ; Sprung falls Dateiende
AE00: JSR P_AF      ; Ein Byte von der Floppy einlesen und in <A> sowie an Adresse $A5 speichern
      CMP #$DF      ; Sonderzeichen $DF?
      BNE AE02      ; Fertig falls nicht Sonderzeichen $DF
      JSR P_AF      ; Sonst nächstes Byte von der Floppy einlesen und in <A> sowie an Adresse $A5 speichern
      CMP #$DF      ; Nochmals $DF?
      BEQ AE02      ; Ja, dann Datenbyte $DF zurückliefern (Bytestuffing)
      CMP #$FF      ; Endekennzeichen $FF?
      BEQ AE01      ; Ja, dann als Status merken
      JSR P_AG      ; Nein, Nach kurzer Verzögerung auf CLOCK high warten
      JMP AE00      ; Erneut ein Byte von Floppy einlesen
AE01: STA *$90
AE02: PLA           ; Indexregister und Flags zurückholen
      TAY
      PLA
      TAX
      PLP
      LDA *$A5      ; Gelesenes Datenbyte nach A holen
      RTS

; Ein Byte von der Floppy einlesen und in <A> sowie an Adresse $A5 speichern
P_AF: LDY #$08      ; Bitzähler
AF00: LDA $DD00     ; Zustand der IEC-Leitungen
      EOR #$50      ; CLOCK invertieren
      TAX
      LDA $DD00     ; Zustand der IEC-Leitungen
      STX $DD00     ; Pegelwechsel auf CLOCK
      ASL A         ; Wert von DATA ins CF
      ROR *$A5      ; und als empfangenes Bit speichern
      DEY           ; Bitzähler vermindern
      BNE AF00      ; Rücksprung falls noch nicht 8 Bits empfangen
      LDA *$A5      ; Empfangenes Byte nach A holen
      DEC $D020     ; Bildschirmrand kurz blinken lassen
      INC $D020
      RTS

; Nach kurzer Verzögerung auf CLOCK high warten (als Zeichen für "Floppy bereit zum Senden"), dann CLOCK und DATA low
P_AG: LDY #$2F      ; Verzögerung
AG00: DEY
      BNE AG00
AG01: LDA $DD00     ; Zustand der IEC-Leitungen
      AND #$40      ; CLOCK prüfen
      BEQ AG01      ; Rücksprung falls CLOCK low
AG02: LDA #$03
      AND $DD00
      ORA #$C4
      STA $DD00     ; CLOCK und DATA auf low
      LDY #$07      ; Verzögerung
AG03: DEY
      BNE AG03
      RTS