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 !