Auch diesem Speichermedium kann man in Pascal noch einiges entlocken, beispielsweise den direkten Zugriff auf einzelne Sektoren oder auf das Directory. Die dafĂŒr verwendeten Routinen eignen sich zum Einbau in eigene Programm eund zeigen meist erst kombiniert ihre LeistungsfĂ€higkeit.
Aufbau der Diskette
Zur besseren VerstÀndlichkeit der folgenden Befehle wollen wir zunÀchst den Aufbau einer Diskette erlÀutern (siehe auch Grafik 1). Sie ist im unbenutzten Zustand noch unbrauchbar, denn erst das Formatieren der Diskette legt die Verteilung der einzelnen Bereiche fest. Das Standardformat einer ATARI ST-Diskette hat 80 Spuren, die in konzentrischen Kreisen um den Mittelpunkt angelegt sind. Jede Spur ist wiederum in neun Teilbereiche untergliedert, die sich Sektoren nennen und je 512 Byte umfassen. Mit diesen Sektoren arbeiten die nun folgenden Routinen.
Lesen und Schreiben eines Sektors
Die erste Routine dieser Folge ist der Direktzugriff auf bestimmte Sektoren der Diskette. Er ist im normalen Pascal-Wortschatz nicht enthalten, aber durch Einsatz der Betriebssystemroutinen erreichbar. DafĂŒr gibt es z. B. im XBIOS des ST die passenden Routinen. Sie benötigen als Parameter folgende Werte:
- Puffer [n â
512 Bytes]: Adresse auf einen ausreichend groĂen Puffer, oder ein entsprechend dimensioniertes Feld
- FĂŒller: unbenutzer Parameter
- Drivenummer (A=0, B = 1)
- Sektornummer (0...9)
- Tracknummer (0...79) bzw. [0...82]
- Diskettenseite (0...1) je nach Laufwerk
- Sektoranzahl [n]: Anzahl der Sektoren, die hintereinander gelesen werden (die Zahl darf aber nicht gröĂer sein als die Anzahl der Sektoren auf einem Track).
Der Aufruf zum Lesen lautet:
FLOPRD(buf,filler,devno,sectno, trackno,sideno,count)
Der Aufruf zum Schreiben lautet:
FLOPWR(buf,filler,devno,sectno, trackno,sideno,count)
Die Funktionen ĂŒbergeben nach ihrer AusfĂŒhrung einen Statuswert. Wenn der Wert Null ist, dann war der Aufruf erfolgreich, bei negativen Werten ist ein Fehler aufgetreten. Solche Werte sollten vom Programm abgefragt werden, um eventuell auftretende Fehler abfangen zu können.
Wenn man nun einen Sektor lesen oder schreiben will, betrĂ€gt der Puffer 512 Bytes und die Sektoranzahl â1â.
program DISC_COPY;
CONST disk_A=0;
disk_B=1;
vorne=0;
hinten=1;
TYPE buff = packed array [1..5000] of char;
VAR dummy : long_integer;
track,sector,count,drive,side,
error,error1,error2,error3,error4 : integer;
daten1,daten2 : buff;
ch : char;
function FLOPRD (VAR buffer: buff; dummy: long_integer; drive,
sector,track,side,count : integer ): integer; xbios(8);
function FLOPWR (VAR buffer: buff; dummy: long_integer; drive,
sector,track,side,count : integer ): integer; xbios(9);
begin { Hauptprogramm }
sector:=1; count:= 9;
writeln (chr(27),'E Backup A â> B');
writeln;
writeln (' Original-Diskette in Laufwerk A');
writeln (' Ziel-Diskette in Laufwerk B');
writeln;
writeln (' (q)uit');
readln (ch);
if ch<>'q' then begin
for track:=0 to 79 do begin
error1:=floprd (daten1,dummy,disk_A,sector,track,vorne,count);
error2:=floprd (daten2,dummy,disk_A,sector,track,hinten,count); {SF 314}
error3:=flopwr (daten1,dummy,disk_B,sector,track,vorne,count);
error4:=flopwr (daten2,dummy,disk_B,sector,track,hinten,count); {SF 314}
writeln(chr(27),âY',chr(40),ehr(37),* Track â,track:3);
writeln(â ******* Status *******'); writeln(errorl:5,error2:5,error3:5,error4:5);
end;
writeln (' Backup ready !!â)
end;
repeat until keypress;
end.
Listing 1
Beim Beispiel des einfachen Diskcopy-Programms (Listing 1) werden aus ZeitgrĂŒnden immer neun Sektoren, also ein gesamter Track, gelesen; der Puffer muĂ deshalb ausreichend dimensioniert werden. Die gelesenen Daten werden danach sofort auf die Diskette in Laufwerk B geschrieben. Das Programm benötigt deshalb zwei Laufwerke. AuĂerdem ist zu beachten, daĂ es momentan von zweiseitigen Laufwerken ausgeht, denn es wird jeweils ein Track auf der Vorder- und RĂŒckseite gelesen. Bei Verwendung von einseitigen Laufwerken entfallen dann die Lese- und Schreibbefehle fĂŒr die zweite Seite (mit jSF 314] gekennzeichnet).
WĂ€hrend des Kopierbetriebs werden die Nummern des gerade in Arbeit befindlichen Tracks und die RĂŒckgabewerte angezeigt. Das Programm macht keine Fehlerabfrage, die Fehlerwerte werden jedoch sehr kurz (!) angezeigt. Eine Verbesserung an dieser Stelle ist dringend zu empfehlen.
Will man den Inhalt eines Sektors nÀher untersuchen oder anschauen, so ist die Definition des Puffers entscheidend. Ist dieses Feld als CHAR definiert, erhÀlt man den Sektorinhalt als Zeichen, bei Definition als BYTE erscheinen die entsprechenden Zahlenwerte.
Laufwerkskennung
Manchmal ist es notwendig, das momentan aktuelle Laufwerk zu kennen. DafĂŒr gibt es die Routine DGETDRV. Sie liefert den gewĂŒnschten numerischen Wert des momentanen Laufwerks und, nach einer einfachen Umrechnung, auch den Kennbuchstaben (siehe Listing 2).
Das Laufwerk, von dem ein Programm geladen wurde, ist das momentan aktive Laufwerk. Es wird beim Diskettenzugriff ohne Diskangabe angesprochen. Will man ein anderes Laufwerk zum aktiven erklÀren, verwendet man folgende Routine:
altdrive:= DSETDRV (neudrive)
wobei Laufwerk A = 0; B = 1; C = 2; ...
In altdrive wird die Nummer des vorherigen aktiven Laufwerks zurĂŒckgegeben.
Speicherplatz
Uber die Aufteilung eines Massenspeichers in Cluster, Sektoren, Byte, belegte und freie Bereiche gibt die Gemdos-Funktion DFREE Auskunft. Sie benötigt als Angabe nur die Laufwerksnummer (wobei Null dem aktuellen Laufwerk (!) entspricht, A=1, B=+ usw.) und gibt dann in einem Puffer vier Werte zurĂŒck:
buff[1] Anzahl der freien Cluster
buff[2] Anzahl der Cluster
buff[3] Anzahl der Byte/Sektor
buff[4] Anzahl der Sektoren/Cluster
Daraus lĂ€Ăt sich dann die Anzahl der freien, belegten und aller verfĂŒgbaren Bytes berechnen (siehe Listing 3).
Directory
Eine bei manchen Anwendungen wichtige Routine ist die Anzeige des Directories. Dazu sind mehrere Systemaufrufe nötig. Der erste lautet FSETDTA und setzt die Disketten-Transfer-Adresse, ab der DTA-Puffer steht. In diesem Puffer werden, von den folgenden Befehlen, die kompletten Directory-Informationen abgelegt. Der Puffer muĂ eine GröĂe von 44 Byte haben und kann als ARRAY oder RECORD angelegt werden. Er enthĂ€lt folgende Informationen:
Byte |
Inhalt |
1...21 |
reserviert fĂŒr TOS 22 Attribut |
23.. .24 |
Uhrzeit |
25..26 |
Datum |
27..30 |
FilelÀnge (hexadezimal) |
31.. .44 |
Filename und Extention |
program LAUFWERK;
VAR
drive : char;
function DGETDRV: integer; gemdos( $19 );
begin
drive:=chr(DGETDRV+65);
write('Sie benutzen momentan ');
writeln('Laufwerk drive);
readln
end.
Listing 2
program MEMORY;
TYPE buf4 = array [1..4] of long_integer;
VAR buff : buf4;
drive,r : integer;
memory,freemem,usedmem : long_integer;
function DFREE (VAR buff: buf4; drv: integer): integer;
gemdos($36);
begin
write('welches Laufwerk ( 0=akt., 1=A, 2=B, usw. ): ');
readln(drive); writeln;
r:=DFREE(buff,drive); { 1 = Laufwerk A, 2=B, usw. }
writeln('Funktionsrueckgabe : ',r);
writeln;
writeln('freie Cluster : ',buff[1]);
writeln('Anzahl der Cluster : ',buff[2]);
writeln('Anzahl Byte/Sector : ',buff[3]);
writeln('Anzahl Sectoren/Cluster: ',buff[4]);
writeln;
freemem:=buff[1]*buff[3]*buff[4];
writeln('frei : ',freemem,' Byte');
usedmem: = (buff[2]-buff[1])*buff[3] *buff[4];
writeln('belegt : ',usedmem,' Byte');
memory:=buff[2]*buff[3]*buff[4];
writeln('gesamt : ',memory,' Byte');
readln
end.
Listing 3
Nach Festlegung der Puffer-Adresse erfolgt der Aufruf FSFIRST. Dieser sucht das erste Diskettenfile, das mit dem angegebenen Muster (Pfadname) ĂŒbereinstimmt und schreibt die Informationen in den Puffer. Dieses Muster kann auch 'Jokerâ (BruchstĂŒcke eines Namens) enthalten (z. B. *.PAS oder A???????.PAS). Der zweite Parameter dieser Funktion ist das Dateiattribut, das eine weitere Selektierung der zu suchenden Dateien festlegen kann. Wenn hier eine Null ĂŒbergeben wird, sind die Unterdirectories ausgeblendet, bei einem Wert von 16 werden auch sie angezeigt (siehe Tabelle).
Wert |
Bedeutung |
0 |
Schreib-/Lesedatei |
1 |
Nur-Lesedatei |
2 |
versteckte Datei |
4 |
System-Datei |
8 |
Volume-Label |
16 |
Unter directory |
Zum Suchen eines weiteren Eintrages dient die Funktion FSNEXT. Diese benötigt keine Parameter (die von FSFIRST gesetzten sind weiterhin gĂŒltig), liefert aber eine Fehlernummer und schreibt die Fileinformation in den DTA.
Das Programm DIRECTORY (Listing 4) zeigt, wie das Directory eines beliebigen Laufwerkes gelesen und auf dem Bildschirm angezeigt wird. Dieses Programm verwendet fast alle der bereits besprochenen Routinen und gibt die Directory in âaufbereiteterâ Form aus (siehe Bild 2). Das jeweilige Directory-File wird hier in einem Record abgelegt, um einen unkomplizierten Zugriff auf die einzelnen Elemente zu haben. Bei nĂ€herer Betrachtung dieses Records wird Ihnen auffallen, daĂ das Datum und die Uhrzeit in jeweils einer Integer-Zahl abgelegt werden. Der genaue Aufbau beider Zahlen kann der Grafik 3 entnommen werden. Die einzelnen Daten (z. B. Tag, Monat, Jahr) mĂŒssen erst voneinander 'getrenntâ werden. Diese Arbeit erledigt das Programm mit einigen Schiebe-Befehlen (ShR & ShL), die man sich eventuell etwas genauer anschauen muĂ.
Der eigentliche Filename ist in einem ARRAY of CHAR abgelegt und wird als Einzelbuchstabe ausgegeben, bis sein letztes Zeichen (chr(O)) erreicht wird.
In den nÀchsten Ausgaben folgen Tips zu
- Filebehandlung (SchĂŒtzen, Löschen, Umbenennen, ...)
- Sound (Erzeugen und Abspielen von Musik) (Interessante Nebeneffekte)
- I/O (Die Schnittstellen: RS232, parallel, MIDI, Tastatur)
(MN & HS)
Bild 2
program DIRECTORY;
TYPE
nametyp = packed array [1..14] of char;
path_name = packed array [1..80] of char;
DIRREC = record
reserved : packed array [0..21] of byte;
time : integer;
date : integer;
size : long_integer;
filename : nametyp;
end;
VAR
dirfile : dirrec;
wdh : integer;
name : String;
path : path_name;
ch : char;
function DGETDRV: integer; gemdos( $19 );
procedure FSETDTA( VAR buf : dirrec ); gemdos( $1a );
function FSFIRST( VAR path: path_name; search_attrib: integer ):integer; gemdos( $4e );
function FSNEXT : integer; gemdos( $4f );
procedure SET_DRIVE;
VAR r,drive : integer;
drv : char;
function DSETDRV( drive: integer ): integer; gemdos( $0E );
begin
write('bitte Laufwerk angeben (A, B, C, usw.):');
read(drv);
if drv in ['A'..'Z'] then drive:=ord(drv)-65
else drive:=ord(drv)-97;
r:=DSETDRV(drive);
if r<0 then begin
writeln('Fehler bei der Eingabe !');
readln
end
end;
procedure SHOWFILE( VAR dirfile : dirrec ) ;
VAR i,jahr,monat,tag,stunden,minuten : integer;
begin
with dirfile do begin
jahr:=shr(date,9);
monat:=shr((date-shl(jahr,9)),5);
tag:=date-shl(monat,5)-shl(jahr,9);
write( tag:2,' ',monat:2,' ',jahr+1980:4,' ');
stunden:=shr(time,11);
minuten;=shr((time-shl(stunden,11)),5);
write( stunden;2,':',minuten:2,' ');
write( size:8,' ');
i := 1 ;
while filename[i]<>chr(0) do begin
write( filename[i] );
i := i + 1
end;
writeln
end
end;
procedure MEMORY;
TYPE buf4 = array [1..4] of long_integer;
VAR buff : buf4;
freemem, usedmem : long_integer;
procedure DFREE (VAR buff: buf4; drv: integer); gemdos( $36 );
begin
writeln;
DFREE(buff,0); { 0 fuer aktuelles Laufwerk }
freemem:=buff[1]*buff[3]*buff[4];
writeln('frei : ',freemem,' Byte');
usedmem:=(buff[2]-buff[1])*buff[3]*buff[4];
writeln('belegt : ',usedmem,' Byte');
end;
procedure PFAD_NAME;
VAR i : integer;
begin
write( 'Pfadname: '); { Pfad }
readln( name );
if name='' then begin
name:='*.*';
writeln( name )
end;
name:=concat( name,chr(0) );
for i := 1 to length( name ) do path[i] := name[i] ;
end;
procedure DIRECTORY_LESEN;
begin
FSETDTA( dirfile ) ; { setzt DTA }
if FSFIRST( path, 16 ) >= 0 then begin { erster Eintrag }
writeln(' Datum Zeit Byte Nameâ);
writeln('---------------------------------------') ;
repeat
SHOWFILE( dirfile ); { Anzeigen }
wdh:=FSNEXT; { weitere Eintraege )
until wdh < 0;
MEMORY;
end
else
write( ' keine Datei gefunden !' );
readln;
end;
begin { Hauptprogramm }
repeat
writeln(chr(27),'E');
writeln('MENUE');
writeln(' aktuelles Laufwerk:',chr(DGETDRV+65));
writeln('(D)irectory lesen');
writeln('(A)usdrucken');
writeln('(L)aufwerk aendern');
writeln('(Q)uit');
writeln; write('Eingabe: ');
read(ch); writeln;
case ch of
'd','D' : begin
PFAD_NAME;
DIRECTORY_LESEN;
end;
'a','A' : begin
PFAD_NAME;
rewrite (outputPRN;');
DIRECTORY_LESEN;
rewrite (output,'CON:*)
end;
'l','L': SET_DRIVE;
end;
until ch='q'
end.