sync with trunk (r46275)
[reactos.git] / 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 0xff
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 cmp BYTE [BYTE bp+BootDrive],BYTE 0xff ; If they have specified a boot drive then use it
93 jne GetDriveParameters
94
95 mov [BYTE bp+BootDrive],dl ; Save the boot drive
96
97
98 GetDriveParameters:
99 mov ah,08h
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
103
104 ; If we get here then the call to the BIOS failed
105 ; so just set CHS equal to the maximum addressable
106 ; size
107 mov cx,0ffffh
108 mov dh,cl
109
110 CalcDriveSize:
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]
125
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
129
130
131 ; Now we must find our way to the first sector of the root directory
132 xor ax,ax
133 xor cx,cx
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
145
146 ; Now calculate the size of the root directory
147 xor dx,dx
148 mov ax,0020h ; Size of dir entry
149 mul si ; Times the number of entries
150 mov bx,[BYTE bp+BytesPerSector]
151 add ax,bx
152 dec ax
153 div bx ; Divided by the size of a sector
154 ; AX now has the number of root directory sectors
155
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
159
160 LoadRootDirSector:
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)
170
171 SearchRootDirSector:
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
186
187 FoundFreeLoader:
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
198
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
204
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.
211 ;jmp 0000:8003h
212 jmp 8003h
213
214
215
216
217 ; Displays an error message
218 ; And reboots
219 ErrBoot:
220 mov si,msgFreeLdr ; FreeLdr not found message
221 call PutChars ; Display it
222
223 Reboot:
224 mov si,msgAnyKey ; Press any key message
225 call PutChars ; Display it
226 xor ax,ax
227 int 16h ; Wait for a keypress
228 int 19h ; Reboot
229
230 PutChars:
231 lodsb
232 or al,al
233 jz short Done
234 mov ah,0eh
235 mov bx,07h
236 int 10h
237 jmp short PutChars
238 Done:
239 retn
240
241 ; Displays a bad boot message
242 ; And reboots
243 BadBoot:
244 mov si,msgDiskError ; Bad boot disk message
245 call PutChars ; Display it
246
247 jmp short Reboot
248
249
250 ; Reads cluster number in AX into [ES:0000]
251 ReadCluster:
252 ; StartSector = ((Cluster - 2) * SectorsPerCluster) + ReservedSectors + HiddenSectors;
253 dec ax ; Adjust start cluster by 2
254 dec ax ; Because the data area starts on cluster 2
255 xor ch,ch
256 mov cl,BYTE [BYTE bp+SectsPerCluster]
257 mul cx ; Times sectors per cluster
258 add ax,[BYTE bp-DataAreaStartLow] ; Add start of data area
259 adc dx,[BYTE bp-DataAreaStartHigh] ; Now we have DX:AX with the logical start sector of OSLOADER.SYS
260 xor bx,bx ; We will load it to [ES:0000], ES loaded before function call
261 ;mov cl,BYTE [BYTE bp+SectsPerCluster]; Sectors per cluster still in CX
262 ;call ReadSectors
263 ;ret
264
265
266
267 ; Reads logical sectors into [ES:BX]
268 ; DX:AX has logical sector number to read
269 ; CX has number of sectors to read
270 ReadSectors:
271
272 ; We can't just check if the start sector is
273 ; in the BIOS CHS range. We have to check if
274 ; the start sector + length is in that range.
275 pusha
276 dec cx
277 add ax,cx
278 adc dx,byte 0
279
280 cmp dx,WORD [BYTE bp-BiosCHSDriveSizeHigh] ; Check if they are reading a sector within CHS range
281 ja ReadSectorsLBA ; No - go to the LBA routine
282 jb ReadSectorsCHS ; Yes - go to the old CHS routine
283 cmp ax,WORD [BYTE bp-BiosCHSDriveSizeLow] ; Check if they are reading a sector within CHS range
284 jbe ReadSectorsCHS ; Yes - go to the old CHS routine
285
286 ReadSectorsLBA:
287 popa
288 ReadSectorsLBALoop:
289 pusha ; Save logical sector number & sector count
290
291 o32 push byte 0
292 push dx ; Put 64-bit logical
293 push ax ; block address on stack
294 push es ; Put transfer segment on stack
295 push bx ; Put transfer offset on stack
296 push byte 1 ; Set transfer count to 1 sector
297 push byte 0x10 ; Set size of packet to 10h
298 mov si,sp ; Setup disk address packet on stack
299
300 ; We are so totally out of space here that I am forced to
301 ; comment out this very beautifully written piece of code
302 ; It would have been nice to have had this check...
303 ;CheckInt13hExtensions: ; Now make sure this computer supports extended reads
304 ; mov ah,0x41 ; AH = 41h
305 ; mov bx,0x55aa ; BX = 55AAh
306 ; mov dl,[BYTE bp+BootDrive] ; DL = drive (80h-FFh)
307 ; int 13h ; IBM/MS INT 13 Extensions - INSTALLATION CHECK
308 ; jc PrintDiskError ; CF set on error (extensions not supported)
309 ; cmp bx,0xaa55 ; BX = AA55h if installed
310 ; jne PrintDiskError
311 ; test cl,1 ; CX = API subset support bitmap
312 ; jz PrintDiskError ; Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported
313
314
315 ; Good, we're here so the computer supports LBA disk access
316 ; So finish the extended read
317 mov dl,[BYTE bp+BootDrive] ; Drive number
318 mov ah,42h ; Int 13h, AH = 42h - Extended Read
319 int 13h ; Call BIOS
320 jc BadBoot ; If the read failed then abort
321
322 add sp,byte 0x10 ; Remove disk address packet from stack
323
324 popa ; Restore sector count & logical sector number
325
326 inc ax ; Increment Sector to Read
327 adc dx,byte 0
328
329 push bx
330 mov bx,es
331 add bx,byte 20h ; Increment read buffer for next sector
332 mov es,bx
333 pop bx
334
335 loop ReadSectorsLBALoop ; Read next sector
336
337 ret
338
339
340 ; Reads logical sectors into [ES:BX]
341 ; DX:AX has logical sector number to read
342 ; CX has number of sectors to read
343 ; CarryFlag set on error
344 ReadSectorsCHS:
345 popa
346 ReadSectorsCHSLoop:
347 pusha
348 xchg ax,cx
349 xchg ax,dx
350 xor dx,dx
351 div WORD [BYTE bp+SectorsPerTrack]
352 xchg ax,cx
353 div WORD [BYTE bp+SectorsPerTrack] ; Divide logical by SectorsPerTrack
354 inc dx ; Sectors numbering starts at 1 not 0
355 xchg cx,dx
356 div WORD [BYTE bp+NumberOfHeads] ; Number of heads
357 mov dh,dl ; Head to DH, drive to DL
358 mov dl,[BYTE bp+BootDrive] ; Drive number
359 mov ch,al ; Cylinder in CX
360 ror ah,2 ; Low 8 bits of cylinder in CH, high 2 bits
361 ; in CL shifted to bits 6 & 7
362 or cl,ah ; Or with sector number
363 mov ax,0201h
364 int 13h ; DISK - READ SECTORS INTO MEMORY
365 ; AL = number of sectors to read, CH = track, CL = sector
366 ; DH = head, DL = drive, ES:BX -> buffer to fill
367 ; Return: CF set on error, AH = status (see AH=01h), AL = number of sectors read
368
369 jc BadBoot
370
371 popa
372 inc ax ;Increment Sector to Read
373 jnz NoCarryCHS
374 inc dx
375
376
377 NoCarryCHS:
378 push bx
379 mov bx,es
380 add bx,byte 20h
381 mov es,bx
382 pop bx
383 ; Increment read buffer for next sector
384 loop ReadSectorsCHSLoop ; Read next sector
385
386 ret
387
388
389 msgDiskError db 'Disk error',0dh,0ah,0
390 msgFreeLdr db 'freeldr.sys not found',0dh,0ah,0
391 ; Sorry, need the space...
392 ;msgAnyKey db 'Press any key to restart',0dh,0ah,0
393 msgAnyKey db 'Press any key',0dh,0ah,0
394 filename db 'FREELDR SYS'
395
396 times 509-($-$$) db 0 ; Pad to 509 bytes
397
398 BootPartition:
399 db 0
400
401 BootSignature:
402 dw 0aa55h ; BootSector signature