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