flat assembler
Message board for the users of flat assembler.

 Index > OS Construction > Error in fatgen103
Author
Mike Gonta

Joined: 26 Dec 2010
Posts: 243
Mike Gonta 28 Mar 2011, 01:41
There is an error in the FAT Volume Initialization algorithm (on page 21) to calculate the number of sectors in a FAT32
File Allocation Table. Even though the document is over 10 years old it's still the most current.
fatgen103, page 21 wrote:
Do not spend too much time trying to figure out why this math works. The basis for the computation is complicated;
It's actually quite simple. Start with the total sectors, subtract the reserved sectors and the sectors used for both FATs.
Then add two clusters to account for the first two FAT entries. The total data sectors of the volume must be less or equal
to 4GB-1 for FAT32 and the MBR table. These sectors are not managed by the FAT. This leaves the total data sectors,
divide by the sectors per cluster for the total data clusters. Divide the total data clusters by the sector size divided by 4
to get the total FAT sectors. Any fractional remainder will require one extra FAT sector.
Code:
`  FATSz = DskSize - BPB_ResvdSecCnt - (2 * FATSz) + (2 * BPB_SecPerClus)/ BPB_SecPerClus * (BPB_BytsPerSec / 4)    `
Multiply both sides by (BPB_SecPerClus * (BPB_BytsPerSec / 4))
Code:
`    FATSz * (BPB_SecPerClus * (BPB_BytsPerSec / 4)) = DskSize - BPB_ResvdSecCnt - (2 * FATSz) + (2 * BPB_SecPerClus)    `
Add (2 * FATSz) to both sides
Code:
`   (FATSz * (BPB_SecPerClus * (BPB_BytsPerSec / 4))) + (2 * FATSz) = DskSize - BPB_ResvdSecCnt + (2 * BPB_SecPerClus)    `
Combine the FATSz
Code:
`    FATSz * ((BPB_SecPerClus * (BPB_BytsPerSec / 4)) + 2) = DskSize - BPB_ResvdSecCnt + (2 * BPB_SecPerClus)    `
Finally divide both sides by ((BPB_SecPerClus * (BPB_BytsPerSec / 4)) + 2) to get FATSz. Round up any fractional remainder.
Code:
`    FATSz = (DskSize - BPB_ResvdSecCnt + (2 * BPB_SecPerClus)) / ((BPB_SecPerClus * (BPB_BytsPerSec / 4)) + 2)    `

This is different (and much simpler) than the MS version:
fatgen103, page 21 wrote:
Code:
```RootDirSectors = ((BPB_RootEntCnt * 32) + (BPB_BytsPerSec - 1)) / BPB_BytsPerSec;
TmpVal1 = DskSize - (BPB_ResvdSecCnt + RootDirSectors);
TmpVal2 = (256 * BPB_SecPerClus) + BPB_NumFATs;
If(FATType == FAT32)
TmpVal2 = TmpVal2 / 2;
FATSz = (TMPVal1 + (TmpVal2 - 1)) / TmpVal2;    ```
fatgen103, page 13 wrote:
Note that on a FAT32 volume the BPB_RootEntCnt value is always 0, so on a FAT32 volume RootDirSectors is always 0.

Here's the FASM code which works as either use16 or use32. Note edx:eax is the 64 bit total bytes size.
Code:
```  shrd eax, edx, cl             ; ecx is BPB_BytsPerSec exponent, 512=9, 2048=11, 4096=12
shr edx, cl                   ; divide size by 512 for DskSize
movzx ebx, WORD [BPB_ResvdSecCnt]
sub eax, ebx
sbb edx, 0                    ; DskSize - BPB_ResvdSecCnt
movzx ebx, BYTE [BPB_SecPerClus]
add eax, ebx                  ; + (2 * BPB_SecPerClus)
jnc .1
mov eax, -1                   ; max size 4GB-1
.1:
sub ecx, 2
shl ebx, cl
add ebx, 2                    ; (BPB_SecPerClus * (BPB_BytsPerSec / 4)) + 2
xor edx, edx
div ebx                       ; (DskSize - BPB_ResvdSecCnt) / ((BPB_SecPerClus * (BPB_BytsPerSec / 4)) + 2)
cmp edx, 0
adc eax, 0                    ; need one extra sector if any remainder    ```

Format any size USB flash drive FAT32. Using a disk editor like HxD the following variables are located at the following offsets from the start of the first volume sector.
Code:
```BPB_SecPerClus                    equ 13 ; BYTE
BPB_ResvdSecCnt                   equ 14 ; WORD
BPB_NumFATs                       equ 16 ; BYTE
DskSize                           equ 32 ; DWORD
FATSz                             equ 36 ; DWORD    ```

Do the math and check the FATSz as actually formatted.

Edit: Added correction to code by revolution
Edit: Added size of sector calculation and compensation for first two FAT entries.

_________________
Mike Gonta
look and see - many look but few see

https://mikegonta.com

Last edited by Mike Gonta on 29 Mar 2011, 09:03; edited 2 times in total
28 Mar 2011, 01:41
revolution
When all else fails, read the source

Joined: 24 Aug 2004
Posts: 20298
revolution 28 Mar 2011, 01:52
Mike Gonta wrote:
Code:
`  shrd edx, eax, 9              ; divide size by 512 for DskSize    `
If you want to divide EDX:EAX by 512 then you should do this:
Code:
```shrd eax,edx,9
shr edx,9    ```
Mike Gonta wrote:
Code:
`  sub eax, ebx                  ; DskSize - BPB_ResvdSecCnt    `
If you need to subtract a 32 bit number from a 64 bit number you need to propagate the carry:
Code:
```sub eax,ebx
sbb edx,0    ```
28 Mar 2011, 01:52
 Display posts from previous: All Posts1 Day7 Days2 Weeks1 Month3 Months6 Months1 Year Oldest FirstNewest First

 Jump to: Select a forum Official----------------AssemblyPeripheria General----------------MainTutorials and ExamplesDOSWindowsLinuxUnixMenuetOS Specific----------------MacroinstructionsOS ConstructionIDE DevelopmentProjects and IdeasNon-x86 architecturesHigh Level LanguagesProgramming Language DesignCompiler Internals Other----------------FeedbackHeapTest Area

Forum Rules:
 You cannot post new topics in this forumYou cannot reply to topics in this forumYou cannot edit your posts in this forumYou cannot delete your posts in this forumYou cannot vote in polls in this forumYou cannot attach files in this forumYou can download files in this forum