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