FAT12/FAT16 support. - Programmers Heaven

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Categories

FAT12/FAT16 support.

lordestabialordestabia Posts: 1Member
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.

Thank you in advance.

Comments

  • AtexAtex Posts: 268Member
    : 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...[code][color=Blue]{ 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),' ');
    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.
    [/color][/code]
Sign In or Register to comment.