Floppy-Kurs: "Es rappelt in der Kiste..." (Teil 6)
Hallo und herzlich Willkommen zum sechsten Teil unseres Floppykurses. Wir wollen unsere Kenntnisse auch hier wieder
vertiefen und zwei weitere Beispielprogramme kennenlerenen. Eines, um das Directory 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 Speicherverlust einlesen können. Dies ist ein
Hinweis auf eine besondere Eigenheit der Floppy. Wenn wir uns nämlich das Inhaltsverzeichnis einer Diskette einfach
nur anzeigen lassen wollen, so können
wir durch Angabe des Filenamens "$" die
Floppy dazu bewegen, uns sehr viel Arbeit abzunehmen. Sie beginnt dann nämlich damit, alle Directoryblöcke von
selbst einzulesen und sie für uns aufzubereiten. Ein direktes " herumpfuschen" auf der Diskette mittels der Block-Befehle entfällt.
Wollen wir uns nun verdeutlichen, was
passiert, wenn wir wie gewohnt das Directory 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 Aufbereitung 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 Basic- Programm, mit dem Befehl LIST auf
dem Bildschirm angezeigt wird. Sie können es sogar mit NEW wieder löschen, oder mit RUN starten, was jedoch nur
einen Syntax-Error bewirkt, da das Directory 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 Zeilennummer entspricht. Diese Zeilennummern
sind zwar nicht immer in einer numerischen Reihenfolge, jedoch macht das Basic unseres Computers da keinen Unterschied. Der interne Aufbau eines Basic-Programms erlaubt es tatsächlich, Zeilennummern wahllos zu vergeben. Wenn wir
ein Programm direkt eingeben ist das
natürlich nicht möglich, da die Eingaberoutine des Betriebssystems die Zeile
anhand der Zeilennummer automatisch an
der logisch richtigen Position des Programms 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 eigentlich nur der Basic-Programm- Speicher
versteht, so daß die LIST-Routine das
' Directory-Programm' listen kann. Deshalb gibt e) einige Bytes, die für uns
wertlos sind und überlesen werden müssen. 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ß normalerweise 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 Linkbytes ü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önnen. Hieran anschließend folgen nun ein
oder mehrere Spacezeichen, sowie der
Filename und die Filekennung im Klartext. 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 ( Basic-) Zeile kennzeichnet. An ihrer Stelle
müssen wir lediglich einen Zeilenvorschub ausgeben ( ein PRINT ohne Parameter) . 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 entsprechende 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 Bytewert 0 einen " illegal quantity error" verursacht. Das liegt daran, daß ein
String, der nur den Bytewert 0 als Zeichen enthält, einem Leerstring entspricht. 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. voherige Teile dieses Kurses) . In Zeile 210 werden nun die Startadresse und die beiden Linkbytes der ersten Zeile überlesen.
Danach beginnt die Hauptschleife des
Unterprogramms. In den Zeilen 220 und
230 lesen wir hier zunächst mit Hilfe
unserer " Zeichen lesen"- Unterroutine, Lowund 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 bewußt ein Semikolon an, damit der folgende Text direkt hinter der Blockanzahl ausgegeben wird. Dies geschieht in den
Zeilen 250 und 260 . Dort lesen wir jeweils 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 Hauptschleife 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 wollen 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 Floppybefehl an den Befehlskanal muß also lauten
" S: TEST" . Hieraufhin beginnt die Floppy
nun, das Inhaltsverzeichnis der eingelegten 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 lediglich 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 normalerweise im Directory nicht mehr angezeigt wird. An dieser Leerstelle wird
das File einfach übersprungen. Im Prinzip sind aber noch alle seine Informationen auf der Diskette enthalten. Zumindest einmal solange, bis Sie wieder
ein neues File auf die Diskette schreiben. Dann nämlich sucht sich die Floppy
den ersten freien Directoryeintrag heraus, der in unserem Fall, der des Files
" TEST" ist, und trägt dort das neue File
ein. Desweiteren kann es dann auch passieren, daß die Datenblocks, die von
" TEST" belegt wurden möglicherweise von
denen des neuen Files überschrieben werden. 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 andere 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 Anzeigen. Hieraufhin muß vom Benutzer entschieden 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 Entsprechenden Code in den Fileeintrag zu schreiben
und alle Blocks des Files zu verfolgen, die von ihm belegt wurden und sie abermals 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 verketteten 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 Vorund Nachteile.
Benutzen wir die erste Methode, so haben
wir bei höherem Programmieraufwand eine
weitaus höhere Arbeitsgeschwindigkeit
( da nur die Blocks eines Files herausgesucht werden müssen und nicht aller Files 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 arbeiten, die ebenfalls gesucht werden müssen. Das aber wiederum macht der Validate- 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 zeitraubender Datenaustausch zwischen Floppy
und C64 entfällt. Da wir unser Programm
in BASIC schreiben wollen, ist er bestimmt 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 Programm ja so erweitern, daß z. B. bei weniger als fünf Files automatisch der
Validate-Befehl benutzt wird und im anderen Fall die " von Hand"- Routine.
Zunächst will ich Ihnen nun eine Unterroutine vorstellen, die uns das Inhaltsverzeichnis einliest und alle Informationen darüber in Variablenfeldern
ablegt. Sie steht in dem Beispielprogramm " 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 Eintrag einem DEL-File entspricht. Ich habe die Routine jedoch so ausgelegt, daß Sie
sie auch für andere Zwecke benutzen können, da sie so ziemlich alle Informationen des Directorys ausliest.
Vorher sind jedoch noch einige Anmerkungen zu der Routine zu machen. Vorab sei
gesagt, daß in den Zeilen 600-620 dieselbe 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 Variablenfelder, die im Hauptprogramm mit Hilfe der
DIM-Anweisung dimensioniert werden müssen. 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 beinhalten kann, werden alle diese Felder auf
maximal 144 Elemente dimensioniert. Die
Felder DT und DS werden benutzt um Track
und Sektor der Folgeblocks des Directorys 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 ( hierauf 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 werden die Vorbereitungen zum Einlesen des
Directorys getroffen, sowie der Diskettenname und die ID in die Variablen
" dn$" und " id$" eingelesen. Diese Variablen können Sie im Hauptprogramm eben- falls weiterverwenden.
Nachdem also in Zeile 200 ein Informationstext ausgegeben wurde, öffnen wir
in Zeile 210 den Befehlskanal und einen
Pufferkanal. Ersterer erhält die Filenummer 1, letzterer die Filenummer 2 .
Beim Öffnen des Befehlskanals initialisieren wir die Diskette auch gleichzeitig. 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 Pufferzeiger 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 Laufvariable " 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 Directoryblock ein, dessen Track und Sektor
ja noch in TR und SE stehen. Anschliessend 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 Fileeinträge stehen. In den Zeilen 310 und 320 werden nun schon einmal Trackund
Sektornummer des Folgeblocks in TR und
SE eingelesen.
Bitte Teil 2 des Floppy-Kurses laden !