----------------------------------------
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öch-
te diesen Kurs nun mit einem anspruchs-
vollen 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 Bei-
spiel 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 umzu-
kopieren, daß jede Diskette optimale
Platzausnutzung aufweisen soll. Am Be-
sten so, daß restlos jeder Block dieser
Diskette beschrieben ist. Beim Zusam-
menstellen bleiben dann aber noch noch
17 Blocks übrig - Mist, kein Platz mehr
für das letzte, 30 Blocks lange Pro-
gramm!
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 (Är-
gerlich - 17 Blocks verschenkt).
3) Sie packen das letzte Programm (Wie-
der Gähn - dauert auch lange und aus-
serdem hat das File hinterher be-
stimmt genau 18 Blocks, so daß es
doch nicht passt).
4) Sie benutzen das Programm "USEDIR",
das wir uns in diesem Kursteil erar-
beiten 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 Direc-
tory. "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 Fileein-
tragsblocks (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 ent-
spricht. Da eine normale Diskette aber
so gut wie nie so viele Files beher-
bergt, liegen eine Menge dieser Direc-
toryblocks brach. Gerade bei einer rand-
vollen 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äch-
lich 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 er-
gibt 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ügba-
ren Blöcke sein und sollte vom Typ "PRG"
sein. Daraus ergeben sich folgende Auf-
gaben für unser Programm:
1) Das zu installierende File einlesen
und benötigte Anzahl von Blöcken er-
mitteln.
2) Anzahl der freien Blöcke der Zieldis-
kette 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 abbre-
chen.
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 vor-
letzten 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 Track- und Sektornummer des er-
sten freien Directoryblocks abgeän-
dert.
9) Jetzt müssen wir nur noch die fehlen-
den Directoryblocks schreiben und den
letzten Directoryblock auf den ersten
Track/Sektor des geschriebenen Files
zeigen lassen - Fertig ist die In-
stallation!
DIE BENÜTIGTEN PROGRAMMTEILE
Kommen wir nun also zu unserem Programm.
Dabei möchte ich Ihnen zunächst einmal
eine Liste der darin enthaltenen Routi-
nen geben. Hierbei habe ich in zwei Ar-
ten unterschieden: in Steuer- und IO-
Routinen. Diese beiden Namen treffen
Ihre Bedeutung zwar nicht voll und ganz,
jedoch musste irgendwo eine Grenze gezo-
gen werden.
Die erste Art, die Steuerroutinen also,
sind alles Funktionen, die entweder
nichts direkt mit der Floppy zu tun ha-
ben, und somit für uns uninteressant
sind, oder aber Funktionen, die wir in
vorangegangenen Kursteilen schon bespro-
chen hatten, und somit nicht noch einer
zweiten Dokumentation bedürfen. All die-
se Routinen werden nur mit ihrem Namen,
ihrer Funktion und ihren Parametern auf-
gefü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 Be-
schreibung benötigen und im Laufe dieses
Kursteils alle nocheinmal genauer doku-
mentiert sind.
Desweiteren werden diverse Speicherzel-
len als Zwischenspeicher für verschie-
dentliche 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-$03FF, dem Kasettenpuffer al-
so, der bei Floppybenutzung ja unbe-
nutzt, und deshalb verwendbar ist. Hier
nun also die besprochenen Beschreibun-
gen:
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 ei-
nen maximal 16 Zeichen langen String
von der Tastatur ein und wird zum Ein-
geben des Filenamens benutzt. Dieser
String liegt nach dem Rücksprung ab
der Adresse NAME. Die Länge des File-
namens steht in der Speicherzelle
MEM0. Am Ende des Namensstring wird
ein Nullbyte als Endmarkierung an-
gefü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 Be-
fehlskanal mit der logischen Filenum-
mer 1. Beim Üffnen wird die Diskette
automatisch initialisiert (Floppybe-
fehl "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 abge-
legt ist, an die Adresse FILEMEM. Sie
benutzt die Zeropageadressen $F9/$FA
als Lesezeiger. Hier steht nach dem
Rücksprung gleichzeitig auch die Enda-
dresse 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 be-
stehen, wird diese Routine benutzt, um
einen Befehl, dessen String an der
Adresse in X- und Y-Register steht und
dessen abschließende Nummer im Akku
übergeben wurde, an die Floppy zu sen-
den.
* 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, wer-
den 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 Zieldisket-
te und legt sie in DIRFREE ab. Deswei-
teren wird die Summe von DSKFREE und
DIRFREE berechnet und in ALLFREE abge-
legt.
* GETNEED: Berechnet die benötigte An-
zahl Blöcke des zuvor eingelesenen
Files und legt Sie bei "NEED" ab.
* CHGENT: Diese Routine sucht den File-
namen in NAME aus dem Directory he-
raus, liest die Nummern des ersten
Tracks und Sektors dieses Files ein,
und überschreibt diese beiden Informa-
tionen 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 Track- und Sek-
tornummer 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 Steuerrouti-
ne 'MAIN' unseres Programms, in der das
oben aufgeführte Aufgabenschema in Pro-
grammcode umgesetzt ist. Anschließend
wollen wir uns mit den benutzten Unter-
routinen beschäftigen.
1) MAIN
Diese Routine steuert das gesamte Pro-
gramm. Zunächst einmal wollen wir die
Bildschirmfarben setzen, den Titeltext
ausgeben und den Namen des zu installie-
renden Files ermitteln. Ist dieser Name
gleich dem String X", so soll das Pro-
gramm 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. Hierauf-
hin wird der Benutzer dazu aufgefordert,
die Zieldiskette einzulegen, von der wir
dann die Anzahl der freien Dir- und
Diskblocks ermitteln. Gleichzeitig wird
dann auch noch das Dummyfile erzeugt. Am
Ende werden alle ermittelten Werte mit-
tels der Routine STATOUT auf dem Bild-
schirm 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ür-
zer 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 heraus-
suchen würden. Es geht aber noch viel
einfacher: das DOS der Floppy beschreibt
die Sektoren einer Diskette nämlich mei-
stens in einer ganz bestimmten Reihen-
folge. Das ist bei den normalen Daten-
blocks nicht unbedingt gesichert, da bei
einer nahezu vollen Diskette diese Rei-
henfolge nicht mehr eingehalten werden
kann. Beim Directorytrack 18 können wir
uns jedoch mit 100%-iger Sicherheit da-
rauf verlassen, daß sie immer eingehal-
ten wird. So gibt es nämlich eine ganz
bestimmte Reihenfolge, in der die Direc-
torytracks geschrieben werden. Wir müs-
sen 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" abge-
legt, 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