FLOAD/Quellcode 2.2

Aus C64-Wiki
Zur Navigation springenZur Suche springen

<< zurück zu FLOAD


FLOAD/Quellcode_2.2: Die folgenden Abschnitte stellen den disassemblierten Floppy-Schnelllader FLOAD, Version 2.2, dar. Sie sind gegliedert in einzelne Codeblöcke, die gemäß ihrer Funktion vor dem und während des Ladens gruppiert sind. Die Floppy-seitigen Schnelllader-Routinen sind für die im Hauptartikel ebenfalls erwähnte FLOAD-Version 2.1 identisch.


Bestimmung des Diskettenlaufwerks-Typs[Bearbeiten | Quelltext bearbeiten]

Damit FLOAD gezielt einen für das angeschlossene Diskettenlaufwerk passenden Schnelllader einsetzen kann, wird zunächst der Floppy-Typ bestimmt. Hierzu liest FLOAD mit Hilfe des "MEMORY-READ"- ("M-R"-) Befehls einige charakteristische Speicherzellen aus dem ROM des Laufwerks aus. Eine Id des erkannten Floppytyps wird anschließend an Adresse $57 abgelegt:

Id Zugehöriger Laufwerkstyp
1 VC-1541/VC-1541 II
2 VC-1570
3 VC-1571
4 VC-1581
5.. VC-1581-Clones (CMD?)
100 nicht erkannt

Auf Basis dieser Id wird anschließend eine passende Floppy-seitige Schnelllade-Routine für den Transfer ins RAM des Diskettenlaufwerks sowie die hierfür benötigtem "M-W"- und "M-E"-Kommandos in drei Puffern (BUF, COM_MW und COM_ME) bereitgestellt. Zudem sorgt dieser Programmteil dafür, dass der Schnelllader mit unterschiedlichen Rechnertypen funktioniert: Ein eventuell mit 2 MHz betriebener C128 wird auf 1 MHz zurückgeschaltet; bei der NTSC-Variante des C64 wird der als Verzögerung eingesetzte Befehl LDA #$EA (unittelbare Adressierung, 2 Systemtakte) durch LDA *$EA (Zeropage-Adressierung, 3 Systemtakte) ersetzt.

P_BA: LDA *$BA       ; Gerätenummer des zuletzt verwendeten Geräts
      STA AA00+$01   ; in Programmcode einfügen
      CMP #$08       ; mit kleinstmöglicher Adresse einer Floppy vergleichen
      BCC BA00       ; Sprung falls kleiner (=ungültig)
      CMP #$1F       ; mit größtmöglicher Adresse einer Floppy+1 vergleichen
      BCC BA01       ; Sprung falls kleiner (=gültig)
BA00: LDA #$08       ; Defaultadresse 8
      STA *$BA       ; merken
BA01: LDA #$80       ; Floppytyp auf "noch nicht bestimmt"
      STA *$57       ; setzen
      STA $D020      ; Bildschirmrand schwarz
      STA $D021      ; Bildschirmhintergrund schwarz
      STA $0291      ; Umschaltung Klein-/Großbuchstaben gesperrt
      STA $D030      ; 1 MHz Taktfrequenz (nur C128 im C64-Modus)
      LDA #$15       ; Basisadresse von Video-RAM und Zeichensatz setzen
      STA $D018
      LDA #<T000     ; Zeiger auf Meldung "FLOAD V2.2"
      LDY #>T000
      JSR $AB1E      ; String ausgeben
BA02: JSR P_BC       ; Floppytyp bestimmen
      BCC BA06       ; Sprung falls kein Fehler
      TAY            ; Resultat holen
      BPL BA03       ; Sprung falls Fehler
      JSR P_BH       ; Floppystatus holen und ausgeben
      LDA #$6F       ; Zeiger auf Meldung "<CR><CR>"
      LDY #$C0
      BNE BA04       ; Unbedingter Sprung
BA03: LDA #<T001     ; Zeiger auf Meldung "DRIVE ERROR!<CR><CR>"
      LDY #>T001
BA04: JSR $AB1E      ; String ausgeben
BA05: LDA $DC01      ; Warten auf Leertaste
      CMP #$FF
      BEQ BA05
      BNE BA02       ; Unbedingter Sprung
BA06: LDX *$57       ; Floppytyp nach X holen
BA07: LDA #$00       ; Wird in P_BC modifziert
      RTS

T000: DB $93,$99,'FLOAD V2.2',$0D,$0D,$00
T001: DB $1C,'DRIVE ERROR!'
T002: DB $0D,$0D,$00
T003: DB $1E,'CHECKING',$0D,$0D,$00
T004: DB ... ; Name des nachzuladenden Programmteils

; Etwa Y Sekunden Verzögerung
P_BB: LDX #$00       ; Inneren Schleifenzähler initialisieren
BB00: LDA $D012      ; Low-Byte der aktuellen Bildschirmzeile
      BNE BB00       ; Warten auf Bildschirmzeile $000 oder $100
      DEX            ; Interen Schleifenzähler vermindern
      BNE BB00       ; Rücksprung falls noch nicht 0
      DEY            ; Äußeren Schleifenzähler (Sekundenzähler) erniedrigen
      BNE P_BB       ; Rücksprung falls noch nicht 0
      CLC
      RTS

COM_MW EQU $C88C     ; Puffer für "MEMORY-WRITE"- ("M-W")-Befehl
COM_ME EQU $C892     ; Puffer für "MEMORY-EXECUTE"- ("M-E")-Befehl 
BUF EQU $C897        ; Puffer für Floppy-seitigen Schnelllader

; Floppytyp bestimmen und nach Adresse $57 (100=unbekannt), zugehörigen Schnelllader vorbereiten
P_BC: LDY *$57       ; Floppytyp holen
      BEQ BC00       ; Sprung falls Floppytyp noch nicht bekannt
      BPL BC03       ; Sprung falls Floppytyp bekannt, Bestimmung nicht nötig
BC00: JSR P_BE       ; Floppytyp ermitteln
      BCS BC01       ; Sprung bei Fehler
      TYA            ; Floppytyp nach A holen
      BEQ BC02       ; Sprung falls Floppytyp nicht erkannt
      CMP #$0B       ; Ungültiger Floppytyp?
      BCC BC03       ; Sprung falls gültig
BC01: SEC            ; Zeichen für "Fehler"
      RTS
BC02: LDY #$64       ; Floppytyp="Unbekannt"
BC03: STY *$57       ; Floppytyp merken
      CPY #$04       ; Floppytyp 1541, 1570, 1571?
      BCS BC05       ; Sprung falls nicht
      JSR P_BD       ; LISTEN an Befehlskanal senden
      LDX #$06       ; Länge des "M-W"-Befehls minus 1 (Setzen des Datenrichtungsregisters von Port B der VIA1)
BC04: LDA T014,X     ; "M-W"-Befehl byteweise lesen
      JSR $FFA8      ; Ausgabe auf IEC-Bus
      DEX            ; Lesezeiger erniedrigen
      BPL BC04       ; Rücksprung falls noch nicht alle Bytes übertragen
      JSR $FFAE      ; UNLISTEN senden
BC05: CPY #$03       ; Floppytyp 1571?
      BNE BC07       ; Sprung falls nicht
      JSR P_BD       ; LISTEN an Befehlskanal senden
      LDX #$04       ; Länge des "U0>M1"-Befehls minus 1 (in 1571-Modus schalten)
BC06: LDA T015,X     ; "U0>M1"-Befehl byteweise lesen
      JSR $FFA8      ; Ausgabe auf IEC-Bus
      DEX            ; Lesezeiger erniedrigen
      BPL BC06       ; Rücksprung falls noch nicht alle Bytes übertragen
      JSR $FFAE      ; UNLISTEN senden
BC07: JSR P_BI       ; Nachzuladende Datei öffnen und wieder schließen
      BCS BC01       ; Sprung falls Fehler
      LDA #<T003     ; Zeiger auf String "CHECKING"
      LDY #>T003
      JSR $AB1E      ; String ausgeben (BASIC)
      JSR BH00       ; Floppy zurücksetzen und Statusmeldung holen
      PHP            ; Rückmeldung retten
      SEI            ; Interrupts verbieten, während Kernal ausgeblendet ist
      LDA *$01       ; Status des Prozessorports holen
      PHA            ; und retten
      LDA #$38       ; Nur RAMs in Adressraum des Prozessors einblenden
      STA *$01
      LDY *$57       ; Floppytyp holen
      CPY #$64       ; Nicht erkannt?
      BEQ BC08       ; Sprung falls nicht erkannt
      CPY #$07       ; Floppytyp ungültig (zu groß)?
      BCC BC10       ; Sprung falls nicht zu groß
BC08: LDX #$6C       ; Länge der Laderoutine mit ROM-Routinen
BC09: LDA P_IA-$01,X ; Laderoutine byteweise lesen
      STA AA01-$01,X ; und an Stelle des Schnellladers kopieren
      DEX            ; Lesezeiger erniedrigen
      BNE BC09       ; Sprung falls noch nicht ganz umkopiert
      LDA #$4C       ; Befehl "JMP P_IC"
      LDX #<P_IC
      LDY #>P_IC
      STA P_AB+$00   ; statt Aufruf des Schnellladers in Programmcode einfügen
      STX P_AB+$01
      STY P_AB+$02
      LDA #$64       ; Floppytyp "Unbekannt"
      STA BA07+$01   ; in Programmcode eintragen
      PLA            ; Status des Prozessorports zurückholen
      STA *$01       ; und wiederherstellen
      PLP            ; Rückmeldung zurückholen
      LDY #$06       ; 6 Sekunden
      JMP P_BB       ; Verzögerung
BC10: CPY #$05       ; Floppytyp=5+?
      BNE BC14       ; Sprung falls nicht
; Floppyseitigen Schnelllader für VC1581 patchen für Floppytyp 5+
      LDA #<P_HB     ; Low-Byte des Floppy-seitigen Schnellladers für VC1581
      STA *$5A       ; als Low-Byte des Adresszeigers merken
      LDA #>P_HB     ; High-Byte des Floppy-seitigen Schnellladers für VC1581
      STA *$5B       ; als High-Byte des Adresszeigers merken
      LDX #$0B       ; 12 Stellen patchen
BC11: LDA T006,X     ; Offset zur nächsten zu patchenden Stelle
      CLC            ; auf Adresszeiger aufaddieren
      ADC *$5A
      STA *$5A
      BCC BC12       ; Sprung falls kein Additionsüberlauf
      INC *$5B       ; sonst Additionsüberlauf berücksichtigen
BC12: LDY T007,X     ; Y=Zeiger auf Patch-Tabelle
      LDA T005+$01,Y ; A=High-Byte des zu patchenden Werts
      PHA            ; merken
      LDA T005,Y     ; A=Low-Byte des zu patchenden Werts
      CPY #$01       ; Nur 1 Byte patchen?
      LDY #$00       ; Schreibzeiger initialisieren
      STA ($5A),Y    ; Low-Byte in Schnelloader einfügen
      PLA            ; High-Byte zurückholen
      BCC BC13       ; Sprung falls nut 1 Byte patchen
      INY            ; sonst Schreibzeiger erhöhen
      STA ($5A),Y    ; und High-Byte in Schnellloader einfügen
BC13: DEX            ; Zähler für Patches erniedrigen
      BPL BC11       ; Rücksprung falls noch nicht alle Patches
; Ende der Sonderbehandlung für Floppytyp 5+
BC14: LDY *$57       ; Floppytyp nach Y holen
      LDA T008-$01,Y ; Länge des Floppy-seitigen Schnellladers holen
      STA BC15+$01   ; und in Code einfügen
      LDX T009-$01,Y ; Low-Byte der Anfangsadresse des Schnellladers nach X
      LDA T010-$01,Y ; High-Byte der Anfangsadresse des Schnellladers nach A
      STX BC16+$01   ; und in Code einarbeiten
      STA BC16+$02
      LDA T011-$01,Y ; Anzahl Blocks für Übertragung des Floppy-seitigen Schnellladers
      STA AA03+$01
BC15: LDX #$EA       ; Länge des Floppy-seitigen Schnellladers
BC16: LDA P_FA-$01,X ; Floppy-seitigen Schnelllader
      STA COM_MW-$01,X  ; in Puffer für Transfer zur Floppy umkopieren
      DEX            ; Lesezeiger erniedrigen
      BNE BC16       ; Rücksprung falls noch nicht ganzer Schnelllader umkopiert
      LDA T012-$01,Y ; Low-Byte der Startadresse des Floppy-seitigen Schnellladers
      STA AA04+$01   ; in Programmcode einarbeiten
      LDA T013-$01,Y ; High-Byte der Startadresse des Floppy-seitigen Schnellladers
      STA AA05+$01   ; in Programmcode einarbeiten
      LDA $02A6      ; Flag für PAL/NTSC
      BNE BC17       ; Sprung falls PAL
      LDA #$A5       ; Opcode für "LDA zpg"
      STA AC04       ; in Programmcode einarbeiten (ersetzt "LDA imm")
BC17: LDA #$00       ; Rückgabewert von P_BA
      STA BA07+$01   ; in Programmcode einarbeiten
      PLA            ; Status des Prozessorports zurückholen
      STA *$01       ; und wiederherstellen
      PLP            ; Rückmeldung zurückholen
      JMP $FF8A      ; I/O initialisieren

; Zu patchende Werte für Floppy-seitigen Schnelllader für VC1581
T005: DB $4E, $58,$2A, $59,$2A, $0A,$28, $0B,$28, $00,$80

; Abstände zwischen zu patchenden Stellen im Floppy-seitigen Schnelllader für VC1581
T006: DB $08,$1B,$0A,$0A,$0A,$0A,$0F,$10,$03,$03,$03,$07

; Zeiger auf zu patchende Werte relativ zu T005
T007: DB $00,$09,$09,$09,$09,$09,$09,$09,$07,$05,$03,$01

; Längen der Floppy-seitigen Schnelllader für verschiedene Floppy-Typen
T008: DB $EA ; VC1541
      DB $C0 ; VC1570
      DB $C0 ; VC1571
      DB $97 ; VC1581
      DB $97 ; ?
      DB $97 ; ?

; Low-Bytes der Anfangsadressen der Floppy-seitigen Schnelllader
T009: DB <[SOURCE1-$01]  ; VC1541
      DB <[SOURCE2-$01]  ; VC1570
      DB <[SOURCE2-$01]  ; VC1571
      DB <[SOURCE3-$01]  ; VC1581
      DB <[SOURCE3-$01]  ; ?
      DB <[SOURCE3-$01]  ; ?

; High-Bytes der Anfangsadressen der Floppy-seitigen Schnelllader
T010: DB >[SOURCE1-$01]  ; VC1541
      DB >[SOURCE2-$01]  ; VC1570
      DB >[SOURCE2-$01]  ; VC1571
      DB >[SOURCE3-$01]  ; VC1581
      DB >[SOURCE3-$01]  ; ?
      DB >[SOURCE3-$01]  ; ?

; Anzahl 35-Byte-Blocks pro Floppy-seitigem Schnelllader
T011: DB $07 ; VC1541
      DB $06 ; VC1570
      DB $06 ; VC1571
      DB $05 ; VC1581
      DB $05 ; ?
      DB $05 ; ?

; Startadressen des Floppy-seitigen Schnellladers, Low-Bytes
T012: DB <[TARGET1-$01]  ; VC1541
      DB <[TARGET2-$01]  ; VC1570
      DB <[TARGET2-$01]  ; VC1571
      DB <[TARGET3-$01]  ; VC1581
      DB <[TARGET3-$01]  ; ?
      DB <[TARGET3-$01]  ; ?

; Startadressen des Floppy-seitigen Schnellladers, High-Bytes
T013: DB >[TARGET1-$01]  ; VC1541
      DB >[TARGET2-$01]  ; VC1570
      DB >[TARGET2-$01]  ; VC1571
      DB >[TARGET3-$01]  ; VC1581
      DB >[TARGET3-$01]  ; ?
      DB >[TARGET3-$01]  ; ?

; Floppy-Kommando zum Setzen des Datenrichtungsregisters von Port B der VIA1 (rückwärts)
T014: DB $1A,$01,$18,$02,'W-M'

; Floppy-Kommando, um die VC-1570/1571 in den 2 MHz-Modus zu schalten (rückwärts)
T015: DB '1M>0U'

; Befehlskanal der Floppy öffnen
P_BD: LDA *$BA       ; Geräteadresse
      JSR $FFB1      ; LISTEN senden
      LDA #$6F       ; Sekundäradresse (Befehlskanal)
      JMP $FF93      ; Sekundäradresse nach LISTEN senden

; Floppytyp ermitteln (1=VC1541, 2=VC1570, 3=VC1571, 4=VC1581, 5+=?, 0=unbekannt)
P_BE: LDA #$01       ; Anzahl der aus dem Floppy-Speicher zu lesenden Bytes
      JSR P_BF       ; Erstes Zeichen aus Floppy-Speicher nach A lesen
      TAY            ; und nach Y retten
      JSR BH05       ; Befehlskanal schließen
      TYA            ; Gelesenes Byte nach A zurückholen
      LDY #$04       ; mit vier möglichen Antworten verschiedener Floppytypen vergleichen
BE00: CMP T017-$01,Y ; im Floppyspeicher vergleichen
      BEQ BE03       ; Sprung falls Floppytyp erkannt
      DEY            ; sonst zum nächsten möglichen Floppytyp übergehen
      BNE BE00       ; Sprung falls noch nicht alle Typen überprüft
BE01: CLC            ; Zeichen für "Kein Fehler"
      RTS
BE02: PLA            ; Rücksprungadresse vom Stack löschen
      PLA
      JMP BH05       ; Befehlskanal schließen
BE03: LDA T018-$01,Y ; Low-Byte der zugehörigen zweiten Leseadresse holen
      STA T016+$03   ; und in "Memory-Read"- ("M-R"-) Kommando aufnehmen
      LDA T019-$01,Y ; High-Byte der zugehörigen zweiten Leseadresse holen
      STA T016+$04   ; und in Memory-Read (M-R-) Kommando aufnehmen
      LDA #$02       ; Anzahl der aus dem Floppy-Speicher zu lesenden Bytes
      JSR P_BF       ; Erstes Zeichen aus Floppy-Speicher nach A lesen
      STA BE04+$01   ; und merken
      JSR $FFCF      ; Zweites Zeichen aus Floppy-Speicher nach A lesen
      STA BE05+$01   ; und merken
      JSR BH05       ; Befehlskanal schließen
      LDY #$09       ; mit neun möglichen Antworten verschiedener Floppytypen vergleichen
BE04: LDA #$00       ; Erstes gelesenes Zeichen
      CMP T020-$01,Y ; mit Wert in Tabelle vergleichen
      BNE BE06       ; Sprung falls ungleich
BE05: LDA #$00       ; Zweites gelesenes Zeichen
      CMP T021-$01,Y ; mit Wert in Tabelle vergleichen
      BEQ BE01       ; Sprung falls beide Zeichen gleich
BE06: DEY            ; sonst zum nächsten möglichen Floppytyp übergehen
      BNE BE04       ; Sprung falls noch nicht alle Typen überprüft
      BEQ BE01       ; Unbedingter Sprung

; "Memory-Read"- ("M-R"-) Befehl an Floppy senden und erstes Byte lesen
P_BF: STA T016+$05   ; Anzahl Bytes in Memory-Read-Kommando aufnehmen
      JSR P_BG       ; Dateiparameter für Befehlskanal setzen
      LDA #$06       ; Länge des Kommandos
      LDX #<T016     ; Low-Byte des Kommandos
      LDY #>T016     ; High-Byte des Kommandos
      JSR $FFBD      ; Dateinamenparameter setzen
      JSR $FFC0      ; OPEN mit Memory-Read- (M-R-) Kommando als Dateinamen
      BCS BE02       ; Bei Fehler Rückkehr in übergeordnete Routine
      LDX #$01       ; Logische Dateinummer
      JSR $FFC6      ; CHKIN Eingabegerät setzen
      JMP $FFCF      ; BASIN Eingabe eines Zeichens

; Dateiparameter für Befehlskanal setzen
P_BG: LDA #$01       ; Logische Dateinummer
      LDX *$BA       ; Geräteadresse
      LDY #$0F       ; Sekundäradresse
      JMP $FFBA      ; Dateiparameter setzen

; Befehl für ersten Lesezugriff auf Floppy-ROM
T016: DB 'M-R',$A0,$FE,$01

; Mögliche Resultate beim ersten Lesezugriff auf Floppy-ROM an Adresse $FEA0
T017: DB $43 ; ?
      DB $0D ; VC1541/1570/1571
      DB $FF ; VC1581
      DB $49 ; ?

; Low-Byte der Adresse für den zweiten Lesezugriff auf Floppy-ROM
T018: DB $A4 ; ?:                Low-Byte von $FEA4
      DB $C6 ; VC1541/1570/1571: Low-Byte von $E5C6
      DB $E9 ; VC1581:           Low-Byte von $A6E9
      DB $A3 ; ?:                Low-Byte von $FEA3

; High-Byte der Adresse für den zweiten Lesezugriff auf Floppy-ROM
T019: DB $FE ; ?:                High-Byte von $FEA4
      DB $E5 ; VC1541/1570/1571: High-Byte von $E5C6
      DB $A6 ; VC1581:           High-Byte von $A6E9
      DB $FE ; ?:                High-Byte von $FEA3

; Vergleichswerte für erstes Byte der zweiten Abfrage
T020: DB $34 ; VC1541
      DB $37 ; VC1570
      DB $37 ; VC1571
      DB $38 ; VC1581
      DB $48 ; ?
      DB $46 ; ?
      DB $36 ; ?
      DB $52 ; ?
      DB $52 ; ?

; Vergleichswerte für zweites Byte der zweiten Abfrage
T021: DB $B1 ; VC1541
      DB $B0 ; VC1570
      DB $B1 ; VC1571
      DB $B1 ; VC1581
      DB $44 ; ?
      DB $44 ; ?
      DB $34 ; ?
      DB $4C ; ?
      DB $44 ; ?

; Floppystatus holen und ausgeben
P_BH: JSR P_BG       ; Dateiparameter für Befehlskanal setzen
      LDA #$00       ; Befehlskanal ohne Dateinamen öffnen
      BEQ BH01       ; Unbedingter Sprung
; Floppy initialisieren, dann Floppystatus holen und ausgeben
BH00: JSR P_BG       ; Dateiparameter für Befehlskanal setzen
      LDA #$02       ; Kommando "UI" als Dateinamen setzen
      LDX #<T022
      LDY #>T022
BH01: JSR $FFBD      ; Filenamenparameter setzen
      JSR $FFC0      ; OPEN
      BCS BH05       ; Sprung bei Fehler
      LDX #$01       ; Logische Dateinummer des Befehlskanals
      JSR $FFC6      ; CHKIN Eingabegerät setzen
      LDY #$00       ; Lesezeiger für Floppystatus
BH02: JSR $FFCF      ; BASIN Eingabe eines Zeichens
      CMP #$0D       ; Abschließendes <CR>?
      BEQ BH05       ; Sprung falls ja
      CMP T023,Y     ; Mit 'IEC' vergleichen
      BNE BH03       ; Sprung falls ungleich
      INY            ; Lesezeiger erhöhen
      CPY #$03       ; Mit Länge des Strings 'IEC' vergleichen
      BNE BH04       ; Sprung falls ungleich
      LDY #$0A       ; ?
      STY *$57
BH03: LDY #$00       ; Logische Dateinummer des Bildschirms
BH04: JSR $FFD2      ; BSOUT Ausgabe eines Zeichens
      BNE BH02       ; Immer Rücksprung
BH05: LDA #$01       ; Logische Dateinummer des Befehlskanals
      JSR $FFC3      ; CLOSE
      JMP $FFCC      ; CLRCH

T022: DB 'UI'
T023: DB 'IEC'

Startroutinen[Bearbeiten | Quelltext bearbeiten]

Die nachfolgende Routine P_AA überträgt die zuvor bereitgestellten, Floppy-seitigen Schnelllade-Routinen mittels des "M-W"-Befehls ("Memory-Write") in Abschnitten zu jeweils 35 Bytes in das RAM der Floppy. Die Anzahl der hierfür nötigen Transfers in Abhängigkeit des Diskettenlaufwerkstyps wurde zuvor aus der Tabelle T011 ermittelt, das Low- und High-Byte der Zieladresse im RAM findet sich in den Tabellen T012 und T013 (bei VC-1541/VC-1570/VC-1571 im Puffer ab Adresse $0500, bei VC-1581 ab Adresse $0900). Anschließend wird der Schnelllader dort mittels "M-E" ("Memory-Execute") gestartet.

Damit die Schnelllade-Routine den nachzuladenden Programmteil auf der Diskette lokalisieren kann, wird die entsprechende Datei zuvor zum Lesen geöffnet und sofort wieder geschlossen — dieser Schritt hinterlässt im RAM der Floppy die Spur- und Sektornummer des ersten Programmblocks (bei VC-1541/1570/1571 an Adresse $18/$19, bei VC-1581 in den Speicherzellen $004C/$0288) und liefert damit den Startpunkt für das blockweise Lesen.

; Floppy-seitigen Fastloader ins Floppy-RAM übertragen und starten
P_AA: SEI
      LDA #$36      ; BASIC-ROM ausblenden
      STA *$01
AA00: LDA #$08      ; Geräteadresse
      STA *$BA      ; merken
AA01: LDX #$F0      ; Sekundäradresse für OPEN
      JSR AD00      ; LISTEN senden
      LDY #$00      ; Lesezeiger initialisieren
AA02: LDA ($BB),Y   ; Dateinamen byteweise lesen
      JSR $FFA8     ; Ausgabe auf IEC-Bus
      INY           ; Lesezeiger erhöhen
      CPY *$B7      ; mit Länge des Dateinamens vergleichen
      BNE AA02      ; Rücksprung falls noch nicht ganz übertragen
      JSR $FFAE     ; UNLISTEN senden
      LDX #$E0      ; Sekundäradresse für CLOSE
      JSR AD00      ; LISTEN senden
      JSR $FFAE     ; UNLISTEN senden
AA03: LDA #$00      ; Anzahl 35-Byte-Blocks
      STA *$F1      ; als Startwert des Zählers setzen
AA04: LDA #$AD      ; Wird durch High-Byte der Startadresse im Floppy-RAM ersetzt
      STA COM_MW+$02 ; in "Memory-Write-" ("M-W-")-Kommando aufnehmen
AA05: LDA #$A2      ; Wird durch Low-Byte der Startadresse im Floppy-RAM ersetzt
      STA COM_MW+$01 ; in "Memory-Write-" ("M-W-")-Kommando aufnehmen
      LDA #<BUF     ; Startadresse des Puffers für Fastloader, Low-Byte
      STA AA08+$01  ; in Programmcode einarbeiten
      LDA #>BUF     ; Startadresse des Puffers für Fastloader, Low-Byte
      STA AA08+$02  ; in Programmcode einarbeiten
AA06: JSR P_AD      ; LISTEN auf Befehlskanal senden
      LDX #$06      ; Länge des "Memory-Write"- ("M-W"_) Kommandos
AA07: LDA COM_MW-$01,X ; "M-W"-Kommando byteweise lesen
      JSR $FFA8     ; Ausgabe auf IEC-Bus
      DEX           ; Lesezeiger erniedrigen
      BNE AA07      ; Rücksprung falls noch nicht ganz übertragen
AA08: LDA BUF,X     ; Floppy-seitigen Schnelllader byteweise lesen
      JSR $FFA8     ; und an Floppy senden
      INX
      CPX #$23      ; Blockgröße erreicht
      BNE AA08
      JSR $FFAE     ; UNLISTEN senden
      TXA           ; A=Blockgröße (35)
      ADC AA08+$01  ; Low-Byte der Leseadresse erhöhen
      STA AA08+$01  ; und zurückschreiben
      BCC AA09      ; Sprung falls kein Additionsübertrag
      INC AA08+$02  ; High-Byte der Leseadresse erhöhen
AA09: CLC
      TXA           ; A=Blocklänge (35)
      ADC COM_MW+$02 ; Low-Byte der Schreibadresse im Floppyspeicher erhöhen
      STA COM_MW+$02 ; und zurückschreiben
      BCC AA10      ; Sprung falls kein Additionsübertrag
      INC COM_MW+$01 ; High-Byte der Schreibadresse erhöhen
AA10: DEC *$F1      ; Blockzähler erniedrigen
      BNE AA06      ; Rücksprung falls noch nicht alle Blocks übertragen
      JSR P_AD      ; Listen auf Befehlskanel senden
      LDY #$05      ; Länge des "Memory-Execute-" ("M-E"-) Kommandos
AA11: LDA COM_ME-$01,Y ; "M-E"-Kommando byteweise lesen
      JSR $FFA8     ; Ausgabe auf IEC-Bus
      DEY           ; Bytezähler erniedrigen
      BNE AA11      ; Rücksprung falls noch nicht ganzes Kommando übertragen
      JSR $FFAE     ; UNLISTEN senden
      SEI
AA12: BIT $DD00     ; Warten auf CLOCK low
      BVS AA12
      AND #$03      ; Bits für VIC-Bank isolieren
      STA AC05+$01  ; und in Programmcode einarbeiten
      ORA #$20      ; Bit für "DATA low" setzen
      STA AC03+$01  ; und in Programmcode einarbeiten
      LDA $D011     ; Bits für vertikales Scrolling
      AND #$07      ; isolieren
      CLC
      ADC #$2F      ; plus Breite des oberen Bildschirmrands
      STA AC02+$01  ; in Programmcode zur Berücksigung von Badlines einarbeiten
      JSR P_AC      ; Einen Datenblock in Puffer an $FC02 einlesen
      JSR P_DB      ; Datei byteweise lesen und entpacken
      RTS

; LISTEN an zuletzt verwendetes Gerät senden
P_AD: LDX #$6F      ; Sekundäradresse
AD00: LDA *$BA      ; Gerätenummer holen
      JSR $FFB1     ; LISTEN senden
      TXA           ; Sekundäradresse nach A
      JMP $FF93     ; Sekundäradresse nach LISTEN senden

; Nachzuladende Datei öffnen und wieder schließen
P_BI: LDA #$01       ; Logische Dateinummer
      LDX *$BA       ; Geräteadresse
      LDY #$08       ; Sekundäradresse
      JSR $FFBA      ; Dateiparameter setzen
      LDA #$10       ; Länge des Dateinamens
      LDX #<T004     ; Zeiger auf Namen des nachzuladenden Programmteils
      LDY #>T004
      JSR $FFBD      ; Dateinamenparameter setzen
      JSR $FFC0      ; OPEN
      LDX #$01       ; Logische Dateinummer
      JSR $FFC6      ; CHKIN
      JSR $FFCF      ; BASIN Eingabe eines Zeichens
      JSR $FFB7      ; Status holen
      PHA            ; und retten
      JSR BH05       ; Befehlskanal schließen
      PLA            ; Status zurückholen
      BNE BI00       ; Sprung falls Dateiende oder Fehler
      CLC            ; Zeichen für "Kein Fehler"
      RTS
BI00: SEC            ; Zeichen für "Fehler"
      LDA #$FF
      RTS

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 die CLOCK-Leitung auf High-Pegel setzt. 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 (Label AC01).

Ein kurzer Low-Impuls von 8 μs Dauer fordert schließlich ein Datenbyte von der Floppy an und synchronisiert gleichzeitig Rechner und Diskettenlaufwerk. Die Übertragungsreihenfolge der Datenbits ist so gewählt, dass sie direkt mittels aufeinanderfolgender EOR-Verknüpfungen und Rechtsverschiebungen in A eingeblendet werden können — lediglich eine von 0 verschiedene VIC-Bank in Bit 0 und 1 des Datenregisters könnte diesen Vorgang stören und wird daher mit einem abschließenden EOR (Label AB05) kompensiert.

Die empfangenen Datenbytes werden zunächst in einem Puffer an Adresse $FC02 zwischengespeichert und später weiterverarbeitet. Von jedem Block des nachzuladenden Programms werden nur die eigentlichen Datenbytes, nicht aber die Verkettungszeiger (Spur- und Sektornummer des nächsten Programmblocks) empfangen. Auch vom letzten, eventuell nur teilweise genutzte Programmblock werden jedoch alle 254 Datenbytes übertragen.

; Ein Byte per Schnelllader von Floppy einlesen
P_AB: PHP          ; Flags retten
      LDA AB01+$01 ; Low-Byte des Lesezeigers holen
      BNE AB01     ; Sprung falls Puffer bereits gefüllt
      STY AB00+$01 ; Lesezeiger in Y in Programmcode retten
      JSR P_AC     ; Datenblock (254 Bytes) in Puffer an $FC02 einlesen
AB00: LDY #$00     ; Lesezeiger in Y wiederherstellen
AB01: LDA $FC00    ; Nächstes Datenbyte aus Puffer holen
      INC AB01+$01 ; Pufferzeiger weiterbewegen
      PLP          ; Flags wiederherstellen
      RTS

; Datenblock (254 Bytes) in Puffer an $FC02 einlesen
P_AC: LDA #$35     ; I/O an $D000-$DFFF, restlicher Adressraum RAM
      STA *$01
AC00: BIT $DD00    ; Warten auf CLOCK high
      BVC AC00
      LDY #$02     ; Erste beide Datenbytes (Verkettungszeiger) werden nicht empfangen
      STY AB01+$01 ; in Programmcode aufnehmen
AC01: SEC
      LDA $D012    ; Aktuelle Bildschirmzeile
AC02: SBC #$00     ; mit oberem Bildschirmrand vergleichen (in P_AA gesetzt)
      BCC AC03     ; Sprung falls noch im oberen Bildschirmrand
      AND #$07     ; Droht eine Badline?
      BEQ AC01     ; Rücksprung falls ja
AC03: LDA #$20     ; DATA low
      STA $DD00    ; zwecks Synchronisation an Floppy senden
      NOP          ; 2 Systemtakte Verzögerung
      AND #$DF     ; DATA high
      STA $DD00    ; Zwecks Synchronisation an Floppy senden
AC04: LDA #$EA     ; 2 Systemtakte Verzögerung (bei PAL, 3 Takte bei NTSC)
      LDA $DD00    ; Datenbit 1 und 0 nach Bit 7 und 6
      LSR A        ; Datenbit 1..0 in Bit 5..4
      LSR A
      EOR $DD00    ; Datenbit 3 und 2 nach Bit 7 und 6
      LSR A        ; Datenbit 3..0 in Bit 5..2
      LSR A
      EOR $DD00    ; Datenbit 5 und 4 nach Bit 7 und 6
      LSR A        ; Datenbit 5..0 in Bit 5..0
      LSR A
      EOR $DD00    ; Datenbit 7 und 6 nach Bit 7 und 6
AC05: EOR #$00     ; Ausgleich für eventuell gesetzte Bits 1 und 0
      STA $FC00,Y  ; Datenbyte in Puffer ab $FC02 schreiben
      INC $D020    ; Rahmen hellgrau aufblinken lassen
      DEC $D020    ; und auf schwarz zurücksetzen
      INY          ; Schreibzeiger erhöhen
      BNE AC01     ; Rücksprung falls noch nicht alle Bytes empfangen
      DEC *$01     ; Ganzer Adressraum RAM
      RTS

C64-seitige Laderoutine basierend auf Kernal ROM-Routinen[Bearbeiten | Quelltext bearbeiten]

Scheitert die Ermittlung des Floppy-Typs (zum Beispiel, weil das ROM der Floppy ausgetauscht wurde), so greift FLOAD für die Datenübertragung auf die ROM-Routinen des Kernals zurück. Hierfür wird der C64-seitige Schnelllader mit der folgenden Routine überschrieben, die dann für die Übertragung jedes einzelnen Datenbyte aufgerufen wird. Die Ladezeit erhöht sich dadurch um den Faktor 3-4.

; Routine basierend auf Kernal ROM-Routinen
P_IA: BIT $A2AB      ; ?
      LDA $D01A      ; IRQ-Maske des VIC
      STA IA00+$01   ; in Programmcode sichern
      LDA $DC0D      ; IRQ-Maske von CIA1
      STA IA01+$01   ; in Programmcode sichern
      LDA #$00       ; Interruptmaske des VIC löschen
      STA $D01A
      LDA #$7F       ; Interruptmaske von CIA1 löschen
      STA $DC0D
      LDA #$4C       ; Opcode für "JMP abs"
      STA AA01
      LDA #$2C       ; Opcode für "BIT abs"
      STA AA01
      LDA #$0E       ; Logische Dateinummer
      LDX *$BA       ; Geräteadresse
      LDY #$00       ; Sekundäradresse
      JSR $FFBA      ; Dateiparameter setzen
      JSR $FFC0      ; OPEN (Dateinamen durch vorausgegangenen Aufruf von P_BI bekannt)
      LDX #$0E       ; Logische Dateinummer
      JSR $FFC6      ; CHKIN Eingabegerät setzen
      SEI            ; Interrupts abschalten während Kernal ausgeblendet
      JSR P_DB       ; Datei byteweise lesen und entpacken
      LDA #$36       ; BASIC-ROM ausblenden
      STA *$01
      JSR P_IB       ; Datei schließen
      SEI
IA00: LDA #$00       ; IRQ-Maske des VIC wiederherstellen
      STA $D01A
IA01: LDA #$00       ; IRQ-Maske von CIA1 wiederherstellen
      STA $DC0D
      RTS

P_IB: LDA #$0E       ; Logische Dateinummer
      JSR $FFC3      ; CLOSE
      JMP $FFCC      ; CLRCH Ein-/Ausgabe rücksetzen

; Ein Byte per ROM-Routinen von Floppy einlesen
P_IC: PHP            ; Flags retten
      LDA #$36       ; BASIC-ROM aus Adressraum ausgeblendet, KERNAL und I/O eingeblendet
      STA *$01
      STX IC00+$01   ; Inhalt von X in Programmcode retten
      JSR $FFCF      ; BASIN Eingabe eines Zeichens
      LDX #$35       ; Nur I/O in Adressraum eingeblendet
      INC $D020      ; Bildschirmrahmen kurz aufblitzen lassen
      DEC $D020
      STX *$01
IC00: LDX #$00       ; Inhalt von X wiederherstellen
      PLP            ; Flags wiederherstellen
      RTS

Floppy-seitige Schnelllade-Routine für VC-1541/1541-II[Bearbeiten | Quelltext bearbeiten]

Die folgenden Routine P_XB liest den nachzuladenden Programmteil Sektor für Sektor in Puffer 3 (ab Adresse $0600) ein und folgt dabei den Verkettungszeigern in den ersten beiden Bytes jedes Sektors. Die Befehlssequenz ab P_XA wird mit Hilfe des Jobcode $E0 (Programm in Jobschleife einbinden) aufgerufen, liest den gewünschten Sektor in Form von GCR-Bytes vom Lesekopf der Floppy und decodiert diese anschließend durch Aufruf der entsprechenden ROM-Routine. Anschließend werden alle Datenbytes ab Offset 2 (also ohne die Verkettung zum nächsten Programmblock) an den Rechner übermittelt.

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

Vor und nach der Übertragung jedes Datenbyte dient die CLOCK-Leitung dazu, die Sendebereitschaft der Floppy zu signalisieren: Während eines Low-Pegels ist die Floppy anderweitig beschäftigt, liest oder decodiert also möglicherweise gerade einen Programmblock; ein High-Pegel weist auf ein zum Transfer bereitstehendes Datenbyte hin.

TARGET1 EQU $0500
SOURCE1:

P_XA: LDA #$06     ; High-Byte des Adresszeigers auf Puffer an $0600 richten
      STA *$31
      JSR $F50A    ; Lesekopf auf Position nach Datenblock-SYNC-Markierung eines Sektors setzen
XA00: BVC XA00     ; Auf Byte von Leseelektronik warten
      CLV          ; Leseelektronik wieder bereit machen
      LDA $1C01    ; Gelesenes GCR-Byte holen
      STA ($30),Y  ; und in Puffer schreiben
      INY          ; Schreibzeiger erhöhen
      BNE XA00     ; Rücksprung falls noch nicht 256 Bytes empfangen
      LDY #$BA     ; Zeiger auf Zusatzpuffer
XA01: BVC XA01     ; Auf Byte von Leseelektronik warten
      CLV          ; Leseelektronik wieder bereit machen
      LDA $1C01    ; Gelesenes GCR-Byte holen
      STA $0100,Y  ; und in Zusatzpuffer ab $01BA schreiben
      INY          ; Schreibzeiger erhöhen
      BNE XA01     ; Rücksprung falls noch nicht 70 Bytes empfangen
      JSR $F8E0    ; Puffer von GCR nach Binär umwandeln
      LDA *$3A     ; Prüfsumme holen
      LDX #$00     ; Gelesene Bytes mit Prüfsumme EOR-verknüpfen
XA02: EOR $0600,X  ; Beschleunigung durch loop unrolling
      EOR $0680,X
      INX          ; Lesezeiger erhöhen
      BPL XA02     ; Rücksprung falls noch nicht bei 128 angekommen
      TAX          ; Ergebnis der Überprüfung testen, muss $00 sein
      BNE P_XA     ; Bei Prüfsummenfehler erneuter Leseversuch
      LDA #$02     ; Erst ab Byte 2 übertragen (Verkettungszeiger ignorieren)
      STA XA03+$01 ; als Low-Byte des Adresszeigers speichern
      LDX #$00
XA03: LDA $0600    ; Datenbyte aus Puffer holen
      TAY          ; und nach Y kopieren
      AND #$0F     ; untere 4 Bit des zu sendenden Byte isolieren
      STX $1800    ; X=$00, DATA und CLOCK auf high setzen
      TAX          ; Low-Nibble von X=untere 4 Bit des Datenbyte
      TYA          ; Datenbyte aus Y nach A übertragen
      LSR A        ; High-Nibble ins Low-Nibble schieben
      LSR A
      LSR A
      LSR A
      TAY          ; Low-Nibble von Y=obere 4 Bit des Datenbyte
      LDA T100,X   ; Bits in X invertieren, umsortieren und nach A
XA04: LDX $1800    ; Auf DATA high warten
      BEQ XA04
      STA $1800    ; Bit 1..0 auf DATA und CLOCK legen
      ASL A        ; Bit 3..2 in Position bringen
      AND #$0F     ; und isolieren
      STA $1800    ; Bit 3..2 auf DATA und CLOCK legen
      LDA T100,Y   ; Bits in Y invertieren, umsortieren und nach A
      STA $1800    ; Bit 5..4 auf DATA und CLOCK legen
      ASL A        ; Bit 7..6 in Position bringen
      AND #$0F     ; und isolieren
      STA $1800    ; Bit 7..6 auf DATA und CLOCK legen
      INC XA03+$01 ; Adresszeiger auf Puffer weiterbewegen
      BNE XA03     ; Rücksprung falls noch nicht ganzer Puffer übertragen
      JSR P_XC     ; CLOCK low, DATA high setzen
      LDA $0601    ; Sektornummer des nächsten Blocks
      STA *$0B     ; merken
      LDA $0600    ; Spurnummer des nächsten Blocks holen
      CMP *$0A     ; in gleicher Spur?
      BNE XA05     ; Sprung falls Spurwechsel nötig oder letzter Block
      JMP P_XA     ; Rücksprung zum Lesen des nächsten Blocks in der gleichen Spur
XA05: STA *$0A     ; Neue Spurnummer merken
      JMP $F418    ; Rückmeldung "00,OK,0,0" bereitstellen

; Einsprung
P_XB: SEI
      JSR P_XC     ; CLOCK low, DATA high setzen
      LDA $1C00    ; Laufwerksanzeige (LED)
      ORA #$08     ; einschalten
      STA $1C00
      LDA #$28     ; Rate des Jobschleifen-Interrupt erhöhen (alle 10 ms statt alle 14 ms)
      STA $1C07
      LDA #$7A     ; Datenrichtungsregister VIA 2 Port B (Defaultwert $1A)
      STA $1802
      LDA *$19     ; Sektor des zuletzt gelesenen Blocks
      STA *$0B     ; merken
      LDA *$18     ; Spur des zuletzt gelesenen Blocks
      STA *$0A     ; merken
      LDA *$31     ; Zeiger auf aktuelle Position im Datenpuffer
      STA XB02+$01 ; merken
XB00: LDA #$E0     ; Jobcode für "Programm in Jobschleife einbinden"
      STA *$02     ; als Job für Puffer 2 an $0500
      CLI          ; Jobschleife aktivieren
XB01: LDA *$02     ; Warten auf Ausführung
      BMI XB01
      SEI          ; Jobschleife deaktivieren
      LDA *$0A     ; Letzter Block gelesen und übertragen?
      BNE XB00     ; Rücksprung falls nicht letzter Block
      LDA #$1A     ; Datenrichtungsregister VIA 2 Port B
      STA $1802    ; Defaultwert wiederherstellen
      LDA $1C00    ; CLOCK high
      AND #$F7
      STA $1C00
XB02: LDA #$00     ; Zeiger auf aktuelle Position im Datenpuffer
      STA *$31     ; wiederherstellen
      CLI          ; Jobschleife aktivieren
      JMP $C194    ; Fehlerrückmeldung bereitstellen

; CLOCK low, DATA high setzen
P_XC: LDY #$08     ; CLOCK low, DATA high setzen
      STY $1800
      RTS

; Invertierte und umsortierte Bits eines Nibble
T100: DB $0F,$07,$0D,$05,$0B,$03,$09,$01,$0E,$06,$0C,$04,$0A,$02,$08,$00

Floppy-seitige Schnelllade-Routine für VC-1570/1571[Bearbeiten | Quelltext bearbeiten]

Der Floppy-seitige Schnelllader für den Laufwerkstyp VC-1570/V1571 verwendet das gleiche Kommunikationsprotokoll wie die Variante für die VC-1541, verlässt sich beim Einlesen der Programmblöcke von Diskette aber auf den Jobcode $80 (Sektor lesen). Die Routine wird dadurch deulich kompakter.

Um aber trotz eines auf 2 MHz verdoppelten Systemtakts das im Protokoll vorgesehene Timing einhalten und alle 8 μs ein Bitpaar liefern zu können, sind zahlreiche NOPs in den Programmcode eingestreut.

TARGET2 EQU $0500
SOURCE2:

P_YA: JSR P_YB     ; CLOCK low, DATA high setzen
      LDA #$7A     ; Datenrichtungsregister VIA 1 Port B (Defaultwert $1A)
      STA $1802
      LDA #$80
      STA *$3B
      JSR $904E    ; Auf 1571-Modus umschalten
      SEI
      LDA #$28     ; Rate des Jobschleifen-Interrupt erhöhen (alle 10 ms statt alle 14 ms)
      STA $1C07
      LDA $1C00    ; CLOCK auf low setzen (unnötig, da bereits bei P_YA geschehen)
      ORA #$08
      STA $1C00
      LDA *$31     ; Zeiger auf aktuellen Puffer
      STA YA04     ; im Programmcode merken
      LDX *$19     ; Sektornummer des ersten Programmblocks
      LDA *$18     ; Spurnummer des ersten Programmblocks
YA00: STX *$0D     ; als Sektornummer für Puffer 3 an $0600 merken
      STA *$0C     ; als Spurnummer für Puffer 3 an $0600 merken
      LDA #$80     ; Jobcode für "Sektor lesen"
      STA *$03     ; als Job für Puffer 3 an $0600
      CLI          ; Jobschleife aktivieren
YA01: LDA *$03     ; Warten auf Ausführung
      BMI YA01
      SEI          ; Jobschleife deaktivieren
      LDA #$02     ; Erste beide Bytes jedes Blocks (Verkettungszeiger) nicht senden
      STA YA02+$01 ; Lesezeiger im Programmcode initialisieren
YA02: LDA $0600    ; Datenbyte aus Puffer holen
      LDY #$00     ; DATA und CLOCK auf high setzen
      STY $1800
      TAY          ; Y=Datenbyte
      AND #$0F     ; untere 4 Bit des zu sendenden Byte isolieren
      TAX          ; Low-Nibble von X=untere 4 Bit des Datenbyte
      TYA          ; Datenbyte aus Y nach A übertragen
      LSR A        ; High-Nibble ins Low-Nibble schieben
      LSR A
      LSR A
      LSR A
      TAY          ; Low-Nibble von Y=obere 4 Bit des Datenbyte
      LDA #$01
YA03: BIT $1800    ; Auf Data high warten
      BEQ YA03
      LDA T200,X   ; Bits in X invertieren, umsortieren und nach A
      NOP          ; 4 Systemtakte (2 us) Verzögerung wegen 2 MHz Systemtakt
      NOP
      STA $1800    ; Bit 1..0 auf DATA und CLOCK legen
      ASL A        ; Bit 3..2 in Position bringen
      AND #$0F     ; und isloieren
      NOP          ; 8 Systemtakte (4 us) Verzögerung wegen 2 MHz Systemtakt
      NOP
      NOP
      NOP
      STA $1800    ; Bit 3..2 auf DATA und CLOCK legen
      NOP          ; 4 Systemtakte (2 us) Verzögerung wegen 2 MHz Systemtakt
      NOP
      LDA T200,Y   ; Bits in Y invertieren, umsortieren und nach A
      NOP          ; 4 Systemtakte (2 us) Verzögerung wegen 2 MHz Systemtakt
      NOP
      STA $1800    ; Bit 5..4 auf DATA und CLOCK legen
      ASL A        ; Bit 7..6 in Position bringen
      AND #$0F     ; und isolieren
      NOP          ; 8 Systemtakte (4 us) Verzögerung wegen 2 MHz Systemtakt
      NOP
      NOP
      NOP
      STA $1800    ; Bit 7..6 auf DATA und CLOCK legen
      NOP
      NOP
      INC YA02+$01 ; Lesezeiger im Programmcode erhöhen
      BNE YA02     ; Rücksprung falls noch nicht ganzer Puffer übertragen
      JSR P_YB     ; CLOCK low, DATA high setzen
      LDX $0601    ; Sektornummer des nächsten Blocks
      LDA $0600    ; Spurnummer des nächsten Blocks
      BNE YA00     ; Rücksprung falls nicht letzter Block
      LDA #$1A     ; Datenrichtungsregister VIA 1 Port B wiederherstellen
      STA $1802
      LDA $1C00    ; Floppy-LED ausschalten
      AND #$F7
      STA $1C00
YA04: LDA #$00     ; Zeiger auf aktuellen Puffer
      STA *$31     ; wiederherstellen
      CLI          ; Jobschleife aktivieren
      JMP $C194    ; Rückmeldung bereitstellen

; CLOCK low, DATA high setzen
P_YB: LDA #$08  ; CLOCK low, DATA high setzen
      STA $1800
      RTS

; Invertierte und umsortierte Bits eines Nibble
T200: DB $0F,$07,$0D,$05,$0B,$03,$09,$01,$0E,$06,$0C,$04,$0A,$02,$08,$00

Floppy-seitige Schnelllade-Routine für VC-1581[Bearbeiten | Quelltext bearbeiten]

Der Floppy-seitige Schnelllader arbeitet sehr ähnlich wie die Variante für die VC-1570/1751, verwendet dabei aber natürlich die für die VC-1581 gültigen Speicheradressen und Mechanismen. Insbesondere ruft er zur Ausführung des Jobcode $80 (Sektor lesen) den Floppycontroller direkt auf, anstatt auf eine Ausführung durch die Jobschleife zu warten.

TARGET3 EQU $0900
SOURCE3:

P_ZA: SEI          ; Jobschleife deaktivieren
      JSR P_ZB     ; CLOCK auf low setzen
      LDA #$94     ; ?
      LDA $004C    ; Spurnummer holen
      LDX $028B    ; Sektornummer holen
ZA00: STA $0015    ; als Spur für Puffer 5 setzen
      STX $0016    ; als Sektor für Puffer 5 setzen
      JSR P_ZC     ; Jobcode $80 (Lesen eines Sektors) für Puffer 5 ausführen
      LDA #$02     ; Schreibzeiger hinter Verkettungspointer setzen
      STA ZA01+$01 ; und in Code einarbeiten
ZA01: LDA $0800    ; Puffer 5 byteweise lesen
      LDY #$00     ; CLOCK auf high setzen
      STY $4001
      TAY          ; Datenbyte nach Y
      AND #$0F     ; Untere 4 Bit des Datenbyte isolieren
      TAX          ; und nach X
      TYA          ; Datenbyte aus Y zurückholen
      LSR A        ; obere 4 Bit nach Bit 3..0 verschieben
      LSR A
      LSR A
      LSR A
      TAY          ; und nach Y
      LDA #$01
ZA02: BIT $4001    ; Warten auf DATA low
      BEQ ZA02     ; DATA high nach 8 us wird nicht geprüft
      LDA T300,X   ; Datenbits 3..0 invertieren und umsortieren
      NOP          ; 4 Systemtakte (2 us) Verzögerung wegen 2 MHz Systemtakt
      NOP
      STA $4001    ; Bit 1..0 auf DATA und CLOCK legen
      ASL A        ; Bit 3..2 in Position bringen
      AND #$0F     ; und isolieren
      NOP          ; 8 Systemtakte (2 us) Verzögerung wegen 2 MHz Systemtakt
      NOP
      NOP
      NOP
      STA $4001    ; Bit 3..2 auf DATA und CLOCK legen
      NOP          ; 4 Systemtakte (2 us) Verzögerung wegen 2 MHz Systemtakt
      NOP
      LDA T300,Y   ; Datenbits 7..4 invertieren und umsortieren
      NOP          ; 4 Systemtakte (2 us) Verzögerung wegen 2 MHz Systemtakt
      NOP
      STA $4001    ; Bit 5..4 auf DATA und CLOCK legen
      ASL A        ; Bit 7..6 in Position bringen
      AND #$0F     ; und isolieren
      NOP          ; 8 Systemtakte (2 us) Verzögerung wegen 2 MHz Systemtakt
      NOP
      NOP
      NOP
      STA $4001    ; Bit 7..6 auf DATA und CLOCK legen
      NOP          ; 4 Systemtakte (2 us) Verzögerung wegen 2 MHz Systemtakt
      NOP
      INC ZA01+$01 ; Lesezeiger weiterbewegen
      BNE ZA01     ; Rücksprung falls noch nicht ganzer Puffer gelesen
      JSR P_ZB     ; CLOCK auf low setzen
      LDX $0801    ; Sektor des nächsten Blocks holen
      LDA $0800    ; Spur des nächsten Blocks holen
      BNE ZA00     ; Rücksprung falls nicht letzter Block
      LDA #$96     ; ?
      CLI          ; Jobschleife aktivieren
      RTS

P_ZB: LDA #$08  ; CLOCK auf low setzen
      STA $4001
      RTS

P_ZC: LDA #$80  ; Jobcode $80 (Lesen eines Sektors)
      LDX #$05  ; Puffernummer
      JMP $FF54 ; STROBE_CONTROLLER: Aufruf des Controllers

; Invertierte und umsortierte Bits eines Nibble
T300: DB $0F,$07,$0D,$05,$0B,$03,$09,$01,$0E,$06,$0C,$04,$0A,$02,$08,$00