Floppy-Kurs: "Es rappelt in der Kiste..." (Teil 6) ----------------------------------------
Hallo und herzlich Willkommen zum sech- sten Teil unseres Floppykurses. Wir wol- len unsere Kenntnisse auch hier wieder vertiefen und zwei weitere Beispielpro- gramme kennenlerenen. Eines, um das Di- rectory auf dem Bildschirm anzuzeigen und eines um gelöschte Files wieder zu retten.
BEISPIEL 1: DIRECTORY ANZEIGEN
Unser erstes Programmbeispiel soll eine Routine sein, mit der Sie das Directory der eingelegten Diskette ohne Speicher- verlust einlesen können. Dies ist ein Hinweis auf eine besondere Eigenheit der Floppy. Wenn wir uns nämlich das In- haltsverzeichnis einer Diskette einfach nur anzeigen lassen wollen, so können wir durch Angabe des Filenamens "$" die Floppy dazu bewegen, uns sehr viel Ar- beit abzunehmen. Sie beginnt dann näm- lich damit, alle Directoryblöcke von selbst einzulesen und sie für uns aufzu- bereiten. Ein direktes "herumpfuschen" auf der Diskette mittels der Block- Befehle entfällt. Wollen wir uns nun verdeutlichen, was passiert, wenn wir wie gewohnt das Di- rectory mit LOAD"$",8 in den Speicher des Computers laden. Wie schon erwähnt, ist das Dollarzeichen für die Floppy das Kennzeichen für eine spezielle Aufberei- tung des isketteninhalts. Sie sendet an den C64 nun Daten, die dieser, wie bei auch beim Laden eines normalen Programms in seinen Basic-Speicher schreibt. Der Computer behandelt das Directory im Prinzip also wie ein Basic-Programm. Dies wird dadurch bestätigt, daß das eingelesene Directory auch wie ein Ba- sic-Programm, mit dem Befehl LIST auf dem Bildschirm angezeigt wird. Sie kön- nen es sogar mit NEW wieder löschen, oder mit RUN starten, was jedoch nur einen Syntax-Error bewirkt, da das Di- rectory ja keine echten Basic-Befehle enthält. Sinnigerweise übermittelt die Floppy in der Directoryliste als erstes auch immer die Blockanzahl eines Files, was für das Basic des C64 einer Zeilen- nummer entspricht. Diese Zeilennummern sind zwar nicht immer in einer numeri- schen Reihenfolge, jedoch macht das Ba- sic unseres Computers da keinen Unter- schied. Der interne Aufbau eines Basic- Programms erlaubt es tatsächlich, Zei- lennummern wahllos zu vergeben. Wenn wir ein Programm direkt eingeben ist das natürlich nicht möglich, da die Eingabe- routine des Betriebssystems die Zeile anhand der Zeilennummer automatisch an der logisch richtigen Position des Pro- gramms einfügt. Der langen Rede kurzer Sinn ist, daß wir beim Öbermitteln des Directorys von der Floppy darauf achten müssen, daß diese es uns in einer Form schickt, die ei- gentlich nur der Basic-Programm-Speicher versteht, so daß die LIST-Routine das 'Directory-Programm' listen kann. Des- halb gibt e) einige Bytes, die für uns wertlos sind und überlesen werden müs- sen. Hierzu erkläre ich Ihnen einfach einmal den Aufbau des Files, das uns die Floppy bei Angabe des Filenamens "$" übermittelt. Zuerst erhalten wir von ihr, wie bei jedem File, daß normaler- weise mittels LOAD in den Speicher des Computers gelangt, eine Adresse, an die das "Programm" geladen werden soll. Dies ist die Basic-Startadresse $0801 (dez. 2049) im Low/High-Byteformat. Wir benö- tigen diese natürlich nicht, und können sie deshalb überlesen. Von nun an ist der Aufbau einer jeden Directoryzeile gleich. Zuerst werden dabei zwei Linkby- tes übertragen, die der Basic-Interpre- ter benötigt, um die einzelnen Zeilen zu verketten. Beide können wir getrost überlesen. Als nächstes erhalten wir dann die Blockanzahl in Low/High-Byte- Darstellung, was für den Basic-Inter- preter einer Zeilennummer entpräche. Diese Nummer müssen wir nun erst einmal umrechnen, bevor wir sie ausgeben kön- nen. Hieran anschließend folgen nun ein oder mehrere Spacezeichen, sowie der Filename und die Filekennung im Klar- text. Alle diese letzten Zeichen werden im ASCII-Code übertragen, so daß wir sie lediglich mit PRINT ausgeben müssen. Am Ende bekommen wir dann noch eine Null übermittelt, die das Ende der (Ba- sic-)Zeile kennzeichnet. An ihrer Stelle müssen wir lediglich einen Zeilenvor- schub ausgeben (ein PRINT ohne Parame- ter). Das Ende des Files ist natürlich dann erreicht, wenn die Statusvariable ST gleich 64 ist. Im Prinzip ist alles also ganz einfach. Hier ist das entspre- chende Basic-Programm, Sie finden es auf dieser MD unter dem Namen "FK.DIR":
10 gosub200 20 end 30 : 31 : 90 rem ***************** 91 rem * zeichen lesen * 92 rem ***************** 93 : 100 get#1,a$ 110 ifa$=""thena=0:return 120 a=asc(a$):return 130 : 131 : 190 rem ********************** 191 rem * directory anzeigen * 192 rem ********************** 193 : 200 print"{clr}";:open1,8,0,"$" 210 fori=1to4:get#1,a$:next 215 : 220 gosub100:bl=a 230 gosub100:bl=bl+256*a 240 print bl; 250 get#1,a$:printa$; 260 ifa$<>""then250 270 print 280 fori=1to2:get#1,a$:next 290 ifst=0then220 300 close1 310 return
In den Zeilen 100-120 sehen Sie eine Routine, die uns ein Byte mittels GET# einliest und es als numerischen Wert in der Variablen "a" abspeichert. Diese Routine ist deshalb notwendig, da eine Umwandlung des Strings "a$" bei dem By- tewert 0 einen "illegal quantity error" verursacht. Das liegt daran, daß ein String, der nur den Bytewert 0 als Zei- chen enthält, einem Leerstring ent- spricht. Deshalb muß mit der IF-THEN- Abfrage überprüft werden, ob "a$" ein Leerstring ist, oder nicht. Ab Zeile 200 sehen wir nun das Programm, das das Directory einliest und auf dem Bildschirm ausgibt. In Zeile 200 wird zunächst einmal der Bildschirm gelöscht und der Filekanal, der uns das Directory sendet, geöffnet. Ich habe hier ganz bewußt die Sekundäradresse 0 benutzt, da sie der Floppy ja automatisch mitteilt, daß wir ein Lesefile öffnen (sh. voheri- ge Teile dieses Kurses). In Zeile 210 werden nun die Startadresse und die bei- den Linkbytes der ersten Zeile überle- sen. Danach beginnt die Hauptschleife des Unterprogramms. In den Zeilen 220 und 230 lesen wir hier zunächst mit Hilfe unserer "Zeichen lesen"-Unterroutine, Low- und High-Byte der Blockanzahl aus, verrechnen diese beiden Werte zu der reellen Blockanzahl und speichern sie in der Variablen "bl" (Wert=Lowbyte+256*- Highbyte). Die Blockanzahl wird jetzt in Zeile 240 auf dem Bildschirm ausgegeben. Wir hängen an den PRINT-Befehl ganz be- wußt ein Semikolon an, damit der folgen- de Text direkt hinter der Blockanzahl ausgegeben wird. Dies geschieht in den Zeilen 250 und 260. Dort lesen wir je- weils ein Zeichen mittels GET# ein, und geben es anschließend auf dem Bildschirm aus. War das eingelesene Zeichen ein Leerstring, entspricht es dem Bytewert 0 und die Zeile ist zu Ende. Jetzt wird in Zeile 270 eine Leerzeile ausgegeben, daß der Cursor am Anfang der nächsten Zeile steht. In Zeile 280 werden die beiden Linkbytes der folgenden Directory-Zeile überlesen. In der IF-THEN-Abfrage in Zeile 290 wird überprüft, ob das Ende des Files schon überlesen wurde. Wenn nicht, wird an den Anfang der Haupt- schleife verzeigt und alles beginnt von vorne. Wenn doch, so wird der Filekanal geschlossen und zum aufrufenden Programm zurückverzweigt.
BEISPIEL 2: GELOESCHTE FILES RETTEN
Um Sie in die Benutzung der Blockbefehle weiter einzuführen, wollen wir uns jetzt einem weiteren Programmbeispiel in BASIC zuwenden. Wir wollen ein einfaches "UN- DELETE"-Programm schreiben. Mit ihm soll es möglich sein, versehentlich mit dem Floppybefehl "SCRATCH" ("S:") gelöschte Dateien wiederherzustellen. Hierzu wol- len wir zunächst einmal klären, was die Floppy eigentlich macht, wenn sie ein File löschen soll. Als erstes bekommt Sie einen Befehl übermittelt, der sie dazu auffordert ein (oder auch mehrere) Files zu löschen. Als Beispiel wollen wir einmal ein File mit Namen "TEST" heranziehen. Der entsprechende Floppybe- fehl an den Befehlskanal muß also lauten "S:TEST". Hieraufhin beginnt die Floppy nun, das Inhaltsverzeichnis der einge- legten Diskette nach einer Datei zu durchsuchen, die den Namen "TEST" trägt. Wird sie gefunden, so trägt die Lösch- Routine des Floppy-Betriebssystems ledi- glich den Wert 0 als Filetyp für dieses File ein, und sucht nun alle Blocks he- raus, die von dem File belegt werden. Jetzt holt sich die Floppy die BAM der Diskette in den Speicher, kennzeichnet alle vom File belegten Datenblocks als 'unbelegt' und schreibt die BAM wieder zurück auf die Diskette. Der Filetyp 0 entspricht dem Filetyp "DEL", der norma- lerweise im Directory nicht mehr ange- zeigt wird. An dieser Leerstelle wird das File einfach übersprungen. Im Prin- zip sind aber noch alle seine Informa- tionen auf der Diskette enthalten. Zu- mindest einmal solange, bis Sie wieder ein neues File auf die Diskette schrei- ben. Dann nämlich sucht sich die Floppy den ersten freien Directoryeintrag he- raus, der in unserem Fall, der des Files "TEST" ist, und trägt dort das neue File ein. Desweiteren kann es dann auch pas- sieren, daß die Datenblocks, die von "TEST" belegt wurden möglicherweise von denen des neuen Files überschrieben wer- den. Ein UNDELETE-Programm hat deshalb nur dann einen Sinn, wenn noch nichts neues auf die Diskette geschrieben wurde (obwohl es auch andere Programme gibt, die selbst dann noch das eine oder ande- re retten können - hierauf kommen wir später zurück). Es muß lediglich alle Einträge im Directory nach Filetyp-0- Einträgen durchsuchen und diese Anzei- gen. Hieraufhin muß vom Benutzer ent- schieden werden, welchen Filetyp das alte File hatte (dies ist die einzige Information, die über selbiges verloren ging). Ist dieser angegeben, so braucht unser Programm nur noch den Entsprechen- den Code in den Fileeintrag zu schreiben und alle Blocks des Files zu verfolgen, die von ihm belegt wurden und sie aber- mals als 'belegt' zu kennzeichnen. Dies kann man über die umständliche Metode tun, indem man aus den Bytes 1 und 2 des Fileeintrags Track und Sektor ersten Datenblocks ermittelt, und nun alle ver- ketteten Blöcke (nächster Block steht immer in den ersten beiden Bytes eines Datenblocks) heraussucht uns diese mit- tels Block-Allocate-Befehl ("B-A") als belegt kennzeichnet. Die zweite Methode ist einfach den Validate-Befehl der Floppy zu benutzen. Dieser sucht nämlich automatisch alle Blocks von gültigen Fileeinträgen im Directory heraus und kennzeichnet sie als 'belegt'. Beide Methoden haben ihre Vor- und Nachteile. Benutzen wir die erste Methode, so haben wir bei höherem Programmieraufwand eine weitaus höhere Arbeitsgeschwindigkeit (da nur die Blocks eines Files herausge- sucht werden müssen und nicht aller Fi- les der Diskette, was sich vor allem dann bemerkbar macht, wenn Sie besonders viele Files auf der Diskette haben). Andererseits ist es bei dieser Methode nicht so einfach, die Blocks eines REL- Files wiederzubelegen, da diese mit zusätzlichen Side-Sektor-Blöcken arbei- ten, die ebenfalls gesucht werden müs- sen. Das aber wiederum macht der Valida- te-Befehl für uns. Ausserdem arbeitet er bei Disketten mit wenigen Files weitaus schneller als unsere Routine, da er ja ein Floppyprogramm ist und ein zeitrau- bender Datenaustausch zwischen Floppy und C64 entfällt. Da wir unser Programm in BASIC schreiben wollen, ist er be- stimmt immer noch etwas schneller. Ich werde mich in meinem Beispiel nun jedoch auf die "von Hand"-Methode beschränken. Wenn Sie möchten, können Sie das Pro- gramm ja so erweitern, daß z.B. bei we- niger als fünf Files automatisch der Validate-Befehl benutzt wird und im an- deren Fall die "von Hand"-Routine. Zunächst will ich Ihnen nun eine Unter- routine vorstellen, die uns das Inhalts- verzeichnis einliest und alle Informa- tionen darüber in Variablenfeldern ablegt. Sie steht in dem Beispielpro- gramm "FK.UNDEL" auf dieser MD in den Zeilen 200-510. Wir benötigen sie, um die einzelnen Fileeinträge zu isolieren und feststellen zu können, welcher Ein- trag einem DEL-File entspricht. Ich habe die Routine jedoch so ausgelegt, daß Sie sie auch für andere Zwecke benutzen kön- nen, da sie so ziemlich alle Informatio- nen des Directorys ausliest. Vorher sind jedoch noch einige Anmerkun- gen zu der Routine zu machen. Vorab sei gesagt, daß in den Zeilen 600-620 die- selbe Routine steht, wie wir sie auch schon im Dir-Programm benutzten. Sie liest ein Zeichen ein, und wandelt es in einen Bytewert um, der nach Aufruf in der Variablen "a" steht. Desweiteren benötigen wir noch einige Variablenfel- der, die im Hauptprogramm mit Hilfe der DIM-Anweisung dimensioniert werden müs- sen. Hier eine Liste der Felder:
Name Elemente Inhalt ----------------------------------------
TY$ 144 Filetyp FT 144 Starttrack des Files FS 144 Startsektor des Files BL 144 Blockanzahl des Files NA$ 144 Name des Files
DT 18 Tracknummern des Dirs DS 18 Sektorennummern des Dirs Die ersten fünf Felder aus dieser Liste dienen ausschließlich der Speicherung von Informationen über ein File. Da das Directory maximal 144 Einträge beinhal- ten kann, werden alle diese Felder auf maximal 144 Elemente dimensioniert. Die Felder DT und DS werden benutzt um Track und Sektor der Folgeblocks des Direc- torys zu speichern. Rein theoretisch ist das zwar nicht unbedingt notwenig, da die Folge der Directoryblöcke immer gleich ist (Block 1 in 18/1, Block 2 in 18/4, etc.), jedoch ist so einfacher mit den Blocks zu handhaben. Im Prinzip könnten wir auch das Feld DT wegfallen lassen, da das Directory IMMER in Track 18 steht, jedoch kann es sich ja auch um eine manipulierte Diskette handeln, die das Directory woanders stehen hat (hie- rauf wollen wir in einem der nächsten Teile des Floppykurses zurückkommen). Nun möchte ich Ihnen die Routine, mit denen wir die Diretcoryinformationen einlesen nicht länger vorenthalten. Hier der erste Teil von Zeile 200 bis 295:
200 print"Gelesene Files:" 210 open1,8,15,"i":open2,8,2,"#" 220 print#1,"u1 2 0 18 0" 230 gosub600:tr=a 240 gosub600:se=a 245 : 250 dn$="":id$="" 260 print#1,"b-p 2 143" 270 fori=0to15:get#2,a$:dn$=dn$+a$:next 280 print#1,"b-p 2 162" 290 fori=1to5:get#2,a$:id$=id$+a$:next 295 :
In diesem Teil unserer Unterroutine wer- den die Vorbereitungen zum Einlesen des Directorys getroffen, sowie der Disket- tenname und die ID in die Variablen "dn$" und "id$" eingelesen. Diese Vari- ablen können Sie im Hauptprogramm eben- falls weiterverwenden. Nachdem also in Zeile 200 ein Informa- tionstext ausgegeben wurde, öffnen wir in Zeile 210 den Befehlskanal und einen Pufferkanal. Ersterer erhält die File- nummer 1, letzterer die Filenummer 2. Beim Öffnen des Befehlskanals initiali- sieren wir die Diskette auch gleich- zeitig. In Zeile 220 senden wir nun den ersten Befehl an die Floppy. Der U1- Befehl in dieser Zeile liest uns den Dir-Header-Block (Track 18, Sektor 0) in den Puffer. In Zeile 230 und 240 lesen wir nun die ersten beiden Bytes dieses Blocks ein, und ordnen sie den Variablen TR und SE zu, die als Variablen für den aktuellen Track/Sektor benutzt werden. Sie enthalten nun Track und Sektor des ersten Directoryblocks. Bevor wir uns jedoch daran machen dieses auszulesen, nehmen wir uns aus dem Dir-Header-Block noch den Diskettennamen und ID heraus. In Zeile 250 werden diese beiden Variablen zunächst einmal gelöscht. Als nächstes positionieren wir den Puffer- zeiger auf die Position 144 des Dir- Header-Blocks. Die 16 dort folgenden Bytes enthalten den Namen der Diskette, der mit Hilfe der Schleife in Zeile 270 in dn$ eingelesen wird. Ebenso wird mit der ID verfahren. Sie steht in den 5 Bytes ab Position 163. In Zeile 280 wird darauf positioniert und der String id$ wird in Zeile 290 eingelesen. Kommen wir nun zum zweiten Teil der Routine, in dem die Directoryeinträge eingelesen werden:
299 q=0 300 print#1,"u1 2 0";tr;se 305 a=int(q/8):dt(a)=tr:ds(a)=se 310 gosub600:tr=a 320 gosub600:se=a 330 forj=1to8 335 printq 340 gosub600:ty(q)=a 350 gosub600:ft(q)=a 360 gosub600:fs(q)=a 370 x$="" 380 fori=1to16:get#2,a$:x$=x$+a$:next 390 na$(q)=x$ 400 fori=1to9:get#2,a$:next 410 gosub600:bl(q)=a 420 gosub600:bl(q)=bl(q)+a*256 425 get#2,a$:get#2,a$ 430 q=q+1 440 next j 450 iftr<>0then300 460 : 470 close1:close2 480 q=q-1 500 ifna$(q)=""thenq=q-1:goto500 510 return
In Zeile 299 wird nun zunächst die Lauf- variable "q" initialisiert. Sie gibt später an, wieviele Einträge in diesem Directory gefunden wurden. Der U1-Befehl in Zeile 300 liest nun den ersten Direc- toryblock ein, dessen Track und Sektor ja noch in TR und SE stehen. Anschlies- send werden beide Variablen im 0. Element von "dt" und "ds" abgelegt. Hierbei wird die Hilfsvaiable "a" als Indexvariable verwendet. Ihr wird der Wert (q dividiert durch 8) zugeordnet, da in einem Block ja immer 8 Fileein- träge stehen. In den Zeilen 310 und 320 werden nun schon einmal Track- und Sektornummer des Folgeblocks in TR und SE eingelesen. Bitte Teil 2 des Floppy-Kurses laden !