: I need to implement FAT12/FAT16 support in a project. Where an I get
: the specifications for it? Or is there any existing source code I
: can use for reference.
Here's a pretty good description of the FAT12/16 FS:
http://www.maverick-os.dk/FileSystemFormats/FAT12_FileSystem.html . You'll also need direct disk access to read/write sectors, since Pascal lacks these. The following code should work, although I never tested, is a public domain release, found on my computer. If your project is OS independent then should try the Int13h functions instead, also AFAIK Windows denies any direct disk access ( or any direct hardware access ), so you'd need to interface through it's drivers...
{ Some proc's to read/write sectors, PD by Arne de Bruijn }
uses Dos,Strings;
type
JmpRec=record { The starting jump in the bootsector }
Code:byte; { $E9 XX XX / $EB XX XX}
case byte of
0:(Adr1:byte; NOP:byte);
1:(Adr2:word);
end;
BpbRec=record { The Bios Data Block (returned by DOS, and stored in }
BytesPSec:word; { the bootsector } SecPClus:byte;
ResSec:word;
NrFATs:byte;
NrROOT:word;
TotalSec:word;
MDB:byte;
SecPFAT:word;
SecPSpoor:word;
NrHead:word;
HidSec32:longint;
TotalSec32:longint;
end;
BootSec=record { The bootsector format in DOS }
JmpCode:JmpRec;
Name:array[0..7] of char; { Isn't meaningfull at all, just FORMAT prg name }
Bpb:BpbRec;
end;
BootSecP=^BootSec;
var
BigPart:boolean; { 32-bit sectors? }
Drive:byte; { which drive are we using }
ROOTSec,FATSec,DataSec:longint; { Some starting sectors }
FAT12:boolean; { 12-bit FAT? }
LastSecNo:longint; { Save last sector number... }
LastError:word; { ... and error code for error report }
function ReadSec(SecNo:longint; var Buf):boolean; assembler;
{ Read a sector using DOS int 25h }
{ Parameters: }
{ SecNo Sector number to read }
{ Buf Your buffer to receive the data (512 bytes will be stored here, }
{ make sure you have enough space allocated!) }
{ Returns TRUE if success, else FALSE }
{ Uses global boolean BigPart to choose between 16-bit (false) and }
{ 32-bit (true) sector number calling }
{ Uses global byte Drive to choose the drive to read from. 0=A:, 1=B: etc. }
var
ParBuf:array[0..9] of byte;
{ Buffer to hold parameters on 32-bit sector call: }
{ ofs size meaning }
{ 0 4 (longint) sectornumber }
{ 4 2 (word) number of sectors to read (set to 1 in this proc.) }
{ 6 4 (pointer) address of buffer }
asm
{ Copy sectornumber to global var for error report }
mov ax,word ptr [SecNo]
mov word ptr [LastSecNo],ax
mov ax,word ptr [SecNo+2]
mov word ptr [LastSecNo+2],ax
push ds { Store DS register (needs to be preserved in TP/BP) }
mov al,Drive { Load Drive no. from global var (DS points still to }
{ data segment }
push ax { Store it on stack }
cmp BigPart,0 { Must we use 32-bit calling? }
jne @DoBig { Yes -> goto @DoBig, No -> continue with 16-bit }
lds bx,Buf { Load address of buffer (Buf) }
mov cx,1 { Number of sectors to read }
mov dx,word ptr [SecNo] { Get number of sector to read (SecNr) }
jmp @DosRead { goto @DosRead, skip the 32-bit part }
@DoBig:
cld { Store forwards in parameter buffer }
mov ax,ss { Load address of parameter buffer (ParBuf) }
mov es,ax
mov ds,ax
lea di,ParBuf { Still loading... }
mov bx,di { Save offset of parameter buffer in BX }
mov ax,word ptr [SecNo] { Get number of sector to read (lo 16-bit part) }
stosw {Lo SecNr} { Store in our buffer }
mov ax,word ptr [SecNo+2] { Get number of sector to read (hi 16-bit part) }
stosw {Hi SecNr} { Store in buffer }
mov ax,1 { Sectors to read }
stosw { Store in buffer }
mov ax,word ptr [Buf] { Get offset of buffer (Buf) }
stosw {Offset Buffer} { Store in buffer }
mov ax,word ptr [Buf+2] { Get segment of buffer (Buf) }
stosw {Segment Buffer} { Store in buffer }
mov cx,-1 { Indicate use of 32-bit calling }
@DosRead: { Actual interrupt calling starts }
pop ax { Get drive number from stack }
push bp { Save BP (must be preserved in TP/BP) }
int 25h { DOS function: read sector(s) }
mov al,1 { Assume success (TRUE, ordinal 1) }
sbb al,0 { Subtract one if carry flag high (set on error by
{ DOS) }
popf { Get flags back DOS had forgotten to do }
pop bp { Get BP back }
pop ds { Get DS back }
mov LastError,ax { Save the errorcode in global var for errorreporting }
end; { Return to caller, al contains return code }
{ (0=FALSE, 1=TRUE) }
function WriteSec(SecNo:longint; var Buf):boolean; assembler;
{ Same as above, but WRITES a sector with contents of Buf }
{ USE WITH CAUNTION! YOU CAN DESTROY IMPORTANT DATA WITH THIS! }
{ (not commented, is exactly the same as ReadSec, only uses INT 26h } { instead
of INT 25h) }var
ParBuf:array[0..9] of byte;
asm
mov ax,word ptr [SecNo]
mov word ptr [LastSecNo],ax
mov ax,word ptr [SecNo+2]
mov word ptr [LastSecNo+2],ax
push ds
mov al,Drive
push ax
cmp BigPart,0
jne @DoBig
lds bx,Buf
mov cx,1
mov dx,word ptr [SecNo]
jmp @DosRead
@DoBig:
cld
mov ax,ss
mov es,ax
mov ds,ax
lea di,ParBuf
mov bx,di
mov ax,word ptr [SecNo]
stosw {Lo SecNr}
mov ax,word ptr [SecNo+2]
stosw {Hi SecNr}
mov ax,1
stosw {Aantal Sectors}
mov ax,word ptr [Buf]
stosw {Offset Buffer}
mov ax,word ptr [Buf+2]
stosw {Segment Buffer}
mov cx,-1
@DosRead:
pop ax
push bp
int 26h
mov al,1
sbb al,0
popf
pop bp
pop ds
mov LastError,ax
end;
procedure DiskRError;
begin
WriteLn('Error reading disk! Sector:',LastSecNo,' Errorcode:',LastError);
Halt(1);
end;
var
Bpb:BpbRec; { Global copy of Bios Parameter block, for ClusToSec }
function ClusToSec(C:word):longint;
{ Convert clusternumber to sector number, because the cluster is often bigger }
{ than one sector, you need to read multiple succeeding sectors to read the }
{ whole cluster (number of sectors in a cluster is in BPB (BPB.SecPClus)) }
{ Uses global BpbRec Bpb and global longint DATASec }
begin
ClusToSec:=((C-2)*Bpb.SecPClus)+DATASec;
end;
const
SizeBpb=SizeOf(BpbRec);
{ Needed for assembly part, AFAIK you can't get the size of a structure }
{ (record) in an asm..end; block (shame on you, Borland (or on me
)) }
var
Buf:pointer;
S:string[1]; { To store driveletter }
I:byte;
begin
I:=0;
asm
mov ax,3000h { Get DOS version }
int 21h
cmp al,3
jb @BadDos
ja @DosOk
cmp ah,20
jae @DosOk
@BadDos: { Lower than 3.2? }
mov I,1 { Set flag }
@DosOk:
end;
if I=1 then
begin WriteLn('Sorry, need DOS version 3.2 or higher!'); Halt(1); end;
if ParamCount=0 then
begin
WriteLn(ParamStr(0),' <driveletter>');
Halt(1);
end;
S:=ParamStr(1);
case UpCase(S[1]) of
'A'..'Z':;
else
begin
WriteLn('Bad drive!');
Halt(1);
end;
end;
Drive:=Ord(UpCase(S[1]))-65;
GetMem(Buf,512);
asm
push ds { Copy DS }
pop es { to ES }
push ds { Save DS }
mov ax,440dh { DOS function 44h (IOCTL), subfunction 0Dh }
{ (blockdriver control) }
mov bl,Drive { Driveno. }
inc bl { Incrase by 1 (0=default, 1=A:) }
mov cx,860h { subsubfunction 860h (get information) }
lds dx,Buf { Load address of buffer where to store result }
int 21h { Call DOS subfunction }
mov al,1 { Assume error }
jc @EndR { Got error? Yes -> goto @EndR }
mov si,dx { Set SI on offset parameterblock }
mov al,2 { Assume floppy }
cmp byte ptr [si+1],5 { Is it a harddisk? }
jne @EndR { No -> goto @EndR }
mov cx,SizeBpb { Get size of BPB record }
add si,7 { Starts at offset 7 in DOS parameter block }
lea di,Bpb { Get address of our global BPB block }
cld { Store forwards }
rep movsb { Copy BPB from DOS to ours }
xor al,al { No errors }
@EndR: { AL contains errorcode: 0=no err., 1=DOS err, }
{ 2=it's a floppy (need something special) }
pop ds { Restore DS }
mov I,al { Save result }
end;
case I of
0:BigPart:=Bpb.TotalSec=0; { It's a harddisk, 16-bit field is 0 for 32-bit }
{ access } 1: { Error
from DOS, report } begin
WriteLn('Can''t get parameter block for drive ',chr(Drive+65),'!');
Halt(1);
end;
2: { It's a floppy. DOS' bpb is only right }
{ for the largest disk size, so we need to read }
{ it ourself }
begin
BigPart:=false; { No 32-bit sectors on floppies }
if not ReadSec(0,Buf^) then { Read bootsector (sector 0) }
DiskRError; { Show error if we got one }
Bpb:=BootSecP(Buf)^.Bpb; { Copy BPB }
end;
end;
with Bpb do {Store some handy information for accessing the disk ourself }
begin
FATSec:=ResSec; { Starting FAT sector }
ROOTSec:=FATSec+(NrFATs*SecPFAT); { Starting ROOT directory sector }
DATASec:=ROOTSec+(NrROOT shr 4); { Starting DATA sector }
if not BigPart then { Is it a 12-bit FAT? }
FAT12:=((TotalSec-DATASec) div SecPClus)<4087 { Yes if less than 4087 sec}
else
FAT12:=false; { Not with 32-bit sectors }
end;
FreeMem(Buf,512);
{ do what you want }
end.