2 * PROJECT: ReactOS Boot Sector for ISO file system (based on ISOLINUX)
3 * LICENSE: GNU GPLv2 or any later version as published by the Free Software Foundation
4 * PROGRAMMERS: H. Peter Anvin
7 * Timo Kreuzer <timo.kreuzer@reactos.org>
8 * Colin Finck <colin@reactos.org>
10 *****************************************************************************
14 * A program to boot Linux kernels off a CD-ROM using the El Torito
15 * boot standard in "no emulation" mode, making the entire filesystem
16 * available. It is based on the SYSLINUX boot loader for MS-DOS
19 * Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
20 * Copyright 2009 Intel Corporation *author: H. Peter Anvin
22 * This program is free software *you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License as published by
24 * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
25 * Boston MA 02111-1307, USA *either version 2 of the License, or
26 * (at your option) any later version *incorporated herein by reference.
28 *****************************************************************************/
30 /* INCLUDES ******************************************************************/
32 #include <freeldr/include/arch/pc/x86common.h>
40 ASSUME CS:.text, DS:.text, ES:.text
42 /* CONSTANTS ******************************************************************/
43 BIOS_timer = HEX(046C) // Timer ticks (1 word)
44 BIOS_magic = HEX(0472) // BIOS reset magic (1 word)
46 // Memory below this point is reserved for the BIOS and the MBR
47 trackbuf = HEX(1000) // Track buffer goes here (8192 bytes)
48 trackbufsize = 8192 // trackbuf ends at 3000h
51 file_sector = 0 // Sector pointer (0 = structure free)
52 file_bytesleft = 4 // Number of bytes left
53 file_left = 8 // Number of sectors left
54 // Another unused DWORD follows here in ISOLINUX
55 #define open_file_t_size 16
58 dir_lba = 0 // Directory start (LBA)
59 dir_len = 4 // Length in bytes
60 dir_clust = 8 // Length in clusters
63 MAX_OPEN_LG2 = 2 // log2(Max number of open files)
65 SECTOR_SHIFT = 11 // 2048 bytes/sector (El Torito requirement)
67 retry_count = 6 // How patient are we with the BIOS?
69 /* UNINITIALIZED VARIABLES ****************************************************/
70 absolute HEX(5000) // Here we keep our BSS stuff
72 resb ISOFileName, 64 // ISO filename canonicalization buffer
73 resb ISOFileNameEnd, 1
74 resb CurrentDir, dir_t_size // Current directory
75 resb RootDir, dir_t_size // Root directory
76 resb DiskSys, 2 // Last INT 13h call
77 resb GetlinsecPtr, 2 // The sector-read pointer
78 resb DiskError, 1 // Error code for disk I/O
79 resb DriveNumber, 1 // CD-ROM BIOS drive number
80 resb ISOFlags, 1 // Flags for ISO directory search
81 resb RetryCount, 1 // Used for disk access retries
83 //align open_file_t_size
85 resb Files, (MAX_OPEN * open_file_t_size)
88 /* ENTRY POINTS ***************************************************************/
90 // Entry point when booted from CD (El Torito standard)
92 mov bx, offset getlinsec_cdrom
96 // Set up our stack and a flat addressing model.
107 // Our boot sector has been loaded to address 0x7C00.
108 // Relocate our 2048 bytes boot sector to the given base address (should be 0x7000).
115 ljmp16 0, relocated // jump into relocated code
121 // Entry point when booted through ISOMBR from a drive (isohybrid mode)
123 mov bx, offset getlinsec_ebios
127 // Save our passed variables (BX from the entry point, DL from the BIOS) before anybody clobbers the registers.
128 mov word ptr ds:[GetlinsecPtr], bx
129 mov byte ptr ds:[DriveNumber], dl
131 // Make sure the keyboard buffer is empty
132 call pollchar_and_empty
134 // If we're booting in hybrid mode and our boot drive is the first HDD (drive 80h),
135 // we have no other option than booting into SETUPLDR.
136 cmp word ptr ds:[GetlinsecPtr], offset getlinsec_ebios
138 cmp byte ptr ds:[DriveNumber], HEX(80)
142 // Read the first sector (MBR) from the first hard disk (drive 80h) to 7C00h.
143 // If we then decide to boot from HDD, we already have it at the right place.
144 // In case of an error (indicated by the Carry Flag), just boot SETUPLDR from our ReactOS medium.
152 // Verify the signature of the read MBR.
153 // If it's invalid, there is probably no OS installed and we just boot SETUPLDR from our ReactOS medium.
154 mov ax, word ptr ds:[HEX(7C00)+510]
159 // We could either boot from the ReactOS medium or from hard disk. Let the user decide!
160 // Display the 'Press key' message.
162 mov si, offset presskey_msg
165 // Count down 5 seconds.
169 // Count in seconds using the BIOS Timer, which runs roughly at 19 ticks per second.
170 // Load its value plus one second into EAX for comparison later.
171 mov eax, ds:[BIOS_timer]
175 // Check for a keypress, boot SETUPLDR from our ReactOS medium if a key was pressed.
176 call pollchar_and_empty
179 // Check if another second has passed (in BIOS Timer ticks).
180 mov ebx, ds:[BIOS_timer]
184 // Another second has passed, so print the dot and decrement the second counter.
185 // If the user hasn't pressed a key after the entire 5 seconds have elapsed, just boot from the first hard disk.
186 mov si, offset dot_msg
194 // Restore a clean context for the hard disk MBR and boot the already loaded MBR.
211 // The BIOS gave us a boot drive number, so in a perfect world we could just use that one now.
212 // Unfortunately, there are many broken BIOSes around, which is why ISOLINUX verifies it and applies some hacks if the number is wrong.
213 // Let's do exactly the same here to achieve maximum compatibility.
215 // Don't do this if we are running in hybrid mode.
216 cmp word ptr ds:[GetlinsecPtr], offset getlinsec_ebios
219 // Use the INT 13 function 4B01h (Get Disk Emulation Status) to fetch the El Torito Spec Packet.
220 // We can use this information to verify that our passed boot drive number really belongs to our CD.
222 mov dl, byte ptr ds:[DriveNumber]
223 mov si, offset spec_packet
226 // If this INT 13 function yields an error, we may be on a broken AWARD BIOS.
227 // Check this and patch if possible.
230 // Check that our passed boot drive number and the number in the Spec Packet match.
231 // If not, try some workarounds to find our drive anyway.
232 mov dl, byte ptr ds:[DriveNumber]
233 cmp byte ptr ds:[sp_drive], dl
234 jne spec_query_failed
237 // Clear Files structures
239 mov cx, (MAX_OPEN*open_file_t_size)/4
243 // Read the entire 2K-sized ISO9660 Primary Volume Descriptor at sector 16 (32K).
244 // This calculation only holds for single-session ISOs, but we should never encounter anything else.
249 // Read the LBA address (offset 2 in the Directory Record) of the root directory (offset 156 in the Primary Volume Descriptor).
250 mov eax, dword ptr ds:[trackbuf+156+2]
251 mov dword ptr ds:[RootDir+dir_lba], eax
252 mov dword ptr ds:[CurrentDir+dir_lba], eax
254 // Read the data length (offset 10 in the Directory Record) of the root directory (offset 156 in the Primary Volume Descriptor).
255 mov eax, dword ptr ds:[trackbuf+156+10]
256 mov dword ptr ds:[RootDir+dir_len], eax
257 mov dword ptr ds:[CurrentDir+dir_len], eax
259 // Calculate the number of clusters and write that to our RootDir and CurrentDir structures.
260 add eax, SECTOR_SIZE-1
261 shr eax, SECTOR_SHIFT
262 mov dword ptr ds:[RootDir+dir_clust],eax
263 mov dword ptr ds:[CurrentDir+dir_clust],eax
265 // Look for the "LOADER" directory (directory is indicated by AL = 2 when using searchdir_iso).
266 mov di, offset loader_dir
271 // No directory was found, so bail out with an error message.
272 mov si, offset no_dir_msg
277 // The directory was found, so update the information in our CurrentDir structure.
278 // Free the file pointer entry at SI in the process.
279 mov dword ptr ds:[CurrentDir+dir_len], eax
280 mov eax, dword ptr ds:[si+file_left]
281 mov dword ptr ds:[CurrentDir+dir_clust], eax
283 xchg eax, dword ptr ds:[si+file_sector]
284 mov dword ptr ds:[CurrentDir+dir_lba], eax
286 // Look for the "SETUPLDR.SYS" file.
287 mov di, offset setupldr_sys
291 // The SETUPLDR file was not found, so bail out with an error message.
292 mov si, offset no_setupldr_msg
297 // Calculate the rounded up number of 2K sectors that need to be read.
299 shr ecx, SECTOR_SHIFT
305 // Load the entire SETUPLDR.SYS (parameter CX = FFFFh) to its designated base address FREELDR_BASE.
306 // Using a high segment address with offset 0 instead of segment 0 with offset FREELDR_BASE apparently increases compatibility with some BIOSes.
307 mov bx, FREELDR_BASE / 16
313 // Pass two parameters to SETUPLDR:
314 // DL = BIOS Drive Number
315 // DH = Boot Partition (0 for HDD booting in hybrid mode, FFh for CD booting)
316 movzx dx, byte ptr ds:[DriveNumber]
317 cmp word ptr ds:[GetlinsecPtr], offset getlinsec_ebios
322 // Transfer execution to the bootloader.
323 ljmp16 0, FREELDR_BASE
326 /* FUNCTIONS *****************************************************************/
328 ///////////////////////////////////////////////////////////////////////////////
329 // Start of BrokenAwardHack --- 10-nov-2002 Knut_Petersen@t-online.de
330 ///////////////////////////////////////////////////////////////////////////////
332 // There is a problem with certain versions of the AWARD BIOS ...
333 // the boot sector will be loaded and executed correctly, but, because the
334 // int 13 vector points to the wrong code in the BIOS, every attempt to
335 // load the spec packet will fail. We scan for the equivalent of
344 // and use <direct far> as the new vector for int 13. The code above is
345 // used to load the boot code into ram, and there should be no reason
346 // for anybody to change it now or in the future. There are no opcodes
347 // that use encodings relativ to IP, so scanning is easy. If we find the
348 // code above in the BIOS code we can be pretty sure to run on a machine
349 // with an broken AWARD BIOS ...
351 ///////////////////////////////////////////////////////////////////////////////
355 .byte HEX(0b8),1,2,HEX(0bb),0,HEX(7c),HEX(0b9),6,0,HEX(0ba),HEX(80),1,HEX(09c),HEX(09a)
358 mov si, offset spec_err_msg // Moved to this place from
359 call writemsg // spec_query_failed
361 mov eax, dword ptr ds:[HEX(13)*4]
362 mov dword ptr ds:[award_oldint13], eax
365 mov ax, HEX(F000) // ES = BIOS Seg
368 xor di, di // start at ES:DI = f000:0
371 mov si, offset award_string // scan for award_string
372 mov cx, 7 // length of award_string = 7dw
373 repz cmpsw // compare
375 jcxz award_found // jmp if found
376 inc di // not found, inc di
380 pop es // No, not this way :-((
382 mov eax, dword ptr ds:[award_oldint13] // restore the original int
383 or eax, eax // 13 vector if there is one
384 jz spec_query_failed // and try other workarounds
385 mov dword ptr ds:[HEX(13)*4], eax
386 jmp spec_query_failed
389 mov eax, dword ptr es:[di+HEX(0e)] // load possible int 13 addr
392 cmp eax, dword ptr ds:[award_oldint13] // give up if this is the
393 jz award_failed // active int 13 vector,
394 mov dword ptr ds:[HEX(13)*4], eax // otherwise change 0:13h*4
396 mov ax, HEX(4B01) // try to read the spec packet
397 mov dl, byte ptr ds:[DriveNumber] // now ... it should not fail
398 mov si, offset spec_packet // any longer
402 jmp found_drive // and leave error recovery code
403 ///////////////////////////////////////////////////////////////////////////////
404 // End of BrokenAwardHack ---- 10-nov-2002 Knut_Petersen@t-online.de
405 ///////////////////////////////////////////////////////////////////////////////
408 // INT 13h, AX=4B01h, DL=<passed in value> failed.
409 // Try to scan the entire 80h-FFh from the end.
411 // some code moved to BrokenAwardHack
418 mov si, offset spec_packet
419 mov byte ptr ds:[si], HEX(13) // Size of buffer
424 mov si, offset maybe_msg
430 cmp byte ptr ds:[sp_drive], dl
433 // Okay, good enough...
434 mov si, offset alright_msg
437 mov byte ptr ds:[DriveNumber], dl
441 // Award BIOS 4.51 apparently passes garbage in sp_drive,
442 // but if this was the drive number originally passed in
443 // DL then consider it "good enough"
445 mov al, byte ptr ds:[DriveNumber]
449 // Intel Classic R+ computer with Adaptec 1542CP BIOS 1.02
450 // passes garbage in sp_drive, and the drive number originally
451 // passed in DL does not have 80h bit set.
461 // No spec packet anywhere. Some particularly pathetic
462 // BIOSes apparently don't even implement function
463 // 4B01h, so we can't query a spec packet no matter
464 // what. If we got a drive number in DL, then try to
465 // use it, and if it works, then well...
466 mov dl, byte ptr ds:[DriveNumber]
467 cmp dl, HEX(81) // Should be 81-FF at least
468 jb fatal_error // If not, it's hopeless
470 // Write a warning to indicate we're on *very* thin ice now
471 mov si, offset nospec_msg
476 jmp .found_drive // Pray that this works...
479 mov si, offset nothing_msg
495 // EAX = file length in bytes
499 // Assumes CS == DS == ES, and trashes BX and CX.
501 // searchdir_iso is a special entry point for ISOLINUX only. In addition
502 // to the above, searchdir_iso passes a file flag mask in AL. This is useful
503 // for searching for directories.
506 xor ax, ax // ZF <- 1
512 mov byte ptr ds:[ISOFlags], al
513 call allocate_file // Temporary file structure for directory
518 mov si, offset CurrentDir
519 cmp byte ptr ds:[di], '/' // If filename begins with slash
521 inc di // Skip leading slash
522 mov si, offset RootDir // Reference root directory instead
524 mov eax, dword ptr ds:[si+dir_clust]
525 mov dword ptr ds:[bx+file_left], eax
526 shl eax, SECTOR_SHIFT
527 mov dword ptr ds:[bx+file_bytesleft], eax
528 mov eax, dword ptr ds:[si+dir_lba]
529 mov dword ptr ds:[bx+file_sector], eax
530 mov edx, dword ptr ds:[si+dir_len]
535 mov cl, byte ptr ds:[di]
541 mov byte ptr ds:[di-1], 0 // Terminate at directory name
542 mov cl, 2 // Search for directory
543 xchg cl, byte ptr ds:[ISOFlags]
545 push di // Save these...
548 // Create recursion stack frame...
549 push offset .resume // Where to "return" to
555 // Get a chunk of the directory
556 // This relies on the fact that ISOLINUX doesn't change SI
560 mov cx, word ptr ds:[BufSafe]
565 movzx eax, byte ptr ds:[si] // Length of directory entry
568 mov cl, byte ptr ds:[si+25]
569 xor cl, byte ptr ds:[ISOFlags]
570 test cl, HEX(8E) // Unwanted file attributes!
573 movzx cx, byte ptr ds:[si+32] // File identifier length
574 add si, 33 // File identifier offset
575 call iso_compare_names
579 sub edx, eax // Decrease bytes left
581 add si, ax // Advance pointer
584 // Did we finish the buffer?
585 cmp si, trackbuf+trackbufsize
586 jb .compare // No, keep going
588 jmp short .getsome // Get some more directory
591 // Advance to the beginning of next sector
592 lea ax, [si+SECTOR_SIZE-1]
593 and ax, not (SECTOR_SIZE-1)
595 jmp short .not_file // We still need to do length checks
598 xor eax, eax // ZF = 1
599 mov dword ptr ds:[bx+file_sector], eax
604 mov eax, dword ptr ds:[si+2] // Location of extent
605 mov dword ptr ds:[bx+file_sector], eax
606 mov eax, dword ptr ds:[si+10] // Data length
607 mov dword ptr ds:[bx+file_bytesleft], eax
609 add eax, SECTOR_SIZE-1
610 shr eax, SECTOR_SHIFT
611 mov dword ptr ds:[bx+file_left], eax
613 jz .failure // Empty file?
620 // We get here if we were only doing part of a lookup
621 // This relies on the fact that .success returns bx == si
622 xchg edx, eax // Directory length in edx
623 pop cx // Old ISOFlags
624 pop di // Next filename pointer
625 mov byte ptr ds:[di-1], '/' // Restore slash
626 mov byte ptr ds:[ISOFlags], cl // Restore the flags
627 jz .failure // Did we fail? If so fail for real!
628 jmp .look_for_slash // Otherwise, next level
631 // allocate_file: Allocate a file structure
644 cmp dword ptr ds:[bx], 0
646 add bx, open_file_t_size // ZF = 0
648 // ZF = 0 if we fell out of the loop
654 // iso_compare_names:
655 // Compare the names DS:SI and DS:DI and report if they are
656 // equal from an ISO 9660 perspective. SI is the name from
657 // the filesystem; CX indicates its length, and ';' terminates.
658 // DI is expected to end with a null.
660 // Note: clobbers AX, CX, SI, DI; assumes DS == ES == base segment
663 // First, terminate and canonicalize input filename
665 mov di, offset ISOFileName
675 cmp di, offset ISOFileNameEnd-1 // Guard against buffer overrun
680 cmp byte ptr ds:[di-1], '.' // Remove terminal dots
685 mov byte ptr ds:[di], 0 // Null-terminate string
690 mov ah, byte ptr ds:[di]
693 jz .success2 // End of string for both
694 and al, al // Is either one end of string?
695 jz .failure2 // If so, failure
698 or ax, HEX(2020) // Convert to lower case
702 and ax, ax // ZF = 0 (at least one will be nonzero)
707 // getfssec: Get multiple clusters from a file, given the file pointer.
711 // SI -> File pointer
712 // CX -> Cluster count
714 // SI -> File pointer (or 0 on EOF)
716 // ECX -> Bytes actually read
724 cmp ecx, dword ptr ds:[si+file_left]
726 mov ecx, dword ptr ds:[si+file_left]
729 mov eax, dword ptr ds:[si+file_sector]
734 // ECX[31:16] == 0 here...
735 add dword ptr ds:[si+file_sector], ecx
736 sub dword ptr ds:[si+file_left], ecx
737 shl ecx, SECTOR_SHIFT // Convert to bytes
738 cmp ecx, dword ptr ds:[si+file_bytesleft]
740 mov ecx, dword ptr ds:[si+file_bytesleft]
742 sub dword ptr ds:[si+file_bytesleft], ecx
743 jnz .ret // CF = 0 in this case...
746 mov dword ptr ds:[si+file_sector], eax // Unused
755 // Information message (DS:SI) output
756 // Prefix with "ISOBOOT: "
761 mov si, offset isoboot_str
792 // writechr: Write a character to the screen.
805 // int13: save all the segment registers and call INT 13h.
806 // Some CD-ROM BIOSes have been found to corrupt segment registers
807 // and/or disable interrupts.
818 setc byte ptr ds:[bp+10] // Propagate CF to the caller
828 // Get one sector. Convenience entry point.
832 // Fall through to getlinsec
835 // Get linear sectors - EBIOS LBA addressing, 2048-byte sectors.
838 jmp word ptr cs:[GetlinsecPtr]
843 // getlinsec implementation for floppy/HDD EBIOS (EDD)
848 shl eax, 2 // Convert to HDD sectors
852 push bp // Sectors left
854 call maxtrans // Enforce maximum transfer size
855 movzx edi, bp // Sectors we are about to read
858 // Form DAPA on stack
867 mov dl, byte ptr ds:[DriveNumber]
871 mov ah, HEX(42) // Extended Read
875 lea sp, [si+16] // Remove DAPA
878 add eax, edi // Advance sector pointer
880 sub bp, di // Sectors left
881 shl di, 9 // 512-byte sectors
882 add bx, di // Advance buffer pointer
883 jnc .no_overflow // Check if we have read more than 64K and need to adjust ES
885 add di, HEX(1000) // Adjust segment by 64K (1000h * 16 = 10000h = 64K + 1)
894 pushad // Try resetting the device
896 mov dl, byte ptr ds:[DriveNumber]
899 loop .retry // CX-- and jump if not zero
905 // Truncate BP to MaxTransfer
908 cmp bp, word ptr ds:[MaxTransfer]
910 mov bp, word ptr ds:[MaxTransfer]
915 // This is the variant we use for real CD-ROMs:
916 // LBA, 2K sectors, some special error handling.
919 mov si, offset dapa // Load up the DAPA
920 mov word ptr ds:[si+4], bx
921 mov word ptr ds:[si+6], es
922 mov dword ptr ds:[si+8], eax
924 push bp // Sectors left
925 cmp bp, word ptr ds:[MaxTransferCD]
927 mov bp, word ptr ds:[MaxTransferCD]
929 mov word ptr ds:[si+2], bp
931 mov dl, byte ptr ds:[DriveNumber]
932 mov ah, HEX(42) // Extended Read
936 movzx eax, word ptr ds:[si+2] // Sectors we read
937 add dword ptr ds:[si+8], eax // Advance sector pointer
938 sub bp, ax // Sectors left
939 shl ax, SECTOR_SHIFT-4 // 2048-byte sectors -> segment
940 add word ptr ds:[si+6], ax // Advance buffer pointer
943 mov eax, dword ptr ds:[si+8] // Next sector
946 // INT 13h with retry
948 mov byte ptr ds:[RetryCount], retry_count
953 add sp, 8*4 // Clean up stack
956 mov byte ptr ds:[DiskError], ah // Save error code
958 mov word ptr ds:[DiskSys], ax // Save system call number
959 dec byte ptr ds:[RetryCount]
962 mov al, byte ptr ds:[RetryCount]
963 mov ah, byte ptr ds:[dapa+2] // Sector transfer count
964 cmp al, 2 // Only 2 attempts left
966 mov ah, 1 // Drop transfer size to 1
969 cmp al, retry_count-2
970 ja .again // First time, just try again
971 shr ah, 1 // Otherwise, try to reduce
972 adc ah, 0 // the max transfer size, but not to 0
974 mov byte ptr ds:[MaxTransferCD], ah
975 mov byte ptr ds:[dapa+2], ah
981 mov si, offset diskerr_msg
983 mov al, byte ptr ds:[DiskError]
985 mov si, offset oncall_str
987 mov ax, word ptr ds:[DiskSys]
989 mov si, offset ondrive_str
994 // Fall through to kaboom
997 // kaboom: write a message and bail out. Wait for a user keypress,
998 // then do a hard reboot.
1001 // Restore a clean context.
1009 // Display the failure message.
1010 mov si, offset err_bootfailed
1013 // Wait for a keypress.
1017 // Disable interrupts and reset the system through a magic BIOS call.
1019 mov word ptr ds:[BIOS_magic], 0
1020 ljmp16 HEX(0F000), HEX(0FFF0)
1023 // writehex[248]: Write a hex number in (AL, AX, EAX) to the console
1030 jmp short writehex_common
1036 jmp short writehex_common
1062 // pollchar_and_empty: Check if we have an input character pending (ZF = 0)
1063 // and empty the input buffer afterwards.
1067 mov ah, 1 // Did the user press a key?
1069 jz .end_pollchar // No, then we're done
1070 mov ah, 0 // Otherwise empty the buffer by reading it
1077 /* INITIALIZED VARIABLES *****************************************************/
1079 .ascii "Press any key to boot from the ReactOS medium", NUL
1083 .ascii "ISOBOOT: ", NUL
1085 .ascii "Loading spec packet failed, trying to wing it...", CR, LF, NUL
1087 .ascii "Found something at drive = ", NUL
1089 .ascii "Looks reasonable, continuing...", CR, LF, NUL
1091 .ascii "Extremely broken BIOS detected, last attempt with drive = ", NUL
1093 .ascii "Failed to locate CD-ROM device; boot failed.", CR, LF, NUL
1095 .ascii "Disk error ", NUL
1097 .ascii ", AX = ", NUL
1099 .ascii ", drive ", NUL
1101 .ascii CR, LF, "Boot failed: press a key to retry...", NUL
1103 .ascii "/LOADER", NUL
1105 .ascii "LOADER dir not found.", CR, LF, NUL
1107 .ascii "SETUPLDR.SYS", NUL
1109 .ascii "SETUPLDR.SYS not found.", CR, LF, NUL
1113 .word trackbufsize/SECTOR_SIZE // Clusters we can load into trackbuf
1115 // Maximum transfer size
1118 .word 127 // Hard disk modes
1123 // El Torito spec packet
1127 .byte HEX(13) // Size of packet
1129 .byte 0 // Media type
1131 .byte 0 // Drive number
1133 .byte 0 // Controller index
1135 .long 0 // LBA for emulated disk image
1137 .word 0 // IDE/SCSI information
1139 .word 0 // User-provided buffer
1141 .word 0 // Load segment
1143 .word 0 // Sector count
1145 .byte 0,0,0 // Simulated CHS geometry
1147 .byte 0 // Scratch, safe to overwrite
1150 // EBIOS disk address packet
1154 .word 16 // Packet size
1156 .word 0 // Block count
1158 .word 0 // Offset of buffer
1160 .word 0 // Segment of buffer
1162 .long 0 // LBA (LSW)
1163 .long 0 // LBA (MSW)
1166 // Extend the size to cover one 2K-sized sector