Magic Disk 64

home to index to text: MD9302-KURSE-FLOPPY-KURS_9.1.txt
----------------------------------------
               Floppy-Kurs              
      "Es rappelt in der Kiste..."      
                (Teil 9)                
----------------------------------------

Herzlich Willkommen zum neunten und letzten Teil des Floppy-Kurses. In den vorangehenden Folgen haben wir einiges über den Aufbau von Disketten und das Ansprechen der Floppy gelernt. Ich möchte diesen Kurs nun mit einem anspruchsvollen Programmbeispiel abschließen, das wir uns im Laufe der heutigen Folge erarbeiten wollen. Es dient als Beispiel für die Programmierung der Floppy in Assembler und gleichzeitig auch als Beispiel für das was möglich ist, wenn eine Diskette richtig manipuliert wird.
DIE AUFGABE Sicherlich kennen Sie das Problem: Sie sind gerade dabei Ihre Diskettensammlung aufzuräumen und Ihre Programme so umzukopieren, daß jede Diskette optimale Platzausnutzung aufweisen soll. Am Besten so, daß restlos jeder Block dieser Diskette beschrieben ist. Beim Zusammenstellen bleiben dann aber noch noch 17 Blocks übrig - Mist, kein Platz mehr für das letzte,30 Blocks lange Programm!
Was tun? Im Prinzip bleiben Ihnen nur die folgenden Möglichkeiten:
1) Sie fangen nochmal von vorne an ( Gähn - Kopieren dauert lange) .
2) Sie belassen alles, wie es ist ( Ärgerlich -17 Blocks verschenkt) .
3) Sie packen das letzte Programm ( Wieder Gähn - dauert auch lange und ausserdem hat das File hinterher bestimmt genau 18 Blocks, so daß es doch nicht passt) .
4) Sie benutzen das Programm " USEDIR", das wir uns in diesem Kursteil erarbeiten wollen.
Nun werden Sie fragen:" Na schön, und wie will dieses Programm nun 13 weitere Blocks freibekommen, wenn die Diskette voll ist?" . Ganz einfach: aus dem Directory." USEDIR" nimmt sich die Tatsache zunutze, daß der Track 18, in dem das Directory einer Diskette steht, nicht für normale Files zur Verfügung steht.
Er ist lediglich für die Fileeinträge reserviert. In jedem der 18 Fileeintragsblocks ( Sektoren 1-18,0 ist für DiskHeader und BAM reserviert) können nun 8 Einträge stehen, was einer maximal mögliche Anzahl von 144 Einträgen entspricht. Da eine normale Diskette aber so gut wie nie so viele Files beherbergt, liegen eine Menge dieser Directoryblocks brach. Gerade bei einer randvollen Diskette werden sie bis in alle Ewigkeit unbenutzt bleiben, da ja keine neuen Files mehr hinzukommen können. Im günstigsten Fall, nämlich dann, wenn Sie weniger als 9 Files auf der Diskette haben, sind das 17 Blocks, die noch zusätzlich frei sind!
Diese Blocks soll UseDir nun einfach als Datenblocks verwenden. Es muß lediglich einen Fileeintrag kreieren, in dem der Zeiger für den ersten Track und Sektor auf einen der unbenutzten DirBlocks zeigt. Wird dieses File dann geladen, so greift das DOS der Floppy schön brav auf diese sonst ungenutzten Blocks zu, so als gäbe es keinen Unterschied. Tatsächlich hat die Diskette dann jedoch 683 Blocks ( maximal) anstelle von nur 664 !
DIE THEORIE Was muß unser Programm nun tun, um ein File auf die oben beschriebene Weise umzukopieren. Zunächst einmal wollen wir hier eine Einschränkung vereinbaren, die uns die Arbeit erleichtern soll: ein File, das auf diese Weise installiert werden soll, muß länger sein, als es freie Directoryblöcke gibt. Daraus ergibt sich natürlich, daß die Diskette mindestens noch einen ' normalen', freien Datenblock hat. Das zu installierende File muß desweiteren natürlich kleiner, oder gleich lang der insgesamt verfügbaren Blöcke sein und sollte vom Typ " PRG" sein. Daraus ergeben sich folgende Aufgaben für unser Programm:
1) Das zu installierende File einlesen und benötigte Anzahl von Blöcken ermitteln.
2) Anzahl der freien Blöcke der Zieldiskette ermitteln.
3) Anzahl der freien Directoryblöcke der Zieldiskette ermitteln.
4) Vergleichen, ob das File den obig gegebenen Restriktionen entspricht und ob noch genügend Platz auf der Zieldiskette ist.
5) Wenn ja, dann weiter, sonst abbrechen.
6) Nun muß berechnet werden, wieviele Bytes in den Directoryblocks Platz haben. Alles restliche wird anschließend, als normales File, mit Hilfe der WriteFile-Routine aus dem vorletzten Kursteil auf die Diskette geschrieben.
7) Jetzt suchen wir den Fileeintrag des soeben gespeicherten Files aus den Directoryblocks heraus und Lesen die Informaton für den ersten Track und Sektor aus dem Eintrag aus.
8) Diese Information wird sogleich in die Trackund Sektornummer des ersten freien Directoryblocks abgeändert.
9) Jetzt müssen wir nur noch die fehlenden Directoryblocks schreiben und den letzten Directoryblock auf den ersten Track/ Sektor des geschriebenen Files zeigen lassen - Fertig ist die Installation!

DIE BENÜTIGTEN PROGRAMMTEILE            

Kommen wir nun also zu unserem Programm.
Dabei möchte ich Ihnen zunächst einmal eine Liste der darin enthaltenen Routinen geben. Hierbei habe ich in zwei Arten unterschieden: in Steuerund IO-Routinen. Diese beiden Namen treffen Ihre Bedeutung zwar nicht voll und ganz, jedoch musste irgendwo eine Grenze gezogen werden.
Die erste Art, die Steuerroutinen also, sind alles Funktionen, die entweder nichts direkt mit der Floppy zu tun haben, und somit für uns uninteressant sind, oder aber Funktionen, die wir in vorangegangenen Kursteilen schon besprochen hatten, und somit nicht noch einer zweiten Dokumentation bedürfen. All diese Routinen werden nur mit ihrem Namen, ihrer Funktion und ihren Parametern aufgeführt, damit Sie wissen, wozu sie da sind, und wie man sie benutzt.
Die zweite Art von Routinen sind größtenteils Floppy-Routinen, die eine Beschreibung benötigen und im Laufe dieses Kursteils alle nocheinmal genauer dokumentiert sind.
Desweiteren werden diverse Speicherzellen als Zwischenspeicher für verschiedentliche Werte benutzt. Diese haben, der besseren Öbersichlichkeit wegen, richtige Namen, und können im Prinzip überall im Speicher stehen. Dennoch will ich die von mir benutzten Speicherzellen hier einmal aufführen. Sie liegen, bis auf einige Außnahmen, im Speicherbereich von $0332-$03 FF, dem Kasettenpuffer also, der bei Floppybenutzung ja unbenutzt, und deshalb verwendbar ist. Hier nun also die besprochenen Beschreibungen:

1) BENUTZTE SPEICHERZELLEN:             
* MEM0-MEM4                             
  Die fünf Zeropageadressen von $02  bis
  $06.  Sie werden für die Zwischenspei-
  cherung   verschiedenster   Ergebnisse
  herangezogen.                         
* FILEMEM ($0F00)                       
  Dies  ist die Anfangsadresse des Spei-
  chers, in den  das  zu  installierende
  Programm geladen wird.                
* NUMBUFF ($0332)                       
  Hier  wird der ASCII-String einer Kon-
  vertierten Zahl  abgelegt  (maximal  5
  Zeichen).                             
* NEED ($0337)                          
  Zwischenspeicher  für  die  Länge  des
  gelesenen Files in Blocks.            
* DSKFREE ($0338/$0339)                 
  Speicher für  die  Anzahl  der  freien
  Blocks der Zieldiskette.              
* ALLFREE ($033A/$033B)                 
  Anzahl der insgesamt (inkl. freie Dir-
  blocks) auf der Zieldiskette verfügba-
  ren Blocks.                           
* DIRFREE ($033C)                       
  Anzahl der freien Directoryblocks     
* DIRSTRT ($033D)                       
  Sektornummer  des ersten freien Direc-
  toryblocks.                           
* FTRACK ($033E)                        
  Tracknummer  des  ersten,  vom  normal
  geschriebenen  File  benutzten, Daten-
  blocks.                               
* FSECTOR ($033F)                       
  Sektornummer des  ersten,  vom  normal
  geschriebenen  File  benutzten, Daten-
  blocks.                               
* DSINDEX ($0340)                       
  Indexzeiger auf den aktuellen  Eintrag
  der Dirblockliste.                    
* COMBUFF ($0341-)                      
  Zwischenspeicher  für  Befehle, die an
  die Floppy gesandt werden sollen.     
* NAME                                  
  Dieses Label beszeichnet  die  Starta-
  dresse,  des  17  Zeichen  langen Zwi-
  schenspeichers für  Filenamen.  Dieser
  befindet sich direkt im Sourcecode.   
2) STEUERROUTINEN                       


* GETIN: Diese Funktion verlangt keine Parameter. Sie liest mit Hilfe der BASIN-Routine des Betriebssystems einen maximal 16 Zeichen langen String von der Tastatur ein und wird zum Eingeben des Filenamens benutzt. Dieser String liegt nach dem Rücksprung ab der Adresse NAME. Die Länge des Filenamens steht in der Speicherzelle MEM0 . Am Ende des Namensstring wird ein Nullbyte als Endmarkierung angefügt.

* STROUT: Diese Routine gibt einen AS-CII- Text auf dem Bildschirm aus. Lo/- Hi-Byte der Textadresse müssen in Akku und Y-Register übergeben werden. Der Text muß mit einem Nullbyte enden.

* OPENCOM: Diese Routine öffnet den Befehlskanal mit der logischen Filenummer 1 . Beim Üffnen wird die Diskette automatisch initialisiert ( Floppybefehl " I")
* OPENIO: Hier wird der Befehlskanal mit der Filenummer 1( wie OPENCOM) und ein Pufferkanal mit der Filenummer 2( und Sekundäradresse 2) geöffet.

* RFILE: Dies ist die " ReadFile"- Routine aus dem letzten Kursteil. Sie liest das File, dessen Namen bei NAME steht und dessen Namenslänge in MEM0 abgelegt ist, an die Adresse FILEMEM. Sie benutzt die Zeropageadressen $ F9/$ FA als Lesezeiger. Hier steht nach dem Rücksprung gleichzeitig auch die Endadresse des gelesenen Files.

* WFILE: Schreibt das File in NAME und MEM0 auf Diskette. Die Startadresse des zu speichernden Bereichs muß dabei in den beiden Zeropageadressen $ FD/$ FE, die Endadresse in $ F9/$ FA stehen.

* STATOUT: Gibt die ermittelten Werte für " Anzahl benötigte Blocks"," Freie DirBlocks", und " Freie Diskblocks" auf dem Bildschirm aus.

3) IO-ROUTINEN:                         
* STRIEC: Wie "STROUT", nur daß  diesmal

ein String auf den IEC-Bus ( also an die Floppy) ausgegeben wird.

* SENDCOM: Da wir zur Erfüllung unserer Aufgabe immer nur Floppybefehle benötigen, die aus einem festen String und einer angehängten, variablen Zahl bestehen, wird diese Routine benutzt, um einen Befehl, dessen String an der Adresse in Xund Y-Register steht und dessen abschließende Nummer im Akku übergeben wurde, an die Floppy zu senden.

* WDUMMY: Diese Routinelegt auf der Zieldiskette ein File mit dem Namen in NAME und MEM0 an und löscht es direkt wieder. Wozu dies notwendig ist, werden wir später sehen.

* GETDSKF: Ermittelt die Anzahl der freien Blocks der Zieldiskette und legt sie in DSKFREE ab.

* GETDIRF: Ermittelt die Anzahl der freien Directoryblocks der Zieldiskette und legt sie in DIRFREE ab. Desweiteren wird die Summe von DSKFREE und DIRFREE berechnet und in ALLFREE abgelegt.

* GETNEED: Berechnet die benötigte Anzahl Blöcke des zuvor eingelesenen Files und legt Sie bei " NEED" ab.

* CHGENT: Diese Routine sucht den Filenamen in NAME aus dem Directory heraus, liest die Nummern des ersten Tracks und Sektors dieses Files ein, und überschreibt diese beiden Informationen mit den Werten für den ersten freien Directoryblock.

* WBLOCKS: Diese Routine schreibt alle fehlenden Blocks des Files in die freien Directoryblocks und gibt im letzten dieser Blocks Trackund Sektornummer des ersten Datenblocks des ' normal' gespeicherten Files an.
DIE PRAXIS Nach all der trockenen Theorie wollen wir nun endlich zur Praxis schreiten.
Beginnen möchte ich mit der Steuerroutine ' MAIN' unseres Programms, in der das oben aufgeführte Aufgabenschema in Programmcode umgesetzt ist. Anschließend wollen wir uns mit den benutzten Unterroutinen beschäftigen.
1) MAIN Diese Routine steuert das gesamte Programm. Zunächst einmal wollen wir die Bildschirmfarben setzen, den Titeltext ausgeben und den Namen des zu installierenden Files ermitteln. Ist dieser Name gleich dem String X", so soll das Programm mit einem RESET verlassen werden:

main   lda #11         ;Bildschirm-     
       sta 53280       ;farben          
       sta 53281       ;setzen.         
       lda #<(text1)   ;Titeltext       
       ldy #>(text1)   ;auf Bildschirm  
       jsr strout      ;ausgeben.       
       jsr getin       ;Und Filename    
                        einlesen.       
       cpy #1          ;Vergleichen, ob 
       bne m3          ;der Name="X"    
       lda #"x"        ;ist.            
       cmp name        ;Wenn nein, dann 
       bne m3          ;weitermachen.   
       jmp 64738       ;Sonst: RESET.   

Als Nächstes müssen wir das File lesen und seine Blocklänge berechnen. Hieraufhin wird der Benutzer dazu aufgefordert, die Zieldiskette einzulegen, von der wir dann die Anzahl der freien Dirund Diskblocks ermitteln. Gleichzeitig wird dann auch noch das Dummyfile erzeugt. Am Ende werden alle ermittelten Werte mittels der Routine STATOUT auf dem Bildschirm ausgegeben:

m3     jsr rfile       ;File einlesen   
       jsr getneed     ;Anzahl der benö-
                        tigten Blocks   
                        berechnen       
       lda #<(text2)   ;"Zieldisk ein-  
       ldy #>(text2)   ;legen"          
       jsr strout      ;ausgeben und    
mloop1 jsr inkey       ;auf Tastendruck 
       beq mloop1      ;warten.         
       lda #<(text3)   ;"Untersuche     
       ldy #>(text3)   ;Zieldisk"       
       jsr strout      ;ausgeben.       
       jsr wdummy      ;Dummy-File anle-
                        gen und löschen.
       jsr openio      ;Kanäle öffnen   
       jsr getdskf     ;Freie Diskblocks
                        ermitteln       
       jsr getdirf     ;Freie Dirblocks 
                        ermitteln.      
       jsr closeio     ;Kanäle schließen
       jsr statout     ;Werte ausgeben. 

Nachdem nun all diese Dinge getan sind, müssen wir nun erst einmal prüfen, ob das gelesene File und die Daten der Zieldiskette es uns ermöglichen, das File auf die Diskette zu schreiben.
Hierbei wird abgebrochen, wenn keine Dirblocks mehr frei sind, das File kürzer als die noch verfügbaren Dirblocks, oder länger als die gesamt verfügbaren Blocks ist:

       lda dirfree     ;DirFree lesen   
       bne m1          ;wenn<>0, weiter.
       lda #<(errtxt1) ;Sonst "Kein Dir-
       ldy #>(errtxt1) ;blk mehr frei"  
       jmp errout      ;ausg. u. Ende.  
m1     cmp need        ;DirFree mit NEED
       bcc m2          ;vergleichen.    
       lda #<(errtxt2) ;Wenn >, dann    
       ldy #>(errtxt2) ;"File zu kurz"  
       jmp errout      ;ausg. u. Ende.  
m2     ldy allfree+1   ;Hi-Byte ALLFREE 
                        lesen.          
       bne ok          ;Wenn <>0, dann  
                        genug Platz.    
       lda allfree+0   ;Sonst Lo-Byte   
                        lesen           
       cmp need        ;u. m. NEED vgl. 
       bcs ok          ;Wenn >, Ok.     
       lda #<(errtxt3) ;Sonst "File zu  
       ldy #>(errtxt3) ;lang" aus-      
       jmp errout      ;geben           

Wurden all diese Vergleiche erfolgreich bestanden, so kann das File installiert werden. Hierzu müssen zunächst einige Vorbereitungen getroffen werden: Als erstes sollten wir die Sektornummer des ersten freien Directoryblocks ermitteln.
Dies ginge natürlich dadurch, indem wir die BAM einlesen würden, und uns einen unbelegten Block als Startblock heraussuchen würden. Es geht aber noch viel einfacher: das DOS der Floppy beschreibt die Sektoren einer Diskette nämlich meistens in einer ganz bestimmten Reihenfolge. Das ist bei den normalen Datenblocks nicht unbedingt gesichert, da bei einer nahezu vollen Diskette diese Reihenfolge nicht mehr eingehalten werden kann. Beim Directorytrack 18 können wir uns jedoch mit 100%- iger Sicherheit darauf verlassen, daß sie immer eingehalten wird. So gibt es nämlich eine ganz bestimmte Reihenfolge, in der die Directorytracks geschrieben werden. Wir müssen lediglich wissen, wieviele Blocks benutzt sind, und uns die Sektornummer des nächsten Blocks aus einer Tabelle zu holen. Dies ist dann automatisch der erste leere Dirblock. Die angesprochene Tabelle ist bei dem Label " BLKTAB" abgelegt, und beinhaltet die folgenden Werte für Sektornummern:

BLKTAB   .byte 0,1,4,7,10,13,16         
         .byte 2,5,8,11,14,17           
         .byte 3,6,9,12,15,18,$ff       

Bitte nun Teil 2 des Floppy-Kurses laden

Valid HTML 4.0 Transitional Valid CSS!