G.I. Joe/Schnelllader
<< 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