home *** CD-ROM | disk | FTP | other *** search
- (* ------------------------------------------------------ *)
- (* DISKMAN.PAS *)
- (* objektorientierte Toolbox zum Zugriff auf Diskette *)
- (* (c) 1991 Gerd Cebulla & DMV-Verlag *)
- (* ------------------------------------------------------ *)
- UNIT DiskMan;
- {$B-}
-
- INTERFACE
-
- USES Dos;
-
- CONST
- DiskError : WORD = 0;
-
- (* Error-Konstanten für DiskError: *)
- dskOk = 0; { fehlerfreie Ausführung }
- dskFileNotFound = 2; { Datei nicht gefunden }
- dskPathNotFound = 3; { Verzeichnis nicht gefunden }
- dskAccessDenied = 5; { unzulässige Parameter o.ä. }
- dskNoMemory = 8; { nicht genug Hauptspeicher }
- dskNoMoreFiles = 18; { keine weiteren Dateieinträge }
-
- (* wenn DiskError > 255, gelten für die *)
- (* Interpretation des Low-Byte die folgenden Werte: *)
- dskWriteProtected = 0; { Diskette ist schreibgeschützt }
- dskDriveNotReady = 2; { Laufwerk ist nicht bereit }
- dskCRCError = 4; { Prüfsumme fehlerhaft }
- dskSeekError = 6; { Suchfehler }
- dskSectorNotFound = 8; { Sektor nicht gefunden }
- dskWriteError = 10; { Schreibfehler }
- dskReadError = 11; { Lesefehler }
- dskGeneralError = 12; { allgemeiner Fehler }
-
- TYPE
- DosNameStr = ARRAY [1..11] OF CHAR;
- IDString = ARRAY [1.. 8] OF CHAR;
-
- PDiskSector = ^DiskSector;
- DiskSector = ARRAY [0..MAXINT] OF BYTE;
-
- PBootSector = ^BootSector;
- BootSector = RECORD { Aufbau des Bootsektors }
- Jump : ARRAY [1..3] OF BYTE;
- { Sprung zur Bootroutine }
- ID : IDString;
- { DOS-Name und -Version }
- BytesPerSector : WORD;
- { Sektorgröße in Byte }
- SectorsPerCluster : BYTE;
- { Clustergröße in Sektoren }
- BootSectors : WORD;
- { Anzahl der Boot-Sektoren }
- Fats : BYTE;
- { Anzahl der FAT-Kopien }
- RootDirEntries : WORD;
- { Anzahl der Einträge im Stammverzeichnis }
- TotalSectors : WORD;
- { Gesamtzahl Sektoren im Volume }
- MediaDescriptor : BYTE; { Datenträger-Kennbyte }
- SectorsPerFat : WORD; { Größe der FAT in Sektoren }
- SectorsPerTrack : WORD; { Sektoren pro Spur }
- Heads : WORD; { Anzahl Köpfe bzw. Seiten }
- VolumeOffset : LONGINT;
- { Abstand des 1. Sektors }
- { im Volume vom 1. Sektor auf dem Datenträger }
-
- { die folgenden Felder sind erst ab DOS 4.0 definiert: }
- TotalSectors32 : LONGINT;
- { Gesamtzahl Sektoren im }
- { Volume bei Partitionen > 32 MByte }
- PhysDrive : BYTE;
- { physik. Laufwerksnummer: }
- { 0 bei Disketten, $80 bei der ersten Festplatte }
- Reserved : BYTE; { nicht benutzt }
- BootBlockType : BYTE;
- { enthält den Wert $29, wenn }
- { es sich um einen erweiterten Bootblock handelt }
- VolumeNo : LONGINT; { Datenträgernummer }
- VolumeLabel : DosNameStr; { Name des Volumes }
- FatSystem : IDString;
- { FAT-Typ als String: 'FAT12' oder 'FAT16' }
- Fill : ARRAY [$3E..MAXINT] OF BYTE;
- { hier folgt die eigentliche Bootroutine }
- END; { BootSector ----------------------------- }
-
- PDirEntry = ^DirEntry;
- DirEntry = RECORD { Aufbau eines Verzeichniseintrags }
- Name : DosNameStr; { Dateiname und Erweiterung }
- Attribute : BYTE; { Dateiattribut }
- Fill : ARRAY [1..10] OF BYTE; { nicht benutzt }
- Time : LONGINT;
- { Datum und Uhrzeit in gepacktem Format }
- FirstCluster : WORD; { Nr. des 1. Clusters der Datei }
- Size : LONGINT; { Dateigröße in Byte }
- END; { DirEntry --------- }
-
- PDirSector = ^DirSector;
- DirSector = ARRAY [0..1023] OF DirEntry;
-
- PDiskInfo = ^DiskInfo;
- DiskInfo = OBJECT
- ID : IDString;
- VolumeLabel : DosNameStr;
- VolumeNo : LONGINT;
- FatSystem : IDString;
- PhysDrive : BYTE;
- MediaDescriptor : BYTE;
- Heads : WORD;
- VolumeOffset : LONGINT;
- SectorsPerTrack : WORD;
- TotalSectors : LONGINT;
- BytesPerSector : WORD;
- ClusterStart : LONGINT;
- { Sektornummer des 1. Clusters im Volume }
- TotalClusters : WORD;
- { Gesamtzahl der Cluster im Volume }
- FreeClusters : WORD;
- { Anzahl der freien Cluster }
- BytesPerCluster : WORD; { Clustergröße in Byte }
- SectorsPerCluster : BYTE; { Clustergröße in Sektoren }
- FatStart : WORD; { 1. Sektor der FAT }
- Fats : BYTE; { Anzahl FATs }
- Fat16 : BOOLEAN;
- { Flag: True bei 16-Bit-FAT, False bei 12-Bit-FAT }
- SectorsPerFat : WORD; { FAT-Größe in Sektoren }
- RootDirStart : WORD;
- { 1. Sektor des Stammverzeichnisses }
- RootDirEntries : WORD;
- { Anzahl der Einträge im Stammverzeichnis }
- RootDirSectors : WORD;
- { Größe des Stammverzeichnisses in Sektoren }
- DirEntriesPerSector : WORD;
- { Anzahl der Einträge pro Verzeichnis-Sektor }
- END; { DiskInfo -------- }
-
- PDiskManager = ^DiskManager;
- DiskManager = OBJECT (DiskInfo)
- DriveNo : BYTE; { Nr. des Laufwerks (0 = A:) }
- FatBuf : PDiskSector; { Zeiger auf FAT-Puffer }
- FatSector : WORD; { aktueller FAT-Sektor }
- FatChanged : BOOLEAN;
- { Flag: wird True, wenn die FAT geändert wurde }
-
- CONSTRUCTOR Init(Laufwerk : CHAR);
- DESTRUCTOR Done; VIRTUAL;
- PROCEDURE FlushDosBuffers; VIRTUAL;
- PROCEDURE GetDiskInfo(VAR Info : DiskInfo); VIRTUAL;
- FUNCTION GetFatEntry(ClusterNr : WORD) : WORD;
- VIRTUAL;
- FUNCTION GetFatWord(ByteNr : LONGINT) : WORD;
- VIRTUAL;
- FUNCTION GetFatByte(ByteNr : LONGINT) : BYTE;
- VIRTUAL;
- PROCEDURE PutFatEntry(ClusterNr, Eintrag : WORD);
- VIRTUAL;
- PROCEDURE PutFatWord(ByteNr : LONGINT;
- Eintrag : WORD); VIRTUAL;
- PROCEDURE PutFatByte(ByteNr : LONGINT;
- Eintrag : BYTE); VIRTUAL;
- PROCEDURE WriteFat; VIRTUAL;
- FUNCTION SaveFat : BOOLEAN; VIRTUAL;
- PROCEDURE ReadSector(VAR Buffer; SektorNr : LONGINT;
- SektorZahl : WORD); VIRTUAL;
- PROCEDURE WriteSector(VAR Buffer; SektorNr : LONGINT;
- SektorZahl : WORD); VIRTUAL;
- FUNCTION TryAgain : BOOLEAN; VIRTUAL;
- END; { DiskManager }
-
- IMPLEMENTATION
-
- TYPE
- DataBlock32 = RECORD
- SektorNr : LONGINT;
- SektorZahl : WORD;
- BufPtr : POINTER;
- END; { DataBlock32 }
-
- PROCEDURE ErrorHandler(AX, BX, CX, DX, SI, DI,
- DS, ES, BP : WORD); INTERRUPT;
- BEGIN
- DiskError := DI OR $FF00;
- AX := 0;
- END; { ErrorHandler }
-
- CONSTRUCTOR DiskManager.Init(Laufwerk : CHAR);
- { Nimmt Initialisierungen vor, legt einen Puffer für die }
- { FAT an und holt allgemeine Informationen über das }
- { Laufwerk ein. Alle Aufrufe von Methoden einer Instanz }
- { beziehen sich auf das hier angegebene Laufwerk. }
- VAR
- Regs : Registers;
- Boot : PBootSector;
- OldInt24 : POINTER;
- BEGIN
- FatBuf := NIL;
- DriveNo := Ord(UpCase(Laufwerk)) - Ord('A');
- GetIntVec($24, OldInt24);
- SetIntVec($24, @ErrorHandler);
- REPEAT
- Regs.AH := $36;
- Regs.DL := Succ(DriveNo);
- MsDos(Regs);
- IF Regs.AX <> $FFFF THEN DiskError := dskOk;
- UNTIL (DiskError = dskOk) OR NOT TryAgain;
- SetIntVec($24, OldInt24);
- IF DiskError <> dskOk THEN BEGIN
- Done;
- Fail;
- END;
- SectorsPerCluster := Regs.AX;
- FreeClusters := Regs.BX;
- BytesPerSector := Regs.CX;
- TotalClusters := Regs.DX;
- BytesPerCluster := BytesPerSector * SectorsPerCluster;
- TotalSectors := LONGINT(SectorsPerCluster)*
- TotalClusters;
- Fat16 := TotalClusters >= $0FEF;
- GetMem(Boot, BytesPerSector);
- IF Boot = NIL THEN BEGIN
- DiskError := dskNoMemory;
- Done;
- Fail;
- END;
- FlushDosBuffers;
- ReadSector(Boot^, 0, 1);
- IF DiskError <> dskOk THEN BEGIN
- FreeMem(Boot, BytesPerSector);
- Done;
- Fail;
- END;
- ID := Boot^.ID;
- MediaDescriptor := Boot^.MediaDescriptor;
- Heads := Boot^.Heads;
- IF Boot^.TotalSectors <> 0 THEN
- TotalSectors := Boot^.TotalSectors
- ELSE
- TotalSectors := Boot^.TotalSectors32;
- SectorsPerTrack := Boot^.SectorsPerTrack;
- FatStart := Boot^.BootSectors;
- Fats := Boot^.Fats;
- SectorsPerFat := Boot^.SectorsPerFat;
- RootDirEntries := Boot^.RootDirEntries;
- RootDirStart := FatStart+SectorsPerFat*Fats;
- DirEntriesPerSector := BytesPerSector DIV
- SizeOf(DirEntry);
- RootDirSectors := Succ(Pred(RootDirEntries) DIV
- DirEntriesPerSector);
- ClusterStart := RootDirStart+RootDirSectors;
- VolumeOffset := Boot^.VolumeOffset;
- IF Boot^.BootBlockType = $29 THEN BEGIN
- VolumeLabel := Boot^.VolumeLabel;
- VolumeNo := Boot^.VolumeNo;
- FatSystem := Boot^.FatSystem;
- PhysDrive := Boot^.PhysDrive;
- END ELSE BEGIN
- VolumeLabel := ' ';
- VolumeNo := 0;
- FatSystem := ' ';
- PhysDrive := 0;
- END;
- FreeMem(Boot, BytesPerSector);
- GetMem(FatBuf, BytesPerSector);
- IF FatBuf = NIL THEN BEGIN
- DiskError := dskNoMemory;
- Done;
- Fail;
- END;
- FatChanged := FALSE;
- FatSector := FatStart;
- ReadSector(FatBuf^, FatSector, 1);
- IF DiskError <> dskOk THEN BEGIN
- Done;
- Fail;
- END;
- END; { DiskManager.Init }
-
- DESTRUCTOR DiskManager.Done;
- { Gibt den FAT-Puffer frei; falls dessen Inhalt geändert }
- { wurde, wird er auf den Datenträger geschrieben. }
- BEGIN
- IF FatBuf <> NIL THEN BEGIN
- IF FatChanged AND SaveFat THEN WriteFat;
- FreeMem(FatBuf, BytesPerSector);
- FatBuf := NIL;
- END;
- END; { DiskManager.Done }
-
- PROCEDURE DiskManager.FlushDosBuffers;
- { Setzt die DOS-Sektorpuffer zurück. }
- VAR
- Regs : Registers;
- BEGIN
- Regs.AH := $0D;
- MsDos(Regs);
- END; { DiskManager.FlushDosBuffers }
-
- PROCEDURE DiskManager.GetDiskInfo(VAR Info : DiskInfo);
- { Liefert Informationen über den Datenträger zurück. }
- BEGIN
- Info := Self;
- END; { DiskManager.GetDiskInfo }
-
- FUNCTION DiskManager.GetFatEntry(ClusterNr : WORD) : WORD;
- { Liest einen Eintrag aus der FAT. Einträge in }
- { 12-Bit-FATs werden automatisch auf 16-Bit-Format }
- { erweitert. }
- VAR
- Eintrag : WORD;
- BEGIN
- IF ClusterNr > SUCC(TotalClusters) THEN
- DiskError := dskAccessDenied
- ELSE BEGIN
- DiskError := dskOk;
- IF Fat16 THEN
- Eintrag := GetFatWord(LONGINT(ClusterNr)*2)
- ELSE BEGIN
- Eintrag := GetFatWord(ClusterNr*3 DIV 2);
- IF ODD(ClusterNr) THEN
- Eintrag := Eintrag SHR 4
- ELSE
- Eintrag := Eintrag AND $0FFF;
- IF Eintrag >= $0FF0 THEN
- Eintrag := Eintrag OR $F000;
- END;
- END;
- IF DiskError = dskOk THEN
- GetFatEntry := Eintrag
- ELSE
- GetFatEntry := 1;
- END; { DiskManager.GetFatEntry }
-
- FUNCTION DiskManager.GetFatWord(ByteNr : LONGINT) : WORD;
- { Liest ein Word aus der FAT. }
- VAR
- Eintrag : WORD;
- BEGIN
- Eintrag := GetFatByte(ByteNr);
- IF DiskError = dskOk THEN
- INC(Eintrag, 256*GetFatByte(Succ(ByteNr)));
- GetFatWord := Eintrag;
- END; { DiskManager.GetFatWord }
-
- FUNCTION DiskManager.GetFatByte(ByteNr : LONGINT) : BYTE;
- { Liest ein Byte aus der FAT. }
- VAR
- SektorNr : LONGINT;
- BEGIN
- SektorNr := ByteNr DIV BytesPerSector+FatStart;
- IF SektorNr <> FatSector THEN BEGIN
- IF FatChanged AND SaveFat THEN WriteFat;
- ReadSector(FatBuf^, SektorNr, 1);
- FatSector := SektorNr;
- FatChanged := FALSE;
- END;
- GetFatByte := FatBuf^[ByteNr MOD BytesPerSector];
- END; { DiskManager.GetFatByte }
-
- PROCEDURE DiskManager.PutFatEntry(ClusterNr, Eintrag : WORD);
- { Schreibt einen Eintrag in die FAT. Angaben im }
- { 16-Bit-Format werden automatisch konvertiert, wenn es }
- { sich um eine 12-Bit-FAT handelt. }
- VAR
- ByteNr : LONGINT;
- BEGIN
- IF ClusterNr > SUCC(TotalClusters) THEN
- DiskError := dskAccessDenied
- ELSE BEGIN
- DiskError := dskOk;
- IF Fat16 THEN
- ByteNr := LONGINT(ClusterNr)*2
- ELSE BEGIN
- Eintrag := Eintrag AND $0FFF;
- IF ODD(ClusterNr) THEN
- Eintrag := Eintrag SHL 4+
- GetFatEntry(PRED(ClusterNr)) SHR 8 AND $000F
- ELSE
- Eintrag := Eintrag+
- GetFatEntry(SUCC(ClusterNr)) SHL 12;
- ByteNr := ClusterNr*3 DIV 2;
- END;
- PutFatWord(ByteNr, Eintrag);
- END;
- END; { DiskManager.PutFatEntry }
-
- PROCEDURE DiskManager.PutFatWord(ByteNr : LONGINT;
- Eintrag : WORD);
- { Schreibt ein Word in die FAT. }
- BEGIN
- PutFatByte(ByteNr, Lo(Eintrag));
- IF DiskError = dskOk THEN
- PutFatByte(Succ(ByteNr), Hi(Eintrag));
- END; { DiskManager.PutFatWord }
-
- PROCEDURE DiskManager.PutFatByte(ByteNr : LONGINT;
- Eintrag : BYTE);
- { Schreibt ein Byte in die FAT. }
- VAR
- SektorNr : LONGINT;
- Index : WORD;
- BEGIN
- SektorNr := ByteNr DIV BytesPerSector+FatStart;
- IF SektorNr <> FatSector THEN BEGIN
- IF FatChanged AND SaveFat THEN WriteFat;
- ReadSector(FatBuf^, SektorNr, 1);
- FatSector := SektorNr;
- FatChanged := FALSE;
- END;
- Index := ByteNr MOD BytesPerSector;
- IF FatBuf^[Index] <> Eintrag THEN BEGIN
- FatBuf^[Index] := Eintrag;
- FatChanged := TRUE;
- END;
- END; { DiskManager.PutFatByte }
-
- PROCEDURE DiskManager.WriteFat;
- { Schreibt den Inhalt des FAT-Puffers auf den }
- { Datenträger. }
- VAR
- SektorNr : LONGINT;
- FatNr : BYTE;
- BEGIN
- SektorNr := FatSector;
- FatNr := 0;
- REPEAT
- WriteSector(FatBuf^, SektorNr, 1);
- INC(SektorNr, SectorsPerFat);
- INC(FatNr);
- UNTIL (DiskError <> dskOk) OR (FatNr = Fats);
- END; { DiskManager.WriteFat }
-
- FUNCTION DiskManager.SaveFat : BOOLEAN;
- { Wenn diese Funktion den Wert TRUE zurückliefert, }
- { werden Änderungen im FAT-Puffer auf den Datenträger }
- { geschrieben, ansonsten unterbleibt die Speicherung. }
- BEGIN
- SaveFat := TRUE;
- END; { DiskManager.SaveFat }
-
- PROCEDURE DiskManager.ReadSector(VAR Buffer;
- SektorNr : LONGINT;
- SektorZahl : WORD);
- { Liest einen oder mehrere Sektoren vom Datenträger in }
- { den angegebenen Puffer ein. }
- { (Benutzer von Turbo-Pascal 6.0 können die inline- }
- { Anweisung durch den in Kommentarklammern gesetzten }
- { asm-Block ersetzen.) }
- VAR
- Data32 : DataBlock32;
- BufPtr : POINTER;
- Laufwerk : BYTE;
- BEGIN
- IF TotalSectors <= $FFFF THEN
- BufPtr := @Buffer
- ELSE BEGIN
- Data32.SektorNr := SektorNr;
- Data32.SektorZahl := SektorZahl;
- Data32.BufPtr := @Buffer;
- BufPtr := @Data32;
- SektorZahl := $FFFF;
- END;
- Laufwerk := DriveNo;
- REPEAT
- INLINE( { asm }
- $1E/ { push ds }
- $55/ { push bp }
- $C5/$9E/BufPtr/ { lds bx,BufPtr }
- $8B/$96/SektorNr/ { mov dx,word ptr SektorNr }
- $8B/$8E/SektorZahl/ { mov cx,SektorZahl }
- $8A/$86/Laufwerk/ { mov al,Laufwerk }
- $CD/$25/ { int $25 }
- $5D/ { pop bp }
- $5D/ { pop bp }
- $1F/ { pop ds }
- $72/$02/ { jc @Ende }
- $31/$C0/ { xor ax,ax }
- { @Ende: }
- $A3/DiskError { mov DiskError,ax }
- ); { end; }
- UNTIL (DiskError = dskOk) OR NOT TryAgain;
- END; { DiskManager.ReadSector }
-
- PROCEDURE DiskManager.WriteSector(VAR Buffer;
- SektorNr : LONGINT;
- SektorZahl : WORD);
- { Schreibt einen oder mehrere Sektoren aus dem }
- { angegebenen Puffer auf den Datenträger. }
- VAR
- Data32 : DataBlock32;
- BufPtr : POINTER;
- Laufwerk : BYTE;
- BEGIN
- IF TotalSectors <= $FFFF THEN
- BufPtr := @Buffer
- ELSE BEGIN
- Data32.SektorNr := SektorNr;
- Data32.SektorZahl := SektorZahl;
- Data32.BufPtr := @Buffer;
- BufPtr := @Data32;
- SektorZahl := $FFFF;
- END;
- Laufwerk := DriveNo;
- REPEAT
- INLINE( { asm }
- $1E/ { push ds }
- $55/ { push bp }
- $C5/$9E/BufPtr/ { lds bx,BufPtr }
- $8B/$96/SektorNr/ { mov dx,word ptr SektorNr }
- $8B/$8E/SektorZahl/ { mov cx,SektorZahl }
- $8A/$86/Laufwerk/ { mov al,Laufwerk }
- $CD/$26/ { int $26 }
- $5D/ { pop bp }
- $5D/ { pop bp }
- $1F/ { pop ds }
- $72/$02/ { jc @Ende }
- $31/$C0/ { xor ax,ax }
- { @Ende: }
- $A3/DiskError { mov DiskError,ax }
- ); { end; }
- UNTIL (DiskError = dskOk) OR NOT TryAgain;
- END; { DiskManager.WriteSector }
-
- FUNCTION DiskManager.TryAgain : BOOLEAN;
- { Wird von ReadSector/WriteSector beim Auftreten eines }
- { Fehlers aufgerufen. Wenn diese Funktion den Wert True }
- { zurückgibt, wird die Schreib-/Leseoperation }
- { wiederholt. }
- BEGIN
- TryAgain := FALSE;
- END; { DiskManager.TryAgain }
-
- END.
- (* ------------------------------------------------------ *)
- (* Ende von DISKMAN.PAS *)
-