G.I. Joe/Schnelllader

Aus C64-Wiki
Zur Navigation springenZur Suche springen

<< zurück zu G.I. Joe


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

Aufruf des Schnellladers aus dem Hauptprogramm heraus[Bearbeiten | Quelltext bearbeiten]

Der folgende Codeabschnitt zeigt, wie durch mehrmaliges Aufrufen der C64-seitigen Schnelllade-Routine nacheinander mehrere Programmteile von Diskette nachgeladen werden. Bemerkenswert ist insbesondere, dass der im X- und Y-Register gespeicherte Dateiname von der Routine nicht verändert wird, so dass nur eines der beiden Register angepasst werden muss, wenn sich die Namen zweier nacheinander geladener Dateien nur in einem Zeichen unterscheiden.

; Nachladen der ersten Programmteile für das Spiel
P_PY: LDX #$54  ; 'T'
      LDY #$49  ; 'I'
      JSR P_PZ  ; Datei "TI" laden
      LDY #$43  ; 'C'
      JSR P_PZ  ; Datei "TC" laden
      LDY #$44  ; 'D'
      JSR P_PZ  ; Datei "TD" laden
      LDX #$43  ; 'C'
      LDY #$52  ; 'R'
      JSR P_PZ  ; Datei "CR" laden
      LDX #$4D  ; 'M'
      LDY #$55  ; 'U'
      JSR P_PZ  ; Datei "MU" laden
      LDX #$54  ; 'T'
      LDY #$41  ; 'A'
      JSR P_PZ  ; Datei "TA" laden
      JMP ...

P_PZ: JSR P_PA  ; C64-seitigen Schnelllader aufrufen
      BCS P_PZ  ; Bei Lesefehler (erkennbar an CF=1) immer wieder versuchen
      RTS

Startroutinen[Bearbeiten | Quelltext bearbeiten]

Die nachfolgende Routine P_AC überträgt die Floppy-seitigen Schnelllade-Routinen mittels des "M-W"-Befehls ("Memory-Write") in Abschnitten zu jeweils 16 Bytes in das RAM der Floppy ab Adresse TARGET1=$0500. Anschließend wird der Schnelllader dort mittels "M-E" ("Memory-Execute") gestartet. Jeder dieser Befehle wird mit einem Carriage Return (ASCII-Code $0D) abgeschlossen.

; Floppy-seitige Schnelllade-Routine in Floppy-Speicher schreiben und dort starten
P_IA: JSR P_IB      ; Befehlskanal der Floppy öffnen
      LDA #<SOURCE1 ; Low-Byte der Anfangsadresse des Floppy-seitigen Schnellladers
      LDX #>SOURCE1 ; High-Byte der Anfangsadresse des Floppy-seitigen Schnellladers
      STA *$9E      ; merken
      STX *$9F      ; merken
IA00: JSR P_ID      ; "Memory Write" ("M-W")-Befehl an Floppy senden
      LDY #$00      ; Lesezeiger initialisieren
IA01: LDA ($9E),Y   ; Floppy-seitigen Schnelllader byteweise lesen
      JSR P_IC      ; und an Floppy senden
      INY           ; Lesezeiger erhöhen
      CPY #16       ; Blockgröße erreicht?
      BNE IA01      ; Sprung wenn noch nicht erreicht
      LDA #$0D      ; <CR>
      JSR P_IC      ; an Floppy senden
      JSR P_IF      ; Befehlskanal der Floppy schließen
      JSR P_IB      ; Befehlskanal der Floppy öffnen
      LDA *$9E      ; Low-Byte der Adresse
      CLC
      ADC #16       ; Blockgröße addieren
      STA *$9E      ; und zurückschreiben
      BCC IA02      ; Sprung falls kein Additionsübertrag
      INC *$9F      ; Additionsübertrag berücksichtigen
IA02: CMP #<SRC1END ; Ende des Floppy-seitigen Schnellladers erreicht (Low-Byte)
      LDA *$9F
      SBC #>SRC1END ; Ende des Floppy-seitigen Schnellladers erreicht (High-Byte)
      BCC IA00      ; Sprung falls noch nicht erreicht
      JSR P_IE      ; "Memory-Execute" ("M-E")-Befehl an Floppy senden
      JSR P_IF      ; Befehlskanal der Floppy schließen
      LDA #$C7      ; CLOCK, DATA und ATN auf high
      STA $DD00
      LDX #$00      ; Etwa 337 ms Verzögerung
IA03: DEY
      BNE IA03
      DEX
      BNE IA03
      RTS

; Befehlskanal der Floppy öffnen
P_IB: LDX #DEVICE   ; Geräteadresse
      LDA #$0F      ; logische Dateinummer
      TAY           ; gleich Sekundäradresse
      JSR $FFBA     ; Dateiparameter setzen
      LDA #$00      ; Länge des Dateinamens
      JSR $FFBD     ; Dateinamenparameter setzen
      JSR $FFC0     ; OPEN
      LDX #$0F      ; logische Dateinummer
      JSR $FFC9     ; CKOUT Ausgabegerät setzen
      RTS

; Zeichen in <A> an Floppy senden
P_IC: STY $CFF8     ; Y retten
      JSR $FFD2     ; Ausgabe des Zeichens in A (BSOUT)
      LDY $CFF8     ; Y zurückholen
      RTS

; "Memory-Write" ("M-W")-Befehl an Floppy senden
P_ID: LDA #$4D      ; 'M'
      JSR P_IC
      LDA #$2D      ; '-'
      JSR P_IC
      LDA #$57      ; 'W'
      JSR P_IC
      LDA *$9E      ; Aus Leseadresse Zieladresse im Floppy-RAM ableiten
      SEC
      SBC #<[SOURCE1-P_AA]
      PHP           ; Eventuellen Additionsübertrag retten
      CLC
      JSR P_IC      ; Low-Byte der Zieladresse senden
      PLP           ; Additionsübertrag zurückholen
      LDA *$9F
      SBC #>[SOURCE1-P_AA]
      CLC
      JSR P_IC      ; High-Byte der Zieladresse senden
      LDA #16       ; Blockgröße
      JSR P_IC      ; senden
      RTS

; "Memory-Execute" ("M-E")-Befehl an Floppy senden
P_IE: LDY #$00      ; Lesezeiger initialisieren
IE00: LDA T100,Y    ; Befehl byteweise lesen
      JSR P_IC      ; und an Floppy senden
      INY           ; Lesezeiger erhöhen
      CPY #$06      ; Schon ganzen Befehl übertragen
      BNE IE00      ; Sprung wenn noch nicht
      RTS

T100: DB 'M-E',$00,$05,$0D

; Befehlskanal der Floppy schließen
P_IF: JSR $FFCC     ; Ein-/Ausgabe rücksetzen (CLRCH)
      LDA #$0F      ; logische Dateinummer
      JSR $FFC3     ; CLOSE
      RTS

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

Die Floppy-seitige Schnelllade-Routine wartet auf drei Bytes, die den Namen des zu ladenden Programms spezifizieren. Als erstes Byte wird jeweils die Zahl 2 übertragen und vom Floppy-seitigen Schnelllader verworfen; das zweite und dritte Byte bilden zusammen den aus zwei Zeichen bestehenden Dateinamen. Möglicherweise war in einer ersten Version des Schnellladers auch eine Unterstützung für unterschiedlich lange Dateinamen geplant, so dass die Zahl 2 die Länge des aktuellen Dateinamens darstellt — allerdings haben alle Unterprogrammen von G.I Joe einen zwei Zeichen langen Dateinamen, so dass eine Längenangabe obsolet ist.

Anschließend lädt der Schnelllader nacheinander, beginnend mit Sektor 1 in Spur 18, mit Hilfe des Jobcode $80 ("Lesen eines Sektors") alle Blocks des Directory und durchsucht sämtliche Einträge nach einer Datei mit dem gewünschten Namen. Wird er dabei fündig, so erfährt er damit die Spur- und Sektornummer des ersten Programmblocks und kann mit dem Laden der gewünschten Datei beginnen. Wird dagegen das Ende des Directory erreicht, ohne dass der Schnelllader auf den gewünschten Dateinamen stößt, so meldet er mit dem Steuercode $AF $F7 einen Fehler.

Nach einer Aufforderung, die Disketten zu wenden, prüft das Spiel anhand einer auf beiden Seiten vorhandenen, kurzen Datei mit dem Namen "YU", ob die gewünschte Seite gelesen werden kann — auf der ersten Seite besteht der Inhalt dieser Datei nur aus Bytes mit dem Wert $01, auf der zweiten Seite aus einem kurzen Programmblock.

Nach dem Laden des vollständigen Programms meldet der Schnellader mit Hilfe des Code $AC $FF das Dateiende und kehrt dann in den Ausgangszustand zurück, erwartet also den Namen der nächsten nachzuladenden Datei. Erst nach einem Reset wechselt die Floppy wieder in den normalen Betriebsmodus.

DEVICE EQU $08 ; Geräteadresse 8

SOURCE1:

; Hauptschleife des Floppy-seitigen Schnellladers
P_AA: JSR P_AF    ; 8 Impulse von etwa 1,3 ms Dauer auf DATA erzeugen
AA00: JSR P_AB    ; 3 Bytes (Byte $02 und Dateinamen) empfangen, erstes ignorieren
      LDA *$0E    ; Erstes Zeichen des empfangenen Dateinamens holen
      STA T000    ; und merken
      LDA *$0F    ; Zweites Zeichen des empfangenen Dateinamens holen
      STA T001    ; und merken
; Directory nach empfangenem Dateinamen durchsuchen
      LDY #$01    ; Erster Sektor des Directory
AA01: LDX #$12    ; Spur des Directory
      STX *$0E    ; als Spur für Puffer 4 an Adresse $0700 merken
      STY *$0F    ; als Sektor für Puffer 4 an Adresse $0700 merken
      JSR P_AC    ; Sektor aus Directory lesen
      LDY #$02    ; Zeiger auf erstes Datenbyte des Directory
AA02: LDA $0700,Y ; Dateityp holen
      AND #$83    ; Schreibschutzbit löschen
      CMP #$82    ; Korrekt geschlossenes Programm (PRG)?
      BNE AA03    ; Sprung falls nicht
      LDA $0703,Y ; Erstes Zeichen des Dateinamens im Directory lesen
      CMP T000    ; und mit Namen der zu ladenden Datei vergleichen
      BNE AA03    ; Sprung falls nicht gleich
      LDA $0704,Y ; Zweites Zeichen des Dateinamens im Directory lesen
      CMP T001    ; und mit Namen der zu ladenden Datei vergleichen
      BNE AA03    ; Sprung falls nicht gleich
      JMP AA05    ; Gewünschte Datei gefunden, Sprung zur Laderoutine
; Zum nächsten Directoryeintrag übergehen
AA03: TYA         ; Lesezeiger nach A
      CLC         ; auf nächsten Directoryeintrag richten
      ADC #$20
      TAY         ; und zurück nach Y
      BCC AA02    ; Rücksprung falls nächster Eintrag im gleichen Sektor
      LDY $0701   ; Sektor des nächsten Blocks im Directory holen
      BPL AA01    ; Sprung falls nicht letzter Block
; Gewünschte Datei nicht gefunden, Fehlercode ($AC $F7) senden
AA04: LDA #$00    ; DATA und CLOCK high, ATN automatisch beantworten
      STA $1800
      LDX #$FE    ; Low-Byte der Dummy-Ladeadresse
      JSR P_AD    ; per DATA versenden, synchronisiert per CLOCK
      LDX #$FE    ; High-Byte der Dummy-Ladeadresse
      JSR P_AD    ; per DATA versenden, synchronisiert per CLOCK
      LDX #$AC    ; Datenbyte $AC (leitet Steuercode eine)
      JSR P_AD    ; per DATA versenden, synchronisiert per CLOCK
      LDX #$F7    ; Datenbyte $F7 (ergibt Steuercode $AC $F7, Ladefehler)
      JSR P_AD    ; per DATA versenden, synchronisiert per CLOCK
      JMP AA00    ; 3 Bytes empfangen
; Gewünschte Datei gefunden
AA05: LDA $0701,Y ; Spur des ersten Blocks des Programms
      STA *$0E    ; als Spur für Puffer 4 an Adresse $0700 merken
      LDA $0702,Y ; Sektor des ersten Blocks des Programms
      STA *$0F    ; als Sektor für Puffer 4 an Adresse $0700 merken
AA06: JSR P_AC    ; Sektor lesen mit Fehlerbehandlung
      LDY #$00    ; Zeiger hinter letztes gültiges Datenbyte im Block
      LDA $0700   ; Spur des nächsten Blocks des Programms
      STA *$0E    ; als nächste Spur für Puffer 4 an Adresse $0700 merken
      BNE AA07    ; Sprung falls nicht letzter Block
      LDY $0701   ; sonst als neuen Zeiger hinter letztes Datenbyte merken
      INY
AA07: STY T000    ; Zeiger hinter letztes Datenbyte merken
      LDA $0701   ; Sektor des nächsten Blocks des Programms
      STA *$0F    ; als nächsten Sektor für Puffer 4 an Adresse $0700 merken
      LDY #$02    ; Lesezeiger zeigt auf erstes gültiges Datenbyte
      LDA #$00    ; DATA und CLOCK high
      STA $1800
AA08: LDX $0700,Y ; Datenbyte aus Block holen
      CPX #$AC    ; Steuerzeichen $AC?
      BNE AA09    ; Sprung wenn nicht Steuerzeichen
      JSR P_AD    ; Steuerzeichen $AC wiederholen (Bytestuffing)
      LDX #$AC    ; X wieder mit Steuerzeichen $AC laden
AA09: JSR P_AD    ; Datenbyte in X versenden
      INY         ; Lesezeiger erhöhen
      CPY T000    ; Letztes Datenbyte versendet?
      BNE AA08    ; Sprung falls nicht letztes Datenbyte
      LDA $0700   ; Spur des nächsten Programmblocks holen
      BEQ AA10    ; Sprung falls letzter Block
      LDX #$AC    ; Sonderzeichen $AC
      JSR P_AD    ; versenden
      LDX #$C3    ; Code für "Datenübertragung anhalten" ($AC $C3)
      JSR P_AD    ; versenden
      LDA #$08    ; DATA high, CLOCK low
      STA $1800
      JMP AA06    ; Nächsten Sektor lesen
AA10: LDX #$AC    ; Sonderzeichen $AC
      JSR P_AD    ; versenden
      LDX #$FF    ; Code für "Datenübertragung abgeschlossen" ($AC $FF)
      JSR P_AD    ; versenden
      JMP AA00    ; Dateinamen des nächsten zu ladenden Programms holen

; Drei Bytes empfangen, erstes Byte in <A>, zweites und drittes Byte an Adresse $0E/$0F zurückliefern
P_AB: LDA #$08    ; DATA high, CLOCK low
      STA $1800
      LDA $1C00   ; Laufwerksanzeige (LED) aus
      AND #$F7
      STA $1C00
      CLI         ; Interrupts zulassen
      LDA #$01    ; Warten auf DATA low
AB00: BIT $1800
      BEQ AB00
      SEI         ; Interrupts verbieten
      LDA #$00    ; DATA high, CLOCK high
      STA $1800
      JSR P_AE    ; Ein Byte empfangen
      PHA         ; und retten (Länge des Dateinamens?)
      JSR P_AE    ; Noch ein Byte empfangen
      STA *$0E    ; und als erstes Zeichen des Dateinamen merken
      JSR P_AE    ; Noch ein Byte empfangen
      STA *$0F    ; und als zweites Zeichen des Dateinamen merken
      LDA #$08    ; DATA high, CLOCK low
      STA $1800
      LDA $1C00   ; Laufwerksanzeige (LED) an
      ORA #$08
      STA $1C00
      PLA         ; Zuerst empfanges Byte nach A zurückholen
      RTS

; Sektor lesen mit Fehlerbehandlung
P_AC: LDY #$05    ; Anzahl Leseversuche
      STY *$8B    ; merken
AC00: CLI         ; Interrupts zulassen, Jobschleife aktivieren
      LDA #$80    ; Jobcode "Sektor lesen"
      STA *$04    ; als Job für Puffer 4 an Adresse $0700
AC01: LDA *$04    ; Warten bis Job "Sektor lesen" erledigt
      BMI AC01
      CMP #$01    ; Kein Fehler?
      BEQ AC05    ; Sprung falls kein Fehler
      DEC *$8B    ; Anzahl Leseversuche vermindern
      LDY *$8B    ; Anzahl Leseversuche holen
      BMI AC04    ; Sprung falls schon alle Leseversuche
      CPY #$02    ; Schon 3 Leseversuche?
      BNE AC02    ; Rücksprung falls nicht 3 Leseversuche
      LDA #$C0    ; Jobcode "Kopf auf Spur 0 setzen"
      STA *$04    ; als Job für Puffer 4 an Adresse $0700
AC02: LDA *$16    ; Erstes Zeichen der ID des zuletzt gelesenen Sektorheaders
      STA *$12    ; als erstes Zeichen der ID der Diskette setzen
      LDA *$17    ; Zweites Zeichen der ID des zuletzt gelesenen Sektorheaders
      STA *$13    ; als zweites Zeichen der ID der Diskette setzen
AC03: LDA *$04    ; Warten bis Job "Kopf aif Spur 0 setzen" erledigt
      BMI AC03
      BPL AC00    ; Immer Sprung zum nächsten Leseversuch
AC04: PLA         ; Rücksprungadresse vom Stack entfernen
      PLA
      JMP AA04    ; Sprung zur Fehlerbehandlung
AC05: SEI         ; Interrupts wieder verbieten, Jobschleife deaktivieren
      RTS

; Datenbyte in <X> per DATA versenden, Synchronisation durch Pegelwechsel auf CLOCK
P_AD: STX *$14    ; Datenbyte merken
      LDA #$04    ; Bitmaske für CLOCK
      JSR AD00    ; Bit 0 und 1 senden
      JSR AD00    ; Bit 2 und 3 senden
      JSR AD00    ; Bit 4 und 5 senden
AD00: LSR *$14    ; LSB nach CF
      LDX #$02    ; Bit für DATA low
      BCC AD01    ; Sprung falls LSB gelöscht
      LDX #$00    ; Bit für DATA high
AD01: BIT $1800   ; Warten auf CLOCK high
      BNE AD01
      STX $1800   ; LSB auf DATA senden
      LSR *$14    ; LSB nach CF
      LDX #$02    ; Bit für DATA low
      BCC AD02    ; Sprung falls LSB gelöscht
      LDX #$00    ; Bit für DATA high
AD02: BIT $1800   ; Warten auf CLOCK low
      BEQ AD02
      STX $1800   ; LSB auf DATA senden
      RTS

; Ein Byte per DATA empfangen, Synchronisation durch Pegelwechsel auf CLOCK
P_AE: LDY #$04    ; 4 Bitpaare empfangen
AE00: LDA #$04    ; Warten auf CLOCK high
AE01: BIT $1800
      BEQ AE01
      LDA $1800
      LSR A       ; /DATA nach CF
      ROR *$14    ; und von links in Adresse $14 schieben
      LDA #$04    ; Warten auf CLOCK low
AE02: BIT $1800
      BNE AE02
      LDA $1800
      LSR A       ; /DATA nach CF
      ROR *$14    ; und von A in Adresse $14 schieben
      DEY         ; Zähler für Bitpaare vermindern
      BNE AE00    ; Rücksprung falls noch nicht 4 Bitpaare
      LDA *$14    ; Empfangenes Byte in A zurückliefern
      RTS

; 8 Impulse von etwa 1,3 ms Dauer auf DATA erzeugen
P_AF: SEI
      CLD
      LDY #$08    ; Insgesamt 8 Durchläufe
AF00: LDA #$10
      STA $1800   ; CLOCK und DATA high, ATN nicht automatisch beantworten
AF01: DEX         ; Verzögerung
      BNE AF01
      LDA #$00
      STA $1800   ; CLOCK und DATA high, ATN automatisch beantworten
AF02: DEX         ; Verzögerung
      BNE AF02
      DEY         ; Zähler für Durchläufe vermindern
      BNE AF00    ; Rücksprung falls noch nicht alle Durchläufe
AF03: LDA $1800   ; Warten auf DATA high und CLOCK high
      AND #$05
      BNE AF03
      LDA $1800   ; Warten auf DATA high und CLOCK high
      AND #$05
      BNE AF03
      RTS

; Dateiname des nachzuladenden Programms
T000: DB $00
T001: DB $00
T002:

SRC1END EQU SOURCE1+T002-P_AA

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

Um einen Programmteil nachzuladen, wird der Routine P_AA aufgerufen, wobei der stets aus zwei Zeichen bestehende Dateiname im X- und Y-Register übergeben wird. Die Routine lässt den Inhalt dieser Register unverändert, so dass nur eines der Register angepasst werden muss, wenn sich die Namen zweier nacheinander geladener Dateien nur in einem Zeichen unterscheiden. Im Erfolgsfall löscht die Routine das Carry-Flag, im Fehlerfall ist dieses Flag anschließend gesetzt.

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

; Datei mit Namen in <X> (erstes Zeichen) und <Y> (zweites Zeichen) laden
P_PA: LDA $DD00    ; Leitungsstatus
      AND #$0F     ; Zustand von ATN, RS232 und VIC-Bank isolieren
      STA PF01+$01 ; speichern als "DATA high, CLOCK high"
      EOR #$10     ; CLOCK invertieren
      STA P_PF+$01 ; speichern als "DATA high, CLOCK low"
      EOR #$30     ; DATA und CLOCK invertieren
      STA PB00+$01 ; speichern als "DATA low, CLOCK high"
      EOR #$B0     ; CLOCK invertieren
      STA P_PD+$01 ; speichern als "DATA low, CLOCK low"
      EOR #$50     ; CLOCK invertieren
      STA PD00+$01 ; speichern als "DATA low, CLOCK high"
      STA PG02+$01
      LDA #$02     ; Unnötig (Länge des Dateinamens?)
      JSR P_PB     ; A, X und Y (Länge und Name aus zwei Zeichen) senden
      JSR P_PG     ; Verzögerung, warten auf CLOCK high
      JSR P_PC     ; Datenbyte in A empfangen
      STA *$9E     ; als Low-Byte der Ladeadresse speichern
      JSR P_PC     ; Datenbyte in A empfangen
      STA *$9F     ; als High-Byte der Ladeadresse speichern
      LDY #$00     ; Schreibzeiger initialisieren
PA00: JSR P_PC     ; Datenbyte in A empfangen
      CMP #$AC     ; Steuercode $AC?
      BNE PA01     ; Sprung falls nicht
      JSR P_PC     ; Nächstes Zeichen in A empfangen
      CMP #$AC     ; Datenbyte $AC mit Bytestuffing? ($AC $AC)
      BEQ PA01     ; Sprung falls Datenbyte $AC
      CMP #$FF     ; Dateiende ($AC $FF)?
      BEQ PA03     ; Sprung falls Dateiende
      CMP #$F7     ; Datei nicht gefunden ($AC $F7)?
      BEQ PA04     ; Sprung falls Datei nicht gefunden
      JSR P_PG     ; Verzögerung, weil Blockende ($AC C3)
      JMP PA00     ; Empfang fortsetzen
PA01: STA ($9E),Y  ; Empfangenes Byte an Ladeadresse schreiben
      INY          ; Schreibzeiger erhöhen
      BNE PA02     ; Sprung falls noch nicht 256 Bytes
      INC *$9F     ; High-Byte der Ladeadresse erhöhen
PA02: JMP PA00     ; Empfang fortsetzen
PA03: CLC          ; CF=0 signalisiert "Datei geladen"
PA04: LDX T200+$01
      LDY T200+$02
      LDA #$00
      RTS

; Datei mit 2 Zeichen langem Namen in Register X und Y anfordern
P_PB: PHA          ; A retten (vermutlich Länge des Dateinamens, ignoriert)
      STX T200+$01 ; X merken (erstes Zeichen des Dateinamens)
      STY T200+$02 ; Y merken (zweites Zeichen des Dateinamens)
PB00: LDA #$27     ; DATA low, CLOCK high
      STA $DD00
      JSR P_PG     ; Auf CLOCK high warten, dann DATA high setzen
      PLA          ; A zurückholen
      JSR P_PE     ; und versenden
      LDA T200+$01 ; Erstes Zeichen des Dateinamens nach A holen
      JSR P_PE     ; und versenden
      LDA T200+$02 ; Zweites Zeichen des Dateinamens nach A holen
      JSR P_PE     ; und versenden
      RTS

; Datenbyte in <A> empfangen
P_PC: JSR P_PD  ; Bit 0 und 1 empfangen
      JSR P_PD  ; Bit 2 und 3 empfangen
      JSR P_PD  ; Bit 4 und 5 empfangen
      JSR P_PD  ; Bit 6 und 7 empfangen
      JSR PC00  ; 10 Systemtakte Verzögerung
      LDA *$B7  ; Empfangenes Byte nach A holen
PC00: RTS

; Zwei Datenbits empfangen und von rechts in Speicherzelle $B7 schieben
P_PD: LDX #$97
      LDA $DD00 ; Status der Leitungen holen
      STX $DD00 ; DATA high, CLOCK low
      ASL A     ; DATA nach CF
      ROR *$B7  ; und von rechts in Speicherzelle $B7 schieben
      PHA       ; 14 Systemtakte Verzögerung
      PLA
      PHA
      PLA
PD00: LDX #$C7
      LDA $DD00 ; Status der Leitungen holen
      STX $DD00 ; DATA high, CLOCK high
      ASL A     ; DATA nach CF
      ROR *$B7  ; und von rechts in Speicherzelle $B7 schieben
      RTS

; Datenbyte in <A> versenden
P_PE: STA *$B7  ; Datenbyte merken
      JSR P_PF  ; Bit 0 und 1 versenden
      JSR P_PF  ; Bit 2 und 3 versenden
      JSR P_PF  ; Bit 4 und 5 versenden
      JSR P_PF  ; Bit 6 und 7 versenden
      RTS

; Niederwertigeste beide Bits von Speicherzelle $B7 versenden
P_PF: LDA #$17  ; CLOCK low, DATA high
      LSR *$B7  ; LSB von Speicherzelle $B7 nach DATA
      BCC PF00
      ORA #$20  ; DATA low
PF00: STA $DD00 ; Leitungen ansteuern
      NOP       ; 8 Systemtakte Verzögerung
      NOP
      NOP
      NOP
PF01: LDA #$07  ; CLOCK high, DATA high
      LSR *$B7  ; LSB von Speicherzelle $B7 nach DATA
      BCC PF02
      ORA #$20  ; DATA low
PF02: STA $DD00 ; Leitungen ansteuern
      NOP       ; 4 Systemtakte Verzögerung
      NOP
      RTS

; Auf CLOCK high warten, dann DATA high setzen
P_PG: LDX #$32     ; Verzögerung
PG00: DEX
      BNE PG00
PG01: LDA $DD00    ; Warten auf CLOCK high
      AND #$40
      BEQ PG01
PG02: LDA #$C7     ; DATA und CLOCK high
      STA $DD00
      LDA #$FE     ; ?
      STA T200+$03
      LDX #$05     ; 26 Systemtakte Verzögerung
PG03: DEX
      BNE PG03
      RTS

T200: DB $05,$31,$11,$FE,$31,$30,$FE,$FF