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