Bad Cat/Schnelllader
<< zurück zu Bad Cat
Bad Cat/Schnelllader: Die folgenden Abschnitte stellen den disassemblierten Floppy-Schnelllader des Spiels Bad Cat dar. Sie sind gegliedert in einzelne Codeblöcke, die gemäß ihrer Funktion vor dem und während des Ladens gruppiert sind.
Startroutinen[Bearbeiten | Quelltext bearbeiten]
Die nachfolgende Routine P_AA
überträgt die Floppy-seitigen Schnelllade-Routinen mittels des "M-W"-Befehls ("Memory-Write") in Abschnitten zu jeweils 34 Bytes in das RAM der Floppy ab Adresse TARGET1=$0400
. Anschließend wird der Schnelllader dort mittels "M-E" ("Memory-Execute") gestartet, bevor der C64 die nachzuladende Datei zum Lesen öffnet und dann seine eigene Schnelllade-Routine aufruft. Da während des Transfers in das RAM der Floppy nur das High-Byte der Zieladresse überprüft wird, sendet die Routine nicht nur den 282 Byte langen Code, sondern unnötigerweise auch noch 230 Bytes der darauffolgenden Lookup-Table (insgesamt 512 Bytes).
Beim anschließenden Einlesen der Programmdaten wird für jedes Byte die Schnelllade-Routine P_AF
aufgerufen und des empfangene Byte direkt an seine Ladeadresse geschrieben. Falls diese Schreibzugriffe die Page $CD erreichen, in der der Schnelllader gespeichert ist, und somit diesen zu überschreiben drohen, ist ein Wechsel zur normalen LOAD-Routine im ROM vorgesehen. Da die Hauptschleife des Schnellladers allerdings bereits bei Label AA11
an Adresse $CCF6 beginnt, brächte ein überlanges Programm den Schnelllader trotz dieses Schutzmechanismus zum Absturz.
DEVICE EQU $08 ; Geräteadresse 8 T000: DB $00,$00,$CD T001: DB $22,$06,$00,'W-M' T002: DB $04,$6C,'E-M' P_AA: LDA *$B4 ; Inhalt von Adresse $B4 STA T000+$00 ; retten LDA *$B5 ; Inhalt von Adresse $B5 STA T000+$01 ; retten LDA $DD00 ; CIA2 Port A holen AND #$03 ; Bits für VIC-Bank isolieren STA AF02+$01 ; als Wert für "ATN high" speichern (Selbstmodifikation) ORA #$08 ; Bit für ATN setzen STA P_AF+$01 ; als Wert für "ATN low" speichern (Selbstmodifikation) LDA #$00 ; Alle Sprites abschalten, verhindert VIC-Zugriffe per DMA STA $D015 LDA #$00 STA *$90 ; Status zurücksetzen STA *$B5 ; ? LDA #$F0 ; Sekundäradresse 0 JSR P_AD ; LISTEN senden LDA *$90 ; Status holen BPL AA00 ; Sprung falls Gerät vorhanden JMP $F707 ; "?DEVICE NOT PRESENT ERROR" AA00: LDY #$00 ; Lesezeiger für Dateinamen initialisieren AA01: LDA ($BB),Y ; Zeichen aus Dateinamen lesen JSR $EDDD ; IECOUT INY ; Lesezeiger erhöhen CPY *$B7 ; mit Länge des Dateinamens vergleichen BCC AA01 ; Rücksprung falls noch nicht ganzer Dateiname gesendet JSR $EDFE ; UNLISTEN senden LDA #$00 STA *$90 ; Status zurücksetzen JSR P_AE ; TALK senden LDA #$60 ; Sekundäradresse JSR $EDC7 ; Sekundäradresse nach TALK ausgeben JSR $EE13 ; IECIN ein Zeichen vom IEC-Bus holen LDA *$90 ; Status holen PHA ; und retten JSR $EDEF ; UNTALK senden PLA ; Status zurückholen AND #$02 ; Timeout? BEQ AA02 ; Sprung wenn kein Timeout JMP $F704 ; "?FILE NOT FOUND ERROR" AA02: LDA #<SOURCE1 ; Low-Byte der Quelladresse des Floppy-seitigen Schnelladers LDY #>SOURCE1 ; High-Byte der Quelladresse des Floppy-seitigen Schnelladers STA *$AE ; Low-Byte merken STY *$AF ; High-Byte merken LDY #<TARGET1 ; Low-Byte der Startadresse im Floppy-Speicher LDA #>TARGET1 ; High-Byte der Startadresse im Floppy-Speicher STY T001+$02 ; Low-Byte in "Memory-Write" ("M-W")-Befehl aufnehmen STA T001+$01 ; High-Byte in "M-W"-Befehl aufnehmen AA03: LDA #$6F ; Sekundäradresse 15 (Befehlskanal) JSR P_AD ; LISTEN senden LDX #$06 ; Länge des "M-W"-Befehls AA04: LDA T001-$01,X ; Befehl zeichenweise lesen JSR $FFA8 ; IECOUT DEX ; Lesezeiger erniedrigen BNE AA04 ; Rücksprung falls noch nich alle Zeichen gesendet AA05: LDA ($AE),Y ; Floppy-seitigen Schnellader aus Speicher lesen JSR $FFA8 ; IECOUT INY ; Schreibzeiger erhöhen BNE AA06 ; Sprung falls kein šbertrag INC *$AF ; High-Byte der Leseadresse erhöhen AA06: INC T001+$02 ; Low-Byte der Zieladresse im "M-W"-Befehl erhöhen BNE AA07 ; Sprung falls kein šbertrag INC T001+$01 ; High-Byte der Zieladresse im "M-W"-Befehl erhöhen AA07: LDA T001+$01 ; High-Byte der Zieladresse holen CMP #>TARGET1+$02 ; Schon 2*256=512 Bytes übertragen? BEQ AA08 ; Sprung falls 512 Bytes übertragen INX ; Lesezeiger erhöhen CPX #34 ; Blockgröße erreicht? BNE AA05 ; Rücksprung falls Blockgröáe nicht erreicht JSR $EDFE ; UNLISTEN senden JMP AA03 ; Nächsten Block übertragen AA08: JSR $EDFE LDA #$6F ; Sekundäradresse 15 (Befehlskanal) JSR P_AD ; LISTEN senden LDY #$04 ; Länge des "Memory-Execute" ("M-E")-Befehls minus 1 AA09: LDA T002,Y ; "M-E"-Befehl zeichenweise lesen JSR $FFA8 ; IECOUT DEY ; Lesezeiger erniedrigen BPL AA09 ; Sprung falls noch nicht alle Zeichen gesendet JSR $EDFE ; UNLISTEN senden LDA *$C3 ; Low-Byte der Zieladresse STA *$AE ; umkopieren LDA *$C4 ; High-Byte der Zieladresse STA *$AF ; umkopieren SEI ; Interrupts während Schnellladeroutine verbieten JSR P_AF ; Zeichen per Schnelllader in <A> einlesen PHA ; und retten (Anzahl gültiger Datenbytes) JSR P_AF ; Zeichen per Schnelllader in <A> einlesen TAY ; und retten (Low-Byte der Ladeadresse) JSR P_AF ; Zeichen per Schnelllader in <A> einlesen STA *$B4 ; und merken (High-Byte der Ladeadresse) LDX *$B9 ; Sekundäradresse für LOAD BEQ AA10 ; Sprung falls nicht absolut STA *$AF ; Low-Byte der Ladeadresse vom Programmanfang speichern STY *$AE ; High-Byte der Ladeadresse vom Programmanfang speichern AA10: PLA ; Anzahl gültiger Datenbytes zurückholen AND #$FF ; und testen BNE P_AC ; Sprung falls ungleich null LDA #$FC BNE AA12 AA11: JSR P_AF ; Zeichen per Schnelllader in <A> einlesen AND #$FF ; untersuchen BNE P_AC ; Sprung falls letzter Block des Programms LDA #$FE ; Nicht letzter Block, enthält 254 Datenbytes AA12: STA *$B5 ; Anzahl gültiger Bytes im Datenblock LDY #$00 AA13: JSR P_AF ; Zeichen per Schnelllader in <A> einlesen STA ($AE),Y ; und in an Ladeadresse schreiben INY ; Schreibzeiger erhöhen CPY *$B5 ; mit Anzahl gültiger Bytes im Datenblock vergleichen BNE AA13 ; Rücksprung falls noch nicht alle Bytes empfangen DEC $D020 ; Farbe des Bildschirmrahmens wechseln PHA ; ? Datenbyte retten EOR #$01 ; ? INC $D020 ; Farbe des Bildschirmrands wiederherstellen LDA *$AE ; Low-Byte der Ladeadresse CLC ADC *$B5 ; um Anzahl gültiger Datenbytes erhöhen STA *$AE ; und zurückschreiben LDA *$AF ; High-Byte der Ladeadresse ADC #$00 ; um eventuellen Additionsübertrag erhöhen STA *$AF ; und zurückschreiben INC *$B4 ; High-Byte der Ladeadresse erhöhen PLA ; ? Datenbyte zurückholen INC $D020 ; Farbe des Bildschirmrahmens wechseln LDA *$B4 ; High-Byte der Ladeadresse CMP T000+$02 ; mit Highbyte der Adresse des Schnelladers vergleichen BNE AA11 ; Rücksprung, falls noch nicht erreicht CLI ; sonst Schnellader verlassen, da überschrieben LDA T000+$00 ; Alten Wert von $B4 STA *$B4 ; wiederherstellen LDA T000+$01 ; Alten Wert von B5 STA *$B5 ; wiederherstellen JSR P_AB ; Load-Vektor wiederherstellen LDA #$F0 ; Sekundäradresse 0 JSR P_AD ; LISTEN senden LDA #$2A ; '*' JSR $EDDD ; IECOUT JSR $EDFE ; UNLISTEN senden JSR P_AE ; TALK senden LDA #$60 ; Sekundäradresse 0 STA *$B9 ; merken JSR $EDC7 ; Sekundäradresse nach TALK ausgeben JMP $F501 ; Sprung zur LOAD-Routine ; LOAD-Vektor wiederherstellen P_AB: LDA #$A5 ; Low-Byte des Load-Vektor STA $0330 ; wiederherstellen LDA #$F4 ; High-Byte des Load-Vektor STA $0331 ; wiederherstellen RTS P_AC: LDY *$B5 ; Anzahl gültiger Bytes im aktuellen Block holen BNE AC00 ; Sprung falls letzter Block SEC SBC #$02 ; Länge des Verkettungspointers subtrahieren AC00: STA *$B5 ; und als Zahl zu empfangender Datenbytes merken DEC *$B5 ; Offset subtrahieren LDY #$00 ; Schreibzeiger initialisieren AC01: JSR P_AF ; Byte von Floppy empfangen STA ($AE),Y ; und an Ladeadresse schreiben INY ; Schreibzeiger erhöhen CPY *$B5 ; Schon alle Datenbytes des aktuellen Blocks empfangen? BNE AC01 ; Rücksprung falls noch nicht alle Datenbytes LDA *$AE ; Low-Byte der Ladeadresse holen CLC ADC *$B5 ; um Anzahl der empfangenen Datenbytes erhöhen STA *$AE ; und merken TAX LDA *$AF ; High-Byte der Ladeadresse holen ADC #$00 ; um eventuellen Additionsübertrag erhöhen STA *$AF ; und merken TAY LDA T000+$00 ; Inhalt von Adresse $B4 STA *$B4 ; wiederherstellen LDA T000+$01 ; Inhalt von Adresse $B5 STA *$B5 ; wiederherstellen LDA T001+$00 CMP #$22 BEQ AC02 JSR P_AB AC02: CLC CLI RTS ; LISTEN senden P_AD: PHA ; Sekundäradresse retten LDA #DEVICE ; Geräteadresse JSR $ED0C ; LISTEN senden PLA ; Sekundäradresse zurückholen JMP $EDB9 ; Sekundäradresse nach LISTEN senden ; TALK senden P_AE: LDA #DEVICE ; Geräteadresse JMP $ED09 ; TALK senden
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 einen Low-Pegel auf der ATN-Leitung mit DATA=high (Label AF00
) quittiert. 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 (Bit 2 der Rasterzeile im VIC-Register $D012 gesetzt, Label AF01
).
Nun beendet die Routine mit ATN=high die Synchronisation und empfängt dann nacheinander 4 Bitpaare. Die Reihenfolge dieser Bits ist auf die Registerbelegung des Port B von VIA1 optimiert (nacheinander Bit 5+7, Bit 4+6, Bit 1+3 und Bit 0+2), so dass nur mit aufwändigen Schiebe- und Rotationsbefehlen das ursprüngliche Datenbyte rekonstruiert werden kann — oder mit der bei Bad Cat verwendeten, etwas speicherintensiveren Methode: Vier Lesezugriffe auf eine Lookup-Table, von denen jeder zwei korrekt positionierte Bits liefert, die anschließend nur noch untereinander mit einem logischen ODER verknüpft werden müssen. Während der VIC den Bildschirmrahmen anzeigt, dauert die Übertragung eines Byte mit dieser Routine 105 μs; während der Darstellung des Bildschirminhalts können pro 8 Rasterzeilen (alle 504 Systemtakte) 3 Bytes übertragen werden.
; Datenbyte von Floppy empfangen P_AF: LDA #$0B STA $DD00 ; Synchronisation per ATN low AF00: LDA $DD00 ; Auf DATA high warten BPL AF00 AF01: LDA $D012 ; Bildschirmzeile des VIC holen CMP #$32 ; Oberste Rasterzeile? BCC AF02 ; Sprung falls Bildschirmrahmen dargestellt AND #$04 ; Warten, bis Badline vorbei CMP #$04 BNE AF01 ; Rücksprung, falls Bit 2 der Rasterzeile gelöscht AF02: LDA #$03 ; ATN high STA $DD00 NOP ; Verzögerung NOP NOP NOP NOP NOP NOP NOP NOP NOP LDX $DD00 ; Bit 5 und 7 des Datenbyte nach Bit 7 und 6 von X LDA T100,X ; Verschiebung an die richtigen Bitpositionen per Lookup-Table LDX $DD00 ; Bit 4 und 6 des Datenbyte nach bit 7 und 6 von X ORA T101,X ; Einblenden an die richtigen Bitpositionen per Lookup-Table LDX $DD00 ; Bit 1 und 3 des Datenbyte nach Bit 7 und 6 von X ORA T102,X ; Verschiebung an die richtigen Bitpositionen per Lookup-Table LDX $DD00 ; Bit 0 und 2 des Datenbyte nach Bit 7 und 6 von X ORA T103,X ; Verschiebung an die richtigen Bitpositionen per Lookup-Table RTS
Floppy-seitige Schnelllade-Routine[Bearbeiten | Quelltext bearbeiten]
Die folgenden Routine P_AA
liest den nachzuladenden Programmteil Sektor für Sektor in Puffer 0 (ab Adresse $0300) ein und folgt dabei den Verkettungszeigern in den ersten beiden Bytes jedes Sektors.
Das Versenden findet parallel auf der CLOCK- und DATA-Leitung statt, wobei jedes Datenbyte in 4 Bitpaare aufgeteilt wird, deren Reihenfolge so gewählt ist, dass sie jeweils nach 8 μs (und nach wenigen Schiebe- und Rotationsbefehlen) in Port B von VIA1 geschrieben werden können. Auf diese Weise wird kurzzeitig eine Datenrate von 250 kBit/s erreicht.
Die Floppy-seitige Schnelllade-Routine liest das zu ladende Programm sektor-weise. Jeder Übertragung zum C64 wird ein Headerbyte vorangestellt, das anzeigt, ob danach noch weitere Sektoren folgen werden (Headerbyte $00), oder ob es sich um den letzten Sektor eines Programms handelt (Headerbyte $02..$FF, entsprechend einer Zahl von 1..254 restlichen Datenbytes).
SOURCE1: ; Kommentare übernommen aus den Büchern: ; Rainer Ellinger/Lothar Englisch/Ralph Gelfand/Norbert Sczcepanowski: ; "Das große Floppybuch zur 1541" ; Rainer Ellinger: ; "Commodore 1571 & 1570 - Das große Floppybuch" ; Geringfügig modifizierte Kopie des Floppy-ROM ab $F4D1 P_BA: JSR $F50A ; Blockheader des Sektors suchen BA00: BVC BA00 ; auf Byte von Diskette warten CLV ; Leseelektronik wieder bereit machen LDA $1C01 ; Byte vom Kopf lesen STA $0300,Y ; und in aktuellen Puffer schreiben INY ; Pufferzeiger auf nächstes Byte setzen BNE BA00 ; Puffer schon voll? LDY #$BA ; ja, Pufferzeiger auf Zusatzpuffer BA01: BVC BA01 ; auf nächstes Byte von Diskette warten CLV ; Flag wieder bereit machen LDA $1C01 ; Byte von Lesekopf holen STA $0100,Y ; und in Zusatzpuffer schreiben INY ; Pufferzeiger auf nächstes Byte setzen BNE BA01 ; Zusatzpuffer voll LDA #$03 STA *$31 JSR $F8E0 ; ja, Sektor von GCR nach Binär wandeln JSR $F5E9 ; Prüfsumme der Daten berechnen EOR *$3A ; mit gelesenem Wert vergleichen BEQ BA02 ; neide identisch? LDA #$05 ; Fehlernummer für READ ERROR (23) BNE BA07 ; unbedingter Sprung zur Fehlerausgabe ; Schnelllade-Routine BA02: LDA $0300 ; Spur des nächsten Blocks holen STA *$08 ; und merken BEQ BA03 ; Sprung falls letzter Block LDA #$00 ; sonst $00 als Header voranstellen STA *$0C ; merken BEQ BA04 ; Unbedingter Sprung BA03: LDA $0301 ; Anzahl gültiger Bytes im letzten Blocks holen STA *$0C ; merken INC *$0C ; um Offset erhöhen BA04: JSR P_BC ; Headerbyte in A an C64 senden LDA $0301 ; Sektor des nächsten Block holen STA *$09 ; und merken LDA *$11 ; Erster Block? BEQ BA05 ; Sprung falls nicht erster Block LDA $0303 ; High-Byte der Ladeadresse STA *$10 ; merken STY *$11 ; Flag für "erster Block" löschen BA05: LDY #$02 ; Lesezeiger initialisieren BA06: LDA $0300,Y JSR P_BC ; Datenbyte in A an C64 senden INY ; Lesezeiger erhöhen CPY *$0C ; Schon alle Datenbytes gelesen? BNE BA06 ; Rücksprung falls nicht alle Datenbytes LDA *$08 ; Letzten Block versendet? BEQ BA07 ; Sprung falls letzter Block LDA #$01 ; Rückmeldung $00="Letzter Block", $01="Nicht letzter Block" BA07: JMP $F969 ; Aktuellen Job beenden, Fehlerrückmeldung bereitstellen ; Einsprung für "Memory-Execute" ("M-E")-Befehl P_BB: LDA #$C8 ; Standardwert setzen STA *$64 ; für Zahl der Halbspuren bis Spur 0 LDA #$04 STA *$5E STA *$5F LDA *$18 ; Spur des zuletzt gelesenen Sektorheaders LDX *$19 ; Sektor des zuletzt gelesenen Sektorheaders STA *$08 ; Spur merken STX *$09 ; Sektor merken LDA #$10 ; Rate des Jobscheifen-Interrupt erhöhen (alle 4 ms statt alle 14 ms) STA $1C07 STA *$11 LDA $1C00 ; CLOCK und DATA high, ATN nicht automatisch beantworten ORA #$08 STA $1C00 ; CLOCK low, ATN automatisch beantworten BB00: LDA #$E0 ; Jobcode "Programm in Jobschleife einbinden" STA *$01 ; als Job für Puffer 1 an Adresse $0400 setzen BB01: LDA *$01 ; Warten bis Job abgearbeitet BMI BB01 BEQ BB02 ; Sprung falls letzter Block CMP #$02 ; Fehler? BCS BB03 ; Sprung falls Fehler LDA #$08 ; Anzahl Leseversuche STA *$0D ; merken INC *$10 ; High-Byte der Ladeadresse erhöhen LDA *$10 ; High-Byte der momentanen Ladeadresse CMP BB06 ; mit Highbyte der Anfangsadresse des Schnelladers vergleichen BNE BB00 ; Rücksprung, falls C64-seitiger Schnelllader nicht überschrieben LDA $0300 ; sonst Spurnummer des aktuellen Blocks STA *$7E ; als Spurnummer des letzten Zugriff setzen LDA $0301 ; Sektornummer des aktuellen Blocks STA $026F ; als Nummer des letzten Sektors setzen JMP $C194 ; Beenden eines Rechnerbefehls, erzeugen der Fehlermeldung BB02: STA *$09 ; 0 als Sektor für Puffer 1 LDA #$12 ; Spur 18 STA *$08 ; als Spur für Puffer 1 LDA #$B0 ; Jobcode "Suchen eines Sektorheaders" STA *$01 ; als Job für Puffer 1 an Adresse $0400 setzen JMP $DAC0 ; Datei schlieáen BB03: TAY ; Fehlernummer nach Y retten DEC *$0D ; Anzahl Leseversuche erniedrigen BNE BB04 ; Rücksprung falls weiterer Leseversuch TYA ; Fehlernummer zurückholen LDX #$02 ; ? Puffernummer JMP $E60A ; Ausgabe der Fehlermeldung (A muá Fehler- und X die Puffernummer enthalten) BB04: LDA *$0D ; Verbleidende Anzahl Leseversuche holen CMP #$04 ; Nur noch 4 Leseversuche? BNE BB00 ; Sprung falls nicht LDA #$C0 ; Jobcode "Kopf auf Spur 0 setzen" STA *$01 ; als Job für Puffer 1 an Adresse $0400 setzen BB05: LDA *$01 ; warten, bis Job abgearbeitet BMI BB05 BPL BB00 ; Unbedingter Sprung zum nächsten Leseversuch BB06: DB $CB ; Datenbyte in A an C64 senden P_BC: TAX ; X=Datenbyte BC00: BIT $1800 ; Auf ATN low warten BPL BC00 LDA #$10 ; DATA und CLOCK high STA $1800 BC01: BIT $1800 ; Auf ATN high warten BMI BC01 TXA ; A=Datenbyte LSR A LSR A LSR A LSR A STA $1800 ; DATA=Bit 5, CLOCK=Bit 7 ASL A AND #$0F STA $1800 ; DATA=Bit 4, CLOCK=Bit 6 TXA ; A=Datenbyte AND #$0F STA $1800 ; DATA=Bit 1, CLOCK=Bit 3 ASL A AND #$0F STA $1800 ; DATA=Bit 0, CLOCK=Bit 2 LDA #$0F NOP STA $1800 ; DATA und CLOCK low RTS
Lookup-Table für das Umsortieren der Datenbits[Bearbeiten | Quelltext bearbeiten]
Die Schnelllade-Routine erzielt ihre hohe Geschwindigkeit dadurch, dass sie die einzelnen Bits jedes Datenbyte in einer an die Registerbelegung des VIA angepassten Reihenfolge überträgt. Das dadurch notwendige, C64-seitige Umsortieren der empfangenen Bits geschieht, indem die aus Port A von CIA2 gelesenen Leitungspegel als Index in die Lookup-Tabels T100
... T103
verwendet werden. Da der Status der CLOCK- und DATA-Leitung in Bit 6 und 7 von Port A zu finden sind, zeigt dieser Index auf Positionen in der Lookup-Table, die — je nach Zustand der Leitungen — um ein Vielfaches von 64 Byte auseinanderliegen. Die dazwischenliegenden, 56 Byte langen Lücken können genutzt werden, um die vier Tabellen T100
... T103
verschränkt abzulegen und damit Speicherplatz zu sparen.
; Umrechnungstabelle Register $DD00 zu Bitpaaren T100: DB $A0,$A0,$A0,$A0,$A0,$A0,$A0,$A0 ; Bit 5 (DATA) low, Bit 7 (CLOCK) low T101: DB $50,$50,$50,$50,$50,$50,$50,$50 ; Bit 4 (DATA) low, Bit 6 (CLOCK) low T102: DB $0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A ; Bit 1 (DATA) low, Bit 3 (CLOCK) low T103: DB $05,$05,$05,$05,$05,$05,$05,$05 ; Bit 0 (DATA) low, Bit 2 (CLOCK) low DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Unbenutzt DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF DB $20,$20,$20,$20,$20,$20,$20,$20 ; Bit 5 (DATA) low, Bit 7 (CLOCK) high DB $10,$10,$10,$10,$10,$10,$10,$10 ; Bit 4 (DATA) low, Bit 6 (CLOCK) high DB $02,$02,$02,$02,$02,$02,$02,$02 ; Bit 1 (DATA) low, Bit 3 (CLOCK) high DB $01,$01,$01,$01,$01,$01,$01,$01 ; Bit 0 (DATA) low, Bit 2 (CLOCK) high DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Unbenutzt DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF DB $80,$80,$80,$80,$80,$80,$80,$80 ; Bit 5 (DATA) high, Bit 7 (CLOCK) low DB $40,$40,$40,$40,$40,$40,$40,$40 ; Bit 4 (DATA) high, Bit 6 (CLOCK) low DB $08,$08,$08,$08,$08,$08,$08,$08 ; Bit 1 (DATA) high, Bit 3 (CLOCK) low DB $04,$04,$04,$04,$04,$04,$04,$04 ; Bit 0 (DATA) high, Bit 2 (CLOCK) low DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Unbenutzt DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF DB $00,$00,$00,$00,$00,$00,$00,$00 ; Bit 5 (DATA) high, Bit 7 (CLOCK) high DB $00,$00,$00,$00,$00,$00,$00,$00 ; Bit 4 (DATA) high, Bit 6 (CLOCK) high DB $00,$00,$00,$00,$00,$00,$00,$00 ; Bit 1 (DATA) high, Bit 3 (CLOCK) high DB $00,$00,$00,$00,$00,$00,$00,$00 ; Bit 0 (DATA) high, Bit 2 (CLOCK) high DB $00,$00,$00,$00,$00,$00,$00,$00 ; Unbenutzt DB $00,$00,$00,$00,$00,$00,$00,$00 DB $00,$00,$00,$00,$00,$00,$00,$00 DB $00,$00,$00,$00,$00,$00,$00,$00