PASCAL ruft TOS: Teil 2 - Die Floppy

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:

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

(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.


Links

Copyright-Bestimmungen: siehe Über diese Seite
Classic Computer Magazines
[ Join Now | Ring Hub | Random | << Prev | Next >> ]