Magic Disk 64

home to index to html: KURS-FLOPPY-KURS.html
              Floppy-Kurs:              
      "Es rappelt in der Kiste..."      
                (Teil 1)                
----------------------------------------
In Anlehnung an  diverse  Geräusche  die
das  Laufwerk  unseres Computerlieblings
an und wann von sich  gibt, wünsche  ich
Sie  herzlich willkommen zum ersten Teil
unseres neuen Floppykurses.             
In  den  nächsten Monaten will ich Ihnen
hier die  Funktionsweise  und  effektive
Benutzung  des  Floppylaufwerks 1541 des
64ers erklären. Beginnend mit den Grund-
lagen  zur  Datenspeichrung auf Diskette
werden wir  uns  über  sequentielle  und
relative  Dateiverwaltung  immer mehr an
die 'Innereien' der Floppy  herantasten,
und uns auch mit dem Diskettenaufbau und
Direktzugriff  beschäftigen. Danach will
ich Ihnen zeigen, wie einfach es ist die
Floppy in Assembler zu programmieren und
Ihnen desweiteren meine gesammelten Tips
und Tricks zur  Floppy  offenbaren.  Ich
wünsche  Ihnen also viel Spaß bei diesem
Kurs und will gleich zu Sache kommen... 
GRUNDLAGEN DER DATENEIN-/AUSGABE        
Dieser erste Teil des Floppy-Kurses soll
ganz den Grundlagen und der  sequentiel-
len  Fileprogrammierung  gewidmet  sein.
Kommen wir zunächst einmal zu den Grund-
begriffen  der Datenein- und ausgabe des
64ers. Wenn Sie sich mit  BASIC  ausken-
nen, so wissen Sie, daß es prinzipiell 5
BASIC-Befehle  gibt, mit denen wir Daten
an externe, oder  von  externen  Geräten
senden und empfangen können. Diese sind:
* OPEN, zum Üffnen eines Datenkanals,   
* PRINT#, zum Senden von Daten in einen 
  Datenkanal,                           
* GET#, zum Lesen von Daten  von  einem 
  Datenkanal,                           
* INPUT#,   zum   einfacheren    (BASIC-
  orientierten) Lesen von einem Datenka-
  nal, und                              
* CLOSE, zum abschließenden Schließen   
  eines Datenkanals.                    
Datenein- und ausgaben werden nun  prin-
zipiell  mit  drei verschiedenen Parame-
tern zu den obigen  Befehlen  gesteuert.
Sie  geben  an,  welchem logischen Kanal
der aktuelle  Datenaustausch  zugeordnet
wird  und  geben  Informationen über die
Art des Zugriffs  (lesen/schreiben)  und
über  das entsprechende Gerät, das ange-
sprochen werden soll. Diese drei Parame-
ter  sind die "logische Filenummer", die
"Sekundäradresse"  und  die  "Gerätenum-
mer".                                   
Die  logische  Filenummer  ist eine Zahl
zwischen 1 und 127, die als Kennung  für
eine  Ein-/Ausgabeoperation dient. Durch
den OPEN-Befehl wird ihr eine  bestimmte
Fileoperation  zugewiesen,  die sie ein-
deutig definiert. Wird also ein Datenka-
nal  zur  Floppy mit der logischen File-
nummer 1 und ein Datenkanal zum  Drucker
mit der logischen Filenummer 2 geöffnet,
so  weiß  das  Betriebssystem  des 64ers
immer, wohin es Daten mit einer der bei-
den   Filenummern   senden   soll.   Ein
"PRINT#1,A$" sendet so die  Stringvaria-
ble A$ an den momentan offenen Floppyka-
nal (nachdem die Filenummer 1  so  durch
'OPEN'  definiert wurde), wohingegen der
Befehl "PRINT#2,A$"  dieselbe  Stringva-
riable  an  den  Drucker sendet (nachdem
ihm die Filenummer 2 zugeordnet wurde). 
Die  Gerätenummer  spezifiziert  nun das
Gerät, mit dem Daten ausgetauscht werden
sollen. Sie kann Werte zwischen 0 und 15
annehmen. Hierzu gibt es eine Liste, die
die verschiedenen  Geräte  spezifiziert.
Das Betriebssystem des C64 benötigt die-
se Nummer deshalb, weil die  verschiede-
nen  Peripheriegeräte mit unterschiedli-
chen Betriebssystemroutinen angesprochen
werden müssen. Die Verteilung der  Gerä-
tenummern  an  die entsprechenden Geräte
ist wiefolgt festgelegt:                
Ger.num.  Gerät                         
----------------------                  
  0       Bildschirm                    
  1       Datasette                     
  2       RS232-Schnittstelle           
  4       Drucker 1                     
  5       Drucker 2                     
  8       Floppylaufwerk 1              
  9       Floppylaufwerk 2              
 10       Floppylaufwerk 3              
 11       Floppylaufwerk 4              
Die restlichen  Nummern  sind  unbelegt.
Wie Sie sehen ist es also möglich bis zu
4  Floppylaufwerke  und 2 Drucker an den
C64  anzuschließen,  die  alle  getrennt
voneinander  angesprochen werden können.
Ebenso ist  die  Kommunikation  mit  dem
Bildschirm  über  die Gerätenummer 0 mö-
glich. Hierbei empfangen Sie direkt  die
Eingaben,  die  gerade  vom Benutzer auf
den Bildschirm geschrieben werden.      
Die     Sekundäradresse    einer    Ein-
/Ausgabeoperation gibt nun die Art einer
Datenoperation an. Sie kann  Werte  zwi-
schen  0  und 15 annehmen, wobei es fol-
gende Bedeutungen gibt:                 
SAdr. Bedeutung                         
------------------------                
   0  Programm laden                    
   1  Programm speichern                
2-14  Daten Lesen oder Schreiben        
  15  Floppybefehlskanal                
In der Regel werden die Sekundäradressen
zwischen 2 und 14 verwendet. Die  Adres-
sen 0, 1 und 15 sind spezielle Adressen,
die  eingens für die Verwaltung von Mas-
senspeichern gedacht sind. Normalerweise
muß man nämlich beim Zugriff  auf  einen
Massenspeicher  (so wie auch das Floppy-
laufwerk einer  ist)  grundsätzlich  die
Art  des  Zugriffs explizit im Filenamen
angeben. Möchte man aber ein Programmfi-
le (dazu später mehr) lesen oder schrei-
ben, so kann man sich die Angabe im  Fi-
lenamen sparen und die Adressen 0 oder 1
benutzen.  Die  Floppy  weiß in dem Fall
direkt, daß sie ein Programmfile  lesen,
bzw. schreiben soll. Dazu will ich Ihnen
später noch ein paar Beispiele liefern. 
Die  Sekundäradresse  15 ist ausschließ-
lich für das Floppylaufwerk  reserviert.
Mit  ihr  wird  der sogenannte Floppybe-
fehlskanal geöffnet. Dieser ist nicht an
ein spezielles File gebunden  und  dient
der  Öbertragung  von  Befehlen  an  die
Floppy selbst. Die 1541 verfügt nämlich,
wie der C64  auch,  über  einen  eigenen
Mikroprozessor  (den  6502,  ein Artver-
wandter des 6510,  der  im  64er  seinen
Dienst   verrichtet)   und  eigene  Ein-
/Ausgabechips.  Sie  stellt  im  Prinzip
einen  autonomen Computer dar, der rich-
tig Programmiert werden kann. Die  wich-
tigsten  Funktionen  sind  in spezeillen
Floppybefehlen zusammengefasst und  wer-
den über den erwähnten Befehlskanal auf-
gerufen (wie z.B. der Befehl,  die  ein-
liegende  Diskette  zu formatieren, oder
ein spezielles File von der einliegenden
Diskette zu löschen). Der Floppybefehls-
kanal hat später, wenn wir die Direktzu-
griffbefehle behandeln, und in Zusammen-
hang  mit  der relativen Dateiverwaltung
eine große Bedeutung.                   
Die  obig  beschriebenen  Parameter, die
der OPEN-Befehl benötigt müssen wiefolgt
angewandt werden (Praxisbeispiele werden
wir im Laufe dieses Kurses noch genügend
kennenlernen, deshalb hier nur eine Syn-
taxdefinition):                         
OPEN (log.Filenr.),(Ger.nr.),(Sek.adr.) 
DER FLOPPYBEFEHLSKANAL                  
Wir wollen uns nun mit der Benutzung des
Floppybefehlskanals   beschäftigen   und
einmal  die  alltäglichen  Floppybefehle
durchgehen.                             
Bevor  Sie  also  einen  Befehl  an  die
Floppy schicken, müssen Sie den Befehls-
kanal öffnen.  Dies  geschieht  mit  dem
Befehl  "OPEN  1,8,15".  Wir öffnen hier
einen Kanal mit der logischen Filenummer
1, verbunden mit dem Gerät Nummer 8 (der
Floppy nämlich) und mit  der  Sekundära-
dresse 15 (eben dem Befehlskanal dersel-
bigen). Nun können Sie der Floppy Befeh-
le senden, die diese dann unabhängig vom
64er  bearbeiten  wird.  Sie können also
Ihren 64er währenddessen in  seinem  ak-
tuellen  Programm  fortfahren lassen. Er
arbeitet, solange die Floppy selbst  ar-
beitet,  unabhängig  von  ihr. Nur, wenn
während dieser Zeit ein weiterer Disket-
tenzugriff notwendig wird, wird der 64er
angehalten (s.u.)                       
Ich   will   Ihnen   nun  die  Standard-
Floppybefehle auflisten, die wir einfach
benutzen können. Später  beim  Direktzu-
griff und bei der relativen Dateiverwal-
tung werden wir ebenfalls über  den  Be-
fehlskanal der Floppy Anweisungen geben,
uns die Daten, die wir von ihr verlangen
entprechend vorzubereiten.              
Kommen  wir  jedoch  erst  einmal zu den
einfachen Befehlen:                     
* Der NEW-Befehl:                       
Mit  diesem  Befehl formatieren wir eine
neue  Diskette.  Grundsätzlich  bestehen
die  Floppybefehle aus einem oder mehre-
ren Buchstaben, sowie den zu  jedem  Be-
fehl  variierenden Parametern. Beim NEW-
Befehl ist die Befehlskennung ein  "N:",
nach diesen beiden Zeichen folgt nun der
Name,  den  die  Diskette erhalten soll,
sowie eine zwei Zeichen lange  Identifi-
kationskennung   ("ID").   Floppybefehle
werden nach dem Üffnen des Befehlskanals
immer mit einem  "PRINT#lfn"  (lfn=logi-
sche   Filenummer)  an  die  Floppy  ge-
schickt.  Mit   dem   folgenden   BASIC-
Kommando  fordern  wir  die  Floppy also
dazu auf, die einliegende  Diskette  mit
dem  Namen  "MEINE DISK" und der ID "MD"
zu formatieren. Der  Befehlskanal  wurde
unter  der logischen Filenummer 1 geöff-
net (wie in obigem Beispiel), also  sen-
den wir den Befehl auch an Kanal 1:     
PRINT#1,"N:MEINE DISK,MD"               
Die Floppy beginnt nun mit der Formatie-
rung der Diskette. Der 64er meldet  sich
mit  einem "READY." zurück, und Sie kön-
nen während  der  Formatierung  mit  ihm
arbeiten.  Sie  können  den Befehlskanal
nun offen halten und  weitere  Floppybe-
fehle senden, oder aber auch irgend eine
andere  Tätigkeit  mit  dem Rechner tun.
Achten Sie allerdings darauf,  daß  beim
weiteren Senden eines Befehls, sowie dem
Schließen des Befehlskanals, der Rechner
blockiert wird. Das liegt daran, daß die
Floppy  während einer internen Operation
dem 64er signalisiert,  daß  sie  gerade
"BUSY",  also  am Arbeiten ist. Soll der
C64 nun mit der Floppy kommunizieren, so
wartet er solange, bis die Floppy wieder
frei wird. Demnach führt  jeder  weitere
Befehl,  der die Floppy anspricht unwei-
gerlich zu einem zwischenzeitlichen Stop
des Rechners.                           
Der NEW-Befehl kennt übrigens zwei  Syn-
taxen.  Öbergeben  Sie  einen  Namen UND
eine ID, dann wird die Diskette physisch
formatiert. Das heißt, sie wird Spur  um
Spur  neu angelegt, wobei die neuen Spu-
ren mit Nullen aufgefüllt werden. Daten,
die sich evtl. auf ihr  befanden  werden
somit  komplett  gelöscht. Öbergeben Sie
aber nur einen Diskettennamen, und keine
ID, so  wird  die  Diskette  "softforma-
tiert".  Es wird lediglich der neue Name
über den  alten  geschrieben,  und  alle
Blocks  als unbelegt gekennzeichnet. Die
ID bleibt die alte. Die alten Daten sind
jedoch immer noch auf den Spuren enthal-
ten  und  können  evtl. gerettet werden.
Ausserdem ist das  softformatieren  wei-
taus  schneller  als  ein "hardformatie-
ren". Es funktioniert allerdings nur bei
schon einmal hardformatierten Disketten,
da diese  die  grundlegende  Diskettens-
truktur schon enthalten.                
Sie schließen den  oben  geöffneten  Be-
fehlskanal  übrigens  wieder  mit "CLOSE
1". Tun Sie das bitte  immer,  wenn  Sie
ein  Datenfile,  oder  den  Befehlskanal
nicht mehr brauchen, da der 64er  intern
immer  nur  10  offene  Kanäle verwalten
kann.                                   
* Der RENAME-Befehl:                    
Mit  dem  Rename-Befehl  können  Sie den
Namen eines schon bestehenden Files  um-
benennen.   Die  Syntax  ist  eigentlich
recht einfach. Das Kürzel für RENAME ist
"R:" es folgen nun der neue und der alte
Filename getrennt durch ein "="-Zeichen.
Hier    ein    Beispiel:PRINT#1,"R:MAGIC
DISK=GAME ON"                           
Dieser Befehl benennt das File "GAME ON"
in "MAGIC DISK" um. Wieder benutzten wir
die  log.  Filenummer  1,  die  oben  im
OPEN-Befehl definiert wurde.            
* Der VALIDATE-Befehl:                  
VALIDATE bedeutet "überprüfen", und sel-
biges   tut   der   Validate-Befehl  der
Floppy. Haben Sie nämlich Grund zu glau-
ben,  daß die Diskettenstruktur durchei-
nandergekommen ist, so zum Beispiel wenn
Sie ein, oder  mehrere  Files  speichern
wollten,  für die aber nicht ausreichend
Platz vorhanden war,  dann  sollten  Sie
diesen  Befehl  verwenden. Er untersucht
die Diskette auf ihre korrekte  Struktur
und  korrigiert  alles,  was nicht einer
normalen  Diskettenstruktur  entspricht.
In  dem Beispiel mit einem nur teilweise
gespeicherten Programm  sind  die  schon
geschiebenen  Blocks  dieses  Files  als
belegt gekennzeichnet, obwohl  das  File
im  Directoryeintrag  mit "0 Blocks" und
einem "*" als unbrauchbar gekennzeichnet
ist. Der Validate-Befehl erkennt nun die
fälschlicherweise  belegten  Blocks  und
gibt sie wieder frei. Desweiteren löscht
er den markierten Eintrag aus dem Direc-
tory. Er  benötigt  keinerlei  Parameter
und  kann folgendermaßen aufgerufen wer-
den:                                    
PRINT#1,"V"                             
Die Floppy beginnt nun mit der  Validie-
rung.  Diese  kann  je nach dem wie voll
und wieviele einzelne Files auf der Dis-
kette  enthalten  sind,  bis  zu mehrere
Minuten in Anspruch nehmen.             
* Der SCRATCH-Befehl:                   
Dieser  Befehl  löscht ein File auf Dis-
kette. Englisch  "to  scratch"  bedeutet
"kratzen"   und  im  Öbertragenen  Sinne
"kratzt" die Floppy tatsächlich ein File
von der Diskettenoberfläche.            
Der Scratch-Befehl wird mit "S:"  einge-
leitet, gefolgt von dem oder den Filena-
men, die gelöscht werden sollen. Hierbei
dürfen Sie sogar sogenannte Filepatterns
benutzen, die eine Fileangabe  abkürzen.
Ein  "Pattern"  ist  eine Maske, die man
der Floppy für einen Filenamen übergibt.
Alle Files,  deren  Namen  dieser  Maske
entsprechen   sind  damit  angesprochen,
werden in unserem Fall  also  durch  den
Scratch-Befehl   gelöscht.  Hier  einige
Beispiele:                              
PRINT#1,"S:FILE1"                       
PRINT#1,"S:TEST1,TEST2,TEST10,TEST11"   
PRINT#1,"S:TEST?"                       
PRINT#1,"S:TEST*"                       
Im ersten Beispiel wird das File  namens
"FILE1"  gelöscht.  Das  zweite Beispiel
löscht  die  Files   "TEST1",   "TEST2",
"TEST10",  und "TEST11". Selbiges können
wir aber auch mit  Filepatterns  verkür-
zen.  So können wir zum Beispiel mit dem
Fragezeichen eine  Fileangabe  abkürzen.
es steht für ein beliebiges Zeichen. Das
dritte Beispiel löscht also gleichzeitig
die  Files "TEST1" und "TEST2". Im vier-
ten Beispiel benutzen wir  den  Asterisk
("*") als Abkürzung. Er steht für alles,
was hinter den Zeichen "TEST" folgt. Wir
löschen  mit diesem Befehl also auf Ein-
mal alle vier Files aus dem zweiten Bei-
spiel.                                  
DIE VERSCHIEDENEN DATEITYPEN            
Nachdem wir nun Grundsätzliches über die
Kommunikation  mit  der  Floppy  gelernt
haben, möchte ich Sie nun in die einzel-
nen  File-Arten  selbiger einführen. Das
benötigen wir, um  die  unterschiedliche
Programmierung  der einzelnen File-Typen
zu verstehen. Insgesamt kennt  die  1541
fünf   verschiedene   File-Typen.  Diese
sind:* Programm-Dateien (PRG):          
In diesen Files sind Daten  sequentiell,
also Byte hinter Byte abgespeichert. Sie
enthalten  ausschließlich Programmdaten,
also  direkt  ausführbare  BASIC-,  oder
Maschinenprogramme.                     
* Sequentielle Dateien (SEQ):           
Diese   Fileart  unterscheidet  sich  in
nichts mit  der  vorherigen.  Die  Daten
liegen  hier  ebenfalls  sequentiell auf
der Diskette  vor.  Der  einzige  Grund,
warum man zwei Filearten zur sequentiel-
len Speicherung gewählt  hat,  ist  der,
daß in SEQ-Files immer nur Daten von und
zu  Programmen gespeichert werden sollen
und keine  Programme  selbst.  Wenn  Sie
also  eine  Adressverwaltung programmie-
ren, so sollten  Sie  Ihre  Adressen  in
einer  SEQ-Datei  speichern.  Dies kenn-
zeichnet Ihre Daten eben als  Daten  und
nicht  als ausführbares Programm (obwohl
Sie genausogut eine PRG-Datei  verwenden
könnten!). Die Unterscheidung ist beson-
ders wichtig für  den  LOAD-Befehl,  der
SEQ-Dateien  gar  nicht erst liest, son-
dern ausschließlich PRG-Dateien  in  den
Speicher des 64ers transferiert.        
* Relative Dateien (REL):               
In  relativen  Dateien  liegen die Daten
für uns Anwender  nicht  hintereinander,
sondern  relativ zueinander vor. Hierbei
wird beim Anlegen einer  REL-Datei  eine
Datensatzlänge  vorgegeben,  die für ein
REL-File immer gleich bleibt.  Wenn  Sie
nun  Daten in die Datei scheiben, werden
selbige zu einem  Datensatz  zusammenge-
fasst  und mit einer fortlaufenden Satz-
nummer versehen. Wenn Sie  zum  Beispiel
die  Adressen  Ihrer Adressverwaltung in
relativen  Datensätzen  speichern,   und
sich merken, unter welchen Datensatznum-
mern Sie die einzelnen Adressen  wieder-
finden,  so  können Sie direkt auf einen
Datensatz zugreifen.  Die  Vorteile  ge-
genüber sequentieller Speicherung liegen
auf  der  Hand:  durch den Direktzugriff
sind die  Daten  erstens  schneller  er-
reichbar,  weil nicht eine komplette Da-
tei eingelesen werden muß, sondern  eben
immer  nur  ein  einziger Datensatz, und
sie können zweitens extern gelagert wer-
den,  so  daß Sie kostbaren Arbeitsspei-
cher im 64er sparen. Die  Programmierung
von  relativen  Dateien  wird  uns im 2.
Teil dieses Kurses noch näher beschäfti-
gen.                                    
Bitte Teil 2 laden...                   
* User-Dateien (USR)                    
Diese  Dateien sind speziell für die di-
rekte Floppyprogrammierung  gedacht.  Da
die 1541 ja über einen eigenen Prozessor
verfügt  kann  dieser auch direkt in Ma-
schinensprache programmiert werden. Legt
man nun ein Assemblerprogramm  in  einem
USR-File  ab,  so  kann  dieses  von der
Floppy direkt in ihren eigenen  Arbeits-
speicher  geladen, und dort von ihr aus-
geführt werden.                         
* Deleted Files (DEL)                   
Mit  der Kennung "DEL" werden Files mar-
kiert, die  von  der  Diskette  gelöscht
wurden.  Die  Floppy löscht nun ein File
nicht wirklich, sondern sie versieht den
File-Eintrag im  Directory  einfach  mit
der  DEL-Kennung  und  gibt die vom File
belegten Blocks schlichtweg  als  'unbe-
legt' wieder frei. Das bedeutet, daß ein
File  nach  seiner  Löschung  auch immer
wieder restauriert werden kann,  solange
man  keine  neuen Daten auf die Diskette
geschrieben hat.  Ein  mit  DEL  gekenn-
zeichneter  Eintrag ist normalerweise im
Directory nicht sichtbar. Wie  das  den-
noch   möglich  ist,  und  wie  man  ein
gelöschtes File wieder retten kann, soll
und auch in einer der nächsten  Ausgaben
dieses Kurses beschäftigen.             
DIE PROGRAMMIERUNG SEQUENTIELLER DATEIEN
Speziell die SEQ- und REL-Dateien sollen
nun in diesem und dem nächsten Teil die-
ses Kurses zum Zuge kommen. Beginnen wir
mit   der  Programmierung  sequentieller
Files.                                  
Wie  oben  schon  erwähnt, liegen in SEQ
und PRG Dateien die  Daten  sequentiell,
also  Byte hinter Byte, vor. In der Rei-
henfolge, mit der wir Daten in ein  sol-
ches  File  hineinschreiben,  müssen wir
sie auch wieder auslesen. Die Vorgehens-
weise   hierbei   ist  denkabr  einfach.
Zunächst  wollen  wir  einmal  ein  File
schreiben.  Hierzu  müssen wir lediglich
ein SEQ-File zum  Schreiben  öffnen  und
dann  mittels  des  PRINT#-Befehls Daten
hineinschreiben. Hierzu ein Beispiel:   
OPEN 1,8,2,"DATEN,S,W"                  
PRINT#1,ZA                              
PRINT#1,a$                              
PRINT#1,x%                              
PRINT#1,CHR$(0);CHR$(40);               
CLOSE 1                                 
Hier öffnen wir zunächst einmal ein File
mit  dem  Namen  "DATEN".  Innerhalb der
Namensangabe  verlangt  die  Floppy  nun
noch  eine  Spezifizierung des File-Typs
und  der  Datenoperation   (Lesen   oder
Schreiben) die durchgeführt werden soll.
Das  "S"  steht dabei für eine (S)equen-
tielle Datei. Andere mögliche  Filetypen
wären  "R"  für  relative,  "P" für Pro-
gramm-, oder aber auch "U"  für  Userda-
teien.  Die  "P" Angabe nimmt dabei eine
Sonderstellung ein. Da sie der  am  häu-
figsten  benutzte  Filetyp  ist, muß sie
nicht explizit  angegeben  werden.  Wenn
keine Typenangabe gemacht ist, nimmt die
Floppy  automatisch  an,  daß es sich um
eine "PRG"-Datei handelt. Dies  funktio-
niert  jedoch  nur, wenn eine der beiden
Sekundäradressen 0 oder 1 benutzt wurde.
Dazu gleich mehr.                       
Direkt nach dem Filetyp kommt, ebenfalls
durch  ein Komma getrennt, eine Bezeich-
nung  der  Datenoperation,  die   durch-
geführt werden soll. Im Beispiel benutz-
ten wir die Operation "W", was  für  (W-
)rite=schreiben  steht.  Insgesamt  sind
hier aber  drei  verschiedene  Operanden
möglich:                                
(R)ead   - zum Lesen einer Datei        
(W)rite  - zum Schreiben einer Datei    
(A)ppend - zum  Anhängen  an  eine  alte
           Datei                        
Die "A"-Option entspricht im Prinzip der
"W"-Option.  Daten  werden in einem File
gespeichert, jedoch mit dem Unterschied,
daß diese Daten an eine schon bestehende
Datei angehängt werden, und  nicht  etwa
wie bei "W" eine neue Datei auf der Dis-
kette angelegt wird.                    
Nachdem  im obigen Beispiel eine sequen-
tielle Datei zum Scheiben geöffnet  wur-
de,   können   wir   Daten  mittels  des
PRINT#-Befehls in  sie  hineinschreiben.
Dies  kann  in  BASIC auf verschiedenste
Arten geschehen, die  Betriebssystemrou-
tine des PRINT#-Befehls passt sich auto-
matisch den Variablentypen an  (wie  Sie
sehen wurden in obigem Beispiel String-,
Float-  und  Integerveriablen  geschrie-
ben). Möchte man ein "echtes" Zeichen in
ein File schreiben, so  benutzt  man  in
der  Regel  die  CHR$-Funktion,  die nur
einzelne Bytes schreibt, in obigem  Bei-
spiel die Bytes mit den Werten 0 und 40.
Wichtig  ist die Angabe eines Semikolons
nach  der  CHR$-Ausgabe.  Dies  bedeutet
nämlich,  wie beim normalen PRINT-Befehl
auch, daß nach der "Ausgabe" auf Disket-
te  die  nächste  Ausgabe  direkt folgen
soll. Andernfalls  hängt  BASIC  nämlich
automatisch  ein  "Carriage Return" (CR,
ASCII-Wert 13) an die  Ausgabe  an.  Bei
der Ausgabe von "reinen" Bytes kann dies
hinderlich  sein,  weshalb man bei CHR$-
Ausgaben in der Regel ein ";" mitangibt.
BASIC-Variablen  MÖSSEN  ohne  Semikolon
ausgegeben werden, da der INPUT#-Befehl,
mit dem man die geschriebenen Daten spä-
ter wieder lesen muß das CR als  Endmar-
kierung eines Wertes heranzieht. Einzel-
ne  Bytes  liest  man  mit   der   GET#-
Funktion,  die  ja sowieso immer nur ein
einzelnes Byte einliest. Hier  ein  Bei-
spiel,  das  die obig geschriebene Datei
wieder einliest:                        
OPEN 1,8,2,"DATEN,S,R"                  
INPUT#1,ZA                              
INPUT#1,A$                              
INPUT#1,x%                              
GET#1,Z1$: GET#1,Z2$                    
CLOSE 1                                 
Die  zwei  einzelnen  Zeichen aus obigem
Beispiel  sind  nun   in   den   String-
Variablen Z1$ und Z2$ gespeichert. Möch-
te man sie wieder  in  echte  Byte-Werte
umwandeln, muß man die ASC-Funktion wie-
folgt benutzen:                         
Z1%=ASC(Z1$)                            
Z2%=ASC(Z2$)                            
Wie  Sie  sehen,  haben wir diesmal auch
beim OPEN-Befehl die "R"-Angabe gemacht,
damit die Floppy  weiß,  daß  wir  Daten
lesen wollen.                           
Was  Sie bei der sequentilellen Filepro-
grammierung unbedingt  anmerken  sollten
ist,  daß  die Daten eben "sequentiell",
also hintereinander, in einem File  lie-
gen. Sie müssen sie demnach haargenau in
der Reihenfolge wieder einlesen, mit der
Sie  sie geschrieben haben. Dies ist gar
nicht so selbstverständlich wie es  aus-
sehen mag, wie Sie im nächsten Monat bei
der  relativen Dateiverwaltung festellen
werden.                                 
Haargenau so, wie wir ein SEQ-File gele-
sen  und  geschrieben  haben, können Sie
mit PRG-Files hantieren. Sie müssen  le-
diglich  das "S" im Filenamen beim OPEN-
Befehl in ein  "P"  abändern.  Ansonsten
ändert sich nichts.                     
Pfiffig bei der Arbeit mit PRG-Files ist
nun die Anwendung der  "besonderen"  Se-
kundäradressen  0  und  1.  Wie ich obig
schon beschrieben hatte, können wir  da-
mit  der  Floppy gleich schon mitteilen,
daß wir ein PRG-File lesen oder  schrei-
ben  wollen. Die explizite Angabe ",P,R"
oder ",P,W" entfällt. Die Sekundäradres-
se  0  steht  für  "PRG-File lesen", die
Adresse 1 für "PRG-File schreiben". Hier
einfach einmal zwei Beispiele:          
"OPEN 1,8,0,"MEINEDATEI"                
(Üffnet  das  PRG-File  "MEINEDATEI" zum
lesen)                                  
"OPEN 1,8,1,"MEINEDATEI"                
(Üffnet  das  PRG-File  "MEINEDATEI" zum
schreiben)                              
Nun können Sie ganz  normal  mit  INPUT#
und  GET# lesen, bzw, mit PRINT# schrei-
ben. Ebenso müssen Sie Ihre Files natür-
lich   wieder   wie  gewohnt  mit  CLOSE
schließen.                              
Die Verkürzung über die Sekundäradressen
wird häufig in Kopierprogrammen benutzt,
weil  man  so nicht noch umständlich den
Filenamen-Appendix anzuhängen  hat  (be-
sonders in Assembler eine zwar einfache,
aber lästige Arbeit).                   
Das  war  es  dann  für diesen Monat. Im
nächsten Monat wollen wir  und  dann  an
die  relative Dateiverwaltung wagen, die
zwar komplizierter zu Programmieren ist,
mit der jedoch auch  sehr  flexibel  und
schnell  gearbeitet werden kann. Bis da-
hin ein allzeit "Gut Hack",             
                       Uli Basters (ub).
  Floppy-Kurs: "Es rappelt in der Kiste"
                 (Teil 2)               
--------------------------------------- 
Hallo  zum  zweiten  Teil  des   Floppy-
Kurses.  In der vorletzten MD hatten ei-
niges über  den  Floppybefehlskanal  und
die  Programmierung  sequentieller Files
gelernt. In diesem Teil wollen  wir  uns
nunmehr  mit  der relativen Dateiverwal-
tung beschäftigen, die zwar  etwas  kom-
plizierter  zu programmieren, dafür aber
weitaus flexibler als  die  sequentielle
Dateiverwaltung zu handhaben ist.       
DIE RELATIVE DATENVERWALTUNG            
Im ersten Teil des Floppy-Kurses  hatten
wir bei den von der Floppy unterstützten
Filetypen auch die sogenannten REL-Files
besprochen.  Sie bezeichnen Dateien, die
einen RELativen Aufbau  haben.  Was  das
bedeutet wollen wir nun klären.         
Sicherlich erinnern Sie  sich,  daß  die
sequentiellen  Files, wie der Name schon
sagt Daten "sequentiell", also Byte hin-
ter  Byte  enthalten.  Wenn wir Daten in
einem solchen File  gespeichert  hatten,
so  mussten wir immer das komplette File
in den Rechner einlesen,  um  die  Daten
weiterverwnden zu  können. Bei relativen
Files ist dieser Aufbau nun anders gere-
gelt. Sie arbeiten mit sogenannten  "Da-
tensätzen",  oder  engl.  "Records".  Im
Prinzip kann man eine relative Datei mit
einem  externen  Variablenfeld  verglei-
chen. Wir geben der Floppy lediglich an,
daß  wir  z.B.  das 24. Element (sprich:
Record)  ansprechen  wollen,  und  schon
können  wir es lesen oder schreiben. Bei
einem sequentiellen File hätten wir  die
23  Eintragungen  vorher  erst überlesen
müssen, bis wir auf das gewollte Element
hätten zugreifen können.                
Die  Vorteile von REL-Files liegen damit
auf der Hand:                           
1) Schnellerer  Zugriff,  da wir nur die
   benötigten Daten ansprechen müssen.  
2) Speicherersparnis  im Rechner selbst,
   da alle Daten  extern  gelagert  sind
   und  kein  Speicherplatz mit momentan
   nicht benötigten Daten belegt wird.  
Jedoch hat die relative  Dateiverwaltung
auch  einen  Nachteil:  da sie von BASIC
aus nicht  unterstützt  wird,  ist  ihre
Programmierung relativ umständlich. Den-
noch lässt sich auch das lernen, und wer
es einmal kann der wird merken, daß  die
Vorteile überwiegen.                    
DIE PROGRAMMIERUNG                      
Bevor wir überhaupt irgendetwas aus oder
in ein relatives File lesen oder schrei-
ben können müssen  wir  es  erst  einmal
generieren. Öberhaupt wird ein relatives
File  ganz  und gar anders behandelt als
ein sequentielles. So wird  beim  Üffnen
nicht mehr zwischen "Lesen" und "Schrei-
ben" unterschieden. Wir öffnen ein  sol-
ches  File  ganz  einfach  um der Floppy
anzuzeigen, daß wir es nun benutzen wol-
len. Ob wir nun lesen oder schreiben ist
ganz  egal.  Die  Floppy erkennt automa-
tisch, wenn  wir  etwas  schreiben  oder
lesen.  Das  heißt also, daß wir bei der
Benutzung eines  relativen  Files  immer
auch  den Floppybefehlskanal öffnen müs-
sen. Öber  spezielle  Befehle  wird  die
Floppy  dann über die folgende Operation
informiert. Sie  stellt  dann,  je  nach
Befehl,  beim  Lesen  auf  dem relativen
Filekanal die gewünschten Daten  bereit,
bzw.  erwartet auf diesem die Daten, die
geschrieben werden sollen.              
Wollen wir nun also einmal ein relatives
File anlegen, damit wir es benutzen kön-
nen. Bevor wir das tun, sollten wir  uns
überlegen,  wie lang ein Datensatz unse-
res REL-Files werden soll, und wie viele
davon wir vorläufig darin speichern wol-
len.  Ersteres  müssen  wir deshalb tun,
weil die Datensätze eines relatives  Fi-
les  immer  gleich  lang sein müssen, um
der Floppy das Auffinden eines Datensat-
zes  zu  ermöglichen. Nehmen wir einfach
einmal an, daß wir 300 Datensätze zu  je
80  Zeichen  anlegen  wollen.  Wie  oben
schon erwähnt, öffnen wir zuerst  einmal
den   Floppybefehlskanal.   Anschließend
folgt der OPEN-Befehl für  das  relative
File.  Wir  legen  dabei wie gewohnt die
logische  Filenummer,  die  Gerätenummer
und  die Sekundäradresse fest, und geben
einen Namen für unsere Datei an. An die-
sen  angehängt  folgt  die  Typenkennung
",L," sowie der  Länge  des  Datensatzes
in  diesem  File.  Hier ein kleiner Hin-
weis:  im  ersten  Teil  dieses   Kurses
erwähnte  ich,  daß die Kennung für eine
relative Datei beim Öffnen ein "R"  ist.
Das war leider eine Fehlinformation! "L"
ist  die  richtige  Bezeichnung  für den
OPEN-Befehl!                            
Hier nun ein Beispiel, in  dem  wir  das
relative  File "TEST" mit 80 Zeichen pro
Datensatz eröffnen:                     
OPEN 1,8,15                             
OPEN 2,8,3,"TEST,R,"+CHR$(80)           
Der  Befehlskanal  hat  nun die logische
Filenummer "1", das  relative  File  die
"2". Wichtig beim Üffnen desletzeren ist
auch  die  Wahl  der Sekundäradresse, da
diese bei  der  Befehlsübergabe  an  den
Befehlskanal  verwendet wird. Wählen Sie
bei der Sekundäradresse bitte nicht  die
vorreservierten  Nummern 0,1 und 15 (sh.
Floppy-Kurs, Teil 1), sondern  nur  Num-
mern  zwischen  2 und 14.Nachdem wir nun
also  alles  geöffnet  hätten,  was  wir
benötigen,  müssen wir jetzt erst einmal
die gewünschten 300  Datensätze  in  der
REL-Datei  anlegen. Das funktioniert ei-
gentlich ganz einfach: wir sprechen  le-
diglich  mit einem speziellen Befehl den
300. Datensatz  an,  und  schreiben  den
Wert 255 hinein. Die Floppy generiert in
diesem  Fall  nämlich  automatisch  alle
fehlenden Datensätze - in  unserem  Bei-
spiel also alle 300. Das Generieren die-
ser Sätze kann jetzt einige Zeit in  An-
spruch  nehmen,  da  die  Floppy nun den
Speicherplatz, den sie  für  alle  Sätze
braucht,  automatisch belegt und mit den
Bytewerten 255 füllt.  Stören  Sie  sich
bitte  nicht  daran, wenn während dieses
Arbeitsgangs (wie auch bei allen anderen
Operationen  mit  relativen  Files)  die
Floppy-LED   zu  blinken  beginnt,  oder
trotz Zugriffs zeitweise  erlischt.  Das
ist ganz normal bei der Arbeit mit rela-
tiven Files.                            
Wenn die Floppy mit dem Anlegen der  re-
lativen  Datei  fertig  ist  blinkt  sie
übrigens sowieso, da wir durch  den  Zu-
griff auf einen noch nicht existierenden
Datensatz  einen  "Record  not present"-
Fehler erzeugt  haben,  der  uns  jedoch
nicht weiter stören soll. Durch Auslesen
des  Befehlskanals  stoppen wir das LED-
Blinken.Hier nun das Ganze als Programm.
10 OPEN 1,8,15                          
20 OPEN 2,8,3,"TEST,R,"+CHR$(80)        
30 HI=INT(300/256): LO=300-256*HI       
40 PRINT#1,"P"+CHR$(3)+CHR$(LO)+CHR$(HI)
   +CHR$(1)                             
50 PRINT#2,CHR$(255)                    
60 INPUT#1,A,B$,C,D:                    
70 PRINT A,B$,C,D                       
80 CLOSE1: CLOSE2                       
Die  OPEN-Befehle  aus den Zeilen 10 und
20 kennen wir  ja  schon.  In  Zeile  30
spalten wir die Zahl 300 (die Anzahl der
Datensätze,  die  wir  in  unserer Datei
verwenden möchten) in Low- und High-Byte
auf, da mit dem CHR$-Befehl ja immer nur
8-Bit-Werte (von 0  bis  255)  übergeben
werden  können,  und  durchaus  mehr Da-
tensätze möglich sein können, müssen wir
einen 16-Bit-Wert in Lo/Hi-Folge an  die
Floppy  senden.  Dies  geschieht  in der
folgenden  Zeile  -   mit   dem   ersten
PRINT#-Befehl senden wir den Positionie-
rungsbefehl an die  Floppy.  Wohlgemerkt
geschieht  dies  über  den Befehlskanal!
Aus dem Beispiel ist die Syntax des  Po-
sitionierungsbefehls ersichtlich. Begin-
nend mit dem Zeichen "P" (für  "positio-
nieren")  werden  in Reihenfolge die Se-
kundäradresse der REL-Datei auf die sich
die Positionierung  beziehen  soll,  die
Datensatznummer  in  Lo/Hi-Folge,  sowie
die Byteposition innerhalb des  entspre-
chenden  Datensatzes  mittels CHR$-Codes
an die Floppy übermittelt. In  Zeile  50
wird  nun  über  den logischen Kanal des
REL-Files der Wert 255 in  den  positio-
nierten  Datensatz  geschrieben.  Da er,
wie alle anderen  vor  ihm,  noch  nicht
existiert,  beginnt die Floppy nun damit
alle  Datensätze   anzulegen,   um   den
Schreibbefehl in Record 300 ausführen zu
können. Es ist übrigens wichtig, daß Sie
beim  Erzeugen  einer REL-Datei den Wert
255 schreiben, weil dieser  nämlich  als
Endmarkierung  beim  Lesen dient. Hierzu
jedoch später mehr.                     
In den Zeile 60 und  70  lesen  wir  nun
noch  den  Fehlerkanal aus und geben die
"Record not present"-Fehlermeldung  aus,
um  die  blinkende Floppy-LED zu löschen
und schließen  anschließend  die  beiden
offenen  Files  -  schon  haben wir eine
REL-Datei zur Verfügung!                
DER SCHREIBZUGRIFF                      
Möchten  wir  nun mit unserer selbst er-
stellten REL-Datei arbeiten,  so  müssen
wir  sie  natürlich  öffnen. Hierbei ist
darauf zu achten, daß wir  dieselbe  Da-
tensatzlänge  angeben,  wie wir sie beim
Erzeugen der Datei verwendet haben.  An-
dernfalls  kommt  die Floppy nämlich mit
der Verwaltung der Datensätze  durchein-
ander,   was   verheerende   Folgen  bei
Schreibzugriffen  haben  kann.  Benutzen
Sie  am  Besten  also den gleichen OPEN-
Befehl, den Sie auch beim Erstellen  be-
nutzt haben!!!                          
Wenn wir jetzt  etwas  in  unsere  Datei
schreiben  möchten,  so verfahren wir im
Prinzip genauso, wie beim Erstellen  der
Datei  (denn  das  war ja nichts anderes
als das Schreiben  in  eine  REL-Datei).
Wir  öffnen  also  zunächst Befehlskanal
und  REL-Datei,  positionieren   mittels
Befehlskanal auf den gewünschten  Daten-
satz und  schreiben  über  die  logische
Filenummer der REL-Datei Daten in diesen
Satz hinein. Hier ein Beispiel:         
10 OPEN 1,8,15                          
20 OPEN 2,8,3,"TEST,R,"+CHR$(80)        
30 PRINT#1,"P"+CHR$(3)+CHR$(1)+CHR$(0)+ 
   CHR$(1)                              
40 PRINT#2,"DIESER TEXT WIRD NUN IN DA  
   TENSATZ NUMMER EINS GESPEICHERT!";   
50 CLOSE1: CLOSE2                       
Im  Positionierbefehl  wird  wieder  die
Sekundäradresse   3  verwendet.  Diesmal
positionieren wir jedoch auf Byte 1  des
ersten Datensatzes unserer Datei "TEST" 
(Die  Werte  1  und  0  entsprechen  der
LO/HI-Darstellung der Zahl 1 --> 256*0+1
= 1). In Zeile 40 wird  dann  mit  einem
ganz  normalen PRINT#-Befehl ein Text in
den positionierten  Datensatz  geschrie-
ben. Da der Datensatz diesmal schon exi-
stiert brauchen wir demnach auch  keinen
Fehler  von  der  Floppy  auszulesen, da
vorraussichtlich keiner auftritt.       
Anstelle  des  Textes  könnte  natürlich
auch eine Stringvariable stehen.  Achten
Sie  bitte  darauf,  daß Sie nie längere
Texte in einen Datensatz schreiben,  als
selbiger  lang  ist,  da Sie sonst einen
Teil der Daten  im  folgenden  Datensatz
verlieren könnten.                      
Wichtig ist auch, ob Sie beim  Schreiben
das   Semikolon  (";")  verwenden,  oder
nicht. Verwenden Sie es nicht, so können
Sie beim Lesen den INPUT#-Befehl verwen-
den. Dieser erkennt das Ende eines Lese-
vorgangs immer an einem "Carriage Return
Code" (kurz "CR" =  CHR$(13)),  der  von
einem  PRINT# OHNE Semikolon immer auto-
matisch gesendet wird. In dem Fall  müs-
sen Sie aber auch bedenken, daß ein Text
den  Sie  schreiben  nie  länger als die
Datensatzlänge-1 sein darf,  da  das  CR
ebenfalls  als ganzes Zeichen in den Da-
tensatz geschrieben wird.               
Nun ist, wie wir  später  sehen  werden,
das Lesen mittels INPUT# nicht immer von
Vorteil, weshalb Sie einen Text auch MIT
Semikolon  schreiben können. In dem Fall
müssen wir später beim Lesen eine  GET#-
Schleife  verwenden, da keine Endmarkie-
rung (das "CR")  für  den  INPUT#-Befehl
geschrieben wurde.                      
DER LESEZUGIFF                          
Auch der Lesezugriff weicht  nicht  son-
derlich  von  den  bisherigen Beispielen
ab. Wie immer öffnen die beiden  Floppy-
kanäle   und   positionieren   auf   den
gewünschten  Datensatz.  Nun  haben  wir
zwei  Möglichkeiten  unsere Daten wieder
auszulesen:                             
Wurden  die  Daten  OHNE  Semikolon  ge-
schrieben,  so   genügt  ein   einfaches
"INPUT#2,A$"  um  unseren Text wieder zu
Lesen und in A$ abzulegen.              
Wurden Sie MIT Semikolon geschrieben, so
müssen  wir den umständlicheren Weg über
eine GET#-Abfrage gehen. Dazu  zwei  An-
merkungen:                              
1) Bei  der  GET#-Abfrage  sollten nicht
   mehr Zeichen gelesen werden, als  ma-
   ximal in dem Datensatz vorhanden sein
   können.  Bei  einer Länge von 80 Zei-
   chen  wird  die  GET#-Schleife   also
   nicht mehr als 80 mal durchlaufen.   
2) Was tun wir, wenn der Datensatzinhalt
   kürzer  ist,  als die festgelegte Da-
   tensatzlänge? Wie ich oben schon ein-
   mal erwähnte, dient der Byte-Wert 255
   als   Endmarkierung  innerhalb  einer
   REL-Datei. Dies stellt sich  so  dar,
   daß  ein  leerer Datensatz alle Bytes
   mit dem Wert 255 gefüllt hat. Schrei-
   ben  wir nun einen Text in diesen Da-
   tensatz, so  werden  alle  benötigten
   Zeichen  mit  dem  Text überschieben.
   Das darauf folgende  Zeichen  enthält
   dann  aber  immer  noch den Wert 255.
   Dementsprechend können wir sagen, daß
   wenn wir  beim  Lesen  eines  Strings
   dieses  Zeichen  erhalten, der String
   zu Ende sein muß.                    
Durch  diese  beiden  Punkte ergibt sich
also folgende Schleife zum  Lesen  eines
Strings:                                
 90 ...                                 
100 A$=""                               
110 FOR i=1 TO 80                       
120 GET#2,B$                            
130 IF ASC(B$)=255 THEN 160             
140 A$=A$+B$                            
150 NEXT                                
160 ...                                 
DATABANKING MIT RELATIVEN FILES         
Sie  sehen,  daß  das  Arbeiten mit REL-
Files trotz aller Gegenteiligen Vorraus-
sagen  eigentlich  relativ  einfach ist.
Wir müssen jeweils nur richtig  positio-
nieren  und  können  dann beliebig Lesen
und Schreiben. Nun gibt es  jedoch  noch
einige  Kniffe,  die  man kennen sollte,
wenn man effektiv mit relativen  Dateien
arbeiten  möchte. Diese will ich nun an-
sprechen.                               
DATENFELDER:                            
Bei jeder Datenverarbeitung  werden  Sie
meist mehrere Angaben in einem Datensatz
machen.  Einfachstes  Beispiel  ist hier
eine Adressverwaltung. Hier  müssen  Sie
pro  Datensatz   einen  Namen,  Strasse,
Wohnort,  Telefonnummer,  etc.  angeben,
die  Sie  nachher  auch immer wieder auf
anhieb in Ihrer Adressdatei finden  müs-
sen.  Diese  einzelnen Einträge in einem
Datensatz nennt man Datenfelder. Wie bei
relativen Files so  üblich,  sollte  man
sich dann jeweils auf eine maximale Län-
ge eines  Datenfeldes  beschränken.  Sie
könnten  nun  für  jedes  Datenfeld eine
eigene  REL-Datei  anlegen,  also   bei-
spielsweise  eine  Datei  mit 30 Zeichen
pro Satz für alle  Namen,  eine  mit  40
Zeichen  für  alle  Straßen, eine mit 15
Zeichen für  alle  Telefonnummern,  eine
mit  4  Zeichen  für alle Postleitzahlen
usw. Diese Lösung birgt jedoch ein  grö-
ßeres Problem in sich: der C64 verwaltet
nämlich immer nur EINE offene REL-Datei.
Das  bedeutet in der Praxis, daß Sie je-
desmal, wenn Sie eine komplette  Adresse
ausgeben  wollen,  alle  Ihre  REL-Files
nacheinander öffnen, lesen und schließen
müssen.  Was  das  an  Programmier-  und
Zeitaufwand  beim  Zugriff  bedeutet ist
verheerend.  Deshalb geht man in der Re-
gel  einen  anderen Weg.  Halen wir doch
einfach  einmal  an  dem  Beispiel   der
Adressverwaltung  fest.  Zunächst wollen
wir uns  einmal  überlegen,  welche  und
wieviele  Felder  wir  verwenden wollen,
und wie lang sie im einzelnen sein  sol-
len.  Für unsere kleine Adressverwaltung
wollen wir 6 Felder pro Datensatz  defi-
nieren:                                 
1) "Name"         (20 Zeichen)          
2) "Vorname"      (15 Zeichen)          
3) "Straße"       (30 Zeichen)          
4) "Postleitzahl" ( 4 Zeichen)          
5) "Ort"          (30 Zeichen)          
6) "Telefon"      (15 Zeichen)          
Kommen  wir  nun zu dem Lösungsweg, denn
man hier in der Regel geht. Anstelle von
6 einzelnen Dateien legen wir  nun  eine
einzige Datei an, in der in einem Daten-
satz jeweils alle 6 Felder abgelegt wer-
den.  Dadurch  ergibt  sich  eine Daten-
satzlänge von 114  Zeichen  (20+15+30+4+
30+15=114). Wir können nun wiederum zwei
Wege gehen, mit denen wir die sechs Fel-
der in einem  Datensatz  speichern.  Ich
gehe  dabei  davon aus, daß die Einträge
vom Programm schon abgefragt wurden  und
in Stringvariablen stehen:              
Die einfachere, dafür jedoch unflexible-
re, Methode  sieht  folgendermaßen  aus:
Wir   schreiben   mit  mehreren  PRINT#-
Befehlen  OHNE Semikolon alle  Stringva-
riablen  hintereinander in ein Datenfeld
hinein. Später können  wir  sie  genauso
wieder  mittels INPUT# einlesen. Hierbei
ist es egal, wie  lang  ein  Feldeintrag
ist,  solange  er  die vorgegebene Länge
nicht überschreitet (wäre das bei  allen
Feldern  nämlich der Fall, so würden wir
mehr Zeichen in einen Datensatz  schrei-
ben,  wie  dieser lang ist, was man tun-
lichst unterlassen sollte). Je nach dem,
wie flexibel unser Adressverwaltungspro-
gramm  sein  soll  entstehen  nun jedoch
diverse Schwierigkeiten. So  müssen  wir
zum  Beispiel  immer  alle  Felder eines
Datensatzes einlesen, wenn wir eine  Da-
tei  z.B. nach einem einzelnen Feld sor-
tieren möchten. Für gerade diese Aufgabe
werden dann immer 5 Felder zuviel  gele-
sen, was sich wiederum auf die Verarbei-
tungszeit ReLativ auswirkt.  Das  zweite
Problem ist die Endmarkierung nach jedem
Feldeintrag.  Wie  oben  ja schon darge-
stellt müssen  wir  bei  dieser  Methode
OHNE Semikolon arbeiten, und in dem Fall
hängt  PRINT#  immer  ein  'CR' an einen
String an.  Dadurch müssen wir  von  den
obigen  Feldlängen  jeweils  ein Zeichen
abziehen (der Name z.B. darf nicht  mehr
20,  sondern  nur  noch  19 Zeichen lang
sein).                                  
Sie  sehen  also, daß diese Methode zwar
einfacher zu programmieren, aber sicher-
lich unflexibler ist.                   
(bitte Teil 2 Laden....)                
Kommen  wir  zu der flexibleren Methode.
Hierbei  halten  wir  wie  oben  an  den
Feldlängen  fest.  Da wir nun ja wissen,
wie lang ein  Feldeintrag  maximal  sein
kann,  können  wir  im Prinzip vorausbe-
rechnen, an welcher Stelle im  Datensatz
welches  Feld  zu finden, bzw. abzulegen
ist, ohne daß  sich  zwei  Felder  über-
schneiden.  Hierbei  müssen  wir  darauf
achten, daß entweder ein Feld  immer  so
lang  ist, wie es maximal sein darf (bei
kürzeren Einträgen wird der Rest einfach
mit SPACE-Zeichen aufgefüllt), oder aber
wir verwenden den Trick  mit  der  GET#-
Abfrage  beim  Lesen.  Letzeres ist wohl
die eleganteste Lösung, da  sie  weniger
Programmieraufwand erfordert und gleich-
zeitig  die  Lese-  und  Schreibzugriffe
beschleunigt, da immer nur so viele Zei-
chen gelesen werden, wie  auch  wirklich
benötigt  werden  (und  nicht  immer die
maximale Feldlänge). Wollen wir nun ein-
mal  ausrechnen, an welchen Bytepositio-
nen die einzelnen Felder abgelgt werden:
Feldename   Beginn      Länge           
-----------------------------           
Name        1.Byte   20 Bytes           
Vorname    21.Byte   15 Bytes           
Straße     36.Byte   30 Bytes           
PLZ        66.Byte    4 Bytes           
Ort        70.Byte   30 Bytes           
Telefon   100.Byte   15 Bytes           
Anhand der Länge eines Feldes können wir
immer  die erste Position des folgenden-
den Feldes berechnen, indem wir die  An-
fangsposition   im   Datensatz  mit  der
Feldlänge addieren.                     
Nun wissen Sie ja, daß man beim Positio-
nierbefehl  nicht  nur die Datensatznum-
mer, sondern auch die  Byteposition  in-
nerhalb  des  gewählten  Datensatzes an-
geben kann. Und über diese Methode  kön-
nen wir nun ganz bequem jede der 6 Feld-
positionen einstellen und  den  Feldein-
trag  hineinschreiben. Beim Lesen können
wir, da wir für jedes Feld  ja  die  An-
fangsposition  innerhalb eines Datensat-
zes  kennen,  dieses  ebenfalls   direkt
anwählen  und  Lesen.  Das ist vor allem
beim Sortieren einer REL-Datei von  Vor-
teil,  da  wir nun nach allen sechs Fel-
dern eine Datei beliebig sortieren  kön-
nen,  ohne  die übrigen fünf Felder noch
extra einlesen zu müssen. Das erhöht die
Arbeitsgeschwindigkeit   ungemein.   Ein
anderer  Geschwindigkeitsvorteil  ergibt
sich beim Ändern von Datensätzen. Wollen
wir zum Beispiel  in  einer  Adressdatei
nur  die  Straße verändern, weil die be-
treffende Person umgezogen ist,  so  ge-
nügt  es  auf das Feld "Straße" (Byte 36
im entsprechenden Datensatz) zu positio-
nieren  und  den neuen Eintrag hineinzu-
schreiben.  Hierbei  müssen  Sie  jedoch
auch  beachten, daß der neue Straßenname
unter Umständen kürzer ist, als der  al-
te.  Wir  müssen  dann  nämlich noch ein
CHR$(255)  nachschicken,  damit   unsere
GET#-Schleife  auch  ihre  Endmarkierung
findet. Andernfalls würde Sie die  rest-
lichen  Zeichen des  alten Straßennamens
mitlesen. Ein Beispiel:                 
Alte Straße      --> "Bahnhofstrasse 76"
Neue Straße      --> "Am Brunnen 2"     
Ergebnis beim                           
Lesen OHNE eine                         
neue Endmar-                            
kierung          --> "Am Brunnen 2se 76"
Schreiben  Sie  aber aich bitte nur dann
ein  CHR$(255)  danach,  wenn  der  neue
Straßenname  kleiner  als  die  maximale
Feldlänge ist.  Andernfalls  würden  Sie
wieder über die Feldgrenze hinausschrei-
ben und so das erste Zeichen des darauf-
folgenden Feldes überschreiben!         
SORTIEREN ODER INDIZIEREN:              
Oben  habe ich schon einmal die Möglich-
keit der Sortierung einer Datenbank  an-
gesprochen. Die augenscheinlich einfach-
ste Art der  Sortierung  wäre  wohl  das
alphabetische  Ordnen der Einträge eines
Feldes (z.B. des Namensfeldes), und  das
anschließende Umkopieren der Datensätze,
so daß im ersten Datensatz auch wirklich
der  alphabetisch  erste  und im letzten
Datensatz der alphabetisch  letzte  Name
steht.  Diese Lösung verwendet aber wohl
keiner, da man sich vorstellen kann, daß
die Sortierung durch das Umkopieren  der
Datensätze  einen  extremen  Zeitaufwand
bedeutet (gerade bei einfachen  Sortier-
algorithmen  ist  die Anzahl der Austau-
sche zwischen  zwei  Einträgen  ungemein
hoch). Zusätzlich erfordert diese Metho-
de nach jedem Neueintrag das  Umkopieren
der  kompletten  REL-Datei, da ein neuer
Datensatz ja ebenfalls irgendwo  einsor-
tiert werden muß, und damit alle folgen-
den Datensätze  einen  Datensatz  weiter
nach  hinten rücken. Lassen Sie uns also
diese Methode in dem  Mülleinmer  werfen
und die schnelle, komfortable und flexi-
ble Methode herauskramen:  "Indizierung"
heißt das Zauberwort!                   
Im  Prinzip  bedeutet  dieses Wort nicht
mehr als  "Sortieren",  jedoch  ist  die
Handhabung etwas anders. Hier möchte ich
noch einmal auf das Beispiel der Adress-
verwaltung zurückgreifen. Gehen wir also
davon aus, daß wir unsere Adressen  nach
den  Namen  der  eingetragenen  Personen
ordnen wollen. Zu  diesem  Zweck  lassen
wir  unseren Sortieralgorithmus nach und
nach alle Namen aus der REL-Datei ausle-
sen  und alphabetisch in eine Liste ein-
ordnen. Dabei wollen wir  uns  zu  jedem
Namen auch seine Datensatznummer merken.
Letztere  können  wir  dann als Referenz
auf den alphabetisch richtigen Datensatz
verwenden. Ich  möchte Ihnen dies anhand
eines Beispiels verdeutlichen.Nehmen wir
einmal eine Adressdatei, die die folgen-
den vier Namenseinträge, in der  Reihen-
folge  in  der  sie  eingegeben  wurden,
enthält:                                
Satznummer   Eintrag (Name)             
---------------------------             
     1       Müller                     
     2       Becker                     
     3       Schmidt                    
     4       Meier                      
Nun  sortieren wir unsere Datei nach Na-
men und erhalten folgende Reihenfolge:  
     2       Becker                     
     4       Meier                      
     1       Müller                     
     3       Schmidt                    
Die   aphabetisch  richtige  Reihenfolge
legen wir nun z.B. in  einem  Variablen-
feld  ab. Dieses Variablenfeld nennt man
Index. Wenn  wir  nun  den  alphabetisch
ersten  Eintrag lesen wollen, so schauen
wir uns einfach den  ersten  Eintrag  in
der  Indexliste an, und lesen den Daten-
satz ein, der dort steht -  im  Beispiel
also den zweiten.                       
Damit wir nicht jedes mal neu  sortieren
müssen  ist  es ratsam die Indexliste in
einem sequentiellen  File  auf  Diskette
abzuspeichern.  Wenn  wir  nun jedesmal,
wenn die Adressverwaltung gestartet wird
das Indexfeld einlesen,  so  können  wir
ganz  einfach  und  schnell alphabetisch
geordnete Datensätze finden und bearbei-
ten.  Ebenso  sollten wir darauf achten,
daß neue Datensätze  in  die  Indexliste
richtig einsortiert werden und der Index
wieder   neu  auf  Diskette  gespeichert
wird.                                   
Die  Indizierung bietet jedoch noch wei-
tere Vorteile. So können wir  auch  ganz
beliebig nach verschiedenen Feldern sor-
tieren und gleichzeitig beide sortierten
Dateien weiterverwenden. Wenn  Sie  Ihre
Adressen manchmal besser über den Vorna-
men finden können, so  ist  es  sinnvoll
auch nach dem Vornamen zu indizieren und
diesen  Index  als Alternative Verwenden
zu können. Beim Umschalten zwischen zwei
Indizes müssen Sie dann jeweils die neue
Indexdatei einlesen. Dies ist sogar  be-
quem  möglich, da wir zu der einen offe-
nen REL-Datei auch noch  eine  SEQ-Datei
öffnen   dürfen,   ohne   daß  der  64er
verrückt spielt. Wir können so als      
deh eit  den  Index wechseln, ohne dabei
noch  umständlicherweise  die  REL-Datei
schließen und anschließend wieder öffnen
zu müssen.                              
EIN WORT ZU NUMERISCHEN EINTRÄGEN:      
Vielleicht wollen Sie  irgenwann  einmal
auch  numerische  Einträge in einer REL-
Datei speichern. Das  wäre  bei  unserer
Adressverwaltung  zum  Beispiel  bei dem
Postleitzahlenfeld sehr gut denkbar.  In
manchen  Fällen  kann uns das auch einen
Speicherplatzvorteil im Gegensatz zu der
Speicherung als  String  geben.  Hierbei
müssen  wir  jedoch immer gewisse Regeln
beachten, sonst werden  Sie  ganz  schön
Probleme  mit den Datensatzlängen bekom-
men. Schreiben Sie nämlich eine  numeri-
sche  Variable (wie z.B. "A" als Float-,
oder "A%" als  Integervariable)  mittels
"PRINT#2,A"  (oder "PRINT#2,A%") in eine
Datei,  so  wandelt  die  PRINT#-Routine
Ihre  Variable immer in eine Zeichenket-
te, also eine  String,  um.  Wenn  diese
numerische Variable nun verschieden vie-
le Stellen besitzen kann, so werden  Sie
ganz  schöne  Schwierigkeiten  bekommen.
Logischerweise hat die Zahl  "100"  eine
Stelle weniger als die Zahl "1000". Hin-
zu kommt, daß auch noch ein "-" als Vor-
zeichen  davor  stehen  kann und daß der
PRINT#-Befehl  immer  noch  ein   SPACE-
Zeichen  ("  ")  vor einer Zahl ausgibt,
oder daß ganz  kleine  oder  ganz  große
Zahlen    in    der   wissenschaftlichen
Schreibweise  (also  z.B.   "3.456+E10")
ausgegeben werden.                      
Wie  lang sollen wir dann unser Feld nun
machen? Eine Antwort darauf ist  schwie-
rig, da das dann immer auch ganz von dem
Verwendungszweck  abhängt. Entweder müs-
sen Sie dem Benutzer schon bei der  Ein-
gabe  der Zahlen gewisse Einschränkungen
auferlegen, oder aber durch  eigene  Um-
konvertierung  einer Zahl eine bestimmte
Bytelänge festlegen können.             
Letzteres möchte ich hier  als  Beispiel
aufzeigen.  Wir  wollen uns auf positive
Integerzahlen  beschränken,  also  ganze
Zahlen  (ohne Nachkommastellen), die von
0 bis 65535 gehen. In diesem Fall genügt
es eine solche Zahl in Low- und Highbyte
aufzuspalten und die  beiden  CHR$-Codes
zu  speichern.  In  der Praxis sieht das
ähnlich wie in der Syntax  des  Positio-
nierbefehls für REL-Dateien aus:        
ZA=1992                                 
HI=INT(ZA/256): LO=ZA-256*LO            
PRINT#2,CHR$(LO);CHR$(HI);              
Damit können S)e nun alle oben genannten
Zahlen  schreiben.  Aber eben nur diese.
Möchten Sie z.B. Float-Zahlen schreiben,
so sollten Sie sich überlegen, wie  groß
oder  klein  diese  maximal sein können.
Dann können Sie sie sich einmal mit  dem
normalen PRINT-Befehl auf dem Bildschirm
ausgeben lassen und die Stellen, die sie
einnehmen abzählen. Wenn Sie sicher sein
können,  daß dies die maximale Länge ei-
ner Ihrer Zahlen ist, so können Sie  sie
als  Feldlänge  definieren. Beachten Sie
aber auch immer, daß  der  PRINT#-Befehl
vor  einer  Zahl  immer  ein Leerzeichen
druckt und daß zusätzlich immer noch ein
Minus-Zeichen  vor  einer  Zahl   stehen
kann.  Außerdem können Sie solche Zahlen
auch nur mittels des INPUT#-Befehls wie-
der  einlesen, weshalb Sie noch ein Zei-
chen mehr für das CR mitrechnen sollten.
Ein anderer Weg um Float-Zahlen zu spei-
chern  wäre  das  direkte übernehmen der
5-Byte-Mantissen-Darstellung,  wie   das
Betriebssystem  des  64ers  sie benutzt,
jedoch  ist  diese  Möglichkeit  relativ
kompliziert,  da  sie den intensiven Ge-
brauch von  Betriebssystemsroutinen  er-
fordert,  wovon  wir momentan die Finger
lassen  wollen  (in  einer der  späteren
Folge: dieses Kurses werde ich aber noch
einmal auf dieses Thema zurückkommen).  
So. Das wäre es dann wieder  einmal  für
diesen  Monat.  In  der nächsten Ausgabe
der "Magic Disk" wollen wir uns mit  den
Direktzugriffsbefehlen der Floppy befas-
sen, die es  uns  ermöglichen,  auf  die
Diskettenblocks direkt zuzugreifen.     
                                    (ub)
              Floppy-Kurs:              
      "Es rappelt in der Kiste..."      
                (Teil 3)                
----------------------------------------
Hallo und herzlich Willkommen zum  drit-
ten  Teil dieses Kurses. Nachdem wir uns
in den ersten beiden Teilen um  die  se-
quentielle und die relative Dateiverwal-
tung gekümmert hatten, wollen wir nun in
die   tieferen   Ebenen   der    Floppy-
Programmierung  einsteigen. Diesen Monat
soll  es  um  die  Direktzugriffsbefehle
gehen,  mit  denen  wir  Daten auf einer
Diskette  direkt  manipulieren   können.
Dabei  haben wir die Möglichkeit auf die
einzelnen Datenblöcke selbiger zuzugrei-
fen  und  sie  unseren Wünschen entspre-
chend zu verändern. Bevor wir  beginnen,
sollten  erst einmal ein paar Grundlagen
geklärt werden.                         
DIE DISKETTENSTRUKTUR                   
Zunächst  wollen  wir  uns  einmal   die
Grundstruktur  einer Diskette anschauen.
Nachdem sie mit  dem  Format-Befehl  der
Floppy  formatiert wurde, enthält Sie 35
konzentrisch angeordnete  Spuren  (engl.
"Tracks")  und  pro Spur 17-21 Sektoren.
Diese  Anordung  ist  typisch  für   das
1541-Format. Die unterschiedliche Anzahl
von  Sektoren  pro  Spur ergibt sich aus
der physikalischen Anordnug der  Spuren.
Dadurch,  daß  die Spuren von außen nach
innen immer  kleiner  werdenden  Kreisen
entsprechen,   wird   demnach  auch  der
Kreisumfang, oder die "Länge" einer Spur
immer  kleiner.  Je  kleiner   nun   die
Spurlänge  ist,  desto  weniger Sektoren
haben auf ihr Platz. Die folgene  Grafik
über  den Aufbau einer Diskette soll Ih-
nen das verdeutlichen...                
(Anm.  d.  Red.:  Bitte wählen Sie jetzt
den 2. Teil  des  Floppykurses  aus  dem
Textmenu!)                              
MD9207/MD9207-KURSE-FLOPPY-KURS_3-B.hires.png
Daraus ergibt sich nun die folgende Sek-
torenverteilung:                        
Spuren  Sektoren-pro-Spur               
-------------------------               
  1-17         21                       
 18-24         19                       
 25-30         18                       
 30-35         17                       
Wichtig ist, daß  Sie  wissen,  wieviele
Sektoren  ein  bestimmter  Track hat, da
wir diese Information bei den  Direktzu-
griffsbefehlen  benötigen, um einen spe-
ziellen  Block  anzusprechen.  Versuchen
Sie  nun  aber  Sektor 19 von Spur 35 zu
lesen, so erhalten  Sie  natürlich  eine
Fehlermeldung von der Floppy zurück (ob-
wohl es einen Sektor 19 bei  den  Tracks
1-24 gibt!).                            
DIE FLOPPY - EIN EIGENSTÄNDIGER COMPUTER
Wie ich vielleicht schon einmal erwähnte
stellt  die  1541  einen   vollblütigen,
unabhängen  Computer  dar.  Sie  verfügt
über einen eigenen 8-Bit-Prozessor  (den
6502,  der  Vorgänger  des  6510 aus dem
64er, der auch im VC20 Verwendung fand),
zwei I/O Bausteine,  die  man  auch  VIA
nennt (Typ 6522, das Vorläufermodell des
6526-CIA-Chips,  wie  er auch zweimal im
C64 vorhanden ist, und ebenfalls im VC20
eingesetzt war), einem 16-KB-ROM mit dem
Betriebssystem (DOS)  und  2KB  RAM  als
Arbeitsspeicher  für  letzeres  und Zwi-
schenspeicher für  Diskettenblocks.  Die
VIA-Bausteine  übernehmen  dabei den Da-
tentransfer mit dem 64er, sowie die  Be-
dienung  der  Floppymechanik.  Natürlich
können Sie dem Floppyprozessor auch  ein
Programm  geben, das er abarbeiten soll.
Dieses muß in Maschinensprache geschrie-
ben sein, wobei der Befehlssatz des 6502
identisch  mit  dem  des  6510  (aus dem
64er) ist.  Die  Speicheraufteilung  der
Floppy sieht folgendermaßen aus:        
Adresse       Belegung                  
----------------------------------------
$0000-$0800   2KB RAM                   
ab $1800      VIA1 (serieller Bus)      
ab $1C00      VIA2 (Laufwerkssteuerung) 
$C000-$FFFF   Betriebssystem-ROM (16 KB)
Alle übrigen Bereiche sind unbelegt. Wie
beim C64 auch, können Sie mit einem Pro-
gramm,  daß  sich im Floppy-RAM befindet
die  Betriebssystemroutinen  der  Floppy
aufrufen  (so  funktionieren z.B. einige
Software-Floppyspeeder).                
Kommen wir nun zum RAM der Floppy,  denn
das  soll  uns eigentlich interessieren.
Ein Datenblock auf einer  Diskette  ist,
wie  Sie  wissen  256  Bytes lang. Diese
Datenlänge wird auch als Einheit für das
Floppy-RAM benutzt: 256 Byte entsprechen
einer sogenannten "Page" (engl.  "Seite"
-  den Assemblerprogrammierern unter Ih-
nen sicherlich ein geläufiger  Begriff).
Das  Floppy-RAM  wird  nun in acht Pages
unterteilt, die von 0 bis 7  durchnumme-
riert sind. Die Pages 0, 1 und 2 sollten
wir  dabei außer Acht lassen, da sie für
die Zeropage,  den  Stack  und  den  Be-
fehls-puffer  der  Floppy  vorreserviert
sind. Die Pages 3-7 sind Datenpuffer für
Diskettenblöcke. Wir werden sie von  nun
an "Puffer" nennen.                     
DIE DIREKTZUGRIFFSBEFEHLE               
Kommen wir nun endlich zu  den  Blockbe-
fehlen  selbst.  Eigentlich gibt es drei
Gruppen, von  Befehlen,  mit  denen  wir
Abläufe innerhalb des "Floppy-Computers"
steuern  können:  die  Blockbefehle  zum
lesen, schreiben, belegen und  freigeben
von Blocks, die Memorybefehle, mit denen
wir   den  Floppy-Speicher  manipulieren
können und die  User-Befehle,  die  eine
Verkürzung von Block- und Memorybefehlen
darstellen.                             
Bevor  wir  nun einen dieser Befehle be-
nutzen können, müssen wir natürlich  den
Floppybefehlskanal  öffnen.  Da wir aber
immer  auch   einen   Arbeitspuffer   im
Floppy-RAM  benötigen,  um die Direktzu-
griffsbefehle (einige  Blockbefehle  und
zwei  Userbefehle)  anwenden  zu können,
müssen wir gleichzeitig auch einen  Puf-
fer  für  uns  vorreservieren.  Entweder
überlassen wir dabei die Wahl  des  Puf-
fers der Floppy, oder aber wir bestimmen
die  Puffernummer  selbst.  Letztes wird
wohl  nur  in  ganz  besonderen   Fällen
vonnöten  sein,  weshalb wir uns auf die
erste Methode festlegen wollen. Das Üff-
nen  des Befehlskanals mit der Zuweisung
eines Arbeitspuffers geschieht über  das
Üffnen  eines  eigenen  Datenkanals, der
für den  Datentransfer  mit  dem  Puffer
herangezogen  wird.  Dies geschieht fol-
gendermaßen:                            
OPEN 1,8,15     :REM Befehlskanal öffnen
OPEN 2,8,2,"#"  :REM Pufferkanal öffnen 
Hier öffnen wir  also  wie  gewohnt  den
Befehlskanal, dem wir die logische File-
nummer  1  zuordnen.  Der  zweite  OPEN-
Befehl  öffnet  einen  Kanal zur Floppy,
den wir zum Auslesen und Schreiben unse-
res  Datenpuffers  verwenden werden. Das
Doppelkreuz ("#") zeigt der  Floppy  an,
daß  wir  mit einem Datenpuffer arbeiten
möchten. Welche Nummer dieser  hat,  sei
der  Floppy  überlassen.  Sie  sucht nun
einfach einen  freien  Puffer  aus,  und
ordnet ihm unserem Kanal zu. Möchten wir
wissen, welcher Puffer ausgewählt wurde,
so  können  wir seine Nummer direkt nach
dem Üffnen durch Lesen des  Pufferkanals
erfahren:                               
GET#2,PN$                               
PRINT ASC(PN$)                          
Hierbei ist  es  wichtig,  daß  Sie  den
GET-Befehl   innerhalb  eines  Programms
benutzen,  da  er  vom  Direktmodus  aus
nicht verwendbar ist.                   
Wenn wir gleich einen bestimmten  Puffer
für  uns  reservieren möchten, so müssen
wir beim OPEN-Befehl einfach die Puffer-
nummer  mit  übergeben. Folgender Befehl
reserviert den Puffer Nummer 3  (Floppy-
Adressbereich $0300-$03FF) für uns:     
OPEN 2,8,2,"#3"                         
Wichtig  ist, daß wir uns die Sekundära-
dresse merken, mit der wir den Pufferka-
nal  geöffnet  hatten.  Sie  wird später
dazu verwendet, um der Floppy  mitzutei-
len auf welchem Datenkanal Sie Daten für
uns  bereitzustellen  hat,  bzw. von uns
empfangen wird (ähnlich den Befehlen für
die   RELative   Dateiverwaltung).   Wie
üblich wählen wir bei der Sekundäradres-
se eine der freien Nummern von 2-14.    
DIE BLOCKBEFEHLE                        
Nachdem wir nun einen Datenpuffer reser-
viert haben und einen Filekanal für die-
sen Puffer offen halten, können wir  uns
jetzt mit den Befehlen beschäftigen, die
wir  der  Floppy geben können. Die erste
Gruppe dieser Befehle sind die  Blockbe-
fehle.  Mit ihnen können wir die einzel-
nen Blocks einer  Diskette  "hardwaremä-
ßig" ansprechen. Aufgrund eines Blockbe-
fehls wird physisch auf den Block  zuge-
griffen  und  in  den Puffer eingelesen,
bzw. aus ihm heraus geschrieben. Für die
Beispiele innerhalb der Befehlsbeschrei-
bungen gelten die obig definierten logi-
schen Filenummern (Befehlskanal=1,  Puf-
ferkanal=2),  sowie die Sekundärdresse 2
für den Pufferkanal.                    
1) BLOCK-READ ("B-R")                   
Mit  diesem  Blockbefehl lesen wir einen
Diskettenblock  in  unseren  Datenpuffer
ein.  Hierbei  müssen wir die Sekundära-
dresse unseres Pufferkanals, die  Drive-
nummer (immer 0), den Track und den Sek-
tor als Parameter  übergeben.  Hier  die
allgemeine Syntax:                      
PRINT#BFN,"B-R";SA;DN;TR;SE             
Hierbei gilt:                           
BFN = logische Filenummer des Befehlska-
      nals                              
 SA = Sekundäradresse des Pufferkanals  
 DN = Drivenummer (immer 0)             
 TR = Tacknummer                        
 SE = Sektornummer                      
(Diese  Variablennamen  und  Abkürzungen
werde  ich  auch im Folgenden weiterver-
wenden.)                                
Mit der folgenden  Anweisung  wird  also
Sektor  1 von Track 18 in unseren Puffer
eingelesen. Von dort aus können Sie  Ihn
nun mittels einer GET-Schleife auslesen:
...                                     
100 PRINT#1,"B-R 2 0 18 1"              
110 FOR I=0 TO255                       
120 GET#2,a$: PRINT ASC(a$)             
130 NEXT                                
...                                     
2) BLOCK-WRITE ("B-W")                  
Mit  diesem  Blockbefehl  schreiben  wir
einen Block aus unserem Datenpuffer  auf
die  Diskette.  Seine  Syntax ist analog
der von "B-R", nur daß diesmal der Block
geschrieben wird:                       
PRINT#BFN,"B-W";SA;DN;TR;SE             
3) BUFFER-POINTER ("B-P")               
Für  jeden  Datenpuffer  der Floppy exi-
stiert ein Zeiger, der auf das  aktuelle
Byte im Puffer zeigt. Möchte man nun ein
ganz spezielles Byte aus dem Puffer aus-
lesen, so muß nicht unbedingt der  ganze
Puffer gelesen werden, um das gewünschte
Byte  zu  erhalten.  Sie  können mit dem
"B-P"-Befehl den Pointer  auf  selbiges 
positionieren und anschließend auslesen.
Als  Parameter benutzen Sie ganz einfach
die Sekundäradresse des Pufferkanals und
die Nummer des gewünschten  Bytes  minus
1. Möchten Sie z.B. das 200. Byte ausle-
sen, so gehen Sie bitte wiefolgt vor:   
PRINT#1,"B-P 2 199"                     
GET#2,A$: A=ASC(A$)                     
Die  zweite, oben angeführte Zeile liest
nun das 200. Byte in eine Stringvariable
und speichert es als Zahl in der  Varia-
blen "A".                               
4) BLOCK-ALLOCATE ("B-A")               
Mit  diesem  Floppybefehl können Sie ge-
zielt einzelne Disketten-Blocks als "be-
legt" kennzeichnen. Das ist dann notwen-
dig, wenn Sie Daten  mittels  "B-W"  auf
einer   Diskette  speichern,  zusätzlich
aber noch normale DOS-Files darauf able-
gen wollen. Wird ein Block nämlich nicht
als  belegt  gekennzeichnet,  so wird er
beim  Speichern  eines  DOS-Files  unter
Umständen  überschrieben.  Die  belegten
Blocks sind  in  der  "Block-Allocation-
Map"  (BAM)  einer Diskette gespeichert,
die sich in Block 18,0 befindet. Mit dem
Block-Allocate-Befehl wird hier dann der
entsprechende Block als belegt eingetra-
gen.  Mehr zum Aufbau der BAM werden wir
im nächsten Teil des Floppy-Kurses  ler-
nen.  Die  allgemeine  Syntax des "B-A"-
Befehls lautet:                         
PRINT#BFN,"B-A"; DN; TR; SE             
Möchten Sie also z.B. den  Block  5  von
Spur 34 als belegt kennzeichnen, so müs-
sen Sie folgenden Befehl benutzen:      
PRINT#1,"B-A 0 34 5"                    
5) BLOCK-FREE ("B-F")                   
Dieser  Befehl  ist  das  Gegenstück  zu
Block-Allocate.  Mit ihm geben Sie einen
belegten  Block  wieder  zur   Benutzung
frei.  Die Syntax ist dabei identisch zu
"B-A":                                  
PRINT#BFN,"B-F"; DN; TR; SE             
Um  den obig belegten Block wieder frei-
zugeben senden Sie den folgenden  Befehl
an die Floppy:                          
PRINT#1,"B-F 0 34 5"                    
6) BLOCK-EXECUTE (B-E)                  
Dieser  Befehl  ist identisch mit Block-
Read. Er beinhaltet  jedoch  zusätzlich,
daß  der Diskettenblock, der in den Puf-
fer geladen werden soll, ein  ausführba-
res  Floppy-Programm  ist,  das nach dem
Laden direkt angesprungen wird.         
DIE MEMORYBEFEHLE                       
Diese  Floppybefehle  beziehen  sich auf
die Floppy selbst und haben mit dem  Di-
rektzugriff  nur indirekt zu tun. Trotz-
dem sind sie nützlich zum Auswerten  von
Daten, weshalb sie hier aufgeführt sind:
1) MEMORY-READ ("M-R")                  
Dieser  Befehl  enstspricht  dem   PEEK-
Befehl  von  BASIC.  Mit  ihm können Sie
gezielt einzelne, oder mehrere Speicher-
adressen der Floppy auslesen. Die allge-
meine Syntax lautet dabei wiefolgt:     
PRINT#BFN,"M-R"; CHR$(LO); CHR$(HI);    
CHR$(N)                                 
Hierbei stehen "LO" und  "HI"  für  Low-
und  Highbyte  der  Adresse  die gelesen
werden soll und "N" für die Anzahl Bytes
(0-255), die ab dort  übertragen  werden
sollen.  Das  Low-  und  Highbyte  einer
Adresse ermitteln Sie mit folgenden For-
meln:                                   
HI=INT(Adresse/256)                     
LO=Adresse-256*HI                       
Alle Parameter  müssen  als  CHR$-Codes,
also dirket, übertragen werden, und dür-
fen nicht als ASCII-Codes  (wie  in  den
bisherigen  Befehlen) erscheinen. Die zu
lesenden Bytes werden ebenfalls als  ab-
soluter  CHR$-Code  auf dem BEFEHLSKANAL
(!) bereitgestellt (nicht etwa  auf  dem
Pufferkanal,  da der interne Floppyspei-
cher nichts mit Datenblocks zu tun hat).
Als Beispiel  für   die  Verwendung  von
"M-R" sei folgendes Programm aufgeführt.
Es  liest  den ASCII-Code der ID der zu-
letzt initialisierten Diskette  aus  dem
Floppyspeicher   aus.  Dieser  wird  vom
Floppy-Betriebssystem automatisch in den
Speicherstellen 18  und  19  gespeichert
(LO=18; HI=0; N=2):                     
10 OPEN 1,8,15                          
20 PRINT#1,"M-R"; CHR$(18);  CHR$(0);   
CHR$(2)                                 
30 GET#1,I1$: GET#1,I2$                 
40 CLOSE 1                              
50 PRINT "ID DER ZULETZT BENUTZTEN DIS  
KETTE: ";I1$;I2$                        
2) MEMORY-WRITE                         
Mit diesem Befehl schreiben Sie Daten in
den Floppy-Speicher.  Er  entpricht  dem
POKE-Befehl  von  BASIC. Auch hier über-
tragen Sie zunächst die Adresse in  Low-
und  Highbytedarstellung,  sowie die An-
zahl der zu schreibenden Bytes. Letztere
hieraufhin gesandt. Hier die  allgemeine
Syntax:                                 
PRINT#BFN,"M-W";   CHR$(LO);   CHR$(HI);
CHR$(N);    CHR$(W1);   CHR$(W2);   ...;
CHR$(Wn-1); CHR$(Wn)                    
3) MEMORY-EXECUTE                       
Dieser  Befehl dient dem Ausführen eines
Assembler-Programms    innerhalb     der
Floppy. Damit hat auch er einen Verwand-
ten in BASIC: den SYS-Befehl. Als  Para-
meter übergeben Sie hier einfach nur die
Startadresse  des  aufzurufenden  Assem-
bler-Programms:                         
PRINT#BFN,"M-E"; CHR$(LO); CHR$(HI)     
DIE USERBEFEHLE                         
Die  dritte  Gruppe  der  Floppy-Direkt-
Befehle sind die Userbefehle. Sie  stel-
len  eine Abkürzung von einigen Befehlen
aus den anderen beiden Gruppen dar.  Die
Userbefehle beginnen alle mit einem "U",
gefolgt von einer Zahl zwischen 1 und 9.
Die  ersten beiden Userbefehle sind wohl
die wichtigsten:                        
1) "U1"                                 
Der  U1-Befehl  ersetzt  den Block-Read-
Befehl. In der Praxis wird er öfter ver-
wendet, da er gegenüber "B-R" einen Vor-
teil bietet. Wird mit  "B-R"  ein  Block
eingelesen  und  anschließend der Inhalt
des Puffers vom 64er aus ausgelesen,  so
wird  das  erste  Byte nicht mitgesandt.
Möchten wir dieses nun aber auch  lesen,
so  müssen wir den "U1"-Befehl benutzen.
Er sendet es zusätzlich mit - im Prinzip
ist der U1-Befehl also besser als "B-R".
Die Syntax ist  identisch  mit  der  von
"B-R":                                  
PRINT#BFN,"U1"; SA; DN; TR; SE          
Ansonsten  verhält  sich  alles  so, wie
auch bei "B-R".                         
2) "U2"                                 
Der  zweite Userbefehl ist der U2-Befehl
(Ähnlichkeiten mit dem Namen  einer  be-
kannten  Rock-Band  sind rein zufällig).
Er ersetzt den "B-W" Befehl.  Auch  hier
ist  der  Vorteil des ersten Bytes gege-
ben. Sie benutzen den U2-Befehl, wie den
"B-W"-Befehl:                           
PRINT#LFB,"U2"; SA; DN; TR; SE          
3) "U3-U8"                              
Diese  sechs  Userbefehle  ersetzen ganz
bestimmte  "M-E"-Kommandos.  Wird  einer
dieser  Befehle  gesandt, so springt die
Floppy in eine Sprungtabelle ab  Adresse
$0500  und  führt das dort stehende Pro-
gramm aus. In der Regel besteht selbiges
aus  einer  Reihe   von   "JMP   $XXXX"-
Anweisungen,  mit denen Sie nun auf ein-
fache Weise, vom 64er  aus,  selbstdefi-
nierte  Programme in der Floppy aktivie-
ren können. Die Userbefehle 3-8  benöti-
gen  keine  Parameter. Bei Benutzung der
Befehle werden folgende Adressen jeweils
angesprungen:                           
Befehl  Springt an Adresse              
--------------------------              
   U3       $0500                       
   U4       $0503                       
   U5       $0506                       
   U6       $0509                       
   U7       $050C                       
   U8       $050F                       
Damit  ersetzen  diese  Userbefehle also
"M-E"-Befehle wie z.B.:                 
PRINT#BFN,"M-E";CHR$(9);CHR$(5);        
Wenn Sie den obigen Befehl abkürzen wol-
len, so genügt auch ein einfaches:      
PRINT#BFN,"U6"                          
4) "U9"                                 
Der "U9"-Befehl wurde eingebaut, um  die
Kompatibilität  der  1541  zum VC20, dem
Vorgänger des C64, zu  wahren.  Mit  ihm
können  Sie  die Floppy in den Modus des
entsprechenden Rechners  schalten.  Dies
geschieht  über  folgende  Kombinationen
des "U9"-Befehls:                       
PRINT#BFN,"U9+"; schaltet in C64-Modus  
PRINT#BFN,"U9-"; schaltet in VC20-Modus 
5) "U:"                                 
Mit  diesem  Userbefehl  lösen Sie einen
RESET in der Floppy aus.  Das  Floppysy-
stem  wird dann wieder in den Einschalt-
zustand zurückversetzt. Der  "U:"-Befehl
entspricht  einem  "SYS 64738" beim C64.
Auch er benötigt keine Parameter.       
Das war es dann wieder für diese  Ausga-
be.  In  der  nächsten Magic-Disk werden
wir uns dann mit dem Aufbau von  Files, 
der BAM und des Directorys beschäftigen.
In  diesem  Zusammenhang werden wir dann
die  hier  erlernten  Floppy-Befehle  in
praktische Beispiele umsetzen.          
                                    (ub)
----------------------------------------
               FLOPPY-KURS              
      "Es rappelt in der Kiste..."      
                (Teil 4)                
----------------------------------------
Hallo und herzlich willkommen zum  vier-
ten  Teil unseres Floppy-Kurses. Nachdem
wir das letzte Mal die Direktzugriffsbe-
fehle  kennengelernt  haben,  wollen wir
uns dieses Mal mit der Struktur auf  der
Diskette  beschäftigen. Dabei werden wir
lernen, wie wir einzelne Diskettenblocks
so manipulieren, daß  zum  Beispiel  der
Typ  eines  Files geändert ist, ein File
nicht mehr gelöscht werden kann, etc.   
WAS STEHT WO?                           
Sicherlich erinnern Sie sich noch daran,
daß die 1541 eine Diskette in 35  Spuren
zu  je 17-21 Sektoren aufteilt. Wir wol-
len uns nun anschauen, wie das  DOS  (so
nennt man das Betriebssystem der Floppy)
nun Daten auf diesen Blocks ablegt.     
Dabei müssen wir  gleich  einmal  unter-
scheiden  zwischen  DOS-internen Verwal-
tungsblöcken und den  reinen  Datenblök-
ken.  In  ersteren  stehen Informationen
wie z.B. der Name der Diskette, ihre ID,
welche Blocks belegt  sind,  und  welche
nicht,  die  Directoryeinträge, etc. Den
Verwaltungsblöcken  ist   ausschließlich
der  gesamte  18.  Track  vorreserviert.
Datenblöcke  weden  nie  dort  abgelegt,
dazu  dienen  jedoch  dann  alle anderen
Tracks (1-17 und 19-35).                
DIE DATENBLÜCKE                         
Zunächst wollen wir uns mit der Struktur
eines  Datenblocks  beschäftigen.  Diese
ist  die einfachere, und demnach schnel-
ler erklärt. Dazu  will  ich  Ihnen  nun
zunächst  einmal beschreiben, was in der
Floppy vorgeht, wenn ein  File  auf  der
Diskette gespeichert wird.              
Zu  aller erst erhält die Floppy vom C64
die Meldung, daß nun ein  Schreibzugriff
erfolgen  soll.  Gleichzeitig mit dieser
Information wird  der  Filename  des  zu
schreibenden  Files,  sowie sein Filetyp
übertragen. Die Floppy schaut nun in den
Blocks, die  für  die  Directoryeinträge
definiert sind nach, wo ein Eintrag noch
frei  ist, oder ob ein neuer hinzugefügt
werden soll. Warum das  so  ist,  wollen
wir  später  bei  den  Verwaltungsblocks
klären. Nehmen wir also  einfach  einmal
an, daß Platz für einen Eintrag gefunden
wurde,  und  daß der angegebene Filename
noch nicht auf  der  Diskette  existiert
(dies  wird  nämlich  ebenfalls  festge-
stellt und bei Zutreffen  wird  mit  der
Fehlermeldung   "File  Exists"  abgebro-
chen). Das DOS trägt nun im freien  Ein-
trag den Namen und Typ des zu speichern-
den Files  ein.  Anschließend  macht  es
sich  auf  die  Suche  nach einem freien
Block in den Tracks 1-17, bzw. 19-35, in
dem Sie die ersten Daten des Files able-
gen  kann. Wurde einer gefunden, so wird
seine Track und  Sektornummer  ebenfalls
im  Directoryeintrag gespeichert. Im an-
dern Fall wird  mit  der  Meldung  "Disk
Full" abgebrochen. Nun wird damit begon-
nen, die zu schreibenden Daten vom Rech-
ner  zu  empfangen  und zu speichern. Da
ein Diskettenblock nur  256  Bytes  lang
ist,  und  zu  speichernde  Daten in der
Regel ein größeres Volumen haben,  sucht
sich  die  Floppy  jedesmal wieder einen
neuen  freien  Block,  wenn  der  letzte
vollgeschrieben ist. Diese Blocks werden
anschließend als 'belegt' gekennzeichnet
und auf der Diskette vermerkt.          
Nun fragen Sie sich bestimmt schon,  wie
das  DOS  später  wieder erkennt, welche
Blocks zu welchem File  gehören  und  in
welcher Reihenfolge diese geladen werden
sollen. Das ist nun ganz einfach gelöst.
Wie  ich  oben  schon  erwähnte kann ein
Datenblock 256 Bytes fassen.  Als  reine
Datenbytes  werden  davon jedoch nur 254
genutzt. Die ersten zwei Bytes sind näm-
lich  für  die  Folgeblocknummer  reser-
viert. Im ersten Byte steht  der  Track,
im   zweiten  der  Sektor  des  nächsten
Blocks, der zu diesem  File  gehört.  In
diesem  ist  dann  wiederum  der nächste
Block vermerkt und so weiter. Um festzu-
stellen,  wann ein File aufhört, enthält
der letzte  Block  als  Tracknummer  den
Wert 0. Im Byte für die Sektornummer ist
nun  ein  Wert enthalten, der die Anzahl
der  in  diesem  Block  benutzten  Bytes
kennzeichnet,  da am Ende, je nach Länge
der gespeicherten Daten, mit hoher Wahr-
scheinlichkeit nicht alle Datenbytes des
Blocks für das File benötigt werden.    
Dieser  Aufbau  gilt  übrigens  für alle
Filetypen. Ausnahmen  bestätigen  jedoch
die  Regel, und so ist es auch in diesem
Fall. Relative Datenfiles benutzen  eine
andere  Datenstruktur,  was in Ihrer be-
sonderen  Funktion  begründet  ist.  Sie
benutzen sogenannte "Side-Sector-Blocks"
um  ihren  relativen Aufbau verwalten zu
können. Da die Manipulation an relativen
Files jedoch wenig ratsam ist,  und  nur
selten  einen Sinn macht, wollen wir den
Aufbau von Side-Sektor-Blöcken wegfallen
lassen.                                 
DIE DIRECTORY-BLOCKS                    
Nun wissen wir also,  auf  welche  Weise
Datenblocks  miteinander verkettet sind.
Was uns nun interessieren soll, ist  die
Art  und Weise, mit der das DOS nun eine
Diskette verwaltet. Im Prinzip haben wir
das in obigem Beispiel auch schon  ange-
sprochen,  nur wie funktioniert z.B. das
Heraussuchen eines freien  Directoryein-
trags, oder freien Blocks?              
Nun, wie schon erwähnt, ist der Track 18
ausschließlich dem DOS  vorbehalten.  Im
Prinzip  ist  er komplett für das Direc-
tory  zuständig,  wobei  hier  natürlich
auch  Informationen  enthalten sind, die
wir nicht beim üblichen LOAD"$",8  ange-
zeigt  bekommen.  Außerdem  ist in einem
ganz  speziellen  Block  die  sogenannte
"BAM"  abgelegt,  was für "Block Availa-
billity Map" steht.  Öbersetzt  bedeutet
das nichts anderes als "Block Verfügbar-
keits Karte",  bzw.  Liste.  Hier  steht
eingetragen,  welche Blocks der Diskette
schon belegt sind, und welche nicht.    
DER DISK-HEADER-BLOCK                   
Den Block, in dem die BAM enthalten ist,
nennt man  "Disk-Header-Block".  Er  ist
der wichtigste Block auf der ganzen Dis-
kette, da nur an ihm das DOS  eine  Dis-
kette  überhaupt  erkennt und bearbeiten
kann. Er  liegt  im  Sektor  0  des  18.
Tracks.  Außer  der  BAM  sind hier noch
viele andere Informationen  gespeichert,
wie  z.B.  Diskettenname und -ID. Wollen
wir uns nun eine  Liste  anschauen,  aus
der Sie ersehen können, welche Bytes des
Disk-Header-Blocks  welche Informationen
enthalten:                              
Byte    Aufgabe                         
----------------------------------------
000      Hier ist  die  Tracknummer  des
         ersten  Directoryblocks enthal-
         ten (normalerweise 18).        
001      Hier steht die Sektornummer des
         ersten  Diretoryblocks  (norma-
         lerweise 1)                    
002      Hier    steht     das     1541-
         Formatkenn-zeichen.    Selbiges
         entspricht  dem   ASCII-Zeichen
         "A" (Code: 65 = $41).          
003      Dieses  Byte  kennzeichnet,  ob
         die eingelegte Diskette doppel-
         seitig  formatiert  ist   (dann
         steht hier der Wert 1). Das ist
         wichtig  für die Benutzer einer
         1571-Floppy, die  in  der  Lage
         ist,  Disketten  beidseitig  zu
         beschreiben. Die 1541  beachtet
         dieses Byte nicht.             
004-007  In  diesen  4  Bytes   ist  die
         Blockbelegung des ersten Tracks
         vermerkt.                      
008-139  Blockbelegungsbytes   für   die
         Tracks 2-34.                   
140-143  Hier  steht  die  Blockbelegung
         des letzten, 35. Tracks.       
144-159  Hier steht  der  Diskettenname,
         der  bei der Formatierung ange-
         geben wurde, und  zwar  im  AS-
         CII-Code.  Ist  ein Name kürzer
         als 16 Zeichen, so  werden  die
         restlichen  Bytes  mit dem Wert
         160 aufgefüllt.                
160-161  Hier  steht  zweimal  der  Wert
         160, was  übrigens  dem  ASCII-
         Zeichen    'SHIFT-SPACE'   ent-
         spricht.                       
162-163  Hier  ist  die  zweistellige ID
         der Diskette vermerkt.         
164      Wert 160.                      
165-166  Formatangabe der Diskette. Die-
         se ist bei 1541-Disketten immer
         "2A". Sie sehen sie im normalen
         Directory,    daß    Sie    mit
         LOAD"$",8 geladen haben,  immer
         am  Ende  der  ersten Zeile, in
         der auch Diskname  und  -ID  zu
         finden sind.                   
167-170  Gefüllt mit 'SHIFT-SPACE' (Wert
         160).                          
171-255  Unbenutzer Bereich. Gefüllt mit
         Nullen.                        
Wie Sie sehen kann hier schon eine Fülle
an   Informationen   abgelesen   werden.
Zunächst einmal sehen wir hier, bei wel-
chem Block das  Directory  beginnt.  Das
ist  in der Regel Sektor 1 von Track 18.
Dieser Wert könnte jedoch mit Hilfe  ei-
nes  Diskmonitors,  oder  eines  kleinen
Block-Lese-und-Schreibprogramms beliebig
geändert werden.                        
Das Formatkennzeichen ist bei der Floppy
1541  das  "A". Es zeigt dem DOS an, daß
dies eine von  einer  1541  (oder  einem
kompatiblen  Laufwerk,  z.B. 1570, 1571)
formatierte Diskette ist. Dieses Format-
kennzeichen  wurde  deshalb  eingeführt,
weil Commodore  für  ältere  Bürorechner
ebenfalls  eigene  Laufwerke gebaut hat,
die wiederum eine andere Diskettenstruk-
tur aufweisen (z.B. mehr Tracks und Sek-
toren). Disketten von solchen Laufwerken
können von der 1541 zwar teilweise gele-
sen, nicht aber beschrieben werden. Des-
halb  verweigert  die  Floppy  auch  den
Schreibzugriff auf Disketten mit anderem
Formatkenneichen.  Dies  kann  man  sich
aber auch  zunutze  machen:  ändert  man
nämlich  den  Wert des 3. Bytes, so kann
man    eine    Diskette    softwaremäßig
schreibschützen. Ein Speichern, Löschen,
oder  Softformatieren ist nun nicht mehr
möglich. Wohl aber kann von der Diskette
geladen werden.                         
Das 4. Byte ist für  die  1541  nutzlos.
Hier  steht  in  der  Regel  eine 0 (nur
sinnvoll in  Verwendung  von  Laufwerken
mit 2 Schreib/Leseköpfen).              
Wie Sie weiterhin aus obiger  Liste  er-
kennen  können,  sind  nun  jeweils vier
Bytes für die Blockbelegung eines  jeden
der  35  Tracks  reserviert. Die Bytes 4
bis 143 enthalten die oben  schon  ange-
sprochene BAM. Auf sie werden wir gleich
zurückkommen.                           
Es  folgen zum Schluß noch zwei Informa-
tionen über die Diskette. Zum einen  der
16  Zeichen lange Diskettenname, zum an-
deren die ID, gefolgt von einem  'SHIFT-
SPACE'  und der DOS-Versionsnummer (nor-
malerweise "2A"). Auch diese beiden  Be-
reiche  können verändert werden. Zusätz-
lich sollten Sie wissen, daß  Sie  nicht
nur  die  ID,  sondern  auch  das SHIFT-
SPACE-Zeichen, sowie das "2A"  verändern
können.  Dadurch hat man die Möglichkeit
eine   5-stellige   "ID"    herzustellen
(natürlich  zählen für das DOS immer nur
noch die  ersten  beiden  Zeichen).  Auf
diese Art und Weise ist es z.B. Möglich,
daß  die  Diskettenseiten der Magic-Disk
die IDs "SIDE1" und "SIDE2" haben (haben
Sie einmal darauf geachtet?).           
DIE BAM                                 
Die  BAM  verwaltet,  wie schon erwähnt,
die freien  und  belegten  Blocks  einer
Diskette.  140  Bytes  sind  für  sie im
Disk-Header-Block reserviert. Wir  haben
ebenso  gelernt,  daß jeweils vier Bytes
die Blockbelegungen eines Tracks  kodie-
ren.  Wollen  wir uns dies einmal anhand
des  ersten  Tracks  betrachten.  Dieser
verfügt   ja  über  21  Sektoren.  Seine
"BAM-Bytes" sind in den Bytes  4  bis  7
des Disk-Header-Blocks (18/0) zu finden.
Im ersten Byte dieser vier Bytes ist nun
die Anzahl der freien Blöcke auf Track 1
vermerkt.  In  den  folgenden drei Bytes
sind nun die Zustände  der  Blocks  0-20
bitweise  kodiert.  Ist ein Bit in einem
der drei Bytes  gesetzt,  so  entspricht
das  der Information, daß der zugehörige
Block frei ist. Ist es gelöscht, so  ist
der  Block  belegt.  Die  Verteilung der
Sektoren auf die drei  Bytes  sieht  nun
folgendermaßen  aus.  Die  Bits  7-0 (in
dieser Reihenfolge!)  des  ersten  Bytes
geben  die Belegung der Sektoren 0-7 an.
Die Bits 7-0 des  zweiten  Bytes  zeigen
den Status für die Blocks 8-15. Die Bits
7-3  des  dritten  Bytes  stehen für die
Blocks 16-20. Bits 2-0 sind  hier  unbe-
legt.  Zur besseren Öbersicht hier noch-
mal  eine  Tabelle   ("DHB"=Disk-Header-
Block):                                 
Byte 1 (für Track 1, Byte 5 des DHB)    
--------------------------------------  
Bit      7   6   5   4   3   2   1   0  
Sektor  00  01  02  03  04  05  06  07  
--------------------------------------  
Byte 2 (für Track 1, Byte 6 des DHB)    
--------------------------------------  
Bit      7   6   5   4   3   2   1   0  
Sektor  08  09  10  11  12  13  14  15  
--------------------------------------  
Byte 3 (für Track 1, Byte 7 des DHB)    
--------------------------------------  
Bit      7   6   5   4   3   2   1   0  
Sektor  16  17  18  19  20  --  --  --  
--------------------------------------  
Alle gesetzten Bits (=freier Block) die-
ser drei Bytes werden nun gezählt und in
das 0. Byte der entsprechenden Track-BAM
(für Track 1, Byte 4 des DHB), eingetra-
gen.  So  baut  sich nun die gesamte BAM
auf, wobei sich in Byte 3  die  Belegung
natürlich  ändert,  da es ja auch Tracks
gibt, die nur 19, 18  oder  17  Sektoren
haben.  In  letztem  Fall  sind dann nur
noch das 7. und 6. Bit von  Byte  3  be-
nutzt.                                  
DIE DIRECTORY-BLOCKS                    
Kommen wir nun zu  den  Directoryblocks.
In  ihnen sind die auf der Diskette ent-
haltenen Filenamen und deren Typ gespei-
chert.  In einen Directoryblock passen 8
Fileeinträge. Sind alle  Einträge  voll,
so  wird  ein neuer Directoryblock ange-
legt. Track und Sektor des erste  Direc-
toryblocks  können  aus dem Disk-Header-
Block  entnommen  werden.  Normalerweise
ist  das  Block 18/1. Kommen wir nun zum
Aufbau eines Directoryblocks.           
Zunächst einmal sind diese Blocks ebenso
wie  normale Datenblocks über die Angabe
des jeweils nächsten Blocks  miteinander
verkettet.  Deshalb stehen in den ersten
beiden Bytes jeweils Track und Sektor   
des nächsten Directoryblocks. Der Letzte
ist mit dem Wert 0 als  Track-  und  dem
Wert 255 als Sektornummer markiert. Hier
nun eine Öbersicht mit der genauen Bele-
gung eines Directoryblocks:             
ByteNr.  Aufgabe                        
---------------------------------       
000      Track des Folgeblocks          
001      Sektor des Folgeblocks         
002-033  Fileeintrag Nr. 1              
034-065  Fileeintrag Nr. 2              
066-097  Fileeintrag Nr. 3              
098-129  Fileeintrag Nr. 4              
130-161  Fileeintrag Nr. 5              
162-193  Fileeintrag Nr. 6              
194-225  Fileeintrag Nr. 7              
226-255  Fileeintrag Nr. 8              
Jeder  einzelne  Eintrag  belegt also 32
Bytes im Directoryblock.  Ich  muß  dies
allerdings  korrigieren. Im Prinzip wer-
den immer nur die ersten  30  Bytes  be-
nutzt.  Die  letzten  beiden Bytes eines
Eintrags sind immer unbenutzt. Daher ist
Eintrag Nr. 8 auch nur  30  Bytes  lang.
Die 2 unbenutzten Bytes fehlen hier ein-
fach, was auch keinen Unterschied macht.
Nun  wollen  wir  uns  einmal anschauen,
welche Informationen  in  den  30  Bytes
eines File-Eintrags zu finden sind. Auch
hier  will ich Ihnen eine Tabelle geben,
damit  die   Öbersichtlichkeit   gewahrt
bleibt:                                 
000      Byte für den Filetyp.          
001-002  Track  und  Sektor  des  ersten
         Datenblocks dieses Files.      
003-018  16 Bytes für den Filenamen.    
019-020  Bei relativen Files stehen hier
         Track  und  Sektor  des  ersten
         Side-Sektor-Blocks (sonst unbe-
         nutzt).                        
021      Bei relativen Files  seht  hier
         die   Länge  eines  Datensatzes
         (sonst unbenutzt).             
022-025  unbenutzt                      
026-027  Zwischenspeicher des Tracks und
         Sektors beim Öberschreiben  mit
         dem  Klammeraffen (" ") vor dem
         Filenamen.  Dies  ist  nur  ein
         temporärer  Speicher. Normaler-
         weise werden diese Bytes  nicht
         beschrieben.                   
028-029  Hier  steht die Länge des Files
         in  Blocks  als   16-Bit-Lo/Hi-
         Zahl.                          
In Byte 0 ist der Filetyp kodiert.  Des-
weiteren  finden sich hier auch Informa-
tionen  darüber,  ob  das  entsprechende
File ordnungsgemäß geschlossen wurde und
ob  es schreibgeschützt ist. In ersterem
Fall ist das File im normalen  Directory
mit  einem  "*"  markiert. Das zeigt an,
daß das File ungültig ist. Ein File  ist
z.B.  ungültig, wenn Sie versuchen etwas
auf eine volle  Diskette  zu  speichern.
Die   Floppy  erkennt  nun  während  des
Schreibens, das kein Platz mehr  vorhan-
den ist, und bricht ab. Zuletzt wird das
fehlerhafte   File   noch  als  ungültig
erklärt, damit man es nicht  mehr  laden
kann.  Mit  einem Validate-Befehl an die
Floppy wird ein solches  File  dann  aus
dem Directory entfernt.                 
Ein schreibgeschütztes File  kann  nicht
mehr überschrieben oder gelöscht werden,
solange es geschützt ist. Dadurch können
Sie  das  ungewollte  Löschen  von Daten
verhindern. Ein so geschütztes File  ist
im  Directory  mit einer spitzen Klammer
nach links ("<") markiert. Wenn Sie sich
einmal das  Inhaltsverzeichnis  der  Ma-
gic-Disk  anschauen,  so werden Sie dort
mehrere  Trennstrich-Files  finden,  die
schreibgeschützt sind.                  
Im  Filetyp-Byte  werden  die  einzelnen
Informationen  nun  folgendermaßen   co-
diert:  Ist  Bit  7  gesetzt, so ist ein
File gültig, es wurde ordnungsgemäß  ge-
schlossen  und kann geladen werden. Wenn
Bit 6  gesetzt  ist,  so  ist  das  File
schreibgeschützt  (normalerweise  ist es
gelöscht, also ungeschützt). Die Bits  0
bis 2 enthalten die möglichen Filecodie-
rungen. Werte von 0 bis 4 sind sinnvoll.
Bitte Teil 2 des Floppy-Kurses laden!!! 
Deren Bedeutung ist folgendermaßen  auf-
geteilt:                                
Bits2-0  Wert  Filetyp                  
----------------------------------------
  000     0    DEL (deleted)            
  001     1    SEQ (sequentielle Datei) 
  010     2    PRG (Programmdatei)      
  011     3    USR (Userdatei)          
  100     4    REL (relative Datei)     
Wie Sie sehen, können aber  noch  andere
Werte  eingetragen werden, die zwar kei-
nen Filetyp spezifizieren, aber  dennoch
möglich  sind.  Dies  hat zur Folge, daß
die Files ganz merkwürdige Bezeichnungen
erhalten. Wird zum Beispiel der Wert  15
(Bit  3  auch noch mitbenutzt) eingetra-
gen, so  hat  ein File  die  Bezeichnung
"?  ".                                  
Alle  anderen Bits des Filetyp-Bytes ei-
nes Directoryeintrags sind unbenutzt.   
Ebenfalls interessant sind das  28.  und
das  29.  Byte eines Fileeintrags. Damit
können Sie nämlich ebenfalls einen klei-
nen  Effekt für den Directoryeintrag er-
zielen. Da hier die Länge des  Files  in
Blocks  angegeben  ist,  kann diese auch
verändert  werden.  Dies  hat  keinerlei
Einfluß  auf  das  Laden  oder Speichern
eines Files, da die Blockangabe vom  DOS
nur  dazu verwendet wird, um beim Ausge-
ben eines Directorys die  Filelänge  di-
rekt  greifbar  zu  haben.  Wenn Sie nun
jemanden verblüffen wollen, so kann hier
z.B. der Wert 0 eingetragen werden. Laut
Directory befindet sich in dem File dann
nichts. Trotzdem kann sich dahinter  ein
Wahnsinns-Programm verstecken.          
Einen änhlichen Effekt können  Sie  auch
durch  Manipulation  der  BAM  erzielen.
Schreiben Sie hier nämlich andere  Werte
in  die  jeweils 0. Bytes der Trackbele-
gungen, so erhalten Sie eine andere  An-
zahl  an  freien Blocks. Das DOS addiert
nämlich diese 35 Bytes auf, um die  ver-
bleibende    Speicherkapazität    ("Free
Blocks") einer Diskette zu ermitteln und
im Directory anzeigen zu können.  Achten
Sie  danach jedoch bitte darauf, daß Sie
nichts mehr  auf  eine  solche  Diskette
speichern,  da nun nämlich vermeintliche
freie Blöcke beschrieben werden könnten,
was zu erheblichem  Datenverlust  führen
kann.  Am  Besten  ist  es  dann,  einen
Schreibzugriff durch Ändern des  Format-
kennzeichens   zu  verhindern.  Öbrigens
sollte ich erwähnen, daß  Änderungen  an
der  BAM  jederzeit  durch den Validate-
Befehl der Floppy wieder rückgängig  ge-
macht  werden  können. Dieses Floppykom-
mando geht nämlich nach  und  nach  alle
Files  der Diskette durch und merkt sich
die jeweils  belegten  Blocks.  Hiernach
kann die ursrüngliche BAM wieder rekons-
truiert werden.                         
Und noch etwas  ist  zu  den  Directory-
einträgen  zu  sagen.  Wenn Sie ein File
löschen, so wird es keineswegs  komplett
von  der Diskette entfernt. Das einzige,
was der Scratch-Befehl bewirkt, ist  das
Freigeben  der belegten Diskettenblocks.
Desweiteren wird als Filetyp der Wert  0
eingetragen,  was dem Filetyp "DEL" ent-
spricht. Gleichzeitig ist das  File  als
'ungültig'  markiert  und nicht schreib-
geschützt (wieso auch). Ein solcher  Fi-
leeintrag  kann  jederzeit  wiederherge-
stellt werden, indem man einen  definer-
ten,  gültigen, Filetyp einträgt und an-
schließend die Diskette  validiert.  Nun
ist  das  File  wieder so vorhanden, wie
vor dem Löschen (inklusive des  Vermeks,
welche Blocks belegt sind).             
Wird jedoch zwischenzeitlich  etwas  auf
der  Diskette gespeichert, so sucht sich
das DOS  zunächst  immer  einen  solchen
'leeren'  Fileeintrag  heraus,  bevor es
einen neuen Eintrag  im  letzten  Direc-
toryblock  anlegt.  Dadurch erklärt sich
auch,  warum  neu   geschriebene   Files
manchmal  mitten  im Directory zu finden
sind und nicht am Ende. Hier wurde  vor-
her  an  dieser  Stelle  ein  altes File
gelöscht.                               
Dies soll es dann wieder einmal  gewesen
sein für diesen Monat. Wenn Sie Ihre neu
hinzugewonnenen  Kenntnisse  einmal aus-
probieren möchten, so  nehmen  Sie  sich
einfach  einen  Diskmonitor zur Hand und
versuchen Sie einmal  einige  Directory-
einträge  oder  den Disk-Header-Block zu
verändern. Weiterhin kann mit  dem  Pro-
gramm  "Disk-Manager"  auf dieser MD das
Directory noch weitaus komfortabler  ma-
nipuliert  werden.  Nun  wissen  Sie  ja
wahrscheinlich  auch,  was   dabei   ge-
schieht.                                
Im nächsten Teil des Floppykurses wollen
wir  uns  mit der Floppybedienung in As-
sembler beschäftigen. Dabei  werden  wir
dann  auch  einige  Programme erstellen,
die sich die hier  kennengelerntern  In-
formationen zunutze machen.             
                                    (ub)
----------------------------------------
               Floppy-Kurs              
      "Es rappelt in der Kiste..."      
                (Teil 5)                
----------------------------------------
Herzlich  Willkommen zum 5. Teil unseres
Floppykurses. In diesem Monat wollen wir
die Kenntnisse, die wir bisher über  die
Floppy  erfahren haben in die Praxis um-
setzen. Das heißt,  daß  wir  in  diesem
Kursteil  einige  nützliche  Anwendungen
zum bisher Erlernten programmieren  wer-
den.  Diese werden zunächst in BASIC be-
handelt. Im nächsten Kursteil wollen wir
dann auf die Programmierung in Assembler
eingehen.                               
1) DISKETTE SCHREIBSCHÖTZEN             
Kommen wir zu unserem  ersten  Programm-
beispiel. Wie ich im dritten Teil dieses
Kurses  schon  erwähnte ist es ganz ein-
fach möglich, eine Diskette  softwaremä-
ßig  schreibzuschützen.  Sie  können an-
schließend weder etwas auf die  Diskette
schreiben,  noch  etwas von ihr löschen.
Das Laden von Programmen  funktiert  je-
doch  nach  wie vor. Einzige Möglichkeit
die Diskette wieder beschreibbar zu  ma-
chen  ist  dann  das  Formatieren  (oder
nochmaliges Behandeln mit  unserem  Pro-
gramm).                                 
Wie  realisieren  wir  nun einen solchen
Schreibschutz? Nun, wie wir mittlerweile
ja wissen, legt die Floppy  beim  Forma-
tieren einer Diskette im Diskheaderblock
einige  wichtige  Informationen über die
Diskette ab. Hier  steht  unter  anderem
auch  das Formatkennzeichen, das bei der
1541 den Buchstaben "A" darstellt. Ande-
re  Commodorelaufwerke  (z.B.  das eines
alten CBM4040) haben  dort  ein  anderes
Zeichen  stehen.  Da  Commodore  bei den
alten Laufwerkstypen ähnliche  Aufzeich-
nugsformate benutzte, sind die Disketten
von  Commodorelaufwerken bis zu gewissen
Grenzen untereinander kompatibel.  Meist
unterscheiden  sie  sich nur in der Auf-
zeichnungsdichte (mehr Tracks auf  einer
Diskette).  Um  nun  zu  vermeiden,  das
Laufwerke von verschiedenen Rechnern die
Daten einer Diskette eines anderen Lauf-
werks zerstören, wurde eine Sicherung in
die  1541 eingebaut. Sie beschreibt aus-
schließlich nur Disketten, die  ein  "A"
als   Formatkennzeichen  tragen.  Andere
werden nicht verändert, wohl  aber  kann
von  Ihnen gelesen werden. Es genügt nun
also, das Formatkennzeichen in ein ande-
res  Zeichen  umzuwandeln,  und die 1541
wird keine Daten mehr darauf  schreiben.
Dies  geschieht im Prinzip ganz einfach.
Wir müssen lediglich den Diskheaderblock
einlesen, das 3. Byte (hier  steht  näm-
lich das Formatkennzeichen) in ein ande-
res Zeichen als "A"  abwandeln  und  den
Block  wieder  auf  die Diskette zurück-
schreiben. Abschließend muß die Diskette
noch  initialisiert  werden,  damit  das
Betriebssystem  der Floppy, das DOS, die
neue Formatkennung auch in  den  Floppy-
speicher übernimmt.                     
Ein  Programm,  das  eine  Diskette  mit
Schreibschutz versieht  und  ihn  wieder
entfernt  finden Sie auf dieser MD unter
dem Namen "FK.SCHREIBSCHUTZ". Die Unter-
routine, mit der eine Diskette geschützt
wird,  steht  in  den Zeilen 400 bis 470
dieses Programms. Hier sind sie nochmals
aufgelistet:                            
Unterroutine "Diskette sichern":        
400 OPEN 1,8,15,"I": OPEN 2,8,2,"#"     
410 PRINT#1,"U1 2 0 18 0"               
430 PRINT#1,"B-P 2 2"                   
440 PRINT#2,"B";                        
450 PRINT#1,"U2 2 0 18 0"               
460 PRINT#1,"I"                         
470 CLOSE1:CLOSE2:RETURN                
Zunächst  einmal öffnen wir hier den Be-
fehlskanal  und  übergeben  gleichzeitig
einen Initialisierungsbefehl an ihn. Mit
letzterem  werden  alle  Grunddaten  der
eingelegten Diskette (z.B.  ID,  Format-
kennung,   etc.)  in  den  Speicher  der
Floppy übertragen. Man sollte vor  einer
direkten  Manipulation  des  Diskheader-
blocks diesen Befehl aufrufen. Desweite-
ren öffnen wir einen Pufferkanal mit der
Kanalnummer  2  (also Sekundäradresse 2)
um Blockoperationen durchführen zu  kön-
nen.  In  Zeile  410 wird nun Spur 0 von
Track 18 in den Puffer, dem Kanal 2  zu-
geordnet  ist,  eingelesen. Dies tun wir
mit dem "U1"-Befehl, der besser ist  als
"Block-Read" ("B-R"). Hiernach folgt die
Positionierung des Pufferzeigers auf das
2.  Byte (mit dem 0. Byte eigentlich das
dritte Byte) des Puffers. In  Zeile  440
wird  nun der Buchstabe "B" an diese Po-
sition geschrieben. Wichtig ist  hierbei
das  Semikolon  (";")  nach  dem  PRINT-
Befehl, da letzterer ohne Semikolon noch
ein CHR$(13) nachsenden würde, womit wir
einen Teil der BAM überschreiben würden!
Nun muß der geänderte Block nur noch mit
Hilfe  des "U2"-Befehls auf die Diskette
zurückgeschrieben werden.  Damit  unsere
Änderung   auch  in  den  Floppyspeicher
übernommen  wird  muß  abschließend  die
Diskette wieder initialisiert werden. In
Zeile  470  werden die beiden Filekanäle
wieder geschlossen und zum Hauptprogramm
zurückgekehrt.                          
Nun  soll  unser Programm den Disketten-
schutz ja auch wieder rückgängig  machen
können. Dies geschieht im Prinzip wieder
genauso. Wir müssen lediglich die fremde
Formatkennung  wieder  in ein "A" umwan-
deln. Jedoch  ergibt  sich  hierbei  ein
kleineres Problem. Dadurch, daß die Dis-
kette ja  schreibgeschützt  ist,  können
wir  den  modifizierten  Disk-Header  ja
nicht  mehr  auf  die  Diskette  zurück-
schreiben!  Hier  behilft  man  sich mit
einem kleinen Trick.  In  der  Speicher-
stelle  $0101 (dez. 257) des Floppy-RAMs
"merkt" sich das DOS das  Formatkennzei-
chen  der  eingelegten Diskette. Es wird
bei jedem Initialisierungsbefehl  wieder
aktualisiert. Ändern wir nun diese Spei-
cherstelle so um, daß in  ihr  der  Wert
"A" steht, so können wir dem DOS vorgau-
keln, es hätte eine Diskette mit korrek-
ter  Formatkennung  vor sich. Erst jetzt
ist es wieder möglich auf  die  Diskette
zu schreiben. Auch hier möchte ich Ihnen
die Unterroutine, die entsprechendes tut
auflisten.  Sie  steht im oben erwähnten
Programm in den Zeilen 300-370:         
Unterroutine "Diskette entsichern":     
300 OPEN 1,8,15,"I": OPEN 2,8,2,"#"     
310 PRINT#1,"U1 2 0 18 0"               
320 PRINT#1,"M-W";CHR$(1);CHR$(1);      
    CHR$(1);CHR$(65);                   
330 PRINT#1,"B-P 2 2"                   
340 PRINT#2,"A";                        
350 PRINT#1,"U2 2 0 18 0"               
360 PRINT#1,"I"                         
370 CLOSE1:CLOSE2:RETURN                
In  den  Zeilen  300 und 310 öffnen wir,
wie gewohnt,  unsere  beiden  Filekanäle
und  lesen  den  Diskheaderblock  in den
Puffer ein. Anschließend jedoch benutzen
wir den Memory-Write-befehl  der  Floppy
um  den  Inhalt  von $0101 zu in "A" ab-
zuändern. Die Adresse $0101  wird  durch
das  Low-Byte 1 und das High-Byte 1 dar-
gestellt. Desweiteren wollen wir nur ein
Byte übertragen. Selbiges  hat  den  AS-
CII-Code 65, was dem Buchstaben "A" ent-
spricht. In  dieser  Reihenfolge  werden
die  Parameter  nun  an den String "M-W"
angehängt. Nach dieser Operation  können
wir  nun den Buffer-Pointer auf das For-
matkennzeichen  positionieren  und  über
den Pufferkanal den Buchstaben "A" hein-
schreiben. Da die Floppy  seit  dem  "M-
W"-Befehl  glaubt, es läge eine Diskette
mit korrekter Formatkennung vor,  können
wir  in  Zeile  350 auch erfolgreich den
Diskheaderblock  zurückschreiben.   Eine
Abschließende  Initialisierung macht die
Diskette ab nun wieder beschreibbar.    
Nun soll unser Programm aber auch in der
Lage sein, zu erkennen, ob eine Diskette
schreibgeschützt ist, oder  nicht.  Auch
dies  ist  sehr  einfach zu realisieren.
Wir gehen dabei wieder den Weg über  die
Speicherstelle  $0101  des  Floppy-RAMs.
Hier die Unterroutine die in den  Zeilen
200 bis 260 zu finden ist:              
Unterroutine "Formatkennung prüfen:"    
200 OPEN 1,8,15,"I"                     
210 PRINT#1,"M-R";CHR$(1);CHR$(1);      
    CHR$(1)                             
220 GET#1,A$                            
230 PRINT "DISKETTE IST ";              
240 IF A$="A" THEN PRINT "NICHT";       
250 PRINT "GESCHUETZT!"                 
260 CLOSE1:RETURN                       
Hier  wird zunächst nur der Befehlskanal
geöffnet.  Einen  Puffer  benötigen  wir
nicht.  Gleichzeitig  wird  die Diskette
initialisiert und somit ihr  Formatkenn-
zeichen   in  die  Speicherstelle  $0101
übertragen. In  Zeile  210  greifen  wir
diesmal   mit   Hilfe  des  Memory-Read-
Befehls ("M-R") auf selbige  zu.  Wieder
werden  Low-  und High-Byte 1, sowie die
Länge 1 als  CHR$-Codes  übergeben.  An-
schließend  können  wir  das Zeichen aus
dem Befehlskanal auslesen. Es folgt  nun
eine  Prüfung,  ob es dem Buchstaben "A"
entspricht. Wenn ja, so wird zuerst  der
Text "nicht" ausgegeben und anschließend
der Text "geschuetzt!". Andernfalls gibt
das  Programm  nur "geschuetzt" aus. Zum
Schluß schließen  wir  den  Befehlskanal
wieder   und  kehren  zum  Hauptprogramm
zurück.                                 
2) ÄNDERN DES DISKETTENNAMENS UND DER ID
Sicherlich  haben  Sie  schon einmal ein
Programm benutzt, das den Namen und  die
ID  einer  Diskette  nachträglich verän-
dert, ohne Daten  zu  zerstören.  Wollen
wir  nun  einmal selbst so etwas schrei-
ben.                                    
Zunächst  möchten wir den Diskettennamen
verändern. Wie Sie aus dem letzten  Kurs
bestimmt  noch  wissen,  steht  der Name
einer Diskette in den Bytes 144-159  des
Dir-Header-Blocks.   Um  ihn  zu  ändern
brauchen wir lediglich diesen  Block  in
einen  Pufferspeicher einzulesen und den
neuen Namen an dieser Position hineinzu-
schreiben.  Dabei  müssen  wir  noch auf
zwei Dinge achten:  Ist  der  neue  Name
kürzer  als  16  Zeichen,  so müssen die
restlichen Zeichen  mit  dem  ASCII-Code
160  aufgefüllt werden. Das ist der Code
für 'SHIFT-SPACE'  der  von  der  Floppy
automatisch  an  kürzere  Namen angefügt
wird. Desweiteren  darf  der  neue  Name
natürlich  nicht  länger  als 16 Zeichen
sein, da wir sonst ja weitere  Daten  im
Diskheaderblock   überschreiben  würden.
Das Programm mit dem Sie  Disknamen  und
-ID ändern können, finden Sie auf dieser
MD unter dem Namen "FK.NAMECHANGE". Hier
nun  ein Auszug mit der Namensänderungs-
routine bei Zeile 200:                  
Unterroutine "Diskname ändern":         
200 OPEN 1,8,15,"I": OPEN 2,8,2,"#"     
210 PRINT#1,"U1 2 0 18 0"               
220 PRINT#1,"B-P 2 144"                 
230 NA$=""                              
240 FOR I=1 TO 16                       
250 GET#1,A$: NA$=NA$+A$                
260 NEXT                                
270 PRINT "AKTUELLER NAME: ";NA$        
280 INPUT "    NEUER NAME";NA$          
285 IF LEN(NA$)>16 THEN NA$=            
    LEFT$(NA$,16)                       
290 IF LEN(NA$)<16 THEN NA$=NA$+        
    CHR$(160): GOTO290                  
300 PRINT#1,"B-P 2 144"                 
310 PRINT#2,NA$;                        
320 PRINT#1,"U2 2 0 18 0"               
330 PRINT#1,"I"                         
340 CLOSE1:CLOSE2:RETURN                
In den Zeilen 200 und  210  öffnen  wir,
wie gewohnt, unsere Kanäle und lesen den
Diskheaderblock   in  den  Floppypuffer.
Anschließend wird der  Pufferzeiger  auf
das  144. Byte des Puffers positioniert.
In den Zeilen 230-260 werden nun die  16
Bytes  des Diskettennamens mit Hilfe ei-
ner Schleife ausgelesen und in  der  Va-
riablen  NA$  abgelegt.  Diese  wird an-
schließend ausgegeben um  den  aktuellen
Diskettennamen  anzuzeigen.  Es wird da-
raufhin nach dem  neuen  Namen  gefragt,
der  nun seinerseits in die Variable NA$
kommt. In der Zeile  285  wird  zunächst
geprüft,  ob  der neue Name zu lang ist.
Wenn ja, so wird er auf  die  ersten  16
Zeichen gekürzt. In Zeile 290 wird abge-
fragt, ob der eingegebene Name nicht  zu
kurz  ist.  Wäre  das der Fall, so würde
beim Schreibvorgang der alte Name  nicht
ganz  überschrieben werden und wäre dann
noch sichtbar. In diesem Fall füllt  die
Schleife  in  Zeile  290  den  Rest  des
Strings NA$ mit 'SHIFT-SPACES' auf. Erst
jetzt kann wieder auf das erste Byte des
Namens (Byte 144  des  Diskheaderblocks)
positioniert  werden  und der String NA$
dorthin geschrieben werden. Abschließend
wird die Änderung mittels "U2"  auf  der
Diskette  aktualisiert,  letztere nochm-
mals initialisiert und alle Kanäle  wer-
den geschlossen.                        
Wollen  wir die ID einer Diskette verän-
dern, so müssen  wir  ähnlich  arbeiten.
Hierbei  wollen wir berücksichtigen, daß
wir für die Directoryanzeige sogar  fünf
Zeichen  (anstelle  von 2) zur Verfügung
haben. Sie sicherlich ist  Ihnen  aufge-
fallen,  daß in den letzten drei Zeichen
der ID im Directory (rechts oben)  immer
der  Text  " 2A" steht, was eine Format-
kennung darstellt.  Diese  Formatkennung
hat  jedoch  keinerlei  Einfluß auf die,
die im dritten Byte des Diskheaderblocks
zu finden ist. Sie  wird  lediglich  zur
Anzeige  beim  Laden des Diretorys benö-
tigt und kann im Prinzip beliebig  geän-
dert  werden.  Die  drei  Formatkennzei-
chenbytes liegen  glücklicherweise  auch
direkt  hinter  den beiden Bytes für die
Disketten-ID, nämlich in den Bytes  162-
166  des  Diskheaderblocks. Hier nun der
Programmteil, der die ID einer  Diskette
ändert:                                 
Unterroutine "ID ändern"                
400 OPEN 1,8,15,"I": OPEN 2,8,2,"#"     
410 PRINT#1,"U1 2 0 18 0"               
420 PRINT#1,"B-P 2 162"                 
430 ID$=""                              
440 FOR I=1 TO 5                        
450 GET#1,A$: ID$=ID$+A$                
460 NEXT                                
470 PRINT "AKTUELLE ID: ";ID$           
480 INPUT "    NEUE ID";ID$             
490 IF LEN(ID$)>5 THEN ID$=LEFT$(ID$,5) 
500 PRINT#1,"B-P 2 162"                 
510 PRINT#2,ID$;                        
520 PRINT#1,"U2 2 0 18 0"               
530 PRINT#1,"I"                         
540 CLOSE1: CLOSE2: RETURN              
Im  Prinzip  arbeitet diese Unterroutine
genauso wie die, die den  Namen  ändert.
Nur  daß diesmal nicht 16, sondern nur 5
Zeichen für  die  ID  eingelesen  werden
müssen.  Desweiteren  kann  eine ID auch
kürzer  sein,  als  5  Zeichen,  nämlich
dann,  wenn  Sie wirklich nur die beiden
ID-Zeichen ändern  wollen,  das  Format-
kennzeichen  aber  gleich  bleiben soll.
Deshalb wird in Zeile 490 der String ID$
nur auf 5 Zeichen gekürzt, falls er län-
ger  sein sollte. Ein kürzerer Text wird
als solcher übernommen und an  die  Ent-
sprechende  Position  (nämlich  162) ge-
schrieben. Auch hier wird  die  Änderung
dann  mittels  "U2"  gespeichert und die
Diskette initialisiert.                 
Das soll es dann wieder  einmal  gewesen
sein  für  diesen  Monat.  Ich hoffe ich
habe Sie zum "Spielen" mit den  Blockbe-
fehlen  animiert.  Nächsten Monat wollen
wir uns mit einer weiteren Anwendung der
Blockbefehle beschäftigen und  dann  die
Programmierung der Floppy in Maschinens-
prache angehen.                         
                                    (ub)
              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 ! 
          Teil 2 Floppy-Kurs 6          
Nun wird eine Schleife durchlaufen,  die
die acht Fileeinträge dieses  Directory-
blocks  in  den  entsprechenden  Feldern
ablegt. Zur Information geben ich  Ihnen
hier nocheinmal den Aufbau eines  Direc-
toryeintrags an (sh.  Floppy-Kurs,  Teil
4):                                     
ByteNr.  Aufgabe                        
----------------------------------------
000      Byte für den Filetyp.          
001-002  Track  und Sektor des ersten   
         Datenblocks dieses Files.      
003-018  16 Bytes für den Filenamen.    
019-020  Bei relativen Files stehen hier
         Track und Sektor des ersten Si-
         Sektor-Blocks (sonst unbenutzt)
021      Bei relativen Files seht hier  
         die Länge eines Datensatzes    
         (sonst unbenutzt).             
022-025  unbenutzt                      
026-027  Zwischenspeicher des Tracks und
         Sektors beim  Öberschreiben mit
         dem Klammeraffen (" ") vor  dem
         Filenamen.                     
028-029  Hier steht die Länge  des Files
         in Blocks als 16-Bit-Lo/Hi-Zahl
030-031  unbenutzt                      
In den Zeilen 340-360 werden nun, obiger
Liste entsprechend, der Reihe  nach  das
Filetypenbyte,  sowie   Starttrack   und
-sektor des Files in die  entsprechenden
Felder eingelesen. Die anschließenden 16
Zeichen stellen den  Filenamen  dar  und
werden  als  Text,   mit   Hilfe   einer
Schleife in das Feld  "na$"  eingelesen.
In Zeile 400 überlesen wir nun 9 Zeichen
des Fileeintrags, die für uns nicht  von
Nutzen sind. In den Zeilen 410  und  420
werden nun noch Low- und  High-Byte  der
Blockanzahl eingelesen  und  umgerechnet
im Feld "bl" abgelegt.  Hiernach  werden
noch  die  beiden  letzten   unbenutzten
Bytes  überlesen,  um  den  Pufferzeiger
auf den Anfang des nächsten Eintrags  zu
positionieren. Nun wird die Laufvariable
q um eins erhöht und an  den  Schleifen-
anfang   zurück   verzweigt.   Ist   die
Schleife 8 Mal durch- laufen worden,  so
haben   wir   alle    Einträge    dieses
Directoryblocks  eingelesen  und  können
den nächsten Block laden. Da der  letzte
Directoryblock immer Track 0 als  Folge-
track beinhaltet können wir mit der  IF-
THEN-Abfrage in Zeile 450 prüfen, ob der
letzte  benutzte  Directoryblock   schon
eingelesen wurde, oder nicht.  Ist  "tr"
ungleich null, so muß die Schleife noch-
mals wiederholt werden und  es  wird  zu
Zeile 300  zurückverzweigt.  Im  anderen
Fall sind wir  beim  letzten  Directory-
block angelangt  und  können  die  File-
kanäle schließen. In Zeile 480 wird  nun
die Laufvariable "q" um 1 erniedrigt, da
dieser Feldindex beim letzten  Durchlauf
ja noch keine  neue  Zuweisung  erhielt.
Zeile 500 dient nun dazu,  den  effektiv
letzten Eintrag in diesem Directoryblock
herauszusuchen, da  ja  nicht  unbedingt
alle 8 Einträge in diesem Block  benutzt
sein  müssen.  Dies  geschieht   einfach
darin, daß wir "q" solange herunerzählen
bis in der  Variablen  na$(q)  ein  Text
steht. Da Leereinträge nur  Nullen  ent-
halten, ist  der  Filename  bei  solchen
Einträgen 16 Mal der Wert CHR $(0),  was
einem Leerstring ("") entspricht.       
Abschließend wird in das aufrufende Pro-
gramm zurückverzweigt. In unseren  Feld-
variablen befinden sich nun alle  Infor-
mationen  des  Directorys,   sowie   der
Diskettenname in "dn$"  und  die  ID  in
"id$". In der  Laufvariablen  "q"  steht
die  Anzahl  der  vorhandenen   Einträge
minus 1  (beachten  Sie  bitte,  daß  im
Indexfeld 0 ebenfalls ein  Eintrag  ent-
halten ist. "q" bezeichnet lediglich die
Nummer des letzten Eintrags). Nun wissen
wir also schon, wie wir  die  Directory-
informationen in den Computer  bekommen.
Die Hauptroutine unseres UN-DEL-Progamms
muß jetzt also nur noch die Unterroutine
bei  Zeile  200  aufrufen  um   sie   zu
erhalten.  Um  jetzt  alle  DEL-Einträge
herauszusuchen,  müssen  wir   lediglich
alle Einträge heraussuchen, die  in  den
unteren 3 Bits  des  Filetypenbytes  den
Wert 0 stehen haben. Wir müssen uns des-
halb auf die unteren 3 Bits beschränken,
da in den Bits 7 und 6 ja auch noch  In-
formationen über  das  File  gespeichert
sind. In den Zeilen 1100-1120 des Haupt-
programms werden diese Einträge nun  he-
rausgesucht  und  deren  Feldindizes  im
Feld "li"  abgelegt.  Das  Hauptprogramm
von UNDEL  beginnt  übrigens  bei  Zeile
1000. Aus Platzgründen will ich Ihnen ab
jetzt nur noch die Zeilen auflisten, die
für uns interessant sind. Der Rest dient
hauptsächlich der Bildschirmformatierung
und soll uns hier deshalb nicht interes-
sieren. Das ganze  Programm  können  Sie
sich ja einmal auf dieser MD anschauen. 
Hier nun die Zeilen 1100-1120:          
1100 z=0                                
1105 fori=0toq                          
1110 if(ty(i)and7)=0thenli(z)=i:z=z+1   
1120 next                               
Wie Sie sehen,  benutzen  wir  hier  die
Laufvariable "z" als  Indexvariable  für
das Feld "li". Mit dem  Ausdruck  "ty(i)
and 7" isolieren wir  die  unteren  drei
Bits  des  Typenbytes  und  löschen  die
evtl. gesetzten Bits 7  und  6  für  den
IF-THEN-Vergleich. Wenn "z"  nun  größer
Null ist, so wurden DEL-Files  gefunden.
Selbige werden in den  Zeilen  1190-1230
auf dem  Bildschirm  ausgegeben  und  es
wird  gefragt,  welches  davon  gerettet
werden soll. In den Zeilen 1240 bis 1350
wird nun nach dem Typ des Files  gefragt
und  ob  es  evtl.  auch  vor   erneutem
Löschen  geschützt  werden  soll.   Hier
wieder ein Programmauszug:              
1240 gosub100:print"Zu retten: ";na$(li(
1250 print"0 - DEL"                     
1260 print"1 - SEQ"                     
1270 print"2 - PRG"                     
1280 print"3 - USR"                     
1290 print"4 - REL"                     
1300 input"Welchen Filetyp soll ich     
     zuordne ";t1                       
1310 if(n<0)or(n>4)then1300             
1320 print"File auch schuetzen (J/N) ?" 
1330 geta$:ifa$=""then1330              
1340 ifa$="j"thent1=t1+64               
1350 t1=t1+128:ty(li(n))=t1             
Wie Sie sehen, wird in die Variable "t1"
eine Zahl zwischen 0 und  4  eingelesen.
Die  Zahlen,  die  die  einzelnen  Typen
zuordnen,  entsprechen  den  Codes,  die
auch die Floppy zur Erkennung des  File-
typs benutzt. In  den  Zeilen  1320-1340
wird nun gefragt, ob das File  auch  vor
dem Löschen geschützt werden soll.  Dies
regelt Bit 6 im Filetyp-Byte.  Soll  das
File geschützt werden, so  wird  zu  der
Variablen "t1" der Wert 64 hinzuaddiert,
was dem Setzen des 6.  Bits  entspricht.
Zusätzlich müssen wir noch Bit 7 setzen,
das anzeigt, daß das File  ein  gültiges
File ist. Dies  geschieht  durch  addie-
ren des Wertes 128. Nun müssen  wir  nur
noch den neuen Wert für den  Filetyp  in
die Feld- variable "ty" übernehmen.  Als
nächstes muß der neue Filetyp im  Direc-
tory  eingetragen  und  die  Datenblocks
des Files  als  'belegt'  gekennzeichnet
werden. Dies  geschieht  in  den  Zeilen
1370-1550. Hier wieder ein Auszug:      
1370 gosub100:print"Schreibe neuen      
     Eintrag"                           
1380 bl=int(li(n)/8):ei=li(n)-bl*8      
1390 tr=dt(bl):se=ds(bl)                
1400 open1,8,15,"i":open2,8,2,"#"       
1410 print#1,"u1 2 0";tr;se             
1420 po=2+ei*32                         
1430 print#1,"b-p 2";po                 
1440 print#2,chr$(t1);                  
1450 print#1,"u2 2 0";tr;se             
1460 :                                  
In den Zeilen  1380-1390  wird  nun  zu-
nächst den Variablen TR und SE die Werte
für  den  Directoryblock,  in  dem   der
Eintrag steht zugewiesen.  Als  nächstes
öffnen  wir  einen  Befehls-  und  einen
Pufferkanal  mit  den  logischen   File-
nummern 1 und 2 (wie auch schon  in  der
Dir-Lese-Routine). In  Zeile  1410  wird
der  Block  des   Eintrags   eingelesen.
Nun muß der Pufferzeiger auf den  Anfang
des  Eintrags  positioniert  werden.  Da
jeder Fileeintrag  32  Bytes  lang  ist,
errechnet sich seine Anfangsposition aus
seiner Position im Block (steht  in  der
Variablen  "ei",  die  in   Zeile   1380
definiert wurde) multipliziert  mit  32.
Zusätzlich müssen wir  noch  zwei  Bytes
aufaddieren,   in   denen   Track-   und
Sektornummer  des   Folgeblocks   stehen
(ganz am Anfang des Directoryblocks). In
Zeile 1430 wird nun auf  die  errechnete
Position positioniert. Nun müssen wir an
dieser Stelle nur noch das  neue  Typen-
byte eintragen (geschieht in Zeile 1440)
und  den  Directoryblock  mittels   "U2"
wieder auf die Diskette zurückschreiben.
Schon ist der Fileeintrag geändert!     
Zum Schluß müssen wir noch alle vom File
belegten Blocks heraussuchen und  wieder
neu belegen. Den ersten davon finden wir
in  den  Feldern  "ft"  und  "fs".  Hier
wieder ein Programmauszug:              
1465 z=0                                
1470 print"Belege Blocks neu... "       
1480 tr=ft(li(n)):se=fs(li(n))          
1490 print#1,"b-a 0";tr;se              
1500 print#1,"u1 2 0";tr;se             
1505 z=z+1                              
1510 gosub600:tr=a                      
1520 gosub600:se=a                      
1530 iftr<>0then1490                    
1540 close1:close2                      
1550 printz;"Blocks gefunden."          
Hier wird die Variable "z" dazu benutzt,
die gefundenen Blocks zu zählen. In     
Zeile 1480 werden nun die Feldeinträge  
in "ft" und "fs" in TR und SE Übertra-  
gen. Zeile 1490 benutzt nun  den  Block-
Allocate-Befehl  um  diesen  Block   als
'belegt' zu kennzeichnen. In Zeile  1500
wird er dann in den Puffer  gelesen,  da
seine ersten beiden  Bytes  ja  auf  den
nächsten Block des Files zeigen.  Dessen
Track- und Sektornummer wird nun in  den
Zeilen 1510 und 1520 in TR und  SE  ein-
gelesen und für den  nächsten  Durchlauf
der Schleife verwendet. Sollte TR jedoch
den Wert 0 aufweisen, so sind  wir  beim
letzten Block angelangt und  können  die
Blockbelegung beenden. Dies regelt die  
IF-THEN-Anweisung in Zeile 1530.        
Somit wären alle wichtigen Teile des UN-
DEL-Programms   besprochen.   Um   einen
Gesamtüberblick zu  erhalten,  rate  ich
Ihnen, sich das Programm auf  dieser  MD
einmal  auszudrucken  und   anzuschauen.
Öbrigens beinhaltet  das  Programm  noch
einen kleinen Schönheitsfehler: Wenn Sie
nämlich z.B. 9 Files auf einer  Diskette
haben, und das letzte davon löschen,  so
findet  es  die   Directory-Lese-Routine
nicht mehr. Das liegt  daran,  daß  nach
dem  Löschen  des  Eintrags  der  zweite
Directoryblock ja  nicht  mehr  benötigt
wird und deshalb als Folgeblock Track 0,
Sektor  255  im  ersten   Directoryblock
eingetragen wird. Um  diesen  Fehler  zu
beheben,  können  Sie  ja  eine  Routine
schreiben, die alle Directoryblocks  der
Reihe nach einliest. Das ist  zwar  sehr
zeitaufwendig, könnte jedoch den  Fehler
beheben. Sie  können  dabei  davon  aus-
gehen, daß die Folgetracks und -sektoren
beim Directory immer  gleich  sind.  Bei
einem vollen Directory  (144  Einträge),
können Sie so diese Einträge  mit  Hilfe
eines     Diskettenmonitors      einfach
herausfinden.                           
Ich verabschiede mich hiermit von  Ihnen
bis zur nächsten Ausgabe, wo wir uns  an
die  Floppyprogrammierung  in  Assembler
heranwagen möchten.                     
                                    (ub)
----------------------------------------
              Floppy-Kurs:              
      "Es rappelt in der Kiste..."      
                (Teil 7)                
----------------------------------------
Hallo und herzlich Willkommen zum  sieb-
ten  Teil  des  Floppy-Kurses. In diesem
Monat möchten wir  uns  an  die  Floppy-
Programmierung  in Assembler heranwagen.
Hierbei werden wir alle notwendigen Rou-
tinen zum I/O-Handling von Maschinenspa-
che aus kennenlernen. Allerdings sollten
Sie schon einige  Assembler-Vorerfahrung
mitbringen  und  zumindest  Kenntnis vom
Befehlssatz des 6510-Prozessors haben.  
DER EINSTIEG IN ASSEMBLER               
Wie Sie bemerkt haben, waren alle bishe-
rigen  Programmbeispiele  und Befehlser-
läuterungen in BASIC programmiert.  Dies
diente   hauptsächlich  der  Einfachheit
halber.  Wenn  Sie  nun  ein  angehender
Assemblerprogrammierer  sind,  so werden
Sie sich desöfteren schon einmal gefragt
haben, wie Sie die Floppy in  Maschinen-
sprache  ansprechen  können. Dies unter-
scheidet sich von BASIC  zwar  schon  in
einigen Punkten, da es etwas aufwendiger
ist, jedoch können wir hier komplett auf
schon  im  Betriebssystem des 64ers vor-
handene Routinen zurückgreifen, die  le-
diglich  mit  den  richtigen  Parametern
gefüttert werden müssen. Diese  Routinen
sind  dieselben,  die auch vom BASIC des
C64 benutzt werden, weshalb die  Parame-
terübergabe  der  von BASIC sehr ähnlich
ist. Kommen wir zunächst einmal zu eini-
gen grundlegenden Dingen:               
ÜFFNEN UND SCHLIESSEN VON FILES         
Jedesmal, wenn wir einen Zugriff auf die
Floppy ausführen  wollten,  mussten  wir
zuvor   immer  einen  Filekanal  öffnen.
Hierzu wurden mit Hilfe des OPEN-Befehls
einige Parameter an die  Floppy  überge-
ben,  die zur eindeutigen Definition des
Kanals notwendig waren. Diese  Parameter
waren  die  Filenummer, die Devicenummer
und die Sekundäradresse. Oft folgte  der
ganzen  Anweisung dann auch noch ein Fi-
lename, dessen Appendix ebenso die  ver-
langte Operation spezifizierte. Man kann
also sagen, daß diese Werte alles Grund-
werte sind, die zum  Datenaustausch  mit
der  Floppy  benötigt werden. Aus diesem
Grund müssen sie auch bei jeder Fileope-
ration angegeben werden. Möchten wir nun
eine  Operation von Assembler aus durch-
führen, so müssen  wir  diese  Parameter
zunächst  dem  Betriebssystem mitteilen.
Dies  geschieht  über  die  beiden  ROM-
Routinen  "SETPAR"  und "SETNAM". SETPAR
hat die Einsprungadresse $FFBA.  An  sie
werden die Filenummer, sowie Geräte- und
Sekundäradresse  übergeben.  SETNAM legt
den Filenamen (inklusive Appendix) fest.
Sie steht bei $FFBD. Beide Routinen  tun
nichts  anderes, als die gegebenen Werte
in Betriebssystem-Zwischenspeichern  der
Zeropage abzulegen.                     
Nachdem die Werte festgelegt sind,  kön-
nen wir die Betriebssystemroutine "OPEN"
aufrufen,  die  uns  den  Kanal  mit der
Floppy öffnet. Ihre Einsprungadresse ist
bei $FFC0. Zum  Schließen  benutzen  wir
die Routine "CLOSE" bei $FFC3.          
Wenn  wir ein File öffnen, so müssen wir
zunächst einmal seinen Namen  festlegen.
Diesen  legen  wir irgendwo im Speicher,
an einer bekannten Adresse, ab. Die Län-
ge  des Namens benötigen wir ebenso. Als
nächstes können wir  SETNAM  und  SETPAR
aufrufen.  SETNAM  wird  die Adresse des
Filenamens und seine Länge,  SETPAR  die
logische Filenummer, die Geräte- und die
Sekundäradesse   übergeben.   Hier  eine
Öbersicht der Registerbelegungen:       
SETPAR: Akku   = Filenummer             
        X-Reg. = Geräteadresse          
        Y-Reg. = Sekundäradresse        
SETNAM: Akku   = Länge des Filenamens   
        X-Reg.  = Low-Byte der Speicher-
                 adresse des Namens     
        Y-Reg. = High-Byte der Speicher-
                 adresse des Namens     
Ich möchte  Ihnen  hierzu  ein  Beispiel
liefern.  Angenommen,  Sie  wollten  das
sequentielle File "TEST" zum Lesen  öff-
nen.  Als  Filenummer  wollen  wir die 1
wählen, als Sekundäradresse 2. Die Gerä-
teadresse  ist  in  Bezug auf die Floppy
natürlich 8 (oder 9,10,11 wenn Sie  eine
zweite, dritte oder vierte Floppy besit-
zen). All  diese  Parameter  entsprechen
also dem BASIC-Befehl:                  
OPEN 1,8,2,"TEST,S,R"                   
Möchten wir diesen Befehl nun in Maschi-
nensprache umsetzen,  so  schreiben  wir
folgendes   Assembler-Programm.  Hierbei
gehe ich davon aus, daß wir den  Filena-
men "TEST,S,R" als ASCII-Code bei Adres-
se $0334 (dem  Kasettenpuffer)  abgelegt
haben:                                  
LDA #$01   ;Filenummer 1                
LDX #$08   ;Geräteadresse 8             
LDY #$02   ;Sekundäradresse 2           
JSR $FFBA  ;SETPAR aufrufen             
LDA #$08   ;Filename ist 8 Zeichen lang 
LDX #$34   ; und liegt bei Adresse      
LDY #$03   ; $0334                      
JSR $FFBD  ;SETNAM aufrufen             
Die Parameter für das File "TEST"  wären
damit festgelegt. Da das ",S,R" im File-
namen enthalten  ist,  weiß  die  Floppy
auch  gleich  schon, daß wir ein sequen-
tielles File lesen möchten.             
Als Nächstes müssen wir das zuvor spezi-
fizierte  File  öffnen.  Dies  geschieht
über die oben schon erwähnte Betriebssy-
stemroutine "OPEN". Sie  benötigt  keine
Parameter und wird direkt aufgerufen:   
JSR  $FFC0  ;File mit zuvor gesetzem Na-
            men und Parametern öffnen   
Nun ist das  File  "TEST"  geöffnet.  Da
aber  auch  jeder  Datenfluß  einmal ein
Ende hat, muß unser File irgendwann ein-
mal  wieder geschlossen werden. Dies ge-
schieht, wer hätte das gedacht, mit  der
ROM-Routine  "CLOSE",  die  bei  Adresse
$FFC3 angesprungen wird. Da auch mehrere
Files gleichzeitig  offen  sein  können,
muß ihr die Filenummer des zu schließen-
den Files im Akku übergeben werden:     
LDA #$01   ;File mit Filenummer 1       
JSR $FFC3  ; schließen                  
DER DATENAUSTAUSCH                      
Wie man ein File  öffnet,  sollte  Ihnen
nun klar sein. Wenn Sie jedoch auch noch
mit  ihm  komunizieren  wollen  (was sie
bestimmt tun möchten, da es anders sinn-
los  wäre ein File zu öffnen), so müssen
Sie   weiterhin   fünf   Betriebssystem-
Routinen kennen, die Ihnen dies ermögli-
chen.  Zunächst  wären  da  "CHKIN"  und
"CHKOUT".  Sie  dienen der Umleitung der
Standard-Ein/Ausgabe  auf  den  entspre-
chenden  Filekanal.  Wenn  Sie aus einem
File lesen wollen, so sollten  Sie  nach
dem  Üffnen  desselben die Routine CHKIN
aufrufen. Mit ihr  teilen  Sie  dem  Be-
triebssystem  mit,  daß  Sie,  wenn  Sie
jetzt etwas einlesen, die Daten aus die-
sem Filekanal haben möchten. Möchten Sie
in  ein  File  schreiben,  so müssen Sie
CHKOUT aufrufen um in das geöffnete File
schreiben  zu  können.  Beide   Routinen
benötigen  die  Filenummer  des entspre-
chenden Files im  X-Register.  Die  Ein-
sprungadresse  von  CHKIN ist $FFC6, die
von CHKOUT $FFC9.                       
Wenn Sie  die  Standard-Ein/Ausgabe  mit
Hilfe unserer beiden Routinen umgeleitet
haben,  so  können  Sie  die Ihnen viel-
leicht schon  bekannten  Routinen  BASIN
($FFCF) und BASOUT ($FFD2) zum Lesen und
Schreiben  von  Daten  vom, bzw. an, das
Ein/Ausgabe-Gerät  benutzen.  Rufen  Sie
diese  Routinen  bei unveränderter Stan-
dard-Ein-/Ausgabe auf, so erscheint  bei
BASIN ein Cursor auf dem Bildschirm, der
dem  Assemblerprogramm  eine Eingabe von
der  Tastatur  übermittelt,  bei  BASOUT
wird ein Zeichen an die aktuelle Cursor-
position gedruckt und der Cursor um eine
Stelle weiterbewegt  (wie  Sie  bemerken
ist das Standard-Eingabegerät die Tasta-
tur, das Standard-Ausgabegerät der Bild-
schirm).                                
Bei umgeleiteter  Eingabe  erhalten  Sie
nun  beim  Aufruf von BASIN das aktuelle
Byte  des  geöffneten  Files   im   Akku
zurück.  Bei  umgeleiteter  Ausgabe wird
jedes Byte, daß Sie in  den  Akku  laden
und  anschließend an BASOUT übergeben in
das geöffnete File hineingeschrieben.   
Haben Sie nun Ihre Fileoperationen been-
det, so ist  es  notwendig,  die  fünfte
Routine  zu  benutzen,  von der ich oben
sprach. Sie heißt "CLRCH" und wird  ver-
wendet, um die Standard-Ein/Ausgabe wie-
der zurückzusetzen  auf  'Tastatur'  und
'Bildschirm'.  Ihre Einsprungadresse ist
$FFCC. Sie wird VOR dem Aufruf von CLOSE
benutzt und benötigt keine Parameter.   
FEHLERERKENNUNG UND BEHANDLUNG          
Bevor   wir  uns  der  Praxis  zuwenden,
zunächst noch ein Wort zur  Fehlererken-
nung.  Hierüber  können wir nämlich beim
Lesen eines Files  auch  erkennen,  wann
wir sein letztes Byte gelesen haben.    
Prinzipiell gilt: tritt während der  Ar-
beit  einer  der  Floppy-Betriebssystem-
Routinen ein Fehler auf, so wird das  an
das  aufrufende  Programm  durch ein ge-
setztes  Carry-Bit  zurückgemeldet.   So
können  Sie also nach jeder der Routinen
durch  eine  einfache  "BCS"-Verzweigung
("Branch on Carry Set") auf eine Fehler-
behandlungsroutine  verzweigen.  Hierbei
steht  dann  im Akku ein Fehlercode, mit
dessen Hilfe Sie  die  Art  des  Fehlers
feststellen  können. Hier eine Liste mit
den möglichen  Fehlermeldungen  und  den
Ursachen für einen aufgetretenen Fehler.
In  eckigen  Klammern stehen jeweils die
Betriebssystem-Routinen,  die  den  ent-
sprechenden   Fehler   auslösen  können.
Steht nichts  dahinter,  so  handelt  es
sich um einen allgemeinen Fehler:       
0: "Break Error"  -  Die  RUN/STOP-Taste
   wurde gedrückt.                      
1: "too many files" - Der 64er verwaltet
   intern maximal 10 offene  Files.  Sie
   versuchten ein elftes File zu öffnen.
   Oder  aber  sie  haben schon zu viele
   Files zur Floppy hin offen (Sie erin-
   nern sich: man darf maximal 3 sequen-
   tielle, oder 1 relatives  und  1  se-
   quentielles  File  gleichzeitig offen
   halten). {OPEN}                      
2: "file  open"  -  Sie  versuchten, ein
   schon offenes File nochmal zu öffnen.
   {OPEN}                               
3: "file not open" - Sie versuchten, ein
   ungeöffnetes File anzusprechen.  {CH-
   KIN, CHKOUT, CLOSE}                  
4: "file  not found" - Das File, das Sie
   zum Lesen  öffnen  wollten  existiert
   gar nicht. {OPEN}                    
5: "device not  present"  -  Die  Floppy
   (oder  das  gewählte Gerät) ist nicht
   eingeschaltet. {OPEN}                
6: "not  an input file" - Sie versuchten
   aus einem  zum  Schreiben  geöffneten
   File zu lesen. {CHKIN}               
7: "not an output file" - Sie versuchten
   in ein zum Lesen geöffneten  File  zu
   schreiben. {CHKOUT}                  
8: "missing filename" - Sie gaben keinen
   Filenamen an. {OPEN}                 
9:  "illegal  device number" - Sie gaben
   eine  ungültige   Devicenummer    an.
   {OPEN}                               
Beachten  Sie bitte, daß nicht unbedingt
alle Fehler aus obiger  Liste  auftreten
müssen, da man die oben genannten Routi-
nen auch zum Anprechen von anderen Gerä-
ten  verwenden kann (z.B. Drucker, Data-
sette, etc.). Üffnen Sie z.B. einen  Ka-
nal  zum Drucker, so brauchen Sie keinen
Filenamen - der Fehler  8  wird  in  dem
Fall also nie auftreten.                
Als weitere Fehlererkennungshilfe stellt
uns das Betriebssystem auch eine Status-
speicherstelle zur  Verfügung.  Sie  ist
absolut identisch mit der Variablen "ST"
von BASIC. Fragt ein BASIC-Programm die-
se Variable ab,  so  greift  der  BASIC-
Interpreter auf eben diese Speicherstel-
le  zurück.  Die  Assemblerprogrammierer
finden  den  I/O-Status in der Zeropage,
nämlich  in  Speicherstelle  $90   (dez.
144).  Um feststellen zu können, ob wäh-
rend der Arbeit mit der Floppy ein  Feh-
ler  aufgetreten ist, müssen wir sie le-
diglich einlesen  und  analysieren.  Ein
Fehler  wird  hierbei  durch das gesetzt
sein eines  oder  mehrerer  Bits  dieser
Speicherstelle gemeldet. Hier eine Bele-
gung der Bits (nur für  die  Arbeit  mit
der  Floppy  gültig, bei Kasettenbetrieb
gilt eine andere Belegung):             
Bit Bedeutung                           
----------------------------------------
 0  Fehler beim Schreiben               
 1  Fehler beim Lesen                   
 6  Datei-Ende wurde erreicht (Lesen)   
 7  Gerät nicht vorhanden oder abge-    
    schaltet ("device not present")     
Sie sehen, daß Sie  hier,  wie  auch  in
BASIC,  das  Ende  eines Files durch ge-
setzt sein des 6. Bits von ST  feststel-
len können. Dann nämlich hat ST den Wert
64,  den wir bei BASIC-Abfragen auch im-
mer  verwendeten.  Sie  sehen   übrigens
auch,  warum  bei  erneutem  Lesen trotz
beendetem  File  die   Fehlernummer   66
zurückgeliefert  wird. Das File ist dann
nämlich zu Ende und es trat  ein  Fehler
beim  Lesen  auf,  weil es ja gar nichts
mehr zu lesen gibt. Damit sind die  Bits
1  und  6 gesetzt (2+64) was dem Wert 66
entspricht.                             
Wenn ST den Wert 0 enthält, so ist alles
gut gegangen. Auf diese Weise können wir
in Assembler  sehr  einfach  den  Fehler
abfragen:  Wir  lesen die Speicherstelle
$90 einfach ein und  verzweigen  mittels
BNE auf eine Fehlerbehandlungsroutine.  
ZUSAMMENFASSUNG                         
Abschließend  zu  diesen Routinen möchte
ich   Ihnen    nun    zwei    Universal-
Lese/Schreib-Routinen   vorstellen,  die
Sie zum Lesen, bzw. Schreiben eines  Fi-
les  verwenden  können. Sie finden diese
Routinen auch auf dieser  MD  unter  dem
Namen  "FK.I/O.S".  Sie  sind  im Hypra-
Ass-Format gespeichert. Um sie sich  an-
zuschauen  können Sie sie wie ein BASIC-
Programm laden und listen.              
Zunächst  einmal  möchte  ich  Ihnen die
"ReadFile"-Routine vorstellen. Sie liest
ein File an eine beliebige Adresse  ein.
Hierbei übergeben Sie Low- und High-Byte
in  X-  und  Y-Register, sowie die Länge
des Filenamens  im  Akku.  Der  Filename
selbst  soll  immer bei $0334 (dez. 820)
stehen:                                 
ReadFile:                               
       stx $fb      ;Startadresse in    
       sty $fc      ; Zeiger schreiben  
       ldx #$34     ;Namensadresse=$0334
       ldy #$03     ;in X/Y (Len in Ak.)
       jsr $ffbd    ;Und Name setzen    
       lda #01      ;Filenummer=1       
       ldx #08      ;Geräteadresse=1    
       ldy #00      ;Sek.=0 (=PRG lesen)
       jsr $ffba    ;Parameter setzen   
       jsr open     ;File öffnen        
       ldx #01      ;Ausgabe auf FNr. 1 
       jsr chkin    ; umleiten          
       ldy #00      ;Index auf 0 setzen 
loop1: jsr basin    ;Byte lesen         
       sta ($fb),y  ;und auf Zeiger-    
                     adresse speichern  
       inc $fb      ;Den Zeiger in      
       bne l1       ; $FB/$FC um 1      
       inc $fc      ; erhöhen           
l1:    lda $90      ;File-Ende erreicht?
       beq loop1    ;Nein, dann weiter! 
       jsr $ffcc    ;Ja, also Standard- 
                     Ein-/Ausgabe       
                     zurücksetzen.      
       lda #01      ;Und File mit File- 
       jmp $ffc3    ; nummer 1 schließen
Als nächstes stelle ich Ihnen die  "Wri-
teFile"-Routine vor. Sie speichert Daten
in  einem beliebigen Speicherbereich auf
Diskette und wird mit denselben Vorraus-
setzungen   aufgerufen  wie  "ReadFile":
Name bei $0334, Namenslänge im Akku  und
Startadresse  des  zu  speichernden  Be-
reichs in X-/Y-Register. Zusätzlich müs-
sen  Sie  die Endadresse dieses Bereichs
(in  Lo/Hi-Darstellung)  vorher  in  den
Speicherstellen $FD/$FE abgelegt haben: 
WriteFile:                              
       stx $fb      ;Startadresse in    
       sty $fc      ; Zeiger schreiben  
       ldx #$34     ;Namensadresse=$0334
       ldy #$03     ;in X/Y (Len in Ak.)
       jsr $ffbd    ;Und Name setzen    
       lda #01      ;Filenummer=1       
       ldx #08      ;Geräteadresse=1    
       ldy #01      ;Sek.=1 (=PRG       
                     schreiben)         
       jsr $ffba    ;Parameter setzen   
       jsr $ffc0    ;File öffnen        
       ldx #01      ;Eingabe auf FNr.1  
       jsr $ffc9    ; umleiten          
       ldy #00      ;Index auf 0 setzen 
loop2: lda ($fb),y  ;Byte aus Sp. holen 
       jsr $ffd2    ;und an Floppy sen- 
                     den                
       inc $fb      ;Den Zeiger in      
       bne l2       ; $FB/$FC um 1      
       inc $fc      ; erhöhen           
l2:    lda $fe      ;Quellzeiger $FB/$FC
       cmp $fc      ; mit Zielzeiger    
       bne loop2    ; $FD/$FE           
       lda $fd      ; vergleichen. Wenn 
       cmp $fb      ; ungleich, dann    
       bne loop2    ; weitermachen.     
       jsr $ffcc    ;Wenn gleich, dann  
                     Standard-I/O       
                     zurücksetzen.      
       lda #01      ;und File mit File- 
       jmp $ffc3    ;nummer 1 schließen 
Wie  Sie  sehen, habe ich hier den Trick
mit den Sekundäradressen  verwendet.  Da
bei  der Sekundäradresse die Werte 0 und
1 für "Programm  lesen"  bzw.  "Programm
schreiben"  stehen,  erübrigt  sich  ein
umständliches anhängen von  ",P,R"  bzw.
",P,W"  an  den  Filenamen. Dafür jedoch
können mit den  Routinen  nur  PRG-Files
gelesen und geschrieben werden.         
Bitte Teil 2 des Floppy-Kurses laden !  
----------------------------------------
              Floppy-Kurs:              
      "Es rappelt in der Kiste..."      
               (Teil 7.2)               
----------------------------------------
Sie können diese Routinen nun universell
verwenden, um  Programmdaten  (z.B.  die
High-Score-Tabelle  in  einem Spiel) auf
Diskette zu schreiben und wieder von ihr
zu lesen.  Aber  Achtung:  die  Routinen
sind  nur zum reinen Lesen und Schreiben
gedacht! Files,  die  normalerweise  mit
LOAD in den Rechner geladen werden, oder
mit  SAVE  abgespeichert  wurden, können
nicht ganz so behandelt werden!  Hierbei
müssen   Sie  beachten,  daß  die  SAVE-
Routine des  Betriebssystems  immer  die
Anfangsadresse  des  Files in den ersten
beiden Bytes (in Lo/Hi-Byte-Darstellung)
mitspeichert. Lesen Sie ein solches File
mit "BASIN" ein, so erhalten Sie in  den
ersten  beiden  Bytes immer zuerst diese
Adresse. Beachten Sie  also,  daß  diese
beiden  Bytes  nicht  zu den eigntlichen
Daten des Files gehören!  Ebenso  können
Sie  kein  mit "WriteFile" geschriebenes
File mit  dem  LOAD-Befehl  laden,  weil
hierbei  nämlich die ersten beiden Bytes
als Startadresse gewertet werden und das
File  anschließend  irgendwohin  in  den
Speicher  geladen wird! Zum Kopieren von
Files sind diese Routinen wiederum opti-
mal  geeignet, da sie die Ladeadresse ja
mitladen und mitspeichern.              
Sie  können  sie aber auch so modifizie-
ren, daß diese Adresse mitberücksichtigt
wird. Hierzu müssen Sie "ReadFile" ledi-
glich  so  umprogrammieren,  daß sie die
ersten beiden Bytes liest  und  als  An-
fangsadresse nimmt, bzw. daß "WriteFile"
die  gegebene  Anfangsadresse, die in X-
und Y-Register übergeben wird, vorher an
das zu  speichernde  File  sendet.  Noch
einfacher  geht  das  aber  mit  den Be-
triebssystemroutinen für LOAD und  SAVE.
Sie nehmen uns diese Arbeit nämlich kom-
plett ab und  sind  zudem  noch  relativ
flexibel zu handhaben.                  
LOAD UND SAVE                           
Wie schon erwähnt  stellt  uns  das  Be-
triebssystem  ebenso  zwei  Routinen zur
Verfügung, mit denen wir komplette Files
nachladen, bzw.  speichern  können.  Der
Aufruf  ist  hierbei  sehr  einfach. Zu-
nächst einmal müssen  wir  unsere  Files
wieder  mittels SETNAM und SETPAR spezi-
fizieren. Danach werden die Prozessorre-
gister  einfach nur noch mit den entpre-
chenden  Parametern  gefüttert  und  die
benötigte Routine wird aufgerufen.      
Kommen  wir zuerst zur LOAD-Routine. Sie
liegt bei $FFD5 und hat folgende Aufruf-
parameter:                              
Akku  : Operationsflag ($00 oder $01)   
X-Reg.: Low-Byte der Ladeadresse        
Y-Reg.: High-Byte der Ladeadresse       
Das  Operationsflag,  das  in  den  Akku
kommt, hat folgende Bewandnis: die LOAD-
Routine kann nämlich auch zum Verifizie-
ren eines Files  verwendet  werden  (BA-
SIC-Befehl "VERIFY"). Hierbei wird haar-
genau so verfahren wie beim  Laden,  je-
doch  mit  dem Unterschied, daß das File
nicht in den Speicher geschrieben,  son-
dern  nur  gelesen  wird. Möchte man nun
laden, so muß der Wert 0 im Akku stehen.
Möchte man verifizieren, so  gehört  der
Wert  1  in ihn hinein. Desweiteren kann
in X- und  Y-Register  die  Startadresse
des  File  übergeben  werden,  an die es
geladen werden  soll.  Die  LOAD-Routine
überliest  dann  ganz einfach die ersten
beiden Bytes, die diese Adresse ja ange-
ben,  und liest das File anschließend an
die gegebene Adresse.  Möchten  Sie  das
File jedoch an seine vorgegebene Adresse
laden,  so  müssen Sie X- und Y-Register
lediglich mit 0 füllen.                 
Zum  besseren  Verständins  einmal  eine
kleine Routine,  die  ein  File  mittels
LOAD-Routine an seine im File vorgegebe-
ne Adresse lädt. Sie verlangt  als  Auf-
rufparameter  die Filenamenlänge im Akku
und den Filenamen bei Adresse $0334:    
LDX #$34    ;Name bei $0334 (Länge ist  
LDY #$03    ; noch vom Aufruf im Akku)  
JSR $FFBD   ;Namen setzen (SETNAM)      
LDA #01     ;Filenummer=1               
LDX #08     ;Gerät Nr. 8                
LDY #00     ;Sek.=0 für "PRG laden"     
JSR $FFBA   ;Parameter setzen (SETPAR)  
LDA #00     ;Load-Flag in Akku          
TAX         ;X/Y löschen (Startadresse  
TAY         ;aus dem File holen)        
JMP $FFD5   ;und laden                  
Wie Sie sehen, werden SETNAM und  SETPAR
genauso  benutzt  wie in der "ReadFile"-
Routine. Im Prinzip können Sie die Anga-
be  einer  Filenummer  weglassen, da Sie
sie selbst ja nicht brauchen. Es wird in
dem Fall gerade die Nummer gewählt,  die
im Akku steht. Nur hat das den Nachteil,
daß der Ladevorgang nicht immer funktio-
niert, wenn Sie noch weitere Files offen
halten.                                 
Das File steht am Ende  der  Routine  an
der  Stelle  im Speicher, die von seinen
ersten beiden Bytes spezifiziert  wurde.
Möchten  Sie  es  allerdings an eine be-
stimmte Adresse laden,  z.B.  $1234,  so
müssen  Sie die beiden Befehle "TAX" und
"TAY" durch die folgenden beiden  Zeilen
ersetzen:                               
LDX #$34                                
LDY #$12                                
Kommen  wir nun zur SAVE-Routine des Be-
triebssystems. Sie wird ähnlich aufgeru-
fen. Zunächst müssen wieder mittels SET-
NAM und SETPAR der Name und die  Parame-
ter des Files gesetzt werden.  Anschlie-
ßend  kann  die  SAVE-Rotine  aufgerufen
werden. Sie  benötigt  jedoch  ein  paar
mehr Parameter, nämlich Start- und  End-
adresse. Da hierfür die drei  Prozessor-
register nicht ausreichen, wurde folgen-
der Weg gegangen: Zunächst legen Sie die
Staradresse des zu speichernden Bereichs
in Lo'Hi Darstellung in  zwei  aufeinan-
derfolgenden Adressen der  Zeropage  ab.
Hierzu bieten sich $FB und  $FC  an,  da
sie  vom  Betriebssystem  nicht  benutzt
werden. Nun laden Sie  einen  "Zeropage-
Zeiger" in den Akku. Dieser  ist  nichts
anderes als die Nummer der Speicherstel-
le in der Sie das Low-Bytes  der  Start-
adresse abgelegt haben. In unserem  Bei-
spiel also $FB. Die  Endadresse  des  zu
speichernden Bereichs legen  Sie  in  X-
und Y-Register ab und rufen anschließend
die  SAVE-Routine  auf.  Ihe  Einsprung-
adresse liegt  bei  $FFD(.  Hier  wieder
eine   Beispielroutine.   Sie   verlangt
wieder  den  Filenamen  bei  $0334   und
dessen Länge im Akku. Desweiteren müssen
Sie die Startadresse in $FB/$FC abgelegt
haben und die Endadresse in  X-  und  Y-
Register übergeben:                     
STX $FD     ;Endadresse in $FD/$FE      
STY $FE     ; sichern                   
LDX #$34    ;Filenamen bei $0334        
LDY #$03    ; setzen                    
JSR $FFBD   ; (SETNAM)                  
LDA #01     ;Filenr. 1                  
LDX #08     ;Geräte Nr. 8               
LDY #01     ;Sek.=1 für "PRG speichern" 
JSR $FFBA   ;Parameter setzen (SETPAR)  
LDA #$FB    ;Zeiger auf Startadr. laden 
LDX $FD     ;Endadresse wieder in       
LDY $FE     ; X- und Y-Reg. holen       
JMP $FFD8   ;Und speichern!             
Wie Sie sehen, wird die Endadresse hier-
bei in $FD/$FE  zwischengespeichert,  um
den Aufruf korrekt durchführen  zu  kön-
nen. Diese beiden  Speicherstellen  sind
übrigens  ebenfalls  vom  Betriebssystem
unbenutzt.                              
Die beiden Programmbeispiele  zum  Laden
und Speichern mittels LOAD und SAVE fin-
den Sie übrigens ebenfalls in  dem  File
"FK.I/O.S" auf  dieser  MD.  Ich  verab-
schiede mich nun wieder einmal  von  Ih-
nen, und wünsche Frohe  Weihnachten  und
ein schönes neues Jahr, in dem wir  dann
tiefer  in  die  Assemblerprogrammierung
der Floppy einsteigen  und  einige  Pro-
grammbeispiele des  täglichen  Gebrauchs
kennenlernen werden.                    
                                    (ub)
----------------------------------------
              Floppy-Kurs:              
      "Es rappelt in der Kiste..."      
                (Teil 8)                
----------------------------------------
Herzlich Willkommen zum achten Teil  des
Floppy-Kurses.  In  diesem  Monat wollen
wir noch etwas weiter in die  Floppypro-
grammierung  in Assembler einsteigen. Im
letzten Monat hatten wir ja schon  ange-
sprochen,  wie  man  ein File öffnet und
mit ihm kommuniziert,  sowie  man  Files
lädt  und  speichert.  Diesmal  will ich
Ihnen eine andere, effektiviere  Methode
zeigen,  mit  der man die Floppy anspre-
chen kann.                              
DIE ALTE METHODE                        
In der letzten Ausgabe des Floppy-Kurses
hatten  wir  gelernt,  wie  man ein File
öffnet, es zur Datenübertragung  bereit-
macht,  Daten  an  es sendet und von ihm
empfängt,  und  es  anschließend  wieder
schließt.  Die  dort behandelte Arbeits-
weise war die einfachste,  die  man  zum
reinen  Lesen oder Schreiben eines Files
anwendet. Es gibt  jedoch  eine  weitere
Methode  Files  anzusprechen.  Diese ist
zwar ein wenig umständlicher, dafür aber
schneller und weitaus flexibler.        
Rekapitulieren  wir: Nach der ersten Me-
thode gingen  wir  immer  folgendermaßen
vor:                                    
1 ) "SETNAM"  mit den Parametern für den
    Filenamen aufrufen.                 
2 ) "SETPAR"  mit  den Datenkanalparame-
    tern aufrufen.                      
3 ) Mit "OPEN" das File öffnen          
4a) War das File eine  Ausgabedatei,  so
    mussten wir "CKOUT" aufrufen.       
4b) War das File eine  Eingabedatei,  so
    mussten wir "CHKIN" aufrufen.       
5a) War das File eine  Ausgabedatei,  so
    wurden  die  Daten mit Hilfe von "B-
    SOUT" geschrieben.                  
5b) War  das  File eine Eingabedatei, so
    wurden die Daten mit Hilfe von  "BA-
    SIN" gelesen.                       
6 ) Abschließend wurde  mittels  "CLRCH"
    die Standardausgabe wieder zurückge-
    stellt.                             
7 ) Das File wurde mit "CLOSE" geschlos-
    sen.                                
Wie gesagt, ist diese Methode  die  ein-
fachste,  wenn Sie ein einziges File le-
diglich  Lesen  oder  Schreiben  wollen.
Wenn  Sie  nun aber mehrere Files offen-
halten und ansprechen wollen, oder  aber
auf einem Kanal Lesen und Schreiben wol-
len (so wie das beim Befehlskanal  unter
Mitbenutzung  von  Pufferkanälen oft der
Fall ist), so versagt diese Methode. Das
liegt daran, daß Sie mit  der  einfachen
Methode  eigentlich  nicht  wirklich mit
den  Dateien  kommunizieren.  Dies   ge-
schieht  dann  nämlich  mit  einem Umweg
über das  Betriebssystem.  Das  Aufrufen
von  "CKOUT" entspricht zum Beispiel dem
Basic-Befehl "CMD". Sie leiten damit die
Daten, die normalerweise auf  dem  Bild-
schirm   ausgegeben   werden,   auf  den
Floppykanal um. Nun ist es aber wiederum
nicht so einfach, einen zum Ausgabekanal
erklärten Filekanal, als Eingabekanal zu
benutzen. Ein  folgendes  "CHKIN"  zeigt
nämlich  keine  Wirkung.  Auch  wenn Sie
zwei Files offenhalten, so  funktioniert
das  Umschalten  zwischen  diesen beiden
Files nicht immer. Der Grund dafür liegt
in der Art und Weise wie  die  Standard-
Ein/Ausgabefiles  im  C64 verwaltet wer-
den.                                    
DIE NEUE METHODE                        
Um  nun  effizienter  mit  der Floppy zu
kommunizieren, müssen wir auf  spezielle
Betriebssystemroutinen    zurückgreifen,
die eigens  für  die  Datenkommunikation
mit der Floppy (bzw. dem IEC-Bus, an dem
Floppy  und Drucker angeschlossen sind),
dienen. Diese Routinen werden im Prinzip
auch bei Verwendung der  ersten  Methode
vom  Betriebssystem  benutzt, jedoch muß
ein  Standard-Kanal  eben  auch  andere,
virtuelle,  Geräte  (wie Bildschirm, Ta-
statur, etc.) ansprechen können, weshalb
die Restriktionen dort etwas  umständli-
cher sind. Deshalb ist eine Ausgabe über
BASOUT,  oder  die  Eingabe  über BASIN,
auch immer langsamer, als wenn  Sie  die
entsprechenden   I/O-Routinen   für  den
IEC-Bus benutzen, da bei den beiden  er-
sten  Routinen  erst  einmal entschieden
werden muß, welche effektiven  Ein-/Aus-
gaberoutinen nun wirklich benutzt werden
müssen  (in  unserem  Fall  nämlich  die
IEC-Busroutinen).                       
Kommen  wir  nun  jedoch  endlich zu den
neuen Routinen und deren  Funktionsprin-
zip. Hierzu müssen wir uns zunächst noch
einmal  verdeutlichen, daß die Floppy im
Prinzip ein eigenständiger Computer ist,
der nichts anderes tut,  als  darauf  zu
warten,  daß  Befehle  über  den IEC-Bus
kommen, um sie auszuführen. Hierbei mei-
ne ich nicht die Floppy-Befehle, wie wir
sie  bisher  kennengelernt  haben. Diese
stellen wiederum eine höhere  Ebene  von
Befehlen  dar. Da der IEC-Bus als Kommu-
nikationsschnittstelle zwischen C64  und
Floppy  dient, werden auf Ihm Daten ent-
weder vom Rechner zur Floppy oder  umge-
kehrt  hin- und herbewegt. Die Verarbei-
tung dieser Daten ist dann dem  jeweili-
gen Gegengerät überlassen, daß die Daten
empfangen hat. Noch dazu kann es vorkom-
men, daß sich bis zu 7 Geräte (der  C64,
max.  2  Drucker  und max. vier Floppys)
über ein und denselben  Bus  miteinander
unterhalten  müssen. In dem Fall muß vor
einer Datenübertragung natürlich festge-
legt  werden  welches der Geräte nun mit
welchem (im Normalfall dem 64er)  kommu-
nizieren  soll. Hierzu dienen nun insge-
samt sechs Betriebssystemroutinen.      
Als erstes müssen wir wieder einmal  ein
File auf die gewohnte Art und Weise öff-
nen. Zunächst übergeben wir die  Filena-
menparameter mit Hilfe von "SETNAM" (Ak-
ku=Länge des Namens, X/Y=Zeiger auf  Na-
mensstring).  Anschließend  muß "SETPAR"
aufgerufen werden. Hierbei kommen  File-
nummer  und  Geräteadresse, wie gewohnt,
in Akku und  X-Register.  Im  Y-Register
wird  die  Sekundäradresse abgelegt. Je-
doch müssen wir hier, anders als  sonst,
die  gewünschte Sekundäradresse plus $60
(=dez. 96) übergeben. Dies hat Floppyin-
terne  Gründe.  Möchten wir also den Be-
fehlskanal ansprechen, so  muß  die  Se-
kundäradresse  $6F  (dez. 111) übergeben
werden. Möchten wir ein PRG-File  lesen,
so  müssten  wir den Wert $60 übergeben,
etc. Zuletzt wird wieder wie üblich  das
File mittels "OPEN" geöffnet.           
Soweit also nichts Neues. Nun müssen wir
uns, wie vorher auch, verdeutlichen,  ob
die  Floppy senden, oder empfangen soll.
Anstelle von "CHKIN" oder "CKOUT" müssen
diesmal jedoch andere  Routinen  benutzt
werden.  Möchten wir Daten empfangen, so
soll die Floppy mit uns "reden". Deshalb
heißt die entsprechende Routine, die sie
dazu  auffordert  uns  Daten  zuzusenden
"TALK" (engl. "reden"). Wollen wir Daten
senden  soll  die  Floppy uns "zuhören".
Die hierfür gedachte Unterroutine  heißt
"LISTEN"  (engl.  "hören").  Da  es, wie
oben schon einmal beschrieben, bis zu  6
Geräte  geben kann, die uns zuhören oder
mit uns reden  sollen,  muß  mit  "TALK"
oder  "LISTEN"  auch  mitgeteilt werden,
welches der Geräte nun  für  uns  bereit
sein  soll. Hierzu müssen Sie vor Aufruf
der Routinen die Geräteadresse  des  ge-
wünschten Gerätes im Akku ablegen.      
Als  nächstes muß dem Gerät an der ande-
ren  Seite  mitgeteilt  werden,  welcher
seiner  offenen  Kanäle senden oder emp-
fangen soll. Hierzu  muß  die  Sekundär-
adresse an das Gerät übermittelt werden.
Dies   geschieht   über   die   Routinen
"SECTLK" und "SECLST".  Beide  verlangen
die  gewünschte  Sekundäradresse (wieder
plus  $60)  als   Parameter   im   Akku.
"SECTLK"  müssen  Sie  nach einem "TALK"
aufrufen, "SECLST" nach einem "LISTEN". 
Nun ist alles vorbereitet, um  Daten  zu
lesen  oder zu schreiben. Dies erledigen
Sie nun am besten mit  den  beiden  spe-
ziellen   IEC-Ein-/Ausgaberoutinen,  die
ich oben schon ansprach. Sie können  sie
genauso  wie "BASIN" und "BASOUT" benut-
zen. Zum Lesen von  Daten  benutzen  Sie
die  Routine  "IECIN",  die das gelesene
Byte im Akku zurückgibt.  Zum  Schreiben
benutzen   Sie   "IECOUT",  der  das  zu
schreibende Byte im Akku übergeben  wer-
deb muß.                                
Haben Sie die Ein- oder Ausgabe beendet,
so müssen  Sie  das  entsprechende  Emp-
fangs-,  bzw.  Sendegerät  wieder in den
Wartezustand  zurückbringen.  Dies   ge-
schieht  über  die Routinen "UNTALK" und
"UNLIST". Die erstere  müssen  Sie  nach
dem  Empfang, die letztere nach dem Sen-
den von Daten benutzen. Dies müssen  Sie
auch  immer dann tun, wenn Sie anschlie-
ßend auf dem selben  Kanal  eine  andere
Operation  ausführen wollen (z.B. Befehl
an Befehlskanal senden und  anschließend
Floppystatus auslesen).                 
Wenn  Sie mit Ihrer Operation nun fertig
sind, so können Sie den zuvor geöffneten
Filekanal wieder schließen.             
Wie  Sie  sehen  ist diese Methode etwas
aufwendiger. Dafür aber können  Sie  nun
problemlos  den  Befehlskanal öffnen und
z.B.  den  "Memory-Read"-Befehl  ("M-R")
benutzen. Hierbei öffnen Sie einen File-
kanal und teilen der Floppy mit, daß sie
Daten  empfangen  soll   ("LISTEN"   und
"SECLST"  aufrufen). Nun senden Sie mit-
tels  "IECOUT"  den  Befehlsstring.  An-
schließend  setzen Sie die Floppy wieder
in der Normalstatus  zurück,  indem  Sie
"UNLIST"  aufrufen.  Hiernach können Sie
nun die zuvor verlangten Daten  vom  Be-
fehlskanal  abholen.  Senden Sie einfach
ein "TALK" und "SECTLK"  und  lesen  Sie
die  Daten mit "IECIN" ein. Abschließend
muß die Floppy wieder  mit  "UNTALK"  in
den  Wartezustand  geschickt  werden und
wir können das File schließen.          
Desweiteren ist das  Bedienen  von  zwei
offenen  Files  nun  weitaus  besser mö-
glich. Bevor Sie eines dieser Files ans-
prechen, müssen Sie einfach den entspre-
chenden Öbertragungsmodus festlegen  und
seine   Sekundäradresse  an  die  Floppy
schicken. Denken Sie  immer  daran,  daß
Sie  nach jeder Lese- oder Schreibopera-
tion ein "UNTALK" oder  "UNLIST"  senden
müssen,  damit  die  Floppy  auf weitere
Befehle reagiert.                       
Hier nun noch die Einsprung-Adressen der
neuen I/O-Routinen:                     
LISTEN: $FFB1                           
  TALK: $FFB4                           
SECLST: $FF93                           
SECTLK: $FF96                           
UNLIST: $FFAE                           
UNTALK: $FFAB                           
IECOUT: $FFA8                           
 IECIN: $FFA5                           
PROGRAMM-BEISPIELE                      
Kommen wir nun noch zu einigen Programm-
beispielen, die Ihnen die Benutzung  der
neuen I/O-Routinen erläutern sollen.    
Die Routinen aus Beispiel 1 und 2 finden
Sie, als Hypra-Ass Quellcode, auf dieser
MD  unter  dem  Namen  "FK.IO2.S".   Der
Quellcode  für  Beispiel 3 steht im File
"FK.SHOWBAM.S". Da es sich dabei um  ein
eigenständiges   Programm       handelt,
existiert hierzu auch  noch  ein,  durch
'RUN'  startbares, File namens "FK.SHOW-
BAM".                                   
1) LESEN UND SCHREIBEN EINES FILES      
Zunächst einmal möchte ich Ihnen zeigen,
wie die beiden Routinen  "READFILE"  und
"WRITEFILE"  aus dem letzten Kursteil in
der neuen Methode aussehen. Hierbei müs-
sen  wir  lediglich  den Aufruf von "CH-
KIN/CKOUT" mit  dem  von  "TALK/SECTALK"
oder  "LISTEN/SECLIST" ersetzen. Der Au-
fruf von "CLRCH" am Ende der Öbertragung
muß mit  einem  "UNTALK"  bzw.  "UNLIST"
ersetzt  werden. Desweiteren muß den Se-
kundäradressen beim Üffnen der Wert  $60
hinzuaddiert werden ('$60' bei ReadFile,
'$61' bei WriteFile).                   
Der  Rest  des Programms sollte sich von
selbst erklären. Damit man die alte  und
neue  Version  der Routinen nicht mitei-
nander  verwechselt,  heißen  die  neuen
Versionen  "IECREAD" und "IECWRITE". Die
Parameter,  die  beim  Aufruf  übergeben
werden  müssen,  sind  dieselben wie bei
den alten Versionen:                    
IECREAD   stx $fb    ;Startadresse in   
          sty $fc    ; Zeiger ablegen.  
          ldx #$34   ;Zeiger auf        
          ldy #$03   ; Filenamen        
          jsr setnam ; setzen.          
          lda #01    ;Fileparameter     
          ldx #08    ; setzen           
          ldy #$60   ; ($60=PRG Lesen)  
          jsr setpar                    
          jsr open   ;File öffnen       
          lda #08    ;"Gerät Nr. 8:     
          jsr talk   ; rede bitte!"     
          lda #$60   ;"Und zwar auf Se- 
          jsr sectlk ; kundäradr. $60)!"
          ldy #00    ;Offset=0          
loop1     jsr iecin  ;Zeichen lesen     
          sta ($fb),y; und ablegen      
          inc $fb    ;Zeiger            
          bne l1     ; um 1             
          inc $fc    ; erhöhen          
l1        lda $90    ;Ende erreicht?    
          beq loop1  ;Nein, dann weiter!
          lda #08    ;Sonst: "Gerät     
          jsr untalk ; Nr.8:Ruhe bitte!"
          lda #01    ;File              
          jmp close  ; schließen        
;***************************************
IECWRITE  stx $fb    ;Startadresse in   
          sty $fc    ; Zeiger ablegen   
          ldx #$34   ;Filenamen         
          ldy #$03   ; setzen           
          jsr setnam                    
          lda #01    ;Fileparameter     
          ldx #08    ; setzen           
          ldy #$61   ; ($61=PRG schr.)  
          jsr setpar                    
          jsr open   ;File öffnen       
          lda #08    ;"Gerät Nr8: Hör'  
          jsr listen ; mir bitte zu!'   
          lda #$61   ;"Und zwar auf Sek.
          jsr seclst ; Adr. $61!")      
          ldy #00    ;Offset=0          
loop2     lda ($fb),y;Zeichen holen     
          jsr iecout ; und abschicken   
          inc $fb    ;Zeiger            
          bne l2     ; um 1             
          inc $fc    ; erhöhen          
l2        lda $fe    ;Und mit           
          cmp $fc    ; Endadresse       
          bne loop2  ; vergleichen      
          lda $fd    ;Wenn ungleich     
          cmp $fb    ; dann weiter      
          bne loop2                     
          lda #08    ;"Gerät Nr.8:      
          jsr unlist ; Bin fertig!"     
          lda #01    ;File              
          jmp close  ; schließen        
2) FLOPPYSTATUS ANZEIGEN                
Ein weiterer Vorteil, der sich  aus  der
Benutzung  der direkten IEC-Routinen er-
gibt, ist, daß Sie nun problemlos,  wäh-
rend  des Arbeitens mit einem File, Sta-
tusmeldungen auf dem Bildschirm ausgeben
können. Da die Standard Ein-/Ausgabe  ja
nicht verändert wurde, erscheinen Texte,
die  mit BSOUT ausgegeben werden sollen,
auch weiterhin auf dem Bildschirm. Umge-
kehrt  wird  bei BASIN von der Tastatur,
und nicht etwa aus dem offenen File  ge-
lesen.                                  
Als Beispiel für eine  solche  Anwendung
möchte  Ich  Ihnen das Auslesen des Feh-
lerkanals der Floppy zeigen. Hierzu  muß
lediglich der Befehlskanal geöffnet, und
die Fehlermeldung, die im ASCII-Code von
der  Floppy  generiert  und  übermittelt
wird, Zeichen für Zeichen ausgelesen und
auf dem  Bildschirm  ausgegeben  werden.
Ich  denke, die Routine erklärt sich von
selbst:                                 
ERRCHN    lda #00    ;Länge Filename=0  
          jsr setnam ; für "Kein Name"  
          lda #01    ;Fileparameter     
          ldx #08    ; setzen           
          ldy #$6f   ; (=Befehlskanal)  
          jsr setpar                    
          jsr open   ;Filekanal öffnen  
          lda #08    ;Floppy Nr.8 zum   
          jsr talk   ; Senden auffordern
          lda #$6f   ;Und zwar auf Kanal
          jsr sectlk ; mit Sek.Adr. $6F 
ecloop1   jsr iecin  ;Zeichen lesen     
          jsr bsout  ;Und auf dem Bild- 
                      schirm ausgeben   
          lda $90    ;Fileende erreicht?
          beq ecloop1; Nein, also weiter
          lda #08    ;Floppy Nr.8       
          jsr untalk ; zurücksetzen     
          lda #01    ;Und Befehlskanal  
          jmp close  ; schließen        
3) BAM ANZEIGEN                         
Als Nächstes  wollen  wir  ein  Programm
schreiben,   daß   uns  anzeigt,  welche
Blocks einer Diskette belegt  sind,  und
welche  nicht. Hierzu müssen wir die BAM
("Block  Availability  Map"),   die   im
Disk-Header-Block (Track 18 Sektor 0) zu
finden  ist,  auslesen und zu einer gra-
fikschen Anzeige  dekodieren.  Dies  ist
ein  hervorragendes  Beispiel um die Be-
nutzung der neuen  I/O-Routinen  zu  de-
monstrieren  und gleichzeitig zu zeigen,
wie man mit der BAM umgeht. Sie erinnern
sich vielleicht noch  an  deren  Aufbau.
Ich  hatte  ihn  in Teil 5 dieses Kurses
besprochen:                             
Im  Disk-Header-Block  sind  die Bytes 4
bis einschließlich 143 für die  BAM  re-
serviert. Hierbei zeigen jeweils 4 Bytes
in  Folge  die  Blockbelegung  für einen
Track an. Im ersten Byte steht dabei die
Gesamtanzahl der  freien  Blocks  dieses
Tracks.  Die  Bits des zweiten Bytes ko-
dieren die Belegung  der  Sektoren  0-7,
die  Bits des dritten Bytes die Belegung
der Sektoren 8-15 und die Bits des vier-
ten  Bytes die Belegung der Sektoren 16-
23. Hat ein Track weniger Sektoren,  als
im  vierten  Byte  angezeigt  werden, so
sind die restlichen  Bits  dieses  Bytes
unbenutzt  (so  z.B.  immer  die letzten
zwei Bits, da es  ja  nie  mehr  als  21
Tracks  gibt).  Ein gesetztes Bit bedeu-
tet, daß der entsprechende  Sektor  frei
ist,  ein  gelöschtes Bit, daß er belegt
ist.                                    
Wollen wir einmal klären,  welche  Dinge
zur Lösung des obigen Problems notwendig
sind. Zunächst müssen wir den Befehlska-
nal und anschließend  einen  Pufferkanal
öffnen.  Nun  muß  der Befehl zur Floppy
gesandt werden, der den  Block  18/0  in
den  geöffneten  Puffer  liest. Ist dies
geschehen, müssen wir nur noch  auf  das
vierte  Byte  des  Puffers positionieren
(dort beginnt ja die BAM) und  die  vier
Belegungs-Bytes für jeden Track einlesen
und darstellen.                         
     Bitte Teil 2 des Kurses laden!     
          Teil 2 Floppy Kurs 8          
Kommen wir also zu unserem Programm:    
main    lda #00       ;Bildschirm-      
        sta 53280     ; farben          
        lda #11       ; ein-            
        sta 53281     ; stellen         
        lda #<(text1) ;Text für Anzeige 
        ldy #>(text1) ; auf Bildschirm  
        jsr strout    ; ausgeben        
mloop5  lda #00       ;Filename setzen  
        jsr setnam    ; (0='Kein Name') 
        lda #01       ;Parameter: LFN 1 
        ldx #08       ; Ger.Adr 8       
        ldy #$6f      ; Sek.Adr 15      
        jsr setpar    ; setzen          
        jsr open      ;Bef.kan. öffnen  
        lda #01       ;Filename "#"     
        ldx #<(bufnam); (für 'Puffer    
        ldy #>(bufnam); reservieren')   
        jsr setnam    ; setzen          
        lda #02       ;Fileparameter    
        ldx #08       ; setzen          
        ldy #$62      ; (Sek.Adr=2)     
        jsr setpar                      
        jsr open      ;Puff.kan. öffnen 
Bis hier sollte wohl  alles  klar  sein.
Wir  haben  in den obigen Zeilen den Be-
fehlskanal und einen Pufferkanal mit der
Sekundäradresse 2 geöffnet (bitte denken
Sie daran, daß beim Üffnen und beim  Be-
nutzen  der  Routinen  SECTLK und SECLST
diese Adresse plus $60 übergeben  werden
muß).                                   
Als  nächstes  können  wir  die   beiden
Floppybefehle  zum Lesen des Blocks 18/0
und zum Positionieren auf Byte 4 senden:
        lda #<(com1)  ;Befehl 'Block    
        ldy #>(com1)  ; 18/0 lesen'     
        jsr sendcom   ; senden.         
        lda #<(com2)  ;Befehl 'Puffer-  
        ldy #>(com2)  ; zeiger auf      
        jsr sendcom   ; Byte 4' senden. 
Wie  Sie  sehen,  benutze  ich hier eine
eigene Routine, um die Befehle  zu  sen-
den.  Sie  heisst "SENDCOM" und benötigt
einen Zeiger auf den Befehlstext in Akku
und  Y-Register.  Zur  Beschreibung  der
Routine kommen wir später. Hier noch die
Syntax  beiden Befehle, die oben gesandt
werden. Sie finden Sie ganz am Ende  des
Quelltextes:                            
com1    .tx "u1 2 0 18 0"               
        .by 13,0                        
com2    .tx "b-p 2 4"                   
        .by 13,0                        
Wie Sie sehen benutzen wir  die  Befehle
"U1" (die bessere Version von "B-R") und
"B-P". Die  einzelnen  Parameter  werden
dabei  als  ASCII-Werte  übertragen. Wie
Sie auch bemerken, so wird bei  den  Be-
fehlen die tatsächliche Sekundäradresse,
nämlich  2  und nicht etwa $62, benutzt.
Der Wert 13 am Ende jedes  Befehls  wird
benötigt,  um  der  Floppy  das Ende des
Befehlsstrigs zu  signalisieren.  Die  0
wird  nicht  mitgesandt.  Sie  dient als
Endmarkierung für die SENDCOM-Routine.  
Machen wir nun jedoch weiter  im  Quell-
text des Hauptprogramms. Die nun folgen-
den Zeilen versetzen die Floppy  in  den
Sendestatus   und   bereiten  die  Bild-
schirmausgabe der  Tracks  vor.  Hierbei
wird  ein Zeiger auf den Bildschirmspei-
cher in $FB/$FC abgelegt, der  nach  der
Ausgabe eines Sektors um 40 erhöht wird.
So  erreichen wir eine vertikale Ausgabe
des Tracks. Für den nächsten  Schleifen-
durchlauf wird der Zeiger wieder auf den
Startwert+1  (nächste  Spalte) zurückge-
setzt:                                  
        lda #08       ;Floppy Nr. 8 zum 
        jsr talk      ;Senden auffordern
        lda #$62      ;Und zwar auf     
        jsr sectlk    ; Sek.Adr 2       
        lda #34       ;Trackzähler      
        sta $03       ; initialisieren  
        lda #163      ;Grundwert Zeiger 
        sta mloop3+1  ; einstellen      
mloop3  ldx #163      ;Bildschirmpo-    
        ldy #04       ; sitionszeiger   
        stx $fb       ; initialisieren  
        sty $fc                         
        inc mloop3+1  ;Spalte für näch- 
                       sten Track um 1  
                       erhöhen          
Nun erfolgt das  eigentliche  Lesen  und
Ausgeben  der  BAM-Bytes. Hierbei müssen
wir immer das jeweils erste Byte überle-
sen,  da  es  ja keine Blockbelegung an-
sich, sondern nur die Anzahl der  freien
Blocks enthält. Die folgenden zwei Bytes
können  normal ausgegeben werden. Da das
letzte Byte immer eine  unterschiedliche
Anzahl  an unbelegten Bits enthält, müs-
sen wir hier aufpassen, daß nur  soviele
Bits  ausgegeben werden, wie tatsächlich
benutzt werden. Dies geschieht über  die
Tabelle  "SECTAB",  die  die  Anzahl der
benutzen Blocks des letzten Bytes  eines
jeden  Tracks  beinhaltet.  Hier  jedoch
erst einmal wieder das Listing:         
        jsr iecin     ;1. Byte überlesen
        jsr iecin     ;2. Byte lesen    
        jsr byteout1  ; und ausgeben    
        jsr iecin     ;3. Byte lesen    
        jsr byteout1  ; und ausgeben    
        jsr iecin     ;4. Byte lesen    
        ldy $03       ;Tracknr. als Zei-
                       ger holen        
        ldx sectab,y  ;Anzahl benutzte  
                       Bits des Tracks  
                       holen.           
        jsr byteout2  ;Und Byte ausgeben
        dec $03       ;Trackzähler-1    
        bpl mloop3    ;Nochmal, wenn >0 
Wie  Sie  sehen, benutze ich eine eigene
Routine zur Ausgabe eines BAM-Bytes. Sie
heisst "BYTEOUT1" und gibt die Bits  0-7
des  BAM-Bytes  vertikal  auf  dem Bild-
schirm aus. Hierbei  wird  ein  "o"  ge-
schrieben,  wenn  der  Block belegt ist,
und ein ".", wenn er unbelegt  ist.  Die
Routine  "BYTEOUT2"  ist im Prinzip die-
selbe, wie BYTEOUT1. Der einzige  Unter-
schied  ist,  daß die zweite Version die
Anzahl  der  auszugebenden  Bits  im  X-
Register  verlangt.  Diese wird nun über
die  SECTAB-Tabelle  eingelesen.  Achten
Sie  darauf, daß die Liste rückwärts ge-
lesen wird, und somit der Reihe nach die
Blockanzahlen der Tracks  35-1  (jeweils
nur  für das letzte Byte und minus 1) in
der Liste verzeichnet sind.  Sie  finden
die Liste ebenfalls am Ende des Quellco-
des.                                    
Fahren  wir nun weiter im Hauptprogramm.
Nachdem alle BAM-Bytes gelesen und  aus-
gegeben  wurden, können wir Befehls- und
Pufferkanal wieder schließen. Vorher muß
die Floppy allerdings wieder in den War-
tezustand zurückversetzt werden. Hierauf
folgt  noch  eine Tastenabfrage, die das
Programm bei Tastendruck wiederholt  und
bei  der Taste '<' den Bildschirm löscht
und zurückspringt:                      
        lda #08       ;Floppy wieder    
        jsr untalk    ; zurücksetzen.   
        lda #02       ;Pufferkanal      
        jsr close     ; schließen       
        lda #01       ;Befehlskanal     
        jsr close     ; schließen       
mloop4  jsr inkey     ;Taste holen      
        beq mloop4    ; Keine --> weiter
        cmp #95       ;= '<' ?          
        beq end       ;Ja, also ENDE.   
        jmp mloop5    ;Nein,also nochmal
end     jmp $e544                       
Wollen wir uns nun die  Routine  SENDCOM
anschauen.  Im  Prinzip  ist  sie nichts
anderes als eine STROUT-Routine, nur daß
die Ausgabe nicht  auf  dem  Bildschirm,
sondern  an die Floppy erfolgt. Sie fin-
den  sie  in  den  Zeilen  990-1070  des
Quelltextes:                            
sendcom   sta $fb     ;Zeiger auf String
          sty $fc     ; ablegen         
          lda #08     ;Floppy Nr.8 zum  
          jsr listen  ; Empfang auffor- 
                        dern            
          lda #$6f    ;Und zwar von Sek.
          jsr seclst  ; Adr. 15         
          ldy #00     ;Offset=0         
scloop1   lda ($fb),y ;Zeichen lesen    
          beq s1      ;Wenn 0 --> fertig
          jsr iecout  ;Zeichen senden   
          iny         ;Zeiger um 1      
          bne scloop1 ; erhöhen und     
          inc $fc     ; Schleife wieder-
          jmp scloop1 ; holen           
s1        lda #08     ;Ende. Floppy Nr. 
          jmp unlist  ; 8 zurücksetzen  
Das soll es dann wieder einmal für  die-
sen  Monat  gewesen  sein.  Sie sind nun
komplett in  die  Bedienung  der  Floppy
1541  eingeführt  und sollten eigentlich
auch schon  eigene  Programme  schreiben
können.  Trotzdem wird es nächsten Monat
noch  einen  Teil  dieses  Floppy-Kurses
geben,  in  dem  wir ein ganz besonderes
Beispielprogramm besprechen werden  wol-
len.                                    
                                    (ub)
----------------------------------------
               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
In  dieser Reihenfolge werden die Blocks
des Track 18 IMMER belegt. Der Wert  $FF
am Ende der Liste steht hier als Endmar-
kierung der Liste, die später  beim  Be-
schreiben  der  Dirblocks  von Bedeutung
ist. Nun müssen wir erst einmal die Sek-
tornummer  des  ersten  freien Dirblocks
ermitteln:                              
ok     lda #19         ;Von der Gesamt- 
                        sektorzahl (19) 
                        für Track 18    
       sec             ;die Anzahl der  
       sbc dirfree     ;freien Dirblocks
                        subtrahieren    
                        (=Anzahl belegte
                        Blocks) und als 
       tay             ;Index in Y-Reg. 
       lda blktab,y    ;Sektornr. lesen 
       sta dirstrt     ;und speichern.  
       sty dsindex     ;Index speichern 
Nun wollen wir das File auf der Zieldis-
kette anlegen. Hierzu schreiben  wir  es
zunächst  ganz normal mittels der WFILE-
Routine. Hierbei sollen jedoch  nur  die
Bytes geschrieben werden, die nicht mehr
in  die Dirblocks passen. Wir müssen nun
also die Anfangsadresse des  zu  schrei-
benden  Files  berechnen.  Da die ersten
beiden Bytes eines Datenblocks immer als
Zeiger auf den nächsten Datenblock  die-
nen,  müssen  wir  also  den  Wert  DIR-
FREE*254 zur FILEMEM-Adresse hinzuaddie-
ren,  um unsere Anfangsadresse zu erhal-
ten:                                    
       ldx dirfree     ;DIRFREE in X    
                        laden (=HiByte  
                        Startadr.).     
       txa             ;u. in Akku holen
       dex             ;HiByte-1        
       asl             ;Akku*2          
       eor #$ff        ;und invertieren 
       clc             ;mit             
       adc #1          ;Öbertrag.       
       adc #<(filemem) ;LoByte von      
       bcc m5          ;FILEMEM         
       inx             ;addieren.       
m5     sta $fd         ;unde ablegen    
       txa             ;HiByte holen    
       clc             ;und mit HiByte  
       adc #>(filemem) ;FILEMEM add.    
       sta $fe         ;u. ablegen      
       jsr wfile       ;File schreiben  
Nachdem die Anfangsadresse berechnet und
in $FD/$FE abgelegt wurde, wird die WFI-
LE-Routine  aufgerufen.  Die  Endadresse
muß  in  $F9/$FA stehen, wo sie noch von
der RFILE-Routine  enthalten  ist  (wird
von ihr als Lesezeiger verwedet).       
Ist  das File nun geschrieben, so müssen
wir nur noch seinen Fileeintrag aus  dem
Directory heraussuchen, Track und Sektor
des  ersten  freien  Dirblocks eintragen
und die fehlenden DIRFREE*254  anfängli-
chen Bytes in den Dirblocks unterzubrin-
gen:                                    
       jsr openio      ;Kanäle öffnen   
       jsr chgent      ;Eintrag suchen  
                        und ändern.     
       jsr wblocks     ;DirBlocks       
                        schreiben.      
       jsr closeio     ;Kanäle schließen
       lda #<(text4)   ;"File install-  
       ldy #>(text4)   ;liert"          
errout jsr strout      ;ausgeben.       
eo1    jsr inkey       ;Auf Tastendruck 
       beq eo1         ;warten.         
       jmp main        ;Und neu starten 
Soviel also  zu  unserer  Steuerroutine.
Kommen wir nun zu den Unterfunktionen:  
2) STRIEC                               
Diese Routine gibt einen  Zeichenstring,
dessen  Adresse  in  Akku und Y-Register
steht,  an  den  Befehlskanal  aus.  Der
String  muß  mit  einem Nullbyte beendet
sein:                                   
striec   sta $fb       ;Adresse in Zei- 
         sty $fc       ;ger ablegen.    
         lda #8        ;Floppy auf      
         jsr listen    ;Befehlskanal    
         lda #$6f      ;empfangsbereit  
         jsr seclst    ;machen.         
         ldy #0        ;String lesen    
siloop1  lda ($fb),y   ;und senden.     
         bne si1                        
         lda #8        ;Floppy zurück-  
         jmp unlist    ;setzen u. Ende. 
si1      jsr iecout    ;Zeichen senden  
         iny           ;und Zeiger+1    
         bne siloop1                    
         inc $fc                        
         jmp siloop1                    
3) SENDCOM                              
Diese  Routine wird dazu verwandt, einen
Floppybefehl zu senden.  Hierbei  unter-
scheidet sie sich jedoch von der Routine
STRIEC.  Es  wird  nämlich nicht nur der
Zeiger des Befehls übergeben (in  X  und
Y-Register),  sondern  auch  ein Wert im
Akku, der vor dem Senden in ASCII  umge-
wandelt  und an den Befehlsstring in X/Y
angehängt wird. Der gesamte Befehl  wird
dabei bei COMBUFF abgelegt und dann mit-
tels STRIEC an die Floppy gesandt:      
sendcom pha            ;Akku retten     
        stx scloop1+1  ;Stringzeiger    
        sty scloop1+2  ;setzen          
        ldy #0         ;Und String      
scloop1 lda $c000,y    ;nach COMBUFF    
        beq sc1        ;umkopieren.     
        sta combuff,y                   
        iny                             
        jmp scloop1                     
sc1     sty mem0       ;Zeiger retten,  
        pla            ;Akku holen,     
        jsr i2a        ;konvertieren.   
        ldy mem0       ;Zeiger zurückh. 
        ldx #0         ;und in ASCII    
scloop2 lda numbuff,x  ;konvertierte    
        beq sc2        ;Zahl an COMBUFF 
        sta combuff,y  ;anhängen.       
        inx                             
        iny                             
        jmp scloop2                     
sc2     lda #13        ;CR anhängen     
        sta combuff,y                   
        iny                             
        lda #0         ;Endmarkierung   
        sta combuff,y  ;anhängen        
        lda #<(combuff);und Inhalt von  
        ldy #>(combuff);COMBUFF an      
        jmp striec     ;Floppy senden.  
4) WDUMMY                               
Kommen wir nun zur Routine "WDUMMY". Wir
sollten zunächst einmal klären, wozu sie
benötigt wird. Wie Sie oben sahen,  wird
sie  aufgerufen, noch BEVOR irgendendet-
was anderes getan wird. Hierbei tut  sie
eigentlich  nichts anderes, als ein File
mit dem Namen in NAME und MEM0  auf  der
Zieldiskette anzulegen und gleich darauf
wieder  zu  löschen. Das deshalb notwen-
dig, um sicherzustellen,  daß  auch  die
richtige  Anzahl  an  Dirblocks als 'be-
legt' gekennzeichnet ist.  Sollten  näm-
lich genau 8 (oder 16,24,etc.) Files auf
der Zieldiskette vorhanden sein, so kann
es  zu  Problemen kommen. Beim Schreiben
eines neuen Files muß das DOS dann  näm-
lich  einen  neuen  Dirblock hinzufügen,
der ab dann nicht mehr zur Datenspeiche-
rung benutzt werden kann. Damit es dabei
keine  Konfrontationen  gibt,  wird  das
File also sporadisch schon einmal  ange-
legt  und direkt danach wieder gelöscht.
Der neue DirBlock wird dann nicht wieder
freigegeben. Der Eintrag bleibt  nämlich
erhalten,  es wird lediglich der Filetyp
'DEL' an das Programm vergeben. Hier nun
also die Routine:                       
wdummy   lda mem0      ;Filename        
         ldx #<(name)  ;in NAME         
         ldy #>(name)  ;und MEM0        
         jsr setnam    ;setzen.         
         lda #1        ;Fileparameter   
         ldx #8        ;"1,8,1" für     
         ldy #1        ;("PRG saven")   
         jsr setpar    ;setzen.         
         jsr open      ;File öffnen.    
         ldx #1        ;Kanal 1 als     
         jsr ckout     ;Ausgabefile     
         jsr bsout     ;Byte ausgeben.  
         jsr clrch     ;Standardkanäle  
                        zurücksetzen.   
         lda #1        ;File wieder     
         jsr close     ;schließen.      
         jsr opencom   ;Befehlskanal    
                        öffnen          
         lda #<(name-2);Name-2 als      
         ldy #>(name-2);Adresszeiger    
         jsr striec    ;senden.         
         lda #1        ;Befehlskanal    
         jmp close     ;schließen.      
Nun  kann  ich Ihnen auch den Grund zei-
gen, warum der  Filename  im  Sourcecode
abgelegt  wird. Hier ist nämlich vor dem
eigentlichen Namen auch  noch  der  Text
"S:"  abgelegt.  Wenn  wir nun NAME-2 an
STRIEC übergeben, so enspricht das einem
Scratchbefehl für den Filenamen  ("S:NA-
ME"). Da STRIEC ein Nullbyte als Endmar-
kierung  verlangt,  wurde   die   GETIN-
Routine  so  ausgelegt, daß sie nach dem
letzten eingelesenen Zeichen ein solches
Byte in den NAME-Puffer  schreibt.  Hier
nun  die  Source-Definition  des Filena-
menspuffers. Für  den  Namen  werden  17
Bytes  reserviert, da ja maximal 16 Zei-
chen plus die Endmarkierung  0  vonnöten
sind:                                   
         .text "s:"                     
name     .byte 0,0,0,0,0,0,0,0          
         .byte 0,0,0,0,0,0,0,0,0        
5) GETNEED                              
Diese  Routine  wird benutzt, um die An-
zahl der vom Quellfile benötigten Blocks
zu ermitteln. Hierbei wird zunächst  die
Startadresse  des  Filespeichers subtra-
hiert, um die effektive Länge  in  Bytes
zu  ermitteln.  Hiernach  wird das High-
Byte mit zwei  multipliziert.  Dies  ist
nämlich  die  Anzahl Bytes, die Aufgrund
des Wegfalls  der  ersten  beiden  Bytes
eines Datenblocks zu der Anzahl Lo-Bytes
addiert werden muß. Ist dieser Wert grö-
ßer als 256, so wird ein Block mehr  ge-
braucht.  Jetzt wird noch die Anzahl der
Low-Bytes  hinzuaddiert.  Gibt  es  auch
hier  einen  Öberlauf, so muß wieder ein
Block hinzuaddiert werden.  Letztendlich
muß  wieder   ein Block hinzugefügt wer-
den, da die letzten Bytes,  selbst  wenn
es  weniger  als 254 sind, dennoch einen
ganzen Block belegen:                   
getneed ldx #0         ;NEED            
        stx need       ;löschen.        
        lda $f9        ;Endadr. d.      
                        Quellfiles (von 
        ldx $fa        ;RFILE noch da)  
                        lesen.          
        sec            ;und Startadresse
        sbc #<(filemem);subtrahieren.   
        bcs rf1        ;Ergebnis wird in
        dex            ;MEM1 (LoByte)   
rf1     sta mem1       ;und             
        txa            ;MEM2 (HiByte)   
        sec            ;abgelegt.       
        sbc #>(filemem)                 
        sta mem2                        
        rol            ;HiByte*2        
        bcc cn1        ;Wenn<256, weiter
        inc need       ;Sonst Blocks+1  
cn1     clc            ;LoByte addieren 
        adc mem1                        
        beq cn2                         
        bcc cn2                         
        inc need       ;Bei Öberlauf    
                        Blocks+1        
cn2     inc need       ;Und nochmal     
                        Blocks+1        
        lda mem2       ;Und Hi-Byte     
        clc            ;addieren.       
        adc need       ;und in NEED     
cn3     sta need       ;ablegen.        
        rts                             
6) GETDISKF                             
Kommen wir nun zur Routine zum Feststel-
len der freien Blocks der Diskette. Die-
ser Wert ist normalerweise nicht  direkt
aus dem DiskHeaderBlock zu erfahren. Die
einzige Möglichkeit wäre, die Blockbele-
gungen der einzelnen Tracks aus der  BAM
zu  lesen  und aufzusummieren. Aber auch
hier wollen wir uns eines kleinen Tricks
bedienen.  Wird  eine  Diskette  nämlich
initialisiert  (was  wir beim Üffnen des
Befehlskanals schon tun), so  liest  die
Floppy  die BAM der Diskette automatisch
ein und berechnet die Anzahl der  freien
Blocks  von  selbst.  Diese  Anzahl wird
dann in den Bytes $02FA (Lo)  und  $02FC
(Hi)  des  Floppyspeichers abgelegt. Was
liegt also näher, als  diesen  Wert  di-
rekt,  über  den  Memory-Read-Befehl der
Floppy auszulesen.  Und  nichts  anderes
tut GETDISKF:                           
getdskf  lda #<(com5)  ;Memory-Read     
         ldy #>(com5)  ;Befehl          
         jsr striec    ;senden.         
         lda #8        ;Floppy sende-   
         jsr talk      ;bereit auf      
         lda #$6f      ;Befehlskanal    
         jsr sectlk    ;machen.         
         jsr iecin     ;Lo-Byte lesen   
         sta dskfree+0 ;und sichern.    
         jsr iecin     ;Byte überlesen. 
         jsr iecin     ;Hi-Byte lesen   
         sta dskfree+1 ;und sichern.    
         lda #8        ;Floppy zurück-  
         jmp untalk    ;setzen.         
Zusätzlich  hierzu muß noch das entspre-
chende Memory-Read-Kommando  im  Source-
text   abgelegt  werden  (3  Zeichen  ab
Adresse $02FA lesen):                   
com5     .text "m-r"                    
         .byte 250,2,3,13,0             
7) GETDIRF                              
Nun kommen wir zur Routine zum Ermitteln
der freien Directoryblocks. Hier  können
wir  keinen  Umweg gehen, sondern müssen
die BAM direkt auslesen. Da  das  Direc-
tory  ausschließlich  in Block 18 steht,
genügt  es,  das  erste  Byte  des  BAM-
Eintrags für Track 18 auszulesen. Dieses
Byte  steht  an Position 72 des DiskHea-
derBlocks. Wir  müssen  also  den  Block
18/0  in  den Puffer lesen, und den Puf-
ferzeiger  auf  72  setzen  um  an   die
gewünschte    Information   zu   kommen.
Gleichzeitig berechnet diese Routine die
Anzahl der insgesamt verfügbaren  Blocks
auf  Diskette.  Hierzu  müssen wir ledi-
glich den Inhalt von DSKFREE und DIRFREE
addieren und in ALLFREE ablegen.        
getdirf  lda #<(com6)  ;"Block 18/0     
         ldy #>(com6)  ;lesen"          
         jsr striec    ;senden.         
         lda #<(com7)  ;"Pufferzeiger   
         ldy #>(com7)  ;auf Byte 72"    
         jsr striec    ;senden.         
         lda #8        ;Floppy sende-   
         jsr talk      ;bereit auf      
         lda #$62      ;Pufferkanal     
         jsr sectlk    ;machen          
         jsr iecin     ;Wert lesen      
         sta dirfree   ;und sichern.    
         ldx dskfree+1 ;Hi-Byte lesen   
         clc           ;Lo-Byte zu      
         adc dskfree+0 ;DIRFREE         
         bcc gf1       ;addieren.       
         inx                            
gf1      sta allfree+0 ;und in ALLFREE  
         stx allfree+1 ;ablegen.        
         lda #8        ;und Floppy      
         jmp untalk    ;zurücksetzen.   
Auch hier benötigen wir zwei Sourcecode-
texte für die Diskkommandos:            
com6     .text "u1 2 0 18 0" ;Sekt. 18/0
         .byte 13,0          ;lesen     
com7     .text "b-p 2 72"    ;Pufferzgr.
         .byte 13,0          ;auf 72.   
8) CHGENT                               
Kommen wir nun zu einer der  wichtigsten
Routinen  unseres  Programms. Sie durch-
sucht die Directoryblocks  nach  unserem
Fileeintrag,   liest    aus   ihm    den
Track/Sektor des ersten  'normalen'  Da-
tenblocks aus und legt ihn in den Adres-
sen FTRACK und FSEKTOR  ab.  Desweiteren
wird  hier dann Track und Sektor des er-
sten, freien Directoryblocks eingetragen
und der Block wieder  auf  die  Diskette
zurückgeschrieben.  Auch  diese  Routine
benutzt die Sektorentabelle  BLKTAB,  um
die  Reihenfolge  der Directroyblocks zu
ermitteln. Desweiteren benötigt sie noch
eine weitere  Tabelle, in  der  die  An-
fangspositionen    der   einzelnen   Fi-
leeinträge eines  Directoryblocks  abge-
legt  sind.  Diese  Tabelle heißt ENTTAB
und sieht folgendermaßen aus:           
enttab   .byte 2,34,66,98,130,162       
         .byte 194,226                  
Kommen  wir  nun jedoch zur eigentlichen
Routine. Sie besteht im Prinzip aus zwei
ineinander  verschachtelten   Schleifen,
die nacheinander die einzelnen Dirblocks
einlesen und jeden einzelnen Eintrag mit
dem  Namen in NAME vergleichen. Wird der
Name gefunden, so werden  die  Schleifen
verlassen. Dann wird nochmals auf diesen
Eintrag  positioniert,  und zwar so, daß
der Bufferpointer direkt  auf  die  zwei
Bytes  für  Starttrack und -sektor zeigt
und dort die Werte für den ersten freien
Dirblock eingertragen (Track 18,  Sektor
in  DIRSTRT).  Abschließend  wird dieser
Directoryblock  inklusive  der  Änderung
wieder  auf die Diskette zurückgeschrie-
ben:                                    
chgent   lda #<(text6) ;"Suche          
         ldy #>(text6) ;Eintrag"        
         jsr strout    ;ausgeben        
         lda #1        ;Blockzähler     
         sta mem1      ;initialisieren  
celoop1  lda #8        ;Floppy zurück-  
         jsr untalk    ;setzen.         
         ldy mem1      ;Blkzähler holen 
         lda blktab,y  ;Sekt.nr. lesen, 
         sta mem3      ;ablegen,        
         ldx #<(com1)  ;und Sektor lesen
         ldy #>(com1)                   
         jsr sendcom                    
         inc mem1      ;Blkzähler+1     
         lda #0        ;Eintragszähler  
         sta mem2      ;initialisieren  
celoop2  ldy mem2      ;Eintr.z. holen  
         cpy #8        ;Mit 8 vgl.      
         beq celoop1   ;Ja, also näch-  
                        sten Block lesen
         lda enttab,y  ;Nein, also Pos. 
                        lesen,          
         sta mem4      ;ablegen,        
         ldx #<(com2)  ;und Pufferzeiger
         ldy #>(com2)  ;positionieren.  
         jsr sendcom                    
         inc mem2      ;Eintr.z+1       
         lda #8        ;Floppy zum      
         jsr talk      ;Senden auf      
         lda #$62      ;Pufferkanal     
         jsr sectlk    ;bewegen.        
         jsr iecin     ;Filetyp holen.  
         cmp #$82      ;u.m. "PRG" vgl. 
         bne celoop2   ;Wenn <>, dann   
                        direkt weiter.  
         jsr iecin     ;Sonst Track     
         sta ftrack    ;und Sektor      
         jsr iecin     ;holen und ab-   
         sta fsector   ;legen.          
Bitte nun Teil 3 des Floppy-Kurses laden
         ldy #$ff      ;Jetzt Filenamen 
celoop3  iny           ;mit NAME        
         jsr iecin     ;vergleichen     
         cmp name,y                     
         beq celoop3                    
         cpy #16       ;Wenn 16 Stellen 
         beq ce1       ;dann OK.        
         cmp #160      ;letztes Byte 160
                        (Endmarkierung) 
         bne celoop2   ;Nein, also wei- 
                        tersuchen.      
         lda name,y    ;Letztes Namens- 
         bne celoop2   ;byte=0? Nein,   
                        weitersuchen.   
ce1      lda #8        ;Floppy          
         jsr untalk    ;zurücksetzen.   
         lda mem4      ;Letzte Eintrags-
         clc           ;position holen, 
         adc #1        ;1 Addieren      
         ldx #<(com2)  ;und setzen      
         ldy #>(com2)                   
         jsr sendcom                    
         lda #8        ;Floppy zum      
         jsr listen    ;Empfang auf     
         lda #$62      ;Pufferkanal     
         jsr seclst    ;bereit machen.  
         lda #18       ;Tracknr.        
         jsr iecout    ;ausgeben.       
         lda dirstrt   ;SektorNr.       
         jsr iecout    ;ausgeben.       
         lda #8        ;Floppy          
         jsr unlist    ;Zurücksetzen    
         lda mem3      ;letzten Dir-    
         ldx #<(com3)  ;block holen,    
         ldy #>(com3)  ;und zurück-     
         jmp sendcom   ;schreiben.      
Auch hier  werden  zwei  Floppykommandos
benötigt. Diesmal jedoch welche mit feh-
lendem letzten Parameter, der von  SEND-
COM hinzugefügt wird:                   
com1     .text "u1 2 0 18";Einen DirBlk 
         .byte 0          ;lesen.       
com2     .text "b-p 2"    ;Auf best.    
         .byte 0          ;Byte posit.  
9) WBLOCKS                              
Dies ist die  zweite,  wichtige  Routine
unseres Programms. Sie füllt die freien 
Directoryblocks  mit  den  Bytes vom Fi-
leanfang, bis zu dem Punkt, an  dem  das
'normale'   File  beginnt.  Gleichzeitig
werden diese  Dirblocks  mit  Hilfe  des
Block-Allocate-Befehls  als 'belegt' ge-
kennzeichnet:                           
wblocks lda #<(text7)  ;"Schreibe Dir-  
        ldy #>(text7)  ;blocks"         
        jsr strout     ;ausgeben.       
        ldx #<(filemem);Zeiger auf      
        ldy #>(filemem);Filemem         
        stx $fd        ;setzen          
        sty $fe                         
wbloop1 lda #<(com4)   ;Pufferzeiger    
        ldy #>(com4)   ;auf 0 posi-     
        jsr striec     ;tionieren.      
        lda #8         ;Floppy auf      
        jsr listen     ;Pufferkanal     
        lda #$62       ;Empfangsbereit  
        jsr seclst     ;machen.         
        ldy dsindex    ;Indexzgr. holen 
wb4     iny            ;und erhöhen.    
        lda blktab,y   ;Sektornr. lesen 
        bpl wb1        ;Wenn>0, dann    
                        nicht letzter   
                        Block           
        lda ftrack     ;Wenn letzter    
        jsr iecout     ;Block, dann     
        lda fsector    ;Starttrack u.   
        jsr iecout     ;-sektor d. norm.
        jmp wb2        ;Files eintr.    
wb1     pha            ;Sonst Sekt. ret-
                        ten,            
        lda #18        ;Track 18        
        jsr iecout     ;senden,         
        pla            ;Sektor holen    
        jsr iecout     ;und senden.     
wb2     ldy #0         ;Anschließend    
wbloop2 lda ($fd),y    ;254 Datenbytes  
        jsr iecout     ;senden.         
        iny                             
        cpy #$fe                        
        bne wbloop2                     
        lda #8         ;Floppy zurück-  
        jsr unlist     ;setzen.         
        lda $fd        ;Zeiger neu      
        clc            ;positionieren   
        adc #$fe       ;(+254).         
        bcc wb3                         
        inc $fe                         
wb3     sta $fd                         
        ldy dsindex    ;Sektornummer    
        lda blktab,y   ;holen,          
        pha            ;retten,         
        ldx #<(com3)   ;und Sektor      
        ldy #>(com3)   ;schreiben.      
        jsr sendcom                     
        pla            ;Sektornummer    
        ldx #<(com8)   ;zurückholen u.  
        ldy #>(com8)   ;Block als 'be-  
        jsr sendcom    ;'legt' kenn-    
                        zeichnen.       
        inc dsindex    ;Index+1         
        dec dirfree    ;freie Dir-      
                        blocks-1 (dient 
                        als Schlei-     
                        fenzähler)      
        bne wbloop1    ;und wiederholen.
        rts                             
Auch hier eine Auflistung der benötigten
Floppybefehle.   COM3  und  COM8  dienen
hierbei als Halbbfehele, die von SENDCOM
ergänzt werden:                         
com3     .text "u2 2 0 18" ;Block auf   
         .byte 0           ;T18 schr.   
com4     .text "b-p 2 0"   ;Bufferptr.  
         .byte 13,0        ;auf 0 setzen
com8     .text "b-a 0 18"  ;Block be-   
         .byte 13,0        ;legen.      
Hiermit  ist der Floppykurs nun beendet.
Für  diejenigen  unter  Ihnen,  die  Ge-
schmack an der Programmierung der Floppy
gefunden haben, halten wir für die näch-
ste Ausgabe einen ganz  besonderen  Lek-
kerbissen parat: ab dann gibt es nämlich
einen  aufbauenden  Floppy-Kurs für Pro-
fis, in dem die Floppy  direkt  program-
miert wird.                             
Öbrigens: Sie sollten es nicht auf  sich
sitzen  lassen,  daß  unser Beispielpro-
gramm mit Einschränkungen arbeitet. Öben
Sie  sich  doch  einmal  selbst  in  der
Floppyprogrammierung  und  versuchen Sie
das Programm  so  zu  modifizieren,  daß
auch Files, die kürzer als die vorhande-
nen freien Dirblocks  sind,  installiert
werden  können.  Viel Spaß dabei und bei
der  Programmierung  der  Floppy  ansich
wünscht Ihnen,                          
                       Uli Basters (ub).



Valid HTML 4.0 Transitional Valid CSS!