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.