e9eb2ee8982f1844e869868964387ee50f488d90
[reactos.git] / reactos / boot / freeldr / bootsect / fat.S
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 #include <asm.inc>
42 #include "../freeldr/include/arch/pc/x86common.h"
43
44 #define BP_REL(x) [bp+x-offset start]
45
46 DataAreaStartHigh = 2
47 DataAreaStartLow = 4
48 BiosCHSDriveSizeHigh = 6
49 BiosCHSDriveSizeLow = 8
50 BiosCHSDriveSize = 8
51 ReadSectorsOffset = 10
52 ReadClusterOffset = 12
53 PutCharsOffset = 14
54 BootSectorStackTop = HEX(7c00) - 16
55
56
57 // org 7c00h
58
59 .code16
60
61 start:
62 jmp main
63 nop
64
65 OEMName:
66 .ascii "FrLdr1.0"
67 BytesPerSector:
68 .word 512
69 SectsPerCluster:
70 .byte 1
71 ReservedSectors:
72 .word 1
73 NumberOfFats:
74 .byte 2
75 MaxRootEntries:
76 .word 224
77 TotalSectors:
78 .word 2880
79 MediaDescriptor:
80 .byte HEX(0f0)
81 SectorsPerFat:
82 .word 9
83 SectorsPerTrack:
84 .word 18
85 NumberOfHeads:
86 .word 2
87 HiddenSectors:
88 .long 0
89 TotalSectorsBig:
90 .long 0
91 BootDrive:
92 .byte HEX(0ff)
93 Reserved:
94 .byte 0
95 ExtendSig:
96 .byte HEX(29)
97 SerialNumber:
98 .long 00000000
99 VolumeLabel:
100 .ascii "NO NAME "
101 FileSystem:
102 .ascii "FAT12 "
103
104 main:
105 xor ax, ax
106 mov ss, ax
107 mov bp, HEX(7c00)
108 mov sp, BootSectorStackTop // Setup a stack
109 mov ds, ax // Make DS correct
110 mov es, ax // Make ES correct
111
112 cmp byte ptr BP_REL(BootDrive), HEX(0ff) // If they have specified a boot drive then use it
113 jne GetDriveParameters
114
115 mov byte ptr BP_REL(BootDrive), dl // Save the boot drive
116
117
118 GetDriveParameters:
119 mov ah, 8
120 mov dl, byte ptr BP_REL(BootDrive) // Get boot drive in dl
121 int HEX(13) // Request drive parameters from the bios
122 jnc CalcDriveSize // If the call succeeded then calculate the drive size
123
124 // If we get here then the call to the BIOS failed
125 // so just set CHS equal to the maximum addressable
126 // size
127 mov cx, HEX(0ffff)
128 mov dh, cl
129
130 CalcDriveSize:
131 // Now that we have the drive geometry
132 // lets calculate the drive size
133 mov bl, ch // Put the low 8-bits of the cylinder count into BL
134 mov bh, cl // Put the high 2-bits in BH
135 shr bh, 6 // Shift them into position, now BX contains the cylinder count
136 and cl, HEX(3f) // Mask off cylinder bits from sector count
137 // CL now contains sectors per track and DH contains head count
138 movzx eax, dh // Move the heads into EAX
139 movzx ebx, bx // Move the cylinders into EBX
140 movzx ecx, cl // Move the sectors per track into ECX
141 inc eax // Make it one based because the bios returns it zero based
142 inc ebx // Make the cylinder count one based also
143 mul ecx // Multiply heads with the sectors per track, result in edx:eax
144 mul ebx // Multiply the cylinders with (heads * sectors) [stored in edx:eax already]
145
146 // We now have the total number of sectors as reported
147 // by the bios in eax, so store it in our variable
148 mov dword ptr [bp - BiosCHSDriveSize], eax
149
150
151 // Now we must find our way to the first sector of the root directory
152 xor ax, ax
153 xor cx, cx
154 mov al, byte ptr BP_REL(NumberOfFats) // Number of fats
155 mul word ptr BP_REL(SectorsPerFat) // Times sectors per fat
156 add ax, word ptr BP_REL(HiddenSectors)
157 adc dx, word ptr BP_REL(HiddenSectors+2) // Add the number of hidden sectors
158 add ax, word ptr BP_REL(ReservedSectors) // Add the number of reserved sectors
159 adc dx, cx // Add carry bit
160 mov word ptr [bp - DataAreaStartLow], ax // Save the starting sector of the root directory
161 mov word ptr [bp - DataAreaStartHigh], dx // Save it in the first 4 bytes before the boot sector
162 mov si, word ptr BP_REL(MaxRootEntries) // Get number of root dir entries in SI
163 pusha // Save 32-bit logical start sector of root dir
164 // DX:AX now has the number of the starting sector of the root directory
165
166 // Now calculate the size of the root directory
167 xor dx, dx
168 mov ax, 32 // Size of dir entry
169 mul si // Times the number of entries
170 mov bx, word ptr BP_REL(BytesPerSector)
171 add ax, bx
172 dec ax
173 div bx // Divided by the size of a sector
174 // AX now has the number of root directory sectors
175
176 add word ptr [bp - DataAreaStartLow], ax // Add the number of sectors of the root directory to our other value
177 adc word ptr [bp - DataAreaStartHigh], cx // Now the first 4 bytes before the boot sector contain the starting sector of the data area
178 popa // Restore root dir logical sector start to DX:AX
179
180 LoadRootDirSector:
181 mov bx, HEX(7e0) // We will load the root directory sector
182 mov es, bx // Right after the boot sector in memory
183 xor bx, bx // We will load it to [0000:7e00h]
184 xor cx, cx // Zero out CX
185 inc cx // Now increment it to 1, we are reading one sector
186 xor di, di // Zero out di
187 push es // Save ES because it will get incremented by 20h
188 call ReadSectors // Read the first sector of the root directory
189 pop es // Restore ES (ES:DI = 07E0:0000)
190
191 SearchRootDirSector:
192 cmp byte ptr es:[di], ch // If the first byte of the directory entry is zero then we have
193 jz ErrBoot // reached the end of the directory and FREELDR.SYS is not here so reboot
194 pusha // Save all registers
195 mov cl, 11 // Put 11 in cl (length of filename in directory entry)
196 mov si, offset filename // Put offset of filename string in DS:SI
197 repe cmpsb // Compare this directory entry against 'FREELDR SYS'
198 popa // Restore all the registers
199 jz FoundFreeLoader // If we found it then jump
200 dec si // SI holds MaxRootEntries, subtract one
201 jz ErrBoot // If we are out of root dir entries then reboot
202 add di, 32 // Increment DI by the size of a directory entry
203 cmp di, HEX(0200) // Compare DI to 512 (DI has offset to next dir entry, make sure we haven't gone over one sector)
204 jc SearchRootDirSector // If DI is less than 512 loop again
205 jmp short LoadRootDirSector // Didn't find FREELDR.SYS in this directory sector, try again
206
207 FoundFreeLoader:
208 // We found freeldr.sys on the disk
209 // so we need to load the first 512
210 // bytes of it to 0000:F800
211 // ES:DI has dir entry (ES:DI == 07E0:XXXX)
212 mov ax, word ptr es:[di + HEX(1a)] // Get start cluster
213 push ax // Save start cluster
214 push FREELDR_BASE / 16 // Put load segment on the stack and load it
215 pop es // Into ES so that we load the cluster at 0000:F800
216 call ReadCluster // Read the cluster
217 pop ax // Restore start cluster of FreeLoader
218
219 // Save the addresses of needed functions so
220 // the helper code will know where to call them.
221 mov word ptr [bp-ReadSectorsOffset], offset ReadSectors // Save the address of ReadSectors
222 mov word ptr [bp-ReadClusterOffset], offset ReadCluster // Save the address of ReadCluster
223 mov word ptr [bp-PutCharsOffset], offset PutChars // Save the address of PutChars
224
225 // Now AX has start cluster of FreeLoader and we
226 // have loaded the helper code in the first 512 bytes
227 // of FreeLoader to 0000:F800. Now transfer control
228 // to the helper code. Skip the first three bytes
229 // because they contain a jump instruction to skip
230 // over the helper code in the FreeLoader image.
231 ljmp16 0, FREELDR_BASE + 3
232
233
234
235
236 // Displays an error message
237 // And reboots
238 ErrBoot:
239 mov si, offset msgFreeLdr // FreeLdr not found message
240 call PutChars // Display it
241
242 Reboot:
243 // mov si, offset msgAnyKey // Press any key message
244 // call PutChars // Display it
245 xor ax, ax
246 int HEX(16) // Wait for a keypress
247 int HEX(19) // Reboot
248
249 PutChars:
250 lodsb
251 or al,al
252 jz short Done
253 mov ah, HEX(0e)
254 mov bx, 7
255 int HEX(10)
256 jmp short PutChars
257 Done:
258 ret
259
260 // Displays a bad boot message
261 // And reboots
262 BadBoot:
263 mov si, offset msgDiskError // Bad boot disk message
264 call PutChars // Display it
265
266 jmp short Reboot
267
268
269 // Reads cluster number in AX into [ES:0000]
270 ReadCluster:
271 // StartSector = ((Cluster - 2) * SectorsPerCluster) + ReservedSectors + HiddenSectors;
272 dec ax // Adjust start cluster by 2
273 dec ax // Because the data area starts on cluster 2
274 xor ch, ch
275 mov cl, byte ptr BP_REL(SectsPerCluster)
276 mul cx // Times sectors per cluster
277 add ax, [bp-DataAreaStartLow] // Add start of data area
278 adc dx, [bp-DataAreaStartHigh] // Now we have DX:AX with the logical start sector of OSLOADER.SYS
279 xor bx, bx // We will load it to [ES:0000], ES loaded before function call
280 //mov cl,BYTE [BYTE bp+SectsPerCluster]// Sectors per cluster still in CX
281 //call ReadSectors
282 //ret
283
284
285
286 // Reads logical sectors into [ES:BX]
287 // DX:AX has logical sector number to read
288 // CX has number of sectors to read
289 ReadSectors:
290
291 // We can't just check if the start sector is
292 // in the BIOS CHS range. We have to check if
293 // the start sector + length is in that range.
294 pusha
295 dec cx
296 add ax, cx
297 adc dx, 0
298
299 cmp dx, word ptr [bp-BiosCHSDriveSizeHigh] // Check if they are reading a sector within CHS range
300 ja ReadSectorsLBA // No - go to the LBA routine
301 jb ReadSectorsCHS // Yes - go to the old CHS routine
302 cmp ax, word ptr [bp-BiosCHSDriveSizeLow] // Check if they are reading a sector within CHS range
303 jbe ReadSectorsCHS // Yes - go to the old CHS routine
304
305 ReadSectorsLBA:
306 popa
307 ReadSectorsLBALoop:
308 pusha // Save logical sector number & sector count
309
310 push 0
311 push 0
312 push dx // Put 64-bit logical
313 push ax // block address on stack
314 push es // Put transfer segment on stack
315 push bx // Put transfer offset on stack
316 push 1 // Set transfer count to 1 sector
317 push HEX(10) // Set size of packet to 10h
318 mov si,sp // Setup disk address packet on stack
319
320 // We are so totally out of space here that I am forced to
321 // comment out this very beautifully written piece of code
322 // It would have been nice to have had this check...
323 //CheckInt13hExtensions: // Now make sure this computer supports extended reads
324 // mov ah,0x41 // AH = 41h
325 // mov bx,0x55aa // BX = 55AAh
326 // mov dl,[BYTE bp+BootDrive] // DL = drive (80h-FFh)
327 // int 13h // IBM/MS INT 13 Extensions - INSTALLATION CHECK
328 // jc PrintDiskError // CF set on error (extensions not supported)
329 // cmp bx,0xaa55 // BX = AA55h if installed
330 // jne PrintDiskError
331 // test cl,1 // CX = API subset support bitmap
332 // jz PrintDiskError // Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported
333
334
335 // Good, we're here so the computer supports LBA disk access
336 // So finish the extended read
337 mov dl, byte ptr BP_REL(BootDrive) // Drive number
338 mov ah, HEX(42) // Int 13h, AH = 42h - Extended Read
339 int HEX(13) // Call BIOS
340 jc BadBoot // If the read failed then abort
341
342 add sp, 16 // Remove disk address packet from stack
343
344 popa // Restore sector count & logical sector number
345
346 inc ax // Increment Sector to Read
347 adc dx, 0
348
349 push bx
350 mov bx, es
351 add bx, HEX(20) // Increment read buffer for next sector
352 mov es, bx
353 pop bx
354
355 loop ReadSectorsLBALoop // Read next sector
356
357 ret
358
359
360 // Reads logical sectors into [ES:BX]
361 // DX:AX has logical sector number to read
362 // CX has number of sectors to read
363 // CarryFlag set on error
364 ReadSectorsCHS:
365 popa
366 ReadSectorsCHSLoop:
367 pusha
368 xchg ax, cx
369 xchg ax, dx
370 xor dx, dx
371 div word ptr BP_REL(SectorsPerTrack)
372 xchg ax, cx
373 div word ptr BP_REL(SectorsPerTrack) // Divide logical by SectorsPerTrack
374 inc dx // Sectors numbering starts at 1 not 0
375 xchg cx, dx
376 div word ptr BP_REL(NumberOfHeads) // Number of heads
377 mov dh, dl // Head to DH, drive to DL
378 mov dl, byte ptr BP_REL(BootDrive) // Drive number
379 mov ch, al // Cylinder in CX
380 ror ah, 2 // Low 8 bits of cylinder in CH, high 2 bits
381 // in CL shifted to bits 6 & 7
382 or cl, ah // Or with sector number
383 mov ax, HEX(0201)
384 int HEX(13) // DISK - READ SECTORS INTO MEMORY
385 // AL = number of sectors to read, CH = track, CL = sector
386 // DH = head, DL = drive, ES:BX -> buffer to fill
387 // Return: CF set on error, AH = status (see AH=01h), AL = number of sectors read
388
389 jc BadBoot
390
391 popa
392 inc ax //Increment Sector to Read
393 jnz NoCarryCHS
394 inc dx
395
396
397 NoCarryCHS:
398 push bx
399 mov bx, es
400 add bx, HEX(20)
401 mov es, bx
402 pop bx
403 // Increment read buffer for next sector
404 loop ReadSectorsCHSLoop // Read next sector
405
406 ret
407
408
409 msgDiskError:
410 .ascii "Disk error", CR, LF, NUL
411 msgFreeLdr:
412 .ascii "ldr not found", CR, LF, NUL
413 // Sorry, need the space...
414 //msgAnyKey:
415 // .ascii "Press any key to restart", CR, LF, NUL
416 filename:
417 .ascii "FREELDR SYS"
418
419 .org 509 // Pad to 509 bytes
420
421 BootPartition:
422 .byte 0
423
424 BootSignature:
425 .word HEX(0aa55) // BootSector signature
426
427 .endcode16
428
429 END