3 ; Copyright (c) 1998, 2000, 2001, 2002 Brian Palmer
20 MaxRootEntries dw 0 ; Always zero for FAT32 volumes
21 TotalSectors dw 0 ; Always zero for FAT32 volumes
22 MediaDescriptor db 0f8h
23 SectorsPerFat dw 0 ; Always zero for FAT32 volumes
32 RootDirStartCluster dd 0
35 Reserved1 times 12 db 0
36 ; End FAT32 Inserted Info
40 SerialNumber dd 00000000h
41 VolumeLabel db 'NO NAME '
42 FileSystem db 'FAT32 '
45 xor ax,ax ; Setup segment registers
46 mov ds,ax ; Make DS correct
47 mov es,ax ; Make ES correct
48 mov ss,ax ; Make SS correct
50 mov sp,7c00h ; Setup a stack
54 cmp BYTE [BYTE bp+BootDrive],BYTE 0xff ; If they have specified a boot drive then use it
55 jne CheckSectorsPerFat
57 mov [BYTE bp+BootDrive],dl ; Save the boot drive
62 cmp WORD [BYTE bp+SectorsPerFat],byte 0x00 ; Check the old 16-bit value of SectorsPerFat
63 jnz CheckFailed ; If it is non-zero then exit with an error
64 CheckTotalSectors: ; Check the old 16-bit value of TotalSectors & MaxRootEntries
65 cmp DWORD [BYTE bp+MaxRootEntries],byte 0x00; by comparing the DWORD at offset MaxRootEntries to zero
66 jnz CheckFailed ; If it is non-zero then exit with an error
67 CheckFileSystemVersion:
68 cmp WORD [BYTE bp+FSVersion],byte 0x00 ; Check the file system version word
69 jna GetDriveParameters ; It is zero, so continue
71 jmp PrintFileSystemError ; If it is not zero then exit with an error
76 mov dl,[BYTE bp+BootDrive] ; Get boot drive in dl
77 int 13h ; Request drive parameters from the bios
78 jnc CalcDriveSize ; If the call succeeded then calculate the drive size
80 ; If we get here then the call to the BIOS failed
81 ; so just set CHS equal to the maximum addressable
87 ; Now that we have the drive geometry
88 ; lets calculate the drive size
89 mov bl,ch ; Put the low 8-bits of the cylinder count into BL
90 mov bh,cl ; Put the high 2-bits in BH
91 shr bh,6 ; Shift them into position, now BX contains the cylinder count
92 and cl,3fh ; Mask off cylinder bits from sector count
93 ; CL now contains sectors per track and DH contains head count
94 movzx eax,dh ; Move the heads into EAX
95 movzx ebx,bx ; Move the cylinders into EBX
96 movzx ecx,cl ; Move the sectors per track into ECX
97 inc eax ; Make it one based because the bios returns it zero based
98 inc ebx ; Make the cylinder count one based also
99 mul ecx ; Multiply heads with the sectors per track, result in edx:eax
100 mul ebx ; Multiply the cylinders with (heads * sectors) [stored in edx:eax already]
102 ; We now have the total number of sectors as reported
103 ; by the bios in eax, so store it in our variable
104 mov [BiosCHSDriveSize],eax
108 ; First we have to load our extra boot code at
109 ; sector 14 into memory at [0000:7e00h]
111 add eax,DWORD [BYTE bp+HiddenSectors] ; Add the number of hidden sectors
114 mov es,bx ; Read sector to [0000:7e00h]
121 ; Reads logical sectors into [ES:BX]
122 ; EAX has logical sector number to read
123 ; CX has number of sectors to read
126 cmp eax,DWORD [BiosCHSDriveSize] ; Check if they are reading a sector outside CHS range
127 jae ReadSectorsLBA ; Yes - go to the LBA routine
128 ; If at all possible we want to use LBA routines because
129 ; They are optimized to read more than 1 sector per read
131 pushad ; Save logical sector number & sector count
133 CheckInt13hExtensions: ; Now check if this computer supports extended reads
134 mov ah,0x41 ; AH = 41h
135 mov bx,0x55aa ; BX = 55AAh
136 mov dl,[BYTE bp+BootDrive] ; DL = drive (80h-FFh)
137 int 13h ; IBM/MS INT 13 Extensions - INSTALLATION CHECK
138 jc ReadSectorsCHS ; CF set on error (extensions not supported)
139 cmp bx,0xaa55 ; BX = AA55h if installed
141 test cl,1 ; CX = API subset support bitmap
142 jz ReadSectorsCHS ; Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported
144 popad ; Restore sector count & logical sector number
147 pushad ; Save logical sector number & sector count
149 cmp cx,64 ; Since the LBA calls only support 0x7F sectors at a time we will limit ourselves to 64
150 jbe ReadSectorsSetupDiskAddressPacket ; If we are reading less than 65 sectors then just do the read
151 mov cx,64 ; Otherwise read only 64 sectors on this loop iteration
153 ReadSectorsSetupDiskAddressPacket:
154 mov [LBASectorsRead],cx
156 push eax ; Put 64-bit logical block address on stack
157 push es ; Put transfer segment on stack
158 push bx ; Put transfer offset on stack
159 push cx ; Set transfer count
160 push byte 0x10 ; Set size of packet to 10h
161 mov si,sp ; Setup disk address packet on stack
164 mov dl,[BYTE bp+BootDrive] ; Drive number
165 mov ah,42h ; Int 13h, AH = 42h - Extended Read
167 jc PrintDiskError ; If the read failed then abort
169 add sp,byte 0x10 ; Remove disk address packet from stack
171 popad ; Restore sector count & logical sector number
174 mov ebx,DWORD [LBASectorsRead]
175 add eax,ebx ; Increment sector to read
178 add dx,bx ; Setup read buffer for next sector
182 sub cx,[LBASectorsRead]
183 jnz ReadSectorsLBA ; Read next sector
192 ; Reads logical sectors into [ES:BX]
193 ; EAX has logical sector number to read
194 ; CX has number of sectors to read
196 popad ; Get logical sector number & sector count off stack
201 movzx ecx,WORD [BYTE bp+SectorsPerTrack]
202 div ecx ; Divide logical by SectorsPerTrack
203 inc dl ; Sectors numbering starts at 1 not 0
204 mov cl,dl ; Sector in CL
207 div WORD [BYTE bp+NumberOfHeads] ; Divide logical by number of heads
208 mov dh,dl ; Head in DH
209 mov dl,[BYTE bp+BootDrive] ; Drive number in DL
210 mov ch,al ; Cylinder in CX
211 ror ah,1 ; Low 8 bits of cylinder in CH, high 2 bits
212 ror ah,1 ; in CL shifted to bits 6 & 7
213 or cl,ah ; Or with sector number
215 int 13h ; DISK - READ SECTORS INTO MEMORY
216 ; AL = number of sectors to read, CH = track, CL = sector
217 ; DH = head, DL = drive, ES:BX -> buffer to fill
218 ; Return: CF set on error, AH = status (see AH=01h), AL = number of sectors read
220 jc PrintDiskError ; If the read failed then abort
224 inc eax ; Increment Sector to Read
227 add dx,byte 20h ; Increment read buffer for next sector
230 loop ReadSectorsCHSLoop ; Read next sector
237 ; Displays a disk error message
240 mov si,msgDiskError ; Bad boot disk message
241 call PutChars ; Display it
245 ; Displays a file system error message
247 PrintFileSystemError:
248 mov si,msgFileSystemError ; FreeLdr not found message
249 call PutChars ; Display it
252 mov si,msgAnyKey ; Press any key message
253 call PutChars ; Display it
255 int 16h ; Wait for a keypress
271 BiosCHSDriveSize dd 0
273 msgDiskError db 'Disk error',0dh,0ah,0
274 msgFileSystemError db 'File system error',0dh,0ah,0
275 msgAnyKey db 'Press any key to restart',0dh,0ah,0
277 times 509-($-$$) db 0 ; Pad to 509 bytes
283 dw 0aa55h ; BootSector signature
288 ; Now starts the extra boot code that we will store
289 ; at sector 14 on a FAT32 volume
291 ; To remain multi-boot compatible with other operating
292 ; systems we must not overwrite anything other than
293 ; the bootsector which means we will have to use
294 ; a different sector like 14 to store our extra boot code
299 ; Now we must get the first cluster of the root directory
300 mov eax,DWORD [BYTE bp+RootDirStartCluster]
301 cmp eax,0ffffff8h ; Check to see if this is the last cluster in the chain
302 jb ContinueSearch ; If not continue, if so then we didn't find freeldr.sys
303 jmp PrintFileNotFound
306 mov es,bx ; Read cluster to [2000:0000h]
307 call ReadCluster ; Read the cluster
310 ; Now we have to find our way through the root directory to
311 ; The FREELDR.SYS file
313 mov bl,[BYTE bp+SectsPerCluster]
314 shl bx,4 ; BX = BX * 512 / 32
315 mov ax,2000h ; We loaded at 2000:0000
320 rep cmpsb ; Compare filenames
321 jz FoundFile ; If same we found it
324 jmp PrintFileNotFound
327 mov ax,es ; We didn't find it in the previous dir entry
328 add ax,2 ; So lets move to the next one
329 mov es,ax ; And search again
333 rep cmpsb ; Compare filenames
334 jz FoundFile ; If same we found it
335 dec bx ; Keep searching till we run out of dir entries
336 jnz FindFile ; Last entry?
338 ; Get the next root dir cluster and try again until we run out of clusters
339 mov eax,DWORD [BYTE bp+RootDirStartCluster]
341 mov [BYTE bp+RootDirStartCluster],eax
345 ; Display "Loading FreeLoader..." message
346 mov si,msgLoading ; Loading message
347 call PutChars ; Display it
349 xor di,di ; ES:DI has dir entry
351 mov ax,WORD [es:di+14h] ; Get start cluster high word
353 mov ax,WORD [es:di+1ah] ; Get start cluster low word
356 cmp eax,2 ; Check and see if the start cluster starts at cluster 2 or above
357 jnb CheckEndCluster ; If so then continue
358 jmp PrintFileSystemError ; If not exit with error
360 cmp eax,0ffffff8h ; Check and see if the start cluster is and end of cluster chain indicator
361 jb InitializeLoadSegment ; If not then continue
362 jmp PrintFileSystemError ; If so exit with error
364 InitializeLoadSegment:
365 mov bx,0F80h ; FREELDR_BASE / 16
369 cmp eax,0ffffff8h ; Check to see if this is the last cluster in the chain
370 jae LoadFileDone ; If so continue, if not then read the next one
372 xor bx,bx ; Load ROSLDR starting at 0000:F800h
378 mov bl,[BYTE bp+SectsPerCluster]
379 shl bx,5 ; BX = BX * 512 / 16
380 mov ax,es ; Increment the load address by
381 add ax,bx ; The size of a cluster
386 call GetFatEntry ; Get the next entry
389 jmp LoadFile ; Load the next cluster (if any)
392 mov dl,[BYTE bp+BootDrive] ; Load boot drive into DL
393 mov dh,[BootPartition] ; Load boot partition into DH
395 ; Transfer execution to the bootloader
396 ;ljmp16 0, FREELDR_BASE
401 ; Returns the FAT entry for a given cluster number
402 ; On entry EAX has cluster number
403 ; On return EAX has FAT entry for that cluster
406 shl eax,2 ; EAX = EAX * 4 (since FAT32 entries are 4 bytes)
407 mov ecx,eax ; Save this for later in ECX
409 movzx ebx,WORD [BYTE bp+BytesPerSector]
411 div ebx ; FAT Sector Number = EAX / BytesPerSector
412 movzx ebx,WORD [BYTE bp+ReservedSectors]
413 add eax,ebx ; FAT Sector Number += ReservedSectors
414 mov ebx,DWORD [BYTE bp+HiddenSectors]
415 add eax,ebx ; FAT Sector Number += HiddenSectors
418 and ecx,ebx ; FAT Offset Within Sector = ECX % BytesPerSector
419 ; EAX holds logical FAT sector number
420 ; ECX holds FAT entry offset
422 ; Now we have to check the extended flags
423 ; to see which FAT is the active one
424 ; and use it, or if they are mirrored then
426 movzx ebx,WORD [BYTE bp+ExtendedFlags] ; Get extended flags and put into ebx
427 and bx,0x0f ; Mask off upper 8 bits, now we have active fat in bl
428 jz LoadFatSector ; If fat is mirrored then skip fat calcs
429 cmp bl,[BYTE bp+NumberOfFats] ; Compare bl to number of fats
430 jb GetActiveFatOffset
431 jmp PrintFileSystemError ; If bl is bigger than numfats exit with error
433 push eax ; Save logical FAT sector number
434 mov eax,[BYTE bp+SectorsPerFatBig] ; Get the number of sectors occupied by one fat in eax
435 mul ebx ; Multiplied by the active FAT index we have in ebx
436 pop edx ; Get logical FAT sector number
437 add eax,edx ; Add the current FAT sector offset
442 mov bx, 9000h ; We will load it to [9000:0000h]
445 ; EAX holds logical FAT sector number
446 ; Check if we have already loaded it
447 cmp eax,DWORD [FatSectorInCache]
448 je LoadFatSectorAlreadyLoaded
450 mov DWORD [FatSectorInCache],eax
455 LoadFatSectorAlreadyLoaded:
457 mov eax,DWORD [es:ecx] ; Get FAT entry
458 and eax,0fffffffh ; Mask off reserved bits
462 FatSectorInCache: ; This variable tells us which sector we currently have in memory
463 dd 0ffffffffh ; There is no need to re-read the same sector if we don't have to
466 ; Reads cluster number in EAX into [ES:0000]
468 ; StartSector = ((Cluster - 2) * SectorsPerCluster) + ReservedSectors + HiddenSectors;
473 movzx ebx,BYTE [BYTE bp+SectsPerCluster]
477 movzx eax,BYTE [BYTE bp+NumberOfFats]
478 mul DWORD [BYTE bp+SectorsPerFatBig]
479 movzx ebx,WORD [BYTE bp+ReservedSectors]
481 add eax,DWORD [BYTE bp+HiddenSectors]
483 add eax,ebx ; EAX now contains the logical sector number of the cluster
484 xor bx,bx ; We will load it to [ES:0000], ES loaded before function call
485 movzx cx,BYTE [BYTE bp+SectsPerCluster]
490 ; Displays a file not found error message
493 mov si,msgFreeLdr ; FreeLdr not found message
494 call PutChars ; Display it
495 mov si,msgAnyKey ; Press any key message
496 call PutChars ; Display it
500 msgFreeLdr db 'freeldr.sys not found',0dh,0ah,0
501 filename db 'FREELDR SYS'
502 msgLoading db 'Loading FreeLoader...',0dh,0ah,0
505 times 1022-($-$$) db 0 ; Pad to 1022 bytes
507 dw 0aa55h ; BootSector signature