Paradroid/Schnelllader
<< zurück zu Paradroid
Paradroid/Schnelllader: Die folgenden Abschnitte stellen den disassemblierten Kassetten-Schnelllader des Spiels Paradroid (Competition Edition) 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]
Direkt hinter dem Dateinamen findet sich im allerersten Block auf Kassette die Interruptroutine des Schnellladers. Diese liest ein einzelnes Bit von Kassette und schiebt dieses von rechts in die Speicherzelle $00A9. Falls es Zeit ist, den Inhalt dieser Speicherzelle zu verarbeiten (während der Synchronisation nach jedem Bit, später nach jedem 8. Bit), so zeigt der BCC-Befehl bei AA00 jeweils auf den hierfür aktuell notwendigen Codeabschnitt.
ORG $033C
DB $03 ; Code für "Absolut zu ladendes Programm"
DW $02A7 ; Startadresse
DW $0334 ; Endadresse+1 (wird nicht erreicht)
DB $05 ; Code für "weiß"
DB $0E ; Code für "Groß-/Kleinbuchstaben"
DB "Paradroid. "
DB $9A ; Code für "hellblau"
; IRQ-Routine des Schnellladers
P_AA: PHA ; Akku retten
TYA
PHA ; Y-Register retten
LDA $DC05 ; Timerstand CIA1 Timer holen (High-Byte)
LDY #$19 ; CIA1 Timer A neu laden und starten (one shot)
STY $DC0E
EOR #$02 ; Bit 9 des Timerstands von CIA1 Timer A invertieren
LSR A ; in CF übertragen (Timerstand>=512: 0-Bit, <512: 1-Bit)
LSR A
ROL *$A9 ; und von rechts in das empfangene Byte schieben
LDA *$A9 ; Empfangenes Byte nach A holen
AA00: BCC AA01 ; Zentraler Sprungverteiler, falls 0-Bit herausgeschoben
BCS AA03 ; sonst zum Ende der Interruptroutine
; 1. Einsprung (Suche nach dem ersten Synchronisationszeichen)
AA01: CMP #$40 ; Synchronisationszeichen $40 empfangen?
BNE AA03 ; zum Ende der Interruptroutine falls nicht
LDA #AA04-AA00-$02
STA AA00+$01 ; sonst nächstes Byte bei 2. Einsprung verarbeiten lassen
AA02: LDA #$FE ; Bitzähler %11111110, immer 8 Bit einlesen
STA *$A9 ; als empfangenes Byte setzen
AA03: LDA $DC0D ; Interruptanforderung löschen
PLA ; Y-Register zurückholen
TAY
PLA ; Akku zurückholen
RTI ; Rückkehr aus Interrupt
; 2. Einsprung: Suche nach dem 2. Synchronisationszeichen
AA04: CMP #$40 ; Weiteres Synchronisationszeichen $40 empfangen?
BEQ AA02 ; dann überlesen
CMP #$5A ; Nächstes Synchronisationszeichen $5A empfangen?
BEQ AA05 ; dann Synchronisation erfolgreich
BNE AA10 ; sonst Synchronisation neu starten
AA05: LDA #AA06-AA00-$02
STA AA00+$01 ; Nächstes Byte bei 3. Einsprung verarbeiten lassen
LDA #$00 ; Prüfsumme für empfangene Bytes initialisieren
STA *$C1
BEQ AA02 ; Bitzähler initialisieren und Rückkehr aus Interrupt
; 3. Einsprung: Header einlesen und in Speicher schreiben
AA06: STA *$FB ; Empfangenes Headerbyte an $FB...$FE speichern
INC AA06+$01 ; Schreibzeiger für Headerbytes weiterzählen
LDA AA06+$01 ; und nach A holen
CMP #$FF ; Schon alle Headerbytes empfangen?
BNE AA02 ; Bitzähler initialisieren und Rückkehr aus Interrupt falls nicht
LDA #AA07-AA00-$02
STA AA00+$01 ; sonst nächstes Byte bei 4. Einsprung verarbeiten lassen
BNE AA02 ; Bitzähler initialisieren und Rückkehr aus Interrupt
; 4. Einsprung: Datenblock einlesen und in Speicher schreiben
AA07: LDY #$00 ; Schreibindex für empfangene Bytes initialisieren
STA ($FB),Y ; Empfangenes Byte in Speicher schreiben
EOR *$C1 ; und in Prüfsumme einarbeiten
STA *$C1
INC *$FB ; Schreibzeiger erhöhen, Low-Byte
BNE AA08 ; Sprung falls kein Überlauf
INC *$FC ; Schreibzeiger erhöhen, High-Byte
AA08: LDA *$FB ; Schreibzeiger mit Endadresse vergleichen, Low-Byte
CMP *$FD
LDA *$FC ; Schreibzeiger mit Endadresse vergleichen, High-Byte
SBC *$FE
BCC AA02 ; Sprung falls Endadresse noch nicht erreicht
LDA #AA09-AA00-$02
STA AA00+$01 ; sonst nächstes byte bei 5. Einsprung verarbeiten lassen
BNE AA02 ; Unbedingter Sprung
; 5. Einsprung: Prüfsumme merken und Flag für "Block gelesen" setzen
AA09: STA *$C2 ; Empfangenes Byte (Prüfsumme) merken
LDA #$FF ; Flag für "Block gelesen" setzen
STA *$02
LDA #$FB ; Schreibzeiger für nächsten Header initialisieren
STA AA06+$01
AA10: LDA #AA01-AA00-$02
STA AA00+$01 ; Nächste Resynchronisation vorbereiten
BNE AA02 ; Bitzähler initialisieren und Rückkehr aus Interrupt
; Auf Blockende warten, dann Checksumme prüfen und Ladebildschirm starten
P_AB: CLI
LDA #$58 ; Opcode für "CLI"
STA BA00 ; anstelle von "NOP"
LDA #$1B ; Bildschirm einschalten
STA $D011
AB00: LDA #$F8 ; Farbcode für "orange"
STA $D020 ; als Rahmenfarbe setzen
LDA *$02 ; Flag für "Vollständigen Block empfangen" (zunächst $00, Farbcode für "schwarz")
STA $D020 ; als Rahmenfarbe setzen
BEQ AB00 ; Rücksprung falls Block noch nicht vollständig empfangen
JSR P_BB ; Checksumme prüfen
LDA #$00 ; Sprung nach $E000 vorbereiten
STA BA01+$01
LDA #$E0
STA BA01+$02
JMP P_BA ; Ladebildschirm starten
Initialisierung[Bearbeiten | Quelltext bearbeiten]
Der folgende Programmteil wird beim Laden von Paradroid von Kassette gelesen. Dieser Vorgang überschreibt den IRQ-Vektor und setzt ihn auf seinen Standardwert $EA31 zurück, was vom Kernal als Ende der I/O-Operation interpretiert wird (an Adresse $F8BE). Über den auf die Initialisierungsroutine P_BA umgebogenen Vektor für die Eingabe einer Zeile startet dann der Schnelllader.
PRG $02A7
; Initialisierung der I/O-Bausteine
P_BA: SEI ; IRQs verbieten
LDA #$05 ; Alle ROMs ausblenden
STA *$01
LDA #$1F
STA $DC0D ; Alle IRQs von CIA1 abschalten
LDA $DC0D ; Eventuell wartende IRQs von CIA1 löschen
LDA #$68 ; Startwert von CIA1 Timer A auf $0368 (912) setzen
STA $DC04 ; Low-Byte
LDA #$03
STA $DC05 ; High-Byte
LDA #$90 ; Signal an CIA1 Pin FLAG löst IRQ aus
STA $DC0D
LDA #<P_AA ; IRQ-Vektor
STA $FFFE ; Low-Byte setzen
LDA #>P_AA ; IRQ-Vektor
STA $FFFF ; High-Byte setzen
LDA #$00
STA *$02 ; Flag für "Block empfangen" löschen
STA *$A9 ; Bitzähler %00000000, empfangenes Byte nach jedem Bit prüfen
BA00: NOP ; Wird im zweiten Durchlauf durch "CLI" ersetzt
BA01: JMP P_BC ; Wird im zweiten Durchlauf durch "JMP $E000" ersetzt
; Checksumme prüfen
P_BB: LDA #$07 ; Alle ROMs einblenden
STA *$01
LDA *$C1 ; Errechnete Checksumme nach A holen
CMP *$C2 ; und mit von Band gelesener Prüfsumme vergleichen
BNE BB00 ; Sprung falls Prüfsummenfehler
RTS ; sonst Rückkehr aus Prüfroutine
BB00: JMP $FCE2 ; Reset
; Sprung nach P_AB per NMI
P_BC: LDA #<P_AB ; NMI-Vektor
STA $FFFA ; Low-Byte setzen
LDA #>P_AB ; NMI-Vektor
STA $FFFB ; High-Byte setzen
LDA #$01 ; Startwert von CIA2 Timer A auf $01xx (256..511) setzen
STA $DD05 ; High-Byte
LDA #$81 ; Unterlauf von CIA2 Timer A löst NMI aus
STA $DD0D
LDA #$99 ; CIA2 Timer A laden und starten (one-shot)
STA $DD0E
BC00: BNE BC00 ; Endlosschleife, die durch NMI von CIA2 verlassen wird
T000: DW $E38B ; Vektor für BASIC-Warmstart
DW P_BA ; Vektor für Eingabe einer Zeile
DW $A57C ; Vektor für Umwandlung in Interpretercode
DW $A71A ; Vektor für Umwandlung in Klartext (LIST)
DW $A7E4 ; Vektor für BASIC-Befehlsadresse holen
DW $AE86 ; Vektor für Ausdruck auswerten
DB $00 ; Akku für SYS-Befehl
DB $51 ; X-Reg für SYS-Befehl
DB $03 ; Y-Reg für SYS-Befehl
DB $30 ; Status-Register für SYS-Befehl
DB $4C ; JMP-Befehl für USR-Funktion
DW $B248 ; USR-Vektor
DB $00
DW $EA31 ; IRQ-Vektor