BIOS Int 13h Extensions Support
[reactos.git] / freeldr / bootsect / fat.asm
1 ; FAT.ASM
2 ; FAT12/16 Boot Sector
3 ; Copyright (c) 1998, 2001 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:7C00 so that the first
13 ; DWORD pushed will be placed at 0000:7BFC
14 ;
15 ; When it locates freeldr.sys on the disk it will
16 ; load the first sector of the file to 0000:7E00
17 ; With the help of this sector we should be able
18 ; to load the entire file off the disk, no matter
19 ; how fragmented it is.
20 ;
21 ; We load the entire FAT table into memory at
22 ; 7000:0000. This improves the speed of floppy disk
23 ; boots dramatically.
24
25
26
27 org 7c00h
28
29 segment .text
30
31 bits 16
32
33 start:
34 jmp short main
35 nop
36
37 OEMName db 'FrLdr1.0'
38 BytesPerSector dw 512
39 SectsPerCluster db 1
40 ReservedSectors dw 1
41 NumberOfFats db 2
42 MaxRootEntries dw 224
43 TotalSectors dw 2880
44 MediaDescriptor db 0f0h
45 SectorsPerFat dw 9
46 SectorsPerTrack dw 18
47 NumberOfHeads dw 2
48 HiddenSectors dd 0
49 TotalSectorsBig dd 0
50 BootDrive db 0
51 Reserved db 0
52 ExtendSig db 29h
53 SerialNumber dd 00000000h
54 VolumeLabel db 'NO NAME '
55 FileSystem db 'FAT12 '
56
57 main:
58 cli
59 cld
60 xor ax,ax
61 mov ss,ax
62 mov bp,7c00h
63 mov sp,bp ; Setup a stack
64 mov ax,cs ; Setup segment registers
65 mov ds,ax ; Make DS correct
66 mov es,ax ; Make ES correct
67
68
69 sti ; Enable ints now
70 mov [BYTE bp+BootDrive],dl ; Save the boot drive
71 xor ax,ax ; Zero out AX
72
73 ; Reset disk controller
74 int 13h
75 jnc Continue1
76 jmp BadBoot ; Reset failed...
77
78 Continue1:
79 ; Now we must find our way to the first sector of the root directory
80 xor ax,ax
81 xor dx,dx
82 mov al,[BYTE bp+NumberOfFats] ; Number of fats
83 mul WORD [BYTE bp+SectorsPerFat] ; Times sectors per fat
84 add ax,WORD [BYTE bp+HiddenSectors]
85 adc dx,WORD [BYTE bp+HiddenSectors+2] ; Add the number of hidden sectors
86 add ax,WORD [BYTE bp+ReservedSectors] ; Add the number of reserved sectors
87 adc dx,byte 0 ; Add carry bit
88 push ax ; Store it on the stack
89 push dx ; Save 32-bit logical start sector
90 push ax
91 push dx ; Save it for later use also
92 ; DX:AX now has the number of the starting sector of the root directory
93
94 ; Now calculate the size of the root directory
95 mov ax,0020h ; Size of dir entry
96 mul WORD [BYTE bp+MaxRootEntries] ; Times the number of entries
97 mov bx,[BYTE bp+BytesPerSector]
98 add ax,bx
99 dec ax
100 div bx ; Divided by the size of a sector
101 ; AX now has the number of root directory sectors
102
103 xchg ax,cx ; Now CX has number of sectors
104 pop dx
105 pop ax ; Restore logical sector start
106 push cx ; Save number of root dir sectors for later use
107 mov bx,7c0h ; We will load the root directory
108 add bx,byte 20h ; Right after the boot sector in memory
109 mov es,bx
110 xor bx,bx ; We will load it to [0000:7e00h]
111 call ReadSectors ; Read the sectors
112
113
114 ; Now we have to find our way through the root directory to
115 ; The OSLOADER.SYS file
116 mov bx,[BYTE bp+MaxRootEntries]; Search entire root directory
117 mov ax,7e0h ; We loaded at 07e0:0000
118 mov es,ax
119 xor di,di
120 mov si,filename
121 mov cx,11
122 rep cmpsb ; Compare filenames
123 jz FoundFile ; If same we found it
124 dec bx
125 jnz FindFile
126 jmp ErrBoot
127
128 FindFile:
129 mov ax,es ; We didn't find it in the previous dir entry
130 add ax,byte 2 ; So lets move to the next one
131 mov es,ax ; And search again
132 xor di,di
133 mov si,filename
134 mov cx,11
135 rep cmpsb ; Compare filenames
136 jz FoundFile ; If same we found it
137 dec bx ; Keep searching till we run out of dir entries
138 jnz FindFile ; Last entry?
139 jmp ErrBoot
140
141 FoundFile:
142 ; We found freeldr.sys on the disk
143 ; so we need to load the first 512
144 ; bytes of it to 0000:7E00
145 xor di,di ; ES:DI has dir entry
146 xor dx,dx
147 mov ax,WORD [es:di+1ah]; Get start cluster
148 dec ax ; Adjust start cluster by 2
149 dec ax ; Because the data area starts on cluster 2
150 xor ch,ch
151 mov cl,BYTE [BYTE bp+SectsPerCluster] ; Times sectors per cluster
152 mul cx
153 pop cx ; Get number of sectors for root dir
154 add ax,cx ; Add it to the start sector of freeldr.sys
155 adc dx,byte 0
156 pop cx ; Get logical start sector of
157 pop bx ; Root directory
158 add ax,bx ; Now we have DX:AX with the logical start
159 adc dx,cx ; Sector of OSLOADER.SYS
160 mov cx,1 ; We will load 1 sector
161 push WORD [es:di+1ah] ; Save start cluster
162 mov bx,7e0h
163 mov es,bx
164 xor bx,bx
165 call ReadSectors ; Load it
166 pop ax ; Restore start cluster
167 jmp LoadFile
168
169
170
171 ; Reads logical sectors into [ES:BX]
172 ; DX:AX has logical sector number to read
173 ; CX has number of sectors to read
174 ; CarryFlag set on error
175 ReadSectors:
176 push ax
177 push dx
178 push cx
179 xchg ax,cx
180 xchg ax,dx
181 xor dx,dx
182 div WORD [BYTE bp+SectorsPerTrack]
183 xchg ax,cx
184 div WORD [BYTE bp+SectorsPerTrack] ; Divide logical by SectorsPerTrack
185 inc dx ; Sectors numbering starts at 1 not 0
186 xchg cx,dx
187 div WORD [BYTE bp+NumberOfHeads] ; Number of heads
188 mov dh,dl ; Head to DH, drive to DL
189 mov dl,[BYTE bp+BootDrive] ; Drive number
190 mov ch,al ; Cylinder in CX
191 ror ah,1 ; Low 8 bits of cylinder in CH, high 2 bits
192 ror ah,1 ; in CL shifted to bits 6 & 7
193 or cl,ah ; Or with sector number
194 mov ax,0201h
195 int 13h ; DISK - READ SECTORS INTO MEMORY
196 ; AL = number of sectors to read, CH = track, CL = sector
197 ; DH = head, DL = drive, ES:BX -> buffer to fill
198 ; Return: CF set on error, AH = status (see AH=01h), AL = number of sectors read
199
200 jc BadBoot
201
202 pop cx
203 pop dx
204 pop ax
205 inc ax ;Increment Sector to Read
206 jnz NoCarry
207 inc dx
208
209
210 NoCarry:
211 push bx
212 mov bx,es
213 add bx,byte 20h
214 mov es,bx
215 pop bx
216 ; Increment read buffer for next sector
217 loop ReadSectors ; Read next sector
218
219 ret
220
221
222
223 ; Displays a bad boot message
224 ; And reboots
225 BadBoot:
226 mov si,msgDiskError ; Bad boot disk message
227 call PutChars ; Display it
228 mov si,msgAnyKey ; Press any key message
229 call PutChars ; Display it
230
231 jmp Reboot
232
233 ; Displays an error message
234 ; And reboots
235 ErrBoot:
236 mov si,msgFreeLdr ; FreeLdr not found message
237 call PutChars ; Display it
238 mov si,msgAnyKey ; Press any key message
239 call PutChars ; Display it
240
241 Reboot:
242 xor ax,ax
243 int 16h ; Wait for a keypress
244 int 19h ; Reboot
245
246 PutChars:
247 lodsb
248 or al,al
249 jz short Done
250 mov ah,0eh
251 mov bx,07h
252 int 10h
253 jmp short PutChars
254 Done:
255 retn
256
257 msgDiskError db 'Disk error',0dh,0ah,0
258 msgFreeLdr db 'FREELDR.SYS not found',0dh,0ah,0
259 msgAnyKey db 'Press any key to restart',0dh,0ah,0
260 filename db 'FREELDR SYS'
261
262 times 510-($-$$) db 0 ; Pad to 510 bytes
263 dw 0aa55h ; BootSector signature
264
265
266
267 ; End of bootsector
268 ;
269 ; Now starts the extra boot code that we will store
270 ; in the first 512 bytes of freeldr.sys
271
272
273
274 LoadFile:
275
276 push ax ; First save AX - the start cluster of freeldr.sys
277
278
279 ; Lets save the contents of the screen
280 ; from B800:0000 to 9000:8000
281 push ds
282 mov ax,0b800h
283 mov ds,ax
284 xor si,si
285 mov ax,9800h
286 mov es,ax
287 xor di,di
288 mov cx,2000 ; Copy 2000 characters [words] (screen is 80x25)
289 rep movsw ; 2 bytes a character (one is the attribute byte)
290 pop ds
291
292 mov ah,03h ; AH = 03h
293 xor bx,bx ; BH = video page
294 int 10h ; BIOS Int 10h Func 3 - Read Cursor Position and Size
295 mov [es:di],dx ; DH = row, DL = column
296
297 ; Display "Loading FreeLoader..." message
298 mov si,msgLoading ; Loading message
299 call PutChars ; Display it
300
301
302 pop ax ; Restore AX
303
304 ; AX has start cluster of freeldr.sys
305 push ax
306 call ReadFatIntoMemory
307 pop ax
308
309 mov bx,7e0h
310 mov es,bx
311
312 LoadFile2:
313 push ax
314 call IsFat12
315 pop ax
316 jnc LoadFile3
317 cmp ax,0ff8h ; Check to see if this is the last cluster in the chain
318 jmp LoadFile4
319 LoadFile3:
320 cmp ax,0fff8h
321 LoadFile4:
322 jae LoadFile_Done ; If so continue, if not then read then next one
323 push ax
324 xor bx,bx ; Load ROSLDR starting at 0000:8000h
325 push es
326 call ReadCluster
327 pop es
328
329 xor bx,bx
330 mov bl,BYTE [BYTE bp+SectsPerCluster]
331 shl bx,5 ; BX = BX * 512 / 16
332 mov ax,es ; Increment the load address by
333 add ax,bx ; The size of a cluster
334 mov es,ax
335
336 call IsFat12
337 pop ax
338 push es
339 jnc LoadFile5
340 call GetFatEntry12 ; Get the next entry
341 jmp LoadFile6
342 LoadFile5:
343 call GetFatEntry16
344 LoadFile6:
345 pop es
346
347 jmp LoadFile2 ; Load the next cluster (if any)
348
349 LoadFile_Done:
350 mov dl,BYTE [BYTE bp+BootDrive]
351 xor ax,ax
352 push ax
353 mov ax,8000h
354 push ax ; We will do a far return to 0000:8000h
355 retf ; Transfer control to ROSLDR
356
357
358 ; Reads the entire FAT into memory at 7000:0000
359 ReadFatIntoMemory:
360 mov ax,WORD [BYTE bp+HiddenSectors]
361 mov dx,WORD [BYTE bp+HiddenSectors+2]
362 add ax,WORD [BYTE bp+ReservedSectors]
363 adc dx,byte 0
364 mov cx,WORD [BYTE bp+SectorsPerFat]
365 mov bx,7000h
366 mov es,bx
367 xor bx,bx
368 call ReadSectors
369 ret
370
371
372 ; Returns the FAT entry for a given cluster number for 16-bit FAT
373 ; On entry AX has cluster number
374 ; On return AX has FAT entry for that cluster
375 GetFatEntry16:
376
377 xor dx,dx
378 mov cx,2 ; AX = AX * 2 (since FAT16 entries are 2 bytes)
379 mul cx
380 shl dx,0fh
381
382 mov bx,7000h
383 add bx,dx
384 mov es,bx
385 mov bx,ax ; Restore FAT entry offset
386 mov ax,WORD [es:bx] ; Get FAT entry
387
388 ret
389
390
391 ; Returns the FAT entry for a given cluster number for 12-bit FAT
392 ; On entry AX has cluster number
393 ; On return AX has FAT entry for that cluster
394 GetFatEntry12:
395
396 push ax
397 mov cx,ax
398 shr ax,1
399 add ax,cx ; AX = AX * 1.5 (AX = AX + (AX / 2)) (since FAT12 entries are 12 bits)
400
401 mov bx,7000h
402 mov es,bx
403 mov bx,ax ; Put FAT entry offset into BX
404 mov ax,WORD [es:bx] ; Get FAT entry
405 pop cx ; Get cluster number from stack
406 and cx,1
407 jz UseLow12Bits
408 and ax,0fff0h
409 shr ax,4
410 jmp GetFatEntry12_Done
411
412 UseLow12Bits:
413 and ax,0fffh
414
415 GetFatEntry12_Done:
416
417 ret
418
419
420 ; Reads cluster number in AX into [ES:0000]
421 ReadCluster:
422 ; StartSector = ((Cluster - 2) * SectorsPerCluster) + + ReservedSectors + HiddenSectors;
423
424 dec ax
425 dec ax
426 xor dx,dx
427 movzx bx,BYTE [BYTE bp+SectsPerCluster]
428 mul bx
429 push ax
430 push dx
431 ; Now calculate the size of the root directory
432 mov ax,0020h ; Size of dir entry
433 mul WORD [BYTE bp+MaxRootEntries] ; Times the number of entries
434 mov bx,WORD [BYTE bp+BytesPerSector]
435 add ax,bx
436 dec ax
437 div bx ; Divided by the size of a sector
438 mov cx,ax
439 ; CX now has the number of root directory sectors
440 xor dx,dx
441 movzx ax,BYTE [BYTE bp+NumberOfFats]
442 mul WORD [BYTE bp+SectorsPerFat]
443 add ax,WORD [BYTE bp+ReservedSectors]
444 adc dx,byte 0
445 add ax,WORD [BYTE bp+HiddenSectors]
446 adc dx,WORD [BYTE bp+HiddenSectors+2]
447 add ax,cx
448 adc dx,byte 0
449 pop cx
450 pop bx
451 add ax,bx
452 adc dx,cx
453 xor bx,bx ; We will load it to [ES:0000], ES loaded before function call
454 movzx cx,BYTE [BYTE bp+SectsPerCluster]
455 call ReadSectors
456 ret
457
458 ; Returns CF = 1 if this is a FAT12 file system
459 ; Otherwise CF = 0 for FAT16
460 IsFat12:
461
462 ; Now calculate the size of the root directory
463 mov ax,0020h ; Size of dir entry
464 mul WORD [BYTE bp+MaxRootEntries] ; Times the number of entries
465 mov bx,WORD [BYTE bp+BytesPerSector]
466 add ax,bx ; Plus (BytesPerSector - 1)
467 dec ax
468 div bx ; Divided by the size of a sector
469 ; AX now has the number of root directory sectors
470
471 mov bx,ax
472 ; Now we must find our way to the first sector of the root directory
473 xor ax,ax
474 xor dx,dx
475 mov al,BYTE [BYTE bp+NumberOfFats] ; Number of fats
476 mul WORD [BYTE bp+SectorsPerFat] ; Times sectors per fat
477 add ax,WORD [BYTE bp+HiddenSectors]
478 adc dx,WORD [BYTE bp+HiddenSectors+2] ; Add the number of hidden sectors
479 add ax,[BYTE bp+ReservedSectors] ; Add the number of reserved sectors
480 adc dx,byte 0 ; Add carry bit
481 add ax,bx
482 adc dx,byte 0 ; Add carry bit
483 ; DX:AX now has the number of the starting sector of the data area
484
485 xor cx,cx
486 mov bx,WORD [BYTE bp+TotalSectors]
487 cmp bx,byte 0
488 jnz IsFat12_2
489 mov bx,WORD [BYTE bp+TotalSectorsBig]
490 mov cx,WORD [BYTE bp+TotalSectorsBig+2]
491
492 ; CX:BX now contains the number of sectors on the volume
493 IsFat12_2:
494 sub bx,ax ; Subtract data area start sector
495 sub cx,dx ; from total sectors of volume
496 mov ax,bx
497 mov dx,cx
498
499 ; DX:AX now contains the number of data sectors on the volume
500 movzx bx,BYTE [BYTE bp+SectsPerCluster]
501 div bx
502 ; AX now has the number of clusters on the volume
503 stc
504 cmp ax,4085
505 jb IsFat12_Done
506 clc
507
508 IsFat12_Done:
509 ret
510
511
512
513 times 998-($-$$) db 0 ; Pad to 998 bytes
514
515 msgLoading db 'Loading FreeLoader...',0dh,0ah,0
516
517 dw 0aa55h ; BootSector signature