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