Hypra-Load/Quellcode 1.0
Die folgenden Abschnitte stellen das vollständige, disassemblierte Programm Hypra-Load 1.0 dar. Sie sind gegliedert in einzelne Codeblöcke, die jeweils an unterschiedlichen Adressen in die Kopie des KERNAL-ROM eingebettet werden. Dort überschreiben sie Ansteuerungsroutinen für Datassette und RS232-Schnittstelle, so dass diese Geräte nach dem Aktivieren von Hypra-Load nicht mehr genutzt werden können.
BASIC-Starter[Bearbeiten | Quelltext bearbeiten]
Der folgende, kurze Codeabschnitt stellt den BASIC-Teil von Hypra-Load dar — einfach die einzelne Zeile 1984 SYS3238
.
ORG $0801 DRIVE EQU $08 ; Geräteadresse 8 P___: DW __00 ; Zeiger auf Ende der BASIC-Zeile DW 1984 ; Zeilennummer DB $9E ; Token für BASIC-Befehl "SYS" DB "3238",$00 ; Startadresse der Routine P_AR __00: DB $00,$00 ; Ende des BASIC-Programms JSR $F32F ; CLALL schließt alle Ein-/Ausgabe Kanäle (nicht verwendet)
C64-seitige Schnelllade-Routinen[Bearbeiten | Quelltext bearbeiten]
Der folgende Code liegt im Kernal-ROM an Adresse TARGET0=$EEBB
und nutzt zudem einen Puffer von 256 Byte Länge an Adresse BUFFER4=$FBA6
. Er beinhaltet zum einen die Schnelllade-Routinen, der auf dem C64 ablaufen, und zum anderen die Programmteile, die den eigentlichen Ladevorgang vorbereiten, indem sie die Floppy-seitigen Codeabschnitte in den Speicher des Diskettenlaufwerks übertragen und dort starten.
SOURCE0: ; Floppyseitige Schnelladeroutinen ins RAM der Floppy übertragen P_AA: LDA #<BUFFER1 ; Quelladresse der Floppy-Routinen ab $F8E2 LDX #>BUFFER1 STA *$A7 STX *$A8 LDA #<TARGET1 ; Zieladresse $0300 im RAM der Floppy LDX #>TARGET1 STA *$A9 STX *$AA ; Routinen in Abschnitten von 30 Bytes per Memory-Write in den ; Floppyspeicher ab Adresse TARGET1=$0300 übertragen AA00: LDA #DRIVE ; Geräteadresse JSR $ED0C ; LISTEN senden LDA #$6F ; Befehlskanal 15 als Sekundäradresse JSR $EDB9 ; Sekundäradresse nach LISTEN senden LDA #$4D ; 'M' JSR $EDDD ; ... auf IEC-Bus ausgeben (IECOUT) LDA #$2D ; '-' JSR $EDDD ; ... auf IEC-Bus ausgeben (IECOUT) LDA #$57 ; 'W' JSR $EDDD ; ... auf IEC-Bus ausgeben (IECOUT) LDY #$00 LDA *$A9 ; Zieladresse (Low-Byte) JSR $EDDD ; ... auf IEC-Bus ausgeben (IECOUT) LDA *$AA ; Zieladresse (High-Byte) JSR $EDDD ; ... auf IEC-Bus ausgeben (IECOUT) LDA #30 ; Daten in Blocks von je 30 Bytes senden JSR $EDDD ; ... auf IEC-Bus ausgeben (IECOUT) AA01: LDA ($A7),Y ; Nacheinander die Daten eines Blocks ab JSR $EDDD ; IECOUT ein Byte auf IEC-Bus ausgeben INY CPY #30 ; Blockgröße erreicht? BCC AA01 ; Rücksprung, falls noch nicht JSR $EDFE ; UNLISTEN senden ; Lesezeiger auf nächsten Block richten CLC LDA *$A7 ; Low-Byte des Lesezeigers ADC #30 ; um Blockgröße erhöhen STA *$A7 ; und zurückschreiben BCC AA02 ; Sprung falls kein Additionsübertrag INC *$A8 ; High-Byte des Lesezeigers erhöhen ; Schreibzeiger auf nächsten Block richten CLC AA02: LDA *$A9 ; Low-Byte des Schreibzeigers LDX *$AA ADC #30 ; um Blockgröße erhöhen STA *$A9 ; und zurückschreiben BCC AA03 ; Sprung falls kein Additionsübertrag INC *$AA ; High-Byte des Schreibzeigers erhöhen AA03: CPX #>TARGET1+2 ; 570 (mehr als 512+30) Bytes kopiert? BCC AA00 ; Rücksprung, falls noch nicht CMP #$00 ; A=$3A BCC AA00 ; Sprung wird nie ausgeführt ; Routine P_AN im Floppyspeicher per Memory-Execute ausführen LDA #DRIVE ; Gerätenummer JSR $ED0C ; LISTEN senden LDA #$6F ; Befehlskanal 15 als Sekundäradresse JSR $EDB9 ; Sekundäradresse nach LISTEN senden LDA #$4D ; 'M' JSR $EDDD ; IECOUT ein Byte auf IEC-Bus ausgeben LDA #$2D ; '-' JSR $EDDD ; IECOUT ein Byte auf IEC-Bus ausgeben LDA #$45 ; 'E' JSR $EDDD ; IECOUT ein Byte auf IEC-Bus ausgeben AA04: LDA #<P_AN JSR $EDDD ; IECOUT ein Byte auf IEC-Bus ausgeben LDA #>P_AN JSR $EDDD ; IECOUT ein Byte auf IEC-Bus ausgeben LDA #$0B ; Bildschirm ausschalten STA $D011 JSR $EDFB ; UNTALK senden (müsste UNLISTEN sein) NOP NOP JMP P_AE ; Ein Byte per Hypra-Load-Protokoll vom IEC-Bus lesen P_AB: LDA #$0B ; C64 fordert per ATN low Datentransfer an STA $DD00 AB00: BIT $DD00 ; Warten auf DATA low als Antwort der Floppy BPL AB00 LDA #$03 ; C64 setzt ATN auf high STA $DD00 LDX #$05 ; Verzögerung AB01: DEX NOP BNE AB01 LDX #$04 ; 4 Bitpaare per Hypra-Load-Protokoll lesen AB02: LDA $DD00 ROL A ROL A ; /CLOCK überträgt niederwertiges Bit jedes Bitpaars ROR *$B0 ; ... von links in Speicherzelle $B0 schieben ROR A ; /DATA überträgt höherwertiges Bit jedes Bitpaars ROR *$B0 ; ... von links in Speicherzelle $B0 schieben NOP ; Verzögerung DEX ; Schon 4 Bitpaare übertragen? BNE AB02 ; Rücksprung falls noch nicht LDA *$B0 ; Empfangenes Byte in A holen EOR #$FF ; ... und invertieren AB03: RTS P_AC: JSR P_AB ; Einleitende Synchronisation CMP #$FF ; Fehler? BEQ AB03 ; Rücksprung bei Fehler LDY #$00 ; Zähler für empfangene Bytes ; Bestenfalls alle 161 Systemtakte ein Datenbyte per Hypra-Load-Protokoll empfangem AC00: LDA #$0B ; ATN low synchronisiert C64 und Floppy STA $DD00 AC01: BIT $DD00 ; Warten auf DATA high (Floppy bereit zum Senden) BPL AC01 LDA #$03 ; ATN high STA $DD00 LDX #$07 ; Verzögerung AC02: DEX BNE AC02 ; Alle 24 Systemtakte 2 Datenbits auf DATA und CLOCK empfangen LDA $DD00 ; DATA und CLOCK enthalten Bit 1 und 0 des Datenbyte ROL A ROL A ; CF=Bit 0 ROR *$B0 ; ... in Adresse $B0 schieben ROR A ; CF=Bit 1 ROR *$B0 ; ... in Adresse $B0 schieben NOP ; Verzögerung NOP LDA $DD00 ; DATA und CLOCK enthalten Bit 3 und 2 des Datenbyte ROL A ROL A ; CF=Bit 2 ROR *$B0 ; ... in Adresse $B0 schieben ROR A ; CF=Bit 3 ROR *$B0 ; ... in Adresse $B0 schieben NOP ; Verzögerung NOP LDA $DD00 ; DATA und CLOCK enthalten Bit 5 und 4 des Datenbyte ROL A ROL A ; CF=Bit 4 ROR *$B0 ; ... in Adresse $B0 schieben ROR A ; CF=Bit 5 ROR *$B0 ; ... in Adresse $B0 schieben NOP ; Verzögerung NOP LDA $DD00 ; DATA und CLOCK enthalten Bit 7 und 6 des Datenbyte ROL A ROL A ; CF=Bit 6 ROR *$B0 ; ... in Adresse $B0 schieben ROR A ; CF=Bit 7 ROR *$B0 ; ... in Adresse $B0 schieben LDA *$B0 ; Datenbyte holen EOR #$FF ; Invertieren (Floppy OUT invertiert, C64 IN nicht) STA BUFFER4,Y ; ... und in Puffer schreiben INY ; Bytezähler erhöhen BNE AC00 ; Rücksprung falls noch nicht 256 Byte empfangen RTS ; Datei per Hypra-Load-Protokoll laden P_AD: SEI ; Interrupts verbieten (würden Timing stören) LDA #$01 STA *$A7 ; Flag für "Erster Block" setzen AD00: LDY #$FF JSR P_AC ; Datenblock (1 Byte Sync+256 Bytes Daten) empfangen CPY #$FF ; Test auf Synchronisationsfehler BEQ AD06 ; Abbruch bei Fehler LDX #$02 ; X=Zeiger auf erstes Datenbyte (hinter Spur/Sektornummer) LDA *$A7 ; Erster Block? BEQ AD01 ; Sprung wenn nicht erster Block LDX #$04 ; X=Zeiger hinter Ladeadresse im ersten Block AD01: LDA BUFFER4 ; Spurnummer des nächsten Blocks (0 bei letztem Block) BNE AD02 ; Sprung wenn nicht letzter Block INC BUFFER4+$01 ; Anzahl gültige Bytes im letzten Block LDA BUFFER4+$01 ; A=Zeiger hinter letztes gültiges Datenbyte (letzter Block) DB $2C AD02: LDA #$00 ; A=Zeiger auf Pufferende (nicht letzter Block) STA *$A8 ; ... für Umkopieren merken AD03: LDA BUFFER4,X ; Datenbytes aus Puffer umkopieren STA ($AE),Y ; ... an Ladeadresse aus LOAD-Routine INC *$AE ; Schreibzeiger weiterbewegen BNE AD04 INC *$AF AD04: INX ; Lesezeiger weiterbewegen CPX *$A8 ; Letztes Datenbyte umkopiert? BNE AD03 ; Nein, weiter kopieren LDX #$00 STX *$A7 ; Flag für "Erster Block" löschen LDA BUFFER4 ; Spurnummer des nächsten Blocks (0 bei letztem Block) BNE AD00 ; Sprung falls nicht letzter Block LDA #$35 ; Nur I/O und RAM in C64-Adressraum einblenden STA *$01 AD05: LDA #$1B ; Bildschirm wieder einschalten STA $D011 LDA #$40 ; Status "Letztes Byte aus Datei gelesen" STA *$90 ; ... als Statusbyte merken CLC ; Zeichen für "kein Fehler" RTS ; Abbruch bei Synchronisationsfehler AD06: LDA #$35 ; Nur I/O und RAM in C64-Adressraum einblenden STA *$01 LDA #$1B ; Bildschirm wieder einschalten STA $D011 NOP LDA #$1D ; "?LOAD ERROR" SEC ; Zeichen für "Fehler" RTS ; Abschluss vorbereiten, dann Datei per Hypra-Load-Protokoll laden P_AE: LDY #$00 ; Zähler initialisieren AE00: LDA P_AF,Y ; Routine P_AF lesen ab $F042 STA TARGET3,Y ; und umkopieren an $0130 INY CPY #$1F ; Komplette Routine kopiert (27 Bytes würden reichen) BNE AE00 ; Rücksprung falls noch nicht kopiert JSR P_AD ; Datei laden JMP TARGET3 ; Abschluss über Kopie von P_AF NOP ; Abschluss: Falls nötig ROMs wieder einschalten und Hypra-Load deaktivieren P_AF: PHP ; Flags merken (Fehlerkennzeichen) PHA ; A merken (Fehlernummer) LDA #$00 ; Prüfsumme initialisieren TAY ; Zähler initialisieren AF00: EOR $A000,Y ; Prüfsumme über erste 256 Byte des kopierten BASIC-ROM errechnen INY BNE AF00 CMP #$80 ; Prüfsumme korrekt? BEQ AF01 ; Sprung falls korrekt LDA #$37 ; Nicht korrekt, also Kopie des BASIC-ROM überschrieben STA *$01 ; daher alle ROMs wieder einschalten und HYPRA-Load deaktivieren AF01: LDX *$AE ; Endadresse des Ladevorgangs nach X/Y LDY *$AF PLA ; A zurückholen (Fehlernummer) PLP ; Flags zurückholen (CF=0: "OK", CF=1: "Fehler") CLI ; Interrupts wieder zulassen RTS ; Rückkehr zur LOAD-Routine ($E178) ; Unnötige Kopien von ROM-Routinen für RS232, teilweise unvollständig ; Unvollständige Kopie (ab Adresse $F05D) ; der ROM-Routine "RS232 CHKIN, Eingabe auf RS232 setzen" an Adresse $F04D ; Von Hypra-Load nicht verwendet, daher nicht kommentiert P_AG: CMP $AD10,X BEQ AG03 AG00: LDA $02A1 LSR A BCS AG00 LDA $DD01 AND #$FD STA $DD01 AG01: LDA $DD01 AND #$04 BEQ AG01 AG02: LDA #$90 CLC JMP AA04 LDA $02A1 AND #$12 BEQ AG02 AG03: CLC RTS ; Kopie der ROM-Routine "GET von RS232" an Adresse $F086 ; Von Hypra-Load nicht verwendet, daher nicht kommentiert P_AH: LDA $0297 LDY $029C CPY $029B BEQ AH00 AND #$F7 STA $0297 LDA ($F7),Y INC $029C RTS AH00: ORA #$08 STA $0297 LDA #$00 RTS ; Kopie der ROM-Routine "Ende der RS232-Übertragung abwarten" an Adresse $F0A4 ; Vor jedem Senden von LISTEN oder TALK von Adresse $ED0E aus aufgerufen, aber ; für Hypra-Load nicht verwendet, daher nicht kommentiert P_AI: PHA LDA $02A1 BEQ AI01 AI00: LDA $02A1 AND #$03 BNE AI00 LDA #$10 STA $DD0D LDA #$00 STA $02A1 AI01: PLA RTS
Floppy-seitige Schnelllade-Routinen[Bearbeiten | Quelltext bearbeiten]
Der folgende Codeblock wird in der Kopie des Kernal ROM ab Adresse BUFFER1=$F8E2
bereitgehalten und überschreibt hier einen Teil der Routinen für die Datassette. Er wird vor jedem Ladevorgang ins FLOPPY-RAM ab Adresse TARGET1=$0300
kopiert.
SOURCE1: ; Kommentare übernommen aus den Büchern: ; Rainer Ellinger/Lothar Englisch/Ralph Gelfand/Norbert Sczcepanowski: ; "Das große Floppybuch zur 1541" ; Rainer Ellinger: ; "Commodore 1571 & 1570 - Das große Floppybuch" P_AJ: LDA *$00 ; Jobrückmeldung holen AND #$06 CMP #$02 ; Fehler "Blockheader nicht gefunden"/"SYNC-Markierung nicht gefunden"? BEQ AJ00 ; Sprung falls Fehler JMP $FD9E ; Fehler $01 ("00,OK") zurückgeben AJ00: NOP LDA #$05 ; Maximale Anzahl Prüfsummenfehler STA *$09 ; merken ; Geringfügig modifizierte Kopie des Floppy-ROM ab $F3B1 (Spur suchen) AJ01: LDX #$5A ; Zahl der Leseversuche (90) STX *$4B ; festlegen LDX #$00 ; Zähler für Zahl der Headerbytes löschen LDA #$52 ; GCR-Kennzeichen für Blockheader STA *$24 ; merken AJ02: JSR $F556 ; auf SYNC-Markierung warten AJ03: BVC AJ03 ; ist Leseelektronik bereit? CLV ; ja, Flag wieder zurücksetzen LDA $1C01 ; Headerkennzeichen von Diskette lesen CMP *$24 ; mit Kennzeichen für Block vergleichen BEQ AJ04 ; liegt kein Blockheader vor? DEC *$4B ; Zähler für Leseversuche erniedrigen BNE AJ02 ; 90 Leseversuche ausgeführt LDA #$0A ; Fehlernummer JMP $F969 ; ausgeben AJ04: BVC AJ04 ; doch, auf nächstes Byte warten CLV ; Leseelektronik wieder aktivieren LDA $1C01 ; Byte von Diskette lesen STA *$25,X ; und in Headerpuffer speichern INX ; Zähler erhöhen CPX #$07 ; mit Zahl der Headerbytes vergleichen BNE AJ04 ; bereits ganzer Header eigelesen? JSR $F497 ; ja, Header von GCR nach Binär wandeln LDA *$16 ; Prüfsumme des Headers EOR *$17 ; berechnen EOR *$18 EOR *$19 EOR *$1A BEQ AJ05 ; Prüfsummenfehler aufgetreten? DEC *$09 ; Zähler für Prüfsummenfehler erniedrigen BNE AJ01 ; Sprung falls noch nicht 5 Prüfsummenfehler aufgetreten JMP $F41E ; Fehler $09 ("27,WRITE ERROR) zurückgeben AJ05: LDA *$18 ; Spurnummer aus gelesenem Header CMP *$06 ; mit gesuchter Spurnummer vergleichen BEQ AJ06 ; Sprung wenn richtige Spur gelesen JMP $F40B ; Fehler $02 ("20,READ ERROR") zurückgeben AJ06: STA *$22 ; Als Spurnummer des aktuellen Jobs speichern LDA #$06 ; Puffer an Adresse $0600 STA *$31 ; als aktuellen Datenpuffer setzen JMP AK11 ; Geringfügig modifizierte Kopie des Floppy-ROM ab $F510 (Sektorheader suchen) P_AK: LDA *$12 ; Erstes Zeichen der ID LDX *$13 ; Zweites Zeichen der ID STA *$16 ; in Headerpuffer übertragen STX *$17 ; in Headerpuffer übertragen LDA *$06 ; Spurnummer holen STA *$18 ; und in Headerpuffer übertragen LDA *$07 ; Sektornummer holen STA *$19 ; und in Headerpuffer übertragen LDA #$00 ; Prüfsumme EOR *$16 ; des erstellten Sektorheaders EOR *$17 ; berechnen EOR *$18 ; und in den EOR *$19 ; Headerpuffer STA *$1A ; schreiben JSR $F934 ; Sektorheader in GCR-Bytes umwandeln LDX #$5A ; Zahl der Leseversuche festlegen (90) AK00: JSR $F556 ; auf nächste SYNC-Markierung warten LDY #$00 ; Pufferzeiger löschen AK01: BVC AK01 ; auf Byte von Diskette warten CLV ; BYTE-READY Flag wieder bereit machen LDA $1C01 ; Byte vom Lesekopf holen CMP $0024,Y ; und m. hergestelltem Header vergleichen BEQ AK02 ; Werte nicht identisch? DEX ; Zähler der Leseversuche erniedrigen BNE AK00 ; noch einen Leseversuch durchführen? JMP $F551 ; Fehlermeldung ausgeben AK02: INY ; doch, Pufferzeiger auf nächstes Zeichen CPY #$08 ; mit Zahl der Headerbytes vergleichen BNE AK01 ; gesamter Header überprüft? JSR $F556 ; auf nächste SYNC-Markierung warten ; Geringfügig modifizierte Kopie des Code ab $F4D4 (Sektor lesen) AK03: BVC AK03 ; auf Byte von Diskette warten CLV ; Leseelektronik wieder bereit machen LDA $1C01 ; Byte vom Kopf lesen STA ($30),Y ; und in aktuellen Puffer schreiben INY ; Pufferzeiger auf nächstes Byte setzen BNE AK03 ; Puffer schon voll? LDY #$BA ; ja, Pufferzeiger auf Zusatzpuffer AK04: BVC AK04 ; auf nächstes Byte von Diskette warten CLV ; Flag wieder bereit machen LDA $1C01 ; Byte von Lesekopf holen STA $0100,Y ; und in Zusatzpuffer schreiben INY ; Pufferzeiger auf nächstes Byte setzen BNE AK04 ; Zusatzpuffer voll? JSR $F8E0 ; ja, Sektor von GCR nach Binär wandeln LDA *$38 ; erstes Byte des Datenblock holen und CMP *$47 ; Kennzeichen für Datenblockheader BEQ AK05 ; Datenblock? JMP $F4F6 ; nein, Fehlermeldung AK05: JSR $F5E9 ; Prüfsumme der Daten berechnen CMP *$3A ; mit gelesenem Wert vergleichen BEQ AK06 ; beide identisch? JMP $F502 ; Fehler $05 ausgeben ; Datenblock an Adresse $0600 per Hypra-Load-Protokoll übertragen AK06: LDY #$00 ; Zeiger auf aktuelles Datenbyte im Puffer LDA #$55 ; Synchronisationsbyte JSR P_AL ; $55 auf seriellem Bus senden ; Datenbyte aus Puffer an Adresse $0600 per Hypra-Load-Protokoll übertragen AK07: LDA $0600,Y ; Byte aus Puffer holen STA *$77 ; und merken AK08: BIT $1800 ; Warten auf ATN low BPL AK08 LDA #$10 ; Empfangsbereit: CLOCK und DATA auf high, ATN nicht automatisch beantworten STA $1800 AK09: BIT $1800 ; Warten auf ATN high BMI AK09 ; Alle 24 Systemtakte 2 Datenbits auf CLOCK und DATA übertragen LDX #$00 ; Initialer Wert TXA ; A=0 ROR *$77 ; Bit 0 des Datenbyte nach CF ROL A ROL A ROR *$77 ; Bit 1 des Datenbyte nach CF ROL A ROL A ; A=%0000x0y0, x=Bit 0, y=Bit 1 STA $1800 ; Bit 0 auf CLOCK, Bit 1 auf DATA übertragen TXA ; A=0 ROR *$77 ; Bit 2 des Datenbyte nach CF ROL A ROL A ROR *$77 ; Bit 3 des Datenbyte nach CF ROL A ROL A ; A=%0000x0y0, x=Bit 2, y=Bit 3 STA $1800 ; Bit 2 auf CLOCK, bit 3 auf DATA übertragen TXA ; A=0 ROR *$77 ; Bit 4 des Datenbyte nach CF ROL A ROL A ROR *$77 ; Bit 5 des Datenbyte nach CF ROL A ROL A ; A=%0000x0y0, x=Bit 4, y=Bit 5 STA $1800 ; Bit 4 auf CLOCK, Bit 5 auf DATA übertragen TXA ; A=0 ROR *$77 ; Bit 6 des Datenbyte nach CF ROL A ROL A ROR *$77 ; Bit 7 des Datenbyte nach CF ROL A ROL A ; A=%0000x0y0, x=Bit 6, y=Bit 7 STA $1800 ; Bit 6 auf CLOCK, Bit 7 auf DATA übertragen LDX #$02 ; Verzögerung AK10: DEX BNE AK10 LDA #$0F ; Stopbit: CLOCK und DATA auf low STA $1800 INY ; Y auf nächstes Byte des Puffers richten BNE AK07 ; Rücksprung falls noch nicht letztes Datenbyte NOP NOP NOP NOP NOP NOP NOP LDA $1C00 ; Floppy-LED aus ORA #$08 STA $1C00 AK11: LDA $0600 ; Letzter Sektor der Datei erreicht? BNE AK13 ; Sprung wenn nicht letzter Sektor AK12: JMP $FD9E ; Fehler $01 ("OK") zurückgeben AK13: CMP *$18 ; Mit Spurnummer im Header vergleichen BNE AK12 ; Sprung falls nicht aktuelle Spur STA *$06 ; Als Spur des nächsten Blocks speichern LDA $0601 ; Sektor des nächsten Blocks holen STA *$07 ; merken JMP P_AK ; Nächsten Sektor lesen ; A auf seriellem Bus senden P_AL: STA *$77 ; Zu sendendes Byte merken AL00: BIT $1800 ; Warten auf ATN low BPL AL00 LDA #$10 ; ATN nicht automatisch beantworten, CLOCK und DATA auf high STA $1800 AL01: BIT $1800 ; Warten auf ATN high BMI AL01 LDX #$04 ; 4 Bitpaare senden AL02: LDA #$00 ROR *$77 ; Niederwertiges Bit eines Bitpaars holen ROL A ; Kommt schließlich nach Bit 4 von A ROL A ROR *$77 ; Höherwertiges Bit eines Bitpaars holen ROL A ; Kommt schließlich nach Bit 6 von A ROL A STA $1800 ; Niederwertiges Bit per CLOCK, höherwertiges Bit per DATA senden DEX ; Schon 4 Bitpaare gesendet? BNE AL02 ; Rücksprung, falls noch nicht alle Bitpaare gesendet NOP ; Verzögerung NOP NOP NOP NOP NOP LDA #$0F ; CLOCK und DATA auf low STA $1800 RTS RTS ; Job <A> für Puffer 0 (Adresse $0300) ausführen lassen; Rückmeldung in A P_AM: STA *$00 ; Jobcode speichern CLI AM00: LDA *$00 ; Warten, bis Jobcode durch Rückmeldung ersetzt BMI AM00 SEI RTS ; Laderoutine im Floppy-Speicher P_AN: SEI NOP NOP NOP NOP NOP NOP LDA *$18 ; Spurnummer des nächsten Datenblocks holen STA $0600 ; im ersten Byte des Datenpuffers speichern STA *$06 ; und als Spur für Puffer 0 LDA *$19 ; Sektornummer des nächsten Datenblocks holen STA $0601 ; im zweiten Byte des Datenpuffers speichern STA *$07 ; und als Sektor für Puffer 0 AN00: LDA #$04 ; Zähler für Kopfpositionierungen STA *$78 ; setzen LDA #$E2 ; Jobcode $Ex="Programm in Jobschleife einbinden" JSR P_AM ; Job in Puffer 0 (an Adresse $300) ausführen CMP #$02 ; Rückmeldung="kein Fehler aufgetreten"? BCC AN05 ; Sprung falls kein Fehler AN01: LDY #$00 ; Zähler für Kopfpositionierungen STY *$78 ; setzen AN02: LDY *$78 LDA $FEDB,Y ; Kopfbewegung bei Lesefehler BEQ AN03 ; Sprung falls keine weitere Bewegung CLI JSR $D676 ; Kopf um die im Akku angegebenen Halbspurschritte bewegen SEI LDA #$E2 ; Job $E2 ausführen JSR P_AM ; für Puffer an Adresse $0300 aufrufen CMP #$02 ; Rückmeldung="OK"? BCC AN05 ; Sprung falls kein Fehler INC *$78 ; Zähler für Kopfpositionierungen erhöhen BNE AN02 ; Sprung falls noch nicht 256 Versuche AN03: LDA #$C0 ; Job $C0 (Schreib-/Lesekopf auf Spur 0 fahren) ausführen JSR P_AM LDA #$E2 ; Job $E2 (Programm in Jobschleife einbinden) ausführen JSR P_AM ; für Puffer an Adresse $0300 aufrufen CMP #$02 ; Rückmeldung="OK"? BCC AN05 ; Sprung falls kein Fehler LDA #$FF ; $FF senden ("LOAD ERROR") JSR P_AL AN04: JMP $EB22 ; Zeropage initialisieren AN05: LDA $0600 ; Spur des nächsten Datenblocks holen BEQ AN04 ; Sprung, falls letzter Datenblock erreicht CMP *$18 ; Nächster Datenblock in gleicher Spur? BEQ AN01 ; Sprung falls ja LDA $0600 ; Spur des nächsten Datenblocks nochmals holen (unnötig) STA *$06 ; und merken LDA $0601 ; Sektornummer des nächsten Datenblocks holen STA *$07 ; und merken JMP AN00 ; Neue Spur ansteuern und nächsten Datenblock einlesen NOP NOP NOP NOP
Startroutinen[Bearbeiten | Quelltext bearbeiten]
Der folgende Codeblock wird ab Adresse TARGET2=$F409
in die Kopie des Kernal-ROM kopiert und überschreibt dort teilweise die Ansteuerung der RS232-Schnittstelle. Die hier abgelegten Routinen werden in der Startphase des Ladevorgangs benötigt.
SOURCE2: ; Einsprung aus LOAD-Routine (Adresse $F4F9); die ersten beiden Bytes ; (=Ladeadresse) sind bereits übertragen und an Adresse $AE/$AF gespeichert ; Routine P_AP ins RAM ab TARGET3=$0130 kopieren und dort ausführen P_AO: LDY #$00 AO00: LDA P_AP,Y STA TARGET3,Y INY CPY #$1F BNE AO00 JMP TARGET3 ; ROMs einschalten und langsam laden, falls Kopie des BASIC-ROM beschädigt P_AP: LDA #$00 ; Prüfsumme initialisieren TAY ; Zähler initialisieren AP00: EOR $A000,Y ; Prüfsumme über erste 256 Byte des kopierten BASIC-ROM errechnen INY BNE AP00 CMP #$80 ; Prüfsumme korrekt? BEQ AP02 ; Sprung falls korrekt LDA #$37 ; Nicht korrekt, also Kopie des BASIC-ROM beim Laden überschrieben STA *$01 ; daher alle ROMs wieder einschalten und HYPRA-Load deaktivieren AP01: JMP $F501 ; Rücksprung in langsame Originalroutine für LOAD AP02: JMP P_AQ ; Sprung zur HYPRA-Load-Routine P_AQ: LDY #$00 LDA ($BB),Y ; Erstes Zeichen des Dateinamens holen CMP #$24 ; '$' (Inhaltsverzeichnis)? BEQ AP01 ; Inhaltsverzeichnis langsam laden ; Sicherstellen, dass außer der Floppy #8 keine weiteren Geräte am IEC-Bus LDA #$01 ; Geräteadresse (läuft von 1..15) STA *$A7 ; Geräteadresse merken AQ00: LDA #$00 STA *$90 ; Statuswort ST löschen LDA *$A7 ; LISTEN nacheinander an Gerät 1..15 senden JSR $ED0C LDA #$6F ; Sekundäradresse 15 JSR $EDB9 ; nach LISTEN senden LDA *$90 ; Statuswort ST holen BPL AQ02 ; Kein Fehler, dann Gerät gefunden AQ01: INC *$A7 ; Geräteadresse hochzählen LDA *$A7 CMP #$10 ; Maximale Adresse überschritten? BNE AQ00 ; Sprung, falls noch nicht alle Adressen geprüft JMP P_AA ; Zur Schnell-Laderoutine AQ02: LDA *$A7 ; Adresse des gefundenen Geräts holen CMP #DRIVE ; Mit Adresse der Floppy vergleichen BEQ AQ01 ; Floppy gefunden, also weiter LDY #$00 ; Anderes Gerät als Floppy gefunden, Fehlermeldung ausgeben AQ03: LDA AQ05,Y ; Meldung "BITTE NUR FLOPPY ANSCHALTEN" zeichenweise lesen BEQ AQ04 ; Nullbyte markiert Meldungsende JSR $FFD2 ; Zeichen ausgeben INY ; Zeiger auf nächstes Zeichen BNE AQ03 ; Rücksprung, nächstes Zeichen der Meldung ausgeben AQ04: JSR $FFE1 ; Warten auf STOP-Taste (als Bestätigung, dass nur Floppy #8 an) BNE AQ04 INC $EAEA ; Sollte wohl "NOP" sein, erhöht stattdessen den Repeat- NOP ; Verzögerungszähler in der ROM-Kopie der Tastaturabfrage NOP JMP P_AA ; Zur Schnell-Laderoutine NOP AQ05: DB $0D,"bitte nur floppy anschalten",$0D DB $00,$00,$00,$00,$00,$00 AQ06: DB $00,$00,$00,$00,$00,$00
Initialisierung[Bearbeiten | Quelltext bearbeiten]
Die folgenden Routinen werden ausgeführt, wenn das Programm Hypra-Load mit RUN gestartet wird. Sie kopieren BASIC- und KERNAL-ROM des C64 in das darunterliegende RAM, deaktivieren dann die ROMs und übertragen die einzelnen Codeabschnitte von Hypra-Load an (hoffentlich) nicht benötigte Stellen des KERNAL-ROMs. Damit haben sie ihre Aufgabe erfüllt — bei einem anschließenden Kaltstart wird der von ihnen belegte Speicherplatz freigegeben, und üblicherweise von nächsten mittels Hypra-Load geladenen Programm überschrieben.
P_AR: SEI LDA #$37 ; Alle ROMs einschalten STA *$01 ; Alle ROMs ins darunterliegende RAM kopieren LDY #<$A000 ; BASIC-ROM beginnt an Adresse $A000 STY *$03 LDA #>$A000 STA *$04 AR00: LDA ($03),Y ; Alle ROMs sowie I/O-Bereich auf sich selbst kopieren STA ($03),Y ; Kopiert unter anderem den CIA-Timer an $DC04/$DC05 auf sich INC *$03 ; selbst, erhöht dadurch die Frequenz des Systeminterrupt BNE AR00 INC *$04 BNE AR00 ; Kopie des KERNAL-ROMs patchen ; Neuer Defaultwert für Prozessorport (Speicheraufteilung) LDA #$E5 ; ROMs ausgeschaltet, I/O sichtbar STA $FDD6 ; KERNAL-Routinen patchen LDA #$4C ; JMP STA $FE72+$00 ; NMI-Routine für RS232 überspringen, da überschrieben STA $F4F9+$00 ; Sprung in Schnelladeroutine statt STOP-Taste abzufragen ; RS232 NMI-Routine überspringen LDA #$BC ; NMI-Routine endet bei $FEBC (Register zurückholen und RTI) STA $FE72+$01 LDA #$FE STA $FE72+$02 ; Aus IEC-Load nach P_AO springen statt STOP-Taste abzufragen LDA #<P_AO STA $F4F9+$01 LDA #>P_AO STA $F4F9+$02 ; Datassetten-Routinen ab BUFFER1=$F8E2 mit Codeblock SOURCE1 überschreiben LDA #<SOURCE1 STA *$03 LDA #>SOURCE1 STA *$04 LDA #<BUFFER1 STA *$05 LDA #>BUFFER1 STA *$06 AR01: LDA ($03),Y ; 512 Byte umkopieren STA ($05),Y INY BNE AR01 INC *$04 INC *$06 LDA *$06 CMP #>BUFFER1+$02 BNE AR01 ; RS232-Routinen ab TARGET0=$EEBB mit Codeblock SOURCE0 überschreiben LDA #<SOURCE0 LDX #>SOURCE0 STA *$03 STX *$04 LDA #<TARGET0 LDX #>TARGET0 STA *$05 STX *$06 AR02: LDA ($03),Y STA ($05),Y INY BNE AR02 INC *$04 INC *$06 LDA *$06 CMP #>TARGET0+$02 BNE AR02 ; Startmeldung des C64 überschreiben AR03: LDA AR05,Y STA $E45F,Y INY CPY #AR06-AR05 BNE AR03 ; RS232-Routinen ab TARGET2=$F409 mit Codeblock SOURCE2 überschreiben LDY #$00 AR04: LDA SOURCE2,Y STA TARGET2,Y INY CPY #SOURCE2_LEN BNE AR04 ; Alle ROMs ausschalten LDA #$35 STA *$01 JMP $FCF8 ; Kaltstart ohne Test des Arbeitsspeichers, stellt Interruptfrequenz wieder her NEWLINE EQU $0D REVS_ON EQU $12 REVS_OFF EQU $92 CLEAR EQU $93 NOP NOP AR05: DB $00,' BYTES FREE',NEWLINE,$00,$00,$00,$00,$00,$00,$00 DB CLEAR,NEWLINE,' **** C64-HYPRA-LOAD-SYSTEM ****',NEWLINE,NEWLINE DB ' (C)84 ',REVS_ON,'TRIBAR',REVS_OFF,' ',$00 DB $81,$48,$20 AR06: DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 DB '8,2,230,175,232',$00,$D4,$94