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