Bad Cat/Schnelllader

Aus C64-Wiki
Zur Navigation springenZur Suche springen

<< zurück zu Bad Cat


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

Startroutinen[Bearbeiten | Quelltext bearbeiten]

Die nachfolgende Routine P_AA überträgt die Floppy-seitigen Schnelllade-Routinen mittels des "M-W"-Befehls ("Memory-Write") in Abschnitten zu jeweils 34 Bytes in das RAM der Floppy ab Adresse TARGET1=$0400. Anschließend wird der Schnelllader dort mittels "M-E" ("Memory-Execute") gestartet, bevor der C64 die nachzuladende Datei zum Lesen öffnet und dann seine eigene Schnelllade-Routine aufruft. Da während des Transfers in das RAM der Floppy nur das High-Byte der Zieladresse überprüft wird, sendet die Routine nicht nur den 282 Byte langen Code, sondern unnötigerweise auch noch 230 Bytes der darauffolgenden Lookup-Table (insgesamt 512 Bytes).

Beim anschließenden Einlesen der Programmdaten wird für jedes Byte die Schnelllade-Routine P_AF aufgerufen und des empfangene Byte direkt an seine Ladeadresse geschrieben. Falls diese Schreibzugriffe die Page $CD erreichen, in der der Schnelllader gespeichert ist, und somit diesen zu überschreiben drohen, ist ein Wechsel zur normalen LOAD-Routine im ROM vorgesehen. Da die Hauptschleife des Schnellladers allerdings bereits bei Label AA11 an Adresse $CCF6 beginnt, brächte ein überlanges Programm den Schnelllader trotz dieses Schutzmechanismus zum Absturz.

DEVICE EQU $08 ; Geräteadresse 8
 
T000: DB $00,$00,$CD
T001: DB $22,$06,$00,'W-M'
T002: DB $04,$6C,'E-M'

P_AA: LDA *$B4          ; Inhalt von Adresse $B4
      STA T000+$00      ; retten
      LDA *$B5          ; Inhalt von Adresse $B5
      STA T000+$01      ; retten
      LDA $DD00         ; CIA2 Port A holen
      AND #$03          ; Bits für VIC-Bank isolieren
      STA AF02+$01      ; als Wert für "ATN high" speichern (Selbstmodifikation)
      ORA #$08          ; Bit für ATN setzen
      STA P_AF+$01      ; als Wert für "ATN low" speichern (Selbstmodifikation)
      LDA #$00          ; Alle Sprites abschalten, verhindert VIC-Zugriffe per DMA
      STA $D015
      LDA #$00
      STA *$90          ; Status zurücksetzen
      STA *$B5          ; ?
      LDA #$F0          ; Sekundäradresse 0
      JSR P_AD          ; LISTEN senden
      LDA *$90          ; Status holen
      BPL AA00          ; Sprung falls Gerät vorhanden
      JMP $F707         ; "?DEVICE NOT PRESENT ERROR"
AA00: LDY #$00          ; Lesezeiger für Dateinamen initialisieren
AA01: LDA ($BB),Y       ; Zeichen aus Dateinamen lesen
      JSR $EDDD         ; IECOUT
      INY               ; Lesezeiger erhöhen
      CPY *$B7          ; mit Länge des Dateinamens vergleichen
      BCC AA01          ; Rücksprung falls noch nicht ganzer Dateiname gesendet
      JSR $EDFE         ; UNLISTEN senden
      LDA #$00
      STA *$90          ; Status zurücksetzen
      JSR P_AE          ; TALK senden
      LDA #$60          ; Sekundäradresse
      JSR $EDC7         ; Sekundäradresse nach TALK ausgeben
      JSR $EE13         ; IECIN ein Zeichen vom IEC-Bus holen
      LDA *$90          ; Status holen
      PHA               ; und retten
      JSR $EDEF         ; UNTALK senden
      PLA               ; Status zurückholen
      AND #$02          ; Timeout?
      BEQ AA02          ; Sprung wenn kein Timeout
      JMP $F704         ; "?FILE NOT FOUND ERROR"
AA02: LDA #<SOURCE1     ; Low-Byte der Quelladresse des Floppy-seitigen Schnelladers
      LDY #>SOURCE1     ; High-Byte der Quelladresse des Floppy-seitigen Schnelladers
      STA *$AE          ; Low-Byte merken
      STY *$AF          ; High-Byte merken
      LDY #<TARGET1     ; Low-Byte der Startadresse im Floppy-Speicher
      LDA #>TARGET1     ; High-Byte der Startadresse im Floppy-Speicher
      STY T001+$02      ; Low-Byte in "Memory-Write" ("M-W")-Befehl aufnehmen
      STA T001+$01      ; High-Byte in "M-W"-Befehl aufnehmen
AA03: LDA #$6F          ; Sekundäradresse 15 (Befehlskanal)
      JSR P_AD          ; LISTEN senden
      LDX #$06          ; Länge des "M-W"-Befehls
AA04: LDA T001-$01,X    ; Befehl zeichenweise lesen
      JSR $FFA8         ; IECOUT
      DEX               ; Lesezeiger erniedrigen
      BNE AA04          ; Rücksprung falls noch nich alle Zeichen gesendet
AA05: LDA ($AE),Y       ; Floppy-seitigen Schnellader aus Speicher lesen
      JSR $FFA8         ; IECOUT
      INY               ; Schreibzeiger erhöhen
      BNE AA06          ; Sprung falls kein šbertrag
      INC *$AF          ; High-Byte der Leseadresse erhöhen
AA06: INC T001+$02      ; Low-Byte der Zieladresse im "M-W"-Befehl erhöhen
      BNE AA07          ; Sprung falls kein šbertrag
      INC T001+$01      ; High-Byte der Zieladresse im "M-W"-Befehl erhöhen
AA07: LDA T001+$01      ; High-Byte der Zieladresse holen
      CMP #>TARGET1+$02 ; Schon 2*256=512 Bytes übertragen?
      BEQ AA08          ; Sprung falls 512 Bytes übertragen
      INX               ; Lesezeiger erhöhen
      CPX #34           ; Blockgröße erreicht?
      BNE AA05          ; Rücksprung falls Blockgröáe nicht erreicht
      JSR $EDFE         ; UNLISTEN senden
      JMP AA03          ; Nächsten Block übertragen
AA08: JSR $EDFE
      LDA #$6F          ; Sekundäradresse 15 (Befehlskanal)
      JSR P_AD          ; LISTEN senden
      LDY #$04          ; Länge des "Memory-Execute" ("M-E")-Befehls minus 1
AA09: LDA T002,Y        ; "M-E"-Befehl zeichenweise lesen
      JSR $FFA8         ; IECOUT
      DEY               ; Lesezeiger erniedrigen
      BPL AA09          ; Sprung falls noch nicht alle Zeichen gesendet
      JSR $EDFE         ; UNLISTEN senden
      LDA *$C3          ; Low-Byte der Zieladresse
      STA *$AE          ; umkopieren
      LDA *$C4          ; High-Byte der Zieladresse
      STA *$AF          ; umkopieren
      SEI               ; Interrupts während Schnellladeroutine verbieten
      JSR P_AF          ; Zeichen per Schnelllader in <A> einlesen
      PHA               ; und retten (Anzahl gültiger Datenbytes)
      JSR P_AF          ; Zeichen per Schnelllader in <A> einlesen
      TAY               ; und retten (Low-Byte der Ladeadresse)
      JSR P_AF          ; Zeichen per Schnelllader in <A> einlesen
      STA *$B4          ; und merken (High-Byte der Ladeadresse)
      LDX *$B9          ; Sekundäradresse für LOAD
      BEQ AA10          ; Sprung falls nicht absolut
      STA *$AF          ; Low-Byte der Ladeadresse vom Programmanfang speichern 
      STY *$AE          ; High-Byte der Ladeadresse vom Programmanfang speichern
AA10: PLA               ; Anzahl gültiger Datenbytes zurückholen
      AND #$FF          ; und testen
      BNE P_AC          ; Sprung falls ungleich null
      LDA #$FC
      BNE AA12
AA11: JSR P_AF          ; Zeichen per Schnelllader in <A> einlesen
      AND #$FF          ; untersuchen
      BNE P_AC          ; Sprung falls letzter Block des Programms
      LDA #$FE          ; Nicht letzter Block, enthält 254 Datenbytes
AA12: STA *$B5          ; Anzahl gültiger Bytes im Datenblock
      LDY #$00
AA13: JSR P_AF          ; Zeichen per Schnelllader in <A> einlesen
      STA ($AE),Y       ; und in an Ladeadresse schreiben
      INY               ; Schreibzeiger erhöhen
      CPY *$B5          ; mit Anzahl gültiger Bytes im Datenblock vergleichen
      BNE AA13          ; Rücksprung falls noch nicht alle Bytes empfangen
      DEC $D020         ; Farbe des Bildschirmrahmens wechseln
      PHA               ; ? Datenbyte retten
      EOR #$01          ; ?
      INC $D020         ; Farbe des Bildschirmrands wiederherstellen
      LDA *$AE          ; Low-Byte der Ladeadresse
      CLC
      ADC *$B5          ; um Anzahl gültiger Datenbytes erhöhen
      STA *$AE          ; und zurückschreiben
      LDA *$AF          ; High-Byte der Ladeadresse
      ADC #$00          ; um eventuellen Additionsübertrag erhöhen
      STA *$AF          ; und zurückschreiben
      INC *$B4          ; High-Byte der Ladeadresse erhöhen
      PLA               ; ? Datenbyte zurückholen
      INC $D020         ; Farbe des Bildschirmrahmens wechseln
      LDA *$B4          ; High-Byte der Ladeadresse
      CMP T000+$02      ; mit Highbyte der Adresse des Schnelladers vergleichen
      BNE AA11          ; Rücksprung, falls noch nicht erreicht
      CLI               ; sonst Schnellader verlassen, da überschrieben
      LDA T000+$00      ; Alten Wert von $B4
      STA *$B4          ; wiederherstellen
      LDA T000+$01      ; Alten Wert von B5
      STA *$B5          ; wiederherstellen
      JSR P_AB          ; Load-Vektor wiederherstellen
      LDA #$F0          ; Sekundäradresse 0
      JSR P_AD          ; LISTEN senden
      LDA #$2A          ; '*'
      JSR $EDDD         ; IECOUT
      JSR $EDFE         ; UNLISTEN senden
      JSR P_AE          ; TALK senden
      LDA #$60          ; Sekundäradresse 0
      STA *$B9          ; merken
      JSR $EDC7         ; Sekundäradresse nach TALK ausgeben
      JMP $F501         ; Sprung zur LOAD-Routine

; LOAD-Vektor wiederherstellen
P_AB: LDA #$A5          ; Low-Byte des Load-Vektor
      STA $0330         ; wiederherstellen
      LDA #$F4          ; High-Byte des Load-Vektor
      STA $0331         ; wiederherstellen
      RTS
      
P_AC: LDY *$B5          ; Anzahl gültiger Bytes im aktuellen Block holen
      BNE AC00          ; Sprung falls letzter Block
      SEC
      SBC #$02          ; Länge des Verkettungspointers subtrahieren
AC00: STA *$B5          ; und als Zahl zu empfangender Datenbytes merken
      DEC *$B5          ; Offset subtrahieren
      LDY #$00          ; Schreibzeiger initialisieren
AC01: JSR P_AF          ; Byte von Floppy empfangen
      STA ($AE),Y       ; und an Ladeadresse schreiben
      INY               ; Schreibzeiger erhöhen
      CPY *$B5          ; Schon alle Datenbytes des aktuellen Blocks empfangen?
      BNE AC01          ; Rücksprung falls noch nicht alle Datenbytes
      LDA *$AE          ; Low-Byte der Ladeadresse holen
      CLC
      ADC *$B5          ; um Anzahl der empfangenen Datenbytes erhöhen
      STA *$AE          ; und merken
      TAX
      LDA *$AF          ; High-Byte der Ladeadresse holen
      ADC #$00          ; um eventuellen Additionsübertrag erhöhen
      STA *$AF          ; und merken
      TAY
      LDA T000+$00      ; Inhalt von Adresse $B4
      STA *$B4          ; wiederherstellen
      LDA T000+$01      ; Inhalt von Adresse $B5
      STA *$B5          ; wiederherstellen
      LDA T001+$00
      CMP #$22
      BEQ AC02
      JSR P_AB
AC02: CLC
      CLI
      RTS

; LISTEN senden
P_AD: PHA               ; Sekundäradresse retten
      LDA #DEVICE       ; Geräteadresse
      JSR $ED0C         ; LISTEN senden
      PLA               ; Sekundäradresse zurückholen
      JMP $EDB9         ; Sekundäradresse nach LISTEN senden

; TALK senden
P_AE: LDA #DEVICE       ; Geräteadresse
      JMP $ED09         ; TALK senden

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

Da das nachzuladende Programm in Form von 4 Bitpaaren übertragen wird, von denen jedes nur für 8 μs auf die DATA- und CLOCK-Leitungen gelegt wird, ist eine exakte Synchronisation von C64 und Floppy wichtig. Zunächst wartet die Schnelllade-Routine daher vor jedem Datenbyte, dass die Floppy bereit zum Senden ist und dies dadurch kundtut, dass sie einen Low-Pegel auf der ATN-Leitung mit DATA=high (Label AF00) quittiert. Anschließend stellt die Routine sicher, dass das Timing nicht durch DMA-Zugriffe des VIC gestört werden kann: Falls der VIC gerade den sichtbaren Teil des Bildschirms darstellt, so wartet die Routine auf eine Rasterzeile, in der Badlines ausgeschlossen sind (Bit 2 der Rasterzeile im VIC-Register $D012 gesetzt, Label AF01).

Nun beendet die Routine mit ATN=high die Synchronisation und empfängt dann nacheinander 4 Bitpaare. Die Reihenfolge dieser Bits ist auf die Registerbelegung des Port B von VIA1 optimiert (nacheinander Bit 5+7, Bit 4+6, Bit 1+3 und Bit 0+2), so dass nur mit aufwändigen Schiebe- und Rotationsbefehlen das ursprüngliche Datenbyte rekonstruiert werden kann — oder mit der bei Bad Cat verwendeten, etwas speicherintensiveren Methode: Vier Lesezugriffe auf eine Lookup-Table, von denen jeder zwei korrekt positionierte Bits liefert, die anschließend nur noch untereinander mit einem logischen ODER verknüpft werden müssen. Während der VIC den Bildschirmrahmen anzeigt, dauert die Übertragung eines Byte mit dieser Routine 105 μs; während der Darstellung des Bildschirminhalts können pro 8 Rasterzeilen (alle 504 Systemtakte) 3 Bytes übertragen werden.

; Datenbyte von Floppy empfangen
P_AF: LDA #$0B
      STA $DD00         ; Synchronisation per ATN low
AF00: LDA $DD00         ; Auf DATA high warten
      BPL AF00
AF01: LDA $D012         ; Bildschirmzeile des VIC holen
      CMP #$32          ; Oberste Rasterzeile?
      BCC AF02          ; Sprung falls Bildschirmrahmen dargestellt
      AND #$04          ; Warten, bis Badline vorbei
      CMP #$04
      BNE AF01          ; Rücksprung, falls Bit 2 der Rasterzeile gelöscht
AF02: LDA #$03          ; ATN high
      STA $DD00
      NOP               ; Verzögerung
      NOP
      NOP
      NOP
      NOP
      NOP
      NOP
      NOP
      NOP
      NOP
      LDX $DD00         ; Bit 5 und 7 des Datenbyte nach Bit 7 und 6 von X
      LDA T100,X        ; Verschiebung an die richtigen Bitpositionen per Lookup-Table
      LDX $DD00         ; Bit 4 und 6 des Datenbyte nach bit 7 und 6 von X
      ORA T101,X        ; Einblenden an die richtigen Bitpositionen per Lookup-Table
      LDX $DD00         ; Bit 1 und 3 des Datenbyte nach Bit 7 und 6 von X
      ORA T102,X        ; Verschiebung an die richtigen Bitpositionen per Lookup-Table
      LDX $DD00         ; Bit 0 und 2 des Datenbyte nach Bit 7 und 6 von X
      ORA T103,X        ; Verschiebung an die richtigen Bitpositionen per Lookup-Table
      RTS

Floppy-seitige Schnelllade-Routine[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.

Das Versenden findet parallel auf der CLOCK- und DATA-Leitung statt, wobei jedes Datenbyte in 4 Bitpaare aufgeteilt wird, deren Reihenfolge so gewählt ist, dass sie jeweils nach 8 μs (und nach wenigen Schiebe- und Rotationsbefehlen) in Port B von VIA1 geschrieben werden können. Auf diese Weise wird kurzzeitig eine Datenrate von 250 kBit/s erreicht.

Die Floppy-seitige Schnelllade-Routine liest das zu ladende Programm sektor-weise. Jeder Übertragung zum C64 wird ein Headerbyte vorangestellt, das anzeigt, ob danach noch weitere Sektoren folgen werden (Headerbyte $00), oder ob es sich um den letzten Sektor eines Programms handelt (Headerbyte $02..$FF, entsprechend einer Zahl von 1..254 restlichen Datenbytes).

SOURCE1:
 
; Kommentare übernommen aus den Büchern:
;   Rainer Ellinger/Lothar Englisch/Ralph Gelfand/Norbert Sczcepanowski:
;     "Das große Floppybuch zur 1541"
;   Rainer Ellinger:
;     "Commodore 1571 & 1570 - Das große Floppybuch"
 
; Geringfügig modifizierte Kopie des Floppy-ROM ab $F4D1
P_BA: JSR $F50A         ; Blockheader des Sektors suchen
BA00: BVC BA00          ; auf Byte von Diskette warten
      CLV               ; Leseelektronik wieder bereit machen
      LDA $1C01         ; Byte vom Kopf lesen
      STA $0300,Y       ; und in aktuellen Puffer schreiben
      INY               ; Pufferzeiger auf nächstes Byte setzen
      BNE BA00          ; Puffer schon voll?
      LDY #$BA          ; ja, Pufferzeiger auf Zusatzpuffer
BA01: BVC BA01          ; auf nächstes Byte von Diskette warten
      CLV               ; Flag wieder bereit machen
      LDA $1C01         ; Byte von Lesekopf holen
      STA $0100,Y       ; und in Zusatzpuffer schreiben
      INY               ; Pufferzeiger auf nächstes Byte setzen
      BNE BA01          ; Zusatzpuffer voll
      LDA #$03
      STA *$31
      JSR $F8E0         ; ja, Sektor von GCR nach Binär wandeln
      JSR $F5E9         ; Prüfsumme der Daten berechnen
      EOR *$3A          ; mit gelesenem Wert vergleichen
      BEQ BA02          ; neide identisch?
      LDA #$05          ; Fehlernummer für READ ERROR (23)
      BNE BA07          ; unbedingter Sprung zur Fehlerausgabe
; Schnelllade-Routine
BA02: LDA $0300         ; Spur des nächsten Blocks holen
      STA *$08          ; und merken
      BEQ BA03          ; Sprung falls letzter Block
      LDA #$00          ; sonst $00 als Header voranstellen
      STA *$0C          ; merken
      BEQ BA04          ; Unbedingter Sprung
BA03: LDA $0301         ; Anzahl gültiger Bytes im letzten Blocks holen
      STA *$0C          ; merken
      INC *$0C          ; um Offset erhöhen
BA04: JSR P_BC          ; Headerbyte in A an C64 senden
      LDA $0301         ; Sektor des nächsten Block holen
      STA *$09          ; und merken
      LDA *$11          ; Erster Block?
      BEQ BA05          ; Sprung falls nicht erster Block
      LDA $0303         ; High-Byte der Ladeadresse
      STA *$10          ; merken
      STY *$11          ; Flag für "erster Block" löschen
BA05: LDY #$02          ; Lesezeiger initialisieren
BA06: LDA $0300,Y
      JSR P_BC          ; Datenbyte in A an C64 senden
      INY               ; Lesezeiger erhöhen
      CPY *$0C          ; Schon alle Datenbytes gelesen?
      BNE BA06          ; Rücksprung falls nicht alle Datenbytes
      LDA *$08          ; Letzten Block versendet?
      BEQ BA07          ; Sprung falls letzter Block
      LDA #$01          ; Rückmeldung $00="Letzter Block", $01="Nicht letzter Block"
BA07: JMP $F969         ; Aktuellen Job beenden, Fehlerrückmeldung bereitstellen

; Einsprung für "Memory-Execute" ("M-E")-Befehl
P_BB: LDA #$C8          ; Standardwert setzen
      STA *$64          ; für Zahl der Halbspuren bis Spur 0
      LDA #$04
      STA *$5E
      STA *$5F
      LDA *$18          ; Spur des zuletzt gelesenen Sektorheaders
      LDX *$19          ; Sektor des zuletzt gelesenen Sektorheaders
      STA *$08          ; Spur merken
      STX *$09          ; Sektor merken
      LDA #$10          ; Rate des Jobscheifen-Interrupt erhöhen (alle 4 ms statt alle 14 ms)
      STA $1C07
      STA *$11
      LDA $1C00         ; CLOCK und DATA high, ATN nicht automatisch beantworten
      ORA #$08
      STA $1C00         ; CLOCK low, ATN automatisch beantworten
BB00: LDA #$E0          ; Jobcode "Programm in Jobschleife einbinden"
      STA *$01          ; als Job für Puffer 1 an Adresse $0400 setzen
BB01: LDA *$01          ; Warten bis Job abgearbeitet
      BMI BB01
      BEQ BB02          ; Sprung falls letzter Block
      CMP #$02          ; Fehler?
      BCS BB03          ; Sprung falls Fehler
      LDA #$08          ; Anzahl Leseversuche
      STA *$0D          ; merken
      INC *$10          ; High-Byte der Ladeadresse erhöhen
      LDA *$10          ; High-Byte der momentanen Ladeadresse
      CMP BB06          ; mit Highbyte der Anfangsadresse des Schnelladers vergleichen
      BNE BB00          ; Rücksprung, falls C64-seitiger Schnelllader nicht überschrieben
      LDA $0300         ; sonst Spurnummer des aktuellen Blocks
      STA *$7E          ; als Spurnummer des letzten Zugriff setzen
      LDA $0301         ; Sektornummer des aktuellen Blocks
      STA $026F         ; als Nummer des letzten Sektors setzen
      JMP $C194         ; Beenden eines Rechnerbefehls, erzeugen der Fehlermeldung
BB02: STA *$09          ; 0 als Sektor für Puffer 1
      LDA #$12          ; Spur 18
      STA *$08          ; als Spur für Puffer 1
      LDA #$B0          ; Jobcode "Suchen eines Sektorheaders"
      STA *$01          ; als Job für Puffer 1 an Adresse $0400 setzen
      JMP $DAC0         ; Datei schlieáen
BB03: TAY               ; Fehlernummer nach Y retten
      DEC *$0D          ; Anzahl Leseversuche erniedrigen
      BNE BB04          ; Rücksprung falls weiterer Leseversuch
      TYA               ; Fehlernummer zurückholen
      LDX #$02          ; ? Puffernummer
      JMP $E60A         ; Ausgabe der Fehlermeldung (A muá Fehler- und X die Puffernummer enthalten)
BB04: LDA *$0D          ; Verbleidende Anzahl Leseversuche holen
      CMP #$04          ; Nur noch 4 Leseversuche?
      BNE BB00          ; Sprung falls nicht
      LDA #$C0          ; Jobcode "Kopf auf Spur 0 setzen"
      STA *$01          ; als Job für Puffer 1 an Adresse $0400 setzen
BB05: LDA *$01          ; warten, bis Job abgearbeitet
      BMI BB05
      BPL BB00          ; Unbedingter Sprung zum nächsten Leseversuch

BB06: DB $CB

; Datenbyte in A an C64 senden
P_BC: TAX               ; X=Datenbyte
BC00: BIT $1800         ; Auf ATN low warten
      BPL BC00
      LDA #$10          ; DATA und CLOCK high
      STA $1800
BC01: BIT $1800         ; Auf ATN high warten
      BMI BC01
      TXA               ; A=Datenbyte
      LSR A
      LSR A
      LSR A
      LSR A
      STA $1800         ; DATA=Bit 5, CLOCK=Bit 7
      ASL A
      AND #$0F
      STA $1800         ; DATA=Bit 4, CLOCK=Bit 6
      TXA               ; A=Datenbyte
      AND #$0F
      STA $1800         ; DATA=Bit 1, CLOCK=Bit 3
      ASL A
      AND #$0F
      STA $1800         ; DATA=Bit 0, CLOCK=Bit 2
      LDA #$0F
      NOP
      STA $1800         ; DATA und CLOCK low
      RTS

Lookup-Table für das Umsortieren der Datenbits[Bearbeiten | Quelltext bearbeiten]

Die Schnelllade-Routine erzielt ihre hohe Geschwindigkeit dadurch, dass sie die einzelnen Bits jedes Datenbyte in einer an die Registerbelegung des VIA angepassten Reihenfolge überträgt. Das dadurch notwendige, C64-seitige Umsortieren der empfangenen Bits geschieht, indem die aus Port A von CIA2 gelesenen Leitungspegel als Index in die Lookup-Tabels T100 ... T103 verwendet werden. Da der Status der CLOCK- und DATA-Leitung in Bit 6 und 7 von Port A zu finden sind, zeigt dieser Index auf Positionen in der Lookup-Table, die — je nach Zustand der Leitungen — um ein Vielfaches von 64 Byte auseinanderliegen. Die dazwischenliegenden, 56 Byte langen Lücken können genutzt werden, um die vier Tabellen T100 ... T103 verschränkt abzulegen und damit Speicherplatz zu sparen.

; Umrechnungstabelle Register $DD00 zu Bitpaaren
T100: DB $A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0 ; Bit 5 (DATA) low, Bit 7 (CLOCK) low
T101: DB $50,$50,$50,$50,$50,$50,$50,$50 ; Bit 4 (DATA) low, Bit 6 (CLOCK) low
T102: DB $0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A ; Bit 1 (DATA) low, Bit 3 (CLOCK) low
T103: DB $05,$05,$05,$05,$05,$05,$05,$05 ; Bit 0 (DATA) low, Bit 2 (CLOCK) low
      DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Unbenutzt
      DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
      DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
      DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
      DB $20,$20,$20,$20,$20,$20,$20,$20 ; Bit 5 (DATA) low, Bit 7 (CLOCK) high
      DB $10,$10,$10,$10,$10,$10,$10,$10 ; Bit 4 (DATA) low, Bit 6 (CLOCK) high
      DB $02,$02,$02,$02,$02,$02,$02,$02 ; Bit 1 (DATA) low, Bit 3 (CLOCK) high
      DB $01,$01,$01,$01,$01,$01,$01,$01 ; Bit 0 (DATA) low, Bit 2 (CLOCK) high
      DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Unbenutzt
      DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
      DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
      DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
      DB $80,$80,$80,$80,$80,$80,$80,$80 ; Bit 5 (DATA) high, Bit 7 (CLOCK) low
      DB $40,$40,$40,$40,$40,$40,$40,$40 ; Bit 4 (DATA) high, Bit 6 (CLOCK) low
      DB $08,$08,$08,$08,$08,$08,$08,$08 ; Bit 1 (DATA) high, Bit 3 (CLOCK) low
      DB $04,$04,$04,$04,$04,$04,$04,$04 ; Bit 0 (DATA) high, Bit 2 (CLOCK) low
      DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Unbenutzt
      DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
      DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
      DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
      DB $00,$00,$00,$00,$00,$00,$00,$00 ; Bit 5 (DATA) high, Bit 7 (CLOCK) high
      DB $00,$00,$00,$00,$00,$00,$00,$00 ; Bit 4 (DATA) high, Bit 6 (CLOCK) high
      DB $00,$00,$00,$00,$00,$00,$00,$00 ; Bit 1 (DATA) high, Bit 3 (CLOCK) high
      DB $00,$00,$00,$00,$00,$00,$00,$00 ; Bit 0 (DATA) high, Bit 2 (CLOCK) high
      DB $00,$00,$00,$00,$00,$00,$00,$00 ; Unbenutzt
      DB $00,$00,$00,$00,$00,$00,$00,$00
      DB $00,$00,$00,$00,$00,$00,$00,$00
      DB $00,$00,$00,$00,$00,$00,$00,$00