Arac/Schnelllader
<< zurück zu Arac
Arac/Schnelllader: Die folgenden Abschnitte stellen den disassemblierten Kassetten-Schnelllader des Spiels Arac (Neuauflage von "Prism Leisure") dar. Sie sind gegliedert in einzelne Codeblöcke, die gemäß ihrer Funktion während des Ladens gruppiert sind.
Dateiname und Interruptroutine des Schnellladers[Bearbeiten | Quelltext bearbeiten]
Dicht hinter dem Dateinamen findet sich im allerersten Block auf Kassette die Interruptroutine des Schnellladers. Diese Routine behandelt die üblichen Phasen eines Ladevorgangs — bitweise Synchronisation, Prüfung auf Synchronisationsbytes und Laden des nachfolgenden Programmteils — und schaltet selbständig zwischen diesen um, wobei ein Statusbyte an Adresse $95 zur Unterscheidung dient.
Das Programm, das sich auf der Kassette dann an diesen Header anschließt, ist nur 64 Bytes lang und übernimmt lediglich die Initialisierung und die Steuerung des Ladevorgangs für die ersten beiden Programmblöcke, bevor die Kontrolle an eine nachgeladene, weitgehend identische Kopie dieser Laderoutine übergeben wird. Im Gegensatz zu vielen anderen Schnellladern geht den Programmblöcken auf Band kein Header mit Anfangs- und Endadressen voraus. Vielmehr sind diese Adressen als Konstanten im Programmcode des Schnellladers abgelegt, während sich auf dem Band nur die eigentlichen Datenbytes finden.
ORG $033C
DB $03 ; Code für "Absolut zu ladendes Programm"
DW $02C0 ; Ladeadresse
DW $0304 ; Endadresse+1
DB "ff " ; Dateiname, aufgefüllt mit Leerzeichen
DB " " ; Füllbytes
P_AA: SEI ; Interrupts während Ladevorgang verbieten
LDA #$05 ; Alle ROMs ausblenden
STA *$01
LDA #$1F ; Alle IRQs von CIA1 deaktivieren
STA $DC0D
STA *$A4 ; Flag für "Ladevorgang abgeschlossen" auf FALSE setzen
LDA $DC0D ; Alle IRQ-Anforderungen von CIA1 löschen
NOP
LDA #$00
STA *$95 ; Ladephase initialisieren ($00=Bit-Synchronisation)
STA *$90 ; Statuswort ST löschen
NOP
NOP
NOP
LDA #$A0 ; Startwert von CIA1 Timer B=$03A0 (928)
STA $DC06
LDA #$03
STA $DC07
LDA #$90 ; CIA1 IRQ bei Impuls an Pin FLAG aktivieren
STA $DC0D
LDA #<P_AB ; Interruptvektor auf Schnelllader umbiegen
STA $FFFE
LDA #>P_AB
STA $FFFF
CLI
AA00: LDA *$A4 ; Auf Abschluss des Ladevorgangs warten
BNE AA00
RTS
DB $00 ; Füllbyte
; Interruptroutine des Schnellladers
P_AB: PHA ; A auf Stack retten
TYA ; Y auf Stack retten
PHA
LDA $DC07 ; High-Byte von CIA1 Timer B holen
LDY #$11 ; CIA1 Timer B neu laden und wieder starten
STY $DC0F
INC $D020 ; Bildschirm-Rahmenfarbe erhöhen
EOR #$02 ; Bit 9 von CIA1 Timer B invertieren
LSR A ; und als gelesenes Bit ins Carry-Flag schieben
LSR A
ROR *$A9 ; Bit von links in gelesenes Byte schreiben
BCC AB02 ; Sprung falls 8 Bits gelesen
NOP
AB00: LDA $DC0D ; Interruptanforderung löschen
PLA ; Geretteten Wert von Y zurückholen
TAY
PLA ; Gerettenen Wert von A zurückholen
RTI ; Rückkehr aus Interrupt
; Zähler für empfangene Bits initialisierung und Rückkehr aus Interrupt
AB01: LDA #$7F ; Bitzähler initialisieren (8 Bits)
STA *$A9
BNE AB00 ; Unbedingter Sprung
NOP
; Bit-Synchronisation
AB02: LDA *$A9 ; Gelesenes Byte nach A holen
LDY *$95 ; Ladephase nach Y holen
BNE AB03 ; Sprung falls nicht Bit-Synchronisation
CMP #$80 ; Synchronisationszeichen $80 gefunden?
BNE AB00 ; Sprung falls nicht, dann weiter suchen
STA *$95 ; sonst Ladephase wechseln ($80=Byte-Synchronisation)
BEQ AB01 ; Unbedingter Sprung
DB $00
; Byte-Synchronisation
AB03: BPL AB05 ; Sprung falls nicht Byte-Synchronisation
CMP #$AA ; Synchronisationszeichen $AA gefunden?
BEQ AB04 ; Sprung falls ja
LDA #$00 ; sonst wieder zu Bit-Synchronisation wechseln
STA *$95 ; Ladephase zurücksetzen ($00=Bit-Synchronisation)
BEQ AB00 ; Unbedingter Sprung
AB04: LDA #$01 ; Ladephase wechseln ($01=Programm laden)
STA *$95
BNE AB01 ; Unbedingter Sprung
NOP
; Laden
AB05: LDY #$00 ; Index für Schreibzugriff setzen
STA ($AC),Y ; Gelesenes Byte in Speicher schreiben
INC *$AC ; Schreibzeiger erhöhen (Low-Byte)
BNE AB06 ; Sprung falls kein Überlauf
INC *$AD ; Schreibzeiger erhöhen (High-Byte)
AB06: SEC ; Schreibzeiger mit Endadresse vergleichen
LDA *$AC ; Low-Byte des Schreibzeigers
SBC *$AE ; minus Low-Byte Endadresse
LDA *$AD ; High-Byte des Schreibzeigers
SBC *$AF ; minus High-Byte der Endadresse
BCC AB01 ; Rücksprung, falls noch nicht alle Bytes gelesen
LDA #$00 ; Flag für "Ladevorgang abgeschlossen" auf TRUE setzen
STA *$A4
BEQ AB01 ; Unbedingter Rücksprung
DB " " ; Füllbytes
Initialisierung[Bearbeiten | Quelltext bearbeiten]
Der folgende Programmteil wird beim Laden von Arac von Kassette gelesen. Dieser Vorgang überschreibt den Vektor für die Eingabe einer BASIC-Zeile mit der Startadresse des Schnellladers und löst damit nach dem Ende des Ladevorgangs einen Autostart aus. Nach einer kurzen Initialisierung von I/O-Bausteinen werden die Anfangs- und Endadressen der zu ladenden Programmteile gesetzt und dann die Kontrolle an die Interruptroutine übergeben.
PRG $02C0
; Einsprung des Schnellladers nach Autostart
P_AC: JSR $FF84 ; CIAs initialisieren
LDA #$00 ; "Programm-Modus" setzen
STA *$9D
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
LDA $D011 ; Bildschirm abschalten
AND #$EF
STA $D011
; Ersten Programmteil laden
LDA #$00 ; Anfangsadresse des ersten Programmteils auf $6000 setzen
STA *$AC
LDA #$60
STA *$AD
LDA #$00 ; Endadresse des ersten Programmteils auf $7400 setzen
STA *$AE
LDA #$74
STA *$AF
JSR P_AA ; Programmteil laden
; Zweiten Programmteil laden
LDA #$00 ; Anfangsadresse des zweiten Programmteils auf $1400 setzen
STA *$AC
LDA #$14
STA *$AD
LDA #$E0 ; Endadresse des zweiten Programmteils auf $23E0 setzen
STA *$AE
LDA #$23
STA *$AF
JSR P_AA ; Programmteil laden
JMP $1BB0 ; Sprung zum nächsten (fast identischen) Fastloader
DW $E38B ; Vektor für BASIC-Warmstart
DW P_AC ; Vektor für Eingabe einer Zeile, umgebogen auf Schnelllader