Hypra-Load/Quellcode 1.0

Aus C64-Wiki
Zur Navigation springenZur Suche springen

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