home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 June
/
SIMTEL_0692.cdr
/
msdos
/
dskutl
/
reform16.arc
/
REFORMAT.PAS
< prev
next >
Wrap
Pascal/Delphi Source File
|
1990-03-03
|
24KB
|
708 lines
{
To: INFO-IBMPC-REQUEST@USC-ISIB.ARPA
From: U001222%HNYKUN11.BITNET@WISCVM.WISC.EDU
Subject: reformat.pas
}
PROGRAM reformat;
{
Program to reformat any disk attached to an Olivetti PC or compatible.
The progam will work well on any MS/PC-DOS machine running under DOS
versions 2.00 up to and including 3.10. Fixed disks up to 32 Meg.
V1.50 Extensions made for big FATs. WORD.INC included for handling of
word integers. MS-DOS function INT21 / 32H used for drive information.
Corrected the problem of DOS losing track of the current directory.
The program has been tested under DOS 2.00 thru 3.10
It will probably work under DOS 3.20 too, but this version will not
run when you try to do so. If you want to test it with DOS 3.20, look
for the string DOS_Versions (it's somewhere near the end of the program)
in this program, and include that version number into the test.
Please let the author know whether you had success or not.
Jos Wennmacker July 86.
Universitair Rekencentrum
Geert Grooteplein Zuid 41
6525 GA Nijmegen
The Netherlands
BITNET address: U015415 at HNYKUN22
v1.6, 23 Nov 89 David Kirschbaum, Toad Hall
- Recoded for TP v5.0
V1.5 Jos added new 16-bit and word code, handling up to 30Meg
hard disks, etc. I cleaned up the integer math inline procedures
a little.
David Kirschbaum, Toad Hall
V1.23 Added FRAMER.INC, WRITEF.INC for framing, fast screen write,
fast screen line clear. Tightened up displays a little.
This version is now VERY PC-specific. Generic MS-DOS functions
for screen stuff to follow.
David Kirschbaum, Toad Hall.
V1.22 Added inline code to replace the INT25.ASM and INT26.ASM
files.
David Kirschbaum, Toad Hall, 3 May 86
V1.21(mod) not publicly released. Changes made by Rick Watson to let
the program recognize the big (16 bit) FATs. He made it run on 20 Meggers
under DOS 3.x. Martin Hobson made it run on 30 Meggers under DOS 3.x.
May 86.
V1.21 Small corrections made to avoid Turbo integer overflow. (Hey,
Borland, what about a WORD type in Turbo, would be very usefull using
the MSDOS features!).
V1.20 First release to the public domain.
What started as a favour to a friend grew into a nice program.
Do you like it? Let me know about it! If I know many people to use it,
I'll keep it up to date, and send you new releases.
Jos Wennmacker
}
{$A+} {word alignment to be nice}
{$B-} {short-circuit Boolean Eval}
{$D-} {no debug}
{$F-} {no far calls}
{$L-} {no local information}
{$R-} {no range checking}
{$S-} {no stack checking}
{$V-} {no string checking}
Uses Dos,Crt; {v1.6}
{-------------------------------- Global types -------------------------------}
TYPE
{------------------- types for word arithmetic see REFORMAT.IN1 -------------}
Relational_Operator = (Eq, Gt, Lt, Ne, Ge, Le);
{----------------------------- Bootrecord layout -----------------------------}
Boot = { the layout of the DOS boot record}
RECORD
Jump: ARRAY[0..2] OF Byte; { Near jump to boot code}
OEM : ARRAY[0..7] OF CHAR; { eight character OEM name}
sectorSize: word; { number of bytes in one sector}
clusterSize: Byte; { sectors per cluster}
reservedSectors: word; { DOS boot code sector(s) typically 1}
numberOfFATs: Byte; { usually 2, but often 1 for RAM disks}
rootDirSize, { maximum entries in root directory}
totalSectors: word; { total sectors in the logical image}
{ A limited description of the drive
bit 0 = 1: twosided, 0: not two-sided
bit 1 = 1: 8-sector, 0: not 8-sector
bit 2 = 1: removable 0: not removable
bits 3-7 must be set to 1
}
mediaDescriptor: Byte;
fatSize, { number of sectors in each FAT copy}
trackSize, { number of sectors per track}
numberOfHeads, { number of heads}
{ sectors hidden from DOS, preceding the DOS partition: including the
master boot record and any non-DOS partitions
}
hiddenSectors: word;
END;
{---------------- Layout of DOS function 32h disk parameter block ------------}
Parms_32 = ^Parameter_table;
Parameter_Table = { layout of the DOS function 32 table}
RECORD
assignedDisk, { 0 = A, 1 = B, ...}
altAD: Byte; { same as above, but 0 for RAM disk}
sectorSize: word; { number of bytes in one sector}
clusterSize_1, { sectors per cluster minus 1}
numberOfHeads_1: Byte; { number of heads minus 1}
reservedSectors: word; { DOS boot code sector(s) typically 1}
numberOfFATs: Byte; { usually 2, but often 1 for RAM disks}
rootDirSize, { maximum entries in root directory}
firstDataSector, { first sector for data storage}
totalDataClusters_1: word; { total data clusters plus 1}
fatSize: Byte; { number of sectors in each FAT copy}
firstDirectorySector: word; { first sector of the root directory}
(* v1.6
DeviceDriverAddress: ^byte; { Far (offset, segment) address of the
DOS device driver for the drive}
*)
DeviceDriverAddress: Pointer; {v1.6}
{ A limited description of the drive
only the low order byte is used:
bit 0 = 1: twosided, 0: not two-sided
bit 1 = 1: 8-sector, 0: not 8-sector
bit 2 = 1: removable 0: not removable
bits 3-7 must be set to 1
}
mediaDescriptor: word;
NextParameterTable: Parms_32; { far pointer to next disk table}
{ Starting cluster of current working directory.
According to Glenn Roberts in his May 86 PC Tech Journal article
"Finding Disk Parameters" this would only hold for PC DOS 2.00
}
currentDirCluster: word;
CurrentDirName: ARRAY [0..63] OF CHAR
{ The name of the current working directory. This does not hold for
DOS 3.x versions. Only for 2.00
}
END;
{-------- Long byte and integerarays for Disk Transfer Area, Fats etc. -------}
intArray = ARRAY[0..32766] OF Word; {v1.6 INTEGER;}
buffer = ARRAY[0..32766] OF Byte;
{--------------------- Element of tree of directory entries ------------------}
DirectoryPointer = ^DirectoryEntry;
(*v1.6 longInteger = ARRAY[0..1] OF INTEGER; *)
DirectoryEntry =
RECORD
{ name + extension w/o period
If the first char is $00, then this entry was never used.
Also the next entries in this directory have never been used.
If the first char is $E5, then this entry was erased.
}
EntryName: ARRAY[0..10] OF CHAR;
{ attribute of entry:
bit if set means
0 read only file
1 hidden file
2 system file
3 volume label
4 subdirectory
5 archive bit
}
attribute: Byte;
reserved: ARRAY[1..10] OF Byte; { for future use}
{ mapped in the bits:
< hh > < mm > < xx >
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
where:
hh binary number of hours [0..23]
mm binary number of minutes [0..59]
xx binary number of two-second
increments [0..28]
}
timeLastUpdated: word;
{ mapped in the bits
< yy > < mm > < dd >
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
where:
yy binary number of years [0..119]
(1980-2099)
mm binary number of month [1..129]
dd binary number of day [1..31]
}
dateLastUpdated: word;
startingCluster: word; { index of the file's first cluster}
fileSize: longint; { filesize in bytes v1.6}
newStartingCluster: word;
Next,
SubDirectory: DirectoryPointer;
END;
{------------------------------- Helps for I/O -------------------------------}
WorkString = STRING[255];
Line = STRING[78];
Str40 = STRING[40];
Disk_Activity = (reading,writing);
{-------------------------------- constants ----------------------------------}
CONST
Header: STRING[37] = ' REFORMAT: an original JOS disk tool ';
Version: STRING[10] = 'Ver: 1.60 ';
PDS: STRING[24] = ' Public Domain Software ';
EnterStr: STRING[28] = 'Press Enter to return to DOS';
HiSpace: STRING[01] = #160; {WriteF blank line flag}
{--------------------------- FAT special values ------------------------------}
unused: word = $0000; { we use typed constants, because}
reservedMinimum: word = $0FF0; { we might have to change these values}
reservedMaximum: word = $0FF6; { the values entered here are for}
badCluster: word = $0FF7; { the old, small FAT format of 12 bits}
lastMinimum: word = $0FF8; { per entry. The new FAT format uses}
lastMaximum: word = $0FFF; { 16 bit-entries. See the procedure}
lastNormal: word = $0FFF; { Getinformation.}
{---------------------- file attribute byte values----------------------------}
READONLY = $01; { file attribute byte}
HIDDENFILE = $02;
SYSTEMFILE = $04;
VOLUMELABEL = $08;
SUBDIRECTORY = $10;
ARCHIVE = $20;
NEVERUSED = $00; { first byte of file's name}
ERASED = $E5;
FIXEDDISK = $F8; { some values of mediaDescriptor}
DUAL8SECTOR = $FF;
SINGLE8SECTOR = $FE;
DUAL9SECTOR = $FD;
SINGLE9SECTOR = $FC;
{---------- constants for the screen layout, for FRAMER and WRITEF procedures}
MIDDLEFIELDY = 18; { little window in the middle}
INPUTFIELDX = 22; { User Input Field}
INPUTFIELDY = 20;
LOGFIELDX = 22; { Activity Logging}
LOGFIELDY = 21;
WARNINGFIELDX = 22; { Warning Message Field}
WARNINGFIELDY = 22;
ERRORFIELDX = 22; { Error Message Field}
ERRORFIELDY = 23;
DISASTERFIELDX = 22; { Disaster Message Field}
DISASTERFIELDY = 24;
{-------- Drive characteristics and constants communications block -----------}
VAR
DriveLetter: CHAR;
driveNumber: Byte;
defaultDrive: Byte;
totalSectors,
sectorSize: word;
media: Byte;
trackSize,
numberOfHeads,
hiddenSectors,
reservedSectors,
rootDirSize: word;
numberOfFATs: Byte;
fatSize: word;
clusterSize: Byte;
firstDataSector,
firstDirectorySector,
totalDataClusters,
freeClusters,
badClusters,
usedClusters: word;
DeviceDriverAddress: Pointer; {v1.6 ^byte;}
CurrentDirectory: ARRAY[0..63] OF CHAR;
DiskLabel: STRING[11];
diskCreationDate,
diskCreationTime: word;
OEM: ARRAY[0..7] OF CHAR;
assignedDisk,
altAD: Byte;
BigFAT: BOOLEAN;
firstFATsector,
directorySectors: word;
{----------------------------- Global variables ------------------------------}
Register: Registers; {v1.6 Regpack;}
oldFATindex,
newFATindex: word;
totalFiles,
hiddenFiles,
hiddenDirectories,
inRootDirectory,
inSubdirectories,
nonContiguousFiles,
subdirectories,
movedClusters,
clustersToMove,
lostClusters,
errors,
count: Word; {v1.6 INTEGER;}
SAVEaddress,
DTAddress: ^buffer;
PermutationAddress,
NewFATAddress,
OldFATAddress: ^intArray;
RootDir: DirectoryPointer;
{---------------------------------- miscellaneous vars -----------------------}
hour,
min,
sec,
frac: Word; {v1.6 INTEGER;}
time: REAL;
movedFieldX,
movedFieldY: Word; {v1.6 INTEGER;}
Instr: CHAR;
AlreadyWritten: BOOLEAN;
TmpStr: STRING[2];
NrStr,
S40: Str40;
Legals: STRING[3];
Parms: Line;
{-----------------------------------------------------------------------------}
{ I REFORMAT.IN1} {Nijmegen .. word arithmetic v1.6 no longer needed}
{$I REFORMAT.IN2} {Toad Hall.. InLine code FOR Int25 AND Int26}
{$I REFORMAT.IN3} {Toad Hall.. fast screen framer, FRAMER.INC}
{$I REFORMAT.IN4} {Toad Hall.. better screen writing AND clearing, WRITEF.INC}
{-----------------------------------------------------------------------------}
{----------------------- general screen I/O routines -------------------------}
PROCEDURE Beep;
BEGIN
WRITE(CHR(7));
END; { of Beep}
PROCEDURE WriteLog(Text: Line);
BEGIN
WriteF(LOGFIELDX, LOGFIELDY, HiSpace);
WriteF(LOGFIELDX, LOGFIELDY, Text);
END; { of WriteLog}
PROCEDURE WriteWarning(Text: Line);
BEGIN
WriteF(WARNINGFIELDX, WARNINGFIELDY, HiSpace);
WriteF(WARNINGFIELDX, WARNINGFIELDY, Text);
END; { of WriteWarning}
PROCEDURE WriteError(Text: Line);
BEGIN
WriteF(ERRORFIELDX, ERRORFIELDY, HiSpace);
WriteF(ERRORFIELDX, ERRORFIELDY, Text);
END; { of WriteError}
PROCEDURE WriteDisaster(Text: Line);
BEGIN
WriteF(DISASTERFIELDX, DISASTERFIELDY, HiSpace);
WriteF(DISASTERFIELDX, DISASTERFIELDY, Text);
END; { of WriteDisaster}
PROCEDURE BlankFields;
BEGIN
WriteError(' ');
WriteWarning(' ');
WriteDisaster(' ');
IF Instr = 'I' THEN Register.flags := 0;
END; { of BlankFields}
PROCEDURE GetInput(Prompt: Str40; VAR Instr: CHAR);
BEGIN
WriteF(INPUTFIELDX, INPUTFIELDY, HiSpace); { really only needs 1}
WriteF(INPUTFIELDX, INPUTFIELDY, Prompt + ': ');
Beep;
(* v1.6
REPEAT UNTIL KeyPressed;
READ(Kbd, Instr);
Instr := UpCase(Instr);
*)
Instr := UpCase(ReadKey); {v1.6}
WRITE(Instr);
END; { of GetInput}
PROCEDURE Exeunt(Prompt: Str40); { we do this a lot}
BEGIN
GetInput(Prompt, Instr);
Height := 25;
WriteF(1, 24, HiSpace); { clear bottom line and leave screen
for user to contemplate}
HALT;
END; { of Exeunt}
{---------------------------- timer control routines -------------------------}
PROCEDURE StartTimer;
{
We want to let the user know how long REFORMAT took to do the job.
}
BEGIN
(* In Turbo:
with Register do begin
ah := $2C;
msdos(Register);
hour := hi(cx);
min := lo(cx);
sec := hi(dx);
frac := lo(dx);
end;
*)
{Toad Hall note: tighter, quicker inline}
InLine(
$B4/$2C { mov ah,$2C}
/$CD/$21 { int $21}
/$31/$C0 { xor ax,ax ;clear msb}
/$86/$E8 { xchg al,ch ;the hour}
/$A3/>HOUR { mov [>hour],ax}
/$86/$C8 { xchg al,cl ;the minute}
/$A3/>MIN { mov [>min],ax}
/$86/$F0 { xchg al,dh ;the second}
/$A3/>SEC { mov [>sec],ax}
/$86/$D0 { xchg al,dl ;the fraction}
/$A3/>FRAC { mov [>frac],ax}
);
END; { of StartTimer}
PROCEDURE StopTimer;
{
We want to let the user know how long REFORMAT took to do the job.
}
BEGIN
(* In Turbo:
with Register do begin
ah := $2C;
msdos(Register);
frac := lo(dx) - frac;
if frac < 0 then begin
frac := frac + 100;
sec := succ(sec);
end;
sec := hi(dx) - sec;
if sec < 0 then begin
sec := sec + 60;
min := succ(min);
end
else if sec >= 60 then begin
sec := sec - 60;
min := pred(min);
end;
min := lo(cx) - min;
if min < 0 then begin
min := min + 60;
hour := succ(hour);
end
else if min >= 60 then begin
min := min - 60;
hour := pred(hour);
end;
hour := hi(cx) - hour;
end;
*)
{Toad Hall Note: Tighter, faster inline. You won't BELIEVE
what a kludge Turbo makes of the above routine.
}
InLine(
$B4/$2C { mov ah,$2C}
/$CD/$21 { int $21}
/$BB/>FRAC { mov bx,>frac ;fraction offset}
/$BE/>SEC { mov si,>sec ;seconds offset}
/$31/$C0 { xor ax,ax ;clear msb}
/$86/$D0 { xchg al,dl ;get fraction}
/$2B/$07 { sub ax,[bx] ;frac = lo(dx)-frac}
/$73/$05 { jnc S1 ;no carry, so not <0}
/$05/$64/$00 { add ax,100 ;<0, adjust}
/$FF/$04 { inc word [si] ;bump seconds}
{S1:}
/$89/$07 { mov [bx],ax ;save fraction}
/$BB/>MIN { mov bx,>min ;get Minute offset}
/$31/$C0 { xor ax,ax ;clear msb}
/$BF/$3C/$00 { mov di,60 ;constant}
/$86/$F0 { xchg al,dh ;get seconds}
/$2B/$04 { sub ax,[si] ;sec = hi(dx)-sec}
/$73/$06 { jnc S2 ;not <0}
/$01/$F8 { add ax,di ;<0, adjust}
/$FF/$07 { inc word [bx] ;min=min+1}
/$EB/$08 { jmp short S3}
{S2:}
/$39/$F8 { cmp ax,di ;>=60?}
/$72/$04 { jb S3 ; nope}
/$29/$F8 { sub ax,di ; sec=sec-60}
/$FF/$0F { dec word [bx] ;min=min-1}
{S3:}
/$89/$04 { mov [si],ax ;save seconds}
/$BE/>HOUR { mov si,>hour ;hour offset}
/$31/$C0 { xor ax,ax ;clear msb}
/$86/$C8 { xchg al,cl ;get minutes}
/$2B/$07 { sub ax,[bx] ;min=lo(cx)-min}
/$73/$06 { jnc S4 ; not below 0}
/$01/$F8 { add ax,di ;min=min+60}
/$FF/$04 { inc word [si] ;hour=hour+1}
/$EB/$08 { jmp short S5}
{S4:}
/$39/$F8 { cmp ax,di ;>=60?}
/$72/$04 { jb S5 ; nope}
/$29/$F8 { sub ax,di ;min=min-60}
/$FF/$0C { dec word [si] ;hour=hour-1}
{S5:}
/$89/$07 { mov [bx],ax ;save minutes}
/$31/$C0 { xor ax,ax ;clear msb}
/$86/$E8 { xchg al,ch ;get hour}
/$2B/$04 { sub ax,[si] ;hour=hi(cx)-hour}
/$89/$04 { mov [si],ax}
);
END; { of StopTimer}
{$I REFORMAT.IN5} {Nijmegen .. disk, FAT, directory, misc. routines}
{------------------------------ MAIN PROGRAM ---------------------------------}
BEGIN
{ v1.6 Permitting user override of precautionary abort
for DOS versions above 3.10
Check_DOS_Version;
}
color := LIGHTGRAY; { initialize global screen attribute }
IF paramcount = 0 { exactly one parameter allowed }
THEN Parms := ''
ELSE IF paramcount = 1
THEN Parms := ParamStr(1)
ELSE Parms := 'in error'; { force error message }
IF LENGTH(Parms) <> 0
THEN IF Parms[1] = '?' { display explanation screens }
THEN BEGIN
WriteDoc;
HALT;
END
ELSE IF ( LENGTH(Parms) > 2 ) { we accept only d or d: }
OR ( (LENGTH(Parms) = 2) AND (Parms[2] <> ':') )
THEN BEGIN
WRITELN;
WRITELN('Invalid parameter: REFORMAT d: or ?');
HALT;
END
ELSE Instr := Parms[1]
ELSE Instr := ' ';
InitCounters;
InitScreen;
GetInformation;
{
The TURBO procedures getmem, move and fillchar will behave the way we want
in case the integer parameters are greater than 32767. (i.e. negative
when interpreted as integers). Block allocation, block move and block
filling can handle as many as 65535 bytes. Allocation however should
never be done in quantities of more than 65521 bytes, because the
pointer values are normalized, which means that the offset will be
in the range 0 through 15. Segments must begin on a paragraph boundary.
}
(* v1.6
GetMem( OldFATaddress, W_mul(totalDataClusters + 2, 2));
GetMem( DTAddress, W_mul(sectorSize, fatSize));
*)
GetMem( OldFATaddress, (totalDataClusters + 2) ShL 1); {v1.6}
GetMem( DTAddress, (sectorSize * fatSize) ); {v1.6}
ReadFat( OldFATaddress^, DTAddress^);
(* v1.6
GetMem( NewFATaddress, W_mul(totalDataClusters + 2, 2));
GetMem( PermutationAddress, W_mul(totalDataClusters + 2, 2));
GetMem( SAVEaddress, W_mul(sectorSize, clusterSize));
*)
GetMem( NewFATaddress, (totalDataClusters + 2) ShL 1); {v1.6}
GetMem( PermutationAddress, (totalDataClusters + 2) ShL 1); {v1.6}
GetMem( SAVEaddress, sectorSize * clusterSize); {v1.6}
ReadDirectories( DTAddress^ );
Move( OldFATaddress^,
(* v1.6
NewFATaddress^, W_mul(totalDataClusters + 2, 2));
*)
NewFATaddress^, (totalDataClusters + 2) ShL 1); {v1.6}
CheckDisk( NewFATaddress^, RootDir);
(* v1.6
FillChar( NewFATaddress^, W_mul(totalDataClusters + 2, 2), 0);
*)
FillChar( NewFATaddress^, (totalDataClusters + 2) ShL 1, 0); {v1.6}
FOR count := 0 TO SUCC(totalDataClusters) DO
PermutationAddress^[count] := count;
Move(OldFATaddress^, NewFATaddress^, 4);
RemakeFAT(OldFATaddress^, NewFATaddress^,
PermutationAddress^, RootDir, 0, 0);
LinkFreeClusters(OldFATaddress^, NewFATaddress^);
CountClustersToMove(PermutationAddress^);
WriteStatistics;
{v1.6 Up to now, we've done nothing destructive and maybe have shown
the user something informative (like will this sucker work with his
DOS 4.01?) or whatever.
NOW we ask him to take his life in his own hands...
}
Check_DOS_Version; {v1.6 moved down here.
may abort and die.}
IF nonContiguousFiles = 0
THEN IF clustersToMove <> 0
THEN BEGIN
color := $70; { inverse }
WriteF(0, middleFieldY,
'All files are contiguous, continue anyway?');
color := LIGHTGRAY;
REPEAT
GetInput('Continue? (Y/N)',Instr);
UNTIL Instr IN ['Y','N'];
IF Instr = 'N'
THEN Exeunt(EnterStr);
END
ELSE BEGIN
WriteLog('Cannot make your disk more optimized!');
Exeunt(EnterStr);
END
ELSE IF clustersToMove = 0
THEN BEGIN
WriteLog('Bad space makes file(s) noncontiguous.');
Exeunt(EnterStr);
END;
IF media = FIXEDDISK
THEN BEGIN
color := $70; { inverse }
WriteF(0, middleFieldY,
'Fixed disk: did you uninstall all protected software?');
color := LIGHTGRAY;
REPEAT
GetInput('Continue? (Y/N)',Instr);
UNTIL Instr IN ['Y','N'];
IF Instr = 'N'
THEN Exeunt(EnterStr);
END;
ResetDisk;
StartTimer;
WriteFAT(NewFATaddress^, DTAddress^);
WriteDirectories(DTAddress^);
DoIt(PermutationAddress^, DTAddress^, SAVEaddress^);
ResetDisk;
ResetSubdirectory;
StopTimer;
STR(hour:2, TmpStr);
Nrstr := TmpStr + ' h ';
STR(min:2, TmpStr);
NrStr := NrStr + TmpStr + ' m ';
STR(sec:2, TmpStr);
NrStr := NrStr + TmpStr + '.';
STR(frac:2, TmpStr);
NrStr := NrStr + TmpStr + ' s';
WriteLog('Done in: '+ NrStr + ' !');
Exeunt(EnterStr);
END.