Magic Disk 64

home to index to text: MD9212-KURSE-FLOPPY-KURS_7.1.txt
----------------------------------------
              Floppy-Kurs:              
      "Es rappelt in der Kiste..."      
                (Teil 7)                
----------------------------------------

Hallo und herzlich Willkommen zum siebten Teil des Floppy-Kurses. In diesem Monat möchten wir uns an die Floppy-Programmierung in Assembler heranwagen.
Hierbei werden wir alle notwendigen Routinen zum I/ O-Handling von Maschinenspache aus kennenlernen. Allerdings sollten Sie schon einige Assembler-Vorerfahrung mitbringen und zumindest Kenntnis vom Befehlssatz des 6510- Prozessors haben.

DER EINSTIEG IN ASSEMBLER               

Wie Sie bemerkt haben, waren alle bishe- rigen Programmbeispiele und Befehlserläuterungen in BASIC programmiert. Dies diente hauptsächlich der Einfachheit halber. Wenn Sie nun ein angehender Assemblerprogrammierer sind, so werden Sie sich desöfteren schon einmal gefragt haben, wie Sie die Floppy in Maschinensprache ansprechen können. Dies unterscheidet sich von BASIC zwar schon in einigen Punkten, da es etwas aufwendiger ist, jedoch können wir hier komplett auf schon im Betriebssystem des 64 ers vorhandene Routinen zurückgreifen, die lediglich mit den richtigen Parametern gefüttert werden müssen. Diese Routinen sind dieselben, die auch vom BASIC des C64 benutzt werden, weshalb die Parameterübergabe der von BASIC sehr ähnlich ist. Kommen wir zunächst einmal zu einigen grundlegenden Dingen:
ÜFFNEN UND SCHLIESSEN VON FILES Jedesmal, wenn wir einen Zugriff auf die Floppy ausführen wollten, mussten wir zuvor immer einen Filekanal öffnen.
Hierzu wurden mit Hilfe des OPEN-Befehls einige Parameter an die Floppy übergeben, die zur eindeutigen Definition des Kanals notwendig waren. Diese Parameter waren die Filenummer, die Devicenummer und die Sekundäradresse. Oft folgte der ganzen Anweisung dann auch noch ein Filename, dessen Appendix ebenso die verlangte Operation spezifizierte. Man kann also sagen, daß diese Werte alles Grundwerte sind, die zum Datenaustausch mit der Floppy benötigt werden. Aus diesem Grund müssen sie auch bei jeder Fileoperation angegeben werden. Möchten wir nun eine Operation von Assembler aus durchführen, so müssen wir diese Parameter zunächst dem Betriebssystem mitteilen.
Dies geschieht über die beiden ROM-Routinen " SETPAR" und " SETNAM" . SETPAR hat die Einsprungadresse $ FFBA. An sie werden die Filenummer, sowie Geräteund Sekundäradresse übergeben. SETNAM legt den Filenamen ( inklusive Appendix) fest.
Sie steht bei $ FFBD. Beide Routinen tun nichts anderes, als die gegebenen Werte in Betriebssystem-Zwischenspeichern der Zeropage abzulegen.
Nachdem die Werte festgelegt sind, können wir die Betriebssystemroutine " OPEN" aufrufen, die uns den Kanal mit der Floppy öffnet. Ihre Einsprungadresse ist bei $ FFC0 . Zum Schließen benutzen wir die Routine " CLOSE" bei $ FFC3 .
Wenn wir ein File öffnen, so müssen wir zunächst einmal seinen Namen festlegen.
Diesen legen wir irgendwo im Speicher, an einer bekannten Adresse, ab. Die Länge des Namens benötigen wir ebenso. Als nächstes können wir SETNAM und SETPAR aufrufen. SETNAM wird die Adresse des Filenamens und seine Länge, SETPAR die logische Filenummer, die Geräteund die Sekundäradesse übergeben. Hier eine Öbersicht der Registerbelegungen:

SETPAR: Akku   = Filenummer             
        X-Reg. = Geräteadresse          
        Y-Reg. = Sekundäradresse        
SETNAM: Akku   = Länge des Filenamens   
        X-Reg.  = Low-Byte der Speicher-
                 adresse des Namens     
        Y-Reg. = High-Byte der Speicher-
                 adresse des Namens     

Ich möchte Ihnen hierzu ein Beispiel liefern. Angenommen, Sie wollten das sequentielle File " TEST" zum Lesen öffnen. Als Filenummer wollen wir die 1 wählen, als Sekundäradresse 2 . Die Geräteadresse ist in Bezug auf die Floppy natürlich 8( oder 9,10,11 wenn Sie eine zweite, dritte oder vierte Floppy besitzen) . All diese Parameter entsprechen also dem BASIC-Befehl:

OPEN 1,8,2,"TEST,S,R"                   

Möchten wir diesen Befehl nun in Maschinensprache umsetzen, so schreiben wir folgendes Assembler-Programm. Hierbei gehe ich davon aus, daß wir den Filenamen " TEST, S, R" als ASCII-Code bei Adresse $0334( dem Kasettenpuffer) abgelegt haben:

LDA #$01   ;Filenummer 1                
LDX #$08   ;Geräteadresse 8             
LDY #$02   ;Sekundäradresse 2           
JSR $FFBA  ;SETPAR aufrufen             
LDA #$08   ;Filename ist 8 Zeichen lang 
LDX #$34   ; und liegt bei Adresse      
LDY #$03   ; $0334                      
JSR $FFBD  ;SETNAM aufrufen             

Die Parameter für das File " TEST" wären damit festgelegt. Da das ", S, R" im Filenamen enthalten ist, weiß die Floppy auch gleich schon, daß wir ein sequentielles File lesen möchten.
Als Nächstes müssen wir das zuvor spezifizierte File öffnen. Dies geschieht über die oben schon erwähnte Betriebssystemroutine " OPEN" . Sie benötigt keine Parameter und wird direkt aufgerufen:
JSR $ FFC0 ; File mit zuvor gesetzem Namen und Parametern öffnen Nun ist das File " TEST" geöffnet. Da aber auch jeder Datenfluß einmal ein Ende hat, muß unser File irgendwann einmal wieder geschlossen werden. Dies geschieht, wer hätte das gedacht, mit der ROM-Routine " CLOSE", die bei Adresse $ FFC3 angesprungen wird. Da auch mehrere Files gleichzeitig offen sein können, muß ihr die Filenummer des zu schließenden Files im Akku übergeben werden:
LDA #$01 ; File mit Filenummer 1 JSR $ FFC3 ; schließen DER DATENAUSTAUSCH Wie man ein File öffnet, sollte Ihnen nun klar sein. Wenn Sie jedoch auch noch mit ihm komunizieren wollen ( was sie bestimmt tun möchten, da es anders sinnlos wäre ein File zu öffnen), so müssen Sie weiterhin fünf Betriebssystem-Routinen kennen, die Ihnen dies ermöglichen. Zunächst wären da " CHKIN" und " CHKOUT" . Sie dienen der Umleitung der Standard-Ein/ Ausgabe auf den entsprechenden Filekanal. Wenn Sie aus einem File lesen wollen, so sollten Sie nach dem Üffnen desselben die Routine CHKIN aufrufen. Mit ihr teilen Sie dem Betriebssystem mit, daß Sie, wenn Sie jetzt etwas einlesen, die Daten aus diesem Filekanal haben möchten. Möchten Sie in ein File schreiben, so müssen Sie CHKOUT aufrufen um in das geöffnete File schreiben zu können. Beide Routinen benötigen die Filenummer des entsprechenden Files im X-Register. Die Einsprungadresse von CHKIN ist $ FFC6, die von CHKOUT $ FFC9 .
Wenn Sie die Standard-Ein/ Ausgabe mit Hilfe unserer beiden Routinen umgeleitet haben, so können Sie die Ihnen viel- leicht schon bekannten Routinen BASIN ($ FFCF) und BASOUT ($ FFD2) zum Lesen und Schreiben von Daten vom, bzw. an, das Ein/ Ausgabe-Gerät benutzen. Rufen Sie diese Routinen bei unveränderter Standard- Ein-/ Ausgabe auf, so erscheint bei BASIN ein Cursor auf dem Bildschirm, der dem Assemblerprogramm eine Eingabe von der Tastatur übermittelt, bei BASOUT wird ein Zeichen an die aktuelle Cursorposition gedruckt und der Cursor um eine Stelle weiterbewegt ( wie Sie bemerken ist das Standard-Eingabegerät die Tastatur, das Standard-Ausgabegerät der Bildschirm) .
Bei umgeleiteter Eingabe erhalten Sie nun beim Aufruf von BASIN das aktuelle Byte des geöffneten Files im Akku zurück. Bei umgeleiteter Ausgabe wird jedes Byte, daß Sie in den Akku laden und anschließend an BASOUT übergeben in das geöffnete File hineingeschrieben.
Haben Sie nun Ihre Fileoperationen beendet, so ist es notwendig, die fünfte Routine zu benutzen, von der ich oben sprach. Sie heißt " CLRCH" und wird verwendet, um die Standard-Ein/ Ausgabe wieder zurückzusetzen auf ' Tastatur' und ' Bildschirm' . Ihre Einsprungadresse ist $ FFCC. Sie wird VOR dem Aufruf von CLOSE benutzt und benötigt keine Parameter.
FEHLERERKENNUNG UND BEHANDLUNG Bevor wir uns der Praxis zuwenden, zunächst noch ein Wort zur Fehlererkennung. Hierüber können wir nämlich beim Lesen eines Files auch erkennen, wann wir sein letztes Byte gelesen haben.
Prinzipiell gilt: tritt während der Arbeit einer der Floppy-Betriebssystem- Routinen ein Fehler auf, so wird das an das aufrufende Programm durch ein gesetztes Carry-Bit zurückgemeldet. So können Sie also nach jeder der Routinen durch eine einfache " BCS"- Verzweigung (" Branch on Carry Set") auf eine Fehlerbehandlungsroutine verzweigen. Hierbei steht dann im Akku ein Fehlercode, mit dessen Hilfe Sie die Art des Fehlers feststellen können. Hier eine Liste mit den möglichen Fehlermeldungen und den Ursachen für einen aufgetretenen Fehler.
In eckigen Klammern stehen jeweils die Betriebssystem-Routinen, die den entsprechenden Fehler auslösen können.
Steht nichts dahinter, so handelt es sich um einen allgemeinen Fehler:
0 :" Break Error"- Die RUN/ STOP-Taste wurde gedrückt.
1 :" too many files"- Der 64 er verwaltet intern maximal 10 offene Files. Sie versuchten ein elftes File zu öffnen.
Oder aber sie haben schon zu viele Files zur Floppy hin offen ( Sie erinnern sich: man darf maximal 3 sequentielle, oder 1 relatives und 1 sequentielles File gleichzeitig offen halten) .{ OPEN}2 :" file open"- Sie versuchten, ein schon offenes File nochmal zu öffnen.
{ OPEN}3 :" file not open"- Sie versuchten, ein ungeöffnetes File anzusprechen.{ CH-KIN, CHKOUT, CLOSE}4 :" file not found"- Das File, das Sie zum Lesen öffnen wollten existiert gar nicht.{ OPEN}5 :" device not present"- Die Floppy ( oder das gewählte Gerät) ist nicht eingeschaltet.{ OPEN}6 :" not an input file"- Sie versuchten aus einem zum Schreiben geöffneten File zu lesen.{ CHKIN}7 :" not an output file"- Sie versuchten in ein zum Lesen geöffneten File zu schreiben.{ CHKOUT}8 :" missing filename"- Sie gaben keinen Filenamen an.{ OPEN}9 :" illegal device number"- Sie gaben eine ungültige Devicenummer an.
{ OPEN} Beachten Sie bitte, daß nicht unbedingt alle Fehler aus obiger Liste auftreten müssen, da man die oben genannten Routinen auch zum Anprechen von anderen Geräten verwenden kann ( z. B. Drucker, Datasette, etc.) . Üffnen Sie z. B. einen Kanal zum Drucker, so brauchen Sie keinen Filenamen - der Fehler 8 wird in dem Fall also nie auftreten.
Als weitere Fehlererkennungshilfe stellt uns das Betriebssystem auch eine Statusspeicherstelle zur Verfügung. Sie ist absolut identisch mit der Variablen " ST" von BASIC. Fragt ein BASIC-Programm diese Variable ab, so greift der BASIC-Interpreter auf eben diese Speicherstelle zurück. Die Assemblerprogrammierer finden den I/ O-Status in der Zeropage, nämlich in Speicherstelle $90( dez.
144) . Um feststellen zu können, ob während der Arbeit mit der Floppy ein Fehler aufgetreten ist, müssen wir sie lediglich einlesen und analysieren. Ein Fehler wird hierbei durch das gesetzt sein eines oder mehrerer Bits dieser Speicherstelle gemeldet. Hier eine Belegung der Bits ( nur für die Arbeit mit der Floppy gültig, bei Kasettenbetrieb gilt eine andere Belegung) :

Bit Bedeutung                           
 0  Fehler beim Schreiben               
 1  Fehler beim Lesen                   
 6  Datei-Ende wurde erreicht (Lesen)   
 7  Gerät nicht vorhanden oder abge-    
    schaltet ("device not present")     

Sie sehen, daß Sie hier, wie auch in BASIC, das Ende eines Files durch gesetzt sein des 6 . Bits von ST feststellen können. Dann nämlich hat ST den Wert 64, den wir bei BASIC-Abfragen auch immer verwendeten. Sie sehen übrigens auch, warum bei erneutem Lesen trotz beendetem File die Fehlernummer 66 zurückgeliefert wird. Das File ist dann nämlich zu Ende und es trat ein Fehler beim Lesen auf, weil es ja gar nichts mehr zu lesen gibt. Damit sind die Bits 1 und 6 gesetzt (2+64) was dem Wert 66 entspricht.
Wenn ST den Wert 0 enthält, so ist alles gut gegangen. Auf diese Weise können wir in Assembler sehr einfach den Fehler abfragen: Wir lesen die Speicherstelle $90 einfach ein und verzweigen mittels BNE auf eine Fehlerbehandlungsroutine.
ZUSAMMENFASSUNG Abschließend zu diesen Routinen möchte ich Ihnen nun zwei Universal-Lese/ Schreib-Routinen vorstellen, die Sie zum Lesen, bzw. Schreiben eines Files verwenden können. Sie finden diese Routinen auch auf dieser MD unter dem Namen " FK. I/ O. S" . Sie sind im Hypra-Ass- Format gespeichert. Um sie sich anzuschauen können Sie sie wie ein BASIC-Programm laden und listen.
Zunächst einmal möchte ich Ihnen die " ReadFile"- Routine vorstellen. Sie liest ein File an eine beliebige Adresse ein.
Hierbei übergeben Sie Lowund High-Byte in Xund Y-Register, sowie die Länge des Filenamens im Akku. Der Filename selbst soll immer bei $0334( dez.820) stehen:
ReadFile:

       stx $fb      ;Startadresse in    
       sty $fc      ; Zeiger schreiben  
       ldx #$34     ;Namensadresse=$0334
       ldy #$03     ;in X/Y (Len in Ak.)
       jsr $ffbd    ;Und Name setzen    
       lda #01      ;Filenummer=1       
       ldx #08      ;Geräteadresse=1    
       ldy #00      ;Sek.=0 (=PRG lesen)
       jsr $ffba    ;Parameter setzen   
       jsr open     ;File öffnen        
       ldx #01      ;Ausgabe auf FNr. 1 

jsr chkin ; umleiten

       ldy #00      ;Index auf 0 setzen 
loop1: jsr basin    ;Byte lesen         
       sta ($fb),y  ;und auf Zeiger-    
                     adresse speichern  
       inc $fb      ;Den Zeiger in      
       bne l1       ; $FB/$FC um 1      
       inc $fc      ; erhöhen           

l1 : lda $90 ; File-Ende erreicht?
beq loop1 ; Nein, dann weiter!

       jsr $ffcc    ;Ja, also Standard- 
                     Ein-/Ausgabe       
                     zurücksetzen.      
       lda #01      ;Und File mit File- 
       jmp $ffc3    ; nummer 1 schließen

Als nächstes stelle ich Ihnen die " WriteFile"- Routine vor. Sie speichert Daten in einem beliebigen Speicherbereich auf Diskette und wird mit denselben Vorraussetzungen aufgerufen wie " ReadFile" :
Name bei $0334, Namenslänge im Akku und Startadresse des zu speichernden Bereichs in X-/ Y-Register. Zusätzlich müssen Sie die Endadresse dieses Bereichs ( in Lo/ Hi-Darstellung) vorher in den Speicherstellen $ FD/$ FE abgelegt haben:
WriteFile:

       stx $fb      ;Startadresse in    
       sty $fc      ; Zeiger schreiben  
       ldx #$34     ;Namensadresse=$0334
       ldy #$03     ;in X/Y (Len in Ak.)
       jsr $ffbd    ;Und Name setzen    
       lda #01      ;Filenummer=1       
       ldx #08      ;Geräteadresse=1    
       ldy #01      ;Sek.=1 (=PRG       
                     schreiben)         
       jsr $ffba    ;Parameter setzen   

jsr $ ffc0 ; File öffnen ldx #01 ; Eingabe auf FNr.1 jsr $ ffc9 ; umleiten ldy #00 ; Index auf 0 setzen loop2 : lda ($ fb), y ; Byte aus Sp. holen jsr $ ffd2 ; und an Floppy senden

       inc $fb      ;Den Zeiger in      
       bne l2       ; $FB/$FC um 1      
       inc $fc      ; erhöhen           
l2:    lda $fe      ;Quellzeiger $FB/$FC
       cmp $fc      ; mit Zielzeiger    
       bne loop2    ; $FD/$FE           
       lda $fd      ; vergleichen. Wenn 
       cmp $fb      ; ungleich, dann    
       bne loop2    ; weitermachen.     
       jsr $ffcc    ;Wenn gleich, dann  
                     Standard-I/O       
                     zurücksetzen.      
       lda #01      ;und File mit File- 
       jmp $ffc3    ;nummer 1 schließen 

Wie Sie sehen, habe ich hier den Trick mit den Sekundäradressen verwendet. Da bei der Sekundäradresse die Werte 0 und 1 für " Programm lesen" bzw." Programm schreiben" stehen, erübrigt sich ein umständliches anhängen von ", P, R" bzw.
", P, W" an den Filenamen. Dafür jedoch können mit den Routinen nur PRG-Files gelesen und geschrieben werden.
Bitte Teil 2 des Floppy-Kurses laden !

Valid HTML 4.0 Transitional Valid CSS!