I am writing a bootloader that needs to load a file from a FAT32 filesytem. Since I need to load both the root directory first to find the start of the file, and then the actual file, I wrote a function to load a sequence of clusters from the disk to 0x8000 in memory (since the bootloader is loaded from 0x7C00-0x7DFF and I use 0x7E00-0x7FFF to load sections of the FAT).
loadClusterChain: ; ARGS: eax = first cluster number
pusha
; Set the memory location to load the file to 0x8000
mov dx, 0x8000
loadClusterChainLoop:
; Store the cluster number (eax) and additional offset (dx) for later
push dx
push eax
; Update the disk address packet
mov word [dapDataOffset], dx
; Account for the cluster numbers being shifted by 2
sub eax, 2
; Calculate how many sectors the cluster is from the start of the data area
xor ebx, ebx
mov bl, byte [bpbSectorsPerCluster]
mul ebx
; Add the start of data area to the root directory offset
add eax, dword [diskDataAreaStart]
; Update the disk address packet start sector
mov dword [dapStartSector], eax
; Update the disk address packet sector count
push eax
xor ax, ax
mov al, byte [bpbSectorsPerCluster]
mov word [dapSectorCount], ax
pop eax
; Load the file cluster
mov ah, 0x42
mov dl, byte [bpbDriveNumber]
mov si, diskAddressPacket
int 0x13
jc diskError
; Retrieve the value of eax but keep it in the stack
pop eax
push eax
; Divide by 128 to calculate the FAT sector to load
shr eax, 7
; Get the number of reserved sectors
xor ebx, ebx
mov bx, word [bpbReservedSectors]
; Add the reserved sectors to the FAT sector, so eax = relevant FAT sector
add eax, ebx
; Update the disk address packet start sector
mov dword [dapStartSector], eax
; Update the disk address packet sector count
mov word [dapSectorCount], 1
; Update the disk address packet data offset to load the fat to 0x7E00
mov word [dapDataOffset], 0x7E00
; Load the FAT sector
mov ah, 0x42
mov dl, byte [bpbDriveNumber]
mov si, diskAddressPacket
int 0x13
jc diskError
; Get the offset of clusters into the FAT sector by getting the remainder when divided by 128
pop eax
and ax, 0x007F
; Multiply offset by 4 to get it in bytes
shl ax, 2
; Calculate the memory address of the cluster in the FAT
add ax, 0x7E00
; Read cluster value
mov bx, ax
mov eax, dword [bx]
; Set bx = 512*bpbSectorsPerCluster
xor bx, bx
mov bl, byte [bpbSectorsPerCluster]
shl bx, 9
; Restore the value of dx and add bx to move dx to the memory location of the end of the file parts loaded so far
pop dx
add dx, bx
; See if this is the end of the chain
cmp eax, 0x0FFFFFFF
jne loadClusterChainLoop
; Ensure the file ends with 0, so when scanning the root entries we will definitely stop
mov bx, dx
mov [bx], byte 0
popa
ret
Here are some of the variables used. diskDataAreaStart
is not 0 when the code runs as the actual value is calculated before the function is called. The other variable names are mostly self-explanatory.
diskDataAreaStart dd 0
diskAddressPacket:
dapSize db 0x10
dapReserved db 0x00
dapSectorCount dw 0
dapDataOffset dw 0
dapDataSegment dw 0
dapStartSector dq 0
I know it's a fairly niche piece of code, but I would be very grateful if someone could give me feedback on it - if I could do something more efficiently or even feedback on how clear my comments were and how easy the code was to follow along would be highly appreciated.