3 ; Copyright (c) 1998, 2001, 2002 Brian Palmer
7 ; This is a FAT12/16 file system boot sector
8 ; that searches the entire root directory
9 ; for the file freeldr.sys and loads it into
12 ; The stack is set to 0000:7BF2 so that the first
13 ; WORD pushed will be placed at 0000:7BF0
15 ; The DWORD at 0000:7BFC or BP-04h is the logical
16 ; sector number of the start of the data area.
18 ; The DWORD at 0000:7BF8 or BP-08h is the total
19 ; sector count of the boot drive as reported by
22 ; The WORD at 0000:7BF6 or BP-0ah is the offset
23 ; of the ReadSectors function in the boot sector.
25 ; The WORD at 0000:7BF4 or BP-0ch is the offset
26 ; of the ReadCluster function in the boot sector.
28 ; The WORD at 0000:7BF2 or BP-0eh is the offset
29 ; of the PutChars function in the boot sector.
31 ; When it locates freeldr.sys on the disk it will
32 ; load the first sector of the file to 0000:8000
33 ; With the help of this sector we should be able
34 ; to load the entire file off the disk, no matter
35 ; how fragmented it is.
37 ; We load the entire FAT table into memory at
38 ; 7000:0000. This improves the speed of floppy disk
42 BootSectorStackTop equ 0x7bf2
43 DataAreaStartHigh equ 0x2
44 DataAreaStartLow equ 0x4
45 BiosCHSDriveSizeHigh equ 0x6
46 BiosCHSDriveSizeLow equ 0x8
47 BiosCHSDriveSize equ 0x8
48 ReadSectorsOffset equ 0xa
49 ReadClusterOffset equ 0xc
50 PutCharsOffset equ 0xe
70 MediaDescriptor db 0f0h
79 SerialNumber dd 00000000h
80 VolumeLabel db 'NO NAME '
81 FileSystem db 'FAT12 '
87 mov sp,BootSectorStackTop ; Setup a stack
88 mov ds,ax ; Make DS correct
89 mov es,ax ; Make ES correct
92 cmp BYTE [BYTE bp+BootDrive],BYTE 0xff ; If they have specified a boot drive then use it
93 jne GetDriveParameters
95 mov [BYTE bp+BootDrive],dl ; Save the boot drive
100 mov dl,[BYTE bp+BootDrive] ; Get boot drive in dl
101 int 13h ; Request drive parameters from the bios
102 jnc CalcDriveSize ; If the call succeeded then calculate the drive size
104 ; If we get here then the call to the BIOS failed
105 ; so just set CHS equal to the maximum addressable
111 ; Now that we have the drive geometry
112 ; lets calculate the drive size
113 mov bl,ch ; Put the low 8-bits of the cylinder count into BL
114 mov bh,cl ; Put the high 2-bits in BH
115 shr bh,6 ; Shift them into position, now BX contains the cylinder count
116 and cl,3fh ; Mask off cylinder bits from sector count
117 ; CL now contains sectors per track and DH contains head count
118 movzx eax,dh ; Move the heads into EAX
119 movzx ebx,bx ; Move the cylinders into EBX
120 movzx ecx,cl ; Move the sectors per track into ECX
121 inc eax ; Make it one based because the bios returns it zero based
122 inc ebx ; Make the cylinder count one based also
123 mul ecx ; Multiply heads with the sectors per track, result in edx:eax
124 mul ebx ; Multiply the cylinders with (heads * sectors) [stored in edx:eax already]
126 ; We now have the total number of sectors as reported
127 ; by the bios in eax, so store it in our variable
128 mov [BYTE bp-BiosCHSDriveSize],eax
131 ; Now we must find our way to the first sector of the root directory
134 mov al,[BYTE bp+NumberOfFats] ; Number of fats
135 mul WORD [BYTE bp+SectorsPerFat] ; Times sectors per fat
136 add ax,WORD [BYTE bp+HiddenSectors]
137 adc dx,WORD [BYTE bp+HiddenSectors+2] ; Add the number of hidden sectors
138 add ax,WORD [BYTE bp+ReservedSectors] ; Add the number of reserved sectors
139 adc dx,cx ; Add carry bit
140 mov WORD [BYTE bp-DataAreaStartLow],ax ; Save the starting sector of the root directory
141 mov WORD [BYTE bp-DataAreaStartHigh],dx ; Save it in the first 4 bytes before the boot sector
142 mov si,WORD [BYTE bp+MaxRootEntries] ; Get number of root dir entries in SI
143 pusha ; Save 32-bit logical start sector of root dir
144 ; DX:AX now has the number of the starting sector of the root directory
146 ; Now calculate the size of the root directory
148 mov ax,0020h ; Size of dir entry
149 mul si ; Times the number of entries
150 mov bx,[BYTE bp+BytesPerSector]
153 div bx ; Divided by the size of a sector
154 ; AX now has the number of root directory sectors
156 add [BYTE bp-DataAreaStartLow],ax ; Add the number of sectors of the root directory to our other value
157 adc [BYTE bp-DataAreaStartHigh],cx ; Now the first 4 bytes before the boot sector contain the starting sector of the data area
158 popa ; Restore root dir logical sector start to DX:AX
161 mov bx,7e0h ; We will load the root directory sector
162 mov es,bx ; Right after the boot sector in memory
163 xor bx,bx ; We will load it to [0000:7e00h]
164 xor cx,cx ; Zero out CX
165 inc cx ; Now increment it to 1, we are reading one sector
166 xor di,di ; Zero out di
167 push es ; Save ES because it will get incremented by 20h
168 call ReadSectors ; Read the first sector of the root directory
169 pop es ; Restore ES (ES:DI = 07E0:0000)
172 cmp [es:di],ch ; If the first byte of the directory entry is zero then we have
173 jz ErrBoot ; reached the end of the directory and FREELDR.SYS is not here so reboot
174 pusha ; Save all registers
175 mov cl,0xb ; Put 11 in cl (length of filename in directory entry)
176 mov si,filename ; Put offset of filename string in DS:SI
177 repe cmpsb ; Compare this directory entry against 'FREELDR SYS'
178 popa ; Restore all the registers
179 jz FoundFreeLoader ; If we found it then jump
180 dec si ; SI holds MaxRootEntries, subtract one
181 jz ErrBoot ; If we are out of root dir entries then reboot
182 add di,BYTE +0x20 ; Increment DI by the size of a directory entry
183 cmp di,0200h ; Compare DI to 512 (DI has offset to next dir entry, make sure we haven't gone over one sector)
184 jc SearchRootDirSector ; If DI is less than 512 loop again
185 jmp short LoadRootDirSector ; Didn't find FREELDR.SYS in this directory sector, try again
188 ; We found freeldr.sys on the disk
189 ; so we need to load the first 512
190 ; bytes of it to 0000:8000
191 ; ES:DI has dir entry (ES:DI == 07E0:XXXX)
192 mov ax,WORD [es:di+1ah] ; Get start cluster
193 push ax ; Save start cluster
194 push WORD 800h ; Put 800h on the stack and load it
195 pop es ; Into ES so that we load the cluster at 0000:8000
196 call ReadCluster ; Read the cluster
197 pop ax ; Restore start cluster of FreeLoader
199 ; Save the addresses of needed functions so
200 ; the helper code will know where to call them.
201 mov WORD [BYTE bp-ReadSectorsOffset],ReadSectors ; Save the address of ReadSectors
202 mov WORD [BYTE bp-ReadClusterOffset],ReadCluster ; Save the address of ReadCluster
203 mov WORD [BYTE bp-PutCharsOffset],PutChars ; Save the address of PutChars
205 ; Now AX has start cluster of FreeLoader and we
206 ; have loaded the helper code in the first 512 bytes
207 ; of FreeLoader to 0000:8000. Now transfer control
208 ; to the helper code. Skip the first three bytes
209 ; because they contain a jump instruction to skip
210 ; over the helper code in the FreeLoader image.
212 push 0 ; push segment (0x0000)
213 mov bx, [0x8000 + 0xA8] ; load the RVA of the EntryPoint into eax
214 add bx, 0x8003 ; RVA -> VA and skip 3 bytes (jump to fathelper code)
215 push bx ; push offset
216 retf ; Transfer control to FreeLoader
221 ; Displays an error message
224 mov si,msgFreeLdr ; FreeLdr not found message
225 call PutChars ; Display it
228 mov si,msgAnyKey ; Press any key message
229 call PutChars ; Display it
231 int 16h ; Wait for a keypress
245 ; Displays a bad boot message
248 mov si,msgDiskError ; Bad boot disk message
249 call PutChars ; Display it
254 ; Reads cluster number in AX into [ES:0000]
256 ; StartSector = ((Cluster - 2) * SectorsPerCluster) + ReservedSectors + HiddenSectors;
257 dec ax ; Adjust start cluster by 2
258 dec ax ; Because the data area starts on cluster 2
260 mov cl,BYTE [BYTE bp+SectsPerCluster]
261 mul cx ; Times sectors per cluster
262 add ax,[BYTE bp-DataAreaStartLow] ; Add start of data area
263 adc dx,[BYTE bp-DataAreaStartHigh] ; Now we have DX:AX with the logical start sector of OSLOADER.SYS
264 xor bx,bx ; We will load it to [ES:0000], ES loaded before function call
265 ;mov cl,BYTE [BYTE bp+SectsPerCluster]; Sectors per cluster still in CX
271 ; Reads logical sectors into [ES:BX]
272 ; DX:AX has logical sector number to read
273 ; CX has number of sectors to read
276 ; We can't just check if the start sector is
277 ; in the BIOS CHS range. We have to check if
278 ; the start sector + length is in that range.
284 cmp dx,WORD [BYTE bp-BiosCHSDriveSizeHigh] ; Check if they are reading a sector within CHS range
285 ja ReadSectorsLBA ; No - go to the LBA routine
286 jb ReadSectorsCHS ; Yes - go to the old CHS routine
287 cmp ax,WORD [BYTE bp-BiosCHSDriveSizeLow] ; Check if they are reading a sector within CHS range
288 jbe ReadSectorsCHS ; Yes - go to the old CHS routine
293 pusha ; Save logical sector number & sector count
296 push dx ; Put 64-bit logical
297 push ax ; block address on stack
298 push es ; Put transfer segment on stack
299 push bx ; Put transfer offset on stack
300 push byte 1 ; Set transfer count to 1 sector
301 push byte 0x10 ; Set size of packet to 10h
302 mov si,sp ; Setup disk address packet on stack
304 ; We are so totally out of space here that I am forced to
305 ; comment out this very beautifully written piece of code
306 ; It would have been nice to have had this check...
307 ;CheckInt13hExtensions: ; Now make sure this computer supports extended reads
308 ; mov ah,0x41 ; AH = 41h
309 ; mov bx,0x55aa ; BX = 55AAh
310 ; mov dl,[BYTE bp+BootDrive] ; DL = drive (80h-FFh)
311 ; int 13h ; IBM/MS INT 13 Extensions - INSTALLATION CHECK
312 ; jc PrintDiskError ; CF set on error (extensions not supported)
313 ; cmp bx,0xaa55 ; BX = AA55h if installed
315 ; test cl,1 ; CX = API subset support bitmap
316 ; jz PrintDiskError ; Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported
319 ; Good, we're here so the computer supports LBA disk access
320 ; So finish the extended read
321 mov dl,[BYTE bp+BootDrive] ; Drive number
322 mov ah,42h ; Int 13h, AH = 42h - Extended Read
324 jc BadBoot ; If the read failed then abort
326 add sp,byte 0x10 ; Remove disk address packet from stack
328 popa ; Restore sector count & logical sector number
330 inc ax ; Increment Sector to Read
335 add bx,byte 20h ; Increment read buffer for next sector
339 loop ReadSectorsLBALoop ; Read next sector
344 ; Reads logical sectors into [ES:BX]
345 ; DX:AX has logical sector number to read
346 ; CX has number of sectors to read
347 ; CarryFlag set on error
355 div WORD [BYTE bp+SectorsPerTrack]
357 div WORD [BYTE bp+SectorsPerTrack] ; Divide logical by SectorsPerTrack
358 inc dx ; Sectors numbering starts at 1 not 0
360 div WORD [BYTE bp+NumberOfHeads] ; Number of heads
361 mov dh,dl ; Head to DH, drive to DL
362 mov dl,[BYTE bp+BootDrive] ; Drive number
363 mov ch,al ; Cylinder in CX
364 ror ah,2 ; Low 8 bits of cylinder in CH, high 2 bits
365 ; in CL shifted to bits 6 & 7
366 or cl,ah ; Or with sector number
368 int 13h ; DISK - READ SECTORS INTO MEMORY
369 ; AL = number of sectors to read, CH = track, CL = sector
370 ; DH = head, DL = drive, ES:BX -> buffer to fill
371 ; Return: CF set on error, AH = status (see AH=01h), AL = number of sectors read
376 inc ax ;Increment Sector to Read
387 ; Increment read buffer for next sector
388 loop ReadSectorsCHSLoop ; Read next sector
393 msgDiskError db 'Disk error',0dh,0ah,0
394 msgFreeLdr db 'ldr not found',0dh,0ah,0
395 ; Sorry, need the space...
396 ;msgAnyKey db 'Press any key to restart',0dh,0ah,0
397 msgAnyKey db 'Press a key',0dh,0ah,0
398 filename db 'FREELDR SYS'
400 times 509-($-$$) db 0 ; Pad to 509 bytes
406 dw 0aa55h ; BootSector signature