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