Sync with trunk (r48144)
[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 push 0 ; push segment (0x0000)
213 mov eax, [0x8000 + 0xA8] ; load the RVA of the EntryPoint into eax
214 add eax, 0x8003 ; RVA -> VA and skip 3 bytes (jump to fathelper code)
215 push ax ; push offset
216 retf ; Transfer control to FreeLoader
217
218
219
220
221 ; Displays an error message
222 ; And reboots
223 ErrBoot:
224 mov si,msgFreeLdr ; FreeLdr not found message
225 call PutChars ; Display it
226
227 Reboot:
228 mov si,msgAnyKey ; Press any key message
229 call PutChars ; Display it
230 xor ax,ax
231 int 16h ; Wait for a keypress
232 int 19h ; Reboot
233
234 PutChars:
235 lodsb
236 or al,al
237 jz short Done
238 mov ah,0eh
239 mov bx,07h
240 int 10h
241 jmp short PutChars
242 Done:
243 retn
244
245 ; Displays a bad boot message
246 ; And reboots
247 BadBoot:
248 mov si,msgDiskError ; Bad boot disk message
249 call PutChars ; Display it
250
251 jmp short Reboot
252
253
254 ; Reads cluster number in AX into [ES:0000]
255 ReadCluster:
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
259 xor ch,ch
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
266 ;call ReadSectors
267 ;ret
268
269
270
271 ; Reads logical sectors into [ES:BX]
272 ; DX:AX has logical sector number to read
273 ; CX has number of sectors to read
274 ReadSectors:
275
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.
279 pusha
280 dec cx
281 add ax,cx
282 adc dx,byte 0
283
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
289
290 ReadSectorsLBA:
291 popa
292 ReadSectorsLBALoop:
293 pusha ; Save logical sector number & sector count
294
295 o32 push byte 0
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
303
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
314 ; jne PrintDiskError
315 ; test cl,1 ; CX = API subset support bitmap
316 ; jz PrintDiskError ; Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported
317
318
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
323 int 13h ; Call BIOS
324 jc BadBoot ; If the read failed then abort
325
326 add sp,byte 0x10 ; Remove disk address packet from stack
327
328 popa ; Restore sector count & logical sector number
329
330 inc ax ; Increment Sector to Read
331 adc dx,byte 0
332
333 push bx
334 mov bx,es
335 add bx,byte 20h ; Increment read buffer for next sector
336 mov es,bx
337 pop bx
338
339 loop ReadSectorsLBALoop ; Read next sector
340
341 ret
342
343
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
348 ReadSectorsCHS:
349 popa
350 ReadSectorsCHSLoop:
351 pusha
352 xchg ax,cx
353 xchg ax,dx
354 xor dx,dx
355 div WORD [BYTE bp+SectorsPerTrack]
356 xchg ax,cx
357 div WORD [BYTE bp+SectorsPerTrack] ; Divide logical by SectorsPerTrack
358 inc dx ; Sectors numbering starts at 1 not 0
359 xchg cx,dx
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
367 mov ax,0201h
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
372
373 jc BadBoot
374
375 popa
376 inc ax ;Increment Sector to Read
377 jnz NoCarryCHS
378 inc dx
379
380
381 NoCarryCHS:
382 push bx
383 mov bx,es
384 add bx,byte 20h
385 mov es,bx
386 pop bx
387 ; Increment read buffer for next sector
388 loop ReadSectorsCHSLoop ; Read next sector
389
390 ret
391
392
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'
399
400 times 509-($-$$) db 0 ; Pad to 509 bytes
401
402 BootPartition:
403 db 0
404
405 BootSignature:
406 dw 0aa55h ; BootSector signature