Novaload/Quellcode2

Aus C64-Wiki
Zur Navigation springenZur Suche springen

<< 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