Fixed freeldr fat16 boot sector to use the bios provided boot device number instead...
[reactos.git] / reactos / boot / freeldr / bootsect / fat.asm
1 ; FAT.ASM
2 ; FAT12/16 Boot Sector
3 ; Copyright (c) 1998, 2001, 2002 Brian Palmer
4
5
6
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
10 ; memory.
11 ;
12 ; The stack is set to 0000:7BF2 so that the first
13 ; WORD pushed will be placed at 0000:7BF0
14 ;
15 ; The DWORD at 0000:7BFC or BP-04h is the logical
16 ; sector number of the start of the data area.
17 ;
18 ; The DWORD at 0000:7BF8 or BP-08h is the total
19 ; sector count of the boot drive as reported by
20 ; the computers bios.
21 ;
22 ; The WORD at 0000:7BF6 or BP-0ah is the offset
23 ; of the ReadSectors function in the boot sector.
24 ;
25 ; The WORD at 0000:7BF4 or BP-0ch is the offset
26 ; of the ReadCluster function in the boot sector.
27 ;
28 ; The WORD at 0000:7BF2 or BP-0eh is the offset
29 ; of the PutChars function in the boot sector.
30 ;
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.
36 ;
37 ; We load the entire FAT table into memory at
38 ; 7000:0000. This improves the speed of floppy disk
39 ; boots dramatically.
40
41
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
51
52
53 org 7c00h
54
55 segment .text
56
57 bits 16
58
59 start:
60 jmp short main
61 nop
62
63 OEMName db 'FrLdr1.0'
64 BytesPerSector dw 512
65 SectsPerCluster db 1
66 ReservedSectors dw 1
67 NumberOfFats db 2
68 MaxRootEntries dw 224
69 TotalSectors dw 2880
70 MediaDescriptor db 0f0h
71 SectorsPerFat dw 9
72 SectorsPerTrack dw 18
73 NumberOfHeads dw 2
74 HiddenSectors dd 0
75 TotalSectorsBig dd 0
76 BootDrive db 0
77 Reserved db 0
78 ExtendSig db 29h
79 SerialNumber dd 00000000h
80 VolumeLabel db 'NO NAME '
81 FileSystem db 'FAT12 '
82
83 main:
84 xor ax,ax
85 mov ss,ax
86 mov bp,7c00h
87 mov sp,BootSectorStackTop ; Setup a stack
88 mov ds,ax ; Make DS correct
89 mov es,ax ; Make ES correct
90
91
92 mov [BYTE bp+BootDrive],dl ; Save the boot drive
93
94
95 GetDriveParameters:
96 mov ah,08h
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
100
101 ; If we get here then the call to the BIOS failed
102 ; so just set CHS equal to the maximum addressable
103 ; size
104 mov cx,0ffffh
105 mov dh,cl
106
107 CalcDriveSize:
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]
122
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
126
127
128 ; Now we must find our way to the first sector of the root directory
129 xor ax,ax
130 xor cx,cx
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
142
143 ; Now calculate the size of the root directory
144 xor dx,dx
145 mov ax,0020h ; Size of dir entry
146 mul si ; Times the number of entries
147 mov bx,[BYTE bp+BytesPerSector]
148 add ax,bx
149 dec ax
150 div bx ; Divided by the size of a sector
151 ; AX now has the number of root directory sectors
152
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
156
157 LoadRootDirSector:
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)
167
168 SearchRootDirSector:
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
183
184 FoundFreeLoader:
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
195
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
201
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.
208 ;jmp 0000:8003h
209 jmp 8003h
210
211
212
213
214 ; Displays an error message
215 ; And reboots
216 ErrBoot:
217 mov si,msgFreeLdr ; FreeLdr not found message
218 call PutChars ; Display it
219
220 Reboot:
221 mov si,msgAnyKey ; Press any key message
222 call PutChars ; Display it
223 xor ax,ax
224 int 16h ; Wait for a keypress
225 int 19h ; Reboot
226
227 PutChars:
228 lodsb
229 or al,al
230 jz short Done
231 mov ah,0eh
232 mov bx,07h
233 int 10h
234 jmp short PutChars
235 Done:
236 retn
237
238 ; Displays a bad boot message
239 ; And reboots
240 BadBoot:
241 mov si,msgDiskError ; Bad boot disk message
242 call PutChars ; Display it
243
244 jmp short Reboot
245
246
247 ; Reads cluster number in AX into [ES:0000]
248 ReadCluster:
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
252 xor ch,ch
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
259 ;call ReadSectors
260 ;ret
261
262
263
264 ; Reads logical sectors into [ES:BX]
265 ; DX:AX has logical sector number to read
266 ; CX has number of sectors to read
267 ReadSectors:
268
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.
272 pusha
273 dec cx
274 add ax,cx
275 adc dx,byte 0
276
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
282
283 ReadSectorsLBA:
284 popa
285 ReadSectorsLBALoop:
286 pusha ; Save logical sector number & sector count
287
288 o32 push byte 0
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
296
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
307 ; jne PrintDiskError
308 ; test cl,1 ; CX = API subset support bitmap
309 ; jz PrintDiskError ; Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported
310
311
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
316 int 13h ; Call BIOS
317 jc BadBoot ; If the read failed then abort
318
319 add sp,byte 0x10 ; Remove disk address packet from stack
320
321 popa ; Restore sector count & logical sector number
322
323 inc ax ; Increment Sector to Read
324 adc dx,byte 0
325
326 push bx
327 mov bx,es
328 add bx,byte 20h ; Increment read buffer for next sector
329 mov es,bx
330 pop bx
331
332 loop ReadSectorsLBALoop ; Read next sector
333
334 ret
335
336
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
341 ReadSectorsCHS:
342 popa
343 ReadSectorsCHSLoop:
344 pusha
345 xchg ax,cx
346 xchg ax,dx
347 xor dx,dx
348 div WORD [BYTE bp+SectorsPerTrack]
349 xchg ax,cx
350 div WORD [BYTE bp+SectorsPerTrack] ; Divide logical by SectorsPerTrack
351 inc dx ; Sectors numbering starts at 1 not 0
352 xchg cx,dx
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
360 mov ax,0201h
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
365
366 jc BadBoot
367
368 popa
369 inc ax ;Increment Sector to Read
370 jnz NoCarryCHS
371 inc dx
372
373
374 NoCarryCHS:
375 push bx
376 mov bx,es
377 add bx,byte 20h
378 mov es,bx
379 pop bx
380 ; Increment read buffer for next sector
381 loop ReadSectorsCHSLoop ; Read next sector
382
383 ret
384
385
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'
392
393 times 509-($-$$) db 0 ; Pad to 509 bytes
394
395 BootPartition:
396 db 0
397
398 BootSignature:
399 dw 0aa55h ; BootSector signature