Novaload/Quellcode2
<< zurück zu Novaload
Novaload/Quellcode2: Der folgende Codeabschnitt stellt die zweite Interruptroutine des Kassetten-Schnellladers Novaload dar. Ausgangspunkt für die Analyse ist der disassemblierte Code aus der Kassettenversion des Spiels Buggy Boy.
Die Routine wird bei jedem Leseimpuls vom Datassetten-Eingang (jeder fallenden Flanke an Pin FLAG von CIA 1) aufgerufen. Sie überprüft zunächst den Stand des Timers A von CIA 1: Dieser startet bei jedem Leseimpuls beim Startwert 1012 und zählt Systemtakte abwärts; falls seit dem letzten Impuls mehr als 500 Systemtakte vergangen sind (der Zählerstand also kleiner als 512 ist), so wurde ein 1-Bit gelesen, ansonsten ein 0-Bit.
Gelesene Bits werden von links in die Speicherzelle an Adresse $A9 geschrieben. Diese wird während des Ladens vor jedem neuen Byte mit dem Wert 127 = %01111111 geladen und dient damit als Bitzähler: Sobald der Rotationsbefehl ein gelöschtes Bit ins Carry-Flag schiebt, wurde eine Gruppe von 8 Bits von der Datassette empfangen.
Der BCS-Befehl hinter dem Label BA00
ist der Dreh- und Angelpunkt der Routine: Die Sprungdistanz dieses Befehls codiert den Zustand des zugrundeliegenden endlichen Automaten und wird bei jedem Zustandswechsel angepasst.
Die Aufgabe der Routine ist es, eine zu dem per SETNAM festgelegten Dateinamen passende Datei auf der Kassette zu finden und absolut in den Hauptspeicher des C64 zu laden. Der Dateiname kann dabei maximal 255 Zeichen lang sein. Bei der Kassettenversion des Spiels Buggy Boy haben allerdings nachzuladenden alle Dateien einen Namen der Länge 0.
Die Routine berechnet in der Speicherzelle $AA
die Summe aller Header- und Datenbytes (ohne Berücksichtigung von Additionsüberträgen). Diese Prüfsumme wird beim Erreichen des Dateiendes mit dem an die Datei angehängten Sollwert verglichen.
ORG $0334 P_BA: PHA ; Register A retten TYA ; Register Y retten PHA LDA $DC05 ; High-Byte CIA 1 Timer A LDY #$11 ; Timer A mit Startwert laden und starten STY $DC0E INC $D020 ; Rahmenfarbe hochzählen EOR #$02 ; Bit 1 des Highbyte von Timer A negieren LSR A ; Zählerstand<512 ergibt CS, sonst CC LSR A ROR *$A9 ; CF von links in Byte an Adresse $A9 schieben BCS BA07 ; Rückkehr aus Interrupt, falls nicht 8. Bit eines Byte LDA *$AA ; Aktuelle Prüfsumme holen STA *$A8 ; und merken ADC *$A9 ; Gelesenes Byte zu Prüfsumme addieren STA *$AA ; und zurückschreiben LDY *$B4 ; Low-Byte des Schreibzeigers holen LDA *$A9 ; Empfangenes Byte holen SEC BA00: BCS BA01 ; Zentraler Sprungverteiler ; 1. Einsprung: Synchronisation (Suche nach dem ersten 1-Bit) BA01: CMP #$80 ; Erstes 1-Bit gelesen? BNE BA07 ; sonst Rückkkehr aus Interrupt LDA #BA02-BA00-$02 ; Sprung von BA00 nach BA02 vorbereiten BNE BA05 ; 2. Einsprung: Synchronisation (Suche nach $AA=%10101010) BA02: SBC #$AA ; Bitkombination $AA=%10101010 gelesen? BEQ BA04 ; Sprung falls ja BA03: LDA #$00 ; sonst wieder Sprung von BA00 nach BA01 vorbereiten BEQ BA19 BA04: STA *$AA ; Prüfsumme initialisieren LDA #BA08-BA00-$02 ; Sprung von BA00 nach BA08 vorbereiten BA05: STA BA00+$01 BA06: LDA #$7F ; Bitzähler auf 8 Bit (%01111111) setzen STA *$A9 STY *$B4 ; Low-Byte des Schreibzeigers zurückschreiben BA07: LDA $DC0D ; Interrupt Request löschen PLA ; Gerettetes Register Y zurückholen TAY PLA ; Gerettetes Register A zurückholen RTI ; Rückkehr aus Interrupt ; 3. Einsprung: Länge des Dateinamens verarbeiten BA08: STA *$B5 ; Namenslänge der gefundenen Datei merken CMP *$B7 ; mit Namenslänge der zu ladenden Datei vergleichen BEQ BA09 ; Sprung falls gleich LDA *$B7 ; Namenslänge der zu ladenden Datei holen BNE BA03 ; Sprung falls Dateinamen nicht Leerstring LDA #BA11-BA00-$02 ; Sprung von BA00 nach BA11 vorbereiten BNE BA05 BA09: LDA #BA10-BA00-$02 ; Sprung von BA00 nach BA10 vorbereiten CPY *$B5 ; Name der zu ladenden Datei leer? BNE BA05 ; Sprung falls nicht leer LDA #BA12-BA00-$02 ; Sprung von BA00 nach BA12 vorbereiten BNE BA05 ; 4: Einsprung: Dateinamen vergleichen BA10: CMP ($BB),Y ; Gelesenes Zeichen gleich gesuchtes Zeichen? BNE BA03 ; Neustart des Ladens falls nicht BA11: LDA #BA12-BA00-$02 ; Sprung von BA00 nach BA12 vorbereiten BNE BA18 ; und Low-Byte des Schreibzeigers erhöhen ; 5. Einsprung: 6 Byte langen Header nach $00AC..$00B1 holen ; $AC..$AD: Anfangsadresse low/high ; $AE..$AF: Endadresse low/high ; $B0..$B1: Länge low/high BA12: STA $00AC,Y ; Gelesenes Byte in Puffer speichern LDA #BA13-BA00-$02 ; Sprung von BA00 nach BA13 vorbereiten INY ; Schreibzeiger erhöhen STY *$B4 ; und merken CPY #$06 ; Schon 6 Bytes empfangen? BNE BA06 ; Rückkehr aus Interrupt falls noch nicht BEQ BA19 ; Sonst Index auf 0 ; 6. Einsprung: Prüfsumme vergleichen BA13: CMP *$A8 ; Gelesene mit berechneter Prüfsumme vergleichen BNE BA16 ; Fehler melden wenn ungleich LDA *$B1 ; Endet nachgeladenes Programm auf Page-Grenze? BEQ BA15 ; Sprung falls ja INC *$AD ; High-Byte des Schreibzeigers erhöhen DEC *$B1 ; High-Byte der Länge vermindern BNE BA14 ; Sprung falls nicht letzte Page LDA *$B0 ; Low-Byte der Länge holen BEQ BA15 ; Sprung falls Vielfaches von 256 Byte STA *$B5 ; sonst als Obergrenze des Index merken BA14: LDA #BA17-BA00-$02 ; Sprung nach BA17 vorbereiten BNE BA05 BA15: STA *$AB ; Rückmeldung "Erfolg" bereitstellen LDA #$1F ; Alle Interrupts von CIA1 deaktivieren STA $DC0D BNE BA07 ; Rückkehr aus Interrupt BA16: LDA #$80 ; Rückmeldung "Fehler" bereitstellen BNE BA15 ; ; 7. Einsprung: Gelesenes Byte in Speicher schreiben BA17: STA ($AC),Y ; Gelesenes Byte in Speicher schreiben LDA #BA13-BA00-$02 ; Sprung von BA00 nach BA13 vorbereiten BA18: INY ; Schreibindex erhöhen CPY *$B5 ; Obergrenze erreicht? BNE BA06 ; Rückkehr aus Interrupt falls nicht BA19: LDY #$00 ; Schreibindex zurücksetzen STY *$B5 BEQ BA05 ; Sprung nach BA13 vorbereiten und Rückkehr aus Interrupt