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:F800
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:F800
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 0F80h ; FREELDR_BASE / 16 ; Put load segment on the stack and load it
195 pop es ; Into ES so that we load the cluster at 0000:F800
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:F800. 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.
211 ;ljmp16 0, FREELDR_BASE + 3
219 ; Displays an error message
222 mov si,msgFreeLdr ; FreeLdr not found message
223 call PutChars ; Display it
226 ; mov si,msgAnyKey ; Press any key message
227 ; call PutChars ; Display it
229 int 16h ; Wait for a keypress
243 ; Displays a bad boot message
246 mov si,msgDiskError ; Bad boot disk message
247 call PutChars ; Display it
252 ; Reads cluster number in AX into [ES:0000]
254 ; StartSector = ((Cluster - 2) * SectorsPerCluster) + ReservedSectors + HiddenSectors;
255 dec ax ; Adjust start cluster by 2
256 dec ax ; Because the data area starts on cluster 2
258 mov cl,BYTE [BYTE bp+SectsPerCluster]
259 mul cx ; Times sectors per cluster
260 add ax,[BYTE bp-DataAreaStartLow] ; Add start of data area
261 adc dx,[BYTE bp-DataAreaStartHigh] ; Now we have DX:AX with the logical start sector of FREELDR.SYS
262 xor bx,bx ; We will load it to [ES:0000], ES loaded before function call
263 ;mov cl,BYTE [BYTE bp+SectsPerCluster]; Sectors per cluster still in CX
269 ; Reads logical sectors into [ES:BX]
270 ; DX:AX has logical sector number to read
271 ; CX has number of sectors to read
274 ; We can't just check if the start sector is
275 ; in the BIOS CHS range. We have to check if
276 ; the start sector + length is in that range.
282 cmp dx,WORD [BYTE bp-BiosCHSDriveSizeHigh] ; Check if they are reading a sector within CHS range
283 ja ReadSectorsLBA ; No - go to the LBA routine
284 jb ReadSectorsCHS ; Yes - go to the old CHS routine
285 cmp ax,WORD [BYTE bp-BiosCHSDriveSizeLow] ; Check if they are reading a sector within CHS range
286 jbe ReadSectorsCHS ; Yes - go to the old CHS routine
291 pusha ; Save logical sector number & sector count
294 push dx ; Put 64-bit logical
295 push ax ; block address on stack
296 push es ; Put transfer segment on stack
297 push bx ; Put transfer offset on stack
298 push byte 1 ; Set transfer count to 1 sector
299 push byte 0x10 ; Set size of packet to 10h
300 mov si,sp ; Setup disk address packet on stack
302 ; We are so totally out of space here that I am forced to
303 ; comment out this very beautifully written piece of code
304 ; It would have been nice to have had this check...
305 ;CheckInt13hExtensions: ; Now make sure this computer supports extended reads
306 ; mov ah,0x41 ; AH = 41h
307 ; mov bx,0x55aa ; BX = 55AAh
308 ; mov dl,[BYTE bp+BootDrive] ; DL = drive (80h-FFh)
309 ; int 13h ; IBM/MS INT 13 Extensions - INSTALLATION CHECK
310 ; jc PrintDiskError ; CF set on error (extensions not supported)
311 ; cmp bx,0xaa55 ; BX = AA55h if installed
313 ; test cl,1 ; CX = API subset support bitmap
314 ; jz PrintDiskError ; Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported
317 ; Good, we're here so the computer supports LBA disk access
318 ; So finish the extended read
319 mov dl,[BYTE bp+BootDrive] ; Drive number
320 mov ah,42h ; Int 13h, AH = 42h - Extended Read
322 jc BadBoot ; If the read failed then abort
324 add sp,byte 0x10 ; Remove disk address packet from stack
326 popa ; Restore sector count & logical sector number
328 inc ax ; Increment Sector to Read
333 add bx,byte 20h ; Increment read buffer for next sector
337 loop ReadSectorsLBALoop ; Read next sector
342 ; Reads logical sectors into [ES:BX]
343 ; DX:AX has logical sector number to read
344 ; CX has number of sectors to read
345 ; CarryFlag set on error
353 div WORD [BYTE bp+SectorsPerTrack]
355 div WORD [BYTE bp+SectorsPerTrack] ; Divide logical by SectorsPerTrack
356 inc dx ; Sectors numbering starts at 1 not 0
358 div WORD [BYTE bp+NumberOfHeads] ; Number of heads
359 mov dh,dl ; Head to DH, drive to DL
360 mov dl,[BYTE bp+BootDrive] ; Drive number
361 mov ch,al ; Cylinder in CX
362 ror ah,2 ; Low 8 bits of cylinder in CH, high 2 bits
363 ; in CL shifted to bits 6 & 7
364 or cl,ah ; Or with sector number
366 int 13h ; DISK - READ SECTORS INTO MEMORY
367 ; AL = number of sectors to read, CH = track, CL = sector
368 ; DH = head, DL = drive, ES:BX -> buffer to fill
369 ; Return: CF set on error, AH = status (see AH=01h), AL = number of sectors read
374 inc ax ;Increment Sector to Read
385 ; Increment read buffer for next sector
386 loop ReadSectorsCHSLoop ; Read next sector
391 msgDiskError db 'Disk error',0dh,0ah,0
392 msgFreeLdr db 'Ldr not found',0dh,0ah,0
393 ; Sorry, need the space...
394 ;msgAnyKey db 'Press any key to restart',0dh,0ah,0
395 ;msgAnyKey db 'Press a key',0dh,0ah,0
396 filename db 'FREELDR SYS'
398 times 509-($-$$) db 0 ; Pad to 509 bytes
404 dw 0aa55h ; BootSector signature