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 mov [BYTE bp+BootDrive],dl ; Save the boot drive
97 mov dl,[BYTE bp+BootDrive] ; Get boot drive in dl
98 int 13h ; Request drive parameters from the bios
99 jnc CalcDriveSize ; If the call succeeded then calculate the drive size
101 ; If we get here then the call to the BIOS failed
102 ; so just set CHS equal to the maximum addressable
108 ; Now that we have the drive geometry
109 ; lets calculate the drive size
110 mov bl,ch ; Put the low 8-bits of the cylinder count into BL
111 mov bh,cl ; Put the high 2-bits in BH
112 shr bh,6 ; Shift them into position, now BX contains the cylinder count
113 and cl,3fh ; Mask off cylinder bits from sector count
114 ; CL now contains sectors per track and DH contains head count
115 movzx eax,dh ; Move the heads into EAX
116 movzx ebx,bx ; Move the cylinders into EBX
117 movzx ecx,cl ; Move the sectors per track into ECX
118 inc eax ; Make it one based because the bios returns it zero based
119 inc ebx ; Make the cylinder count one based also
120 mul ecx ; Multiply heads with the sectors per track, result in edx:eax
121 mul ebx ; Multiply the cylinders with (heads * sectors) [stored in edx:eax already]
123 ; We now have the total number of sectors as reported
124 ; by the bios in eax, so store it in our variable
125 mov [BYTE bp-BiosCHSDriveSize],eax
128 ; Now we must find our way to the first sector of the root directory
131 mov al,[BYTE bp+NumberOfFats] ; Number of fats
132 mul WORD [BYTE bp+SectorsPerFat] ; Times sectors per fat
133 add ax,WORD [BYTE bp+HiddenSectors]
134 adc dx,WORD [BYTE bp+HiddenSectors+2] ; Add the number of hidden sectors
135 add ax,WORD [BYTE bp+ReservedSectors] ; Add the number of reserved sectors
136 adc dx,cx ; Add carry bit
137 mov WORD [BYTE bp-DataAreaStartLow],ax ; Save the starting sector of the root directory
138 mov WORD [BYTE bp-DataAreaStartHigh],dx ; Save it in the first 4 bytes before the boot sector
139 mov si,WORD [BYTE bp+MaxRootEntries] ; Get number of root dir entries in SI
140 pusha ; Save 32-bit logical start sector of root dir
141 ; DX:AX now has the number of the starting sector of the root directory
143 ; Now calculate the size of the root directory
145 mov ax,0020h ; Size of dir entry
146 mul si ; Times the number of entries
147 mov bx,[BYTE bp+BytesPerSector]
150 div bx ; Divided by the size of a sector
151 ; AX now has the number of root directory sectors
153 add [BYTE bp-DataAreaStartLow],ax ; Add the number of sectors of the root directory to our other value
154 adc [BYTE bp-DataAreaStartHigh],cx ; Now the first 4 bytes before the boot sector contain the starting sector of the data area
155 popa ; Restore root dir logical sector start to DX:AX
158 mov bx,7e0h ; We will load the root directory sector
159 mov es,bx ; Right after the boot sector in memory
160 xor bx,bx ; We will load it to [0000:7e00h]
161 xor cx,cx ; Zero out CX
162 inc cx ; Now increment it to 1, we are reading one sector
163 xor di,di ; Zero out di
164 push es ; Save ES because it will get incremented by 20h
165 call ReadSectors ; Read the first sector of the root directory
166 pop es ; Restore ES (ES:DI = 07E0:0000)
169 cmp [es:di],ch ; If the first byte of the directory entry is zero then we have
170 jz ErrBoot ; reached the end of the directory and FREELDR.SYS is not here so reboot
171 pusha ; Save all registers
172 mov cl,0xb ; Put 11 in cl (length of filename in directory entry)
173 mov si,filename ; Put offset of filename string in DS:SI
174 repe cmpsb ; Compare this directory entry against 'FREELDR SYS'
175 popa ; Restore all the registers
176 jz FoundFreeLoader ; If we found it then jump
177 dec si ; SI holds MaxRootEntries, subtract one
178 jz ErrBoot ; If we are out of root dir entries then reboot
179 add di,BYTE +0x20 ; Increment DI by the size of a directory entry
180 cmp di,0200h ; Compare DI to 512 (DI has offset to next dir entry, make sure we haven't gone over one sector)
181 jc SearchRootDirSector ; If DI is less than 512 loop again
182 jmp short LoadRootDirSector ; Didn't find FREELDR.SYS in this directory sector, try again
185 ; We found freeldr.sys on the disk
186 ; so we need to load the first 512
187 ; bytes of it to 0000:8000
188 ; ES:DI has dir entry (ES:DI == 07E0:XXXX)
189 mov ax,WORD [es:di+1ah] ; Get start cluster
190 push ax ; Save start cluster
191 push WORD 800h ; Put 800h on the stack and load it
192 pop es ; Into ES so that we load the cluster at 0000:8000
193 call ReadCluster ; Read the cluster
194 pop ax ; Restore start cluster of FreeLoader
196 ; Save the addresses of needed functions so
197 ; the helper code will know where to call them.
198 mov WORD [BYTE bp-ReadSectorsOffset],ReadSectors ; Save the address of ReadSectors
199 mov WORD [BYTE bp-ReadClusterOffset],ReadCluster ; Save the address of ReadCluster
200 mov WORD [BYTE bp-PutCharsOffset],PutChars ; Save the address of PutChars
202 ; Now AX has start cluster of FreeLoader and we
203 ; have loaded the helper code in the first 512 bytes
204 ; of FreeLoader to 0000:8000. Now transfer control
205 ; to the helper code. Skip the first three bytes
206 ; because they contain a jump instruction to skip
207 ; over the helper code in the FreeLoader image.
214 ; Displays an error message
217 mov si,msgFreeLdr ; FreeLdr not found message
218 call PutChars ; Display it
221 mov si,msgAnyKey ; Press any key message
222 call PutChars ; Display it
224 int 16h ; Wait for a keypress
238 ; Displays a bad boot message
241 mov si,msgDiskError ; Bad boot disk message
242 call PutChars ; Display it
247 ; Reads cluster number in AX into [ES:0000]
249 ; StartSector = ((Cluster - 2) * SectorsPerCluster) + ReservedSectors + HiddenSectors;
250 dec ax ; Adjust start cluster by 2
251 dec ax ; Because the data area starts on cluster 2
253 mov cl,BYTE [BYTE bp+SectsPerCluster]
254 mul cx ; Times sectors per cluster
255 add ax,[BYTE bp-DataAreaStartLow] ; Add start of data area
256 adc dx,[BYTE bp-DataAreaStartHigh] ; Now we have DX:AX with the logical start sector of OSLOADER.SYS
257 xor bx,bx ; We will load it to [ES:0000], ES loaded before function call
258 ;mov cl,BYTE [BYTE bp+SectsPerCluster]; Sectors per cluster still in CX
264 ; Reads logical sectors into [ES:BX]
265 ; DX:AX has logical sector number to read
266 ; CX has number of sectors to read
269 ; We can't just check if the start sector is
270 ; in the BIOS CHS range. We have to check if
271 ; the start sector + length is in that range.
277 cmp dx,WORD [BYTE bp-BiosCHSDriveSizeHigh] ; Check if they are reading a sector within CHS range
278 ja ReadSectorsLBA ; No - go to the LBA routine
279 jb ReadSectorsCHS ; Yes - go to the old CHS routine
280 cmp ax,WORD [BYTE bp-BiosCHSDriveSizeLow] ; Check if they are reading a sector within CHS range
281 jbe ReadSectorsCHS ; Yes - go to the old CHS routine
286 pusha ; Save logical sector number & sector count
289 push dx ; Put 64-bit logical
290 push ax ; block address on stack
291 push es ; Put transfer segment on stack
292 push bx ; Put transfer offset on stack
293 push byte 1 ; Set transfer count to 1 sector
294 push byte 0x10 ; Set size of packet to 10h
295 mov si,sp ; Setup disk address packet on stack
297 ; We are so totally out of space here that I am forced to
298 ; comment out this very beautifully written piece of code
299 ; It would have been nice to have had this check...
300 ;CheckInt13hExtensions: ; Now make sure this computer supports extended reads
301 ; mov ah,0x41 ; AH = 41h
302 ; mov bx,0x55aa ; BX = 55AAh
303 ; mov dl,[BYTE bp+BootDrive] ; DL = drive (80h-FFh)
304 ; int 13h ; IBM/MS INT 13 Extensions - INSTALLATION CHECK
305 ; jc PrintDiskError ; CF set on error (extensions not supported)
306 ; cmp bx,0xaa55 ; BX = AA55h if installed
308 ; test cl,1 ; CX = API subset support bitmap
309 ; jz PrintDiskError ; Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported
312 ; Good, we're here so the computer supports LBA disk access
313 ; So finish the extended read
314 mov dl,[BYTE bp+BootDrive] ; Drive number
315 mov ah,42h ; Int 13h, AH = 42h - Extended Read
317 jc BadBoot ; If the read failed then abort
319 add sp,byte 0x10 ; Remove disk address packet from stack
321 popa ; Restore sector count & logical sector number
323 inc ax ; Increment Sector to Read
328 add bx,byte 20h ; Increment read buffer for next sector
332 loop ReadSectorsLBALoop ; Read next sector
337 ; Reads logical sectors into [ES:BX]
338 ; DX:AX has logical sector number to read
339 ; CX has number of sectors to read
340 ; CarryFlag set on error
348 div WORD [BYTE bp+SectorsPerTrack]
350 div WORD [BYTE bp+SectorsPerTrack] ; Divide logical by SectorsPerTrack
351 inc dx ; Sectors numbering starts at 1 not 0
353 div WORD [BYTE bp+NumberOfHeads] ; Number of heads
354 mov dh,dl ; Head to DH, drive to DL
355 mov dl,[BYTE bp+BootDrive] ; Drive number
356 mov ch,al ; Cylinder in CX
357 ror ah,2 ; Low 8 bits of cylinder in CH, high 2 bits
358 ; in CL shifted to bits 6 & 7
359 or cl,ah ; Or with sector number
361 int 13h ; DISK - READ SECTORS INTO MEMORY
362 ; AL = number of sectors to read, CH = track, CL = sector
363 ; DH = head, DL = drive, ES:BX -> buffer to fill
364 ; Return: CF set on error, AH = status (see AH=01h), AL = number of sectors read
369 inc ax ;Increment Sector to Read
380 ; Increment read buffer for next sector
381 loop ReadSectorsCHSLoop ; Read next sector
386 msgDiskError db 'Disk error',0dh,0ah,0
387 msgFreeLdr db 'freeldr.sys not found',0dh,0ah,0
388 ; Sorry, need the space...
389 ;msgAnyKey db 'Press any key to restart',0dh,0ah,0
390 msgAnyKey db 'Press any key',0dh,0ah,0
391 filename db 'FREELDR SYS'
393 times 509-($-$$) db 0 ; Pad to 509 bytes
399 dw 0aa55h ; BootSector signature