Removed code to save screen contents before entering freeldr
[reactos.git] / freeldr / bootsect / fat32.asm
1 ; FAT32.ASM
2 ; FAT32 Boot Sector
3 ; Copyright (c) 1998, 2000, 2001 Brian Palmer
4
5 ;org 7c00h
6 org 0
7
8 segment .text
9
10 bits 16
11
12 start:
13 jmp short main
14 nop
15
16 OEMName db 'FrLdr1.0'
17 BytesPerSector dw 512
18 SectsPerCluster db 0
19 ReservedSectors dw 32
20 NumberOfFats db 2
21 MaxRootEntries dw 0 ; Always zero for FAT32 volumes
22 TotalSectors dw 0 ; Always zero for FAT32 volumes
23 MediaDescriptor db 0f8h
24 SectorsPerFat dw 0 ; Always zero for FAT32 volumes
25 SectorsPerTrack dw 0
26 NumberOfHeads dw 0
27 HiddenSectors dd 0
28 TotalSectorsBig dd 0
29 ; FAT32 Inserted Info
30 SectorsPerFatBig dd 0
31 ExtendedFlags dw 0
32 FSVersion dw 0
33 RootDirStartCluster dd 0
34 FSInfoSector dw 0
35 BackupBootSector dw 6
36 Reserved1 times 12 db 0
37 ; End FAT32 Inserted Info
38 BootDrive db 0
39 Reserved db 0
40 ExtendSig db 29h
41 SerialNumber dd 00000000h
42 VolumeLabel db 'NO NAME '
43 FileSystem db 'FAT32 '
44
45 main:
46 cli
47 cld
48
49 ; Lets copy ourselves from 0000:7c00 to 9000:0000
50 ; and transfer control to the new code
51 xor ax,ax
52 mov ds,ax
53 mov si,7c00h
54 mov ax,9000h
55 mov es,ax
56 xor di,di
57 mov cx,512
58 rep movsb
59 jmp 0x9000:RealMain
60
61 ; Now we are executing at 9000:xxxx
62 ; We are now free to load freeldr.sys at 0000:7e00
63 RealMain:
64 xor ax,ax
65 mov bp,ax
66 mov sp,ax ; Setup a stack
67 mov ax,cs ; Setup segment registers
68 mov ds,ax ; Make DS correct
69 mov es,ax ; Make ES correct
70 mov ss,ax ; Make SS correct
71 sti ; Enable ints now
72
73
74
75
76 CheckSectorsPerFat:
77 cmp WORD [BYTE bp+SectorsPerFat],byte 0x00 ; Check the old 16-bit value of SectorsPerFat
78 jnz CheckFailed ; If it is non-zero then exit with an error
79 CheckTotalSectors: ; Check the old 16-bit value of TotalSectors & MaxRootEntries
80 cmp DWORD [BYTE bp+MaxRootEntries],byte 0x00; by comparing the DWORD at offset MaxRootEntries to zero
81 jnz CheckFailed ; If it is non-zero then exit with an error
82 CheckFileSystemVersion:
83 cmp WORD [BYTE bp+FSVersion],byte 0x00 ; Check the file system version word
84 jna GetDriveParameters ; It is zero, so continue
85 CheckFailed:
86 jmp PrintFileSystemError ; If it is not zero then exit with an error
87
88
89 GetDriveParameters:
90 mov ax,0800h
91 mov dl,[BYTE bp+BootDrive] ; Get boot drive in dl
92 int 13h ; Request drive parameters from the bios
93 jnc CalcDriveSize ; If the call succeeded then calculate the drive size
94
95 ; If we get here then the call to the BIOS failed
96 ; so just set CHS equal to the maximum addressable
97 ; size
98 mov cx,0ffffh
99 mov dh,cl
100
101 CalcDriveSize:
102 ; Now that we have the drive geometry
103 ; lets calculate the drive size
104 mov bl,ch ; Put the low 8-bits of the cylinder count into BL
105 mov bh,cl ; Put the high 2-bits in BH
106 shr bh,6 ; Shift them into position, now BX contains the cylinder count
107 and cl,3fh ; Mask off cylinder bits from sector count
108 ; CL now contains sectors per track and DH contains head count
109 movzx eax,dh ; Move the heads into EAX
110 movzx ebx,bx ; Move the cylinders into EBX
111 movzx ecx,cl ; Move the sectors per track into ECX
112 inc eax ; Make it one based because the bios returns it zero based
113 inc ebx ; Make the cylinder count one based also
114 mul ecx ; Multiply heads with the sectors per track, result in edx:eax
115 mul ebx ; Multiply the cylinders with (heads * sectors) [stored in edx:eax already]
116
117 ; We now have the total number of sectors as reported
118 ; by the bios in eax, so store it in our variable
119 mov [BiosCHSDriveSize],eax
120
121
122 LoadExtraBootCode:
123 ; First we have to load our extra boot code at
124 ; sector 14 into memory at [9000:0200h]
125 mov eax,0eh
126 add eax,DWORD [BYTE bp+HiddenSectors] ; Add the number of hidden sectors
127 mov cx,1
128 mov bx,9000h
129 mov es,bx ; Read sector to [9000:0200h]
130 mov bx,0200h
131 call ReadSectors
132 jmp StartSearch
133
134
135
136 ; Reads logical sectors into [ES:BX]
137 ; EAX has logical sector number to read
138 ; CX has number of sectors to read
139 ReadSectors:
140 cmp eax,DWORD [BiosCHSDriveSize] ; Check if they are reading a sector within CHS range
141 jbe ReadSectorsCHS ; Yes - go to the old CHS routine
142
143 ReadSectorsLBA:
144 pushad ; Save logical sector number & sector count
145
146 o32 push byte 0
147 push eax ; Put 64-bit logical block address on stack
148 push es ; Put transfer segment on stack
149 push bx ; Put transfer offset on stack
150 push byte 1 ; Set transfer count to 1 sector
151 push byte 0x10 ; Set size of packet to 10h
152 mov si,sp ; Setup disk address packet on stack
153
154 CheckInt13hExtensions: ; Now make sure this computer supports extended reads
155 mov ah,0x41 ; AH = 41h
156 mov bx,0x55aa ; BX = 55AAh
157 mov dl,[BYTE bp+BootDrive] ; DL = drive (80h-FFh)
158 int 13h ; IBM/MS INT 13 Extensions - INSTALLATION CHECK
159 jc PrintDiskError ; CF set on error (extensions not supported)
160 cmp bx,0xaa55 ; BX = AA55h if installed
161 jne PrintDiskError
162 test cl,1 ; CX = API subset support bitmap
163 jz PrintDiskError ; Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported
164
165
166 ; Good, we're here so the computer supports LBA disk access
167 ; So finish the extended read
168 mov dl,[BYTE bp+BootDrive] ; Drive number
169 mov ah,42h ; Int 13h, AH = 42h - Extended Read
170 int 13h ; Call BIOS
171 jc PrintDiskError ; If the read failed then abort
172
173 add sp,0x10 ; Remove disk address packet from stack
174
175 popad ; Restore sector count & logical sector number
176
177 inc eax ; Increment sector to read
178 mov dx,es
179 add dx,byte 20h ; Increment read buffer for next sector
180 mov es,dx
181
182 loop ReadSectorsLBA ; Read next sector
183
184 ret
185
186
187 ; Reads logical sectors into [ES:BX]
188 ; EAX has logical sector number to read
189 ; CX has number of sectors to read
190 ReadSectorsCHS:
191 pushad
192 xor edx,edx
193 movzx ecx,WORD [BYTE bp+SectorsPerTrack]
194 div ecx ; Divide logical by SectorsPerTrack
195 inc dl ; Sectors numbering starts at 1 not 0
196 mov cl,dl ; Sector in CL
197 mov edx,eax
198 shr edx,16
199 div WORD [BYTE bp+NumberOfHeads] ; Divide logical by number of heads
200 mov dh,dl ; Head in DH
201 mov dl,[BYTE bp+BootDrive] ; Drive number in DL
202 mov ch,al ; Cylinder in CX
203 ror ah,1 ; Low 8 bits of cylinder in CH, high 2 bits
204 ror ah,1 ; in CL shifted to bits 6 & 7
205 or cl,ah ; Or with sector number
206 mov ax,0201h
207 int 13h ; DISK - READ SECTORS INTO MEMORY
208 ; AL = number of sectors to read, CH = track, CL = sector
209 ; DH = head, DL = drive, ES:BX -> buffer to fill
210 ; Return: CF set on error, AH = status (see AH=01h), AL = number of sectors read
211
212 jc PrintDiskError ; If the read failed then abort
213
214 popad
215
216 inc eax ; Increment Sector to Read
217
218 mov dx,es
219 add dx,byte 20h ; Increment read buffer for next sector
220 mov es,dx
221
222 loop ReadSectorsCHS ; Read next sector
223
224 ret
225
226
227
228
229 ; Displays a disk error message
230 ; And reboots
231 PrintDiskError:
232 mov si,msgDiskError ; Bad boot disk message
233 call PutChars ; Display it
234 mov si,msgAnyKey ; Press any key message
235 call PutChars ; Display it
236
237 jmp Reboot
238
239 ; Displays a file system error message
240 ; And reboots
241 PrintFileSystemError:
242 mov si,msgFileSystemError ; FreeLdr not found message
243 call PutChars ; Display it
244 mov si,msgAnyKey ; Press any key message
245 call PutChars ; Display it
246
247 Reboot:
248 xor ax,ax
249 int 16h ; Wait for a keypress
250 int 19h ; Reboot
251
252 PutChars:
253 lodsb
254 or al,al
255 jz short Done
256 mov ah,0eh
257 mov bx,07h
258 int 10h
259 jmp short PutChars
260 Done:
261 retn
262
263
264
265 BiosCHSDriveSize dd 0
266
267 msgDiskError db 'Disk error',0dh,0ah,0
268 msgFileSystemError db 'File system error',0dh,0ah,0
269 msgAnyKey db 'Press any key to restart',0dh,0ah,0
270
271 times 510-($-$$) db 0 ; Pad to 510 bytes
272 dw 0aa55h ; BootSector signature
273
274
275 ; End of bootsector
276 ;
277 ; Now starts the extra boot code that we will store
278 ; at sector 14 on a FAT32 volume
279 ;
280 ; To remain multi-boot compatible with other operating
281 ; systems we must not overwrite anything other than
282 ; the bootsector which means we will have to use
283 ; a different sector like 14 to store our extra boot code
284
285
286
287 StartSearch:
288 ; Now we must get the first cluster of the root directory
289 mov eax,DWORD [BYTE bp+RootDirStartCluster]
290 cmp eax,0ffffff8h ; Check to see if this is the last cluster in the chain
291 jb ContinueSearch ; If not continue, if so then we didn't find freeldr.sys
292 jmp PrintFileNotFound
293 ContinueSearch:
294 mov bx,7e0h
295 mov es,bx ; Read cluster to [0000:7e00h]
296 call ReadCluster ; Read the cluster
297
298
299 ; Now we have to find our way through the root directory to
300 ; The OSLOADER.SYS file
301 xor bx,bx
302 mov bl,[BYTE bp+SectsPerCluster]
303 shl bx,4 ; BX = BX * 512 / 32
304 mov ax,7e0h ; We loaded at 07e0:0000
305 mov es,ax
306 xor di,di
307 mov si,filename
308 mov cx,11
309 rep cmpsb ; Compare filenames
310 jz FoundFile ; If same we found it
311 dec bx
312 jnz FindFile
313 jmp PrintFileNotFound
314
315 FindFile:
316 mov ax,es ; We didn't find it in the previous dir entry
317 add ax,2 ; So lets move to the next one
318 mov es,ax ; And search again
319 xor di,di
320 mov si,filename
321 mov cx,11
322 rep cmpsb ; Compare filenames
323 jz FoundFile ; If same we found it
324 dec bx ; Keep searching till we run out of dir entries
325 jnz FindFile ; Last entry?
326
327 ; Get the next root dir cluster and try again until we run out of clusters
328 mov eax,DWORD [BYTE bp+RootDirStartCluster]
329 call GetFatEntry
330 mov [BYTE bp+RootDirStartCluster],eax
331 jmp StartSearch
332
333 FoundFile:
334
335 ; Display "Loading FreeLoader..." message
336 mov si,msgLoading ; Loading message
337 call PutChars ; Display it
338
339 popad
340
341 xor di,di ; ES:DI has dir entry
342 xor dx,dx
343 mov ax,WORD [es:di+14h] ; Get start cluster high word
344 shl eax,16
345 mov ax,WORD [es:di+1ah] ; Get start cluster low word
346
347 CheckStartCluster:
348 cmp eax,2 ; Check and see if the start cluster starts at cluster 2 or above
349 jnb CheckEndCluster ; If so then continue
350 jmp PrintFileSystemError ; If not exit with error
351 CheckEndCluster:
352 cmp eax,0ffffff8h ; Check and see if the start cluster is and end of cluster chain indicator
353 jb InitializeLoadSegment ; If not then continue
354 jmp PrintFileSystemError ; If so exit with error
355
356 InitializeLoadSegment:
357 mov bx,7e0h
358 mov es,bx
359
360 LoadFile:
361 cmp eax,0ffffff8h ; Check to see if this is the last cluster in the chain
362 jae LoadFileDone ; If so continue, if not then read the next one
363 push eax
364 xor bx,bx ; Load ROSLDR starting at 0000:7e00h
365 push es
366 call ReadCluster
367 pop es
368
369 xor bx,bx
370 mov bl,[BYTE bp+SectsPerCluster]
371 shl bx,5 ; BX = BX * 512 / 16
372 mov ax,es ; Increment the load address by
373 add ax,bx ; The size of a cluster
374 mov es,ax
375
376 pop eax
377 push es
378 call GetFatEntry ; Get the next entry
379 pop es
380
381 jmp LoadFile ; Load the next cluster (if any)
382
383 LoadFileDone:
384 mov dl,[BYTE bp+BootDrive]
385 xor ax,ax ; We loaded at 0000:7e00 but the entry point is 0000:8000
386 push ax ; because the first 512 bytes is fat helper code
387 push WORD 8000h ; We will do a far return to 0000:8000h
388 retf ; Transfer control to ROSLDR
389
390
391 ; Returns the FAT entry for a given cluster number
392 ; On entry EAX has cluster number
393 ; On return EAX has FAT entry for that cluster
394 GetFatEntry:
395
396 shl eax,2 ; EAX = EAX * 4 (since FAT32 entries are 4 bytes)
397 mov ecx,eax ; Save this for later in ECX
398 xor edx,edx
399 movzx ebx,WORD [BYTE bp+BytesPerSector]
400 push ebx
401 div ebx ; FAT Sector Number = EAX / BytesPerSector
402 movzx ebx,WORD [BYTE bp+ReservedSectors]
403 add eax,ebx ; FAT Sector Number += ReservedSectors
404 mov ebx,DWORD [BYTE bp+HiddenSectors]
405 add eax,ebx ; FAT Sector Number += HiddenSectors
406 pop ebx
407 dec ebx
408 and ecx,ebx ; FAT Offset Within Sector = ECX % BytesPerSector
409 ; EAX holds logical FAT sector number
410 ; ECX holds FAT entry offset
411
412 ; Now we have to check the extended flags
413 ; to see which FAT is the active one
414 ; and use it, or if they are mirrored then
415 ; no worries
416 movzx ebx,WORD [BYTE bp+ExtendedFlags] ; Get extended flags and put into ebx
417 and bx,0x0f ; Mask off upper 8 bits, now we have active fat in bl
418 jz LoadFatSector ; If fat is mirrored then skip fat calcs
419 cmp bl,[BYTE bp+NumberOfFats] ; Compare bl to number of fats
420 jb GetActiveFatOffset
421 jmp PrintFileSystemError ; If bl is bigger than numfats exit with error
422 GetActiveFatOffset:
423 push eax ; Save logical FAT sector number
424 mov eax,[BYTE bp+SectorsPerFatBig] ; Get the number of sectors occupied by one fat in eax
425 mul ebx ; Multiplied by the active FAT index we have in ebx
426 pop edx ; Get logical FAT sector number
427 add eax,edx ; Add the current FAT sector offset
428
429 LoadFatSector:
430 push ecx
431 ; EAX holds logical FAT sector number
432 mov bx,7000h
433 mov es,bx
434 xor bx,bx ; We will load it to [7000:0000h]
435 mov cx,1
436 call ReadSectors
437 mov bx,7000h
438 mov es,bx
439 pop ecx
440 mov eax,DWORD [es:ecx] ; Get FAT entry
441 and eax,0fffffffh ; Mask off reserved bits
442
443 ret
444
445
446 ; Reads cluster number in EAX into [ES:0000]
447 ReadCluster:
448 ; StartSector = ((Cluster - 2) * SectorsPerCluster) + ReservedSectors + HiddenSectors;
449
450 dec eax
451 dec eax
452 xor edx,edx
453 movzx ebx,BYTE [BYTE bp+SectsPerCluster]
454 mul ebx
455 push eax
456 xor edx,edx
457 movzx eax,BYTE [BYTE bp+NumberOfFats]
458 mul DWORD [BYTE bp+SectorsPerFatBig]
459 movzx ebx,WORD [BYTE bp+ReservedSectors]
460 add eax,ebx
461 add eax,DWORD [BYTE bp+HiddenSectors]
462 pop ebx
463 add eax,ebx ; EAX now contains the logical sector number of the cluster
464 xor bx,bx ; We will load it to [ES:0000], ES loaded before function call
465 movzx cx,BYTE [BYTE bp+SectsPerCluster]
466 call ReadSectors
467 ret
468
469
470 ; Displays a file not found error message
471 ; And reboots
472 PrintFileNotFound:
473 mov si,msgFreeLdr ; FreeLdr not found message
474 call PutChars ; Display it
475 mov si,msgAnyKey ; Press any key message
476 call PutChars ; Display it
477
478 jmp Reboot
479
480 msgFreeLdr db 'FREELDR.SYS not found',0dh,0ah,0
481 filename db 'FREELDR SYS'
482 msgLoading db 'Loading FreeLoader...',0dh,0ah,0
483
484
485 times 1022-($-$$) db 0 ; Pad to 1022 bytes
486
487 dw 0aa55h ; BootSector signature