LDS03: Load sectors from disk
LDS was the start of a disk recovery system. It never came off the ground since I had migrated to Linux
completely around that time. It was started because a friend asked me if I could read the contents off a pair
of old 5,25" floppy disks.
In order to come up with a general solution, I decided to make the 'lds' program. It would analyze the
bootsector and after that, try to get as much off the disk as possible. If necessary, by using repeated
attempts and then statistically determine the most probable data representation of lost data.
After some weeks, the swamp got deeper and deeper. So I decided to get bold and solve the problem by knife and scissor. I cut the top of the plastic envelope of the floppy disk, took out the magnetic medium and transplanted it into a known good envelope. I put the mechanically recovered disk in a suitable drive and tried a 'COPY' operation. Over 95% of the contents came over undamaged... So much for doing things the systematic way.
LDS03: the source
The disk repair program to be, that never even came close to being...
MODULE lds03;
(* 02 : add some more functionality OK : March 10, 2003 *)
(* 03 : add colours to the screen before adding functions OK : *)
FROM Display IMPORT displayAttr, Goto, ClrEOL, ClrEOS, Write;
FROM InOut IMPORT Read, ReadCard, (*Write,*) WriteCard, WriteLn, WriteString,
WriteHex;
FROM SYSTEM IMPORT ASSEMBLER;
FROM System IMPORT Terminate;
FROM Keyboard IMPORT KeyPressed;
FROM Xchar IMPORT KeyW8, Print;
TYPE Formats = (SSLD, SSSD, DSLD, DSSD, DSDD, DSHD, DSQD);
VAR MediaDescriptor, FilesMax, FATcount,
CouldBe, ProbNot, ReservedSectors,
SectorsPerFAT,
SectorPerCluster, BytesPerSector, MaxTrack,
MaxSector, MaxHead, TotalSectors, SpC,
MaxDrive, Drive, err, DriveType, Sector : CARDINAL;
SectBuff : ARRAY [0..511] OF CHAR;
PROCEDURE WriteStatus (errorcode : CARDINAL);
VAR OldValue : CARDINAL;
BEGIN
OldValue := displayAttr;
Goto (24, 9); ClrEOL;
displayAttr := 4; Goto (24, 9);
IF errorcode > 0 THEN Write (07C) END;
CASE errorcode OF
0 : displayAttr := 14; Print ('OK'); |
1 : Print ('Invalid function requested'); |
2 : Print ('Could not find address mark'); |
3 : Print ('Disk is write protected'); |
4 : Print ('Sector not found'); |
5 : Print ('Reset failed'); |
6 : Print ('The disk was changed'); |
7 : Print ('Bad parameter table'); |
8 : Print ('DMA controller overflow'); |
9 : Print ('Cannot read outside one track'); |
10 : Print ('Bad sector'); |
11 : Print ('Bad track'); |
12 : Print ('Media type not found'); |
13 : Print ('Invalid number of sectors found'); |
14 : Print ('Control data address mark found'); |
15 : Print ('DMA arbitration level out of range'); |
16 : Print ('Uncorrectable CRC/EEC on read'); |
32 : Print ('Floppy controller error'); |
64 : Print ('Track not found'); |
128 : Print ('Drive does not respond'); |
170 : Print ('Drive not ready'); |
187 : Print ('Undefined error'); |
204 : Print ('Write fault'); |
224 : Print ('Controller status error'); |
255 : Print ('Sense operation failed')
ELSE
Print ('Error'); WriteCard (errorcode, 4)
END;
displayAttr := OldValue
END WriteStatus;
PROCEDURE ResetDrive (drive : CARDINAL); (* Reset diskdrive and controller after an error. *)
BEGIN
err := 0;
ASM
MOV AH, 0
MOV DL, drive
INT 013H
MOV err, AH
END
END ResetDrive;
PROCEDURE ReadSector (Sector : CARDINAL; VAR buffer : ARRAY OF CHAR) : BOOLEAN;
VAR trk, sct, hd : CARDINAL;
BEGIN
trk := Sector DIV SpC;
sct := (Sector MOD SpC) + 1;
IF sct > MaxSector THEN
hd := 1;
DEC (sct, MaxSector)
ELSE
hd := 0
END;
err := 0;
ASM
MOV AL, 1
MOV AH, 2
MOV DL, Drive
MOV DH, hd
MOV CL, sct
MOV CH, trk
LES BX, buffer
INT 013H
MOV err, AH
END;
WriteStatus (err);
IF err > 0 THEN
ResetDrive (Drive);
RETURN FALSE
ELSE
RETURN TRUE
END
END ReadSector;
PROCEDURE ScanDisk (): BOOLEAN;
VAR sector, status : CARDINAL;
BEGIN
FOR Sector := 0 TO TotalSectors -1 DO
FillScreen;
IF ReadSector (sector, SectBuff) = FALSE THEN KeyW8 (' ') END;
IF KeyPressed () = TRUE THEN RETURN FALSE END
END;
RETURN TRUE
END ScanDisk;
PROCEDURE CheckDrive (drv : CARDINAL);
(* Determine what kind of drive (i.e HARDWARE) there is in this computer *)
BEGIN
err := 0;
ASM
MOV AH, 8
MOV DL, drv
INT 013H
MOV err, AH
MOV DriveType, BL
END
END CheckDrive;
PROCEDURE SetFormat () : BOOLEAN; (* Specify to the OS what kind of floppy in in the drive *)
BEGIN
err := 0;
ASM
MOV AH, 018H
MOV CH, MaxTrack
MOV CL, MaxSector
MOV DL, Drive
INT 013H
MOV err, AH
END;
IF err > 0 THEN
RETURN FALSE
ELSE
RETURN TRUE
END
END SetFormat;
PROCEDURE SetParams (format : Formats);
BEGIN
CASE format OF
SSLD : TotalSectors := 320; MaxTrack := 39; MaxHead := 0; MaxSector := 8; |
DSLD : TotalSectors := 640; MaxTrack := 39; MaxHead := 1; MaxSector := 8; |
SSSD : TotalSectors := 360; MaxTrack := 39; MaxHead := 0; MaxSector := 9; |
DSSD : TotalSectors := 720; MaxTrack := 39; MaxHead := 1; MaxSector := 9; |
DSDD : TotalSectors := 1440; MaxTrack := 79; MaxHead := 1; MaxSector := 9; |
DSHD : TotalSectors := 2400; MaxTrack := 79; MaxHead := 1; MaxSector := 15; |
DSQD : TotalSectors := 2880; MaxTrack := 79; MaxHead := 1; MaxSector := 18
END;
SpC := MaxSector * (MaxHead + 1)
END SetParams;
PROCEDURE GetKey (str : ARRAY OF CHAR) : CHAR;
VAR key : CHAR;
index : CARDINAL;
BEGIN
LOOP
Read (key);
key := CAP (key);
FOR index := 0 TO HIGH (str) DO
IF str [index] = key THEN EXIT END
END;
Write (07C)
END;
RETURN key
END GetKey;
PROCEDURE ManualMode;
VAR key : CHAR;
BEGIN
CASE DriveType OF
1 : UserMessage (6); key := GetKey ('ABCD'); |
2 : UserMessage (7); key := GetKey ('ABCDEF'); |
3 : key := 'G'
ELSE
UserMessage (8); key := GetKey ('GH')
END;
CASE key OF
'A' : SetParams (SSLD); |
'B' : SetParams (DSLD); |
'C' : SetParams (SSSD); |
'D' : SetParams (DSSD); |
'E' : SetParams (DSDD); |
'F' : SetParams (DSHD); |
'G' : SetParams (DSDD)
ELSE
SetParams (DSQD)
END
END ManualMode;
PROCEDURE ScanBits (word : CARDINAL) : CARDINAL;
VAR k, result : CARDINAL;
BEGIN
result := 0;
FOR k := 0 TO 15 DO
IF k IN BITSET (word) THEN INC (result) END;
END;
RETURN result
END ScanBits;
PROCEDURE CheckBS (src : ARRAY OF CHAR);
VAR value, i : CARDINAL;
char : CHAR;
BEGIN
CouldBe := 0;
ProbNot := 0;
value := ORD (src [0]); (* JMP instruction *)
IF (value = 0E9H) OR (value = 0EBH) THEN
INC (CouldBe)
ELSE
INC (ProbNot, 18)
END;
value := 256 * ORD (src [510]) + ORD (src [511]); (* Bootsignature *)
IF value = 0AA55H THEN
INC (CouldBe)
ELSE
INC (ProbNot, 16)
END;
value := BytesPerSector;
IF value > 127 THEN
IF ScanBits (value) = 1 THEN
INC (CouldBe)
ELSE
INC (ProbNot, 8)
END
ELSE
INC (ProbNot, 8)
END;
value := SectorPerCluster;
IF ScanBits (value) = 1 THEN
INC (CouldBe)
ELSE
INC (ProbNot, 4)
END;
IF ReservedSectors = 0 THEN
INC (ProbNot, 4)
ELSE
IF ReservedSectors > 4 THEN
INC (ProbNot, 4)
ELSE
INC (CouldBe)
END
END;
IF (FATcount = 1) OR (FATcount = 2) THEN
INC (CouldBe)
ELSE
INC (ProbNot, 4)
END;
IF (FilesMax # 0) AND (FilesMax MOD 16 = 0) THEN
INC (CouldBe)
ELSE
INC (ProbNot, 6)
END;
IF MediaDescriptor < 0EDH THEN
INC (ProbNot, 2)
ELSE
INC (CouldBe)
END;
IF SectorsPerFAT > 15 THEN
INC (ProbNot, 6)
ELSE
INC (CouldBe)
END;
IF MaxSector > 40 THEN
INC (ProbNot, 4)
ELSE
INC (CouldBe)
END;
IF MaxHead > 1 THEN
INC (ProbNot, 4)
ELSE
INC (CouldBe)
END;
FOR i := 3 TO 11 DO (* Scan OEM name for alphanumeric data *)
char := src [i];
IF (char > ' ') AND (char <= '~') THEN
INC (CouldBe)
ELSE
INC (ProbNot, 3)
END
END
END CheckBS;
PROCEDURE Init;
VAR try, ThisOne : CARDINAL;
char : CHAR;
Flag : BOOLEAN;
BEGIN
displayAttr := 02H; Goto (0,0); ClrEOS;
ThisOne := 0;
LOOP
ResetDrive (ThisOne);
CheckDrive (ThisOne);
IF DriveType = 0 THEN EXIT END;
Write (CHR (ThisOne + ORD ('A')));
Print (': ');
CASE DriveType OF
1 : Print ('5¬"/360 Kb'); |
2 : Print ('5¬"/1.2 Mb'); |
3 : Print ('3«"/720 Kb'); |
4 : Print ('3«"/1.44 Mb');
END;
WriteLn;
INC (ThisOne);
IF ThisOne > 4 THEN EXIT END
END;
IF ThisOne = 0 THEN
UserMessage (4);
Terminate (2)
ELSIF ThisOne > 4 THEN
MaxDrive := 3
ELSE
MaxDrive := ThisOne - 1
END;
Print ('Floppy diskdrives found in this computer: ');
WriteCard (MaxDrive + 1, 3);
WriteLn;
IF MaxDrive > 0 THEN
Print ('Which drive to use? Specify driveletter : ');
WriteLn;
char := GetKey ('ABCD');
Drive := ORD (char) - ORD ('A')
ELSE
Drive := 0
END;
IF Drive > MaxDrive THEN
UserMessage (2);
Terminate (4)
END;
CheckDrive (Drive);
CASE DriveType OF (* Set preliminary parameters *)
1 : SetParams (DSSD); | (* 360 Kb *)
2 : SetParams (DSHD); | (* 1,2 Mb *)
3 : SetParams (DSDD) (* 720 Kb *)
ELSE
SetParams (DSQD) (* 1,44 Mb *)
END;
Goto (5, 0);
Print ('Now I will try to read the bootsector of this disk.');
Goto (7, 4);
Print ('Attempt :');
try := 1;
LOOP
Goto (7, 14);
WriteCard (try, 3);
IF ReadSector (0, SectBuff) = TRUE THEN EXIT END;
ResetDrive (Drive);
INC (try);
IF try > 10 THEN EXIT END
END;
Goto (9,0);
IF try > 10 THEN
WriteString ('Could not read first sector. Switching to manual mode.');
ManualMode;
ResetDrive (Drive); IF ReadSector (0, SectBuff) = FALSE THEN Write (07C) END;
ResetDrive (Drive); IF ReadSector (0, SectBuff) = FALSE THEN RETURN END
END;
char := SectBuff [21]; (* get media descriptor *)
MediaDescriptor := ORD (char);
Goto (20, 0);
WriteString ('In this drive is a ');
CASE ORD (char) OF
0FFH : SetParams (DSLD); WriteString ('320 Kb'); |
0FEH : SetParams (SSLD); WriteString ('160 Kb'); |
0FDH : SetParams (DSSD); WriteString ('360 Kb'); |
0FCH : SetParams (SSSD); WriteString ('180 Kb'); |
0F9H : IF DriveType = 2 THEN
SetParams (DSHD); WriteString ('1.2 Mb')
ELSE
SetParams (DSDD); WriteString ('720 Kb')
END; |
0F0H : SetParams (DSQD); WriteString ('1.44 Mb')
ELSE
UserMessage (5)
END;
WriteString (' format floppy disk.'); WriteLn;
WriteString ('Press the SPACEbar to continue.'); KeyW8 (' ')
END Init;
PROCEDURE BuildScreen;
VAR x, y : CARDINAL;
BEGIN
displayAttr := 02;
Goto ( 0, 0); ClrEOS;
displayAttr := 0EH;
Goto ( 0, 0); Print ('LDS : Load Diskette Sectors');
displayAttr := 07;
Goto ( 2, 0); Print ('Drive');
Goto ( 2, 17); Print ('tracks,');
Goto ( 2, 28); Print ('heads,');
Goto ( 2, 39); Print ('sectors/track,');
Goto ( 2, 60); Print ('sectors,');
Goto ( 2, 75); Print ('Kb');
displayAttr := 03H;
Goto ( 3, 0); Print ('Track :');
Goto ( 4, 0); Print ('Sector :');
Goto ( 5, 0); Print ('Head : ');
displayAttr := 05H;
Goto ( 7, 0); Print ('Sector contents:');
x := 3;
y := 8;
displayAttr := 02H;
Goto (y,x); Write ('Ú'); FOR x := 4 TO 72 DO Write ('Ä') END; Write ('¿');
x := 3;
FOR y := 9 TO 17 DO
Goto (y, x ); Write ('³'); WriteCard (y - 9, 2);
Goto (y, x + 70); Write ('³');
END;
Goto (y,x); Write ('À'); FOR x := 4 TO 72 DO Write ('Ä') END; Write ('Ù');
displayAttr := 0AH;
Goto (19, 0); Print ('Commands:');
Goto (20, 0); Print ('S/s = Sectors UP/down');
Goto (21, 0); Print ('T/t = Track UP/down');
Goto (22, 0); Print ('H/h = toggle Head');
Goto (20, 27); Print ('B = Goto Bootsector');
Goto (21, 27); Print ('V = View Sector');
Goto (22, 27); Print ('F = File this sector');
Goto (20, 50); Print ('R = Read ¿');
Goto (21, 50); Print ('W = Write ÃÄ current sector');
Goto (22, 50); Print ('E = Edit Ù');
displayAttr := 0BH;
Goto (24, 0); Print ('Status :');
END BuildScreen;
PROCEDURE FillScreen;
VAR OldAttr : CARDINAL;
BEGIN
OldAttr := displayAttr;
displayAttr := 02H;
Goto (2, 6); Write (CHR (Drive + ORD ('A'))); Write (':');
Goto (2,11); WriteCard (MaxTrack + 1, 5);
Goto (2,24); WriteCard (MaxHead + 1, 3);
Goto (2,34); WriteCard (MaxSector, 4);
Goto (2,54); WriteCard (TotalSectors, 5);
Goto (2,69); WriteCard (TotalSectors DIV 2, 5);
Goto (3, 9); WriteCard (Sector DIV SpC, 3);
Goto (4, 9); WriteCard ((Sector MOD SpC) MOD MaxSector + 1, 3);
Goto (5,11);
IF (Sector MOD SpC) >= MaxSector THEN Print ('1') ELSE Print ('0') END;
Goto (7, 7); WriteCard (Sector, 5);
displayAttr := OldAttr;
END FillScreen;
PROCEDURE UserMessage (code : CARDINAL);
BEGIN
WriteLn;
CASE code OF
1 : WriteString ('Thank you for using LDS. I hope it met your wishes.');
WriteLn;
WriteString ('LDS is free software as defined in the GNU GPL') |
2 : WriteString ("You know, just as well as I, that you simply don't have that drive.");
WriteLn;
WriteString ('So stop fooling around and restart LDS. Aborting NOW.');
WriteLn; |
3 : WriteString ('Sorry, this driveletter is not in your computer.') |
4 : WriteString ('This computer has no disks, so it is no use to continue.');
WriteLn;
WriteString ('Goodbye.'); |
5 : WriteString ('Unsupported mediadescriptor found.'); |
6 : WriteString ('So you have a 360 Kb floppy diskdrive.');
WriteLn;
WriteString ('Please specify the density of the DISK itself');
WriteLn;
WriteString (' A = 160 Kb B = 320 Kb C = 180 Kb D = 360 Kb');
WriteLn;
WriteLn; |
7 : WriteString ('So you got a 1.2 Mb floppy diskdrive.');
WriteLn;
WriteString ('Please specify the density of the DISK itself');
WriteLn;
WriteString (' A = 160 Kb B = 320 Kb C = 180 Kb D = 360 Kb');
WriteLn;
WriteString (' E = 720 Kb F = 1.2 Mb');
WriteLn;
WriteLn; |
8 : WriteString ('You have a 3,5" diskdrive capable for 1.44 Mb floppies.');
WriteLn;
WriteString ('Please specify the density of the DISK itself:');
WriteLn;
WriteString (' G = 720 Kb H = 1.44 Mb');
WriteLn;
WriteLn
END;
WriteLn
END UserMessage;
PROCEDURE ShowSector;
VAR i, j, index : CARDINAL;
char : CHAR;
BEGIN
index := 0;
FOR i := 0 TO 7 DO
Goto (i + 9, 7);
FOR j := 0 TO 63 DO
char := SectBuff [index];
INC (index);
IF ORD (char) > 31 THEN
Write (char)
ELSE
Write ('.')
END
END
END
END ShowSector;
PROCEDURE ViewSector (VAR buffer : ARRAY OF CHAR);
VAR i, j, n, index,
OldAttr : CARDINAL;
token : CHAR;
BEGIN
OldAttr := displayAttr;
Goto (0,0); displayAttr := 04H; ClrEOS;
Goto (0,0); WriteString ('View/Edit sector contents');
Goto (1,0);
displayAttr := 02;
Print ('ÚÄÄÄÄÂ');
FOR n := 0 TO 48 DO Write ('Ä') END; Write ('Â');
FOR n := 0 TO 17 DO Write ('Ä') END; Write ('¿');
WriteLn;
Print ('³ ³ ');
Goto (2, 55); Write ('³');
Goto (2, 74); Write ('³');
displayAttr := 0EH;
Goto (2, 7);
Print ('00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F');
Goto (2, 57);
Print ('...3...7...A...F');
displayAttr := 02H;
WriteLn;
Print ('ÆÍÍÍÍØ');
FOR n := 0 TO 48 DO Write ('Í') END; Write ('Ø');
FOR n := 0 TO 17 DO Write ('Í') END; Write ('µ');
WriteLn;
FOR i := 0 TO 17 DO
Print ('³ '); WriteHex (i, 2); Print (' ³');
FOR n := 0 TO 48 DO Write (' ') END; Write ('³');
FOR n := 0 TO 17 DO Write (' ') END; Write ('³');
WriteLn
END;
Print ('ÀÄÄÄÄÁ');
FOR n := 0 TO 48 DO Write ('Ä') END; Write ('Á');
FOR n := 0 TO 17 DO Write ('Ä') END; Write ('Ù');
index := 0;
FOR i := 0 TO 17 DO
Goto (4 + i, 7);
FOR j := 0 TO 15 DO
token := buffer [index];
INC (index);
WriteHex (ORD (token), 2);
Write (' ')
END
END;
index := 0;
displayAttr := 03H;
FOR i := 0 TO 17 DO
Goto (4 + i, 57);
FOR j := 0 TO 15 DO
token := buffer [index];
INC (index);
IF token >= ' ' THEN
Write (token)
ELSE
Write ('.')
END
END
END;
displayAttr := OldAttr
END ViewSector;
PROCEDURE ProcessKeys;
VAR token : CHAR;
Flag : BOOLEAN;
BEGIN
LOOP
Read (token);
CASE token OF
'U', 'u' : INC (Sector); |
'D', 'd' : DEC (Sector); |
'S' : INC (Sector); |
's' : DEC (Sector); |
'T' : INC (Sector, 2 * MaxSector); |
't' : DEC (Sector, 2 * MaxSector); |
'H', 'h' : IF Sector MOD SpC >= MaxSector THEN
DEC (Sector, MaxSector)
ELSE
INC (Sector, MaxSector)
END; |
'R', 'r' : IF ReadSector (Sector, SectBuff) = TRUE THEN
ShowSector
END; |
'C', 'c' : IF ScanDisk () = FALSE THEN Write (07C) END;|
'B', 'b' : Sector := 0; |
'V', 'v', 'F', 'f',
'W', 'w', 'E', 'e',
033C,
'Q', 'q' : EXIT
ELSE
Write (07C)
END;
IF Sector > TotalSectors THEN Sector := TotalSectors - 1 END;
FillScreen;
END
END ProcessKeys;
BEGIN
Init;
BuildScreen;
ShowSector;
FillScreen;
ProcessKeys;
ViewSector (SectBuff);
KeyW8 (' ');
Goto (0,0); ClrEOS; UserMessage (1);
END lds03.
Page created on 28 October 2006 and
Page equipped with FroogleBuster technology