---------------------------------------- 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