All pastes #421112 Raw Edit

FAT Source Code

public text v1 · immutable
#421112 ·published 2007-04-02 16:49 UTC
rendered paste body
Copyright (C) 2005-2007 Peter Kleissner
see www.toasteros.at.tt -> Impressum -> License  // http://t0ast3r.t0.ohost.de/index.php?page=impressum&sub=license
just see, learn, don't copy

---==== Published under the ToasterOS GENERAL PUBLIC LICENSE ===---


; FAT functions

; - Get_Short_Name
; - Determine_FAT_Type
; - Get_Count_of_Clusters
; - Get_DataSec
; - First_Sector_of_Cluster
; - Get_First_Data_Sector
; - Get_Root_Dir_Sectors
; - Get_FirstRootDirSecNum
; - Get_FATSz
; - Get_TotSec
; - Get_FATSecNum_Offset
; - Read_FAT_entry
; - Write_FAT_entry
; - Get_Next_Cluster
; - Get_Entry_Count
; - Return_SecPerClusVal






Determine_FAT_Type:

; [Drive] = drive to determine the FAT type

; return:
; all FAT values in Drive_Buffer are set correct (inclusive FAT determination)


; read the BIOS Parameter Block
Read 0, 1, Open_File_Destroy


; copy relevant values of the BIOS Parameter Block

; set "Sectors per Cluster"
movzx ebx,byte [API_Buffer+13]
mov [Drive_Buffer+0Bh],bl

; set "dwords per Sector"
movzx eax,word [API_Buffer+11]
shr ax,2
mov [Drive_Buffer+0Ch],ax

; set "dwords per Cluster"
mul ebx								; Sectors per Cluster" * "dwords per Sector"
mov [Drive_Buffer+0Eh],eax

; set "File Allocation Table" (= "Reserved Sector Count")
mov ax,word [API_Buffer+14]					; FAT Offset = Reserved Sector Count
mov [Drive_Buffer+1Ch],ax


; set the value "First_Data_Sector"
call Get_First_Data_Sector

; ---- OBSOLETE ----
; set the value "entries per cluster"
;call Get_Entry_Count
; ---- OBSOLETE ----

; set the value "FAT Type"
call Get_Count_of_Clusters

; set the values "first root directory sector" and "last root directory sector"
call Get_FirstRootDirSecNum

ret






Get_First_Data_Sector:

; static function (only called once), needs BIOS Parameter Block
; writes "First_Data_Sector" into Drive_Buffer

; (BPB_NumFATs * FATSz)
call Get_FATSz
movzx ebx,byte [API_Buffer+16]
mul ebx

; BPB_ResvdSecCnt + (BPB_NumFATs * FATSz)
movzx ecx,word [API_Buffer+14]
add ecx,eax

; BPB_ResvdSecCnt + (BPB_NumFATs * FATSz) + RootDirSectors
call Get_Root_Dir_Sectors
add eax,ecx

mov [Drive_Buffer+12h],eax					; set "first data sector"

ret






Get_FATSz:

; static function (only called once), needs BIOS Parameter Block
; returns the FATSz


; C++

; If(BPB_FATSz16 != 0)
;   FATSz = BPB_FATSz16;
; Else
;   FATSz = BPB_FATSz32;


; Assembler

movzx eax,word [API_Buffer+22]					; eax = BPB_FATSz16

or eax,eax							; eax = 0 ?
jnz Get_FATSz_Exit						; if not zero, it's FATSz16

; else it's FATSz32
mov eax,[API_Buffer+36]						; eax = BPB_FATSz32

Get_FATSz_Exit:

ret






Get_Count_of_Clusters:

; static function (only called once), needs BIOS Parameter Block
; writes "FAT Type" into Drive_Buffer

; CountofClusters = DataSec / BPB_SecPerClus
call Get_DataSec

xor edx,edx
movzx ebx,byte [Drive_Buffer+0Bh]
div ebx


; C++

; If(CountofClusters < 4085)
;   /* Volume is FAT12 */
; else
;   if(CountofClusters < 65525)
;     /* Volume is FAT16 */
;   else
;     /* Volume is FAT32 */


; Assembler

mov [Drive_Buffer+0Ah],byte 12					; suppose FAT Type is FAT12
cmp eax,4085
jl Determine_FAT_Type_Exit					; if CountofClusters < 4085 exit

mov [Drive_Buffer+0Ah],byte 16					; suppose FAT Type is FAT16
cmp eax,65525
jl Determine_FAT_Type_Exit					; if CountofClusters < 65525 exit

; else FAT Type is FAT32
mov [Drive_Buffer+0Ah],byte 32

Determine_FAT_Type_Exit:

ret






Get_DataSec:

; static function (only called once), needs BIOS Parameter Block and returns static value
; return = DataSec

; DataSec = TotSec - FirstDataSector
call Get_TotSec
sub eax,[Drive_Buffer+12h]

ret






Get_TotSec:

; static function (only called once), needs BIOS Parameter Block and returns static value
; return = TotSec

; C++

; If(BPB_TotSec16 != 0)
;   TotSec = BPB_TotSec16;
; Else
;   TotSec = BPB_TotSec32;


; Assembler

movzx eax,word [API_Buffer+19]					; eax = BPB_TotSec16

or eax,eax							; eax = 0 ?
jnz Get_TotSec_Exit						; if not zero, it's TotSec16

; else it's TotSec32
mov eax,[API_Buffer+32]						; eax = BPB_TotSec32

Get_TotSec_Exit:

ret






Get_Root_Dir_Sectors:

; static function (only called once), needs BIOS Parameter Block
; return = Root Directory Sectors

; (BPB_RootEntCnt * 32)
movzx eax,word [API_Buffer+17]
imul eax,32

; eax = zero ? (only on FAT32 drives)
or eax,eax
jz Get_Root_Dir_Sectors_Exit

; (BPB_RootEntCnt * 32) + (BPB_BytsPerSec - 1)
movzx edx,word [API_Buffer+11]
dec edx
add eax,edx

; ((BPB_RootEntCnt * 32) + (BPB_BytsPerSec - 1)) / BPB_BytsPerSec
xor edx,edx
movzx ebx,word [API_Buffer+11]
div ebx

Get_Root_Dir_Sectors_Exit:

ret






Get_FirstRootDirSecNum:

; static function (only called once), needs BIOS Parameter Block
; writes "first root directory sector" and "last root directory sector" into Drive_Buffer


; share function into FAT12/16 and FAT32
cmp [Drive_Buffer+0Ah],byte 32
je Get_FirstRootDirSecNum_FAT32


; FirstRootDirSecNum = BPB_ResvdSecCnt + (BPB_NumFATs * BPB_FATSz);
call Get_FATSz
movzx ebx,byte [API_Buffer+16]
mul ebx								; BPB_FATSz * BPB_NumFATs

movzx ebx,word [API_Buffer+14]
add eax,ebx							; BPB_ResvdSecCnt + (BPB_NumFATs * BPB_FATSz16)

mov [Drive_Buffer+16h],eax					; set "first root directory sector"

call Get_Root_Dir_Sectors					; calculate last root directory sector (add values)
add ax,[Drive_Buffer+16h]
mov [Drive_Buffer+1Ah],ax					; set "last root directory sector"

ret 


Get_FirstRootDirSecNum_FAT32:
; FirstRootDirSecNum = BPB_RootClus * Sectors per Cluster
mov eax,[API_Buffer+44]
imul eax,[API_Buffer+13]

mov [Drive_Buffer+16h],eax					; set "first root directory sector"
mov [Drive_Buffer+1Ah],word 00h					; set "last root directory sector" (on FAT32 not avl)

ret





%if 0 = 1

Get_Entry_Count:

; static function (only called once), needs BIOS Parameter Block
; writes "entries per cluster" into Drive_Buffer

; calculate the count of entrys per sector (BPB_BytsPerSec / Entry_Size)
movzx eax,word [API_Buffer+11]
xor edx,edx
mov ebx,32
div ebx

; calculate the count of entries per cluster
movzx ebx,byte [API_Buffer+13]
mul ebx								; * sectors per cluster

mov [Drive_Buffer+1Ch],eax					; set "entries per cluster"

ret

%endif







; non static functions, which are called more than once and returns non static values



First_Sector_of_Cluster:

; normal FAT32 ?
cmp [Drive_Buffer+0Ah],byte 32
je First_Sector_of_Cluster_normal

; normal cluster type ?
cmp [cluster_type],byte 18h
jne First_Sector_of_Cluster_normal

; else the cluster is a sector in the root directory area
; (do nothing)

ret


; eax = cluster n
; return = first sector of cluster n

; n - 2
First_Sector_of_Cluster_normal:
dec eax
dec eax

; (n - 2) * BPB_SecPerClus
movzx ebx,byte [Drive_Buffer+0Bh]
mul ebx

; ((n - 2) * BPB_SecPerClus) + FirstDataSector
add eax,[Drive_Buffer+12h]

ret







Get_FATSecNum_Offset:

; eax = cluster number n

; return = sector to read (element of the File Allocation Table)
; reutrn(edx) = Offset in the EFFECTIVE sector


; if FAT12 is set, and offset streches over boundarys, the carry flag is set
; in all other cases (non FAT12 or in boundarys) the carry flag is cleared



; If(FATType == FAT16)
;   FATOffset = N * 2;
; Else
;   if (FATType == FAT32)
;     FATOffset = N * 4;
;
; ThisFATSecNum = BPB_ResvdSecCnt + (FATOffset / BPB_BytsPerSec);
; ThisFATEntOffset = REM(FATOffset / BPB_BytsPerSec);


; special FAT12?
cmp [Drive_Buffer+0Ah],byte 12
je Get_FATSecNum_Offset_FAT12

; calculate the FATOffset (N * 2 or 4)
shl eax,1							; suppose FAT Type is 16
cmp [Drive_Buffer+0Ah],byte 16
je Calculate_ThisFAT						; if FAT 16, exit

shl eax,1							; else mul one more with 2 (in effect eax * 4)

Calculate_ThisFAT:
; FATOffset / BPB_BytsPerSec
xor edx,edx
sub ebx,ebx
imul bx, word [Drive_Buffer+0Ch], 4				; bx = bytes per sector
div ebx								; return(edx) = Offset in sector

; BPB_ResvdSecCnt + (FATOffset / BPB_BytsPerSec)
movzx ebx,word [Drive_Buffer+1Ch]
add eax,ebx							; return = sector

clc
ret



Get_FATSecNum_Offset_FAT12:

; FATOffset = N + (N / 2);
; /* Multiply by 1.5 without using floating point, the divide by 2 rounds DOWN */
; ThisFATSecNum = BPB_ResvdSecCnt + (FATOffset / BPB_BytsPerSec);
; ThisFATEntOffset = REM(FATOffset / BPB_BytsPerSec);
;
; We now have to check for the sector boundary case:
;
; If(ThisFATEntOffset == (BPB_BytsPerSec – 1)) {
; /* This cluster access spans a sector boundary in the FAT */
; /* There are a number of strategies to handling this. The */
; /* easiest is to always load FAT sectors into memory */
; /* in pairs if the volume is FAT12 (if you want to load */
; /* FAT sector N, you also load FAT sector N+1 immediately */
; /* following it in memory unless sector N is the last FAT */
; /* sector). It is assumed that this is the strategy used here */
; /* which makes this if test for a sector boundary span */
; /* unnecessary. */
; }


; the same code as by "Calculate_ThisFAT"
; FATOffset / BPB_BytsPerSec
xor edx,edx
sub ebx,ebx
imul bx, word [Drive_Buffer+0Ch], 4				; bx = bytes per sector
div ebx								; return(edx) = Offset in sector

; BPB_ResvdSecCnt + (FATOffset / BPB_BytsPerSec)
movzx ebx,word [Drive_Buffer+1Ch]
add eax,ebx							; return = sector

; If(ThisFATEntOffset == (BPB_BytsPerSec – 1)) then boundary strech
dec ebx
cmp edx,ebx
je Get_FATSecNum_Offset_FAT12_boundary

clc
ret

; if here, the entry is over boundarys, so set cf to indicate it
Get_FATSecNum_Offset_FAT12_boundary:
stc
ret







Read_FAT_entry:

; eax = Cluster

mov [Temp],eax						; store the Cluster temporary (for FAT12)

; special FAT12?
cmp [Drive_Buffer+0Ah],byte 12
je Read_FAT12_entry


call Get_FATSecNum_Offset				; get the Sector and the Offset of the entry

; read the SST
Read eax, 1, Read_FAT_Entry_Exit


; now share the function into FAT16 and FAT32
cmp [Drive_Buffer+0Ah],byte 32
je Read_FAT32_entry

lea edi,[API_Buffer+edx]				; edi = Offset in the Buffer
movzx eax,word [edi]					; eax = searched entry
jmp Read_FAT_Entry_succ


Read_FAT32_entry:
lea edi,[API_Buffer+edx]				; edi = Offset in the Buffer
mov eax,[edi]						; eax = searched entry
;jmp Read_FAT_Entry_succ


Read_FAT_Entry_succ:
clc

Read_FAT_Entry_Exit:
ret



Read_FAT12_entry:					; now: same code as by Read_FAT16/32_entry

call Get_FATSecNum_Offset				; get the Sector and the Offset of the entry
jc Read_FAT12_entry_boundary				; if the entry streches about sector boundarys, handle special

; read the SST
Read eax, 1, Read_FAT_Entry_Exit

lea edi,[API_Buffer+edx*2]				; edi = Offset in the Buffer
movzx eax,word [edi]					; eax = searched (word) entry

; odd?
test [Temp],dword 01b
je Read_FAT12_entry_odd

; if here, we want the low 12 bits of the word, so clear the other (and exit)
or eax,0F000h
xor eax,0F000h
jmp Read_FAT_Entry_succ

Read_FAT12_entry_odd:
; if here, we want only the high 12 bits of the word, so shift right about 4 bits (and exit)
shr eax,4
jmp Read_FAT_Entry_succ


Read_FAT12_entry_boundary:				; if here, the entry streches over sector boundarys

mov [Temp],eax						; save the sector

; read the SST
Read eax, 1, Read_FAT_Entry_Exit

lea edi,[API_Buffer+edx*2]				; edi = Offset in the Buffer

; restore the needed sector (+ 1 !)
mov eax,[Temp]
inc eax

; read the LEAST 4 bits of the ENTRY, which are - in fact - the HIGH NIBBLE bits of the read byte
mov bl,[edi]						; just use bl instead of al; bl wouldn't change by the ToasterOS function definition

; read the next Sector of the SST
Read eax, 1, Read_FAT_Entry_Exit

; now merge the 4 bits in bl and the byte at API_Buffer + 0
movzx eax,byte [API_Buffer+0]
shl eax,4						; shl the byte (by 4 bits)

shr bl,4						; the high nibble of the byte is the low nibble we need

or al,bl						; add the entry (end exit)
clc

ret







Write_FAT_entry:

; eax = Cluster
; ebx = value; or implicit used bx/b12 for using FAT12, FAT16
;   the caller shouldn't determine the size of using, because all FAT functions return 32 bit (probably zero extended) values

; maintaily the code (explicit the initialising process) is the same as by Read_FAT_Entry

mov [Temp],eax						; store the Cluster temporary (for FAT12)

; special FAT12?
cmp [Drive_Buffer+0Ah],byte 12
je Write_FAT12_entry

push ebx						; store ebx, it will be used in Get_FATSecNum_Offset
call Get_FATSecNum_Offset				; get the Sector and the Offset of the entry
pop ebx							; restore ebx

add eax,[Drive_Buffer+1Ch]				; eax = sector to modify
mov ecx,eax						; store to-change sector

; read the SST
Read eax, 1, Write_FAT_Entry_Exit


; now share the function into FAT16 and FAT32
cmp [Drive_Buffer+0Ah],byte 32
je Write_FAT32_entry

lea edi,[API_Buffer+edx*2]				; edi = Offset in the Buffer
mov [edi],bx						; store new data into the entry
jmp Write_entry

Write_FAT32_entry:
lea edi,[API_Buffer+edx*4]				; edi = Offset in the Buffer
mov [edi],ebx						; store new data (now 32 bit) into the entry


Write_entry:						; write the to-change sector
Write_into ecx, 1


Write_FAT_Entry_Exit:

ret



Write_FAT12_entry:					; now: same code as by Read/Write_FAT16/32_entry

push ebx						; store ebx, it will be used in Get_FATSecNum_Offset
call Get_FATSecNum_Offset				; get the Sector and the Offset of the entry
pop ebx							; restore ebx
jc Write_FAT12_entry_boundary				; if the entry streches about sector boundarys, handle special

add eax,[Drive_Buffer+1Ch]				; eax = sector to modify
mov ecx,eax

; read the SST
Read eax, 1, Write_FAT_Entry_Exit

lea edi,[API_Buffer+edx*2]				; edi = Offset in the Buffer
mov ax,[edi]						; eax = searched (word) entry


; odd?
test [Temp],dword 01b
je Write_FAT12_entry_odd

; if here, we want to change the low 12 bits of the word, so clear the other
or ebx,0F000h
xor ebx,0F000h						; clear high 4 bits of the value to store (also: or)
or ax,bx
mov ax,[edi]
jmp Write_entry

Write_FAT12_entry_odd:
; if here, we want to change only the high 12 bits of the word, so shift right about 4 bits
or ebx,0F000h
xor ebx,0F000h						; clear high 4 bits of the value to store (also: or)
shr ebx,4						; shift ebx, the value
or ax,bx
mov ax,[edi]
jmp Write_entry


Write_FAT12_entry_boundary:				; if here, the entry streches over sector boundarys

add eax,[Drive_Buffer+1Ch]				; eax = sector to modify
mov ecx,eax

; read the SST
Read eax, 1, Write_FAT_Entry_Exit

lea edi,[API_Buffer+edx*2]				; edi = Offset in the Buffer

; modify the top 4 bits of the byte, which are the least 4 bits of the entry
mov al,[edi]
mov dl,bl
or dl,00001111b
xor dl,00001111b
or al,bl
mov [edi],al

; write the first to-change sector
Write_into ecx, 1, Write_FAT_Entry_Exit


; restore the needed sector (+ 1 !)
inc ecx

; read the next Sector of the SST
Read ecx, 1, Write_FAT_Entry_data_losing


; store the remaining byte of the entry
shr ebx,4
mov [API_Buffer+0],bl

; write the second to-change sector
Write_into ecx, 1, Write_FAT_Entry_data_losing

jmp Write_FAT_Entry_Exit



Write_FAT_Entry_data_losing:
; comes here if there is an error while accessing (reading or writting) a FAT12 entry streches over sector boundarys

; contact security guard/change to security mode?
; ?

ret







Get_Next_Cluster:

; eax = Cluster
;   even on FAT12/16 root directorys it's interpreted as cluster size

; this function gets the next CLUSTER of any directory (specified by the given cluster), included the Root Directory
; cf is set if EOF is reached, then return = entry


; share into FAT12/16 and FAT32
cmp [Drive_Buffer+0Ah],byte 32
je Get_Next_Cluster_FAT32


; check if cluster is a sector of the FAT12/16 root directory
cmp [cluster_type],byte 18h
jne Get_Next_Cluster_no_root_directory


; if here, the cluster is a sector of the root directory
; if (Sectors per Cluster + Current Cluster) > Last_Root_Dir_Sector then EOC is reached
push bx
movzx bx,byte [Drive_Buffer+0Bh]
add ax,bx							; add Sectors per Cluster
pop bx

cmp [Drive_Buffer+1Ah],ax					; eax > last sector of the root directory?
jnl Get_Next_Cluster_RD_inc

; if here, the last sector of the root directory is reached, so there is no more cluster
mov eax,0FFFFFFFFh						; EOC is reached
stc
ret

Get_Next_Cluster_RD_inc:					; normal exit (already incremented)
; eax is already up to date (because of the add Sectors per Cluster)
clc
ret



Get_Next_Cluster_no_root_directory:				; share into FAT12 and FAT16
cmp [Drive_Buffer+0Ah],byte 16
je Get_Next_Cluster_FAT16


Get_Next_Cluster_FAT12:
call Read_FAT_entry

; EOF reached?
cmp eax,0FF8h
jl Get_Next_Cluster_succ					; if (eax < 0FF8h) then it's not the end

; else set cf (and exit)
stc
ret


Get_Next_Cluster_FAT16:
call Read_FAT_entry

; EOF reached?
cmp eax,0FFF8h
jl Get_Next_Cluster_succ					; if (eax < 0FFF8h) then it's not the end

; else set cf (and exit)
stc
ret


Get_Next_Cluster_FAT32:
call Read_FAT_entry

; the function returns a (32 bit) pointer, but on FAT32 entries there are only 28 bits used, so clear the top 4 bits
or eax,0F0000000h
xor eax,0F0000000h

; EOF reached?
cmp eax,0FFFFFF8h
jl Get_Next_Cluster_succ					; if (eax < 0FFFFFF8h) then it's not the end

; else set cf (and exit)
stc
ret


Get_Next_Cluster_succ:

clc
ret