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