Hypra-Load/Quellcode 2.1

Aus C64-Wiki
Zur Navigation springenZur Suche springen

<< zurück zu Hypra-Load

Die folgenden Abschnitte stellen das vollständige, disassemblierte Programm Hypra-Load 2.1 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.

Autostart-Header[Bearbeiten | Quelltext bearbeiten]

Der folgende, kurze Codeabschnitt sorgt dafür, dass Hypra-Load 2.1 nach dem Laden sofort startet und sich installiert. Er überschreibt während des Ladens den STOP-Vektor und biegt ihn auf die zuvor schon geladene Routine P_AA um. Da während des Ladens an ROM-Adresse $F4F9 nach jedem Lesen eines Bytes die STOP-Taste über diesen Vektor abgefragt wird, wird anschließend sofort die Routine P_AA aufgerufen. Diese sorgt zum einen dafür, dass das Laden des restlichen Programmcodes ab Adresse $0800 fortgesetzt wird, zum anderen biegt sie den CLALL-Vektor auf die Initialisierungsroutine P_AJ von Hypra-Load und sorgt damit dafür, dass das Programm sofort nach dem Laden gestartet und installiert wird. Anschließend setzt die Routine noch den STOP-Vektor auf seinen Originalwert zurück, bevor sie zum "IEC-Load" zurückkehrt.

ORG $02ED

DRIVE EQU $08   ; Geräteadresse 8

P_AA: LDA #$08      ; Adresse des nächsten zu speichernden Byte (high)
      STA *$AF      ; ... auf BASIC-Programmspeicher richten
      LDA #$00      ; Adresse des nächsten zu speichernden Byte (low)
      STA *$AE      ; ... auf BASIC-Programmspeicher richten
      LDA #<SOURCE2 ; Low-Byte der Startadresse für die Initialisierungsroutine
      STA $032C     ; als CLALL-Vektor low
      LDA #>SOURCE2 ; High-Byte der Startadresse für die Initialisierungsroutine
      STA $032D     ; als CLALL-Vektor high
      LDA #$ED      ; STOP-Vektor low
      STA $0328     ; ... wiederherstellen (obsolet, da unverändert)
      LDA #$F6      ; STOP-Vektor high
      STA $0329     ; ... wiederherstellen
      RTS           ; zurück zum "IEC-Load"

; Kopie des Speicherinhalts zwischen dem Ende von P_AA und dem STOP-Vektor
      DW  $AE86     ; Vektor für Ausdruck auswerten
      DB  $00       ; Akku für SYS-Befehl
      DB  $00       ; X-Reg für SYS-Befehl
      DB  $00       ; Y-Reg für SYS-Befehl
      DB  $00       ; Status-Register für SYS-Befehl
      JMP $B248     ; JMP-Befehl für USR-Funktion (zeigt auf "?ILLEGAL QUANTITY")
      DB  $00
      DW $EA31      ; IRQ-Vektor
      DW $FE66      ; BRK-Vektor
      DW $FE47      ; NMI-Vektor
      DW $F34A      ; OPEN-Vektor
      DW $F291      ; CLOSE-Vektor
      DW $F20E      ; CHKIN-Vektor
      DW $F250      ; CKOUT-Vektor
      DW $F333      ; CLRCH-Vektor
      DW $F157      ; INPUT-Vektor
      DW $F1CA      ; OUTPUT-Vektor

      DW P_AA       ; Verbogener STOP-Vektor ($02ED statt $F6ED)

C64-seitige Schnelllade-Routinen[Bearbeiten | Quelltext bearbeiten]

Der folgende Code liegt im Kernal-ROM an Adresse TARGET0=$F72C und überschreibt hier einen Teil der Routinen für die Datassette. 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:

P_AB: LDA #<BUFFER1 ; Quelladresse der Floppy-Routinen ab $EEBB
      LDX #>BUFFER1
      STA *$A7      ; setzen
      STX *$A8
      LDA #<TARGET1 ; Zieladresse $0300 im RAM der Floppy
      LDX #>TARGET1
      STA *$A9      ; setzen
      STX *$AA
; Routinen in Abschnitten von 29 Bytes per Memory-Write in den
; Floppyspeicher ab Adresse TARGET1=$0300 übertragen
AB00: 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     ; IECOUT ein Byte auf IEC-Bus ausgeben
      LDA #$2D      ; '-'
      JSR $EDDD     ; IECOUT ein Byte auf IEC-Bus ausgeben
      LDA #$57      ; 'W'
      JSR $EDDD     ; IECOUT ein Byte auf IEC-Bus ausgeben
      LDY #$00
      LDA *$A9      ; Zieladresse für Schreiben low
      JSR $EDDD     ; IECOUT ein Byte auf IEC-Bus ausgeben
      LDA *$AA      ; Zieladresse für Schreiben high
      JSR $EDDD     ; IECOUT ein Byte auf IEC-Bus ausgeben
      LDA #$1D      ; Anzahl Zeichen pro Datenblock
      JSR $EDDD     ; IECOUT ein Byte auf IEC-Bus ausgeben
AB01: LDA ($A7),Y
      JSR $EDDD     ; IECOUT ein Byte auf IEC-Bus ausgeben
      INY
      CPY #29       ; Blockgröße erreicht?
      BCC AB01      ; Rücksprung, falls noch nicht
      JSR $EDFE     ; UNLISTEN senden
; Lesezeiger auf nächsten Block richten
      CLC
      LDA *$A7      ; Low-Byte des Lesezeigers
      ADC #29       ; um Blockgröße erhöhen
      STA *$A7      ; und zurückschreiben
      BCC AB02      ; Sprung falls kein Additionsübertrag
      INC *$A8      ; High-Byte des Lesezeigers erhöhen
; Schreibzeiger auf nächsten Block richten
      CLC
AB02: LDA *$A9      ; Low-Byte des Schreibzeigers
      ADC #29       ; um Blockgröße erhöhen
      STA *$A9      ; und zurückschreiben
      BCC AB03      ; Sprung falls kein Additionsübertrag
      INC *$AA      ; High-Byte des Schreibzeigers erhöhen
AB03: LDA *$A9      ; Low-Byte des Schreibzeigers
      CMP #$D0      ; Zeigt Schreibzeiger auf $04D0 (16*29=464 Bytes geschrieben)?
      BNE AB00      ; Rücksprung falls noch nicht
      LDA #DRIVE
      JSR $ED0C     ; LISTEN senden
      LDA #$6F
      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
      LDA #<P_AG
      JSR $EDDD     ; IECOUT ein Byte auf IEC-Bus ausgeben
      LDA #>P_AG
      JSR $EDDD     ; IECOUT ein Byte auf IEC-Bus ausgeben
      JSR $EDFB     ; UNTALK senden (müsste UNLISTEN sein)
      JSR $EEB3     ; 1 ms Verzögerung
      JSR $EEB3     ; 1 ms Verzögerung
      SEI
      LDA #$0B      ; Bildschirm abschalten
      STA $D011
      LDA #$00      ; Blockzähler initialisieren
      STA *$BB      ; und merken
AB04: LDA #$FE      ; 254 Bytes
      STA *$BC      ; als Zahl gültiger Datenbytes im Blocks merken
      JSR P_AC      ; Synchronisationsbyte vom IEC-Bus holen
      CMP #$FF      ; Test auf Fehlercode
      BEQ AB13      ; Sprung falls Fehler (Sprung sollte nach AB11 führen)
      JSR P_AC      ; Erstes Byte (Spur des nächsten Blocks) vom IEC-Bus holen
      STA *$B7      ; und merken
      JSR P_AC      ; Zweites Byte (Sektor des nächsten Blocks) vom IEC-Bus holen
      TAY           ; in Y merken
      LDA *$B7      ; Spur des nächsten Blocks holen
      BNE AB05      ; Sprung wenn nicht letzter Block
; Letzten Block einer Datei behandeln
      DEY
      STY *$BC      ; Zahl der gültigen Datenbytes in diesem Block merken
AB05: LDA *$BB      ; Blockzähler
      BNE AB06      ; Sprung wenn nicht erster Block
      DEC *$BC      ; ansonsten zwei Datenbytes weniger im ersten Block
      DEC *$BC
      JSR P_AC      ; Ladeadresse überlesen
      JSR P_AC
AB06: LDY #$00      ; Schreibindex für byteweises Laden
AB07: JSR P_AC      ; Datenbyte vom IEC-Bus holen
      STA ($AE),Y   ; und an die Ladeadresse im Speicher schreiben
      INY           ; Schreibzeiger weiterbewegen
      CPY *$BC      ; Schon alle gültigen Datenbytes geschrieben?
      BNE AB07      ; Rücksprung, falls noch nicht alle Bytes
      CLC
      LDA *$BC      ; Low-Byte der Ladeadresse
      ADC *$AE      ; um Anzahl der gültigen Bytes im aktuellen Block
      STA *$AE      ; weiterbewegen
      BCC AB08      ; Sprung falls kein Additionsübertrag
      INC *$AF      ; High-Byte der Ladeadresse erhöhen
AB08: INC *$BB      ; Blockzähler erhöhen
      LDA *$B7      ; Spur des nächsten Datenblocks holen
      BNE AB04      ; Rücksprung falls nicht letzter Datenblock
      DEC *$BB      ; Blockzähler erniedrigen
      BNE AB09      ; Sprung, falls Programm aus mehr als einem Block bestand
      INC *$BC      ; Anzahl noch zu überlesender Datenbytes um zwei erhöhen
      INC *$BC
AB09: LDA *$BC      ; Anzahl noch zu lesender Datenbytes holen
      CMP #$FE      ; Schon alle restlichen Datenbytes empfangen?
      BEQ AB10      ; Sprung falls ja
      JSR P_AC      ; Nächstes Datenbyte holen und ignorieren
      INC *$BC      ; Anzahl gültiger Datenbytes im Block erhöhen
      CLC
      BCC AB09      ; Immer Rücksprung
AB10: LDA #$00      ; Statusbyte "OK"
      DB  $2C
AB11: LDA #$1D      ; "?LOAD ERROR" (nicht verwendet)
      STA *$90      ; Statusbyte schreiben
      JMP P_AD      ; Ladevorgang abschließen
AB12: LDA *$90      ; Statusbyte holen
      CMP #$01      ; "OK" löscht CF, alle anderen Werte setzen CF
      PHA           ; Fehlercode auf Stack sichern
      PHP           ; Fehlerflag CF auf Stack sichern
AB13: TYA           ; Anzahl gültiger Datenbytes im letzten Block
      LDX *$AE      ; Programm-Endeadresse low
      LDY *$AF      ; Programm-Endeadresse high
      JMP TARGET2

; Datenbyte mit Hypra-Protokoll empfangen und in Akku zurückliefern
P_AC: LDA #$23      ; DATA low
      STA $DD00
AC00: BIT $DD00     ; Warten auf CLOCK high ("bereit")
      BVC AC00
      LDA #$03      ; DATA high
      STA $DD00
      LDX #$07      ; Verzögerung
AC01: DEX
      BNE AC01
      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)
      LDX #$03      ; DATA und CLOCK auf high
      STX $DD00
      RTS

; Abschluss des Ladevorgangs
P_AD: PLA           ; Low-Byte der Ladeadresse vom Stack holen
      TAY           ; und nach Y
      PLA           ; High-Byte der Ladeadresse vom Stack holen
      TAX           ; und nach X
      PHA
      TYA
      PHA
      CPX #$E0      ; Kopie des KERNAL durch Laden überschrieben?
      BCC AD01      ; Sprung falls KERNAL nicht überschrieben
; Prüfsumme über die ersten 256 Bytes der BASIC-ROM-Kopie berechnen
      LDA #$00
      TAY
AD00: EOR $A000,Y
      INY
      BNE AD00
      CMP #$80      ; Prüfsumme korrekt?
      BNE AD03      ; Sprung wenn Prüfsumme falsch
; Prüfsumme über die ersten 256 Bytes der Floppy-seitigen Routinen berechnen
AD01: LDA #$00
      TAY
AD02: EOR BUFFER1,Y
      INY
      BNE AD02
      CMP #$70      ; Prüfsumme korrekt?
      BNE AD03      ; Sprung wenn Prüfsumme falsch
      LDY #$35      ; Alle ROMs ausschalten, Hypra-Load bleibt aktiv
      DB  $2C
AD03: LDY #$37      ; Alle ROMs aktivieren und Hypra-Load deaktivieren
Kurzen Programmabschnitt für Abschluss des Ladevorgangs an Adresse $TARGET2=$0061 kopieren
      LDA #$85        ; "STA *$01"
      STA TARGET2+$00
      LDA #$01
      STA TARGET2+$01
      LDA #$28        ; "PLP" holt Fehlerflag CF vom Stack
      STA TARGET2+$02
      LDA #$68        ; "PLA" holt fehlercode vom Stack
      STA TARGET2+$03
      LDA #$60        ; "RTS" Rückkehr in LOAD-Routine ($E178)
      STA TARGET2+$04
      LDA #$1B      ; Bildschirm wieder einschalten
      STA $D011
      JMP AB12

Floppy-seitige Schnelllade-Routinen[Bearbeiten | Quelltext bearbeiten]

Der folgende Codeblock wird in der Kopie des Kernal ROM ab Adresse BUFFER1=$EEBB bereitgehalten und überschreibt hier einen Teil der RS232-Routinen. 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_AE: LDA *$00      ; Jobrückmeldung holen
      AND #$06
      CMP #$02      ; Fehler "Blockheader nicht gefunden"/"SYNC-Markierung nicht gefunden"?
      BEQ AE00      ; Sprung falls Fehler
      JMP $FD9E     ; Fehler $01 ("00,OK") zurückgeben
AE00: NOP
      LDA #$05      ; Maximale Anzahl Prüfsummenfehler
      STA *$09      ; merken
AE01: LDX #$5A      ; Zahl der Leseversuche
      STX *$4B      ; festlegen
      LDX #$00      ; Zähler für Zahl der Headerbytes löschen
      LDA #$52      ; GCR-Kennzeichen für Blockheader
      STA *$24      ; merken
AE02: JSR $F556     ; Auf nächste SYNC-Markierung warten
AE03: BVC AE03      ; ist Leseelektronik bereit?
      CLV           ; ja, Flag wieder zurücksetzen
      LDA $1C01     ; Headerkennzeichen von Diskette lesen
      CMP *$24      ; mit Kennzeichen für Block vergleichen
      BEQ AE04      ; liegt kein Blockheader vor?
      DEC *$4B      ; Zähler für Leseversuche erniedrigen
      BNE AE02      ; 90 Leseversuche ausgeführt
      LDA #$0A      ; Fehlernummer
      JMP $F969     ; ausgeben
AE04: BVC AE04      ; doch, auf nächstes Byte warten
      CLV           ; Leseelektronik wieder aktivieren
      LDA $1C01     ; Byte von Diskette lesen
      STA *$25,X    ; und im Headerpuffer speichern
      INX           ; Zähler erhöhen
      CPX #$07      ; mit Zahl der Headerbytes vergleichen
      BNE AE04      ; bereits ganzer eingelesen?
      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 AE05      ; Prüfsummenfehler aufgetreten?
      DEC *$09      ; Zähler für Prüfsummenfehler erniedrigen
      BNE AE01      ; Sprung falls noch nicht 5 Prüfsummenfehler aufgetreten
      JMP $F41E     ; Fehler $09 ("27,WRITE ERROR") zurückgeben
AE05: LDA *$18      ; Spurnummer aus gelesenem Header
      CMP *$06      ; mit gesuchter Spurnummer vergleichen
      BEQ AE06      ; Sprung wenn richtige Spur gelesen
      JMP $F40B     ; Fehler $02 ("20,READ ERROR") zurückgeben
AE06: STA *$22      ; Als Spurnummer des aktuellen Jobs speichern
      LDA #$06      ; Puffer an Adresse $0600
      STA *$31      ; als aktuellen Datenpuffer setzen
      JMP AF08

; Geringfügig modifizierte Kopie des Floppy-ROM ab $F510 (Sektorheader suchen)
P_AF: 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)
AF00: JSR $F556     ; Auf nächste SYNC-Markierung warten
      LDY #$00      ; Pufferzeiger löschen
AF01: BVC AF01      ; 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 AF02      ; Werte nicht identisch?
      DEX           ; Zähler der Leseversuche erniedrigen
      BNE AF00      ; noch einen Leseversuch durchführen?
      JMP $F551     ; Fehlermeldung ausgeben
AF02: INY           ; doch, Pufferzeiger auf nächstes Zeichen
      CPY #$08      ; mit Zahl der Headerbytes vergleichen
      BNE AF01      ; gesamter Header überprüft?
      JSR $F556     ; auf nächste SYNC-Markierung warten
; Geringfügig modifizierte Kopie des Code ab $F4D4 (Sektor lesen)
AF03: BVC AF03      ; 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 AF03      ; Puffer schon voll?
      LDY #$BA      ; ja, Pufferzeiger auf Zusatzpuffer
AF04: BVC AF04      ; auf nächstes Byte von Diskette warten
      CLV           ; Flag wieder bereit machen
      LDA $1C01     ; Byte vom Lesekopf holen
      STA $0100,Y   ; und in Zusatzpuffer schreiben
      INY           ; Pufferzeiger auf nächstes Byte setzen
      BNE AF04      ; 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 AF05      ; Datenblock?
      JMP $F4F6     ; nein, Fehlermeldung
AF05: JSR $F5E9     ; Prüfsumme der Daten berechnen
      CMP *$3A      ; mit gelesenem Wert vergleichen
      BEQ AF06      ; beide identisch?
      JMP $F502     ; Fehler $05 ausgeben
; Datenblock an Adresse $0600 per Hypra-Load-Protokoll ausgeben
AF06: LDY #$00      ; Zeiger auf aktuelles Datenbyte im Puffer
      LDA #$55      ; Synchronisationsbyte
      JSR P_AI      ; $55 auf seriellem Bus senden
AF07: LDA $0600,Y   ; Byte aus Puffer holen
      JSR P_AI      ; und auf seriellem Bus ausgeben
      INY           ; Y auf nächstes Byte des Puffers richten
      BNE AF07      ; Rücksprung falls noch nicht letztes Datenbyte
      LDA $1C00     ; Floppy-LED umschalten
      EOR #$08
      STA $1C00
AF08: LDA $0600     ; Letzter Sektor der Datei erreicht?
      BNE AF10      ; Sprung wenn nicht letzter Sektor
AF09: JMP $FD9E     ; Fehler $01 ("OK") zurückgeben
AF10: CMP *$18      ; Mit Spurnummer im Header vergleichen
      BNE AF09      ; 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_AF      ; Nächsten Sektor lesen

; Laderoutine im Floppy-Speicher
P_AG: SEI
      LDA #$08      ; CLOCK auf low setzen ("nicht bereit")
      STA $1800
      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
AG00: LDA #$04      ; Zähler für Kopfpositionierungen
      STA *$78      ; setzen
      LDA #$E2      ; Jobcode $Ex="Programm in Jobschleife einbinden"
      JSR P_AH      ; Job in Puffer 0 (an Adresse $0300) ausführen
      CMP #$02      ; Rückmeldung="Kein Fehler aufgetreten"?
      BCC AG05      ; Sprung falls kein Fehler
AG01: LDY #$00      ; Zähler für Kopfpositionierungen
      STY *$78      ; setzen
AG02: LDY *$78
      LDA $FEDB,Y   ; Kopfbewegung bei Lesefehler
      BEQ AG03      ; 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_AH      ; für Puffer an Adresse $0300 aufrufen
      CMP #$02      ; Rückmeldung="OK"?
      BCC AG05      ; Sprung falls kein Fehler
      INC *$78      ; Zähler für Kopfpositionierungen erhöhen
      BNE AG02      ; Sprung falls noch nicht 256 Versuche
AG03: LDA #$C0      ; Job $C0 (Schreib-/Lesekopf auf Spur 0 fahren) ausführen
      JSR P_AH
      LDA #$E2      ; Job $E2 (Programm in Jobschleife einbinden) ausführen
      JSR P_AH      ; für Puffer an Adresse $0300 aufrufen
      CMP #$02      ; Rückmeldung ="OK"?
      BCC AG05      ; Sprung falls kein Fehler
      LDA #$FF      ; $FF senden ("LOAD ERROR")
      JSR P_AI
AG04: JMP $EB22     ; Zeropage initialisieren
AG05: LDA $0600     ; Spur des nächsten Datenblocks holen
      BEQ AG04      ; Sprung, falls letzter Datenblock erreicht
      CMP *$18      ; Nächster Datenblock in gleicher Spur?
      BEQ AG01      ; 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 AG00      ; Neue Spur ansteuern und nächsten Datenblock einlesen

; Job <A> für Puffer 0 (Adresse $0300) ausführen lassen, Rückmeldung in A
P_AH: STA *$00      ; Jobcode speichern
      CLI
AH00: LDA *$00      ; Warten, bis Jobcode durch Rückmeldung ersetzt
      BMI AH00
      SEI
      RTS

; Datenbyte aus <A> per Hypra-Load-Protokoll übertragen
P_AI: STA *$77      ; Byte merken
      LDX #$01      ; Initialer Wert
      TXA           ; A=1
AI00: BIT $1800     ; DATA testen
      BEQ AI00      ; Rücksprung falls high
      LDA #$00      ; CLOCK (und DATA) auf high ("bereit")
      STA $1800     ; setzen
      TXA           ; A=1
AI01: BIT $1800     ; DATA testen
      BNE AI01      ; Rücksprung falls low
      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
AI02: DEX
      BNE AI02
      LDA #$08      ; Stopbit: CLOCK auf low
      STA $1800
      RTS

Initialisierung[Bearbeiten | Quelltext bearbeiten]

Die folgenden Routinen werden über den verbogegen CLALL-Vektor angesprungen und ausgeführt, sobald Hypra-Load vollständig geladen ist. 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 — durch den Aufruf des NEW-Befehls wird der von ihnen belegte Speicherplatz freigegeben, und üblicherweise von nächsten mittels Hypra-Load geladenen Programm überschrieben.

SOURCE2:

; Initialisierungscode
; Alle ROMs ins darunterliegende RAM kopieren
P_AJ: LDY #<$A000   ; BASIC-ROM beginnt an Adresse $A000
      STY *$61
      LDA #>$A000
      STA *$62
AJ00: LDA ($61),Y   ; ROM auf sich selbst kopieren
      STA ($61),Y
      INC *$61      ; Low-Byte der Lese-/Schreibadresse erhöhen
      BNE AJ00      ; Rücksprung noch innerhalb der aktuelle Page
      INC *$62      ; High-Byte der Lese-Schreibadresse erhöhen
      BEQ AJ01      ; Sprung falls Ende des KERNAL-ROM erreicht
      LDA *$62      ; High-Byte der Lese-/Schreibadresse holen
      CMP #$C0      ; und mit Ende des BASIC-ROM vergleichen
      BNE AJ00      ; Sprung falls nicht Ende des BASIC-ROM
      LDA #$E0      ; Adresse auf Anfang des KERNAL-ROM setzen
      STA *$62
      BNE AJ00      ; Immer zurückspringen
; Kopie des KERNAL-ROms patchen
AJ01: LDA #$E5      ; ROMs ausgeschaltet, I/O sichtbar
      STA $FDD6
      LDA #$4C      ; JMP
      STA $F4F9+$00
; Aus IEC-Load nach P_AB springen statt STOP-Taste abzufragen
      LDA #<P_AB
      STA $F4F9+$01
      LDA #>P_AB
      STA $F4F9+$02
; Datassetten-Routinen ab TARGET0=$F72C mit Codeblock SOURCE0 überschreiben
      LDX #$00
AJ02: LDA SOURCE0+$0000,X
      STA TARGET0+$0000,X
      INX
      BNE AJ02
AJ03: LDA SOURCE0+$0100,X
      STA TARGET0+$0100,X
      INX
      CPX #SOURCE0_LEN-$0100
      BNE AJ03
; RS232-Routinen ab BUFFER1=$EEBB mit Codeblock SOURCE1 überschreiben
      LDX #$00
AJ04: LDA SOURCE1+$0000,X
      STA BUFFER1+$0000,X
      INX
      BNE AJ04
AJ05: LDA SOURCE1+$0100,X
      STA BUFFER1+$0100,X
      INX
      CPX #SOURCE1_LEN-$0100
      BNE AJ05
; Default-Parameter für LOAD und SAVE setzen
      LDA #DRIVE    ; Gerätenummer
      STA $E1DA     ; als Default-LOAD/SAVE-Parameter setzen
      LDA #$01      ; Sekundäradresse
      STA $E1DC     ; als Default-LOAD/SAVE-Parameter setzen
      LDA #$35      ; I/O und RAM einschalten
      STA *$01
      LDY #$00      ; Lesezeiger für Dateinamen initialisieren
      LDA ($BB),Y   ; Dateinamen zeichenweise lesen
      CMP #$5E      ; und mit Pfeil nach oben vergleichen
      BEQ P_AK      ; Sprung falls Pfeil nach oben gefunden
; Startmeldung anzeigen und zu BASIC zurückkehren, wenn kein Programm nachgeladen werden soll
      JSR $E453     ; BASIC-Vektoren laden
      JSR $E3BF     ; RAM für BASIC initialisieren
      CLC
      JSR $FD15     ; Hardware und I/O Vektoren setzen
      LDA #<AJ06
      LDY #>AJ06
      JSR $AB1E     ; String ausgeben
      JSR $A644     ; BASIC-Befehl NEW
      JMP $E39D     ; Warmstart

AJ06: DB  $0D
      DB  'HYPRA-LOAD V2.1 (C) 1985 TRIBAR',$0D
      DB  'BORIS SCHNEIDER+KARSTEN SCHRAMM',$0D,$00

; Autoload-Funktion
P_AK: CLC
      LDA #$02      ; Um 2 Zeichen
      ADC *$BB      ; den Zeiger auf den Dateinamen
      STA *$BB      ; weiterbewegen
      BCC AK01      ; Sprung falls kein Additionsübertrag
      INC *$BC      ; High-Byte des Zeigers auf den Dateinamen erhöhen
AK01: DEC *$B7      ; Länge des Dateinamens um 2 vermindern
      DEC *$B7
      NOP
      LDX #$00      ;
AK02: LDA SOURCE3,X ; Routine P_AL an Adresse $02C0 umkopieren
      STA P_AL,X
      INX
      CPX #SOURCE3_LEN
      BNE AK02
      JMP P_AL      ; und ausführen

Nachlade-Routine[Bearbeiten | Quelltext bearbeiten]

Beim unmittelbaren Nachladen eines Programms wird der folgende Code an Adresse TARGET3=$02C0 umkopiert und dort ausgeführt. Dieses Umkopieren ist nötig, da vor dem Start des Ladevorgangs alle ROMs wieder aktiviert werden und dadurch die von Hypra-Load installierten Codeanschnitte anschließend unerreichbar sind.

SOURCE3:

; Unmittelbares Nachladen eines Programms
P_AL: JSR $E453  ; BASIC-Vektoren laden
      JSR $E3BF  ; RAM für BASIC initialisieren
      CLC
      JSR $FD15  ; Hardware und I/O Vektoren setzen
      JSR $A644  ; BASIC-Befehl NEW
      JSR $FFD5  ; BASIC-Befehl LOAD
      LDA #$37   ; I/O und alle ROMs einschalten
      STA *$01
      LDA *$AE   ; Programmende=Beginn der Variablen (low)
      STA *$2D
      LDA *$AF   ; Programmende=Beginn der Variablen (high)
      STA *$2E
      JSR $A663  ; BASIC-Befehl CLR
      JSR $A68E  ; Programmzeiger auf BASIC-Start
      JMP $A7AE  ; Interpreterschleife

      DB $00,$31