FLOAD/Quellcode 2.2
<< 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