3 ; Copyright (c) 1998, 2000, 2001 Brian Palmer
21 MaxRootEntries dw 0 ; Always zero for FAT32 volumes
22 TotalSectors dw 0 ; Always zero for FAT32 volumes
23 MediaDescriptor db 0f8h
24 SectorsPerFat dw 0 ; Always zero for FAT32 volumes
33 RootDirStartCluster dd 0
36 Reserved1 times 12 db 0
37 ; End FAT32 Inserted Info
41 SerialNumber dd 00000000h
42 VolumeLabel db 'NO NAME '
43 FileSystem db 'FAT32 '
49 ; Lets copy ourselves from 0000:7c00 to 9000:0000
50 ; and transfer control to the new code
61 ; Now we are executing at 9000:xxxx
62 ; We are now free to load freeldr.sys at 0000:7e00
66 mov sp,ax ; Setup a stack
67 mov ax,cs ; Setup segment registers
68 mov ds,ax ; Make DS correct
69 mov es,ax ; Make ES correct
70 mov ss,ax ; Make SS correct
77 cmp WORD [BYTE bp+SectorsPerFat],byte 0x00 ; Check the old 16-bit value of SectorsPerFat
78 jnz CheckFailed ; If it is non-zero then exit with an error
79 CheckTotalSectors: ; Check the old 16-bit value of TotalSectors & MaxRootEntries
80 cmp DWORD [BYTE bp+MaxRootEntries],byte 0x00; by comparing the DWORD at offset MaxRootEntries to zero
81 jnz CheckFailed ; If it is non-zero then exit with an error
82 CheckFileSystemVersion:
83 cmp WORD [BYTE bp+FSVersion],byte 0x00 ; Check the file system version word
84 jna GetDriveParameters ; It is zero, so continue
86 jmp PrintFileSystemError ; If it is not zero then exit with an error
91 mov dl,[BYTE bp+BootDrive] ; Get boot drive in dl
92 int 13h ; Request drive parameters from the bios
93 jnc CalcDriveSize ; If the call succeeded then calculate the drive size
95 ; If we get here then the call to the BIOS failed
96 ; so just set CHS equal to the maximum addressable
102 ; Now that we have the drive geometry
103 ; lets calculate the drive size
104 mov bl,ch ; Put the low 8-bits of the cylinder count into BL
105 mov bh,cl ; Put the high 2-bits in BH
106 shr bh,6 ; Shift them into position, now BX contains the cylinder count
107 and cl,3fh ; Mask off cylinder bits from sector count
108 ; CL now contains sectors per track and DH contains head count
109 movzx eax,dh ; Move the heads into EAX
110 movzx ebx,bx ; Move the cylinders into EBX
111 movzx ecx,cl ; Move the sectors per track into ECX
112 inc eax ; Make it one based because the bios returns it zero based
113 inc ebx ; Make the cylinder count one based also
114 mul ecx ; Multiply heads with the sectors per track, result in edx:eax
115 mul ebx ; Multiply the cylinders with (heads * sectors) [stored in edx:eax already]
117 ; We now have the total number of sectors as reported
118 ; by the bios in eax, so store it in our variable
119 mov [BiosCHSDriveSize],eax
123 ; First we have to load our extra boot code at
124 ; sector 14 into memory at [9000:0200h]
126 add eax,DWORD [BYTE bp+HiddenSectors] ; Add the number of hidden sectors
129 mov es,bx ; Read sector to [9000:0200h]
136 ; Reads logical sectors into [ES:BX]
137 ; EAX has logical sector number to read
138 ; CX has number of sectors to read
140 cmp eax,DWORD [BiosCHSDriveSize] ; Check if they are reading a sector within CHS range
141 jbe ReadSectorsCHS ; Yes - go to the old CHS routine
144 pushad ; Save logical sector number & sector count
147 push eax ; Put 64-bit logical block address on stack
148 push es ; Put transfer segment on stack
149 push bx ; Put transfer offset on stack
150 push byte 1 ; Set transfer count to 1 sector
151 push byte 0x10 ; Set size of packet to 10h
152 mov si,sp ; Setup disk address packet on stack
154 CheckInt13hExtensions: ; Now make sure this computer supports extended reads
155 mov ah,0x41 ; AH = 41h
156 mov bx,0x55aa ; BX = 55AAh
157 mov dl,[BYTE bp+BootDrive] ; DL = drive (80h-FFh)
158 int 13h ; IBM/MS INT 13 Extensions - INSTALLATION CHECK
159 jc PrintDiskError ; CF set on error (extensions not supported)
160 cmp bx,0xaa55 ; BX = AA55h if installed
162 test cl,1 ; CX = API subset support bitmap
163 jz PrintDiskError ; Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported
166 ; Good, we're here so the computer supports LBA disk access
167 ; So finish the extended read
168 mov dl,[BYTE bp+BootDrive] ; Drive number
169 mov ah,42h ; Int 13h, AH = 42h - Extended Read
171 jc PrintDiskError ; If the read failed then abort
173 add sp,0x10 ; Remove disk address packet from stack
175 popad ; Restore sector count & logical sector number
177 inc eax ; Increment sector to read
179 add dx,byte 20h ; Increment read buffer for next sector
182 loop ReadSectorsLBA ; Read next sector
187 ; Reads logical sectors into [ES:BX]
188 ; EAX has logical sector number to read
189 ; CX has number of sectors to read
193 movzx ecx,WORD [BYTE bp+SectorsPerTrack]
194 div ecx ; Divide logical by SectorsPerTrack
195 inc dl ; Sectors numbering starts at 1 not 0
196 mov cl,dl ; Sector in CL
199 div WORD [BYTE bp+NumberOfHeads] ; Divide logical by number of heads
200 mov dh,dl ; Head in DH
201 mov dl,[BYTE bp+BootDrive] ; Drive number in DL
202 mov ch,al ; Cylinder in CX
203 ror ah,1 ; Low 8 bits of cylinder in CH, high 2 bits
204 ror ah,1 ; in CL shifted to bits 6 & 7
205 or cl,ah ; Or with sector number
207 int 13h ; DISK - READ SECTORS INTO MEMORY
208 ; AL = number of sectors to read, CH = track, CL = sector
209 ; DH = head, DL = drive, ES:BX -> buffer to fill
210 ; Return: CF set on error, AH = status (see AH=01h), AL = number of sectors read
212 jc PrintDiskError ; If the read failed then abort
216 inc eax ; Increment Sector to Read
219 add dx,byte 20h ; Increment read buffer for next sector
222 loop ReadSectorsCHS ; Read next sector
229 ; Displays a disk error message
232 mov si,msgDiskError ; Bad boot disk message
233 call PutChars ; Display it
234 mov si,msgAnyKey ; Press any key message
235 call PutChars ; Display it
239 ; Displays a file system error message
241 PrintFileSystemError:
242 mov si,msgFileSystemError ; FreeLdr not found message
243 call PutChars ; Display it
244 mov si,msgAnyKey ; Press any key message
245 call PutChars ; Display it
249 int 16h ; Wait for a keypress
265 BiosCHSDriveSize dd 0
267 msgDiskError db 'Disk error',0dh,0ah,0
268 msgFileSystemError db 'File system error',0dh,0ah,0
269 msgAnyKey db 'Press any key to restart',0dh,0ah,0
271 times 510-($-$$) db 0 ; Pad to 510 bytes
272 dw 0aa55h ; BootSector signature
277 ; Now starts the extra boot code that we will store
278 ; at sector 14 on a FAT32 volume
280 ; To remain multi-boot compatible with other operating
281 ; systems we must not overwrite anything other than
282 ; the bootsector which means we will have to use
283 ; a different sector like 14 to store our extra boot code
288 ; Now we must get the first cluster of the root directory
289 mov eax,DWORD [BYTE bp+RootDirStartCluster]
290 cmp eax,0ffffff8h ; Check to see if this is the last cluster in the chain
291 jb ContinueSearch ; If not continue, if so then we didn't find freeldr.sys
292 jmp PrintFileNotFound
295 mov es,bx ; Read cluster to [0000:7e00h]
296 call ReadCluster ; Read the cluster
299 ; Now we have to find our way through the root directory to
300 ; The OSLOADER.SYS file
302 mov bl,[BYTE bp+SectsPerCluster]
303 shl bx,4 ; BX = BX * 512 / 32
304 mov ax,7e0h ; We loaded at 07e0:0000
309 rep cmpsb ; Compare filenames
310 jz FoundFile ; If same we found it
313 jmp PrintFileNotFound
316 mov ax,es ; We didn't find it in the previous dir entry
317 add ax,2 ; So lets move to the next one
318 mov es,ax ; And search again
322 rep cmpsb ; Compare filenames
323 jz FoundFile ; If same we found it
324 dec bx ; Keep searching till we run out of dir entries
325 jnz FindFile ; Last entry?
327 ; Get the next root dir cluster and try again until we run out of clusters
328 mov eax,DWORD [BYTE bp+RootDirStartCluster]
330 mov [BYTE bp+RootDirStartCluster],eax
335 ; Display "Loading FreeLoader..." message
336 mov si,msgLoading ; Loading message
337 call PutChars ; Display it
341 xor di,di ; ES:DI has dir entry
343 mov ax,WORD [es:di+14h] ; Get start cluster high word
345 mov ax,WORD [es:di+1ah] ; Get start cluster low word
348 cmp eax,2 ; Check and see if the start cluster starts at cluster 2 or above
349 jnb CheckEndCluster ; If so then continue
350 jmp PrintFileSystemError ; If not exit with error
352 cmp eax,0ffffff8h ; Check and see if the start cluster is and end of cluster chain indicator
353 jb InitializeLoadSegment ; If not then continue
354 jmp PrintFileSystemError ; If so exit with error
356 InitializeLoadSegment:
361 cmp eax,0ffffff8h ; Check to see if this is the last cluster in the chain
362 jae LoadFileDone ; If so continue, if not then read the next one
364 xor bx,bx ; Load ROSLDR starting at 0000:7e00h
370 mov bl,[BYTE bp+SectsPerCluster]
371 shl bx,5 ; BX = BX * 512 / 16
372 mov ax,es ; Increment the load address by
373 add ax,bx ; The size of a cluster
378 call GetFatEntry ; Get the next entry
381 jmp LoadFile ; Load the next cluster (if any)
384 mov dl,[BYTE bp+BootDrive]
385 xor ax,ax ; We loaded at 0000:7e00 but the entry point is 0000:8000
386 push ax ; because the first 512 bytes is fat helper code
387 push WORD 8000h ; We will do a far return to 0000:8000h
388 retf ; Transfer control to ROSLDR
391 ; Returns the FAT entry for a given cluster number
392 ; On entry EAX has cluster number
393 ; On return EAX has FAT entry for that cluster
396 shl eax,2 ; EAX = EAX * 4 (since FAT32 entries are 4 bytes)
397 mov ecx,eax ; Save this for later in ECX
399 movzx ebx,WORD [BYTE bp+BytesPerSector]
401 div ebx ; FAT Sector Number = EAX / BytesPerSector
402 movzx ebx,WORD [BYTE bp+ReservedSectors]
403 add eax,ebx ; FAT Sector Number += ReservedSectors
404 mov ebx,DWORD [BYTE bp+HiddenSectors]
405 add eax,ebx ; FAT Sector Number += HiddenSectors
408 and ecx,ebx ; FAT Offset Within Sector = ECX % BytesPerSector
409 ; EAX holds logical FAT sector number
410 ; ECX holds FAT entry offset
412 ; Now we have to check the extended flags
413 ; to see which FAT is the active one
414 ; and use it, or if they are mirrored then
416 movzx ebx,WORD [BYTE bp+ExtendedFlags] ; Get extended flags and put into ebx
417 and bx,0x0f ; Mask off upper 8 bits, now we have active fat in bl
418 jz LoadFatSector ; If fat is mirrored then skip fat calcs
419 cmp bl,[BYTE bp+NumberOfFats] ; Compare bl to number of fats
420 jb GetActiveFatOffset
421 jmp PrintFileSystemError ; If bl is bigger than numfats exit with error
423 push eax ; Save logical FAT sector number
424 mov eax,[BYTE bp+SectorsPerFatBig] ; Get the number of sectors occupied by one fat in eax
425 mul ebx ; Multiplied by the active FAT index we have in ebx
426 pop edx ; Get logical FAT sector number
427 add eax,edx ; Add the current FAT sector offset
431 ; EAX holds logical FAT sector number
434 xor bx,bx ; We will load it to [7000:0000h]
440 mov eax,DWORD [es:ecx] ; Get FAT entry
441 and eax,0fffffffh ; Mask off reserved bits
446 ; Reads cluster number in EAX into [ES:0000]
448 ; StartSector = ((Cluster - 2) * SectorsPerCluster) + ReservedSectors + HiddenSectors;
453 movzx ebx,BYTE [BYTE bp+SectsPerCluster]
457 movzx eax,BYTE [BYTE bp+NumberOfFats]
458 mul DWORD [BYTE bp+SectorsPerFatBig]
459 movzx ebx,WORD [BYTE bp+ReservedSectors]
461 add eax,DWORD [BYTE bp+HiddenSectors]
463 add eax,ebx ; EAX now contains the logical sector number of the cluster
464 xor bx,bx ; We will load it to [ES:0000], ES loaded before function call
465 movzx cx,BYTE [BYTE bp+SectsPerCluster]
470 ; Displays a file not found error message
473 mov si,msgFreeLdr ; FreeLdr not found message
474 call PutChars ; Display it
475 mov si,msgAnyKey ; Press any key message
476 call PutChars ; Display it
480 msgFreeLdr db 'FREELDR.SYS not found',0dh,0ah,0
481 filename db 'FREELDR SYS'
482 msgLoading db 'Loading FreeLoader...',0dh,0ah,0
485 times 1022-($-$$) db 0 ; Pad to 1022 bytes
487 dw 0aa55h ; BootSector signature