; FATHELP.ASM ; FAT12/16 Boot Sector Helper Code ; Copyright (c) 1998, 2001, 2002, 2003 Brian Palmer ;org 8000h segment .text bits 16 BootSectorStackTop equ 0x7bf2 DataAreaStartHigh equ 0x2 DataAreaStartLow equ 0x4 BiosCHSDriveSizeHigh equ 0x6 BiosCHSDriveSizeLow equ 0x8 BiosCHSDriveSize equ 0x8 ReadSectorsOffset equ 0xa ReadClusterOffset equ 0xc PutCharsOffset equ 0xe OEMName equ 3 BytesPerSector equ 11 SectsPerCluster equ 13 ReservedSectors equ 14 NumberOfFats equ 16 MaxRootEntries equ 17 TotalSectors equ 19 MediaDescriptor equ 21 SectorsPerFat equ 22 SectorsPerTrack equ 24 NumberOfHeads equ 26 HiddenSectors equ 28 TotalSectorsBig equ 32 BootDrive equ 36 Reserved equ 37 ExtendSig equ 38 SerialNumber equ 39 VolumeLabel equ 43 FileSystem equ 54 BootPartition equ 0x7dfd ; This code will be stored in the first 512 bytes ; of freeldr.sys. The first 3 bytes will be a jmp ; instruction to skip past the FAT helper code ; that is stored in the rest of the 512 bytes. ; ; This code is loaded at 0000:8000 so we have to ; encode a jmp instruction to jump to 0000:8200 global _mainCRTStartup ; For Mingw32 builds where the linker looks for this symbol _mainCRTStartup: global start start: db 0xe9 db 0xfd db 0x01 ; Now starts the extra boot code that we will store ; in the first 512 bytes of freeldr.sys. This code ; allows the FAT12/16 bootsector to navigate the ; FAT table so that we can still load freeldr.sys ; even if it is fragmented. FatHelperEntryPoint: push ax ; First save AX - the start cluster of freeldr.sys ; Display "Loading FreeLoader..." message mov esi,msgLoading ; Loading message call [bp-PutCharsOffset] ; Display it call ReadFatIntoMemory pop ax ; Restore AX (start cluster) ; AX has start cluster of freeldr.sys mov bx,800h mov es,bx LoadFile: push ax call IsFat12 pop ax jnc LoadFile2 cmp ax,0ff8h ; Check to see if this is the last cluster in the chain jmp LoadFile3 LoadFile2: cmp ax,0fff8h LoadFile3: jae LoadFile_Done ; If so continue, if not then read then next one push ax xor bx,bx ; Load ROSLDR starting at 0000:8000h push es call [bp-ReadClusterOffset] pop es xor bx,bx mov bl,BYTE [BYTE bp+SectsPerCluster] shl bx,5 ; BX = BX * 512 / 16 mov ax,es ; Increment the load address by add ax,bx ; The size of a cluster mov es,ax call IsFat12 pop ax push es jnc LoadFile4 call GetFatEntry12 ; Get the next entry jmp LoadFile5 LoadFile4: call GetFatEntry16 LoadFile5: pop es jmp LoadFile ; Load the next cluster (if any) LoadFile_Done: mov dl,BYTE [BYTE bp+BootDrive] ; Load the boot drive into DL mov dh,[BootPartition] ; Load the boot partition into DH push WORD 0x0000 push WORD 0x8000 ; We will do a far return to 0000:8000h retf ; Transfer control to ROSLDR ; Reads the entire FAT into memory at 7000:0000 ReadFatIntoMemory: mov ax,WORD [BYTE bp+HiddenSectors] mov dx,WORD [BYTE bp+HiddenSectors+2] add ax,WORD [BYTE bp+ReservedSectors] adc dx,byte 0 mov cx,WORD [BYTE bp+SectorsPerFat] mov bx,7000h mov es,bx xor bx,bx call [bp-ReadSectorsOffset] ret ; Returns the FAT entry for a given cluster number for 16-bit FAT ; On entry AX has cluster number ; On return AX has FAT entry for that cluster GetFatEntry16: mov cx,2 ; AX = AX * 2 (since FAT16 entries are 2 bytes) mul cx shl dx,12 mov bx,7000h add bx,dx mov es,bx mov bx,ax ; Restore FAT entry offset mov ax,WORD [es:bx] ; Get FAT entry ret ; Returns the FAT entry for a given cluster number for 12-bit FAT ; On entry AX has cluster number ; On return AX has FAT entry for that cluster GetFatEntry12: push ax mov cx,ax shr ax,1 add ax,cx ; AX = AX * 1.5 (AX = AX + (AX / 2)) (since FAT12 entries are 12 bits) mov bx,7000h mov es,bx mov bx,ax ; Put FAT entry offset into BX mov ax,WORD [es:bx] ; Get FAT entry pop cx ; Get cluster number from stack and cx,1 jz UseLow12Bits and ax,0fff0h shr ax,4 jmp GetFatEntry12_Done UseLow12Bits: and ax,0fffh GetFatEntry12_Done: ret ; Returns CF = 1 if this is a FAT12 file system ; Otherwise CF = 0 for FAT16 IsFat12: mov ebx,DWORD [BYTE bp-DataAreaStartLow] ; EBX now has the number of the starting sector of the data area ; starting from the beginning of the disk, so subtrace hidden sectors sub ebx,DWORD [BYTE bp+HiddenSectors] xor eax,eax mov ax,WORD [BYTE bp+TotalSectors] cmp ax,byte 0 jnz IsFat12_2 mov eax,DWORD [BYTE bp+TotalSectorsBig] ; EAX now contains the number of sectors on the volume IsFat12_2: sub eax,ebx ; Subtract data area start sector xor edx,edx ; from total sectors of volume ; EDX:EAX now contains the number of data sectors on the volume movzx ebx,BYTE [BYTE bp+SectsPerCluster] div ebx ; EAX now has the number of clusters on the volume stc cmp eax,4085 jb IsFat12_Done clc IsFat12_Done: ret msgLoading db 'Loading FreeLoader...',0dh,0ah,0 times 510-($-$$) db 0 ; Pad to 510 bytes dw 0aa55h ; BootSector signature