minor corrections by M.Taguchi
[reactos.git] / freeldr / bootsect / ext2.asm
1 ; EXT2.ASM
2 ; EXT2 Boot Sector
3 ; Copyright (c) 2002, 2003 Brian Palmer
4
5 ; [bp-0x04] Here we will store the number of sectors per track
6 ; [bp-0x08] Here we will store the number of heads
7 ; [bp-0x0c] Here we will store the size of the disk as the BIOS reports in CHS form
8 ; [bp-0x10] Here we will store the number of LBA sectors read
9
10 SECTORS_PER_TRACK equ 0x04
11 NUMBER_OF_HEADS equ 0x08
12 BIOS_CHS_DRIVE_SIZE equ 0x0C
13 LBA_SECTORS_READ equ 0x10
14
15
16 EXT2_ROOT_INO equ 2
17 EXT2_S_IFMT equ 0f0h
18 EXT2_S_IFREG equ 080h
19
20
21 org 7c00h
22
23 segment .text
24
25 bits 16
26
27 start:
28 jmp short main
29 nop
30
31 BootDrive db 0x80
32 ;BootPartition db 0 ; Moved to end of boot sector to have a standard format across all boot sectors
33 ;SectorsPerTrack db 63 ; Moved to [bp-SECTORS_PER_TRACK]
34 ;NumberOfHeads dw 16 ; Moved to [bp-NUMBER_OF_HEADS]
35 ;BiosCHSDriveSize dd (1024 * 1024 * 63) ; Moved to [bp-BIOS_CHS_DRIVE_SIZE]
36 ;LBASectorsRead dd 0 ; Moved to [bp-LBA_SECTORS_READ]
37
38 Ext2VolumeStartSector dd 263088 ; Start sector of the ext2 volume
39 Ext2BlockSize dd 2 ; Block size in sectors
40 Ext2BlockSizeInBytes dd 1024 ; Block size in bytes
41 Ext2PointersPerBlock dd 256 ; Number of block pointers that can be contained in one block
42 Ext2GroupDescPerBlock dd 32 ; Number of group descriptors per block
43 Ext2FirstDataBlock dd 1 ; First data block (1 for 1024-byte blocks, 0 for bigger sizes)
44 Ext2InodesPerGroup dd 2048 ; Number of inodes per group
45 Ext2InodesPerBlock dd 8 ; Number of inodes per block
46
47 Ext2ReadEntireFileLoadSegment:
48 dw 0
49 Ext2InodeIndirectPointer:
50 dd 0
51 Ext2InodeDoubleIndirectPointer:
52 dd 0
53 Ext2BlocksLeftToRead:
54 dd 0
55
56 main:
57 xor ax,ax ; Setup segment registers
58 mov ds,ax ; Make DS correct
59 mov es,ax ; Make ES correct
60 mov ss,ax ; Make SS correct
61 mov bp,7c00h
62 mov sp,7b00h ; Setup a stack
63
64
65 cmp BYTE [BYTE bp+BootDrive],BYTE 0xff ; If they have specified a boot drive then use it
66 jne GetDriveParameters
67
68 mov [BYTE bp+BootDrive],dl ; Save the boot drive
69
70
71 GetDriveParameters:
72 mov ah,08h
73 mov dl,[BYTE bp+BootDrive] ; Get boot drive in dl
74 int 13h ; Request drive parameters from the bios
75 jnc CalcDriveSize ; If the call succeeded then calculate the drive size
76
77 ; If we get here then the call to the BIOS failed
78 ; so just set CHS equal to the maximum addressable
79 ; size
80 mov cx,0ffffh
81 mov dh,cl
82
83 CalcDriveSize:
84 ; Now that we have the drive geometry
85 ; lets calculate the drive size
86 mov bl,ch ; Put the low 8-bits of the cylinder count into BL
87 mov bh,cl ; Put the high 2-bits in BH
88 shr bh,6 ; Shift them into position, now BX contains the cylinder count
89 and cl,3fh ; Mask off cylinder bits from sector count
90 ; CL now contains sectors per track and DH contains head count
91 movzx eax,dh ; Move the heads into EAX
92 movzx ebx,bx ; Move the cylinders into EBX
93 movzx ecx,cl ; Move the sectors per track into ECX
94 inc eax ; Make it one based because the bios returns it zero based
95 mov [BYTE bp-NUMBER_OF_HEADS],eax ; Save number of heads
96 mov [BYTE bp-SECTORS_PER_TRACK],ecx ; Save number of sectors per track
97 inc ebx ; Make the cylinder count one based also
98 mul ecx ; Multiply heads with the sectors per track, result in edx:eax
99 mul ebx ; Multiply the cylinders with (heads * sectors) [stored in edx:eax already]
100
101 ; We now have the total number of sectors as reported
102 ; by the bios in eax, so store it in our variable
103 mov [BYTE bp-BIOS_CHS_DRIVE_SIZE],eax
104
105
106 LoadExtraBootCode:
107 ; First we have to load our extra boot code at
108 ; sector 1 into memory at [0000:7e00h]
109 ;mov eax,01h
110 xor eax,eax
111 inc eax ; Read logical sector 1, EAX now = 1
112 mov cx,1 ; Read one sector
113 mov bx,7e00h ; Read sector to [0000:7e00h]
114 call ReadSectors
115
116 jmp LoadRootDirectory
117
118
119
120 ; Reads ext2 group descriptor into [7000:8000]
121 ; We read it to this arbitrary location so
122 ; it will not cross a 64k boundary
123 ; EAX has group descriptor number to read
124 Ext2ReadGroupDesc:
125 shl eax,5 ; Group = (Group * sizeof(GROUP_DESCRIPTOR) /* 32 */)
126 xor edx,edx
127 div DWORD [BYTE bp+Ext2GroupDescPerBlock] ; Group = (Group / Ext2GroupDescPerBlock)
128 add eax,DWORD [BYTE bp+Ext2FirstDataBlock] ; Group = Group + Ext2FirstDataBlock + 1
129 inc eax ; EAX now has the group descriptor block number
130 ; EDX now has the group descriptor offset in the block
131
132 ; Adjust the read offset so that the
133 ; group descriptor is read to 7000:8000
134 mov ebx,78000h
135 sub ebx,edx
136 shr ebx,4
137 mov es,bx
138 xor bx,bx
139
140
141 ; Everything is now setup to call Ext2ReadBlock
142 ; Instead of using the call instruction we will
143 ; just put Ext2ReadBlock right after this routine
144
145 ; Reads ext2 block into [ES:BX]
146 ; EAX has logical block number to read
147 Ext2ReadBlock:
148 mov ecx,DWORD [BYTE bp+Ext2BlockSize]
149 mul ecx
150 jmp ReadSectors
151
152 ; Reads ext2 inode into [6000:8000]
153 ; We read it to this arbitrary location so
154 ; it will not cross a 64k boundary
155 ; EAX has inode number to read
156 Ext2ReadInode:
157 dec eax ; Inode = Inode - 1
158 xor edx,edx
159 div DWORD [BYTE bp+Ext2InodesPerGroup] ; Inode = (Inode / Ext2InodesPerGroup)
160 mov ebx,eax ; EBX now has the inode group number
161 mov eax,edx
162 xor edx,edx
163 div DWORD [BYTE bp+Ext2InodesPerBlock] ; Inode = (Inode / Ext2InodesPerBlock)
164 shl edx,7 ; FIXME: InodeOffset *= 128 (make the array index a byte offset)
165 ; EAX now has the inode offset block number from inode table
166 ; EDX now has the inode offset in the block
167
168 ; Save the inode values and put the group
169 ; descriptor number in EAX and read it in
170 push edx
171 push eax
172 mov eax,ebx
173 call Ext2ReadGroupDesc
174
175 ; Group descriptor has been read, now
176 ; grab the inode table block number from it
177 push WORD 7000h
178 pop es
179 mov di,8008h
180 pop eax ; Restore inode offset block number from stack
181 add eax,DWORD [es:di] ; Add the inode table start block
182
183 ; Adjust the read offset so that the
184 ; inode we want is read to 6000:8000
185 pop edx ; Restore inode offset in the block from stack
186 mov ebx,68000h
187 sub ebx,edx
188 shr ebx,4
189 mov es,bx
190 xor bx,bx
191
192 call Ext2ReadBlock
193 ret
194
195
196 ; Reads logical sectors into [ES:BX]
197 ; EAX has logical sector number to read
198 ; CX has number of sectors to read
199 ReadSectors:
200 add eax,DWORD [BYTE bp+Ext2VolumeStartSector] ; Add the start of the volume
201 cmp eax,DWORD [BYTE bp-BIOS_CHS_DRIVE_SIZE] ; Check if they are reading a sector outside CHS range
202 jae ReadSectorsLBA ; Yes - go to the LBA routine
203 ; If at all possible we want to use LBA routines because
204 ; They are optimized to read more than 1 sector per read
205
206 pushad ; Save logical sector number & sector count
207
208 CheckInt13hExtensions: ; Now check if this computer supports extended reads
209 mov ah,0x41 ; AH = 41h
210 mov bx,0x55aa ; BX = 55AAh
211 mov dl,[BYTE bp+BootDrive] ; DL = drive (80h-FFh)
212 int 13h ; IBM/MS INT 13 Extensions - INSTALLATION CHECK
213 jc ReadSectorsCHS ; CF set on error (extensions not supported)
214 cmp bx,0xaa55 ; BX = AA55h if installed
215 jne ReadSectorsCHS
216 test cl,1 ; CX = API subset support bitmap
217 jz ReadSectorsCHS ; Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported
218
219 popad ; Restore sector count & logical sector number
220
221 ReadSectorsLBA:
222 pushad ; Save logical sector number & sector count
223
224 cmp cx,byte 64 ; Since the LBA calls only support 0x7F sectors at a time we will limit ourselves to 64
225 jbe ReadSectorsSetupDiskAddressPacket ; If we are reading less than 65 sectors then just do the read
226 mov cx,64 ; Otherwise read only 64 sectors on this loop iteration
227
228 ReadSectorsSetupDiskAddressPacket:
229 mov [BYTE bp-LBA_SECTORS_READ],cx
230 mov WORD [BYTE bp-LBA_SECTORS_READ+2],0
231 o32 push byte 0
232 push eax ; Put 64-bit logical block address on stack
233 push es ; Put transfer segment on stack
234 push bx ; Put transfer offset on stack
235 push cx ; Set transfer count
236 push byte 0x10 ; Set size of packet to 10h
237 mov si,sp ; Setup disk address packet on stack
238
239
240 mov dl,[BYTE bp+BootDrive] ; Drive number
241 mov ah,42h ; Int 13h, AH = 42h - Extended Read
242 int 13h ; Call BIOS
243 jc PrintDiskError ; If the read failed then abort
244
245 add sp,byte 0x10 ; Remove disk address packet from stack
246
247 popad ; Restore sector count & logical sector number
248
249 push bx
250 mov ebx,DWORD [BYTE bp-LBA_SECTORS_READ]
251 add eax,ebx ; Increment sector to read
252 shl ebx,5
253 mov dx,es
254 add dx,bx ; Setup read buffer for next sector
255 mov es,dx
256 pop bx
257
258 sub cx,[BYTE bp-LBA_SECTORS_READ]
259 jnz ReadSectorsLBA ; Read next sector
260
261 ret
262
263
264 ; Reads logical sectors into [ES:BX]
265 ; EAX has logical sector number to read
266 ; CX has number of sectors to read
267 ReadSectorsCHS:
268 popad ; Get logical sector number & sector count off stack
269
270 ReadSectorsCHSLoop:
271 pushad
272 xor edx,edx
273 mov ecx,DWORD [BYTE bp-SECTORS_PER_TRACK]
274 div ecx ; Divide logical by SectorsPerTrack
275 inc dl ; Sectors numbering starts at 1 not 0
276 mov cl,dl ; Sector in CL
277 mov edx,eax
278 shr edx,16
279 div WORD [BYTE bp-NUMBER_OF_HEADS] ; Divide logical by number of heads
280 mov dh,dl ; Head in DH
281 mov dl,[BYTE bp+BootDrive] ; Drive number in DL
282 mov ch,al ; Cylinder in CX
283 ror ah,2 ; Low 8 bits of cylinder in CH, high 2 bits
284 ; in CL shifted to bits 6 & 7
285 or cl,ah ; Or with sector number
286 mov ax,0201h
287 int 13h ; DISK - READ SECTORS INTO MEMORY
288 ; AL = number of sectors to read, CH = track, CL = sector
289 ; DH = head, DL = drive, ES:BX -> buffer to fill
290 ; Return: CF set on error, AH = status (see AH=01h), AL = number of sectors read
291
292 jc PrintDiskError ; If the read failed then abort
293
294 popad
295
296 inc eax ; Increment Sector to Read
297
298 mov dx,es
299 add dx,byte 20h ; Increment read buffer for next sector
300 mov es,dx
301
302 loop ReadSectorsCHSLoop ; Read next sector
303
304 ret
305
306
307
308
309 ; Displays a disk error message
310 ; And reboots
311 PrintDiskError:
312 mov si,msgDiskError ; Bad boot disk message
313 call PutChars ; Display it
314
315 Reboot:
316 mov si,msgAnyKey ; Press any key message
317 call PutChars ; Display it
318 xor ax,ax
319 int 16h ; Wait for a keypress
320 int 19h ; Reboot
321
322 PutChars:
323 lodsb
324 or al,al
325 jz short Done
326 call PutCharsCallBios
327 jmp short PutChars
328 PutCharsCallBios:
329 mov ah,0eh
330 mov bx,07h
331 int 10h
332 retn
333 Done:
334 mov al,0dh
335 call PutCharsCallBios
336 mov al,0ah
337 call PutCharsCallBios
338 retn
339
340
341
342 msgDiskError db 'Disk error',0
343 ; Sorry, need the space...
344 ;msgAnyKey db 'Press any key to restart',0
345 msgAnyKey db 'Press any key',0
346
347 times 509-($-$$) db 0 ; Pad to 509 bytes
348
349 BootPartition db 0
350
351 dw 0aa55h ; BootSector signature
352
353
354 ; End of bootsector
355 ;
356 ; Now starts the extra boot code that we will store
357 ; at sector 1 on a EXT2 volume
358
359
360
361 LoadRootDirectory:
362
363 mov eax,EXT2_ROOT_INO ; Put the root directory inode number in EAX
364 call Ext2ReadInode ; Read in the inode
365
366 ; Point ES:DI to the inode structure at 6000:8000
367 push WORD 6000h
368 pop es
369 mov di,8000h
370 push di
371 push es ; Save these for later
372
373 ; Get root directory size from inode structure
374 mov eax,DWORD [es:di+4]
375 push eax
376
377 ; Now that the inode has been read in load
378 ; the root directory file data to 0000:8000
379 call Ext2ReadEntireFile
380
381 ; Since the root directory was loaded to 0000:8000
382 ; then add 8000h to the root directory's size
383 pop eax
384 mov edx,8000h ; Set EDX to the current offset in the root directory
385 add eax,edx ; Initially add 8000h to the size of the root directory
386
387 SearchRootDirectory:
388 push edx ; Save current offset in root directory
389 push eax ; Save the size of the root directory
390
391 ; Now we have to convert the current offset
392 ; in the root directory to a SEGMENT:OFFSET pair
393 mov eax,edx
394 xor edx,edx
395 mov ecx,16
396 div ecx ; Now AX:DX has segment & offset
397 mov es,ax
398 mov di,dx
399 push di ; Save the start of the directory entry
400 add di,byte 8 ; Add the offset to the filename
401 mov si,filename
402 mov cl,11
403 rep cmpsb ; Compare the file names
404 pop di
405 pop eax
406 pop edx
407 jz FoundFile
408
409 ; Nope, didn't find it in this entry, keep looking
410 movzx ecx,WORD [es:di+4]
411 add edx,ecx
412
413 ; Check to see if we have reached the
414 ; end of the root directory
415 cmp edx,eax
416 jb SearchRootDirectory
417 jmp PrintFileNotFound
418
419 FoundFile:
420 mov eax,[es:di] ; Get inode number from directory entry
421 call Ext2ReadInode ; Read in the inode
422
423 ; Point ES:DI to the inode structure at 6000:8000
424 pop es
425 pop di ; These were saved earlier
426
427 mov cx,[es:di] ; Get the file mode so we can make sure it's a regular file
428 and ch,EXT2_S_IFMT ; Mask off everything but the file type
429 cmp ch,EXT2_S_IFREG ; Make sure it's a regular file
430 je LoadFreeLoader
431 jmp PrintRegFileError
432
433 LoadFreeLoader:
434 mov si,msgLoading ; "Loading FreeLoader..." message
435 call PutChars ; Display it
436
437 call Ext2ReadEntireFile ; Read freeldr.sys to 0000:8000
438
439 mov dl,[BYTE bp+BootDrive]
440 mov dh,[BYTE bp+BootPartition]
441 push byte 0 ; We loaded at 0000:8000
442 push WORD 8000h ; We will do a far return to 0000:8000h
443 retf ; Transfer control to FreeLoader
444
445
446
447
448
449 ; Reads ext2 file data into [0000:8000]
450 ; This function assumes that the file's
451 ; inode has been read in to 6000:8000 *and*
452 ; ES:DI points to 6000:8000
453 ; This will load all the blocks up to
454 ; and including the double-indirect pointers.
455 ; This should be sufficient because it
456 ; allows for ~64MB which is much bigger
457 ; than we need for a boot loader.
458 Ext2ReadEntireFile:
459
460 ; Reset the load segment
461 mov WORD [BYTE bp+Ext2ReadEntireFileLoadSegment],800h
462
463 ; Now we must calculate how
464 ; many blocks to read in
465 ; We will do this by rounding the
466 ; file size up to the next block
467 ; size and then dividing by the block size
468 mov eax,DWORD [BYTE bp+Ext2BlockSizeInBytes] ; Get the block size in bytes
469 push eax
470 dec eax ; Ext2BlockSizeInBytes -= 1
471 add eax,DWORD [es:di+4] ; Add the file size
472 xor edx,edx
473 pop ecx ; Divide by the block size in bytes
474 div ecx ; EAX now contains the number of blocks to load
475 push eax
476
477 ; Make sure the file size isn't zero
478 cmp eax,byte 0
479 jnz Ext2ReadEntireFile2
480 jmp PrintFileSizeError
481
482 Ext2ReadEntireFile2:
483 ; Save the indirect & double indirect pointers
484 mov edx,DWORD [es:di+0x58] ; Get indirect pointer
485 mov [BYTE bp+Ext2InodeIndirectPointer],edx ; Save indirect pointer
486 mov edx,DWORD [es:di+0x5c] ; Get double indirect pointer
487 mov [BYTE bp+Ext2InodeDoubleIndirectPointer],edx ; Save double indirect pointer
488
489 ; Now copy the direct pointers to 7000:0000
490 ; so that we can call Ext2ReadDirectBlocks
491 push ds ; Save DS
492 push es
493 push WORD 7000h
494 pop es
495 pop ds
496 mov si,8028h
497 xor di,di ; DS:SI = 6000:8028 ES:DI = 7000:0000
498 mov cx,24 ; Moving 24 words of data
499 rep movsw
500 pop ds ; Restore DS
501
502 ; Now we have all the block pointers in the
503 ; right location so read them in
504 pop eax ; Restore the total number of blocks in this file
505 xor ecx,ecx ; Set the max count of blocks to read to 12
506 mov cl,12 ; which is the number of direct block pointers in the inode
507 call Ext2ReadDirectBlockList
508
509 ; Check to see if we actually have
510 ; blocks left to read
511 cmp eax,byte 0
512 jz Ext2ReadEntireFileDone
513
514 ; Now we have read all the direct blocks in
515 ; the inode. So now we have to read the indirect
516 ; block and read all it's direct blocks
517 push eax ; Save the total block count
518 mov eax,DWORD [BYTE bp+Ext2InodeIndirectPointer] ; Get the indirect block pointer
519 push WORD 7000h
520 pop es
521 xor bx,bx ; Set the load address to 7000:0000
522 call Ext2ReadBlock ; Read the block
523
524 ; Now we have all the block pointers from the
525 ; indirect block in the right location so read them in
526 pop eax ; Restore the total block count
527 mov ecx,DWORD [BYTE bp+Ext2PointersPerBlock] ; Get the number of block pointers that one block contains
528 call Ext2ReadDirectBlockList
529
530 ; Check to see if we actually have
531 ; blocks left to read
532 cmp eax,byte 0
533 jz Ext2ReadEntireFileDone
534
535 ; Now we have read all the direct blocks from
536 ; the inode's indirect block pointer. So now
537 ; we have to read the double indirect block
538 ; and read all it's indirect blocks
539 ; (whew, it's a good thing I don't support triple indirect blocks)
540 mov [BYTE bp+Ext2BlocksLeftToRead],eax ; Save the total block count
541 mov eax,DWORD [BYTE bp+Ext2InodeDoubleIndirectPointer] ; Get the double indirect block pointer
542 push WORD 7800h
543 pop es
544 push es ; Save an extra copy of this value on the stack
545 xor bx,bx ; Set the load address to 7000:8000
546 call Ext2ReadBlock ; Read the block
547
548 pop es ; Put 7800h into ES (saved on the stack already)
549 xor di,di
550
551 Ext2ReadIndirectBlock:
552 mov eax,DWORD [es:di] ; Get indirect block pointer
553 add di,BYTE 4 ; Update DI for next array index
554 push es
555 push di
556
557 push WORD 7000h
558 pop es
559 xor bx,bx ; Set the load address to 7000:0000
560 call Ext2ReadBlock ; Read the indirect block
561
562 ; Now we have all the block pointers from the
563 ; indirect block in the right location so read them in
564 mov eax,DWORD [BYTE bp+Ext2BlocksLeftToRead] ; Restore the total block count
565 mov ecx,DWORD [BYTE bp+Ext2PointersPerBlock] ; Get the number of block pointers that one block contains
566 call Ext2ReadDirectBlockList
567 mov [BYTE bp+Ext2BlocksLeftToRead],eax ; Save the total block count
568 pop di
569 pop es
570
571 ; Check to see if we actually have
572 ; blocks left to read
573 cmp eax,byte 0
574 jnz Ext2ReadIndirectBlock
575
576 Ext2ReadEntireFileDone:
577 ret
578
579 ; Reads a maximum number of blocks
580 ; from an array at 7000:0000
581 ; and updates the total count
582 ; ECX contains the max number of blocks to read
583 ; EAX contains the number of blocks left to read
584 ; On return:
585 ; EAX contians the new number of blocks left to read
586 Ext2ReadDirectBlockList:
587 cmp eax,ecx ; Compare it to the maximum number of blocks to read
588 ja CallExt2ReadDirectBlocks ; If it will take more blocks then just read all of the blocks
589 mov cx,ax ; Otherwise adjust the block count accordingly
590
591 CallExt2ReadDirectBlocks:
592 sub eax,ecx ; Subtract the number of blocks being read from the total count
593 push eax ; Save the new total count
594 call Ext2ReadDirectBlocks
595 pop eax ; Restore the total count
596 ret
597
598
599 ; Reads a specified number of blocks
600 ; from an array at 7000:0000
601 ; CX contains the number of blocks to read
602 Ext2ReadDirectBlocks:
603
604 push WORD 7000h
605 pop es
606 xor di,di ; Set ES:DI = 7000:0000
607
608 Ext2ReadDirectBlocksLoop:
609 mov eax,[es:di] ; Get direct block pointer from array
610 add di,BYTE 4 ; Update DI for next array index
611
612 push cx ; Save number of direct blocks left
613 push es ; Save array segment
614 push di ; Save array offset
615 mov es,[BYTE bp+Ext2ReadEntireFileLoadSegment]
616 xor bx,bx ; Setup load address for next read
617
618 call Ext2ReadBlock ; Read the block (this updates ES for the next read)
619
620 mov [BYTE bp+Ext2ReadEntireFileLoadSegment],es ; Save updated ES
621
622 pop di ; Restore the array offset
623 pop es ; Restore the array segment
624 pop cx ; Restore the number of blocks left
625
626 loop Ext2ReadDirectBlocksLoop
627
628 ; At this point all the direct blocks should
629 ; be loaded and ES (Ext2ReadEntireFileLoadSegment)
630 ; should be ready for the next read.
631 ret
632
633
634
635 ; Displays a file not found error message
636 ; And reboots
637 PrintFileNotFound:
638 mov si,msgFreeLdr ; FreeLdr not found message
639 jmp short DisplayItAndReboot
640
641 ; Displays a file size is 0 error
642 ; And reboots
643 PrintFileSizeError:
644 mov si,msgFileSize ; Error message
645 jmp short DisplayItAndReboot
646
647 ; Displays a file is not a regular file error
648 ; And reboots
649 PrintRegFileError:
650 mov si,msgRegFile ; Error message
651 DisplayItAndReboot:
652 call PutChars ; Display it
653 jmp Reboot
654
655 msgFreeLdr db 'freeldr.sys not found',0
656 msgFileSize db 'File size is 0',0
657 msgRegFile db 'freeldr.sys isnt a regular file',0
658 filename db 'freeldr.sys'
659 msgLoading db 'Loading FreeLoader...',0
660
661 times 1022-($-$$) db 0 ; Pad to 1022 bytes
662
663 dw 0aa55h ; BootSector signature